@godxjp/ui 32.8.0 → 32.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/DESIGN.md +11 -3
  2. package/README.md +5 -5
  3. package/agent/DESIGN.md +11 -3
  4. package/agent/START-HERE.md +9 -7
  5. package/agent/components/AppShell.json +1 -0
  6. package/agent/components/BlockEditor.json +1 -0
  7. package/agent/components/PageContainer.json +1 -0
  8. package/agent/components/Progress.json +3 -3
  9. package/agent/components/SearchInput.json +1 -0
  10. package/agent/components/Topbar.json +2 -0
  11. package/agent/components/TopbarItem.json +1 -0
  12. package/agent/components/TopbarSearch.json +49 -0
  13. package/agent/components/Tree.json +1 -0
  14. package/agent/components-index.json +5 -0
  15. package/agent/components.json +60 -3
  16. package/agent/index.json +7 -7
  17. package/agent/llms.txt +7 -7
  18. package/agent/tokens.json +39 -3
  19. package/dist/components/data-display/progress.d.ts +12 -6
  20. package/dist/components/data-display/progress.js +1 -1
  21. package/dist/components/data-display/tree.js +39 -0
  22. package/dist/components/layout/breadcrumb.js +2 -2
  23. package/dist/components/layout/index.d.ts +2 -0
  24. package/dist/components/layout/index.js +2 -0
  25. package/dist/components/layout/page-container.js +2 -2
  26. package/dist/components/layout/topbar-search.d.ts +12 -0
  27. package/dist/components/layout/topbar-search.js +87 -0
  28. package/dist/contracts/measurement.json +1 -1
  29. package/dist/i18n/messages/en.json +1 -0
  30. package/dist/i18n/messages/ja.json +1 -0
  31. package/dist/i18n/messages/vi.json +1 -0
  32. package/dist/props/components/layout.prop.d.ts +22 -0
  33. package/dist/props/registry.d.ts +9 -0
  34. package/dist/props/registry.js +16 -0
  35. package/dist/styles/card-layout.css +1 -1
  36. package/dist/styles/data-display-layout.css +37 -2
  37. package/dist/styles/dialog-layout.css +2 -0
  38. package/dist/styles/layers.json +7 -1
  39. package/dist/styles/navigation-layout.css +3 -0
  40. package/dist/styles/shell-layout.css +123 -2
  41. package/dist/styles.css +1 -1
  42. package/dist/tokens/components/card.css +3 -1
  43. package/dist/tokens/components/data-display.css +2 -0
  44. package/dist/tokens/components/shell.css +7 -0
  45. package/package.json +2 -2
  46. package/scripts/_agent-setup.mjs +10 -5
  47. package/scripts/cli.mjs +16 -7
  48. package/scripts/visual-audit.mjs +1412 -92
package/agent/tokens.json CHANGED
@@ -2556,10 +2556,16 @@
2556
2556
  "value": "var(--font-weight-medium)"
2557
2557
  },
2558
2558
  {
2559
- "description": "Card component tokens: card chrome derives from semantic layout tokens.",
2559
+ "description": "gh#1305 (ADR C12): sentence case, no tracking by default; a theme opts into small caps.",
2560
2560
  "name": "--stat-card-label-letter-spacing",
2561
2561
  "tier": "component",
2562
- "value": "0.04em"
2562
+ "value": "normal"
2563
+ },
2564
+ {
2565
+ "description": "Card component tokens: card chrome derives from semantic layout tokens.",
2566
+ "name": "--stat-card-label-letter-case",
2567
+ "tier": "component",
2568
+ "value": "none"
2563
2569
  },
2564
2570
  {
2565
2571
  "description": "Card component tokens: card chrome derives from semantic layout tokens.",
@@ -5387,6 +5393,12 @@
5387
5393
  "tier": "component",
5388
5394
  "value": "calc(2 * var(--control-height-xs) + var(--space-2))"
5389
5395
  },
