@hanzo/ui 8.1.4 → 8.1.6

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 (81) hide show
  1. package/dist/backends/gui/textarea.cjs +5 -10
  2. package/dist/backends/gui/textarea.d.ts +11 -32
  3. package/dist/backends/gui/textarea.d.ts.map +1 -1
  4. package/dist/backends/gui/textarea.js +5 -10
  5. package/dist/backends/gui/textarea.js.map +1 -1
  6. package/dist/chat/Code.cjs +2 -2
  7. package/dist/chat/Code.d.ts +16 -3
  8. package/dist/chat/Code.d.ts.map +1 -1
  9. package/dist/chat/Code.js +2 -2
  10. package/dist/chat/Code.js.map +1 -1
  11. package/dist/chat/Composer.cjs +17 -47
  12. package/dist/chat/Composer.d.ts +40 -3
  13. package/dist/chat/Composer.d.ts.map +1 -1
  14. package/dist/chat/Composer.js +17 -47
  15. package/dist/chat/Composer.js.map +1 -1
  16. package/dist/chat/Failure.cjs +11 -30
  17. package/dist/chat/Failure.d.ts +15 -3
  18. package/dist/chat/Failure.d.ts.map +1 -1
  19. package/dist/chat/Failure.js +11 -30
  20. package/dist/chat/Failure.js.map +1 -1
  21. package/dist/chat/Header.cjs +2 -2
  22. package/dist/chat/Header.d.ts +20 -3
  23. package/dist/chat/Header.d.ts.map +1 -1
  24. package/dist/chat/Header.js +2 -2
  25. package/dist/chat/Header.js.map +1 -1
  26. package/dist/chat/Message.cjs +16 -33
  27. package/dist/chat/Message.d.ts +26 -15
  28. package/dist/chat/Message.d.ts.map +1 -1
  29. package/dist/chat/Message.js +16 -33
  30. package/dist/chat/Message.js.map +1 -1
  31. package/dist/chat/Sources.cjs +15 -10
  32. package/dist/chat/Sources.d.ts +26 -6
  33. package/dist/chat/Sources.d.ts.map +1 -1
  34. package/dist/chat/Sources.js +14 -9
  35. package/dist/chat/Sources.js.map +1 -1
  36. package/dist/chat/Step.cjs +18 -44
  37. package/dist/chat/Step.d.ts +9 -20
  38. package/dist/chat/Step.d.ts.map +1 -1
  39. package/dist/chat/Step.js +18 -44
  40. package/dist/chat/Step.js.map +1 -1
  41. package/dist/chat/Thread.cjs +19 -17
  42. package/dist/chat/Thread.d.ts +25 -3
  43. package/dist/chat/Thread.d.ts.map +1 -1
  44. package/dist/chat/Thread.js +19 -17
  45. package/dist/chat/Thread.js.map +1 -1
  46. package/dist/chat/index.cjs +22 -46
  47. package/dist/chat/index.d.ts +21 -45
  48. package/dist/chat/index.d.ts.map +1 -1
  49. package/dist/chat/index.js +21 -45
  50. package/dist/chat/index.js.map +1 -1
  51. package/dist/chat/pure.cjs +22 -0
  52. package/dist/chat/pure.d.ts +3 -0
  53. package/dist/chat/pure.d.ts.map +1 -0
  54. package/dist/chat/pure.js +16 -0
  55. package/dist/chat/pure.js.map +1 -0
  56. package/dist/chat/send.cjs +12 -13
  57. package/dist/chat/send.d.ts +8 -6
  58. package/dist/chat/send.d.ts.map +1 -1
  59. package/dist/chat/send.js +12 -13
  60. package/dist/chat/send.js.map +1 -1
  61. package/dist/chat/stick.cjs +3 -5
  62. package/dist/chat/stick.d.ts +2 -4
  63. package/dist/chat/stick.d.ts.map +1 -1
  64. package/dist/chat/stick.js +3 -5
  65. package/dist/chat/stick.js.map +1 -1
  66. package/dist/gallery.cjs +5 -4
  67. package/dist/gallery.d.ts.map +1 -1
  68. package/dist/gallery.js +5 -4
  69. package/dist/gallery.js.map +1 -1
  70. package/dist/product/CopyButton.cjs +2 -2
  71. package/dist/product/CopyButton.d.ts +6 -2
  72. package/dist/product/CopyButton.d.ts.map +1 -1
  73. package/dist/product/CopyButton.js +2 -2
  74. package/dist/product/CopyButton.js.map +1 -1
  75. package/dist/product/pure.cjs +10 -1
  76. package/dist/product/pure.d.ts +2 -0
  77. package/dist/product/pure.d.ts.map +1 -1
  78. package/dist/product/pure.js +5 -0
  79. package/dist/product/pure.js.map +1 -1
  80. package/dist/styles.css +14 -2
  81. package/package.json +7 -1
@@ -1,46 +1,27 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  /**
4
- * Failure — the turn did not complete, and what to do about it.
4
+ * Failure — the turn did not arrive, and what to do about it.
5
5
  *
6
6
  * Distinct from a `Step` that errored: a step can fail inside an answer that
7
- * still arrives, this is the answer not arriving. hanzo/chat keeps the same
8
- * separation — `ToolCall`'s error state, and separately `MessageContent.tsx:153`
9
- * swapping the whole body for `ErrorMessage`.
7
+ * still lands. Retry sits beside the message, not in the hover strip, so it is
8
+ * reachable on a touch device.
10
9
  *
11
- * Four surfaces draw this line and no two draw it the same way: chat's
12
- * `ErrorBox` (`components/Messages/Content/Error.tsx`), the app builder's
13
- * `$red8` text with the prose instruction "Something went wrong — please try
14
- * again" (`ask-ai/chat-thread.tsx:258`), the extension's `.ae-error` div
15
- * (`AnswerEngine.tsx:336`), and hanzo.app `/chat`, which is the interesting
16
- * one: it sets `Message.error` at `app/chat/page.tsx:292` and never reads it
17
- * anywhere in the render, so a stream that dies reports through a detached
18
- * banner and the turn itself says nothing.
19
- *
20
- * Two of the four offer a way to try again. Both put it next to the message
21
- * rather than in the hover strip, because a hover affordance on the thing that
22
- * just failed is not reachable on a touch device.
23
- *
24
- * `role="alert"` with `aria-live="assertive"` is chat's, and it is right: a
25
- * turn failing is the one event in a conversation that has to interrupt, since
26
- * the reader is waiting on prose that is never going to come.
10
+ * `role="alert"` with `aria-live="assertive"`: the reader is waiting on prose
11
+ * that is never coming, which is the one event in a conversation that has to
12
+ * interrupt.
27
13
  */
28
14
  import { XStack, YStack } from '@hanzo/gui';
29
15
  import { RefreshCw } from '@hanzogui/lucide-icons-2';
30
16
  import { Button } from '../backends/gui/index.js';
31
17
  import { ink } from '../backends/gui/ink.js';
32
18
  import { slot } from '../backends/gui/slot.js';
