From 72b3c0d8cea5e6c6be6b928e514282029d7ed25e Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Wed, 6 May 2026 02:17:17 +0000 Subject: [PATCH] feat(frontend): add text editor flow and create-file UX - replace textarea editor with CodeMirror and syntax highlighting\n- integrate editor mode header (save button, hide unused controls)\n- fix breadcrumb/editor navigation behavior and transitions\n- add Create File action with ghost-rename flow and auto-open in editor\n- refine create-file icon shape and plus cutout alignment --- frontend/package.json | 3 + frontend/src/App.vue | 4 +- frontend/src/assets/svg/create-file.svg | 4 +- frontend/src/components/FileExplorer.vue | 45 +++++++-- frontend/src/components/Gallery.vue | 42 +++++++-- frontend/src/components/HeaderMain.vue | 5 + frontend/src/views/TextEditorView.vue | 112 +++++++++++++++++------ 7 files changed, 172 insertions(+), 43 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 6061686..4e2dd9c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,8 +18,11 @@ "node": ">=18.0.0" }, "dependencies": { + "@codemirror/language-data": "^6.5.2", + "@codemirror/theme-one-dark": "^6.1.3", "@imengyu/vue3-context-menu": "^1.5.3", "@vueuse/core": "^14.1.0", + "codemirror": "^6.0.2", "esbuild": "^0.27.2", "lodash": "^4.17.23", "lodash-es": "^4.17.23", diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 71afd77..32ea297 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -131,7 +131,9 @@ const globalShortcutHandler = (event: KeyboardEvent) => { const fileExplorer = store.fileExplorer as any if (!fileExplorer) return const c = fileExplorer.isCursor() - const input = ['INPUT', 'TEXTAREA'].includes((event.target as HTMLElement).tagName) + const target = event.target as HTMLElement + const input = + ['INPUT', 'TEXTAREA'].includes(target.tagName) || !!target.closest('.cm-editor') const keyup = event.type === 'keyup' // Always clear repeat timer on arrow keyup, even if focus moved to input diff --git a/frontend/src/assets/svg/create-file.svg b/frontend/src/assets/svg/create-file.svg index 3486c99..f9ed1a5 100644 --- a/frontend/src/assets/svg/create-file.svg +++ b/frontend/src/assets/svg/create-file.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/frontend/src/components/FileExplorer.vue b/frontend/src/components/FileExplorer.vue index 227f26a..986ea05 100644 --- a/frontend/src/components/FileExplorer.vue +++ b/frontend/src/components/FileExplorer.vue @@ -12,10 +12,10 @@ - + - + @@ -206,8 +206,20 @@ const rename = async (doc: Doc, newName: string) => { } } defineExpose({ + newFile() { + const now = Math.floor(Date.now() / 1000) + editing.value = new Doc({ + loc: loc.value, + key: 'new', + name: 'New File.txt', + dir: false, + mtime: now, + size: 0, + allocated: 0 + }) + store.cursor = editing.value.key + }, newFolder() { - console.log('New folder') const now = Math.floor(Date.now() / 1000) editing.value = new Doc({ loc: loc.value, @@ -350,19 +362,36 @@ onUnmounted(() => { keyboardFollowScroll.cancel() clearInterval(modifiedTimer) }) -const mkdir = async (doc: Doc, name: string) => { +const editRoute = (path: string) => + '/edit/' + + path + .split('/') + .map(part => encodeURIComponent(part)) + .join('/') + +const createItem = async (doc: Doc, name: string) => { doc.name = name doc.key = crypto.randomUUID() store.addGhost(doc) editing.value = null const path = doc.loc ? `${doc.loc}/${name}` : name try { - const res = await apiFetch(filesUrl(path), { method: 'MKCOL' }) + const res = doc.dir + ? await apiFetch(filesUrl(path), { method: 'MKCOL' }) + : await apiFetch(filesUrl(path), { + method: 'PUT', + body: '', + headers: { 'Content-Type': 'text/plain; charset=utf-8' } + }) if (!res.ok) throw new Error(await parseErrorMessage(res)) - router.push(doc.urlrouter) + if (doc.dir) { + router.push(doc.urlrouter) + } else { + router.push(editRoute(path)) + } } catch (err) { - console.error('Mkdir failed', err) - store.showToast(err instanceof Error ? err.message : 'Mkdir failed') + console.error('Create failed', err) + store.showToast(err instanceof Error ? err.message : 'Create failed') } } const showFolderBreadcrumb = (i: number) => { diff --git a/frontend/src/components/Gallery.vue b/frontend/src/components/Gallery.vue index 3ca587e..abd11fa 100644 --- a/frontend/src/components/Gallery.vue +++ b/frontend/src/components/Gallery.vue @@ -1,6 +1,6 @@