5396
+ {
5397
+ "description": "Below 40rem the gutter collapses to this (ADR C05: first line ≤24px from the inner edge) and the handle rises above the caret's block at its inline end instead of sitting beside it.",
5398
+ "name": "--block-editor-compact-gutter-inline-size",
5399
+ "tier": "component",
5400
+ "value": "var(--block-editor-frame-padding)"
5401
+ },
5390
5402
  {
5391
5403
  "description": "Data-display component tokens — small-by-design text knobs (rule #45/#46).",
5392
5404
  "name": "--block-editor-min-height",
@@ -5640,7 +5652,7 @@
5640
5652
  "value": "initial"
5641
5653
  },
5642
5654
  {
5643
- "description": "Progress track + fill — `initial` so the role defaults re-resolve under a scoped theme. Track reads --secondary, fill reads --success; a service re-tones once. Defaults = hsl(var(--secondary)) track · hsl(var(--success)) fill.",
5655
+ "description": "Progress track + fill — `initial` so the role defaults re-resolve under a scoped theme. Track reads --secondary, the default (`primary`) fill reads the brand mark; a service re-tones once. Defaults = hsl(var(--secondary)) track · hsl(var(--mark-primary, var(--primary))) fill (gh#1308 — it was the success mark; status tones are opt-in and do not read this knob).",
5644
5656
  "name": "--progress-track-background",
5645
5657
  "tier": "component",
5646
5658
  "value": "initial"
@@ -10241,6 +10253,18 @@
10241
10253
  "tier": "component",
10242
10254
  "value": "var( --font-size-2xs, calc(var(--font-size-base) / var(--font-size-ratio) / var(--font-size-ratio)) )"
10243
10255
  },
10256
+ {
10257
+ "description": "Section label case and tracking (gh#1305, ADR C12): default sentence case, no tracking — tiny letter-spaced capitals (11px, 0.08em) are the classic \"AI dashboard\" eyebrow. A theme that wants small caps back sets both.",
10258
+ "name": "--sidebar-section-label-letter-case",
10259
+ "tier": "component",
10260
+ "value": "none"
10261
+ },
10262
+ {
10263
+ "description": "Shell (sidebar / topbar / kbd) component tokens — small-by-design text knobs (rule #45/#46). A service re-tunes chrome text without moving the global scale.",
10264
+ "name": "--sidebar-section-label-letter-spacing",
10265
+ "tier": "component",
10266
+ "value": "normal"
10267
+ },
10244
10268
  {
10245
10269
  "description": "Shell (sidebar / topbar / kbd) component tokens — small-by-design text knobs (rule #45/#46). A service re-tunes chrome text without moving the global scale.",
10246
10270
  "name": "--sidebar-product-tenant-font-size",
@@ -10709,6 +10733,18 @@
10709
10733
  "tier": "component",
10710
10734
  "value": "none"
10711
10735
  },
