From a2ea545b79f312d5d3967cd5d176e2374c5f0d27 Mon Sep 17 00:00:00 2001 From: Diego Imbert <70353967+diegoimbert@users.noreply.github.com> Date: Tue, 24 Jun 2025 18:42:46 +0200 Subject: [PATCH] save editor cursor positions (#6039) * save global css panel cursor position * save editor cursor positions globally * better initialCursorPos + avoid flicker * unused var err * editorPositionMap global * handle cursor pos saving in editors * better editor keys * feat: add workspaceStore to all editor key props for better isolation - Added workspaceStore import to InlineScriptEditor.svelte - Updated all editor keys to include workspace prefix: - CssSettings: `app-global-css-editor-${$workspaceStore}-${$appPath}` - InlineScriptEditor: `app-inline-${$workspaceStore}-${$appPath}-${id}` - FlowModuleComponent: `flow-inline-${$workspaceStore}-${$pathStore}-${flowModule.id}` This ensures cursor positions are isolated per workspace for multi-workspace scenarios. Co-authored-by: Ruben Fiszel --------- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> Co-authored-by: Ruben Fiszel --- frontend/src/lib/components/Editor.svelte | 10 ++++++++++ frontend/src/lib/components/SimpleEditor.svelte | 16 ++++++++++++++-- .../editor/componentsPanel/CssSettings.svelte | 12 ++++++------ .../inlineScriptsPanel/InlineScriptEditor.svelte | 4 +++- frontend/src/lib/components/apps/types.ts | 14 +++++++------- .../flows/content/FlowModuleComponent.svelte | 1 + frontend/src/lib/utils.ts | 3 +++ 7 files changed, 44 insertions(+), 16 deletions(-) diff --git a/frontend/src/lib/components/Editor.svelte b/frontend/src/lib/components/Editor.svelte index cbaa1bfc11..d15b989c70 100644 --- a/frontend/src/lib/components/Editor.svelte +++ b/frontend/src/lib/components/Editor.svelte @@ -155,6 +155,7 @@ import { writable } from 'svelte/store' import { formatResourceTypes } from './copilot/chat/script/core' import FakeMonacoPlaceHolder from './FakeMonacoPlaceHolder.svelte' + import { editorPositionMap } from '$lib/utils' // import EditorTheme from './EditorTheme.svelte' let divEl: HTMLDivElement | null = null @@ -188,6 +189,7 @@ export let extraLib: string | undefined = undefined export let changeTimeout: number = 500 export let loadAsync = false + export let key: string | undefined = undefined let lang = scriptLangToEditorLang(scriptLang) $: lang = scriptLangToEditorLang(scriptLang) @@ -1277,6 +1279,10 @@ tabSize: lang == 'python' ? 4 : 2, folding }) + if (key && editorPositionMap?.[key]) { + editor.setPosition(editorPositionMap[key]) + editor.revealPositionInCenterIfOutsideViewport(editorPositionMap[key]) + } } catch (e) { console.error('Error loading monaco:', e) return @@ -1306,6 +1312,10 @@ dispatch('blur') }) + editor?.onDidChangeCursorPosition((event) => { + if (key) editorPositionMap[key] = event.position + }) + editor?.onDidFocusEditorText(() => { dispatch('focus') diff --git a/frontend/src/lib/components/SimpleEditor.svelte b/frontend/src/lib/components/SimpleEditor.svelte index 7f2f79c98d..59fddc13f3 100644 --- a/frontend/src/lib/components/SimpleEditor.svelte +++ b/frontend/src/lib/components/SimpleEditor.svelte @@ -14,7 +14,8 @@ Uri as mUri, languages, type IRange, - type IDisposable + type IDisposable, + type IPosition } from 'monaco-editor' languages.typescript.javascriptDefaults.setCompilerOptions({ @@ -78,6 +79,7 @@ import { vimMode } from '$lib/stores' import { initVim } from './monaco_keybindings' import FakeMonacoPlaceHolder from './FakeMonacoPlaceHolder.svelte' + import { editorPositionMap } from '$lib/utils' // import { createConfiguredEditor } from 'vscode/monaco' // import type { IStandaloneCodeEditor } from 'vscode/vscode/vs/editor/standalone/browser/standaloneCodeEditor' @@ -114,7 +116,8 @@ allowVim = false, tailwindClasses = [], class: className = '', - loadAsync = false + loadAsync = false, + key }: { lang: string code?: string @@ -137,6 +140,8 @@ tailwindClasses?: string[] class?: string loadAsync?: boolean + initialCursorPos?: IPosition + key?: string } = $props() const dispatch = createEventDispatcher() @@ -380,6 +385,10 @@ snippetsPreventQuickSuggestions: disableSuggestions } }) + if (key && editorPositionMap?.[key]) { + editor.setPosition(editorPositionMap[key]) + editor.revealPositionInCenterIfOutsideViewport(editorPositionMap[key]) + } } catch (e) { console.error('Error loading monaco:', e) return @@ -394,6 +403,9 @@ updateCode() }, 200) }) + editor.onDidChangeCursorPosition((event) => { + if (key) editorPositionMap[key] = event.position + }) editor.onDidFocusEditorText(() => { if (!editor) return diff --git a/frontend/src/lib/components/apps/editor/componentsPanel/CssSettings.svelte b/frontend/src/lib/components/apps/editor/componentsPanel/CssSettings.svelte index 63cbf553ac..629352a87f 100644 --- a/frontend/src/lib/components/apps/editor/componentsPanel/CssSettings.svelte +++ b/frontend/src/lib/components/apps/editor/componentsPanel/CssSettings.svelte @@ -13,13 +13,12 @@ import { resolveTheme } from './themeUtils' import ThemeCodePreview from './ThemeCodePreview.svelte' import { sendUserToast } from '$lib/toast' + const { app, appPath } = getContext('AppViewerContext') - const { app } = getContext('AppViewerContext') - - let cssEditor: SimpleEditor | undefined = undefined - let alertHeight: number | undefined = undefined - let themeViewer: any = undefined - let selectedTab: 'css' | 'theme' = 'css' + let cssEditor: SimpleEditor | undefined = $state(undefined) + let alertHeight: number | undefined = $state(undefined) + let themeViewer: any = $state(undefined) + let selectedTab: 'css' | 'theme' = $state('css') function insertSelector(selector: string) { if ($app?.theme?.type === 'path') { @@ -80,6 +79,7 @@ small automaticLayout bind:this={cssEditor} + key={`app-global-css-editor-${$workspaceStore}-${$appPath}`} /> {:else} diff --git a/frontend/src/lib/components/apps/editor/inlineScriptsPanel/InlineScriptEditor.svelte b/frontend/src/lib/components/apps/editor/inlineScriptsPanel/InlineScriptEditor.svelte index 703aea891f..f72c27e543 100644 --- a/frontend/src/lib/components/apps/editor/inlineScriptsPanel/InlineScriptEditor.svelte +++ b/frontend/src/lib/components/apps/editor/inlineScriptsPanel/InlineScriptEditor.svelte @@ -23,7 +23,7 @@ import DiffEditor from '$lib/components/DiffEditor.svelte' import CacheTtlPopup from './CacheTtlPopup.svelte' import EditorSettings from '$lib/components/EditorSettings.svelte' - import { userStore } from '$lib/stores' + import { userStore, workspaceStore } from '$lib/stores' const { runnableComponents, @@ -387,6 +387,7 @@ } $app = $app }} + key={`app-inline-${$workspaceStore}-${$appPath}-${id}`} args={Object.entries(fields).reduce((acc, [key, obj]) => { acc[key] = obj.type === 'static' ? obj.value : undefined return acc @@ -415,6 +416,7 @@ inferSuggestions(e.detail.code) $app = $app }} + key={`app-inline-${$workspaceStore}-${$appPath}-${id}`} /> {/if} diff --git a/frontend/src/lib/components/apps/types.ts b/frontend/src/lib/components/apps/types.ts index b000fc2326..5e20f29358 100644 --- a/frontend/src/lib/components/apps/types.ts +++ b/frontend/src/lib/components/apps/types.ts @@ -23,7 +23,7 @@ import type { TemplateV2AppInput, UploadAppInput, UploadS3AppInput, - UserAppInput, + UserAppInput } from './inputType' import type { World } from './rx' import type { FilledItem } from './svelte-grid/types' @@ -141,13 +141,13 @@ export type HiddenRunnable = { export type AppTheme = | { - type: 'path' - path: string - } + type: 'path' + path: string + } | { - type: 'inlined' - css: string - } + type: 'inlined' + css: string + } export type App = { grid: GridItem[] diff --git a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte index 70ffe5a3ec..2388679010 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte @@ -447,6 +447,7 @@ }, {} )} + key={`flow-inline-${$workspaceStore}-${$pathStore}-${flowModule.id}`} /> = {}