@cueplusplus/ui 0.20.1 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/dist/chat/agent-pile.js +2 -2
  3. package/dist/chrome/_status-bar.js +1 -1
  4. package/dist/chrome/app-shell.js +1 -1
  5. package/dist/chrome/navigation-menu.js +3 -3
  6. package/dist/chrome/row.js +1 -1
  7. package/dist/chrome/scroll-area.js +1 -1
  8. package/dist/chrome/section-header.js +1 -1
  9. package/dist/chrome/segmented-control.js +1 -1
  10. package/dist/chrome/tabs.js +1 -1
  11. package/dist/chrome/toolbar.js +1 -1
  12. package/dist/chrome/tree.js +3 -3
  13. package/dist/date/calendar.js +3 -3
  14. package/dist/dmx/channel-matrix.js +1 -1
  15. package/dist/dmx/universe-grid.js +1 -1
  16. package/dist/elements/agent-handoff.js +3 -3
  17. package/dist/elements/agent-plan.js +1 -1
  18. package/dist/elements/agent-status.js +1 -1
  19. package/dist/elements/agent-surface.d.ts +9 -6
  20. package/dist/elements/agent-surface.js +3 -2
  21. package/dist/elements/approval-card.d.ts +29 -3
  22. package/dist/elements/approval-card.js +18 -8
  23. package/dist/elements/artifact-card.js +2 -2
  24. package/dist/elements/canvas-split.js +3 -3
  25. package/dist/elements/chart.js +1 -1
  26. package/dist/elements/chat-panel.js +2 -2
  27. package/dist/elements/checkpoint-history.js +1 -1
  28. package/dist/elements/code-diff.js +1 -1
  29. package/dist/elements/code-runner.js +1 -1
  30. package/dist/elements/composer.d.ts +8 -2
  31. package/dist/elements/composer.js +20 -12
  32. package/dist/elements/computer-use.js +2 -2
  33. package/dist/elements/confidence-marker.js +1 -1
  34. package/dist/elements/connection-state.js +2 -2
  35. package/dist/elements/context-breakdown.js +1 -1
  36. package/dist/elements/context-usage.js +1 -1
  37. package/dist/elements/conversation-search.js +2 -2
  38. package/dist/elements/cost-meter.js +1 -1
  39. package/dist/elements/data-table.js +1 -1
  40. package/dist/elements/diagram.js +1 -1
  41. package/dist/elements/draft-restore.js +2 -2
  42. package/dist/elements/edit-message.js +1 -1
  43. package/dist/elements/elements.css +134 -0
  44. package/dist/elements/elicitation-form.d.ts +8 -1
  45. package/dist/elements/elicitation-form.js +24 -16
  46. package/dist/elements/empty-state.js +3 -3
  47. package/dist/elements/error-state.js +2 -2
  48. package/dist/elements/event-feed.d.ts +88 -0
  49. package/dist/elements/event-feed.js +190 -0
  50. package/dist/elements/feedback-dialog.js +2 -2
  51. package/dist/elements/file-tree.js +1 -1
  52. package/dist/elements/flow-graph.js +2 -2
  53. package/dist/elements/generative.css +13 -0
  54. package/dist/elements/generative.d.ts +8 -6
  55. package/dist/elements/generative.js +4 -3
  56. package/dist/elements/image-generation.js +2 -2
  57. package/dist/elements/index.d.ts +7 -3
  58. package/dist/elements/index.js +5 -2
  59. package/dist/elements/inline-citation.js +1 -1
  60. package/dist/elements/job-progress.js +1 -1
  61. package/dist/elements/launcher-bubble.js +3 -3
  62. package/dist/elements/loading-state.js +1 -1
  63. package/dist/elements/map-answer.js +1 -1
  64. package/dist/elements/math-block.js +1 -1
  65. package/dist/elements/mcp-server-panel.js +3 -3
  66. package/dist/elements/memory-chips.js +1 -1
  67. package/dist/elements/message-attachment.js +3 -3
  68. package/dist/elements/message-branches.js +1 -1
  69. package/dist/elements/message-pair.js +2 -2
  70. package/dist/elements/message-queue.js +1 -1
  71. package/dist/elements/message-timing.js +1 -1
  72. package/dist/elements/mobile-composer.js +1 -1
  73. package/dist/elements/model-picker.js +1 -1
  74. package/dist/elements/number-ticker.js +1 -1
  75. package/dist/elements/onboarding.js +3 -3
  76. package/dist/elements/permission-grant.js +3 -3
  77. package/dist/elements/prompt-library.js +1 -1
  78. package/dist/elements/question-card.d.ts +50 -0
  79. package/dist/elements/question-card.js +117 -0
  80. package/dist/elements/queue-dock.js +1 -1
  81. package/dist/elements/quota-banner.js +1 -1
  82. package/dist/elements/quote-reply.js +3 -3
  83. package/dist/elements/read-aloud.js +2 -2
  84. package/dist/elements/reasoning-effort.js +2 -2
  85. package/dist/elements/reasoning-panel.js +3 -3
  86. package/dist/elements/recommendation-card.js +2 -2
  87. package/dist/elements/regenerate-menu.js +1 -1
  88. package/dist/elements/request-delivery.js +31 -0
  89. package/dist/elements/research-report.js +1 -1
  90. package/dist/elements/retrieval-chunks.js +3 -3
  91. package/dist/elements/revert-dock.js +1 -1
  92. package/dist/elements/reviewable-diff.js +4 -4
  93. package/dist/elements/schedule-card.js +2 -2
  94. package/dist/elements/score-breakdown.js +2 -2
  95. package/dist/elements/scroll-anchor.js +2 -2
  96. package/dist/elements/settings-panel.js +3 -3
  97. package/dist/elements/sources.js +1 -1
  98. package/dist/elements/spec-sheet.js +1 -1
  99. package/dist/elements/stopped-run.js +2 -2
  100. package/dist/elements/streaming-text.js +2 -2
  101. package/dist/elements/subagent-list.js +4 -4
  102. package/dist/elements/suggestions.js +1 -1
  103. package/dist/elements/surfaces.d.ts +6 -6
  104. package/dist/elements/surfaces.js +7 -7
  105. package/dist/elements/tail-status.js +1 -1
  106. package/dist/elements/terminal-block.js +1 -1
  107. package/dist/elements/thinking-indicator.js +1 -1
  108. package/dist/elements/timeline.js +1 -1
  109. package/dist/elements/todo-list.js +1 -1
  110. package/dist/elements/tool-call.d.ts +18 -2
  111. package/dist/elements/tool-call.js +20 -11
  112. package/dist/elements/tool-error.js +2 -2
  113. package/dist/elements/tool-group.d.ts +11 -3
  114. package/dist/elements/tool-group.js +62 -38
  115. package/dist/elements/tool-state.d.ts +13 -0
  116. package/dist/elements/tool-state.js +44 -0
  117. package/dist/elements/tool-timeline.js +2 -2
  118. package/dist/elements/trace-waterfall.js +2 -2
  119. package/dist/elements/unsupported-request-card.d.ts +31 -0
  120. package/dist/elements/unsupported-request-card.js +80 -0
  121. package/dist/elements/vocabulary.d.ts +50 -1
  122. package/dist/elements/vocabulary.js +50 -1
  123. package/dist/elements/voice-conversation.js +5 -5
  124. package/dist/elements/web-preview.js +1 -1
  125. package/dist/elements/web-search.js +2 -2
  126. package/dist/elements/work-collapse.js +1 -1
  127. package/dist/forms/file-upload.js +1 -1
  128. package/dist/forms/rating.js +1 -1
  129. package/dist/forms/select.js +1 -1
  130. package/dist/forms/switch.js +2 -2
  131. package/dist/forms/toggle.js +1 -1
  132. package/dist/index.d.ts +3 -1
  133. package/dist/index.js +3 -1
  134. package/dist/instruments/_ledger-disclosure.js +1 -1
  135. package/dist/instruments/data-tree.js +1 -1
  136. package/dist/instruments/meter.js +1 -1
  137. package/dist/instruments/seam-grid.js +1 -1
  138. package/dist/layout/_collapse.js +3 -3
  139. package/dist/layout/bento.js +1 -1
  140. package/dist/layout/breadcrumb.js +1 -1
  141. package/dist/layout/carousel.js +1 -1
  142. package/dist/layout/collapsible.js +1 -1
  143. package/dist/layout/index.d.ts +3 -1
  144. package/dist/layout/index.js +3 -1
  145. package/dist/layout/item.js +1 -1
  146. package/dist/layout/link.js +1 -1
  147. package/dist/layout/pagination.js +1 -1
  148. package/dist/layout/pane-seam.d.ts +75 -0
  149. package/dist/layout/pane-seam.js +127 -0
  150. package/dist/layout/resizable.d.ts +2 -1
  151. package/dist/layout/resizable.js +3 -2
  152. package/dist/layout/sidebar.js +3 -3
  153. package/dist/layout/split-pane.d.ts +74 -0
  154. package/dist/layout/split-pane.js +135 -0
  155. package/dist/layout/stepper.js +1 -1
  156. package/dist/lib/auto-grow.js +42 -0
  157. package/dist/overlays/_surface.d.ts +4 -4
  158. package/dist/overlays/_surface.js +4 -4
  159. package/dist/overlays/drawer.js +1 -1
  160. package/dist/overlays/menubar.js +1 -1
  161. package/dist/overlays/sheet.js +1 -1
  162. package/dist/overlays/toast.js +1 -1
  163. package/dist/primitives/button.js +1 -1
  164. package/dist/primitives/chip.d.ts +1 -1
  165. package/dist/system/agent-skin.d.ts +11 -5
  166. package/manifest/components/agent-surface.json +5 -4
  167. package/manifest/components/approval-card.json +45 -7
  168. package/manifest/components/composer-input.json +16 -2
  169. package/manifest/components/elicitation-form.json +16 -1
  170. package/manifest/components/event-feed.json +171 -0
  171. package/manifest/components/event-row.json +116 -0
  172. package/manifest/components/generative-ui.json +4 -3
  173. package/manifest/components/pane-seam.json +171 -0
  174. package/manifest/components/question-card.json +157 -0
  175. package/manifest/components/resizable.json +1 -1
  176. package/manifest/components/split-pane.json +224 -0
  177. package/manifest/components/tool-call.json +46 -2
  178. package/manifest/components/tool-group.json +15 -3
  179. package/manifest/components/unsupported-request-card.json +117 -0
  180. package/manifest/fixtures.json +16 -0
  181. package/manifest/manifest.json +88 -10
  182. package/manifest/tokens.json +9 -2
  183. package/package.json +2 -2
