@botiverse/raft-ui 0.81.0 → 0.82.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 (28) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/components/choice-list/choice-list.d.mts +8 -0
  3. package/dist/components/choice-list/choice-list.mjs +37 -16
  4. package/dist/components/choice-list/choice-list.recipe.mjs +1 -0
  5. package/dist/components/composer/composer-drop-overlay.d.mts +9 -4
  6. package/dist/components/composer/composer-drop-overlay.mjs +2 -5
  7. package/dist/components/list-row/index.d.mts +2 -2
  8. package/dist/components/list-row/list-row.d.mts +9 -0
  9. package/dist/components/list-row/list-row.mjs +20 -1
  10. package/dist/components/list-row/list-row.recipe.d.mts +3 -0
  11. package/dist/components/list-row/list-row.recipe.mjs +1 -0
  12. package/dist/components/message-action-card/index.d.mts +2 -2
  13. package/dist/components/message-action-card/message-action-card.d.mts +8 -0
  14. package/dist/components/message-action-card/message-action-card.mjs +16 -1
  15. package/dist/components/message-action-card/message-action-card.recipe.mjs +5 -0
  16. package/dist/components/resizable/resizable.recipe.mjs +4 -4
  17. package/dist/components/scroll-area/use-scrollbars.mjs +2 -1
  18. package/dist/components/steps/otter-animation-visibility.mjs +45 -0
  19. package/dist/components/steps/otter-indicator.css +131 -106
  20. package/dist/components/steps/otter-indicator.d.mts +5 -8
  21. package/dist/components/steps/otter-indicator.mjs +38 -314
  22. package/dist/components/steps/ottie-assets/error.webp +0 -0
  23. package/dist/components/steps/ottie-assets/finish.webp +0 -0
  24. package/dist/components/steps/ottie-assets/process.webp +0 -0
  25. package/dist/components/steps/ottie-assets/wait.webp +0 -0
  26. package/dist/index.d.mts +3 -3
  27. package/dist/index.mjs +3 -3
  28. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,41 @@
