@keyfunc/keybricks-ds 2.1.1 → 2.3.1

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.
Files changed (240) hide show
  1. package/DEPLOY.md +13 -4
  2. package/DESIGN.md +104 -19
  3. package/INTEGRATION.md +75 -20
  4. package/SKILL.md +11 -11
  5. package/_adherence.oxlintrc.json +28 -11
  6. package/_scripts/build-index.mjs +15 -4
  7. package/assets/icons/icon-index.js +1 -1
  8. package/assets/icons/icon-index.json +1 -1
  9. package/components/_cards/accordion.card.html +1 -1
  10. package/components/_cards/action-chip.card.html +11 -14
  11. package/components/_cards/activity.card.html +2 -2
  12. package/components/_cards/alert.card.html +2 -2
  13. package/components/_cards/app-chrome.card.html +1 -1
  14. package/components/_cards/banner.card.html +1 -1
  15. package/components/_cards/breadcrumb.card.html +4 -4
  16. package/components/_cards/button-1-base.card.html +23 -26
  17. package/components/_cards/button-2-states.card.html +14 -12
  18. package/components/_cards/button-3-sizes-icons.card.html +20 -19
  19. package/components/_cards/button-4-group.card.html +14 -31
  20. package/components/_cards/canvas-zoom-controls.card.html +1 -1
  21. package/components/_cards/cards-indicators.card.html +1 -1
  22. package/components/_cards/charts-1-sparkline.card.html +4 -4
  23. package/components/_cards/charts-10-gauge.card.html +2 -2
  24. package/components/_cards/charts-11-funnel.card.html +2 -2
  25. package/components/_cards/charts-12-treemap.card.html +2 -2
  26. package/components/_cards/charts-14-radar.card.html +3 -3
  27. package/components/_cards/charts-15-distribution.card.html +2 -2
  28. package/components/_cards/charts-16-pareto.card.html +2 -2
  29. package/components/_cards/charts-17-candlestick.card.html +2 -2
  30. package/components/_cards/charts-18-sankey.card.html +2 -2
  31. package/components/_cards/charts-19-chord.card.html +2 -2
  32. package/components/_cards/charts-2-line.card.html +3 -3
  33. package/components/_cards/charts-20-sunburst.card.html +2 -2
  34. package/components/_cards/charts-21-worldmap.card.html +3 -3
  35. package/components/_cards/charts-22-gantt.card.html +2 -2
  36. package/components/_cards/charts-23-bar3d.card.html +2 -2
  37. package/components/_cards/charts-24-pie3d.card.html +2 -2
  38. package/components/_cards/charts-25-surface3d.card.html +2 -2
  39. package/components/_cards/charts-26-scatter3d.card.html +2 -2
  40. package/components/_cards/charts-27-ribbon3d.card.html +2 -2
  41. package/components/_cards/charts-3-bar.card.html +6 -6
  42. package/components/_cards/charts-4-barlist.card.html +2 -2
  43. package/components/_cards/charts-5-donut.card.html +2 -2
  44. package/components/_cards/charts-6-scatter.card.html +2 -2
  45. package/components/_cards/charts-7-heatmap.card.html +2 -2
  46. package/components/_cards/charts-8-waterfall.card.html +2 -2
  47. package/components/_cards/charts-9-bullet.card.html +3 -3
  48. package/components/_cards/charts-99-fullpage.card.html +1 -1
  49. package/components/_cards/checkbox.card.html +6 -3
  50. package/components/_cards/codeblock.card.html +2 -2
  51. package/components/_cards/combobox.card.html +6 -4
  52. package/components/_cards/context-menu.card.html +6 -4
  53. package/components/_cards/date-picker.card.html +8 -5
  54. package/components/_cards/diffvalue.card.html +2 -2
  55. package/components/_cards/divider.card.html +2 -2
  56. package/components/_cards/drawer.card.html +3 -3
  57. package/components/_cards/dropdown-menu.card.html +2 -2
  58. package/components/_cards/dropzone.card.html +5 -3
  59. package/components/_cards/empty-state.card.html +2 -2
  60. package/components/_cards/filter-bar.card.html +1 -1
  61. package/components/_cards/icon-button.card.html +14 -14
  62. package/components/_cards/icons-1-core.card.html +4 -4
  63. package/components/_cards/icons-2-feather.card.html +4 -4
  64. package/components/_cards/icons-3-unicons.card.html +3 -3
  65. package/components/_cards/input.card.html +5 -5
  66. package/components/_cards/kbd.card.html +1 -1
  67. package/components/_cards/keyvalue.card.html +1 -1
  68. package/components/_cards/listrow.card.html +1 -1
  69. package/components/_cards/loading.card.html +2 -2
  70. package/components/_cards/log-viewer.card.html +2 -2
  71. package/components/_cards/modal.card.html +1 -1
  72. package/components/_cards/multi-select.card.html +6 -4
  73. package/components/_cards/number-stepper.card.html +9 -6
  74. package/components/_cards/pagination.card.html +6 -4
  75. package/components/_cards/people.card.html +2 -2
  76. package/components/_cards/pipeline-node.card.html +8 -8
  77. package/components/_cards/pipeline-palette.card.html +2 -2
  78. package/components/_cards/radio.card.html +12 -7
  79. package/components/_cards/search.card.html +6 -6
  80. package/components/_cards/segmented-control.card.html +11 -10
  81. package/components/_cards/select.card.html +7 -3
  82. package/components/_cards/slider.card.html +2 -2
  83. package/components/_cards/smart-table.card.html +3 -3
  84. package/components/_cards/spreadsheet.card.html +2 -2
  85. package/components/_cards/stepper.card.html +2 -2
  86. package/components/_cards/table.card.html +79 -17
  87. package/components/_cards/tabs.card.html +6 -4
  88. package/components/_cards/tag.card.html +6 -6
  89. package/components/_cards/textarea.card.html +1 -1
  90. package/components/_cards/timeline.card.html +3 -3
  91. package/components/_cards/toast.card.html +7 -5
  92. package/components/_cards/toggles.card.html +14 -9
  93. package/components/_cards/tooltip.card.html +3 -3
  94. package/components/_cards/tree.card.html +2 -2
  95. package/components/_shared/card-kit.css +25 -0
  96. package/components/_shared/card-kit.js +50 -0
  97. package/components/_shared/src-buttons.js +1 -1
  98. package/components/_shared/src-charts.js +1 -1
  99. package/components/_shared/src-data.js +1 -1
  100. package/components/_shared/src-display.js +1 -1
  101. package/components/_shared/src-feedback.js +1 -1
  102. package/components/_shared/src-forms.js +1 -1
  103. package/components/_shared/src-icons.js +1 -1
  104. package/components/_shared/src-navigation.js +1 -1
  105. package/components/_shared/src-tabs.js +1 -1
  106. package/components/_shared/view-source.js +3 -3
  107. package/components/_vue/index.mjs +7 -0
  108. package/components/buttons/ActionChip.d.ts +1 -1
  109. package/components/buttons/Button.d.ts +1 -1
  110. package/components/buttons/Button.jsx +1 -1
  111. package/components/buttons/Button.vue +1 -1
  112. package/components/buttons/IconButton.d.ts +1 -1
  113. package/components/buttons/IconButton.jsx +5 -3
  114. package/components/buttons/IconButton.vue +3 -2
  115. package/components/charts/Bar3D.jsx +1 -1
  116. package/components/charts/Bar3D.vue +2 -2
  117. package/components/charts/BarChart.jsx +3 -3
  118. package/components/charts/BarChart.vue +6 -6
  119. package/components/charts/BarList.jsx +3 -3
  120. package/components/charts/BarList.vue +3 -3
  121. package/components/charts/BoxPlot.vue +2 -2
  122. package/components/charts/BulletChart.jsx +5 -5
  123. package/components/charts/BulletChart.vue +5 -5
  124. package/components/charts/CandlestickChart.vue +3 -3
  125. package/components/charts/ChartKit.jsx +4 -3
  126. package/components/charts/ChartTooltip.vue +1 -1
  127. package/components/charts/ChordChart.jsx +1 -1
  128. package/components/charts/ChordChart.vue +2 -2
  129. package/components/charts/DonutChart.jsx +1 -1
  130. package/components/charts/DonutChart.vue +2 -2
  131. package/components/charts/FunnelChart.jsx +3 -3
  132. package/components/charts/FunnelChart.vue +4 -4
  133. package/components/charts/GanttChart.vue +1 -1
  134. package/components/charts/GaugeChart.jsx +2 -2
  135. package/components/charts/GaugeChart.vue +2 -2
  136. package/components/charts/HeatmapChart.jsx +1 -1
  137. package/components/charts/HeatmapChart.vue +1 -1
  138. package/components/charts/Histogram.jsx +2 -2
  139. package/components/charts/Histogram.vue +4 -4
  140. package/components/charts/LineChart.vue +2 -2
  141. package/components/charts/ParetoChart.jsx +1 -1
  142. package/components/charts/ParetoChart.vue +3 -3
  143. package/components/charts/Pie3D.jsx +1 -1
  144. package/components/charts/Pie3D.vue +2 -2
  145. package/components/charts/PlotGrid.vue +1 -1
  146. package/components/charts/RadarChart.vue +1 -1
  147. package/components/charts/Ribbon3D.jsx +1 -1
  148. package/components/charts/Ribbon3D.vue +2 -2
  149. package/components/charts/SankeyChart.jsx +1 -1
  150. package/components/charts/SankeyChart.vue +2 -2
  151. package/components/charts/Scatter3D.vue +1 -1
  152. package/components/charts/ScatterChart.jsx +1 -1
  153. package/components/charts/ScatterChart.vue +3 -3
  154. package/components/charts/Sparkline.jsx +1 -1
  155. package/components/charts/Sparkline.vue +1 -1
  156. package/components/charts/SunburstChart.vue +1 -1
  157. package/components/charts/Surface3D.jsx +1 -1
  158. package/components/charts/Surface3D.vue +2 -2
  159. package/components/charts/TreemapChart.jsx +1 -1
  160. package/components/charts/TreemapChart.vue +2 -2
  161. package/components/charts/WaterfallChart.jsx +1 -1
  162. package/components/charts/WaterfallChart.vue +5 -4
  163. package/components/charts/WorldMap.jsx +4 -4
  164. package/components/charts/WorldMap.vue +5 -5
  165. package/components/data/DiffValue.d.ts +1 -0
  166. package/components/data/DiffValue.jsx +1 -1
  167. package/components/data/DiffValue.vue +1 -1
  168. package/components/data/KeyValue.d.ts +1 -1
  169. package/components/data/KeyValue.jsx +2 -2
  170. package/components/data/KeyValue.vue +2 -2
  171. package/components/data/Pagination.jsx +2 -2
  172. package/components/data/Pagination.vue +1 -1
  173. package/components/data/SmartTable.jsx +5 -5
  174. package/components/data/SmartTable.vue +6 -6
  175. package/components/data/Spreadsheet.jsx +5 -5
  176. package/components/data/Spreadsheet.vue +6 -6
  177. package/components/data/Table.d.ts +58 -4
  178. package/components/data/Table.jsx +153 -15
  179. package/components/data/Table.vue +105 -15
  180. package/components/data/data.prompt.md +1 -1
  181. package/components/display/Avatar.jsx +2 -2
  182. package/components/display/Avatar.vue +1 -1
  183. package/components/display/AvatarGroup.vue +1 -1
  184. package/components/display/Badge.d.ts +4 -1
  185. package/components/display/Badge.jsx +10 -5
  186. package/components/display/IconTile.vue +9 -4
  187. package/components/display/Kbd.d.ts +1 -1
  188. package/components/display/Kbd.jsx +1 -1
  189. package/components/display/Kbd.vue +1 -1
  190. package/components/feedback/ActivityItem.jsx +1 -1
  191. package/components/feedback/Modal.jsx +1 -1
  192. package/components/feedback/Modal.vue +1 -1
  193. package/components/feedback/Timeline.vue +2 -2
  194. package/components/forms/Combobox.d.ts +1 -1
  195. package/components/forms/DatePicker.d.ts +1 -1
  196. package/components/forms/Input.d.ts +1 -1
  197. package/components/forms/Input.jsx +2 -2
  198. package/components/forms/Input.vue +1 -1
  199. package/components/forms/MultiSelect.d.ts +1 -1
  200. package/components/forms/NumberStepper.d.ts +1 -1
  201. package/components/forms/SearchInput.d.ts +1 -1
  202. package/components/forms/Select.d.ts +1 -1
  203. package/components/forms/Select.jsx +1 -1
  204. package/components/forms/Select.vue +1 -1
  205. package/components/forms/Slider.jsx +1 -1
  206. package/components/forms/Slider.vue +1 -1
  207. package/components/forms/Switch.jsx +3 -3
  208. package/components/forms/Switch.vue +11 -8
  209. package/components/forms/Textarea.vue +1 -1
  210. package/components/forms/input-base.js +1 -1
  211. package/components/icons/Icon.jsx +17 -1
  212. package/components/icons/Icon.prompt.md +2 -0
  213. package/components/icons/Icon.vue +1 -1
  214. package/components/icons/icon-paths.js +15 -0
  215. package/components/index.js +1 -1
  216. package/components/navigation/Breadcrumb.jsx +3 -7
  217. package/components/navigation/Breadcrumb.vue +2 -8
  218. package/components/navigation/Sidebar.jsx +3 -22
  219. package/components/navigation/Sidebar.vue +2 -7
  220. package/components/navigation/Tree.jsx +1 -1
  221. package/components/navigation/Tree.vue +9 -4
  222. package/components/navigation/nav-model.js +23 -0
  223. package/components/navigation/theme-store.js +9 -0
  224. package/components/tabs/PillTabs.vue +1 -1
  225. package/components/tabs/SegmentedControl.d.ts +1 -1
  226. package/components/tabs/SegmentedControl.jsx +1 -1
  227. package/components/tabs/SegmentedControl.vue +1 -1
  228. package/components/tabs/Tabs.jsx +1 -1
  229. package/components/tabs/ViewToggle.d.ts +1 -1
  230. package/package.json +2 -2
  231. package/readme.md +6 -5
  232. package/readme.zh-CN.md +6 -5
  233. package/tokens/alpha.css +3 -1
  234. package/tokens/colors.css +1 -1
  235. package/tokens/dark.css +1 -1
  236. package/tokens/layout.css +7 -5
  237. package/tokens/tokens.d.ts +22 -0
  238. package/tokens/tokens.js +39 -6
  239. package/tokens/tokens.json +90 -11
  240. package/tokens/typography.css +26 -9
