@nextop-os/ui-system 0.0.16 → 0.0.18

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 (41) hide show
  1. package/AGENTS.md +106 -0
  2. package/README.md +15 -10
  3. package/agent/install-skill.mjs +65 -5
  4. package/agent/nextop-ui-system/SKILL.md +145 -69
  5. package/agent/nextop-ui-system/references/extract-base-component.md +87 -0
  6. package/agent/nextop-ui-system/references/maintain-inventory.md +45 -0
  7. package/agent/nextop-ui-system/references/promote-business-component.md +238 -0
  8. package/agent/nextop-ui-system/references/use-existing-component.md +37 -0
  9. package/agent/nextop-ui-system/scripts/create-business-preview.mjs +658 -0
  10. package/dist/chunk-GE5YVRTV.js +859 -0
  11. package/dist/chunk-GE5YVRTV.js.map +1 -0
  12. package/dist/chunk-KJQ366TA.js +70 -0
  13. package/dist/chunk-KJQ366TA.js.map +1 -0
  14. package/dist/chunk-LVHEV755.js +2553 -0
  15. package/dist/chunk-LVHEV755.js.map +1 -0
  16. package/dist/components/index.d.ts +162 -11
  17. package/dist/components/index.js +62 -2
  18. package/dist/date-format.d.ts +6 -0
  19. package/dist/date-format.js +11 -0
  20. package/dist/date-format.js.map +1 -0
  21. package/dist/dev-vite.js +12 -5
  22. package/dist/dev-vite.js.map +1 -1
  23. package/dist/icons/index.d.ts +90 -46
  24. package/dist/icons/index.js +93 -11
  25. package/dist/index.d.ts +3 -2
  26. package/dist/index.js +161 -11
  27. package/dist/metadata/components.json +1331 -263
  28. package/dist/metadata/components.schema.json +4 -0
  29. package/dist/metadata/index.d.ts +3 -1
  30. package/dist/metadata/index.js +1331 -263
  31. package/dist/metadata/index.js.map +1 -1
  32. package/dist/styles/base.css +85 -0
  33. package/dist/styles/index.css +1 -0
  34. package/dist/styles/semantic.css +8 -0
  35. package/dist/styles/theme.css +94 -1
  36. package/package.json +11 -2
  37. package/ui-system.md +640 -0
  38. package/dist/chunk-5COFORA5.js +0 -1159
  39. package/dist/chunk-5COFORA5.js.map +0 -1
  40. package/dist/chunk-TT7B6HKG.js +0 -205
  41. package/dist/chunk-TT7B6HKG.js.map +0 -1
