3 Commits

Author SHA1 Message Date
程序员阿江(Relakkes)
19357fcd87 feat(brand): rebuild the app mark as a vector and recolor it to the themes
`app-icon.png` is a 120KB bitmap, and the seven `.svg` files beside it under
docs/images are 193 bytes each — an `<image>` tag wrapping that bitmap. So the
mark carried its own blue/cyan/orange through all six palettes while everything
around it moved, and there was nothing to recolor.

Measured the bitmap back into geometry rather than redrawing it: connected
components to separate the elements, algebraic circle fits for the arcs, angular
histograms for the openings and stroke widths. Big C is center (415,566) r=131
stroke=60, opening 160°; the second C is center (615,576) r=117 cut into two 68°
arcs; the cursor is a four-point polygon with one notch. The slight ellipticity
and unequal stroke ends were raster artifacts, so they were rounded out. The
shape is otherwise unchanged — differencing a render against the original leaves
only antialiasing.

Color maps the original's three layers onto the palette's own three: the C's take
墨 `--cc-t1`, the bar and cursor and sparkles take 朱 `--cc-ac`.

Six themes do not need six icons. Their accent resolves to two values — #96442B
across the four light palettes, #D07B52 across the two dark ones, which is the
same ochre lightened. Only the ink varies, and ink should follow body text
anyway.

The app icon stays one fixed artwork. Dock and taskbar are drawn by the OS, which
knows nothing about the in-app theme, so paper ground with ink C's ships to every
platform. Inside the app the mark is a vector on tokens and repaints per theme.

Four ideas — two C's, the bar, the cursor, two sparkles — collapse below 24px, so
every consumer sheds parts as it shrinks: sparkles above 40px, cursor above 24px,
C's and bar always. That applies inside icon.icns and icon.ico as much as in
BrandSeal.

BrandSeal was the 「哈」 glyph in a terracotta square; it is now the CC mark drawn
inline on `--color-text-primary` and `--color-brand`. Its five call sites — the
sidebar, both empty states, the H5 connect view, the gallery — follow, and the
about pane drops its `<img>` for the same component.

README leads with the horizontal lockup inside a `<picture>` so GitHub serves the
dark cut in dark mode.
2026-07-27 04:27:41 +08:00
程序员阿江(Relakkes)
c712f52858 feat(desktop): 「纸·墨·印」全量 UI 重设计
按设计交付稿重构桌面端整体视觉,只换表现层,功能、数据契约与
交互逻辑零改动。

主题与地基:
- 6 套主题(纯白/纸墨/经典暖色/青瓷/墨夜/墨夜蓝),源色块 + 单一
  语义映射层两层结构;老设置 light→warm-classic 自动迁移
- 状态色全部成对并过 AA;新增 --color-on-brand-soft、
  --color-search-highlight 对、--color-border-strong(1.4.11 控件边界)
- 衬线标题自托管 Noto Serif SC 拉丁分片,中文回退系统衬线
- 圆角/阴影/动效阶梯按稿定稿;新组件 BrandSeal 印章

屏幕:外壳(52px 标签条陶土下划线、印章侧栏、选中会话浮起卡)、
零态、活跃会话、composer 全部弹层、技能市场、定时任务、设置全
分区、Trace、终端、⌘K,及设计稿未覆盖界面的统一收敛。

Review 修复(两轮 6 主题全量走查 + 盲区补扫挖出):
- 未分层 CSS 压死 utilities 两处(图标 24px、focus ring 被吃)
- font-[var(--font-mono)] 无效写法 35 处(mono 从未生效)
- tertiary 文字与控件边框对比度全线达标化,contrast 守门扩容
- /N alpha 修饰符清零(Safari 15 WebView 整条丢弃)
- 触屏 44px 主目标口径统一;hover 门控控件 H5 常显
- 错误态修复:定时任务静默失败、Trace/会话列表错误呈现
- 宠物窗口 ink-blue 暗色覆盖补齐

测试:264 文件 / 3162 用例全绿(新增六主题对比度、跨文件弹层
一致性、触摸目标、错误态等守门);tsc 零报错;构建通过。
2026-07-26 22:54:45 +08:00
程序员阿江(Relakkes)
f91b62f485 docs(desktop): document the component library and its rules
`components/AGENTS.md` is the reference: what to use for a given need,
where a new component goes, and the style / i18n / a11y / test rules.
`desktop/AGENTS.md` now routes here — the line it replaces ("reuse the
existing desktop design system") named nothing to look up and so was not
an executable instruction for a person or a model.

`docs/component-library-plan.md` keeps the audit evidence and a record of
what actually shipped, including where the plan was wrong.

Two rules earned their own sections because the library broke them
itself:

- Overriding a component's utility with `className` does not reliably
  win. Tailwind sorts same-utility arbitrary values by value and takes
  the last, regardless of the order they were passed. `hoverTone="danger"`
  was a no-op with exactly the two tones it was built for, and its test
  passed because it only asserted the red class was *present*, never that
  the neutral one was gone. Assert that a class prefix appears exactly
  once, and prove it by reverting the fix.
- The library must not compose user-visible English. `SearchField` built
  `Clear ${label}`, which made adopting it an i18n regression for every
  caller that had already translated its clear button. The first repair —
  falling back to `label` — was worse: the input and its clear button
  then shared an accessible name and `getByLabelText` matched both.

Reuse went from 22% to 75% (524 component uses against 179 remaining
native buttons). The remainder are elements that should not be
components: `role="tab"`, `menuitem`, `option`, `treeitem`, `gridcell`,
whole-row and whole-card click targets, drag handles, and the OS
titlebar.
2026-07-26 18:31:34 +08:00