33
- export function Failure({ children, onRetry, retryLabel = 'Try again' }) {
19
+ export function Failure({ children, onRetry, retryLabel = 'Try again', ...props }) {
34
20
  return (_jsxs(YStack, { ...slot('failure'), role: "alert", "aria-live": "assertive", width: "100%", gap: "$2", px: "$3", py: "$2.5", rounded: "$4", borderWidth: 1,
35
- // The edge again — see `product/tone.ts`. Its `stopped` register is
36
- // `$color4` behind a `$color9` border for exactly this: with no hue to
37
- // spend, an outline is what separates "this one" from its neighbours.
38
- borderColor: "$color9", bg: "$color4", children: [ink(children, undefined, { size: '$2', color: '$color12' }), onRetry ? (
39
- // The real Button, not a `role="button"` box with an `onPress`. A View
40
- // wearing the role takes focus and then does nothing on Enter or Space,
41
- // because the activation behaviour belongs to the element and not to
42
- // the attribute — and the one control on a failed turn is the last one
43
- // that should be pointer-only.
21
+ // The `stopped` register from `product/tone.ts`: an outline, not a hue.
22
+ borderColor: "$color9", bg: "$color4", ...props, children: [ink(children, undefined, { size: '$2', color: '$color12' }), onRetry ? (
23
+ // A real Button: a View wearing `role="button"` takes focus and then
24
+ // ignores Enter and Space.
44
25
  _jsx(XStack, { self: "flex-start", children: _jsxs(Button, { ...slot('failure-retry'), variant: "outline", size: "sm", onPress: onRetry, children: [_jsx(RefreshCw, { size: 13 }), retryLabel] }) })) : null] }));
45
26
  }
46
27
  //# sourceMappingURL=Failure.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Failure.js","sourceRoot":"","sources":["../../src/chat/Failure.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAGpD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAA;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAU3C,MAAM,UAAU,OAAO,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,WAAW,EAAgB;IACnF,OAAO,CACL,MAAC,MAAM,OACD,IAAI,CAAC,SAAS,CAAC,EACnB,IAAI,EAAC,OAAO,eACF,WAAW,EACrB,KAAK,EAAC,MAAM,EACZ,GAAG,EAAC,IAAI,EACR,EAAE,EAAC,IAAI,EACP,EAAE,EAAC,MAAM,EACT,OAAO,EAAC,IAAI,EACZ,WAAW,EAAE,CAAC;QACd,oEAAoE;QACpE,uEAAuE;QACvE,sEAAsE;QACtE,WAAW,EAAC,SAAS,EACrB,EAAE,EAAC,SAAS,aAEX,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAC3D,OAAO,CAAC,CAAC,CAAC;YACT,uEAAuE;YACvE,wEAAwE;YACxE,qEAAqE;YACrE,uEAAuE;YACvE,+BAA+B;YAC/B,KAAC,MAAM,IAAC,IAAI,EAAC,YAAY,YACvB,MAAC,MAAM,OAAK,IAAI,CAAC,eAAe,CAAC,EAAE,OAAO,EAAC,SAAS,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAE,OAAO,aAC7E,KAAC,SAAS,IAAC,IAAI,EAAE,EAAE,GAAI,EACtB,UAAU,IACJ,GACF,CACV,CAAC,CAAC,CAAC,IAAI,IACD,CACV,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"Failure.js","sourceRoot":"","sources":["../../src/chat/Failure.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;;GAUG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AAGpD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAA;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAU3C,MAAM,UAAU,OAAO,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,WAAW,EAAE,GAAG,KAAK,EAAgB;IAC7F,OAAO,CACL,MAAC,MAAM,OACD,IAAI,CAAC,SAAS,CAAC,EACnB,IAAI,EAAC,OAAO,eACF,WAAW,EACrB,KAAK,EAAC,MAAM,EACZ,GAAG,EAAC,IAAI,EACR,EAAE,EAAC,IAAI,EACP,EAAE,EAAC,MAAM,EACT,OAAO,EAAC,IAAI,EACZ,WAAW,EAAE,CAAC;QACd,wEAAwE;QACxE,WAAW,EAAC,SAAS,EACrB,EAAE,EAAC,SAAS,KACR,KAAK,aAER,GAAG,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAC3D,OAAO,CAAC,CAAC,CAAC;YACT,qEAAqE;YACrE,2BAA2B;YAC3B,KAAC,MAAM,IAAC,IAAI,EAAC,YAAY,YACvB,MAAC,MAAM,OAAK,IAAI,CAAC,eAAe,CAAC,EAAE,OAAO,EAAC,SAAS,EAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAE,OAAO,aAC7E,KAAC,SAAS,IAAC,IAAI,EAAE,EAAE,GAAI,EACtB,UAAU,IACJ,GACF,CACV,CAAC,CAAC,CAAC,IAAI,IACD,CACV,CAAA;AACH,CAAC"}
@@ -18,8 +18,8 @@ const jsx_runtime_1 = require("react/jsx-runtime");
18
18
  const gui_1 = require("@hanzo/gui");
19
19
  const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
20
20
  const slot_1 = require("../backends/gui/slot.cjs");
21
- function Header({ title, onOpenMenu, children }) {
22
- return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { ...(0, slot_1.slot)('chat-header'), width: "100%", height: 48, shrink: 0, items: "center", gap: "$2", px: "$3", borderBottomWidth: 1, borderColor: "$borderColor", bg: "$background", children: [(0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$1", shrink: 1, children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", fontWeight: "500", numberOfLines: 1, children: title }), onOpenMenu ? ((0, jsx_runtime_1.jsx)(HeaderButton, { label: "Conversation options", onPress: onOpenMenu, children: (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Ellipsis, { size: 16 }) })) : null] }), (0, jsx_runtime_1.jsx)(gui_1.XStack, { flex: 1 }), children] }));
21
+ function Header({ title, onOpenMenu, children, leading, ...props }) {
22
+ return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { ...(0, slot_1.slot)('chat-header'), width: "100%", height: 48, shrink: 0, items: "center", gap: "$2", px: "$3", borderBottomWidth: 1, borderColor: "$borderColor", bg: "$background", ...props, children: [leading, (0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$1", shrink: 1, children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", fontWeight: "500", numberOfLines: 1, children: title }), onOpenMenu ? ((0, jsx_runtime_1.jsx)(HeaderButton, { label: "Conversation options", onPress: onOpenMenu, children: (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Ellipsis, { size: 16 }) })) : null] }), (0, jsx_runtime_1.jsx)(gui_1.XStack, { flex: 1 }), children] }));
23
23
  }
