anentrypoint-design 1.0.9 → 1.0.11
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 +2 -1
- package/dist/247420.css +179 -8
- package/dist/247420.js +56 -30
- package/dist/index.html +29 -3
- package/package.json +1 -1
- package/src/components/chat/message.js +1 -1
- package/src/components/content/charts.js +26 -5
- package/src/components/shell/atoms.js +3 -2
- package/src/components/shell/icons.js +13 -1
- package/src/components/theme-toggle.js +18 -0
- package/src/css/app-shell/chat-polish.css +18 -0
- package/src/css/app-shell/hero-content.css +38 -5
- package/src/css/app-shell/kits-appended.css +56 -2
- package/src/css/app-shell/primitives.css +4 -0
- package/src/css/app-shell/row-print.css +13 -0
- package/src/css/app-shell/sidebar-misc.css +15 -0
- package/src/css/app-shell/topbar.css +35 -1
- package/src/kits/os/freddie/pages-core.js +13 -3
- package/src/kits/os/freddie/pages-os.js +2 -2
- package/src/kits/os/freddie-dashboard.js +16 -5
- package/src/kits/os/theme.css +47 -6
- package/src/page-html/client-script.js +18 -2
- package/src/page-html/page-styles.js +10 -0
- package/types/components.d.ts +1 -1
|
@@ -80,7 +80,23 @@ body.canvas-host { background: transparent !important; }
|
|
|
80
80
|
.app-topbar > .brand { flex: 0 0 auto; }
|
|
81
81
|
.app-topbar > .app-search { flex: 1 1 auto; }
|
|
82
82
|
.app-topbar > nav { margin-left: auto; }
|
|
83
|
-
|
|
83
|
+
/* Utility cluster grouping: the theme toggle sits after the repo-info nav
|
|
84
|
+
links with no visual separation between the two groups, so the row read
|
|
85
|
+
as one undifferentiated strip of controls. A thin inset rule plus extra
|
|
86
|
+
left gap reads as "these are a distinct utility group", matching the
|
|
87
|
+
subtle divider already used between .app-status segments. */
|
|
88
|
+
.app-topbar > .app-topbar-theme {
|
|
89
|
+
flex: 0 0 auto;
|
|
90
|
+
margin-left: var(--space-2);
|
|
91
|
+
padding-left: var(--space-3);
|
|
92
|
+
position: relative;
|
|
93
|
+
}
|
|
94
|
+
.app-topbar > .app-topbar-theme::before {
|
|
95
|
+
content: ''; position: absolute; left: 0; top: 50%;
|
|
96
|
+
transform: translateY(-50%);
|
|
97
|
+
width: 1px; height: 18px;
|
|
98
|
+
background: var(--rule-strong);
|
|
99
|
+
}
|
|
84
100
|
.app-topbar nav { display: flex; gap: var(--space-1); font-size: var(--fs-sm); flex-wrap: wrap; }
|
|
85
101
|
.app-topbar nav a { flex: 0 0 auto; }
|
|
86
102
|
.app-topbar nav a {
|
|
@@ -171,6 +187,15 @@ body.canvas-host { background: transparent !important; }
|
|
|
171
187
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
172
188
|
min-width: 0; flex: 0 1 auto;
|
|
173
189
|
}
|
|
190
|
+
/* Ancestor trail segments previously had no rule of their own and simply
|
|
191
|
+
inherited .app-crumb's base --fg-3 — the same tone the separators sit on
|
|
192
|
+
top of (before their own 0.5 opacity dims them further). That leaves only
|
|
193
|
+
two visually distinct tiers in practice (dim separator, bold leaf) rather
|
|
194
|
+
than three, since the ancestor tone reads as "default text colour", not a
|
|
195
|
+
deliberate middle step. --fg-3 is the crumb bar's OWN base, so explicitly
|
|
196
|
+
restating it here is the anchor for the 3-tier read: separator (--fg-3 at
|
|
197
|
+
0.5 opacity, dimmest) < ancestor (--fg-3, mid) < leaf (--fg bold, brightest). */
|
|
198
|
+
.app-crumb > span:not(.sep):not(.leaf):not(.crumb-right) { color: var(--fg-3); }
|
|
174
199
|
.app-crumb .leaf { color: var(--fg); font-weight: 600; flex: 0 1 auto; }
|
|
175
200
|
.app-crumb .crumb-right { margin-left: auto; color: var(--fg-3); flex: 0 0 auto; }
|
|
176
201
|
|
|
@@ -189,6 +214,15 @@ body.canvas-host { background: transparent !important; }
|
|
|
189
214
|
.app-side-shell {
|
|
190
215
|
background: var(--bg); overflow-y: auto; min-height: 0;
|
|
191
216
|
scrollbar-width: thin; scrollbar-color: var(--rule-strong) transparent;
|
|
217
|
+
/* Bottom-edge fade: a rail longer than the viewport otherwise shows only
|
|
218
|
+
the thin scrollbar track as its sole "there is more below" cue. Mirrors
|
|
219
|
+
the horizontal fade already used by .os-instances (src/kits/os/theme.css)
|
|
220
|
+
for its own overflow edge, oriented top-to-bottom for this vertical
|
|
221
|
+
scroller. The top edge stays fully opaque (rows start flush under the
|
|
222
|
+
topbar with no need to hint upward scroll on initial load); only the
|
|
223
|
+
trailing 24px softens toward transparent. */
|
|
224
|
+
-webkit-mask-image: linear-gradient(to bottom, black calc(100% - 24px), transparent);
|
|
225
|
+
mask-image: linear-gradient(to bottom, black calc(100% - 24px), transparent);
|
|
192
226
|
}
|
|
193
227
|
.app-side-shell::-webkit-scrollbar { width: 8px; }
|
|
194
228
|
.app-side-shell::-webkit-scrollbar-thumb { background: var(--rule-strong); border-radius: var(--r-pill, 999px); }
|
|
@@ -44,6 +44,16 @@ export function makeCorePages(ctx) {
|
|
|
44
44
|
const tools = h0.pi.tools.size;
|
|
45
45
|
const skills = h0.pi.skills.size;
|
|
46
46
|
const health = (typeof h0.pi.health === 'function') ? h0.pi.health() : { ok: true };
|
|
47
|
+
// Epoch-ms fields (ts, or any *At/*Time-suffixed key) render as raw
|
|
48
|
+
// 13-digit numbers otherwise -- unreadable and gives no sense of
|
|
49
|
+
// recency. Format as a locale timestamp; anything that doesn't
|
|
50
|
+
// parse as a plausible epoch-ms value falls through to String(v)
|
|
51
|
+
// unchanged so this never mangles a genuine small integer.
|
|
52
|
+
const isEpochMsKey = (k) => k === 'ts' || /(At|Time)$/.test(k);
|
|
53
|
+
const fmtHealthValue = (k, v) => {
|
|
54
|
+
if (isEpochMsKey(k) && typeof v === 'number' && v > 1e12) return new Date(v).toLocaleString();
|
|
55
|
+
return String(v);
|
|
56
|
+
};
|
|
47
57
|
return [
|
|
48
58
|
Hero({ title: 'assistant', body: 'open js agent harness — in-page agent runtime.', accent: h0.version || 'web' }),
|
|
49
59
|
Kpi({ items: [[sessions.length, 'sessions'], [tools, 'tools'], [skills, 'skills']] }),
|
|
@@ -55,7 +65,7 @@ export function makeCorePages(ctx) {
|
|
|
55
65
|
['set api key', 'keys tab -> click chip to set value'],
|
|
56
66
|
['add cron', 'cron tab -> form'],
|
|
57
67
|
] }) }),
|
|
58
|
-
Panel({ title: 'host', children: Receipt({ rows: Object.entries(health).map(([k, v]) => [k,
|
|
68
|
+
Panel({ title: 'host', children: Receipt({ rows: Object.entries(health).map(([k, v]) => [k, fmtHealthValue(k, v)]) }) }),
|
|
59
69
|
];
|
|
60
70
|
},
|
|
61
71
|
async sessions(h0) {
|
|
@@ -79,7 +89,7 @@ export function makeCorePages(ctx) {
|
|
|
79
89
|
Kpi({ items: [[list.length, 'sessions']] }),
|
|
80
90
|
Panel({ title: 'recent sessions', count: list.length, children: list.length === 0
|
|
81
91
|
? EmptyState({ text: 'no sessions yet — open chat and send a message', glyph: Icon('thread') })
|
|
82
|
-
: Table({ headers: ['id', 'title', 'platform', 'model', 'cwd', 'skill', ''], rows }) }),
|
|
92
|
+
: Table({ headers: ['id', 'title', 'platform', 'model', 'cwd', 'skill', ''], striped: true, rows }) }),
|
|
83
93
|
];
|
|
84
94
|
},
|
|
85
95
|
async agents(h0) {
|
|
@@ -88,7 +98,7 @@ export function makeCorePages(ctx) {
|
|
|
88
98
|
Kpi({ items: [[a.count || 0, 'active'], [a.turns || 0, 'turns']] }),
|
|
89
99
|
Panel({ title: 'agent overview', children: Receipt({ rows: [
|
|
90
100
|
['total turns', String(a.turns || 0)],
|
|
91
|
-
['active session', a.active || '
|
|
101
|
+
['active session', a.active || '—'],
|
|
92
102
|
['last activity', a.last_activity ? new Date(a.last_activity).toLocaleString() : '—'],
|
|
93
103
|
] }) }),
|
|
94
104
|
];
|
|
@@ -14,7 +14,7 @@ export function makeOsPages(ctx) {
|
|
|
14
14
|
Kpi({ items: [[list.length, 'instances'], [activeId || '—', 'active']] }),
|
|
15
15
|
Panel({ title: 'instances', count: list.length, children: list.length === 0
|
|
16
16
|
? EmptyState({ text: 'no instances', glyph: Icon('square') })
|
|
17
|
-
: Table({ headers: ['id', 'active', 'shells', 'windows'],
|
|
17
|
+
: Table({ headers: ['id', 'active', 'shells', 'windows'], striped: true,
|
|
18
18
|
rows: list.map(i => [i.id, i.id === activeId ? Icon('circle') : '', String((i.shells || []).length), String((i.windows || []).length)]) }) }),
|
|
19
19
|
];
|
|
20
20
|
},
|
|
@@ -25,7 +25,7 @@ export function makeOsPages(ctx) {
|
|
|
25
25
|
Kpi({ items: [[wins.length, 'windows'], [focused ? (focused.id || focused.title || '?') : '—', 'focused']] }),
|
|
26
26
|
Panel({ title: 'windows', count: wins.length, children: wins.length === 0
|
|
27
27
|
? EmptyState({ text: 'no windows open', glyph: Icon('square') })
|
|
28
|
-
: Table({ headers: ['id', 'title', 'min', 'max', 'pos'],
|
|
28
|
+
: Table({ headers: ['id', 'title', 'min', 'max', 'pos'], striped: true,
|
|
29
29
|
rows: wins.map(w => [w.id || '?', w.title || '', w.min ? Icon('circle') : '', w.max ? Icon('circle') : '',
|
|
30
30
|
(w.el ? `${w.el.offsetLeft},${w.el.offsetTop} ${w.el.offsetWidth}×${w.el.offsetHeight}` : '')]) }) }),
|
|
31
31
|
];
|
|
@@ -19,6 +19,13 @@ export function createFreddieDashboard({ instance, bootHost, osSurfaces, loading
|
|
|
19
19
|
const state = { active: 'home', ts: new Date().toLocaleTimeString(), body: null, error: null };
|
|
20
20
|
let host = instance.host || null;
|
|
21
21
|
const allRoutes = osSurfaces ? [...ROUTES, ...OS_ROUTE_DEFS] : ROUTES;
|
|
22
|
+
// Bumped on every setActive() and captured by loadActive() before its
|
|
23
|
+
// first await; a resolving page whose generation no longer matches the
|
|
24
|
+
// live one is a stale in-flight nav (user clicked a second page before
|
|
25
|
+
// the first's async page() resolved) and must not overwrite state.body —
|
|
26
|
+
// otherwise the crumb/side (synchronous) shows the new page while the
|
|
27
|
+
// main content silently keeps whichever page happened to resolve last.
|
|
28
|
+
let navGeneration = 0;
|
|
22
29
|
|
|
23
30
|
async function ensureHost() {
|
|
24
31
|
if (host) return host;
|
|
@@ -27,7 +34,7 @@ export function createFreddieDashboard({ instance, bootHost, osSurfaces, loading
|
|
|
27
34
|
return host;
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
function setActive(p) { state.active = p; rerender(); }
|
|
37
|
+
function setActive(p) { state.active = p; navGeneration++; rerender(); }
|
|
31
38
|
if (typeof window !== 'undefined') window.__fd_nav = setActive;
|
|
32
39
|
|
|
33
40
|
function rerender() { webjsx.applyDiff(root, view()); loadActive(); }
|
|
@@ -72,15 +79,19 @@ export function createFreddieDashboard({ instance, bootHost, osSurfaces, loading
|
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
async function loadActive() {
|
|
82
|
+
const myGeneration = navGeneration;
|
|
83
|
+
let body, error = null;
|
|
75
84
|
try {
|
|
76
85
|
const h0 = await ensureHost();
|
|
77
86
|
const page = PAGES[state.active] || PAGES.home;
|
|
78
|
-
|
|
79
|
-
state.error = null;
|
|
87
|
+
body = await page(h0, instance);
|
|
80
88
|
} catch (e) {
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
error = String(e && e.stack || e);
|
|
90
|
+
body = Panel({ title: 'error', children: pre(error) });
|
|
83
91
|
}
|
|
92
|
+
if (myGeneration !== navGeneration) return; // superseded by a later nav click
|
|
93
|
+
state.body = body;
|
|
94
|
+
state.error = error;
|
|
84
95
|
state.ts = new Date().toLocaleTimeString();
|
|
85
96
|
webjsx.applyDiff(root, view());
|
|
86
97
|
}
|
package/src/kits/os/theme.css
CHANGED
|
@@ -105,12 +105,24 @@ html, body {
|
|
|
105
105
|
* (barely above the base tone, no imagery/texture asset to fetch or theme)
|
|
106
106
|
* gives the desktop depth without competing with window chrome — scoped to
|
|
107
107
|
* .ds-247420 .wm-root specifically (the actual desktop viewport BEHIND
|
|
108
|
-
* windows), not the page background used by bare/non-OS surfaces.
|
|
108
|
+
* windows), not the page background used by bare/non-OS surfaces.
|
|
109
|
+
* A faint repeating dot-grid is layered on top of the wash: the radial wash
|
|
110
|
+
* alone reads as a barely-perceptible tint over a large empty area (the
|
|
111
|
+
* reported "huge empty dark canvas" below a single default window), while a
|
|
112
|
+
* grid of low-opacity dots gives the eye actual anchor points across that
|
|
113
|
+
* space without competing with window chrome or requiring an image asset. */
|
|
109
114
|
.ds-247420 .wm-root {
|
|
110
|
-
background
|
|
115
|
+
/* Split into background-image/-color longhands, NOT the `background`
|
|
116
|
+
shorthand: some engines fail to parse a multi-layer gradient list
|
|
117
|
+
shorthand-form when any layer's color-stop uses color-mix() — the
|
|
118
|
+
whole declaration is dropped and the canvas silently renders flat.
|
|
119
|
+
The longhand form parses these same layers correctly. */
|
|
120
|
+
background-image:
|
|
121
|
+
radial-gradient(circle, color-mix(in oklab, var(--os-fg) 6%, transparent) 1px, transparent 1px),
|
|
111
122
|
radial-gradient(ellipse 120% 80% at 20% -10%, color-mix(in oklab, var(--os-accent) 6%, transparent), transparent 60%),
|
|
112
|
-
radial-gradient(ellipse 100% 70% at 100% 100%, color-mix(in oklab, var(--os-accent) 4%, transparent), transparent 55%)
|
|
113
|
-
|
|
123
|
+
radial-gradient(ellipse 100% 70% at 100% 100%, color-mix(in oklab, var(--os-accent) 4%, transparent), transparent 55%);
|
|
124
|
+
background-color: var(--os-bg-0);
|
|
125
|
+
background-size: 28px 28px, auto, auto;
|
|
114
126
|
}
|
|
115
127
|
@media (prefers-reduced-transparency: reduce) {
|
|
116
128
|
.ds-247420 .wm-root { background: var(--os-bg-0); }
|
|
@@ -354,6 +366,8 @@ html, body {
|
|
|
354
366
|
border-radius: var(--radius-lg, var(--r-3));
|
|
355
367
|
box-shadow: var(--shadow-2);
|
|
356
368
|
overflow: hidden;
|
|
369
|
+
display: flex;
|
|
370
|
+
flex-direction: column;
|
|
357
371
|
}
|
|
358
372
|
.ds-247420 .wm-win.wm-focused {
|
|
359
373
|
border-color: color-mix(in oklab, var(--os-fg) 28%, transparent);
|
|
@@ -364,6 +378,8 @@ html, body {
|
|
|
364
378
|
background: var(--os-bg-2);
|
|
365
379
|
border: none;
|
|
366
380
|
padding: var(--space-2) var(--space-2-75);
|
|
381
|
+
display: flex;
|
|
382
|
+
align-items: center;
|
|
367
383
|
gap: var(--space-2-5);
|
|
368
384
|
transition: background 120ms ease;
|
|
369
385
|
}
|
|
@@ -442,7 +458,12 @@ html, body {
|
|
|
442
458
|
linear-gradient(135deg, transparent 0 45%, currentColor 45% 55%, transparent 55% 100%),
|
|
443
459
|
linear-gradient(135deg, transparent 0 70%, currentColor 70% 80%, transparent 80% 100%);
|
|
444
460
|
}
|
|
445
|
-
.ds-247420 .wm-body {
|
|
461
|
+
.ds-247420 .wm-body {
|
|
462
|
+
background: var(--os-bg-1);
|
|
463
|
+
flex: 1 1 auto;
|
|
464
|
+
min-height: 0;
|
|
465
|
+
overflow: auto;
|
|
466
|
+
}
|
|
446
467
|
|
|
447
468
|
.os-side-rail {
|
|
448
469
|
position: fixed;
|
|
@@ -593,6 +614,18 @@ html, body {
|
|
|
593
614
|
.app-frame { width: 100%; height: 100%; border: 0; background: var(--os-bg-0); }
|
|
594
615
|
.app-iframe { width: 100%; height: 100%; border: 0; display: block; }
|
|
595
616
|
.app-iframe.web { background: #ffffff; } /* intentional: white canvas for embedded external web content that expects white */
|
|
617
|
+
/* Browser-pane empty state (about:blank): a bare framed iframe with no
|
|
618
|
+
* content is a large dead rectangle -- this placeholder fills that space
|
|
619
|
+
* with a real affordance until a navigation lands. Hidden the instant the
|
|
620
|
+
* pane's own url stops being about:blank (docs/browser-pane.js). */
|
|
621
|
+
.bp-empty {
|
|
622
|
+
width: 100%; height: 100%;
|
|
623
|
+
display: flex; align-items: center; justify-content: center;
|
|
624
|
+
color: var(--os-fg-3);
|
|
625
|
+
font: var(--fs-sm) var(--os-font);
|
|
626
|
+
background: var(--os-bg-1);
|
|
627
|
+
text-align: center;
|
|
628
|
+
}
|
|
596
629
|
.app-text-cursor { }
|
|
597
630
|
|
|
598
631
|
@media (prefers-reduced-motion: no-preference) {
|
|
@@ -936,7 +969,11 @@ html.ds-247420 { touch-action: pan-x pan-y; overscroll-behavior: none; -webkit-t
|
|
|
936
969
|
.ds-247420 .tb-sess-badge {
|
|
937
970
|
display: inline-flex; align-items: center; justify-content: center;
|
|
938
971
|
min-width: 16px; height: 14px; padding: 0 var(--space-1);
|
|
939
|
-
|
|
972
|
+
/* A neutral window-COUNT chip, not a notification/alert badge -- a small
|
|
973
|
+
* corner radius (--r-0, 4px) reads as a rectangular count pill, distinct
|
|
974
|
+
* from the fully-round .tb-sess-dot presence indicator elsewhere in this
|
|
975
|
+
* chip, so the two don't get visually conflated. */
|
|
976
|
+
border-radius: var(--r-0);
|
|
940
977
|
background: color-mix(in oklab, var(--fg) 14%, transparent);
|
|
941
978
|
font-size: var(--fs-pico); font-weight: 600; line-height: 1;
|
|
942
979
|
}
|
|
@@ -1071,6 +1108,10 @@ html.ds-247420 { touch-action: pan-x pan-y; overscroll-behavior: none; -webkit-t
|
|
|
1071
1108
|
.ds-247420 .tb-sessions-card-actions { display: flex; gap: var(--space-1-75); margin-top: var(--space-1); }
|
|
1072
1109
|
.ds-247420 .tb-sessions-card-actions button { flex: 1; height: 24px; font-size: var(--fs-nano); padding: 0 var(--space-2); border-radius: var(--radius-sm, var(--r-0)); border: 1px solid color-mix(in oklab, var(--fg) 16%, transparent); background: transparent; color: inherit; cursor: pointer; font-family: inherit; }
|
|
1073
1110
|
.ds-247420 .tb-sessions-card-actions button:hover { background: color-mix(in oklab, var(--fg) 6%, transparent); }
|
|
1111
|
+
/* Baseline (non-hover) danger tint so the destructive action reads as
|
|
1112
|
+
* distinct at rest, not only on hover -- otherwise Delete is visually
|
|
1113
|
+
* identical to Active/Export until the pointer happens to land on it. */
|
|
1114
|
+
.ds-247420 .tb-sessions-card-actions button.danger { color: var(--color-danger); border-color: color-mix(in oklab, var(--color-danger) 40%, transparent); }
|
|
1074
1115
|
.ds-247420 .tb-sessions-card-actions button.danger:hover { background: var(--color-danger); color: var(--on-color); border-color: var(--color-danger); }
|
|
1075
1116
|
.ds-247420 .tb-sessions-empty-mid { padding: 40px; text-align: center; opacity: 0.6; font-size: var(--fs-tiny); }
|
|
1076
1117
|
|
|
@@ -35,9 +35,15 @@ function heroNode(hero) {
|
|
|
35
35
|
hero.body || hero.subheading,
|
|
36
36
|
hero.accent ? h('span', { class: 'ds-hero-accent' }, ' ' + hero.accent) : null,
|
|
37
37
|
) : null,
|
|
38
|
+
// Every hero CTA is a plain <a href> that navigates away from this page
|
|
39
|
+
// (GitHub, the portfolio site, ...), never an in-page action -- so the
|
|
40
|
+
// trailing arrow is appended here once, at the render layer, rather than
|
|
41
|
+
// hand-typed per label in content yaml where it could drift out of sync
|
|
42
|
+
// with the "navigates away = arrow" rule used elsewhere (row ctas,
|
|
43
|
+
// signin's in-page submit buttons).
|
|
38
44
|
Array.isArray(hero.ctas) && hero.ctas.length
|
|
39
45
|
? h('div', { class: 'ds-hero-actions' }, ...hero.ctas.map((c, i) =>
|
|
40
|
-
h('a', { key: i, class: i === 0 ? 'btn btn-accent' : 'btn btn-ghost', href: c.href || '#' }, c.label || c.cta || 'go')))
|
|
46
|
+
h('a', { key: i, class: i === 0 ? 'btn btn-accent' : 'btn btn-ghost', href: c.href || '#' }, (c.label || c.cta || 'go') + ' ->')))
|
|
41
47
|
: null,
|
|
42
48
|
badgeRow,
|
|
43
49
|
);
|
|
@@ -56,8 +62,18 @@ function showcaseNode(showcase) {
|
|
|
56
62
|
C.Btn({ key: 'b1', variant: 'primary', children: 'Primary' }),
|
|
57
63
|
C.Btn({ key: 'b2', variant: 'default', children: 'Default' }),
|
|
58
64
|
C.Btn({ key: 'b3', variant: 'ghost', children: 'Ghost' }),
|
|
59
|
-
|
|
65
|
+
// .ds-showcase-btn-danger-group gives Danger its own visually separated
|
|
66
|
+
// cluster (extra leading gap + a hairline divider) instead of sitting in
|
|
67
|
+
// the same uniform-gap row as Primary -- two similarly-weighted filled
|
|
68
|
+
// buttons side by side otherwise compete for "the one action to take"
|
|
69
|
+
// with nothing marking Danger as the deliberately-set-apart one.
|
|
70
|
+
h('span', { key: 'b4-group', class: 'ds-showcase-btn-danger-group' },
|
|
71
|
+
C.Btn({ key: 'b4', variant: 'danger', children: 'Danger' })),
|
|
60
72
|
);
|
|
73
|
+
// Hue documents STATUS, not decoration: green = shipping/live, blue = pre-
|
|
74
|
+
// release/in-flux, purple = newly added. Badge (not a bespoke pill) carries
|
|
75
|
+
// the '0 violations' count so every status indicator in this row is one of
|
|
76
|
+
// the two real chip-family components, never a one-off styled span.
|
|
61
77
|
const chipRow = h('div', { class: 'ds-showcase-row' },
|
|
62
78
|
C.Chip({ key: 'c1', tone: 'green', children: 'Live' }),
|
|
63
79
|
C.Chip({ key: 'c2', tone: 'blue', children: 'Beta' }),
|
|
@@ -106,6 +106,16 @@ a.row:hover .ds-row-arrow { opacity: 1 }
|
|
|
106
106
|
letter-spacing: 0.06em; color: var(--fg-3, #6b6b6b);
|
|
107
107
|
}
|
|
108
108
|
.ds-showcase-row { display: flex; flex-wrap: wrap; gap: var(--space-2, 8px); align-items: center; }
|
|
109
|
+
/* Sets Danger apart from Primary/Default/Ghost in the button showcase row: an
|
|
110
|
+
extra left margin beyond the row's own gap, plus a hairline divider, so a
|
|
111
|
+
danger action never reads as just one more same-weight choice beside the
|
|
112
|
+
primary action. */
|
|
113
|
+
.ds-showcase-btn-danger-group {
|
|
114
|
+
display: inline-flex; align-items: center;
|
|
115
|
+
margin-left: var(--space-3, 16px);
|
|
116
|
+
padding-left: var(--space-3, 16px);
|
|
117
|
+
border-left: 1px solid var(--rule, rgba(0,0,0,.12));
|
|
118
|
+
}
|
|
109
119
|
/* Card-grid layout (panel.layout === 'cards'): a browsable gallery of tiles
|
|
110
120
|
instead of a dense text-row list, opt-in per panel via home.yaml's
|
|
111
121
|
kits.layout: cards. */
|
package/types/components.d.ts
CHANGED
|
@@ -267,7 +267,7 @@ export declare function Lede(props?: LedeProps): VNode;
|
|
|
267
267
|
*/
|
|
268
268
|
export interface DotProps {
|
|
269
269
|
/** @default 'on' */
|
|
270
|
-
tone?: 'on' | 'live' | (string & {});
|
|
270
|
+
tone?: 'on' | 'live' | 'warn' | (string & {});
|
|
271
271
|
}
|
|
272
272
|
export declare function Dot(props?: DotProps): VNode;
|
|
273
273
|
|