From 4cda65f92e55881d8b98eb26522d5167e2be9b1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=A2=E9=98=B3?= Date: Tue, 28 Jul 2026 14:32:15 +0800 Subject: [PATCH 1/4] docs: design image template tag chip polish --- ...8-image-template-tag-chip-polish-design.md | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-28-image-template-tag-chip-polish-design.md diff --git a/docs/superpowers/specs/2026-07-28-image-template-tag-chip-polish-design.md b/docs/superpowers/specs/2026-07-28-image-template-tag-chip-polish-design.md new file mode 100644 index 0000000..074d3e8 --- /dev/null +++ b/docs/superpowers/specs/2026-07-28-image-template-tag-chip-polish-design.md @@ -0,0 +1,44 @@ +# Image Template Tag Chip Polish Design + +## Goal + +Make the image-template tag filters feel quieter, denser, and more consistent with the rest of OfficeDex while removing the visually heavy native horizontal scrollbar. + +## Scope + +Only the image-template tag filter row is affected. Template filtering behavior, tag counts, selected state semantics, keyboard accessibility, and the image-template card layout remain unchanged. + +## Typography and geometry + +Use the approved compact editorial direction: + +- Label text: `12px`, weight `500`, line-height `1.2`. +- Count text: `10px`, weight `500`. +- Chip minimum height: `26px`. +- Internal horizontal gap: `4px`. +- Chip padding: `2px 8px`. +- Row gap: `8px`. +- Preserve the existing pill radius, border colors, background colors, hover state, and selected state. +- In unselected chips, render the count with the existing muted text token so the label remains the primary scan target. +- In selected and hovered chips, let the count inherit the interactive foreground color for consistent contrast. + +## Horizontal overflow + +Keep `overflow-x: auto` so trackpads, touch gestures, mouse horizontal scrolling, and keyboard-reachable controls continue to work. Hide the native scrollbar visually across Firefox and WebKit/Blink: + +- Firefox: `scrollbar-width: none`. +- WebKit/Blink: hide `::-webkit-scrollbar` by setting its display to `none`. +- Keep momentum scrolling on touch devices. +- Do not clip overflowing tags or replace scrolling with wrapping. + +## Accessibility + +The controls remain native buttons with their existing `aria-pressed` state. Hiding the scrollbar must not remove scrolling or keyboard access. Text remains at least `10px` for the secondary numeric count and `12px` for the actionable label. + +## Verification + +- Add a focused stylesheet contract test that reads `dialogue.css` and verifies the approved chip metrics and cross-browser scrollbar-hiding rules. +- Run the focused stylesheet test and the existing image-template tag and dialogue screen tests. +- Run TypeScript linting. +- Start the browser build and visually verify that the tag row uses the compact typography, has no visible native scrollbar, and still scrolls horizontally when content overflows. + From 4247fc95ae078363fc2a4ced41891a1af77fe3ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=A2=E9=98=B3?= Date: Tue, 28 Jul 2026 14:35:17 +0800 Subject: [PATCH 2/4] docs: plan image template tag chip polish --- ...26-07-28-image-template-tag-chip-polish.md | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-28-image-template-tag-chip-polish.md diff --git a/docs/superpowers/plans/2026-07-28-image-template-tag-chip-polish.md b/docs/superpowers/plans/2026-07-28-image-template-tag-chip-polish.md new file mode 100644 index 0000000..2ad34c5 --- /dev/null +++ b/docs/superpowers/plans/2026-07-28-image-template-tag-chip-polish.md @@ -0,0 +1,159 @@ +# Image Template Tag Chip Polish Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Polish the image-template tag chips with the approved compact typography and hide the native horizontal scrollbar without removing horizontal scrolling. + +**Architecture:** Keep the existing React markup and filtering behavior unchanged. Add one focused CSS contract test to the existing dialogue screen suite, then make the minimum stylesheet-only change in `dialogue.css`. + +**Tech Stack:** React, TypeScript, CSS, Vitest, Testing Library, Vite. + +--- + +### Task 1: Lock the approved chip typography and overflow behavior + +**Files:** +- Modify: `src/renderer/screens/DialogueScreens.test.tsx` +- Modify: `src/renderer/styles/dialogue.css:973-1006` + +- [ ] **Step 1: Write the failing stylesheet contract test** + +Add this test beside the existing image-template tag filtering tests in `src/renderer/screens/DialogueScreens.test.tsx`: + +```tsx +it("uses compact tag chips without a visible native horizontal scrollbar", () => { + const css = readFileSync("src/renderer/styles/dialogue.css", "utf8"); + const rowRule = css.match(/\.image-template-tag-filters\s*\{[^}]*\}/s)?.[0] ?? ""; + const chipRule = css.match(/\.image-template-tag-filters button\s*\{[^}]*\}/s)?.[0] ?? ""; + const countRule = css.match(/\.image-template-tag-filters button b\s*\{[^}]*\}/s)?.[0] ?? ""; + const interactiveCountRule = css.match(/\.image-template-tag-filters button:hover b,\s*\.image-template-tag-filters button\.is-selected b\s*\{[^}]*\}/s)?.[0] ?? ""; + const webkitScrollbarRule = css.match(/\.image-template-tag-filters::\-webkit-scrollbar\s*\{[^}]*\}/s)?.[0] ?? ""; + + expect(rowRule).toContain("overflow-x: auto;"); + expect(rowRule).toContain("scrollbar-width: none;"); + expect(rowRule).toContain("-webkit-overflow-scrolling: touch;"); + expect(chipRule).toContain("min-height: 26px;"); + expect(chipRule).toContain("gap: 4px;"); + expect(chipRule).toContain("padding: 2px 8px;"); + expect(chipRule).toContain("font-size: 12px;"); + expect(chipRule).toContain("font-weight: 500;"); + expect(chipRule).toContain("line-height: 1.2;"); + expect(countRule).toContain("color: var(--n-muted);"); + expect(countRule).toContain("font-size: 10px;"); + expect(countRule).toContain("font-weight: 500;"); + expect(interactiveCountRule).toContain("color: inherit;"); + expect(webkitScrollbarRule).toContain("display: none;"); +}); +``` + +- [ ] **Step 2: Run the focused test and verify RED** + +Run: + +```bash +npm test -- --run src/renderer/screens/DialogueScreens.test.tsx -t "uses compact tag chips without a visible native horizontal scrollbar" +``` + +Expected: FAIL because the current row uses `scrollbar-width: thin`, the chips inherit the larger body font, and no WebKit scrollbar-hiding rule exists. + +- [ ] **Step 3: Implement the minimum CSS change** + +Replace the existing tag-filter rules in `src/renderer/styles/dialogue.css` with: + +```css +.image-template-tag-filters { + display: flex; + min-width: 0; + gap: 8px; + overflow-x: auto; + padding: 0 1px 2px; + scrollbar-width: none; + -webkit-overflow-scrolling: touch; +} + +.image-template-tag-filters::-webkit-scrollbar { + display: none; +} + +.image-template-tag-filters button { + display: inline-flex; + flex: 0 0 auto; + align-items: center; + gap: 4px; + min-height: 26px; + border: 1px solid var(--n-hairline); + border-radius: var(--radius-full); + background: var(--n-canvas); + color: var(--n-slate); + padding: 2px 8px; + font-family: inherit; + font-size: 12px; + font-weight: 500; + line-height: 1.2; + letter-spacing: -0.01em; + cursor: pointer; +} + +.image-template-tag-filters button b { + color: var(--n-muted); + font-size: 10px; + font-weight: 500; +} + +.image-template-tag-filters button:hover, +.image-template-tag-filters button.is-selected { + border-color: var(--n-primary); + background: var(--n-primary-soft); + color: var(--n-primary); +} + +.image-template-tag-filters button:hover b, +.image-template-tag-filters button.is-selected b { + color: inherit; +} +``` + +- [ ] **Step 4: Run focused tests and verify GREEN** + +Run: + +```bash +npm test -- --run src/renderer/screens/DialogueScreens.test.tsx -t "uses compact tag chips without a visible native horizontal scrollbar" +npm test -- --run src/renderer/imageTemplateTags.test.ts src/renderer/screens/DialogueScreens.test.tsx +``` + +Expected: the focused contract test passes; the existing image-template tag and dialogue suite reports `172` or more passing tests with zero failures. + +- [ ] **Step 5: Run static verification** + +Run: + +```bash +npm run lint +git diff --check +``` + +Expected: both commands exit successfully with no TypeScript or whitespace errors. + +- [ ] **Step 6: Verify the UI in the browser build** + +Run: + +```bash +npm run dev:browser +``` + +Open `http://localhost:3100/`, enter the Image generation screen, and confirm: + +- chips render at the approved compact density; +- counts are visually subordinate until hover or selection; +- no native horizontal scrollbar is visible; +- trackpad or shift-wheel horizontal scrolling still reveals off-screen tags; +- selecting a tag still filters the template cards. + +- [ ] **Step 7: Commit the implementation** + +```bash +git add src/renderer/screens/DialogueScreens.test.tsx src/renderer/styles/dialogue.css +git commit -m "style: polish image template tag filters" +``` From b553372566184c28e134409cabee5125709bc951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=A2=E9=98=B3?= Date: Tue, 28 Jul 2026 14:44:27 +0800 Subject: [PATCH 3/4] style: polish image template tag filters --- ...8-image-template-tag-chip-polish-design.md | 1 - src/renderer/screens/DialogueScreens.test.tsx | 24 +++++++++++++++ src/renderer/styles/dialogue.css | 29 ++++++++++++++----- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/docs/superpowers/specs/2026-07-28-image-template-tag-chip-polish-design.md b/docs/superpowers/specs/2026-07-28-image-template-tag-chip-polish-design.md index 074d3e8..71bac09 100644 --- a/docs/superpowers/specs/2026-07-28-image-template-tag-chip-polish-design.md +++ b/docs/superpowers/specs/2026-07-28-image-template-tag-chip-polish-design.md @@ -41,4 +41,3 @@ The controls remain native buttons with their existing `aria-pressed` state. Hid - Run the focused stylesheet test and the existing image-template tag and dialogue screen tests. - Run TypeScript linting. - Start the browser build and visually verify that the tag row uses the compact typography, has no visible native scrollbar, and still scrolls horizontally when content overflows. - diff --git a/src/renderer/screens/DialogueScreens.test.tsx b/src/renderer/screens/DialogueScreens.test.tsx index 50d65f1..832a832 100644 --- a/src/renderer/screens/DialogueScreens.test.tsx +++ b/src/renderer/screens/DialogueScreens.test.tsx @@ -4712,6 +4712,30 @@ describe("DialogueScreen state machine", () => { expect(screen.queryByText("UGC")).toBeNull(); }); + it("uses compact tag chips without a visible native horizontal scrollbar", () => { + const css = readFileSync("src/renderer/styles/dialogue.css", "utf8"); + const rowRule = css.match(/\.image-template-tag-filters\s*\{[^}]*\}/s)?.[0] ?? ""; + const chipRule = css.match(/\.image-template-tag-filters button\s*\{[^}]*\}/s)?.[0] ?? ""; + const countRule = css.match(/\.image-template-tag-filters button b\s*\{[^}]*\}/s)?.[0] ?? ""; + const interactiveCountRule = css.match(/\.image-template-tag-filters button:hover b,\s*\.image-template-tag-filters button\.is-selected b\s*\{[^}]*\}/s)?.[0] ?? ""; + const webkitScrollbarRule = css.match(/\.image-template-tag-filters::\-webkit-scrollbar\s*\{[^}]*\}/s)?.[0] ?? ""; + + expect(rowRule).toContain("overflow-x: auto;"); + expect(rowRule).toContain("scrollbar-width: none;"); + expect(rowRule).toContain("-webkit-overflow-scrolling: touch;"); + expect(chipRule).toContain("min-height: 26px;"); + expect(chipRule).toContain("gap: 4px;"); + expect(chipRule).toContain("padding: 2px 8px;"); + expect(chipRule).toContain("font-size: 12px;"); + expect(chipRule).toContain("font-weight: 500;"); + expect(chipRule).toContain("line-height: 1.2;"); + expect(countRule).toContain("color: var(--n-muted);"); + expect(countRule).toContain("font-size: 10px;"); + expect(countRule).toContain("font-weight: 500;"); + expect(interactiveCountRule).toContain("color: inherit;"); + expect(webkitScrollbarRule).toContain("display: none;"); + }); + it("keeps the selected template and edited prompt when the tag filter changes", async () => { listImageTemplatesSpy.mockResolvedValueOnce([ { id: 1, slug: "hero", title: "Hero", description: "", promptPreset: "hero", sortOrder: 1, enabled: true, tags: ["Studio"] }, diff --git a/src/renderer/styles/dialogue.css b/src/renderer/styles/dialogue.css index bf9c936..268dc4c 100644 --- a/src/renderer/styles/dialogue.css +++ b/src/renderer/styles/dialogue.css @@ -976,27 +976,37 @@ gap: 8px; overflow-x: auto; padding: 0 1px 2px; - scrollbar-width: thin; + scrollbar-width: none; + -webkit-overflow-scrolling: touch; +} + +.image-template-tag-filters::-webkit-scrollbar { + display: none; } .image-template-tag-filters button { display: inline-flex; flex: 0 0 auto; align-items: center; - gap: 6px; - min-height: 30px; + gap: 4px; + min-height: 26px; border: 1px solid var(--n-hairline); border-radius: var(--radius-full); background: var(--n-canvas); color: var(--n-slate); - padding: 4px 10px; - font: inherit; + padding: 2px 8px; + font-family: inherit; + font-size: 12px; + font-weight: 500; + line-height: 1.2; + letter-spacing: -0.01em; cursor: pointer; } .image-template-tag-filters button b { - color: inherit; - font-size: 11px; + color: var(--n-muted); + font-size: 10px; + font-weight: 500; } .image-template-tag-filters button:hover, @@ -1006,6 +1016,11 @@ color: var(--n-primary); } +.image-template-tag-filters button:hover b, +.image-template-tag-filters button.is-selected b { + color: inherit; +} + .image-template-grid { display: flex; gap: 10px; From c18b369d604146d5065596dd7f068cda2f0bcaf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=A2=E9=98=B3?= Date: Tue, 28 Jul 2026 14:57:23 +0800 Subject: [PATCH 4/4] release: prepare OfficeDex 0.6.8 --- package-lock.json | 4 ++-- package.json | 2 +- wails.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 186bee8..f1599f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "officedex", - "version": "0.6.7", + "version": "0.6.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "officedex", - "version": "0.6.7", + "version": "0.6.8", "license": "GPL-3.0-only", "dependencies": { "@ant-design/icons": "^6.1.0", diff --git a/package.json b/package.json index 535437d..ca9f09d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "officedex", - "version": "0.6.7", + "version": "0.6.8", "description": "The AI desktop workspace for documents, slides, and spreadsheets. Wails v2 + React 19 client for OfficeCLI.", "license": "GPL-3.0-only", "homepage": "https://github.com/officecli/officedex#readme", diff --git a/wails.json b/wails.json index c94dbb8..e33bd17 100644 --- a/wails.json +++ b/wails.json @@ -14,7 +14,7 @@ }, "info": { "productName": "OfficeDex", - "productVersion": "0.6.7", + "productVersion": "0.6.8", "copyright": "", "comments": "OfficeDex desktop app (Wails v2 + Go)" }