@twoux/ui 0.0.0-stage → 0.1.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 (325) hide show
  1. package/LICENSE.md +61 -0
  2. package/LICENSES/Apache-2.0.txt +202 -0
  3. package/NOTICE.md +64 -0
  4. package/README.md +211 -2
  5. package/SHADCN-DIFF.md +228 -0
  6. package/agent/AGENTS.md +358 -0
  7. package/agent/docs-md/accessibility.md +49 -0
  8. package/agent/docs-md/action-catalogue.md +81 -0
  9. package/agent/docs-md/actions.md +74 -0
  10. package/agent/docs-md/agent-activity-log.md +70 -0
  11. package/agent/docs-md/agent-permissions.md +82 -0
  12. package/agent/docs-md/agent-run.md +90 -0
  13. package/agent/docs-md/agents-on-behalf.md +74 -0
  14. package/agent/docs-md/ai-column.md +85 -0
  15. package/agent/docs-md/ai-origin-label.md +70 -0
  16. package/agent/docs-md/announcing-features.md +73 -0
  17. package/agent/docs-md/answer-actions.md +81 -0
  18. package/agent/docs-md/app-shell.md +114 -0
  19. package/agent/docs-md/artifact-card.md +89 -0
  20. package/agent/docs-md/ask-on-screen.md +72 -0
  21. package/agent/docs-md/assistant-page.md +100 -0
  22. package/agent/docs-md/assistant-panel.md +142 -0
  23. package/agent/docs-md/avatar.md +82 -0
  24. package/agent/docs-md/banner.md +106 -0
  25. package/agent/docs-md/board.md +163 -0
  26. package/agent/docs-md/breadcrumb.md +89 -0
  27. package/agent/docs-md/breakdown-table.md +97 -0
  28. package/agent/docs-md/bulk-action-bar.md +112 -0
  29. package/agent/docs-md/button.md +293 -0
  30. package/agent/docs-md/card.md +115 -0
  31. package/agent/docs-md/change-list.md +99 -0
  32. package/agent/docs-md/change-request.md +166 -0
  33. package/agent/docs-md/chart-area.md +87 -0
  34. package/agent/docs-md/chart-bar.md +100 -0
  35. package/agent/docs-md/chart-box-plot.md +88 -0
  36. package/agent/docs-md/chart-bullet.md +89 -0
  37. package/agent/docs-md/chart-candlestick.md +112 -0
  38. package/agent/docs-md/chart-card-line.md +120 -0
  39. package/agent/docs-md/chart-donut.md +90 -0
  40. package/agent/docs-md/chart-drill-down.md +72 -0
  41. package/agent/docs-md/chart-funnel.md +71 -0
  42. package/agent/docs-md/chart-heatmap.md +76 -0
  43. package/agent/docs-md/chart-histogram.md +81 -0
  44. package/agent/docs-md/chart-map.md +165 -0
  45. package/agent/docs-md/chart-mixed.md +88 -0
  46. package/agent/docs-md/chart-price.md +92 -0
  47. package/agent/docs-md/chart-radial-bars.md +109 -0
  48. package/agent/docs-md/chart-radial-lines.md +111 -0
  49. package/agent/docs-md/chart-sankey.md +104 -0
  50. package/agent/docs-md/chart-scatter.md +87 -0
  51. package/agent/docs-md/chart-share-bar.md +89 -0
  52. package/agent/docs-md/chart-treemap.md +98 -0
  53. package/agent/docs-md/chart-waterfall.md +93 -0
  54. package/agent/docs-md/checkbox.md +131 -0
  55. package/agent/docs-md/choosing-selection-control.md +71 -0
  56. package/agent/docs-md/citations-sources.md +79 -0
  57. package/agent/docs-md/citing-sources.md +63 -0
  58. package/agent/docs-md/clarify-before-acting.md +69 -0
  59. package/agent/docs-md/clarifying-question.md +92 -0
  60. package/agent/docs-md/code-block.md +174 -0
  61. package/agent/docs-md/colours.md +87 -0
  62. package/agent/docs-md/columns-menu.md +140 -0
  63. package/agent/docs-md/combobox.md +161 -0
  64. package/agent/docs-md/command-palette.md +126 -0
  65. package/agent/docs-md/confidence-note.md +87 -0
  66. package/agent/docs-md/configurable-dashboard.md +76 -0
  67. package/agent/docs-md/confirmation-dialog.md +93 -0
  68. package/agent/docs-md/connected-tools.md +80 -0
  69. package/agent/docs-md/connector-picker.md +127 -0
  70. package/agent/docs-md/consistency.md +53 -0
  71. package/agent/docs-md/content-density.md +61 -0
  72. package/agent/docs-md/conversation-history.md +78 -0
  73. package/agent/docs-md/create-record.md +80 -0
  74. package/agent/docs-md/csv-import.md +88 -0
  75. package/agent/docs-md/dashboard-items.md +78 -0
  76. package/agent/docs-md/data-export.md +61 -0
  77. package/agent/docs-md/data-grid.md +137 -0
  78. package/agent/docs-md/data-vis-colours.md +76 -0
  79. package/agent/docs-md/data-visualisation.md +76 -0
  80. package/agent/docs-md/date-picker.md +178 -0
  81. package/agent/docs-md/delete-record.md +75 -0
  82. package/agent/docs-md/dense-table.md +495 -0
  83. package/agent/docs-md/design-tokens.md +53 -0
  84. package/agent/docs-md/disabled-read-only.md +75 -0
  85. package/agent/docs-md/drag-and-drop.md +74 -0
  86. package/agent/docs-md/dropdown-menu.md +137 -0
  87. package/agent/docs-md/edit-record.md +73 -0
  88. package/agent/docs-md/empty-loading-error.md +78 -0
  89. package/agent/docs-md/empty-state.md +128 -0
  90. package/agent/docs-md/error-messages.md +75 -0
  91. package/agent/docs-md/error-state.md +123 -0
  92. package/agent/docs-md/export-menu.md +86 -0
  93. package/agent/docs-md/feedback-prompt.md +88 -0
  94. package/agent/docs-md/field-mapping.md +78 -0
  95. package/agent/docs-md/file-drop.md +110 -0
  96. package/agent/docs-md/filter-bar.md +138 -0
  97. package/agent/docs-md/filter-panel.md +103 -0
  98. package/agent/docs-md/filtering-search.md +79 -0
  99. package/agent/docs-md/follow-ups-starters.md +79 -0
  100. package/agent/docs-md/form-validation.md +95 -0
  101. package/agent/docs-md/generated-columns.md +65 -0
  102. package/agent/docs-md/generated-drafts-files.md +71 -0
  103. package/agent/docs-md/grouped-list.md +89 -0
  104. package/agent/docs-md/guide-cue.md +170 -0
  105. package/agent/docs-md/help-panel.md +103 -0
  106. package/agent/docs-md/help-system.md +74 -0
  107. package/agent/docs-md/iconography.md +49 -0
  108. package/agent/docs-md/inbox.md +108 -0
  109. package/agent/docs-md/inline-editing-validation.md +80 -0
  110. package/agent/docs-md/inline-input-in-answer.md +70 -0
  111. package/agent/docs-md/items-palette.md +116 -0
  112. package/agent/docs-md/keyboard-model.md +47 -0
  113. package/agent/docs-md/keyboard-navigation.md +68 -0
  114. package/agent/docs-md/keyboard-shortcut-hint.md +87 -0
  115. package/agent/docs-md/keyboard-shortcut-sheet.md +90 -0
  116. package/agent/docs-md/labelling-ai-output.md +66 -0
  117. package/agent/docs-md/layout.md +47 -0
  118. package/agent/docs-md/list-table.md +254 -0
  119. package/agent/docs-md/loading-skeleton.md +91 -0
  120. package/agent/docs-md/managing-agents.md +71 -0
  121. package/agent/docs-md/message-composer.md +105 -0
  122. package/agent/docs-md/migrating.md +49 -0
  123. package/agent/docs-md/motion.md +93 -0
  124. package/agent/docs-md/navigation.md +76 -0
  125. package/agent/docs-md/notification-channels.md +65 -0
  126. package/agent/docs-md/notifications-decision.md +100 -0
  127. package/agent/docs-md/onboarding-step.md +114 -0
  128. package/agent/docs-md/order-book.md +108 -0
  129. package/agent/docs-md/page-header.md +96 -0
  130. package/agent/docs-md/pagination.md +88 -0
  131. package/agent/docs-md/pane-layout.md +105 -0
  132. package/agent/docs-md/period-comparison.md +66 -0
  133. package/agent/docs-md/plan-card.md +120 -0
  134. package/agent/docs-md/popover-rules.md +88 -0
  135. package/agent/docs-md/prompt-editor.md +90 -0
  136. package/agent/docs-md/propose-apply.md +65 -0
  137. package/agent/docs-md/provider-logo.md +97 -0
  138. package/agent/docs-md/radio.md +83 -0
  139. package/agent/docs-md/ranked-list.md +102 -0
  140. package/agent/docs-md/reasoning-progress.md +72 -0
  141. package/agent/docs-md/record-detail.md +71 -0
  142. package/agent/docs-md/regenerating-answers.md +70 -0
  143. package/agent/docs-md/run-status-bar.md +94 -0
  144. package/agent/docs-md/saved-tasks-schedules.md +63 -0
  145. package/agent/docs-md/saved-views.md +149 -0
  146. package/agent/docs-md/saving-views.md +68 -0
  147. package/agent/docs-md/scheduler.md +258 -0
  148. package/agent/docs-md/secondary-panels.md +72 -0
  149. package/agent/docs-md/segmented-control.md +88 -0
  150. package/agent/docs-md/selection-bulk-actions.md +70 -0
  151. package/agent/docs-md/separator.md +75 -0
  152. package/agent/docs-md/setup-checklist.md +92 -0
  153. package/agent/docs-md/shape-elevation.md +55 -0
  154. package/agent/docs-md/sheet-grid.md +211 -0
  155. package/agent/docs-md/side-panel.md +134 -0
  156. package/agent/docs-md/sign-in.md +114 -0
  157. package/agent/docs-md/spacing.md +55 -0
  158. package/agent/docs-md/split-button.md +100 -0
  159. package/agent/docs-md/stat-compare.md +82 -0
  160. package/agent/docs-md/static-dashboard.md +75 -0
  161. package/agent/docs-md/status-badge.md +140 -0
  162. package/agent/docs-md/step-card.md +75 -0
  163. package/agent/docs-md/stepper.md +92 -0
  164. package/agent/docs-md/suggested-changes.md +91 -0
  165. package/agent/docs-md/switch.md +78 -0
  166. package/agent/docs-md/table-settings.md +81 -0
  167. package/agent/docs-md/tabs.md +85 -0
  168. package/agent/docs-md/tag-input.md +108 -0
  169. package/agent/docs-md/tag.md +79 -0
  170. package/agent/docs-md/task-builder.md +87 -0
  171. package/agent/docs-md/team-instructions.md +72 -0
  172. package/agent/docs-md/templates-gallery.md +85 -0
  173. package/agent/docs-md/text-input.md +160 -0
  174. package/agent/docs-md/thinking-steps.md +89 -0
  175. package/agent/docs-md/timeline.md +189 -0
  176. package/agent/docs-md/timestamp.md +84 -0
  177. package/agent/docs-md/timestamps.md +70 -0
  178. package/agent/docs-md/toast.md +117 -0
  179. package/agent/docs-md/tooltip.md +90 -0
  180. package/agent/docs-md/typography.md +62 -0
  181. package/agent/docs-md/undo-destructive.md +66 -0
  182. package/agent/docs-md/unsaved-changes.md +71 -0
  183. package/agent/docs-md/usage-meter.md +80 -0
  184. package/agent/docs-md/user-feedback.md +71 -0
  185. package/agent/docs-md/voice-and-tone.md +60 -0
  186. package/agent/docs-md/why-choice.md +93 -0
  187. package/agent/docs-md/workspace-onboarding.md +74 -0
  188. package/agent/docs-md/writing.md +48 -0
  189. package/agent/llms-full.txt +18369 -0
  190. package/agent/llms.txt +198 -0
  191. package/agent/skills/twoux-audit/SKILL.md +49 -0
  192. package/agent/skills/twoux-build/SKILL.md +39 -0
  193. package/agent/skills/twoux-design/SKILL.md +54 -0
  194. package/agent/skills/twoux-write/SKILL.md +32 -0
  195. package/bin/check.mjs +123 -0
  196. package/bin/docs.mjs +24 -0
  197. package/bin/guard.mjs +27 -0
  198. package/bin/init.mjs +113 -0
  199. package/bin/knowledge.mjs +87 -0
  200. package/bin/mcp.mjs +116 -0
  201. package/bin/theme.mjs +168 -0
  202. package/bin/twoux.mjs +25 -0
  203. package/package.json +80 -3
  204. package/registry/jobs.json +1028 -0
  205. package/registry/palette.json +770 -0
  206. package/registry/rules.json +913 -0
  207. package/src/components/agent-review.tsx +364 -0
  208. package/src/components/ai-basics.tsx +212 -0
  209. package/src/components/ai-work.tsx +304 -0
  210. package/src/components/ai.tsx +227 -0
  211. package/src/components/alert-dialog.tsx +203 -0
  212. package/src/components/alert.tsx +86 -0
  213. package/src/components/avatar.tsx +114 -0
  214. package/src/components/badge.tsx +59 -0
  215. package/src/components/board.tsx +397 -0
  216. package/src/components/breadcrumb.tsx +114 -0
  217. package/src/components/button-group.tsx +90 -0
  218. package/src/components/button.tsx +109 -0
  219. package/src/components/calendar.tsx +247 -0
  220. package/src/components/card.tsx +102 -0
  221. package/src/components/chart-map.tsx +336 -0
  222. package/src/components/charts-more.tsx +1331 -0
  223. package/src/components/charts-radial.tsx +328 -0
  224. package/src/components/charts.tsx +390 -0
  225. package/src/components/checkbox.tsx +40 -0
  226. package/src/components/code.tsx +139 -0
  227. package/src/components/collapsible.tsx +37 -0
  228. package/src/components/combobox.tsx +339 -0
  229. package/src/components/command-extras.tsx +11 -0
  230. package/src/components/command.tsx +196 -0
  231. package/src/components/data-grid.tsx +213 -0
  232. package/src/components/data-table.tsx +334 -0
  233. package/src/components/data-views.tsx +740 -0
  234. package/src/components/date-picker.tsx +686 -0
  235. package/src/components/dense-table.tsx +207 -0
  236. package/src/components/dialog.tsx +162 -0
  237. package/src/components/dropdown-menu.tsx +264 -0
  238. package/src/components/empty.tsx +114 -0
  239. package/src/components/feedback-prompt.tsx +80 -0
  240. package/src/components/field.tsx +254 -0
  241. package/src/components/fields.tsx +112 -0
  242. package/src/components/flow.tsx +840 -0
  243. package/src/components/guide-cue.tsx +273 -0
  244. package/src/components/inbox.tsx +113 -0
  245. package/src/components/input-group.tsx +176 -0
  246. package/src/components/input.tsx +26 -0
  247. package/src/components/kbd.tsx +34 -0
  248. package/src/components/label.tsx +27 -0
  249. package/src/components/labels.tsx +176 -0
  250. package/src/components/motion.tsx +136 -0
  251. package/src/components/onboarding.tsx +454 -0
  252. package/src/components/order-book.tsx +185 -0
  253. package/src/components/pane-layout.tsx +417 -0
  254. package/src/components/popover.tsx +96 -0
  255. package/src/components/provider-logos.data.ts +49 -0
  256. package/src/components/radio-group.tsx +52 -0
  257. package/src/components/scheduler.tsx +997 -0
  258. package/src/components/segmented-control.tsx +28 -0
  259. package/src/components/select.tsx +195 -0
  260. package/src/components/separator.tsx +31 -0
  261. package/src/components/sheet.tsx +148 -0
  262. package/src/components/shell.tsx +426 -0
  263. package/src/components/shortcut-sheet.tsx +83 -0
  264. package/src/components/skeleton.tsx +18 -0
  265. package/src/components/sonner.tsx +107 -0
  266. package/src/components/spinner.tsx +13 -0
  267. package/src/components/split-button.tsx +58 -0
  268. package/src/components/stepper.tsx +78 -0
  269. package/src/components/switch.tsx +43 -0
  270. package/src/components/table.tsx +126 -0
  271. package/src/components/tabs.tsx +118 -0
  272. package/src/components/tag-input.tsx +253 -0
  273. package/src/components/textarea.tsx +21 -0
  274. package/src/components/theme.tsx +80 -0
  275. package/src/components/timeline.tsx +408 -0
  276. package/src/components/toggle-group.tsx +87 -0
  277. package/src/components/toggle.tsx +54 -0
  278. package/src/components/tooltip.tsx +65 -0
  279. package/src/components/workspace-actions.tsx +294 -0
  280. package/src/components/workspace-import.tsx +465 -0
  281. package/src/components/workspace.tsx +1884 -0
  282. package/src/index.ts +80 -0
  283. package/src/lib/active-indicator.tsx +53 -0
  284. package/src/lib/chart-kit.tsx +72 -0
  285. package/src/lib/cn.ts +6 -0
  286. package/src/lib/shortcuts.tsx +61 -0
  287. package/src/lib/workspace.tsx +10 -0
  288. package/src/shadcn/alert-dialog.tsx +196 -0
  289. package/src/shadcn/alert.tsx +65 -0
  290. package/src/shadcn/avatar.tsx +108 -0
  291. package/src/shadcn/badge.tsx +47 -0
  292. package/src/shadcn/breadcrumb.tsx +108 -0
  293. package/src/shadcn/button-group.tsx +83 -0
  294. package/src/shadcn/button.tsx +63 -0
  295. package/src/shadcn/calendar.tsx +220 -0
  296. package/src/shadcn/card.tsx +91 -0
  297. package/src/shadcn/checkbox.tsx +31 -0
  298. package/src/shadcn/collapsible.tsx +33 -0
  299. package/src/shadcn/command.tsx +184 -0
  300. package/src/shadcn/dialog.tsx +158 -0
  301. package/src/shadcn/dropdown-menu.tsx +256 -0
  302. package/src/shadcn/empty.tsx +103 -0
  303. package/src/shadcn/field.tsx +248 -0
  304. package/src/shadcn/input-group.tsx +170 -0
  305. package/src/shadcn/input.tsx +20 -0
  306. package/src/shadcn/kbd.tsx +28 -0
  307. package/src/shadcn/label.tsx +23 -0
  308. package/src/shadcn/popover.tsx +88 -0
  309. package/src/shadcn/radio-group.tsx +44 -0
  310. package/src/shadcn/select.tsx +189 -0
  311. package/src/shadcn/separator.tsx +27 -0
  312. package/src/shadcn/sheet.tsx +142 -0
  313. package/src/shadcn/skeleton.tsx +13 -0
  314. package/src/shadcn/sonner.tsx +40 -0
  315. package/src/shadcn/switch.tsx +34 -0
  316. package/src/shadcn/table.tsx +115 -0
  317. package/src/shadcn/tabs.tsx +90 -0
  318. package/src/shadcn/textarea.tsx +17 -0
  319. package/src/shadcn/toggle-group.tsx +83 -0
  320. package/src/shadcn/toggle.tsx +46 -0
  321. package/src/shadcn/tooltip.tsx +56 -0
  322. package/src/styles/palette.css +127 -0
  323. package/src/styles/theme.css +35 -0
  324. package/src/styles/twoux.css +236 -0
  325. package/theme.json +14 -0