24
24
  function HeaderButton({ label, onPress, children }) {
25
25
  return ((0, jsx_runtime_1.jsx)(gui_1.XStack, { width: 32, height: 32, items: "center", justify: "center", rounded: "$3", cursor: "pointer", opacity: 0.7, onPress: onPress, role: "button", tabIndex: 0, "aria-label": label, ...(0, slot_1.tip)(label), hoverStyle: { bg: '$color4', opacity: 1 }, pressStyle: { bg: '$color5' }, children: children }));
@@ -1,12 +1,29 @@
1
- import type { ReactNode } from 'react';
2
- export interface HeaderProps {
1
+ /**
2
+ * Header — the conversation title bar.
3
+ *
4
+ * Title on the left, `children` as the action slot on the right. The actions are
5
+ * a slot rather than a fixed set because what belongs there is per-surface:
6
+ * hanzo.chat shares and opens a details rail, the console shows the owning
7
+ * project, hanzo.app shows neither.
8
+ */
9
+ import { XStack } from '@hanzo/gui';
10
+ import type { ComponentProps, ReactNode } from 'react';
11
+ export interface HeaderProps extends Omit<ComponentProps<typeof XStack>, 'children'> {
3
12
  title: string;
4
13
  /** Overflow menu next to the title. Omit to hide it. */
5
14
  onOpenMenu?: () => void;
6
15
  /** Action slot, rendered at the end of the row. */
7
16
  children?: ReactNode;
17
+ /**
18
+ * Slot BEFORE the title — a rail toggle, a back arrow.
19
+ *
20
+ * hanzo.app `/chat` leads with a sidebar toggle and kept its own header
21
+ * rather than adopt this one, because the row began at the title and there
22
+ * was nowhere to put it.
23
+ */
24
+ leading?: ReactNode;
8
25
  }
9
- export declare function Header({ title, onOpenMenu, children }: HeaderProps): import("react/jsx-runtime").JSX.Element;
26
+ export declare function Header({ title, onOpenMenu, children, leading, ...props }: HeaderProps): import("react/jsx-runtime").JSX.Element;
10
27
  export interface HeaderButtonProps {
11
28
  /** Required — the accessible name for an icon-only control. */
12
29
  label: string;
@@ -1 +1 @@
1
- {"version":3,"file":"Header.d.ts","sourceRoot":"","sources":["../../src/chat/Header.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAED,wBAAgB,MAAM,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,WAAW,2CAiClE;AAED,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAED,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,iBAAiB,2CAqB3E;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAAE,KAAe,EAAE,OAAO,EAAE,EAAE,gBAAgB,2CAuBzE;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB;AAED,wBAAgB,WAAW,CAAC,EAAE,KAA8B,EAAE,OAAO,EAAE,EAAE,gBAAgB,2CAMxF;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAW,EAAE,EAAE,UAAU,2CAe1D"}
1
+ {"version":3,"file":"Header.d.ts","sourceRoot":"","sources":["../../src/chat/Header.tsx"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,OAAO,EAAe,MAAM,EAAU,MAAM,YAAY,CAAA;AAExD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItD,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,UAAU,CAAC;IAClF,KAAK,EAAE,MAAM,CAAA;IACb,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,SAAS,CAAA;CACpB;AAED,wBAAgB,MAAM,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,WAAW,2CAmCrF;AAED,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB;AAED,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,iBAAiB,2CAqB3E;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAAE,KAAe,EAAE,OAAO,EAAE,EAAE,gBAAgB,2CAuBzE;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB;AAED,wBAAgB,WAAW,CAAC,EAAE,KAA8B,EAAE,OAAO,EAAE,EAAE,gBAAgB,2CAMxF;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,KAAK,CAAC,EAAE,QAAQ,EAAE,KAAW,EAAE,EAAE,UAAU,2CAe1D"}
@@ -11,8 +11,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
11
  import { SizableText, XStack, YStack } from '@hanzo/gui';
12
12
  import { Ellipsis, PanelRight, Share2 } from '@hanzogui/lucide-icons-2';
13
13
  import { slot, tip } from '../backends/gui/slot.js';
14
- export function Header({ title, onOpenMenu, children }) {
15
- return (_jsxs(XStack, { ...slot('chat-header'), width: "100%", height: 48, shrink: 0, items: "center", gap: "$2", px: "$3", borderBottomWidth: 1, borderColor: "$borderColor", bg: "$background", children: [_jsxs(XStack, { items: "center", gap: "$1", shrink: 1, children: [_jsx(SizableText, { size: "$2", fontWeight: "500", numberOfLines: 1, children: title }), onOpenMenu ? (_jsx(HeaderButton, { label: "Conversation options", onPress: onOpenMenu, children: _jsx(Ellipsis, { size: 16 }) })) : null] }), _jsx(XStack, { flex: 1 }), children] }));
14
+ export function Header({ title, onOpenMenu, children, leading, ...props }) {
15
+ return (_jsxs(XStack, { ...slot('chat-header'), width: "100%", height: 48, shrink: 0, items: "center", gap: "$2", px: "$3", borderBottomWidth: 1, borderColor: "$borderColor", bg: "$background", ...props, children: [leading, _jsxs(XStack, { items: "center", gap: "$1", shrink: 1, children: [_jsx(SizableText, { size: "$2", fontWeight: "500", numberOfLines: 1, children: title }), onOpenMenu ? (_jsx(HeaderButton, { label: "Conversation options", onPress: onOpenMenu, children: _jsx(Ellipsis, { size: 16 }) })) : null] }), _jsx(XStack, { flex: 1 }), children] }));
16
16
  }
17
17
  export function HeaderButton({ label, onPress, children }) {
18
18
  return (_jsx(XStack, { width: 32, height: 32, items: "center", justify: "center", rounded: "$3", cursor: "pointer", opacity: 0.7, onPress: onPress, role: "button", tabIndex: 0, "aria-label": label, ...tip(label), hoverStyle: { bg: '$color4', opacity: 1 }, pressStyle: { bg: '$color5' }, children: children }));
@@ -1 +1 @@
1
- {"version":3,"file":"Header.js","sourceRoot":"","sources":["../../src/chat/Header.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Header.js","sourceRoot":"","sources":["../../src/chat/Header.tsx"],"names":[],"mappings":"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"}
@@ -9,55 +9,38 @@ const jsx_runtime_1 = require("react/jsx-runtime");
9
9
  *
10
10
  * Role drives presentation and nothing else: a user turn is a contained bubble
11
11
  * that shrinks to its content, an assistant turn is full-bleed prose, a system
12
- * turn is a muted aside. Content is `children`, so the surface keeps its own
13
- * markdown pipeline — the surfaces disagree on remark plugins and none of that
14
- * belongs in a presentational package.
12
+ * turn is a muted aside. The contrast is what says who is speaking, so there is
13
+ * no name and no timestamp, and `icon` is opt-in.
15
14
  *
16
- * That arrangement is not this package's taste; it is what all six turn
17
- * renderers across the estate independently arrived at. hanzo/chat states it
18
- * outright in `client/src/components/Chat/Messages/ui/MessageRender.tsx:131`:
19
- * "the user's turn is a glass bubble on the right, the reply plain and full
20
- * width — the contrast is what says who is speaking." No avatar, no sender
21
- * name, no timestamp — which is why `icon` is opt-in and there is no `name`
22
- * prop. Only the read-only share view renders a name, and only one surface
23
- * renders a timestamp.
15
+ * Content is `children`: the markdown pipeline stays with the surface.
24
16
  */
25
17
  const gui_1 = require("@hanzo/gui");
26
18
  const ink_1 = require("../backends/gui/ink.cjs");
27
19
  const slot_1 = require("../backends/gui/slot.cjs");
28
20
  /**
29
- * The mark that says a turn is still arriving.
21
+ * The mark that says a turn is still arriving. Static, not blinking.
30
22
  *
31
- * Placed by whoever knows where the prose ends. A surface rendering markdown
32
- * owns that node and can append this inside it; `Message busy` puts it at the
33
- * end of the turn instead, which is all this package can know.
34
- *
35
- * It does not blink. Every surface that ships one draws it static — an 8x16
36
- * block on hanzo.app `/chat`, a 2px caret in the builder, a 12px `⬤` from
37
- * hanzo/chat's `.result-streaming::after`. A caret is the narrowest of the
38
- * three and the one that reads as "the sentence continues" rather than as a
39
- * bullet. Decorative, so it is hidden from assistive tech: the turn's text is
40
- * already live-announced by the surface, and a cursor announced as content is
41
- * noise on every token.
23
+ * Placed by whoever knows where the prose ends: a surface rendering markdown
24
+ * owns that node and can append this inside it, and `Message busy` puts it at
25
+ * the end of the turn. Decorative, so assistive tech skips it — the surface
26
+ * already live-announces the text.
42
27
  */
43
28
  function Caret() {
44
29
  return (0, jsx_runtime_1.jsx)(gui_1.YStack, { ...(0, slot_1.slot)('caret'), width: 2, height: 15, rounded: 1, bg: "$color12", "aria-hidden": true });
45
30
  }
46
- function Message({ role, children, actions, busy = false, icon }) {
31
+ function Message({ role, children, actions, busy = false, icon, body, ...props }) {
47
32
  if (role === 'system')
48
- return ((0, jsx_runtime_1.jsx)(gui_1.XStack, { ...(0, slot_1.slot)('message'), "data-role": "system", width: "100%", justify: "center", py: "$2", children: (0, jsx_runtime_1.jsx)(gui_1.Text, { fontSize: "$1", color: "$color10", text: "center", children: children }) }));
33
+ return ((0, jsx_runtime_1.jsx)(gui_1.XStack, { ...(0, slot_1.slot)('message'), "data-role": "system", width: "100%", justify: "center", py: "$2", ...props, children: (0, jsx_runtime_1.jsx)(gui_1.Text, { fontSize: "$1", color: "$color10", text: "center", children: children }) }));
49
34
  const mine = role === 'user';
50
- return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { ...(0, slot_1.slot)('message'), "data-role": role, width: "100%", justify: mine ? 'flex-end' : 'flex-start', gap: "$2", children: [icon && !mine ? (0, jsx_runtime_1.jsx)(gui_1.YStack, { pt: "$1", children: icon }) : null, (0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('message-body'), "data-bubble": mine ? 'true' : undefined, gap: "$2",
51
- // 85%, because that is the number three independent implementations
52
- // reached: hanzo/chat's `USER_TURN` (`common/turn.ts:17`,
53
- // `max-w-[85%]`), hanzo.app `/chat` (`app/chat/page.tsx:521`,
54
- // `maxWidth="85%"`) and the app builder's `UserBubble`
55
- // (`ask-ai/chat-thread.tsx:141`). This package said 80% and no one
56
- // said 80%.
35
+ return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { ...(0, slot_1.slot)('message'), "data-role": role, width: "100%", justify: mine ? 'flex-end' : 'flex-start', gap: "$2",
36
+ // Makes `actions` revealable with `$group-hover`. Unnamed: gui types
37
+ // `group` as a boolean, so a name does not compile.
38
+ group: true, ...props, children: [icon && !mine ? (0, jsx_runtime_1.jsx)(gui_1.YStack, { pt: "$1", children: icon }) : null, (0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('message-body'), "data-bubble": mine ? 'true' : undefined, gap: "$2",
39
+ // 85% is what the surfaces converged on for a user turn.
57
40
  maxW: mine ? '85%' : '100%', flex: mine ? undefined : 1, ...(mine && {
58
41
  bg: '$color3',
59
42
  rounded: '$5',
60
43
  px: '$3',
61
44
  py: '$2',
62
- }), children: [(0, ink_1.ink)(children), busy ? (0, jsx_runtime_1.jsx)(Caret, {}) : null, actions ? (0, jsx_runtime_1.jsx)(gui_1.XStack, { gap: "$1", items: "center", children: actions }) : null] })] }));
45
+ }), ...body, children: [(0, ink_1.ink)(children), busy ? (0, jsx_runtime_1.jsx)(Caret, {}) : null, actions ? (0, jsx_runtime_1.jsx)(gui_1.XStack, { gap: "$1", items: "center", children: actions }) : null] })] }));
63
46
  }
@@ -1,22 +1,29 @@
1
- import type { ReactNode } from 'react';
2
- export type Role = 'user' | 'assistant' | 'system';
3
1
  /**
4
- * The mark that says a turn is still arriving.
2
+ * Message — one turn in a thread.
3
+ *
4
+ * Role drives presentation and nothing else: a user turn is a contained bubble
5
+ * that shrinks to its content, an assistant turn is full-bleed prose, a system
6
+ * turn is a muted aside. The contrast is what says who is speaking, so there is
7
+ * no name and no timestamp, and `icon` is opt-in.
5
8
  *
6
- * Placed by whoever knows where the prose ends. A surface rendering markdown
7
- * owns that node and can append this inside it; `Message busy` puts it at the
8
- * end of the turn instead, which is all this package can know.
9
+ * Content is `children`: the markdown pipeline stays with the surface.
10
+ */
11
+ import { XStack } from '@hanzo/gui';
12
+ import type { ComponentProps, ReactNode } from 'react';
13
+ export type Role = 'user' | 'assistant' | 'system';
14
+ /** What a gui stack accepts. `role` is dropped: here it names the speaker, not
15
+ * an ARIA role. `aria-label` still reaches the row. */
16
+ type Stack = Omit<ComponentProps<typeof XStack>, 'children' | 'role'>;
17
+ /**
18
+ * The mark that says a turn is still arriving. Static, not blinking.
9
19
  *
10
- * It does not blink. Every surface that ships one draws it static — an 8x16
11
- * block on hanzo.app `/chat`, a 2px caret in the builder, a 12px `⬤` from
12
- * hanzo/chat's `.result-streaming::after`. A caret is the narrowest of the
13
- * three and the one that reads as "the sentence continues" rather than as a
14
- * bullet. Decorative, so it is hidden from assistive tech: the turn's text is
15
- * already live-announced by the surface, and a cursor announced as content is
16
- * noise on every token.
20
+ * Placed by whoever knows where the prose ends: a surface rendering markdown
21
+ * owns that node and can append this inside it, and `Message busy` puts it at
22
+ * the end of the turn. Decorative, so assistive tech skips it — the surface
23
+ * already live-announces the text.
17
24
  */
18
25
  export declare function Caret(): import("react/jsx-runtime").JSX.Element;
19
- export interface MessageProps {
26
+ export interface MessageProps extends Stack {
20
27
  role: Role;
21
28
  children?: ReactNode;
22
29
  /** Row rendered under the turn — copy, retry, feedback. */
@@ -25,6 +32,10 @@ export interface MessageProps {
25
32
  busy?: boolean;
26
33
  /** Leading affordance for an assistant turn: avatar or model mark. */
27
34
  icon?: ReactNode;
35
+ /** The bubble's own props — the inner box, not the row. Reaches the fill, the
36
+ * corner and the width cap. Absent, nothing changes. */
37
+ body?: Stack;
28
38
  }
29
- export declare function Message({ role, children, actions, busy, icon }: MessageProps): import("react/jsx-runtime").JSX.Element;
39
+ export declare function Message({ role, children, actions, busy, icon, body, ...props }: MessageProps): import("react/jsx-runtime").JSX.Element;
40
+ export {};
30
41
  //# sourceMappingURL=Message.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../src/chat/Message.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAKtC,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;AAElD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,KAAK,4CAEpB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,IAAI,CAAA;IACV,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,sEAAsE;IACtE,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB;AAED,wBAAgB,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAY,EAAE,IAAI,EAAE,EAAE,YAAY,2CA6CpF"}
1
+ {"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../src/chat/Message.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,OAAO,EAAQ,MAAM,EAAU,MAAM,YAAY,CAAA;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAKtD,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;AAElD;wDACwD;AACxD,KAAK,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC,CAAA;AAErE;;;;;;;GAOG;AACH,wBAAgB,KAAK,4CAEpB;AAED,MAAM,WAAW,YAAa,SAAQ,KAAK;IACzC,IAAI,EAAE,IAAI,CAAA;IACV,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,8DAA8D;IAC9D,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,sEAAsE;IACtE,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;6DACyD;IACzD,IAAI,CAAC,EAAE,KAAK,CAAA;CACb;AAED,wBAAgB,OAAO,CAAC,EACtB,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,IAAY,EACZ,IAAI,EACJ,IAAI,EACJ,GAAG,KAAK,EACT,EAAE,YAAY,2CAoDd"}
@@ -5,56 +5,39 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
5
  *
6
6
  * Role drives presentation and nothing else: a user turn is a contained bubble
7
7
  * that shrinks to its content, an assistant turn is full-bleed prose, a system
8
- * turn is a muted aside. Content is `children`, so the surface keeps its own
9
- * markdown pipeline — the surfaces disagree on remark plugins and none of that
10
- * belongs in a presentational package.
8
+ * turn is a muted aside. The contrast is what says who is speaking, so there is
9
+ * no name and no timestamp, and `icon` is opt-in.
11
10
  *
12
- * That arrangement is not this package's taste; it is what all six turn
13
- * renderers across the estate independently arrived at. hanzo/chat states it
14
- * outright in `client/src/components/Chat/Messages/ui/MessageRender.tsx:131`:
15
- * "the user's turn is a glass bubble on the right, the reply plain and full
16
- * width — the contrast is what says who is speaking." No avatar, no sender
17
- * name, no timestamp — which is why `icon` is opt-in and there is no `name`
18
- * prop. Only the read-only share view renders a name, and only one surface
19
- * renders a timestamp.
11
+ * Content is `children`: the markdown pipeline stays with the surface.
20
12
  */
21
13
  import { Text, XStack, YStack } from '@hanzo/gui';
22
14
  import { ink } from '../backends/gui/ink.js';
23
15
  import { slot } from '../backends/gui/slot.js';
24
16
  /**
25
- * The mark that says a turn is still arriving.
17
+ * The mark that says a turn is still arriving. Static, not blinking.
26
18
  *
27
- * Placed by whoever knows where the prose ends. A surface rendering markdown
28
- * owns that node and can append this inside it; `Message busy` puts it at the
29
- * end of the turn instead, which is all this package can know.
30
- *
31
- * It does not blink. Every surface that ships one draws it static — an 8x16
32
- * block on hanzo.app `/chat`, a 2px caret in the builder, a 12px `⬤` from
33
- * hanzo/chat's `.result-streaming::after`. A caret is the narrowest of the
34
- * three and the one that reads as "the sentence continues" rather than as a
35
- * bullet. Decorative, so it is hidden from assistive tech: the turn's text is
36
- * already live-announced by the surface, and a cursor announced as content is
37
- * noise on every token.
19
+ * Placed by whoever knows where the prose ends: a surface rendering markdown
20
+ * owns that node and can append this inside it, and `Message busy` puts it at
21
+ * the end of the turn. Decorative, so assistive tech skips it — the surface
22
+ * already live-announces the text.
38
23
  */
39
24
  export function Caret() {
40
25
  return _jsx(YStack, { ...slot('caret'), width: 2, height: 15, rounded: 1, bg: "$color12", "aria-hidden": true });
41
26
  }
42
- export function Message({ role, children, actions, busy = false, icon }) {
27
+ export function Message({ role, children, actions, busy = false, icon, body, ...props }) {
43
28
  if (role === 'system')
44
- return (_jsx(XStack, { ...slot('message'), "data-role": "system", width: "100%", justify: "center", py: "$2", children: _jsx(Text, { fontSize: "$1", color: "$color10", text: "center", children: children }) }));
29
+ return (_jsx(XStack, { ...slot('message'), "data-role": "system", width: "100%", justify: "center", py: "$2", ...props, children: _jsx(Text, { fontSize: "$1", color: "$color10", text: "center", children: children }) }));
45
30
  const mine = role === 'user';
46
- return (_jsxs(XStack, { ...slot('message'), "data-role": role, width: "100%", justify: mine ? 'flex-end' : 'flex-start', gap: "$2", children: [icon && !mine ? _jsx(YStack, { pt: "$1", children: icon }) : null, _jsxs(YStack, { ...slot('message-body'), "data-bubble": mine ? 'true' : undefined, gap: "$2",
47
- // 85%, because that is the number three independent implementations
48
- // reached: hanzo/chat's `USER_TURN` (`common/turn.ts:17`,
49
- // `max-w-[85%]`), hanzo.app `/chat` (`app/chat/page.tsx:521`,
50
- // `maxWidth="85%"`) and the app builder's `UserBubble`
51
- // (`ask-ai/chat-thread.tsx:141`). This package said 80% and no one
52
- // said 80%.
31
+ return (_jsxs(XStack, { ...slot('message'), "data-role": role, width: "100%", justify: mine ? 'flex-end' : 'flex-start', gap: "$2",
32
+ // Makes `actions` revealable with `$group-hover`. Unnamed: gui types
33
+ // `group` as a boolean, so a name does not compile.
34
+ group: true, ...props, children: [icon && !mine ? _jsx(YStack, { pt: "$1", children: icon }) : null, _jsxs(YStack, { ...slot('message-body'), "data-bubble": mine ? 'true' : undefined, gap: "$2",
35
+ // 85% is what the surfaces converged on for a user turn.
53
36
  maxW: mine ? '85%' : '100%', flex: mine ? undefined : 1, ...(mine && {
54
37
  bg: '$color3',
55
38
  rounded: '$5',
56
39
  px: '$3',
57
40
  py: '$2',
58
- }), children: [ink(children), busy ? _jsx(Caret, {}) : null, actions ? _jsx(XStack, { gap: "$1", items: "center", children: actions }) : null] })] }));
41
+ }), ...body, children: [ink(children), busy ? _jsx(Caret, {}) : null, actions ? _jsx(XStack, { gap: "$1", items: "center", children: actions }) : null] })] }));
59
42
  }
60
43
  //# sourceMappingURL=Message.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Message.js","sourceRoot":"","sources":["../../src/chat/Message.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAGjD,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAA;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAI3C;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,KAAK;IACnB,OAAO,KAAC,MAAM,OAAK,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,EAAC,UAAU,wBAAe,CAAA;AAClG,CAAC;AAaD,MAAM,UAAU,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,GAAG,KAAK,EAAE,IAAI,EAAgB;IACnF,IAAI,IAAI,KAAK,QAAQ;QACnB,OAAO,CACL,KAAC,MAAM,OAAK,IAAI,CAAC,SAAS,CAAC,eAAY,QAAQ,EAAC,KAAK,EAAC,MAAM,EAAC,OAAO,EAAC,QAAQ,EAAC,EAAE,EAAC,IAAI,YACnF,KAAC,IAAI,IAAC,QAAQ,EAAC,IAAI,EAAC,KAAK,EAAC,UAAU,EAAC,IAAI,EAAC,QAAQ,YAC/C,QAAQ,GACJ,GACA,CACV,CAAA;IAEH,MAAM,IAAI,GAAG,IAAI,KAAK,MAAM,CAAA;IAC5B,OAAO,CACL,MAAC,MAAM,OACD,IAAI,CAAC,SAAS,CAAC,eACR,IAAI,EACf,KAAK,EAAC,MAAM,EACZ,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,EACzC,GAAG,EAAC,IAAI,aAEP,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAC,MAAM,IAAC,EAAE,EAAC,IAAI,YAAE,IAAI,GAAU,CAAC,CAAC,CAAC,IAAI,EACvD,MAAC,MAAM,OACD,IAAI,CAAC,cAAc,CAAC,iBACX,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EACtC,GAAG,EAAC,IAAI;gBACR,oEAAoE;gBACpE,0DAA0D;gBAC1D,8DAA8D;gBAC9D,uDAAuD;gBACvD,mEAAmE;gBACnE,YAAY;gBACZ,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAC3B,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KACtB,CAAC,IAAI,IAAI;oBACX,EAAE,EAAE,SAAS;oBACb,OAAO,EAAE,IAAI;oBACb,EAAE,EAAE,IAAI;oBACR,EAAE,EAAE,IAAI;iBACT,CAAC,aAED,GAAG,CAAC,QAAQ,CAAC,EACb,IAAI,CAAC,CAAC,CAAC,KAAC,KAAK,KAAG,CAAC,CAAC,CAAC,IAAI,EACvB,OAAO,CAAC,CAAC,CAAC,KAAC,MAAM,IAAC,GAAG,EAAC,IAAI,EAAC,KAAK,EAAC,QAAQ,YAAE,OAAO,GAAU,CAAC,CAAC,CAAC,IAAI,IAC7D,IACF,CACV,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"Message.js","sourceRoot":"","sources":["../../src/chat/Message.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;GASG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAGjD,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAA;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAQ3C;;;;;;;GAOG;AACH,MAAM,UAAU,KAAK;IACnB,OAAO,KAAC,MAAM,OAAK,IAAI,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,EAAC,UAAU,wBAAe,CAAA;AAClG,CAAC;AAgBD,MAAM,UAAU,OAAO,CAAC,EACtB,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,IAAI,GAAG,KAAK,EACZ,IAAI,EACJ,IAAI,EACJ,GAAG,KAAK,EACK;IACb,IAAI,IAAI,KAAK,QAAQ;QACnB,OAAO,CACL,KAAC,MAAM,OACD,IAAI,CAAC,SAAS,CAAC,eACT,QAAQ,EAClB,KAAK,EAAC,MAAM,EACZ,OAAO,EAAC,QAAQ,EAChB,EAAE,EAAC,IAAI,KACH,KAAK,YAET,KAAC,IAAI,IAAC,QAAQ,EAAC,IAAI,EAAC,KAAK,EAAC,UAAU,EAAC,IAAI,EAAC,QAAQ,YAC/C,QAAQ,GACJ,GACA,CACV,CAAA;IAEH,MAAM,IAAI,GAAG,IAAI,KAAK,MAAM,CAAA;IAC5B,OAAO,CACL,MAAC,MAAM,OACD,IAAI,CAAC,SAAS,CAAC,eACR,IAAI,EACf,KAAK,EAAC,MAAM,EACZ,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,EACzC,GAAG,EAAC,IAAI;QACR,qEAAqE;QACrE,oDAAoD;QACpD,KAAK,WACD,KAAK,aAER,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,KAAC,MAAM,IAAC,EAAE,EAAC,IAAI,YAAE,IAAI,GAAU,CAAC,CAAC,CAAC,IAAI,EACvD,MAAC,MAAM,OACD,IAAI,CAAC,cAAc,CAAC,iBACX,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EACtC,GAAG,EAAC,IAAI;gBACR,yDAAyD;gBACzD,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAC3B,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KACtB,CAAC,IAAI,IAAI;oBACX,EAAE,EAAE,SAAS;oBACb,OAAO,EAAE,IAAI;oBACb,EAAE,EAAE,IAAI;oBACR,EAAE,EAAE,IAAI;iBACT,CAAC,KACE,IAAI,aAEP,GAAG,CAAC,QAAQ,CAAC,EACb,IAAI,CAAC,CAAC,CAAC,KAAC,KAAK,KAAG,CAAC,CAAC,CAAC,IAAI,EACvB,OAAO,CAAC,CAAC,CAAC,KAAC,MAAM,IAAC,GAAG,EAAC,IAAI,EAAC,KAAK,EAAC,QAAQ,YAAE,OAAO,GAAU,CAAC,CAAC,CAAC,IAAI,IAC7D,IACF,CACV,CAAA;AACH,CAAC"}
@@ -2,24 +2,29 @@
2
2
  'use client';
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.Sources = Sources;
5
- exports.SourceChip = SourceChip;
5
+ exports.SourceCard = SourceCard;
6
6
  const jsx_runtime_1 = require("react/jsx-runtime");
7
7
  /**
8
- * Sources — the citation grid for a turn.
8
+ * Sources — the citations for a turn, as a strip of cards.
9
9
  *
10
- * A dense grid of marks rather than a list of titles. Sources are scanned for
11
- * "which of these do I already trust", and twenty full-width titles would push
12
- * the rail well past a screen. Each mark keeps its title as accessible name and
13
- * tooltip, so the information is reordered, not discarded.
10
+ * Each card leads with the host, which is what a reader scans to decide whether
11
+ * they trust a source; the title is the tiebreaker under it. The strip scrolls
12
+ * sideways so it stays one row tall at any count.
13
+ *
14
+ * The number matches a marker in the prose. Minting that marker belongs to the
15
+ * markdown pipeline, which stays with the surface.
14
16
  */
15
17
  const gui_1 = require("@hanzo/gui");
16
18
  const slot_1 = require("../backends/gui/slot.cjs");
17
- function Sources({ sources, title = 'Sources', onOpen }) {
19
+ /** Card width, px. Fixed, so a strip scans as a row rather than ragged columns. */
20
+ const CARD = 168;
21
+ function Sources({ sources, title = 'Sources', onOpen, ...props }) {
18
22
  if (sources.length === 0)
19
23
  return null;
20
- return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('sources'), gap: "$2", p: "$3", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", children: [(0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$1.5", children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", fontWeight: "500", color: "$color10", children: title }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", color: "$color9", children: sources.length })] }), (0, jsx_runtime_1.jsx)(gui_1.XStack, { flexWrap: "wrap", gap: "$1.5", children: sources.map((source) => ((0, jsx_runtime_1.jsx)(SourceChip, { source: source, onOpen: onOpen }, source.id))) })] }));
24
+ return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('sources'), gap: "$2", ...props, children: [(0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$1.5", children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", fontWeight: "500", color: "$color10", children: title }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", color: "$color9", children: sources.length })] }), (0, jsx_runtime_1.jsx)(gui_1.XStack, { ...(0, slot_1.slot)('sources-strip'), overflow: "scroll", contain: "content", gap: "$2", pb: "$1", children: sources.map((source, i) => ((0, jsx_runtime_1.jsx)(SourceCard, { source: source, index: i + 1, onOpen: onOpen }, source.id))) })] }));
21
25
  }