10736
+ {
10737
+ "description": "TopbarSearch (ADR C02): the inline field's TEXT AREA never shrinks below this — measured after the icon and clear/shortcut padding, in the field's own font. Below its `collapseBelow` step the field folds to a search cell instead of shrinking. 8ch is a repo-chosen policy, not a standard.",
10738
+ "name": "--topbar-search-min-text-inline-size",
10739
+ "tier": "component",
10740
+ "value": "8ch"
10741
+ },
10742
+ {
10743
+ "description": "The inline field's preferred width from the step up; it shrinks toward the floor above.",
10744
+ "name": "--topbar-search-inline-size",
10745
+ "tier": "component",
10746
+ "value": "16rem"
10747
+ },
10712
10748
  {
10713
10749
  "description": "TOPBAR BAR ITEM — the shape of an interactive cell IN the bar, as opposed to a control dropped into it. The bar's own chrome (Fluent's command bar, SLDS's global header, Atlassian's navigation, and the right-hand action slot of enterprise pro-layouts generally) draws a trigger as a full-height cell whose hover is the bar's surface; a `Button` in the same slot draws a --control-height pill with its own hover fill and its own ring, floating inside a taller strip. NO HEIGHT KNOB, DELIBERATELY. The cell's height IS the bar's, whatever the bar's is — `--app-shell-bar-height` inside AppShell, `--topbar-height` for a standalone Topbar, the coarse-pointer override on a touch device. A knob here would be a second answer that goes stale the moment either of those moves.",
10714
10750
  "name": "--topbar-item-padding-inline",
@@ -1,12 +1,18 @@
1
1
  import * as React from "react";
2
- export type ProgressTone = "success" | "warning" | "destructive";
3
2
  /**
4
- * A breakdown SLICE takes two more tones than the meter (gh#1302): `info` and `neutral`, the same
5
- * pair `Legend` already keys — a partition with four states (done · done-awaiting · in progress ·
6
- * not started) needs four distinguishable fills, and the meter's three made two of them identical.
7
- * Each paints exactly what the matching Legend swatch paints, so a key and its bar stay one.
3
+ * The METER's tone. `primary` (the default, gh#1308) is the brand role for ordinary progress — a
4
+ * reading position, an upload; the status tones are opt-in for a bar that reports that status
5
+ * (ADR C09). `primary` is the name `Badge` already gives its brand tone.
8
6
  */
9
- export type ProgressSegmentTone = ProgressTone | "info" | "neutral";
7
+ export type ProgressTone = "primary" | "success" | "warning" | "destructive";
8
+ /**
9
+ * A breakdown SLICE takes two more status tones than the meter (gh#1302): `info` and `neutral`, the
10
+ * same pair `Legend` already keys — a partition with four states (done · done-awaiting · in
11
+ * progress · not started) needs four distinguishable fills, and the meter's three made two of them
12
+ * identical. Each paints exactly what the matching Legend swatch paints, so a key and its bar stay
13
+ * one. No `primary`: a partition is states, and Legend keys no brand swatch.
14
+ */
15
+ export type ProgressSegmentTone = Exclude<ProgressTone, "primary"> | "info" | "neutral";
10
16
  /**
11
17
  * One slice of a BREAKDOWN bar — how much of the total this state holds, in the SAME unit as every
12
18
  * other slice (counts, bytes, yen). Never a percentage: the bar owns the ratio arithmetic, so the
@@ -73,7 +73,7 @@ function Progress(props) {
73
73
  const meterValue = value ?? 0;
74
74
  const isOver = over && meterValue > 100;
75
75
  const boundedValue = Math.max(0, Math.min(100, meterValue));
76
- const effectiveTone = tone ?? (isOver ? "destructive" : "success");
76
+ const effectiveTone = tone ?? (isOver ? "destructive" : "primary");
77
77
  return /* @__PURE__ */ jsx(
78
78
  "div",
79
79
  {
@@ -5,6 +5,7 @@ import { mergeRefs } from "@react-aria/utils";
5
5
  import { ChevronDown, ChevronRight, File as FileIcon, Folder, FolderOpen } from "lucide-react";
6
6
  import { useTranslation } from "../../i18n/use-translation.js";
7
7
  import { cn } from "../../lib/utils.js";
8
+ import { truncatedPart } from "../../lib/truncation.js";
8
9
  import { CheckboxVisual } from "../data-entry/checkbox.js";
9
10
  import { Skeleton } from "../feedback/skeleton.js";
10
11
  import {
@@ -273,6 +274,44 @@ function TreeRoot({
273
274
  const measured = firstRow?.offsetHeight ?? 0;
274
275
  if (measured > 0 && measured !== rowHeight) setRowHeight(measured);
275
276
  }, [isWindowed, rowHeight, size, visible.length]);
277
+ const markTruncatedLabels = React.useCallback(() => {
278
+ const tree = treeRef.current;
279
+ if (!tree) return;
280
+ for (const label of tree.querySelectorAll(".ui-tree-label")) {
281
+ if (label.dataset.truncated !== void 0 && label.parentElement === document.activeElement)
282
+ continue;
283
+ const ownTitle = label.dataset.truncatedTitle !== void 0;
284
+ const part = truncatedPart(label);
285
+ if (part) {
286
+ label.dataset.truncated = "";
287
+ if (!label.title || ownTitle) {
288
+ label.title = (label.textContent ?? "").replace(/\s+/g, " ").trim();
289
+ label.dataset.truncatedTitle = "";
290
+ }
291
+ } else if (label.dataset.truncated !== void 0) {
292
+ delete label.dataset.truncated;
293
+ if (ownTitle) {
294
+ label.removeAttribute("title");
295
+ delete label.dataset.truncatedTitle;
296
+ }
297
+ }
298
+ }
299
+ }, []);
300
+ React.useEffect(() => {
301
+ markTruncatedLabels();
302
+ });
303
+ React.useEffect(() => {
304
+ const tree = treeRef.current;
305
+ if (!tree || typeof ResizeObserver === "undefined") return;
306
+ const observer = new ResizeObserver(markTruncatedLabels);
307
+ observer.observe(tree);
308
+ const refold = () => requestAnimationFrame(markTruncatedLabels);
309
+ tree.addEventListener("focusout", refold);
310
+ return () => {
311
+ observer.disconnect();
312
+ tree.removeEventListener("focusout", refold);
313
+ };
314
+ }, [markTruncatedLabels]);
276
315
  const nodeRefs = React.useRef(/* @__PURE__ */ new Map());
277
316
  const pendingFocus = React.useRef(null);
278
317
  React.useEffect(() => {
@@ -26,8 +26,8 @@ function Breadcrumb({
26
26
  className: "ui-breadcrumb-item",
27
27
  "data-ellipsis": item.ellipsis ? "" : void 0,
28
28
  children: [
29
- renderBreadcrumbSegment({ item, index, isLast, items, itemRender, LinkComponent }),
30
- !isLast ? /* @__PURE__ */ jsx("span", { className: "ui-breadcrumb-separator", "aria-hidden": "true", children: separatorNode }) : null
29
+ index > 0 ? /* @__PURE__ */ jsx("span", { className: "ui-breadcrumb-separator", "aria-hidden": "true", children: separatorNode }) : null,
30
+ renderBreadcrumbSegment({ item, index, isLast, items, itemRender, LinkComponent })
31
31
  ]
32
32
  },
33
33
  item.to ?? index
@@ -41,6 +41,8 @@ export { Topbar } from "./topbar.js";
41
41
  export type { TopbarProp, TopbarProps } from "./topbar.js";
42
42
  export { TopbarItem } from "./topbar-item.js";
43
43
  export type { TopbarItemProp, TopbarItemProps } from "./topbar-item.js";
44
+ export { TopbarSearch } from "./topbar-search.js";
45
+ export type { TopbarSearchProp, TopbarSearchProps } from "./topbar-search.js";
44
46
  export { ResponsiveGrid } from "./responsive-grid.js";
45
47
  export type { ResponsiveGridProps } from "./responsive-grid.js";
46
48
  export { MasterDetail } from "./master-detail.js";
@@ -18,6 +18,7 @@ import { Breadcrumb } from "./breadcrumb.js";
18
18
  import { createSidebarLink, Sidebar, SidebarHeader, SidebarItem, SidebarSection } from "./sidebar.js";
19
19
  import { Topbar } from "./topbar.js";
20
20
  import { TopbarItem } from "./topbar-item.js";
21
+ import { TopbarSearch } from "./topbar-search.js";
21
22
  import { ResponsiveGrid } from "./responsive-grid.js";
22
23
  import { MasterDetail } from "./master-detail.js";
23
24
  import { SplitPane } from "./split-pane.js";
@@ -59,6 +60,7 @@ export {
59
60
  SplitPane,
60
61
  Topbar,
61
62
  TopbarItem,
63
+ TopbarSearch,
62
64
  createSidebarLink,
63
65
  useAppShellNavigationMode
64
66
  };
@@ -123,6 +123,7 @@ function PageContainerRoot({
123
123
  className: "ui-breadcrumb-item",
124
124
  "data-ellipsis": item.ellipsis ? "" : void 0,
125
125
  children: [
126
+ i > 0 ? /* @__PURE__ */ jsx("span", { className: "ui-breadcrumb-separator", "aria-hidden": "true", children: /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true" }) }) : null,
126
127
  item.to && !isLast ? /* @__PURE__ */ jsx(BreadcrumbCrumbText, { item, children: /* @__PURE__ */ jsx(
127
128
  LinkComponent,
128
129
  {
@@ -138,8 +139,7 @@ function PageContainerRoot({
138
139
  "aria-current": isLast ? "page" : void 0,
139
140
  children: item.label
140
141
  }
141
- ) }),
142
- !isLast && /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true" })
142
+ ) })
143
143
  ]
144
144
  },
145
145
  i
@@ -0,0 +1,12 @@
1
+ import * as React from "react";
2
+ import type { TopbarSearchProp } from "../../props/components/layout.prop.js";
3
+ export type { TopbarSearchProp, TopbarSearchProp as TopbarSearchProps, } from "../../props/components/layout.prop.js";
4
+ /**
5
+ * TopbarSearch — the bar's search box, folded to a named search cell below `collapseBelow` (ADR C02).
6
+ *
7
+ * WAI-ARIA APG disclosure: the cell is a button with `aria-expanded` / `aria-controls`; the panel it
8
+ * shows holds the ONE `SearchInput` this component renders. Which of the two is on screen is decided
9
+ * by CSS at the step (`data-collapse-below`), not by a media-query hook, so SSR and the first client
10
+ * paint agree and the field keeps its value across a resize.
11
+ */
12
+ export declare function TopbarSearch({ collapseBelow, className, ...props }: TopbarSearchProp): React.JSX.Element;
@@ -0,0 +1,87 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Search, X } from "lucide-react";
5
+ import { useTranslation } from "../../i18n/use-translation.js";
6
+ import { cn } from "../../lib/utils.js";
7
+ import { SearchInput } from "../data-entry/search-input.js";
8
+ import { TopbarItem } from "./topbar-item.js";
9
+ function TopbarSearch({ collapseBelow = "sm", className, ...props }) {
10
+ const { t } = useTranslation();
11
+ const [open, setOpen] = React.useState(false);
12
+ const triggerRef = React.useRef(null);
13
+ const panelRef = React.useRef(null);
14
+ const pressedInside = React.useRef(false);
15
+ const panelId = React.useId();
16
+ const name = props.ariaLabel ?? t("common.search");
17
+ React.useEffect(() => {
18
+ if (open) panelRef.current?.querySelector("input")?.focus();
19
+ }, [open]);
20
+ const close = (returnFocus) => {
21
+ setOpen(false);
22
+ if (returnFocus) triggerRef.current?.focus();
23
+ };
24
+ return /* @__PURE__ */ jsxs(
25
+ "div",
26
+ {
27
+ "data-slot": "topbar-search",
28
+ "data-collapse-below": collapseBelow,
29
+ "data-open": open ? "true" : void 0,
30
+ className: cn("ui-topbar-search", className),
31
+ children: [
32
+ /* @__PURE__ */ jsx(
33
+ TopbarItem,
34
+ {
35
+ ref: triggerRef,
36
+ className: "ui-topbar-search-trigger",
37
+ "aria-label": name,
38
+ "aria-expanded": open,
39
+ "aria-controls": panelId,
40
+ icon: /* @__PURE__ */ jsx(Search, { "aria-hidden": "true" }),
41
+ onClick: () => setOpen(true)
42
+ }
43
+ ),
44
+ /* @__PURE__ */ jsxs(
45
+ "div",
46
+ {
47
+ ref: panelRef,
48
+ id: panelId,
49
+ className: "ui-topbar-search-panel",
50
+ onKeyDown: (event) => {
51
+ if (open && event.key === "Escape" && !event.nativeEvent.isComposing) {
52
+ event.preventDefault();
53
+ close(true);
54
+ }
55
+ },
56
+ onPointerDownCapture: () => {
57
+ pressedInside.current = true;
58
+ setTimeout(() => {
59
+ pressedInside.current = false;
60
+ });
61
+ },
62
+ onBlur: (event) => {
63
+ if (open && !pressedInside.current && !event.currentTarget.contains(event.relatedTarget)) {
64
+ close(false);
65
+ }
66
+ },
67
+ children: [
68
+ /* @__PURE__ */ jsx(SearchInput, { ...props }),
69
+ /* @__PURE__ */ jsx(
70
+ TopbarItem,
71
+ {
72
+ className: "ui-topbar-search-close",
73
+ "aria-label": t("layout.topbar.closeSearch"),
74
+ icon: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" }),
75
+ onClick: () => close(true)
76
+ }
77
+ )
78
+ ]
79
+ }
80
+ )
81
+ ]
82
+ }
83
+ );
84
+ }
85
+ export {
86
+ TopbarSearch
87
+ };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
3
- "version": "32.8.0",
3
+ "version": "32.10.1",
4
4
  "targetSize": {
5
5
  "standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
6
6
  "min": 24,
@@ -340,6 +340,7 @@
340
340
  "more": "More",
341
341
  "search": "Search",
342
342
  "searchPlaceholder": "Search...",
343
+ "closeSearch": "Close search",
343
344
  "notifications": "Notifications",
344
345
  "tweaks": "Open settings"
345
346
  },
@@ -335,6 +335,7 @@
335
335
  "more": "その他",
336
336
  "search": "検索",
337
337
  "searchPlaceholder": "検索...",
338
+ "closeSearch": "検索を閉じる",
338
339
  "notifications": "通知",
339
340
  "tweaks": "設定を開く"
340
341
  },
@@ -336,6 +336,7 @@
336
336
  "more": "Thêm",
337
337
  "search": "Tìm kiếm",
338
338
  "searchPlaceholder": "Tìm kiếm...",
339
+ "closeSearch": "Đóng tìm kiếm",
339
340
  "notifications": "Thông báo",
340
341
  "tweaks": "Mở tùy chỉnh"
341
342
  },
@@ -3,6 +3,7 @@ import type * as React from "react";
3
3
  import type { ComponentType, ReactNode, SVGProps } from "react";
4
4
  import type { BreadcrumbProp, TitleProp, SubtitleProp, StatusProp, ExtraProp, FooterProp, PageDensityProp, DensityProp, PageContainerVariantProp, CenteredShellWidthProp, CenteredShellAlignProp, CenteredShellPresetProp, ErrorSurfaceModeProp, ErrorSurfaceStatusProp, AuthShellPresetProp, MobileShellHeightProp, MobileShellWidthProp, OrientationProp, TextAlignProp, TextToneProp, BreakpointProp, GapProp, PadProp, PadRawProp, ClassNameProp, ChildrenProp, IdProp, DisabledProp, DescriptionProp, ActionProp, IconProp, HeadingLevelProp, ToneProp, WidthProp, SizeProp } from "../vocabulary/index.js";
5
5
  import type { EmptyStateToneProp } from "./data-display.prop.js";
6
+ import type { SearchInputProp } from "./data-entry.prop.js";
6
7
  /**
7
8
  * Arrangement of the page header's title band and its `extra` slot below the 640px step.
8
9
  * `responsive-inline` keeps `extra` beside the title band at the `--page-header-extra-measure`
@@ -1568,6 +1569,27 @@ export type TopbarProp = Omit<React.HTMLAttributes<HTMLDivElement>, "children">
1568
1569
  /** Escape hatch — render fully custom bar content instead of the three slots. */
1569
1570
  children?: ReactNode;
1570
1571
  };
1572
+ /**
1573
+ * @see TopbarSearch — the bar's one search box, which folds to a named search cell on a narrow bar.
1574
+ *
1575
+ * A `SearchInput` dropped into a `Topbar` slot shrinks with its cluster: at 390px in a consumer bar
1576
+ * its text area held under 3ch, so neither a query nor the placeholder could be read (ADR C02).
1577
+ * Below `collapseBelow` this renders a `TopbarItem` named by `ariaLabel`; activating it opens the
1578
+ * field across the whole bar (Material 3 search bar → search view), focused, with a close cell.
1579
+ * Escape or the close cell folds it back and returns focus to the trigger; focus leaving the open
1580
+ * field folds it too. From the step up it is the inline field, whose text area never shrinks below
1581
+ * `--topbar-search-min-text-inline-size` (8ch).
1582
+ *
1583
+ * Every `SearchInput` prop passes through unchanged. ONE field is rendered at every width, so the
1584
+ * typed query survives a resize and the open/closed switch.
1585
+ */
1586
+ export type TopbarSearchProp = SearchInputProp & {
1587
+ /**
1588
+ * The step below which the field folds to a search cell. Default `"sm"` (40rem) — the bar's own
1589
+ * compact step, shared with `TopbarItem labelHideBelow` and `AppShell logoCompactBelow`.
1590
+ */
1591
+ collapseBelow?: BreakpointProp;
1592
+ };
1571
1593
  /**
1572
1594
  * @see TopbarItem — ONE interactive cell of a {@link TopbarProp} slot: the account button, a
1573
1595
  * settings trigger, a notifications bell.
@@ -1456,6 +1456,15 @@ export declare const COMPONENT_PROP_REGISTRY: {
1456
1456
  readonly reason: "Flex `hideBelow` scoped to the cell's label — icon-only below a breakpoint; visually hidden so the accessible name survives (gh#726).";
1457
1457
  }];
1458
1458
  };
1459
+ readonly TopbarSearchProp: {
1460
+ readonly group: "layout";
1461
+ readonly file: "components/layout.prop.ts";
1462
+ readonly vocabulary: readonly ["LabelProp", "PlaceholderProp", "DisabledProp", "ClassNameProp", "IdProp", {
1463
+ readonly field: "collapseBelow";
1464
+ readonly local: true;
1465
+ readonly reason: "Flex `hideBelow`'s breakpoint vocabulary, naming the step below which the bar's search folds to a search cell (ADR C02).";
1466
+ }];
1467
+ };
1459
1468
  readonly NavListProp: {
1460
1469
  readonly group: "layout";
1461
1470
  readonly file: "components/layout.prop.ts";
@@ -1579,6 +1579,22 @@ const COMPONENT_PROP_REGISTRY = {
1579
1579
  }
1580
1580
  ]
1581
1581
  },
1582
+ TopbarSearchProp: {
1583
+ group: "layout",
1584
+ file: "components/layout.prop.ts",
1585
+ vocabulary: [
1586
+ "LabelProp",
1587
+ "PlaceholderProp",
1588
+ "DisabledProp",
1589
+ "ClassNameProp",
1590
+ "IdProp",
1591
+ {
1592
+ field: "collapseBelow",
1593
+ local: true,
1594
+ reason: "Flex `hideBelow`'s breakpoint vocabulary, naming the step below which the bar's search folds to a search cell (ADR C02)."
1595
+ }
1596
+ ]
1597
+ },
1582
1598
  NavListProp: {
1583
1599
  group: "layout",
1584
1600
  file: "components/layout.prop.ts",
@@ -501,7 +501,7 @@
501
501
  font-weight: var(--stat-card-label-font-weight);
502
502
  letter-spacing: var(--stat-card-label-letter-spacing);
503
503
  line-height: var(--line-height-normal);
504
- text-transform: uppercase;
504
+ text-transform: var(--stat-card-label-letter-case);
505
505
  }
506
506
 
507
507
  [data-slot="stat-card-label"] svg {
@@ -189,7 +189,11 @@
189
189
  .ui-progress-bar {
190
190
  height: 100%;
191
191
  border-radius: inherit;
192
- background: var(--progress-fill-background, hsl(var(--mark-success, var(--text-success))));
192
+ background: var(--progress-fill-background, hsl(var(--mark-primary, var(--primary))));
193
+ }
194
+
195
+ .ui-progress[data-tone="success"] .ui-progress-bar {
196
+ background: hsl(var(--mark-success, var(--text-success)));
193
197
  }
194
198
 
195
199
  .ui-progress[data-tone="warning"] .ui-progress-bar {
@@ -291,11 +295,15 @@
291
295
  }
292
296
 
293
297
  .ui-progress-ring-indicator {
294
- stroke: var(--progress-fill-background, hsl(var(--mark-success, var(--text-success))));
298
+ stroke: var(--progress-fill-background, hsl(var(--mark-primary, var(--primary))));
295
299
 
296
300
  stroke-linecap: butt;
297
301
  }
298
302
 
303
+ .ui-progress[data-tone="success"] .ui-progress-ring-indicator {
304
+ stroke: hsl(var(--mark-success, var(--text-success)));
305
+ }
306
+
299
307
  .ui-progress[data-tone="warning"] .ui-progress-ring-indicator {
300
308
  stroke: hsl(var(--mark-warning, var(--text-warning)));
301
309
  }
@@ -1574,6 +1582,25 @@
1574
1582
  gap: var(--space-1);
1575
1583
  }
1576
1584
 
1585
+ @media (width < 40rem) {
1586
+ .ui-block-editor {
1587
+ padding-inline-start: var(--block-editor-compact-gutter-inline-size);
1588
+ }
1589
+
1590
+ .ui-block-editor-handle {
1591
+ inset-inline: auto var(--block-editor-compact-gutter-inline-size);
1592
+ translate: 0 -100%;
1593
+ padding: var(--stroke-hairline);
1594
+ border: var(--stroke-hairline) solid hsl(var(--border));
1595
+ border-radius: var(--popover-radius, var(--radius));
1596
+ background-color: hsl(var(--popover) / var(--popover-alpha, 100%));
1597
+ }
1598
+
1599
+ .ui-block-editor-handle[data-first-block] {
1600
+ translate: none;
1601
+ }
1602
+ }
1603
+
1577
1604
  @media (hover: hover) {
1578
1605
  .ui-block-editor:not(:hover):not(:focus-within) .ui-block-editor-handle {
1579
1606
  opacity: 0;
@@ -2924,6 +2951,14 @@
2924
2951
  white-space: nowrap;
2925
2952
  }
2926
2953
 
2954
+ .ui-tree:not([data-virtual])
2955
+ .ui-tree-node:focus-visible
2956
+ > .ui-tree-label[data-truncated]:not(:has([data-ellipsis])) {
2957
+ overflow: visible;
2958
+ white-space: normal;
2959
+ overflow-wrap: anywhere;
2960
+ }
2961
+
2927
2962
  .ui-tree-group {
2928
2963
  display: flex;
2929
2964
  flex-direction: column;
@@ -458,6 +458,8 @@
458
458
 
459
459
  background-color: var(--sheet-surface-background, hsl(var(--background)));
460
460
  box-shadow: var(--sheet-shadow, var(--shadow-lg));
461
+
462
+ outline: none;
461
463
  }
462
464
 
463
465
  .ui-sheet-panel[data-side="top"] {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "32.8.0",
2
+ "version": "32.10.1",
3
3
  "generatedBy": "scripts/gen-style-layers.mjs — do not edit by hand; run `pnpm gen:style-layers`",
4
4
  "base": "base.css",
5
5
  "fonts": "fonts.css",
@@ -1072,6 +1072,12 @@
1072
1072
  "shell-layout.css"
1073
1073
  ]
1074
1074
  },
1075
+ "TopbarSearch": {
1076
+ "layers": [
1077
+ "shell-layout.css",
1078
+ "control.css"
1079
+ ]
1080
+ },
1075
1081
  "Transfer": {
1076
1082
  "layers": [
1077
1083
  "layout.css",
@@ -802,6 +802,9 @@
802
802
  display: inline-flex;
803
803
  flex: 0 0 auto;
804
804
  align-items: center;
805
+
806
+ align-self: flex-start;
807
+ block-size: 1lh;
805
808
  }
806
809
 
807
810
  .ui-breadcrumb-menu-trigger {