@nadicodeai/ui 12.1.127 → 12.1.129

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.
@@ -10,7 +10,7 @@ import { type StatusBadgeTone } from "./status-badge.js";
10
10
  * @instead An Agent as a record among others in a page's list is a RecordList row with an agent IdentityMark; an Agent's own page heads with ObjectPage.
11
11
  * @behaviour It is the row's presentation alone: the consumer owns the control around it (Nadia's button that opens the Agent, a scene's list item that opens nothing). The current row's name is set in 500. A state tone draws the state line as a small StatusBadge; without a tone it stays plain text. Narrow, the orb stands alone and the words stay for a screen reader; the consumer names it with its state line in a tooltip, as Nadia's list does. Only the orb moves in the list. The name is 14 px and the state line 12 px at every size. The drawn orb leaves about 10 px of its 64 px box empty, so at 64 px that empty edge overlaps the row's padding (6 px above and below, 4 px at the start) and the words stand 4 px from the box: the row hugs the visible orb and the name sits beside it, as beside a solid picture. In Nadia's list her orb is 64 px in her real state, and 40 px in the narrow column from 640 to 899 px.
12
12
  * @words The name, then one state line: what she is doing now ("Answering on Telegram", "Working") or what she is for ("Sorts requests"). The tone never says more than the line does. Nadia's list says "Working", "Idle" or "Needs you"; the badge marks a state that needs the person ("Needs you", a failed set-up), and the orb shows working by itself.
13
- * @access The orb is decorative; the name and the state line are the row's text, read by its control. StatusBadge owns its decorative mark, so the line carries its meaning.
13
+ * @access The orb is decorative; the name and the state line are the row's text, read by its control. StatusBadge owns its decorative shape, so the line carries its meaning.
14
14
  */
