@sableui/react 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/AGENTS.md +10 -3
  2. package/CHANGELOG.md +235 -0
  3. package/README.md +11 -4
  4. package/bin/sable.mjs +266 -30
  5. package/dist/{_contract-CDa9-NdB.d.ts → _contract-DlklV0Im.d.ts} +2 -2
  6. package/dist/activity-DT8VK9fx.d.ts +25 -0
  7. package/dist/adapters/github.d.ts +41 -0
  8. package/dist/adapters/github.js +24 -0
  9. package/dist/blocks/index.d.ts +1 -1
  10. package/dist/components/activity-heatmap/activity-heatmap.js +420 -0
  11. package/dist/components/activity-heatmap/activity-heatmap.meta.js +142 -0
  12. package/dist/components/activity-heatmap/index.js +9 -0
  13. package/dist/components/avatar/avatar.js +16 -7
  14. package/dist/components/avatar/avatar.meta.js +43 -10
  15. package/dist/components/avatar/avatar.variants.js +26 -2
  16. package/dist/components/code-block/code-block.js +5 -3
  17. package/dist/components/code-block/code-block.meta.js +6 -0
  18. package/dist/components/collapsible/collapsible.js +6 -5
  19. package/dist/components/collapsible/collapsible.meta.js +14 -5
  20. package/dist/components/data-table/data-table.js +132 -45
  21. package/dist/components/data-table/data-table.meta.js +63 -4
  22. package/dist/components/date-picker/date-picker.js +13 -6
  23. package/dist/components/date-picker/date-picker.meta.js +22 -0
  24. package/dist/components/dialog/dialog.js +25 -5
  25. package/dist/components/dialog/dialog.meta.js +26 -5
  26. package/dist/components/media-image/media-image.js +3 -1
  27. package/dist/components/media-image/media-image.meta.js +15 -1
  28. package/dist/components/media-tile/media-tile.js +13 -1
  29. package/dist/components/media-tile/media-tile.meta.js +10 -0
  30. package/dist/components/menu/menu.js +2 -0
  31. package/dist/components/menu/menu.meta.js +2 -2
  32. package/dist/components/page-header/page-header.js +9 -4
  33. package/dist/components/page-header/page-header.meta.js +26 -1
  34. package/dist/components/panel-group/panel-group.js +17 -1
  35. package/dist/components/panel-group/panel-group.meta.js +43 -3
  36. package/dist/components/pill/pill.meta.js +15 -6
  37. package/dist/components/pill/pill.variants.js +1 -0
  38. package/dist/components/search-input/search-input.js +30 -5
  39. package/dist/components/search-input/search-input.meta.js +29 -4
  40. package/dist/components/section-header/section-header.js +27 -17
  41. package/dist/components/section-header/section-header.meta.js +27 -5
  42. package/dist/components/sheet/sheet.js +15 -3
  43. package/dist/components/sheet/sheet.meta.js +2 -2
  44. package/dist/components/side-nav/side-nav.variants.js +8 -3
  45. package/dist/components/sidebar/index.js +4 -0
  46. package/dist/components/sidebar/sidebar.js +90 -11
  47. package/dist/components/sidebar/sidebar.meta.js +38 -4
  48. package/dist/components/table/table.js +7 -3
  49. package/dist/components/tabs/tabs.js +10 -2
  50. package/dist/components/tabs/tabs.meta.js +4 -0
  51. package/dist/components/workspace/workspace.js +13 -3
  52. package/dist/components/workspace/workspace.meta.js +109 -1
  53. package/dist/file-hashes.json +94 -80
  54. package/dist/foundations/typography.js +13 -0
  55. package/dist/icon/Icon.js +21 -2
  56. package/dist/icon/index.d.ts +36 -1
  57. package/dist/icon/index.js +5 -0
  58. package/dist/icon/names.js +240 -0
  59. package/dist/icon-sets/reicon.js +41 -1
  60. package/dist/index.d.ts +540 -75
  61. package/dist/index.js +18 -1
  62. package/dist/layout/AspectBox.js +2 -5
  63. package/dist/layout/Bleed.js +10 -6
  64. package/dist/layout/Center.js +11 -3
  65. package/dist/layout/Columns.js +6 -3
  66. package/dist/layout/Grid.js +14 -6
  67. package/dist/layout/GridOverlay.js +7 -7
  68. package/dist/layout/Region.js +18 -3
  69. package/dist/layout/Row.js +27 -20
  70. package/dist/layout/Spacer.js +13 -2
  71. package/dist/layout/SplitPane.js +1 -1
  72. package/dist/layout/Stack.js +25 -18
  73. package/dist/layout/index.d.ts +57 -6
  74. package/dist/lib/chart/activity.js +32 -0
  75. package/dist/lib/chart/calendar.js +67 -0
  76. package/dist/lib/chart/chart-tooltip.js +2 -2
  77. package/dist/lib/chart/heat.js +55 -0
  78. package/dist/lib/cn.js +1 -0
  79. package/dist/lib/focus-return.js +53 -0
  80. package/dist/lib/taxonomy.js +1 -0
  81. package/dist/lib/use-media-query.js +41 -0
  82. package/dist/{meta-DX5r5wQL.d.ts → meta-Dfm9AExe.d.ts} +6 -0
  83. package/dist/patterns/dashboard-page.js +23 -17
  84. package/dist/patterns/index.d.ts +7 -2
  85. package/dist/patterns/index.js +5 -1
  86. package/dist/patterns/tracker-page.js +392 -0
  87. package/dist/patterns/tracker-page.meta.js +40 -0
  88. package/dist/presets/index.d.ts +1 -1
  89. package/dist/styles.css +301 -188
  90. package/dist/{theme-provider-BydfM5Z0.d.ts → theme-provider-BUBealk_.d.ts} +1 -1
  91. package/dist/themes/_contract.js +9 -1
  92. package/dist/themes/atelier/index.d.ts +1 -1
  93. package/dist/themes/ember/index.d.ts +1 -1
  94. package/dist/themes/index.d.ts +2 -2
  95. package/dist/themes/meridian/index.d.ts +1 -1
  96. package/dist/themes/poppy/index.d.ts +1 -1
  97. package/dist/themes/prism/index.d.ts +1 -1
  98. package/dist/themes/sable/index.d.ts +1 -1
  99. package/dist/themes/verdant/index.d.ts +1 -1
  100. package/llms/components/activity-heatmap.md +133 -0
  101. package/llms/components/avatar.md +32 -9
  102. package/llms/components/code-block.md +1 -0
  103. package/llms/components/collapsible.md +12 -4
  104. package/llms/components/data-table.md +40 -3
  105. package/llms/components/date-picker.md +15 -1
  106. package/llms/components/dialog.md +34 -3
  107. package/llms/components/media-image.md +12 -0
  108. package/llms/components/media-tile.md +21 -0
  109. package/llms/components/menu.md +1 -1
  110. package/llms/components/page-header.md +16 -1
  111. package/llms/components/panel-group.md +34 -2
  112. package/llms/components/pill.md +24 -5
  113. package/llms/components/search-input.md +37 -4
  114. package/llms/components/section-header.md +28 -4
  115. package/llms/components/sheet.md +2 -2
  116. package/llms/components/sidebar.md +17 -4
  117. package/llms/components/tabs.md +1 -0
  118. package/llms/components/workspace.md +81 -1
  119. package/llms/foundations/typography.md +4 -1
  120. package/llms/layout/aspect-box.md +1 -1
  121. package/llms/layout/bleed.md +1 -1
  122. package/llms/layout/center.md +1 -1
  123. package/llms/layout/columns.md +1 -1
  124. package/llms/layout/grid.md +1 -1
  125. package/llms/layout/region.md +1 -1
  126. package/llms/layout/row.md +9 -1
  127. package/llms/layout/spacer.md +1 -1
  128. package/llms/layout/split-pane.md +1 -1
  129. package/llms/layout/stack.md +1 -1
  130. package/llms/patterns/dashboard-page.md +18 -15
  131. package/llms/patterns/tracker-page.md +450 -0
  132. package/llms/reference/token-intent.md +3 -1
  133. package/llms/reference/utilities.md +4 -1
  134. package/llms/tasks/auditing-an-app.md +35 -1
  135. package/llms/tasks/building-a-page.md +3 -2
  136. package/llms/tasks/building-a-tool.md +101 -5
  137. package/llms/tasks/building-with-sable.md +11 -3
  138. package/llms/tasks/creating-a-preset.md +9 -0
  139. package/llms/tasks/getting-started.md +10 -3
  140. package/llms/tasks/using-presets.md +1 -2
  141. package/llms-full.txt +1215 -89
  142. package/llms.txt +11 -9
  143. package/package.json +7 -1
  144. package/registry.json +1021 -70
  145. package/scripts/adoption.mjs +252 -0
  146. package/scripts/agent-setup.mjs +110 -18
  147. package/scripts/app-css.mjs +129 -0
  148. package/scripts/grade.mjs +178 -2
  149. package/scripts/lint-rules.mjs +126 -0
  150. package/scripts/wiring.mjs +2 -1
  151. package/skills/entry/SKILL.md +1 -1
  152. package/skills/sable/SKILL.md +64 -6
  153. package/skills/sable/agents/openai.yaml +1 -1
  154. package/targets/ios.json +9 -0