package/DEPLOY.md CHANGED
@@ -55,11 +55,11 @@ deploys the documentation site. Watch it under *Build → Pipelines*.
55
55
  **5. Cut the first release.**
56
56
 
57
57
  ```bash
58
- git tag v2.1.1 && git push --tags
58
+ git tag v2.3.1 && git push --tags
59
59
  ```
60
60
 
61
61
  That tag runs `generated:check` → `version:check` → `npm publish`. Confirm under *Deploy →
62
- Package Registry*: `@keyfunc/keybricks-ds` 2.1.1.
62
+ Package Registry*: `@keyfunc/keybricks-ds` 2.3.1.
63
63
 
64
64
  **6. Hand developers two things:** the Pages URL, and the `.npmrc` snippet in §4 below.
65
65
 
@@ -96,7 +96,7 @@ Published (`files` in `package.json`): `components/`, `tokens/`, `assets/`, `_sc
96
96
  > that mode — Vue apps on a git dependency should deep-import
97
97
  > `@keyfunc/keybricks-ds/components/<group>/<Name>.vue`, which is the recommended path anyway.
98
98
 
99
- Not published: `guidelines/`, `ui_kits/`, `templates/`, `index.html`, `gallery.html`,
99
+ Not published: `guidelines/`, `index.html`, `gallery.html`,
100
100
  `_ds_bundle.js`. Those are the documentation site — they go to Pages, not into every
101
101
  `node_modules`.
102
102
 
@@ -156,7 +156,7 @@ GitLab injects `CI_JOB_TOKEN` — use that instead and no secret is stored anywh
156
156
  **Install — straight from git** (no registry, no publish; works today)
157
157
 
158
158
  ```json