1
+ ## [0.82.0](https://github.com/botiverse/raft-design/compare/v0.81.0...v0.82.0) (2026-11-09)
2
+
3
+ ### Changed
4
+
5
+ - **OtterIndicator uses Ottie's approved four poses**: relaxed side-lying, quiet typing, a puzzled head tilt with a question mark, and one celebratory hop. The SVG props and ref remain supported. It pauses outside the viewport and in hidden documents; reduced motion shows a still. The HTML art layer sizes from the SVG box so Safari/WebKit scales it correctly at 40px and larger.
6
+ - Why: the previous unrelated otter drawing and sleeping wait pose did not match Ottie or the recent-activity card (Artea's approved B study).
7
+ - Affects: consumers of OtterIndicator, including Raft's recent-activity card. Artwork loads as separate transparent WebP files only for a displayed status; there is no frame timer or per-frame React update.
8
+ - Docs: https://raft-design.botiverse.build/components/steps/#ottie-illustration
9
+
10
+ ### Breaking
11
+
12
+ - **`ComposerDropOverlayPrompt` takes its text as `children`, which is required**: the icon cluster stays, and the text under it is what you pass. It no longer falls back to the built-in "Drop files to attach", and children no longer replace the icons.
13
+ - Why: the component library carried product copy in English that no app could translate (Remy's helper-text sweep, #proj-workspace:#250).
14
+ - Affects: `<ComposerDropOverlayPrompt />` no longer type-checks; pass the text, e.g. `<ComposerDropOverlayPrompt>{t("dropToAttach")}</ComposerDropOverlayPrompt>`. A caller that passed children for a label without icons uses `ComposerDropOverlayLabel` instead.
15
+ - Docs: https://raft-design.botiverse.build/components/composer/
16
+
17
+ ### Added
18
+
19
+ - **Data parts beside the explanation parts**: three additive parts, so data no longer has to sit in a slot meant for a sentence that explains. Nothing existing changes: no prop, part or style is renamed or restyled.
20
+ - `ListRowSubtitle`: data about a row's subject under its title (an agent's description, a channel's topic, an app's byline), in the title's ink at 400 and `text-footnote`. It follows the row's tone and disabled state with the title. `ListRowDescription` stays the place for a setting's consequence.
21
+ - ChoiceList items take `meta`: lines of data people compare across rows (a quota, free disk, a count, a status), in `text-footnote` in `foreground-muted` as the description is, after the `description` lines. A row still draws at most two lines in all, the description's first; a meta line has a description line's height, so moving a line from one to the other doesn't move the rows. Radios and checkboxes are described by the description lines, then the meta lines.
22
+ - `MessageActionCardHeaderMeta`: the time on a resolved card, in Space Mono at the hint tier: a line of its own under the title, or the end of the `MessageActionCardHeaderDescription` line it is placed in ("Approved by Martin · 10:51").
23
+ - Why: grey text is to mean one of two things, a sentence written to explain or a component's own notes on its data (times, counts, IDs), and data people read as the content uses the body ink. Apps had put data in the explanation parts (`ListRowDescription`, a ChoiceList item's `description`, `MessageActionCardHeaderDescription`) because there was nowhere else for it; these parts give it a place before those parts are kept for explanations (Kirby, Remy's helper-text plan).
24
+ - Affects: nothing until an app uses them. Data moved into `ListRowSubtitle` gets darker and a size smaller than in `ListRowDescription`; data moved into a ChoiceList item's `meta` looks as it did in `description`; a time moved into `MessageActionCardHeaderMeta` is set in Space Mono at the hint tier.
25
+ - Docs: https://raft-design.botiverse.build/components/list-row/#data-and-explanation, https://raft-design.botiverse.build/components/choice-list/#content, https://raft-design.botiverse.build/components/message-action-card/#anatomy
26
+
27
+ ### Fixed
28
+
29
+ - **A Resizable seam's handle no longer covers the scrollbar beside it**: the 12px handle (24px under a coarse pointer) lies over the start of the panel after the boundary instead of straddling it; its hover and focus line stays on the seam.
30
+ - Why: a panel's scrollbar runs along its end edge, the seam's other side, and the centered handle covered its last 6px, where the thumb is, so dragging the thumb resized the panel instead (Raft app's Activity list, #proj-raft-ui:21bae128).
31
+ - Affects: the handle now takes the pointer over the first 12px of the next panel (24px by touch) instead of 6px on each side. Nothing else moves.
32
+ - Docs: https://raft-design.botiverse.build/components/resizable/#behavior
33
+
34
+ - **ScrollArea measures in the frame after mounting, not while React commits**: where a `ResizeObserver` watches the sizes, the first read of the container's `clientHeight` and `scrollHeight` waits for the next animation frame, as every later one already did. Without a `ResizeObserver` it still measures at once.
35
+ - Why: the read in the commit forced a layout of the whole page each time an area mounted. Entering a server in the Raft app mounts the sidebar, the message list and the panels at once, and those reads took about 120ms of layout (InNOut's measurement, #proj-frontend:#40).
36
+ - Affects: the frame runs before the paint that first shows the area, so nothing on screen changes. Until it runs, an overflowing area has no thumb length and no overflow classes, and `onEdgeChange` is first called in it: code that reads them in a layout effect right after mounting finds them unset, and can call `actionsRef.measure` for a measure at once.
37
+ - Docs: https://raft-design.botiverse.build/components/scroll-area/
38
+
1
39
  ## [0.81.0](https://github.com/botiverse/raft-design/compare/v0.80.0...v0.81.0) (2026-11-09)
2
40
 
3
41
  ### Fixed
@@ -11,6 +11,14 @@ export type ChoiceListItem = {
11
11
  * (null, false or "") is not drawn. Colour inside a line is the caller's.
12
12
  */
13
13
  description?: ReactNode | readonly ReactNode[];
14
+ /**
15
+ * Lines of data under the description: what people compare across rows,
16
+ * such as a quota, free disk or a count ("120 GB free · 3 agents"), drawn
17
+ * as the description is (muted), after it. The row still draws at most two
18
+ * lines under the name, the description's first. An empty line is not
19
+ * drawn. Colour inside a line is the caller's.
20
+ */
21
+ meta?: ReactNode | readonly ReactNode[];
14
22
  /** Drawn before the radio's or checkbox's row text: an avatar, an icon. */
15
23
  media?: ReactNode;
16
24
  /**
@@ -18,6 +18,13 @@ function isDrawn(line) {
18
18
  function descriptionLines(description) {
19
19
  return (Array.isArray(description) ? description : [description]).filter(isDrawn);
20
20
  }
21
+ function rowLines(item) {
22
+ const description = descriptionLines(item.description).slice(0, MAX_DESCRIPTION_LINES);
23
+ return {
24
+ description,
25
+ meta: descriptionLines(item.meta).slice(0, MAX_DESCRIPTION_LINES - description.length)
26
+ };
27
+ }
21
28
  function CutLine({ id, slot, className, children }) {
22
29
  const ref = useOverflowTitle();
23
30
  return /* @__PURE__ */ jsx("span", {
@@ -133,6 +140,7 @@ function ChoiceList(props) {
133
140
  if (process.env.NODE_ENV === "production") return;
134
141
  if (!ariaLabel && !ariaLabelledBy) console.warn("raft-ui: a ChoiceList has neither aria-label nor aria-labelledby.");
135
142
  if (items.some((item) => descriptionLines(item.description).length > MAX_DESCRIPTION_LINES)) console.warn(`raft-ui: a ChoiceList item has more than ${MAX_DESCRIPTION_LINES} description lines; only the first ${MAX_DESCRIPTION_LINES} are drawn.`);
143
+ else if (items.some((item) => descriptionLines(item.description).length + descriptionLines(item.meta).length > MAX_DESCRIPTION_LINES)) console.warn(`raft-ui: a ChoiceList item has more than ${MAX_DESCRIPTION_LINES} description and meta lines together; only the first ${MAX_DESCRIPTION_LINES} are drawn, the description's first.`);
136
144
  }, [
137
145
  ariaLabel,
138
146
  ariaLabelledBy,
@@ -166,13 +174,14 @@ function ChoiceList(props) {
166
174
  };
167
175
  const rowElement = (item) => {
168
176
  const id = `${baseId}-${items.indexOf(item)}`;
169
- const lines = descriptionLines(item.description).slice(0, MAX_DESCRIPTION_LINES);
177
+ const { description: lines, meta } = rowLines(item);
170
178
  const rowDisabled = disabled || item.disabled === true;
179
+ const describedBy = [lines.length > 0 ? `${id}-description` : null, meta.length > 0 ? `${id}-meta` : null].filter(Boolean);
171
180
  const markProps = {
172
181
  value: item.value,
173
182
  disabled: item.disabled,
174
183
  "aria-labelledby": `${id}-label`,
175
- "aria-describedby": lines.length > 0 ? `${id}-description` : void 0
184
+ "aria-describedby": describedBy.length > 0 ? describedBy.join(" ") : void 0
176
185
  };
177
186
  return /* @__PURE__ */ jsxs("label", {
178
187
  "data-slot": "choice-list-item",
@@ -188,20 +197,32 @@ function ChoiceList(props) {
188
197
  /* @__PURE__ */ jsxs("span", {
189
198
  "data-slot": "choice-list-body",
190
199
  className: slots.body(),
191
- children: [/* @__PURE__ */ jsx(CutLine, {
192
- id: `${id}-label`,
193
- slot: "choice-list-label",
194
- className: slots.label(),
195
- children: item.label
196
- }), lines.length > 0 ? /* @__PURE__ */ jsx("span", {
197
- id: `${id}-description`,
198
- className: "contents",
199
- children: lines.map((line, index) => /* @__PURE__ */ jsx(CutLine, {
200
- slot: "choice-list-description",
201
- className: slots.description(),
202
- children: line
203
- }, index))
204
- }) : null]
200
+ children: [
201
+ /* @__PURE__ */ jsx(CutLine, {
202
+ id: `${id}-label`,
203
+ slot: "choice-list-label",
204
+ className: slots.label(),
205
+ children: item.label
206
+ }),
207
+ lines.length > 0 ? /* @__PURE__ */ jsx("span", {
208
+ id: `${id}-description`,
209
+ className: "contents",
210
+ children: lines.map((line, index) => /* @__PURE__ */ jsx(CutLine, {
211
+ slot: "choice-list-description",
212
+ className: slots.description(),
213
+ children: line
214
+ }, index))
215
+ }) : null,
216
+ meta.length > 0 ? /* @__PURE__ */ jsx("span", {
217
+ id: `${id}-meta`,
218
+ className: "contents",
219
+ children: meta.map((line, index) => /* @__PURE__ */ jsx(CutLine, {
220
+ slot: "choice-list-meta",
221
+ className: slots.meta(),
222
+ children: line
223
+ }, index))
224
+ }) : null
225
+ ]
205
226
  })
206
227
  ]
207
228
  }, item.value);
@@ -22,6 +22,7 @@ const choiceList = tv({
22
22
  body: ["grid min-w-0 flex-1 gap-0.5"],
23
23
  label: ["truncate font-semibold text-foreground", "in-data-disabled:text-foreground-disabled"],
24
24
  description: ["block truncate font-sans text-footnote font-normal text-foreground-muted", "in-data-disabled:text-foreground-disabled"],
25
+ meta: ["block truncate font-sans text-footnote font-normal text-foreground-muted", "in-data-disabled:text-foreground-disabled"],
25
26
  action: [
26
27
  "relative z-10 col-start-2 flex h-lh items-center self-start pr-1",
27
28
  "mt-option-inset pointer-coarse:mt-[calc((--spacing(11)-1lh)/2)]",
@@ -1,9 +1,14 @@
1
+ import * as React from "react";
1
2
  import { useRender } from "@base-ui/react/use-render";
2
3
  //#region src/components/composer/composer-drop-overlay.d.ts
3
4
  export type ComposerDropOverlayProps = useRender.ComponentProps<"div">;
4
- export declare function ComposerDropOverlay({ className, render, ...props }: ComposerDropOverlayProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
5
+ export declare function ComposerDropOverlay({ className, render, ...props }: ComposerDropOverlayProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
5
6
  export type ComposerDropOverlayLabelProps = useRender.ComponentProps<"span">;
6
- export declare function ComposerDropOverlayLabel({ className, render, ...props }: ComposerDropOverlayLabelProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
7
- export type ComposerDropOverlayPromptProps = ComposerDropOverlayLabelProps;
8
- export declare function ComposerDropOverlayPrompt({ children, ...props }: ComposerDropOverlayPromptProps): import("react").JSX.Element;
7
+ export declare function ComposerDropOverlayLabel({ className, render, ...props }: ComposerDropOverlayLabelProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
8
+ export type ComposerDropOverlayPromptProps = Omit<ComposerDropOverlayLabelProps, "children"> & {
9
+ /** What the prompt says, such as "Drop files to attach": the caller's copy, in its locale. */
10
+ children: React.ReactNode;
11
+ };
12
+ /** The label with the files-and-upload icon cluster above the caller's text. */
13
+ export declare function ComposerDropOverlayPrompt({ children, ...props }: ComposerDropOverlayPromptProps): React.JSX.Element;
9
14
  //#endregion
@@ -28,12 +28,9 @@ function ComposerDropOverlayLabel({ className, render, ...props }) {
28
28
  props: mergeProps(defaultProps, props)
29
29
  });
30
30
  }
31
+ /** The label with the files-and-upload icon cluster above the caller's text. */
31
32
  function ComposerDropOverlayPrompt({ children, ...props }) {
32
33
  const { dropOverlayIcon, dropOverlayIconCluster } = composerDropOverlay();
33
- if (children) return /* @__PURE__ */ jsx(ComposerDropOverlayLabel, {
34
- ...props,
35
- children
36
- });
37
34
  return /* @__PURE__ */ jsxs(ComposerDropOverlayLabel, {
38
35
  ...props,
39
36
  children: [/* @__PURE__ */ jsxs("span", {
@@ -53,7 +50,7 @@ function ComposerDropOverlayPrompt({ children, ...props }) {
53
50
  children: /* @__PURE__ */ jsx(Image, { className: "text-foreground-muted" })
54
51
  })
55
52
  ]
56
- }), /* @__PURE__ */ jsx("span", { children: "Drop files to attach" })]
53
+ }), /* @__PURE__ */ jsx("span", { children })]
57
54
  });
58
55
  }
59
56
  //#endregion
@@ -1,2 +1,2 @@
1
- import { ListRow, ListRowBody, ListRowBodyProps, ListRowBottom, ListRowBottomProps, ListRowChevron, ListRowChevronProps, ListRowDescription, ListRowDescriptionProps, ListRowGroup, ListRowGroupProps, ListRowIcon, ListRowIconProps, ListRowMeta, ListRowMetaProps, ListRowProps, ListRowTitle, ListRowTitleProps, ListRowTop, ListRowTopProps, ListRowValue, ListRowValueProps } from "./list-row.mjs";
2
- export { ListRow, ListRowBody, type ListRowBodyProps, ListRowBottom, type ListRowBottomProps, ListRowChevron, type ListRowChevronProps, ListRowDescription, type ListRowDescriptionProps, ListRowGroup, type ListRowGroupProps, ListRowIcon, type ListRowIconProps, ListRowMeta, type ListRowMetaProps, type ListRowProps, ListRowTitle, type ListRowTitleProps, ListRowTop, type ListRowTopProps, ListRowValue, type ListRowValueProps };
1
+ import { ListRow, ListRowBody, ListRowBodyProps, ListRowBottom, ListRowBottomProps, ListRowChevron, ListRowChevronProps, ListRowDescription, ListRowDescriptionProps, ListRowGroup, ListRowGroupProps, ListRowIcon, ListRowIconProps, ListRowMeta, ListRowMetaProps, ListRowProps, ListRowSubtitle, ListRowSubtitleProps, ListRowTitle, ListRowTitleProps, ListRowTop, ListRowTopProps, ListRowValue, ListRowValueProps } from "./list-row.mjs";
2
+ export { ListRow, ListRowBody, type ListRowBodyProps, ListRowBottom, type ListRowBottomProps, ListRowChevron, type ListRowChevronProps, ListRowDescription, type ListRowDescriptionProps, ListRowGroup, type ListRowGroupProps, ListRowIcon, type ListRowIconProps, ListRowMeta, type ListRowMetaProps, type ListRowProps, ListRowSubtitle, type ListRowSubtitleProps, ListRowTitle, type ListRowTitleProps, ListRowTop, type ListRowTopProps, ListRowValue, type ListRowValueProps };
@@ -47,6 +47,15 @@ export declare function ListRowBottom({ className, render, ...props }: ListRowBo
47
47
  export type ListRowTitleProps = useRender.ComponentProps<"span">;
48
48
  /** 16/24 600 on one line; the row's tone sets its weight and ink. */
49
49
  export declare function ListRowTitle({ className, render, ...props }: ListRowTitleProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
50
+ export type ListRowSubtitleProps = useRender.ComponentProps<"span">;
51
+ /**
52
+ * Data about the row's subject on the line under the title: an agent's or a
53
+ * channel's description, a channel's topic, a person's role, an app's byline.
54
+ * 400 in the row's ink, a size under the title, cut to one line. A setting's
55
+ * consequence goes in `ListRowDescription`; a time, a date or a gray count in
56
+ * `ListRowMeta`.
57
+ */
58
+ export declare function ListRowSubtitle({ className, render, ...props }: ListRowSubtitleProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
50
59
  export type ListRowDescriptionProps = useRender.ComponentProps<"span"> & {
51
60
  /** Let the text run onto more lines instead of truncating: a setting's
52
61
  * consequence. */
@@ -109,6 +109,25 @@ function ListRowTitle({ className, render, ...props }) {
109
109
  props: mergeProps(defaultProps, props)
110
110
  });
111
111
  }
112
+ /**
113
+ * Data about the row's subject on the line under the title: an agent's or a
114
+ * channel's description, a channel's topic, a person's role, an app's byline.
115
+ * 400 in the row's ink, a size under the title, cut to one line. A setting's
116
+ * consequence goes in `ListRowDescription`; a time, a date or a gray count in
117
+ * `ListRowMeta`.
118
+ */
119
+ function ListRowSubtitle({ className, render, ...props }) {
120
+ const { subtitle } = listRow();
121
+ const defaultProps = {
122
+ "data-slot": "list-row-subtitle",
123
+ className: subtitle({ className })
124
+ };
125
+ return useRender({
126
+ defaultTagName: "span",
127
+ render,
128
+ props: mergeProps(defaultProps, props)
129
+ });
130
+ }
112
131
  /** The preview (who spoke first: "Wug: …") or a setting's consequence, 14/20 ink-2. */
113
132
  function ListRowDescription({ className, render, wrap = false, ...props }) {
114
133
  const { description } = listRow({ wrap });
@@ -164,4 +183,4 @@ function ListRowChevron({ className, render, ...props }) {
164
183
  });
165
184
  }
166
185
  //#endregion
167
- export { ListRow, ListRowBody, ListRowBottom, ListRowChevron, ListRowDescription, ListRowGroup, ListRowIcon, ListRowMeta, ListRowTitle, ListRowTop, ListRowValue };
186
+ export { ListRow, ListRowBody, ListRowBottom, ListRowChevron, ListRowDescription, ListRowGroup, ListRowIcon, ListRowMeta, ListRowSubtitle, ListRowTitle, ListRowTop, ListRowValue };
@@ -36,6 +36,7 @@ export declare const listRow: import("tailwind-variants/lite").TVReturnType<{
36
36
  body: string[];
37
37
  line: string[];
38
38
  title: string[];
39
+ subtitle: string[];
39
40
  description: string[];
40
41
  meta: string[];
41
42
  value: string[];
@@ -77,6 +78,7 @@ export declare const listRow: import("tailwind-variants/lite").TVReturnType<{
77
78
  body: string[];
78
79
  line: string[];
79
80
  title: string[];
81
+ subtitle: string[];
80
82
  description: string[];
81
83
  meta: string[];
82
84
  value: string[];
@@ -118,6 +120,7 @@ export declare const listRow: import("tailwind-variants/lite").TVReturnType<{
118
120
  body: string[];
119
121
  line: string[];
120
122
  title: string[];
123
+ subtitle: string[];
121
124
  description: string[];
122
125
  meta: string[];
123
126
  value: string[];
@@ -25,6 +25,7 @@ const listRow = tv({
25
25
  "text-body font-semibold",
26
26
  "group-data-[tone=unread]/list-row:font-bold"
27
27
  ],
28
+ subtitle: ["min-w-0 flex-1 truncate", "text-footnote font-normal text-(--list-row-ink)"],
28
29
  description: ["min-w-0 flex-1 truncate", "text-callout text-(--list-row-ink-2)"],
29
30
  meta: ["min-w-0 truncate", "font-mono text-mono-meta font-normal tabular-nums text-(--list-row-ink-3)"],
30
31
  value: ["max-w-[45%] shrink-0 truncate", "text-subhead text-(--list-row-ink-3)"],
@@ -1,2 +1,2 @@
1
- import { MessageActionCard, MessageActionCardActions, MessageActionCardActionsNote, MessageActionCardActionsNoteProps, MessageActionCardActionsProps, MessageActionCardCollapseTrigger, MessageActionCardCollapseTriggerProps, MessageActionCardCompletion, MessageActionCardCompletionProps, MessageActionCardContent, MessageActionCardContentProps, MessageActionCardHeader, MessageActionCardHeaderDescription, MessageActionCardHeaderDescriptionProps, MessageActionCardHeaderIcon, MessageActionCardHeaderIconProps, MessageActionCardHeaderProps, MessageActionCardHeaderStatus, MessageActionCardHeaderStatusProps, MessageActionCardHeaderTitle, MessageActionCardHeaderTitleProps, MessageActionCardHint, MessageActionCardHintLabel, MessageActionCardHintLabelProps, MessageActionCardHintProps, MessageActionCardNotice, MessageActionCardNoticeProps, MessageActionCardProperties, MessageActionCardPropertiesProps, MessageActionCardProperty, MessageActionCardPropertyLabel, MessageActionCardPropertyLabelProps, MessageActionCardPropertyProps, MessageActionCardPropertyValue, MessageActionCardPropertyValueProps, MessageActionCardProps, MessageActionCardScope, MessageActionCardScopeDescription, MessageActionCardScopeDescriptionProps, MessageActionCardScopeList, MessageActionCardScopeListLabel, MessageActionCardScopeListLabelProps, MessageActionCardScopeListProps, MessageActionCardScopeListViewport, MessageActionCardScopeListViewportProps, MessageActionCardScopeName, MessageActionCardScopeNameProps, MessageActionCardScopeProps, MessageActionCardStatus, MessageActionCardStatusProps } from "./message-action-card.mjs";
2
- export { MessageActionCard, MessageActionCardActions, MessageActionCardActionsNote, type MessageActionCardActionsNoteProps, type MessageActionCardActionsProps, MessageActionCardCollapseTrigger, type MessageActionCardCollapseTriggerProps, MessageActionCardCompletion, type MessageActionCardCompletionProps, MessageActionCardContent, type MessageActionCardContentProps, MessageActionCardHeader, MessageActionCardHeaderDescription, type MessageActionCardHeaderDescriptionProps, MessageActionCardHeaderIcon, type MessageActionCardHeaderIconProps, type MessageActionCardHeaderProps, MessageActionCardHeaderStatus, type MessageActionCardHeaderStatusProps, MessageActionCardHeaderTitle, type MessageActionCardHeaderTitleProps, MessageActionCardHint, MessageActionCardHintLabel, type MessageActionCardHintLabelProps, type MessageActionCardHintProps, MessageActionCardNotice, type MessageActionCardNoticeProps, MessageActionCardProperties, type MessageActionCardPropertiesProps, MessageActionCardProperty, MessageActionCardPropertyLabel, type MessageActionCardPropertyLabelProps, type MessageActionCardPropertyProps, MessageActionCardPropertyValue, type MessageActionCardPropertyValueProps, type MessageActionCardProps, MessageActionCardScope, MessageActionCardScopeDescription, type MessageActionCardScopeDescriptionProps, MessageActionCardScopeList, MessageActionCardScopeListLabel, type MessageActionCardScopeListLabelProps, type MessageActionCardScopeListProps, MessageActionCardScopeListViewport, type MessageActionCardScopeListViewportProps, MessageActionCardScopeName, type MessageActionCardScopeNameProps, type MessageActionCardScopeProps, MessageActionCardStatus, type MessageActionCardStatusProps };
1
+ import { MessageActionCard, MessageActionCardActions, MessageActionCardActionsNote, MessageActionCardActionsNoteProps, MessageActionCardActionsProps, MessageActionCardCollapseTrigger, MessageActionCardCollapseTriggerProps, MessageActionCardCompletion, MessageActionCardCompletionProps, MessageActionCardContent, MessageActionCardContentProps, MessageActionCardHeader, MessageActionCardHeaderDescription, MessageActionCardHeaderDescriptionProps, MessageActionCardHeaderIcon, MessageActionCardHeaderIconProps, MessageActionCardHeaderMeta, MessageActionCardHeaderMetaProps, MessageActionCardHeaderProps, MessageActionCardHeaderStatus, MessageActionCardHeaderStatusProps, MessageActionCardHeaderTitle, MessageActionCardHeaderTitleProps, MessageActionCardHint, MessageActionCardHintLabel, MessageActionCardHintLabelProps, MessageActionCardHintProps, MessageActionCardNotice, MessageActionCardNoticeProps, MessageActionCardProperties, MessageActionCardPropertiesProps, MessageActionCardProperty, MessageActionCardPropertyLabel, MessageActionCardPropertyLabelProps, MessageActionCardPropertyProps, MessageActionCardPropertyValue, MessageActionCardPropertyValueProps, MessageActionCardProps, MessageActionCardScope, MessageActionCardScopeDescription, MessageActionCardScopeDescriptionProps, MessageActionCardScopeList, MessageActionCardScopeListLabel, MessageActionCardScopeListLabelProps, MessageActionCardScopeListProps, MessageActionCardScopeListViewport, MessageActionCardScopeListViewportProps, MessageActionCardScopeName, MessageActionCardScopeNameProps, MessageActionCardScopeProps, MessageActionCardStatus, MessageActionCardStatusProps } from "./message-action-card.mjs";
2
+ export { MessageActionCard, MessageActionCardActions, MessageActionCardActionsNote, type MessageActionCardActionsNoteProps, type MessageActionCardActionsProps, MessageActionCardCollapseTrigger, type MessageActionCardCollapseTriggerProps, MessageActionCardCompletion, type MessageActionCardCompletionProps, MessageActionCardContent, type MessageActionCardContentProps, MessageActionCardHeader, MessageActionCardHeaderDescription, type MessageActionCardHeaderDescriptionProps, MessageActionCardHeaderIcon, type MessageActionCardHeaderIconProps, MessageActionCardHeaderMeta, type MessageActionCardHeaderMetaProps, type MessageActionCardHeaderProps, MessageActionCardHeaderStatus, type MessageActionCardHeaderStatusProps, MessageActionCardHeaderTitle, type MessageActionCardHeaderTitleProps, MessageActionCardHint, MessageActionCardHintLabel, type MessageActionCardHintLabelProps, type MessageActionCardHintProps, MessageActionCardNotice, type MessageActionCardNoticeProps, MessageActionCardProperties, type MessageActionCardPropertiesProps, MessageActionCardProperty, MessageActionCardPropertyLabel, type MessageActionCardPropertyLabelProps, type MessageActionCardPropertyProps, MessageActionCardPropertyValue, type MessageActionCardPropertyValueProps, type MessageActionCardProps, MessageActionCardScope, MessageActionCardScopeDescription, type MessageActionCardScopeDescriptionProps, MessageActionCardScopeList, MessageActionCardScopeListLabel, type MessageActionCardScopeListLabelProps, type MessageActionCardScopeListProps, MessageActionCardScopeListViewport, type MessageActionCardScopeListViewportProps, MessageActionCardScopeName, type MessageActionCardScopeNameProps, type MessageActionCardScopeProps, MessageActionCardStatus, type MessageActionCardStatusProps };
@@ -23,6 +23,13 @@ export declare function MessageActionCardHeaderTitle({ className, render, ...pro
23
23
  * MessageActionCardHeaderTitle; it stays visible while the card is folded.
24
24
  */
25
25
  export declare function MessageActionCardHeaderDescription({ className, render, ...props }: useRender.ComponentProps<"span">): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
26
+ /**
27
+ * The time on a resolved card, in Space Mono at the hint tier. Place it
28
+ * inside MessageActionCardHeaderTitle for a line of its own under the title,
29
+ * or at the end of MessageActionCardHeaderDescription to close its line:
30
+ * "Approved by Martin · 10:51". It stays visible while the card is folded.
31
+ */
32
+ export declare function MessageActionCardHeaderMeta({ className, render, ...props }: MessageActionCardHeaderMetaProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
26
33
  /**
27
34
  * The card's state label in the header: plain text led by a square pixel in
28
35
  * the variant's colour, so a column of "Done" cards does not shout.
@@ -64,6 +71,7 @@ export type MessageActionCardProps = useRender.ComponentProps<"div"> & {
64
71
  export type MessageActionCardHeaderIconProps = useRender.ComponentProps<"span">;
65
72
  export type MessageActionCardHeaderTitleProps = useRender.ComponentProps<"span">;
66
73
  export type MessageActionCardHeaderDescriptionProps = useRender.ComponentProps<"span">;
74
+ export type MessageActionCardHeaderMetaProps = useRender.ComponentProps<"span">;
67
75
  export type MessageActionCardStatusProps = BadgeProps;
68
76
  export type MessageActionCardCollapseTriggerProps = Omit<ComponentProps<typeof Collapsible.Trigger>, "className"> & {
69
77
  className?: string;
@@ -79,6 +79,21 @@ function MessageActionCardHeaderDescription({ className, render, ...props }) {
79
79
  state: { slot: "message-action-card-header-description" }
80
80
  });
81
81
  }
82
+ /**
83
+ * The time on a resolved card, in Space Mono at the hint tier. Place it
84
+ * inside MessageActionCardHeaderTitle for a line of its own under the title,
85
+ * or at the end of MessageActionCardHeaderDescription to close its line:
86
+ * "Approved by Martin · 10:51". It stays visible while the card is folded.
87
+ */
88
+ function MessageActionCardHeaderMeta({ className, render, ...props }) {
89
+ const { headerMeta } = messageActionCard();
90
+ return useRender({
91
+ defaultTagName: "span",
92
+ render,
93
+ props: mergeProps({ className: headerMeta({ className }) }, props),
94
+ state: { slot: "message-action-card-header-meta" }
95
+ });
96
+ }
82
97
  const STATUS_TONES = {
83
98
  default: "neutral",
84
99
  muted: "neutral",
@@ -292,4 +307,4 @@ function MessageActionCardActionsNote({ className, render, ...props }) {
292
307
  });
293
308
  }
294
309
  //#endregion
295
- export { MessageActionCard, MessageActionCardActions, MessageActionCardActionsNote, MessageActionCardCollapseTrigger, MessageActionCardCompletion, MessageActionCardContent, MessageActionCardHeader, MessageActionCardHeaderDescription, MessageActionCardHeaderIcon, MessageActionCardHeaderStatus, MessageActionCardHeaderTitle, MessageActionCardHint, MessageActionCardHintLabel, MessageActionCardNotice, MessageActionCardProperties, MessageActionCardProperty, MessageActionCardPropertyLabel, MessageActionCardPropertyValue, MessageActionCardScope, MessageActionCardScopeDescription, MessageActionCardScopeList, MessageActionCardScopeListLabel, MessageActionCardScopeListViewport, MessageActionCardScopeName, MessageActionCardStatus };
310
+ export { MessageActionCard, MessageActionCardActions, MessageActionCardActionsNote, MessageActionCardCollapseTrigger, MessageActionCardCompletion, MessageActionCardContent, MessageActionCardHeader, MessageActionCardHeaderDescription, MessageActionCardHeaderIcon, MessageActionCardHeaderMeta, MessageActionCardHeaderStatus, MessageActionCardHeaderTitle, MessageActionCardHint, MessageActionCardHintLabel, MessageActionCardNotice, MessageActionCardProperties, MessageActionCardProperty, MessageActionCardPropertyLabel, MessageActionCardPropertyValue, MessageActionCardScope, MessageActionCardScopeDescription, MessageActionCardScopeList, MessageActionCardScopeListLabel, MessageActionCardScopeListViewport, MessageActionCardScopeName, MessageActionCardStatus };
@@ -16,6 +16,11 @@ const messageActionCard = tv({
16
16
  ],
17
17
  headerTitle: ["min-w-0 flex-1", "pt-[calc((27px-1lh)/2)]"],
18
18
  headerDescription: ["block", "mt-0.5 text-footnote font-normal text-foreground-muted"],
19
+ headerMeta: [
20
+ "block in-data-[slot=message-action-card-header-description]:inline",
21
+ "mt-0.5 in-data-[slot=message-action-card-header-description]:mt-0",
22
+ "font-mono text-mono-meta font-normal tabular-nums text-foreground-hint"
23
+ ],
19
24
  headerStatus: ["ml-auto shrink-0", "mt-[calc((27px-1lh)/2)] text-caption font-semibold text-foreground-muted"],
20
25
  status: [
21
26
  "inline-flex h-auto items-center gap-1.5 rounded-none border-0 bg-transparent p-0 shadow-none",
@@ -5,17 +5,17 @@ const resizable = tv({ slots: {
5
5
  panel: ["min-h-0 min-w-0"],
6
6
  handle: [
7
7
  "right-0 z-10 cursor-ew-resize touch-none select-none",
8
- "relative -ml-1.5 -mr-1.5 flex w-3 shrink-0 items-center justify-center pointer-coarse:-ml-3 pointer-coarse:-mr-3 pointer-coarse:w-6",
8
+ "relative -mr-3 flex w-3 shrink-0 items-center justify-center pointer-coarse:-mr-6 pointer-coarse:w-6",
9
9
  "before:pointer-events-none before:absolute before:transition-colors before:content-['']",
10
10
  "focus-visible:outline-none focus-visible:before:outline-2 focus-visible:before:outline-offset-2 focus-visible:before:outline-foreground",
11
- "data-[orientation=horizontal]:-mt-1.5 data-[orientation=horizontal]:-mb-1.5 data-[orientation=horizontal]:ml-0 data-[orientation=horizontal]:mr-0 data-[orientation=horizontal]:h-3 data-[orientation=horizontal]:w-full data-[orientation=horizontal]:cursor-ns-resize",
11
+ "data-[orientation=horizontal]:-mb-3 data-[orientation=horizontal]:mr-0 data-[orientation=horizontal]:h-3 data-[orientation=horizontal]:w-full data-[orientation=horizontal]:cursor-ns-resize pointer-coarse:data-[orientation=horizontal]:-mb-6 pointer-coarse:data-[orientation=horizontal]:mr-0 pointer-coarse:data-[orientation=horizontal]:h-6",
12
12
  "bg-transparent",
13
- "before:top-0 before:left-[5px] pointer-coarse:before:left-[11px] before:h-full before:w-px before:translate-none before:rounded-none",
13
+ "before:top-0 before:-left-px before:h-full before:w-px before:translate-none before:rounded-none",
14
14
  "before:bg-transparent",
15
15
  "hover:before:bg-foreground-muted",
16
16
  "data-[separator=active]:before:bg-foreground-muted",
17
17
  "focus-visible:keyboard:before:w-0.5 focus-visible:keyboard:before:bg-foreground focus-visible:before:outline-none",
18
- "data-[orientation=horizontal]:before:top-[5px] data-[orientation=horizontal]:before:left-0",
18
+ "data-[orientation=horizontal]:before:-top-px data-[orientation=horizontal]:before:left-0",
19
19
  "data-[orientation=horizontal]:before:h-px data-[orientation=horizontal]:before:w-full",
20
20
  "data-[orientation=horizontal]:focus-visible:keyboard:before:h-0.5"
21
21
  ]
@@ -399,7 +399,8 @@ function useScrollbars({ containerRef, contentRef, fadeRef, guttersPosition, roo
399
399
  /** Binds the observers and the scroll listener to `container`; returns what undoes it. */
400
400
  const bind = useCallback((container, content, root) => {
401
401
  container.addEventListener("scroll", handleScroll, { passive: true });
402
- measure(true);
402
+ if (typeof ResizeObserver === "undefined") measure(true);
403
+ else scheduleFrame(true);
403
404
  const classes = root && typeof MutationObserver !== "undefined" ? new MutationObserver(applyClasses) : null;
404
405
  if (root) classes?.observe(root, {
405
406
  attributeFilter: ["class"],
@@ -0,0 +1,45 @@
1
+ //#region src/components/steps/otter-animation-visibility.ts
2
+ /** One observer/listener per document, regardless of the number of illustrations. */
3
+ const documents = /* @__PURE__ */ new WeakMap();
4
+ function observeOtterAnimation(node) {
5
+ const doc = node.ownerDocument;
6
+ let manager = documents.get(doc);
7
+ if (!manager) {
8
+ const visible = /* @__PURE__ */ new Map();
9
+ const update = (element, intersects) => {
10
+ element.dataset.animate = String(intersects && !doc.hidden);
11
+ };
12
+ const onVisibility = () => {
13
+ for (const [element, intersects] of visible) update(element, intersects);
14
+ };
15
+ const Observer = doc.defaultView?.IntersectionObserver;
16
+ const observer = Observer ? new Observer((entries) => {
17
+ for (const entry of entries) {
18
+ const element = entry.target;
19
+ if (!visible.has(element)) continue;
20
+ visible.set(element, entry.isIntersecting);
21
+ update(element, entry.isIntersecting);
22
+ }
23
+ }) : null;
24
+ doc.addEventListener("visibilitychange", onVisibility);
25
+ manager = { observe(element) {
26
+ visible.set(element, !observer);
27
+ update(element, !observer);
28
+ observer?.observe(element);
29
+ return () => {
30
+ observer?.unobserve(element);
31
+ visible.delete(element);
32
+ delete element.dataset.animate;
33
+ if (visible.size === 0) {
34
+ observer?.disconnect();
35
+ doc.removeEventListener("visibilitychange", onVisibility);
36
+ documents.delete(doc);
37
+ }
38
+ };
39
+ } };
40
+ documents.set(doc, manager);
41
+ }
42
+ return manager.observe(node);
43
+ }
44
+ //#endregion
45
+ export { observeOtterAnimation };