22
- function SourceChip({ source, onOpen }) {
26
+ function SourceCard({ source, index, onOpen, ...props }) {
23
27
  const press = onOpen ? () => onOpen(source) : undefined;
24
- return ((0, jsx_runtime_1.jsx)(gui_1.XStack, { ...(0, slot_1.slot)('source-chip'), width: 28, height: 28, items: "center", justify: "center", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", bg: "$color3", overflow: "hidden", cursor: press ? 'pointer' : undefined, onPress: press, role: press ? 'button' : undefined, tabIndex: press ? 0 : undefined, "aria-label": source.title, ...(0, slot_1.tip)(source.title), hoverStyle: press ? { borderColor: '$color8' } : undefined, pressStyle: press ? { bg: '$color5' } : undefined, children: source.icon ?? ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", color: "$color11", children: source.title.charAt(0).toUpperCase() })) }));
28
+ const label = source.host ?? source.title;
29
+ return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('source-card'), width: CARD, shrink: 0, gap: "$1.5", p: "$2.5", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$color2", cursor: press ? 'pointer' : undefined, onPress: press, role: press ? 'button' : undefined, tabIndex: press ? 0 : undefined, "aria-label": source.title, ...(0, slot_1.tip)(source.title), hoverStyle: press ? { borderColor: '$color8' } : undefined, pressStyle: press ? { bg: '$color4' } : undefined, ...props, children: [(0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: "$1.5", children: [index != null ? ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", color: "$color9", children: index })) : null, (0, jsx_runtime_1.jsx)(gui_1.XStack, { width: 14, height: 14, shrink: 0, items: "center", justify: "center", rounded: "$1", overflow: "hidden", bg: "$color4", children: source.icon ?? ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", color: "$color11", children: label.charAt(0).toUpperCase() })) }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", color: "$color10", numberOfLines: 1, children: label })] }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", color: "$color12", numberOfLines: 3, children: source.title })] }));
25
30
  }