@@ -1,11 +1,15 @@
1
1
  # Task: building a tool (workspace register)
2
2
 
3
- An editor, a canvas app, a console — anything whose primary interaction is
4
- *operating on something* rather than reading and navigating.
3
+ An editor, a canvas app, a console, or a records workspace (an issue tracker, a
4
+ support queue, a pipeline): anything whose primary interaction is *operating on
5
+ something* rather than reading and navigating.
5
6
 
6
7
  If the primary interaction is reading down a page, stop: you want
7
8
  [building-a-page.md](./building-a-page.md). A dashboard that scrolls is a page
8
- with dense content, not a workspace.
9
+ with dense content, not a workspace. A tracker or inbox where people work
10
+ through records one at a time, with the selected one open beside the list, is a
11
+ workspace. Start that one from the `TrackerPage` pattern
12
+ (`sable pattern TrackerPage --skeleton`) and read "A records workspace" below.
9
13
 
10
14
  ## The shell
11
15
 
@@ -29,12 +33,88 @@ hierarchy do not automatically transfer to dense operating surfaces.
29
33
  </Workspace>
30
34
  ```
31
35
 
36
+ Every slot is optional; the body is always the one growing row. A bar that
37
+ belongs to one column (a list's breadcrumbs and view switch, with the inspector
38
+ running the full height beside it, as in Linear) is a `WorkspaceBar` placed first
39
+ inside that column, not the `toolbar` slot.
40
+
32
41
  `Workspace` sets the compact density scope for its whole subtree. Nothing
33
42
  hardcodes a height: every control reads `--control-*`, so the same
34
43
  `<Button size="md">` is 40px on a page and 32px here. A `Dialog` opened from
35
44
  inside resets to comfortable, because a modal is a page-register surface even
36
45
  when a tool launched it.
37
46
 
47
+ ## A records workspace
48
+
49
+ The shape of an issue tracker, a support queue or a CRM pipeline. Every piece
50
+ below exists, so the build is composition; `sable pattern TrackerPage
51
+ --skeleton` has the whole tree.
52
+
53
+ ```tsx
54
+ <Workspace statusBar={<StatusBar>…</StatusBar>} bottomNav={<BottomNav>…</BottomNav>}>
55
+ <PanelGroup>
56
+ <Panel label="Navigation" width={220} narrow="sheet" side="left"
57
+ open={narrow ? navOpen : true} onOpenChange={setNavOpen} scroll={false}>
58
+ <Sidebar variant="inline">
59
+ <SidebarHeader><SidebarBrand mark={…} label="Studio" />{search}</SidebarHeader>
60
+ <SidebarNav>
61
+ <SidebarItem icon={…} label="Inbox" badge={…} />
62
+ <SidebarGroup label="Workspace">{items}</SidebarGroup>
63
+ </SidebarNav>
64
+ <SidebarFooter>
65
+ <Menu>
66
+ <MenuTrigger render={<SidebarAccount avatar={<Avatar size="2xs" … />} label="Emma Lee" detail="Studio" />} />
67
+ <MenuContent side="top" align="start">{settings, appearance, sign out}</MenuContent>
68
+ </Menu>
69
+ </SidebarFooter>
70
+ </Sidebar>
71
+ </Panel>
72
+ <WorkspaceCanvas className="flex flex-col">
73
+ <WorkspaceBar><Breadcrumbs>…</Breadcrumbs></WorkspaceBar>
74
+ <WorkspaceBar><Tabs><TabsList variant="pill">…</TabsList></Tabs></WorkspaceBar>
75
+ {filters}
76
+ <DataTable groupBy={(r) => r.status} groups={…} isRowSelected={…} onRowClick={…} />
77
+ </WorkspaceCanvas>
78
+ <Panel label="Details" width={320} narrow="sheet" open={selected != null}
79
+ onOpenChange={(o) => !o && setSelected(null)} scroll={false}>
80
+ <PanelSection title="Properties"><PropertyRow label="Status">…</PropertyRow></PanelSection>
81
+ </Panel>
82
+ </PanelGroup>
83
+ </Workspace>
84
+ ```
85
+
86
+ The decisions that keep it one system:
87
+
88
+ - **One grid down the rail.** SidebarBrand, SidebarItem, SidebarGroup and
89
+ SidebarAccount share the icon and label columns, and inside the compact scope
90
+ they take 32px rows on their own. Do not drop SideNav, an Entity or a stack of
91
+ ghost Buttons into the rail: each brings its own insets.
92
+ - **The account is one row and a menu.** Settings, appearance (light, dark,
93
+ system) and sign-out go in the SidebarAccount's menu, not in a Settings button
94
+ above it or a badge beside it. Its detail line ("Studio · Demo") carries what a
95
+ badge would.
96
+ - **One table, grouped.** Rows grouped by state are one `DataTable` with
97
+ `groupBy`: one header, columns that line up, a count and an add action per
98
+ group. Not one table per group inside Collapsibles.
99
+ - **The inspector renders once.** `Panel narrow="sheet"` docks it on a desktop
100
+ and makes it a Sheet below `lg`. Not a media-query hook plus a second copy in a
101
+ Sheet. The nav rail does the same from the left, and fills its sheet.
102
+ - **Reading inside the tool is page-sized.** An overview, a description, notes or
103
+ a docs tab goes in `<Density value="comfortable">`, or its prose renders at the
104
+ 11px chrome tier.
105
+ - **A view switch in chrome is a pill.** `TabsList variant="pill"`: its fill takes
106
+ the compact `--button-radius`, about 5px, so it reads as a segment. Underline
107
+ tabs are the page-section idiom.
108
+ - **Ship both modes, and a way to switch.** The look is a preset with light and
109
+ dark; SableThemeProvider applies it, and the account menu or a Settings dialog
110
+ holds the mode switch. A tool asked to be compact and technical takes
111
+ `type-base` (13 or 14) in the preset, which rebuilds the whole type scale,
112
+ instead of per-tier overrides.
113
+ - **State has a shape.** A status with more than on and off (partly done, in
114
+ review) can be an app-owned glyph, but give it a label (`role="img"`,
115
+ `aria-label`) and colour it from the palette (`var(--yellow-500)`) so it
116
+ follows the mode.
117
+
38
118
  ## What you will need, and what to avoid reaching for
39
119
 
40
120
  | you want | use | NOT |
@@ -46,6 +126,17 @@ when a tool launched it.
46
126
  | a colour property | `ColorField` | `<input type="color">` — OS dialog, ignores the theme, wrong size |
47
127
  | ambient document facts | `StatusBar` | a toolbar — status is glanced at, not operated |
48
128
  | chrome typography | `text-ui-md` / `-sm` / `-body` / `-section` / `-micro` / `-num` | `text-body-*` — page tiers, ~1/3 too large here |
129
+ | a title in a panel | `text-title-lg` (14px `text-ui-title` in compact) | a heading tier, or a page-tier override on `:root` |
130
+ | a top bar over one column | `WorkspaceBar` first in the column, with `Breadcrumbs` | a hand-built `<header>` with `/` spans |
131
+ | nav groups in a rail | `SidebarGroup` | `SideNav` + `SideNavGroup` — another set of columns |
132
+ | the account at the foot of a rail | `SidebarAccount` as a `MenuTrigger` | an Entity + a Settings Button + a Badge |
133
+ | a view switch in a bar | `TabsList variant="pill"` | underline tabs (page idiom), a hand-built segmented row |
134
+ | rows grouped by state | `DataTable groupBy` + `groups` | a DataTable per group in Collapsibles |
135
+ | a date property | `DatePicker size="xs"` | `<input type="date">` — OS format, wrong size |
136
+ | a person in a row | `Avatar size="2xs"` / `"xs"`, `AvatarGroup` for several | a class on Avatar to shrink it |
137
+ | a project or workspace mark | `Avatar shape="square" tone="…"` | a tinted `<span>` |
138
+ | an inspector on a phone | `Panel narrow="sheet"` | a media-query hook and a second render in a Sheet |
139
+ | a reading tab | `<Density value="comfortable">` | prose in chrome tiers |
49
140
 
50
141
  ## Menus, selects and popovers adapt on their own
51
142
 
@@ -104,9 +195,12 @@ a modal is its own surface rather than part of the control that opened it.
104
195
  the nesting reads inverted. Use `treeIndent(depth)`; never hand-write an inset.
105
196
  - **No pills in chrome.** The compact scope flattens `--button-radius` to ~5px —
106
197
  one of two page-register rules the workspace register deliberately reverses.
198
+ `TabsList variant="pill"` takes that radius too, so in a bar it is a segment.
107
199
  - **Drag with window listeners, not `setPointerCapture`.** Capture throws for an
108
200
  inactive pointer id and the throw aborts the handler mid-gesture, silently.
109
- - **An unmapped `<Icon name>` renders nothing.** No error. Check the contract.
201
+ - **An unmapped `<Icon name>` renders nothing.** A development build warns and
202
+ names the nearest names, and Lucide or Heroicons spellings resolve, but check
203
+ with `sable icon <name>` before you write one, through a wrapper too.
110
204
 
111
205
  ## Verify
112
206
 
@@ -136,6 +230,8 @@ and distinguish observed behavior from untested claims.
136
230
  ## Read next
137
231
 
138
232
  - `DESIGN.md → Registers` — doctrine, measured numbers, the two reversals
233
+ - `sable pattern TrackerPage --skeleton` — the records workspace, whole
139
234
  - `llms/components/workspace.md`, `panel-group.md`, `panel-section.md`,
140
- `tree.md`, `scrub-field.md`, `status-bar.md`, `color-field.md`
235
+ `sidebar.md`, `data-table.md`, `tree.md`, `scrub-field.md`, `status-bar.md`,
236
+ `color-field.md`
141
237
  - `llms/reference/token-intent.md` — which token to reach for
@@ -46,7 +46,13 @@ for each recognizable element with `sable search "<need>" --json`; inspect the
46
46
  matching `component <Name> --props` and `--examples` before implementation.
47
47
  Use Sable's composed anatomy and supported variants. A hand-built button, field,
48
48
  menu, dialog, table, or card that imitates a shipped component violates the contract.
49
- That binds behaviour and parts, not layouts. Results marked ◇ (patterns, blocks,
49
+ That binds behaviour and parts, not layouts. Search a part by its plain name and
50
+ its role before writing a class or component for it (`sable search breadcrumb`,
51
+ `sable search "top bar"`); exported parts such as `PropertyRow` and `WorkspaceBar`
52
+ are listed by name, and `sable component <Part>` answers with their entry. When a
53
+ part's look or size disagrees with the design, use its variant or prop, or report
54
+ the missing variant; never rebuild it. `sable icon <name>` says whether an icon
55
+ exists. Results marked ◇ (patterns, blocks,
50
56
  and compositions such as MediaTile) are references: use one when its layout fits
51
57
  the brief, otherwise compose your own from Sable's parts. The brief's signature
52
58
  surfaces, where the product's identity lives, are designed from its direction.
@@ -65,8 +71,10 @@ implement genuinely app-specific structure without creating parallel controls.
65
71
 
66
72
  ## Finish the working product
67
73
 
68
- Run `sable doctor`, `sable lint <source paths> --json`, and the project's build/type
69
- checks. Inspect desktop/mobile layouts, operate the primary task and recovery
74
+ Run `sable check <source paths>` (doctor, lint and grade) and the project's
75
+ build/type checks. Answer each Rebuilt parts note in the reuse inventory, and fix
76
+ a colour-modes warning: app colours live in a preset with both modes, and a
77
+ `ThemeSwitcher` under `SableThemeProvider` lets the user review each. Inspect desktop/mobile layouts, operate the primary task and recovery
70
78
  path, and check keyboard focus, fonts, reduced motion, and meaningful states.
71
79
  Use [reviewing-a-design.md](./reviewing-a-design.md),
72
80
  [reviewing-a-journey.md](./reviewing-a-journey.md), and
@@ -21,6 +21,15 @@ are optional: set `page-inset` and the `region-*` caps (`form`, `feed`, `content
21
21
  `wide`, `full`) in `tokens.shared` only when the direction calls for a different
