@hanzo/ui 8.1.3 → 8.1.5

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 (78) hide show
  1. package/dist/backends/gui/textarea.cjs +35 -3
  2. package/dist/backends/gui/textarea.d.ts +34 -5
  3. package/dist/backends/gui/textarea.d.ts.map +1 -1
  4. package/dist/backends/gui/textarea.js +35 -3
  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 +16 -10
  12. package/dist/chat/Composer.d.ts +59 -3
  13. package/dist/chat/Composer.d.ts.map +1 -1
  14. package/dist/chat/Composer.js +16 -10
  15. package/dist/chat/Composer.js.map +1 -1
  16. package/dist/chat/Failure.cjs +2 -2
  17. package/dist/chat/Failure.d.ts +29 -3
  18. package/dist/chat/Failure.d.ts.map +1 -1
  19. package/dist/chat/Failure.js +2 -2
  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 +10 -4
  27. package/dist/chat/Message.d.ts +42 -3
  28. package/dist/chat/Message.d.ts.map +1 -1
  29. package/dist/chat/Message.js +10 -4
  30. package/dist/chat/Message.js.map +1 -1
  31. package/dist/chat/Sources.cjs +33 -10
  32. package/dist/chat/Sources.d.ts +46 -6
  33. package/dist/chat/Sources.d.ts.map +1 -1
  34. package/dist/chat/Sources.js +32 -9
  35. package/dist/chat/Sources.js.map +1 -1
  36. package/dist/chat/Step.cjs +2 -2
  37. package/dist/chat/Step.d.ts +4 -3
  38. package/dist/chat/Step.d.ts.map +1 -1
  39. package/dist/chat/Step.js +2 -2
  40. package/dist/chat/Step.js.map +1 -1
  41. package/dist/chat/Thread.cjs +38 -13
  42. package/dist/chat/Thread.d.ts +55 -3
  43. package/dist/chat/Thread.d.ts.map +1 -1
  44. package/dist/chat/Thread.js +38 -13
  45. package/dist/chat/Thread.js.map +1 -1
  46. package/dist/chat/index.cjs +19 -2
  47. package/dist/chat/index.d.ts +18 -1
  48. package/dist/chat/index.d.ts.map +1 -1
  49. package/dist/chat/index.js +18 -1
  50. package/dist/chat/index.js.map +1 -1
  51. package/dist/chat/pure.cjs +41 -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 +35 -0
  55. package/dist/chat/pure.js.map +1 -0
  56. package/dist/chat/send.cjs +10 -1
  57. package/dist/chat/send.d.ts +9 -0
  58. package/dist/chat/send.d.ts.map +1 -1
  59. package/dist/chat/send.js +10 -1
  60. package/dist/chat/send.js.map +1 -1
  61. package/dist/chat/stick.cjs +1 -1
  62. package/dist/chat/stick.js +1 -1
  63. package/dist/gallery.cjs +5 -4
  64. package/dist/gallery.d.ts.map +1 -1
  65. package/dist/gallery.js +5 -4
  66. package/dist/gallery.js.map +1 -1
  67. package/dist/product/CopyButton.cjs +2 -2
  68. package/dist/product/CopyButton.d.ts +12 -2
  69. package/dist/product/CopyButton.d.ts.map +1 -1
  70. package/dist/product/CopyButton.js +2 -2
  71. package/dist/product/CopyButton.js.map +1 -1
  72. package/dist/product/pure.cjs +12 -1
  73. package/dist/product/pure.d.ts +2 -0
  74. package/dist/product/pure.d.ts.map +1 -1
  75. package/dist/product/pure.js +7 -0
  76. package/dist/product/pure.js.map +1 -1
  77. package/dist/styles.css +14 -2
  78. package/package.json +7 -1
@@ -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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"}
@@ -43,11 +43,17 @@ const slot_1 = require("../backends/gui/slot.cjs");
43
43
  function Caret() {
44
44
  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
45
  }
46
- function Message({ role, children, actions, busy = false, icon }) {
46
+ function Message({ role, children, actions, busy = false, icon, body, ...props }) {
47
47
  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 }) }));
