@adea-ai/ui 0.120.0 → 0.122.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/components/composites/account-menu/account-menu.d.ts +8 -0
  2. package/dist/components/composites/account-menu/account-menu.d.ts.map +1 -1
  3. package/dist/components/composites/account-menu/account-menu.js +43 -15
  4. package/dist/components/composites/account-menu/account-menu.js.map +1 -1
  5. package/dist/components/conversation/composer-menu.d.ts +31 -0
  6. package/dist/components/conversation/composer-menu.d.ts.map +1 -0
  7. package/dist/components/conversation/composer-menu.js +63 -0
  8. package/dist/components/conversation/composer-menu.js.map +1 -0
  9. package/dist/components/conversation/conversation-pane.d.ts +38 -0
  10. package/dist/components/conversation/conversation-pane.d.ts.map +1 -0
  11. package/dist/components/conversation/conversation-pane.js +83 -0
  12. package/dist/components/conversation/conversation-pane.js.map +1 -0
  13. package/dist/components/conversation/conversation-surface.d.ts +6 -0
  14. package/dist/components/conversation/conversation-surface.d.ts.map +1 -1
  15. package/dist/components/conversation/conversation-surface.js +10 -4
  16. package/dist/components/conversation/conversation-surface.js.map +1 -1
  17. package/dist/components/conversation/index.d.ts +2 -0
  18. package/dist/components/conversation/index.d.ts.map +1 -1
  19. package/dist/components/conversation/index.js +3 -1
  20. package/dist/components/conversation/message-composer.d.ts +6 -0
  21. package/dist/components/conversation/message-composer.d.ts.map +1 -1
  22. package/dist/components/conversation/message-composer.js +10 -1
  23. package/dist/components/conversation/message-composer.js.map +1 -1
  24. package/dist/components/ui/command/command.d.ts +13 -0
  25. package/dist/components/ui/command/command.d.ts.map +1 -1
  26. package/dist/components/ui/command/command.js +35 -2
  27. package/dist/components/ui/command/command.js.map +1 -1
  28. package/dist/components/ui/command/index.d.ts +1 -1
  29. package/dist/components/ui/command/index.d.ts.map +1 -1
  30. package/dist/components/ui/command/index.js +2 -2
  31. package/dist/components/ui/kbd/index.d.ts +1 -0
  32. package/dist/components/ui/kbd/index.d.ts.map +1 -1
  33. package/dist/components/ui/kbd/index.js +2 -1
  34. package/dist/components/ui/kbd/platform-modifier.d.ts +28 -0
  35. package/dist/components/ui/kbd/platform-modifier.d.ts.map +1 -0
  36. package/dist/components/ui/kbd/platform-modifier.js +37 -0
  37. package/dist/components/ui/kbd/platform-modifier.js.map +1 -0
  38. package/dist/index.js +5 -2
  39. package/dist/r/conversation.json +10 -0
  40. package/dist/r/kbd.json +5 -0
  41. package/dist/r/registry.json +15 -0
  42. package/dist/r/src/components/composites/account-menu/account-menu.tsx +44 -4
  43. package/dist/r/src/components/conversation/composer-menu.tsx +67 -0
  44. package/dist/r/src/components/conversation/conversation-pane.tsx +111 -0
  45. package/dist/r/src/components/conversation/conversation-surface.tsx +12 -1
  46. package/dist/r/src/components/conversation/index.ts +7 -0
  47. package/dist/r/src/components/conversation/message-composer.tsx +8 -0
  48. package/dist/r/src/components/ui/command/command.tsx +35 -0
  49. package/dist/r/src/components/ui/command/index.ts +2 -0
  50. package/dist/r/src/components/ui/kbd/index.ts +5 -0
  51. package/dist/r/src/components/ui/kbd/platform-modifier.ts +37 -0
  52. package/package.json +1 -1
  53. package/registry.json +15 -0
  54. package/src/components/composites/account-menu/account-menu.tsx +44 -4
  55. package/src/components/conversation/composer-menu.tsx +67 -0
  56. package/src/components/conversation/conversation-pane.tsx +111 -0
  57. package/src/components/conversation/conversation-surface.tsx +12 -1
  58. package/src/components/conversation/index.ts +7 -0
  59. package/src/components/conversation/message-composer.tsx +8 -0
  60. package/src/components/ui/command/command.tsx +35 -0
  61. package/src/components/ui/command/index.ts +2 -0
  62. package/src/components/ui/kbd/index.ts +5 -0
  63. package/src/components/ui/kbd/platform-modifier.ts +37 -0
@@ -60,6 +60,14 @@ export type AccountMenuProps = {
60
60
  platform?: 'desktop' | 'web';
61
61
  /** Whether a session exists. Decides the sign-in/sign-out row. */
62
62
  authenticated: boolean;
63
+ /**
64
+ * Draw the pending-update indicator: an accent dot on the trigger and on the
65
+ * `updates` row — the id `createAppMenuItems` mints, the same convention the
66
+ * fallback icons follow — and extend both accessible names with "update
67
+ * available", because the dot itself carries no text. The updater remains the
68
+ * caller's: the prop only mirrors its state into the menu.
69
+ */
70
+ updateAvailable?: boolean;
63
71
  /** Local-only applications can omit the session actions altogether. */
64
72
  showSession?: boolean;
65
73
  /** Disable the session row while a sign-in or sign-out is in flight. */
@@ -1 +1 @@
1
- {"version":3,"file":"account-menu.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/account-menu/account-menu.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAGnD,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAEjE,OAAO,EACL,YAAY,EAMb,MAAM,sCAAsC,CAAA;AAG7C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IAClB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,SAAS,GAAG,KAAK,CAAA;IAC5B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,KAAK,IAAI,CAAA;CACtE,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mFAAmF;IACnF,SAAS,CAAC,EAAE,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC,WAAW,CAAC,CAAA;IAC5D,qDAAqD;IACrD,MAAM,CAAC,EAAE,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC,QAAQ,CAAC,CAAA;IACtD,oEAAoE;IACpE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC1B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,uCAAuC;IACvC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAA;IACjC,sEAAsE;IACtE,QAAQ,CAAC,EAAE,SAAS,GAAG,KAAK,CAAA;IAC5B,kEAAkE;IAClE,aAAa,EAAE,OAAO,CAAA;IACtB,uEAAuE;IACvE,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,wEAAwE;IACxE,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB,CAAA;AAWD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,eAoJlD;AAED,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,WAAW,CAAC,EAAE,eAAe,CAAA;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,OAAO,CAAC,EAAE,eAAe,CAAC,oBAAoB,CAAC,CAAA;IAC/C,MAAM,CAAC,EAAE,eAAe,CAAC,oBAAoB,CAAC,CAAA;IAC9C,UAAU,CAAC,EAAE,eAAe,CAAC,oBAAoB,CAAC,CAAA;IAClD,SAAS,CAAC,EAAE,eAAe,CAAC,oBAAoB,CAAC,CAAA;IACjD,UAAU,CAAC,EAAE,eAAe,CAAC,oBAAoB,CAAC,CAAA;CACnD,GAAG,eAAe,EAAE,CAoCpB"}
1
+ {"version":3,"file":"account-menu.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/account-menu/account-menu.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAGnD,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAEjE,OAAO,EACL,YAAY,EAMb,MAAM,sCAAsC,CAAA;AAG7C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IAClB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,SAAS,GAAG,KAAK,CAAA;IAC5B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,KAAK,IAAI,CAAA;CACtE,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,mFAAmF;IACnF,SAAS,CAAC,EAAE,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC,WAAW,CAAC,CAAA;IAC5D,qDAAqD;IACrD,MAAM,CAAC,EAAE,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC,QAAQ,CAAC,CAAA;IACtD,oEAAoE;IACpE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC1B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,uCAAuC;IACvC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAA;IACjC,sEAAsE;IACtE,QAAQ,CAAC,EAAE,SAAS,GAAG,KAAK,CAAA;IAC5B,kEAAkE;IAClE,aAAa,EAAE,OAAO,CAAA;IACtB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,uEAAuE;IACvE,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,wEAAwE;IACxE,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB,CAAA;AAWD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,eAoLlD;AAED,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,WAAW,CAAC,EAAE,eAAe,CAAA;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,OAAO,CAAC,EAAE,eAAe,CAAC,oBAAoB,CAAC,CAAA;IAC/C,MAAM,CAAC,EAAE,eAAe,CAAC,oBAAoB,CAAC,CAAA;IAC9C,UAAU,CAAC,EAAE,eAAe,CAAC,oBAAoB,CAAC,CAAA;IAClD,SAAS,CAAC,EAAE,eAAe,CAAC,oBAAoB,CAAC,CAAA;IACjD,UAAU,CAAC,EAAE,eAAe,CAAC,oBAAoB,CAAC,CAAA;CACnD,GAAG,eAAe,EAAE,CAoCpB"}
@@ -7,7 +7,9 @@ import { createComponent, insert, memo, mergeProps, template } from "solid-js/we
7
7
  import { CircleHelp, Info, LogIn, LogOut, Megaphone, RefreshCw, Settings2, Smartphone, UserRound } from "lucide-solid";
8
8
  import { For, Show, createSignal, onCleanup, splitProps } from "solid-js";
9
9
  //#region src/components/composites/account-menu/account-menu.tsx
10
- var _tmpl$ = /*#__PURE__*/ template(`<span>`);
10
+ var _tmpl$ = /*#__PURE__*/ template(`<span data-slot=account-menu-update-dot aria-hidden=true class="absolute top-1.5 right-2.5 size-2 rounded-full bg-primary">`);
11
+ var _tmpl$2 = /*#__PURE__*/ template(`<span>`);
12
+ var _tmpl$3 = /*#__PURE__*/ template(`<span data-slot=account-menu-update-dot aria-hidden=true class="ms-auto size-2 shrink-0 rounded-full bg-primary">`);
11
13
  var FALLBACK_ICONS = {
12
14
  mobile: Smartphone,
13
15
  settings: Settings2,
@@ -27,6 +29,7 @@ function AccountMenu(props) {
27
29
  "items",
28
30
  "platform",
29
31
  "authenticated",
32
+ "updateAvailable",
30
33
  "showSession",
31
34
  "busy",
32
35
  "label",
@@ -44,6 +47,15 @@ function AccountMenu(props) {
44
47
  scheduledAfterClose = void 0;
45
48
  });
46
49
  const visible = () => local.items.filter((item) => !item.platform || item.platform === (local.platform ?? "desktop"));
50
+ const triggerName = () => `${local.label ?? "Account and settings"}${local.updateAvailable ? ", update available" : ""}`;
51
+ const updateDot = () => createComponent(Show, {
52
+ get when() {
53
+ return local.updateAvailable;
54
+ },
55
+ get children() {
56
+ return _tmpl$();
57
+ }
58
+ });
47
59
  const menu = () => createComponent(DropdownMenu, {
48
60
  modal: false,
49
61
  get placement() {
@@ -70,15 +82,16 @@ function AccountMenu(props) {
70
82
  return local.size ?? "icon-md";
71
83
  },
72
84
  get ["class"]() {
73
- return cn("text-muted-foreground", local.class);
85
+ return cn("relative text-muted-foreground", local.class);
74
86
  },
87
+ "data-slot": "account-menu-trigger",
75
88
  get ["aria-label"]() {
76
- return local.label ?? "Account and settings";
89
+ return triggerName();
77
90
  },
78
91
  onFocus: () => local.onIntent?.(),
79
92
  onPointerEnter: () => local.onIntent?.()
80
93
  }, rest, { get children() {
81
- return createComponent(UserRound, { "aria-hidden": "true" });
94
+ return [createComponent(UserRound, { "aria-hidden": "true" }), memo(() => updateDot())];
82
95
  } }));
83
96
  },
84
97
  get children() {
@@ -86,15 +99,16 @@ function AccountMenu(props) {
86
99
  as: SideRailButton,
87
100
  ref: (element) => trigger = element,
88
101
  get label() {
89
- return local.label ?? "Account and settings";
102
+ return triggerName();
90
103
  },
91
104
  get ["class"]() {
92
- return local.class;
105
+ return cn("relative", local.class);
93
106
  },
107
+ "data-slot": "account-menu-trigger",
94
108
  onFocus: () => local.onIntent?.(),
95
109
  onPointerEnter: () => local.onIntent?.()
96
110
  }, rest, { get children() {
97
- return createComponent(UserRound, { "aria-hidden": "true" });
111
+ return [createComponent(UserRound, { "aria-hidden": "true" }), memo(() => updateDot())];
98
112
  } }));