15
15
  export declare function AgentListRow({ className, color, current, form, motionKey, name, narrow, size, state, stateLine, stateTone, ...props }: Omit<React.ComponentProps<"span">, "color"> & {
16
16
  /** The pigment of the Agent's chosen form; Nadia's striped ribbon keeps her own. */
@@ -12,7 +12,7 @@ import { cn } from "../lib/utils.js";
12
12
  * @instead An Agent as a record among others in a page's list is a RecordList row with an agent IdentityMark; an Agent's own page heads with ObjectPage.
13
13
  * @behaviour It is the row's presentation alone: the consumer owns the control around it (Nadia's button that opens the Agent, a scene's list item that opens nothing). The current row's name is set in 500. A state tone draws the state line as a small StatusBadge; without a tone it stays plain text. Narrow, the orb stands alone and the words stay for a screen reader; the consumer names it with its state line in a tooltip, as Nadia's list does. Only the orb moves in the list. The name is 14 px and the state line 12 px at every size. The drawn orb leaves about 10 px of its 64 px box empty, so at 64 px that empty edge overlaps the row's padding (6 px above and below, 4 px at the start) and the words stand 4 px from the box: the row hugs the visible orb and the name sits beside it, as beside a solid picture. In Nadia's list her orb is 64 px in her real state, and 40 px in the narrow column from 640 to 899 px.
14
14
  * @words The name, then one state line: what she is doing now ("Answering on Telegram", "Working") or what she is for ("Sorts requests"). The tone never says more than the line does. Nadia's list says "Working", "Idle" or "Needs you"; the badge marks a state that needs the person ("Needs you", a failed set-up), and the orb shows working by itself.
15
- * @access The orb is decorative; the name and the state line are the row's text, read by its control. StatusBadge owns its decorative mark, so the line carries its meaning.
15
+ * @access The orb is decorative; the name and the state line are the row's text, read by its control. StatusBadge owns its decorative shape, so the line carries its meaning.
16
16
  */
17
17
  export function AgentListRow({ className, color, current = false, form, motionKey, name, narrow = false, size = 40, state = "idle", stateLine, stateTone, ...props }) {
18
18
  return (_jsxs("span", { className: cn("flex w-full min-w-0 items-center gap-3 text-left text-sm", size === 64 && "gap-1", narrow && "justify-center", className), "data-slot": "agent-list-row", "data-current": current || undefined, ...props, children: [_jsx(AgentOrb, { className: cn(size === 64 && "-my-1.5 -ml-1"), color: color, decorative: true, form: form, motionKey: motionKey, size: size, state: state }), _jsxs("span", { className: cn("grid min-w-0", narrow && "sr-only"), children: [_jsx("span", { className: cn("truncate text-foreground", current && "font-medium"), children: name }), stateLine ? (_jsx("span", { className: "flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground", children: stateTone ? (_jsx(StatusBadge, { label: stateLine, size: "sm", tone: stateTone })) : (_jsx("span", { className: "truncate", children: stateLine })) })) : null] })] }));
@@ -28,11 +28,12 @@ type BadgeProps = Omit<useRender.ComponentProps<"span">, "onClick" | "children"
28
28
  * type beside it: `sm` (20 px) next to small labels, `md` (24 px) in a row, `lg` (32 px) in a
29
29
  * header. The category fills (teal, purple, pink) name a kind.
30
30
  * @instead "For a colored dot without text, use `Status Dot`." (https://vercel.com/geist/badge)
31
- * Ours: status dots are not used; a state keeps its mark and word together in `StatusBadge`.
31
+ * Ours: a single dot is never a status; a state is `StatusBadge`, its tone's shape of dots and its
32
+ * word, with no pill.
32
33
  * "For clickable filter chips that toggle a query, use the `pill` variant or a small `Button`." (https://vercel.com/geist/badge)
33
34
  * Ours: there is no `pill` variant; a filter that toggles a query is a `Button` or
34
- * `CollectionFilters`. For a state use `StatusBadge`, which picks the tone and its mark: the gray,
35
- * green, red, amber and blue fills, soft or solid, are its alone. One categorical fact in a row is
35
+ * `CollectionFilters`. For a state use `StatusBadge`, which draws the tone as its shape; Badge has
36
+ * no gray, green, red, amber or blue fill. One categorical fact in a row is
36
37
  * a `Tag`. A change in a figure is a `TrendBadge`.
37
38
  * @behaviour "Badges are static labels." (https://vercel.com/geist/badge)
38
39
  * "Don’t wire `onClick` onto them; promote to a Button or link if the user can act on the value." (https://vercel.com/geist/badge)
@@ -45,11 +46,11 @@ type BadgeProps = Omit<useRender.ComponentProps<"span">, "onClick" | "children"
45
46
  * Ours: the badge is the `render` of a `TooltipTrigger` with `tabIndex={0}`, so keyboard and touch
46
47
  * users reach the limit it names.
47
48
  * "Don’t add a checkmark icon for success states or an X for errors; the variant carries that signal." (https://vercel.com/geist/badge)
48
- * Ours: only `StatusBadge` carries a state, and it always draws its tone's mark.
49
+ * Ours: only `StatusBadge` carries a state, and it always draws its tone's shape.
49
50
  * "Map meaning to color: `green` for healthy, `red` for error, `amber` for warning, `blue` for informational or production, `gray` for neutral." (https://vercel.com/geist/badge)
50
51
  * "The `-subtle` suffix tones any of them down on dense surfaces." (https://vercel.com/geist/badge)
51
- * Ours: those fills are `StatusBadge`'s, as the tones `success`, `danger`, `warning`, `info` and
52
- * `neutral`, with `emphasis` `subtle` or `solid`. `Badge` is a fixed-height pill (20, 24 or 32 px)
52
+ * Ours: those meanings are `StatusBadge`'s, as the tones `success`, `danger`, `warning`, `info` and
53
+ * `neutral`, each drawn as its shape. `Badge` is a fixed-height pill (20, 24 or 32 px)
53
54
  * with no border: colour sits on the fill and the word. A soft fill takes its hue's strong shade
54
55
  * for the word, `default` is the foreground colour inverted, and `outline` is neutral, a
55
56
  * page-colour fill held by a 1 px inset ring. With an icon, the left padding is 6, 8 or 12 px, so
@@ -1 +1 @@
1
- {"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../../src/components/badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAIlE,QAAA,MAAM,aAAa;;;8EAuBlB,CAAC;AAEF,KAAK,SAAS,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAEzE,KAAK,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC,GACxF,YAAY,CAAC,OAAO,aAAa,CAAC,GAClC,CACI;IACE,QAAQ,EAAE,SAAS,CAAC;IACpB,qDAAqD;IACrD,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GACD;IACE,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;IAChB,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;CACf,CACJ,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,iBAAS,KAAK,CAAC,EACb,SAAS,EACT,OAAmB,EACnB,IAAW,EACX,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,GAAG,KAAK,EACT,EAAE,UAAU,8FA0BZ;AAED,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../../src/components/badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACtD,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAIlE,QAAA,MAAM,aAAa;;;8EAuBlB,CAAC;AAEF,KAAK,SAAS,GAAG,WAAW,CAAC,YAAY,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAEzE,KAAK,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC,GACxF,YAAY,CAAC,OAAO,aAAa,CAAC,GAClC,CACI;IACE,QAAQ,EAAE,SAAS,CAAC;IACpB,qDAAqD;IACrD,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GACD;IACE,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;IAChB,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;CACf,CACJ,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,iBAAS,KAAK,CAAC,EACb,SAAS,EACT,OAAmB,EACnB,IAAW,EACX,IAAI,EACJ,QAAQ,EACR,MAAM,EACN,GAAG,KAAK,EACT,EAAE,UAAU,8FA0BZ;AAED,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,CAAC"}
@@ -35,11 +35,12 @@ const badgeVariants = cva("group/badge inline-flex w-fit max-w-full shrink-0 ite
35
35
  * type beside it: `sm` (20 px) next to small labels, `md` (24 px) in a row, `lg` (32 px) in a
36
36
  * header. The category fills (teal, purple, pink) name a kind.
37
37
  * @instead "For a colored dot without text, use `Status Dot`." (https://vercel.com/geist/badge)
38
- * Ours: status dots are not used; a state keeps its mark and word together in `StatusBadge`.
38
+ * Ours: a single dot is never a status; a state is `StatusBadge`, its tone's shape of dots and its
39
+ * word, with no pill.
39
40
  * "For clickable filter chips that toggle a query, use the `pill` variant or a small `Button`." (https://vercel.com/geist/badge)
40
41
  * Ours: there is no `pill` variant; a filter that toggles a query is a `Button` or
41
- * `CollectionFilters`. For a state use `StatusBadge`, which picks the tone and its mark: the gray,
42
- * green, red, amber and blue fills, soft or solid, are its alone. One categorical fact in a row is
42
+ * `CollectionFilters`. For a state use `StatusBadge`, which draws the tone as its shape; Badge has
43
+ * no gray, green, red, amber or blue fill. One categorical fact in a row is
43
44
  * a `Tag`. A change in a figure is a `TrendBadge`.
44
45
  * @behaviour "Badges are static labels." (https://vercel.com/geist/badge)
45
46
  * "Don’t wire `onClick` onto them; promote to a Button or link if the user can act on the value." (https://vercel.com/geist/badge)
@@ -52,11 +53,11 @@ const badgeVariants = cva("group/badge inline-flex w-fit max-w-full shrink-0 ite
52
53
  * Ours: the badge is the `render` of a `TooltipTrigger` with `tabIndex={0}`, so keyboard and touch
53
54
  * users reach the limit it names.
54
55
  * "Don’t add a checkmark icon for success states or an X for errors; the variant carries that signal." (https://vercel.com/geist/badge)
55
- * Ours: only `StatusBadge` carries a state, and it always draws its tone's mark.
56
+ * Ours: only `StatusBadge` carries a state, and it always draws its tone's shape.
56
57
  * "Map meaning to color: `green` for healthy, `red` for error, `amber` for warning, `blue` for informational or production, `gray` for neutral." (https://vercel.com/geist/badge)
57
58
  * "The `-subtle` suffix tones any of them down on dense surfaces." (https://vercel.com/geist/badge)
58
- * Ours: those fills are `StatusBadge`'s, as the tones `success`, `danger`, `warning`, `info` and
59
- * `neutral`, with `emphasis` `subtle` or `solid`. `Badge` is a fixed-height pill (20, 24 or 32 px)
59
+ * Ours: those meanings are `StatusBadge`'s, as the tones `success`, `danger`, `warning`, `info` and
60
+ * `neutral`, each drawn as its shape. `Badge` is a fixed-height pill (20, 24 or 32 px)
60
61
  * with no border: colour sits on the fill and the word. A soft fill takes its hue's strong shade
61
62
  * for the word, `default` is the foreground colour inverted, and `outline` is neutral, a
62
63
  * page-colour fill held by a 1 px inset ring. With an icon, the left padding is 6, 8 or 12 px, so
@@ -25,7 +25,7 @@ export type CompanyCardLabels = {
25
25
  * @instead A company's facts are `SettingsFactsSection`. A company in a row is its `IdentityMark`.
26
26
  * @behaviour One object of the ID-1 proportion (856 by 540), dark by nature in every theme. Its
27
27
  * top line carries the wordmark and, once Sofia has found it on the company's site, the company's
28
- * own logo after a hairline, both stamped in `{colors.identity-white}` as one ink; a company with
28
+ * own logo after a hairline in its original colours; a company with
29
29
  * no logo found has the wordmark alone. Below are the holder's name and the company's domain. No
30
30
  * Agent stands on it, and nothing sits in the engraving: a guilloché engraving in
31
31
  * `{colors.identity-white}` at 10%. It fills in as the form is typed and answers the pointer. It
@@ -52,7 +52,7 @@ export declare function CompanyCard({ name, domain, edition, issued, labels, log
52
52
  edition: CompanyCardEdition;
53
53
  issued: boolean;
54
54
  labels: CompanyCardLabels;
55
- /** The company's own logo, one ink in white; absent until someone has found it. */
55
+ /** The company's own logo; absent until lookup has found it. */
56
56
  logo?: string;
57
57
  /**
58
58
  * Waits for most of the card to be on screen before it turns once in the
@@ -1 +1 @@
1
- {"version":3,"file":"company-card.d.ts","sourceRoot":"","sources":["../../src/components/company-card.tsx"],"names":[],"mappings":"AASA,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,SAAS,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAqBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,MAAM,EACN,OAAO,EACP,MAAM,EACN,MAAM,EACN,IAAI,EACJ,WAAmB,GACpB,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,+BAsSA"}
1
+ {"version":3,"file":"company-card.d.ts","sourceRoot":"","sources":["../../src/components/company-card.tsx"],"names":[],"mappings":"AASA,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,SAAS,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAqBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,MAAM,EACN,OAAO,EACP,MAAM,EACN,MAAM,EACN,IAAI,EACJ,WAAmB,GACpB,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,+BAiSA"}
@@ -40,7 +40,7 @@ function reason(cause) {
40
40
  * @instead A company's facts are `SettingsFactsSection`. A company in a row is its `IdentityMark`.
41
41
  * @behaviour One object of the ID-1 proportion (856 by 540), dark by nature in every theme. Its
42
42
  * top line carries the wordmark and, once Sofia has found it on the company's site, the company's
43
- * own logo after a hairline, both stamped in `{colors.identity-white}` as one ink; a company with
43
+ * own logo after a hairline in its original colours; a company with
44
44
  * no logo found has the wordmark alone. Below are the holder's name and the company's domain. No
45
45
  * Agent stands on it, and nothing sits in the engraving: a guilloché engraving in
46
46
  * `{colors.identity-white}` at 10%. It fills in as the form is typed and answers the pointer. It
@@ -29,8 +29,7 @@ export type IdentityMarkProps = {
29
29
  kind: "company";
30
30
  name: string;
31
31
  /**
32
- * The company's logo: the white ink Sofia kept from its site, drawn on a dark tile in every
33
- * theme, or a square icon in its own colours (an SVG tile), which fills the tile as it is.
32
+ * The company's own logo, kept by lookup in its original colours and proportions.
34
33
  */
35
34
  logo?: string;
36
35
  /** The company's standing, independent of which directory opened it. */
@@ -56,7 +55,7 @@ export type IdentityMarkProps = {
56
55
  * @family Status
57
56
  * @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.
58
57
  * @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`.
59
- * @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 company is its logo: the one white ink Sofia kept from its site on a black arch with its edition's colour edge in both themes, or a square SVG icon in its own colours filling the tile, as Vercel shows a project's favicon; its two initials on a compact arch, cobalto for a company and arancio for an approved Partner, until a logo is found or when the stored logo does not load. The edition follows the company's standing, never the directory it was opened from; a person is their picture, then their initials, in a round mark; a record with its own object picture, such as a Library skill or plugin, draws that picture on its kind's ground; 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 Library skill takes the purple category ground with the Skills icon and a plugin the `info-soft` ground with the Plugins icon, both without a ring, so the ground says which kind it is, and an entry that has its own object picture draws it on that ground in the icon's place. A company arch has a semicircular top and `{rounded.sm}` bottom corners; a record tile takes `{rounded.sm}` (6 px) corners, `{rounded.md}` (8 px) at 40 and 64; an app's tile takes `{rounded.md}` at every size.
58
+ * @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 company is its own logo, contained whole in a neutral tile in its original colours and proportions, as Vercel shows a project's favicon; its two initials on a compact arch, cobalto for a company and arancio for an approved Partner, until a logo is found or when the stored logo does not load. The edition follows the company's standing, never the directory it was opened from; a person is their picture, then their initials, in a round mark; a record with its own object picture, such as a Library skill or plugin, draws that picture on its kind's ground; 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 Library skill takes the purple category ground with the Skills icon and a plugin the `info-soft` ground with the Plugins icon, both without a ring, so the ground says which kind it is, and an entry that has its own object picture draws it on that ground in the icon's place. A company arch has a semicircular top and `{rounded.sm}` bottom corners; a record tile takes `{rounded.sm}` (6 px) corners, `{rounded.md}` (8 px) at 40 and 64; an app's tile takes `{rounded.md}` at every size.
60
59
  * @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, and no other kind takes letters.
61
60
  * @access Decorative beside the record's name. Alone, as in a narrow rail, `label` names it.
62
61
  */
@@ -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;AAKnE,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;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CACjC,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,0FAA0F;IAC1F,IAAI,EAAE,aAAa,CAAC;IACpB,oEAAoE;IACpE,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC/C,4GAA4G;IAC5G,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CACJ,CAAC;AAmGF;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,SAAS,CA0EhE"}
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;AAKnE,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;IACd,wEAAwE;IACxE,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CACjC,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,0FAA0F;IAC1F,IAAI,EAAE,aAAa,CAAC;IACpB,oEAAoE;IACpE,IAAI,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC/C,4GAA4G;IAC5G,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CACJ,CAAC;AA+EF;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,SAAS,CAwEhE"}
@@ -20,26 +20,6 @@ const TONES = {
20
20
  company: "bg-nc-identity-cobalto text-nc-identity-white",
21
21
  partner: "bg-nc-identity-arancio text-nc-identity-ink",
22
22
  };
23
- const SVG_DATA = "data:image/svg+xml";
24
- /**
25
- * Whether the logo is a square icon in its own colours: an SVG data URL whose box is square. The
26
- * logos Sofia keeps are a raster white ink, never SVG, so everything else is ink for the dark tile.
27
- */
28
- function logoIsColourTile(logo) {
29
- if (!logo.startsWith(SVG_DATA))
30
- return false;
31
- const comma = logo.indexOf(",");
32
- let svg;
33
- try {
34
- const body = logo.slice(comma + 1);
35
- svg = logo.slice(0, comma).endsWith(";base64") ? atob(body) : decodeURIComponent(body);
36
- }
37
- catch {
38
- return false;
39
- }
40
- const box = /viewBox\s*=\s*["']\s*[-\d.]+[\s,]+[-\d.]+[\s,]+([\d.]+)[\s,]+([\d.]+)/i.exec(svg);
41
- return box !== null && box[1] === box[2];
42
- }
43
23
  function identityInitials(name) {
44
24
  return name
45
25
  .split(/\s+/)
@@ -72,7 +52,7 @@ function Tile({ kind, label, size, tone, children, }) {
72
52
  * @family Status
73
53
  * @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.
74
54
  * @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`.
75
- * @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 company is its logo: the one white ink Sofia kept from its site on a black arch with its edition's colour edge in both themes, or a square SVG icon in its own colours filling the tile, as Vercel shows a project's favicon; its two initials on a compact arch, cobalto for a company and arancio for an approved Partner, until a logo is found or when the stored logo does not load. The edition follows the company's standing, never the directory it was opened from; a person is their picture, then their initials, in a round mark; a record with its own object picture, such as a Library skill or plugin, draws that picture on its kind's ground; 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 Library skill takes the purple category ground with the Skills icon and a plugin the `info-soft` ground with the Plugins icon, both without a ring, so the ground says which kind it is, and an entry that has its own object picture draws it on that ground in the icon's place. A company arch has a semicircular top and `{rounded.sm}` bottom corners; a record tile takes `{rounded.sm}` (6 px) corners, `{rounded.md}` (8 px) at 40 and 64; an app's tile takes `{rounded.md}` at every size.
55
+ * @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 company is its own logo, contained whole in a neutral tile in its original colours and proportions, as Vercel shows a project's favicon; its two initials on a compact arch, cobalto for a company and arancio for an approved Partner, until a logo is found or when the stored logo does not load. The edition follows the company's standing, never the directory it was opened from; a person is their picture, then their initials, in a round mark; a record with its own object picture, such as a Library skill or plugin, draws that picture on its kind's ground; 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 Library skill takes the purple category ground with the Skills icon and a plugin the `info-soft` ground with the Plugins icon, both without a ring, so the ground says which kind it is, and an entry that has its own object picture draws it on that ground in the icon's place. A company arch has a semicircular top and `{rounded.sm}` bottom corners; a record tile takes `{rounded.sm}` (6 px) corners, `{rounded.md}` (8 px) at 40 and 64; an app's tile takes `{rounded.md}` at every size.
76
56
  * @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, and no other kind takes letters.
77
57
  * @access Decorative beside the record's name. Alone, as in a narrow rail, `label` names it.
78
58
  */
@@ -90,7 +70,7 @@ export function IdentityMark(props) {
90
70
  return (_jsxs(Avatar, { "data-kind": "person", "data-slot": "identity-mark", style: { width: size, height: size }, ...avatarName(props.label), children: [props.image ? _jsx(AvatarImage, { alt: "", src: props.image }) : null, _jsx(AvatarFallback, { style: { fontSize: measures.initials }, children: identityInitials(props.name) })] }));
91
71
  case "company": {
92
72
  const initials = (_jsx(Tile, { kind: "company", label: props.label, size: size, tone: props.edition ?? "company", children: _jsx("span", { "aria-hidden": "true", className: "leading-none font-semibold", style: { fontSize: measures.initials }, children: identityInitials(props.name) }) }));
93
- return props.logo ? (_jsx(CompanyLogo, { colourTile: logoIsColourTile(props.logo), edition: props.edition ?? "company", fallback: initials, logo: props.logo, naming: naming(props.label), size: size })) : (initials);
73
+ return props.logo ? (_jsx(CompanyLogo, { fallback: initials, logo: props.logo, naming: naming(props.label), size: size })) : (initials);
94
74
  }
95
75
  case "record":
96
76
  return (_jsx(Tile, { kind: "record", 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 })) }));
@@ -1,56 +1,51 @@
1
1
  import type { ComponentProps } from "react";
2
- import { type BadgeSize } from "./badge.js";
3
2
  type StatusBadgeTone = "success" | "warning" | "danger" | "info" | "neutral";
4
- type StatusBadgeEmphasis = "subtle" | "solid";
3
+ type StatusBadgeSize = "sm" | "md" | "lg";
5
4
  type StatusBadgeProps = Omit<ComponentProps<"span">, "children" | "onClick"> & {
6
5
  tone: StatusBadgeTone;
7
6
  label: string;
8
- size?: BadgeSize;
9
- /** `subtle` in rows, tables, lists and menus; `solid` where the status stands on its own. */
10
- emphasis?: StatusBadgeEmphasis;
7
+ size?: StatusBadgeSize;
11
8
  /** How long the status has held, set after the word in tabular figures. */
12
9
  since?: string;
13
10
  };
14
11
  /**
15
- * A record's state: a mark and a word in one of five tones.
12
+ * A record's state: a shape of dots and a word, in one of five tones.
16
13
  *
17
14
  * @family Status
18
15
  * @when "Use Badge for short, scannable metadata that sits next to the thing it describes: status, plan tier, environment, or role." (https://vercel.com/geist/badge)
19
16
  * Ours: a record's state, when it sets the row apart: failed, waiting on a person, a step in
20
17
  * motion, paused, connected. Gray is the default and every terminal state that needs no action;
21
18
  * green is healthy or complete; red is unusable or failed; amber is waiting on a person's
22
- * decision; blue is a step in motion.
19
+ * decision; blue is a step in motion. Each tone has its own shape: green a circle, blue a diamond,
20
+ * amber a triangle, red a square, gray an empty ring.
23
21
  * "Map meaning to color: `green` for healthy, `red` for error, `amber` for warning, `blue` for informational or production, `gray` for neutral." (https://vercel.com/geist/badge)
24
22
  * Ours: success is green, danger red, warning amber, info blue, neutral gray, whatever the state's
25
23
  * name, in our colour values. The tone and the word are required, and the type asks for both.
26
24
  * @instead "For non-deployment statuses (Workflow runs, Queue messages, Sandbox, Cron jobs), use a `Badge` with the canonical state vocabulary instead of repurposing the dot." (https://vercel.com/geist/status-dot)
27
25
  * Ours: that `Badge` is `StatusBadge`. For a category (an installation, a partner) use `Tag`. For
28
26
  * plain metadata (a role, a plan) use `Badge`. For a control reporting its own action use
29
- * `ActionStatus`. A state beside its name still uses this badge; status dots are not used.
27
+ * `ActionStatus`. A state beside its name still uses this component.
30
28
  * @behaviour "Badges are static labels." (https://vercel.com/geist/badge)
31
29
  * "Don’t wire `onClick` onto them; promote to a Button or link if the user can act on the value." (https://vercel.com/geist/badge)
32
- * Ours: a status never moves and is never a button.
33
- * "The `-subtle` suffix tones any of them down on dense surfaces." (https://vercel.com/geist/badge)
34
- * Ours: `subtle`, the default, is for rows, tables, lists and menus: it fills the pill with the
35
- * tone's soft ground and sets the mark and the word in its strong shade; gray takes the foreground
36
- * colour, never a muted one. `solid` is for a status that stands on its own, such as an object's
37
- * page header or a card title: it fills the pill with the tone's strong colour and sets the mark
38
- * and the word on it. No border and no ring either way. The `subtle` pairs are the tone's soft
39
- * ground with its deep shade (gray: `canvas-soft-2` with the foreground colour).
40
- * "Don’t add a checkmark icon for success states or an X for errors; the variant carries that signal." (https://vercel.com/geist/badge)
41
- * Ours: it always draws its tone's mark, even for success and error: the mark repeats the tone as a
42
- * shape so a status reads without colour. Each app keeps one value-to-tone map in one module;
43
- * nothing else picks a status colour. A healthy record shows no badge, except the
44
- * operational-state and run-outcome columns defined by DESIGN.md's Status rule.
30
+ * Ours: a status never moves and is never a button. It is the tone's shape and the word, with no
31
+ * pill, no fill and no border, as Vercel's deployment list sets a state beside its time. The shape
32
+ * is filled with dots on a honeycomb grid, the material of the Agent orbs: an outline of evenly
33
+ * spaced dots and a field of dots inside it; gray's ring stays empty, the quiet tone. The dots
34
+ * take the tone's colour and the word the foreground colour; amber's outline takes its deep step
35
+ * so the triangle holds on white. `size`
36
+ * follows the type beside it: `sm` (a 14 px shape, 12 px word) in a small line such as an Agent's
37
+ * state line, `md` (16 px, 13 px) in a row, `lg` (20 px, 14 px) beside an object's name.
45
38
  * @words "Title Case, one word when possible, two max: `Active`, `Pending`, `Pro`, `Enterprise Trial`." (https://vercel.com/geist/badge)
46
39
  * Ours: a state, not a verb (*Failed*, *Waiting for you*), in sentence case, one to three words, no
47
40
  * trailing ellipsis. Italian runs a third longer: *In attesa di te*.
48
41
  * "Match the canonical API or log term: `Production` not `Prod`, `Deployed` not `Live`, `Canceled` not `Cancelled` (the Vercel API uses one L)." (https://vercel.com/geist/badge)
49
42
  * "Skip stuffing sentences inside (`Currently Active`, `You are on Pro`); the surrounding row supplies the context." (https://vercel.com/geist/badge)
50
43
  * @access "Don’t rely on color alone; the text has to be readable without it." (https://vercel.com/geist/badge)
51
- * Ours: the word is the name; the mark is `aria-hidden`, so colour never carries the state alone.
52
- * Not focusable.
44
+ * Ours: the word is the name; the shape is `aria-hidden`, so colour never carries the state alone.
45
+ * Not focusable. Each app keeps one value-to-tone map in one module; nothing else picks a status
46
+ * colour. A healthy record shows no status, except the operational-state and run-outcome columns
47
+ * defined by DESIGN.md's Status rule.
53
48
  */
54
- declare function StatusBadge({ tone, label, since, className, size, emphasis, ...props }: StatusBadgeProps): import("react").JSX.Element;
55
- export { StatusBadge, type StatusBadgeEmphasis, type StatusBadgeProps, type StatusBadgeTone };
49
+ declare function StatusBadge({ tone, label, since, className, size, ...props }: StatusBadgeProps): import("react").JSX.Element;
50
+ export { StatusBadge, type StatusBadgeProps, type StatusBadgeTone };
56
51
  //# sourceMappingURL=status-badge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"status-badge.d.ts","sourceRoot":"","sources":["../../src/components/status-badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAG5C,OAAO,EAAS,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG3D,KAAK,eAAe,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAE7E,KAAK,mBAAmB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAgD9C,KAAK,gBAAgB,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,GAAG;IAC7E,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,mBAAmB,CAAC;IAC/B,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,iBAAS,WAAW,CAAC,EACnB,IAAI,EACJ,KAAK,EACL,KAAK,EACL,SAAS,EACT,IAAW,EACX,QAAmB,EACnB,GAAG,KAAK,EACT,EAAE,gBAAgB,+BAqBlB;AAED,OAAO,EAAE,WAAW,EAAE,KAAK,mBAAmB,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"status-badge.d.ts","sourceRoot":"","sources":["../../src/components/status-badge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAS5C,KAAK,eAAe,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAE7E,KAAK,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAsD1C,KAAK,gBAAgB,GAAG,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC,GAAG;IAC7E,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,iBAAS,WAAW,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAW,EAAE,GAAG,KAAK,EAAE,EAAE,gBAAgB,+BA4B9F;AAED,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,CAAC"}
@@ -1,93 +1,71 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { CircleCheck, CircleDot, CirclePause, CircleX, Clock } from "lucide-react";
3
- import { Badge } from "./badge.js";
2
+ import { STATUS_MARK_DOT_RADIUS, STATUS_MARK_PATHS, } from "../internal/status-mark.js";
4
3
  import { cn } from "../lib/utils.js";
5
- const FILL_BY_TONE = {
6
- success: {
7
- name: "green",
8
- subtle: "bg-success-soft text-success-soft-foreground",
9
- solid: "bg-success text-success-foreground",
10
- },
4
+ // One shape per tone, always the same one: the tone said again as a shape, so a
5
+ // status reads without colour. Amber's edge takes the deep step, because giallo
6
+ // alone does not hold a shape on white.
7
+ const TONE = {
8
+ success: { name: "green", shape: "circle", ink: "text-success" },
11
9
  warning: {
12
10
  name: "amber",
13
- subtle: "bg-warning-soft text-warning-soft-foreground",
14
- solid: "bg-warning text-warning-foreground",
15
- },
16
- danger: {
17
- name: "red",
18
- subtle: "bg-destructive-soft text-destructive-soft-foreground",
19
- solid: "bg-destructive text-destructive-foreground",
20
- },
21
- info: {
22
- name: "blue",
23
- subtle: "bg-info-soft text-info-soft-foreground",
24
- solid: "bg-info text-info-foreground",
11
+ shape: "triangle",
12
+ ink: "text-warning",
13
+ edge: "text-warning-soft-foreground",
25
14
  },
26
- neutral: {
27
- name: "gray",
28
- subtle: "bg-accent text-foreground",
29
- solid: "bg-muted-foreground text-background",
30
- },
31
- };
32
- // One mark per tone, always the same one: the tone said again as a shape, so a
33
- // status reads without colour. The info dot is filled and still.
34
- const MARK_BY_TONE = {
35
- success: CircleCheck,
36
- warning: Clock,
37
- danger: CircleX,
38
- info: CircleDot,
39
- neutral: CirclePause,
15
+ danger: { name: "red", shape: "square", ink: "text-destructive" },
16
+ info: { name: "blue", shape: "diamond", ink: "text-info" },
17
+ neutral: { name: "gray", shape: "ring", ink: "text-muted-foreground" },
40
18
  };
41
- // Every mark is a circle, which has less air round it than a square glyph, so
42
- // it sits closer to the pill's edge.
43
- const CIRCLE_MARK_INSET = {
44
- sm: "has-data-[icon=inline-start]:pl-1",
45
- md: "has-data-[icon=inline-start]:pl-1.25",
46
- lg: "has-data-[icon=inline-start]:pl-2",
19
+ const SIZE = {
20
+ sm: { text: "text-caption gap-1", mark: "size-3.5" },
21
+ md: { text: "text-label-sm gap-1.5", mark: "size-4" },
22
+ lg: { text: "text-label gap-2", mark: "size-5" },
47
23
  };
24
+ function StatusMark({ shape, className, edge, }) {
25
+ const paths = STATUS_MARK_PATHS[shape];
26
+ return (_jsxs("svg", { "aria-hidden": "true", className: cn("shrink-0", className), "data-slot": "status-badge-mark", "data-mark": shape, fill: "none", stroke: "currentColor", strokeLinecap: "round", viewBox: "-10 -10 20 20", children: [_jsx("path", { d: paths.inner, strokeOpacity: 0.92, strokeWidth: 2 * STATUS_MARK_DOT_RADIUS.inner }), _jsx("path", { className: edge, d: paths.edge, strokeWidth: 2 * STATUS_MARK_DOT_RADIUS.edge })] }));
27
+ }
48
28
  /**
49
- * A record's state: a mark and a word in one of five tones.
29
+ * A record's state: a shape of dots and a word, in one of five tones.
50
30
  *
51
31
  * @family Status
52
32
  * @when "Use Badge for short, scannable metadata that sits next to the thing it describes: status, plan tier, environment, or role." (https://vercel.com/geist/badge)
53
33
  * Ours: a record's state, when it sets the row apart: failed, waiting on a person, a step in
54
34
  * motion, paused, connected. Gray is the default and every terminal state that needs no action;
55
35
  * green is healthy or complete; red is unusable or failed; amber is waiting on a person's
56
- * decision; blue is a step in motion.
36
+ * decision; blue is a step in motion. Each tone has its own shape: green a circle, blue a diamond,
37
+ * amber a triangle, red a square, gray an empty ring.
57
38
  * "Map meaning to color: `green` for healthy, `red` for error, `amber` for warning, `blue` for informational or production, `gray` for neutral." (https://vercel.com/geist/badge)
58
39
  * Ours: success is green, danger red, warning amber, info blue, neutral gray, whatever the state's
59
40
  * name, in our colour values. The tone and the word are required, and the type asks for both.
60
41
  * @instead "For non-deployment statuses (Workflow runs, Queue messages, Sandbox, Cron jobs), use a `Badge` with the canonical state vocabulary instead of repurposing the dot." (https://vercel.com/geist/status-dot)
61
42
  * Ours: that `Badge` is `StatusBadge`. For a category (an installation, a partner) use `Tag`. For
62
43
  * plain metadata (a role, a plan) use `Badge`. For a control reporting its own action use
63
- * `ActionStatus`. A state beside its name still uses this badge; status dots are not used.
44
+ * `ActionStatus`. A state beside its name still uses this component.
64
45
  * @behaviour "Badges are static labels." (https://vercel.com/geist/badge)
65
46
  * "Don’t wire `onClick` onto them; promote to a Button or link if the user can act on the value." (https://vercel.com/geist/badge)
66
- * Ours: a status never moves and is never a button.
67
- * "The `-subtle` suffix tones any of them down on dense surfaces." (https://vercel.com/geist/badge)
68
- * Ours: `subtle`, the default, is for rows, tables, lists and menus: it fills the pill with the
69
- * tone's soft ground and sets the mark and the word in its strong shade; gray takes the foreground
70
- * colour, never a muted one. `solid` is for a status that stands on its own, such as an object's
71
- * page header or a card title: it fills the pill with the tone's strong colour and sets the mark
72
- * and the word on it. No border and no ring either way. The `subtle` pairs are the tone's soft
73
- * ground with its deep shade (gray: `canvas-soft-2` with the foreground colour).
74
- * "Don’t add a checkmark icon for success states or an X for errors; the variant carries that signal." (https://vercel.com/geist/badge)
75
- * Ours: it always draws its tone's mark, even for success and error: the mark repeats the tone as a
76
- * shape so a status reads without colour. Each app keeps one value-to-tone map in one module;
77
- * nothing else picks a status colour. A healthy record shows no badge, except the
78
- * operational-state and run-outcome columns defined by DESIGN.md's Status rule.
47
+ * Ours: a status never moves and is never a button. It is the tone's shape and the word, with no
48
+ * pill, no fill and no border, as Vercel's deployment list sets a state beside its time. The shape
49
+ * is filled with dots on a honeycomb grid, the material of the Agent orbs: an outline of evenly
50
+ * spaced dots and a field of dots inside it; gray's ring stays empty, the quiet tone. The dots
51
+ * take the tone's colour and the word the foreground colour; amber's outline takes its deep step
52
+ * so the triangle holds on white. `size`
53
+ * follows the type beside it: `sm` (a 14 px shape, 12 px word) in a small line such as an Agent's
54
+ * state line, `md` (16 px, 13 px) in a row, `lg` (20 px, 14 px) beside an object's name.
79
55
  * @words "Title Case, one word when possible, two max: `Active`, `Pending`, `Pro`, `Enterprise Trial`." (https://vercel.com/geist/badge)
80
56
  * Ours: a state, not a verb (*Failed*, *Waiting for you*), in sentence case, one to three words, no
81
57
  * trailing ellipsis. Italian runs a third longer: *In attesa di te*.
82
58
  * "Match the canonical API or log term: `Production` not `Prod`, `Deployed` not `Live`, `Canceled` not `Cancelled` (the Vercel API uses one L)." (https://vercel.com/geist/badge)
83
59
  * "Skip stuffing sentences inside (`Currently Active`, `You are on Pro`); the surrounding row supplies the context." (https://vercel.com/geist/badge)
84
60
  * @access "Don’t rely on color alone; the text has to be readable without it." (https://vercel.com/geist/badge)
85
- * Ours: the word is the name; the mark is `aria-hidden`, so colour never carries the state alone.
86
- * Not focusable.
61
+ * Ours: the word is the name; the shape is `aria-hidden`, so colour never carries the state alone.
62
+ * Not focusable. Each app keeps one value-to-tone map in one module; nothing else picks a status
63
+ * colour. A healthy record shows no status, except the operational-state and run-outcome columns
64
+ * defined by DESIGN.md's Status rule.
87
65
  */
88
- function StatusBadge({ tone, label, since, className, size = "md", emphasis = "subtle", ...props }) {
89
- const Mark = MARK_BY_TONE[tone];
90
- const fill = FILL_BY_TONE[tone];
91
- return (_jsxs(Badge, { className: cn(fill[emphasis], CIRCLE_MARK_INSET[size], className), "data-emphasis": emphasis, "data-variant": fill.name, icon: _jsx(Mark, { "aria-hidden": "true" }), size: size, ...props, children: [label, since ? (_jsx("span", { className: "tabular-nums", "data-slot": "status-badge-since", children: since })) : null] }));
66
+ function StatusBadge({ tone, label, since, className, size = "md", ...props }) {
67
+ const { name, shape, ink, edge } = TONE[tone];
68
+ const { text, mark } = SIZE[size];
69
+ return (_jsxs("span", { className: cn("inline-flex w-fit max-w-full shrink-0 items-center font-medium whitespace-nowrap text-foreground", text, className), "data-size": size, "data-slot": "status-badge", "data-variant": name, ...props, children: [_jsx(StatusMark, { className: cn(mark, ink), edge: edge, shape: shape }), label, since ? (_jsx("span", { className: "font-normal text-muted-foreground tabular-nums", "data-slot": "status-badge-since", children: since })) : null] }));
92
70
  }
93
71
  export { StatusBadge };
@@ -3,10 +3,8 @@ import { type ReactNode } from "react";
3
3
  * A company's logo on its tile, and `fallback`, its initials, when the stored logo does not load,
4
4
  * so a broken address never leaves an empty tile.
5
5
  */
6
- export declare function CompanyLogo({ logo, colourTile, edition, size, naming, fallback, }: {
6
+ export declare function CompanyLogo({ logo, size, naming, fallback, }: {
7
7
  logo: string;
8
- colourTile: boolean;
9
- edition: "company" | "partner";
10
8
  size: number;
11
9
  naming: {
12
10
  role: "img";
@@ -1 +1 @@
1
- {"version":3,"file":"company-logo.d.ts","sourceRoot":"","sources":["../../src/internal/company-logo.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAIjD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,UAAU,EACV,OAAO,EACP,IAAI,EACJ,MAAM,EACN,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,SAAS,GAAG,SAAS,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,IAAI,EAAE,KAAK,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,aAAa,EAAE,IAAI,CAAA;KAAE,CAAC;IACxE,QAAQ,EAAE,SAAS,CAAC;CACrB,+TAgCA"}
1
+ {"version":3,"file":"company-logo.d.ts","sourceRoot":"","sources":["../../src/internal/company-logo.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAEjD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,IAAI,EAAE,KAAK,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,aAAa,EAAE,IAAI,CAAA;KAAE,CAAC;IACxE,QAAQ,EAAE,SAAS,CAAC;CACrB,+TAoBA"}
@@ -1,18 +1,13 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useState } from "react";
4
- import { cn } from "../lib/utils.js";
5
4
  /**
6
5
  * A company's logo on its tile, and `fallback`, its initials, when the stored logo does not load,
7
6
  * so a broken address never leaves an empty tile.
8
7
  */
9
- export function CompanyLogo({ logo, colourTile, edition, size, naming, fallback, }) {
8
+ export function CompanyLogo({ logo, size, naming, fallback, }) {
10
9
  const [failedLogo, setFailedLogo] = useState(null);
11
10
  if (failedLogo === logo)
12
11
  return fallback;
13
- return (_jsx("span", { className: cn("inline-flex shrink-0 items-center justify-center overflow-hidden", colourTile
14
- ? size >= 40
15
- ? "rounded-md"
16
- : "rounded-sm"
17
- : cn("rounded-t-full rounded-b-sm border-2 bg-nc-identity-ink", edition === "partner" ? "border-nc-identity-arancio" : "border-nc-identity-cobalto")), "data-kind": "company", "data-slot": "identity-mark", style: { width: size, height: size }, ...naming, children: _jsx("img", { alt: "", className: colourTile ? "size-full object-contain" : "max-h-[60%] max-w-[78%] object-contain", "data-slot": "identity-mark-logo", onError: () => setFailedLogo(logo), src: logo }) }));
12
+ return (_jsx("span", { className: "inline-flex shrink-0 items-center justify-center overflow-hidden rounded-md bg-nc-card-graphite ring-1 ring-border ring-inset", "data-kind": "company", "data-slot": "identity-mark", style: { width: size, height: size }, ...naming, children: _jsx("img", { alt: "", className: "size-full object-contain", "data-slot": "identity-mark-logo", onError: () => setFailedLogo(logo), src: logo }) }));
18
13
  }
@@ -0,0 +1,16 @@
1
+ export type StatusMarkShape = "circle" | "diamond" | "triangle" | "square" | "ring";
2
+ export type StatusMarkDot = {
3
+ x: number;
4
+ y: number;
5
+ edge: boolean;
6
+ };
7
+ export declare const STATUS_MARK_DOTS: Record<StatusMarkShape, readonly StatusMarkDot[]>;
8
+ export declare const STATUS_MARK_DOT_RADIUS: {
9
+ readonly inner: 0.7;
10
+ readonly edge: 0.76;
11
+ };
12
+ export declare const STATUS_MARK_PATHS: Record<StatusMarkShape, {
13
+ inner: string;
14
+ edge: string;
15
+ }>;
16
+ //# sourceMappingURL=status-mark.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-mark.d.ts","sourceRoot":"","sources":["../../src/internal/status-mark.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEpF,MAAM,MAAM,aAAa,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AA+GpE,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,eAAe,EAAE,SAAS,aAAa,EAAE,CAM9E,CAAC;AAEF,eAAO,MAAM,sBAAsB;;;CAAsC,CAAC;AAgB1E,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,eAAe,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAMtF,CAAC"}
@@ -0,0 +1,128 @@
1
+ /*
2
+ * The five status marks: a shape filled with dots on a honeycomb grid, the
3
+ * material of the Agent orbs' dotted forms. Every coordinate is on a 20-unit
4
+ * square centred on 0, the size the marks are tuned at; a row of evenly spaced
5
+ * edge dots draws the outline and the inner dots keep clear of it, so the
6
+ * shape reads before the texture. The dots are computed once, at load.
7
+ */
8
+ const PITCH = 1.9;
9
+ // Sized by eye so the five carry the same weight beside a word; the triangle
10
+ // sits low because its weight is in its base.
11
+ const MARKS = {
12
+ circle: { outline: { kind: "circle", r: 6.7 } },
13
+ diamond: {
14
+ outline: {
15
+ kind: "polygon",
16
+ corners: [
17
+ [0, -7.8],
18
+ [7.8, 0],
19
+ [0, 7.8],
20
+ [-7.8, 0],
21
+ ],
22
+ },
23
+ },
24
+ triangle: {
25
+ outline: {
26
+ kind: "polygon",
27
+ corners: [
28
+ [0, -6.9],
29
+ [7.9, 6.6],
30
+ [-7.9, 6.6],
31
+ ],
32
+ },
33
+ },
34
+ square: {
35
+ outline: {
36
+ kind: "polygon",
37
+ corners: [
38
+ [-5.8, -5.8],
39
+ [5.8, -5.8],
40
+ [5.8, 5.8],
41
+ [-5.8, 5.8],
42
+ ],
43
+ },
44
+ },
45
+ ring: { outline: { kind: "circle", r: 6.7 }, hollow: true },
46
+ };
47
+ function sides(corners) {
48
+ return corners.map((start, index) => [start, corners[(index + 1) % corners.length]]);
49
+ }
50
+ function contains(outline, [x, y]) {
51
+ if (outline.kind === "circle")
52
+ return Math.hypot(x, y) <= outline.r;
53
+ let inside = false;
54
+ for (const [[ax, ay], [bx, by]] of sides(outline.corners)) {
55
+ if (ay > y !== by > y && x < ((bx - ax) * (y - ay)) / (by - ay) + ax)
56
+ inside = !inside;
57
+ }
58
+ return inside;
59
+ }
60
+ function distanceToEdge(outline, [x, y]) {
61
+ if (outline.kind === "circle")
62
+ return outline.r - Math.hypot(x, y);
63
+ return Math.min(...sides(outline.corners).map(([[ax, ay], [bx, by]]) => {
64
+ const dx = bx - ax;
65
+ const dy = by - ay;
66
+ const t = Math.max(0, Math.min(1, ((x - ax) * dx + (y - ay) * dy) / (dx * dx + dy * dy)));
67
+ return Math.hypot(x - ax - t * dx, y - ay - t * dy);
68
+ }));
69
+ }
70
+ // A dot on every corner, then evenly spaced along each side.
71
+ function edgeDots(outline) {
72
+ if (outline.kind === "circle") {
73
+ const count = Math.round((2 * Math.PI * outline.r) / PITCH);
74
+ return Array.from({ length: count }, (_, index) => {
75
+ const angle = -Math.PI / 2 + (index / count) * 2 * Math.PI;
76
+ return [Math.cos(angle) * outline.r, Math.sin(angle) * outline.r];
77
+ });
78
+ }
79
+ return sides(outline.corners).flatMap(([[ax, ay], [bx, by]]) => {
80
+ const count = Math.max(1, Math.round(Math.hypot(bx - ax, by - ay) / PITCH));
81
+ return Array.from({ length: count }, (_, step) => [ax + ((bx - ax) * step) / count, ay + ((by - ay) * step) / count]);
82
+ });
83
+ }
84
+ function innerDots(outline) {
85
+ const rowHeight = (PITCH * Math.sqrt(3)) / 2;
86
+ const dots = [];
87
+ for (let row = -6; row <= 6; row++) {
88
+ for (let column = -6; column <= 6; column++) {
89
+ const dot = [column * PITCH + (row % 2 === 0 ? 0 : PITCH / 2), row * rowHeight];
90
+ if (contains(outline, dot) && distanceToEdge(outline, dot) >= PITCH * 0.85)
91
+ dots.push(dot);
92
+ }
93
+ }
94
+ return dots;
95
+ }
96
+ function dotsOf(shape) {
97
+ const { outline, hollow } = MARKS[shape];
98
+ return [
99
+ ...(hollow ? [] : innerDots(outline)).map(([x, y]) => ({ x, y, edge: false })),
100
+ ...edgeDots(outline).map(([x, y]) => ({ x, y, edge: true })),
101
+ ];
102
+ }
103
+ export const STATUS_MARK_DOTS = {
104
+ circle: dotsOf("circle"),
105
+ diamond: dotsOf("diamond"),
106
+ triangle: dotsOf("triangle"),
107
+ square: dotsOf("square"),
108
+ ring: dotsOf("ring"),
109
+ };
110
+ export const STATUS_MARK_DOT_RADIUS = { inner: 0.7, edge: 0.76 };
111
+ // Each layer of dots as one path of zero-length strokes: drawn with round caps
112
+ // at twice the dot's radius, every `M x y h0` is one dot, so a mark is two
113
+ // nodes however many dots it has.
114
+ const pathOf = (dots) => dots.map(({ x, y }) => `M${x.toFixed(2)} ${y.toFixed(2)}h0`).join("");
115
+ function pathsOf(shape) {
116
+ const dots = STATUS_MARK_DOTS[shape];
117
+ return {
118
+ inner: pathOf(dots.filter((dot) => !dot.edge)),
119
+ edge: pathOf(dots.filter((dot) => dot.edge)),
120
+ };
121
+ }
122
+ export const STATUS_MARK_PATHS = {
123
+ circle: pathsOf("circle"),
124
+ diamond: pathsOf("diamond"),
125
+ triangle: pathsOf("triangle"),
126
+ square: pathsOf("square"),
127
+ ring: pathsOf("ring"),
128
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/showcase/specimens/status.tsx"],"names":[],"mappings":"AAyDA,iBAAS,oBAAoB,gCAiB5B;AAYD,iBAAS,aAAa,gCA2DrB;AAED,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;CAoQQ,CAAC"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/showcase/specimens/status.tsx"],"names":[],"mappings":"AAwDA,iBAAS,oBAAoB,gCAiB5B;AAYD,iBAAS,aAAa,gCA2DrB;AAED,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;CAoQQ,CAAC"}
@@ -22,8 +22,7 @@ import { StatusBadge } from "../../components/status-badge.js";
22
22
  import { Tag } from "../../components/tag.js";
23
23
  import { ObjectFactValue } from "../../components/object-fact-value.js";
24
24
  import { ARANCIO, COBALTO, nothing, VERDE } from "../samples.js";
25
- // A one-ink logo as Sofia keeps it: white where the mark is, transparent elsewhere.
26
- const SAMPLE_LOGO = `data:image/svg+xml,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 16"><text x="0" y="13" fill="white" font-family="sans-serif" font-size="14" font-weight="700">ROSSI</text></svg>')}`;
25
+ const SAMPLE_LOGO = `data:image/svg+xml,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 16"><rect width="64" height="16" fill="white"/><text x="0" y="13" fill="navy" font-family="sans-serif" font-size="14" font-weight="700">ROSSI</text></svg>')}`;
27
26
  const SAMPLE_ICON = `data:image/svg+xml,${encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="7" fill="royalblue"/><path d="M10 9h8a5 5 0 0 1 0 10h-4v5h-4z" fill="white"/></svg>')}`;
28
27
  const MEMBERS = [
29
28
  { id: "1", name: "Ada Rossi" },
@@ -669,8 +669,8 @@ export const USAGE = [
669
669
  family: "Status",
670
670
  summary: 'Short metadata beside its subject: a role, a plan, "Beta".',
671
671
  when: '"Use Badge for short, scannable metadata that sits next to the thing it describes: status, plan tier, environment, or role." (https://vercel.com/geist/badge) "One badge per row; two side by side is a sign the row needs a second column." (https://vercel.com/geist/badge) Ours: a state is a `StatusBadge`; a `Badge` is a role (*Owner*), a plan, an environment, *Beta*. It always has a word, or one `icon` with a `title`, and the type asks for it. `size` follows the type beside it: `sm` (20 px) next to small labels, `md` (24 px) in a row, `lg` (32 px) in a header. The category fills (teal, purple, pink) name a kind.',
672
- instead: '"For a colored dot without text, use `Status Dot`." (https://vercel.com/geist/badge) Ours: status dots are not used; a state keeps its mark and word together in `StatusBadge`. "For clickable filter chips that toggle a query, use the `pill` variant or a small `Button`." (https://vercel.com/geist/badge) Ours: there is no `pill` variant; a filter that toggles a query is a `Button` or `CollectionFilters`. For a state use `StatusBadge`, which picks the tone and its mark: the gray, green, red, amber and blue fills, soft or solid, are its alone. One categorical fact in a row is a `Tag`. A change in a figure is a `TrendBadge`.',
673
- behaviour: '"Badges are static labels." (https://vercel.com/geist/badge) "Don’t wire `onClick` onto them; promote to a Button or link if the user can act on the value." (https://vercel.com/geist/badge) Ours: it takes no `onClick`, no hover and no focus; it becomes a link only through `render` with a real anchor, and only `outline` takes the hover, a gray fill. "Keep badge content to text or `icon` + text." (https://vercel.com/geist/badge) "Never stack two icons or a child Badge inside a Badge." (https://vercel.com/geist/badge) Ours: never a paragraph either. "Pair lifecycle badges (`Alpha`, `Beta`, `Early Access`) with a `Tooltip` that names the limit, like `Alpha: API may change before GA`." (https://vercel.com/geist/badge) Ours: the badge is the `render` of a `TooltipTrigger` with `tabIndex={0}`, so keyboard and touch users reach the limit it names. "Don’t add a checkmark icon for success states or an X for errors; the variant carries that signal." (https://vercel.com/geist/badge) Ours: only `StatusBadge` carries a state, and it always draws its tone\'s mark. "Map meaning to color: `green` for healthy, `red` for error, `amber` for warning, `blue` for informational or production, `gray` for neutral." (https://vercel.com/geist/badge) "The `-subtle` suffix tones any of them down on dense surfaces." (https://vercel.com/geist/badge) Ours: those fills are `StatusBadge`\'s, as the tones `success`, `danger`, `warning`, `info` and `neutral`, with `emphasis` `subtle` or `solid`. `Badge` is a fixed-height pill (20, 24 or 32 px) with no border: colour sits on the fill and the word. A soft fill takes its hue\'s strong shade for the word, `default` is the foreground colour inverted, and `outline` is neutral, a page-colour fill held by a 1 px inset ring. With an icon, the left padding is 6, 8 or 12 px, so the mark and the word look evenly spaced. The icon takes the word\'s colour.',
672
+ instead: '"For a colored dot without text, use `Status Dot`." (https://vercel.com/geist/badge) Ours: a single dot is never a status; a state is `StatusBadge`, its tone\'s shape of dots and its word, with no pill. "For clickable filter chips that toggle a query, use the `pill` variant or a small `Button`." (https://vercel.com/geist/badge) Ours: there is no `pill` variant; a filter that toggles a query is a `Button` or `CollectionFilters`. For a state use `StatusBadge`, which draws the tone as its shape; Badge has no gray, green, red, amber or blue fill. One categorical fact in a row is a `Tag`. A change in a figure is a `TrendBadge`.',
673
+ behaviour: '"Badges are static labels." (https://vercel.com/geist/badge) "Don’t wire `onClick` onto them; promote to a Button or link if the user can act on the value." (https://vercel.com/geist/badge) Ours: it takes no `onClick`, no hover and no focus; it becomes a link only through `render` with a real anchor, and only `outline` takes the hover, a gray fill. "Keep badge content to text or `icon` + text." (https://vercel.com/geist/badge) "Never stack two icons or a child Badge inside a Badge." (https://vercel.com/geist/badge) Ours: never a paragraph either. "Pair lifecycle badges (`Alpha`, `Beta`, `Early Access`) with a `Tooltip` that names the limit, like `Alpha: API may change before GA`." (https://vercel.com/geist/badge) Ours: the badge is the `render` of a `TooltipTrigger` with `tabIndex={0}`, so keyboard and touch users reach the limit it names. "Don’t add a checkmark icon for success states or an X for errors; the variant carries that signal." (https://vercel.com/geist/badge) Ours: only `StatusBadge` carries a state, and it always draws its tone\'s shape. "Map meaning to color: `green` for healthy, `red` for error, `amber` for warning, `blue` for informational or production, `gray` for neutral." (https://vercel.com/geist/badge) "The `-subtle` suffix tones any of them down on dense surfaces." (https://vercel.com/geist/badge) Ours: those meanings are `StatusBadge`\'s, as the tones `success`, `danger`, `warning`, `info` and `neutral`, each drawn as its shape. `Badge` is a fixed-height pill (20, 24 or 32 px) with no border: colour sits on the fill and the word. A soft fill takes its hue\'s strong shade for the word, `default` is the foreground colour inverted, and `outline` is neutral, a page-colour fill held by a 1 px inset ring. With an icon, the left padding is 6, 8 or 12 px, so the mark and the word look evenly spaced. The icon takes the word\'s colour.',
674
674
  words: '"Title Case, one word when possible, two max: `Active`, `Pending`, `Pro`, `Enterprise Trial`." (https://vercel.com/geist/badge) Ours: sentence case (*Enterprise trial*). "Match the canonical API or log term: `Production` not `Prod`, `Deployed` not `Live`, `Canceled` not `Cancelled` (the Vercel API uses one L)." (https://vercel.com/geist/badge) Ours: the term is our own API\'s. "Skip stuffing sentences inside (`Currently Active`, `You are on Pro`); the surrounding row supplies the context." (https://vercel.com/geist/badge)',
675
675
  access: '"Set `title` for icon-only or ambiguous badges so screen readers announce the meaning." (https://vercel.com/geist/badge) Ours: an icon alone must set `title`, and the type asks for it. Read in place as text. "Don’t rely on color alone; the text has to be readable without it." (https://vercel.com/geist/badge) Ours: a coloured variant still carries its word.',
676
676
  },
@@ -714,7 +714,7 @@ export const USAGE = [
714
714
  summary: "What says which thing a record is, at a fixed size at the start of its row, its object's head or its feed line: an Agent's orb, an app's logo, a company's initials, a person's picture, or a record kind's icon on its tile.",
715
715
  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.",
716
716
  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`.",
717
- 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 company is its logo: the one white ink Sofia kept from its site on a black arch with its edition's colour edge in both themes, or a square SVG icon in its own colours filling the tile, as Vercel shows a project's favicon; its two initials on a compact arch, cobalto for a company and arancio for an approved Partner, until a logo is found or when the stored logo does not load. The edition follows the company's standing, never the directory it was opened from; a person is their picture, then their initials, in a round mark; a record with its own object picture, such as a Library skill or plugin, draws that picture on its kind's ground; 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 Library skill takes the purple category ground with the Skills icon and a plugin the `info-soft` ground with the Plugins icon, both without a ring, so the ground says which kind it is, and an entry that has its own object picture draws it on that ground in the icon's place. A company arch has a semicircular top and `{rounded.sm}` bottom corners; a record tile takes `{rounded.sm}` (6 px) corners, `{rounded.md}` (8 px) at 40 and 64; an app's tile takes `{rounded.md}` at every size.",
717
+ 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 company is its own logo, contained whole in a neutral tile in its original colours and proportions, as Vercel shows a project's favicon; its two initials on a compact arch, cobalto for a company and arancio for an approved Partner, until a logo is found or when the stored logo does not load. The edition follows the company's standing, never the directory it was opened from; a person is their picture, then their initials, in a round mark; a record with its own object picture, such as a Library skill or plugin, draws that picture on its kind's ground; 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 Library skill takes the purple category ground with the Skills icon and a plugin the `info-soft` ground with the Plugins icon, both without a ring, so the ground says which kind it is, and an entry that has its own object picture draws it on that ground in the icon's place. A company arch has a semicircular top and `{rounded.sm}` bottom corners; a record tile takes `{rounded.sm}` (6 px) corners, `{rounded.md}` (8 px) at 40 and 64; an app's tile takes `{rounded.md}` at every size.",
718
718
  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, and no other kind takes letters.",
719
719
  access: "Decorative beside the record's name. Alone, as in a narrow rail, `label` names it.",
720
720
  },
@@ -788,12 +788,12 @@ export const USAGE = [
788
788
  name: "StatusBadge",
789
789
  module: "status-badge",
790
790
  family: "Status",
791
- summary: "A record's state: a mark and a word in one of five tones.",
792
- when: '"Use Badge for short, scannable metadata that sits next to the thing it describes: status, plan tier, environment, or role." (https://vercel.com/geist/badge) Ours: a record\'s state, when it sets the row apart: failed, waiting on a person, a step in motion, paused, connected. Gray is the default and every terminal state that needs no action; green is healthy or complete; red is unusable or failed; amber is waiting on a person\'s decision; blue is a step in motion. "Map meaning to color: `green` for healthy, `red` for error, `amber` for warning, `blue` for informational or production, `gray` for neutral." (https://vercel.com/geist/badge) Ours: success is green, danger red, warning amber, info blue, neutral gray, whatever the state\'s name, in our colour values. The tone and the word are required, and the type asks for both.',
793
- instead: '"For non-deployment statuses (Workflow runs, Queue messages, Sandbox, Cron jobs), use a `Badge` with the canonical state vocabulary instead of repurposing the dot." (https://vercel.com/geist/status-dot) Ours: that `Badge` is `StatusBadge`. For a category (an installation, a partner) use `Tag`. For plain metadata (a role, a plan) use `Badge`. For a control reporting its own action use `ActionStatus`. A state beside its name still uses this badge; status dots are not used.',
794
- behaviour: '"Badges are static labels." (https://vercel.com/geist/badge) "Don’t wire `onClick` onto them; promote to a Button or link if the user can act on the value." (https://vercel.com/geist/badge) Ours: a status never moves and is never a button. "The `-subtle` suffix tones any of them down on dense surfaces." (https://vercel.com/geist/badge) Ours: `subtle`, the default, is for rows, tables, lists and menus: it fills the pill with the tone\'s soft ground and sets the mark and the word in its strong shade; gray takes the foreground colour, never a muted one. `solid` is for a status that stands on its own, such as an object\'s page header or a card title: it fills the pill with the tone\'s strong colour and sets the mark and the word on it. No border and no ring either way. The `subtle` pairs are the tone\'s soft ground with its deep shade (gray: `canvas-soft-2` with the foreground colour). "Don’t add a checkmark icon for success states or an X for errors; the variant carries that signal." (https://vercel.com/geist/badge) Ours: it always draws its tone\'s mark, even for success and error: the mark repeats the tone as a shape so a status reads without colour. Each app keeps one value-to-tone map in one module; nothing else picks a status colour. A healthy record shows no badge, except the operational-state and run-outcome columns defined by DESIGN.md\'s Status rule.',
791
+ summary: "A record's state: a shape of dots and a word, in one of five tones.",
792
+ when: '"Use Badge for short, scannable metadata that sits next to the thing it describes: status, plan tier, environment, or role." (https://vercel.com/geist/badge) Ours: a record\'s state, when it sets the row apart: failed, waiting on a person, a step in motion, paused, connected. Gray is the default and every terminal state that needs no action; green is healthy or complete; red is unusable or failed; amber is waiting on a person\'s decision; blue is a step in motion. Each tone has its own shape: green a circle, blue a diamond, amber a triangle, red a square, gray an empty ring. "Map meaning to color: `green` for healthy, `red` for error, `amber` for warning, `blue` for informational or production, `gray` for neutral." (https://vercel.com/geist/badge) Ours: success is green, danger red, warning amber, info blue, neutral gray, whatever the state\'s name, in our colour values. The tone and the word are required, and the type asks for both.',
793
+ instead: '"For non-deployment statuses (Workflow runs, Queue messages, Sandbox, Cron jobs), use a `Badge` with the canonical state vocabulary instead of repurposing the dot." (https://vercel.com/geist/status-dot) Ours: that `Badge` is `StatusBadge`. For a category (an installation, a partner) use `Tag`. For plain metadata (a role, a plan) use `Badge`. For a control reporting its own action use `ActionStatus`. A state beside its name still uses this component.',
794
+ behaviour: "\"Badges are static labels.\" (https://vercel.com/geist/badge) \"Don’t wire `onClick` onto them; promote to a Button or link if the user can act on the value.\" (https://vercel.com/geist/badge) Ours: a status never moves and is never a button. It is the tone's shape and the word, with no pill, no fill and no border, as Vercel's deployment list sets a state beside its time. The shape is filled with dots on a honeycomb grid, the material of the Agent orbs: an outline of evenly spaced dots and a field of dots inside it; gray's ring stays empty, the quiet tone. The dots take the tone's colour and the word the foreground colour; amber's outline takes its deep step so the triangle holds on white. `size` follows the type beside it: `sm` (a 14 px shape, 12 px word) in a small line such as an Agent's state line, `md` (16 px, 13 px) in a row, `lg` (20 px, 14 px) beside an object's name.",
795
795
  words: '"Title Case, one word when possible, two max: `Active`, `Pending`, `Pro`, `Enterprise Trial`." (https://vercel.com/geist/badge) Ours: a state, not a verb (*Failed*, *Waiting for you*), in sentence case, one to three words, no trailing ellipsis. Italian runs a third longer: *In attesa di te*. "Match the canonical API or log term: `Production` not `Prod`, `Deployed` not `Live`, `Canceled` not `Cancelled` (the Vercel API uses one L)." (https://vercel.com/geist/badge) "Skip stuffing sentences inside (`Currently Active`, `You are on Pro`); the surrounding row supplies the context." (https://vercel.com/geist/badge)',
796
- access: '"Don’t rely on color alone; the text has to be readable without it." (https://vercel.com/geist/badge) Ours: the word is the name; the mark is `aria-hidden`, so colour never carries the state alone. Not focusable.',
796
+ access: '"Don’t rely on color alone; the text has to be readable without it." (https://vercel.com/geist/badge) Ours: the word is the name; the shape is `aria-hidden`, so colour never carries the state alone. Not focusable. Each app keeps one value-to-tone map in one module; nothing else picks a status colour. A healthy record shows no status, except the operational-state and run-outcome columns defined by DESIGN.md\'s Status rule.',
797
797
  },
798
798
  {
799
799
  name: "Tag",
@@ -815,7 +815,7 @@ export const USAGE = [
815
815
  instead: "An Agent as a record among others in a page's list is a RecordList row with an agent IdentityMark; an Agent's own page heads with ObjectPage.",
816
816
  behaviour: "It is the row's presentation alone: the consumer owns the control around it (Nadia's button that opens the Agent, a scene's list item that opens nothing). The current row's name is set in 500. A state tone draws the state line as a small StatusBadge; without a tone it stays plain text. Narrow, the orb stands alone and the words stay for a screen reader; the consumer names it with its state line in a tooltip, as Nadia's list does. Only the orb moves in the list. The name is 14 px and the state line 12 px at every size. The drawn orb leaves about 10 px of its 64 px box empty, so at 64 px that empty edge overlaps the row's padding (6 px above and below, 4 px at the start) and the words stand 4 px from the box: the row hugs the visible orb and the name sits beside it, as beside a solid picture. In Nadia's list her orb is 64 px in her real state, and 40 px in the narrow column from 640 to 899 px.",
817
817
  words: 'The name, then one state line: what she is doing now ("Answering on Telegram", "Working") or what she is for ("Sorts requests"). The tone never says more than the line does. Nadia\'s list says "Working", "Idle" or "Needs you"; the badge marks a state that needs the person ("Needs you", a failed set-up), and the orb shows working by itself.',
818
- access: "The orb is decorative; the name and the state line are the row's text, read by its control. StatusBadge owns its decorative mark, so the line carries its meaning.",
818
+ access: "The orb is decorative; the name and the state line are the row's text, read by its control. StatusBadge owns its decorative shape, so the line carries its meaning.",
819
819
  },
820
820
  {
821
821
  name: "Breadcrumb",
@@ -1748,7 +1748,7 @@ export const USAGE = [
1748
1748
  summary: "The company's nadicode card: what the person is making while they sign up, and what the website shows being issued.",
1749
1749
  when: "Sign-up, where a person watches their company's card being made, and the website, which shows one being issued. Beside a wide form it fills in as the form is typed; on a phone the form keeps the screen, and the card appears once it is issued. The account's Profile shows the person the same card. The Partner invitation panel opens with its card, the `partner` edition, on the identity plane, as the invitation it shares, and the invitation dialog shows the card the invited company will be issued. A page an invitation leads to shows one card, the person's own: who invited them is named above the page's heading, never as a second card beside it.",
1750
1750
  instead: "A company's facts are `SettingsFactsSection`. A company in a row is its `IdentityMark`.",
1751
- behaviour: "One object of the ID-1 proportion (856 by 540), dark by nature in every theme. Its top line carries the wordmark and, once Sofia has found it on the company's site, the company's own logo after a hairline, both stamped in `{colors.identity-white}` as one ink; a company with no logo found has the wordmark alone. Below are the holder's name and the company's domain. No Agent stands on it, and nothing sits in the engraving: a guilloché engraving in `{colors.identity-white}` at 10%. It fills in as the form is typed and answers the pointer. It is the one object besides an Agent that moves on its own, once: when it first appears, and again when the account is asked for and it is issued, it turns in the light in 2.2 s, leaning one way and back while the light crosses it from corner to corner, so a person sees it is a card that answers the hand, and then it is still. The foil wakes as the pointer comes within one card width and eases toward it; a pointer that near ends the turn, and a pointer anywhere else on the page moves nothing. Where the browser has WebGPU, its foil is vgpu's holographic card shader: matte graphite (`{colors.card-graphite}`, its own tone, so the card keeps its look whatever the heights do) whose etched contours diffract the pointer's light, turning toward it by at most 9°. The diffraction and its pearlescent wash fall in pure bands of the identity hues, never a colour between two of them: a company's card takes the cool half, cobalto, azzurro and verde, and a Partner's the warm half, giallo, arancio and rosso. Without WebGPU the engraving catches the light as flat, hard-edged stripes of the same hues. Under reduced motion the card keeps still and the light still finds the foil.",
1751
+ behaviour: "One object of the ID-1 proportion (856 by 540), dark by nature in every theme. Its top line carries the wordmark and, once Sofia has found it on the company's site, the company's own logo after a hairline in its original colours; a company with no logo found has the wordmark alone. Below are the holder's name and the company's domain. No Agent stands on it, and nothing sits in the engraving: a guilloché engraving in `{colors.identity-white}` at 10%. It fills in as the form is typed and answers the pointer. It is the one object besides an Agent that moves on its own, once: when it first appears, and again when the account is asked for and it is issued, it turns in the light in 2.2 s, leaning one way and back while the light crosses it from corner to corner, so a person sees it is a card that answers the hand, and then it is still. The foil wakes as the pointer comes within one card width and eases toward it; a pointer that near ends the turn, and a pointer anywhere else on the page moves nothing. Where the browser has WebGPU, its foil is vgpu's holographic card shader: matte graphite (`{colors.card-graphite}`, its own tone, so the card keeps its look whatever the heights do) whose etched contours diffract the pointer's light, turning toward it by at most 9°. The diffraction and its pearlescent wash fall in pure bands of the identity hues, never a colour between two of them: a company's card takes the cool half, cobalto, azzurro and verde, and a Partner's the warm half, giallo, arancio and rosso. Without WebGPU the engraving catches the light as flat, hard-edged stripes of the same hues. Under reduced motion the card keeps still and the light still finds the foil.",
1752
1752
  words: "Every word it shows is a prop from the product's catalogue.",
1753
1753
  access: "Hidden from assistive technology and holds nothing interactive; the form owns every word the card repeats.",
1754
1754
  },
@@ -171,6 +171,7 @@
171
171
  :has(
172
172
  > :only-child:is(
173
173
  [data-slot="badge"],
174
+ [data-slot="status-badge"],
174
175
  [data-slot="trend-badge"],
175
176
  [data-slot="chart-sparkline"],
176
177
  [data-slot="button"],
@@ -804,7 +805,6 @@
804
805
  background: color-mix(in srgb, var(--nc-identity-white) 35%, transparent);
805
806
  }
806
807
 
807
- /* The logo is stored as one ink in white, so it sits on the graphite as the wordmark does. */
808
808
  .nc-company-card-logo {
809
809
  display: block;
810
810
  width: auto;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nadicodeai/ui",
3
- "version": "12.1.127",
3
+ "version": "12.1.129",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -83,7 +83,7 @@
83
83
  "@base-ui/react": "^1.9.0",
84
84
  "@iconify-icons/logos": "^2.0.3",
85
85
  "@iconify/react": "^6.0.2",
86
- "@nadicodeai/design-system": "12.1.127",
86
+ "@nadicodeai/design-system": "12.1.129",
87
87
  "@tanstack/react-table": "^9.3.1",
88
88
  "@vgpu/wgsl": "0.6.0",
89
89
  "class-variance-authority": "^0.7.1",