48
+ 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
49
  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",
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",
51
+ // The row is a hover group, so an action strip can be revealed with
52
+ // `$group-hover` instead of by a wrapper each surface adds itself. Both
53
+ // hanzo/chat and hanzo.app reveal theirs on hover and both had to supply
54
+ // the group. Unnamed on purpose: gui types `group` as a BOOLEAN here, so
55
+ // `group="turn"` type-errors rather than registering a named group.
56
+ 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",
51
57
  // 85%, because that is the number three independent implementations
52
58
  // reached: hanzo/chat's `USER_TURN` (`common/turn.ts:17`,
53
59
  // `max-w-[85%]`), hanzo.app `/chat` (`app/chat/page.tsx:521`,
@@ -59,5 +65,5 @@ function Message({ role, children, actions, busy = false, icon }) {
59
65
  rounded: '$5',
60
66
  px: '$3',
61
67
  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] })] }));
68
+ }), ...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
69
  }
@@ -1,5 +1,32 @@
1
- import type { ReactNode } from 'react';
1
+ /**
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. Content is `children`, so the surface keeps its own
7
+ * markdown pipeline — the surfaces disagree on remark plugins and none of that
8
+ * belongs in a presentational package.
9
+ *
10
+ * That arrangement is not this package's taste; it is what all six turn
11
+ * renderers across the estate independently arrived at. hanzo/chat states it
12
+ * outright in `client/src/components/Chat/Messages/ui/MessageRender.tsx:131`:
13
+ * "the user's turn is a glass bubble on the right, the reply plain and full
14
+ * width — the contrast is what says who is speaking." No avatar, no sender
15
+ * name, no timestamp — which is why `icon` is opt-in and there is no `name`
16
+ * prop. Only the read-only share view renders a name, and only one surface
17
+ * renders a timestamp.
18
+ */
19
+ import { XStack } from '@hanzo/gui';
20
+ import type { ComponentProps, ReactNode } from 'react';
2
21
  export type Role = 'user' | 'assistant' | 'system';
22
+ /**
23
+ * What a gui stack accepts — id, className, style, the `$` style props.
24
+ *
25
+ * `role` is dropped because this component already has one and means something
26
+ * else by it: the ARIA role of the row is not the speaker of the turn. The row
27
+ * needs no ARIA role, and `aria-label` is still reachable for the name.
28
+ */
29
+ type Stack = Omit<ComponentProps<typeof XStack>, 'children' | 'role'>;
3
30
  /**
4
31
  * The mark that says a turn is still arriving.
5
32
  *
@@ -16,7 +43,7 @@ export type Role = 'user' | 'assistant' | 'system';
16
43
  * noise on every token.
17
44
  */
18
45
  export declare function Caret(): import("react/jsx-runtime").JSX.Element;
19
- export interface MessageProps {
46
+ export interface MessageProps extends Stack {
20
47
  role: Role;
21
48
  children?: ReactNode;
22
49
  /** Row rendered under the turn — copy, retry, feedback. */
@@ -25,6 +52,18 @@ export interface MessageProps {
25
52
  busy?: boolean;
26
53
  /** Leading affordance for an assistant turn: avatar or model mark. */
27
54
  icon?: ReactNode;
55
+ /**
56
+ * The bubble's own props — the inner box, not the row.
57
+ *
58
+ * The same reason `DialogContent` grew `overlay`: this component owns the
59
+ * body so a caller never has to assemble one, but owning is not hiding, and
60
+ * with nothing through, the fill, the corner and the width cap were
61
+ * unreachable from outside the package. hanzo/chat's turn is `glass hz-turn`
62
+ * with a `color-mix` fill it applies fleet-wide (`style.css:389`); it cannot
63
+ * adopt a bubble welded to `$color3`. Absent, nothing changes.
64
+ */
65
+ body?: Stack;
28
66
  }
29
- export declare function Message({ role, children, actions, busy, icon }: MessageProps): import("react/jsx-runtime").JSX.Element;
67
+ export declare function Message({ role, children, actions, busy, icon, body, ...props }: MessageProps): import("react/jsx-runtime").JSX.Element;
68
+ export {};
30
69
  //# 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;;;;;;;;;;;;;;;;;GAiBG;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;;;;;;GAMG;AACH,KAAK,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC,CAAA;AAErE;;;;;;;;;;;;;;GAcG;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;;;;;;;;;OASG;IACH,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,2CA4Dd"}
@@ -39,11 +39,17 @@ import { slot } from '../backends/gui/slot.js';
39
39
  export function Caret() {
40
40
  return _jsx(YStack, { ...slot('caret'), width: 2, height: 15, rounded: 1, bg: "$color12", "aria-hidden": true });
41
41
  }
42
- export function Message({ role, children, actions, busy = false, icon }) {
42
+ export function Message({ role, children, actions, busy = false, icon, body, ...props }) {
43
43
  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 }) }));
