@spunto/design-system 0.9.3 → 0.11.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 (31) hide show
  1. package/README.md +102 -4
  2. package/package.json +9 -1
  3. package/src/components/command-palette.tsx +702 -0
  4. package/src/components/devcontainer/catalog-card.tsx +269 -0
  5. package/src/components/devcontainer/catalog-tile.tsx +133 -0
  6. package/src/components/devcontainer/extension-card.tsx +146 -0
  7. package/src/components/devcontainer/feature-card.tsx +157 -0
  8. package/src/components/devcontainer/feature-tile.tsx +63 -0
  9. package/src/components/devcontainer/image-card.tsx +107 -0
  10. package/src/components/devcontainer/image-tile.tsx +56 -0
  11. package/src/components/devcontainer/index.ts +68 -0
  12. package/src/components/devcontainer/publisher-badge.tsx +91 -0
  13. package/src/components/devcontainer/registry-ref.tsx +89 -0
  14. package/src/components/devcontainer/registry.ts +154 -0
  15. package/src/components/devcontainer/stack-mark-paths.ts +60 -0
  16. package/src/components/devcontainer/stack-mark.tsx +92 -0
  17. package/src/components/devcontainer/types.ts +84 -0
  18. package/src/components/kbd.tsx +117 -0
  19. package/src/components/projects/advanced-sections.tsx +136 -0
  20. package/src/components/projects/build-manifest.tsx +116 -0
  21. package/src/components/projects/extension-picker.tsx +169 -0
  22. package/src/components/projects/feature-picker.tsx +193 -0
  23. package/src/components/projects/form-section.tsx +97 -0
  24. package/src/components/projects/image-picker.tsx +206 -0
  25. package/src/components/projects/index.ts +58 -0
  26. package/src/components/projects/project-form.tsx +488 -0
  27. package/src/components/projects/repo-list.tsx +302 -0
  28. package/src/components/projects/secret-list.tsx +159 -0
  29. package/src/components/projects/types.ts +136 -0
  30. package/src/index.ts +35 -0
  31. package/styles.css +13 -0
package/README.md CHANGED
@@ -40,7 +40,9 @@ const nextConfig = { transpilePackages: ["@spunto/design-system"] }
40
40
  - **`./colors`** — `cssVar`, `chartColors`, `chartRamp`, `segColors` for JS/chart contexts.
41
41
  - **Primitives** — form + layout building blocks styled on `@base-ui/react`:
42
42
  - _Basics_ — `Button` (+`buttonVariants`), `Card` (+parts), `Badge` (+`badgeVariants`),
43
- `Separator`, `Spinner`, `Skeleton`, `Avatar` (+`AvatarImage`/`AvatarFallback`).
43
+ `Separator`, `Spinner`, `Skeleton`, `Avatar` (+`AvatarImage`/`AvatarFallback`),
44
+ `Kbd` — one keyboard cap, or a whole shortcut (`keys="mod+k"` → ⌘ K, Ctrl K off Apple),
45
+ spelled out for screen readers.
44
46
  - _Forms_ — `Input`, `Textarea`, `Label`, `Switch`, `Checkbox`,
45
47
  `RadioGroup` (+`RadioGroupItem`), `Select` (+`SelectTrigger`/`SelectValue`/
46
48
  `SelectContent`/`SelectItem`/`SelectGroup`/`SelectGroupLabel`/`SelectSeparator`).
@@ -51,6 +53,20 @@ const nextConfig = { transpilePackages: ["@spunto/design-system"] }
51
53
  (a confirmation variant, non-dismissible), `Tooltip`. Their portals render into
52
54
  the provider's overlay container; `Tooltip`'s shared delay group is mounted by the
53
55
  provider too.
56
+ - _CommandPalette_ — `CommandPalette` (+`CommandPaletteInput`/`List`/`Group`/`Item`/
57
+ `Empty`/`Loading`/`Separator`/`Footer`/`Trigger`), the ⌘K palette: an input, groups,
58
+ items, and a jump. **Domain-free**, hence its place in the root entry — it knows
59
+ nothing of projects, workers or deployments; those are items the app hands it. Base UI
60
+ `Autocomplete` (combobox/listbox roles, `aria-activedescendant`, ↑↓ + scroll-into-view,
61
+ ↵ activates the highlighted item) rendered *inline* inside a Base UI `Dialog` (focus
62
+ trap, focus restore, Escape, backdrop), portalled into the provider's overlay container
63
+ like the other overlays. Compositional like `Dialog`/`Select`, not a config array —
64
+ which is why the root runs Base UI in `mode="none"` and each item decides for itself
65
+ whether it matches, with the same `Autocomplete.useFilter()` collator. Presentational
66
+ only: `loading` is a prop, results are children, and a server-side search is
67
+ `value`/`onValueChange` + `filter={null}`. Navigation is `href` + `render.link`, never
68
+ `next/link` — same rule as `WorkerCard`. Not virtualized: a few hundred items stay
69
+ fluid (a non-matching item renders nothing), beyond that filter server-side.
54
70
  - _Terminal_ — `Terminal` (+`TerminalHandle`, `TerminalOptions`), a transport-agnostic
