@dbx-tools/ui-mastra 0.6.217 → 0.6.218

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  // Hand edits are overwritten on the next watch; this file is read-only.
4
4
 
5
5
  export const PACKAGE_IDENTIFIER = "@dbx-tools/ui-mastra";
6
- export const PACKAGE_VERSION = "0.6.217";
6
+ export const PACKAGE_VERSION = "0.6.218";
7
7
  export * as reactBubbles from "./src/react/bubbles.tsx";
8
8
  export * as reactChatApprovals from "./src/react/chat-approvals.ts";
9
9
  export * as reactChatComposer from "./src/react/chat-composer.tsx";
package/package.json CHANGED
@@ -31,12 +31,12 @@
31
31
  "react-dom": "~19.2.4"
32
32
  },
33
33
  "dependencies": {
34
- "@dbx-tools/shared-core": "0.6.217",
35
- "@dbx-tools/shared-genie": "0.6.217",
36
- "@dbx-tools/shared-mastra": "0.6.217",
37
- "@dbx-tools/shared-model": "0.6.217",
38
- "@dbx-tools/ui-appkit": "0.6.217",
39
- "@dbx-tools/ui-branding": "0.6.217",
34
+ "@dbx-tools/shared-core": "0.6.218",
35
+ "@dbx-tools/shared-genie": "0.6.218",
36
+ "@dbx-tools/shared-mastra": "0.6.218",
37
+ "@dbx-tools/shared-model": "0.6.218",
38
+ "@dbx-tools/ui-appkit": "0.6.218",
39
+ "@dbx-tools/ui-branding": "0.6.218",
40
40
  "@mastra/client-js": "1.46.0",
41
41
  "@tanstack/react-table": "^8.21.3",
42
42
  "ai": "^5.0.0",
@@ -52,7 +52,7 @@
52
52
  "publishConfig": {
53
53
  "access": "public"
54
54
  },
55
- "version": "0.6.217",
55
+ "version": "0.6.218",
56
56
  "type": "module",
57
57
  "exports": {
58
58
  "./react": "./src/react/index.ts",
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Reset and measure one composer textarea when its container has usable width.
3
+ *
4
+ * Resetting to `auto` before reading `scrollHeight` allows deleted content and
5
+ * wider containers to shrink as well as letting multiline drafts grow.
6
+ */
7
+ export function autosizeComposerTextarea(
8
+ group: HTMLElement | null,
9
+ textarea: HTMLTextAreaElement | null,
10
+ ): void {
11
+ if (!group || !textarea || group.getBoundingClientRect().width <= 0) return;
12
+ textarea.style.height = "auto";
13
+ textarea.style.height = `${textarea.scrollHeight}px`;
14
+ }
15
+
16
+ /**
17
+ * Observe width changes without reacting to the group height that the textarea
18
+ * itself controls. A zero-width observation resets the comparison sentinel so
19
+ * reopening at the same prior width still triggers a fresh measurement.
20
+ */
21
+ export function observeComposerWidth(
22
+ group: HTMLElement,
23
+ resize: () => void,
24
+ Observer: typeof ResizeObserver | undefined = globalThis.ResizeObserver,
25
+ ): () => void {
26
+ if (!Observer) return () => {};
27
+ let measuredWidth = 0;
28
+ const observer = new Observer((entries) => {
29
+ const width = entries[0]?.contentRect.width ?? group.getBoundingClientRect().width;
30
+ if (!Number.isFinite(width)) return;
31
+ if (width <= 0) {
32
+ measuredWidth = 0;
33
+ return;
34
+ }
35
+ if (width === measuredWidth) return;
36
+ measuredWidth = width;
37
+ resize();
38
+ });
39
+ observer.observe(group);
40
+ return () => observer.disconnect();
41
+ }
@@ -32,6 +32,7 @@ import {
32
32
  XIcon,
33
33
  } from "lucide-react";
34
34
  import { useCallback, useLayoutEffect, useRef, useState } from "react";
35
+ import { autosizeComposerTextarea, observeComposerWidth } from "./_composer-autosize.ts";
35
36
  import { ExportMenu } from "./export-menu.tsx";
36
37
  import { SuggestionPills } from "./suggestion-pills.tsx";
37
38
  import type { ChatViewProps } from "./types.ts";
@@ -278,14 +279,22 @@ export const ChatComposer = ({
278
279
  onResumeFollow,
279
280
  }: ChatComposerProps) => {
280
281
  const [input, setInput] = useState("");
282
+ const inputGroupRef = useRef<HTMLDivElement>(null);
281
283
  const textareaRef = useRef<HTMLTextAreaElement>(null);
282
284
 
285
+ const resizeTextarea = useCallback(() => {
286
+ autosizeComposerTextarea(inputGroupRef.current, textareaRef.current);
287
+ }, []);
288
+
289
+ useLayoutEffect(() => {
290
+ resizeTextarea();
291
+ }, [input, isLoadingHistory, resizeTextarea]);
292
+
283
293
  useLayoutEffect(() => {
284
- const element = textareaRef.current;
285
- if (!element) return;
286
- element.style.height = "auto";
287
- element.style.height = `${element.scrollHeight}px`;
288
- }, [input]);
294
+ const group = inputGroupRef.current;
295
+ if (!group) return;
296
+ return observeComposerWidth(group, resizeTextarea);
297
+ }, [resizeTextarea]);
289
298
 
290
299
  const isRunning = status === "submitted" || status === "streaming";
291
300
  const submit = () => {
@@ -336,7 +345,10 @@ export const ChatComposer = ({
336
345
  onRemoveSteer={onRemoveSteer}
337
346
  onReorderSteers={onReorderSteers}
338
347
  />
339
- <InputGroup className="rounded-2xl border-border/80 shadow-sm transition-shadow focus-within:shadow-md">
348
+ <InputGroup
349
+ ref={inputGroupRef}
350
+ className="rounded-2xl border-border/80 shadow-sm transition-shadow focus-within:shadow-md"
351
+ >
340
352
  <InputGroupTextarea
341
353
  ref={textareaRef}
342
354
  value={input}
@@ -350,7 +362,7 @@ export const ChatComposer = ({
350
362
  placeholder={isLoadingHistory ? "Loading history..." : "Send a message..."}
351
363
  rows={1}
352
364
  disabled={isLoadingHistory}
353
- className="max-h-48 text-base md:text-sm"
365
+ className="field-sizing-fixed max-h-48 overflow-y-auto text-base md:text-sm"
354
366
  />
355
367
  <InputGroupAddon align="inline-end">
356
368
  {isRunning && onStop && !input.trim() ? (