@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.
- package/README.md +102 -4
- package/package.json +9 -1
- package/src/components/command-palette.tsx +702 -0
- package/src/components/devcontainer/catalog-card.tsx +269 -0
- package/src/components/devcontainer/catalog-tile.tsx +133 -0
- package/src/components/devcontainer/extension-card.tsx +146 -0
- package/src/components/devcontainer/feature-card.tsx +157 -0
- package/src/components/devcontainer/feature-tile.tsx +63 -0
- package/src/components/devcontainer/image-card.tsx +107 -0
- package/src/components/devcontainer/image-tile.tsx +56 -0
- package/src/components/devcontainer/index.ts +68 -0
- package/src/components/devcontainer/publisher-badge.tsx +91 -0
- package/src/components/devcontainer/registry-ref.tsx +89 -0
- package/src/components/devcontainer/registry.ts +154 -0
- package/src/components/devcontainer/stack-mark-paths.ts +60 -0
- package/src/components/devcontainer/stack-mark.tsx +92 -0
- package/src/components/devcontainer/types.ts +84 -0
- package/src/components/kbd.tsx +117 -0
- package/src/components/projects/advanced-sections.tsx +136 -0
- package/src/components/projects/build-manifest.tsx +116 -0
- package/src/components/projects/extension-picker.tsx +169 -0
- package/src/components/projects/feature-picker.tsx +193 -0
- package/src/components/projects/form-section.tsx +97 -0
- package/src/components/projects/image-picker.tsx +206 -0
- package/src/components/projects/index.ts +58 -0
- package/src/components/projects/project-form.tsx +488 -0
- package/src/components/projects/repo-list.tsx +302 -0
- package/src/components/projects/secret-list.tsx +159 -0
- package/src/components/projects/types.ts +136 -0
- package/src/index.ts +35 -0
- 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 —
|
|
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"
|
|
86
|
-
import { WorkerCard } from "@spunto/design-system/workers"
|
|
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.
|
|
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"
|