44
+ 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
45
  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",
46
+ return (_jsxs(XStack, { ...slot('message'), "data-role": role, width: "100%", justify: mine ? 'flex-end' : 'flex-start', gap: "$2",
47
+ // The row is a hover group, so an action strip can be revealed with
48
+ // `$group-hover` instead of by a wrapper each surface adds itself. Both
49
+ // hanzo/chat and hanzo.app reveal theirs on hover and both had to supply
50
+ // the group. Unnamed on purpose: gui types `group` as a BOOLEAN here, so
51
+ // `group="turn"` type-errors rather than registering a named group.
52
+ 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",
47
53
  // 85%, because that is the number three independent implementations
48
54
  // reached: hanzo/chat's `USER_TURN` (`common/turn.ts:17`,
49
55
  // `max-w-[85%]`), hanzo.app `/chat` (`app/chat/page.tsx:521`,
@@ -55,6 +61,6 @@ export function Message({ role, children, actions, busy = false, icon }) {
55
61
  rounded: '$5',
56
62
  px: '$3',
57
63
  py: '$2',
58
- }), children: [ink(children), busy ? _jsx(Caret, {}) : null, actions ? _jsx(XStack, { gap: "$1", items: "center", children: actions }) : null] })] }));
64
+ }), ...body, children: [ink(children), busy ? _jsx(Caret, {}) : null, actions ? _jsx(XStack, { gap: "$1", items: "center", children: actions }) : null] })] }));
59
65
  }