99
113
  }
100
114
  }), createComponent(DropdownMenuContent, {
@@ -124,6 +138,9 @@ function AccountMenu(props) {
124
138
  get disabled() {
125
139
  return item.disabled;
126
140
  },
141
+ get ["aria-label"]() {
142
+ return memo(() => !!(local.updateAvailable && item.id === "updates"))() ? `${item.label}, update available` : void 0;
143
+ },
127
144
  onSelect: () => {
128
145
  pendingAfterClose = item.onSelectAfterClose ? { callback: item.onSelectAfterClose } : void 0;
129
146
  item.onSelect?.();
@@ -135,11 +152,22 @@ function AccountMenu(props) {
135
152
  return item.keyshortcuts;
136
153
  },
137
154
  get children() {
138
- return [memo(() => item.icon ?? renderFallbackIcon(item.id)), (() => {
139
- var _el$2 = _tmpl$();
140
- insert(_el$2, () => item.label);
141
- return _el$2;
142
- })()];
155
+ return [
156
+ memo(() => item.icon ?? renderFallbackIcon(item.id)),
157
+ (() => {
158
+ var _el$3 = _tmpl$2();
159
+ insert(_el$3, () => item.label);
160
+ return _el$3;
161
+ })(),
162
+ createComponent(Show, {
163
+ get when() {
164
+ return memo(() => !!local.updateAvailable)() && item.id === "updates";
165
+ },
166
+ get children() {
167
+ return _tmpl$3();
168
+ }
169
+ })
170
+ ];
143
171
  }
144
172
  })
145
173
  });
@@ -166,9 +194,9 @@ function AccountMenu(props) {
166
194
  return createComponent(LogOut, { "aria-hidden": "true" });
167
195
  }
168
196
  }), (() => {
169
- var _el$ = _tmpl$();
170
- insert(_el$, () => local.authenticated ? "Sign out" : "Sign in");
171
- return _el$;
197
+ var _el$2 = _tmpl$2();
198
+ insert(_el$2, () => local.authenticated ? "Sign out" : "Sign in");
199
+ return _el$2;
172
200
  })()];
173
201
  }
174
202
  });
