@synopackageland/ui 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,218 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ## 0.9.0 (2026-10-11)
6
+
7
+ - **Breaking change:** the package is published only as `@synopackageland/ui` on npmjs. `@synology-eg/syno-pkg-land-ui` and its internal registry release are gone; replace the package name in `package.json` and every import (`@synology-eg/syno-pkg-land-ui/theme.css` becomes `@synopackageland/ui/theme.css`, and so on). The repository moved to https://github.com/synopackageland/syno-pkg-land-ui. The `syno-pkg-land-ui` CLI and CSS names are unchanged.
8
+ - **破壞性變更:** 套件只以 `@synopackageland/ui` 發佈到 npmjs。`@synology-eg/syno-pkg-land-ui` 與內部 registry 發版已移除;請在 `package.json` 與所有 import 換成新名稱(例如 `@synology-eg/syno-pkg-land-ui/theme.css` 改為 `@synopackageland/ui/theme.css`)。原始碼 repo 移至 https://github.com/synopackageland/syno-pkg-land-ui。`syno-pkg-land-ui` CLI 與 CSS 名稱不變。
9
+ - Fix: the npm package ships `CHANGELOG.md` again, so installed consumers can read release notes offline (npm 11 no longer packs it implicitly). `pnpm check:package` now fails when the package lacks it.
10
+ - 修正:npm 包重新附上 `CHANGELOG.md`,安裝後可離線閱讀版本紀錄(npm 11 不再自動打包)。套件缺少它時,`pnpm check:package` 會失敗。
11
+
12
+ ## 0.8.1 (2026-10-10)
13
+
14
+ - Fix: FilePicker sizes follow DSM — a 1024 base with `B`/`KB`/`MB`/`GB`/`TB` labels and at most one decimal (`16 * 1024 ** 3` shows `16 GB`, previously `16.0 GB`; sizes of 1 TB and more no longer show as thousands of GB). The rule is exported as `formatFileSize(bytes)` for application text that repeats a limit.
15
+ - 修正:FilePicker 的大小顯示改依 DSM —— 1024 進位、標籤 `B`/`KB`/`MB`/`GB`/`TB`、最多一位小數(`16 * 1024 ** 3` 顯示 `16 GB`,原為 `16.0 GB`;1 TB 以上不再顯示成數千 GB)。同一規則匯出為 `formatFileSize(bytes)`,供應用程式重述上限的文字使用。
16
+ - Fix: `SectionTabs` no longer shows a vertical scrollbar. Its 30px tabs overflowed the strip's bottom border by 1px (10px when a narrow strip also showed a horizontal scrollbar). The baseline is now painted inside the strip, and a narrow strip scrolls sideways without a visible scrollbar. `Tabs` now does the same instead of letting its tabs spill out of the strip unreachable. `pnpm check:tabs` checks both at 320–1280px in every theme.
17
+ - 修正:`SectionTabs` 不再出現垂直捲軸。原本 30px 的分頁超出 strip 底線 1px(窄 strip 同時出現橫向捲軸時為 10px)。底線改畫在 strip 內,窄 strip 改為無可見捲軸的橫向捲動。`Tabs` 同樣改為橫向捲動,不再讓分頁溢出 strip 而無法到達。`pnpm check:tabs` 在 320–1280px、各 theme 下檢查兩者。
18
+
19
+ ## 0.8.0 (2026-10-08)
20
+
21
+ - **Breaking change:** rename the `syno-ui` CSS class and data attributes to `syno-pkg-land-ui`, without aliases. The themes style only the new names, and components render only the new attributes. Update consumer stylesheets, DOM queries and test selectors:
22
+
23
+ | 0.7.x | 0.8.0 |
24
+ | --- | --- |
25
+ | `.syno-ui-root` | `.syno-pkg-land-ui-root` |
26
+ | `[data-syno-ui]` | `[data-syno-pkg-land-ui]` |
27
+ | `[data-syno-ui-select-trigger]` | `[data-syno-pkg-land-ui-select-trigger]` |
28
+ | `[data-syno-ui-select-search]` | `[data-syno-pkg-land-ui-select-search]` |
29
+ | `[data-syno-ui-select-clear]` | `[data-syno-pkg-land-ui-select-clear]` |
30
+ | `[data-syno-ui-select-create-hint]` | `[data-syno-pkg-land-ui-select-create-hint]` |
31
+ | `[data-syno-ui-select-tag]` | `[data-syno-pkg-land-ui-select-tag]` |
32
+ | `[data-syno-ui-select-tag-remove]` | `[data-syno-pkg-land-ui-select-tag-remove]` |
33
+
34
+ The embedded theme still scopes every rule to the app root (`[data-ui-app]`, unchanged). A literal replacement of `syno-ui` with `syno-pkg-land-ui` covers every row.
35
+ - **破壞性變更(breaking change):** `syno-ui` 開頭的 CSS class 與 data attribute 改名為 `syno-pkg-land-ui`,不保留別名。theme 只樣式化新名稱,元件只輸出新屬性。consumer 的 stylesheet、DOM 查詢與測試 selector 要依上表更新(把 `syno-ui` 逐字換成 `syno-pkg-land-ui` 即可涵蓋全部)。embedded theme 仍把所有規則 scope 在 app root(`[data-ui-app]`,未改名)。
36
+
37
+ ## 0.7.0 (2026-10-08)
38
+
39
+ - **Breaking change:** rename the CLI bin from `syno-ui` to `syno-pkg-land-ui`, without an alias. The npm package remains `@synopackageland/ui`. Update scripts to `npx --no syno-pkg-land-ui <command>` after installing, or `npx -y -p @synopackageland/ui@0.7.0 syno-pkg-land-ui <command>` without a local install. Help, validation hints, generated AI prompts, diagnostic prefixes and release smoke use the new name. The default screenshot directory is now `syno-pkg-land-ui-render`; pass `--out` to choose a fixed directory.
40
+ - **破壞性變更(breaking change):** CLI bin 從 `syno-ui` 改名為 `syno-pkg-land-ui`,不保留別名。npm 套件名仍為 `@synopackageland/ui`。已安裝時改用 `npx --no syno-pkg-land-ui <command>`;未安裝時用 `npx -y -p @synopackageland/ui@0.7.0 syno-pkg-land-ui <command>`。help、驗證提示、產生的 AI prompt、診斷前綴與發版 smoke 同步改名。預設截圖目錄改為 `syno-pkg-land-ui-render`;可用 `--out` 固定目錄。
41
+ - Fix `--help` to print usage on stdout and exit 0, allowing packed-release smoke to succeed. Preserve existing CSS classes and data attributes for consumer compatibility; only injection Symbol descriptions change, retaining their exported keys and identity semantics.
42
+ - 修正 `--help`:將 usage 輸出到 stdout 並回傳 0,讓 tarball 發版 smoke 可成功執行。為相容 consumer,保留既有 CSS class 與 data attribute;僅改 injection Symbol 的描述文字,保留匯出 key 與身分比對語義。
43
+
44
+ ## 0.6.1 (2026-10-08)
45
+
46
+ - Fix: the public package now declares its `syno-ui` bin, so `npx @synopackageland/ui <command>` works. 0.6.0 shipped `dist/cli.js` without the `bin` field. Release staging keeps `bin` and refuses a missing, empty or non-Node bin target; the tarball audit requires every bin target in `dist/` with a `#!/usr/bin/env node` line; and the release job installs the packed tarball in a fresh project and runs `syno-ui --help` and `syno-ui catalog` before publishing.
47
+ - 修正:public 套件補上 `syno-ui` bin,`npx @synopackageland/ui <command>` 可以執行(0.6.0 有 `dist/cli.js` 但缺 `bin` 欄位)。發版 staging 保留 `bin`,bin 目標不存在、空檔或不是 Node script 就拒絕;tarball audit 要求每個 bin 目標在 `dist/` 且以 `#!/usr/bin/env node` 開頭;發版前會把打包好的 tarball 裝進全新專案並執行 `syno-ui --help` 與 `syno-ui catalog`。
48
+ - Fix: `syno-ui render` and the browser checks launch Chrome with `--disable-partial-raster`. Partial raster could redraw an anti-aliased edge one colour level differently, so identical pages sometimes screenshotted differently; this made `check:host` fail intermittently on CI.
49
+ - 修正:`syno-ui render` 與瀏覽器檢查啟動 Chrome 時加上 `--disable-partial-raster`。partial raster 重繪反鋸齒邊緣時可能差一個色階,相同頁面截圖偶爾不一致,造成 CI 上 `check:host` 偶發失敗。
50
+
51
+ ## 0.6.0 (2026-10-08)
52
+
53
+ - **Breaking default change:** `AppRoot` now defaults to `modal-scope="app"`. Dialog/Sheet and popup layers block only that app's background and never mutate `document.body`, so an open dialog no longer locks the DSM desktop or hides other apps. App mode needs a bounded root size (`<AppRoot style="height: 100%">`). Full-page web/Portal apps that want document-wide modality pass `modal-scope="document"`; nesting AppRoots now requires `modal-scope="document"` on both. `pnpm check:host` adds an outbound check: with the embedded theme and an open dialog, DSM elements keep every computed style, their own `@keyframes`, document fonts and html/body inline styles.
54
+ - **預設行為變更(breaking):** `AppRoot` 預設改為 `modal-scope="app"`。Dialog/Sheet 與 popup 只擋該 app 的背景,不再改 `document.body`,開著的 dialog 不會鎖住 DSM 桌面或隱藏其他 app。app 模式需要有界的 root 尺寸(`<AppRoot style="height: 100%">`)。需要整頁 modal 的 web/Portal 頁面請傳 `modal-scope="document"`;巢狀 AppRoot 兩層都要設 `modal-scope="document"`。`pnpm check:host` 新增反向檢查:載入 embedded theme 並開 dialog 後,DSM 元素的 computed style、自身 `@keyframes`、文件字型與 html/body inline style 都不變。
55
+ - Adaptive DSM layout: `AppLayout` switches by its own width between `mobile` (≤600px; slide-out navigation, phone bottom sheets, narrow-table cards), `window` and, only with `wide-layout="web"`, the wide `web` Portal layout. Maximized DSM windows stay in `window` mode. See `docs/adaptive-layout.md`. The phone drawer no longer slides out on first mount, and its keyboard handling stays inside its own app.
56
+ - 自適應 DSM 版面:`AppLayout` 依自身寬度切換 `mobile`(≤600px;滑出導覽、手機 bottom sheet、窄表格卡片)、`window`,以及需 `wide-layout="web"` 才啟用的寬版 `web` Portal 版面。最大化的 DSM 視窗維持 `window`。手機 drawer 首次 mount 不再滑出,鍵盤處理限於自己的 app。
57
+ - New `./json` entry: `SynoJsonRenderer` renders validated JSON UI specs (json-render 0.21) from a component catalog; `AppShell` accepts `wideLayout`. `validateSynoJsonSpec` reports errors with JSON pointer paths and fix hints, including `$computed` functions and `t()` message keys. The catalog-generated JSON Schema and AI prompt are exported as `./json-catalog.schema.json` and `./json-ai-prompt.md`.
58
+ - 新增 `./json` entry:`SynoJsonRenderer` 依 catalog 渲染驗證過的 JSON UI spec;`validateSynoJsonSpec` 以 JSON pointer 路徑與修正提示回報錯誤。由 catalog 生成的 JSON Schema 與 AI prompt 以 `./json-catalog.schema.json`、`./json-ai-prompt.md` 匯出。
59
+ - New `syno-ui` CLI (`npx @synopackageland/ui <command>`): `catalog`, `describe`, `schema`, `prompt`, `validate` (`--json`, `--strict`; exit 1 for spec errors, 2 for unreadable inputs) and `render`, which validates, screenshots 390/640/960/1350px in a local Chrome and names overflowing or clipped spec elements. See the AI tools section of `docs/json-ui.md`.
60
+ - 新增 `syno-ui` CLI:`catalog`、`describe`、`schema`、`prompt`、`validate` 與 `render`(用本機 Chrome 截 390/640/960/1350px 並指出 overflow/被裁切的 spec 元素)。
61
+ - Fix: the embedded theme's Tailwind `spin`/`pulse` keyframes are renamed `ui-spin`/`ui-pulse`, so they cannot replace DSM or other apps' animations of the same name (since 0.5.8); the build rejects unprefixed keyframes. The package now ships the documents the README links to. DataTable's empty mobile view is outside its ARIA list; Portal page surfaces are separated from the toolbar.
62
+ - 修正:embedded theme 的 `spin`/`pulse` keyframes 改名為 `ui-spin`/`ui-pulse`,不再蓋掉 DSM 或其他 app 同名動畫,build 會擋未加前綴的 keyframes。套件附上 README 連結的文件。DataTable 行動版空狀態移出 ARIA list;Portal page surface 與 toolbar 分開。
63
+
64
+ ## 0.5.8 (2026-10-07)
65
+
66
+ - Fix embedded Tabs/Select controls reverting to the browser font while their parents use the theme font. The host reset now preserves inherited control fonts; host acceptance checks computed font stacks as well as exact screenshot equality. Update Switch browser acceptance for ids forwarded to the actual control.
67
+ - 修正 embedded Tabs/Select 被 host reset 改回瀏覽器字型,改為繼承 app 字型;host gate 同時驗證 computed font stack 與圖像完全一致。Switch 瀏覽器驗收更新為實際 control 上的 id。
68
+ - Add Ubuntu GitHub Actions CI and a public npm tag release pipeline, sharing frozen npmjs installs, library/package/notices checks, responsive/host/switch/layout/motion acceptance and workbench builds. Pin actions by commit and cache pnpm/Chromium.
69
+ - Gate public releases on exact stable tag/version, default-branch ancestry, npm version availability and the actual packed dist/exports/license/metadata. Branches, PRs and manual dispatch rehearse with `npm publish --dry-run`; only pushed version tags can publish and create a changelog-backed GitHub Release. Preserve the internal release path and default ego motion runner; add a Playwright motion backend for Linux.
70
+ - 新增 Ubuntu CI 與 public npm tag 發版流程,共用 npmjs 鎖定安裝、完整 library/package/notices、responsive/host/switch/layout/motion 與 workbench 檢查;action 釘 SHA 並快取 pnpm/Chromium。
71
+ - 正式發版驗證穩定版 tag/版本、預設分支歷史、npm 未存在及實際 tarball 的 dist/exports/授權/metadata。分支、PR、手動執行以 dry-run 演練;只有推 tag 可發 npm 並建立 changelog Release。保留內部發版與 ego motion,新增 Linux Playwright motion。
72
+
73
+ ## 0.5.7 (2026-10-07)
74
+
75
+ - Fix inherited `FormItem direction="auto"` inside narrow containers: the Form now preserves the requested direction in its context, and every item resolves auto using its own width. A 96px FieldCell stays vertical even in a wide Form. Explicit Form/FormItem directions remain supported. Remove the downstream `direction="vertical"` workaround when the intent is auto.
76
+ - **Default change:** SettingsRow stacks its label above its control below 520px of its own width; wider rows retain two columns. Container-responsive Form, FormItem, RepeatRow, FieldCell, CardGrid and overflow layouts use local geometry. Row/Grid/Space responsive breakpoint objects retain their documented viewport semantics.
77
+ - **Default change:** built-in component text now follows reactive `ConfigProvider locale` across en, zh-TW, zh-CN, ja, ko, de, fr, es and it. This includes Link external/download annotations, select/search placeholders, dialog/panel/wizard actions, loading/empty states, tree/grid/pagination controls, notifications/toast announcements, date/color/schedule pickers and typography hints. Existing explicit text/label props and partial text objects win, including empty strings. DatePicker's calendar locale now inherits Provider locale unless explicitly supplied. Validation fallback messages use the locale at validation time; controller-generated message-box titles use the locale when opened. See `docs/component-defaults-0.5.7.md` for the audited inventory.
78
+ - Remove downstream Link `external-label`/`download-label` translations when they only duplicate these defaults. Keep application link text and custom terminology translated by the application.
79
+ - Document FilePicker's existing `type` → `size` → `duplicate` → `count` rejection precedence, single-file replacement/removal, empty selection and application read/parse errors; no new rejection reason or replacement behavior.
80
+ - Verify KeyValueRow's existing wrapping with a 256-character unbroken version string in a 320px nested container: it wraps without overflow in all responsive gate modes. Downstream `.connect-value` wrapping can be removed for this tested composition.
81
+ - Extend `pnpm check:responsive` with inherited-auto 96px FieldCells, nested 320px Form/SettingsRow/CardGrid and long-value measurements at 640/960/1280/1920 in layered/embedded/embedded-host modes.
82
+
83
+ ## 0.5.6 (2026-10-07)
84
+
85
+ - Form controls automatically bind the enclosing `FormItem` control id, description, help/error, required and invalid ARIA state. Explicit ids update the label target; custom descriptions merge with the field's descriptions. Input changes, selection commits, disabled state and blur report to field validation. Composite popup inputs cannot reuse the field id; sibling controls have one primary control and retain unique ids when it is removed.
86
+ - `Select`, the combobox family, `Checkbox`, `Switch`, numeric/date/color controls, `SearchField`, `CopyCutField`, `FilePicker` and `ScheduleTable` forward identity, ARIA, data attributes and control listeners to their actual control; layout class/style stay on wrappers. `SegmentItem` and `SplitButton` forward attrs to their action button. Overlay attrs target the popup surface (Tooltip attrs target its trigger), and custom dialog/popover/menu ids retain trigger `aria-controls` links. `Progress` attrs target its progressbar.
87
+ - **Default change:** `Form` and standalone `FormItem` use `direction="auto"`: below 520px of available container width, labels stack above controls; wider containers retain the horizontal 180px label column. Pass `direction="horizontal"` for the old fixed layout, or `direction="vertical"` for an always-stacked form. A FormItem can override its enclosing form. Zero-width/SSR containers initially retain the horizontal layout.
88
+ - `Input` and `Select` protect readable minimum widths (96/128px, capped by the available width); Select labels truncate while the arrow cannot shrink. Keep adjacent controls in wrapping `FieldCell`/`RepeatRow` compositions.
89
+ - `RepeatRow` reserves a separate remove column and aligns removal with the first control's center, including labelled cells and wrapped rows; new `removeDisabled` prop. **Default change:** the remove button stays on the first line rather than following the last wrapped cell. Row fields now live in an inner wrapping element; update direct-child CSS selectors to `[data-repeat-fields] > …`.
90
+ - `FilePicker variant="compact"` renders a normal button and optional file list. All text, limits, removal and rejection messages accept `text` overrides with named `{name}`, `{size}`, `{count}` placeholders. Built-in locales: en, zh-TW, zh-CN, ja, ko, de, fr, es, it; `ConfigProvider locale` supplies a reactive default and an explicit FilePicker locale wins. The existing dropzone is retained, with a single native keyboard action instead of nested button roles. Disabled drop/selection/removal paths refuse changes.
91
+ - New `Link`: native href/download behavior, external/download icons, safe new-tab rel, visible keyboard focus, disabled activation, and overridable accessibility labels. New `KeyValueRow`: compact read-only description-list rows with value/label slots.
92
+ - `Wizard showSteps` adds an optional noninteractive step guide using registered step headings; navigation still goes through the existing guards. `footer.startLabel` and `progressText(current,total)` make the remaining default text replaceable; footer actions wrap.
93
+ - Scoped preflight normalizes heading/paragraph/list/button/form/table defaults after the DSM host reset and before library utilities. Both themes use the same base rules; the reset-only entry remains host reset only. **Default change:** raw app headings, paragraphs and lists no longer retain browser margins/bullets, and raw buttons no longer retain browser borders/backgrounds. Add explicit app typography/spacing classes or semantic `Typo*` components where those defaults were intentional.
94
+ - **Default change:** `EmptyState` minimum height is 112px with 20px vertical padding (previously 192px / 40px). Pass `class="min-h-48 py-10"` to restore the previous space.
95
+ - Remaining internal strings in DatePicker, InputSteppers, ColorPicker, AboutDialog, ToastProvider, ScheduleTable, TypoHeader, AppSidebar, SectionTabs and AdvancePanelGrid are now configurable. ScheduleTable supports disabled state and translated day labels. Forced-colors mode uses an outline for keyboard focus because shadows can disappear; no dark theme is claimed.
96
+ - TreeView disabled nodes cannot expand through their toggle, initiate a drag, accept a drop, or reorder through Alt+Arrow keyboard actions.
97
+ - Build gates: `pnpm check` builds before typecheck/tests; MotionPreview uses the package self-reference with source types so a clean checkout does not require pre-existing dist for typecheck. Release staging verifies every exported JS/type/CSS file exists and is nonempty before writing the destination.
98
+ - The “SDK / 0.5.6 fixes” story covers the new and fixed compositions. `pnpm check:responsive` captures 36 images (dashboard/form/defects × 640/960/1280/1920 × layered/embedded/embedded-host) and records geometry in `measurements.json`. README includes a jsdom Select keyboard helper and migration examples.
99
+
100
+ ## 0.5.5 (2026-10-06)
101
+
102
+ Page layout that adapts to the window (DSM Control Panel / Package Center behaviour: the content area fills the window, cards add columns as it grows, form fields keep a fixed sensible width). Everything below is additive except the two defaults marked **default change**.
103
+
104
+ - `PageContent` (new): wraps `PageHeader`, `SectionTabs` and the page body so they share one left and right edge. `width="fill"` (default) uses the whole content area; `width="readable"` caps it at `--ui-page-readable-width` (880px); any CSS length is a custom cap. The header actions always end at the same edge as the content.
105
+ - `CardGrid` (new): `repeat(auto-fit, minmax(min(100%, minItemWidth), 1fr))`, equal-height cards. Props: `minItemWidth` (`280px`), `gap`, `fit` (`'stretch'` grows the cards to use the whole row; `'fill'` keeps empty tracks, so a lone card stays at column width).
106
+ - Form fields: `Form`, `FormItem` and `SettingsRow` take `fieldWidth` (`'xs'` 96px, `'sm'` 160px, `'md'` 320px, `'lg'` 480px, `'full'`, or a CSS length). **Default change:** controls stop at `'md'` instead of stretching to the container; pass `field-width="full"` for the old behaviour (a textarea, a table, a `RepeatRows` block).
107
+ - **Default change:** a horizontal `FormItem` has a 12px gap between its label column and the control.
108
+ - `Card` `footerAlign` (`'start'` default, `'end'`, `'between'`); the footer is a wrapping flex row. New `FormActions` (`align`, `labelWidth`) does the same for a button row outside a card.
109
+ - `RepeatRows` / `RepeatRow` / `FieldCell` (new): editable repeated rows. `FieldCell width` sizes each field to its content (`'xs'` for a port, `'sm'` for a protocol); cells wrap on narrow widths; `RepeatRow` ends in a delete button (`removeLabel`, emits `remove`); `RepeatRows` ends in the add button (`addLabel`, `addDisabled`, emits `add`). The component does not own the data.
110
+ - `EmptyState` already centres in its container, so inside `PageContent` it centres in the content area.
111
+ - `pnpm check:responsive` screenshots a dashboard and a settings form at 640/960/1280/1920px in the layered, embedded and embedded + host themes and asserts no horizontal overflow, header/content edge alignment, column counts, field caps and footer alignment. The compositions live in the "DSM 7 / Page layout" story.
112
+
113
+ ### Usage
114
+
115
+ ```vue
116
+ <!-- dashboard -->
117
+ <PageContent>
118
+ <PageHeader title="Overview"><template #actions><Button>Recheck</Button></template></PageHeader>
119
+ <CardGrid><Card v-for="p in providers" :key="p.name" :title="p.name">…<Button variant="primary">Set up</Button></Card></CardGrid>
120
+ </PageContent>
121
+
122
+ <!-- settings form -->
123
+ <PageContent width="readable">
124
+ <PageHeader title="Cloudflare" />
125
+ <SectionTabs v-model="tab" :items="tabs" />
126
+ <Card title="Connection">
127
+ <Form><FormItem label="Domain"><Input v-model="domain" /></FormItem></Form>
128
+ <Callout tone="warning">…</Callout><Checkbox v-model="agreed" label="I understand" />
129
+ <template #footer><FormActions><Button variant="primary" :disabled="!agreed">Apply</Button><Button variant="ghost">Cancel</Button></FormActions></template>
130
+ </Card>
131
+ </PageContent>
132
+ ```
133
+
134
+ ## 0.5.4 (2026-10-06)
135
+
136
+ - DSM-embedded fix: `host-reset.css` reverts bare elements to browser defaults, and the library relied on a Tailwind preflight that the embedded theme does not ship. `Card`, `EmptyState`, `PageHeader` and `WizardStep` headings now set `margin: 0` (a Card header was 64px tall because of the default `h3` margin), and the `SettingsSection`, `Card` and `ExpandableItem` toggle buttons reset their border, background and padding; the dashed header rules of `SettingsSection` and `Fieldset` draw only the bottom edge (a collapsible section showed a grey bordered button instead of the blue DSM title row). No API change.
137
+
138
+ ## 0.5.3 (2026-10-06)
139
+
140
+ - Match the supplied Control Panel reference: blue selected tab labels, bold 14px main labels / 13px section labels, and a raised notch in the divider instead of a blue underline. The marker keeps its 120ms movement and panels keep their 160ms fade.
141
+ - DataGrid footer now draws both horizontal borders, with a divider before the refresh control. The grid fills available space in a flex container so its footer can rest at the bottom while rows scroll independently; `flex-none` opts out.
142
+ - Hide the reorder handle and its extra column when row dragging is disabled or omitted. Enabled dragging and pivot-column handles remain supported.
143
+
144
+ ## 0.5.2 (2026-10-06)
145
+
146
+ - Tabs and SectionTabs share a sliding underline (120ms), measured again when labels or layout change. Tabs content fades in over 160ms without delaying selection or keyboard focus.
147
+ - SectionTabs optionally renders named `panel-${value}` slots with the same fade. Existing navigation-only usage is supported; caller attributes still reach the nav.
148
+ - Spinner uses a full rotating SVG ring with a smoothly growing/shrinking arc. Dash animation loops at one full circumference, removing the clipped half-ring and angle reset.
149
+ - These animations respect `prefers-reduced-motion` in standalone and DSM-embedded themes.
150
+
151
+ ## 0.4.2 (2026-10-05)
152
+
153
+ ### Switch (visual fix, backward compatible)
154
+
155
+ - The switch track is a `<button>`, and its browser padding (1px 6px) was never reset in the DSM-embedded theme: the knob started 6px from the track edge and the checked knob overflowed the right edge by 4px. The track now has `padding: 0`, so the knob rests 1px from the track edge in both states. No API change; consumers need no code change, only the bump.
156
+ - The switch toggles on click in the library (component tests plus a real-browser click in the standalone and embedded themes, with and without host CSS). `pnpm check:switch` covers the geometry and the click.
157
+
158
+ ## 0.4.1 (2026-10-03)
159
+
160
+ ### Controls (visual change, backward compatible)
161
+
162
+ - The theme gains one shared control chrome: `--ui-control-bg` / `-hover` / `-active`, `--ui-control-border` / `-active`, `--ui-control-divider` / `-on-accent`, `--ui-control-track`, `--ui-shadow-control-active`, `--ui-radius-control` (4px) and `--ui-motion-ease`. The resting surface, the hover border and the disabled label alpha follow the DSM 7 measurements; the resting frame, the hover and pressed tones, the divider, the press depth and the control radius are derived from them (DSM leaves the resting frame transparent and uses a 3px radius).
163
+ - `Button`, `IconButton` and `SplitButton` share those tokens: a resting 1px frame, a deeper hover surface and frame, a real pressed state (darker surface, stronger frame, 1px inset depth) and a focus state that sharpens the frame as well as showing the ring. Primary and danger keep their colour on press instead of showing a lighter halo, and their resting frame is one step darker than the fill.
164
+ - A disabled grey or ghost button now keeps its surface and dims only the label; a loading button keeps full strength and shows a visible ring, white on the accent fills (`--ui-spin-color`). `IconButton` gains the matching hover, pressed and focus states, and ghost/secondary dim the glyph instead of the whole box.
165
+ - `SplitButton` is one control again: both halves hover and press together, and a real 1px divider replaces the doubled border (it no longer turns grey on the blue variant).
166
+ - `Input`, `Textarea`, `Select`, `SearchField`, `Checkbox`, `RadioGroup` and `Switch` share the control radius, easing and focus-ring tokens; `--ui-focus-ring` is slightly stronger (0.30 → 0.35).
167
+
168
+ **Behaviour changes to check when upgrading:** controls read `--ui-radius-control`, so an override of `--ui-radius-sm` or `--ui-radius-md` no longer reshapes them. A disabled grey button no longer fades its surface, and a loading button is no longer dimmed.
169
+
170
+ ## 0.4.0 (2026-10-03)
171
+
172
+ - Bundle a hash-verified patch of Reka 2.10.5: final modal cleanup restores host body styles and priorities. Consumers need no Reka dependency or patch. Vue remains an external peer.
173
+ - Add opt-in `AppRoot modal-scope="app"` for local Dialog/Sheet and popup layer, focus, scroll and ARIA ownership. Default document modality is preserved.
174
+ - Forward cancellable Dialog/Sheet Escape, outside interaction and autofocus events.
175
+ - Reject nested app-local AppRoots and simultaneous app/document overlays. Add packed-consumer Chromium acceptance and dist runtime/license audits.
176
+
177
+ ## 0.3.1 (2026-10-02)
178
+
179
+ ### Cursor
180
+
181
+ - Every clickable control now shows the hand cursor: buttons (including icon buttons and both halves of a split button), links, tabs, section tabs, sidebar items, menu, dropdown and select items, checkbox / radio / switch controls and their labels, sortable grid headers, clickable rows, close buttons and upload drop zones. Disabled, read-only and busy controls keep the default cursor; text inputs keep the text cursor. The rules live in the theme (`theme.css` and every `themes/*` export).
182
+
183
+ ## 0.3.0 (2026-10-01)
184
+
185
+ ### Theme: native DSM 7 look (visual change, minor bump)
186
+
187
+ Tokens were re-measured against DSM 7.4 (`docs/dsm-native-metrics.md`). Apps that use the tokens get the new look without code changes; apps that hard-code sizes may need a pass.
188
+
189
+ - Font: `Verdana, Arial, …` (CJK fallbacks) at **13px** (was system sans 14px); text `#414b55`, secondary and muted text are 60% / 40% alpha of it.
190
+ - Accent `#057feb` (hover `#0075db`, active `#0062b8`), success `#009e05`, warning `#f58414`, danger `#e64040`.
191
+ - Selected / hover tints `rgb(5 127 235 / 10% | 4%)`; borders `#c6d4e0` at 70% / 40%.
192
+ - Radii 3px (navigation 5px); controls 24 / 28 / 34px (were 28 / 34 / 40); navigation row 42px; page padding 16px; sidebar 215px.
193
+ - Bare buttons in `AppSidebar`, `SectionTabs`, `SearchField` and ghost `IconButton` now set `border-0 bg-transparent` themselves (needed so the embedded and layered themes render alike).
194
+ - `Button` secondary is the flat grey DSM button; `AppSidebar`, `SectionTabs` (30px strip, bold 12px labels, blue underline), menus, `SearchField` follow DSM.
195
+ - New tokens: `--ui-font-size-lg`, `--ui-shadow-card`, `--ui-grid-header-height`, `--ui-grid-row-height`, `--ui-grid-footer-height`. Density modes now scale the grid row height too.
196
+
197
+ ### Loading
198
+
199
+ - `Spinner` is the DSM ring; `size` is now the box size (default 24, was an 18px icon).
200
+ - New `LoadingMask` (centred message over busy content), `LoadingArea` (centred in its container) and `LoadingIndicator`.
201
+ - `Button loading` uses the ring.
202
+
203
+ ### DataGrid
204
+
205
+ - Unbound `sort` now sorts internally (before, the grid did nothing unless bound); `default-sort` sets the start state; bound `sort` stays controlled.
206
+ - Keyboard sort (Enter/Space), `aria-sort`.
207
+ - Column resize with drag and arrow keys, `minWidth`, `v-model:column-widths`, `@column-resize`, `resizable`.
208
+ - Footer bar (`footer`, item count, `refreshable`/`refreshing`/`@refresh`), `select-on-row-click`, `max-height`, column dividers, `loading-text`.
209
+ - `labels` prop for every built-in string (unblocks localised pagination); `emptyText` still works.
210
+ - Types `DataGridColumnWidths`, `DataGridLabels`.
211
+
212
+ **Behaviour changes to check when upgrading:** `stickyHeader` now defaults to `true`; `loading` shows a mask instead of skeleton rows (`loadingRows` is ignored and deprecated); `width`/`minWidth` accept numbers; the footer counter text moved after the page controls; the grid has a trailing filler column once all widths are known.
213
+
214
+ ### Toolbar
215
+
216
+ - `AppSidebar`: idle items are transparent (only the selected item is filled); optional `searchable` field and `collapsible` section headers (both off by default).
217
+ - `SplitButton` caret is a solid triangle.
218
+ - New `SplitButton`; `SearchField` gets `variant="filter"`.
package/README.md CHANGED
@@ -361,6 +361,10 @@ DSM-style application building blocks:
361
361
  `Tabs` and `SectionTabs` move a raised selection notch in 120ms. Selected labels