159
- "@keyfunc/keybricks-ds": "git+ssh://git@gitlab.epm2demo.cn/Felix/keybricks-ds.git#v2.1.1"
159
+ "@keyfunc/keybricks-ds": "git+ssh://git@gitlab.epm2demo.cn/Felix/keybricks-ds.git#v2.3.1"
160
160
  ```
161
161
 
162
162
  Because the package has no build step, a git dependency needs no `prepare` script — npm
@@ -202,3 +202,12 @@ consuming app to fail CI on the two rules that break theming:
202
202
 
203
203
  Both are the same rule stated twice: components read the semantic layer, and only the
204
204
  semantic layer. That is what makes dark mode cost nothing.
205
+
206
+ ## Runtime checks
207
+
208
+ `_tests/` holds browser pages that exercise the real components: `react-runtime.html` (the compiled
209
+ bundle), `table-selection.html` (Table loaded from source: checkbox spacing at every width, code chips, two-line rows, sorting), `vue-runtime.html` (the `.vue` files, loaded with vue3-sfc-loader), `cards-interaction*.html`
210
+ (clicks through the documentation demos) and `icon-rules.html` (the Keybricks glyphs against the drawing rules). Serve the repo root (`npx serve .`), open a page and read the
211
+ console — every line starts with PASS or FAIL. Run them after changing a component in either framework.
212
+ `measure-cards.html` is a tool, not a test: it measures each documentation page at its card width so the
213
+ `viewport` sizes in the cards, `site-nav.js` and `gallery.html` can be kept in step.
package/DESIGN.md CHANGED
@@ -84,7 +84,7 @@ Never put `--success` (the solid) behind text — use the `-bg` / `-text` pair.
84
84
  Series walk `--series-1…10` in order; signed values are ALWAYS `--chart-pos` / `--chart-neg`
85
85
  (diverging bars, waterfall bridges, deltas, auto sparklines); totals and combo overlay lines are
86
86
  `--chart-total`; guides are dashed `--chart-guide`. Lines are straight or monotone-cubic — never
87
- a spline that overshoots a real value. Numbers are `--font-mono`, compact-formatted. Every chart
87
+ a spline that overshoots a real value. Numbers are `--font-sans` with tabular figures, compact-formatted. Every chart
88
88
  answers a hover at every size: a tile-tier chart drops its axes and legend, never its tooltip.
89
89
  Geography is data, not a drawing — country outlines come from Natural Earth via d3-geo.
90
90
  3D charts are presentation, not analysis: plain SVG painter’s-algorithm rendering, one upper-left
@@ -99,7 +99,7 @@ feedback colors mean **severity**. Do not mix the two vocabularies.
99
99
 
100
100
  ## Typography
101
101
 
102
- `--font-sans` is Inter. `--font-mono` is Menlo. There is no third family. Under
102
+ `--font-sans` is Inter. `--font-mono` is Menlo, and it is for code only: code blocks, logs, inline code in the docs, and config codes (`FIN_CURRENCY_DEFAULT`), which render as a code chip — Menlo at `--text-code` (12px; 11px in sm; Latin, so not bumped in CJK) on `--bg-inset` with a hairline border (`code: true` on a Table column). Everything else in the interface — IDs, amounts, chart numbers, keyboard keys — is `--font-sans`; numbers that must line up add `font-variant-numeric: tabular-nums`. There is no third family. Under
103
103
  `lang="zh" | "ja" | "ko"` the sans stack continues into the system CJK face (PingFang SC /
104
104
  Hiragino / Apple SD Gothic Neo on macOS, Microsoft YaHei / Yu Gothic UI / Malgun Gothic on
105
105
  Windows, Noto Sans CJK elsewhere); Latin glyphs stay Inter.
@@ -142,15 +142,98 @@ may be `--text-xs`. Never put a 14px label and a 12px control on the same baseli
142
142
 
143
143
  A header cell is the **same size as its data cells**, weight 600, `--text-secondary`, sentence
144
144
  case, no letter-spacing. The header is set apart by `--bg-table-header`, never by caps or a
145
- heavier rule. Mono columns (`mono: true`) keep the row's size in `--font-mono` with tabular
146
- figures — codes and amounts are never a point smaller than the words beside them.
145
+ heavier rule. Amount and ID columns (`mono: true`) keep the row's size, font and colour and add
146
+ only tabular figures; config-code columns (`code: true`) use the code chip — codes and amounts are never a point smaller than the words beside them.
147
+
148
+ ### Sortable headers
149
+
150
+ Mark a column `sortable: true` and its header gets a sort button: a muted sm `IconButton` at the
151
+ **right end of the header cell** — the same edge as a two-line row's `⋯`, so the two line up. The label
152
+ keeps its alignment (left, or right for number columns, where it sits 4px before the button).
153
+
154
+ - **Unsorted** — the button appears only while the header is hovered (or holds keyboard focus), icon
155
+ `arrow-up`. It keeps its space while hidden, so the label never shifts.
156
+ - **Click** cycles ascending (`arrow-up`) → descending (`arrow-down`) → unsorted. The tooltip names the
157
+ next step: *Sort ascending* · *Sort descending* · *Clear sorting*.
158
+ - **Sorted** — the button stays without hover and the label turns `--text-heading`; the header carries
159
+ `aria-sort`.
160
+ - **One column at a time.** Clicking another column's button starts it at ascending and clears the first.
161
+ - A long label truncates; the button never shrinks. With `resizable` the button sits inside the cell
162
+ padding, left of the divider, so the two never overlap.
163
+ - Action columns (row ⋯, checkboxes) are never `sortable`, so they show no button.
164
+
165
+ API: `sort` / `onSortChange` control it (`{ key, direction: "asc" | "desc" }` or `null`; Vue
166
+ `v-model:sort`, `@sort-change`); `defaultSort` seeds it uncontrolled. The table reorders rows itself —
167
+ numbers (commas allowed) numerically, text by locale with numeric collation, empty values last — unless
168
+ the column passes `compare(a, b, rowA, rowB)` or the table sets `manualSort` (rows arrive sorted from
169
+ the server). Selection indices and `onRowMore` always refer to the original `rows` order.
170
+
171
+ ### Two-line name rows
172
+
173
+ The name column of a document list (folders, models, sheets) is a **two-line title cell**:
174
+ `columns: [{ key: "name", label: "Name", width: 300, twoLine: true }, …]`. Left to right:
175
+
176
+ 1. an `IconTile kind="row"` tinted by the row's `hue` (`source` · `join` · `transform` · `merge` ·
177
+ `output`; `neutral` is the grey tile for folders and containers);
178
+ 2. two lines of text — the **title** in the tier's control text, `--text-heading`, regular weight, and the
179
+ **description** in `--text-xs`, `--text-secondary`. Each is one line and truncates with an ellipsis. Give the column a `width`; in a tight table it
180
+ shrinks to `minWidth` (default 180px) before the other columns give way;
181
+ 3. an sm `⋯` `IconButton` on the right (`onRowMore(row, index, event)`; Vue `@row-more`; `more: false` hides it).
182
+
183
+ An optional 14px **mark** sits right after the title: `mark: "favorite"` is a filled star in `--warning`;
184
+ `{ icon, color, label }` draws any 14px outline icon. Fields default to the row keys `icon`, `hue`,
185
+ `description`, `mark`; pass `twoLine: { icon, hue, description, mark }` with field names or
186
+ `(row, index) => value` accessors to read them from elsewhere.
187
+
188
+ The header stays the tier's bar height; only body rows grow. Every other cell stays vertically centred
189
+ and unchanged. All measures are `--title-cell-*` tier tokens:
190
+
191
+ | | sm | md | lg |
192
+ | --- | --- | --- | --- |
193
+ | Icon tile (`--title-cell-tile`, `-tile-r`) | 28 · r8 | **32 · r8** | 40 · r10 |
194
+ | Icon in the tile (`--title-cell-icon`) | 14 | 16 | 20 |
195
+ | Tile → text (`--title-cell-tile-gap`) | 8 | 12 | 12 |
196
+ | Title | 12 / 16 (13 / 18 CJK) | 14 / 20 | 14 / 20 |
197
+ | Description | 12 / 16 (13 / 18 CJK) | 12 / 16 (13 / 18 CJK) | 12 / 16 (13 / 18 CJK) |
198
+ | Title → description (`--title-cell-gap`) | 2 | 2 | 4 |
199
+ | Row padding top and bottom (`--title-cell-py`) | 8 | 10 | 14 |
200
+ | **Row height** (Latin / CJK) | **50 / 54** | **58 / 60** | **68 / 70** |
201
+ | Header | 36 | 40 | 48 |
202
+ | `⋯` button | sm 28 | sm 28 | sm 28 |
203
+
204
+ **Opening a row.** Pass `onRowOpen(row, index, event)` (Vue `@row-open`) and the icon tile and the title open
205
+ the row: both get the hand cursor, and the title is a keyboard target (Enter / Space). The ⋯ button and the
206
+ expand arrow never open the row. Without the callback nothing in the cell looks clickable.
207
+
208
+ **Tree rows.** Set `tree` on the table and nest rows through `children` — or set `hasChildren: true` on a
209
+ row whose children you load when it opens. The two-line column then indents **24px per level** and reserves a
210
+ **20px arrow slot + 4px** before the tile, so a child's arrow sits exactly under its parent's tile and leaf
211
+ rows line up with folders. The arrow is a muted `chevron-right` (tier icon size) that turns down when
212
+ open, with the `--overlay-hover` wash on hover; rows carry `aria-level` and `aria-expanded`. Expansion
213
+ is `expandedKeys` / `onExpandChange(row, expanded, keys)` (Vue `v-model:expandedKeys`, `@expand-change`),
214
+ or `defaultExpandedKeys` uncontrolled; `rowKey` names the key field (default `id`). Sorting reorders
215
+ siblings only — children stay under their parent. Every index the table reports (selection, `onRowMore`,
216
+ `onRowOpen`) is the row's depth-first position over all rows, expanded or not, so it never moves when the
217
+ user sorts or expands; for a flat table that is simply its position in `rows`.
218
+
219
+ **Labels.** The table draws six words itself — the sort tooltips, ⋯, and the arrow's expand / collapse.
220
+ `labels: { sortAsc, sortDesc, sortClear, more, expand, collapse }` translates them; missing keys fall
221
+ back to English. Pass them in the product's language — a Chinese table with English tooltips is a bug.
222
+
223
+ In sm the title and description are the same size, so the description is told apart by colour alone —
224
+ use sm two-line rows only where the list is dense and short labels are expected.
147
225
 
148
226
  ### Chinese, Japanese and Korean
149
227
 
150
228
  Han, kana and Hangul have no capitals and no italics, so emphasis is weight. Under
151
- `:lang(zh|ja|ko)` the tokens change for you: `--weight-medium` becomes **600** (Microsoft
152
- YaHei, Meiryo, Yu Gothic UI and Malgun Gothic ship no 500 face, so 500 silently renders as
153
- Regular on Windows) and every `--tracking-*` is **0**. CJK text is never smaller than the Latin
229
+ `:lang(zh|ja|ko)` the tokens change for you: `--weight-medium` and `--weight-semibold` both become
230
+ **500** — square glyphs carry more strokes, so PingFang 600 next to Inter 500 reads a full step bolder —
231
+ and every `--tracking-*` is **0**. (Microsoft YaHei, Meiryo, Yu Gothic UI and Malgun Gothic ship no 500
232
+ face, so on Windows these render Regular; size, colour and the header fill keep the hierarchy.) Below 14px CJK is one step larger than
233
+ Latin — `--text-xs` 13px (leading 18) and `--text-2xs` 12px — so no square glyph renders under
234
+ 12px; chart numerals read `--text-2xs-mono` and stay 11px. Text sized from geometry (switch inside text, avatar initials) is floored at `--text-glyph-min`: 11px, 12px in CJK. Inside an sm region `--weight-medium`
235
+ is 400 for CJK, because bold strokes merge at 13px. Simplified and Traditional Chinese, Japanese
236
+ and Korean get the same rule. CJK text is never smaller than the Latin
154
237
  text beside it, never letter-spaced and never transformed; a CJK table header or section label
155
238
  is emphasised by weight alone. Set `lang` on `<html>`, or on the region when one panel switches
156
239
  language — every component re-reads `--font-sans`, so a per-region switch works.
@@ -175,14 +258,14 @@ so two controls that share a row can never disagree. `md` is the default on `:ro
175
258
 
