@stcn52/pro 0.0.0-stage → 0.2.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 (163) hide show
  1. package/CHANGELOG.md +621 -0
  2. package/CONVENTIONS.md +152 -0
  3. package/LICENSE +21 -0
  4. package/README.md +226 -2
  5. package/RELEASING.md +51 -0
  6. package/THIRD_PARTY_NOTICES.md +52 -0
  7. package/dist/index.cjs +23272 -0
  8. package/dist/index.cjs.map +1 -0
  9. package/dist/index.d.cts +5068 -0
  10. package/dist/index.d.ts +5068 -0
  11. package/dist/index.js +23060 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/styles.css +10485 -0
  14. package/dist/tokens.css +322 -0
  15. package/docs/antd-alignment-plan.md +983 -0
  16. package/docs/api.md +3669 -0
  17. package/docs/components.md +1706 -0
  18. package/docs/measurements.md +152 -0
  19. package/docs/qa/README.md +50 -0
  20. package/docs/qa/browser-checks.json +394 -0
  21. package/docs/qa/d-chrome-checklist-dark.png +0 -0
  22. package/docs/qa/d-chrome-checks.json +234 -0
  23. package/docs/qa/d-chrome-detail-dark.png +0 -0
  24. package/docs/qa/d-chrome-filters-dark.png +0 -0
  25. package/docs/qa/d-chrome-shell-dark.png +0 -0
  26. package/docs/qa/d-detail-checks.json +222 -0
  27. package/docs/qa/d-detail-comments-failed-dark.png +0 -0
  28. package/docs/qa/d-detail-preview-dark.png +0 -0
  29. package/docs/qa/d-entity-checks.json +294 -0
  30. package/docs/qa/d-entity-failed-dark.png +0 -0
  31. package/docs/qa/d-workbench-canvas-dark.png +0 -0
  32. package/docs/qa/d-workbench-checks.json +338 -0
  33. package/docs/qa/d-workbench-edit-failed-dark.png +0 -0
  34. package/docs/qa/d-workbench-roadmap-dark.png +0 -0
  35. package/docs/qa/d-workbench-wizard-failed-dark.png +0 -0
  36. package/docs/qa/date-range-mobile-dark.jpg +0 -0
  37. package/docs/qa/p0-cascader-checks.json +228 -0
  38. package/docs/qa/p0-cascader-multiple-dark.png +0 -0
  39. package/docs/qa/p0-config-checks.json +430 -0
  40. package/docs/qa/p0-config-nested-dark.png +0 -0
  41. package/docs/qa/p0-date-checks.json +595 -0
  42. package/docs/qa/p0-date-time-dark.png +0 -0
  43. package/docs/qa/p0-form-lifecycle-checks.json +30 -0
  44. package/docs/qa/p0-form-lifecycle.png +0 -0
  45. package/docs/qa/p0-select-checks.json +378 -0
  46. package/docs/qa/p0-select-virtual-dark.png +0 -0
  47. package/docs/qa/p0-table-checks.json +290 -0
  48. package/docs/qa/p0-table-virtual-dark.png +0 -0
  49. package/docs/qa/p0-transfer-checks.json +322 -0
  50. package/docs/qa/p0-transfer-pagination-dark.png +0 -0
  51. package/docs/qa/p0-tree-checks.json +242 -0
  52. package/docs/qa/p0-tree-range-dark.png +0 -0
  53. package/docs/qa/p1-app-browser-checks.json +34 -0
  54. package/docs/qa/p1-app-feedback-dark.png +0 -0
  55. package/docs/qa/p1-avatar-checks.json +370 -0
  56. package/docs/qa/p1-avatar-dark.png +0 -0
  57. package/docs/qa/p1-browser-checks.json +218 -0
  58. package/docs/qa/p1-card-checks.json +262 -0
  59. package/docs/qa/p1-card-dark.png +0 -0
  60. package/docs/qa/p1-content-navigation-browser-checks.json +114 -0
  61. package/docs/qa/p1-controls-browser-checks.json +86 -0
  62. package/docs/qa/p1-controls-desktop-dark.png +0 -0
  63. package/docs/qa/p1-dialog-nested-dark.png +0 -0
  64. package/docs/qa/p1-display-browser-checks.json +114 -0
  65. package/docs/qa/p1-display-desktop-dark.png +0 -0
  66. package/docs/qa/p1-divider-dark.png +0 -0
  67. package/docs/qa/p1-drawer-browser-checks.json +302 -0
  68. package/docs/qa/p1-drawer-rtl-dark.png +0 -0
  69. package/docs/qa/p1-general-input-browser-checks.json +114 -0
  70. package/docs/qa/p1-general-input-desktop-dark.png +0 -0
  71. package/docs/qa/p1-image-browser-checks.json +38 -0
  72. package/docs/qa/p1-image-preview-dark.png +0 -0
  73. package/docs/qa/p1-inline-browser-checks.json +58 -0
  74. package/docs/qa/p1-inline-desktop-dark.png +0 -0
  75. package/docs/qa/p1-layout-browser-checks.json +114 -0
  76. package/docs/qa/p1-layout-desktop-dark.png +0 -0
  77. package/docs/qa/p1-menu-checks.json +357 -0
  78. package/docs/qa/p1-menu-long-dark.png +0 -0
  79. package/docs/qa/p1-nav-dark.png +0 -0
  80. package/docs/qa/p1-nav-sidebar-checks.json +218 -0
  81. package/docs/qa/p1-navigation-browser-checks.json +114 -0
  82. package/docs/qa/p1-navigation-mobile-dark.png +0 -0
  83. package/docs/qa/p1-pagination-checks.json +309 -0
  84. package/docs/qa/p1-pagination-dark.png +0 -0
  85. package/docs/qa/p1-pagination-mobile-dark.png +0 -0
  86. package/docs/qa/p1-popup-actions-browser-checks.json +58 -0
  87. package/docs/qa/p1-popup-actions-desktop-dark.png +0 -0
  88. package/docs/qa/p1-progress-browser-checks.json +30 -0
  89. package/docs/qa/p1-progress-desktop-dark.png +0 -0
  90. package/docs/qa/p1-rate-half-desktop-dark.png +0 -0
  91. package/docs/qa/p1-segment-checks.json +426 -0
  92. package/docs/qa/p1-segment-dark.png +0 -0
  93. package/docs/qa/p1-sidebar-dark.png +0 -0
  94. package/docs/qa/p1-slider-rate-browser-checks.json +114 -0
  95. package/docs/qa/p1-states-browser-checks.json +34 -0
  96. package/docs/qa/p1-states-checks.json +342 -0
  97. package/docs/qa/p1-states-dark.png +0 -0
  98. package/docs/qa/p1-states-mobile-dark.png +0 -0
  99. package/docs/qa/p1-tabs-editable-dark.png +0 -0
  100. package/docs/qa/p1-tag-dark.png +0 -0
  101. package/docs/qa/p1-tag-divider-checks.json +330 -0
  102. package/docs/qa/p1-upload-browser-checks.json +30 -0
  103. package/docs/qa/p1-upload-list-dark.png +0 -0
  104. package/docs/qa/p2-ai-panel-checks.json +401 -0
  105. package/docs/qa/p2-ai-panel-dark.png +0 -0
  106. package/docs/qa/p2-alert-dark.png +0 -0
  107. package/docs/qa/p2-app-final-checks.json +309 -0
  108. package/docs/qa/p2-app-final-dark.png +0 -0
  109. package/docs/qa/p2-badge-alert-checks.json +466 -0
  110. package/docs/qa/p2-beam-motion-checks.json +58 -0
  111. package/docs/qa/p2-calendar-dark.png +0 -0
  112. package/docs/qa/p2-calendar-timeline-checks.json +254 -0
  113. package/docs/qa/p2-charts-checks.json +242 -0
  114. package/docs/qa/p2-charts-dark.png +0 -0
  115. package/docs/qa/p2-color-gradient-dark.png +0 -0
  116. package/docs/qa/p2-content-navigation-checks.json +794 -0
  117. package/docs/qa/p2-dialog-popover-dark.png +0 -0
  118. package/docs/qa/p2-display-checks.json +128 -0
  119. package/docs/qa/p2-display-dark.png +0 -0
  120. package/docs/qa/p2-drawer-final-dark.png +0 -0
  121. package/docs/qa/p2-extras-checks.json +482 -0
  122. package/docs/qa/p2-general-inputs-checks.json +154 -0
  123. package/docs/qa/p2-general-inputs-dark.png +0 -0
  124. package/docs/qa/p2-icon-checks.json +466 -0
  125. package/docs/qa/p2-icon-dark.png +0 -0
  126. package/docs/qa/p2-image-dark.png +0 -0
  127. package/docs/qa/p2-inline-editors-checks.json +199 -0
  128. package/docs/qa/p2-input-button-checks.json +778 -0
  129. package/docs/qa/p2-input-dark.png +0 -0
  130. package/docs/qa/p2-layout-final-checks.json +136 -0
  131. package/docs/qa/p2-layout-final-dark.png +0 -0
  132. package/docs/qa/p2-lists-checks.json +290 -0
  133. package/docs/qa/p2-masonry-dark.png +0 -0
  134. package/docs/qa/p2-media-checks.json +212 -0
  135. package/docs/qa/p2-mentions-color-checks.json +362 -0
  136. package/docs/qa/p2-overlay-checks.json +370 -0
  137. package/docs/qa/p2-people-picker-dark.png +0 -0
  138. package/docs/qa/p2-popconfirm-final-dark.png +0 -0
  139. package/docs/qa/p2-popup-final-checks.json +223 -0
  140. package/docs/qa/p2-qr-browser-decode.json +30 -0
  141. package/docs/qa/p2-quick-select-dark.png +0 -0
  142. package/docs/qa/p2-quick-text-dark.png +0 -0
  143. package/docs/qa/p2-rate-dark.png +0 -0
  144. package/docs/qa/p2-scroll-navigation-checks.json +198 -0
  145. package/docs/qa/p2-scroll-navigation-dark.png +0 -0
  146. package/docs/qa/p2-selection-bar-checks.json +302 -0
  147. package/docs/qa/p2-selection-bar-dark.png +0 -0
  148. package/docs/qa/p2-slider-rate-checks.json +262 -0
  149. package/docs/qa/p2-switch-dark.png +0 -0
  150. package/docs/qa/p2-tabs-dark.png +0 -0
  151. package/docs/qa/p2-timeline-dark.png +0 -0
  152. package/docs/qa/p2-toast-dark.png +0 -0
  153. package/docs/qa/p2-toggle-checks.json +834 -0
  154. package/docs/qa/p2-tour-dark.png +0 -0
  155. package/docs/qa/p2-upload-dark.png +0 -0
  156. package/docs/qa/table-details.md +26 -0
  157. package/docs/qa/table-mobile-dark.jpg +0 -0
  158. package/docs/qa/transfer-mobile-light.jpg +0 -0
  159. package/docs/qa/tree-browser-checks.json +226 -0
  160. package/docs/qa/tree-details.md +25 -0
  161. package/docs/qa/tree-directory-mobile-dark.jpg +0 -0
  162. package/docs/tokens.md +255 -0
  163. package/package.json +95 -4