@@ -1 +1 @@
1
- {"version":3,"file":"account-menu.js","names":["CircleHelp","Info","LogIn","LogOut","Megaphone","RefreshCw","Settings2","Smartphone","UserRound","ComponentProps","JSX","For","Show","createSignal","onCleanup","splitProps","cn","Button","ButtonProps","SideRailButton","DropdownMenu","DropdownMenuContent","DropdownMenuGroup","DropdownMenuItem","DropdownMenuSeparator","DropdownMenuTrigger","Tooltip","TooltipContent","TooltipTrigger","AccountMenuItem","id","label","icon","Element","shortcut","keyshortcuts","disabled","platform","onSelect","onSelectAfterClose","trigger","HTMLButtonElement","AccountMenuProps","class","placement","gutter","hideArrow","size","railTrigger","items","authenticated","showSession","busy","onSignIn","onSignOut","onIntent","FALLBACK_ICONS","Record","mobile","settings","about","help","feedback","updates","AccountMenu","props","local","rest","pendingAfterClose","callback","NonNullable","scheduledAfterClose","menuOpen","setMenuOpen","tooltipOpen","setTooltipOpen","undefined","visible","filter","item","menu","_$createComponent","modal","onOpenChange","open","children","when","fallback","_$mergeProps","as","AccountMenuButton","ref","element","variant","aria-label","onFocus","onPointerEnter","onCloseAutoFocus","event","selection","preventDefault","queueMicrotask","each","_$memo","renderFallbackIcon","_el$2","_tmpl$","_$insert","_el$","createAppMenuItems","options","primaryItem","settingsShortcut","onAbout","onHelp","onFeedback","onUpdates","onSettings","Icon","_$createComponent","_$mergeProps","_$memo"],"sources":["../../../../src/components/composites/account-menu/account-menu.tsx"],"sourcesContent":["import {\n CircleHelp,\n Info,\n LogIn,\n LogOut,\n Megaphone,\n RefreshCw,\n Settings2,\n Smartphone,\n UserRound,\n} from 'lucide-solid'\nimport type { ComponentProps, JSX } from 'solid-js'\nimport { For, Show, createSignal, onCleanup, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { Button, type ButtonProps } from '../../ui/button/button'\nimport { SideRailButton } from '../../layout/side-rail/side-rail'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../../ui/dropdown-menu/dropdown-menu'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../../ui/tooltip/tooltip'\n\n/**\n * AccountMenu.\n *\n * The trailing control in a rail or a top bar: the account, the way into settings,\n * and the way out. Every desktop application has one and they are all the same menu,\n * which is why it belongs here rather than being redrawn per app.\n *\n * ## The items are the caller's\n *\n * The menu takes a list rather than hard-coding one. An application adds and removes\n * entries — a mobile hand-off that only makes sense on desktop, a feedback link that\n * only exists once there is somewhere to send it — and a component that decided the\n * list could not be used by two products.\n *\n * `disabled` on an item is deliberately visible rather than hidden: a greyed \"Help\n * Center\" says the destination is coming, while an absent one says it does not exist.\n */\nexport type AccountMenuItem = {\n id: string\n label: string\n icon?: JSX.Element\n /** A chord drawn at the trailing edge, e.g. `⌘,`. */\n shortcut?: string\n /**\n * The parseable chord behind `shortcut`, e.g. `Meta+,`. The glyph is\n * `aria-hidden` so the item's accessible name stays exactly its label —\n * same contract as `SideRailItem`. Supply it whenever `shortcut` is drawn.\n */\n keyshortcuts?: string\n disabled?: boolean\n /** Render only on this platform. Omit to always render. */\n platform?: 'desktop' | 'web'\n onSelect?: () => void\n /** Run after the menu's close-focus cycle, with its trigger as a stable opener. */\n onSelectAfterClose?: (trigger: HTMLButtonElement | undefined) => void\n}\n\nexport type AccountMenuProps = {\n class?: string\n /** Anchor a rail menu beside its trigger, or keep the default upward placement. */\n placement?: ComponentProps<typeof DropdownMenu>['placement']\n /** Space between the trigger and menu, in pixels. */\n gutter?: ComponentProps<typeof DropdownMenu>['gutter']\n /** Hide the menu pointer when aligning it beside a compact rail. */\n hideArrow?: boolean\n /** The trigger size. Icon-only menus default to the standard icon size. */\n size?: ButtonProps['size']\n /**\n * Render the trigger as a `SideRailButton` row instead of a bare icon\n * button: same geometry, hover treatment, and flush collapsed-rail tip as\n * every other entry in the surrounding rail. The component's own tooltip\n * is skipped — the rail row owns its help.\n */\n railTrigger?: boolean\n /** The entries above the separator. */\n items: readonly AccountMenuItem[]\n /** Which platform this is rendered on, for items that declare one. */\n platform?: 'desktop' | 'web'\n /** Whether a session exists. Decides the sign-in/sign-out row. */\n authenticated: boolean\n /** Local-only applications can omit the session actions altogether. */\n showSession?: boolean\n /** Disable the session row while a sign-in or sign-out is in flight. */\n busy?: boolean\n /** The name shown on the trigger's accessible label. */\n label?: string\n onSignIn?: () => void\n onSignOut?: () => void\n /** Fires on hover or focus of the trigger — a chance to prefetch. */\n onIntent?: () => void\n}\n\nconst FALLBACK_ICONS: Record<string, typeof Settings2> = {\n mobile: Smartphone,\n settings: Settings2,\n about: Info,\n help: CircleHelp,\n feedback: Megaphone,\n updates: RefreshCw,\n}\n\nexport function AccountMenu(props: AccountMenuProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'placement',\n 'gutter',\n 'hideArrow',\n 'size',\n 'railTrigger',\n 'items',\n 'platform',\n 'authenticated',\n 'showSession',\n 'busy',\n 'label',\n 'onSignIn',\n 'onSignOut',\n 'onIntent',\n ])\n\n let trigger: HTMLButtonElement | undefined\n let pendingAfterClose:\n | { callback: NonNullable<AccountMenuItem['onSelectAfterClose']> }\n | undefined\n let scheduledAfterClose:\n | { callback: NonNullable<AccountMenuItem['onSelectAfterClose']> }\n | undefined\n const [menuOpen, setMenuOpen] = createSignal(false)\n const [tooltipOpen, setTooltipOpen] = createSignal(false)\n\n onCleanup(() => {\n pendingAfterClose = undefined\n scheduledAfterClose = undefined\n })\n\n const visible = () =>\n local.items.filter((item) => !item.platform || item.platform === (local.platform ?? 'desktop'))\n\n // Built by reference at each return, not once up front: Solid fixes a node's\n // owner chain when the JSX expression runs, and an eagerly created tree would\n // never see the Tooltip provider below — Kobalte's TooltipTrigger then throws\n // `useTooltipContext must be used within a Tooltip component`.\n const menu = () => (\n <DropdownMenu\n modal={false}\n placement={local.placement ?? 'top-start'}\n gutter={local.gutter}\n onOpenChange={(open) => {\n setMenuOpen(open)\n if (open) setTooltipOpen(false)\n }}\n >\n <Show\n when={local.railTrigger}\n fallback={\n <TooltipTrigger\n as={AccountMenuButton}\n ref={(element: HTMLButtonElement) => (trigger = element)}\n variant=\"ghost\"\n size={local.size ?? 'icon-md'}\n class={cn('text-muted-foreground', local.class)}\n aria-label={local.label ?? 'Account and settings'}\n onFocus={() => local.onIntent?.()}\n onPointerEnter={() => local.onIntent?.()}\n {...rest}\n >\n <UserRound aria-hidden=\"true\" />\n </TooltipTrigger>\n }\n >\n {/* Rail parity: the trigger is a SideRailButton row — the same\n geometry and flush collapsed-rail tip as every other rail entry —\n so the composite's own tooltip stays out of the way. */}\n <DropdownMenuTrigger\n as={SideRailButton}\n ref={(element: HTMLButtonElement) => (trigger = element)}\n label={local.label ?? 'Account and settings'}\n class={local.class}\n onFocus={() => local.onIntent?.()}\n onPointerEnter={() => local.onIntent?.()}\n {...rest}\n >\n <UserRound aria-hidden=\"true\" />\n </DropdownMenuTrigger>\n </Show>\n <DropdownMenuContent\n hideArrow={local.hideArrow}\n class=\"min-w-56 max-h-(--kb-popper-available-height) overflow-x-hidden overflow-y-auto\"\n onCloseAutoFocus={(event) => {\n const selection = pendingAfterClose\n pendingAfterClose = undefined\n if (!selection) return\n\n event.preventDefault()\n scheduledAfterClose = selection\n queueMicrotask(() => {\n if (scheduledAfterClose !== selection) return\n scheduledAfterClose = undefined\n selection.callback(trigger)\n })\n }}\n >\n <DropdownMenuGroup>\n <For each={visible()}>\n {(item) => (\n <DropdownMenuItem\n disabled={item.disabled}\n onSelect={() => {\n pendingAfterClose = item.onSelectAfterClose\n ? { callback: item.onSelectAfterClose }\n : undefined\n item.onSelect?.()\n }}\n shortcut={item.shortcut}\n keyshortcuts={item.keyshortcuts}\n >\n {item.icon ?? renderFallbackIcon(item.id)}\n <span>{item.label}</span>\n </DropdownMenuItem>\n )}\n </For>\n </DropdownMenuGroup>\n <Show when={local.showSession !== false}>\n <DropdownMenuSeparator />\n <DropdownMenuGroup>\n <DropdownMenuItem\n disabled={local.busy}\n onSelect={() => (local.authenticated ? local.onSignOut?.() : local.onSignIn?.())}\n >\n <Show when={local.authenticated} fallback={<LogIn aria-hidden=\"true\" />}>\n <LogOut aria-hidden=\"true\" />\n </Show>\n <span>{local.authenticated ? 'Sign out' : 'Sign in'}</span>\n </DropdownMenuItem>\n </DropdownMenuGroup>\n </Show>\n </DropdownMenuContent>\n </DropdownMenu>\n )\n\n if (local.railTrigger) return menu()\n\n return (\n <Tooltip open={tooltipOpen()} onOpenChange={(open) => setTooltipOpen(open && !menuOpen())}>\n {menu()}\n {/* Both overlays own a Popper context; the tooltip must stay outside the menu root. */}\n <TooltipContent placement=\"top\">{local.label ?? 'Account and settings'}</TooltipContent>\n </Tooltip>\n )\n}\n\n/** The common support menu, with one app-specific entry before the shared items. */\nexport function createAppMenuItems(options: {\n primaryItem?: AccountMenuItem\n settingsShortcut?: string\n onAbout?: AccountMenuItem['onSelectAfterClose']\n onHelp?: AccountMenuItem['onSelectAfterClose']\n onFeedback?: AccountMenuItem['onSelectAfterClose']\n onUpdates?: AccountMenuItem['onSelectAfterClose']\n onSettings?: AccountMenuItem['onSelectAfterClose']\n}): AccountMenuItem[] {\n return [\n ...(options.primaryItem ? [options.primaryItem] : []),\n {\n id: 'about',\n label: 'About',\n disabled: !options.onAbout,\n onSelectAfterClose: options.onAbout,\n },\n {\n id: 'help',\n label: 'Help Center',\n disabled: !options.onHelp,\n onSelectAfterClose: options.onHelp,\n },\n {\n id: 'feedback',\n label: 'Send Feedback',\n disabled: !options.onFeedback,\n onSelectAfterClose: options.onFeedback,\n },\n {\n id: 'updates',\n label: 'Updates',\n platform: 'desktop',\n disabled: !options.onUpdates,\n onSelectAfterClose: options.onUpdates,\n },\n {\n id: 'settings',\n label: 'Settings',\n shortcut: options.settingsShortcut,\n disabled: !options.onSettings,\n onSelectAfterClose: options.onSettings,\n },\n ]\n}\n\nfunction AccountMenuButton(props: ComponentProps<typeof Button>) {\n return <DropdownMenuTrigger as={Button} {...props} />\n}\n\n/**\n * A known item id gets its conventional glyph, so a caller that only wants the\n * standard menu does not have to import six icons to get it.\n */\nfunction renderFallbackIcon(id: string): JSX.Element {\n const Icon = FALLBACK_ICONS[id]\n return Icon ? <Icon aria-hidden=\"true\" /> : null\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"account-menu.js","names":["CircleHelp","Info","LogIn","LogOut","Megaphone","RefreshCw","Settings2","Smartphone","UserRound","ComponentProps","JSX","For","Show","createSignal","onCleanup","splitProps","cn","Button","ButtonProps","SideRailButton","DropdownMenu","DropdownMenuContent","DropdownMenuGroup","DropdownMenuItem","DropdownMenuSeparator","DropdownMenuTrigger","Tooltip","TooltipContent","TooltipTrigger","AccountMenuItem","id","label","icon","Element","shortcut","keyshortcuts","disabled","platform","onSelect","onSelectAfterClose","trigger","HTMLButtonElement","AccountMenuProps","class","placement","gutter","hideArrow","size","railTrigger","items","authenticated","updateAvailable","showSession","busy","onSignIn","onSignOut","onIntent","FALLBACK_ICONS","Record","mobile","settings","about","help","feedback","updates","AccountMenu","props","local","rest","pendingAfterClose","callback","NonNullable","scheduledAfterClose","menuOpen","setMenuOpen","tooltipOpen","setTooltipOpen","undefined","visible","filter","item","triggerName","updateDot","_$createComponent","when","children","_tmpl$","menu","modal","onOpenChange","open","fallback","_$mergeProps","as","AccountMenuButton","ref","element","variant","aria-label","onFocus","onPointerEnter","_$memo","onCloseAutoFocus","event","selection","preventDefault","queueMicrotask","each","renderFallbackIcon","_el$3","_tmpl$2","_$insert","_tmpl$3","_el$2","createAppMenuItems","options","primaryItem","settingsShortcut","onAbout","onHelp","onFeedback","onUpdates","onSettings","Icon","_$createComponent","_$mergeProps","_$memo"],"sources":["../../../../src/components/composites/account-menu/account-menu.tsx"],"sourcesContent":["import {\n CircleHelp,\n Info,\n LogIn,\n LogOut,\n Megaphone,\n RefreshCw,\n Settings2,\n Smartphone,\n UserRound,\n} from 'lucide-solid'\nimport type { ComponentProps, JSX } from 'solid-js'\nimport { For, Show, createSignal, onCleanup, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { Button, type ButtonProps } from '../../ui/button/button'\nimport { SideRailButton } from '../../layout/side-rail/side-rail'\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../../ui/dropdown-menu/dropdown-menu'\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../../ui/tooltip/tooltip'\n\n/**\n * AccountMenu.\n *\n * The trailing control in a rail or a top bar: the account, the way into settings,\n * and the way out. Every desktop application has one and they are all the same menu,\n * which is why it belongs here rather than being redrawn per app.\n *\n * ## The items are the caller's\n *\n * The menu takes a list rather than hard-coding one. An application adds and removes\n * entries — a mobile hand-off that only makes sense on desktop, a feedback link that\n * only exists once there is somewhere to send it — and a component that decided the\n * list could not be used by two products.\n *\n * `disabled` on an item is deliberately visible rather than hidden: a greyed \"Help\n * Center\" says the destination is coming, while an absent one says it does not exist.\n */\nexport type AccountMenuItem = {\n id: string\n label: string\n icon?: JSX.Element\n /** A chord drawn at the trailing edge, e.g. `⌘,`. */\n shortcut?: string\n /**\n * The parseable chord behind `shortcut`, e.g. `Meta+,`. The glyph is\n * `aria-hidden` so the item's accessible name stays exactly its label —\n * same contract as `SideRailItem`. Supply it whenever `shortcut` is drawn.\n */\n keyshortcuts?: string\n disabled?: boolean\n /** Render only on this platform. Omit to always render. */\n platform?: 'desktop' | 'web'\n onSelect?: () => void\n /** Run after the menu's close-focus cycle, with its trigger as a stable opener. */\n onSelectAfterClose?: (trigger: HTMLButtonElement | undefined) => void\n}\n\nexport type AccountMenuProps = {\n class?: string\n /** Anchor a rail menu beside its trigger, or keep the default upward placement. */\n placement?: ComponentProps<typeof DropdownMenu>['placement']\n /** Space between the trigger and menu, in pixels. */\n gutter?: ComponentProps<typeof DropdownMenu>['gutter']\n /** Hide the menu pointer when aligning it beside a compact rail. */\n hideArrow?: boolean\n /** The trigger size. Icon-only menus default to the standard icon size. */\n size?: ButtonProps['size']\n /**\n * Render the trigger as a `SideRailButton` row instead of a bare icon\n * button: same geometry, hover treatment, and flush collapsed-rail tip as\n * every other entry in the surrounding rail. The component's own tooltip\n * is skipped — the rail row owns its help.\n */\n railTrigger?: boolean\n /** The entries above the separator. */\n items: readonly AccountMenuItem[]\n /** Which platform this is rendered on, for items that declare one. */\n platform?: 'desktop' | 'web'\n /** Whether a session exists. Decides the sign-in/sign-out row. */\n authenticated: boolean\n /**\n * Draw the pending-update indicator: an accent dot on the trigger and on the\n * `updates` row — the id `createAppMenuItems` mints, the same convention the\n * fallback icons follow — and extend both accessible names with \"update\n * available\", because the dot itself carries no text. The updater remains the\n * caller's: the prop only mirrors its state into the menu.\n */\n updateAvailable?: boolean\n /** Local-only applications can omit the session actions altogether. */\n showSession?: boolean\n /** Disable the session row while a sign-in or sign-out is in flight. */\n busy?: boolean\n /** The name shown on the trigger's accessible label. */\n label?: string\n onSignIn?: () => void\n onSignOut?: () => void\n /** Fires on hover or focus of the trigger — a chance to prefetch. */\n onIntent?: () => void\n}\n\nconst FALLBACK_ICONS: Record<string, typeof Settings2> = {\n mobile: Smartphone,\n settings: Settings2,\n about: Info,\n help: CircleHelp,\n feedback: Megaphone,\n updates: RefreshCw,\n}\n\nexport function AccountMenu(props: AccountMenuProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'placement',\n 'gutter',\n 'hideArrow',\n 'size',\n 'railTrigger',\n 'items',\n 'platform',\n 'authenticated',\n 'updateAvailable',\n 'showSession',\n 'busy',\n 'label',\n 'onSignIn',\n 'onSignOut',\n 'onIntent',\n ])\n\n let trigger: HTMLButtonElement | undefined\n let pendingAfterClose:\n | { callback: NonNullable<AccountMenuItem['onSelectAfterClose']> }\n | undefined\n let scheduledAfterClose:\n | { callback: NonNullable<AccountMenuItem['onSelectAfterClose']> }\n | undefined\n const [menuOpen, setMenuOpen] = createSignal(false)\n const [tooltipOpen, setTooltipOpen] = createSignal(false)\n\n onCleanup(() => {\n pendingAfterClose = undefined\n scheduledAfterClose = undefined\n })\n\n const visible = () =>\n local.items.filter((item) => !item.platform || item.platform === (local.platform ?? 'desktop'))\n\n // The pending update reaches the user twice on each surface: as a dot and as\n // words. The dot is aria-hidden, so the accessible names carry the state —\n // which is why the names are extended here rather than left to the caller.\n const triggerName = () =>\n `${local.label ?? 'Account and settings'}${local.updateAvailable ? ', update available' : ''}`\n const updateDot = () => (\n <Show when={local.updateAvailable}>\n <span\n data-slot=\"account-menu-update-dot\"\n aria-hidden=\"true\"\n class=\"absolute top-1.5 right-2.5 size-2 rounded-full bg-primary\"\n />\n </Show>\n )\n\n // Built by reference at each return, not once up front: Solid fixes a node's\n // owner chain when the JSX expression runs, and an eagerly created tree would\n // never see the Tooltip provider below — Kobalte's TooltipTrigger then throws\n // `useTooltipContext must be used within a Tooltip component`.\n const menu = () => (\n <DropdownMenu\n modal={false}\n placement={local.placement ?? 'top-start'}\n gutter={local.gutter}\n onOpenChange={(open) => {\n setMenuOpen(open)\n if (open) setTooltipOpen(false)\n }}\n >\n <Show\n when={local.railTrigger}\n fallback={\n <TooltipTrigger\n as={AccountMenuButton}\n ref={(element: HTMLButtonElement) => (trigger = element)}\n variant=\"ghost\"\n size={local.size ?? 'icon-md'}\n class={cn('relative text-muted-foreground', local.class)}\n data-slot=\"account-menu-trigger\"\n aria-label={triggerName()}\n onFocus={() => local.onIntent?.()}\n onPointerEnter={() => local.onIntent?.()}\n {...rest}\n >\n <UserRound aria-hidden=\"true\" />\n {updateDot()}\n </TooltipTrigger>\n }\n >\n {/* Rail parity: the trigger is a SideRailButton row — the same\n geometry and flush collapsed-rail tip as every other rail entry —\n so the composite's own tooltip stays out of the way. */}\n <DropdownMenuTrigger\n as={SideRailButton}\n ref={(element: HTMLButtonElement) => (trigger = element)}\n label={triggerName()}\n class={cn('relative', local.class)}\n data-slot=\"account-menu-trigger\"\n onFocus={() => local.onIntent?.()}\n onPointerEnter={() => local.onIntent?.()}\n {...rest}\n >\n <UserRound aria-hidden=\"true\" />\n {updateDot()}\n </DropdownMenuTrigger>\n </Show>\n <DropdownMenuContent\n hideArrow={local.hideArrow}\n class=\"min-w-56 max-h-(--kb-popper-available-height) overflow-x-hidden overflow-y-auto\"\n onCloseAutoFocus={(event) => {\n const selection = pendingAfterClose\n pendingAfterClose = undefined\n if (!selection) return\n\n event.preventDefault()\n scheduledAfterClose = selection\n queueMicrotask(() => {\n if (scheduledAfterClose !== selection) return\n scheduledAfterClose = undefined\n selection.callback(trigger)\n })\n }}\n >\n <DropdownMenuGroup>\n <For each={visible()}>\n {(item) => (\n <DropdownMenuItem\n disabled={item.disabled}\n aria-label={\n local.updateAvailable && item.id === 'updates'\n ? `${item.label}, update available`\n : undefined\n }\n onSelect={() => {\n pendingAfterClose = item.onSelectAfterClose\n ? { callback: item.onSelectAfterClose }\n : undefined\n item.onSelect?.()\n }}\n shortcut={item.shortcut}\n keyshortcuts={item.keyshortcuts}\n >\n {item.icon ?? renderFallbackIcon(item.id)}\n <span>{item.label}</span>\n <Show when={local.updateAvailable && item.id === 'updates'}>\n <span\n data-slot=\"account-menu-update-dot\"\n aria-hidden=\"true\"\n class=\"ms-auto size-2 shrink-0 rounded-full bg-primary\"\n />\n </Show>\n </DropdownMenuItem>\n )}\n </For>\n </DropdownMenuGroup>\n <Show when={local.showSession !== false}>\n <DropdownMenuSeparator />\n <DropdownMenuGroup>\n <DropdownMenuItem\n disabled={local.busy}\n onSelect={() => (local.authenticated ? local.onSignOut?.() : local.onSignIn?.())}\n >\n <Show when={local.authenticated} fallback={<LogIn aria-hidden=\"true\" />}>\n <LogOut aria-hidden=\"true\" />\n </Show>\n <span>{local.authenticated ? 'Sign out' : 'Sign in'}</span>\n </DropdownMenuItem>\n </DropdownMenuGroup>\n </Show>\n </DropdownMenuContent>\n </DropdownMenu>\n )\n\n if (local.railTrigger) return menu()\n\n return (\n <Tooltip open={tooltipOpen()} onOpenChange={(open) => setTooltipOpen(open && !menuOpen())}>\n {menu()}\n {/* Both overlays own a Popper context; the tooltip must stay outside the menu root. */}\n <TooltipContent placement=\"top\">{local.label ?? 'Account and settings'}</TooltipContent>\n </Tooltip>\n )\n}\n\n/** The common support menu, with one app-specific entry before the shared items. */\nexport function createAppMenuItems(options: {\n primaryItem?: AccountMenuItem\n settingsShortcut?: string\n onAbout?: AccountMenuItem['onSelectAfterClose']\n onHelp?: AccountMenuItem['onSelectAfterClose']\n onFeedback?: AccountMenuItem['onSelectAfterClose']\n onUpdates?: AccountMenuItem['onSelectAfterClose']\n onSettings?: AccountMenuItem['onSelectAfterClose']\n}): AccountMenuItem[] {\n return [\n ...(options.primaryItem ? [options.primaryItem] : []),\n {\n id: 'about',\n label: 'About',\n disabled: !options.onAbout,\n onSelectAfterClose: options.onAbout,\n },\n {\n id: 'help',\n label: 'Help Center',\n disabled: !options.onHelp,\n onSelectAfterClose: options.onHelp,\n },\n {\n id: 'feedback',\n label: 'Send Feedback',\n disabled: !options.onFeedback,\n onSelectAfterClose: options.onFeedback,\n },\n {\n id: 'updates',\n label: 'Updates',\n platform: 'desktop',\n disabled: !options.onUpdates,\n onSelectAfterClose: options.onUpdates,\n },\n {\n id: 'settings',\n label: 'Settings',\n shortcut: options.settingsShortcut,\n disabled: !options.onSettings,\n onSelectAfterClose: options.onSettings,\n },\n ]\n}\n\nfunction AccountMenuButton(props: ComponentProps<typeof Button>) {\n return <DropdownMenuTrigger as={Button} {...props} />\n}\n\n/**\n * A known item id gets its conventional glyph, so a caller that only wants the\n * standard menu does not have to import six icons to get it.\n */\nfunction renderFallbackIcon(id: string): JSX.Element {\n const Icon = FALLBACK_ICONS[id]\n return Icon ? <Icon aria-hidden=\"true\" /> : null\n}\n"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { ComponentProps } from 'solid-js';
2
+ import { Button } from '../ui/button/button';
3
+ export type ComposerMenuProps = ComponentProps<'div'> & {
4
+ /** A name for what the menu offers, e.g. "Mention an Agent". */
5
+ label?: string;
6
+ };
7
+ /**
8
+ * ComposerMenu.
9
+ *
10
+ * The panel a composer raises above the field: mention suggestions while the
11
+ * reader types a name, the picker of what can be attached, follow-up prompts.
12
+ * It is deliberately *not* a `Menu`: there is no trigger and no focus trap —
13
+ * the reader keeps typing while it is open, and it opens and closes on the
14
+ * draft's own grammar rather than on a button press.
15
+ *
16
+ * Rows are `ComposerMenuItem`s; anything else a caller renders (a checkbox
17
+ * list, a heading) sits inside the same panel. Placement stays with the host —
18
+ * a suggestion menu renders in the composer's `menu` slot, a picker anchors to
19
+ * its own control.
20
+ */
21
+ export declare function ComposerMenu(props: ComposerMenuProps): import("solid-js").JSX.Element;
22
+ export type ComposerMenuItemProps = ComponentProps<typeof Button>;
23
+ /**
24
+ * ComposerMenuItem.
25
+ *
26
+ * One row of a `ComposerMenu`. A ghost button stretched across the panel —
27
+ * the same row every menu in the library draws, so a suggestion never reads as
28
+ * a stray button floating over the draft.
29
+ */
30
+ export declare function ComposerMenuItem(props: ComposerMenuItemProps): import("solid-js").JSX.Element;
31
+ //# sourceMappingURL=composer-menu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer-menu.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/composer-menu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAI9C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAE5C,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG;IACtD,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,kCAkBpD;AAED,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,OAAO,MAAM,CAAC,CAAA;AAEjE;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,kCAY5D"}
@@ -0,0 +1,63 @@
1
+ import { cn } from "../../lib/utils.js";
2
+ import { overlaySurface } from "../../lib/overlay.js";
3
+ import { Button } from "../ui/button/button.js";
4
+ import { createComponent, insert, mergeProps, spread, template } from "solid-js/web";
5
+ import { splitProps } from "solid-js";
6
+ //#region src/components/conversation/composer-menu.tsx
7
+ var _tmpl$ = /*#__PURE__*/ template(`<div role=group data-slot=composer-menu>`);
8
+ /**
9
+ * ComposerMenu.
10
+ *
11
+ * The panel a composer raises above the field: mention suggestions while the
12
+ * reader types a name, the picker of what can be attached, follow-up prompts.
13
+ * It is deliberately *not* a `Menu`: there is no trigger and no focus trap —
14
+ * the reader keeps typing while it is open, and it opens and closes on the
15
+ * draft's own grammar rather than on a button press.
16
+ *
17
+ * Rows are `ComposerMenuItem`s; anything else a caller renders (a checkbox
18
+ * list, a heading) sits inside the same panel. Placement stays with the host —
19
+ * a suggestion menu renders in the composer's `menu` slot, a picker anchors to
20
+ * its own control.
21
+ */
22
+ function ComposerMenu(props) {
23
+ const [local, rest] = splitProps(props, [
24
+ "class",
25
+ "label",
26
+ "children"
27
+ ]);
28
+ return (() => {
29
+ var _el$ = _tmpl$();
30
+ spread(_el$, mergeProps({
31
+ get ["aria-label"]() {
32
+ return local.label;
33
+ },
34
+ get ["class"]() {
35
+ return cn(overlaySurface, "flex max-h-56 min-w-60 flex-col gap-0.5 overflow-y-auto p-1", local.class);
36
+ }
37
+ }, rest), false, true);
38
+ insert(_el$, () => local.children);
39
+ return _el$;
40
+ })();
41
+ }
42
+ /**
43
+ * ComposerMenuItem.
44
+ *
45
+ * One row of a `ComposerMenu`. A ghost button stretched across the panel —
46
+ * the same row every menu in the library draws, so a suggestion never reads as
47
+ * a stray button floating over the draft.
48
+ */
49
+ function ComposerMenuItem(props) {
50
+ const [local, rest] = splitProps(props, ["class", "children"]);
51
+ return createComponent(Button, mergeProps({
52
+ variant: "ghost",
53
+ get ["class"]() {
54
+ return cn("w-full justify-start gap-2 text-start font-normal", local.class);
55
+ }
56
+ }, rest, { get children() {
57
+ return local.children;
58
+ } }));
59
+ }
60
+ //#endregion
61
+ export { ComposerMenu, ComposerMenuItem };
62
+
63
+ //# sourceMappingURL=composer-menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer-menu.js","names":["ComponentProps","splitProps","cn","overlaySurface","Button","ComposerMenuProps","label","ComposerMenu","props","local","rest","_el$","_tmpl$","_$spread","_$mergeProps","aria-label","class","_$insert","children","ComposerMenuItemProps","ComposerMenuItem","_$createComponent","variant","_$mergeProps","_$createComponent"],"sources":["../../../src/components/conversation/composer-menu.tsx"],"sourcesContent":["import type { ComponentProps } from 'solid-js'\nimport { splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { overlaySurface } from '#lib/overlay'\nimport { Button } from '../ui/button/button'\n\nexport type ComposerMenuProps = ComponentProps<'div'> & {\n /** A name for what the menu offers, e.g. \"Mention an Agent\". */\n label?: string\n}\n\n/**\n * ComposerMenu.\n *\n * The panel a composer raises above the field: mention suggestions while the\n * reader types a name, the picker of what can be attached, follow-up prompts.\n * It is deliberately *not* a `Menu`: there is no trigger and no focus trap —\n * the reader keeps typing while it is open, and it opens and closes on the\n * draft's own grammar rather than on a button press.\n *\n * Rows are `ComposerMenuItem`s; anything else a caller renders (a checkbox\n * list, a heading) sits inside the same panel. Placement stays with the host —\n * a suggestion menu renders in the composer's `menu` slot, a picker anchors to\n * its own control.\n */\nexport function ComposerMenu(props: ComposerMenuProps) {\n const [local, rest] = splitProps(props, ['class', 'label', 'children'])\n\n return (\n <div\n role=\"group\"\n aria-label={local.label}\n data-slot=\"composer-menu\"\n class={cn(\n overlaySurface,\n 'flex max-h-56 min-w-60 flex-col gap-0.5 overflow-y-auto p-1',\n local.class\n )}\n {...rest}\n >\n {local.children}\n </div>\n )\n}\n\nexport type ComposerMenuItemProps = ComponentProps<typeof Button>\n\n/**\n * ComposerMenuItem.\n *\n * One row of a `ComposerMenu`. A ghost button stretched across the panel —\n * the same row every menu in the library draws, so a suggestion never reads as\n * a stray button floating over the draft.\n */\nexport function ComposerMenuItem(props: ComposerMenuItemProps) {\n const [local, rest] = splitProps(props, ['class', 'children'])\n\n return (\n <Button\n variant=\"ghost\"\n class={cn('w-full justify-start gap-2 text-start font-normal', local.class)}\n {...rest}\n >\n {local.children}\n </Button>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgBO,aAAaC,OAA0B;CACrD,MAAM,CAACC,OAAOC,QAAQT,WAAWO,OAAO;EAAC;EAAS;EAAS;CAAU,CAAC;CAEtE,cAAA;EAAA,IAAAG,OAAAC,OAAA;EAAAC,OAAAF,MAAAG,WAAA;GAAA,KAAA,gBAAA;IAAA,OAGgBL,MAAMH;GAAK;GAAA,KAAA,WAAA;IAAA,OAEhBJ,GACLC,gBACA,+DACAM,MAAMO,KACR;GAAC;EAAA,GACGN,IAAI,GAAA,OAAA,IAAA;EAAAO,OAAAN,YAEPF,MAAMS,QAAQ;EAAA,OAAAP;CAAA,EAAA,CAAA;AAGrB;;;;;;;;AAWA,SAAgBS,iBAAiBZ,OAA8B;CAC7D,MAAM,CAACC,OAAOC,QAAQT,WAAWO,OAAO,CAAC,SAAS,UAAU,CAAC;CAE7D,OAAAa,gBACGjB,QAAMU,WAAA;EACLQ,SAAO;EAAA,KAAA,WAAA;GAAA,OACApB,GAAG,qDAAqDO,MAAMO,KAAK;EAAC;CAAA,GACvEN,MAAI,EAAA,IAAAQ,WAAA;EAAA,OAEPT,MAAMS;CAAQ,EAAA,CAAA,CAAA;AAGrB"}
@@ -0,0 +1,38 @@
1
+ import type { ComponentProps, JSX } from 'solid-js';
2
+ export type ConversationPaneProps = ComponentProps<'section'> & {
3
+ /** Above the transcript: the conversation's identity and its actions. */
4
+ header?: JSX.Element;
5
+ /** The transcript — typically a `ConversationSurface` with `gutter`. */
6
+ children: JSX.Element;
7
+ /** Below the transcript: the composer. */
8
+ composer?: JSX.Element;
9
+ /**
10
+ * The thread side panel. Its presence opens the second column; at narrow
11
+ * widths the panel overlays the conversation instead of squeezing it, and
12
+ * the conversation chrome steps aside — out of the accessibility tree with
13
+ * it, which is why the overlay hides with `visibility` rather than paint.
14
+ */
15
+ thread?: JSX.Element;
16
+ /**
17
+ * Apply the shared page gutters to the composer, tracking the transcript's
18
+ * own gutter (`ConversationSurface`'s `gutter`) so the two read as one pane.
19
+ */
20
+ gutter?: boolean;
21
+ };
22
+ /**
23
+ * ConversationPane.
24
+ *
25
+ * The frame a conversation lives in: a header above, the transcript, a composer
26
+ * below, and — while a thread is open — the thread panel beside all of it.
27
+ *
28
+ * The layout earns its place in the library because the thread is where
29
+ * applications get it wrong. Two impulses both fail: shrinking the transcript
30
+ * to make room turns a busy thread into a one-word-per-line stripe, and
31
+ * *hiding* the transcript with `display: none` unmounts it, losing scroll
32
+ * position and DOM. So the desktop layout gives the thread a fixed column and
33
+ * leaves the transcript fluid; below the collapse width the panel overlays the
34
+ * conversation in full and the conversation steps aside invisibly — still
35
+ * mounted, still in the DOM, gone from the accessibility tree.
36
+ */
37
+ export declare function ConversationPane(props: ConversationPaneProps): JSX.Element;
38
+ //# sourceMappingURL=conversation-pane.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-pane.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/conversation-pane.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAInD,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,SAAS,CAAC,GAAG;IAC9D,yEAAyE;IACzE,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,wEAAwE;IACxE,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAA;IACrB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACtB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,eAsE5D"}
@@ -0,0 +1,83 @@
1
+ import { cn } from "../../lib/utils.js";
2
+ import { className, createComponent, effect, insert, mergeProps, spread, template } from "solid-js/web";
3
+ import { Show, splitProps } from "solid-js";
4
+ //#region src/components/conversation/conversation-pane.tsx
5
+ var _tmpl$ = /*#__PURE__*/ template(`<section data-slot=conversation-pane><div>`);
6
+ var _tmpl$2 = /*#__PURE__*/ template(`<div>`);
7
+ /**
8
+ * ConversationPane.
9
+ *
10
+ * The frame a conversation lives in: a header above, the transcript, a composer
11
+ * below, and — while a thread is open — the thread panel beside all of it.
12
+ *
13
+ * The layout earns its place in the library because the thread is where
14
+ * applications get it wrong. Two impulses both fail: shrinking the transcript
15
+ * to make room turns a busy thread into a one-word-per-line stripe, and
16
+ * *hiding* the transcript with `display: none` unmounts it, losing scroll
17
+ * position and DOM. So the desktop layout gives the thread a fixed column and
18
+ * leaves the transcript fluid; below the collapse width the panel overlays the
19
+ * conversation in full and the conversation steps aside invisibly — still
20
+ * mounted, still in the DOM, gone from the accessibility tree.
21
+ */
22
+ function ConversationPane(props) {
23
+ const [local, rest] = splitProps(props, [
24
+ "class",
25
+ "header",
26
+ "composer",
27
+ "thread",
28
+ "gutter",
29
+ "children"
30
+ ]);
31
+ const threadOpen = () => local.thread !== void 0;
32
+ return (() => {
33
+ var _el$ = _tmpl$(), _el$2 = _el$.firstChild;
34
+ spread(_el$, mergeProps({
35
+ get ["data-conversation-thread-open"]() {
36
+ return threadOpen() || void 0;
37
+ },
38
+ get ["class"]() {
39
+ return cn("relative grid h-full min-h-0 grid-rows-[auto_minmax(0,1fr)_auto]", threadOpen() ? "grid-cols-1 md:grid-cols-[minmax(20rem,1fr)_minmax(18rem,22rem)] lg:grid-cols-[minmax(24rem,1fr)_minmax(20rem,26rem)]" : "grid-cols-1", local.class);
40
+ }
41
+ }, rest), false, true);
42
+ insert(_el$, createComponent(Show, {
43
+ get when() {
44
+ return local.header;
45
+ },
46
+ children: (header) => (() => {
47
+ var _el$3 = _tmpl$2();
48
+ insert(_el$3, header);
49
+ effect(() => className(_el$3, cn("col-start-1 row-start-1 min-w-0", threadOpen() && "max-md:invisible")));
50
+ return _el$3;
51
+ })()
52
+ }), _el$2);
53
+ insert(_el$2, () => local.children);
54
+ insert(_el$, createComponent(Show, {
55
+ get when() {
56
+ return local.composer;
57
+ },
58
+ children: (composer) => (() => {
59
+ var _el$4 = _tmpl$2();
60
+ insert(_el$4, composer);
61
+ effect(() => className(_el$4, cn("col-start-1 row-start-3 min-w-0", local.gutter && "mx-[clamp(0.8rem,3vw,2.5rem)] mb-[1.15rem] max-[30rem]:mx-2", threadOpen() && "max-md:invisible")));
62
+ return _el$4;
63
+ })()
64
+ }), null);
65
+ insert(_el$, createComponent(Show, {
66
+ get when() {
67
+ return local.thread;
68
+ },
69
+ children: (thread) => (() => {
70
+ var _el$5 = _tmpl$2();
71
+ insert(_el$5, thread);
72
+ effect(() => className(_el$5, cn("min-h-0 min-w-0", threadOpen() && "max-md:absolute max-md:inset-0 max-md:z-(--z-docked) max-md:*:border-s-0 md:col-start-2 md:row-span-3 md:row-start-1")));
73
+ return _el$5;
74
+ })()
75
+ }), null);
76
+ effect(() => className(_el$2, cn("col-start-1 row-start-2 flex min-h-0 min-w-0 flex-col", threadOpen() && "max-md:invisible")));
77
+ return _el$;
78
+ })();
79
+ }
80
+ //#endregion
81
+ export { ConversationPane };
82
+
83
+ //# sourceMappingURL=conversation-pane.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-pane.js","names":["ComponentProps","JSX","Show","splitProps","cn","ConversationPaneProps","header","Element","children","composer","thread","gutter","ConversationPane","props","local","rest","threadOpen","undefined","_el$","_tmpl$","_el$2","firstChild","_$spread","_$mergeProps","data-conversation-thread-open","class","_$insert","_$createComponent","when","_el$3","_tmpl$2","_$effect","_$className","_el$4","_el$5","_$mergeProps","_$createComponent","_$className"],"sources":["../../../src/components/conversation/conversation-pane.tsx"],"sourcesContent":["import type { ComponentProps, JSX } from 'solid-js'\nimport { Show, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\n\nexport type ConversationPaneProps = ComponentProps<'section'> & {\n /** Above the transcript: the conversation's identity and its actions. */\n header?: JSX.Element\n /** The transcript — typically a `ConversationSurface` with `gutter`. */\n children: JSX.Element\n /** Below the transcript: the composer. */\n composer?: JSX.Element\n /**\n * The thread side panel. Its presence opens the second column; at narrow\n * widths the panel overlays the conversation instead of squeezing it, and\n * the conversation chrome steps aside — out of the accessibility tree with\n * it, which is why the overlay hides with `visibility` rather than paint.\n */\n thread?: JSX.Element\n /**\n * Apply the shared page gutters to the composer, tracking the transcript's\n * own gutter (`ConversationSurface`'s `gutter`) so the two read as one pane.\n */\n gutter?: boolean\n}\n\n/**\n * ConversationPane.\n *\n * The frame a conversation lives in: a header above, the transcript, a composer\n * below, and — while a thread is open — the thread panel beside all of it.\n *\n * The layout earns its place in the library because the thread is where\n * applications get it wrong. Two impulses both fail: shrinking the transcript\n * to make room turns a busy thread into a one-word-per-line stripe, and\n * *hiding* the transcript with `display: none` unmounts it, losing scroll\n * position and DOM. So the desktop layout gives the thread a fixed column and\n * leaves the transcript fluid; below the collapse width the panel overlays the\n * conversation in full and the conversation steps aside invisibly — still\n * mounted, still in the DOM, gone from the accessibility tree.\n */\nexport function ConversationPane(props: ConversationPaneProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'header',\n 'composer',\n 'thread',\n 'gutter',\n 'children',\n ])\n const threadOpen = () => local.thread !== undefined\n\n return (\n <section\n data-slot=\"conversation-pane\"\n data-conversation-thread-open={threadOpen() || undefined}\n class={cn(\n 'relative grid h-full min-h-0 grid-rows-[auto_minmax(0,1fr)_auto]',\n threadOpen()\n ? 'grid-cols-1 md:grid-cols-[minmax(20rem,1fr)_minmax(18rem,22rem)] lg:grid-cols-[minmax(24rem,1fr)_minmax(20rem,26rem)]'\n : 'grid-cols-1',\n local.class\n )}\n {...rest}\n >\n <Show when={local.header}>\n {(header) => (\n <div class={cn('col-start-1 row-start-1 min-w-0', threadOpen() && 'max-md:invisible')}>\n {header()}\n </div>\n )}\n </Show>\n\n <div\n class={cn(\n 'col-start-1 row-start-2 flex min-h-0 min-w-0 flex-col',\n threadOpen() && 'max-md:invisible'\n )}\n >\n {local.children}\n </div>\n\n <Show when={local.composer}>\n {(composer) => (\n <div\n class={cn(\n 'col-start-1 row-start-3 min-w-0',\n local.gutter && 'mx-[clamp(0.8rem,3vw,2.5rem)] mb-[1.15rem] max-[30rem]:mx-2',\n threadOpen() && 'max-md:invisible'\n )}\n >\n {composer()}\n </div>\n )}\n </Show>\n\n <Show when={local.thread}>\n {(thread) => (\n <div\n class={cn(\n 'min-h-0 min-w-0',\n threadOpen() &&\n 'max-md:absolute max-md:inset-0 max-md:z-(--z-docked) max-md:*:border-s-0 md:col-start-2 md:row-span-3 md:row-start-1'\n )}\n >\n {thread()}\n </div>\n )}\n </Show>\n </section>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwCA,SAAgBY,iBAAiBC,OAA8B;CAC7D,MAAM,CAACC,OAAOC,QAAQZ,WAAWU,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;CAAU,CACX;CACD,MAAMG,mBAAmBF,MAAMJ,WAAWO,KAAAA;CAE1C,cAAA;EAAA,IAAAC,OAAAC,OAAA,GAAAC,QAAAF,KAAAG;EAAAC,OAAAJ,MAAAK,WAAA;GAAA,KAAA,mCAAA;IAAA,OAGmCP,WAAW,KAAKC,KAAAA;GAAS;GAAA,KAAA,WAAA;IAAA,OACjDb,GACL,oEACAY,WAAW,IACP,0HACA,eACJF,MAAMW,KACR;GAAC;EAAA,GACGV,IAAI,GAAA,OAAA,IAAA;EAAAW,OAAAR,MAAAS,gBAEPzB,MAAI;GAAA,IAAC0B,OAAI;IAAA,OAAEd,MAAMR;GAAM;GAAAE,WACpBF,kBAAM;IAAA,IAAAuB,QAAAC,QAAA;IAAAJ,OAAAG,OAEHvB,MAAM;IAAAyB,aAAAC,UAAAH,OADGzB,GAAG,mCAAmCY,WAAW,KAAK,kBAAkB,CAAC,CAAA;IAAA,OAAAa;GAAA,EAAA,CAAA;EAGtF,CAAA,GAAAT,KAAA;EAAAM,OAAAN,aASAN,MAAMN,QAAQ;EAAAkB,OAAAR,MAAAS,gBAGhBzB,MAAI;GAAA,IAAC0B,OAAI;IAAA,OAAEd,MAAML;GAAQ;GAAAD,WACtBC,oBAAQ;IAAA,IAAAwB,QAAAH,QAAA;IAAAJ,OAAAO,OAQLxB,QAAQ;IAAAsB,aAAAC,UAAAC,OANF7B,GACL,mCACAU,MAAMH,UAAU,+DAChBK,WAAW,KAAK,kBAClB,CAAC,CAAA;IAAA,OAAAiB;GAAA,EAAA,CAAA;EAIJ,CAAA,GAAA,IAAA;EAAAP,OAAAR,MAAAS,gBAGFzB,MAAI;GAAA,IAAC0B,OAAI;IAAA,OAAEd,MAAMJ;GAAM;GAAAF,WACpBE,kBAAM;IAAA,IAAAwB,QAAAJ,QAAA;IAAAJ,OAAAQ,OAQHxB,MAAM;IAAAqB,aAAAC,UAAAE,OANA9B,GACL,mBACAY,WAAW,KACT,sHACJ,CAAC,CAAA;IAAA,OAAAkB;GAAA,EAAA,CAAA;EAIJ,CAAA,GAAA,IAAA;EAAAH,aAAAC,UAAAZ,OAjCMhB,GACL,yDACAY,WAAW,KAAK,kBAClB,CAAC,CAAA;EAAA,OAAAE;CAAA,EAAA,CAAA;AAkCT"}
@@ -34,6 +34,12 @@ export type ConversationSurfaceProps = ComponentProps<'div'> & {
34
34
  empty?: JSX.Element;
35
35
  /** A header that scrolls with the transcript, e.g. a day divider or a banner. */
36
36
  header?: JSX.Element;
37
+ /**
38
+ * The shared page gutter: reading padding inside the scroller plus a stable
39
+ * gutter, so the scrollbar never shifts the text when it appears. The page
40
+ * frame around the surface is `ConversationPane`'s job.
41
+ */
42
+ gutter?: boolean;
37
43
  };
38
44
  export declare function ConversationSurface(props: ConversationSurfaceProps): JSX.Element;
39
45
  //# sourceMappingURL=conversation-surface.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-surface.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/conversation-surface.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAMnD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,2BAA2B,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAAA;AAEvF,MAAM,MAAM,wBAAwB,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG;IAC7D,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,sFAAsF;IACtF,sBAAsB,CAAC,EAAE,2BAA2B,CAAA;IACpD,kFAAkF;IAClF,uBAAuB,CAAC,EAAE,CAAC,QAAQ,EAAE,2BAA2B,KAAK,IAAI,CAAA;IACzE,iEAAiE;IACjE,KAAK,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACnB,iFAAiF;IACjF,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CACrB,CAAA;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,eAuElE"}
1
+ {"version":3,"file":"conversation-surface.d.ts","sourceRoot":"","sources":["../../../src/components/conversation/conversation-surface.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAMnD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,2BAA2B,GAAG,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAAA;AAEvF,MAAM,MAAM,wBAAwB,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG;IAC7D,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,sFAAsF;IACtF,sBAAsB,CAAC,EAAE,2BAA2B,CAAA;IACpD,kFAAkF;IAClF,uBAAuB,CAAC,EAAE,CAAC,QAAQ,EAAE,2BAA2B,KAAK,IAAI,CAAA;IACzE,iEAAiE;IACjE,KAAK,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACnB,iFAAiF;IACjF,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,eA4ElE"}
@@ -6,7 +6,7 @@ import { ArrowDown } from "lucide-solid";
6
6
  import { Show, splitProps } from "solid-js";
7
7
  //#region src/components/conversation/conversation-surface.tsx
8
8
  var _tmpl$ = /*#__PURE__*/ template(`<div class="pointer-events-none absolute inset-x-0 bottom-3 flex justify-center">`);
9
- var _tmpl$2 = /*#__PURE__*/ template(`<div data-slot=conversation-surface><div class="min-h-0 flex-1 overflow-y-auto overscroll-contain"><div data-slot=conversation-content>`);
9
+ var _tmpl$2 = /*#__PURE__*/ template(`<div data-slot=conversation-surface><div><div data-slot=conversation-content>`);
10
10
  var _tmpl$3 = /*#__PURE__*/ template(`<div class=p-4>`);
11
11
  function ConversationSurface(props) {
12
12
  const [local, rest] = splitProps(props, [
@@ -18,6 +18,7 @@ function ConversationSurface(props) {
18
18
  "onReadingPositionChange",
19
19
  "empty",
20
20
  "header",
21
+ "gutter",
21
22
  "children",
22
23
  "onScroll",
23
24
  "ref",
@@ -43,9 +44,14 @@ function ConversationSurface(props) {
43
44
  follow.bindScroller(element);
44
45
  if (typeof local.ref === "function") local.ref(element);
45
46
  }, _el$2);
46
- spread(_el$2, mergeProps({ get tabindex() {
47
- return local.tabindex ?? 0;
48
- } }, rest), false, true);
47
+ spread(_el$2, mergeProps({
48
+ get tabindex() {
49
+ return local.tabindex ?? 0;
50
+ },
51
+ get ["class"]() {
52
+ return cn("min-h-0 flex-1 overflow-y-auto overscroll-contain", local.gutter && "px-[clamp(0.8rem,3vw,2.5rem)] pt-[1.35rem] pb-[1.1rem] [scrollbar-gutter:stable] max-[30rem]:px-2");
53
+ }
54
+ }, rest), false, true);
49
55
  var _ref$ = follow.bindContent;
50
56
  typeof _ref$ === "function" ? use(_ref$, _el$3) : follow.bindContent = _el$3;
51
57
  insert(_el$3, createComponent(Show, {
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-surface.js","names":["ArrowDown","ComponentProps","JSX","Show","splitProps","cn","Button","createScrollFollow","ConversationReadingPosition","Readonly","top","following","ConversationSurfaceProps","threshold","follow","resetKey","initialReadingPosition","onReadingPositionChange","position","empty","Element","header","ConversationSurface","props","local","rest","enabled","initialPosition","onPositionChange","onScroll","EventHandlerUnion","HTMLDivElement","Event","event","handler","_el$","_tmpl$2","_el$2","firstChild","_el$3","addEventListener","_$use","element","bindScroller","ref","_$spread","_$mergeProps","tabindex","_ref$","bindContent","_$insert","_$createComponent","when","children","fallback","_el$5","_tmpl$3","atBottom","_el$4","_tmpl$","type","size","variant","onClick","jump","_$effect","_$className","class","_$mergeProps","_$use","_$createComponent","_$className"],"sources":["../../../src/components/conversation/conversation-surface.tsx"],"sourcesContent":["import { ArrowDown } from 'lucide-solid'\nimport type { ComponentProps, JSX } from 'solid-js'\nimport { Show, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { Button } from '../ui/button/button'\nimport { createScrollFollow } from './scroll-follow'\n\n/**\n * ConversationSurface.\n *\n * The scrolling transcript. Its job is small and it is the difference between a\n * transcript that stays readable as it grows and one that fights the reader:\n *\n * - **Stick to the bottom.** New messages scroll into view only when the reader is\n * already at the bottom. Someone reading back through history must not be yanked\n * to the end by an arriving message, and that is the single most common way a\n * chat surface is made unusable.\n * - **Offer a way back.** Once the reader is not at the bottom, a control appears\n * that returns them. Pixel distance cannot prove an unread message count.\n * - **`overscroll-contain`.** The transcript scrolls to its end and stops, rather\n * than dragging the pane behind it — which in a desktop shell means the whole\n * window moves.\n */\nexport type ConversationReadingPosition = Readonly<{ top: number; following: boolean }>\n\nexport type ConversationSurfaceProps = ComponentProps<'div'> & {\n /** How far from the bottom still counts as \"at the bottom\", in px. */\n threshold?: number\n /** Disable automatic positioning while the host owns scroll restoration. */\n follow?: boolean\n /** Optional conversation identity: changing it re-arms follow at the bottom. */\n resetKey?: string\n /** Host snapshot consumed on mount/identity reset, never on ordinary prop updates. */\n initialReadingPosition?: ConversationReadingPosition\n /** Actual follow intent and native offset; storage/identity remain host-owned. */\n onReadingPositionChange?: (position: ConversationReadingPosition) => void\n /** Rendered above the transcript when there is nothing in it. */\n empty?: JSX.Element\n /** A header that scrolls with the transcript, e.g. a day divider or a banner. */\n header?: JSX.Element\n}\n\nexport function ConversationSurface(props: ConversationSurfaceProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'threshold',\n 'follow',\n 'resetKey',\n 'initialReadingPosition',\n 'onReadingPositionChange',\n 'empty',\n 'header',\n 'children',\n 'onScroll',\n 'ref',\n 'tabindex',\n ])\n // Kobalte provides the control's semantics; transcript follow is a native\n // geometry contract. KiroCrew's plain-scroller controller handles streaming,\n // content collapse and pane resizing without treating them as user consent.\n const follow = createScrollFollow({\n enabled: () => local.follow !== false,\n resetKey: () => local.resetKey,\n threshold: () => local.threshold ?? 80,\n initialPosition: () => local.initialReadingPosition,\n onPositionChange: (position) => local.onReadingPositionChange?.(position),\n })\n const onScroll: JSX.EventHandlerUnion<HTMLDivElement, Event> = (event) => {\n follow.onScroll()\n const handler = local.onScroll\n if (typeof handler === 'function') handler(event)\n else if (handler) handler[0](handler[1], event)\n }\n\n return (\n <div\n data-slot=\"conversation-surface\"\n class={cn('relative flex min-h-0 flex-1 flex-col', local.class)}\n >\n <div\n ref={(element) => {\n follow.bindScroller(element)\n if (typeof local.ref === 'function') local.ref(element)\n }}\n onScroll={onScroll}\n tabindex={local.tabindex ?? 0}\n class=\"min-h-0 flex-1 overflow-y-auto overscroll-contain\"\n {...rest}\n >\n <div ref={follow.bindContent} data-slot=\"conversation-content\">\n <Show when={local.header}>{local.header}</Show>\n <Show when={local.children} fallback={<div class=\"p-4\">{local.empty}</div>}>\n {local.children}\n </Show>\n </div>\n </div>\n\n <Show when={!follow.atBottom()}>\n <div class=\"pointer-events-none absolute inset-x-0 bottom-3 flex justify-center\">\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n class=\"pointer-events-auto gap-1.5 shadow-md\"\n onClick={follow.jump}\n >\n <ArrowDown />\n Jump to latest\n </Button>\n </div>\n </Show>\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;AA0CA,SAAgBsB,oBAAoBC,OAAiC;CACnE,MAAM,CAACC,OAAOC,QAAQrB,WAAWmB,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAAU,CACX;CAID,MAAMT,SAASP,mBAAmB;EAChCmB,eAAeF,MAAMV,WAAW;EAChCC,gBAAgBS,MAAMT;EACtBF,iBAAiBW,MAAMX,aAAa;EACpCc,uBAAuBH,MAAMR;EAC7BY,mBAAmBV,aAAaM,MAAMP,0BAA0BC,QAAQ;CAC1E,CAAC;CACD,MAAMW,YAA0DI,UAAU;EACxEnB,OAAOe,SAAS;EAChB,MAAMK,UAAUV,MAAMK;EACtB,IAAI,OAAOK,YAAY,YAAYA,QAAQD,KAAK;OAC3C,IAAIC,SAASA,QAAQ,EAAE,CAACA,QAAQ,IAAID,KAAK;CAChD;CAEA,cAAA;EAAA,IAAAE,OAAAC,QAAA,GAAAC,QAAAF,KAAAG,YAAAC,QAAAF,MAAAC;EAAAD,MAAAG,iBAAA,UAUgBX,QAAQ;EAAAY,KAJZC,YAAY;GAChB5B,OAAO6B,aAAaD,OAAO;GAC3B,IAAI,OAAOlB,MAAMoB,QAAQ,YAAYpB,MAAMoB,IAAIF,OAAO;EACxD,GAACL,KAAA;EAAAQ,OAAAR,OAAAS,WAAA,EAAA,IAEDC,WAAQ;GAAA,OAAEvB,MAAMuB,YAAY;EAAC,EAAA,GAEzBtB,IAAI,GAAA,OAAA,IAAA;EAAA,IAAAuB,QAEElC,OAAOmC;EAAW,OAAAD,UAAA,aAAAP,IAAAO,OAAAT,KAAA,IAAlBzB,OAAOmC,cAAWV;EAAAW,OAAAX,OAAAY,gBACzBhD,MAAI;GAAA,IAACiD,OAAI;IAAA,OAAE5B,MAAMH;GAAM;GAAA,IAAAgC,WAAA;IAAA,OAAG7B,MAAMH;GAAM;EAAA,CAAA,GAAA,IAAA;EAAA6B,OAAAX,OAAAY,gBACtChD,MAAI;GAAA,IAACiD,OAAI;IAAA,OAAE5B,MAAM6B;GAAQ;GAAA,IAAEC,WAAQ;IAAA,cAAA;KAAA,IAAAC,QAAAC,QAAA;KAAAN,OAAAK,aAAoB/B,MAAML,KAAK;KAAA,OAAAoC;IAAA,EAAA,CAAA;GAAA;GAAA,IAAAF,WAAA;IAAA,OAChE7B,MAAM6B;GAAQ;EAAA,CAAA,GAAA,IAAA;EAAAH,OAAAf,MAAAgB,gBAKpBhD,MAAI;GAAA,IAACiD,OAAI;IAAA,OAAE,CAACtC,OAAO2C,SAAS;GAAC;GAAA,IAAAJ,WAAA;IAAA,IAAAK,QAAAC,OAAA;IAAAT,OAAAQ,OAAAP,gBAEzB7C,QAAM;KACLsD,MAAI;KACJC,MAAI;KACJC,SAAO;KAAA,SAAA;KAAA,IAEPC,UAAO;MAAA,OAAEjD,OAAOkD;KAAI;KAAA,IAAAX,WAAA;MAAA,OAAA,CAAAF,gBAEnBnD,WAAS,CAAA,CAAA,GAAA,gBAAA;KAAA;IAAA,CAAA,CAAA;IAAA,OAAA0D;GAAA;EAAA,CAAA,GAAA,IAAA;EAAAO,aAAAC,UAAA/B,MA7BT9B,GAAG,yCAAyCmB,MAAM2C,KAAK,CAAC,CAAA;EAAA,OAAAhC;CAAA,EAAA,CAAA;AAoCrE"}
1
+ {"version":3,"file":"conversation-surface.js","names":["ArrowDown","ComponentProps","JSX","Show","splitProps","cn","Button","createScrollFollow","ConversationReadingPosition","Readonly","top","following","ConversationSurfaceProps","threshold","follow","resetKey","initialReadingPosition","onReadingPositionChange","position","empty","Element","header","gutter","ConversationSurface","props","local","rest","enabled","initialPosition","onPositionChange","onScroll","EventHandlerUnion","HTMLDivElement","Event","event","handler","_el$","_tmpl$2","_el$2","firstChild","_el$3","addEventListener","_$use","element","bindScroller","ref","_$spread","_$mergeProps","tabindex","class","_ref$","bindContent","_$insert","_$createComponent","when","children","fallback","_el$5","_tmpl$3","atBottom","_el$4","_tmpl$","type","size","variant","onClick","jump","_$effect","_$className","_$mergeProps","_$use","_$createComponent","_$className"],"sources":["../../../src/components/conversation/conversation-surface.tsx"],"sourcesContent":["import { ArrowDown } from 'lucide-solid'\nimport type { ComponentProps, JSX } from 'solid-js'\nimport { Show, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { Button } from '../ui/button/button'\nimport { createScrollFollow } from './scroll-follow'\n\n/**\n * ConversationSurface.\n *\n * The scrolling transcript. Its job is small and it is the difference between a\n * transcript that stays readable as it grows and one that fights the reader:\n *\n * - **Stick to the bottom.** New messages scroll into view only when the reader is\n * already at the bottom. Someone reading back through history must not be yanked\n * to the end by an arriving message, and that is the single most common way a\n * chat surface is made unusable.\n * - **Offer a way back.** Once the reader is not at the bottom, a control appears\n * that returns them. Pixel distance cannot prove an unread message count.\n * - **`overscroll-contain`.** The transcript scrolls to its end and stops, rather\n * than dragging the pane behind it — which in a desktop shell means the whole\n * window moves.\n */\nexport type ConversationReadingPosition = Readonly<{ top: number; following: boolean }>\n\nexport type ConversationSurfaceProps = ComponentProps<'div'> & {\n /** How far from the bottom still counts as \"at the bottom\", in px. */\n threshold?: number\n /** Disable automatic positioning while the host owns scroll restoration. */\n follow?: boolean\n /** Optional conversation identity: changing it re-arms follow at the bottom. */\n resetKey?: string\n /** Host snapshot consumed on mount/identity reset, never on ordinary prop updates. */\n initialReadingPosition?: ConversationReadingPosition\n /** Actual follow intent and native offset; storage/identity remain host-owned. */\n onReadingPositionChange?: (position: ConversationReadingPosition) => void\n /** Rendered above the transcript when there is nothing in it. */\n empty?: JSX.Element\n /** A header that scrolls with the transcript, e.g. a day divider or a banner. */\n header?: JSX.Element\n /**\n * The shared page gutter: reading padding inside the scroller plus a stable\n * gutter, so the scrollbar never shifts the text when it appears. The page\n * frame around the surface is `ConversationPane`'s job.\n */\n gutter?: boolean\n}\n\nexport function ConversationSurface(props: ConversationSurfaceProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'threshold',\n 'follow',\n 'resetKey',\n 'initialReadingPosition',\n 'onReadingPositionChange',\n 'empty',\n 'header',\n 'gutter',\n 'children',\n 'onScroll',\n 'ref',\n 'tabindex',\n ])\n // Kobalte provides the control's semantics; transcript follow is a native\n // geometry contract. KiroCrew's plain-scroller controller handles streaming,\n // content collapse and pane resizing without treating them as user consent.\n const follow = createScrollFollow({\n enabled: () => local.follow !== false,\n resetKey: () => local.resetKey,\n threshold: () => local.threshold ?? 80,\n initialPosition: () => local.initialReadingPosition,\n onPositionChange: (position) => local.onReadingPositionChange?.(position),\n })\n const onScroll: JSX.EventHandlerUnion<HTMLDivElement, Event> = (event) => {\n follow.onScroll()\n const handler = local.onScroll\n if (typeof handler === 'function') handler(event)\n else if (handler) handler[0](handler[1], event)\n }\n\n return (\n <div\n data-slot=\"conversation-surface\"\n class={cn('relative flex min-h-0 flex-1 flex-col', local.class)}\n >\n <div\n ref={(element) => {\n follow.bindScroller(element)\n if (typeof local.ref === 'function') local.ref(element)\n }}\n onScroll={onScroll}\n tabindex={local.tabindex ?? 0}\n class={cn(\n 'min-h-0 flex-1 overflow-y-auto overscroll-contain',\n local.gutter &&\n 'px-[clamp(0.8rem,3vw,2.5rem)] pt-[1.35rem] pb-[1.1rem] [scrollbar-gutter:stable] max-[30rem]:px-2'\n )}\n {...rest}\n >\n <div ref={follow.bindContent} data-slot=\"conversation-content\">\n <Show when={local.header}>{local.header}</Show>\n <Show when={local.children} fallback={<div class=\"p-4\">{local.empty}</div>}>\n {local.children}\n </Show>\n </div>\n </div>\n\n <Show when={!follow.atBottom()}>\n <div class=\"pointer-events-none absolute inset-x-0 bottom-3 flex justify-center\">\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"secondary\"\n class=\"pointer-events-auto gap-1.5 shadow-md\"\n onClick={follow.jump}\n >\n <ArrowDown />\n Jump to latest\n </Button>\n </div>\n </Show>\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;AAgDA,SAAgBuB,oBAAoBC,OAAiC;CACnE,MAAM,CAACC,OAAOC,QAAQtB,WAAWoB,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAAU,CACX;CAID,MAAMV,SAASP,mBAAmB;EAChCoB,eAAeF,MAAMX,WAAW;EAChCC,gBAAgBU,MAAMV;EACtBF,iBAAiBY,MAAMZ,aAAa;EACpCe,uBAAuBH,MAAMT;EAC7Ba,mBAAmBX,aAAaO,MAAMR,0BAA0BC,QAAQ;CAC1E,CAAC;CACD,MAAMY,YAA0DI,UAAU;EACxEpB,OAAOgB,SAAS;EAChB,MAAMK,UAAUV,MAAMK;EACtB,IAAI,OAAOK,YAAY,YAAYA,QAAQD,KAAK;OAC3C,IAAIC,SAASA,QAAQ,EAAE,CAACA,QAAQ,IAAID,KAAK;CAChD;CAEA,cAAA;EAAA,IAAAE,OAAAC,QAAA,GAAAC,QAAAF,KAAAG,YAAAC,QAAAF,MAAAC;EAAAD,MAAAG,iBAAA,UAUgBX,QAAQ;EAAAY,KAJZC,YAAY;GAChB7B,OAAO8B,aAAaD,OAAO;GAC3B,IAAI,OAAOlB,MAAMoB,QAAQ,YAAYpB,MAAMoB,IAAIF,OAAO;EACxD,GAACL,KAAA;EAAAQ,OAAAR,OAAAS,WAAA;GAAA,IAEDC,WAAQ;IAAA,OAAEvB,MAAMuB,YAAY;GAAC;GAAA,KAAA,WAAA;IAAA,OACtB3C,GACL,qDACAoB,MAAMH,UACJ,mGACJ;GAAC;EAAA,GACGI,IAAI,GAAA,OAAA,IAAA;EAAA,IAAAwB,QAEEpC,OAAOqC;EAAW,OAAAD,UAAA,aAAAR,IAAAQ,OAAAV,KAAA,IAAlB1B,OAAOqC,cAAWX;EAAAY,OAAAZ,OAAAa,gBACzBlD,MAAI;GAAA,IAACmD,OAAI;IAAA,OAAE7B,MAAMJ;GAAM;GAAA,IAAAkC,WAAA;IAAA,OAAG9B,MAAMJ;GAAM;EAAA,CAAA,GAAA,IAAA;EAAA+B,OAAAZ,OAAAa,gBACtClD,MAAI;GAAA,IAACmD,OAAI;IAAA,OAAE7B,MAAM8B;GAAQ;GAAA,IAAEC,WAAQ;IAAA,cAAA;KAAA,IAAAC,QAAAC,QAAA;KAAAN,OAAAK,aAAoBhC,MAAMN,KAAK;KAAA,OAAAsC;IAAA,EAAA,CAAA;GAAA;GAAA,IAAAF,WAAA;IAAA,OAChE9B,MAAM8B;GAAQ;EAAA,CAAA,GAAA,IAAA;EAAAH,OAAAhB,MAAAiB,gBAKpBlD,MAAI;GAAA,IAACmD,OAAI;IAAA,OAAE,CAACxC,OAAO6C,SAAS;GAAC;GAAA,IAAAJ,WAAA;IAAA,IAAAK,QAAAC,OAAA;IAAAT,OAAAQ,OAAAP,gBAEzB/C,QAAM;KACLwD,MAAI;KACJC,MAAI;KACJC,SAAO;KAAA,SAAA;KAAA,IAEPC,UAAO;MAAA,OAAEnD,OAAOoD;KAAI;KAAA,IAAAX,WAAA;MAAA,OAAA,CAAAF,gBAEnBrD,WAAS,CAAA,CAAA,GAAA,gBAAA;KAAA;IAAA,CAAA,CAAA;IAAA,OAAA4D;GAAA;EAAA,CAAA,GAAA,IAAA;EAAAO,aAAAC,UAAAhC,MAjCT/B,GAAG,yCAAyCoB,MAAMwB,KAAK,CAAC,CAAA;EAAA,OAAAb;CAAA,EAAA,CAAA;AAwCrE"}
@@ -2,6 +2,8 @@ export { ChatComposer, type ChatComposerProps, type ChatComposerControl } from '
2
2
  export { BusySendButton, type BusySendMode, type BusySendButtonProps } from './busy-send-button';
3
3
  export { ConversationAvatar, type ConversationAvatarKind, type ConversationAvatarProps, } from './conversation-avatar';
4
4
  export { ConversationSurface, type ConversationSurfaceProps, type ConversationReadingPosition, } from './conversation-surface';
5
+ export { ConversationPane, type ConversationPaneProps } from './conversation-pane';
6
+ export { ComposerMenu, ComposerMenuItem, type ComposerMenuProps, type ComposerMenuItemProps, } from './composer-menu';
5
7
  export { ComposerAttachmentButton, ComposerHint, MessageComposer, type MessageComposerProps, } from './message-composer';
6
8
  export { MessageBody, MessageDayDivider, MessageGroup, MessageRow, type MessageRowProps, } from './message-row';
7
9
  export { TranscriptComposition, transcriptDisclosureKey, type TranscriptCompositionProps, type TranscriptCompositionFold, type TranscriptCompositionRowContext, type TranscriptCompositionRendererProps, type TranscriptDisplayKind, type GroupedTranscript, type TranscriptDisplayItem, type TranscriptFoldState, type TranscriptFoldMode, type TranscriptFoldPlan, type TranscriptMounting, type TranscriptRow, type TranscriptTurnItem, } from './transcript-composition';