- Implement auto-sync to Shopify: saving media for an unsynced product now creates it as a Draft in Shopify.
- Update Product.ts to default new items to DRAFT status.
- Allow getMediaSavePlan to run without a shopify_id (planning for new products).
- Fix description saving in mediaHandlers to reconcile 'body_html' and common variants.
- Sanitize empty quotes in MediaManager description textarea.
- Update mediaHandlers.test.ts to verify auto-creation behavior and fix mock pollution.
This change modifies the validation/planning phase to skip the expensive thumbnail generation step in 'getUnifiedMediaState'. Since the planning phase primarily needs file IDs and names to calculate deletions, adoptions, and reorders, skipping the thumbnail verification/retrieval (including sidecar checks) significantly reduces the latency of the 'Save Changes' operation.
- Update mediaHandlers.ts to accept an optional forcedThumbnailUrl in updateSpreadsheetThumbnail, enabling updates without re-fetching backend state.
- Update MediaManager.html execution plan to trigger the sheet update immediately (optimistically) using the predicted first item from the plan, running in parallel with other execution phases.
- Ensure the execution flow waits for both the sheet update and other operations to complete before finishing.
- Add failing global function verification test (GlobalFunctions.test.ts) and fix missing exports in global.ts.
- Refactor MediaManager.html UI:
- Implement
enderPlanHtml to standardize Plan (Details) and Execution views.
- Show 'Skipped' state for empty save phases.
- Visually decouple 'Sheet Update' from 'Reorder' phase.
- Separate 'Manual Link' operations into their own 'Linking' section in the plan view, distinct from Adoptions.
- Fix TypeErrors in
enderPlanHtml (undefined actions) and
enderMatch (missing DOM elements).
- Update MediaService.test.ts to match new filename constraints on reorder.
- Update mediaHandlers.test.ts to correctly spy on loose MediaService instances.
- Ensure all tests pass.
- Updates the 'Link Media' wizard and confirmation modal to make filenames clickable.
- Links Drive files to their view page.
- Links Shopify files to the Admin Content > Files page, derived from the product admin URL.
- Applies primary theme color to links for better visibility.
- **Batch Linking UI**: Added 'queueing' mechanism for links, allowing multiple manual links to be defined before saving.
- **Critical Save Fix**: Intercept saveChanges to strictly enforce the 'Confirmation Wizard' for pending links, ensuring items are merged in memory before backend processing to prevent duplication.
- **Adoption Persistence**: Updated MediaService to explicitly write shopify_media_id to Drive file properties during save, fixing race conditions where linked items were re-adopted as orphans.
- **Plan Accuracy**: Updated calculateDiff to exclude pending link items from generating duplicate 'Sync' or 'Adopt' actions.
- **Order Preservation**: Implemented logic to ensure the 'Synced' item creates/persists at the position of the *first* item in the linked pair.
- **Testing**: Added src/MediaStateLogic.test.ts as a permanent test suite for complex frontend state logic, covering queuing, plan generation, and invariant safety.
- Fix Uncaught SyntaxError in MediaManager.html by using attribute selector for IDs with special characters (e.g. Shopify GIDs).
- Ensure Link Selected button visibility updates correctly by refactoring updateLinkButtonState and calling it on selection changes.
- Removed full-page 'Connecting...' loading screen after matching modal closes.
- Ensured main UI (SKU info and gallery shell) appears immediately after the wizard.
- Leveraged grid-level loading indicator for non-blocking background refresh.
- Verified transition stability with integration and handler tests.
- Switched from index-based to unique ID-based deletion to fix incorrect target after reordering.
- Enhanced media state normalization in loadMedia to ensure robust link button () visibility.
- Implemented targeted UI updates via ui.updateCardState(id) to prevent thumbnail flickering on status changes.
- Standardized vertical card spacing to 8px and corrected Gallery card action bar overhang/overlap.
- Added selection logic to MediaState to track Drive-only and Shopify-only items
- Refined UI to include a link button () in media card action overlays
- Reordered action buttons to: Preview, Link, Delete
- Replaced JS confirm with a visual side-by-side matching modal for linking
- Added adaptive 'Link Selected' button to the gallery header
- Fixed TypeError by restoring the quick-links element
Significant performance improvements to the 'Loading media...' phase:
- Reduced client-server round trips by consolidating the initial handshake (diagnostics + media fetch) into a single backend call: getMediaManagerInitialState.
- Implemented batched Google Drive metadata retrieval in GASDriveService using the Advanced Drive API, eliminating per-file property fetching calls.
- Switched to HtmlService templates in showMediaManager to pass initial SKU/Title data directly, enabling the UI shell to appear instantly upon opening.
- Updated documentation (ARCHITECTURE.md, MEMORY.md) to clarify Webpack global assignment requirements for GAS functions.
- Verified with comprehensive updates to unit and integration tests.
- Added .grid-disabled CSS class to prevent pointer events and provide visual feedback (grayscale/opacity) during save.
- Implemented UI.prototype.setSavingState to toggle grid interaction and disable SortableJS reordering.
- Integrated setSavingState into controller.saveChanges to block edits while saving is in progress.
- Added loading message updates ('Refreshing media...' and 'Loading media...') for better UX.
- Implemented simultaneous execution of getMediaDiagnostics and getMediaForSku in MediaManager.html to speed up initial load and refresh.
- Added a translucent grid-loading-overlay that appears over existing tiles during refresh, preventing interaction while maintaining context.
- Differentiated loading messages: 'Connecting to systems...' for initial load vs 'Refreshing media...' for updates.
- Fixed a syntax error in the save handler.
- Remove recursive polling in MediaManager.html; context is now loaded once at startup.
- Optimize getSelectedProductInfo in mediaHandlers.ts to reduce SpreadsheetApp API calls.
- Update related tests to match new optimization.
- **Preload Thumbnails**: Implemented image preloading in the media matching wizard to ensure instant rendering of candidate matches.
- **Async Linking**: Refactored the linking confirmation to be asynchronous. The UI now optimistically advances to the next match immediately, performing the backend linking in the background.
- **Completion Gate**: Added a check to ensure all pending background linking operations verify completion before the wizard closes and reloading occurs.
- **Video Support**: Verified that filename-based matching correctly handles video files by extracting filenames from Shopify video URLs.
- Unified transfer session UI layout with instructions at the top and footer controls.
- Implemented side-by-side 'Re-open Popup' and 'Cancel' buttons with proper state management (disabled/enabled).
- Added dynamic service context to instructions (e.g., 'Importing from Google Photos').
- Refactored UI class to handle new DOM structure and button logic.
- Updated controller to support new UI interactions and improved cancellation flow.
- Fix corrupted line in src/MediaManager.html causing syntax error.
- Add ESLint integration to build process to prevent future syntax errors.
- Create .eslintrc.js with TypeScript and HTML support.
- Relax strict lint rules to accommodate existing codebase.
- **UI Overhaul**: Moved the activity log to a dedicated, expandable card at the bottom of the Media Manager modal.
- **Styling**: Updated the log card to match the application's light theme using CSS variables (`--surface`, `--text`).
- **Log Streaming**: Replaced batch logging with real-time streaming via `CacheService` and `pollJobLogs`.
- **Session Resumption**: Implemented logic to resume log polling for active jobs upon page reload.
- **Fixes**:
- Exposed `pollJobLogs` in `global.ts` to fix "Script function not found" error.
- Updated `mediaHandlers.test.ts` with `CacheService` mocks and new signatures.
- Removed legacy auto-hide/toggle logic for the log.
- Update MediaService delegation tests in src/mediaHandlers.test.ts to use mock.results for more reliable instance retrieval.
- Fix CellImageBuilder failure in src/mediaHandlers.ts by using public Shopify thumbnail URLs for synced items and direct Drive thumbnail endpoints for non-synced items.
- Fallback to IMAGE() formula in the spreadsheet for Drive items to avoid authentication issues with native cell images.
- Add test_*.txt to .gitignore to keep the workspace clean.
- Ensure all tests pass with updated log expectations and mock data.
- Ensure Shopify video sync updates Media Manager with active video previews
- Fix "Image load failed" error for video icons by using Base64 SVG
- Resolve Drive picker origin error by using google.script.host.origin
- Fix Drive video playback issues by using Drive iframe player
- Add `test:log` script to package.json for full output logging in Windows
- Update .gitignore to exclude coverage, test_output.txt, and .agent/
- Remove test_output.txt from git tracking
Updates logic to detect processing state (including READY-but-no-sources race condition) and propagates contentUrl updates to the frontend immediately.
revert feat: Implement Server-Side Chunked Transfer for Drive Uploads
- Implemented 'Client-Orchestrated, Server-Side Chunked Transfer' to bypass CORS and 50MB limits for Google Photos.
- Added 'getResumableUploadUrl' to GASDriveService for high-priority video processing.
- Refactored 'MediaManager.html' to orchestrate uploads using 'transferRemoteChunk' loop.
- Added 'getRemoteFileSize' and 'transferRemoteChunk' to 'mediaHandlers.ts'.
- Updated 'global.ts' to expose new backend functions.
revert feat: Add custom video thumbnails for Drive uploads
- Implemented custom thumbnail injection in GASDriveService.getResumableUploadUrl.
- Fetches thumbnails from Google Photos using w320 size to avoid API limits.
- Added strict < 2MB size check for thumbnails.
- Updated mediaHandlers and MediaManager to pass sourceUrl to the backend.
- This allows Drive to display a visual cue immediately for video files still processing.
- Implemented "sidecar" thumbnail logic: imports video thumbnails from Google Photos as hidden Drive files to display immediately while videos process.
- Updated MediaService to serve sidecar thumbnails via server-side Base64 encoding, bypassing CORS restrictions.
- Implemented lifecycle management: detects video processing completion to automatically cleanup sidecar files and fallback to native Drive thumbnails.
- Enhanced Media Manager UI: added processing warning banner and refined processing tile styling (centered, lighter overlay).
- Upgraded Drive API to v3 and improved file creation robustness with Advanced API fallbacks.
- Implemented custom thumbnail injection in GASDriveService.getResumableUploadUrl.
- Fetches thumbnails from Google Photos using w320 size to avoid API limits.
- Added strict < 2MB size check for thumbnails.
- Updated mediaHandlers and MediaManager to pass sourceUrl to the backend.
- This allows Drive to display a visual cue immediately for video files still processing.
- Implemented 'Client-Orchestrated, Server-Side Chunked Transfer' to bypass CORS and 50MB limits for Google Photos.
- Added 'getResumableUploadUrl' to GASDriveService for high-priority video processing.
- Refactored 'MediaManager.html' to orchestrate uploads using 'transferRemoteChunk' loop.
- Added 'getRemoteFileSize' and 'transferRemoteChunk' to 'mediaHandlers.ts'.
- Updated 'global.ts' to expose new backend functions.
This commit adds robust handling for Google Drive videos that are still processing (lacking thumbnails). Changes include: 1. Backend (MediaService.ts): Implement try/catch around thumbnail generation. If it fails, return a placeholder and flag the item as 'isProcessing'. 2. Frontend (MediaManager.html): - Add polling logic to check for updates on processing items every 15s. - Add UI support for processing state: slate background, centered animated hourglass emoji. - Implement sand animation (toggling hourglass state) and rotation animation (180deg flip on poll event). - Fix badges and positioning issues.
This commit fixes a bug where videos imported from the Google Photos Picker were being downloaded as static thumbnails. Changes include: 1. Frontend (MediaManager.html): Correctly access nested 'mediaFile' properties from the Picker API response to ensure valid mimeType and filename are passed to the backend. Restored logic to force 'video/mp4' mimeType if 'mediaMetadata.video' is present. Added debug logging. 2. Backend (mediaHandlers.ts): Restored missing 'else if' block for URL handling that was causing 'No File ID' errors. Implemented logic to append '=dv' parameter for video downloads. Added safeguard to rename downloaded files to '.mp4' if the content type is video but the extension is wrong.
- Revert 'Unified Embedded Picker' which caused 403 errors due to iframe restrictions on the Google Photos Picker.
- Implement a 'Popup Window' flow for Google Photos selections, keeping the Media Manager active.
- Restore 'Classic' Embedded Picker for Google Drive (DocsView) as it is compatible with iframes.
- Update ppsscript.json with drive.photos.readonly scope for correct permissions.
- Update Media Manager UI to separate Drive and Photos buttons.
- Update \MediaService.ts\ to populate \ humbnail\ and \contentUrl\ from Shopify media when a Drive file is synced.
- Enable \synced\ videos to use the Shopify video URL for playback/hover.
- Update \MediaManager.html\ to allow \synced\ items to render as \<video>\ tags if they have a valid \contentUrl\.
- Add regression tests in \MediaService.test.ts\ for thumbnail and video sync behavior.
- Detect video mime types in MediaService to set correct resource ('VIDEO') and mediaContentType.
- Add fileSize to stagedUploadsCreate payload as required by Shopify for videos.
- Add safety check for missing upload targets to prevent crashes.
- Implement getSize in MockDriveService.
- Add unit test in MediaService.test.ts to verify correct resource and fileSize handling for video uploads.
- Update mock in mediaManager.integration.test.ts to support getSize().
- Added matching wizard to MediaManager.html for linking Drive files to orphaned Shopify media on load.
- Updated MediaService.ts to extract filenames from Shopify URLs for better matching.
- Added linkDriveFileToShopifyMedia method to MediaService and exposed it via mediaHandlers and global.ts.
- Improved UX in MediaManager with image transition clearing and button state feedback.
- Added UI and logic to handle cases where the Media Manager is opened for a row without a SKU.
- Displays a user-friendly error message with a Close button.
- Fixed an issue where the Gallery card was not properly hidden in the error state.
Updated drag event listeners in MediaManager.html to check for 'Files' in dataTransfer.types. This ensures the upload overlay only appears when files are dragged from the OS, preventing interference with SortableJS reordering.
- Resolved persistent 'SyntaxError: Unexpected token class' by refactoring 'MediaState' and 'UI' classes in MediaManager.html to standard ES5 function constructors.
- Resolved 'SyntaxError: Unexpected identifier src' by rewriting 'createCard' to use 'document.createElement' instead of template strings for dynamic media elements.
- Consolidated script tags in MediaManager.html to prevent Apps Script parser merge issues.
- Updated docs/ARCHITECTURE.md and MEMORY.md to formally document client-side constraints (No ES6 classes, strict DOM manipulation for media).
- Note: Google Drive video animate-on-hover functionality is implemented but currently pending verification/fix.
- **UI Refactor**:
- Split Media Manager header into two distinct cards: 'Product Info' and 'Upload Options'.
- 'Product Info' now displays the Product Title and SKU.
- Renamed upload buttons to 'Google Drive', 'Google Photos', and 'Your Computer' for clarity.
- Added global drag-and-drop support with overlay.
- Replaced full-screen 'Connecting' overlay with an inline spinner for better UX and log visibility.
- **Backend**:
- Renamed getSelectedSku to getSelectedProductInfo in mediaHandlers.ts to fetch and return both SKU and Title.
- Updated global.ts exports and mediaHandlers.test.ts to support the new signature.
- **Fixes**:
- Resolved an infinite loop issue in loadMedia caused by incorrect SKU state handling.
- Removed the full-screen 'Connecting' overlay in MediaManager.html
- Implemented an inline loading spinner control
- Ensure log container is visible immediately during initialization so users can track progress
- Backend (MediaService):
- Implemented robust contentUrl generation for Drive files using drive.google.com/uc pattern.
- Added mimeType exposure to unified media state.
- Frontend (MediaManager):
- Replaced video tag with iframe embedding the Google Drive Preview player (/preview) for reliable playback.
- Fixed syntax error in console logging causing UI freeze.
- Updated gallery card logic to prioritize video content URLs.
- Tests (Integration):
- Refactored mediaManager.integration.test.ts to align with new logic.
- Mocked DriveApp completely to support orphan adoption flows.
- Updated file renaming expectations to support timestamped filenames.
- Documentation:
- Updated MEMORY.md with video preview strategy.
This major refactor addresses improper image matching and duplication:
- Implemented strict ID-based matching in 'MediaService', removing the greedy filename matching fallback.
- Redesigned synchronization pipeline to treat Google Drive as the Source of Truth, supporting orphan adoption (Shopify -> Drive) and secure uploads.
- Implemented 'gallery_order' using Drive file properties (supporting both v2 and v3 APIs) for stable, drag-and-drop global ordering.
- Added conditional file renaming using timestamps to enforce '_' naming convention without unnecessary renames.
- Fixed runtime errors in 'MediaService' loops and updated 'ShopifyMediaService' GraphQL mutations to match correctly schema.
- Rewrote 'MediaService.test.ts' with robust test cases for strict matching, adoption, sorting, and reordering.
- Renamed src/MediaSidebar.html to src/MediaManager.html to align with modal UI.
- Fixed race condition in Photo Picker polling preventing duplicate imports.
- Updated global.ts, initMenu.ts, and mediaHandlers.ts used in the fix.
- Fixed unit tests for mediaHandlers.
- Debug Server Error: Fix 403 Forbidden on Photos download by adding OAuth headers.
- Resilience: Implement 3-step import (Copy/Download -> Get Folder -> Move) to isolate failures.
- Workaround: Add blob sanitization and Advanced Drive API (v2) fallback for fragile DriveApp.createFile behavior.
- Docs: Update MEMORY.md and ARCHITECTURE.md with media handling quirks.
- Test: Add comprehensive unit tests for mediaHandlers.ts achieving >80% coverage.