package/ui-system.md ADDED
@@ -0,0 +1,640 @@
1
+ # UI System
2
+
3
+ This document defines the responsibility boundary for the shared visual-system package under `packages/ui/*`.
4
+
5
+ ## Purpose
6
+
7
+ The shared UI system package exists to hold product-facing visual foundations that are reused across renderer surfaces.
8
+
9
+ The current package is:
10
+
11
+ - `packages/ui/system`
12
+
13
+ It is organized into two public component layers:
14
+
15
+ - `base`
16
+ Basic visual primitives and foundations such as tokens, icons, Button,
17
+ Input, Dialog, Select, Card, Badge, and Toast.
18
+ - `business`
19
+ Multi-end reusable Nextop business display components. These components may
20
+ expose domain display props such as workspace, file, task, or agent state, and
21
+ should compose `base` primitives instead of recreating them.
22
+
23
+ The package owns:
24
+
25
+ - design tokens
26
+ - shared theme styles
27
+ - icon exports
28
+ - presentation primitives
29
+ - reusable business display components
30
+
31
+ It does not own:
32
+
33
+ - business logic or host-side side effects
34
+ - app-specific workflows
35
+ - daemon, Electron, router, or host adapter calls
36
+
37
+ ## Current Package Role
38
+
39
+ `@nextop-os/ui-system` is the single source of truth for:
40
+
41
+ - CSS token definitions
42
+ - structural shared workbench CSS in `packages/workbench/surface/src/styles/workbench.css`
43
+ - structural shared terminal CSS in `packages/workspace/terminal/src/styles/terminal.css`
44
+ - Tailwind-facing semantic theme variables
45
+ - shared SVG and icon APIs
46
+ - shadcn-derived React primitives
47
+
48
+ Desktop renderer code should consume this package instead of defining a second token or primitive layer in `apps/desktop`.
49
+
50
+ The visual language that this package should serve is defined in [Desktop Visual Language](../../../docs/conventions/desktop-visual-language.md).
51
+
52
+ ## Public API
53
+
54
+ `@nextop-os/ui-system` should expose a small, stable surface:
55
+
56
+ - `@nextop-os/ui-system`
57
+ Root runtime entry for shared primitives, icon components, and the small set of utility exports that primitives depend on
58
+ - `@nextop-os/ui-system/styles.css`
59
+ Shared stylesheet entry loaded by renderer shells
60
+ - `@nextop-os/ui-system/components`
61
+ Stable component barrel for tooling and rare category-focused imports
62
+ - `@nextop-os/ui-system/icons`
63
+ Stable icon barrel for tooling and rare category-focused imports
64
+ - `@nextop-os/ui-system/utils`
65
+ Stable utility entry for shadcn CLI integration and primitive support code
66
+ - `@nextop-os/ui-system/metadata`
67
+ Tooling entry for component metadata used by storyboard, dev server, and
68
+ agent skills
69
+ - `@nextop-os/ui-system/dev-vite`
70
+ Development-tooling entry for external Vite apps that opt into local source
71
+ sync; this is not a runtime component API
72
+
73
+ Default consumption rules:
74
+
75
+ - application code should prefer importing primitives and icons from `@nextop-os/ui-system`
76
+ - renderer entrypoints should import `@nextop-os/ui-system/styles.css` once
77
+ - shadcn monorepo aliases may target `@nextop-os/ui-system/components` and `@nextop-os/ui-system/utils`
78
+ - application runtime code must not import `@nextop-os/ui-system/dev-vite`
79
+ - consumers must not deep import `@nextop-os/ui-system/src/*` or component file
80
+ paths; use the root package and stable subpaths instead
81
+
82
+ ## Automated Enforcement
83
+
84
+ The repository enforces the shared UI boundary with:
85
+
86
+ - `pnpm check:ui-boundaries`
87
+ - `pnpm check:ui-boundaries:staged`
88
+
89
+ Use the script output as the source of truth for the mechanically-checkable rules.
90
+
91
+ - `check:ui-boundaries:staged` is the fast local hook variant for staged files
92
+ - `check:ui-boundaries` is the full-repository variant for `pre-push` and CI
93
+ - `@nextop-os/workbench-surface/styles.css` and `@nextop-os/workspace-terminal/styles.css` are the only non-UI-system package stylesheets allowed by the boundary check; they must remain structural and variable-driven, not product-branded
94
+
95
+ Keep this check aligned with the package exports and file boundary rules:
96
+
97
+ - if a new stable public subpath is intentionally added, update both `packages/ui/system/package.json` and the import-check script
98
+ - do not "fix" the script by broadening the allowed list unless the package boundary itself is intentionally changing
99
+
100
+ ## Metadata Rules
101
+
102
+ Every public UI-system metadata entry must have a stable `id`.
103
+
104
+ - `id` is the user-facing tooling identifier for storyboard anchors, dev server
105
+ metadata, and agent skill lookups
106
+ - `id` must be globally unique and use readable kebab-case, for example
107
+ `button`, `dialog-content`, `button-variants`, or `styles-css`
108
+ - `id` should not be derived at runtime by consumers; read it from
109
+ `@nextop-os/ui-system/metadata`
110
+ - `name` and `export` remain the TypeScript API identity, while `id` is the
111
+ stable human-readable inventory identity
112
+ - `layer` must be either `base` or `business`
113
+ - `business` components may use business display nouns in their props, but
114
+ should still receive data, labels, status, and callbacks from the consuming
115
+ host
116
+
117
+ ## Component Promotion Protocol
118
+
119
+ Use this protocol when moving UI from an application or business package into
120
+ `@nextop-os/ui-system`.
121
+
122
+ The UI system follows a shadcn-like self-owned source model:
123
+
124
+ - Radix or equivalent headless primitives provide accessible interaction
125
+ behavior where a proven primitive exists
126
+ - CVA-style variant definitions keep component variants explicit and typed
127
+ - Tailwind classes consume shared semantic tokens instead of local palettes
128
+ - Nextop owns the checked-in source, public exports, metadata, storyboard
129
+ examples, and boundary validation
130
+
131
+ Before extracting a component, decide whether the target is `base`, `business`,
132
+ or not suitable for UI-system promotion.
133
+
134
+ Promote to `base` when the component is a foundation primitive:
135
+
136
+ - it has no product workflow or domain noun in its public contract
137
+ - props describe presentation, interaction, accessibility, variants, refs,
138
+ slots, class names, or children
139
+ - the component can be reused by unrelated surfaces without explaining a
140
+ business concept
141
+ - a known shadcn or Radix primitive can provide the starting point, or the
142
+ exception is documented in review
143
+
144
+ Promote to `business` when the component is a reusable business display unit:
145
+
146
+ - it represents a cross-surface Nextop concept such as workspace, file, task,
147
+ agent, run, project, or account display state
148
+ - it receives all business data, labels, statuses, permissions, and callbacks
149
+ from the host through props
150
+ - it receives user-visible copy through props, labels, or children instead of
151
+ introducing new hardcoded product strings inside the shared component body
152
+ - it composes `base` components for buttons, fields, dialogs, cards, icons, and
153
+ overlays instead of rebuilding those primitives locally
154
+ - it can be rendered in storyboard with controlled sample data and no daemon,
155
+ router, store, or host adapter
156
+
157
+ Do not promote a component when it owns:
158
+
159
+ - daemon, Electron, filesystem, router, or host-adapter calls
160
+ - data fetching, cache mutation, persistence, or polling
161
+ - global app store ownership
162
+ - workspace registration, navigation, onboarding, or other app workflow
163
+ orchestration
164
+ - product copy that cannot be supplied by props or children
165
+
166
+ For every promoted component, define this contract before editing code:
167
+
168
+ - component `id` and `layer`
169
+ - source usage being replaced
170
+ - intended reuse surfaces
171
+ - public props and callbacks
172
+ - host-owned state and side effects that remain outside the component
173
+ - stable export path
174
+ - metadata entry
175
+ - storyboard states and examples
176
+ - validation commands
177
+
178
+ The bundled `packages/ui/system/agent/nextop-ui-system/SKILL.md` skill is the
179
+ standard prompt-level workflow for this judgment-heavy promotion step. Boundary
180
+ scripts are still required, but they only catch mechanical violations.
181
+
182
+ ## API Shape And Composition
183
+
184
+ The public API must be derived from the source state matrix and intended reuse
185
+ surfaces, not from every conditional branch in the original component.
186
+
187
+ - Avoid boolean prop proliferation for rendering modes. Standard UI booleans
188
+ such as `disabled`, `loading`, `selected`, `open`, `required`, or `invalid`
189
+ are acceptable when they represent real state. Mode switches such as `isFoo`,
190
+ `showBar`, and `withBaz` should usually become a finite variant,
191
+ discriminated union, explicit component variant, slot, or composed child.
192
+ - Use explicit variants when combinations would otherwise create impossible
193
+ states. Prefer a narrow discriminated union or named component variant over a
194
+ component that accepts unrelated mode booleans.
195
+ - Prefer `children` and named slots for caller-owned visual regions. Use render
196
+ props only when the shared component must pass data back to the caller.
197
+ - User-visible copy must stay caller-owned by default. Do not add hardcoded
198
+ product strings inside `@nextop-os/ui-system` components when the text can be
199
+ supplied by `labels`, `title`, `description`, `children`, or other explicit
200
+ props. If a legacy-compatible fallback string must exist temporarily, treat it
201
+ as compatibility debt rather than the preferred API pattern.
202
+ - Use compound components and context only when the component is complex enough
203
+ that consumers need to assemble subparts while sharing state. Do not add a
204
+ provider for a simple card, row, badge, or button wrapper.
205
+ - If shared state is necessary, make the context contract narrow and
206
+ injectable: `state`, `actions`, and `meta`. State implementation remains
207
+ outside the visual subcomponents, and host-owned daemon, Electron, router,
208
+ store, query, persistence, filesystem, or workflow behavior remains outside
209
+ the UI-system package.
210
+ - For new components, follow the repository's React 19 baseline, including
211
+ `ref` as a prop where a ref is part of the public contract. Do not rewrite
212
+ shadcn or Radix-acquired components only to chase API style parity.
213
+
214
+ Document the API shape decision before promotion: which state axes became
215
+ variants, props, slots, children, compound subcomponents, provider state, or
216
+ host-owned caller logic.
217
+
218
+ ## Token Rules
219
+
220
+ - CSS variables are the source of truth for theme values
221
+ - Tailwind utilities should consume the same token layer rather than defining a parallel color system
222
+ - prefer semantic token names such as `background`, `foreground`, `primary`, `muted`, and `destructive` over raw palette leakage in public APIs
223
+ - keep nextop-specific token extensions additive and minimal
224
+ - Build primitives for a calm workbench shell, not for marketing-card
225
+ theatrics.
226
+
227
+ For cross-surface stacking, use shared semantic `z-index` tokens instead of
228
+ local magic numbers. Current global layer tokens live in
229
+ `packages/ui/system/src/styles/theme.css` and should be the default source of
230
+ truth for:
231
+
232
+ - workbench chrome overlays
233
+ - popovers and menus
234
+ - toasts
235
+ - full-panel overlays
236
+ - dialogs and their backdrops
237
+
238
+ When a surface needs a new global layer, add a responsibility-named token to the
239
+ shared theme rather than introducing another raw `z-[12345]` value in app code.
240
+ Small component-internal stacking such as `z-1` on a pseudo-element can stay
241
+ local when it does not participate in global overlay ordering.
242
+
243
+ ### Z-Index Design Rules
244
+
245
+ Treat `z-index` as an ordering system, not as a per-component escape hatch.
246
+
247
+ Use these rules:
248
+
249
+ - use shared global tokens when a layer can overlap content owned by another component, feature, portal, or renderer surface
250
+ - use package-local or component-local variables when the layer only needs to order parts inside one isolated surface
251
+ - keep local decorative layering simple; values such as `0`, `1`, `2`, or `3` are acceptable when they never compete with global overlays
252
+ - do not introduce new raw high values such as `9999`, `10000`, or `z-[12345]` in app code
253
+ - prefer a new responsibility-named token over “one bigger number” when an existing global layer is not sufficient
254
+
255
+ Questions to ask before adding or changing a layer:
256
+
257
+ 1. Can this element ever overlap a portal, popover, toast, dialog, or another feature-owned overlay?
258
+ 2. Is this a global interaction layer or only internal ordering inside one component?
259
+ 3. Would another engineer understand the layer’s purpose from its token name alone?
260
+
261
+ If the answer to the first question is yes, the layer should almost always use a
262
+ shared global token.
263
+
264
+ ### Current Global Layers
265
+
266
+ The current shared global `z-index` tokens are:
267
+
268
+ - `--z-workbench-chrome`
269
+ Top or bottom workbench chrome rendered above window content but below global popovers and dialogs.
270
+ - `--z-workbench-genie`
271
+ The genie animation layer that must stay above ordinary workbench chrome.
272
+ - `--z-popover`
273
+ Cross-feature floating UI such as menus, switchers, and layout popovers.
274
+ - `--z-toast`
275
+ Toast notifications that should stay above popovers.
276
+ - `--z-panel`
277
+ Full-panel overlays such as workspace settings surfaces.
278
+ - `--z-panel-popover`
279
+ Popovers or menus that are portaled from within a full-panel overlay and must stay above the panel but below dialog backdrops.
280
+ - `--z-dialog-overlay`
281
+ Dialog backdrops that should dim or block panel surfaces beneath them.
282
+ - `--z-dialog`
283
+ Dialog content rendered above dialog backdrops.
284
+
285
+ These tokens are intentionally semantic and ordered by responsibility, not by
286
+ visual implementation detail.
287
+
288
+ ### Local Layering Rules
289
+
290
+ Local layers should stay local when they only solve ordering inside one bounded
291
+ surface. Good examples:
292
+
293
+ - a selected tile border inside one settings grid
294
+ - a resize handle above adjacent pane content inside one file manager surface
295
+ - a tooltip above its own dock icon inside one workbench dock
296
+ - background decorations behind launcher content
297
+
298
+ For these cases:
299
+
300
+ - prefer package-local variables such as `--workbench-z-dock-tooltip` or `--workspace-file-manager-dialog-overlay-z-index`
301
+ - keep the numeric scale tight and relative to the owning surface
302
+ - do not promote a local layer into the global theme unless another surface needs to reason about it
303
+
304
+ ### Migration Rules
305
+
306
+ When touching existing code:
307
+
308
+ - replace raw high `z-index` values with the nearest existing semantic token when the layer is globally meaningful
309
+ - if no existing token matches, add a new one in `packages/ui/system/src/styles/theme.css` and document it here
310
+ - if the layer is local-only, prefer a package or component variable instead of a new global token
311
+ - remove transitional duplicate declarations such as a Tailwind `z-*` class plus an overriding inline `style.zIndex`
312
+
313
+ ## Reusable Package Styling Rules
314
+
315
+ Reusable packages outside `packages/ui/*` should not create their own visual
316
+ systems. They should consume `@nextop-os/ui-system` primitives, icons, tokens, and
317
+ token-backed Tailwind utilities as their default styling model.
318
+
319
+ For reusable packages that render Tailwind utility classes, consumers are
320
+ responsible for including the published package output in Tailwind source
321
+ scanning. For example, a consumer of `@nextop-os/workbench-surface` should include
322
+ that package's built output in its Tailwind entrypoint or equivalent build
323
+ configuration:
324
+
325
+ ```css
326
+ @source "../node_modules/@nextop-os/workbench-surface/dist";
327
+ ```
328
+
329
+ Within this monorepo, reusable packages that require consumer Tailwind scanning
330
+ should declare that requirement in their `package.json`:
331
+
332
+ ```json
333
+ {
334
+ "nextop": {
335
+ "tailwindSourceRoot": "src"
336
+ }
337
+ }
338
+ ```
339
+
340
+ `pnpm check:ui-boundaries` validates that the desktop renderer entrypoint
341
+ `apps/desktop/src/renderer/src/style.css` includes matching `@source` directives
342
+ for any imported workspace package that declares `nextop.tailwindSourceRoot`.
343
+ It also validates that the path matches the declared source root and reports the
344
+ exact `@source` line to add or replace.
345
+
346
+ Tailwind source troubleshooting checklist:
347
+
348
+ - if a reusable package introduces new utility classes but the desktop UI does not change at runtime, confirm the package declares `nextop.tailwindSourceRoot` when it renders runtime Tailwind classes
349
+ - confirm the desktop renderer Tailwind entrypoint includes the package source path through `@source`
350
+ - re-run `pnpm check:ui-boundaries` before assuming the issue is a hot-reload or build-cache problem
351
+
352
+ Package-local CSS in reusable packages is an exception, not the default. Add it
353
+ only when the package needs selectors, keyframes, or structural behavior that is
354
+ awkward to express through UI system primitives and Tailwind utilities.
355
+
356
+ When package-local CSS is necessary:
357
+
358
+ - use UI system CSS variables or Tailwind-facing semantic tokens
359
+ - keep the CSS structural and package-responsibility-specific
360
+ - do not define raw palette values, a second token layer, or app-specific visual roles
361
+ - do not include product styling, product copy, or app-specific state concepts
362
+ - document the public stylesheet entrypoint in the package README and package release docs
363
+
364
+ If a reusable package repeatedly needs visual primitives or new semantic tokens,
365
+ prefer moving that foundation into `@nextop-os/ui-system` before adding more
366
+ package-local CSS.
367
+
368
+ ## Design Foundation Compliance
369
+
370
+ All components promoted into `@nextop-os/ui-system` must fully follow the
371
+ existing design foundations owned by this package. Promotion must align with the
372
+ shared token model, theme variables, spacing rhythm, radius scale, typography,
373
+ surface language, focus states, disabled states, and the existing `base`
374
+ primitive vocabulary.
375
+
376
+ Do not introduce a second visual language during promotion. Avoid raw palette
377
+ values, ad hoc spacing scales, local radius conventions, duplicate button or
378
+ field treatments, or component-specific CSS that should be expressed through
379
+ existing tokens or primitives.
380
+
381
+ After a component is promoted, start an independent design-foundation review
382
+ subagent before reporting completion. Give the subagent the promoted component
383
+ files, source usage, selected states, storyboard entry, metadata entry, and this
384
+ document. The subagent should verify that the component follows the design
385
+ foundation and report any drift. If a subagent cannot be started in the current
386
+ environment, report the verification as blocked instead of claiming full
387
+ design-foundation compliance.
388
+
389
+ ## Component Rules
390
+
391
+ - `base` primitives should stay low-level and presentation-focused
392
+ - `business` components may include reusable business display semantics, but
393
+ must stay host-agnostic and side-effect-free
394
+ - `packages/ui/system` is the repository's shared Radix and shadcn host package
395
+ - for primitives that exist in the shadcn registry, start from shadcn CLI output targeted at `packages/ui/system`; do not hand-author a fresh component body when the upstream primitive can be downloaded
396
+ - keep `packages/ui/system/components.json` healthy enough that `pnpm dlx shadcn@latest add <component> -c packages/ui/system` remains the default acquisition path
397
+ - treat CLI-generated source as the canonical starting point; repository-specific edits should stay narrow and mechanical, such as package import aliases, stable barrel exports, icon-layer routing, and token-backed class adjustments required by boundary checks
398
+ - if a desired primitive is not available from shadcn, prefer composing directly from `radix-ui` inside `packages/ui/system` and document that exception in the change review or follow-up docs
399
+ - if the current package structure makes CLI acquisition awkward, fix the host package structure or configuration first instead of silently replacing the workflow with a handwritten primitive
400
+ - keep primitive APIs close to upstream shadcn patterns unless product-specific constraints require a deviation
401
+ - do not place app-specific workflows such as launcher flows, workspace
402
+ registration, or route-owned panels in the shared package
403
+ - do not add new hardcoded user-visible copy inside `@nextop-os/ui-system`
404
+ components when the text can be supplied by props, `labels`, `title`,
405
+ `description`, or `children`; keep translation lookup and copy selection in
406
+ the caller
407
+ - export UI-system components through stable package barrels instead of exposing
408
+ per-file component paths
409
+ - only move a component into `@nextop-os/ui-system` when it is a real
410
+ visual-system primitive or reusable business display component with more than
411
+ one plausible consumer
412
+ - every public component, icon, utility, style entry, or tooling-visible UI
413
+ export must have metadata in
414
+ `packages/ui/system/src/metadata/components.json`
415
+ - metadata `source` paths must point at existing files under `packages/ui/system/src`
416
+ and `from` must use a stable public entrypoint
417
+ - run `node tools/scripts/check-ui-metadata.mjs` or
418
+ `pnpm check:ui-boundaries` after adding, removing, or renaming UI-system
419
+ public exports
420
+
421
+ ### Primitive Sourcing Workflow
422
+
423
+ Use this workflow when adding or replacing a shared primitive:
424
+
425
+ 1. Run shadcn CLI against `packages/ui/system` when the primitive exists in the registry.
426
+ 2. Keep the downloaded component body as the baseline implementation.
427
+ 3. Apply only the minimum package-specific adaptation required to satisfy repository rules.
428
+ 4. Export the primitive through the stable `@nextop-os/ui-system` barrels.
429
+ 5. Re-run `pnpm check:ui-boundaries` after the adaptation pass.
430
+
431
+ Repository-specific adaptation is allowed for:
432
+
433
+ - replacing direct third-party icon imports with `@nextop-os/ui-system` icon exports when the UI boundary check requires it
434
+ - switching import aliases to package-local `#components`, `#icons`, or `#lib` paths
435
+ - aligning classes with shared CSS tokens or other repository-owned boundary rules
436
+
437
+ Repository-specific adaptation is not a reason to skip CLI acquisition. The rule of thumb is:
438
+
439
+ - download first
440
+ - adapt second
441
+ - do not handwrite the primitive body from scratch unless there is no upstream shadcn primitive to start from
442
+
443
+ ### Business Component Workflow
444
+
445
+ Use this workflow when promoting reusable business UI:
446
+
447
+ 1. Read existing `@nextop-os/ui-system/metadata` and storyboard entries first.
448
+ 2. Reuse existing `base` and `business` components when they cover the need.
449
+ 3. Keep host state, side effects, data loading, routing, and daemon calls in the
450
+ original app or package.
451
+ 4. Extract only the reusable display surface and typed callback contract.
452
+ 5. Compose `base` primitives for controls, overlays, cards, icons, and layout
453
+ affordances.
454
+ 6. Add metadata with `layer: "business"` and a readable stable `id`.
455
+ 7. Add storyboard examples that cover empty, loading, normal, disabled, and
456
+ error-like display states when those states are part of the public contract.
457
+ 8. Replace the original duplicated UI with a stable public import from
458
+ `@nextop-os/ui-system`.
459
+
460
+ Business components should look like controlled React components:
461
+
462
+ ```tsx
463
+ <WorkspaceSummaryCard
464
+ workspace={workspace}
465
+ status={workspaceStatus}
466
+ disabled={!canOpenWorkspace}
467
+ onOpen={handleOpenWorkspace}
468
+ />
469
+ ```
470
+
471
+ They should not reach back into the host:
472
+
473
+ ```tsx
474
+ useWorkspaceStore();
475
+ useNavigate();
476
+ invokeDaemon();
477
+ fetch("/api/workspaces");
478
+ localStorage.setItem("workspace", id);
479
+ ```
480
+
481
+ ## Promotion Review Gate
482
+
483
+ Use this gate for every base or business component promotion. It adapts
484
+ frontend design review practice to Nextop's product standard; do not import
485
+ general marketing, portfolio, or decorative frontend heuristics into the shared
486
+ workbench system.
487
+
488
+ ### Frictionless
489
+
490
+ - The migrated consumer preserves the original task path and interaction count
491
+ unless the user approved a behavior change.
492
+ - Primary, secondary, destructive, cancel, and recovery actions keep their
493
+ visual hierarchy and remain reachable by keyboard.
494
+ - The shared component does not introduce dead ends, hidden required steps, or
495
+ new caller-owned state requirements.
496
+
497
+ ### Quality Craft
498
+
499
+ - Selected states have before/after visual parity evidence from the source
500
+ route, storyboard, or smallest reproducible view.
501
+ - The implementation uses existing `@nextop-os/ui-system` primitives and
502
+ canonical tokens before adding component-local CSS.
503
+ - Light, dark, focus-visible, hover, disabled, invalid, selected, loading, and
504
+ reduced-motion states are covered when they exist in the public contract.
505
+ - Layout, density, spacing, radius, typography, icon sizing, border, color,
506
+ opacity, shadow, and responsive behavior have no unapproved drift.
507
+ - Any intentional delta is named, justified, and reported as approved rather
508
+ than hidden inside the promotion.
509
+
510
+ ### Trustworthy
511
+
512
+ - Loading, empty, disabled, error-like, and permission-limited states keep clear
513
+ labels and actionable recovery where the source had them.
514
+ - AI-generated or inferred content keeps provenance, confidence, or disclaimer
515
+ treatment host-owned and visible when applicable.
516
+ - Error copy and status labels are supplied by the caller or labels props; the
517
+ shared component must not invent product policy or workflow meaning.
518
+ - New user-visible copy should enter the component boundary through props,
519
+ labels, or children rather than new hardcoded strings inside the shared
520
+ component.
521
+ - The component boundary leaves daemon, Electron, router, persistence, query,
522
+ filesystem, polling, i18n lookup, and workflow orchestration in the host.
523
+
524
+ Report the promotion review in this structure:
525
+
526
+ - context: component id/layer, source usage, user task, selected states, and
527
+ intended reuse surfaces
528
+ - summary: pass, needs work, or blocked
529
+ - pillar assessment: Frictionless, Quality craft, Trustworthy
530
+ - design-system compliance: tokens, primitives, metadata, storyboard, stable
531
+ exports, and public imports
532
+ - issues: blocking, major, and minor, with concrete file references
533
+ - validation: exact commands and results
534
+ - risks: uncovered states, unavailable visual evidence, unresolved subagent
535
+ review, or approved visual deltas.
536
+
537
+ ## Agent Skill Rules
538
+
539
+ Use the bundled `nextop-ui-system` skill for prompt-level work involving
540
+ `@nextop-os/ui-system`. The source lives under
541
+ `packages/ui/system/agent/nextop-ui-system/SKILL.md` so it can ship with the UI
542
+ system package.
543
+
544
+ The skill should route internally across these scenarios:
545
+
546
+ - use an existing UI-system component
547
+ - extract a new `base` component
548
+ - extract a new `business` component
549
+ - maintain metadata, ids, exports, or storyboard coverage
550
+
551
+ The skill must treat this document, `packages/ui/AGENTS.md`, metadata, and
552
+ boundary scripts as the source of truth. It should not duplicate long copies of
553
+ the rules; it should point the coding agent to the right files, force the
554
+ base/business decision, and require validation.
555
+
556
+ External business repositories that promote UI into the shared system should add
557
+ a short agent instruction such as:
558
+
559
+ ```md
560
+ When promoting business UI into @nextop-os/ui-system, use the
561
+ nextop-ui-system skill and follow packages/ui/system/ui-system.md.
562
+ ```
563
+
564
+ After installing `@nextop-os/ui-system`, external repositories can configure the
565
+ bundled skill with:
566
+
567
+ ```bash
568
+ pnpm exec nextop-ui-system-install-skill
569
+ ```
570
+
571
+ The command copies the bundled skill into `.codex/skills/nextop-ui-system` in
572
+ the current repository. When `.nextop-ui-system-dev/` is present, the installer
573
+ prefers the synced source checkout so the skill and bundled UI-system rules stay
574
+ aligned with the current local UI-system source. It refuses to overwrite local
575
+ changes unless run with `--force`.
576
+
577
+ ## Storyboard Rules
578
+
579
+ `apps/ui-storyboard` is the local component inventory and example surface for
580
+ `@nextop-os/ui-system`.
581
+
582
+ - the component list, categories, statuses, and inventory counts must come from
583
+ `@nextop-os/ui-system/metadata`
584
+ - storyboard navigation should group component stories by `layer`
585
+ - examples may stay hand-written so they can show realistic composition and
586
+ edge states
587
+ - visible component stories should display the component `id` prominently and
588
+ support copying it from the UI
589
+ - the storyboard should import public UI-system entrypoints and metadata, not
590
+ private component file paths
591
+ - keep the storyboard as a development surface; it should not become a product
592
+ shell or marketing page
593
+
594
+ ## External Dev Server Rules
595
+
596
+ The UI-system dev server exists only for local external development. It lets an
597
+ external app keep normal `@nextop-os/ui-system` imports while temporarily resolving
598
+ the stable entrypoints to a generated local cache.
599
+
600
+ - start it with `pnpm --filter @nextop-os/ui-system dev:server`
601
+ - external Vite apps opt in with `nextopUISystemDev` from
602
+ `@nextop-os/ui-system/dev-vite`
603
+ - when the server is unavailable, external apps must fall back to their
604
+ installed package in `node_modules`
605
+ - the generated `.nextop-ui-system-dev/` cache belongs in the external app's
606
+ `.gitignore`
607
+ - Tailwind consumers must include both the installed package output and the
608
+ generated dev cache in source scanning, for example
609
+ `@source "../node_modules/@nextop-os/ui-system/dist";` and
610
+ `@source "../.nextop-ui-system-dev";`
611
+ - do not make CI, production builds, or package publishing depend on the dev
612
+ server
613
+ - `@nextop-os/ui-system/dev-vite` may be imported only from bundler config or
614
+ tooling files
615
+
616
+ ## Icon Rules
617
+
618
+ - renderer-visible icons should be exported through the root package or the stable `@nextop-os/ui-system/icons` barrel
619
+ - generic system icons may wrap a third-party icon set
620
+ - product marks and custom status glyphs should live as local SVG components in the package
621
+ - icons should default to `currentColor` unless a specific token-driven treatment is required
622
+
623
+ ## Review Heuristics
624
+
625
+ When reviewing a change under `packages/ui/*`, prefer these checks:
626
+
627
+ - is the new export responsibility-named and stable enough to support for a while
628
+ - could a consumer do the same work through the root package instead of a new subpath
629
+ - is a proposed helper really part of primitive support, or should it stay local to one app
630
+ - does the change make the package easier to consume without exposing its folder layout
631
+
632
+ ## Review Questions
633
+
634
+ When reviewing a change under `packages/ui/*`, ask:
635
+
636
+ 1. Is this a visual-system concern or an app-specific component?
637
+ 2. Does this change preserve CSS tokens as the source of truth?
638
+ 3. Is the package API still narrow, stable, and responsibility-named?
639
+ 4. Would this be clearer if it stayed local to one app instead?
640
+ 5. Are consumers being nudged toward the root package and stable barrels instead of internal paths?