package/CHANGELOG.md ADDED
@@ -0,0 +1,621 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@stcn52/pro` are documented in this file.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
6
+ the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.2.1] — 2026-10-09
11
+
12
+ - Table: preserve selections added during asynchronous bulk operations, align sorting entry points, avoid repeated full-row scans, correct pinned action widths and tree loading geometry, and support wrapping cells and accessible inline actions.
13
+ - Workbench: export the full selected set or reject unavailable records, support local column unfreezing, reset empty-board filters, and show accurate totals for unpaginated grouped lists and boards.
14
+ - Async boundaries: isolate attachment sessions by record, stop disabled forms after validation, accumulate batched uncontrolled state updates, and reject obsolete entity select values.
15
+
16
+ - Clear all 13 lint warnings: publish controlled props before layout effects, reset closed overlays without effect cascades, isolate ref-backed event handlers and render composer tools as components; enforce hook checks and a zero-warning verification gate.
17
+
18
+ - Ship editors: validate owned drag sessions, quarter/year boundaries and wizard drafts; wait for real workbench imports/deletes/edits and preserve new selection, parse quoted multiline CSV safely, guard queued callbacks after unmount, and contain mobile canvas cards.
19
+
20
+ - Ship entities: await real entity/picker/share submissions, retain failed drafts and isolate reopened sessions; deduplicate relation counts without dropping off-page IDs, respect provider disabled, validate scheduled recipients, and copy only host-supplied links after confirmed success.
21
+
22
+ - Ship detail: asynchronous comment submission with draft-preserving retry, cyclic thread safety, provider-aware relationship controls, attachment reader/timer cleanup and latest callbacks, explicit preview download stand-ins and CSV formula protection.
23
+
24
+ - Ship chrome: provider-aware rail/header/tools, per-rail disabled overrides, safe empty filters and operator resets, localized business controls, F2/IME title editing, finite badges, controlled inline editing focus and responsive reading surfaces.
25
+
26
+ - App feedback: inherit nested placement/caps, isolate closed message handles from reused keys, and settle all close subscribers while preserving reentrant notices and timers when host callbacks fail.
27
+
28
+ - Popup actions: drop blocked uncontrolled menus/confirmations, preserve controlled ownership and protect modified/IME open keys; localize confirmation labels/errors, retain zero content and normalize nonfinite drawer dimensions.
29
+
30
+ - Layout: localized sider/splitter controls and explicit sider disabled overrides; finite numeric dimensions/gaps/flex, responsive collapse deduplication, pointer capture completion and stale-drag/IME protection.
31
+
32
+ - Display: locale and explicit-null breadcrumb separators, fractional-only statistics; Typography protects IME commits and external focus, cancels stale async copy attempts before clipboard writes and localizes controls.
33
+
34
+ - Upload/Image: localize controls, preserve falsy rejection errors and latest async transfer configuration, avoid duplicate stale removals; drop blocked uncontrolled previews, normalize rotations and retain zero placeholders.
35
+
36
+ - General inputs: finite/IME-safe numeric stepping, duplicate suggestion/tree/time change suppression, inherited-disabled tree search overrides; localize numeric/tree controls and clock validity, discard blocked uncontrolled popups.
37
+
38
+ - Overlays: Escape dismisses one leaf layer, modal Tab includes related portals, nested popovers rise above their anchors; tooltips merge descriptions and fit/re-measure viewport edges, toasts pause for keyboard focus and normalize invalid durations.
39
+
40
+ - Badge/Alert: numeric-string caps and zero rules, finite overflow/offset handling, localized status/close labels and reentrant-safe afterClose tracking.
41
+
42
+ - Tabs/Collapse/Steps: recover owned focus after dynamic removal, hiding or disabling; preserve external focus during tab removal, localize editable labels and protect modified/IME navigation keys.
43
+
44
+ - Input/Buttons: IME-safe Enter and clear behavior, localized clear/password controls, existing-id Field labels and merged accessible feedback; zero-content actions and split-menu disabled overrides.
45
+
46
+ - Radio/Checkbox/Switch: native group container refs/attributes, zero-valued checkbox labels, duplicate radio selection suppression, wrapped radio groups and IME-safe switch keys.
47
+
48
+ - Slider/Rate: disabled and canceled completion guards, finite extreme ranges and valid marks; selected-star clearing, zero-count ratings, locale labels and hover resets.
49
+
50
+ - AiPanel: Promise submission locks, failed-draft retry, stale-session protection, locale/disabled and IME handling; share Dialog modal stacking and follow streamed text in the actual body scroll container.
51
+
52
+ - Inline editors: disabled/readOnly and field configuration forwarding, accessible synchronous validation, IME protection, safe clear/unassigned identifiers and dynamic option focus; PeoplePicker accepts host loading/error/retry.
53
+
54
+ - SelectionBar: respect disabled commands and global overrides; lock pending operations, retain failed selections and await all round-robin writes before reporting success.
55
+
56
+ - Charts: localized empty data, finite/extreme numeric geometry, proportion-safe donut totals, data descriptions and responsive legends; protect interactive scatter points.
57
+ - Icon: safe prototype-like-name fallback, accessible unknown glyphs, native span refs/attributes and bounded numeric sizes.
58
+ - States/Progress: visible default Skeleton geometry, bounded placeholder/step counts, locale and native refs; honor null progress formatting and wrap narrow feedback content.
59
+ - Nav/Sidebar: isolate overflow keyboard/disabled state, preserve host routes and native refs, track label sizing; localize sidebar controls and end resizing on lost pointer capture.
60
+ - Segment: single keyboard entry, dynamic focus recovery, duplicate selection suppression, native refs/custom content/block layout; Chip consumes provider disabled.
61
+ - Tag/Divider: native attributes/ref, custom tag icons and close-event isolation; locale/disabled overrides, dotted rules and wrapping RTL captions.
62
+ - Card: native section attributes/ref, compact/inner variants, Grid/Meta composition, localized loading and narrow-content wrapping.
63
+ - Avatar: numeric sizing, square shape, measured children scaling, Unicode initials, srcSet fallback, native refs and accessible stack overflow.
64
+ - Pagination: suppress current/empty paging notifications, bound numeric precision, discard disabled size-menu state and protect IME quick entry; verify controlled/default state and total changes.
65
+
66
+ - Menu/ContextMenu: single Tab entry, dynamic focus recovery, disabled branches and RTL nested Escape/backward handling; bounded long menus preserve internal scroll, and submenus flip into available viewport space.
67
+
68
+ - ConfigProvider: nested light/dark themes and merged CSS tokens now reach body portals; undefined token overrides inherit. Pagination and selector/date labels consume locale, table controls consume disabled, and RTL spacing, popup alignment and fixed-column offsets mirror correctly.
69
+
70
+ - DatePicker/DateRangePicker: local time confirmation, week/month/quarter/year modes, multiple dates, lazy presets, strict format parsing, controlled popups and shared date/time bounds. ISO string values and native refs remain unchanged.
71
+
72
+ - DataTable: hierarchical rows, nested headers, additive sort arrays, radio selection, fixed-height virtual windows and aggregate change events. Summary now receives processed roots before pagination; merged/detail/group bodies retain full rendering.
73
+
74
+ - Complete Cascader multiple/intermediate paths, shared Tree check conduction, field mapping,
75
+ controlled popups/search, rich labels and source-safe lazy loading with retry.
76
+
77
+ - Complete Tree/DirectoryTree cached range selection, expansion-independent directory picks,
78
+ leaf icons, RTL/IME behavior and native drag geometry. `onDrop.dropPosition` now uses
79
+ the target sibling index; `relativeDropPosition` exposes the previous -1/0/1 meaning.
80
+
81
+ - Complete Transfer independent controlled checks, directional move payloads, page-safe selections,
82
+ rich/custom lists, search hooks and native refs with guarded custom callbacks.
83
+
84
+ - Complete Select/MultiSelect grouped virtual choices, labeled-value caching, controlled opening,
85
+ clear controls, custom option/label/tag rendering and async states while preserving string callbacks.
86
+
87
+ - Add Carousel, target-aware modal Tour, real SVG/canvas QRCode, native Watermark and responsive SVG BorderBeam; reuse Dialog focus/stack behavior for Tour.
88
+
89
+ - Add Mentions with caret-safe insertion, IME and keyboard suggestions; add ColorPicker with solid/gradient HEX/RGB/HSB editing, alpha, presets and controlled opening.
90
+
91
+ - Add Form conditional/render items, selective updates, preserve policies and filtered focus/scroll options
92
+ while protecting StrictMode and dynamic list remounts.
93
+
94
+ - Add App-bound message/notification APIs and static host restoration; add keyed Toast updates,
95
+ destroy-all, close callbacks, timer cleanup and persistent-hover handling.
96
+
97
+ - Add real-file Upload with async preflight/manual mode, multipart XHR or cancellable custom requests,
98
+ progress/error lists, accept/count limits and drag/drop.
99
+
100
+ - Add native Image loading/fallback and controlled previews with zoom, rotation and explicit groups.
101
+
102
+ - Add four-edge Drawer sharing Dialog modal behavior, bounded dimensions and independent
103
+ keyboard, mask and close-button controls.
104
+
105
+ - Add Dropdown and Popconfirm with native button refs, existing Menu/Popover behavior,
106
+ controlled visibility and async confirmation retry/cancellation.
107
+
108
+ - Fix nested Dialog scroll locks, top-layer keyboard handling, focus restoration and hidden/disabled focus filtering.
109
+
110
+ - Add compound Layout, responsive Row/Col/Grid, Flex/Space/Compact and accessible Splitter
111
+ with controlled sizing, bounds, keyboard/pointer adjustment and collapse restoration.
112
+
113
+ - Add Breadcrumb, responsive Descriptions, string-preserving Statistic and compound Typography
114
+ with async copying, editing, expansion and native link/heading semantics.
115
+
116
+ - Add InputNumber, AutoComplete, TreeSelect and TimePicker with Form/config integration,
117
+ numeric drafts, IME-safe suggestions, reused Tree loading/checks and native clock validation.
118
+
119
+ - Extend Badge status/rich counts/RTL offsets and Alert banner/title/variants/custom close lifecycle.
120
+
121
+ - Add Input affixes, clear/count/status/variants with native reset and Form integration;
122
+ extend button shapes/ghost/danger, loading actions, RTL switch text/keyboard and group option attributes.
123
+ - Expand Tabs editing/lifecycle/placement, Collapse keyboard/extra actions and Steps item states.
124
+
125
+ - P1 navigation: controlled Menu selection/expansion and inline/horizontal modes,
126
+ independent-menu state isolation, RTL/disabled Nav and ContextMenu, controlled
127
+ Sidebar sizing and collapsed focus isolation. Share rich-label search text.
128
+
129
+ - P1: expand Pagination, Segment, Avatar, Tag, Card and Divider contracts;
130
+ preserve controlled DataTable pagination and add display/interaction regressions.
131
+
132
+ - Add absolute FormItem dependency paths with user-triggered transitive
133
+ revalidation, cyclic dependency protection and stale async-result regression coverage.
134
+
135
+ - Add opt-in fixed-height Select/MultiSelect virtual scrolling, complete-list
136
+ keyboard navigation, tags/token separators and distinct-value maxCount.
137
+ - Preserve virtual option focus during scrolling and prevent delayed Popover
138
+ autofocus from overriding user navigation; defer tokenization during IME input.
139
+
140
+ - Add nested Form name paths, subtree validation/reset, copied values and
141
+ FormList dynamic/nested arrays with stable keys; expose Form.Item/Form.List
142
+ aliases and guard unsafe paths, cycles and excessive depth/indexes.
143
+
144
+ - Add ConfigProvider locale/direction/disabled defaults and a flat-field Form
145
+ foundation with async validation, resets, watched values and submission.
146
+ - Existing core controls consume disabled defaults; portals retain language
147
+ and direction, and Select/SplitButton close interactive layers when disabled.
148
+
149
+ - Complete Tree hierarchy capabilities: controlled state, cascade/strict checks, multi-selection, two-level lazy loading and retry, search, connector lines/custom rendering, virtual scrolling, ref scrolling and drag lifecycle/drop rules.
150
+ - Add DirectoryTree with folder/file icons, click/double-click expansion and Ctrl/Meta/Shift selection; retain legacy Tree route and reorder APIs.
151
+
152
+ - Complete DataTable details: local/remote pagination, local sorting and column filters, disabled row selection, expandable details, hidden/wrapping/merged cells, summary/title/footer and scroll configuration.
153
+ - Fix grouped and merged-cell cursor navigation, auxiliary column alignment, embedded control clicks, loading/error feedback and fixed expansion-column geometry.
154
+
155
+ ### Added
156
+
157
+ - Select/MultiSelect shared search: rich-label text, custom filtering/sorting,
158
+ controlled queries, remote search callbacks, and enabled-option keyboard navigation.
159
+
160
+ - `Cascader`, `Transfer`, `DatePicker`, and `DateRangePicker`: searchable hierarchy,
161
+ dual-list selection, themed calendar and mutually constrained date ranges.
162
+
163
+ - `CheckboxGroup`, `PasswordInput`, and `Rate`, reusing the existing controls
164
+ with controlled/uncontrolled state, native keyboard interaction and no new dependencies.
165
+
166
+ - `Slider`, `Steps`, and `Alert`: native range input, accessible controlled step
167
+ navigation, and dismissible inline feedback. Includes stories, tests and docs.
168
+
169
+ - `Radio` / `RadioGroup`, `Tabs`, `Collapse`, and `Badge`, informed by Ant Design
170
+ component contracts and implemented with existing tokens and no new dependencies.
171
+ Includes native radio navigation, accessible tab panels, controlled/uncontrolled
172
+ selections, accordion mode, badge overflow, Storybook examples and tests.
173
+
174
+ ### Fixed
175
+
176
+ - MultiSelect disabled choices, nested labels, dialog/checkbox semantics, large
177
+ and invalid states, and clearing that preserves locked and unknown selections.
178
+ - Select popup declaration now matches its dialog container.
179
+
180
+ ### Changed
181
+
182
+ - Local release script now validates only with `--dry-run`; tagging, publication
183
+ and deployment require the platform workflow.
184
+
185
+ ### Fixed
186
+
187
+ - **A cell's commands no longer slice the value they sit on.** The boxes a
188
+ `Column.actions` cell reveals on hover had no backdrop of their own, so the
189
+ value under them ended mid-glyph — in the `Cell Commands` story the ID cell read
190
+ `S` and then two boxes. The overlay now fades into the cell's **own** tint
191
+ (`--pro-cell-overlay`: the row's wash at rest, the accent tint while the pointer
192
+ is on the cell), exactly the way the row's commands already faded into the row,
193
+ so a narrow column loses its value gradually instead of cutting it. The story
194
+ itself was demoing the feature in a 90px ID column while its two boxes measured
195
+ 52px; it now uses a 160px cell, where the value and both boxes read together
196
+ (the product's own ID column is 120px and carries one box).
197
+ - **The `Cell Commands` story named an icon the sprite does not have.** `copy`
198
+ is not in `ICONS`, and an unknown name draws an empty box — which is what the
199
+ screenshot showed. It uses `hashtag`, the glyph the demo's own "Copy ID"
200
+ affordance uses.
201
+
202
+ ### Fixed
203
+
204
+ - **A widget inside a cell keeps its own keys.** The grid's key listener sits on
205
+ the `<tr>`, so everything pressed inside a cell bubbled to it and the row acted
206
+ on it: a space in a rename field selected the row instead of typing a space, an
207
+ `Enter` on a `Column.actions` box opened the record (and the row's
208
+ `preventDefault` stopped the box from running at all), and an arrow key inside
209
+ an in-cell editor moved the cursor and pulled the focus out from under it,
210
+ committing the half-typed value. The row now answers only for the grid
211
+ (`src/components/table/keyboard.ts`): a key is the widget's when something
212
+ already handled it or when the focus is inside an `input`, `textarea`, `select`,
213
+ `button`, `a[href]`, `[contenteditable]` or one of the combobox / listbox /
214
+ textbox / slider / switch roles — a list of roles, not of this library's
215
+ classes, so a host's own editor inside a cell is treated the same way.
216
+ - **Focus follows the cursor in a grouped grid too.** `groupBy` renders one
217
+ `<tbody>` per group and the focus query only looked inside a single one, so ↑ ↓
218
+ moved the outlined cell while the keyboard stayed behind. The query now runs
219
+ over the whole scroll container, whichever body the cell is in.
220
+ - **The grid stops jumping while it loads.** The skeleton rows carried no pinned
221
+ geometry, so a grid with a frozen column drew its header frozen and its loading
222
+ rows unfrozen, then snapped sideways when the data landed. The skeletons wear
223
+ the same offsets as the rows they stand in for.
224
+ - **The frozen edge shadow is drawn once.** With `selectable` + `showIndex` + a
225
+ frozen column, the body drew the shadow on the index cell _and_ on the last
226
+ frozen data column while the header drew it once; the body now agrees with the
227
+ header (the shadow belongs to the outermost pinned cell of the run).
228
+ - **A group header spans the header tools column.** `headerTools` adds a column
229
+ to the header row, and the group row's `colSpan` did not count it, so the group
230
+ band stopped one column short (visible wherever a list groups _and_ shows the
231
+ column-settings gear).
232
+ - **A row keeps its announced position when it has a context menu.** The
233
+ `aria-rowindex` was added to the wrapper instead of the row, so every row of a
234
+ right-clickable grid — i.e. every `ListWorkbench` row — announced no position
235
+ at all. It is now set on the `<tr>` itself, which is also the menu's trigger.
236
+ - **`RichTextToolbar` is back in the published surface.** It is re-exported by
237
+ `src/ship/EntityDialog.tsx` rather than declared in it, and
238
+ `scripts/public-surface.mjs` only read declarations, so the name was missing
239
+ from `docs/api.md`, from the test-coverage check and from the story-coverage
240
+ check while being a real runtime export. The script now follows
241
+ `export * from` and `export { … } from` (the public surface keeps its 268 other
242
+ names unchanged).
243
+
244
+ ### Changed
245
+
246
+ - **`DataTable` is split by concern.** The 912-line `src/components/Table.tsx`
247
+ became `src/components/table/`: the props contract (`types`), the pinned-column
248
+ arithmetic and the skeleton geometry (`geometry`), the keyboard (`keyboard`),
249
+ the grouping (`grouping`), and the pieces the grid is assembled from —
250
+ `DataTable`, `TableHead`, `TableRow`, `SkeletonRows`, `BulkBar`, `ColumnMenu`,
251
+ `CellCommands`. `src/components/Table.tsx` stays as the barrel so every import
252
+ path (`../components/Table`, the stories, the tests, `src/index.ts`) is
253
+ unchanged, and `DataTableProps` stays next to `DataTable` because generated docs
254
+ resolve a component's props type in the component's own file.
255
+
256
+ ### Changed
257
+
258
+ - **The pointer works in two levels**, the product's own: the row it is in takes the
259
+ light wash (`--surface-table-row-hover`, with the pick cell's gutter following it)
260
+ and the cell under it takes a step deeper — the accent tint, which over that wash
261
+ lands on the reference's measured `#ebf0fb`. A hovered row still reads as a row and
262
+ the cell it points at reads as the target, instead of one lone cell lighting up in
263
+ a grid of unchanged rows.
264
+ - **A selected grid row is neutral, not blue.** Sampled off the reference: the
265
+ selected row is `#fafafa` while the accent stays on its checkbox (`#57abfb`), so
266
+ a selected row still reads as a row and the control carries the state. The grid
267
+ uses a new `--surface-table-row-selected` (the head's tone in both themes); the
268
+ accent-tinted `--surface-selected` is unchanged for everything else.
269
+ - The pick column wears the header's tone (`--surface-table-head`, `#fafafa`) like
270
+ a spreadsheet's row-header gutter, instead of the rows' white. A row carrying a
271
+ state of its own — hover, selection — still wins, so the gutter is never the odd
272
+ one out in its own row.
273
+ - **The grid is drawn as a grid.** The cells had a bottom border only, so the
274
+ columns were separated by whitespace and nothing but the frozen edge; every
275
+ column now carries the same hairline on its right (the filler and the structural
276
+ pick / action / tool cells included in the exclusion, since they are not
277
+ columns), which is what makes it read as a table.
278
+ - **The grid is a spreadsheet, not a list of rows.** Hovering lights up the cell
279
+ under the pointer (a light wash and a hairline) instead of washing the whole row,
280
+ and the keyboard cursor is a **cell**: ← → walk the columns, ↑ ↓ the rows,
281
+ Home/End jump to the ends of the row, Enter opens the record, Space toggles its
282
+ selection, and focus rides with the cursor on a roving tabindex — one tab stop
283
+ for the whole grid, which is the cursor cell. The arrow arithmetic moved out of
284
+ `useListCursor` (still exported) because a row index is no longer the thing being
285
+ moved.
286
+
287
+ ### Added
288
+
289
+ - **`Column.actions` — commands that live inside a cell.** Each column can carry
290
+ `[{ id, icon, label, onSelect(row) }]`: the boxes overlay the cell's right edge,
291
+ appear while the pointer is on **that cell** (or it holds focus), and each brings
292
+ its own tooltip and callback — the product's "Click to copy" on the identifier,
293
+ next to the value. The component owns the box, the reveal and the tooltip; the
294
+ host supplies the icon, the name and the write, which is all an integration needs.
295
+ - The **assignee cell's** person-add affordance: the host can name it
296
+ (`assignLabel(row)`) and take it over (`onAssign(row)`), or leave both out and let
297
+ the cell open its own people picker.
298
+
299
+ - `Toast` grew the things a host kept re-implementing: six **placements**
300
+ (`ToastProvider placement`, default unchanged at `top-center`), a **`warning`**
301
+ tone (the product's amber), an optional **title** with the message as its
302
+ description, one inline **action** (Undo / View / Retry) that does not dismiss
303
+ the toast itself, a **`filled`** variant on the tone's own colour, a
304
+ **`closeable`** switch, an **icon** override, and `max` to cap the stack.
305
+ `push` now returns the toast's id so a host can dismiss it early, and its third
306
+ argument takes an options object as well as the bare duration it took before.
307
+
308
+ ### Added
309
+
310
+ - **The selection bar is `DataTable`'s.** The product covers the grid's header row
311
+ with it while rows are selected, and the stylesheet was written for exactly that
312
+ (`.pro-selection-bar` is absolutely positioned, 50px, full width) — but only
313
+ `ListWorkbench` rendered one, above the table, so a `DataTable` on its own had no
314
+ bar at all. The table now renders it **in place of the header row**: the covered
315
+ header stands down (`inert`, and the select-all lives on the bar instead of
316
+ existing twice), the bar's box centres in the pick column and its count starts
317
+ where the first data column does, so nothing is off by a few pixels. `SelectionBar`
318
+ moved from the ship layer to `src/components/SelectionBar.tsx` (same public
319
+ export), and `buildBulkCommands` there turns the host's data into the bar the
320
+ table and the board both use.
321
+ - The grid keeps the column widths it was given: `table-layout: fixed` spreads the
322
+ leftover space over every column, so a 44px pick column came out 66px and nothing
323
+ could line up with it. A trailing auto-width filler column takes the slack.
324
+ - **The bulk bar is the component's.** `ListWorkbench` takes `bulk` — the data and
325
+ the write for Update status, Move in baseline, Schedule, Distribute workitems and
326
+ Edit properties — and builds the whole bar itself, in the product's order, above
327
+ whatever `bulkActions` adds and its own More (export / delete) and Cancel. The
328
+ round-robin spread, the confirmation message and the bulk edit dialog are the
329
+ library's; a page supplies choices and a write and nothing else. That is the
330
+ point of the exercise: a page should not re-implement a command the product
331
+ always shows.
332
+ - A lone command in `bulkActions` keeps its own label, icon and `onSelect`
333
+ instead of being folded into a submenu named after its id — which is what a bar
334
+ needs to mix grouped commands (Update status ›) with single ones (Distribute
335
+ workitems).
336
+ - **Frozen columns are part of the list view.** `ListView.frozen` names the
337
+ columns pinned to the left edge; `ListWorkbench` pins them (`fixed: 'left'`),
338
+ answers the column menu's Freeze / Unfreeze into that view state, and forwards
339
+ Move / Insert / Remove to the host, which owns `columns`. The demo's lists
340
+ freeze `id`, so the grid opens the way the product's does: the checkbox column
341
+ and the ID scroll together, the rest goes under them, and the frozen run carries
342
+ the edge shadow. The header ⋯ now works in the real list, not only in the
343
+ DataTable story.
344
+ - **`Menu` grows submenus.** An item may carry `items`: it renders a caret and
345
+ `aria-haspopup="menu"` / `aria-expanded`, opens those entries beside its own row
346
+ and keeps the parent open until something in the child is chosen (`ArrowRight`
347
+ opens it, `ArrowLeft` closes it and restores the focus). `divider: true` renders
348
+ the hairline `ContextMenuItem` already supported, so `Menu` and the context menu
349
+ finally agree on what an item is. `SelectionBar`'s hand-rolled submenu can move
350
+ onto this.
351
+ - **`DataTable`'s column menu** (`onColumnCommand`), the product's header ⋯:
352
+ Sort by (a real submenu — Ascending / Descending / Clear sort, the active
353
+ direction checked), Move left / Move right (disabled at the ends), Insert left /
354
+ Insert right, Freeze or Unfreeze (the pin reflects `column.fixed`) and Remove
355
+ this column. The library reports a `ColumnCommand` and reorders nothing itself —
356
+ the host owns `columns`. The story's harness applies every command, so the demo
357
+ moves, inserts, freezes and removes columns for real.
358
+ - `DataTable` gained the two things its grid was missing next to its stylesheet:
359
+ - **A row cursor** (`cursor`): one tab stop for the body, ↑ ↓ walking an
360
+ `is-cursor` row, Home / End jumping, Enter opening it, Space toggling its
361
+ selection, and a pointer click parking the cursor where it landed. It uses the
362
+ library's own `useListCursor`, which clamps the cursor when a filter shrinks
363
+ the list under it. `ListWorkbench` turns it on, so the product's list screen is
364
+ one tab stop instead of fifty.
365
+ - **The cursor row shows its checkbox.** The pick cell is "the row number at
366
+ rest, the checkbox on hover / selected / while a selection is active" — the
367
+ keyboard cursor is the pointer's hover, and it was missing from that list, so
368
+ the cursor row kept printing its number over an invisible checkbox. The number
369
+ now steps aside only when there _is_ a checkbox to take its place
370
+ (`:has(.pro-checkbox)`), so a host with no selection writer keeps its numbers.
371
+ - **Density** `'default' | 'compact'`: 36px rows and header with `--text-sm`
372
+ cells, and a reserved actions column instead of the floating overlay (that is
373
+ what `.pro-table__cell--actions`, until now dead, right-aligns).
374
+
375
+ ### Fixed
376
+
377
+ - **The row's commands sit at the row's end again.** The overlay resolved its
378
+ containing block against the actions cell — zero-width and `position: sticky`
379
+ whenever a column was frozen — so with a frozen column the pencil and the ⋯
380
+ landed at the **scrollport's** edge (mid-grid) instead of the row's. The cell
381
+ keeps the frozen edge's shadow and gives up the pinning, so the overlay's
382
+ containing block is the row itself: measured in the story, `offsetParent` is the
383
+ `TR` and the box sits 16px from the row's right edge.
384
+
385
+ - The pick cell's checkbox belongs to the **pick cell**. It used to appear as soon
386
+ as the pointer was anywhere in the row, so pointing at a title, a status or an
387
+ empty column offered a selection box the operator never asked for — the opposite
388
+ of the cell the interaction now belongs to. Only the pick cell reveals it; a
389
+ selected row, the cursor row and the bulk-selection mode keep theirs on screen,
390
+ because there the box is a state rather than an offer.
391
+
392
+ - The selection bar is exactly as tall as the header row it stands in for. It was
393
+ pinned at 50px while the grid's header measured 44px, so it hung 6px past the row
394
+ — visible as the `Cancel` button's box sticking out below the table's top edge.
395
+ Both now read one token (`--table-head-height`), so they cannot drift apart
396
+ again: measured in the story, the bar and the header row are the same 44px box
397
+ (40→84) with the first data row starting exactly at its bottom edge.
398
+
399
+ - A frozen column cannot look like a column of its own any more. The pinned cells
400
+ painted their own `--surface-default` and stayed in line with their row only
401
+ because the row states out-specified them (`tr:hover td` is 0,2,3 against
402
+ `td.is-fixed` at 0,2,1); any other row background — a host's own row class, a
403
+ state the stylesheet does not know — was covered by that opaque cell instead, and
404
+ a band that stops matching its row is what reads as an outlined column. The row
405
+ carries the background now and the pinned cells take `background: inherit`, so
406
+ the band is the row's colour by construction and still opaque for the columns
407
+ sliding under it.
408
+ - The pick column is centred. Its checkbox and its row number are two states of
409
+ one 44px slot, but the checkbox was laid out in the flow with `opacity: 0`, so
410
+ it took 16px + a gap and pushed the number off the axis — the header's
411
+ select-all sat 14px left of where the number was, and neither matched the
412
+ column. The checkbox is now absolutely centred and the cell aligns its content,
413
+ with the selector naming the element (`.pro-table td.pro-table__pick`) because
414
+ `.pro-table td` outranks a bare class. Measured on the built demo: every row's
415
+ number and box, and the header's box, are within 0px of the cell's centre.
416
+ - The pick cell no longer depends on `:has()` to hide its row number: the cell
417
+ says `is-selectable` when it really holds a checkbox. A browser without `:has()`
418
+ drops that whole rule, which left the checkbox drawn _next to_ the number.
419
+ - `DataTable` now does what its stylesheet already promised:
420
+ - **Only pin what is frozen.** The checkbox and actions columns were pinned
421
+ whenever the grid had a selection or row commands, so an unfrozen grid carried
422
+ a right-edge shadow with nothing to shadow and an opaque background on the
423
+ pick cell. They are pinned now only when a data column is (`column.fixed`).
424
+ - **Fixed columns work.** `.is-fixed-left` / `.is-fixed-right` and the two
425
+ `--shadow-table-fixed-*` tokens were dead CSS — nothing set the classes. A
426
+ column takes `fixed: 'left' | 'right'`, the component measures the offsets
427
+ from the preceding pinned widths, and the outer column of each run gets the
428
+ edge shadow. The checkbox and actions columns are pinned like the product's.
429
+ - **Group headers fold.** They drew a caret and did nothing; they are buttons
430
+ with `aria-expanded` now, uncontrolled (open) or controlled through
431
+ `expandedGroups` / `onExpandedGroupsChange`.
432
+ - **No dead controls.** The pick column keeps its row number, but its checkbox
433
+ (and the select-all box) only render when `onSelectedKeysChange` can write the
434
+ selection — the five host call sites that passed `selectable` without a
435
+ handler were showing checkboxes that did nothing. A `sortable` column whose
436
+ table has no `onSortChange` is disabled with a title instead of silently
437
+ ignoring clicks.
438
+ - `aria-rowcount` counts the header row as ARIA requires, and flat rows carry
439
+ `aria-rowindex`.
440
+ - Removed the unused `.pro-table__cell--compact` rule.
441
+
442
+ ### Fixed
443
+
444
+ - The focus ring is drawn once. `base.css`'s `.pro-root :focus-visible` (0,2,0)
445
+ out-specified the inline cell editors' own `outline: none` (0,1,0), so an open
446
+ cell showed the global 2px ring _and_ the cell's accent hairline — two
447
+ concentric boxes around the title. `.pro-cell-editor :focus-visible` now wins on
448
+ order, and `CONVENTIONS.md` records the specificity trap.
449
+ - The global focus rule no longer sets `border-radius`, which used to overrule a
450
+ component's own radius the moment it took focus (a focused button rendered 2px
451
+ instead of 4px). The ring follows the element's radius natively.
452
+
453
+ ### Changed
454
+
455
+ - `Tag` (and with it `StatusTag` and `CountTag`) is cut with the button's corner:
456
+ `border-radius: var(--radius)` instead of an 18px pill, and `LevelTag`'s badge
457
+ follows. A tag and a button are the same kind of object on the page, so they now
458
+ share the radius token. `Tag`'s `square` prop is gone — the button shape is the
459
+ default — and `pill` is the opt-in for the fully-rounded shape.
460
+
461
+ ### Added
462
+
463
+ - Storybook (`pnpm storybook`, `pnpm build:storybook`): the react-vite framework,
464
+ the docs, a11y and themes addons, a preview that loads the published
465
+ stylesheet, renders inside `.pro-root` and switches `data-theme` from the
466
+ toolbar. The args tables come from `react-docgen-typescript` reading the same
467
+ props declarations `docs/api.md` documents.
468
+ - `scripts/check-story-coverage.mjs`, wired into `pnpm verify`: every runtime
469
+ export has to be named by a `.stories.tsx`. The budget went 101 → 0, so all 113
470
+ exports are covered today (48 story files).
471
+
472
+ ## [0.2.0] — 2026-10-07
473
+
474
+ The release that makes `@stcn52/pro` a library you can consume rather than a
475
+ source tree you have to share: a repository, a test suite, a gate, generated
476
+ reference documentation and a package whose contents are checked.
477
+
478
+ ### Added
479
+
480
+ - **Repository** — a standalone git repository with `LICENSE` (MIT),
481
+ `.gitignore`, this changelog and `CONVENTIONS.md` (the authoring spec), plus
482
+ `engines`, `packageManager` and `files` in `package.json`.
483
+ - **Tests** — vitest + Testing Library + jsdom: 263 tests over 17 files covering
484
+ every one of the 113 exported components and hooks (rendering, controlled
485
+ values, the keyboard path, `Esc` and focus). `scripts/check-test-coverage.mjs`
486
+ enforces the claim: it reads the runtime exports out of `src/` and fails when
487
+ any of them is named by no test.
488
+ - **Static quality** — ESLint 10 flat config with the React hooks rules (16
489
+ known warnings, pinned with `--max-warnings`), and Prettier across the
490
+ repository.
491
+ - **`pnpm verify`** — one gate: package contract, typecheck, boundaries, test
492
+ coverage, docs, lint, format, tests, build, pack, `publint --strict`,
493
+ `are-the-types-wrong` and a pack smoke test that unpacks the tarball into a
494
+ throwaway consumer and imports the built ESM, the built CJS and the stylesheet
495
+ from there. Everything runs offline.
496
+ - **Boundary checks** — `scripts/check-boundaries.mjs` fails on any host symbol
497
+ in `src/` (repository name, router, storage, route literals, the demo store,
498
+ `app-*` class names) and on any host stylesheet rule whose subject is a
499
+ `pro-*` class. Every class the library renders must be `pro-*` (or an `is-*`
500
+ state hook).
501
+ - **Generated documentation** — `pnpm gen:docs` writes `docs/api.md` (every runtime
502
+ export with the props table of its props type, read from the TypeScript AST)
503
+ and `docs/tokens.md` (the token declarations, grouped as `tokens.css` groups
504
+ them, dark overrides apart). `scripts/check-docs.mjs` fails on a stale
505
+ generated file and on an export the README or `docs/components.md` does not
506
+ name.
507
+ - **`ListHeader`'s title is a real `<h2>`** (with `font: inherit`, so the
508
+ measured 15px/400 type is unchanged): it is the only title a tab page renders.
509
+
510
+ ### Changed
511
+
512
+ - `Field`'s generated id is handed to the control only (a component, or a
513
+ labellable native element) — injecting it into a `<span>` wrapper used to point
514
+ the label at something a label cannot name. `EntityDialog` names its field ids
515
+ explicitly.
516
+ - `Sidebar` follows a shell-level collapse: `SidebarCollapseProvider` wraps a
517
+ page tree once and every sidebar inside collapses with it, instead of a host
518
+ stylesheet hiding `.pro-sidebar`.
519
+ - `EntityDialog` no longer renders the host's `app-counter` / `app-input-wrap`
520
+ classes; the counter is the library's own `pro-input-counter` /
521
+ `pro-input-wrap`. `RichTextToolbar` and the rest of `EntityDialog`'s host
522
+ classes became `pro-richtext*` / `pro-dialog__*`, and the `@mention` highlight
523
+ became `.pro-mention` in the stylesheet.
524
+ - The narrow-screen rules that hid the sidebar (1024px) and the rail (640px)
525
+ live in the library now, not in the demo's stylesheet.
526
+ - `QuickStart` takes `done` / `total` for the checklist counter; `Tag` takes
527
+ `tone="accent"` and `size="sm"` for the quarter chip.
528
+ - The `New project` wizard's classes moved from the host's `proj-*` prefix into
529
+ the library's `pro-wizard__*` namespace.
530
+
531
+ ### Fixed
532
+
533
+ - `Menu`'s typeahead swallowed keystrokes typed into a searchable menu's search
534
+ field: it preventDefaulted any printable character that matched an item, so `d`
535
+ could not be entered in a list containing "Duplicate".
536
+ - `Input`'s `size?: 'md' | 'lg'` was intersected with the DOM's numeric `size`
537
+ attribute, which collapsed the prop to `undefined` for a typed caller.
538
+ - `TypeBadge` threw on a kind it does not know; it renders a neutral badge now.
539
+ - `Icon` returned nothing for an unknown name while the docs promised a warning
540
+ and an empty box; it warns and keeps the box.
541
+ - `Dialog` derived its title id from `Math.random()` during render (now
542
+ `useId()`); `Tree` carried two dead assignments; `ContextMenu`'s `useCallback`
543
+ missed the coordinates it reads; `PeoplePicker`'s effect missed `setCursor`;
544
+ `Toast` kept a stale `eslint-disable`.
545
+ - `exports["."]` pointed the `require` condition at the ESM type declarations;
546
+ `import` and `require` carry their own `types` (`index.d.ts` / `index.d.cts`).
547
+
548
+ ### Notes
549
+
550
+ - Still 0.x: the demo is the only consumer, and 1.0.0 is for the point where the
551
+ API is frozen for a second one.
552
+ - `docs/measurements.md` keeps recording where each measurement came from.
553
+
554
+ ## [0.1.0] — 2026-10-07
555
+
556
+ The first cut: the Ship workbench restored as a library, then the demo's
557
+ components pulled out of the demo.
558
+
559
+ ### Added
560
+
561
+ - **Foundations** — `cx`, the `Intl`-backed formatters (`formatNumber`,
562
+ `formatListTime`, `formatDateTime`, `formatCompact`, `pluralize`),
563
+ `useControllableState`, `statusColor`, `avatarColor`, `initials`, `nextRuleId`.
564
+ - **Tokens and styles** — `tokens.css` (raw scale + purpose-named semantic
565
+ tokens, light and dark), `base.css`, `primitives.css`, `ship.css`, published
566
+ together as `styles.css` and separately as `tokens.css`.
567
+ - **Icons** — `Icon`, `ICONS`, `IconName`: 146 glyphs taken verbatim from the
568
+ product's sprite.
569
+ - **Primitives** — `Button`, `SplitButton`, `ActionButton`, `Input`, `Textarea`,
570
+ `SearchInput`, `Field`, `Checkbox`, `Switch`, `Select`, `MultiSelect`,
571
+ `Popover`, `Menu`, `ContextMenu`, `Tooltip`, `Dialog`, `Toast` /
572
+ `ToastProvider` / `useToast`, `DataTable`, `Pagination`, `Tag` / `StatusTag` /
573
+ `LevelTag` / `CountTag`, `Avatar` / `AvatarStack`, `Progress`, `Metric`,
574
+ `BarChart`, `Donut`, `Sparkline`, `ScatterPlot`, `Crumbs`, `KeyValue`, `Card`,
575
+ `Nav`, `Tree` / `TreeGroup`, `Sidebar`, `Segment`, `Chip`, `Divider`,
576
+ `EmptyState`, `ErrorState`, `Skeleton`, `TableSkeleton`, `LoadingBlock`,
577
+ `InlineSpinner`, `Banner`.
578
+ - **Ship surface** — `AppRail`, `AppHeader`, `ProductHeader`, `HeaderActions`,
579
+ `BrandMark`, `ListHeader`, `ListToolbar`, `FilterPanel`, `FilterChip`,
580
+ `BoardView`, `RoadmapView`, `DocSplit`, `TitleCell`, `TypeBadge`, `ProductCell`,
581
+ `StarButton`, `QuickStart`, `TableBanner`, `SelectionBar`, `VoteCount`,
582
+ `DetailSheet` / `DetailSection` / `DetailField` / `Feed` / `FeedFilter` /
583
+ `CommentComposer`.
584
+ - **Composites extracted from the demo** — `ListWorkbench`, `CommentThread`,
585
+ `AttachmentUpload`, `LinkPicker`, `FilePreview`, `TransitionsTimeline`,
586
+ `EntityDialog`, `EntityPicker`, `ShareDialog`, `ReportCanvas`, `RoadmapEditor`,
587
+ `ProjectWizard`, `DetailBlock`, `DetailTool`, `EditableText`, `AccountMenu`,
588
+ `ShortcutsDialog`, `QuickStartDialog`.
589
+ - **Theming** — `data-theme="light" | "dark" | "auto"` on any ancestor;
590
+ components only read CSS variables.
591
+
592
+ ### Changed
593
+
594
+ - `EntityDialog` no longer renders the host's `app-counter` / `app-input-wrap`
595
+ classes; the counter is the library's own `pro-input-counter` /
596
+ `pro-input-wrap`.
597
+ - `QuickStart` takes `done` / `total` for the checklist counter; `Tag` takes
598
+ `tone="accent"` and `size="sm"` for the quarter chip.
599
+ - The `New project` wizard's classes moved from the host's `proj-*` prefix into
600
+ the library's `pro-wizard__*` namespace.
601
+
602
+ - Add general Empty, Spin and Result feedback entries; extend Skeleton with
603
+ compound placeholders and content switching, cancellable loading delay,
604
+ localized descriptions, inert busy content and reduced-motion handling.
605
+
606
+ - Extend Slider with native double range handles, marks, vertical layout, step
607
+ normalization, tooltips and completed interaction events; extend Rate with
608
+ half-star ratings, configurable count, hover feedback and custom characters.
609
+
610
+ - Extend Progress with circle/dashboard, segmented steps, percent/status,
611
+ success overlays, formatted information and logical dashboard gaps in RTL.
612
+
613
+ - Extend Tabs with placements, RTL keys, editable cards, dynamic selection and
614
+ close-focus recovery; add Collapse header keys/extra actions/lifecycle variants
615
+ and Steps item statuses, offsets, localized text and provider-disabled support.
616
+
617
+ - Add standalone Calendar and Timeline with ISO date selection, month/year views, keyboard/RTL navigation, controlled data, custom cells, pending items and logical layouts.
618
+
619
+ - Add List/List.Item.Meta, grouped Listy with fixed-height virtualization and scroll refs, and measured responsive Masonry.
620
+
621
+ - Add Affix, Anchor, FloatButton/Group and shared BackTop with custom scroll containers, logical placement, progress and reduced-motion support.