@nadicodeai/ui 12.1.136 → 12.1.138
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/dist/components/agent-orb.d.ts +5 -1
- package/dist/components/agent-orb.d.ts.map +1 -1
- package/dist/components/agent-orb.js +5 -1
- package/dist/components/brand.d.ts +5 -5
- package/dist/components/brand.d.ts.map +1 -1
- package/dist/components/brand.js +5 -5
- package/dist/components/catalog-entry.d.ts +5 -4
- package/dist/components/catalog-entry.d.ts.map +1 -1
- package/dist/components/catalog-entry.js +4 -3
- package/dist/components/company-card/renderer.d.ts +8 -0
- package/dist/components/company-card/renderer.d.ts.map +1 -1
- package/dist/components/company-card/renderer.js +122 -53
- package/dist/components/company-card.d.ts.map +1 -1
- package/dist/components/company-card.js +70 -39
- package/dist/components/hive-scene.d.ts.map +1 -1
- package/dist/components/hive-scene.js +46 -8
- package/dist/components/identity-mark.d.ts +4 -4
- package/dist/components/identity-mark.d.ts.map +1 -1
- package/dist/components/identity-mark.js +2 -3
- package/dist/components/scroll-area.js +3 -3
- package/dist/components/settings-section.d.ts +26 -15
- package/dist/components/settings-section.d.ts.map +1 -1
- package/dist/components/settings-section.js +23 -16
- package/dist/components/switch.d.ts +2 -1
- package/dist/components/switch.d.ts.map +1 -1
- package/dist/components/switch.js +3 -2
- package/dist/components/theme-mode-switcher.d.ts +1 -1
- package/dist/components/theme-mode-switcher.js +1 -1
- package/dist/internal/agent-orb-motion.d.ts +4 -0
- package/dist/internal/agent-orb-motion.d.ts.map +1 -1
- package/dist/internal/agent-orb-motion.js +1 -0
- package/dist/internal/agent-orb.d.ts +1 -0
- package/dist/internal/agent-orb.d.ts.map +1 -1
- package/dist/internal/agent-orb.js +207 -86
- package/dist/showcase/specimens/compositions.js +1 -1
- package/dist/showcase/specimens/inputs.d.ts.map +1 -1
- package/dist/showcase/specimens/inputs.js +2 -2
- package/dist/showcase/specimens/status.js +2 -2
- package/dist/showcase/usage.generated.d.ts.map +1 -1
- package/dist/showcase/usage.generated.js +13 -13
- package/dist/standalone/index.js +2 -2
- package/package.json +2 -2
|
@@ -239,18 +239,25 @@ function HiveScene({ mode, column = "default", logo, picture, work, chrome, labe
|
|
|
239
239
|
drawComb(comb, width, height);
|
|
240
240
|
context = sizeCanvas(flow, width, height);
|
|
241
241
|
};
|
|
242
|
+
const observeLayout = (onLayout) => {
|
|
243
|
+
const resize = new ResizeObserver(onLayout);
|
|
244
|
+
resize.observe(scene);
|
|
245
|
+
if (wordsColumn)
|
|
246
|
+
resize.observe(wordsColumn);
|
|
247
|
+
return resize;
|
|
248
|
+
};
|
|
242
249
|
place();
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
if (wordsColumn)
|
|
246
|
-
resize.observe(wordsColumn);
|
|
247
|
-
if (still)
|
|
250
|
+
if (still) {
|
|
251
|
+
const resize = observeLayout(place);
|
|
248
252
|
return () => resize.disconnect();
|
|
253
|
+
}
|
|
249
254
|
const colours = orbRefs.current.map((canvas) => canvas ? getComputedStyle(canvas).color : "white");
|
|
250
255
|
const particles = [];
|
|
251
256
|
let frame = 0;
|
|
252
|
-
let last =
|
|
257
|
+
let last = null;
|
|
253
258
|
let nextSpawn = 0;
|
|
259
|
+
// A browser that cannot tell whether the scene is seen shows it as seen.
|
|
260
|
+
let onScreen = typeof IntersectionObserver === "undefined";
|
|
254
261
|
const spawn = () => {
|
|
255
262
|
const source = Math.floor(Math.random() * AGENTS.length);
|
|
256
263
|
const seat = seats[source];
|
|
@@ -287,8 +294,11 @@ function HiveScene({ mode, column = "default", logo, picture, work, chrome, labe
|
|
|
287
294
|
trail: [],
|
|
288
295
|
});
|
|
289
296
|
};
|
|
297
|
+
const flowing = () => context !== null && !reduced.matches && seats.some((seat) => seat !== null);
|
|
298
|
+
const running = () => onScreen && !document.hidden && (particles.length > 0 || flowing());
|
|
290
299
|
const tick = (now) => {
|
|
291
|
-
|
|
300
|
+
frame = 0;
|
|
301
|
+
const dt = last === null ? 0 : Math.min(0.05, (now - last) / 1000);
|
|
292
302
|
last = now;
|
|
293
303
|
nextSpawn -= dt;
|
|
294
304
|
const paint = context;
|
|
@@ -322,12 +332,40 @@ function HiveScene({ mode, column = "default", logo, picture, work, chrome, labe
|
|
|
322
332
|
}
|
|
323
333
|
paint.globalAlpha = 1;
|
|
324
334
|
}
|
|
335
|
+
if (running())
|
|
336
|
+
frame = requestAnimationFrame(tick);
|
|
337
|
+
};
|
|
338
|
+
const play = () => {
|
|
339
|
+
if (!running()) {
|
|
340
|
+
cancelAnimationFrame(frame);
|
|
341
|
+
frame = 0;
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
if (frame)
|
|
345
|
+
return;
|
|
346
|
+
last = null;
|
|
325
347
|
frame = requestAnimationFrame(tick);
|
|
326
348
|
};
|
|
327
|
-
|
|
349
|
+
const resize = observeLayout(() => {
|
|
350
|
+
place();
|
|
351
|
+
play();
|
|
352
|
+
});
|
|
353
|
+
const visibility = onScreen
|
|
354
|
+
? undefined
|
|
355
|
+
: new IntersectionObserver((entries) => {
|
|
356
|
+
onScreen = entries.at(-1)?.isIntersecting ?? false;
|
|
357
|
+
play();
|
|
358
|
+
});
|
|
359
|
+
visibility?.observe(scene);
|
|
360
|
+
document.addEventListener("visibilitychange", play);
|
|
361
|
+
reduced.addEventListener("change", play);
|
|
362
|
+
play();
|
|
328
363
|
return () => {
|
|
329
364
|
cancelAnimationFrame(frame);
|
|
330
365
|
resize.disconnect();
|
|
366
|
+
visibility?.disconnect();
|
|
367
|
+
document.removeEventListener("visibilitychange", play);
|
|
368
|
+
reduced.removeEventListener("change", play);
|
|
331
369
|
};
|
|
332
370
|
}, [mode, still, wordsColumn]);
|
|
333
371
|
return (_jsxs("div", { ...props, className: cn("nc-hive dark", className), "data-mode": mode, "data-slot": "hive-scene", ref: sceneRef, ...(mode === "loading" ? { "aria-label": label, role: "status" } : {}), children: [chrome, _jsx("img", { alt: logo.label, className: "nc-hive-logo", "data-slot": "hive-logo", src: logo.src }), picture, _jsx("canvas", { "aria-hidden": "true", className: "nc-hive-comb", ref: combRef }), _jsx("canvas", { "aria-hidden": "true", className: "nc-hive-flow", ref: flowRef }), still
|
|
@@ -42,11 +42,11 @@ export type IdentityMarkProps = {
|
|
|
42
42
|
image?: string;
|
|
43
43
|
} | {
|
|
44
44
|
kind: "record";
|
|
45
|
-
/** The
|
|
45
|
+
/** The record's own icon when it names one, as a skill does in its hub card; otherwise its kind's, the one its section has in the navigation: Mail for a mailbox. */
|
|
46
46
|
icon: IconComponent;
|
|
47
47
|
/** A catalogue kind's ground; neutral is the ringed card ground. */
|
|
48
|
-
tone?: "neutral" | "
|
|
49
|
-
/** The record's own object picture, a transparent image drawn on its kind's ground in place of the icon. */
|
|
48
|
+
tone?: "neutral" | "plugin" | "teal";
|
|
49
|
+
/** The record's own object picture, a transparent image drawn on its kind's ground in place of the icon at 64; smaller marks draw the icon. */
|
|
50
50
|
picture?: string;
|
|
51
51
|
});
|
|
52
52
|
/**
|
|
@@ -58,7 +58,7 @@ export type IdentityMarkProps = {
|
|
|
58
58
|
* @family Status
|
|
59
59
|
* @when The first thing in a record's row (32), at the head of its object page (64), in a feed or a sidebar identity line (24), on a Library entry (40), for the company at the top of Nadia's list (28): wherever it tells a person which thing they are looking at.
|
|
60
60
|
* @instead An icon beside a card's title, a KPI's label or a section heading only repeats the words and is left out. A picture for a first use or a whole-page moment is the empty state's or the page's own. A model is its `ModelIdentity`; a maker's mark beside a model's name in a list of models is `ModelMark` at 24.
|
|
61
|
-
* @behaviour Static, except an Agent's orb, which moves with her state. An Agent is always her real orb from the orb engine, at the largest orb size not above the mark's, with no tile and no ring, never an avatar or a painted circle, and `frame` lets the product wrap that orb, such as in the transition that carries her to her page; an app keeps its own logo in its bordered tile on the page ground, or its first letter when it has no logo; a record that belongs to a model maker, such as a key held at that maker, is the maker's `ModelMark` at the mark's size, with its initials when LobeHub has no mark for it; a company is its own logo, contained whole in its original colours and proportions on a dark ringed tile, as Vercel shows a project's favicon, and until a logo is found, or when the stored logo does not load, its initials in white on that same tile, so a found logo only replaces the letters; a company's mark is the same whatever its standing and whichever directory opened it, and a Partner is said in words where its standing matters, never by a colour; a person is their picture, then their initials, in a round mark; a record with
|
|
61
|
+
* @behaviour Static, except an Agent's orb, which moves with her state. An Agent is always her real orb from the orb engine, at the largest orb size not above the mark's, with no tile and no ring, never an avatar or a painted circle, and `frame` lets the product wrap that orb, such as in the transition that carries her to her page; an app keeps its own logo in its bordered tile on the page ground, or its first letter when it has no logo; a record that belongs to a model maker, such as a key held at that maker, is the maker's `ModelMark` at the mark's size, with its initials when LobeHub has no mark for it; a company is its own logo, contained whole in its original colours and proportions on a dark ringed tile, as Vercel shows a project's favicon, and until a logo is found, or when the stored logo does not load, its initials in white on that same tile, so a found logo only replaces the letters; a company's mark is the same whatever its standing and whichever directory opened it, and a Partner is said in words where its standing matters, never by a colour; a person is their picture, then their initials, in a round mark; a record with no picture of its own takes its kind's own icon, the same icon its section has in the navigation (Mail for a mailbox, a server for an MCP server, a key for a password), in a ringed tile on the card ground; a skill takes the icon its hub card names, and the Skills icon when it names none, on that same neutral ringed tile, so the icon alone tells one skill from another and no colour stands for the kind; a plugin takes the `info-soft` ground with the Plugins icon, without a ring; a record's own object picture, such as a Library skill's or plugin's, is drawn only at 64, at the head of its own page, on its kind's ground in the icon's place, because at a row's size its detail does not read. A record tile takes `{rounded.sm}` (6 px) corners, `{rounded.md}` (8 px) at 40 and 64; a company's tile and an app's tile take `{rounded.md}` at every size.
|
|
62
62
|
* @words None of its own: the record's name stands beside it. Initials stand for a company or a person: the first letters of the name's first two words. An app with no logo takes the first letter of its name as a monogram, a model maker with no mark the initials of its slug, and no other kind takes letters.
|
|
63
63
|
* @access Decorative beside the record's name. Alone, as in a narrow rail, `label` names it.
|
|
64
64
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity-mark.d.ts","sourceRoot":"","sources":["../../src/components/identity-mark.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEnF,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,aAAa,EACnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAW,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAMnE,MAAM,MAAM,gBAAgB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAE3D,qHAAqH;AACrH,MAAM,MAAM,aAAa,GAAG,aAAa,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;CAC5C,CAAC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,4GAA4G;IAC5G,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,4GAA4G;IAC5G,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,CACA;IACE,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,EAAE;QAAE,IAAI,EAAE,cAAc,CAAC;QAAC,KAAK,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,aAAa,CAAA;KAAE,CAAC;IACrF,kGAAkG;IAClG,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,SAAS,CAAC;CAC1C,GACD,CAAC;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,CAAC,GAC7C;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,wFAAwF;IACxF,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,
|
|
1
|
+
{"version":3,"file":"identity-mark.d.ts","sourceRoot":"","sources":["../../src/components/identity-mark.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEnF,OAAO,EAEL,KAAK,cAAc,EAEnB,KAAK,aAAa,EACnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAW,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAMnE,MAAM,MAAM,gBAAgB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAE3D,qHAAqH;AACrH,MAAM,MAAM,aAAa,GAAG,aAAa,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;CAC5C,CAAC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,4GAA4G;IAC5G,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,4GAA4G;IAC5G,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,CACA;IACE,IAAI,EAAE,OAAO,CAAC;IACd,GAAG,EAAE;QAAE,IAAI,EAAE,cAAc,CAAC;QAAC,KAAK,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,aAAa,CAAA;KAAE,CAAC;IACrF,kGAAkG;IAClG,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,SAAS,CAAC;CAC1C,GACD,CAAC;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,CAAC,GAC7C;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,wFAAwF;IACxF,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAChD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,qKAAqK;IACrK,IAAI,EAAE,aAAa,CAAC;IACpB,oEAAoE;IACpE,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;IACrC,+IAA+I;IAC/I,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CACJ,CAAC;AA4DF;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,SAAS,CA6DhE"}
|
|
@@ -15,7 +15,6 @@ const MEASURES = {
|
|
|
15
15
|
};
|
|
16
16
|
const TONES = {
|
|
17
17
|
neutral: "bg-card text-muted-foreground ring-1 ring-border ring-inset",
|
|
18
|
-
skill: "bg-category-purple text-category-purple-foreground",
|
|
19
18
|
plugin: "bg-info-soft text-info-soft-foreground",
|
|
20
19
|
teal: "bg-category-teal text-category-teal-foreground",
|
|
21
20
|
};
|
|
@@ -39,7 +38,7 @@ function RecordTile({ label, size, tone, children, }) {
|
|
|
39
38
|
* @family Status
|
|
40
39
|
* @when The first thing in a record's row (32), at the head of its object page (64), in a feed or a sidebar identity line (24), on a Library entry (40), for the company at the top of Nadia's list (28): wherever it tells a person which thing they are looking at.
|
|
41
40
|
* @instead An icon beside a card's title, a KPI's label or a section heading only repeats the words and is left out. A picture for a first use or a whole-page moment is the empty state's or the page's own. A model is its `ModelIdentity`; a maker's mark beside a model's name in a list of models is `ModelMark` at 24.
|
|
42
|
-
* @behaviour Static, except an Agent's orb, which moves with her state. An Agent is always her real orb from the orb engine, at the largest orb size not above the mark's, with no tile and no ring, never an avatar or a painted circle, and `frame` lets the product wrap that orb, such as in the transition that carries her to her page; an app keeps its own logo in its bordered tile on the page ground, or its first letter when it has no logo; a record that belongs to a model maker, such as a key held at that maker, is the maker's `ModelMark` at the mark's size, with its initials when LobeHub has no mark for it; a company is its own logo, contained whole in its original colours and proportions on a dark ringed tile, as Vercel shows a project's favicon, and until a logo is found, or when the stored logo does not load, its initials in white on that same tile, so a found logo only replaces the letters; a company's mark is the same whatever its standing and whichever directory opened it, and a Partner is said in words where its standing matters, never by a colour; a person is their picture, then their initials, in a round mark; a record with
|
|
41
|
+
* @behaviour Static, except an Agent's orb, which moves with her state. An Agent is always her real orb from the orb engine, at the largest orb size not above the mark's, with no tile and no ring, never an avatar or a painted circle, and `frame` lets the product wrap that orb, such as in the transition that carries her to her page; an app keeps its own logo in its bordered tile on the page ground, or its first letter when it has no logo; a record that belongs to a model maker, such as a key held at that maker, is the maker's `ModelMark` at the mark's size, with its initials when LobeHub has no mark for it; a company is its own logo, contained whole in its original colours and proportions on a dark ringed tile, as Vercel shows a project's favicon, and until a logo is found, or when the stored logo does not load, its initials in white on that same tile, so a found logo only replaces the letters; a company's mark is the same whatever its standing and whichever directory opened it, and a Partner is said in words where its standing matters, never by a colour; a person is their picture, then their initials, in a round mark; a record with no picture of its own takes its kind's own icon, the same icon its section has in the navigation (Mail for a mailbox, a server for an MCP server, a key for a password), in a ringed tile on the card ground; a skill takes the icon its hub card names, and the Skills icon when it names none, on that same neutral ringed tile, so the icon alone tells one skill from another and no colour stands for the kind; a plugin takes the `info-soft` ground with the Plugins icon, without a ring; a record's own object picture, such as a Library skill's or plugin's, is drawn only at 64, at the head of its own page, on its kind's ground in the icon's place, because at a row's size its detail does not read. A record tile takes `{rounded.sm}` (6 px) corners, `{rounded.md}` (8 px) at 40 and 64; a company's tile and an app's tile take `{rounded.md}` at every size.
|
|
43
42
|
* @words None of its own: the record's name stands beside it. Initials stand for a company or a person: the first letters of the name's first two words. An app with no logo takes the first letter of its name as a monogram, a model maker with no mark the initials of its slug, and no other kind takes letters.
|
|
44
43
|
* @access Decorative beside the record's name. Alone, as in a narrow rail, `label` names it.
|
|
45
44
|
*/
|
|
@@ -60,6 +59,6 @@ export function IdentityMark(props) {
|
|
|
60
59
|
case "company":
|
|
61
60
|
return (_jsx(CompanyMark, { initialsSize: measures.initials, logo: props.logo, name: props.name, naming: naming(props.label), size: size }));
|
|
62
61
|
case "record":
|
|
63
|
-
return (_jsx(RecordTile, { label: props.label, size: size, tone: props.tone ?? "neutral", children: props.picture ? (_jsx("img", { alt: "", className: "size-[84%] object-contain", "data-slot": "identity-mark-picture", src: props.picture })) : (_jsx(props.icon, { "aria-hidden": "true", size: measures.icon })) }));
|
|
62
|
+
return (_jsx(RecordTile, { label: props.label, size: size, tone: props.tone ?? "neutral", children: props.picture && size === 64 ? (_jsx("img", { alt: "", className: "size-[84%] object-contain", "data-slot": "identity-mark-picture", src: props.picture })) : (_jsx(props.icon, { "aria-hidden": "true", size: measures.icon })) }));
|
|
64
63
|
}
|
|
65
64
|
}
|
|
@@ -6,9 +6,9 @@ import { ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon } fro
|
|
|
6
6
|
import { Button } from "./button.js";
|
|
7
7
|
import { cn } from "../lib/utils.js";
|
|
8
8
|
const viewportAxis = {
|
|
9
|
-
y: "overflow-x-hidden! [mask-image:linear-gradient(to_bottom,transparent,
|
|
10
|
-
x: "overflow-y-hidden! [mask-image:linear-gradient(to_right,transparent,
|
|
11
|
-
both: "[
|
|
9
|
+
y: "overflow-x-hidden! data-overflow-y-start:[mask-image:linear-gradient(to_bottom,transparent,black_2.5rem)] data-overflow-y-end:[mask-image:linear-gradient(to_top,transparent,black_2.5rem)] data-overflow-y-start:data-overflow-y-end:[mask-image:linear-gradient(to_bottom,transparent,black_2.5rem,black_calc(100%_-_2.5rem),transparent)]",
|
|
10
|
+
x: "overflow-y-hidden! data-overflow-x-start:[mask-image:linear-gradient(to_right,transparent,black_2.5rem)] data-overflow-x-end:[mask-image:linear-gradient(to_left,transparent,black_2.5rem)] data-overflow-x-start:data-overflow-x-end:[mask-image:linear-gradient(to_right,transparent,black_2.5rem,black_calc(100%_-_2.5rem),transparent)]",
|
|
11
|
+
both: "[--fade-x:linear-gradient(black,black)] [--fade-y:linear-gradient(black,black)] data-overflow-x-start:[--fade-x:linear-gradient(to_right,transparent,black_2.5rem)] data-overflow-x-end:[--fade-x:linear-gradient(to_left,transparent,black_2.5rem)] data-overflow-x-start:data-overflow-x-end:[--fade-x:linear-gradient(to_right,transparent,black_2.5rem,black_calc(100%_-_2.5rem),transparent)] data-overflow-y-start:[--fade-y:linear-gradient(to_bottom,transparent,black_2.5rem)] data-overflow-y-end:[--fade-y:linear-gradient(to_top,transparent,black_2.5rem)] data-overflow-y-start:data-overflow-y-end:[--fade-y:linear-gradient(to_bottom,transparent,black_2.5rem,black_calc(100%_-_2.5rem),transparent)] [mask-composite:intersect] [mask-image:var(--fade-y),var(--fade-x)]",
|
|
12
12
|
};
|
|
13
13
|
function scrollToNeighbour(viewport, content, axis, direction) {
|
|
14
14
|
const edges = (box) => axis === "y" ? { start: box.top, end: box.bottom } : { start: box.left, end: box.right };
|
|
@@ -81,7 +81,7 @@ export type SavingSettingsSectionProps = SettingsSectionBase & SavingSection;
|
|
|
81
81
|
* @family Compositions
|
|
82
82
|
* @when One setting a person changes on a SettingsPage or as a section of an ObjectPage: Personal details, Company identity, Notifications. A typed value takes `save`; a switch or a short reversible choice applies at once through its own control's change, with no Save. Also a plan-gated feature, a delete card, one read-only value (an ID in a read-only mono InputGroup with its copy button, no Save), and a paid quantity (its stepper field, the price as the description, a two-fact summary under it).
|
|
83
83
|
* @instead Read-only facts of an object are a SettingsFactsSection; a table or a list of records is a TableCard; Theme and Language live in the account menu; a record's edit in a list is a RowEditForm.
|
|
84
|
-
* @behaviour "Groups related form controls inside a bordered card with optional footer actions." (https://vercel.com/geist/fieldset) Ours: one setting per card
|
|
84
|
+
* @behaviour "Groups related form controls inside a bordered card with optional footer actions." (https://vercel.com/geist/fieldset) Ours: one setting per card, a single SettingsField with the description its only help, unless the card's description covers several settings: then they are its SettingsRows, switches and selects together. A typed value (a name, a URL, a server, a port, a key) gets its own card. A semantic subgroup of related controls inside a field, such as radios or checkboxes, is a `FieldSet` with a `FieldLegend`. Several settings in one card are declared by SettingsRows, never by classes at the call site. "Use Toggle for a single boolean setting where ON takes effect immediately, like `Password Protection` or `Auto-Cancel Builds`." (https://vercel.com/geist/toggle) "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) "Disable Toggle only when the action is impossible (missing plan, locked policy); pair with helper text or a `Tooltip` that names the resolver." (https://vercel.com/geist/toggle) Ours: a switch never applies on change; it is a row of the card, a draft until the footer's Save, as Vercel's settings cards are. A switch blocked from above is shown off and disabled, the footer saying why and never a tooltip. Required: the title and the description. The header's actions stand at the title's right; on a card narrower than 28 rem they keep the title's row and the description runs under both, as Vercel's cards read on a phone, and when the title's longest word would not fit beside them they move under the title, so they never cover it. The footer's left line (`footerHint`, `save.hint`) is optional and specific to the card, never a generic line repeated on every card: a limit, what a value is for, a plan it needs, a link; absent, it is nothing, and a card with neither a line nor a button has no footer. Ours: `save`: a typed value, one card with its own Save in the footer, always visible. The section owns the changed state: Save stays disabled until a field differs from what is saved and says `pendingLabel` while saving with Cancel beside it; a completed save calls `saved`, and the product raises one toast from its catalogue ("Completion toasts follow `{Noun} {past-participle}`: `Blob deleted`, `Domain added`, `Environment variable saved`. Never include `successfully`; the action name implies it." https://vercel.com/geist/toast), and Save returns to disabled, with no saved state in the footer. Ours: the section never raises a toast itself. A failed save keeps every value and shows under its field, or above the footer when no field owns it. `tone` marks a state with a command (a failed payment, a trial ending): a 1px border and a footer tinted in the hue with its line in it, the line saying the condition in words; title, line, fields and buttons stay neutral, an outline secondary then a default primary. A button takes the hue of its command, not of the card: `destructive` (red) for a permanent loss (Delete), `warning` (amber) for a disruptive command that can be undone (Pause, Archive). A delete card is `tone="error"` with the object's summary as its body; an archive card is neutral with a `warning` button in the footer. "Use a Note for inline contextual feedback next to the field, card, or section it describes: a region change warning above a region picker, a rate-limit notice next to a usage gauge." (https://vercel.com/geist/note) "One Note per concept." (https://vercel.com/geist/note) Ours: a message about the fields inside is a MessageNotice in the body, the card staying neutral. "Disabled checkboxes still need a Tooltip naming the reason; a greyed box with no explanation reads as a bug." (https://vercel.com/geist/checkbox) Ours: `lockedReason` (Geist's disabled wall) is the reason, written as the footer's line, not in a tooltip; it keeps the fields in view, inert with muted text, under a not-allowed cursor, the title and line unchanged; `unlock` is the way to get access, left out when nothing the person does unlocks it. A locked section has no `save` and no header `actions`.
|
|
85
85
|
* @words "Helper text is sentence case, one sentence with a trailing period, on a sibling element wired through `aria-describedby`." (https://vercel.com/geist/input) Ours: the description is that helper text for the whole card, and no field has its own. "Render an optional one-sentence description as a sibling under the label that explains ON only. Don’t describe OFF; it’s the negation." (https://vercel.com/geist/toggle) Ours: a header-switch card's description says what turning it on does. The title names the values ("Personal details"); the description says what changing them does, in one line; the footer line is a Learn more link, a limit ("Use 32 characters at most."), what stays unaffected, or what is blocked and why ("Ask an owner to change this."); Save and Saving come from the product's catalogue, and so does the toast a `saved` callback raises; a delete card's description opens "Permanently delete".
|
|
86
86
|
* @access "Provide an accessible name through `children`, `aria-label`, or `aria-labelledby`." (https://vercel.com/geist/toggle) Ours: the title is the section's h2 and names the form that saves it, and the header switch. The footer's line is one status, announced politely; a failure under a field marks the control invalid and describes it. Locked fields are inert, so the section's title, description and reason are what a screen reader meets.
|
|
87
87
|
*/
|
|
@@ -120,7 +120,7 @@ type SettingsFieldProps = SettingsFieldBase & ({
|
|
|
120
120
|
*
|
|
121
121
|
* @family Inputs
|
|
122
122
|
* @when The one value of a SettingsSection: a name, an address, a choice, a switch.
|
|
123
|
-
* @instead A field of a drawer's or a dialog's form is a Field from the form's own owner; a read-only fact is a SettingsFactsSection; several switches
|
|
123
|
+
* @instead A field of a drawer's or a dialog's form is a Field from the form's own owner; a read-only fact is a SettingsFactsSection; several switches and selects in one card are SettingsRows; a switch that is the whole card and applies at once is the section's `headerSwitch`.
|
|
124
124
|
* @behaviour "Helper text is sentence case, one sentence with a trailing period, on a sibling element wired through `aria-describedby`." (https://vercel.com/geist/input) Ours: label required, no help line: the card's description helps, and the (i) `info` is for a part of a value that needs a sentence. The control is 20rem wide under its label (`controlWidth`: `full` for a textarea, `natural` for a segmented control). `control="choice"` puts a checkbox or radio before its sentence; `control="switch"` puts the word for the switch's state after it and requires `stateLabel`. "Validate on blur, not on every keystroke; surface the message by passing a string to `error`." (https://vercel.com/geist/input) Ours: a validation `error` updates as the value is corrected, and it or a failed save about this field shows under the control and marks it invalid; colour sits on the control's edge and the message, never the label.
|
|
125
125
|
* @words "Labels are short Title Case nouns: `Project Name`, `Domain`, `Environment Variable Name`." (https://vercel.com/geist/input) Ours: sentence case, a noun for the value ("Display name"). "Validation names the field and the constraint, ends in a period, and skips `please` (`Project name is required.`, `Code must be 6 digits.`)." (https://vercel.com/geist/input) Ours: an error says what to do next; `info.text` is one sentence and `info.label` names its button ("About display name"); a switch shows its state word ("Enabled", "Disabled") beside it.
|
|
126
126
|
* @access "Don’t wrap a labelled `<Input>` in a `Tooltip`; put the explainer on a sibling icon button so the label stays announced." (https://vercel.com/geist/input) Ours: that sibling button is the (i) `info`. The control receives its id, `aria-invalid` and `aria-describedby` through `controlProps`, so the label, the info and the error are read with it. The info button has its own name and opens on focus.
|
|
@@ -138,6 +138,8 @@ type SettingsRowBase = {
|
|
|
138
138
|
type SettingsSwitchRow = SettingsRowBase & {
|
|
139
139
|
checked: boolean;
|
|
140
140
|
onCheckedChange: (checked: boolean) => void;
|
|
141
|
+
options?: never;
|
|
142
|
+
control?: never;
|
|
141
143
|
};
|
|
142
144
|
type SettingsSelectRow = SettingsRowBase & {
|
|
143
145
|
value: string;
|
|
@@ -146,30 +148,39 @@ type SettingsSelectRow = SettingsRowBase & {
|
|
|
146
148
|
value: string;
|
|
147
149
|
label: string;
|
|
148
150
|
}[];
|
|
151
|
+
checked?: never;
|
|
152
|
+
control?: never;
|
|
153
|
+
};
|
|
154
|
+
type SettingsControlRow = SettingsRowBase & {
|
|
155
|
+
/** A control whose usage page puts it in a settings row, such as ThemeModeSwitcher. */
|
|
156
|
+
control: (props: {
|
|
157
|
+
controlProps: SettingsFieldControlProps;
|
|
158
|
+
}) => React.ReactNode;
|
|
159
|
+
options?: never;
|
|
160
|
+
checked?: never;
|
|
161
|
+
};
|
|
162
|
+
type StateLabels = {
|
|
163
|
+
on: string;
|
|
164
|
+
off: string;
|
|
149
165
|
};
|
|
150
166
|
type SettingsRowsProps = {
|
|
151
|
-
kind: "switch";
|
|
152
167
|
/** The word beside each switch for its state: "Enabled" and "Disabled". */
|
|
153
|
-
stateLabels:
|
|
154
|
-
|
|
155
|
-
off: string;
|
|
156
|
-
};
|
|
157
|
-
rows: readonly SettingsSwitchRow[];
|
|
168
|
+
stateLabels: StateLabels;
|
|
169
|
+
rows: readonly (SettingsSwitchRow | SettingsSelectRow | SettingsControlRow)[];
|
|
158
170
|
} | {
|
|
159
|
-
kind: "select";
|
|
160
171
|
stateLabels?: never;
|
|
161
|
-
rows: readonly SettingsSelectRow[];
|
|
172
|
+
rows: readonly (SettingsSelectRow | SettingsControlRow)[];
|
|
162
173
|
};
|
|
163
174
|
/**
|
|
164
|
-
* Several switches
|
|
175
|
+
* Several switches and selects in one card: each row its label on the left and its control on the right.
|
|
165
176
|
*
|
|
166
177
|
* @family Inputs
|
|
167
|
-
* @when A card of
|
|
168
|
-
* @instead One setting is a SettingsField.
|
|
169
|
-
* @behaviour
|
|
178
|
+
* @when A card of switches and selects that the card's one description covers: a project's Git switches, a team's notification channels, a set of defaults, how long a computer stays awake beside whether the window goes to the tray.
|
|
179
|
+
* @instead One setting is a SettingsField. A text box is a field of its own card.
|
|
180
|
+
* @behaviour A row with `options` is a select, a row with `checked` a switch, and a row with `control` draws a control whose usage page puts it in a row, such as ThemeModeSwitcher; a card may hold any of them together, as Vercel's Root Directory card holds its switches; `stateLabels` is required once any row is a switch. Required on every row: the label and its `info` (an (i) with its sentence in a tooltip and on the control); no help line sits under a row. A switch row shows its state word beside the switch (`stateLabels`) and is controlled, so the word always follows the switch. Rows stand 16 px apart; below 28rem a row stacks its control under its label. "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) Ours: a switch is a draft the card's Save applies.
|
|
170
181
|
* @words "Title Case noun phrase, 1–4 words, naming what is true when ON: `Password Protection`, not `Enable Password Protection`." (https://vercel.com/geist/toggle) Ours: sentence case, a noun for what it controls ("Pull request comments"), the same for a switch as for a select. "Render an optional one-sentence description as a sibling under the label that explains ON only. Don’t describe OFF; it’s the negation." (https://vercel.com/geist/toggle) Ours: that sentence is `info.text`. "Options are Title Case when short and match canonical branding (`Next.js`, not `NextJS`); keep the same register across the list." (https://vercel.com/geist/select) Ours: a select's options are sentence case.
|
|
171
182
|
* @access Each row's control is named by the row's label and described by its info and its error. The info button has its own name and opens on focus.
|
|
172
183
|
*/
|
|
173
|
-
declare function SettingsRows(
|
|
184
|
+
declare function SettingsRows({ rows, stateLabels }: SettingsRowsProps): React.JSX.Element;
|
|
174
185
|
export { SettingsField, SettingsRows, SettingsSection };
|
|
175
186
|
//# sourceMappingURL=settings-section.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-section.d.ts","sourceRoot":"","sources":["../../src/components/settings-section.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"settings-section.d.ts","sourceRoot":"","sources":["../../src/components/settings-section.tsx"],"names":[],"mappings":"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"}
|
|
@@ -26,7 +26,7 @@ const ACTIONS_ON_TITLE_ROW_WHEN_NARROW = "@max-md/settings-section:flex @max-md/
|
|
|
26
26
|
* @family Compositions
|
|
27
27
|
* @when One setting a person changes on a SettingsPage or as a section of an ObjectPage: Personal details, Company identity, Notifications. A typed value takes `save`; a switch or a short reversible choice applies at once through its own control's change, with no Save. Also a plan-gated feature, a delete card, one read-only value (an ID in a read-only mono InputGroup with its copy button, no Save), and a paid quantity (its stepper field, the price as the description, a two-fact summary under it).
|
|
28
28
|
* @instead Read-only facts of an object are a SettingsFactsSection; a table or a list of records is a TableCard; Theme and Language live in the account menu; a record's edit in a list is a RowEditForm.
|
|
29
|
-
* @behaviour "Groups related form controls inside a bordered card with optional footer actions." (https://vercel.com/geist/fieldset) Ours: one setting per card
|
|
29
|
+
* @behaviour "Groups related form controls inside a bordered card with optional footer actions." (https://vercel.com/geist/fieldset) Ours: one setting per card, a single SettingsField with the description its only help, unless the card's description covers several settings: then they are its SettingsRows, switches and selects together. A typed value (a name, a URL, a server, a port, a key) gets its own card. A semantic subgroup of related controls inside a field, such as radios or checkboxes, is a `FieldSet` with a `FieldLegend`. Several settings in one card are declared by SettingsRows, never by classes at the call site. "Use Toggle for a single boolean setting where ON takes effect immediately, like `Password Protection` or `Auto-Cancel Builds`." (https://vercel.com/geist/toggle) "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) "Disable Toggle only when the action is impossible (missing plan, locked policy); pair with helper text or a `Tooltip` that names the resolver." (https://vercel.com/geist/toggle) Ours: a switch never applies on change; it is a row of the card, a draft until the footer's Save, as Vercel's settings cards are. A switch blocked from above is shown off and disabled, the footer saying why and never a tooltip. Required: the title and the description. The header's actions stand at the title's right; on a card narrower than 28 rem they keep the title's row and the description runs under both, as Vercel's cards read on a phone, and when the title's longest word would not fit beside them they move under the title, so they never cover it. The footer's left line (`footerHint`, `save.hint`) is optional and specific to the card, never a generic line repeated on every card: a limit, what a value is for, a plan it needs, a link; absent, it is nothing, and a card with neither a line nor a button has no footer. Ours: `save`: a typed value, one card with its own Save in the footer, always visible. The section owns the changed state: Save stays disabled until a field differs from what is saved and says `pendingLabel` while saving with Cancel beside it; a completed save calls `saved`, and the product raises one toast from its catalogue ("Completion toasts follow `{Noun} {past-participle}`: `Blob deleted`, `Domain added`, `Environment variable saved`. Never include `successfully`; the action name implies it." https://vercel.com/geist/toast), and Save returns to disabled, with no saved state in the footer. Ours: the section never raises a toast itself. A failed save keeps every value and shows under its field, or above the footer when no field owns it. `tone` marks a state with a command (a failed payment, a trial ending): a 1px border and a footer tinted in the hue with its line in it, the line saying the condition in words; title, line, fields and buttons stay neutral, an outline secondary then a default primary. A button takes the hue of its command, not of the card: `destructive` (red) for a permanent loss (Delete), `warning` (amber) for a disruptive command that can be undone (Pause, Archive). A delete card is `tone="error"` with the object's summary as its body; an archive card is neutral with a `warning` button in the footer. "Use a Note for inline contextual feedback next to the field, card, or section it describes: a region change warning above a region picker, a rate-limit notice next to a usage gauge." (https://vercel.com/geist/note) "One Note per concept." (https://vercel.com/geist/note) Ours: a message about the fields inside is a MessageNotice in the body, the card staying neutral. "Disabled checkboxes still need a Tooltip naming the reason; a greyed box with no explanation reads as a bug." (https://vercel.com/geist/checkbox) Ours: `lockedReason` (Geist's disabled wall) is the reason, written as the footer's line, not in a tooltip; it keeps the fields in view, inert with muted text, under a not-allowed cursor, the title and line unchanged; `unlock` is the way to get access, left out when nothing the person does unlocks it. A locked section has no `save` and no header `actions`.
|
|
30
30
|
* @words "Helper text is sentence case, one sentence with a trailing period, on a sibling element wired through `aria-describedby`." (https://vercel.com/geist/input) Ours: the description is that helper text for the whole card, and no field has its own. "Render an optional one-sentence description as a sibling under the label that explains ON only. Don’t describe OFF; it’s the negation." (https://vercel.com/geist/toggle) Ours: a header-switch card's description says what turning it on does. The title names the values ("Personal details"); the description says what changing them does, in one line; the footer line is a Learn more link, a limit ("Use 32 characters at most."), what stays unaffected, or what is blocked and why ("Ask an owner to change this."); Save and Saving come from the product's catalogue, and so does the toast a `saved` callback raises; a delete card's description opens "Permanently delete".
|
|
31
31
|
* @access "Provide an accessible name through `children`, `aria-label`, or `aria-labelledby`." (https://vercel.com/geist/toggle) Ours: the title is the section's h2 and names the form that saves it, and the header switch. The footer's line is one status, announced politely; a failure under a field marks the control invalid and describes it. Locked fields are inert, so the section's title, description and reason are what a screen reader meets.
|
|
32
32
|
*/
|
|
@@ -48,8 +48,8 @@ function controlPropsOf({ id, info, error, invalid, disabled, required, }) {
|
|
|
48
48
|
.join(" ") || undefined,
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
-
function SettingsFieldLabel({ id, label, info, }) {
|
|
52
|
-
const fieldLabel = (_jsx(FieldLabel, { htmlFor: id, id: `${id}-label`, children: label }));
|
|
51
|
+
function SettingsFieldLabel({ id, label, info, beside = false, }) {
|
|
52
|
+
const fieldLabel = (_jsx(FieldLabel, { htmlFor: id, id: `${id}-label`, className: beside ? "text-xs font-medium text-foreground" : undefined, children: label }));
|
|
53
53
|
if (!info)
|
|
54
54
|
return fieldLabel;
|
|
55
55
|
return (_jsxs("div", { className: "flex items-center gap-1", children: [fieldLabel, _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { render: _jsx(Button, { "aria-label": info.label, size: "icon-xs", variant: "ghost" }), children: _jsx(Info, { "aria-hidden": "true" }) }), _jsx(TooltipContent, { children: info.text })] }), _jsx("span", { hidden: true, id: `${id}-info`, children: info.text })] }));
|
|
@@ -74,7 +74,7 @@ function controlCellClassName(control, controlWidth) {
|
|
|
74
74
|
*
|
|
75
75
|
* @family Inputs
|
|
76
76
|
* @when The one value of a SettingsSection: a name, an address, a choice, a switch.
|
|
77
|
-
* @instead A field of a drawer's or a dialog's form is a Field from the form's own owner; a read-only fact is a SettingsFactsSection; several switches
|
|
77
|
+
* @instead A field of a drawer's or a dialog's form is a Field from the form's own owner; a read-only fact is a SettingsFactsSection; several switches and selects in one card are SettingsRows; a switch that is the whole card and applies at once is the section's `headerSwitch`.
|
|
78
78
|
* @behaviour "Helper text is sentence case, one sentence with a trailing period, on a sibling element wired through `aria-describedby`." (https://vercel.com/geist/input) Ours: label required, no help line: the card's description helps, and the (i) `info` is for a part of a value that needs a sentence. The control is 20rem wide under its label (`controlWidth`: `full` for a textarea, `natural` for a segmented control). `control="choice"` puts a checkbox or radio before its sentence; `control="switch"` puts the word for the switch's state after it and requires `stateLabel`. "Validate on blur, not on every keystroke; surface the message by passing a string to `error`." (https://vercel.com/geist/input) Ours: a validation `error` updates as the value is corrected, and it or a failed save about this field shows under the control and marks it invalid; colour sits on the control's edge and the message, never the label.
|
|
79
79
|
* @words "Labels are short Title Case nouns: `Project Name`, `Domain`, `Environment Variable Name`." (https://vercel.com/geist/input) Ours: sentence case, a noun for the value ("Display name"). "Validation names the field and the constraint, ends in a period, and skips `please` (`Project name is required.`, `Code must be 6 digits.`)." (https://vercel.com/geist/input) Ours: an error says what to do next; `info.text` is one sentence and `info.label` names its button ("About display name"); a switch shows its state word ("Enabled", "Disabled") beside it.
|
|
80
80
|
* @access "Don’t wrap a labelled `<Input>` in a `Tooltip`; put the explainer on a sibling icon button so the label stays announced." (https://vercel.com/geist/input) Ours: that sibling button is the (i) `info`. The control receives its id, `aria-invalid` and `aria-describedby` through `controlProps`, so the label, the info and the error are read with it. The info button has its own name and opens on focus.
|
|
@@ -86,28 +86,35 @@ function SettingsField({ id, label, info, error, invalid = Boolean(error), disab
|
|
|
86
86
|
const controlCell = (_jsxs("div", { "data-slot": "settings-field-control", className: cn("flex min-w-0", controlCellClassName(control, controlWidth)), children: [children({ controlProps }), control === "switch" ? _jsx("span", { className: "text-sm", children: stateLabel }) : null] }));
|
|
87
87
|
return (_jsx("div", { "data-slot": "settings-field", "data-invalid": invalid ? true : undefined, className: "contents", children: _jsxs(Field, { "data-invalid": invalid ? true : undefined, "data-disabled": disabled ? true : undefined, orientation: beside ? "horizontal" : "vertical", className: cn("items-start", className), ...props, children: [beside ? controlCell : null, _jsxs(FieldContent, { children: [_jsx(SettingsFieldLabel, { id: id, label: label, info: info }), beside ? errors : null] }), beside ? null : controlCell, beside ? null : errors] }) }));
|
|
88
88
|
}
|
|
89
|
-
function SettingsRow({ id, label, info, error, disabled, children, }) {
|
|
89
|
+
function SettingsRow({ id, label, info, error, disabled, kind, children, }) {
|
|
90
90
|
const invalid = Boolean(error);
|
|
91
91
|
const controlProps = controlPropsOf({ id, info, error, invalid, disabled });
|
|
92
|
-
return (_jsx("div", { "data-slot": "settings-row", "data-invalid": invalid ? true : undefined, className: "contents", children: _jsxs(Field, { "data-invalid": invalid ? true : undefined, "data-disabled": disabled ? true : undefined, orientation: "horizontal", className: "flex-wrap has-[>[data-slot=field-content]]:items-center", children: [_jsxs(FieldContent, { className: "flex-auto", children: [_jsx(SettingsFieldLabel, { id: id, label: label, info: info }), _jsx(SettingsFieldErrors, { id: id, error: error })] }), _jsx("div", { "data-slot": "settings-row-control", className: "flex items-center gap-2
|
|
92
|
+
return (_jsx("div", { "data-slot": "settings-row", "data-invalid": invalid ? true : undefined, className: "contents", children: _jsxs(Field, { "data-invalid": invalid ? true : undefined, "data-disabled": disabled ? true : undefined, orientation: "horizontal", className: "flex-wrap has-[>[data-slot=field-content]]:items-center", children: [_jsxs(FieldContent, { className: "flex-auto", children: [_jsx(SettingsFieldLabel, { id: id, label: label, info: info, beside: true }), _jsx(SettingsFieldErrors, { id: id, error: error })] }), _jsx("div", { "data-slot": "settings-row-control", "data-kind": kind, className: "flex items-center gap-2 data-[kind=control]:w-64 data-[kind=control]:max-w-full data-[kind=select]:w-48 data-[kind=select]:max-w-full", children: children({ controlProps }) })] }) }));
|
|
93
93
|
}
|
|
94
94
|
/**
|
|
95
|
-
* Several switches
|
|
95
|
+
* Several switches and selects in one card: each row its label on the left and its control on the right.
|
|
96
96
|
*
|
|
97
97
|
* @family Inputs
|
|
98
|
-
* @when A card of
|
|
99
|
-
* @instead One setting is a SettingsField.
|
|
100
|
-
* @behaviour
|
|
98
|
+
* @when A card of switches and selects that the card's one description covers: a project's Git switches, a team's notification channels, a set of defaults, how long a computer stays awake beside whether the window goes to the tray.
|
|
99
|
+
* @instead One setting is a SettingsField. A text box is a field of its own card.
|
|
100
|
+
* @behaviour A row with `options` is a select, a row with `checked` a switch, and a row with `control` draws a control whose usage page puts it in a row, such as ThemeModeSwitcher; a card may hold any of them together, as Vercel's Root Directory card holds its switches; `stateLabels` is required once any row is a switch. Required on every row: the label and its `info` (an (i) with its sentence in a tooltip and on the control); no help line sits under a row. A switch row shows its state word beside the switch (`stateLabels`) and is controlled, so the word always follows the switch. Rows stand 16 px apart; below 28rem a row stacks its control under its label. "Pair with a form footer only when the setting needs an explicit `Save` step." (https://vercel.com/geist/toggle) Ours: a switch is a draft the card's Save applies.
|
|
101
101
|
* @words "Title Case noun phrase, 1–4 words, naming what is true when ON: `Password Protection`, not `Enable Password Protection`." (https://vercel.com/geist/toggle) Ours: sentence case, a noun for what it controls ("Pull request comments"), the same for a switch as for a select. "Render an optional one-sentence description as a sibling under the label that explains ON only. Don’t describe OFF; it’s the negation." (https://vercel.com/geist/toggle) Ours: that sentence is `info.text`. "Options are Title Case when short and match canonical branding (`Next.js`, not `NextJS`); keep the same register across the list." (https://vercel.com/geist/select) Ours: a select's options are sentence case.
|
|
102
102
|
* @access Each row's control is named by the row's label and described by its info and its error. The info button has its own name and opens on focus.
|
|
103
103
|
*/
|
|
104
|
-
function SettingsRows(
|
|
105
|
-
return (_jsx("div", { "data-slot": "settings-rows",
|
|
104
|
+
function SettingsRows({ rows, stateLabels }) {
|
|
105
|
+
return (_jsx("div", { "data-slot": "settings-rows", className: "flex flex-col gap-4", children: rows.map((row) => {
|
|
106
|
+
if (row.control !== undefined)
|
|
107
|
+
return controlRow(row);
|
|
108
|
+
return row.options === undefined ? switchRow(row, stateLabels) : selectRow(row);
|
|
109
|
+
}) }));
|
|
106
110
|
}
|
|
107
|
-
function
|
|
108
|
-
return
|
|
111
|
+
function switchRow(row, stateLabels) {
|
|
112
|
+
return (_jsx(SettingsRow, { kind: "switch", ...row, children: ({ controlProps }) => (_jsxs(_Fragment, { children: [_jsx(Switch, { ...controlProps, checked: row.checked, name: row.name, onCheckedChange: row.onCheckedChange, size: "md" }), _jsx("span", { className: "text-sm", children: row.checked ? stateLabels?.on : stateLabels?.off })] })) }, row.id));
|
|
109
113
|
}
|
|
110
|
-
function
|
|
111
|
-
return
|
|
114
|
+
function controlRow(row) {
|
|
115
|
+
return (_jsx(SettingsRow, { kind: "control", ...row, children: row.control }, row.id));
|
|
116
|
+
}
|
|
117
|
+
function selectRow(row) {
|
|
118
|
+
return (_jsx(SettingsRow, { kind: "select", ...row, children: ({ controlProps }) => (_jsx(NativeSelect, { ...controlProps, name: row.name, onChange: (event) => row.onValueChange(event.target.value), value: row.value, children: row.options.map((option) => (_jsx(NativeSelectOption, { value: option.value, children: option.label }, option.value))) })) }, row.id));
|
|
112
119
|
}
|
|
113
120
|
export { SettingsField, SettingsRows, SettingsSection };
|
|
@@ -23,7 +23,8 @@ import { type ControlName } from "../internal/control-name.js";
|
|
|
23
23
|
* named: `aria-label` or `aria-labelledby`, or an `id` that a `FieldLabel`'s `htmlFor` points at,
|
|
24
24
|
* and a switch with none of them does not compile. A bare `Switch` is the 28 by 14 px track with an
|
|
25
25
|
* 11 px thumb; `md` is 36 by 20 px and `lg` 40 by 24 px. Colour: off is the input grey, on is
|
|
26
|
-
*
|
|
26
|
+
* cobalto, the thumb white in both themes, and a disabled switch turns grey, on or off, with a
|
|
27
|
+
* greyer thumb and no shadow. Space flips
|
|
27
28
|
* it. On a settings page it is a draft its card's footer Save applies, and the product's toast
|
|
28
29
|
* confirms the save, in one of two places: in the card's body under its label, its state word after
|
|
29
30
|
* it (`SettingsField`'s `control="switch"`); or at the end of a row of a card of several settings,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"switch.d.ts","sourceRoot":"","sources":["../../src/components/switch.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGlE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D
|
|
1
|
+
{"version":3,"file":"switch.d.ts","sourceRoot":"","sources":["../../src/components/switch.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGlE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,iBAAS,MAAM,CAAC,EACd,SAAS,EACT,IAAW,EACX,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,IAAI,CAAC,KAAK,GAC3B,WAAW,GAAG;IACZ,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CAC3B,+BAiBF;AAED,OAAO,EAAE,MAAM,EAAE,CAAC"}
|
|
@@ -24,7 +24,8 @@ import { cn } from "../lib/utils.js";
|
|
|
24
24
|
* named: `aria-label` or `aria-labelledby`, or an `id` that a `FieldLabel`'s `htmlFor` points at,
|
|
25
25
|
* and a switch with none of them does not compile. A bare `Switch` is the 28 by 14 px track with an
|
|
26
26
|
* 11 px thumb; `md` is 36 by 20 px and `lg` 40 by 24 px. Colour: off is the input grey, on is
|
|
27
|
-
*
|
|
27
|
+
* cobalto, the thumb white in both themes, and a disabled switch turns grey, on or off, with a
|
|
28
|
+
* greyer thumb and no shadow. Space flips
|
|
28
29
|
* it. On a settings page it is a draft its card's footer Save applies, and the product's toast
|
|
29
30
|
* confirms the save, in one of two places: in the card's body under its label, its state word after
|
|
30
31
|
* it (`SettingsField`'s `control="switch"`); or at the end of a row of a card of several settings,
|
|
@@ -47,6 +48,6 @@ import { cn } from "../lib/utils.js";
|
|
|
47
48
|
* both states; a missing name does not compile.
|
|
48
49
|
*/
|
|
49
50
|
function Switch({ className, size = "sm", ...props }) {
|
|
50
|
-
return (_jsx(SwitchPrimitive.Root, { "data-slot": "switch", "data-size": size, className: cn("nc-transition-paint peer group/switch relative inline-flex shrink-0 items-start rounded-full border border-transparent p-0 after:absolute after:top-1/2 after:left-1/2 after:size-touch-target after:-translate-x-1/2 after:-translate-y-1/2 aria-invalid:border-destructive data-[size=sm]:h-3.5 data-[size=sm]:w-7 data-[size=md]:h-5 data-[size=md]:w-9 data-[size=lg]:h-6 data-[size=lg]:w-10 data-checked:bg-primary data-unchecked:bg-input data-disabled:cursor-not-allowed data-disabled:data-checked:bg-input/50 [@media(hover:hover)_and_(pointer:fine)]:after:hidden", className), ...props, children: _jsx(SwitchPrimitive.Thumb, { "data-slot": "switch-thumb", className: "pointer-events-none mt-px block rounded-full bg-
|
|
51
|
+
return (_jsx(SwitchPrimitive.Root, { "data-slot": "switch", "data-size": size, className: cn("nc-transition-paint peer group/switch relative inline-flex shrink-0 items-start rounded-full border border-transparent p-0 after:absolute after:top-1/2 after:left-1/2 after:size-touch-target after:-translate-x-1/2 after:-translate-y-1/2 aria-invalid:border-destructive data-[size=sm]:h-3.5 data-[size=sm]:w-7 data-[size=md]:h-5 data-[size=md]:w-9 data-[size=lg]:h-6 data-[size=lg]:w-10 data-checked:bg-primary data-unchecked:bg-input data-disabled:cursor-not-allowed data-disabled:data-checked:bg-input/50 [@media(hover:hover)_and_(pointer:fine)]:after:hidden", className), ...props, children: _jsx(SwitchPrimitive.Thumb, { "data-slot": "switch-thumb", className: "pointer-events-none mt-px block rounded-full bg-primary-foreground ring-0 nc-transition-transform not-data-disabled:nc-elevation-resting group-data-[size=sm]/switch:ml-px group-data-[size=sm]/switch:size-2.75 group-data-[size=md]/switch:size-4.25 group-data-[size=lg]/switch:ml-px group-data-[size=lg]/switch:size-5.25 group-data-[size=sm]/switch:data-checked:translate-x-3.5 group-data-[size=md]/switch:data-checked:translate-x-4.25 group-data-[size=lg]/switch:data-checked:translate-x-4 data-disabled:bg-muted data-unchecked:translate-x-0" }) }));
|
|
51
52
|
}
|
|
52
53
|
export { Switch };
|
|
@@ -23,7 +23,7 @@ export type ThemeModeSwitcherProps = Omit<React.ComponentProps<typeof RadioGroup
|
|
|
23
23
|
* The three appearances side by side, each with its glyph and its name.
|
|
24
24
|
*
|
|
25
25
|
* @family Choices
|
|
26
|
-
* @when An Appearance setting on a settings page, where the choice has room to show its names.
|
|
26
|
+
* @when An Appearance setting on a settings page, where the choice has room to show its names: its own field, or a row of a SettingsRows card, `compact`, beside settings of the same card.
|
|
27
27
|
* @instead Inside a menu, the same choice is ThemeModeMenu; a choice that is not the appearance is a RadioGroup or a ToggleGroup.
|
|
28
28
|
* @behaviour Exclusive: one mode is selected; on a settings card the choice is a draft the card's Save applies. The order is System, Light, Dark everywhere.
|
|
29
29
|
* @words The setting's name, then each mode's name from the product's catalogue: "System", "Light", "Dark".
|
|
@@ -17,7 +17,7 @@ const themeModeIcons = {
|
|
|
17
17
|
* The three appearances side by side, each with its glyph and its name.
|
|
18
18
|
*
|
|
19
19
|
* @family Choices
|
|
20
|
-
* @when An Appearance setting on a settings page, where the choice has room to show its names.
|
|
20
|
+
* @when An Appearance setting on a settings page, where the choice has room to show its names: its own field, or a row of a SettingsRows card, `compact`, beside settings of the same card.
|
|
21
21
|
* @instead Inside a menu, the same choice is ThemeModeMenu; a choice that is not the appearance is a RadioGroup or a ToggleGroup.
|
|
22
22
|
* @behaviour Exclusive: one mode is selected; on a settings card the choice is a draft the card's Save applies. The order is System, Light, Dark everywhere.
|
|
23
23
|
* @words The setting's name, then each mode's name from the product's catalogue: "System", "Light", "Dark".
|
|
@@ -10,6 +10,10 @@ export declare function stateSpeed(state: AgentOrbState, form: AgentOrbFigure, a
|
|
|
10
10
|
gestureAge: (age: number) => number;
|
|
11
11
|
settle: (age: number, delay?: number) => number;
|
|
12
12
|
}): number;
|
|
13
|
+
export declare const ORB_FRAME_RATE: {
|
|
14
|
+
readonly resting: 15;
|
|
15
|
+
readonly busy: 30;
|
|
16
|
+
};
|
|
13
17
|
/**
|
|
14
18
|
* Reduced motion turns movement off and keeps opacity and colour, so a
|
|
15
19
|
* thinking or working orb breathes between full ink and 0.45 over 1.6 s,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-orb-motion.d.ts","sourceRoot":"","sources":["../../src/internal/agent-orb-motion.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAc7F,wBAAgB,WAAW,CAAC,IAAI,EAAE,cAAc,UAE/C;AAED,wBAAgB,UAAU,CACxB,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,cAAc,EACpB,GAAG,SAAI,EACP,MAAM;;;;;;
|
|
1
|
+
{"version":3,"file":"agent-orb-motion.d.ts","sourceRoot":"","sources":["../../src/internal/agent-orb-motion.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAc7F,wBAAgB,WAAW,CAAC,IAAI,EAAE,cAAc,UAE/C;AAED,wBAAgB,UAAU,CACxB,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,cAAc,EACpB,GAAG,SAAI,EACP,MAAM;;;;;;sBA6Cc,MAAM;kBACV,MAAM;CA9CA,UAWvB;AAED,eAAO,MAAM,cAAc;;;CAAqC,CAAC;AAKjE;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,UAIlE;AAED,wBAAgB,aAAa,CAAC,KAAK,CAAC,EAAE,mBAAmB;;;;;;sBAgBnC,MAAM;kBACV,MAAM;EAGvB;AAUD,wBAAgB,UAAU,CACxB,KAAK,EAAE,aAAa,EACpB,GAAG,EAAE,MAAM,EACX,MAAM;;;;;;sBAjBc,MAAM;kBACV,MAAM;CAgBA,EACtB,OAAO,UAAQ;;;;;;EA+ChB;AA6ED,wBAAgB,WAAW,CACzB,OAAO,EAAE,wBAAwB,EACjC,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,KAAK,EAAE,MAAM,GAAG,OAAO;AACvB,mFAAmF;AACnF,GAAG,SAAI,EACP,MAAM;;;;;;sBAxJc,MAAM;kBACV,MAAM;CAuJA,EACtB,WAAW,SAA8B,QA4E1C"}
|
|
@@ -24,6 +24,7 @@ export function stateSpeed(state, form, age = 0, timing = defaultTiming) {
|
|
|
24
24
|
};
|
|
25
25
|
return speed[state] * nativeSpeed(form);
|
|
26
26
|
}
|
|
27
|
+
export const ORB_FRAME_RATE = { resting: 15, busy: 30 };
|
|
27
28
|
/** The breath's period, in seconds. */
|
|
28
29
|
const BREATH_SECONDS = 1.6;
|
|
29
30
|
/**
|
|
@@ -44,6 +44,7 @@ export type AgentOrbMountOptions = AgentOrbMountBase & ({
|
|
|
44
44
|
motion: "ambient";
|
|
45
45
|
state?: never;
|
|
46
46
|
});
|
|
47
|
+
export declare const LONGEST_FRAME_SECONDS = 0.1;
|
|
47
48
|
export declare function mountAgentOrb(canvas: HTMLCanvasElement, options: AgentOrbMountOptions): {
|
|
48
49
|
destroy(): void;
|
|
49
50
|
setPaused(_value: boolean): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-orb.d.ts","sourceRoot":"","sources":["../../src/internal/agent-orb.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAEb,KAAK,QAAQ,EAId,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,KAAK,aAAa,
|
|
1
|
+
{"version":3,"file":"agent-orb.d.ts","sourceRoot":"","sources":["../../src/internal/agent-orb.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAEb,KAAK,QAAQ,EAId,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,KAAK,aAAa,EAQnB,MAAM,oBAAoB,CAAC;AAE5B,QAAA,MAAM,aAAa;;;;;;;;CAQ0B,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG,MAAM,OAAO,aAAa,CAAC;AACtD;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,YAAY,GAAG,OAAO,GAAG,OAAO,CAAC;AAC9D,MAAM,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;AAE7D,kDAAkD;AAClD,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,cAAc,CAAC;AACxD,+FAA+F;AAC/F,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,CAAC;AA+DlD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAuDpF;AA2ID,KAAK,iBAAiB,GAAG;IACvB,IAAI,EAAE,cAAc,CAAC;IACrB;;;OAGG;IACH,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAClD,CAAC;IAAE,MAAM,CAAC,EAAE,QAAQ,CAAC;IAAC,KAAK,CAAC,EAAE,aAAa,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAmIxF,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,oBAAoB;;sBAIzC,OAAO;qBAAuB,aAAa;EA2LvF"}
|