From 9fe86b62b9e5dbd3a133316ab258a06b76022a26 Mon Sep 17 00:00:00 2001 From: th Date: Fri, 24 Apr 2026 08:33:20 +0200 Subject: [PATCH] 0.14.0: multi-folder media drawer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Clipart store holds an array of root folders instead of a single handle, plus an activeHandleIndex for the in-folder view. Getters: activeHandle, inOverview, hasAnyFolder, canGoBack, folderName. - Actions: addFolder (appends a root), enterFolder, exitFolder, removeFolder. Single-folder installs auto-drill into the sole root and behave exactly like the pre-refactor drawer; adding a 2nd+ folder pops back to the root overview. - IDB persistence migrated from the 'clipartDir' single-handle key to a 'clipartDirs' array. Legacy records are migrated on first load. saveClipartFolders unwraps Pinia proxies (toRaw per element) so IDB's structured clone accepts the payload. - ClipartItem union gained a 'folder' kind so root tiles render as first-class media items. listClipart emits nested directories too, but the drawer keeps drill-down one level deep for now. - ClipartDrawer header: three equally-sized .header-btn chips — back (only when canGoBack), refresh (when viewing a folder), and a new `+` (when any folder is attached). No more big 'Choose folder' button — empty state is a centred 96px dashed + with a wrapped hint. - Folder tiles carry a hover `×` to remove a root from the library. - DocumentSettings media tab lists all configured folders with a per- folder remove action; primary button is now 'Add folder'. - Pinia store now calls acceptHMRUpdate so Vite HMR actually swaps the store definition between reloads instead of leaving the first- load state shape behind. - Tests: store actions (addFolder / enterFolder / exitFolder / remove / IDB round-trip), drawer UI (centred + empty state, overview with folder tiles + click-to-enter, back visibility, single-root hides back, header + button triggers addFolder). Tests isolated with try/finally so a failing store assertion can't leak error state into subsequent drawer tests. Co-Authored-By: Claude Opus 4.7 (1M context) --- package.json | 2 +- src/components/CanvasStage.vue | 2 +- src/components/ClipartDrawer.vue | 224 +++++++++++++++++++----- src/components/DocumentSettings.vue | 50 ++++-- src/devtools/tests.ts | 261 +++++++++++++++++++++++++--- src/i18n/messages.ts | 26 ++- src/lib/clipartLibrary.ts | 102 ++++++++--- src/stores/clipart.ts | 171 ++++++++++++++---- src/version.ts | 2 +- 9 files changed, 698 insertions(+), 142 deletions(-) diff --git a/package.json b/package.json index 4d593c2..aeb293b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ruwio", "private": true, - "version": "0.13.1", + "version": "0.14.0", "type": "module", "scripts": { "dev": "vite", diff --git a/src/components/CanvasStage.vue b/src/components/CanvasStage.vue index 05f42d3..461d8ae 100644 --- a/src/components/CanvasStage.vue +++ b/src/components/CanvasStage.vue @@ -2809,7 +2809,7 @@ function onContainerDrop(e: DragEvent) { e.preventDefault() const idx = Number.parseInt(clipartIdx, 10) const item = Number.isFinite(idx) ? clipart.items[idx] : null - if (item) { + if (item && item.kind !== 'folder') { void importClipartAt(item.file, worldX, worldY) } return diff --git a/src/components/ClipartDrawer.vue b/src/components/ClipartDrawer.vue index 415388b..c189665 100644 --- a/src/components/ClipartDrawer.vue +++ b/src/components/ClipartDrawer.vue @@ -1,6 +1,16 @@