362
362
  are blue; main labels are bold 14px and section labels bold 13px. `Tabs` fades its
363
363
  new panel in over 160ms, with immediate selection and keyboard response.
364
+ A strip narrower than its tabs keeps one row and scrolls sideways (trackpad,
365
+ touch, Shift+wheel or keyboard focus) with no visible scrollbar; it never
366
+ scrolls vertically. In a grid or flex parent, give the strip's track or item
367
+ `min-width: 0` (`minmax(0, 1fr)`) so it can be narrower than its tabs.
364
368
  `SectionTabs` can also render content using named `panel-${value}` slots:
365
369
 
366
370
  ```vue
@@ -470,23 +474,20 @@ pnpm add @synopackageland/ui
470
474
 
471
475
  ## Releases
472
476
 
473
- The public npm package is `@synopackageland/ui`. Both release targets are built
474
- from the same source and share runtime files, exports, MIT license and notices.
475
- Public repository and issue tracker links will be added once those are selected.
477
+ The package is published to npmjs as `@synopackageland/ui` only.
478
+ The source repository is https://github.com/synopackageland/syno-pkg-land-ui.
476
479
  See the [documentation](https://www.varvis.me/docs/) for usage.
477
480
 
478
481
  ### CI and tag releases
479
482
 
480
483
  GitHub Actions runs on Ubuntu for pushes to `master` and `ci/**`, pull requests,
481
484
  and manual dispatch. Both workflows share `.github/actions/verify`: a frozen
482
- pnpm install from npmjs, `pnpm check` (build, lint, typecheck, unit tests and both
483
- package-content audits), `notices:check`, responsive/host/switch/layout/motion
485
+ pnpm install from npmjs, `pnpm check` (build, lint, typecheck, unit tests and the
486
+ package-content audit), `notices:check`, responsive/host/switch/layout/motion
484
487
  browser checks, and the Histoire workbench build. pnpm and Playwright Chromium
485
488
  are cached. Browser screenshots and measurements are retained for seven days.
486
- The internal target audit inspects metadata offline; it never contacts an
487
- internal registry. The existing `pnpm release` internal workflow remains available.
488
489
 
489
- To release the public package:
490
+ To release the package:
490
491
 
491
492
  1. Update `package.json` to a new stable `X.Y.Z` version and add the matching
492
493
  `## X.Y.Z (date)` section to `CHANGELOG.md`. Keep `pnpm-lock.yaml` current.
@@ -502,9 +503,9 @@ To release the public package:
502
503
 
503
504
  The release workflow checks the exact tag/version, default-branch ancestry,
504
505
  nonempty changelog section and npm version availability. It runs the same full
505
- CI gates, stages a public package into an empty directory, packs it, and audits
506
+ CI gates, stages the package into an empty directory, packs it, and audits
506
507
  the actual tarball (every exported dist file, MIT license, notices, bundled Reka,
507
- public name and exact `publishConfig`). A separate job rechecks the artifact and
508
+ package name and no `publishConfig`). A separate job rechecks the artifact and
508
509
  registry before publishing with `NPM_TOKEN`, then creates a GitHub Release from
509
510
  that version's changelog using `GITHUB_TOKEN`. Publishing uses `--access public
510
511
  --ignore-scripts`, without provenance. Only the publish job has `contents: write`.
@@ -534,13 +535,12 @@ GitHub-hosted job; the host gate also exercises the default-modal isolation fixt
534
535
  Ubuntu CI 在 `master`、`ci/**` push、PR 與手動執行時,從 npmjs 安裝鎖定依賴,
535
536
  共用完整 build/lint/typecheck/test/package audit/notices 與
536
537
  responsive/host/switch/layout/motion、Histoire build 檢查。
537
- 快取 pnpm 與 Chromium;瀏覽器證據保留七天。內部目標只做離線 metadata audit,
538
- 不連內部 registry;既有 `pnpm release` 保留。
538
+ 快取 pnpm 與 Chromium;瀏覽器證據保留七天。
539
539
 
540
540
  發版時先更新 `package.json` 版號與 CHANGELOG 對應段落,通過 CI 後合併至
541
541
  `master`,再推送合併 commit 的 `vX.Y.Z` tag(指令如上)。Workflow 驗證 tag、
542
- 預設分支歷史與 npm 版本未存在,完成同一組檢查後 stage public、pack 並稽核
543
- 實際 tgz,確保 dist、所有 exports、MIT/第三方授權與公開 metadata 齊全。
542
+ 預設分支歷史與 npm 版本未存在,完成同一組檢查後 stage、pack 並稽核
543
+ 實際 tgz,確保 dist、所有 exports、MIT/第三方授權與套件 metadata 齊全。
544
544
  獨立 publish job 重新驗證,再以 `NPM_TOKEN` 發 npm,成功後以 changelog 建立
545
545
  GitHub Release;不使用 provenance。npm 已成功但 GitHub Release 失敗時,另行
546
546
  建立 Release;重跑會拒絕已存在的 npm 版本。
@@ -592,6 +592,12 @@ document, set a distinct `app.config.idPrefix` for each app.
592
592
  </ConfigProvider>
593
593
  ```
594
594
 
595
+ FilePicker sizes (`max {size}` and each file row) follow DSM: a 1024 base
596
+ with `B`, `KB`, `MB`, `GB`, `TB` labels and at most one decimal, so
597
+ `:max-size="16 * 1024 ** 3"` reads `max 16 GB`, as DSM File Station would show
598
+ it. The same rule is exported as `formatFileSize(bytes)`; use it, or the same
599
+ DSM labels, in application text that repeats a limit, rather than `GiB`/`MiB`.
600
+
595
601
  FilePicker retains browser `File[]` semantics. Built-in `locale` values are
596
602
  `en`, `zh-TW`, `zh-CN`, `ja`, `ko`, `de`, `fr`, `es`, `it`; an explicit picker
597
603
  locale overrides ConfigProvider. Its `text` accepts partial overrides of