@archetypeai/ds-cli 0.13.0 → 0.15.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/files/AGENTS.md CHANGED
@@ -70,6 +70,10 @@ Use semantic tokens for anything themed; standard Tailwind for layout.
70
70
  - Spacing scale: `xs sm md lg xl` work as `p-lg`, `gap-md`, `space-y-xl`, `mb-sm`
71
71
  - Chart series colors: `var(--chart-1)` … `var(--chart-5)`
72
72
  - Radius on interactive elements: `rounded-interactive`
73
+ - Scrollbars are owned by the theme (`--scrollbar`, `--scrollbar-track`,
74
+ `--scrollbar-width`). Every `overflow-*` container is already on-brand in both
75
+ themes — write no scrollbar CSS at all. `bg-scrollbar` exists for a
76
+ custom-rendered thumb
73
77
 
74
78
  Standard Tailwind is fine for spacing/sizing (`p-4`, `w-full`, `gap-2`) and
75
79
  layout (`flex`, `grid`, `absolute`). Status colors communicate state — never
@@ -169,4 +173,8 @@ Compose project-specific components from package primitives in
169
173
  `sideBarSelection`, `lensTray*`, `backToDashboard`) exist in the package
170
174
  types but are internal to the Archetype AI console product — never use them
171
175
  - Keep the `@source` directives and the CSS import order exactly as above
176
+ - Never style scrollbars in app code — no `::-webkit-scrollbar` rules, no
177
+ `scrollbar-color`/`scrollbar-width`. The theme sets both, and setting
178
+ `scrollbar-*` locally also silently disables the webkit pseudo-elements on
179
+ that element
172
180
  - `layerchart` stays pinned exactly
package/files/CLAUDE.md CHANGED
@@ -70,6 +70,10 @@ Use semantic tokens for anything themed; standard Tailwind for layout.
70
70
  - Spacing scale: `xs sm md lg xl` work as `p-lg`, `gap-md`, `space-y-xl`, `mb-sm`
71
71
  - Chart series colors: `var(--chart-1)` … `var(--chart-5)`
72
72
  - Radius on interactive elements: `rounded-interactive`
73
+ - Scrollbars are owned by the theme (`--scrollbar`, `--scrollbar-track`,
74
+ `--scrollbar-width`). Every `overflow-*` container is already on-brand in both
75
+ themes — write no scrollbar CSS at all. `bg-scrollbar` exists for a
76
+ custom-rendered thumb
73
77
 
74
78
  Standard Tailwind is fine for spacing/sizing (`p-4`, `w-full`, `gap-2`) and
75
79
  layout (`flex`, `grid`, `absolute`). Status colors communicate state — never
@@ -169,4 +173,8 @@ Compose project-specific components from package primitives in
169
173
  `sideBarSelection`, `lensTray*`, `backToDashboard`) exist in the package
170
174
  types but are internal to the Archetype AI console product — never use them
171
175
  - Keep the `@source` directives and the CSS import order exactly as above
176
+ - Never style scrollbars in app code — no `::-webkit-scrollbar` rules, no
177
+ `scrollbar-color`/`scrollbar-width`. The theme sets both, and setting
178
+ `scrollbar-*` locally also silently disables the webkit pseudo-elements on
179
+ that element
172
180
  - `layerchart` stays pinned exactly
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "archetypeai-design-system",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "components": [
5
5
  {
6
6
  "name": "alert",
@@ -38,6 +38,39 @@
38
38
  "`<AspectRatio ratio={16 / 9}>` wrapping an image, video, or bg-muted placeholder."
39
39
  ]
40
40
  },