60
66
  //# 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;;;;;;;;;;;;;;;;;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;AAa3C;;;;;;;;;;;;;;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;AAwBD,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,oEAAoE;QACpE,wEAAwE;QACxE,yEAAyE;QACzE,yEAAyE;QACzE,oEAAoE;QACpE,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,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,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,47 @@
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
+ * This used to be a wrapping grid of 28px marks with the title reachable only
11
+ * as a tooltip, and the argument for it was density: sources are scanned for
12
+ * "which of these do I already trust", and twenty full-width titles push the
13
+ * rail past a screen. The density argument is right. Hiding the HOST was not,
14
+ * because the host is the answer to that exact question — a favicon is sixteen
15
+ * pixels of ambiguity and `nytimes.com` is a verdict — and neither surface that
16
+ * ships citations hides it. hanzo/chat's `SourceItem` (`components/Web/
17
+ * Sources.tsx`) draws favicon, domain, title and a snippet; the extension's
18
+ * `answer/Source.tsx:9-26` draws a number, a favicon, the host and a
19
+ * three-line-clamped title, and its own comment records where that came from:
20
+ * "Source card — chat AnswerView: 160px, number pill first, favicon, host,
21
+ * 3-line title".
22
+ *
23
+ * So density is kept by scrolling sideways rather than by discarding the text —
24
+ * the strip is one row tall whatever the count, which is what the grid was
25
+ * really buying. Both surfaces already scroll: `.ae-sources` is
26
+ * `display:flex; overflow-x:auto` at `newtab.css:260`.
27
+ *
28
+ * The number is not decoration. Both surfaces number the cards to match a
29
+ * marker in the prose, and the marker itself is not here: chat mints it with a
30
+ * remark plugin over private-use codepoints (`components/Web/plugin.ts`), which
31
+ * belongs to the markdown pipeline that stays with the surface.
14
32
  */
15
33
  const gui_1 = require("@hanzo/gui");
16
34
  const slot_1 = require("../backends/gui/slot.cjs");
17
- function Sources({ sources, title = 'Sources', onOpen }) {
35
+ /** Card width, px. The extension's is 168 and it cites chat's 160; the point of
36
+ * a fixed width is that a strip of them scans as a row rather than as ragged
37
+ * columns, so it is one number and not a range. */
38
+ const CARD = 168;
39
+ function Sources({ sources, title = 'Sources', onOpen, ...props }) {
18
40
  if (sources.length === 0)
19
41
  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))) })] }));
42
+ 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
43
  }
22
- function SourceChip({ source, onOpen }) {
44
+ function SourceCard({ source, index, onOpen, ...props }) {
23
45
  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() })) }));
46
+ const label = source.host ?? source.title;
47
+ 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
48
  }
@@ -1,23 +1,63 @@
1
- import type { ReactNode } from 'react';
1
+ /**
2
+ * Sources — the citations for a turn, as a strip of cards.
3
+ *
4
+ * This used to be a wrapping grid of 28px marks with the title reachable only
5
+ * as a tooltip, and the argument for it was density: sources are scanned for
6
+ * "which of these do I already trust", and twenty full-width titles push the
7
+ * rail past a screen. The density argument is right. Hiding the HOST was not,
8
+ * because the host is the answer to that exact question — a favicon is sixteen
9
+ * pixels of ambiguity and `nytimes.com` is a verdict — and neither surface that
10
+ * ships citations hides it. hanzo/chat's `SourceItem` (`components/Web/
11
+ * Sources.tsx`) draws favicon, domain, title and a snippet; the extension's
12
+ * `answer/Source.tsx:9-26` draws a number, a favicon, the host and a
13
+ * three-line-clamped title, and its own comment records where that came from:
14
+ * "Source card — chat AnswerView: 160px, number pill first, favicon, host,
15
+ * 3-line title".
16
+ *
17
+ * So density is kept by scrolling sideways rather than by discarding the text —
18
+ * the strip is one row tall whatever the count, which is what the grid was
19
+ * really buying. Both surfaces already scroll: `.ae-sources` is
20
+ * `display:flex; overflow-x:auto` at `newtab.css:260`.
21
+ *
22
+ * The number is not decoration. Both surfaces number the cards to match a
23
+ * marker in the prose, and the marker itself is not here: chat mints it with a
24
+ * remark plugin over private-use codepoints (`components/Web/plugin.ts`), which
25
+ * belongs to the markdown pipeline that stays with the surface.
26
+ */
27
+ import { YStack } from '@hanzo/gui';
28
+ import type { ComponentProps, ReactNode } from 'react';
2
29
  export interface Source {
3
30
  id: string;
4
31
  /** Accessible name and tooltip — a title, repo or file path. */
5
32
  title: string;
6
33
  /** Opened on press. Surfaces without navigation can omit it. */
7
34
  href?: string;
8
- /** Favicon or mark. Falls back to the first letter of `title`. */
35
+ /** Favicon or mark. Falls back to the first letter of `host` or `title`. */
9
36
  icon?: ReactNode;
37
+ /**
38
+ * Who published it — `nytimes.com`, a repo, a filename. Shown above the
39
+ * title, because this is the line a reader actually scans.
40
+ *
41
+ * Derived by the SURFACE, not here. The extension parses it from the URL and
42
+ * strips `www.` (`Source.tsx:10-15`); chat is handed a domain by the server
43
+ * alongside a server-derived favicon. A `new URL()` in this package would be
44
+ * a third answer, and it would be the wrong one for a source that is a file
45
+ * path rather than a link.
46
+ */
47
+ host?: string;
10
48
  }
11
- export interface SourcesProps {
49
+ export interface SourcesProps extends Omit<ComponentProps<typeof YStack>, 'children'> {
12
50
  sources: Source[];
13
51
  title?: string;
14
52
  /** Raised on press. Given `href`, a surface may navigate instead. */
15
53
  onOpen?: (source: Source) => void;
16
54
  }
17
- export declare function Sources({ sources, title, onOpen }: SourcesProps): import("react/jsx-runtime").JSX.Element | null;
18
- export interface SourceChipProps {
55
+ export declare function Sources({ sources, title, onOpen, ...props }: SourcesProps): import("react/jsx-runtime").JSX.Element | null;
56
+ export interface SourceCardProps extends Omit<ComponentProps<typeof YStack>, 'children' | 'role'> {
19
57
  source: Source;
58
+ /** Position in the list, 1-based — the number the prose's marker refers to. */
59
+ index?: number;
20
60
  onOpen?: (source: Source) => void;
21
61
  }
22
- export declare function SourceChip({ source, onOpen }: SourceChipProps): import("react/jsx-runtime").JSX.Element;
62
+ export declare function SourceCard({ source, index, onOpen, ...props }: SourceCardProps): import("react/jsx-runtime").JSX.Element;
23
63
  //# 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;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAuB,MAAM,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAStD,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;;;;;;;;;OASG;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,kDAiCrF;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,2CA6D9E"}
@@ -1,22 +1,45 @@
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
+ * This used to be a wrapping grid of 28px marks with the title reachable only
7
+ * as a tooltip, and the argument for it was density: sources are scanned for
8
+ * "which of these do I already trust", and twenty full-width titles push the
9
+ * rail past a screen. The density argument is right. Hiding the HOST was not,
10
+ * because the host is the answer to that exact question — a favicon is sixteen
11
+ * pixels of ambiguity and `nytimes.com` is a verdict — and neither surface that
12
+ * ships citations hides it. hanzo/chat's `SourceItem` (`components/Web/
13
+ * Sources.tsx`) draws favicon, domain, title and a snippet; the extension's
14
+ * `answer/Source.tsx:9-26` draws a number, a favicon, the host and a
15
+ * three-line-clamped title, and its own comment records where that came from:
16
+ * "Source card — chat AnswerView: 160px, number pill first, favicon, host,
17
+ * 3-line title".
18
+ *
19
+ * So density is kept by scrolling sideways rather than by discarding the text —
20
+ * the strip is one row tall whatever the count, which is what the grid was
21
+ * really buying. Both surfaces already scroll: `.ae-sources` is
22
+ * `display:flex; overflow-x:auto` at `newtab.css:260`.
23
+ *
24
+ * The number is not decoration. Both surfaces number the cards to match a
25
+ * marker in the prose, and the marker itself is not here: chat mints it with a
26
+ * remark plugin over private-use codepoints (`components/Web/plugin.ts`), which
27
+ * belongs to the markdown pipeline that stays with the surface.
10
28
  */
11
29
  import { SizableText, XStack, YStack } from '@hanzo/gui';
12
30
  import { slot, tip } from '../backends/gui/slot.js';
13
- export function Sources({ sources, title = 'Sources', onOpen }) {
31
+ /** Card width, px. The extension's is 168 and it cites chat's 160; the point of
32
+ * a fixed width is that a strip of them scans as a row rather than as ragged
33
+ * columns, so it is one number and not a range. */
34
+ const CARD = 168;
35
+ export function Sources({ sources, title = 'Sources', onOpen, ...props }) {
14
36
  if (sources.length === 0)
15
37
  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))) })] }));
38
+ 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
39
  }
18
- export function SourceChip({ source, onOpen }) {
40
+ export function SourceCard({ source, index, onOpen, ...props }) {
19
41
  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() })) }));
42
+ const label = source.host ?? source.title;
43
+ 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
44
  }
22
45
  //# 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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"}
@@ -65,7 +65,7 @@ const mark = (status) => {
65
65
  return (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Ban, { size: MARK, color: "$color11" });
66
66
  }
67
67
  };
68
- function Step({ name, status = 'done', detail, children, defaultOpen, open, onOpenChange, }) {
68
+ function Step({ name, status = 'done', detail, children, defaultOpen, open, onOpenChange, ...props }) {
69
69
  // A step with no body is a statement, not a control. Rendering a dead chevron
70
70
  // and a focusable trigger over nothing is how a keyboard user is sent to a
71
71
  // stop that does not answer.
@@ -77,5 +77,5 @@ function Step({ name, status = 'done', detail, children, defaultOpen, open, onOp
77
77
  // its `stopped` register with a border rather than a fill. A step that
78
78
  // errored is set apart the same way, so it still reads as a failure in a
79
79
  // brand that retunes the whole ramp.
80
- borderColor: status === 'error' ? '$color9' : '$borderColor', bg: "$color2", overflow: "hidden", children: [opens ? (0, jsx_runtime_1.jsx)(gui_2.CollapsibleTrigger, { width: "100%", children: header }) : header, opens ? ((0, jsx_runtime_1.jsx)(gui_2.CollapsibleContent, { children: (0, jsx_runtime_1.jsx)(gui_1.YStack, { ...(0, slot_1.slot)('step-body'), px: "$3", py: "$2.5", gap: "$2", borderTopWidth: 1, borderColor: "$borderColor", children: children }) })) : null] }));
80
+ borderColor: status === 'error' ? '$color9' : '$borderColor', bg: "$color2", overflow: "hidden", ...props, children: [opens ? (0, jsx_runtime_1.jsx)(gui_2.CollapsibleTrigger, { width: "100%", children: header }) : header, opens ? ((0, jsx_runtime_1.jsx)(gui_2.CollapsibleContent, { children: (0, jsx_runtime_1.jsx)(gui_1.YStack, { ...(0, slot_1.slot)('step-body'), px: "$3", py: "$2.5", gap: "$2", borderTopWidth: 1, borderColor: "$borderColor", children: children }) })) : null] }));
81
81
  }
@@ -1,4 +1,5 @@
1
- import type { ReactNode } from 'react';
1
+ import type { ComponentProps, ReactNode } from 'react';
2
+ import { Collapsible } from '../backends/gui/index.js';
2
3
  /**
3
4
  * How a step went.
4
5
  *
@@ -10,7 +11,7 @@ import type { ReactNode } from 'react';
10
11
  * step that is running are both "not yet".
11
12
  */
12
13
  export type Ran = 'running' | 'done' | 'error' | 'cancelled';
13
- export interface StepProps {
14
+ export interface StepProps extends Omit<ComponentProps<typeof Collapsible>, 'children' | 'open' | 'defaultOpen' | 'onOpenChange'> {
14
15
  /** What ran — a tool name, "Reasoning", "Plan". */
15
16
  name: string;
16
17
  status?: Ran;
@@ -30,5 +31,5 @@ export interface StepProps {
30
31
  open?: boolean;
31
32
  onOpenChange?: (open: boolean) => void;
32
33
  }
33
- export declare function Step({ name, status, detail, children, defaultOpen, open, onOpenChange, }: StepProps): import("react/jsx-runtime").JSX.Element;
34
+ export declare function Step({ name, status, detail, children, defaultOpen, open, onOpenChange, ...props }: StepProps): import("react/jsx-runtime").JSX.Element;
34
35
  //# sourceMappingURL=Step.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Step.d.ts","sourceRoot":"","sources":["../../src/chat/Step.tsx"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAKtC;;;;;;;;;GASG;AACH,MAAM,MAAM,GAAG,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,CAAA;AA8B5D,MAAM,WAAW,SAAS;IACxB,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,GAAG,CAAA;IACZ;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;kEAC8D;IAC9D,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,oBAAoB;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;CACvC;AAED,wBAAgB,IAAI,CAAC,EACnB,IAAI,EACJ,MAAe,EACf,MAAM,EACN,QAAQ,EACR,WAAW,EACX,IAAI,EACJ,YAAY,GACb,EAAE,SAAS,2CA4DX"}
1
+ {"version":3,"file":"Step.d.ts","sourceRoot":"","sources":["../../src/chat/Step.tsx"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtD,OAAO,EAAE,WAAW,EAAmD,MAAM,iBAAiB,CAAA;AAG9F;;;;;;;;;GASG;AACH,MAAM,MAAM,GAAG,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,CAAA;AA8B5D,MAAM,WAAW,SACf,SAAQ,IAAI,CAAC,cAAc,CAAC,OAAO,WAAW,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,aAAa,GAAG,cAAc,CAAC;IACtG,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,GAAG,CAAA;IACZ;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;kEAC8D;IAC9D,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,oBAAoB;IACpB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;CACvC;AAED,wBAAgB,IAAI,CAAC,EACnB,IAAI,EACJ,MAAe,EACf,MAAM,EACN,QAAQ,EACR,WAAW,EACX,IAAI,EACJ,YAAY,EACZ,GAAG,KAAK,EACT,EAAE,SAAS,2CA6DX"}
package/dist/chat/Step.js CHANGED
@@ -62,7 +62,7 @@ const mark = (status) => {
62
62
  return _jsx(Ban, { size: MARK, color: "$color11" });
63
63
  }
64
64
  };
65
- export function Step({ name, status = 'done', detail, children, defaultOpen, open, onOpenChange, }) {
65
+ export function Step({ name, status = 'done', detail, children, defaultOpen, open, onOpenChange, ...props }) {
66
66
  // A step with no body is a statement, not a control. Rendering a dead chevron
67
67
  // and a focusable trigger over nothing is how a keyboard user is sent to a
68
68
  // stop that does not answer.
@@ -74,6 +74,6 @@ export function Step({ name, status = 'done', detail, children, defaultOpen, ope
74
74
  // its `stopped` register with a border rather than a fill. A step that
75
75
  // errored is set apart the same way, so it still reads as a failure in a
76
76
  // brand that retunes the whole ramp.
77
- borderColor: status === 'error' ? '$color9' : '$borderColor', bg: "$color2", overflow: "hidden", children: [opens ? _jsx(CollapsibleTrigger, { width: "100%", children: header }) : header, opens ? (_jsx(CollapsibleContent, { children: _jsx(YStack, { ...slot('step-body'), px: "$3", py: "$2.5", gap: "$2", borderTopWidth: 1, borderColor: "$borderColor", children: children }) })) : null] }));
77
+ borderColor: status === 'error' ? '$color9' : '$borderColor', bg: "$color2", overflow: "hidden", ...props, children: [opens ? _jsx(CollapsibleTrigger, { width: "100%", children: header }) : header, opens ? (_jsx(CollapsibleContent, { children: _jsx(YStack, { ...slot('step-body'), px: "$3", py: "$2.5", gap: "$2", borderTopWidth: 1, borderColor: "$borderColor", children: children }) })) : null] }));
78
78
  }
79
79
  //# sourceMappingURL=Step.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Step.js","sourceRoot":"","sources":["../../src/chat/Step.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Step.js","sourceRoot":"","sources":["../../src/chat/Step.tsx"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  'use client';"use strict";
2
2
  'use client';
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.Thread = Thread;
4
+ exports.Thread = void 0;
5
5
  const jsx_runtime_1 = require("react/jsx-runtime");
6
6
  /**
7
7
  * Thread — the scrolling column of turns.
@@ -10,6 +10,28 @@ const jsx_runtime_1 = require("react/jsx-runtime");
10
10
  * to the bottom, and stop following the instant the reader scrolls up. The
11
11
  * decision is `pinned()`, asserted without a DOM; this component only wires it
12
12
  * to the platform's scroll view, so momentum and keyboard stay the host's.
13
+ *
14
+ * That decision is now the fleet's only answer to "am I at the bottom", which
15
+ * had five: 50px in two places, 97%-of-height in a third, an
16
+ * `IntersectionObserver` at a 0.85 ratio in a fourth, and on the thread itself
17
+ * nothing at all — hanzo.app `/chat` ran `scrollIntoView({behavior:'smooth'})`
18
+ * on every `messages` change, and since a streaming turn rewrites `messages`
19
+ * per SSE delta, scrolling up to re-read was undone several times a second.
20
+ *
21
+ * Measured in Chromium against the packed tarball, 2500 tokens at 4ms:
22
+ * following holds a 0px gap through 2501 commits; parked at offset 0 the reader
23
+ * STAYS at 0 while content grows 1015 → 1375; returning to the end resumes
24
+ * following at 0px.
25
+ *
26
+ * There is no `onLayout` here any more, and nothing is lost with it. It was
27
+ * passed to keep a `track` ref current, gui does not implement it on this
28
+ * component so React forwarded it to the DOM as an unknown handler — the noise
29
+ * hanzo.app reported — and the ref it fed was WRITE-ONLY: `track` was read in
30
+ * exactly one place, `onScroll`, which rebuilds all three numbers from the
31
+ * event before reading them. So the handler was dead, the state it kept was
32
+ * dead, and the measurement the two together looked like they were doing was
33
+ * being done properly a few lines below the whole time. The scroll event
34
+ * carries `layoutMeasurement`; nothing else needs to ask.
13
35
  */
14
36
  const gui_1 = require("@hanzo/gui");
15
37
  const react_1 = require("react");
@@ -17,26 +39,29 @@ const slot_1 = require("../backends/gui/slot.cjs");
17
39
  const stick_1 = require("./stick.cjs");
18
40
  /** Reading-measure cap for prose, in px. */
19
41
  const MEASURE = 768;
20
- function Thread({ children, maxWidth = MEASURE, slack = stick_1.SLACK, gap = 24 }) {
42
+ exports.Thread = (0, react_1.forwardRef)(function Thread({ children, maxWidth = MEASURE, slack = stick_1.SLACK, gap = 24, onScroll, column, ...props }, ref) {
21
43
  const view = (0, react_1.useRef)(null);
22
- const track = (0, react_1.useRef)({ offset: 0, viewport: 0, content: 0 });
23
44
  const follow = (0, react_1.useRef)(true);
45
+ // Ours to scroll with, the caller's because three consumers need this node.
46
+ const hold = (0, react_1.useCallback)((el) => {
47
+ view.current = el;
48
+ if (typeof ref === 'function')
49
+ ref(el);
50
+ else if (ref)
51
+ ref.current = el;
52
+ }, [ref]);
24
53
  const grew = (0, react_1.useCallback)(() => {
25
54
  if (follow.current)
26
55
  view.current?.scrollToEnd({ animated: false });
27
56
  }, []);
28
- return ((0, jsx_runtime_1.jsx)(gui_1.ScrollView, { ref: view, ...(0, slot_1.slot)('thread'), flex: 1, width: "100%", scrollEventThrottle: 16, onLayout: (e) => {
29
- track.current = { ...track.current, viewport: e.nativeEvent.layout.height };
30
- }, onContentSizeChange: (_w, h) => {
31
- track.current = { ...track.current, content: h };
32
- grew();
33
- }, onScroll: (e) => {
57
+ return ((0, jsx_runtime_1.jsx)(gui_1.ScrollView, { ref: hold, ...(0, slot_1.slot)('thread'), flex: 1, width: "100%", scrollEventThrottle: 16, onContentSizeChange: grew, onScroll: (e) => {
34
58
  const { contentOffset, layoutMeasurement, contentSize } = e.nativeEvent;
35
- track.current = {
59
+ const track = {
36
60
  offset: contentOffset.y,
37
61
  viewport: layoutMeasurement.height,
38
62
  content: contentSize.height,
39
63
  };
40
- follow.current = (0, stick_1.pinned)(track.current, slack);
41
- }, children: (0, jsx_runtime_1.jsx)(gui_1.YStack, { ...(0, slot_1.slot)('thread-column'), width: "100%", gap: gap, py: gap, ...(maxWidth ? { maxW: maxWidth, self: 'center', px: '$3' } : null), children: children }) }));
42
- }
64
+ follow.current = (0, stick_1.pinned)(track, slack);
65
+ onScroll?.(e, { pinned: follow.current, track });
66
+ }, ...props, children: (0, jsx_runtime_1.jsx)(gui_1.YStack, { ...(0, slot_1.slot)('thread-column'), width: "100%", gap: gap, py: gap, ...(maxWidth ? { maxW: maxWidth, self: 'center', px: '$3' } : null), ...column, children: children }) }));
67
+ });