@@ -30,7 +30,7 @@ import { QUIET_INK } from "../system/_style-quiet-ink.js";
30
30
  * size and leading and the hover ground, and lane R6.Z its weight (falling back
31
31
  * through the label weight to the `400` it drew), the same way.
32
32
  */
33
- const collapseTriggerClasses = `group/collapse flex ${COLLAPSE_HEIGHT} w-full cursor-pointer items-center ${COLLAPSE_GAP} ${COLLAPSE_PAD} ${COLLAPSE_RADIUS} text-left ${CONTROL_FACE} ${COLLAPSE_SIZE} ${COLLAPSE_LEADING} ${QUIET_INK} ${COLLAPSE_WEIGHT} transition-colors duration-150 outline-none select-none ${COLLAPSE_HOVER_INK} ${COLLAPSE_HOVER_GROUND} ${COLLAPSE_FOCUS_RING} ${COLLAPSE_OPEN_INK} data-[disabled]:pointer-events-none data-[disabled]:opacity-40`;
33
+ const collapseTriggerClasses = `group/collapse flex ${COLLAPSE_HEIGHT} w-full cursor-pointer items-center ${COLLAPSE_GAP} ${COLLAPSE_PAD} ${COLLAPSE_RADIUS} text-left ${CONTROL_FACE} ${COLLAPSE_SIZE} ${COLLAPSE_LEADING} ${QUIET_INK} ${COLLAPSE_WEIGHT} transition-colors duration-fast outline-none select-none ${COLLAPSE_HOVER_INK} ${COLLAPSE_HOVER_GROUND} ${COLLAPSE_FOCUS_RING} ${COLLAPSE_OPEN_INK} data-[disabled]:pointer-events-none data-[disabled]:opacity-40`;
34
34
  /** The label between the summary row's gutters — truncates, never wraps. */
35
35
  const collapseLabelClasses = "min-w-0 flex-1 truncate";
36
36
  /**
@@ -39,7 +39,7 @@ const collapseLabelClasses = "min-w-0 flex-1 truncate";
39
39
  * Its size and ink read the style set (round 6, lane R6.Z), falling back to
40
40
  * the `icon-sm` and `fg-subtle` it drew.
41
41
  */
42
- const collapseCaretClasses = `${COLLAPSE_CARET_SIZE} shrink-0 ${COLLAPSE_CARET_INK} ${COLLAPSE_CARET_OPACITY} transition-transform duration-150 ease-out group-data-[panel-open]/collapse:rotate-90 motion-reduce:transition-none`;
42
+ const collapseCaretClasses = `${COLLAPSE_CARET_SIZE} shrink-0 ${COLLAPSE_CARET_INK} ${COLLAPSE_CARET_OPACITY} transition-transform duration-fast ease-out group-data-[panel-open]/collapse:rotate-90 motion-reduce:transition-none`;
43
43
  /**
44
44
  * Enter/exit for a panel, animated from the height Base UI measures for us.
45
45
  *
@@ -48,7 +48,7 @@ const collapseCaretClasses = `${COLLAPSE_CARET_SIZE} shrink-0 ${COLLAPSE_CARET_I
48
48
  * make the panel jump at both ends. The body lives one element in — see
49
49
  * {@link collapsePanelBodyClasses}.
50
50
  */