41
+ {
42
+ "name": "avatar",
43
+ "import": "@archetypeai/ds-ui-svelte-console/primitives/avatar",
44
+ "source": "https://design-system-console.archetypeai.workers.dev/r/avatar.json",
45
+ "description": "User or entity avatar with an image and an initials fallback.",
46
+ "usage": [
47
+ "Always compose Root + Fallback: `Avatar.Root` on its own renders an empty circle. The fallback is what shows while the image loads, when there is no `src`, and when the image 404s — so initials (`{user.name.split(' ').map((p) => p[0]).join('')}`) belong there even when you expect a photo.",
48
+ "Image: `Avatar.Image src={user.avatarUrl} alt={user.name}` — it is a real `<img>`, so `alt` is yours to pass and a missing one is an accessibility violation. Use the person's name, not \"avatar\".",
49
+ "Size: `sm` (28px) for dense chrome like the side-nav profile row, `default` (36px) for lists and headers, `lg` (48px) for a profile or detail header. The size lands on `data-size` and the fallback's type scales off it through `group-data-[size=…]/avatar`, so overriding the root's `size-*` by hand desynchronises the two — add a size variant instead.",
50
+ "Shape: `circle` for people, `squircle` (12px radius at every size) for non-human entities (an org, a workspace, a model) so the two read apart at a glance. The radius is deliberately generous — at `sm` the squircle sits close to a circle, which is the intended look.",
51
+ "Skipping the loading flash: pass `loadingStatus=\"loaded\"` when the image is already cached or inlined, which shows it immediately instead of flashing the fallback. It is bindable, so `bind:loadingStatus` also reads the outcome (`loading` | `loaded` | `error`) if the caller needs to react to a broken image.",
52
+ "Stacking (“who is on this session”): a `flex -space-x-2` row plus `*:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background *:data-[slot=avatar]:border-0` on the container. The ring is what cuts each avatar out of the one behind it, and it hangs off `data-slot`, so separation stays a container concern rather than a prop. Drop the border in a stack: the root's `border-border` sits just inside the ring, so leaving it on renders a grey rim against a white gap against the next rim, and the overlap reads muddy. Stack images, not initials — the overlap covers each avatar's trailing edge, which a face survives and two letters do not."
53
+ ],
54
+ "variants": {
55
+ "avatarVariants": {
56
+ "axes": {
57
+ "size": [
58
+ "sm",
59
+ "default",
60
+ "lg"
61
+ ],
62
+ "shape": [
63
+ "circle",
64
+ "squircle"
65
+ ]
66
+ },
67
+ "defaults": {
68
+ "size": "default",
69
+ "shape": "circle"
70
+ }
71
+ }
72
+ }
73
+ },
41
74
  {
42
75
  "name": "badge",
43
76
  "import": "@archetypeai/ds-ui-svelte-console/primitives/badge",
@@ -250,6 +283,19 @@
250
283
  "source": "https://design-system-console.archetypeai.workers.dev/r/console-utils.json",
251
284
  "description": "className merge helper cn() (clsx + tailwind-merge with the rounded-interactive extension). Re-exported as $lib/utils in scaffolded apps."
252
285
  },