@@ -1,23 +1,43 @@
1
- import type { ReactNode } from 'react';
1
+ /**
2
+ * Sources — the citations for a turn, as a strip of cards.
3
+ *
4
+ * Each card leads with the host, which is what a reader scans to decide whether
5
+ * they trust a source; the title is the tiebreaker under it. The strip scrolls
6
+ * sideways so it stays one row tall at any count.
7
+ *
8
+ * The number matches a marker in the prose. Minting that marker belongs to the
9
+ * markdown pipeline, which stays with the surface.
10
+ */
11
+ import { YStack } from '@hanzo/gui';
12
+ import type { ComponentProps, ReactNode } from 'react';
2
13
  export interface Source {
3
14
  id: string;
4
15
  /** Accessible name and tooltip — a title, repo or file path. */
5
16
  title: string;
6
17
  /** Opened on press. Surfaces without navigation can omit it. */
7
18
  href?: string;
8
- /** Favicon or mark. Falls back to the first letter of `title`. */
19
+ /** Favicon or mark. Falls back to the first letter of `host` or `title`. */
9
20
  icon?: ReactNode;
21
+ /**
22
+ * Who published it — `nytimes.com`, a repo, a filename.
23
+ *
24
+ * Derived by the surface: a `new URL()` here would be wrong for a source that
25
+ * is a file path rather than a link.
26
+ */
27
+ host?: string;
10
28
  }