55
71
  xterm.js surface: mount, shared dark ANSI theme, fit-on-resize. No opinion on where
56
72
  bytes come from (WebSocket, SSE, a static string) — feed it via the `write`/`writeln`
@@ -73,7 +89,7 @@ const nextConfig = { transpilePackages: ["@spunto/design-system"] }
73
89
  provider is also the umbrella that mounts the tooltip delay group and the overlay
74
90
  portal container the dialogs/selects render into.
75
91
 
76
- ## Domain components — `@spunto/design-system/workers`
92
+ ## Domain components — `/workers`, `/devcontainer`, `/projects`
77
93
 
78
94
  Everything above is domain-free: a `Button` knows nothing about Spunto. Components
79
95
  that **do** know a Spunto concept live behind their own entry point, so the root
@@ -82,10 +98,14 @@ of a worker. **The rule for what comes next: any new component that knows a doma
82
98
  concept goes behind a domain sub-export, never in the root entry.**
83
99
 
84
100
  ```tsx
85
- import { Button, Card } from "@spunto/design-system" // primitives
86
- import { WorkerCard } from "@spunto/design-system/workers" // domain
101
+ import { Button, Card } from "@spunto/design-system" // primitives
102
+ import { WorkerCard } from "@spunto/design-system/workers" // domain
103
+ import { ImageCard, FeatureCard, ExtensionCard } from "@spunto/design-system/devcontainer" // domain
104
+ import { ProjectForm } from "@spunto/design-system/projects" // domain
87
105
  ```
88
106
 
107
+ ### `@spunto/design-system/workers`
108
+
89
109
  - **`WorkerCard`** — a Spunto worker as a card: state, author, node, setup
90
110
  progress, git branches, resources, banners, actions. **Purely presentational** —
91
111
  hand it a snapshot plus slots and it draws. The rule that keeps it that way:
@@ -103,6 +123,84 @@ import { WorkerCard } from "@spunto/design-system/workers" // domain
103
123
  minimum, everything optional but `id`, and an unknown state falls back to
104
124
  `pending` instead of throwing.
105
125
 
