@heyramzi/ui 1.53.0 → 1.53.1
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/package.json +1 -1
- 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 +12 -14
- package/src/agents/agent-cast.svelte +2 -2
- package/src/agents/demo.svelte +1 -1
- package/src/agents/helpers.ts +1 -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 +1 -1
- package/src/components/data-table/table-skeleton.svelte +1 -1
- 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 +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/segmented/animated-tabs.svelte +2 -2
- package/src/components/segmented/segment.svelte +2 -2
- package/src/components/segmented/segmented-filter.svelte +1 -1
- 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 +9 -4
- package/src/components/star-rating-input/star-rating-input.svelte +11 -4
- 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 -1
- package/src/icons/trades.ts +3 -3
- package/src/lib/sliding-pill.ts +3 -3
package/package.json
CHANGED
|
@@ -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 { autoInk, richer, shade } from './vendor/color';
|
|
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 { autoInk, 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,7 +21,7 @@
|
|
|
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,
|
|
@@ -33,9 +33,7 @@
|
|
|
33
33
|
reducedMotion,
|
|
34
34
|
resolveTheme as themeOf,
|
|
35
35
|
warmIdle,
|
|
36
|
-
} from './helpers';
|
|
37
|
-
|
|
38
|
-
type Props = AgentAvatarProps;
|
|
36
|
+
} from './helpers.js';
|
|
39
37
|
|
|
40
38
|
let {
|
|
41
39
|
name,
|
|
@@ -106,7 +104,7 @@
|
|
|
106
104
|
'aria-hidden': ariaHidden,
|
|
107
105
|
onclick: userClick,
|
|
108
106
|
...rest
|
|
109
|
-
}:
|
|
107
|
+
}: AgentAvatarProps = $props();
|
|
110
108
|
|
|
111
109
|
const shapePath = (t: BotAvatarType) => SHAPE_PATHS[t] ?? SHAPE_PATHS.clover;
|
|
112
110
|
|
|
@@ -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,5 @@
|
|
|
1
1
|
/* Pure helpers for the avatar: numbers, theme, lights, idle scheduling. */
|
|
2
|
-
import { stockLights } from './vendor/plastic';
|
|
2
|
+
import { stockLights } from './vendor/plastic.js';
|
|
3
3
|
|
|
4
4
|
export const clamp = (v: number, lo: number, hi: number) =>
|
|
5
5
|
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;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
7
7
|
|
|
8
8
|
// ========================= TYPES
|
|
9
|
-
|
|
9
|
+
interface Props extends Omit<HTMLButtonAttributes, "class"> {
|
|
10
10
|
duration?: number;
|
|
11
11
|
onConfirm: () => void;
|
|
12
12
|
disabled?: boolean;
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
class?: string;
|
|
20
20
|
idle: Snippet;
|
|
21
21
|
holding: Snippet;
|
|
22
|
-
}
|
|
22
|
+
}
|
|
23
23
|
|
|
24
24
|
// ========================= PROPS
|
|
25
25
|
let {
|
|
@@ -9,7 +9,7 @@ import type { PhosphorIcon, IconWeight } from "../../lib/icon-types.js";
|
|
|
9
9
|
// =========================
|
|
10
10
|
// TYPES
|
|
11
11
|
// =========================
|
|
12
|
-
|
|
12
|
+
interface Props {
|
|
13
13
|
// Shown when nothing is picked or the board is empty. Both mean "content
|
|
14
14
|
// goes here", so both get a dashed `border-2` edge.
|
|
15
15
|
icon?: PhosphorIcon;
|
|
@@ -8,7 +8,7 @@ import { cn } from "@heyramzi/utils";
|
|
|
8
8
|
// =========================
|
|
9
9
|
// TYPES
|
|
10
10
|
// =========================
|
|
11
|
-
|
|
11
|
+
interface Props {
|
|
12
12
|
// Answers three questions in one order: what is this, what state is it in,
|
|
13
13
|
// what can I do with it. Each queue page used to order them differently.
|
|
14
14
|
title: string;
|
|
@@ -11,7 +11,7 @@ import type { PhosphorIcon } from "../../lib/icon-types.js";
|
|
|
11
11
|
// =========================
|
|
12
12
|
// TYPES
|
|
13
13
|
// =========================
|
|
14
|
-
|
|
14
|
+
interface Props {
|
|
15
15
|
// One panel per idea in a detail pane. Hand-rolled copies drifted between
|
|
16
16
|
// 8px and 12px corners, so rounded-xl lives here once.
|
|
17
17
|
label?: string;
|
|
@@ -8,7 +8,7 @@ import { cn } from "@heyramzi/utils";
|
|
|
8
8
|
// =========================
|
|
9
9
|
// TYPES
|
|
10
10
|
// =========================
|
|
11
|
-
|
|
11
|
+
interface Props {
|
|
12
12
|
// A hairline band inside `DetailHeader` or `DetailPanel` for the record's
|
|
13
13
|
// own facts, so the page never nests cards inside cards.
|
|
14
14
|
muted?: boolean;
|
|
@@ -8,7 +8,7 @@ import { cn } from "@heyramzi/utils";
|
|
|
8
8
|
// =========================
|
|
9
9
|
// TYPES
|
|
10
10
|
// =========================
|
|
11
|
-
|
|
11
|
+
interface Props {
|
|
12
12
|
// A flat stack of panels at one gap. `xl:pr-1` keeps the top panel's
|
|
13
13
|
// corner clear of the shell's scrollbar.
|
|
14
14
|
|
|
@@ -12,7 +12,7 @@ import { MASTER_DETAIL_PANE, type MasterDetailPane } from "./queue-rail.js";
|
|
|
12
12
|
// =========================
|
|
13
13
|
// TYPES
|
|
14
14
|
// =========================
|
|
15
|
-
|
|
15
|
+
interface Props {
|
|
16
16
|
// The list rail (an <aside>) and the content pane. Pages style both; this
|
|
17
17
|
// shell owns the two columns and the min-h-0/min-w-0 scroll containment.
|
|
18
18
|
rail: Snippet;
|
|
@@ -10,7 +10,7 @@ import { stepTo } from "./step.js";
|
|
|
10
10
|
// =========================
|
|
11
11
|
// TYPES
|
|
12
12
|
// =========================
|
|
13
|
-
|
|
13
|
+
interface Props {
|
|
14
14
|
/** Ids in the order the list shows them. */
|
|
15
15
|
ids: readonly string[];
|
|
16
16
|
current: string | null | undefined;
|
|
@@ -8,7 +8,7 @@ import { cn } from "@heyramzi/utils";
|
|
|
8
8
|
// =========================
|
|
9
9
|
// TYPES
|
|
10
10
|
// =========================
|
|
11
|
-
|
|
11
|
+
interface Props {
|
|
12
12
|
// The scroller under `RailHead`. `min-h-0` makes a flex child scroll instead
|
|
13
13
|
// of growing its parent, and it's the line hand-rolled rails forgot.
|
|
14
14
|
children: Snippet;
|
|
@@ -9,7 +9,7 @@ import { cn } from "@heyramzi/utils";
|
|
|
9
9
|
// =========================
|
|
10
10
|
// TYPES
|
|
11
11
|
// =========================
|
|
12
|
-
|
|
12
|
+
interface Props {
|
|
13
13
|
// The fixed top of a queue rail: label, filter, search. A fixed height, so
|
|
14
14
|
// rows start on the same line on every queue page.
|
|
15
15
|
label?: string;
|
|
@@ -10,7 +10,7 @@ import { MASTER_DETAIL_PANE, type MasterDetailPane } from "./queue-rail.js";
|
|
|
10
10
|
// =========================
|
|
11
11
|
// TYPES
|
|
12
12
|
// =========================
|
|
13
|
-
|
|
13
|
+
interface Props {
|
|
14
14
|
// One row shape for every queue rail, in a fixed order: identity, preview,
|
|
15
15
|
// facts. Three pages used to draw three different rows.
|
|
16
16
|
title: string;
|
|
@@ -9,7 +9,7 @@ import type { PhosphorIcon } from "../../lib/icon-types.js";
|
|
|
9
9
|
// =========================
|
|
10
10
|
// TYPES
|
|
11
11
|
// =========================
|
|
12
|
-
|
|
12
|
+
interface Props {
|
|
13
13
|
// The one label style for panel, rail group and sub-section headers. Retyped
|
|
14
14
|
// copies drifted to a different tracking value.
|
|
15
15
|
icon?: PhosphorIcon;
|
|
@@ -12,7 +12,7 @@ import { cn } from "@heyramzi/utils";
|
|
|
12
12
|
* A brand's mark at glyph size: a kit icon, a channel avatar, a client logo.
|
|
13
13
|
* One renderer, because three places each handled a missing file their own way.
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
interface Props {
|
|
16
16
|
/** The file. A missing or broken one falls back to the label's initial. */
|
|
17
17
|
src?: string;
|
|
18
18
|
/** Read for the fallback letter. The mark itself is decorative. */
|
|
@@ -18,7 +18,7 @@ import type { PickerOption } from "./types.js";
|
|
|
18
18
|
* wraps `Picker` because the kit strip and the channel row wrote the same two
|
|
19
19
|
* snippets.
|
|
20
20
|
*/
|
|
21
|
-
|
|
21
|
+
interface Props {
|
|
22
22
|
options: PickerOption<V>[];
|
|
23
23
|
value: V | null;
|
|
24
24
|
onSelect: (value: V) => void;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// It replaced four pickers in one sidebar, each built differently. Only one
|
|
13
13
|
// had search, only one had arrow keys, and the check mark sat in three places.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
interface Props {
|
|
16
16
|
/** Flat list. Mutually exclusive with `groups`. */
|
|
17
17
|
options?: PickerOption<V>[];
|
|
18
18
|
/** Titled runs, when the menu has a shape worth naming. */
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
// WHY re-declare `value` and `onValueChange`: the primitive types them as a
|
|
41
41
|
// bare `string`, so fourteen pages cast back to their union. Generic now, the
|
|
42
42
|
// one narrowing happens here.
|
|
43
|
-
|
|
43
|
+
interface Props extends Omit<TabsPrimitive.RootProps, "value" | "onValueChange"> {
|
|
44
44
|
tabs: readonly TabDef<T>[];
|
|
45
45
|
value?: T;
|
|
46
46
|
onValueChange?: (value: T) => void;
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
max?: number;
|
|
68
68
|
// What the menu segment says. Translate at the call site.
|
|
69
69
|
moreLabel?: string;
|
|
70
|
-
}
|
|
70
|
+
}
|
|
71
71
|
|
|
72
72
|
let {
|
|
73
73
|
ref = $bindable(null),
|
|
@@ -36,13 +36,13 @@ import { Mark } from "../mark/index.js";
|
|
|
36
36
|
// =========================
|
|
37
37
|
// TYPES
|
|
38
38
|
// =========================
|
|
39
|
-
|
|
39
|
+
interface Props extends SegmentContent {
|
|
40
40
|
/**
|
|
41
41
|
* `true`/`false` when the caller owns the state. `"auto"` when bits-ui owns it
|
|
42
42
|
* in a `data-state` on the trigger, and the stylesheet answers.
|
|
43
43
|
*/
|
|
44
44
|
active?: boolean | "auto";
|
|
45
|
-
}
|
|
45
|
+
}
|
|
46
46
|
|
|
47
47
|
// =========================
|
|
48
48
|
// PROPS
|
|
@@ -13,7 +13,7 @@ import { segmentedTriggerClass } from "./segmented.js";
|
|
|
13
13
|
// =========================
|
|
14
14
|
// TYPES
|
|
15
15
|
// =========================
|
|
16
|
-
|
|
16
|
+
interface Props {
|
|
17
17
|
// WHY: picks which rows are listed, with no panel of its own. That's a
|
|
18
18
|
// segmented control, not a tab set.
|
|
19
19
|
|
|
@@ -17,7 +17,7 @@ import { segmentedTriggerClass } from "./segmented.js";
|
|
|
17
17
|
// =========================
|
|
18
18
|
// TYPES
|
|
19
19
|
// =========================
|
|
20
|
-
|
|
20
|
+
interface Props {
|
|
21
21
|
// WHY a third component: tabs are a `tablist`, filters a `group` of toggles,
|
|
22
22
|
// and this goes to sibling pages, so it's a `nav` of links with `aria-current`.
|
|
23
23
|
|
|
@@ -18,7 +18,7 @@ import type { SegmentContent } from "./segment.svelte";
|
|
|
18
18
|
|
|
19
19
|
// WHY never active: `splitSegments` pulls the current item into a visible slot,
|
|
20
20
|
// so this never fights bits-ui's `data-state="open"`.
|
|
21
|
-
|
|
21
|
+
interface Props {
|
|
22
22
|
items: readonly (SegmentContent & { value: V })[];
|
|
23
23
|
onSelect: (value: V) => void;
|
|
24
24
|
/** What the segment says. Translate at the call site. */
|
|
@@ -10,7 +10,7 @@ import { slidingPill } from "../../lib/sliding-pill.js";
|
|
|
10
10
|
// =========================
|
|
11
11
|
// TYPES
|
|
12
12
|
// =========================
|
|
13
|
-
|
|
13
|
+
interface Props {
|
|
14
14
|
/** Fill the row rather than sit at its natural width. */
|
|
15
15
|
fill?: boolean;
|
|
16
16
|
/** `pill`, a bordered well with a travelling highlight, or `underline`, a travelling rule. */
|
|
@@ -86,12 +86,17 @@ let {
|
|
|
86
86
|
.track :global(.ui-segment[data-state="active"] + .ui-segment)::before,
|
|
87
87
|
.track :global(.ui-segment[aria-current="page"] + .ui-segment)::before,
|
|
88
88
|
.track :global(.ui-segment[aria-pressed="true"] + .ui-segment)::before,
|
|
89
|
-
.track :global([data-slot="tabs-list"]:has(> .ui-segment:last-child[data-state="active"]) + .ui-segment)::before
|
|
90
|
-
.track :global(.ui-segment:hover)::before,
|
|
91
|
-
.track :global(.ui-segment:hover + .ui-segment)::before {
|
|
89
|
+
.track :global([data-slot="tabs-list"]:has(> .ui-segment:last-child[data-state="active"]) + .ui-segment)::before {
|
|
92
90
|
opacity: 0;
|
|
93
91
|
}
|
|
94
92
|
|
|
93
|
+
@media (hover: hover) and (pointer: fine) {
|
|
94
|
+
.track :global(.ui-segment:hover)::before,
|
|
95
|
+
.track :global(.ui-segment:hover + .ui-segment)::before {
|
|
96
|
+
opacity: 0;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
95
100
|
/* The label's colour lands with the pill, on the same fast step. */
|
|
96
101
|
.track :global(.ui-segment) {
|
|
97
102
|
transition:
|
|
@@ -25,7 +25,7 @@ const POP_WINDOW_MS = 350 + 4 * 40;
|
|
|
25
25
|
import { Star } from "phosphor-svelte";
|
|
26
26
|
import { cn } from "@heyramzi/utils";
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
interface Props {
|
|
29
29
|
/** Current rating value (0 = unrated). */
|
|
30
30
|
value: number;
|
|
31
31
|
/** Maximum rating. Defaults to 5. */
|
|
@@ -187,8 +187,10 @@ function handleLeave() {
|
|
|
187
187
|
box-shadow: 0 0 0 2px var(--color-ring);
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
@media (hover: hover) and (pointer: fine) {
|
|
191
|
+
.hit-button:not(.is-disabled):hover {
|
|
192
|
+
transform: scale(1.1);
|
|
193
|
+
}
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
.hit-button:not(.is-disabled):active {
|
|
@@ -248,9 +250,14 @@ function handleLeave() {
|
|
|
248
250
|
transition: none;
|
|
249
251
|
}
|
|
250
252
|
|
|
251
|
-
.hit-button:not(.is-disabled):hover,
|
|
252
253
|
.hit-button:not(.is-disabled):active {
|
|
253
254
|
transform: none;
|
|
254
255
|
}
|
|
256
|
+
|
|
257
|
+
@media (hover: hover) and (pointer: fine) {
|
|
258
|
+
.hit-button:not(.is-disabled):hover {
|
|
259
|
+
transform: none;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
255
262
|
}
|
|
256
263
|
</style>
|
|
@@ -43,7 +43,7 @@ export function usePathCalculator() {
|
|
|
43
43
|
|
|
44
44
|
// A cubic with horizontal handles, not a quadratic through a raised midpoint.
|
|
45
45
|
|
|
46
|
-
// The quadratic put its single control point at `midY
|
|
46
|
+
// The quadratic put its single control point at `midY` minus `curvature`, which for a fan into one node lands past the far end:
|
|
47
47
|
|
|
48
48
|
// the top beam dove below the hub and crossed the middle beam before arriving, and three beams bundled into a lens for the last stretch.
|
|
49
49
|
|
|
@@ -24,7 +24,7 @@ export interface BorderBeamProps extends Omit<HTMLAttributes<HTMLDivElement>, "c
|
|
|
24
24
|
// preset stop is mapped by index (cycling if shorter) so layout/timing
|
|
25
25
|
// stay identical, only hues swap. Accepts hex (#rrggbb), rgb(), rgba().
|
|
26
26
|
colors?: string[];
|
|
27
|
-
// WHY: the bloom layer is the single most expensive part of the beam
|
|
27
|
+
// WHY: the bloom layer is the single most expensive part of the beam: a blurred conic-gradient re-rasterized every frame.
|
|
28
28
|
|
|
29
29
|
// Opt-out gives a 2-3x perf win for callers who only need the stroke and inner shimmer.
|
|
30
30
|
bloom?: boolean;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/*
|
|
14
|
-
* irradiance * BRDF_Lambert(diffuse * (1
|
|
14
|
+
* irradiance * BRDF_Lambert(diffuse * (1 minus metalness)) = (brightness * PI) * (1 / PI) * (base * 0.8) = brightness * 0.8 * base
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
/*
|
package/src/icons/trades.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Trade icon map
|
|
2
|
+
* Trade icon map. Tabler covers the domain Lucide and Phosphor miss.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
/*
|
|
6
|
-
* Lucide/Phosphor gap audit (Sept 2026) against the 14 trade pills in the "NOT SURE? TAP WHAT YOU DO" strip
|
|
6
|
+
* Lucide/Phosphor gap audit (Sept 2026) against the 14 trade pills in the "NOT SURE? TAP WHAT YOU DO" strip. Lucide has `hammer`, `wrench`, `hard-hat`, `bolt`, `brick-wall`,
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
/*
|
|
10
|
-
* `building`, `car
|
|
10
|
+
* `building`, `car`, but no `trowel`, no tiling grid, no `window` cleaning `spray/squeegee`, no `crane`, no dedicated `handyman` toolset. Phosphor has `hammer`, `wrench`, `hard-hat`, `paint-roller`, and also no `trowel`.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/*
|
package/src/lib/sliding-pill.ts
CHANGED
|
@@ -18,8 +18,8 @@ export const slidingPill: Attachment<HTMLElement> = (pill) => {
|
|
|
18
18
|
|
|
19
19
|
let settled = false;
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
const active = list
|
|
21
|
+
const move = (animate: boolean) => {
|
|
22
|
+
const active = list.querySelector<HTMLElement>(ACTIVE);
|
|
23
23
|
if (!active) {
|
|
24
24
|
pill.style.opacity = "0";
|
|
25
25
|
return;
|
|
@@ -37,7 +37,7 @@ export const slidingPill: Attachment<HTMLElement> = (pill) => {
|
|
|
37
37
|
pill.style.width = `${active.offsetWidth}px`;
|
|
38
38
|
void pill.offsetWidth;
|
|
39
39
|
pill.style.transition = "";
|
|
40
|
-
}
|
|
40
|
+
};
|
|
41
41
|
|
|
42
42
|
// Position first, THEN hand the highlight over. Swapped, one frame draws both
|
|
43
43
|
// the trigger's own background and a zero-width pill: a flicker on hydration.
|