@heyramzi/ui 1.53.0 → 1.54.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/COMPONENTS.md +6 -0
- package/package.json +2 -2
- package/scripts/lib/sync-core.mjs +1 -1
- package/scripts/sync-shadcn.mjs +11 -11
- package/scripts/sync-transitions.mjs +7 -7
- package/src/agents/agent-avatar.svelte +14 -15
- package/src/agents/agent-cast.svelte +2 -2
- package/src/agents/demo.svelte +1 -1
- package/src/agents/helpers.ts +10 -1
- package/src/agents/index.ts +2 -2
- package/src/agents/page-state.ts +1 -1
- package/src/agents/types.ts +3 -3
- package/src/components/copy/copy-button.test.ts +3 -1
- package/src/components/data-table/infinite-scroll.svelte +1 -1
- package/src/components/data-table/pagination.svelte +1 -1
- package/src/components/data-table/row-arrow.svelte +1 -1
- package/src/components/data-table/row-context-menu.svelte +1 -1
- package/src/components/data-table/sortable-head.svelte +2 -2
- package/src/components/data-table/table-skeleton.svelte +1 -1
- package/src/components/docs/doc-page.svelte +161 -0
- package/src/components/docs/index.ts +12 -0
- package/src/components/docs/on-this-page.svelte +217 -0
- package/src/components/docs/scroll-spy.svelte.ts +97 -0
- package/src/components/field-row/field-row.svelte +57 -0
- package/src/components/field-row/index.ts +1 -0
- package/src/components/hold-to-confirm/hold-to-confirm.svelte +2 -2
- package/src/components/icon/icon.svelte +1 -1
- package/src/components/list-page/detail-empty.svelte +1 -1
- package/src/components/list-page/detail-header.svelte +1 -1
- package/src/components/list-page/detail-panel.svelte +1 -1
- package/src/components/list-page/detail-row.svelte +1 -1
- package/src/components/list-page/detail-stack.svelte +1 -1
- package/src/components/list-page/filter-item.svelte +1 -1
- package/src/components/list-page/filter-popover.svelte +1 -1
- package/src/components/list-page/grouped-list.svelte +1 -1
- package/src/components/list-page/list-page-shell.svelte +1 -1
- package/src/components/list-page/master-detail.svelte +1 -1
- package/src/components/list-page/prev-next.svelte +1 -1
- package/src/components/list-page/rail-body.svelte +1 -1
- package/src/components/list-page/rail-head.svelte +1 -1
- package/src/components/list-page/rail-row.svelte +1 -1
- package/src/components/list-page/section-label.svelte +1 -1
- package/src/components/list-page/toolbar-search.svelte +2 -2
- package/src/components/list-page/toolbar-trigger.ts +1 -1
- package/src/components/locale-switch/locale-switch.svelte +1 -1
- package/src/components/mark/mark.svelte +1 -1
- package/src/components/picker/mark-picker.svelte +1 -1
- package/src/components/picker/picker.svelte +1 -1
- package/src/components/picker/trigger.ts +2 -2
- package/src/components/section-card/index.ts +1 -0
- package/src/components/section-card/section-card.svelte +43 -0
- package/src/components/section-header/index.ts +1 -0
- package/src/components/section-header/section-header.svelte +54 -0
- package/src/components/section-header/section-header.test.ts +25 -0
- package/src/components/segmented/animated-tabs.svelte +2 -2
- package/src/components/segmented/collapse.test.ts +10 -0
- package/src/components/segmented/collapse.ts +5 -0
- package/src/components/segmented/segment.svelte +2 -2
- package/src/components/segmented/segmented-filter.svelte +6 -5
- package/src/components/segmented/segmented-nav.svelte +1 -1
- package/src/components/segmented/segmented-overflow.svelte +1 -1
- package/src/components/segmented/segmented-track.svelte +29 -4
- package/src/components/segmented/segmented.test.ts +1 -1
- package/src/components/segmented/segmented.ts +2 -2
- package/src/components/star-rating-input/star-rating-input.svelte +11 -4
- package/src/components/table-view/index.ts +3 -0
- package/src/components/table-view/table-view.svelte +103 -0
- package/src/components/table-view/table-view.test.ts +53 -0
- package/src/components/table-view/types.ts +14 -0
- package/src/components/theme/index.ts +17 -0
- package/src/components/theme/theme-provider.svelte +47 -0
- package/src/components/theme/theme-provider.test.ts +61 -0
- package/src/effects/animated-beam/diagrams/BeamSvg.svelte +1 -1
- package/src/effects/animated-beam/path-calculator.svelte.ts +1 -1
- package/src/effects/border-beam/types.ts +1 -1
- package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +1 -1
- package/src/effects/shader-gradient/shaders.ts +1 -1
- package/src/effects/spotlight/Spotlight.svelte +1 -2
- package/src/icons/trades.ts +3 -3
- package/src/lib/sliding-pill.ts +3 -3
- package/src/lib/tone.test.ts +10 -0
- package/src/lib/tone.ts +1 -1
- package/src/shadcn/badge/badge.svelte +1 -1
- package/src/shadcn/button/button.svelte +10 -1
- package/src/shadcn/button/button.test.ts +19 -0
- package/src/shadcn/checkbox/checkbox.svelte +1 -1
- package/src/shadcn/context-menu/context-menu-separator.svelte +4 -1
- package/src/shadcn/dropdown-menu/dropdown-menu-separator.svelte +4 -1
- package/src/shadcn/input/input.svelte +2 -2
- package/src/shadcn/progress/progress.svelte +8 -2
- package/src/shadcn/progress/tone.test.ts +13 -0
- package/src/shadcn/progress/tone.ts +15 -0
- package/src/shadcn/sidebar/sidebar-group-label.svelte +1 -1
- package/src/shadcn/table/table-cell.svelte +21 -1
- package/src/shadcn/table/table-cell.test.ts +16 -0
- package/src/tokens/base.css +183 -0
- package/src/tokens/craft.css +179 -0
- package/src/tokens/index.css +19 -0
- package/src/tokens/palette.css +410 -0
- package/src/tokens/primitives.css +223 -0
- package/scripts/relative-imports.mjs +0 -39
- package/src/tokens.css +0 -993
package/COMPONENTS.md
CHANGED
|
@@ -71,7 +71,13 @@ Generic pieces built on the primitives, imported from `@heyramzi/ui/components/<
|
|
|
71
71
|
- `mark` — Mark, a logo or photo glyph with an initial fallback
|
|
72
72
|
- `open-in` — OpenIn, a link out to a tool that takes the tool's name, label and mark as a prop
|
|
73
73
|
- `picker` — Picker, MarkPicker, pickerTriggerClass, a command palette at popover scale
|
|
74
|
+
- `docs` — DocPage, OnThisPage, ScrollSpy: one page anatomy for documentation, an icon, title and summary header, a reading column and a contents rail that becomes a bar on a narrow pane
|
|
75
|
+
- `section-card` — SectionCard, a card with a fixed-height header, an h3 title, optional actions and a line under it
|
|
76
|
+
- `section-header` — SectionHeader, a section label with a count chip, a line of description and actions
|
|
77
|
+
- `field-row` — FieldRow, a labelled control with its action button at the control's height
|
|
78
|
+
- `table-view` — TableView, a read-only table from columns and rows: numeric columns, footer row, custom cell
|
|
74
79
|
- `segmented` — AnimatedTabs (pill or underline), SegmentedFilter, SegmentedNav, SectionRuns, one bordered track with a sliding pill, a badge note and an overflow menu
|
|
80
|
+
- `theme` — ThemeProvider, mode, userPrefersMode, setMode, toggleMode, resetMode: the one light and dark system (mode-watcher, optional peer). Mount `<ThemeProvider initial onChange themeColors modeStorageKey />` once; flip with `toggleMode()`, read `mode.current`. Pair with `theme-toggle`'s `revealTheme(event, toggleMode)` for the wipe
|
|
75
81
|
- `star-rating-input` — StarRatingInput
|
|
76
82
|
- `svg` — Svg, an SVG file imported with `?raw`
|
|
77
83
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heyramzi/ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.54.0",
|
|
4
4
|
"description": "Svelte 5 UI primitives: effects, animations, and shared component CSS.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"types": "./src/index.ts",
|
|
29
29
|
"default": "./src/index.ts"
|
|
30
30
|
},
|
|
31
|
-
"./tokens.css": "./src/tokens.css",
|
|
31
|
+
"./tokens.css": "./src/tokens/index.css",
|
|
32
32
|
"./lib/*.js": {
|
|
33
33
|
"svelte": "./src/lib/*.ts",
|
|
34
34
|
"types": "./src/lib/*.ts",
|
|
@@ -15,7 +15,7 @@ const get = (url) => fetch(url, { headers: { "user-agent": "heyramzi-ui-sync" }
|
|
|
15
15
|
|
|
16
16
|
export async function fetchText(url) {
|
|
17
17
|
const res = await get(url);
|
|
18
|
-
if (!res.ok) throw new Error(`${res.status} ${res.statusText}
|
|
18
|
+
if (!res.ok) throw new Error(`${res.status} ${res.statusText}: ${url}`);
|
|
19
19
|
return await res.text();
|
|
20
20
|
}
|
|
21
21
|
|
package/scripts/sync-shadcn.mjs
CHANGED
|
@@ -238,14 +238,14 @@ for (const { item, payload } of fetched) {
|
|
|
238
238
|
};
|
|
239
239
|
|
|
240
240
|
if (!moved.length && !gone.length) unchanged++;
|
|
241
|
-
else if (fresh) buckets.added.push(`${item.name}
|
|
241
|
+
else if (fresh) buckets.added.push(`${item.name}: ${files.length} files, new primitive`);
|
|
242
242
|
else if (blocked)
|
|
243
|
-
for (const s of moved) buckets.port.push(`${label(s.path)}
|
|
243
|
+
for (const s of moved) buckets.port.push(`${label(s.path)}: upstream moved, ${s.state} here`);
|
|
244
244
|
else if (apply.length)
|
|
245
|
-
buckets.synced.push(`${item.name}
|
|
245
|
+
buckets.synced.push(`${item.name}: ${apply.map((s) => basename(s.path)).join(", ")}`);
|
|
246
246
|
|
|
247
247
|
for (const g of gone)
|
|
248
|
-
buckets.removed.push(`${label(g.path)}
|
|
248
|
+
buckets.removed.push(`${label(g.path)}: gone upstream${g.ours ? ", kept (ours)" : ""}`);
|
|
249
249
|
|
|
250
250
|
if (CHECK) continue;
|
|
251
251
|
|
|
@@ -261,14 +261,14 @@ for (const { item, payload } of fetched) {
|
|
|
261
261
|
|
|
262
262
|
for (const name of Object.keys(previous)) {
|
|
263
263
|
if (entries[name]) continue;
|
|
264
|
-
buckets.removed.push(`${name}
|
|
264
|
+
buckets.removed.push(`${name}: gone from the registry`);
|
|
265
265
|
if (!CHECK) await removeDeep(`${VENDOR}/${name}`);
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
const ours = readdirSync(LOCAL, { withFileTypes: true })
|
|
269
269
|
.filter((e) => e.isDirectory() && !entries[e.name] && !LOCAL_ONLY.has(e.name))
|
|
270
270
|
.map((e) => e.name);
|
|
271
|
-
for (const name of ours) buckets.unported.push(`${name}
|
|
271
|
+
for (const name of ours) buckets.unported.push(`${name}: in src/shadcn, not in the registry`);
|
|
272
272
|
|
|
273
273
|
/* The docs site's own demo for each primitive, vendored on the same three-way
|
|
274
274
|
* rule. Upstream writes them; the site renders them. */
|
|
@@ -284,7 +284,7 @@ for (const name of demoNames) {
|
|
|
284
284
|
fetchTextIf(`${DOCS}/content/components/${name}.md`),
|
|
285
285
|
]);
|
|
286
286
|
if (raw === null) {
|
|
287
|
-
buckets.unported.push(`${name}
|
|
287
|
+
buckets.unported.push(`${name}: no demo upstream`);
|
|
288
288
|
continue;
|
|
289
289
|
}
|
|
290
290
|
|
|
@@ -296,12 +296,12 @@ for (const name of demoNames) {
|
|
|
296
296
|
// The imports of the demo that renders: ours when kept, upstream's when written.
|
|
297
297
|
const shown = state === "diverged" ? local : next;
|
|
298
298
|
|
|
299
|
-
if (state === "dropped") buckets.removed.push(`${label(path)}
|
|
299
|
+
if (state === "dropped") buckets.removed.push(`${label(path)}: dropped here, kept out`);
|
|
300
300
|
else manifest.push({ name, ...meta(doc, name), imports: publicImports(shown) });
|
|
301
301
|
|
|
302
302
|
if (base !== raw) {
|
|
303
|
-
if (state === "diverged") buckets.port.push(`${label(path)}
|
|
304
|
-
else if (state !== "current") buckets.synced.push(`${name}
|
|
303
|
+
if (state === "diverged") buckets.port.push(`${label(path)}: upstream moved, ours here`);
|
|
304
|
+
else if (state !== "current") buckets.synced.push(`${name}: demo`);
|
|
305
305
|
}
|
|
306
306
|
if (CHECK) continue;
|
|
307
307
|
|
|
@@ -345,7 +345,7 @@ if (!CHECK) {
|
|
|
345
345
|
|
|
346
346
|
process.exit(
|
|
347
347
|
report({
|
|
348
|
-
title: `shadcn-svelte
|
|
348
|
+
title: `shadcn-svelte: ${items.length} primitives`,
|
|
349
349
|
buckets,
|
|
350
350
|
unchanged,
|
|
351
351
|
footer: `${Object.values(entries).filter((e) => e.adapted).length} adapted here, ${LOCAL_ONLY.size} ours`,
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
/*
|
|
15
|
-
* Paid ones are dropped here and never vendored
|
|
15
|
+
* Paid ones are dropped here and never vendored (Ramzi, 14 Sep 2026: "the pro effects we don't need them").
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
/*
|
|
@@ -100,20 +100,20 @@ for (const { slug, name, css } of free) {
|
|
|
100
100
|
const hash = sha256(css);
|
|
101
101
|
entries[slug] = { name, sha256: hash, local: PORTED[slug] ?? null };
|
|
102
102
|
|
|
103
|
-
if (!previous[slug]) buckets.added.push(`${slug}
|
|
104
|
-
else if (previous[slug].sha256 !== hash) buckets.changed.push(`${slug}
|
|
103
|
+
if (!previous[slug]) buckets.added.push(`${slug}: ${name}`);
|
|
104
|
+
else if (previous[slug].sha256 !== hash) buckets.changed.push(`${slug}: ${name}`);
|
|
105
105
|
else unchanged++;
|
|
106
106
|
|
|
107
|
-
if (!PORTED[slug]) buckets.unported.push(`${slug}
|
|
107
|
+
if (!PORTED[slug]) buckets.unported.push(`${slug}: nothing under src/ owns it`);
|
|
108
108
|
else if (!existsSync(`${SRC}/${PORTED[slug]}`))
|
|
109
|
-
buckets.unported.push(`${slug}
|
|
109
|
+
buckets.unported.push(`${slug}: PORTED says ${PORTED[slug]}, which is missing`);
|
|
110
110
|
|
|
111
111
|
if (!CHECK) await writeFileDeep(`${VENDOR}/${slug}.css`, css);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
for (const slug of Object.keys(previous)) {
|
|
115
115
|
if (entries[slug]) continue;
|
|
116
|
-
buckets.removed.push(`${slug}
|
|
116
|
+
buckets.removed.push(`${slug}: gone upstream or moved to pro`);
|
|
117
117
|
if (!CHECK) await removeDeep(`${VENDOR}/${slug}.css`);
|
|
118
118
|
}
|
|
119
119
|
|
|
@@ -138,7 +138,7 @@ if (!CHECK) {
|
|
|
138
138
|
|
|
139
139
|
process.exit(
|
|
140
140
|
report({
|
|
141
|
-
title: `transitions.dev
|
|
141
|
+
title: `transitions.dev: ${free.length} free`,
|
|
142
142
|
buckets,
|
|
143
143
|
unchanged,
|
|
144
144
|
footer: `${paid} pro skipped`,
|
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
BotAvatarShading,
|
|
5
5
|
BotAvatarState,
|
|
6
6
|
BotAvatarType,
|
|
7
|
-
} from './vendor/types';
|
|
8
|
-
import type { AgentAvatarProps } from './types';
|
|
9
|
-
import { botAvatarPresets, stateLabels } from './vendor/presets';
|
|
10
|
-
import { SHAPE_PATHS, SHAPE_PARTS } from './vendor/shapes';
|
|
11
|
-
import {
|
|
12
|
-
import { Sim, restPose } from './vendor/engine';
|
|
13
|
-
import { draw, LIGHT_DEFAULTS, OVERSCAN, RISE, type DrawConfig } from './vendor/draw';
|
|
14
|
-
import { bakesPending, hurry, onLanded, takeMissed, warmPlastic, type FurStyle } from './vendor/plastic';
|
|
15
|
-
import { subscribe, pointer } from './vendor/ticker';
|
|
7
|
+
} from './vendor/types.js';
|
|
8
|
+
import type { AgentAvatarProps } from './types.js';
|
|
9
|
+
import { botAvatarPresets, stateLabels } from './vendor/presets.js';
|
|
10
|
+
import { SHAPE_PATHS, SHAPE_PARTS } from './vendor/shapes.js';
|
|
11
|
+
import { richer, shade } from './vendor/color.js';
|
|
12
|
+
import { Sim, restPose } from './vendor/engine.js';
|
|
13
|
+
import { draw, LIGHT_DEFAULTS, OVERSCAN, RISE, type DrawConfig } from './vendor/draw.js';
|
|
14
|
+
import { bakesPending, hurry, onLanded, takeMissed, warmPlastic, type FurStyle } from './vendor/plastic.js';
|
|
15
|
+
import { subscribe, pointer } from './vendor/ticker.js';
|
|
16
16
|
import {
|
|
17
17
|
SHOW_WITHIN,
|
|
18
18
|
bodyPath,
|
|
@@ -21,21 +21,20 @@
|
|
|
21
21
|
partPaths,
|
|
22
22
|
showReady,
|
|
23
23
|
waiters,
|
|
24
|
-
} from './page-state';
|
|
24
|
+
} from './page-state.js';
|
|
25
25
|
import {
|
|
26
26
|
clamp,
|
|
27
27
|
clampOpt,
|
|
28
28
|
deviceDpr,
|
|
29
29
|
hashSeed,
|
|
30
|
+
inkFor,
|
|
30
31
|
lightsFor,
|
|
31
32
|
pointerStrength,
|
|
32
33
|
q,
|
|
33
34
|
reducedMotion,
|
|
34
35
|
resolveTheme as themeOf,
|
|
35
36
|
warmIdle,
|
|
36
|
-
} from './helpers';
|
|
37
|
-
|
|
38
|
-
type Props = AgentAvatarProps;
|
|
37
|
+
} from './helpers.js';
|
|
39
38
|
|
|
40
39
|
let {
|
|
41
40
|
name,
|
|
@@ -106,7 +105,7 @@
|
|
|
106
105
|
'aria-hidden': ariaHidden,
|
|
107
106
|
onclick: userClick,
|
|
108
107
|
...rest
|
|
109
|
-
}:
|
|
108
|
+
}: AgentAvatarProps = $props();
|
|
110
109
|
|
|
111
110
|
const shapePath = (t: BotAvatarType) => SHAPE_PATHS[t] ?? SHAPE_PATHS.clover;
|
|
112
111
|
|
|
@@ -140,7 +139,7 @@
|
|
|
140
139
|
vivid,
|
|
141
140
|
),
|
|
142
141
|
);
|
|
143
|
-
const inkColor = $derived(ink ??
|
|
142
|
+
const inkColor = $derived(ink ?? inkFor(body));
|
|
144
143
|
const seedValue = $derived(Math.min(1, Math.max(0, seed ?? hashSeed(uid))));
|
|
145
144
|
const stateKey = $derived((botState in stateLabels ? botState : 'default') as BotAvatarState);
|
|
146
145
|
const frozen = $derived(paused || !(speed > 0));
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import AgentAvatar from './agent-avatar.svelte';
|
|
3
|
-
import type { Agent, AgentState } from './types';
|
|
3
|
+
import type { Agent, AgentState } from './types.js';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
interface Props {
|
|
6
6
|
/** The cast to lay out. */
|
|
7
7
|
agents: Agent[];
|
|
8
8
|
/** Selected agent id, when the cast is a picker. */
|
package/src/agents/demo.svelte
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import AgentAvatar from './agent-avatar.svelte';
|
|
3
3
|
import AgentCast from './agent-cast.svelte';
|
|
4
|
-
import type { Agent, AgentState } from './types';
|
|
4
|
+
import type { Agent, AgentState } from './types.js';
|
|
5
5
|
|
|
6
6
|
const cast: Agent[] = [
|
|
7
7
|
{ id: 'ada', name: 'Ada', role: 'Researcher', shape: 'clover', color: '#41C4FF', group: 'Research' },
|
package/src/agents/helpers.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
/* Pure helpers for the avatar: numbers, theme, lights, idle scheduling. */
|
|
2
|
-
import {
|
|
2
|
+
import { DARK_INK, LIGHT_INK, luminance } from './vendor/color.js';
|
|
3
|
+
import { stockLights } from './vendor/plastic.js';
|
|
4
|
+
|
|
5
|
+
const contrast = (a: number, b: number) => (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
|
|
6
|
+
|
|
7
|
+
/** Off-black or off-white eyes, whichever reads stronger on the body: the vendor's fixed cutoff left 2:1 eyes on mid blues. */
|
|
8
|
+
export function inkFor(body: string): string {
|
|
9
|
+
const l = luminance(body);
|
|
10
|
+
return contrast(l, luminance(DARK_INK)) >= contrast(l, luminance(LIGHT_INK)) ? DARK_INK : LIGHT_INK;
|
|
11
|
+
}
|
|
3
12
|
|
|
4
13
|
export const clamp = (v: number, lo: number, hi: number) =>
|
|
5
14
|
Math.min(hi, Math.max(lo, Number.isFinite(v) ? v : 1));
|
package/src/agents/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { default as AgentAvatar } from './agent-avatar.svelte';
|
|
2
2
|
export { default as AgentCast } from './agent-cast.svelte';
|
|
3
|
-
export type { Agent, AgentAvatarProps, AgentShape } from './types';
|
|
3
|
+
export type { Agent, AgentAvatarProps, AgentShape } from './types.js';
|
|
4
4
|
export {
|
|
5
5
|
agentPresets,
|
|
6
6
|
agentShapes,
|
|
@@ -8,4 +8,4 @@ export {
|
|
|
8
8
|
agentStates,
|
|
9
9
|
agentPalette,
|
|
10
10
|
agentStateLabels,
|
|
11
|
-
} from './types';
|
|
11
|
+
} from './types.js';
|
package/src/agents/page-state.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* What every avatar on the page shares: the batching shown-gate, so a row of
|
|
2
2
|
them comes in together, the path caches, and one theme watcher. */
|
|
3
|
-
import { bakesPending } from './vendor/plastic';
|
|
3
|
+
import { bakesPending } from './vendor/plastic.js';
|
|
4
4
|
|
|
5
5
|
export const SHOW_WITHIN = 4000;
|
|
6
6
|
export const waiters = new Map<object, { ready: () => boolean; show: () => void }>();
|
package/src/agents/types.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BotAvatarFace, BotAvatarProps, BotAvatarType } from './vendor/types';
|
|
1
|
+
import type { BotAvatarFace, BotAvatarProps, BotAvatarType } from './vendor/types.js';
|
|
2
2
|
|
|
3
3
|
/** The body shapes an agent can be drawn as. */
|
|
4
4
|
export type AgentShape = BotAvatarType;
|
|
@@ -11,7 +11,7 @@ export type {
|
|
|
11
11
|
BotAvatarHat as AgentHat,
|
|
12
12
|
BotAvatarGlasses as AgentGlasses,
|
|
13
13
|
BotAvatarPreset as AgentPreset,
|
|
14
|
-
} from './vendor/types';
|
|
14
|
+
} from './vendor/types.js';
|
|
15
15
|
|
|
16
16
|
export {
|
|
17
17
|
botAvatarPresets as agentPresets,
|
|
@@ -20,7 +20,7 @@ export {
|
|
|
20
20
|
botAvatarStates as agentStates,
|
|
21
21
|
botAvatarPalette as agentPalette,
|
|
22
22
|
stateLabels as agentStateLabels,
|
|
23
|
-
} from './vendor/presets';
|
|
23
|
+
} from './vendor/presets.js';
|
|
24
24
|
|
|
25
25
|
/** One member of a cast: a name, a role, and the face they draw. */
|
|
26
26
|
export type Agent = {
|
|
@@ -16,8 +16,10 @@ function render(props: Props) {
|
|
|
16
16
|
document.body.appendChild(target);
|
|
17
17
|
const app = mount(CopyButton, { target, props });
|
|
18
18
|
flushSync();
|
|
19
|
+
const button = target.querySelector("button");
|
|
20
|
+
if (!button) throw new Error("CopyButton rendered no button");
|
|
19
21
|
return {
|
|
20
|
-
button
|
|
22
|
+
button,
|
|
21
23
|
target,
|
|
22
24
|
done: () => {
|
|
23
25
|
void unmount(app);
|
|
@@ -5,7 +5,7 @@ import { cn } from "@heyramzi/utils";
|
|
|
5
5
|
import type { SortDirection, SortState } from "./data-table.js";
|
|
6
6
|
|
|
7
7
|
// ========================= TYPES
|
|
8
|
-
|
|
8
|
+
interface Props {
|
|
9
9
|
key: keyof T & string;
|
|
10
10
|
sort: SortState<T>;
|
|
11
11
|
onSort: (key: keyof T & string) => void;
|
|
@@ -50,7 +50,7 @@ function handleKeydown(e: KeyboardEvent) {
|
|
|
50
50
|
{:else if direction === "desc"}
|
|
51
51
|
<CaretDown class="size-3.5 text-foreground" weight="bold" />
|
|
52
52
|
{:else}
|
|
53
|
-
<CaretUpDown class="size-3.5 text-muted-foreground/
|
|
53
|
+
<CaretUpDown class="size-3.5 text-muted-foreground/75" />
|
|
54
54
|
{/if}
|
|
55
55
|
</span>
|
|
56
56
|
</th>
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import type { Component, Snippet } from "svelte";
|
|
6
|
+
import OnThisPage from "./on-this-page.svelte";
|
|
7
|
+
import {
|
|
8
|
+
ScrollSpy,
|
|
9
|
+
type DocHeading,
|
|
10
|
+
type LocateHeading,
|
|
11
|
+
} from "./scroll-spy.svelte.js";
|
|
12
|
+
|
|
13
|
+
// =========================
|
|
14
|
+
// TYPES
|
|
15
|
+
// =========================
|
|
16
|
+
interface Props {
|
|
17
|
+
title: string;
|
|
18
|
+
/** One line under the title. Optional: a page with nothing to add leaves it out. */
|
|
19
|
+
summary?: string;
|
|
20
|
+
icon?: Component<{ class?: string; weight?: "bold" }>;
|
|
21
|
+
/** Where the page lives, as a reader would say it: "Marketing / Reports". */
|
|
22
|
+
section?: string;
|
|
23
|
+
/** Already worded and localised: "Updated 30 Jul 2026". */
|
|
24
|
+
updated?: string;
|
|
25
|
+
/** Room at the right of the title for a page's own controls. */
|
|
26
|
+
actions?: Snippet;
|
|
27
|
+
/** The document's h2s, which the contents rail walks. Under two, there's no rail. */
|
|
28
|
+
headings?: DocHeading[];
|
|
29
|
+
/** The rail's name: "On this page". */
|
|
30
|
+
contentsLabel: string;
|
|
31
|
+
/** Supplied by a document that lives in a frame; the default reads the DOM by id. */
|
|
32
|
+
locate?: LocateHeading;
|
|
33
|
+
/** Changes when the page is swapped without navigation, so the pane returns to the top. */
|
|
34
|
+
resetKey?: string;
|
|
35
|
+
/** The reading column's width. `wide` is for a document that brings tables and boards. */
|
|
36
|
+
measure?: "prose" | "wide";
|
|
37
|
+
/** The scroll box, for a document that has to place things inside it. */
|
|
38
|
+
scroller?: HTMLElement | null;
|
|
39
|
+
children: Snippet;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// =========================
|
|
43
|
+
// PROPS
|
|
44
|
+
// =========================
|
|
45
|
+
let {
|
|
46
|
+
title,
|
|
47
|
+
summary,
|
|
48
|
+
icon: Icon,
|
|
49
|
+
section,
|
|
50
|
+
updated,
|
|
51
|
+
actions,
|
|
52
|
+
headings = [],
|
|
53
|
+
contentsLabel,
|
|
54
|
+
locate,
|
|
55
|
+
resetKey,
|
|
56
|
+
measure = "prose",
|
|
57
|
+
scroller = $bindable(null),
|
|
58
|
+
children,
|
|
59
|
+
}: Props = $props();
|
|
60
|
+
|
|
61
|
+
// =========================
|
|
62
|
+
// STATE
|
|
63
|
+
// =========================
|
|
64
|
+
const spy = new ScrollSpy();
|
|
65
|
+
|
|
66
|
+
// DERIVED
|
|
67
|
+
let hasRail = $derived(headings.length > 1);
|
|
68
|
+
let meta = $derived([section, updated].filter(Boolean) as string[]);
|
|
69
|
+
|
|
70
|
+
// =========================
|
|
71
|
+
// EFFECTS
|
|
72
|
+
// =========================
|
|
73
|
+
$effect(() => spy.attach(scroller, headings, locate));
|
|
74
|
+
|
|
75
|
+
$effect(() => {
|
|
76
|
+
void resetKey;
|
|
77
|
+
scroller?.scrollTo({ top: 0 });
|
|
78
|
+
});
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<!-- =========================== -->
|
|
82
|
+
<!-- MARKUP -->
|
|
83
|
+
<!-- =========================== -->
|
|
84
|
+
<!-- The anatomy, once: header, then the reading column with its contents rail.
|
|
85
|
+
A surface supplies content and never lays this out, so two pages can't
|
|
86
|
+
arrive with different layouts. The pane is its own container, since the
|
|
87
|
+
room left after the map is what decides the rail, not the window. -->
|
|
88
|
+
<div
|
|
89
|
+
bind:this={scroller}
|
|
90
|
+
class="doc-page @container/page thin-scrollbar relative min-h-0 flex-1 overflow-y-auto"
|
|
91
|
+
>
|
|
92
|
+
{#if hasRail}
|
|
93
|
+
<!-- Below the rail's width the contents sit on a strip that stays put. -->
|
|
94
|
+
<div class="sticky top-0 z-20 border-b border-border bg-background/95 backdrop-blur @3xl/page:hidden">
|
|
95
|
+
<OnThisPage
|
|
96
|
+
variant="bar"
|
|
97
|
+
items={headings}
|
|
98
|
+
active={spy.active}
|
|
99
|
+
label={contentsLabel}
|
|
100
|
+
onselect={(id) => spy.jump(id)}
|
|
101
|
+
/>
|
|
102
|
+
</div>
|
|
103
|
+
{/if}
|
|
104
|
+
|
|
105
|
+
<div
|
|
106
|
+
class="mx-auto flex w-full gap-10 px-5 @2xl/page:px-8 {measure === 'wide'
|
|
107
|
+
? 'max-w-[84rem]'
|
|
108
|
+
: 'max-w-[68rem]'}"
|
|
109
|
+
>
|
|
110
|
+
<article class="min-w-0 flex-1 pb-20 pt-8 @2xl/page:pt-10">
|
|
111
|
+
<header class="mb-8 border-b border-border pb-6">
|
|
112
|
+
<div class="flex items-start gap-4">
|
|
113
|
+
{#if Icon}
|
|
114
|
+
<span
|
|
115
|
+
class="flex size-11 shrink-0 items-center justify-center rounded-xl bg-indigo-100 text-brand-indigo dark:bg-indigo-800/30 dark:text-brand-indigo-light"
|
|
116
|
+
>
|
|
117
|
+
<Icon class="size-5" weight="bold" />
|
|
118
|
+
</span>
|
|
119
|
+
{/if}
|
|
120
|
+
<div class="min-w-0 flex-1">
|
|
121
|
+
<h1
|
|
122
|
+
class="font-heading text-2xl font-semibold leading-tight tracking-tight text-balance text-foreground @2xl/page:text-[1.75rem]"
|
|
123
|
+
>
|
|
124
|
+
{title}
|
|
125
|
+
</h1>
|
|
126
|
+
{#if summary}
|
|
127
|
+
<p class="mt-1.5 max-w-[60ch] text-[15px] leading-relaxed text-muted-foreground text-pretty">
|
|
128
|
+
{summary}
|
|
129
|
+
</p>
|
|
130
|
+
{/if}
|
|
131
|
+
{#if meta.length > 0}
|
|
132
|
+
<p class="mt-3 flex flex-wrap items-center gap-x-2 text-xs tabular-nums text-muted-foreground">
|
|
133
|
+
{#each meta as item, i (item)}
|
|
134
|
+
{#if i > 0}<span aria-hidden="true" class="size-0.5 rounded-full bg-current opacity-50"></span>{/if}
|
|
135
|
+
<span>{item}</span>
|
|
136
|
+
{/each}
|
|
137
|
+
</p>
|
|
138
|
+
{/if}
|
|
139
|
+
</div>
|
|
140
|
+
{#if actions}
|
|
141
|
+
<div class="flex shrink-0 items-center gap-2">{@render actions()}</div>
|
|
142
|
+
{/if}
|
|
143
|
+
</div>
|
|
144
|
+
</header>
|
|
145
|
+
|
|
146
|
+
{@render children()}
|
|
147
|
+
</article>
|
|
148
|
+
|
|
149
|
+
{#if hasRail}
|
|
150
|
+
<!-- `self-start`, since a sticky child only travels inside its own box. -->
|
|
151
|
+
<aside class="hidden w-48 shrink-0 self-start pt-10 @3xl/page:sticky @3xl/page:top-0 @3xl/page:block">
|
|
152
|
+
<OnThisPage
|
|
153
|
+
items={headings}
|
|
154
|
+
active={spy.active}
|
|
155
|
+
label={contentsLabel}
|
|
156
|
+
onselect={(id) => spy.jump(id)}
|
|
157
|
+
/>
|
|
158
|
+
</aside>
|
|
159
|
+
{/if}
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The documentation anatomy: one page component and the contents rail inside it.
|
|
3
|
+
* A surface supplies a title and a body, and never lays a document out itself.
|
|
4
|
+
*/
|
|
5
|
+
export { default as DocPage } from "./doc-page.svelte";
|
|
6
|
+
export { default as OnThisPage } from "./on-this-page.svelte";
|
|
7
|
+
export {
|
|
8
|
+
ScrollSpy,
|
|
9
|
+
READ_LINE,
|
|
10
|
+
type DocHeading,
|
|
11
|
+
type LocateHeading,
|
|
12
|
+
} from "./scroll-spy.svelte.js";
|