176
259
  | Token | sm | md | lg | Used by |
177
260
  | --- | --- | --- | --- | --- |
178
- | `--control-h` | 24 | **32** | 36 | buttons, inputs, selects, search, steppers, segmented controls, chips, pagination items, menu rows |
179
- | `--control-text` | 12 | **14** | 14 | text in controls and their labels, table cells |
261
+ | `--control-h` | 28 | **32** | 40 | buttons, inputs, selects, search, steppers, segmented controls, chips, pagination items, menu rows |
262
+ | `--control-text` | 12 (13 CJK) | **14** | 14 | text in controls and their labels, table cells |
180
263
  | `--control-icon` | 14 | 16 | 16 | icon inside a control |
181
264
  | `--control-px` | 8 | 12 | 16 | horizontal padding of a text control or button |
182
265
  | `--control-gap` | 8 | 8 | 8 | inside one group: label → its control, button → button |
183
266
  | `--group-gap` | 12 | 16 | 24 | between groups in one bar: field → next field |
184
- | `--bar-h` | 32 | 40 | 44 | toolbar, filter bar, tab bar, table row, list row |
185
- | `--bar-card-h` | 40 | 48 | 52 | a bar inside a bordered card (table toolbar, pagination) |
267
+ | `--bar-h` | 36 | 40 | 48 | toolbar, filter bar, tab bar, table row, list row |
268
+ | `--bar-card-h` | 44 | 48 | 56 | a bar inside a bordered card (table toolbar, pagination) |
186
269
  | `--edge-pad` | 12 | 16 | 16 | card edge → first control or cell |
187
270
  | `--cell-px` | 8 | 12 | 16 | horizontal padding of a table cell |
188
271
  | `--check-size` | 14 | 16 | 16 | checkbox and radio box |
@@ -198,18 +281,18 @@ Where each tier belongs:
198
281
 
199
282
  Rules:
200
283
 
201
- - **One tier per row.** Never a 24px icon button beside a 32px input.
284
+ - **One tier per row.** Never a 28px icon button beside a 32px input.
202
285
  - **Nothing interactive is smaller than 24px.** The former `xs` sizes and `dense` props map to `sm`.
203
286
  - A per-control `size` prop only stamps `data-size` on that control. Use it for a control that
204
287
  stands alone in its band — never to "fix" one control inside a bar.
205
288
  - Text inside a control is `--control-text`, not `--text-xs` because it looks tidier.
206
- - `--h-button` and `--h-input` are aliases of `--control-h`; `--h-button-sm` is 24px. Prefer the
289
+ - `--h-button` and `--h-input` are aliases of `--control-h`; `--h-button-sm` is 28px. Prefer the
207
290
  tier tokens in new code.
208
291
 
209
292
  ### Button emphasis — pick the variant, never the size
210
293
 
211
294
  The tier says how dense a region is; the **variant** says how important an action is. Never
212
- shrink a button to make it feel lighter: a 24px button beside 32px controls and 14px text reads
295
+ shrink a button to make it feel lighter: a 28px button beside 32px controls and 14px text reads
213
296
  as a mistake on a calm page, and the tier rule above forbids it anyway. Reach for a quieter
214
297
  variant instead.
215
298
 
@@ -267,7 +350,9 @@ Nothing bounces. Nothing slides more than a few pixels. Keyframes available:
267
350
 
268
351
  - **Hover** — every pointer target answers it, one step at a time:
269
352
  filled and boxed buttons, menu rows, list rows, nav and tree items, segments → one surface step