package/src/index.ts ADDED
@@ -0,0 +1,80 @@
1
+ export { cn } from "./lib/cn";
2
+ export { ShortcutsProvider, useShortcut, useShortcutsEnabled } from "./lib/shortcuts";
3
+ // Generated from shadcn/ui (scripts/tailor-shadcn.mjs). See SHADCN-DIFF.md for every TwoUX change.
4
+ export * from "./components/alert";
5
+ export * from "./components/alert-dialog";
6
+ export * from "./components/avatar";
7
+ export * from "./components/badge";
8
+ export * from "./components/breadcrumb";
9
+ export * from "./components/button";
10
+ export * from "./components/button-group";
11
+ export * from "./components/card";
12
+ export * from "./components/checkbox";
13
+ export * from "./components/collapsible";
14
+ export * from "./components/command";
15
+ export * from "./components/dialog";
16
+ export * from "./components/dropdown-menu";
17
+ export * from "./components/empty";
18
+ export * from "./components/field";
19
+ export * from "./components/input";
20
+ export * from "./components/input-group";
21
+ export * from "./components/kbd";
22
+ export * from "./components/label";
23
+ export * from "./components/popover";
24
+ export * from "./components/select";
25
+ export * from "./components/radio-group";
26
+ export * from "./components/separator";
27
+ export * from "./components/sheet";
28
+ export * from "./components/skeleton";
29
+ export * from "./components/sonner";
30
+ export { toast } from "sonner";
31
+ export * from "./components/switch";
32
+ export * from "./components/table";
33
+ export * from "./components/tabs";
34
+ export * from "./components/textarea";
35
+ export * from "./components/toggle";
36
+ export * from "./components/toggle-group";
37
+ export * from "./components/tooltip";
38
+ // TwoUX compositions built on the above
39
+ export * from "./components/ai";
40
+ export * from "./components/ai-basics";
41
+ export * from "./components/ai-work";
42
+ export * from "./components/board";
43
+ export * from "./components/charts";
44
+ export * from "./components/code";
45
+ export * from "./components/command-extras";
46
+ export * from "./components/data-grid";
47
+ export * from "./components/data-table";
48
+ export * from "./components/dense-table";
49
+ export * from "./components/data-views";
50
+ export * from "./components/feedback-prompt";
51
+ export * from "./components/fields";
52
+ export * from "./components/inbox";
53
+ export * from "./components/labels";
54
+ export * from "./components/segmented-control";
55
+ export * from "./components/split-button";
56
+ export * from "./components/shell";
57
+ export * from "./components/shortcut-sheet";
58
+ export * from "./components/stepper";
59
+ export * from "./components/spinner";
60
+ export * from "./components/theme";
61
+ export * from "./components/motion";
62
+ export * from "./components/agent-review";
63
+ export * from "./components/onboarding";
64
+ export * from "./components/charts-more";
65
+ export * from "./components/workspace";
66
+ export * from "./components/workspace-import";
67
+ export * from "./components/workspace-actions";
68
+ export * from "./components/timeline";
69
+ export * from "./components/scheduler";
70
+ export * from "./components/charts-radial";
71
+ export * from "./components/chart-map";
72
+ export * from "./components/guide-cue";
73
+ export * from "./components/calendar";
74
+ export * from "./components/date-picker";
75
+ export * from "./components/combobox";
76
+ export * from "./components/tag-input";
77
+ export * from "./components/order-book";
78
+ export * from "./components/pane-layout";
79
+ export * from "./components/flow";
80
+ export { useWorkspace } from "./lib/workspace";
@@ -0,0 +1,53 @@
1
+ /**
2
+ * TwoUX: one indicator that slides to the active item (tabs underline, segment pill),
3
+ * instead of each item painting its own and the highlight jumping.
4
+ * Place it as the first child of a `relative` list. It follows the child matching `selector`.
5
+ */
6
+ import * as React from "react"
7
+ import { cn } from "./cn"
8
+
9
+ type Box = { left: number; top?: number; width: number; height?: number }
10
+
11
+ export function ActiveIndicator({ selector, fit = "box", className }: { selector: string; fit?: "x" | "box"; className?: string }) {
12
+ const ref = React.useRef<HTMLSpanElement>(null)
13
+ const [box, setBox] = React.useState<Box | null>(null)
14
+ const [animate, setAnimate] = React.useState(false)
15
+
16
+ React.useLayoutEffect(() => {
17
+ const list = ref.current?.parentElement
18
+ if (!list) return
19
+ const measure = () => {
20
+ const el = list.querySelector<HTMLElement>(`:scope > ${selector}`)
21
+ if (!el || !el.offsetWidth) return setBox(null)
22
+ setBox(fit === "x" ? { left: el.offsetLeft, width: el.offsetWidth } : { left: el.offsetLeft, top: el.offsetTop, width: el.offsetWidth, height: el.offsetHeight })
23
+ }
24
+ measure()
25
+ const mo = new MutationObserver(measure)
26
+ mo.observe(list, { subtree: true, childList: true, attributes: true, attributeFilter: ["data-state"] })
27
+ const ro = new ResizeObserver(measure)
28
+ ro.observe(list)
29
+ for (const c of Array.from(list.children)) ro.observe(c)
30
+ // First position is set without motion; only later changes slide.
31
+ const raf = requestAnimationFrame(() => setAnimate(true))
32
+ return () => {
33
+ mo.disconnect()
34
+ ro.disconnect()
35
+ cancelAnimationFrame(raf)
36
+ }
37
+ }, [selector, fit])
38
+
39
+ return (
40
+ <span
41
+ ref={ref}
42
+ aria-hidden="true"
43
+ data-slot="active-indicator"
44
+ className={cn(
45
+ "pointer-events-none absolute",
46
+ animate && "transition-[left,top,width,height] duration-(--duration-base) ease-(--ease-out)",
47
+ !box && "invisible",
48
+ className
49
+ )}
50
+ style={box ?? undefined}
51
+ />
52
+ )
53
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Shared chart internals for TwoUX's own-SVG charts (charts-more, charts-radial, chart-map, timeline).
3
+ * Not exported from the package index: components use these, apps never do.
4
+ */
5
+ import * as React from "react"
6
+
7
+ export const fmt = (n: number) => n.toLocaleString("en-GB")
8
+ export const MINUS = "−"
9
+ export const pct = (part: number, whole: number, digits = 1) => `${whole ? ((part / whole) * 100).toFixed(digits) : "0"}%`
10
+ /** Neutral mark for totals and Other: never a series or status colour. */
11
+ export const NEUTRAL = "var(--color-ink-3)"
12
+ /** Rough width of 12px text, to decide whether a label fits before drawing it. */
13
+ export const textW = (s: string) => s.length * 6.6
14
+ export const svgCls = "block overflow-visible rounded-xs focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"
15
+ export const halo = "stroke-surface [paint-order:stroke]"
16
+
17
+ export function useWidth<T extends HTMLElement>() {
18
+ const ref = React.useRef<T>(null)
19
+ const [w, setW] = React.useState(0)
20
+ React.useLayoutEffect(() => {
21
+ const el = ref.current
22
+ if (!el) return
23
+ const ro = new ResizeObserver(([e]) => setW(Math.round(e.contentRect.width)))
24
+ ro.observe(el)
25
+ setW(el.clientWidth)
26
+ return () => ro.disconnect()
27
+ }, [])
28
+ return [ref, w] as const
29
+ }
30
+
31
+ /** One active mark, moved with the arrow keys, Home and End; Esc clears. */
32
+ export function useActive(n: number) {
33
+ const [active, setActive] = React.useState<number | null>(null)
34
+ const onKey = (e: React.KeyboardEvent) => {
35
+ if (!n) return
36
+ if (e.key === "ArrowRight" || e.key === "ArrowDown") { e.preventDefault(); setActive((a) => (a === null ? 0 : Math.min(n - 1, a + 1))) }
37
+ else if (e.key === "ArrowLeft" || e.key === "ArrowUp") { e.preventDefault(); setActive((a) => (a === null ? n - 1 : Math.max(0, a - 1))) }
38
+ else if (e.key === "Home") { e.preventDefault(); setActive(0) }
39
+ else if (e.key === "End") { e.preventDefault(); setActive(n - 1) }
40
+ else if (e.key === "Escape") setActive(null)
41
+ }
42
+ return { active, setActive, onKey }
43
+ }
44
+
45
+ export function niceStep(raw: number) {
46
+ if (raw <= 0) return 1
47
+ const mag = 10 ** Math.floor(Math.log10(raw))
48
+ return [1, 2, 2.5, 5, 10].map((m) => m * mag).find((s) => s >= raw) ?? raw
49
+ }
50
+
51
+ export type TipRow = { label: string; value: string; color?: string }
52
+ /** Tooltip: value strong, label secondary, a line key for the series. Never the only way to a value. */
53
+ export function Tip({ x, y, width, title, rows }: { x: number; y: number; width: number; title: string; rows: TipRow[] }) {
54
+ const left = x + 12 + 184 > width ? Math.max(0, x - 12 - 184) : x + 12
55
+ return (
56
+ <div
57
+ role="status"
58
+ className="pointer-events-none absolute z-10 grid min-w-44 gap-1 rounded-md border border-line bg-surface px-2.5 py-2 text-sm shadow-pop left-(--x) top-(--y)"
59
+ style={{ "--x": `${left}px`, "--y": `${Math.max(0, y)}px` } as React.CSSProperties}
60
+ >
61
+ <span className="font-medium text-ink">{title}</span>
62
+ {rows.map((r) => (
63
+ <span key={r.label} className="flex items-center gap-2">
64
+ {r.color && <span aria-hidden="true" className="h-0.5 w-3 shrink-0 rounded-full bg-(--c)" style={{ "--c": r.color } as React.CSSProperties} />}
65
+ <span className="text-ink-2">{r.label}</span>
66
+ <span className="tabular ml-auto pl-3 font-medium text-ink">{r.value}</span>
67
+ </span>
68
+ ))}
69
+ </div>
70
+ )
71
+ }
72
+
package/src/lib/cn.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * TwoUX: keyboard shortcuts that match what the button shows.
3
+ * <ShortcutsProvider enabled={setting}> wraps the app once. Button's `shortcut` then both shows the key
4
+ * and presses the button. Single-key shortcuts obey `enabled`, so people can turn them off (WCAG 2.1.4);
5
+ * shortcuts with ⌘/Ctrl/⌥ always work.
6
+ * A shortcut fires only when its button is visible and enabled, focus is not in a text field,
7
+ * and no dialog is open (unless the button is inside that dialog).
8
+ */
9
+ import * as React from "react"
10
+
11
+ type Ctx = { enabled: boolean }
12
+ const ShortcutsContext = React.createContext<Ctx | null>(null)
13
+
14
+ export function ShortcutsProvider({ enabled = true, children }: { enabled?: boolean; children: React.ReactNode }) {
15
+ const value = React.useMemo(() => ({ enabled }), [enabled])
16
+ return <ShortcutsContext.Provider value={value}>{children}</ShortcutsContext.Provider>
17
+ }
18
+
19
+ /** Parses "C", "⇧C", "⌘↵", "⌘K", "Esc" into a matcher. */
20
+ function parse(shortcut: string) {
21
+ const mods = { meta: false, shift: false, alt: false }
22
+ let key = shortcut
23
+ for (;;) {
24
+ if (key.startsWith("⌘")) { mods.meta = true; key = key.slice(1) }
25
+ else if (key.startsWith("⇧")) { mods.shift = true; key = key.slice(1) }
26
+ else if (key.startsWith("⌥")) { mods.alt = true; key = key.slice(1) }
27
+ else break
28
+ }
29
+ const named: Record<string, string> = { "↵": "Enter", Esc: "Escape", "⌫": "Backspace", "←": "ArrowLeft", "→": "ArrowRight", "↑": "ArrowUp", "↓": "ArrowDown" }
30
+ return { ...mods, key: (named[key] ?? key).toLowerCase(), single: !mods.meta && !mods.alt }
31
+ }
32
+
33
+ const isTyping = (el: Element | null) => !!el?.closest("input:not([type=checkbox]):not([type=radio]), textarea, select, [contenteditable=true]")
34
+
35
+ /** Presses `ref` when `shortcut` is typed. Used by Button; usable for any element. */
36
+ /** Whether single-key shortcuts are on. Components with their own single-key keys (zoom, etc.) check this. */
37
+ export function useShortcutsEnabled() {
38
+ return React.useContext(ShortcutsContext)?.enabled ?? true
39
+ }
40
+
41
+ export function useShortcut(shortcut: string | undefined, ref: React.RefObject<HTMLElement | null>, disabled = false) {
42
+ const ctx = React.useContext(ShortcutsContext)
43
+ React.useEffect(() => {
44
+ if (!shortcut || !ctx || disabled) return
45
+ const want = parse(shortcut)
46
+ if (want.single && !ctx.enabled) return
47
+ const onKey = (e: KeyboardEvent) => {
48
+ if (e.defaultPrevented || e.repeat) return
49
+ if (e.key.toLowerCase() !== want.key || (e.metaKey || e.ctrlKey) !== want.meta || e.shiftKey !== want.shift || e.altKey !== want.alt) return
50
+ if (want.single && isTyping(document.activeElement)) return
51
+ const el = ref.current
52
+ if (!el || !el.isConnected || el.offsetParent === null || el.closest("[inert]") || (el as HTMLButtonElement).disabled || el.getAttribute("aria-disabled") === "true") return
53
+ const dialog = document.querySelector("[role=dialog][data-state=open], [role=alertdialog][data-state=open]")
54
+ if (dialog && !dialog.contains(el)) return
55
+ e.preventDefault()
56
+ el.click()
57
+ }
58
+ document.addEventListener("keydown", onKey)
59
+ return () => document.removeEventListener("keydown", onKey)
60
+ }, [shortcut, ctx, disabled, ref])
61
+ }
@@ -0,0 +1,10 @@
1
+ import * as React from "react"
2
+
3
+ /**
4
+ * Workspace density (see Content density): set by AppShell frame="flush" for tool pages that keep several regions in view.
5
+ * Components read it to drop their own chrome and gaps, never their sizes: ChartFrame goes flush, PageHeader tightens
6
+ * its padding. Text, controls, rows and targets stay the same.
7
+ */
8
+ export const WorkspaceContext = React.createContext(false)
9
+ /** True inside AppShell frame="flush". */
10
+ export const useWorkspace = () => React.useContext(WorkspaceContext)
@@ -0,0 +1,196 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "cn"
5
+ import { AlertDialog as AlertDialogPrimitive } from "radix-ui"
6
+
7
+ import { Button } from "@/registry/new-york-v4/ui/button"
8
+
9
+ function AlertDialog({
10
+ ...props
11
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
12
+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
13
+ }
14
+
15
+ function AlertDialogTrigger({
16
+ ...props
17
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
18
+ return (
19
+ <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
20
+ )
21
+ }
22
+
23
+ function AlertDialogPortal({
24
+ ...props
25
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
26
+ return (
27
+ <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
28
+ )
29
+ }
30
+
31
+ function AlertDialogOverlay({
32
+ className,
33
+ ...props
34
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
35
+ return (
36
+ <AlertDialogPrimitive.Overlay
37
+ data-slot="alert-dialog-overlay"
38
+ className={cn(
39
+ "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
40
+ className
41
+ )}
42
+ {...props}
43
+ />
44
+ )
45
+ }
46
+
47
+ function AlertDialogContent({
48
+ className,
49
+ size = "default",
50
+ ...props
51
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
52
+ size?: "default" | "sm"
53
+ }) {
54
+ return (
55
+ <AlertDialogPortal>
56
+ <AlertDialogOverlay />
57
+ <AlertDialogPrimitive.Content
58
+ data-slot="alert-dialog-content"
59
+ data-size={size}
60
+ className={cn(
61
+ "group/alert-dialog-content fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-[size=sm]:max-w-xs data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[size=default]:sm:max-w-lg",
62
+ className
63
+ )}
64
+ {...props}
65
+ />
66
+ </AlertDialogPortal>
67
+ )
68
+ }
69
+
70
+ function AlertDialogHeader({
71
+ className,
72
+ ...props
73
+ }: React.ComponentProps<"div">) {
74
+ return (
75
+ <div
76
+ data-slot="alert-dialog-header"
77
+ className={cn(
78
+ "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
79
+ className
80
+ )}
81
+ {...props}
82
+ />
83
+ )
84
+ }
85
+
86
+ function AlertDialogFooter({
87
+ className,
88
+ ...props
89
+ }: React.ComponentProps<"div">) {
90
+ return (
91
+ <div
92
+ data-slot="alert-dialog-footer"
93
+ className={cn(
94
+ "flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
95
+ className
96
+ )}
97
+ {...props}
98
+ />
99
+ )
100
+ }
101
+
102
+ function AlertDialogTitle({
103
+ className,
104
+ ...props
105
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
106
+ return (
107
+ <AlertDialogPrimitive.Title
108
+ data-slot="alert-dialog-title"
109
+ className={cn(
110
+ "text-lg font-semibold sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
111
+ className
112
+ )}
113
+ {...props}
114
+ />
115
+ )
116
+ }
117
+
118
+ function AlertDialogDescription({
119
+ className,
120
+ ...props
121
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
122
+ return (
123
+ <AlertDialogPrimitive.Description
124
+ data-slot="alert-dialog-description"
125
+ className={cn("text-sm text-muted-foreground", className)}
126
+ {...props}
127
+ />
128
+ )
129
+ }
130
+
131
+ function AlertDialogMedia({
132
+ className,
133
+ ...props
134
+ }: React.ComponentProps<"div">) {
135
+ return (
136
+ <div
137
+ data-slot="alert-dialog-media"
138
+ className={cn(
139
+ "mb-2 inline-flex size-16 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8",
140
+ className
141
+ )}
142
+ {...props}
143
+ />
144
+ )
145
+ }
146
+
147
+ function AlertDialogAction({
148
+ className,
149
+ variant = "default",
150
+ size = "default",
151
+ ...props
152
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
153
+ Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
154
+ return (
155
+ <Button variant={variant} size={size} asChild>
156
+ <AlertDialogPrimitive.Action
157
+ data-slot="alert-dialog-action"
158
+ className={cn(className)}
159
+ {...props}
160
+ />
161
+ </Button>
162
+ )
163
+ }
164
+
165
+ function AlertDialogCancel({
166
+ className,
167
+ variant = "outline",
168
+ size = "default",
169
+ ...props
170
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
171
+ Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
172
+ return (
173
+ <Button variant={variant} size={size} asChild>
174
+ <AlertDialogPrimitive.Cancel
175
+ data-slot="alert-dialog-cancel"
176
+ className={cn(className)}
177
+ {...props}
178
+ />
179
+ </Button>
180
+ )
181
+ }
182
+
183
+ export {
184
+ AlertDialog,
185
+ AlertDialogAction,
186
+ AlertDialogCancel,
187
+ AlertDialogContent,
188
+ AlertDialogDescription,
189
+ AlertDialogFooter,
190
+ AlertDialogHeader,
191
+ AlertDialogMedia,
192
+ AlertDialogOverlay,
193
+ AlertDialogPortal,
194
+ AlertDialogTitle,
195
+ AlertDialogTrigger,
196
+ }
@@ -0,0 +1,65 @@
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+ import { cn } from "cn"
4
+
5
+ const alertVariants = cva(
6
+ "relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
7
+ {
8
+ variants: {
9
+ variant: {
10
+ default: "bg-card text-card-foreground",
11
+ destructive:
12
+ "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 [&>svg]:text-current",
13
+ },
14
+ },
15
+ defaultVariants: {
16
+ variant: "default",
17
+ },
18
+ }
19
+ )
20
+
21
+ function Alert({
22
+ className,
23
+ variant,
24
+ ...props
25
+ }: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
26
+ return (
27
+ <div
28
+ data-slot="alert"
29
+ role="alert"
30
+ className={cn(alertVariants({ variant }), className)}
31
+ {...props}
32
+ />
33
+ )
34
+ }
35
+
36
+ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
37
+ return (
38
+ <div
39
+ data-slot="alert-title"
40
+ className={cn(
41
+ "col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
42
+ className
43
+ )}
44
+ {...props}
45
+ />
46
+ )
47
+ }
48
+
49
+ function AlertDescription({
50
+ className,
51
+ ...props
52
+ }: React.ComponentProps<"div">) {
53
+ return (
54
+ <div
55
+ data-slot="alert-description"
56
+ className={cn(
57
+ "col-start-2 grid justify-items-start gap-1 text-sm text-muted-foreground [&_p]:leading-relaxed",
58
+ className
59
+ )}
60
+ {...props}
61
+ />
62
+ )
63
+ }
64
+
65
+ export { Alert, AlertTitle, AlertDescription }
@@ -0,0 +1,108 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "cn"
5
+ import { Avatar as AvatarPrimitive } from "radix-ui"
6
+
7
+ function Avatar({
8
+ className,
9
+ size = "default",
10
+ ...props
11
+ }: React.ComponentProps<typeof AvatarPrimitive.Root> & {
12
+ size?: "default" | "sm" | "lg"
13
+ }) {
14
+ return (
15
+ <AvatarPrimitive.Root
16
+ data-slot="avatar"
17
+ data-size={size}
18
+ className={cn(
19
+ "group/avatar relative flex size-8 shrink-0 overflow-hidden rounded-full select-none data-[size=lg]:size-10 data-[size=sm]:size-6",
20
+ className
21
+ )}
22
+ {...props}
23
+ />
24
+ )
25
+ }
26
+
27
+ function AvatarImage({
28
+ className,
29
+ ...props
30
+ }: React.ComponentProps<typeof AvatarPrimitive.Image>) {
31
+ return (
32
+ <AvatarPrimitive.Image
33
+ data-slot="avatar-image"
34
+ className={cn("aspect-square size-full", className)}
35
+ {...props}
36
+ />
37
+ )
38
+ }
39
+
40
+ function AvatarFallback({
41
+ className,
42
+ ...props
43
+ }: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
44
+ return (
45
+ <AvatarPrimitive.Fallback
46
+ data-slot="avatar-fallback"
47
+ className={cn(
48
+ "flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
49
+ className
50
+ )}
51
+ {...props}
52
+ />
53
+ )
54
+ }
55
+
56
+ function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
57
+ return (
58
+ <span
59
+ data-slot="avatar-badge"
60
+ className={cn(
61
+ "absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground ring-2 ring-background select-none",
62
+ "group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
63
+ "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
64
+ "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
65
+ className
66
+ )}
67
+ {...props}
68
+ />
69
+ )
70
+ }
71
+
72
+ function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
73
+ return (
74
+ <div
75
+ data-slot="avatar-group"
76
+ className={cn(
77
+ "group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
78
+ className
79
+ )}
80
+ {...props}
81
+ />
82
+ )
83
+ }
84
+
85
+ function AvatarGroupCount({
86
+ className,
87
+ ...props
88
+ }: React.ComponentProps<"div">) {
89
+ return (
90
+ <div
91
+ data-slot="avatar-group-count"
92
+ className={cn(
93
+ "relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
94
+ className
95
+ )}
96
+ {...props}
97
+ />
98
+ )
99
+ }
100
+
101
+ export {
102
+ Avatar,
103
+ AvatarImage,
104
+ AvatarFallback,
105
+ AvatarBadge,
106
+ AvatarGroup,
107
+ AvatarGroupCount,
108
+ }