22
22
  page proportion, and keep each cap's role.
23
23
 
24
+ Overall type size is one token. Sable's scale is built on a 16px base;
25
+ `"type-base": "14"` (or 13) in `tokens.shared` rebuilds every page tier at that
26
+ base, keeping their ratios, and spacing follows because the rhythm is derived
27
+ from body size. Use it when the direction is denser or more technical than the
28
+ bundled presets (a tracker, a console, "like Linear"). Don't rescale the root
29
+ font-size, and don't override tiers one by one to the same end. Retune a single
30
+ tier only when that tier's role changes. Inside a Workspace the chrome already
31
+ uses its own compact `ui-*` tiers.
32
+
24
33
  ```bash
25
34
  pnpm exec sable preset --template
26
35
  pnpm exec sable preset --check .sable/presets/clay.json --json
@@ -79,15 +79,22 @@ In a monorepo where Sable belongs to one workspace package, run setup in that
79
79
  package with `--root` naming the repository root (for example
80
80
  `pnpm --filter web exec sable setup --agent --root ../..`); the entries go
81
81
  there and resolve Sable through the package.
82
+ Setup also adds a short managed block to the project's `AGENTS.md` and
83
+ `CLAUDE.md`, the instructions an agent reads on every task, so a UI request
84
+ goes through the skill even when it doesn't mention Sable. Only the text between
85
+ its `<!-- sable:begin -->` and `<!-- sable:end -->` markers is Sable's;
86
+ `--no-instructions` skips it.
82
87
  Each entry resolves `skills/sable/SKILL.md` from the installed package, so package
83
88
  upgrades also update the workflow. Other agents can read that file directly.
84
89
  Use `$sable build <idea>`, `inspire <references>`, `audit <target>`, or
85
90
  `polish <target>` in Codex; use `/sable` with the same modes in Claude Code or
86
91
  Cursor. `create` is a build alias. These are agent modes, not shell commands.
87
92
 
88
- Upgrade with `pnpm update @sableui/react --latest`, then
89
- `pnpm exec sable setup --agent --update`. Setup only replaces managed files
90
- that have not been locally edited; `--dry-run` checks without writing. Keep
93
+ Upgrade with `pnpm exec sable upgrade`: it updates the package, then runs the
94
+ new version's `init`, which refreshes the skill entries and the instructions
95
+ block and checks the wiring. From 0.17 or earlier, which has no `upgrade`, run
96
+ `pnpm update @sableui/react --latest` and then `pnpm exec sable init` once.
97
+ Setup only replaces managed files that have not been locally edited; `--dry-run` checks without writing. Keep
91
98
  project preferences in project instructions. Package installation alone does not
92
99
  change agent settings. See [building-with-sable.md](./building-with-sable.md),
93
100
  [creating-a-preset.md](./creating-a-preset.md), and
@@ -102,8 +102,7 @@ saved file. Replace, don't add beside:
102
102
  ## Upgrade the package and skill
103
103
 
104
104
  ```bash
105
- pnpm update @sableui/react --latest
106
- pnpm exec sable setup --agent --update
105
+ pnpm exec sable upgrade
107
106
  pnpm exec sable preset --check .sable/presets/clay.json --json
108
107
  pnpm exec sable doctor
109
108
  ```