feat(editor): persist full editor preferences#1
feat(editor): persist full editor preferences#1kbdevs wants to merge 3 commits intofix/hud-reviewfrom
Conversation
Keep the last selected aspect ratio and custom X:Y values between editing sessions so the timeline opens with the user's preferred crop setup.
Keep the editor's background, effects, crop, cursor, and export controls between sessions so the workspace reopens with the user's last setup.
There was a problem hiding this comment.
3 issues found across 5 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/components/video-editor/editorPreferences.ts">
<violation number="1" location="src/components/video-editor/editorPreferences.ts:226">
P2: Persisting uploaded wallpaper data URLs in this localStorage blob can exhaust storage quota and make all editor preferences stop saving.</violation>
</file>
<file name="src/components/video-editor/timeline/TimelineEditor.tsx">
<violation number="1" location="src/components/video-editor/timeline/TimelineEditor.tsx:682">
P2: This saves preset aspect ratios as the user's custom ratio. Because `aspectRatio` is copied into `customAspectWidth/customAspectHeight` for every non-`native` selection, choosing a preset like `1:1` will overwrite the persisted custom inputs for the next session.</violation>
</file>
<file name="src/components/video-editor/VideoEditor.tsx">
<violation number="1" location="src/components/video-editor/VideoEditor.tsx:894">
P1: The unconditional wallpaper init effect overwrites the persisted wallpaper preference on every mount. Since this effect runs with `[]` deps and `getAssetPath` is async, it will also race with `applyLoadedProject` and overwrite project-loaded wallpapers. This effect should be conditional — only resolve the default path when no custom wallpaper has been set (e.g., when the current value still matches the unresolved default).</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| const resolvedPath = await getAssetPath( | ||
| DEFAULT_WALLPAPER_RELATIVE_PATH, | ||
| ); | ||
| const resolvedPath = await getAssetPath(DEFAULT_WALLPAPER_RELATIVE_PATH); |
There was a problem hiding this comment.
P1: The unconditional wallpaper init effect overwrites the persisted wallpaper preference on every mount. Since this effect runs with [] deps and getAssetPath is async, it will also race with applyLoadedProject and overwrite project-loaded wallpapers. This effect should be conditional — only resolve the default path when no custom wallpaper has been set (e.g., when the current value still matches the unresolved default).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/video-editor/VideoEditor.tsx, line 894:
<comment>The unconditional wallpaper init effect overwrites the persisted wallpaper preference on every mount. Since this effect runs with `[]` deps and `getAssetPath` is async, it will also race with `applyLoadedProject` and overwrite project-loaded wallpapers. This effect should be conditional — only resolve the default path when no custom wallpaper has been set (e.g., when the current value still matches the unresolved default).</comment>
<file context>
@@ -912,9 +891,7 @@ export default function VideoEditor() {
- const resolvedPath = await getAssetPath(
- DEFAULT_WALLPAPER_RELATIVE_PATH,
- );
+ const resolvedPath = await getAssetPath(DEFAULT_WALLPAPER_RELATIVE_PATH);
if (mounted) {
setWallpaper(resolvedPath);
</file context>
| try { | ||
| const current = loadEditorPreferences(); | ||
| const merged = normalizeEditorPreferences({ ...current, ...preferences }, current); | ||
| globalThis.localStorage.setItem(EDITOR_PREFERENCES_STORAGE_KEY, JSON.stringify(merged)); |
There was a problem hiding this comment.
P2: Persisting uploaded wallpaper data URLs in this localStorage blob can exhaust storage quota and make all editor preferences stop saving.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/video-editor/editorPreferences.ts, line 226:
<comment>Persisting uploaded wallpaper data URLs in this localStorage blob can exhaust storage quota and make all editor preferences stop saving.</comment>
<file context>
@@ -0,0 +1,230 @@
+ try {
+ const current = loadEditorPreferences();
+ const merged = normalizeEditorPreferences({ ...current, ...preferences }, current);
+ globalThis.localStorage.setItem(EDITOR_PREFERENCES_STORAGE_KEY, JSON.stringify(merged));
+ } catch {
+ // Ignore storage failures so editor controls still work.
</file context>
| saveEditorPreferences({ | ||
| customAspectWidth, | ||
| customAspectHeight, | ||
| }); | ||
| }, [customAspectHeight, customAspectWidth]); |
There was a problem hiding this comment.
P2: This saves preset aspect ratios as the user's custom ratio. Because aspectRatio is copied into customAspectWidth/customAspectHeight for every non-native selection, choosing a preset like 1:1 will overwrite the persisted custom inputs for the next session.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/video-editor/timeline/TimelineEditor.tsx, line 682:
<comment>This saves preset aspect ratios as the user's custom ratio. Because `aspectRatio` is copied into `customAspectWidth/customAspectHeight` for every non-`native` selection, choosing a preset like `1:1` will overwrite the persisted custom inputs for the next session.</comment>
<file context>
@@ -676,6 +678,13 @@ export default function TimelineEditor({
}, [aspectRatio]);
+ useEffect(() => {
+ saveEditorPreferences({
+ customAspectWidth,
+ customAspectHeight,
</file context>
| saveEditorPreferences({ | |
| customAspectWidth, | |
| customAspectHeight, | |
| }); | |
| }, [customAspectHeight, customAspectWidth]); | |
| if (isCustomAspectRatio(aspectRatio)) { | |
| saveEditorPreferences({ | |
| customAspectWidth, | |
| customAspectHeight, | |
| }); | |
| } | |
| }, [aspectRatio, customAspectHeight, customAspectWidth]); |
|
Closing this stacked fork PR; the changes are being kept on webadderallorg#38 instead. |
There was a problem hiding this comment.
Pull request overview
Adds a dedicated editor-preferences persistence layer (localStorage-backed) so the video editor can restore the last-used settings across sessions, including expanded editor controls and custom aspect inputs.
Changes:
- Introduces
editorPreferencesmodule with normalization, load/save helpers, and Vitest coverage. - Initializes editor/timeline/settings UI state from persisted preferences.
- Persists editor control changes (and custom wallpapers/custom aspect inputs) back to storage.
Reviewed changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/components/video-editor/timeline/TimelineEditor.tsx |
Loads/saves custom aspect ratio input preferences. |
src/components/video-editor/editorPreferences.ts |
New preferences schema, normalization, and localStorage load/save helpers. |
src/components/video-editor/editorPreferences.test.ts |
Adds unit tests for normalization and persistence behavior. |
src/components/video-editor/VideoEditor.tsx |
Initializes many editor controls from preferences and persists changes. |
src/components/video-editor/SettingsPanel.tsx |
Persists custom wallpapers list and keeps background tabs in sync with selected wallpaper type. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| const [wallpaper, setWallpaper] = useState<string>(initialEditorPreferences.wallpaper); | ||
| const [shadowIntensity, setShadowIntensity] = useState(initialEditorPreferences.shadowIntensity); |
| saveEditorPreferences({ | ||
| customAspectWidth, | ||
| customAspectHeight, | ||
| }); |
| useEffect(() => { | ||
| saveEditorPreferences({ | ||
| wallpaper, | ||
| shadowIntensity, | ||
| backgroundBlur, | ||
| zoomMotionBlur, | ||
| connectZooms, |
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/components/launch/LaunchWindow.tsx">
<violation number="1" location="src/components/launch/LaunchWindow.tsx:229">
P2: Treat the unknown platform as unsupported until `getPlatform()` resolves; otherwise the HUD capture toggle renders briefly on Linux and can call an unsupported action.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| ? recordingsDirectory.split(/[\\/]/).filter(Boolean).pop() || recordingsDirectory | ||
| : "recordings"; | ||
| const dividerClass = "mx-1 h-5 w-px shrink-0 bg-white/35"; | ||
| const supportsHudCaptureProtection = platform !== "linux"; |
There was a problem hiding this comment.
P2: Treat the unknown platform as unsupported until getPlatform() resolves; otherwise the HUD capture toggle renders briefly on Linux and can call an unsupported action.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/components/launch/LaunchWindow.tsx, line 229:
<comment>Treat the unknown platform as unsupported until `getPlatform()` resolves; otherwise the HUD capture toggle renders briefly on Linux and can call an unsupported action.</comment>
<file context>
@@ -204,6 +226,7 @@ export function LaunchWindow() {
? recordingsDirectory.split(/[\\/]/).filter(Boolean).pop() || recordingsDirectory
: "recordings";
const dividerClass = "mx-1 h-5 w-px shrink-0 bg-white/35";
+ const supportsHudCaptureProtection = platform !== "linux";
const toggleMicrophone = () => {
</file context>
| const supportsHudCaptureProtection = platform !== "linux"; | |
| const supportsHudCaptureProtection = platform !== null && platform !== "linux"; |
Summary
Summary by cubic
Persist all editor preferences between sessions so the editor and timeline reopen with your last background, effects, crop, cursor, export, and aspect ratio (including custom X:Y). Adds a new
editorPreferencesmodule with safe normalization and unit tests.New Features
Bug Fixes
Written for commit acf796b. Summary will update on new commits.