126
+ ### `@spunto/design-system/devcontainer`
127
+
128
+ The three catalogs you compose an environment from. They exist identically in the
129
+ dashboard and in Spunto Lite, and were hand-drawn as raw `<button>`/`<li>` on both
130
+ sides — exactly the duplication this package is for.
131
+
132
+ - **`ImageCard`** — a devcontainer base image: runtime mark, label, publisher +
133
+ avatar, image reference in mono, description.
134
+ - **`FeatureCard`** — a devcontainer feature: the author's avatar (the GitHub org
135
+ behind the `ghcr.io` ref), label, description, option chips (`version=lts`…),
136
+ OCI reference.
137
+ - **`ImageTile` / `FeatureTile`** (+ the shared `CatalogTile`) — the same entries
138
+ at chip size: a mark, a name, one trailing detail. What a catalog needs past a
139
+ dozen entries, where picking is a *logo* task and full cards become a wall.
140
+ They flow and wrap like tags; the pickers pair them with the full card of
141
+ what's selected.
142
+ - **`ExtensionCard`** (+ `ExtensionCardSkeleton`) — a VS Code extension: its real
143
+ Open VSX icon, display name, publisher + verified badge, version, formatted
144
+ install count, rating.
145
+
146
+ All three share one selectable state (flame check + ring), one **compact
147
+ read-only** variant for the project sheet, and one **`custom`** variant for a
148
+ hand-typed reference that degrades cleanly.
149
+
150
+ - **Nothing here fetches anything.** `onSelect` is a callback, the option row
151
+ (`optionsSlot`) and the trailing control (`action`) are slots. The author and
152
+ the logo are **derived from the reference** by pure functions, because the
153
+ catalogs carry neither: `parseRegistryRef`, `derivePublisher`
154
+ (`ghcr.io/<owner>/…` → that GitHub org, avatar included via
155
+ `github.com/<owner>.png`; `mcr.microsoft.com/devcontainers/…` → Dev Containers)
156
+ and `resolveStackMark`. `publisher` / `mark` / `iconUrl` are the overrides for
157
+ the day the API knows better — no component change needed then.
158
+ - **Brand marks ship with the package** as single-path SVGs drawn with
159
+ `currentColor` (`StackMark`, `STACK_MARK_PATHS`): no third-party CDN, and each
160
+ logo inherits the warm accent of its card instead of dragging 20 clashing brand
161
+ palettes into one grid.
162
+ - **Its bricks, exported individually**: `CatalogCard` (the selectable shell),
163
+ `CatalogMark` (the icon → brand → avatar → monogram cascade), `CatalogChip`,
164
+ `PublisherBadge`/`PublisherAvatar`, `RegistryRef` (mono + copy button), plus the
165
+ pure helpers `parseRegistryRef`, `derivePublisher`, `githubAvatarUrl`,
166
+ `resolveStackMark`, `resolveFeatureOptions`, `formatCount`, `parseExtensionId`.
167
+ - **Types are structural** here too: `DevcontainerImageEntry`,
168
+ `DevcontainerFeatureEntry` and `VscodeExtensionEntry` are all-optional, and an
169
+ unknown registry degrades to a monogram instead of an invented author.
170
+
171
+ ### `@spunto/design-system/projects`
172
+
173
+ - **`ProjectForm`** — the whole "compose a dev environment" screen: identity,
174
+ base image, repos, features, extensions, lifecycle, ports, prewarm images,
175
+ docker-in-docker, secrets, plus the live **build manifest** that recaps it and
176
+ carries the submit button.
177
+ - **Controlled by one value.** A single `ProjectFormValue` in, one `onChange`
178
+ out, `onSubmit(value)` at the end. Start from `emptyProjectFormValue()`, or
179
+ `toProjectFormValue(partial)` to fill the gaps of an existing project. The
180
+ value is *always* readable — which is what makes validation, drafts and
181
+ previews possible at all.
182
+ - **Which sections show is a prop** (`CREATE_SECTIONS` / `EDIT_SECTIONS` / your
183
+ own list, in your own order). Creation and editing are the same component with
184
+ a different list, not two screens that drift apart.
185
+ - **Everything past identity / base image / repos folds under "Advanced
186
+ options"** (`advancedSections`, `[]` to disable). Two rules keep that honest:
187
+ the closed fold *writes out what it holds* ("2 features · lifecycle · 3
188
+ secrets") instead of showing a bare chevron, and the form opens it by itself
189
+ when any of those sections already has a value — so editing a project never
190
+ hides a setting you made earlier.
191
+ - **Nothing fetches.** Catalogs come in as data, the marketplace search is
192
+ `onSearchExtensions`, the GitHub combobox is `renderRepoField` (without it: a
193
+ plain `<input list>` + `<datalist>`, no dependency, keyboard-friendly), and
194
+ `extras` slips app-specific content *inside* a section — the deploy key card,
195
+ a warning — without forking the component.
196
+ - **Its bricks, exported individually**: `FormSection`, `Field`,
197
+ `BuildManifest`, `RepoList` (+`deriveFromGitUrl`), `SecretList`,
198
+ `ImagePicker`/`FeaturePicker`/`ExtensionPicker`, and the pure `manifestRows`.
199
+ - **Responsive by container query, not viewport.** The form is a full page in
200
+ one app and a 700 px panel in another; it reads its own width to decide
201
+ whether the manifest sits beside it and whether the catalogs are one column or
202
+ two.
203
+
106
204
  ## Toasts — `SpuntoProvider` + `toast()`
107
205
 
108
206
  `SpuntoProvider` is the design system's client umbrella provider. Mount it once
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spunto/design-system",
3
- "version": "0.9.3",
3
+ "version": "0.11.0",
4
4
  "description": "Spunto's shared design system — warm/flame tokens, color constants, and UI primitives.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -37,6 +37,14 @@
37
37
  "types": "./src/components/workers/index.ts",
38
38
  "import": "./src/components/workers/index.ts"
39
39
  },
40
+ "./devcontainer": {
41
+ "types": "./src/components/devcontainer/index.ts",
42
+ "import": "./src/components/devcontainer/index.ts"
43
+ },
44
+ "./projects": {
45
+ "types": "./src/components/projects/index.ts",
46
+ "import": "./src/components/projects/index.ts"
47
+ },
40
48
  "./fonts": {
41
49
  "types": "./src/components/fonts.tsx",
42
50
  "import": "./src/components/fonts.tsx"