51
- const PANEL_MOTION = "overflow-hidden transition-[height] duration-150 ease-out motion-reduce:transition-none data-[starting-style]:h-0 data-[ending-style]:h-0";
51
+ const PANEL_MOTION = "overflow-hidden transition-[height] duration-fast ease-out motion-reduce:transition-none data-[starting-style]:h-0 data-[ending-style]:h-0";
52
52
  /** {@link PANEL_MOTION} against the accordion's measured height. */
53
53
  const accordionPanelClasses = `${PANEL_MOTION} h-(--accordion-panel-height)`;
54
54
  /** {@link PANEL_MOTION} against the collapsible's measured height. */
@@ -58,7 +58,7 @@ const MIN_HEIGHT_CLASSES = {
58
58
  /** The tile surface, shared by both variants so a link tile is not a second look. */
59
59
  const TILE_CLASS = "flex flex-col gap-(--cue-space-3) overflow-hidden rounded-surface border border-border bg-surface-1 p-(--cue-space-5)";
60
60
  /** What a link tile adds: the hover tint and the focus ring the whole surface takes. */
61
- const LINK_TILE_CLASS = `no-underline transition-colors duration-150 outline-none hover:bg-surface-2 ${FOCUS_RING_OFFSET_2}`;
61
+ const LINK_TILE_CLASS = `no-underline transition-colors duration-fast outline-none hover:bg-surface-2 ${FOCUS_RING_OFFSET_2}`;
62
62
  /** The host elements a link tile may not contain, plus anything carrying `tabIndex`. */
63
63
  const INTERACTIVE_TAGS = /* @__PURE__ */ new Set([
64
64
  "a",
@@ -11,7 +11,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
11
11
  * face, hover ink and the trail's gap read the style set (round 6, spec §11,
12
12
  * lane R6.Z: `_style-crumb.ts`), each falling back to what it drew.
13
13
  */
14
- const CRUMB_LINK_CLASSES = `cursor-pointer rounded-control border-0 bg-transparent p-0 ${CRUMB_FACE} ${CRUMB_SIZE} ${CRUMB_INK} no-underline transition-colors duration-150 outline-none ${CRUMB_HOVER_INK} ${FOCUS_RING_OFFSET_2}`;
14
+ const CRUMB_LINK_CLASSES = `cursor-pointer rounded-control border-0 bg-transparent p-0 ${CRUMB_FACE} ${CRUMB_SIZE} ${CRUMB_INK} no-underline transition-colors duration-fast outline-none ${CRUMB_HOVER_INK} ${FOCUS_RING_OFFSET_2}`;
15
15
  /**
16
16
  * Where you are, and how to get back.
17
17
  *
@@ -203,7 +203,7 @@ const Carousel = {
203
203
  "aria-label": dotLabel(index),
204
204
  "aria-current": index === selectedIndex ? "true" : void 0,
205
205
  onClick: () => scrollTo(index),
206
- className: cn(`size-[0.25rem] cursor-pointer rounded-full border-0 p-0 transition-colors duration-150 outline-none ${FOCUS_RING_OFFSET_2} motion-reduce:transition-none`, index === selectedIndex ? "bg-accent" : "bg-border-strong")
206
+ className: cn(`size-[0.25rem] cursor-pointer rounded-full border-0 p-0 transition-colors duration-fast outline-none ${FOCUS_RING_OFFSET_2} motion-reduce:transition-none`, index === selectedIndex ? "bg-accent" : "bg-border-strong")
207
207
  }, index))
208
208
  });
209
209
  })
@@ -99,7 +99,7 @@ const Disclosure = React.forwardRef(function Disclosure({ className, label = "ad
99
99
  children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, {
100
100
  ref,
101
101
  "data-slot": "disclosure-trigger",
102
- className: cn(`group/disclosure inline-flex cursor-pointer items-center gap-(--cue-space-2) self-start border-0 bg-transparent p-0 py-(--cue-space-2) text-(length:--cue-text-label) ${DISCLOSURE_INK} ${DISCLOSURE_WEIGHT} transition-colors duration-150 outline-none select-none ${DISCLOSURE_HOVER_INK} ${FOCUS_RING_OFFSET_2}`, LABEL_FACE, LABEL_TRACKING, LABEL_CASE, className),
102
+ className: cn(`group/disclosure inline-flex cursor-pointer items-center gap-(--cue-space-2) self-start border-0 bg-transparent p-0 py-(--cue-space-2) text-(length:--cue-text-label) ${DISCLOSURE_INK} ${DISCLOSURE_WEIGHT} transition-colors duration-fast outline-none select-none ${DISCLOSURE_HOVER_INK} ${FOCUS_RING_OFFSET_2}`, LABEL_FACE, LABEL_TRACKING, LABEL_CASE, className),
103
103
  ...elementProps,
104
104
  children: [
105
105
  /* @__PURE__ */ jsx("span", {
@@ -5,7 +5,9 @@ import { Accordion, AccordionHeaderProps, AccordionItemProps, AccordionPanelProp
5
5
  import { SpaceStep, Stack, StackAlign, StackJustify, StackProps } from "./stack.js";
6
6
  import { Bento, BentoColumns, BentoMinHeight, BentoRootProps, BentoRowSpan, BentoRows, BentoSpan, BentoTileProps } from "./bento.js";
7
7
  import { Breadcrumb, BreadcrumbItem, BreadcrumbProps } from "./breadcrumb.js";
8
+ import { PaneSeam, PaneSeamEdge, PaneSeamOrientation, PaneSeamProps } from "./pane-seam.js";
8
9
  import { Reveal, RevealProps } from "./reveal.js";
10
+ import { SplitPane, SplitPaneLayout, SplitPaneProps } from "./split-pane.js";
9
11
  import { Card, CardContentProps, CardDescriptionProps, CardFooterProps, CardHeaderProps, CardRootProps, CardTitleProps } from "./card.js";
10
12
  import { Collapsible, CollapsiblePanelProps, CollapsibleRootProps, CollapsibleTriggerProps, Disclosure, DisclosureProps } from "./collapsible.js";
11
13
  import { DescriptionList, DescriptionListDetailProps, DescriptionListLayout, DescriptionListRootProps, DescriptionListTermProps } from "./description-list.js";
@@ -17,4 +19,4 @@ import { Pagination, PaginationEntry, PaginationProps, paginationRange } from ".
17
19
  import { Preview, PreviewAxis, PreviewPresentation, PreviewProps, PreviewResizeOptions } from "./preview.js";
18
20
  import { Stepper, StepperProps, StepperState, StepperStep } from "./stepper.js";
19
21
  import { Timeline, TimelineContentProps, TimelineItemProps, TimelineMarkerProps, TimelineRootProps } from "./timeline.js";
20
- export { Accordion, type AccordionHeaderProps, type AccordionItemProps, type AccordionPanelProps, type AccordionRootProps, type AccordionTriggerProps, Bento, type BentoColumns, type BentoMinHeight, type BentoRootProps, type BentoRowSpan, type BentoRows, type BentoSpan, type BentoTileProps, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, Card, type CardContentProps, type CardDescriptionProps, type CardFooterProps, type CardHeaderProps, type CardRootProps, type CardTitleProps, Collapsible, type CollapsiblePanelProps, type CollapsibleRootProps, type CollapsibleTriggerProps, Container, type ContainerProps, type ContainerWidth, DescriptionList, type DescriptionListDetailProps, type DescriptionListLayout, type DescriptionListRootProps, type DescriptionListTermProps, Disclosure, type DisclosureProps, Frames, type FramesHeight, type FramesItemProps, type FramesLayout, type FramesRootProps, Grid, type GridColumns, type GridProps, Item, type ItemProps, Link, type LinkProps, Pagination, type PaginationEntry, type PaginationProps, Preview, type PreviewAxis, type PreviewGround, type PreviewPresentation, type PreviewProps, type PreviewResizeOptions, Reveal, type RevealProps, Sidebar, type SidebarItemProps, type SidebarRailProps, type SidebarRootProps, type SidebarSectionProps, type SpaceStep, Stack, type StackAlign, type StackJustify, type StackProps, Stepper, type StepperProps, type StepperState, type StepperStep, Timeline, type TimelineContentProps, type TimelineItemProps, type TimelineMarkerProps, type TimelineRootProps, itemVariants, linkVariants, paginationRange, useSidebar };
22
+ export { Accordion, type AccordionHeaderProps, type AccordionItemProps, type AccordionPanelProps, type AccordionRootProps, type AccordionTriggerProps, Bento, type BentoColumns, type BentoMinHeight, type BentoRootProps, type BentoRowSpan, type BentoRows, type BentoSpan, type BentoTileProps, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, Card, type CardContentProps, type CardDescriptionProps, type CardFooterProps, type CardHeaderProps, type CardRootProps, type CardTitleProps, Collapsible, type CollapsiblePanelProps, type CollapsibleRootProps, type CollapsibleTriggerProps, Container, type ContainerProps, type ContainerWidth, DescriptionList, type DescriptionListDetailProps, type DescriptionListLayout, type DescriptionListRootProps, type DescriptionListTermProps, Disclosure, type DisclosureProps, Frames, type FramesHeight, type FramesItemProps, type FramesLayout, type FramesRootProps, Grid, type GridColumns, type GridProps, Item, type ItemProps, Link, type LinkProps, Pagination, type PaginationEntry, type PaginationProps, PaneSeam, type PaneSeamEdge, type PaneSeamOrientation, type PaneSeamProps, Preview, type PreviewAxis, type PreviewGround, type PreviewPresentation, type PreviewProps, type PreviewResizeOptions, Reveal, type RevealProps, Sidebar, type SidebarItemProps, type SidebarRailProps, type SidebarRootProps, type SidebarSectionProps, type SpaceStep, SplitPane, type SplitPaneLayout, type SplitPaneProps, Stack, type StackAlign, type StackJustify, type StackProps, Stepper, type StepperProps, type StepperState, type StepperStep, Timeline, type TimelineContentProps, type TimelineItemProps, type TimelineMarkerProps, type TimelineRootProps, itemVariants, linkVariants, paginationRange, useSidebar };
@@ -5,7 +5,9 @@ import { Accordion } from "./accordion.js";
5
5
  import { Stack } from "./stack.js";
6
6
  import { Bento } from "./bento.js";
7
7
  import { Breadcrumb } from "./breadcrumb.js";
8
+ import { PaneSeam } from "./pane-seam.js";
8
9
  import { Reveal } from "./reveal.js";
10
+ import { SplitPane } from "./split-pane.js";
9
11
  import { Card } from "./card.js";
10
12
  import { Collapsible, Disclosure } from "./collapsible.js";
11
13
  import { DescriptionList } from "./description-list.js";
@@ -16,4 +18,4 @@ import { Pagination, paginationRange } from "./pagination.js";
16
18
  import { Preview } from "./preview.js";
17
19
  import { Stepper } from "./stepper.js";
18
20
  import { Timeline } from "./timeline.js";
19
- export { Accordion, Bento, Breadcrumb, Card, Collapsible, Container, DescriptionList, Disclosure, Frames, Grid, Item, Link, Pagination, Preview, Reveal, Sidebar, Stack, Stepper, Timeline, itemVariants, linkVariants, paginationRange, useSidebar };
21
+ export { Accordion, Bento, Breadcrumb, Card, Collapsible, Container, DescriptionList, Disclosure, Frames, Grid, Item, Link, Pagination, PaneSeam, Preview, Reveal, Sidebar, SplitPane, Stack, Stepper, Timeline, itemVariants, linkVariants, paginationRange, useSidebar };
@@ -24,7 +24,7 @@ const itemVariants = cva("flex w-full items-center gap-(--cue-space-4) rounded-c
24
24
  soft: "bg-surface-2"
25
25
  },
26
26
  interactive: {
27
- true: `cursor-pointer transition-colors duration-150 outline-none hover:bg-surface-2 ${FOCUS_RING} disabled:pointer-events-none disabled:opacity-40`,
27
+ true: `cursor-pointer transition-colors duration-fast outline-none hover:bg-surface-2 ${FOCUS_RING} disabled:pointer-events-none disabled:opacity-40`,
28
28
  false: ""
29
29
  }
30
30
  },
@@ -38,7 +38,7 @@ import { useRender } from "@base-ui/react/use-render";
38
38
  * @example
39
39
  * <a className={linkVariants({ variant: "underline" })} href="/docs">Docs</a>
40
40
  */
41
- const linkVariants = cva(`cursor-pointer rounded-control ${CONTROL_FACE} ${LINK_WEIGHT} transition-colors duration-150 outline-none ${HOVER_INK} ${LINK_FOCUS_RING}`, {
41
+ const linkVariants = cva(`cursor-pointer rounded-control ${CONTROL_FACE} ${LINK_WEIGHT} transition-colors duration-fast outline-none ${HOVER_INK} ${LINK_FOCUS_RING}`, {
42
42
  variants: { variant: {
43
43
  plain: `no-underline ${LINK_INK}`,
44
44
  underline: `underline ${INLINE_LINK_INK} ${LINK_RULE} ${LINK_OFFSET} ${HOVER_RULE}`
@@ -62,7 +62,7 @@ function paginationRange(page, pageCount, siblingCount = 1) {
62
62
  return typeof before === "number" && typeof after === "number" && after - before === 2 ? before + 1 : entry;
63
63
  });
64
64
  }
65
- const PAGE_BUTTON_CLASSES = `inline-flex h-control-sm min-w-control-sm cursor-pointer items-center justify-center rounded-control border-0 bg-transparent px-(--cue-space-2) ${CONTROL_FACE} font-[number:var(--cue-style-control-weight,inherit)] text-(length:--cue-text-ui) tabular-nums ${GHOST_INK} transition-colors duration-150 outline-none select-none hover:bg-surface-2 hover:text-fg ${FOCUS_RING_INSET}`;
65
+ const PAGE_BUTTON_CLASSES = `inline-flex h-control-sm min-w-control-sm cursor-pointer items-center justify-center rounded-control border-0 bg-transparent px-(--cue-space-2) ${CONTROL_FACE} font-[number:var(--cue-style-control-weight,inherit)] text-(length:--cue-text-ui) tabular-nums ${GHOST_INK} transition-colors duration-fast outline-none select-none hover:bg-surface-2 hover:text-fg ${FOCUS_RING_INSET}`;
66
66
  /**
67
67
  * The pager: previous, a window of page numbers, next.
68
68
  *
@@ -0,0 +1,75 @@
1
+ import * as React from "react";
2
+ //#region src/layout/pane-seam.d.ts
3
+ /** Which way the seam runs: between side-by-side panes, or between stacked ones. */
4
+ type PaneSeamOrientation = "vertical" | "horizontal";
5
+ /** Which side of the seam the pane it resizes sits on. */
6
+ type PaneSeamEdge = "start" | "end";
7
+ interface PaneSeamProps extends Omit<React.ComponentPropsWithoutRef<"div">, "children" | "defaultValue" | "onChange"> {
8
+ /**
9
+ * The name a screen reader lists the seam by: `Resize the inspector`. Required,
10
+ * because a separator nobody can name is one a keyboard user cannot find.
11
+ */
12
+ label: string;
13
+ /** The pane's size in pixels, when the caller holds it. */
14
+ value?: number;
15
+ /** The size to start from when nothing controls it. Defaults to `min`. */
16
+ defaultValue?: number;
17
+ /** Called with every new size, already clamped to `min` and `max`. */
18
+ onValueChange?: (value: number) => void;
19
+ /** The smallest the pane may become, in pixels. */
20
+ min: number;
21
+ /** The largest the pane may become, in pixels. */
22
+ max: number;
23
+ /** How far one arrow press moves the seam, in pixels. Defaults to 16. */
24
+ step?: number;
25
+ /** How far Shift with an arrow moves it, in pixels. Defaults to 64. */
26
+ largeStep?: number;
27
+ /**
28
+ * `vertical`, the default, divides side-by-side panes and resizes a width;
29
+ * `horizontal` divides stacked panes and resizes a height.
30
+ */
31
+ orientation?: PaneSeamOrientation;
32
+ /**
33
+ * Where the pane sits: `start`, the default, before the seam (left, or top),
34
+ * or `end`, after it. Moving the seam away from the pane grows it. A vertical
35
+ * seam reads the document's direction, so `start` is the right-hand pane
36
+ * under `dir="rtl"`.
37
+ */
38
+ edge?: PaneSeamEdge;
39
+ }
40
+ /**
41
+ * One accessible seam that resizes one pane, with no dependency (cue-ui-55).
42
+ *
43
+ * `Resizable` divides a frame between panel groups on `react-resizable-panels`,
44
+ * an optional peer. Most consoles need one thing less: a side pane whose edge a
45
+ * reader can drag, and move from the keyboard. This is that edge, and nothing
46
+ * else. It owns a number, not a layout: apply `value` to the pane's width (or
47
+ * height) and the seam supplies the rest.
48
+ *
49
+ * The rest is the part a hand-written separator forgets. It is a focusable
50
+ * `role="separator"` that says its size and its bounds (`aria-valuenow`,
51
+ * `-valuemin`, `-valuemax`), so the width is spoken as well as draggable. The
52
+ * arrows move it by `step`, Shift by `largeStep`, and Home and End jump to the
53
+ * bounds. Every value is clamped, so the pane can never squeeze its neighbour
54
+ * out of use.
55
+ *
56
+ * @example
57
+ * const [width, setWidth] = React.useState(320);
58
+ * const inspector = { width };
59
+ *
60
+ * <div className="flex">
61
+ * <main className="min-w-0 flex-1">…</main>
62
+ * <PaneSeam
63
+ * label="Resize the inspector"
64
+ * edge="end"
65
+ * min={240}
66
+ * max={640}
67
+ * value={width}
68
+ * onValueChange={setWidth}
69
+ * />
70
+ * <aside style={inspector}>…</aside>
71
+ * </div>
72
+ */
73
+ declare const PaneSeam: React.ForwardRefExoticComponent<PaneSeamProps & React.RefAttributes<HTMLDivElement>>;
74
+ //#endregion
75
+ export { PaneSeam, PaneSeamEdge, PaneSeamOrientation, PaneSeamProps };
@@ -0,0 +1,127 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { FOCUS_RING_INSET_2 } from "../system/_focus-ring.js";
4
+ import * as React from "react";
5
+ import { jsx } from "react/jsx-runtime";
6
+ //#region src/layout/pane-seam.tsx
7
+ const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
8
+ /**
9
+ * One accessible seam that resizes one pane, with no dependency (cue-ui-55).
10
+ *
11
+ * `Resizable` divides a frame between panel groups on `react-resizable-panels`,
12
+ * an optional peer. Most consoles need one thing less: a side pane whose edge a
13
+ * reader can drag, and move from the keyboard. This is that edge, and nothing
14
+ * else. It owns a number, not a layout: apply `value` to the pane's width (or
15
+ * height) and the seam supplies the rest.
16
+ *
17
+ * The rest is the part a hand-written separator forgets. It is a focusable
18
+ * `role="separator"` that says its size and its bounds (`aria-valuenow`,
19
+ * `-valuemin`, `-valuemax`), so the width is spoken as well as draggable. The
20
+ * arrows move it by `step`, Shift by `largeStep`, and Home and End jump to the
21
+ * bounds. Every value is clamped, so the pane can never squeeze its neighbour
22
+ * out of use.
23
+ *
24
+ * @example
25
+ * const [width, setWidth] = React.useState(320);
26
+ * const inspector = { width };
27
+ *
28
+ * <div className="flex">
29
+ * <main className="min-w-0 flex-1">…</main>
30
+ * <PaneSeam
31
+ * label="Resize the inspector"
32
+ * edge="end"
33
+ * min={240}
34
+ * max={640}
35
+ * value={width}
36
+ * onValueChange={setWidth}
37
+ * />
38
+ * <aside style={inspector}>…</aside>
39
+ * </div>
40
+ */
41
+ const PaneSeam = React.forwardRef(function PaneSeam({ label, value, defaultValue, onValueChange, min, max, step = 16, largeStep = 64, orientation = "vertical", edge = "start", className, onKeyDown, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, ...elementProps }, ref) {
42
+ const [own, setOwn] = React.useState(() => clamp(defaultValue ?? min, min, max));
43
+ const current = clamp(value ?? own, min, max);
44
+ const drag = React.useRef(null);
45
+ const [dragging, setDragging] = React.useState(false);
46
+ const commit = (next) => {
47
+ const clamped = clamp(Math.round(next), min, max);
48
+ if (clamped === current) return;
49
+ if (value === void 0) setOwn(clamped);
50
+ onValueChange?.(clamped);
51
+ };
52
+ /**
53
+ * Which physical direction grows the pane: +1 is right (or down), -1 left (or
54
+ * up). Read at the moment of input, from the nearest `dir`, so a seam moved
55
+ * into a right-to-left subtree follows it.
56
+ */
57
+ const growth = (element) => {
58
+ const away = edge === "start" ? 1 : -1;
59
+ if (orientation === "horizontal") return away;
60
+ return element.closest("[dir]")?.getAttribute("dir") === "rtl" ? -away : away;
61
+ };
62
+ return /* @__PURE__ */ jsx("div", {
63
+ ref,
64
+ role: "separator",
65
+ tabIndex: 0,
66
+ "aria-label": label,
67
+ "aria-orientation": orientation,
68
+ "aria-valuenow": current,
69
+ "aria-valuemin": min,
70
+ "aria-valuemax": max,
71
+ "data-slot": "pane-seam",
72
+ "data-orientation": orientation,
73
+ "data-edge": edge,
74
+ "data-dragging": dragging || void 0,
75
+ className: cn(`relative shrink-0 touch-none bg-border transition-colors duration-fast outline-none select-none hover:bg-accent data-dragging:bg-accent focus-visible:bg-accent ${FOCUS_RING_INSET_2} motion-reduce:transition-none`, orientation === "vertical" ? "min-h-8 w-[0.25rem] cursor-col-resize self-stretch" : "h-[0.25rem] w-full cursor-row-resize", className),
76
+ onKeyDown: (event) => {
77
+ onKeyDown?.(event);
78
+ if (event.defaultPrevented) return;
79
+ const by = event.shiftKey ? largeStep : step;
80
+ const forward = orientation === "vertical" ? "ArrowRight" : "ArrowDown";
81
+ const back = orientation === "vertical" ? "ArrowLeft" : "ArrowUp";
82
+ let next;
83
+ if (event.key === forward) next = current + by * growth(event.currentTarget);
84
+ else if (event.key === back) next = current - by * growth(event.currentTarget);
85
+ else if (event.key === "Home") next = min;
86
+ else if (event.key === "End") next = max;
87
+ if (next === void 0) return;
88
+ event.preventDefault();
89
+ commit(next);
90
+ },
91
+ onPointerDown: (event) => {
92
+ onPointerDown?.(event);
93
+ if (event.defaultPrevented || event.button !== 0) return;
94
+ event.preventDefault();
95
+ event.currentTarget.setPointerCapture?.(event.pointerId);
96
+ drag.current = {
97
+ pointer: event.pointerId,
98
+ from: orientation === "vertical" ? event.clientX : event.clientY,
99
+ size: current,
100
+ grow: growth(event.currentTarget)
101
+ };
102
+ setDragging(true);
103
+ },
104
+ onPointerMove: (event) => {
105
+ onPointerMove?.(event);
106
+ const held = drag.current;
107
+ if (held === null || held.pointer !== event.pointerId) return;
108
+ const at = orientation === "vertical" ? event.clientX : event.clientY;
109
+ commit(held.size + (at - held.from) * held.grow);
110
+ },
111
+ onPointerUp: (event) => {
112
+ onPointerUp?.(event);
113
+ if (drag.current?.pointer !== event.pointerId) return;
114
+ drag.current = null;
115
+ setDragging(false);
116
+ event.currentTarget.releasePointerCapture?.(event.pointerId);
117
+ },
118
+ onPointerCancel: (event) => {
119
+ onPointerCancel?.(event);
120
+ drag.current = null;
121
+ setDragging(false);
122
+ },
123
+ ...elementProps
124
+ });
125
+ });
126
+ //#endregion
127
+ export { PaneSeam };
@@ -20,7 +20,8 @@ interface ResizableHandleProps extends React.ComponentProps<typeof Separator> {
20
20
  *
21
21
  * `react-resizable-panels` is an **optional peer**: import this from
22
22
  * `@cueplusplus/ui/layout/resizable`, never from the core barrel, so an app
23
- * with no split view never installs one.
23
+ * with no split view never installs one. For one seam beside one pane,
24
+ * `PaneSeam` in the core barrel needs no peer at all.
24
25
  *
25
26
  * @example
26
27
  * <Resizable.Group orientation="horizontal">
@@ -18,7 +18,8 @@ const ResizableOrientationContext = React.createContext("horizontal");
18
18
  *
19
19
  * `react-resizable-panels` is an **optional peer**: import this from
20
20
  * `@cueplusplus/ui/layout/resizable`, never from the core barrel, so an app
21
- * with no split view never installs one.
21
+ * with no split view never installs one. For one seam beside one pane,
22
+ * `PaneSeam` in the core barrel needs no peer at all.
22
23
  *
23
24
  * @example
24
25
  * <Resizable.Group orientation="horizontal">
@@ -55,7 +56,7 @@ const Resizable = {
55
56
  return /* @__PURE__ */ jsxs(Separator, {
56
57
  elementRef: elementRef ?? ref,
57
58
  "data-slot": "resizable-handle",
58
- className: cn(`relative flex items-center justify-center bg-border transition-colors duration-150 outline-none data-[separator=hover]:bg-accent data-[separator=active]:bg-accent data-[separator=focus]:bg-accent ${FOCUS_RING_INSET_2} data-[separator=disabled]:opacity-40 motion-reduce:transition-none`, orientation === "vertical" ? "h-[0.25rem] w-full" : "w-[0.25rem] self-stretch", className),
59
+ className: cn(`relative flex items-center justify-center bg-border transition-colors duration-fast outline-none data-[separator=hover]:bg-accent data-[separator=active]:bg-accent data-[separator=focus]:bg-accent ${FOCUS_RING_INSET_2} data-[separator=disabled]:opacity-40 motion-reduce:transition-none`, orientation === "vertical" ? "h-[0.25rem] w-full" : "w-[0.25rem] self-stretch", className),
59
60
  ...elementProps,
60
61
  children: [withGrip ? /* @__PURE__ */ jsx("span", {
61
62
  "data-slot": "resizable-grip",
@@ -100,7 +100,7 @@ const SidebarRoot = React.forwardRef(function SidebarRoot({ className, landmark
100
100
  ref,
101
101
  "data-slot": "sidebar",
102
102
  "data-collapsed": collapsed ? "" : void 0,
103
- className: cn("group/sidebar relative flex h-full min-h-0 shrink-0 flex-col border-r border-border bg-surface-1 transition-[width] duration-150 ease-out motion-reduce:transition-none", "[counter-reset:cue-rail]", className),
103
+ className: cn("group/sidebar relative flex h-full min-h-0 shrink-0 flex-col border-r border-border bg-surface-1 transition-[width] duration-fast ease-out motion-reduce:transition-none", "[counter-reset:cue-rail]", className),
104
104
  style: {
105
105
  width: collapsed ? collapsedWidth : width,
106
106
  ...style
@@ -161,7 +161,7 @@ const Sidebar = {
161
161
  "data-slot": "sidebar-item",
162
162
  "data-active": active ? "" : void 0,
163
163
  "aria-current": active ? "page" : void 0,
164
- className: cn(`relative flex h-control-md w-full cursor-pointer items-center gap-(--cue-space-3) rounded-control border-0 bg-transparent px-(--cue-space-3) text-left font-mono text-(length:--cue-text-ui) no-underline transition-colors duration-150 outline-none select-none hover:bg-surface-2 hover:text-fg ${FOCUS_RING_INSET} ${NAV_ITEM} ${RAIL_COUNTER} disabled:pointer-events-none disabled:opacity-40`, className),
164
+ className: cn(`relative flex h-control-md w-full cursor-pointer items-center gap-(--cue-space-3) rounded-control border-0 bg-transparent px-(--cue-space-3) text-left font-mono text-(length:--cue-text-ui) no-underline transition-colors duration-fast outline-none select-none hover:bg-surface-2 hover:text-fg ${FOCUS_RING_INSET} ${NAV_ITEM} ${RAIL_COUNTER} disabled:pointer-events-none disabled:opacity-40`, className),
165
165
  children: /* @__PURE__ */ jsxs(Fragment, { children: [
166
166
  Icon === void 0 ? null : /* @__PURE__ */ jsx(Icon, {
167
167
  "aria-hidden": "true",
@@ -193,7 +193,7 @@ const Sidebar = {
193
193
  onClick?.(event);
194
194
  if (!event.defaultPrevented) toggle();
195
195
  },
196
- className: cn(`absolute inset-y-0 right-0 w-[0.25rem] translate-x-1/2 cursor-ew-resize border-0 bg-transparent p-0 transition-colors duration-150 outline-none hover:bg-accent ${FOCUS_RING_INSET_2} motion-reduce:transition-none`, className),
196
+ className: cn(`absolute inset-y-0 right-0 w-[0.25rem] translate-x-1/2 cursor-ew-resize border-0 bg-transparent p-0 transition-colors duration-fast outline-none hover:bg-accent ${FOCUS_RING_INSET_2} motion-reduce:transition-none`, className),
197
197
  ...elementProps
198
198
  });
199
199
  })
@@ -0,0 +1,74 @@
1
+ import * as React from "react";
2
+ //#region src/layout/split-pane.d.ts
3
+ /** Whether the panes sit side by side or stack into a drill-in. */
4
+ type SplitPaneLayout = "split" | "stacked";
5
+ interface SplitPaneProps extends Omit<React.ComponentPropsWithoutRef<"div">, "children"> {
6
+ /** The master: a list of rows, a tree, a feed. It owns its own keyboard. */
7
+ list: React.ReactNode;
8
+ /** The detail for whatever the list has selected. */
9
+ detail: React.ReactNode;
10
+ /**
11
+ * What the list is, in a word or two: `events`. It names the seam (`Resize
12
+ * the events`), the Back button (`Back to events`) and the fold (`Hide
13
+ * events`), so each control says what it acts on.
14
+ */
15
+ listLabel: string;
16
+ /** The list's width in pixels, when the caller holds it. */
17
+ listSize?: number;
18
+ /** The list's starting width when nothing controls it. Defaults to 320. */
19
+ defaultListSize?: number;
20
+ /** Called with every new list width, clamped to `minListSize` and `maxListSize`. */
21
+ onListSizeChange?: (size: number) => void;
22
+ /** The narrowest the list may be dragged, in pixels. Defaults to 200. */
23
+ minListSize?: number;
24
+ /** The widest the list may be dragged, in pixels. Defaults to 480. */
25
+ maxListSize?: number;
26
+ /**
27
+ * The frame width, in pixels, below which the panes stack. Unset, they stack
28
+ * once the detail would be narrower than twice the list.
29
+ */
30
+ stackBelow?: number;
31
+ /**
32
+ * Which pane shows when stacked: the detail when `true`, the list when not.
33
+ * Side by side, both always show.
34
+ */
35
+ detailOpen?: boolean;
36
+ /** Called by the stacked detail's Back button and by Escape inside it. */
37
+ onBack?: () => void;
38
+ /** Whether the list can fold to a thin tab while side by side. */
39
+ collapsible?: boolean;
40
+ /** Whether the list is folded, when the caller holds it. */
41
+ collapsed?: boolean;
42
+ /** Whether the list starts folded when nothing controls it. */
43
+ defaultCollapsed?: boolean;
44
+ /** Called when the list folds (`true`) or opens (`false`). */
45
+ onCollapsedChange?: (collapsed: boolean) => void;
46
+ }
47
+ /**
48
+ * A list beside its detail, which stacks into a drill-in when narrow (#102).
49
+ *
50
+ * The master-detail opinion on top of {@link PaneSeam}. Side by side while
51
+ * the detail would be at least twice the list, measured on the frame rather
52
+ * than the window, so it works in any column. Below that it stacks the way a
53
+ * phone does: the list, or the detail with a Back button, and Escape inside the
54
+ * detail goes back too. `detailOpen` says which shows; selection and the list's
55
+ * own keyboard stay with the list.
56
+ *
57
+ * The frame measures itself with a `ResizeObserver` and draws side by side
58
+ * until it has, so a server render and a browser without one get the split.
59
+ *
60
+ * @example
61
+ * const [selected, setSelected] = React.useState<string | null>(null);
62
+ *
63
+ * <SplitPane
64
+ * listLabel="events"
65
+ * list={<EventFeed events={events} selectedId={selected} onSelect={setSelected} />}
66
+ * detail={selected ? <EventDetail id={selected} /> : <EmptyState title="Pick an event" />}
67
+ * detailOpen={selected !== null}
68
+ * onBack={() => setSelected(null)}
69
+ * collapsible
70
+ * />
71
+ */
72
+ declare const SplitPane: React.ForwardRefExoticComponent<SplitPaneProps & React.RefAttributes<HTMLDivElement>>;
73
+ //#endregion
74
+ export { SplitPane, SplitPaneLayout, SplitPaneProps };
@@ -0,0 +1,135 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { FOCUS_RING_INSET_2 } from "../system/_focus-ring.js";
4
+ import { ChevronLeftGlyph, ChevronRightGlyph } from "./_glyphs.js";
5
+ import { PaneSeam } from "./pane-seam.js";
6
+ import * as React from "react";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/layout/split-pane.tsx
9
+ /**
10
+ * A list beside its detail, which stacks into a drill-in when narrow (#102).
11
+ *
12
+ * The master-detail opinion on top of {@link PaneSeam}. Side by side while
13
+ * the detail would be at least twice the list, measured on the frame rather
14
+ * than the window, so it works in any column. Below that it stacks the way a
15
+ * phone does: the list, or the detail with a Back button, and Escape inside the
16
+ * detail goes back too. `detailOpen` says which shows; selection and the list's
17
+ * own keyboard stay with the list.
18
+ *
19
+ * The frame measures itself with a `ResizeObserver` and draws side by side
20
+ * until it has, so a server render and a browser without one get the split.
21
+ *
22
+ * @example
23
+ * const [selected, setSelected] = React.useState<string | null>(null);
24
+ *
25
+ * <SplitPane
26
+ * listLabel="events"
27
+ * list={<EventFeed events={events} selectedId={selected} onSelect={setSelected} />}
28
+ * detail={selected ? <EventDetail id={selected} /> : <EmptyState title="Pick an event" />}
29
+ * detailOpen={selected !== null}
30
+ * onBack={() => setSelected(null)}
31
+ * collapsible
32
+ * />
33
+ */
34
+ const SplitPane = React.forwardRef(function SplitPane({ list, detail, listLabel, listSize, defaultListSize = 320, onListSizeChange, minListSize = 200, maxListSize = 480, stackBelow, detailOpen = false, onBack, collapsible = false, collapsed, defaultCollapsed = false, onCollapsedChange, className, ...elementProps }, ref) {
35
+ const node = React.useRef(null);
36
+ const setNode = React.useCallback((element) => {
37
+ node.current = element;
38
+ if (typeof ref === "function") ref(element);
39
+ else if (ref) ref.current = element;
40
+ }, [ref]);
41
+ const [ownSize, setOwnSize] = React.useState(defaultListSize);
42
+ const size = listSize ?? ownSize;
43
+ const [ownCollapsed, setOwnCollapsed] = React.useState(defaultCollapsed);
44
+ const folded = collapsible && (collapsed ?? ownCollapsed);
45
+ const [width, setWidth] = React.useState(null);
46
+ React.useLayoutEffect(() => {
47
+ const element = node.current;
48
+ if (element === null || typeof ResizeObserver === "undefined") return;
49
+ const measure = () => setWidth(element.getBoundingClientRect().width);
50
+ const observer = new ResizeObserver(measure);
51
+ observer.observe(element);
52
+ measure();
53
+ return () => observer.disconnect();
54
+ }, []);
55
+ const threshold = stackBelow ?? size * 3;
56
+ const layout = width !== null && width < threshold ? "stacked" : "split";
57
+ const resize = (next) => {
58
+ if (listSize === void 0) setOwnSize(next);
59
+ onListSizeChange?.(next);
60
+ };
61
+ const fold = (next) => {
62
+ if (collapsed === void 0) setOwnCollapsed(next);
63
+ onCollapsedChange?.(next);
64
+ };
65
+ const quiet = `flex items-center justify-center rounded-control text-fg-muted transition-colors duration-fast outline-none hover:bg-surface-2 hover:text-fg ${FOCUS_RING_INSET_2} motion-reduce:transition-none`;
66
+ return /* @__PURE__ */ jsx("div", {
67
+ ref: setNode,
68
+ "data-slot": "split-pane",
69
+ "data-layout": layout,
70
+ "data-detail-open": detailOpen || void 0,
71
+ "data-collapsed": folded || void 0,
72
+ className: cn("flex min-h-0 min-w-0", className),
73
+ ...elementProps,
74
+ children: layout === "stacked" ? detailOpen ? /* @__PURE__ */ jsxs("div", {
75
+ "data-slot": "split-pane-detail",
76
+ className: "flex min-h-0 min-w-0 flex-1 flex-col",
77
+ onKeyDown: (event) => {
78
+ if (event.key !== "Escape" || event.defaultPrevented) return;
79
+ event.preventDefault();
80
+ onBack?.();
81
+ },
82
+ children: [/* @__PURE__ */ jsx("div", {
83
+ className: "flex shrink-0 items-center border-b border-border px-(--cue-space-2) py-(--cue-space-1)",
84
+ children: /* @__PURE__ */ jsxs("button", {
85
+ type: "button",
86
+ onClick: onBack,
87
+ className: cn(quiet, "gap-(--cue-space-1) px-(--cue-space-2) py-(--cue-space-1) text-(length:--cue-text-ui)"),
88
+ children: [/* @__PURE__ */ jsx(ChevronLeftGlyph, { className: "size-3" }), `Back to ${listLabel}`]
89
+ })
90
+ }), /* @__PURE__ */ jsx("div", {
91
+ className: "min-h-0 min-w-0 flex-1",
92
+ children: detail
93
+ })]
94
+ }) : /* @__PURE__ */ jsx("div", {
95
+ "data-slot": "split-pane-list",
96
+ className: "min-h-0 min-w-0 flex-1",
97
+ children: list
98
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [folded ? /* @__PURE__ */ jsxs("button", {
99
+ type: "button",
100
+ "aria-expanded": false,
101
+ onClick: () => fold(false),
102
+ className: cn(quiet, "w-(--cue-space-6) shrink-0 self-stretch rounded-none border-e border-border"),
103
+ children: [/* @__PURE__ */ jsx(ChevronRightGlyph, { className: "size-3" }), /* @__PURE__ */ jsx("span", {
104
+ className: "sr-only",
105
+ children: `Show ${listLabel}`
106
+ })]
107
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
108
+ "data-slot": "split-pane-list",
109
+ className: "relative min-h-0 shrink-0",
110
+ style: { width: size },
111
+ children: [list, collapsible ? /* @__PURE__ */ jsxs("button", {
112
+ type: "button",
113
+ "aria-expanded": true,
114
+ onClick: () => fold(true),
115
+ className: cn(quiet, "absolute end-(--cue-space-1) top-(--cue-space-1) size-(--cue-space-6)"),
116
+ children: [/* @__PURE__ */ jsx(ChevronLeftGlyph, { className: "size-3" }), /* @__PURE__ */ jsx("span", {
117
+ className: "sr-only",
118
+ children: `Hide ${listLabel}`
119
+ })]
120
+ }) : null]
121
+ }), /* @__PURE__ */ jsx(PaneSeam, {
122
+ label: `Resize the ${listLabel}`,
123
+ min: minListSize,
124
+ max: maxListSize,
125
+ value: Math.min(maxListSize, Math.max(minListSize, size)),
126
+ onValueChange: resize
127
+ })] }), /* @__PURE__ */ jsx("div", {
128
+ "data-slot": "split-pane-detail",
129
+ className: "min-h-0 min-w-0 flex-1",
130
+ children: detail
131
+ })] })
132
+ });
133
+ });
134
+ //#endregion
135
+ export { SplitPane };
@@ -19,7 +19,7 @@ const INDEX_TONES = {
19
19
  current: "text-accent",
20
20
  upcoming: "text-fg-subtle"
21
21
  };
22
- const STEP_BUTTON_CLASSES = `inline-flex cursor-pointer items-center gap-(--cue-space-2) rounded-control border-0 bg-transparent p-0 ${LABEL_FACE} text-[length:inherit] tracking-[inherit] text-inherit ${LABEL_CASE} transition-colors duration-150 outline-none hover:text-fg ${FOCUS_RING_OFFSET_2}`;
22
+ const STEP_BUTTON_CLASSES = `inline-flex cursor-pointer items-center gap-(--cue-space-2) rounded-control border-0 bg-transparent p-0 ${LABEL_FACE} text-[length:inherit] tracking-[inherit] text-inherit ${LABEL_CASE} transition-colors duration-fast outline-none hover:text-fg ${FOCUS_RING_OFFSET_2}`;
23
23
  /**
24
24
  * The step trail: `1 target · 2 preview · 3 apply`.
25
25
  *