286
+ {
287
+ "name": "copy-button",
288
+ "import": "@archetypeai/ds-ui-svelte-console/primitives/copy-button",
289
+ "source": "https://design-system-console.archetypeai.workers.dev/r/copy-button.json",
290
+ "description": "Copy-to-clipboard control that owns its own copied state.",
291
+ "usage": [
292
+ "Icon beside a value: `CopyButton value={job.id} label=\"Copy job ID\" title={job.id}` — `label` is the accessible name, so say what is being copied rather than \"Copy\", and pass the full value as `title` whenever the visible text is truncated.",
293
+ "Value inside the control: pass the value as children (`<CopyButton value={id} label=\"Copy blueprint ID\">{id}</CopyButton>`) and the whole thing becomes one copy target with one hover, instead of a 16px icon beside plain text. The hit area follows automatically — children give the `inline` size, icon-only gives the 36px `icon` size.",
294
+ "Feedback: a check mark replaces the glyph for 1500ms (`feedbackDuration`) and a toast fires on both success and failure, so a mounted `<Toaster />` is a prerequisite. Pass `toast={false}` where the inline check is feedback enough — a table of IDs, where a toast per copy is noise — and the component announces the copy to screen readers itself instead. `successMessage` / `errorMessage` override the wording; `onCopy(copied)` hooks anything else.",
295
+ "Inside a row that navigates: `stopPropagation` keeps the click off the row's own handler (and off an enclosing anchor). Reveal-on-hover is the Button axis, `visibility=\"hover\"`, which needs `group` on the row — but not when the value is inside the control, since it would fade the value too.",
296
+ "Copying without a button: `copyText(text)` is exported from the same subpath for a labelled action that happens to copy (a \"Copy CSV\" button in a dialog footer). It resolves to whether the write landed."
297
+ ]
298
+ },
253
299
  {
254
300
  "name": "counter",
255
301
  "import": "@archetypeai/ds-ui-svelte-console/primitives/counter",
@@ -258,6 +304,7 @@
258
304
  "usage": [
259
305
  "Count: `Counter value={412}` rolls only the digit places that changed; past 999 it abbreviates (`1.2K`, `123K`, `1.2M`) so it never grows a fifth column, and the exact number stays in its title + accessible label.",
260
306
  "Absent vs zero vs loading: omit the component entirely when there is no count, pass `value={0}` for an empty one, and `value={null}` while it is still loading (reserves the same width so nothing reflows when it arrives).",
307
+ "At least, for a list you cannot size: `Counter value={103} atLeast` renders `103+` when the caller has loaded 103 rows and knows only that more exist (keyset pagination, a capped query). The number is the caller's own, not a rounded threshold, and the accessible label reads \"at least 103\". Past 999 the abbreviation truncates instead of rounding, so `1250` shows `1.2K+` and never claims rows nobody loaded. `atLeast` is ignored on `value={0}`, where it would state nothing.",
261
308
  "Tone & size: `tone=\"muted\"` for a quieter pill against busy chrome; `size=\"sm\"` for dense rows (tables, list items). Inside a tab strip use `Tabs.Count`, which re-colors it for the active/inactive trigger."
262
309
  ],
263
310
  "variants": {
@@ -707,11 +754,17 @@
707
754
  "source": "https://design-system-labs.archetypeai.workers.dev/r/logo.json",
708
755
  "description": "Archetype AI brand logo mark.",
709
756
  "usage": [
710
- "Headers: `<Logo class=\"h-6 w-auto\" />` next to the app title; sizes sm | default | lg."
757
+ "Headers: `<Logo class=\"h-6 w-auto\" />` next to the app title; sizes sm | default | lg.",
758
+ "Compact lockup: `variant=\"compact\"` is the stacked social/avatar artwork (~1.6:1 instead of the wordmark's ~5.4:1). Use it where a horizontal wordmark will not fit — square tiles, avatars, narrow sidebars, splash marks. Defaults to `variant=\"default\"`.",
759
+ "Sizing is shared: both variants honour size sm | default | lg (h-4 | h-6 | h-8) and take their width from the artwork aspect, so compact renders far narrower at the same height. Prefer default or lg for compact."
711
760
  ],
712
761
  "variants": {
713
762
  "logoVariants": {
714
763
  "axes": {
764
+ "variant": [
765
+ "default",
766
+ "compact"
767
+ ],
715
768
  "size": [
716
769
  "sm",
717
770
  "default",
@@ -719,6 +772,7 @@
719
772
  ]
720
773
  },
721
774
  "defaults": {
775
+ "variant": "default",
722
776
  "size": "default"
723
777
  }
724
778
  }
@@ -967,7 +1021,8 @@
967
1021
  "Icon view toggle (list/grid): `Tabs.List variant=\"toggle\"` + `Tabs.Trigger variant=\"icon\"` with an icon + title.",
968
1022
  "Segmented source switch (Library | File IDs | Upload): `variant=\"toggle\"` list + triggers `size=\"pill\" typography=\"mono\"`.",
969
1023
  "Page-section tabs (Progress | Logs | Artifacts): `Tabs.List variant=\"line\"` with plain text triggers; always `Tabs.Root bind:value`.",
970
- "Count on a trigger: `<Tabs.Trigger value=\"logs\">Logs <Tabs.Count value={logCount} /></Tabs.Trigger>` — an odometer pill that abbreviates past 999 (`1.2K`) so the strip never reflows. Pass `value={null}` while the count is still loading (it reserves the width), `value={0}` for an empty tab, and omit `Tabs.Count` entirely for a tab that has no count at all."
1024
+ "Count on a trigger: `<Tabs.Trigger value=\"logs\">Logs <Tabs.Count value={logCount} /></Tabs.Trigger>` — an odometer pill that abbreviates past 999 (`1.2K`) so the strip never reflows. Pass `value={null}` while the count is still loading (it reserves the width), `value={0}` for an empty tab, and omit `Tabs.Count` entirely for a tab that has no count at all.",
1025
+ "Count on a tab that cannot be sized: `<Tabs.Count value={loaded} atLeast />` renders `103+` for a keyset-paginated tab that knows only how many rows it has so far. Prefer it over dropping the count entirely, which reads as \"none\"."
971
1026
  ],
972
1027
  "variants": {
973
1028
  "tabsListVariants": {
@@ -1072,8 +1127,11 @@
1072
1127
  "description": "Hover hint.",
1073
1128
  "usage": [
1074
1129
  "Wrap the app/section in `Tooltip.Provider delayDuration={200}`.",
1075
- "Field help: `Tooltip.Trigger variant=\"info\"` holding an InfoIcon placed next to a Label sectionHeader, explanation in Tooltip.Content.",
1076
- "`variant=\"inlineLink\"` underlined trigger flowing inside prose (supported file requirements)."
1130
+ "Field help: `Tooltip.Trigger variant=\"info\"` is a filled 20px disc holding a mono `i` — pass it no children, it renders its own glyph. Explanation goes in `Tooltip.Content`.",
1131
+ "`aria-label` on the info trigger is required and is the caller's: name what is being explained (`aria-label=\"About Detection Log\"`). The glyph is `aria-hidden`, so without a label the button has no accessible name.",
1132
+ "The info disc squares against `Counter`'s height on purpose — a list title, its `Counter` and its info disc in one `flex items-center gap-2` row is the intended cluster, the solid pill reading as the count and the muted one as the meaning.",
1133
+ "`variant=\"inlineLink\"` — underlined trigger flowing inside prose (supported file requirements).",
1134
+ "The arrow centres itself on the trigger on all four sides and protrudes half its box — `arrowClasses` is for its ink or size, not for centring corrections. If a tip looks off, it is a bug in `tooltipArrowVariants`, not something to counter-shift at the call site."
1077
1135
  ],
1078
1136
  "variants": {
1079
1137
  "tooltipTriggerVariants": {
@@ -1094,9 +1152,16 @@
1094
1152
  "name": "video-player",
1095
1153
  "import": "@archetypeai/ds-ui-svelte-labs/primitives/video-player",
1096
1154
  "source": "https://design-system-labs.archetypeai.workers.dev/r/video-player.json",
1097
- "description": "Media-playback card with controls and aspect ratio; composes Card + AspectRatio + Button + Slider.",
1155
+ "description": "Media-playback card whose seek track doubles as a timeline: named marker windows, tinted highlight bands and bindable playback state. Composes Card + AspectRatio + Button + Slider.",
1098
1156
  "usage": [
1099
- "`<VideoPlayer src muted />` for the standard controlled player; `controls={false}` for ambient/looping footage."
1157
+ "`<VideoPlayer src muted />` for the standard controlled player; `controls={false}` for ambient/looping footage. `preload=\"metadata\"` by default, so `duration` is known before the first play.",
1158
+ "Playback state: `bind:time` / `bind:duration` / `bind:paused` mirror the media element out, and `time` and `paused` are writable - assigning to `time` seeks, assigning `paused = false` plays. That is how another view drives the player.",
1159
+ "Markers: `markers={[{ start, end, label?, color? }]}` in **seconds, not fractions** draws one focusable dot per window on the seek track. Clicking a dot expands it into a range pill, seeks to its start and plays; one is expanded at a time, and `bind:activeMarker` is its index (or null). Scrubbing by hand, or a click on the video surface, collapses it. `loopActiveMarker` loops playback inside the expanded window.",
1160
+ "Linked brushing: `highlights={[{ start, end, color? }]}` tints regions another view is pointing at (brushing in), and `onMarkerHover(index | null)` fires on hover **and focus** so a hovered marker can highlight the matching row elsewhere (brushing out).",
1161
+ "Two lifecycle traps when `src` changes: a new `src` remounts the player and Svelte's media bindings write your bound values into the fresh element, so reset `time = 0` and `paused = true` yourself or a stale position becomes the new clip's start; and `bind:duration` holds the previous clip's value until `loadedmetadata`, so reseed `duration` from a known length if marker geometry must be right on the first frame (`duration === 0` collapses every marker to the left edge).",
1162
+ "`controlsVisibility=\"always\"` pins the control bar open - required whenever markers must stay clickable during playback, since the default `\"auto\"` fades the bar (and the markers with it) 2.5s into playback. `formatTime` replaces the mm:ss clock in the time readout and the marker captions.",
1163
+ "Style hooks: `data-slot` on `video-player`, `-video`, `-overlay`, `-controls`, `-time`, `-markers`, `-marker` (carries `data-expanded`), `-marker-caption`, `-highlight`. A per-marker `color` inks the outline and fill via `currentColor`.",
1164
+ "Marker seeking needs an origin that serves HTTP Range requests; against one that does not, seeks silently do nothing (serve the clip from a blob URL instead)."
1100
1165
  ],
1101
1166
  "variants": {
1102
1167
  "videoPlayerOverlayVariants": {
@@ -1120,6 +1185,28 @@
1120
1185
  "defaults": {
1121
1186
  "visible": "true"
1122
1187
  }
1188
+ },
1189
+ "videoPlayerMarkerVariants": {
1190
+ "axes": {
1191
+ "expanded": [
1192
+ "true",
1193
+ "false"
1194
+ ],
1195
+ "filled": [
1196
+ "true",
1197
+ "false"
1198
+ ],
1199
+ "emphasis": [
1200
+ "hidden",
1201
+ "full",
1202
+ "ambient"
1203
+ ]
1204
+ },
1205
+ "defaults": {
1206
+ "expanded": "false",
1207
+ "filled": "false",
1208
+ "emphasis": "ambient"
1209
+ }
1123
1210
  }
1124
1211
  }
1125
1212
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archetypeai/ds-cli",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "Archetype AI Design System CLI Tool",
5
5
  "type": "module",
6
6
  "bin": {