11
- export interface SourcesProps {
29
+ export interface SourcesProps extends Omit<ComponentProps<typeof YStack>, 'children'> {
12
30
  sources: Source[];
13
31
  title?: string;
14
32
  /** Raised on press. Given `href`, a surface may navigate instead. */
15
33
  onOpen?: (source: Source) => void;
16
34
  }
17
- export declare function Sources({ sources, title, onOpen }: SourcesProps): import("react/jsx-runtime").JSX.Element | null;
18
- export interface SourceChipProps {
35
+ export declare function Sources({ sources, title, onOpen, ...props }: SourcesProps): import("react/jsx-runtime").JSX.Element | null;
36
+ export interface SourceCardProps extends Omit<ComponentProps<typeof YStack>, 'children' | 'role'> {
19
37
  source: Source;
38
+ /** Position in the list, 1-based — the number the prose's marker refers to. */
39
+ index?: number;
20
40
  onOpen?: (source: Source) => void;
21
41
  }
22
- export declare function SourceChip({ source, onOpen }: SourceChipProps): import("react/jsx-runtime").JSX.Element;
42
+ export declare function SourceCard({ source, index, onOpen, ...props }: SourceCardProps): import("react/jsx-runtime").JSX.Element;
23
43
  //# sourceMappingURL=Sources.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Sources.d.ts","sourceRoot":"","sources":["../../src/chat/Sources.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAA;IACV,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAA;IACb,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,IAAI,CAAC,EAAE,SAAS,CAAA;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;CAClC;AAED,wBAAgB,OAAO,CAAC,EAAE,OAAO,EAAE,KAAiB,EAAE,MAAM,EAAE,EAAE,YAAY,kDAqB3E;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;CAClC;AAED,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,eAAe,2CA+B7D"}
1
+ {"version":3,"file":"Sources.d.ts","sourceRoot":"","sources":["../../src/chat/Sources.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,OAAO,EAAuB,MAAM,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAOtD,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAA;IACV,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAA;IACb,gEAAgE;IAChE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,4EAA4E;IAC5E,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,UAAU,CAAC;IACnF,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;CAClC;AAED,wBAAgB,OAAO,CAAC,EAAE,OAAO,EAAE,KAAiB,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,EAAE,YAAY,kDA6BrF;AAED,MAAM,WAAW,eAAgB,SAAQ,IAAI,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAC/F,MAAM,EAAE,MAAM,CAAA;IACd,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;CAClC;AAED,wBAAgB,UAAU,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,2CA2D9E"}
@@ -1,22 +1,27 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  /**
4
- * Sources — the citation grid for a turn.
4
+ * Sources — the citations for a turn, as a strip of cards.
5
5
  *
6
- * A dense grid of marks rather than a list of titles. Sources are scanned for
7
- * "which of these do I already trust", and twenty full-width titles would push
8
- * the rail well past a screen. Each mark keeps its title as accessible name and
9
- * tooltip, so the information is reordered, not discarded.
6
+ * Each card leads with the host, which is what a reader scans to decide whether
7
+ * they trust a source; the title is the tiebreaker under it. The strip scrolls
8
+ * sideways so it stays one row tall at any count.
9
+ *
10
+ * The number matches a marker in the prose. Minting that marker belongs to the
11
+ * markdown pipeline, which stays with the surface.
10
12
  */
11
13
  import { SizableText, XStack, YStack } from '@hanzo/gui';
12
14
  import { slot, tip } from '../backends/gui/slot.js';
13
- export function Sources({ sources, title = 'Sources', onOpen }) {
15
+ /** Card width, px. Fixed, so a strip scans as a row rather than ragged columns. */
16
+ const CARD = 168;
17
+ export function Sources({ sources, title = 'Sources', onOpen, ...props }) {
14
18
  if (sources.length === 0)
15
19
  return null;
16
- return (_jsxs(YStack, { ...slot('sources'), gap: "$2", p: "$3", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", children: [_jsxs(XStack, { items: "center", gap: "$1.5", children: [_jsx(SizableText, { size: "$1", fontWeight: "500", color: "$color10", children: title }), _jsx(SizableText, { size: "$1", color: "$color9", children: sources.length })] }), _jsx(XStack, { flexWrap: "wrap", gap: "$1.5", children: sources.map((source) => (_jsx(SourceChip, { source: source, onOpen: onOpen }, source.id))) })] }));
20
+ return (_jsxs(YStack, { ...slot('sources'), gap: "$2", ...props, children: [_jsxs(XStack, { items: "center", gap: "$1.5", children: [_jsx(SizableText, { size: "$1", fontWeight: "500", color: "$color10", children: title }), _jsx(SizableText, { size: "$1", color: "$color9", children: sources.length })] }), _jsx(XStack, { ...slot('sources-strip'), overflow: "scroll", contain: "content", gap: "$2", pb: "$1", children: sources.map((source, i) => (_jsx(SourceCard, { source: source, index: i + 1, onOpen: onOpen }, source.id))) })] }));
17
21
  }
18
- export function SourceChip({ source, onOpen }) {
22
+ export function SourceCard({ source, index, onOpen, ...props }) {
19
23
  const press = onOpen ? () => onOpen(source) : undefined;
20
- return (_jsx(XStack, { ...slot('source-chip'), width: 28, height: 28, items: "center", justify: "center", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", bg: "$color3", overflow: "hidden", cursor: press ? 'pointer' : undefined, onPress: press, role: press ? 'button' : undefined, tabIndex: press ? 0 : undefined, "aria-label": source.title, ...tip(source.title), hoverStyle: press ? { borderColor: '$color8' } : undefined, pressStyle: press ? { bg: '$color5' } : undefined, children: source.icon ?? (_jsx(SizableText, { size: "$1", color: "$color11", children: source.title.charAt(0).toUpperCase() })) }));
24
+ const label = source.host ?? source.title;
25
+ return (_jsxs(YStack, { ...slot('source-card'), width: CARD, shrink: 0, gap: "$1.5", p: "$2.5", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$color2", cursor: press ? 'pointer' : undefined, onPress: press, role: press ? 'button' : undefined, tabIndex: press ? 0 : undefined, "aria-label": source.title, ...tip(source.title), hoverStyle: press ? { borderColor: '$color8' } : undefined, pressStyle: press ? { bg: '$color4' } : undefined, ...props, children: [_jsxs(XStack, { items: "center", gap: "$1.5", children: [index != null ? (_jsx(SizableText, { size: "$1", color: "$color9", children: index })) : null, _jsx(XStack, { width: 14, height: 14, shrink: 0, items: "center", justify: "center", rounded: "$1", overflow: "hidden", bg: "$color4", children: source.icon ?? (_jsx(SizableText, { size: "$1", color: "$color11", children: label.charAt(0).toUpperCase() })) }), _jsx(SizableText, { size: "$1", color: "$color10", numberOfLines: 1, children: label })] }), _jsx(SizableText, { size: "$1", color: "$color12", numberOfLines: 3, children: source.title })] }));
21
26
  }
22
27
  //# sourceMappingURL=Sources.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Sources.js","sourceRoot":"","sources":["../../src/chat/Sources.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAGxD,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAA;AAmBhD,MAAM,UAAU,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,GAAG,SAAS,EAAE,MAAM,EAAgB;IAC1E,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAErC,OAAO,CACL,MAAC,MAAM,OAAK,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAC,IAAI,EAAC,CAAC,EAAC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,WAAW,EAAE,CAAC,EAAE,WAAW,EAAC,cAAc,aAClG,MAAC,MAAM,IAAC,KAAK,EAAC,QAAQ,EAAC,GAAG,EAAC,MAAM,aAC/B,KAAC,WAAW,IAAC,IAAI,EAAC,IAAI,EAAC,UAAU,EAAC,KAAK,EAAC,KAAK,EAAC,UAAU,YACrD,KAAK,GACM,EACd,KAAC,WAAW,IAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC,SAAS,YACnC,OAAO,CAAC,MAAM,GACH,IACP,EAET,KAAC,MAAM,IAAC,QAAQ,EAAC,MAAM,EAAC,GAAG,EAAC,MAAM,YAC/B,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CACvB,KAAC,UAAU,IAAiB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,IAAzC,MAAM,CAAC,EAAE,CAAoC,CAC/D,CAAC,GACK,IACF,CACV,CAAA;AACH,CAAC;AAOD,MAAM,UAAU,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,EAAmB;IAC5D,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAEvD,OAAO,CACL,KAAC,MAAM,OACD,IAAI,CAAC,aAAa,CAAC,EACvB,KAAK,EAAE,EAAE,EACT,MAAM,EAAE,EAAE,EACV,KAAK,EAAC,QAAQ,EACd,OAAO,EAAC,QAAQ,EAChB,OAAO,EAAC,IAAI,EACZ,WAAW,EAAE,CAAC,EACd,WAAW,EAAC,cAAc,EAC1B,EAAE,EAAC,SAAS,EACZ,QAAQ,EAAC,QAAQ,EACjB,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EACrC,OAAO,EAAE,KAAK,EACd,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAClC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,gBACnB,MAAM,CAAC,KAAK,KACpB,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,EACrB,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,EAC1D,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,YAEhD,MAAM,CAAC,IAAI,IAAI,CACd,KAAC,WAAW,IAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC,UAAU,YACpC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GACzB,CACf,GACM,CACV,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"Sources.js","sourceRoot":"","sources":["../../src/chat/Sources.tsx"],"names":[],"mappings":"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"}