270
- (`--bg-hover`, `--action-primary-hover`, `--soft-hover`); field boxes (inputs, selects, date,
353
+ (`--bg-hover`, `--action-primary-hover`, `--soft-hover`); a borderless `IconButton` → the alpha
354
+ overlay `--overlay-hover` (press `--overlay-press`), because it sits on white rows, grey table headers
355
+ and grey bars alike and a solid `--bg-hover` disappears on `--bg-table-header`; field boxes (inputs, selects, date,
271
356
  combobox, multi-select, search, stepper) → border `--border-control` to `--border-strong`;
272
357
  text-only controls (inactive tabs, legend items, clear ×, copy) → ink to `--text-heading`;
273
358
  table body rows → `--bg-inset`; checkbox and radio rings → `--text-muted`; switch tracks →
@@ -320,7 +405,7 @@ The list that prevents the most rework. These are absolute.
320
405
  - **Do not mix size tiers in one row.** The region declares `data-size`; the controls inherit. A
321
406
  `size` prop on one control inside a bar is a bug, not a fix.
322
407
  - **Do not shrink, letter-space or uppercase Chinese, Japanese or Korean text.** Under `lang`
323
- the emphasis weight is 600 and tracking is 0 — that is the whole CJK rule.
408
+ the emphasis and heading weight is 500 (medium 400 at sm), tracking is 0, and sizes under 14px step up by one — that is the whole CJK rule.
324
409
  - **Do not put dark text on a saturated fill.** `--on-accent` (white) is the only option,
325
410
  in both themes — this is why the dark-mode primary button keeps white text.
326
411
  - **Do not scale or shift elements on press.** Press is a color step only.
@@ -328,14 +413,14 @@ The list that prevents the most rework. These are absolute.
328
413
  container, no insert rules on a node that can't take children.
329
414
  - **Do not build a new primitive when one exists.** Check the component index in
330
415
  `INTEGRATION.md` first — there are 117.
331
- - **Do not use emoji as icons.** Use `<Icon name="..." />` (362 glyphs) or `<Unicon>` (1,206).
416
+ - **Do not use emoji as icons.** Use `<Icon name="..." />` (330 glyphs) or `<Unicon>` (1,206).
332
417
  - **Do not invent new component variants.** If `Button` lacks the variant you want, that is a
333
418
  design-system request, not a local override.
334
419
  - **Do not stack two primary buttons.** One primary action per view; everything else is
335
420
  `outline`, `ghost`, or `text`.
336
421
  - **Do not use full-uppercase text.** Table headers and section labels are sentence case, weight
337
- 600, tracking 0 — the same size as the text they label. (The 10px eyebrows in this site's own
338
- card chrome are documentation furniture, not a component pattern.)
422
+ 600, tracking 0 — the same size as the text they label. (This site's own card labels follow the
423
+ same rule: 12px, sentence case.)
339
424
  - **Do not animate anything longer than 360ms** or with a bounce/elastic easing.
340
425
 
341
426
  ---
package/INTEGRATION.md CHANGED
@@ -31,7 +31,7 @@ Not using a registry yet? A git dependency works with no publishing at all — t
31
31
  has no build step, so npm can use the checkout as-is:
32
32
 
33
33
  ```json
34
- "@keyfunc/keybricks-ds": "git+ssh://git@gitlab.epm2demo.cn/Felix/keybricks-ds.git#v2.1.1"
34
+ "@keyfunc/keybricks-ds": "git+ssh://git@gitlab.epm2demo.cn/Felix/keybricks-ds.git#v2.3.1"
35
35
  ```
36
36
 
37
37
  **Source-only package.** `.jsx` and `.vue` files are published as authored, so your
@@ -98,7 +98,7 @@ import Textarea from '@keyfunc/keybricks-ds/components/forms/Textarea.vue'
98
98
  ```
99
99
 
100
100
  That is the whole integration. The `data-size="lg"` on the form is the size tier — every control
101
- inside it is 36px tall; leave it off and they are the app default, 32px (see *Size tiers* below).
101
+ inside it is 40px tall; leave it off and they are the app default, 32px (see *Size tiers* below).
102
102
  Everything below is reference material.
103
103
 
104
104
  ---
@@ -136,9 +136,9 @@ properties — `--control-h`, `--control-text`, `--control-icon`, `--control-px`
136
136
  A **region** sets them with one attribute:
137
137
 
138
138
  ```html
139
- <div data-size="sm">…</div> <!-- 24px controls · 12px text · 32px rows — dense grids, pipeline nodes -->
139
+ <div data-size="sm">…</div> <!-- 28px controls · 12px text (13px CJK) · 36px rows — dense grids, pipeline nodes -->
140
140
  <div data-size="md">…</div> <!-- 32px controls · 14px text · 40px rows — the app default (:root) -->
141
- <div data-size="lg">…</div> <!-- 36px controls · 14px text · 44px rows — dialogs, short forms -->
141
+ <div data-size="lg">…</div> <!-- 40px controls · 14px text · 48px rows — dialogs, short forms -->
142
142
  ```
143
143
 
144
144
  Put `data-size` on the toolbar, filter bar, form, table or panel and leave the controls' `size`
@@ -180,8 +180,11 @@ components (`Input.jsx` exports `Input` and `Textarea`; `Badge.jsx` exports `Bad
180
180
  cannot export values, so on the Vue side every such secondary is its own file with a default
181
181
  export — `Textarea.vue`, `IconTile.vue`, `StatusBadge.vue`, `Tooltip.vue`, `ToastStack.vue`, `Port.vue`, `PortLabel.vue`, `ChartLegend.vue`, `ChartTooltip.vue`, `PlotGrid.vue`, `XLabels.vue`, `ButtonGroup.vue`, `StatCard.vue`, `Toast.vue`,
182
182
  `Spinner.vue`, `PageHeading.vue`, and so on — always named exactly as the React export.
183
- Shared non-component values live in plain modules: `forms/input-base.js` (`inputBase`),
184
- `pipeline/pipeline-catalog.js` (`PIPELINE_CATALOG`), `pipeline/port-skin.js` (`portSkin`), `px.js` (`px`), `icons/icon-paths.js`.
183
+ Shared non-component values live in plain modules: `navigation/nav-model.js` (`KEYBRICKS_NAV`),
184
+ `navigation/theme-store.js` (`applyStoredTheme`), `pipeline/pipeline-catalog.js` (`PIPELINE_CATALOG`),
185
+ `icons/icon-paths.js`, `icons/feather-paths.js` — all five are also exported from
186
+ `@keyfunc/keybricks-ds/vue` — plus the internal helpers `forms/input-base.js` (`inputBase`),
187
+ `pipeline/port-skin.js` (`portSkin`) and `px.js` (`px`).
185
188
 
186
189
  ```js
187
190
  // React
@@ -287,7 +290,7 @@ import Textarea from '@keyfunc/keybricks-ds/components/forms/Textarea.vue'
287
290
  ### `icons/` — iconography
288
291
  | Component | What it's for |
289
292
  | --- | --- |
290
- | `Icon` | 362 stroke glyphs (the product's own set plus all of Feather). `<Icon name="download" size={16} />` |
293
+ | `Icon` | 330 stroke glyphs: the 83-glyph core set plus all of Feather (on a shared name the core glyph wins). `<Icon name="download" size={16} />` |
291
294
  | `Unicon` | 1,206 additional Unicons line glyphs, loaded from CDN. Use when `Icon` has no match. |
292
295
 
293
296
  ### `pipeline/` — the data-pipeline editor
@@ -487,7 +490,7 @@ a migration note in the release. Token renames are major because your own code r
487
490
  Pin a minor range and update deliberately:
488
491
 
489
492
  ```json
490
- "@keyfunc/keybricks-ds": "~2.1.1"
493
+ "@keyfunc/keybricks-ds": "~2.3.1"
491
494
  ```
492
495
 
493
496
  ### Migrating from 1.x to 2.0
@@ -513,6 +516,70 @@ Pin a minor range and update deliberately:
513
516
  - **Tokens outside CSS.** `tokens.json` / `tokens.js` / `tokens.d.ts` gained the tier tokens
514
517
  (`--control-*`, `--bar-h`, `--group-gap`, …) — 352 light tokens in all (2.0.2 adds `--danger-soft-hover`).
515
518
 
519
+ ### 2.3.1
520
+
521
+ - **`Table` row open, tree rows and labels.** `onRowOpen(row, index, event)` (Vue `@row-open`) makes the icon tile and title of a two-line cell open the row (hand cursor; Enter / Space on the title). `tree` with nested `children` (or `hasChildren` for lazy rows) indents the two-line column 24px per level with an expand arrow — `expandedKeys` / `defaultExpandedKeys` / `onExpandChange(row, expanded, keys)` (Vue `v-model:expandedKeys`, `@expand-change`), `rowKey` (default `id`); sorting reorders siblings only. `labels: { sortAsc, sortDesc, sortClear, more, expand, collapse }` translates every word the table draws (English by default). Indices reported by selection and callbacks are depth-first over all rows — unchanged for flat tables.
522
+ - Additive only: flat tables, existing props and indices behave exactly as in 2.3.0.
523
+
524
+ ### 2.3.0
525
+
526
+ - **sm and lg size tiers.** sm controls are **28px** (was 24) with 36px rows and bars (was 32);
527
+ lg controls are **40px** (was 36) with 48px rows (was 44); md is unchanged. 24px sat at the
528
+ floor of the industry range and left the segmented control's segments 18px tall; 36 was only
529
+ 4px above md, where Ant Design, Fluent 2, Primer and shadcn all step to 40. sm text stays 12px
530
+ for Latin. Layouts built on the tier tokens follow automatically; anything you sized by hand to
531
+ sit beside an sm or lg control should be re-checked.
532
+ - **CJK small sizes are one step larger.** Under `lang="zh" | "ja" | "ko"` (Simplified and
533
+ Traditional Chinese alike) `--text-xs` / `--text-sm` are 13px with 18px leading and
534
+ `--text-2xs` is 12px, so no CJK text renders below 12px. 14px and up are unchanged. In sm
535
+ regions the CJK medium weight is 400. Outside sm, CJK `--weight-medium` and `--weight-semibold` are both 500 (they were 600): at the same number, CJK glyphs read heavier than Latin, so 600 looked a step bolder than English and erased the difference between labels and headers. On Windows (no 500 face in YaHei / Meiryo / Malgun) both render Regular. Chart numerals use the new `--text-2xs-mono`
536
+ (11px in every language).
537
+ - `Table` selection: the checkbox now sits in the first column's leading space instead of a column of its own. Both sides of the checkbox now equal the edge padding (16px; 12px in sm), so the box sits evenly between the card edge and the first column's text and stays aligned with card titles and toolbars. Before, the gap was 40px and grew with the table width when columns declared widths (up to ~120px). If you styled the old selection cell (`td:first-child` holding only the checkbox), move that styling to the first data cell.
538
+ - **`Table` two-line name rows**: a `twoLine` column renders an `IconTile` (kind "row", tinted by `hue`; `neutral` = grey), a title over a description (each one line, truncated), an optional 14px mark (`"favorite"` = filled star in `--warning`) and a trailing sm `⋯` (`onRowMore(row, index, event)`, Vue `@row-more`). md rows are 58px (sm 50 · lg 68; CJK 54 · 60 · 70); the header keeps the bar height. New tier tokens `--title-cell-py`, `--title-cell-gap`, `--title-cell-tile`, `--title-cell-icon`, `--title-cell-tile-r`, `--title-cell-tile-gap`. `IconTile kind="row"` is now tinted when you pass `hue` (unchanged without it), adds `hue="neutral"` and an `iconStyle` prop.
539
+ - **`Table` sortable headers**: `sortable: true` on a column adds a muted sm sort button at the header cell's right end (in line with a two-line row's `⋯`) — on hover while unsorted (`arrow-up`), kept while sorted (`arrow-up` / `arrow-down`, label `--text-heading`, `aria-sort`). Click cycles ascending → descending → none; one column at a time; tooltips *Sort ascending* / *Sort descending* / *Clear sorting*. New props `sort`, `defaultSort`, `onSortChange` (Vue `v-model:sort`, `@sort-change`), `manualSort`; column `compare`. Selection indices keep referring to the original `rows` order.
540
+ - `IconButton` hover and press are now alpha overlays (`--overlay-hover`, `--overlay-press`) instead of the solid `--bg-hover` / `--bg-press`. On white they are the same colour as before; on a grey surface (table header, toolbar) the hover is now visible instead of matching the background. `--overlay-press` is 12% (was 8%, unused) and the dark theme gets its own overlay values (8% / 16%).
541
+ - **Fix — `Table` `code` columns:** only text and number values get the code chip; an element in the cell (a `Skeleton` while loading, a link) renders as is. Before, a loading row in a code column drew a chip around the skeleton and grew to about 50px.
542
+ - **Two AI glyphs join the core icon set**: `ai` (rounded frame, open at the top-right, with a large and a small four-point star) and `ai-sparkles` (a large and a small four-point star). Both are traced from the reference glyphs and redrawn to the core-set rules: stroke 2 with no fill, integer endpoints, at least 2px clear between separate elements. The core set is 83 glyphs; `Icon` resolves 330 names. Search terms: ai, assistant, copilot, agent, intelligent, generative, sparkles, magic.
543
+ - `Pagination`: page items and prev/next lose their border (GitHub Primer / Atlassian style). The current page takes the neutral selected fill (`--bg-active`, weight 600) instead of the action blue — it is a state, not an action; the others are plain until hovered. Size is unchanged — items stay control-height (32px at md).
544
+ - The monospace font is now for code only (`CodeBlock`, `LogViewer`, code in the docs). `Table`/`SmartTable` `mono` columns, `KeyValue` and `DiffValue` `mono`, `Spreadsheet` numbers and formula bar, `Kbd`, the `Input`/`Textarea` counter, the `Slider` value and every chart number now use `--font-sans` with tabular figures; `mono` columns also take the normal text colour. The `mono` props and the `--text-2xs-mono` token keep their names. Config codes get a new column option, `code: true`: a code chip — Menlo at the new `--text-code` / `--leading-code` (12/18px, 11/16px in sm, not bumped in CJK) on `--bg-inset` with a hairline border. Switch config-code columns from `mono` to `code`.
545
+ - `Table` column separators (the resize handles in the header) are one step darker: new token `--border-split` — `#D4D4D4` light, `#404040` dark (was `--border-control`, #E5E5E5 / #333333). Hover and drag still turn them blue.
546
+ - New tokens: `--text-2xs-mono` and `--text-glyph-min` (11px, 12px in CJK) — the floor for text that is sized from geometry: `Switch` inside/knob text and `Avatar` initials no longer drop to 9–10px. `--h-button-sm` is 28px.
547
+ - Removed the `ui_kits/keybricks-app` product recreation and the `templates/web-app` starter: the
548
+ production designs now live in their own projects. No component or token is affected.
549
+ - **Fix — React entry:** `applyStoredTheme` was documented but missing from
550
+ `@keyfunc/keybricks-ds` (the barrel generator skipped lower-case names). It is exported now.
551
+ - **Fix — Vue entry:** `KEYBRICKS_NAV` and `applyStoredTheme` existed for Vue only as comments.
552
+ They now live in `navigation/nav-model.js` and `navigation/theme-store.js`, which React re-exports
553
+ and `@keyfunc/keybricks-ds/vue` exports directly, along with `PIPELINE_CATALOG`, `VUE_ICON_PATHS`
554
+ and `FEATHER_PATHS` — one source for both frameworks.
555
+ - **Fixes — Vue matched to React** (found by a token-by-token and prop-by-prop audit of every
556
+ React/Vue pair): `Tree` keyboard focus ring uses `--focus-border` (was `--focus-ring`) and now
557
+ accepts `treeData`, `expandedKeys`, `selectedKeys`, `checkedKeys`; `Switch` accepts
558
+ `inlinePrompt`; `Timeline` title and meta carry their line-height and tracking (CJK meta now
559
+ gets its 18px leading); `Spreadsheet` column headers carry `--tracking-xs`; `SmartTable` URL
560
+ cells use `--link`; `FunnelChart` bands animate width; `WaterfallChart` shows the hover band.
561
+ `BulletChart` range bands use `--neutral-alpha-12` in both frameworks (theme-aware; was a
562
+ hard-coded black in Vue and an undefined token in React).
563
+ - Sidebar nav model (`KEYBRICKS_NAV`): Reconciliation uses the `reconciliation` icon and
564
+ Consolidation the `consolidation` icon; the Management › Reconciliation entry no longer reuses
565
+ `network`.
566
+ - Component cards share one specimen layout (label column, 12px sentence-case labels) and every
567
+ size-tier demo shows a Latin row and a CJK row.
568
+
569
+ ### 2.2.0
570
+
571
+ - **Eleven Keybricks glyphs join the core icon set** — `<Icon name="journal" />` in React and
572
+ Vue alike. Modules: `reconciliation`, `consolidation`, `journal`, `event`, `view-tree`.
573
+ Actions: `drilldown`, `add-below`, `copy-data`, `task-execution`, `time-future`,
574
+ `time-restatement`. Each was checked against the core-set drawing rules, now written down in
575
+ the Iconography section of `readme.md`; six were redrawn to meet them (paths inside 2–22,
576
+ integer coordinates, 2px between separate elements, rx 1 on small rects, true circular
577
+ corners). The core set is now 81 glyphs; with Feather, `Icon` resolves 328 names.
578
+ - Two submitted glyphs were already in the library and were not duplicated: *hierarchy* is
579
+ `layers` and *change* is Feather's `repeat`. Both words now find them in the icon search.
580
+ - The docs gave the icon total as 362. That counted the 40 names Feather shares with the core
581
+ set twice; the number of distinct names is now 328.
582
+
516
583
  ### 2.1.1
517
584
 
518
585
  - **Chart series palette replaced** (`--series-1…8`, light and dark). One lightness band, a chroma
@@ -570,18 +637,6 @@ Pin a minor range and update deliberately:
570
637
 
571
638
  Release mechanics, the CI pipeline and the Pages site are documented in `DEPLOY.md`.
572
639
 
573
- ## Templates
574
-
575
- A template is a ready-made screen you copy into a new project as a starting point, rather than a
576
- component you import. Each lives in `templates/<slug>/`.
577
-
578
- | Template | What you get |
579
- | --- | --- |
580
- | `templates/web-app/` | The full app shell: sidebar with the real nav model, top bar, page heading, stat tiles and a data table. |
581
-
582
- Copy the folder, then edit one line — the `base` in `ds-base.js` — to point at wherever the design
583
- system sits relative to the copied page.
584
-
585
640
  ## Getting help
586
641
 
587
642
  - **Documentation site** — the GitLab Pages build: `index.html` for the browsable system
package/SKILL.md CHANGED
@@ -4,30 +4,30 @@ description: Use this skill to generate well-branded interfaces and assets for K
4
4
  user-invocable: true
5
5
  ---
6
6
 
7
- Read the README.md file within this skill, and explore the other available files. This skill's documentation is bilingual (English / 中文)。
7
+ Read the readme.md file within this skill, and explore the other available files. This skill's documentation is bilingual (English / 中文)。
8
8
 
9
9
  Key facts / 关键信息: Keybricks is a near-monochrome, hairline-bordered, 14px-Inter enterprise UI with one action blue (#155dfc, dark mode #1f6feb), five node-category hues, and a signature node-pipeline editor.
10
10
  (Keybricks 是一个近乎单色、细边框、以 14px Inter 为主的企业级界面,仅有一个强调蓝(浅色 #155dfc / 深色 #1f6feb)、五种节点分类色,以及标志性的数据管道编辑器。)
11
- Tokens live in `tokens/*.css` (linked via `styles.css`, includes `tokens/dark.css` for dark mode and `tokens/motion.css` for motion); React components in `components/<group>/` (each with a `.d.ts` contract and `.prompt.md` usage note); full-screen recreations in `ui_kits/keybricks-app/`.
12
- (设计令牌位于 `tokens/*.css`(经 `styles.css` 汇总,含深色模式 `tokens/dark.css` 与动效 `tokens/motion.css`);React 组件位于 `components/<分组>/`(各附 `.d.ts` 类型定义与 `.prompt.md` 用法说明);完整界面复刻位于 `ui_kits/keybricks-app/`。)
11
+ Tokens live in `tokens/*.css` (linked via `styles.css`, includes `tokens/dark.css` for dark mode and `tokens/motion.css` for motion); React and Vue 3 components in `components/<group>/` (each React file with a `.d.ts` contract; each group with a `.prompt.md` usage note).
12
+ (设计令牌位于 `tokens/*.css`(经 `styles.css` 汇总,含深色模式 `tokens/dark.css` 与动效 `tokens/motion.css`);React 与 Vue 3 组件位于 `components/<分组>/`(React 文件附 `.d.ts` 类型定义,每个分组附 `.prompt.md` 用法说明)。)
13
13
 
14
14
  If creating visual artifacts (slides, mocks, throwaway prototypes, etc), copy assets out and create static HTML files for the user to view. If working on production code, you can copy assets and read the rules here to become an expert in designing with this brand.
15
15
 
16
16
  If the user invokes this skill without any other guidance, ask them what they want to build or design, ask some questions, and act as an expert designer who outputs HTML artifacts _or_ production code, depending on the need.
17
17
 
18
18
  Rules of thumb / 要点速览:
19
- - Inter 14px/400 is the default; 500 for nav/headings/labels; 600 for node & panel titles and table headers; 300 only for big numerals. Menlo for codes, data cells, logs — at the row's own size. In Chinese/Japanese/Korean, 500 becomes 600 and tracking is 0.
20
- (Inter 14px/常规 为默认;500 用于导航/标题/标签;600 用于节点与面板标题及表头;300 仅用于大号数字。代码/数据/日志用 Menlo,与所在行同字号。中/日/韩文中 500 改为 600,字距为 0。)
21
- - One size tier per region: `data-size="sm" | "md" | "lg"` = 24 / 32 / 36px controls. Set it on the bar, form, table or dialog; controls inherit. Never mix heights or text sizes in one row; nothing interactive under 24px.
22
- (每个区域只用一档尺寸:`data-size="sm" | "md" | "lg"` 对应 24 / 32 / 36px 控件。设在工具栏、表单、表格或对话框上,控件自动继承。同一行不混用高度或字号;可交互元素不小于 24px。)
19
+ - Inter 14px/400 is the default; 500 for nav/headings/labels; 600 for node & panel titles and table headers; 300 only for big numerals. Menlo only for code — CodeBlock, LogViewer and config-code chips; amounts and IDs stay in Inter with tabular figures. In Chinese/Japanese/Korean, 500 and 600 both become 500, tracking is 0, and text below 14px is one step larger (12→13, 11→12).
20
+ (Inter 14px/常规 为默认;500 用于导航/标题/标签;600 用于节点与面板标题及表头;300 仅用于大号数字。Menlo 只用于代码——代码块、日志和配置代码标签;金额与编号仍用 Inter 等宽数字。中/日/韩文中 500 与 600 都改为 500(同字重下方块字更显粗),字距为 0,14px 以下的字号比英文大一档(12→13、11→12)。)
21
+ - One size tier per region: `data-size="sm" | "md" | "lg"` = 28 / 32 / 40px controls (rows and bars 36 / 40 / 48). Set it on the bar, form, table or dialog; controls inherit. Never mix heights or text sizes in one row; nothing interactive under 24px.
22
+ (每个区域只用一档尺寸:`data-size="sm" | "md" | "lg"` 对应 28 / 32 / 40px 控件(行与工具栏 36 / 40 / 48)。设在工具栏、表单、表格或对话框上,控件自动继承。同一行不混用高度或字号;可交互元素不小于 24px。)
23
23
  - Backgrounds: app #fafafa, surfaces white, hairline borders rgba(229,229,229,.6); shadows only on canvas nodes.
24
24
  (背景:应用层 #fafafa,卡片层白色,细边框;仅画布节点有阴影。)
25
25
  - One blue CTA per view; ghost text buttons beside it. Color only for meaning (category hues, status).
26
26
  (每个界面只有一个蓝色主按钮,旁边配文字按钮;颜色只用于表达含义。)
27
- - Icons: Lucide stroke set via the Icon component — never emoji, never hand-drawn SVG.
28
- (图标统一用 Icon 组件封装的 Lucide 线性图标集——禁止 emoji,禁止手绘 SVG。)
29
- - Radii: 4 controls · 8 buttons/inputs · 10 cards · 14/16 tiles & nodes · full circles.
30
- (圆角:4px 控件、8px 按钮/输入框、10px 卡片、14/16px 图标块与节点、端口/胶囊全圆角。)
27
+ - Icons: the Icon component — Lucide stroke glyphs plus the Keybricks glyphs drawn to the same rules (`ai`, `ai-sparkles`, module icons) — never emoji, never ad-hoc SVG.
28
+ (图标统一用 Icon 组件:Lucide 线性图标,加上按同一规范绘制的 Keybricks 图标(`ai`、`ai-sparkles`、模块图标)——禁止 emoji,禁止临时手绘 SVG。)
29
+ - Radii: 4 checkboxes · 8 buttons/inputs/row icon tiles · 10 cards · 14/16 tiles & nodes · full circles.
30
+ (圆角:4px 复选框、8px 按钮/输入框/行内图标块、10px 卡片、14/16px 图标块与节点、端口/胶囊全圆角。)
31
31
  - Copy: Title Case labels, imperative verb-first buttons ("Run Test", "Mark as New"), terse technical vocabulary.
32
32
  (文案:标签用词首大写,按钮用祈使动词开头,术语简洁精确。)
33
33
  - Dark mode: set `<html data-theme="dark">`. Style only via semantic tokens (`--bg-surface`, `--text-body`, `--border-card`, `--track`, `--soft-bg`, `--info-bg`, `--on-accent`, `--ring-surface`) — never raw `--neutral-*`/`--blue-*` — and both themes work automatically.
@@ -30,8 +30,7 @@
30
30
  "components/pipeline/**",
31
31
  "components/tabs/**",
32
32
  "site-nav.js",
33
- "tokens/**",
34
- "ui_kits/keybricks-app/**"
33
+ "tokens/**"
35
34
  ],
36
35
  "message": "Import design-system components from 'index.js', not component internals."
37
36
  }
@@ -509,12 +508,8 @@
509
508
  "message": "<Switch> tone must be one of 'primary' | 'semantic'."
510
509
  },
511
510
  {
512
- "selector": "JSXOpeningElement[name.name='DataTableColumn'] > JSXAttribute > JSXIdentifier[name!=/^(?:key|label|width|minWidth|mono|align|render|key|ref|className|style|children)$/]",
513
- "message": "<DataTableColumn> doesn't accept that prop. Declared props: key, label, width, minWidth, mono, align, render."
514
- },
515
- {
516
- "selector": "JSXOpeningElement[name.name='DataTableColumn'] > JSXAttribute[name.name='align'] > Literal[value!=/^(?:left|right|center)$/]",
517
- "message": "<DataTableColumn> align must be one of 'left' | 'right' | 'center'."
511
+ "selector": "JSXOpeningElement[name.name='DataTableTwoLine'] > JSXAttribute > JSXIdentifier[name!=/^(?:icon|hue|description|mark|more|key|ref|className|style|children)$/]",
512
+ "message": "<DataTableTwoLine> doesn't accept that prop. Declared props: icon, hue, description, mark, more."
518
513
  },
519
514
  {
520
515
  "selector": "JSXOpeningElement[name.name='Tabs'] > JSXAttribute > JSXIdentifier[name!=/^(?:tabs|active|onChange|size|style|key|ref|className|style|children)$/]",
@@ -828,7 +823,7 @@
828
823
  "Switch": {
829
824
  "replaces": []
830
825
  },
831
- "DataTableColumn": {
826
+ "DataTableTwoLine": {
832
827
  "replaces": []
833
828
  },
834
829
  "Tabs": {
@@ -908,6 +903,7 @@
908
903
  "--blue-950",
909
904
  "--border-card",
910
905
  "--border-control",
906
+ "--border-split",
911
907
  "--border-strong",
912
908
  "--border-subtle",
913
909
  "--brand-alpha-12",
@@ -1033,6 +1029,7 @@
1033
1029
  "--leading-3xl",
1034
1030
  "--leading-4xl",
1035
1031
  "--leading-base",
1032
+ "--leading-code",
1036
1033
  "--leading-display",
1037
1034
  "--leading-lg",
1038
1035
  "--leading-md",
@@ -1172,11 +1169,14 @@
1172
1169
  "--teal-950",
1173
1170
  "--text-2xl",
1174
1171
  "--text-2xs",
1172
+ "--text-2xs-mono",
1175
1173
  "--text-3xl",
1176
1174
  "--text-4xl",
1177
1175
  "--text-base",
1178
1176
  "--text-body",
1177
+ "--text-code",
1179
1178
  "--text-display",
1179
+ "--text-glyph-min",
1180
1180
  "--text-heading",
1181
1181
  "--text-inverse",
1182
1182
  "--text-lg",
@@ -1189,6 +1189,12 @@
1189
1189
  "--text-strong",
1190
1190
  "--text-xl",
1191
1191
  "--text-xs",
1192
+ "--title-cell-gap",
1193
+ "--title-cell-icon",
1194
+ "--title-cell-py",
1195
+ "--title-cell-tile",
1196
+ "--title-cell-tile-gap",
1197
+ "--title-cell-tile-r",
1192
1198
  "--tooltip-bg",
1193
1199
  "--tooltip-fg",
1194
1200
  "--topbar-h",
@@ -1281,6 +1287,7 @@
1281
1287
  "--action-dark-fg": "color",
1282
1288
  "--border-card": "color",
1283
1289
  "--border-control": "color",
1290
+ "--border-split": "color",
1284
1291
  "--border-subtle": "color",
1285
1292
  "--border-strong": "color",
1286
1293
  "--action-primary": "color",
@@ -1448,6 +1455,10 @@
1448
1455
  "--text-2xs": "font",
1449
1456
  "--leading-2xs": "font",
1450
1457
  "--tracking-2xs": "font",
1458
+ "--text-2xs-mono": "font",
1459
+ "--text-code": "font",
1460
+ "--leading-code": "font",
1461
+ "--text-glyph-min": "font",
1451
1462
  "--text-xs": "font",
1452
1463
  "--leading-xs": "font",
1453
1464
  "--tracking-xs": "font",
@@ -1485,6 +1496,8 @@
1485
1496
  "--weight-regular": "font",
1486
1497
  "--weight-medium": "font",
1487
1498
  "--weight-semibold": "font",
1499
+ "--control-text": "font",
1500
+ "--control-leading": "font",
1488
1501
  "--radius-sm": "radius",
1489
1502
  "--radius-md": "radius",
1490
1503
  "--radius-lg": "radius",
@@ -1506,8 +1519,6 @@
1506
1519
  "--h-subnav-item": "spacing",
1507
1520
  "--h-tree-item": "spacing",
1508
1521
  "--control-h": "spacing",
1509
- "--control-text": "font",
1510
- "--control-leading": "font",
1511
1522
  "--control-tracking": "font",
1512
1523
  "--control-icon": "spacing",
1513
1524
  "--control-px": "spacing",
@@ -1521,6 +1532,12 @@
1521
1532
  "--tag-h": "spacing",
1522
1533
  "--switch-w": "spacing",
1523
1534
  "--switch-h": "spacing",
1535
+ "--title-cell-py": "spacing",
1536
+ "--title-cell-gap": "spacing",
1537
+ "--title-cell-tile": "spacing",
1538
+ "--title-cell-icon": "spacing",
1539
+ "--title-cell-tile-r": "spacing",
1540
+ "--title-cell-tile-gap": "spacing",
1524
1541
  "--h-button": "color",
1525
1542
  "--h-input": "color",
1526
1543
  "--h-button-sm": "spacing",
@@ -19,16 +19,22 @@ const dirs = readdirSync(componentsDir)
19
19
  .filter(d => !d.startsWith('_') && !d.startsWith('.') && statSync(join(componentsDir, d)).isDirectory())
20
20
  .sort();
21
21
 
22
- /** Named exports declared in a .jsx source file. */
22
+ /* Lower-case names that are public API (everything else starting lower-case is an internal helper). */
23
+ const PUBLIC_LOWER = new Set(['applyStoredTheme']);
24
+ const isPublic = (n) => /^[A-Z][A-Za-z0-9_]*$/.test(n) || PUBLIC_LOWER.has(n);
25
+ /* Framework-neutral modules the Vue entry re-exports (React gets the same values through the .jsx files). */
26
+ const VUE_MODULES = ['navigation/nav-model.js', 'navigation/theme-store.js', 'pipeline/pipeline-catalog.js', 'icons/icon-paths.js', 'icons/feather-paths.js'];
27
+
28
+ /** Named public exports declared in a source file. */
23
29
  function exportsOf(src) {
24
30
  const names = new Set();
25
- for (const re of [/export\s+function\s+([A-Z][A-Za-z0-9_]*)/g, /export\s+const\s+([A-Z][A-Za-z0-9_]*)/g]) {
26
- let m; while ((m = re.exec(src))) names.add(m[1]);
31
+ for (const re of [/export\s+function\s+([A-Za-z_][A-Za-z0-9_]*)/g, /export\s+const\s+([A-Za-z_][A-Za-z0-9_]*)/g]) {
32
+ let m; while ((m = re.exec(src))) if (isPublic(m[1])) names.add(m[1]);
27
33
  }
28
34
  for (const m of src.matchAll(/export\s*\{([^}]*)\}/g)) {
29
35
  for (const part of m[1].split(',')) {
30
36
  const name = part.trim().split(/\s+as\s+/).pop().trim();
31
- if (/^[A-Z][A-Za-z0-9_]*$/.test(name)) names.add(name);
37
+ if (isPublic(name)) names.add(name);
32
38
  }
33
39
  }
34
40
  return [...names].sort();
@@ -70,6 +76,11 @@ for (const dir of dirs) {
70
76
  }
71
77
  }
72
78
 
79
+ vue += '\n/* shared values */\n' + VUE_MODULES.map(p => {
80
+ const names = exportsOf(readFileSync(join(componentsDir, p), 'utf8'));
81
+ return names.length ? `export { ${names.join(', ')} } from "../${p}";` : '';
82
+ }).filter(Boolean).join('\n') + '\n';
83
+
73
84
  const targets = [['index.js', react], ['index.d.ts', types]];
74
85
  if (withVue) targets.push(['_vue/index.mjs', vue]);
75
86
  const stale = [];