From 15dbf0f8fdaa395c4422793551af8be54b221bc5 Mon Sep 17 00:00:00 2001 From: dev Date: Wed, 27 May 2026 20:52:15 +0200 Subject: [PATCH] =?UTF-8?q?feat(words):=20drawer=20contextual=20panels=20?= =?UTF-8?q?=E2=80=94=20format/image/table/code/list=20(DRAWER-2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The companion drawer now switches its body content based on where the caret is. Five context modes wired to runCommand on the snippet provider, all reading current state to show active highlights: - format: B/I/U/S/code mark toggles (data-active reflects activeMarks), align L/C/R/J (data-active reflects currentTextAlign), Add link (URL prompt) + Remove (canUnlink-gated), Clear formatting. - image: read-only view of src, alt (italic + "(missing)" placeholder when empty), caption, status (pending → accent tint, error → threat tint). Edit-image flow deferred to a follow-up (needs a new updateImage operation; for the spike, view-only is enough to validate the panel works). - table: Rows +/- · Columns +/- · Row/Col header toggles · Striped / Compact toggles · Cell tone Plain/Muted/Accent. Every toggle reads current* state so the user sees which buttons are currently applied. - code: Plain + TS/JS/CSS/HTML/JSON/Bash/Py language chips, data-active reflects currentCodeLanguage. - list: Bullet/Numbered/Check kind chips · ⇤ Outdent / ⇥ Indent. All buttons use onmousedown preventDefault so the editor selection isn't dropped when clicking a drawer action (would otherwise lose the target paragraph / table cell / list item). CSS adds a generic .data-words-drawer-action chip (with hover, active, ghost, full, risk tones) + readonly-text style with pending/error tints. Reused across all panels, so adding new panels later means markup-only, no CSS extension. Verified in browser: - Click table cell → drawer shows TABLE panel with Striped active (matches sample's striped=true) and Plain tone active (matches default cell tone). - Click code block → drawer shows CODE panel with TS chip active (matches sample's language='ts'). - Click list item → drawer shows LIST panel with kind + indent actions. Concept A+D validated visually: ONE surface replaces what would otherwise be bubble menu + slash menu + heading picker + language picker + table popover + image controls + link editor. The contextual switch happens automatically; the user just moves the caret. DRAWER-3 (polish): mobile bottom-sheet layout, A/B toggle in demo controls, image-edit flow — deferred to next session if needed. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../components/words/words-drawer.svelte | 283 +++++++++++++++++- src/uix/eidos/components/words/words.css | 83 +++++ 2 files changed, 361 insertions(+), 5 deletions(-) diff --git a/src/uix/eidos/components/words/words-drawer.svelte b/src/uix/eidos/components/words/words-drawer.svelte index 5ec6b8321..83323be44 100644 --- a/src/uix/eidos/components/words/words-drawer.svelte +++ b/src/uix/eidos/components/words/words-drawer.svelte @@ -174,6 +174,32 @@ function toggleOpen() { open = !open; } + + /** + * Return the image block currently containing the caret, or + * undefined if the caret is not on an image. The image panel uses + * this to display block attributes (src/alt/caption/status). + */ + function imageBlockAtCaret( + s: ProviderSnippetProps + ): { readonly src: string; readonly alt?: string; readonly caption?: string; readonly status?: 'pending' | 'error' } | undefined { + const blockIndex = s.selection?.anchor.path[0]; + if (blockIndex === undefined) return undefined; + const block = s.document.children[blockIndex]; + if (!block || block.type !== 'image') return undefined; + return block; + } + + const CODE_LANGUAGES = [ + { id: '', label: 'Plain' }, + { id: 'ts', label: 'TS' }, + { id: 'js', label: 'JS' }, + { id: 'css', label: 'CSS' }, + { id: 'html', label: 'HTML' }, + { id: 'json', label: 'JSON' }, + { id: 'bash', label: 'Bash' }, + { id: 'python', label: 'Py' } + ] as const;