@moldable-ai/ui 0.2.29 → 0.2.32

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 (215) hide show
  1. package/dist/catalog/component-catalog.d.ts +9 -2
  2. package/dist/catalog/component-catalog.d.ts.map +1 -1
  3. package/dist/catalog/component-catalog.js +11 -7
  4. package/dist/catalog/core-control-stories.d.ts.map +1 -1
  5. package/dist/catalog/core-control-stories.js +1 -1
  6. package/dist/catalog/core-foundation-stories.d.ts.map +1 -1
  7. package/dist/catalog/core-foundation-stories.js +5 -5
  8. package/dist/components/app-error-boundary.d.ts +1 -1
  9. package/dist/components/chat/chat-image-lightbox.d.ts +8 -0
  10. package/dist/components/chat/chat-image-lightbox.d.ts.map +1 -1
  11. package/dist/components/chat/chat-image-lightbox.js +24 -2
  12. package/dist/components/chat/chat-input.d.ts +3 -2
  13. package/dist/components/chat/chat-input.d.ts.map +1 -1
  14. package/dist/components/chat/chat-input.js +3 -3
  15. package/dist/components/chat/chat-message.d.ts.map +1 -1
  16. package/dist/components/chat/chat-message.js +3 -1
  17. package/dist/components/chat/chat-panel.d.ts +3 -1
  18. package/dist/components/chat/chat-panel.d.ts.map +1 -1
  19. package/dist/components/chat/chat-panel.js +53 -39
  20. package/dist/components/chat/conversation-history.d.ts +4 -2
  21. package/dist/components/chat/conversation-history.d.ts.map +1 -1
  22. package/dist/components/chat/conversation-history.js +20 -15
  23. package/dist/components/ui/alert.d.ts +1 -1
  24. package/dist/components/ui/app-frame.d.ts +20 -1
  25. package/dist/components/ui/app-frame.d.ts.map +1 -1
  26. package/dist/components/ui/app-frame.js +37 -7
  27. package/dist/components/ui/badge.d.ts +1 -1
  28. package/dist/components/ui/badge.js +1 -1
  29. package/dist/components/ui/button.d.ts +5 -3
  30. package/dist/components/ui/button.d.ts.map +1 -1
  31. package/dist/components/ui/button.js +39 -11
  32. package/dist/components/ui/card.d.ts +12 -2
  33. package/dist/components/ui/card.d.ts.map +1 -1
  34. package/dist/components/ui/card.js +3 -2
  35. package/dist/components/ui/checkbox.d.ts +4 -1
  36. package/dist/components/ui/checkbox.d.ts.map +1 -1
  37. package/dist/components/ui/checkbox.js +4 -4
  38. package/dist/components/ui/context-menu.d.ts.map +1 -1
  39. package/dist/components/ui/context-menu.js +7 -6
  40. package/dist/components/ui/date-field.d.ts +1 -1
  41. package/dist/components/ui/dropdown-menu.d.ts +1 -0
  42. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  43. package/dist/components/ui/dropdown-menu.js +9 -8
  44. package/dist/components/ui/icon-button.d.ts +1 -1
  45. package/dist/components/ui/icon-button.d.ts.map +1 -1
  46. package/dist/components/ui/icon-button.js +1 -0
  47. package/dist/components/ui/input.d.ts +6 -2
  48. package/dist/components/ui/input.d.ts.map +1 -1
  49. package/dist/components/ui/input.js +2 -2
  50. package/dist/components/ui/material.d.ts +8 -3
  51. package/dist/components/ui/material.d.ts.map +1 -1
  52. package/dist/components/ui/material.js +2 -2
  53. package/dist/components/ui/menu-items.d.ts +2 -1
  54. package/dist/components/ui/menu-items.d.ts.map +1 -1
  55. package/dist/components/ui/menu-items.js +3 -2
  56. package/dist/components/ui/message-scroller-controller.d.ts +15 -17
  57. package/dist/components/ui/message-scroller-controller.d.ts.map +1 -1
  58. package/dist/components/ui/message-scroller-controller.js +106 -91
  59. package/dist/components/ui/message-scroller-primitive.d.ts +2 -0
  60. package/dist/components/ui/message-scroller-primitive.d.ts.map +1 -1
  61. package/dist/components/ui/message-scroller-primitive.js +2 -0
  62. package/dist/components/ui/message-scroller.d.ts.map +1 -1
  63. package/dist/components/ui/message-scroller.js +2 -2
  64. package/dist/components/ui/number-input.d.ts +1 -1
  65. package/dist/components/ui/popover.js +1 -1
  66. package/dist/components/ui/search-field.d.ts +4 -2
  67. package/dist/components/ui/search-field.d.ts.map +1 -1
  68. package/dist/components/ui/search-field.js +16 -6
  69. package/dist/components/ui/segmented-control.d.ts +3 -1
  70. package/dist/components/ui/segmented-control.d.ts.map +1 -1
  71. package/dist/components/ui/segmented-control.js +23 -17
  72. package/dist/components/ui/select.js +2 -2
  73. package/dist/components/ui/sidebar.d.ts +10 -4
  74. package/dist/components/ui/sidebar.d.ts.map +1 -1
  75. package/dist/components/ui/sidebar.js +74 -17
  76. package/dist/components/ui/status.d.ts +1 -1
  77. package/dist/components/ui/status.js +1 -1
  78. package/dist/components/ui/switch.d.ts +3 -1
  79. package/dist/components/ui/switch.d.ts.map +1 -1
  80. package/dist/components/ui/switch.js +3 -2
  81. package/dist/components/ui/toolbar-controls.d.ts +13 -3
  82. package/dist/components/ui/toolbar-controls.d.ts.map +1 -1
  83. package/dist/components/ui/toolbar-controls.js +35 -10
  84. package/dist/components/ui/toolbar.d.ts +5 -4
  85. package/dist/components/ui/toolbar.d.ts.map +1 -1
  86. package/dist/components/ui/toolbar.js +3 -3
  87. package/dist/index.d.ts +2 -1
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +3 -1
  90. package/dist/lib/frame-lifecycle.d.ts +1 -0
  91. package/dist/lib/frame-lifecycle.d.ts.map +1 -1
  92. package/dist/lib/frame-lifecycle.js +7 -0
  93. package/dist/lib/theme.d.ts +39 -5
  94. package/dist/lib/theme.d.ts.map +1 -1
  95. package/dist/lib/theme.js +105 -17
  96. package/dist/tokens/contracts.d.ts +79 -0
  97. package/dist/tokens/contracts.d.ts.map +1 -0
  98. package/dist/tokens/contracts.js +57 -0
  99. package/dist/tokens/index.d.ts +2 -0
  100. package/dist/tokens/index.d.ts.map +1 -0
  101. package/dist/tokens/index.js +1 -0
  102. package/docs/README.md +2 -0
  103. package/docs/adaptive-materials.md +14 -9
  104. package/docs/design-tokens.md +10 -6
  105. package/docs/foundations.md +3 -3
  106. package/docs/macos-design-audit.md +144 -0
  107. package/docs/native-integration.md +4 -4
  108. package/docs/platform-architecture.md +189 -0
  109. package/docs/standalone-app-windows.md +37 -0
  110. package/docs/visual-regression.md +6 -8
  111. package/package.json +13 -1
  112. package/src/components/ui/accordion.md +46 -0
  113. package/src/components/ui/alert.md +12 -4
  114. package/src/components/ui/app-frame.md +8 -5
  115. package/src/components/ui/aspect-ratio.md +37 -0
  116. package/src/components/ui/avatar.md +48 -0
  117. package/src/components/ui/breadcrumb.md +52 -0
  118. package/src/components/ui/button-group.md +43 -0
  119. package/src/components/ui/button.md +21 -4
  120. package/src/components/ui/calendar.md +42 -0
  121. package/src/components/ui/card.md +43 -0
  122. package/src/components/ui/carousel.md +47 -0
  123. package/src/components/ui/chart.md +52 -0
  124. package/src/components/ui/checkbox.md +36 -0
  125. package/src/components/ui/collapsible.md +2 -3
  126. package/src/components/ui/color-well.md +14 -4
  127. package/src/components/ui/combobox.md +3 -3
  128. package/src/components/ui/confirm-dialog.md +5 -4
  129. package/src/components/ui/context-menu.md +3 -2
  130. package/src/components/ui/date-field.md +6 -1
  131. package/src/components/ui/date-picker.md +6 -3
  132. package/src/components/ui/dialog.md +14 -12
  133. package/src/components/ui/drawer.md +58 -0
  134. package/src/components/ui/dropdown-menu.md +16 -14
  135. package/src/components/ui/error-boundary-view.md +1 -1
  136. package/src/components/ui/field.md +2 -1
  137. package/src/components/ui/form.md +71 -0
  138. package/src/components/ui/grid.md +4 -3
  139. package/src/components/ui/icon-button.md +4 -1
  140. package/src/components/ui/input-group.md +49 -0
  141. package/src/components/ui/input-otp.md +56 -0
  142. package/src/components/ui/input.md +40 -0
  143. package/src/components/ui/inspector.md +47 -7
  144. package/src/components/ui/item.md +5 -3
  145. package/src/components/ui/kbd.md +30 -0
  146. package/src/components/ui/label.md +29 -0
  147. package/src/components/ui/list.md +2 -2
  148. package/src/components/ui/material.md +25 -12
  149. package/src/components/ui/menu-items.md +62 -0
  150. package/src/components/ui/menubar.md +58 -0
  151. package/src/components/ui/message-scroller-primitive.md +70 -0
  152. package/src/components/ui/message-scroller.md +89 -0
  153. package/src/components/ui/navigation-button-group.md +4 -3
  154. package/src/components/ui/navigation-menu.md +52 -0
  155. package/src/components/ui/number-input.md +8 -0
  156. package/src/components/ui/pagination.md +51 -0
  157. package/src/components/ui/panel.md +6 -0
  158. package/src/components/ui/popover.md +10 -7
  159. package/src/components/ui/progress.md +31 -0
  160. package/src/components/ui/radio-group.md +36 -0
  161. package/src/components/ui/resizable.md +2 -1
  162. package/src/components/ui/scroll-area.md +3 -1
  163. package/src/components/ui/search-field.md +6 -1
  164. package/src/components/ui/segmented-control.md +6 -1
  165. package/src/components/ui/separator.md +35 -0
  166. package/src/components/ui/sidebar.md +29 -6
  167. package/src/components/ui/skeleton.md +36 -0
  168. package/src/components/ui/slider.md +58 -0
  169. package/src/components/ui/spinner.md +41 -0
  170. package/src/components/ui/split-view.md +8 -4
  171. package/src/components/ui/switch.md +44 -0
  172. package/src/components/ui/table.md +7 -7
  173. package/src/components/ui/tabs.md +3 -1
  174. package/src/components/ui/textarea.md +41 -0
  175. package/src/components/ui/toggle-group.md +2 -2
  176. package/src/components/ui/toggle.md +9 -9
  177. package/src/components/ui/toolbar-controls.md +24 -12
  178. package/src/components/ui/toolbar.md +6 -6
  179. package/src/styles/index.css +298 -117
  180. package/src/tokens/contracts.ts +131 -0
  181. package/src/tokens/index.css +6 -0
  182. package/src/tokens/index.ts +23 -0
  183. package/src/tokens/macos-26/README.md +37 -0
  184. package/src/tokens/macos-26/aliases.css +53 -0
  185. package/src/tokens/macos-26/compatibility.css +7 -0
  186. package/src/tokens/macos-26/index.css +3 -0
  187. package/src/tokens/macos-26/source/provenance.generated.json +28 -0
  188. package/src/tokens/macos-26/source/sidebar.generated.css +29 -0
  189. package/src/tokens/macos-26/source.css +7 -0
  190. package/src/tokens/macos-26/web-adaptations.json +25 -0
  191. package/src/tokens/macos-26/web-adaptations.md +15 -0
  192. package/src/tokens/macos-27/accessibility.css +11 -0
  193. package/src/tokens/macos-27/aliases.css +304 -0
  194. package/src/tokens/macos-27/compatibility.css +212 -0
  195. package/src/tokens/macos-27/index.css +15 -0
  196. package/src/tokens/macos-27/recipes.css +23 -0
  197. package/src/tokens/macos-27/source/chrome.generated.css +338 -0
  198. package/src/tokens/macos-27/source/colors.generated.css +140 -0
  199. package/src/tokens/macos-27/source/context.generated.css +18 -0
  200. package/src/tokens/macos-27/source/controls.generated.css +764 -0
  201. package/src/tokens/macos-27/source/effects.generated.css +68 -0
  202. package/src/tokens/macos-27/source/kit.generated.css +50 -0
  203. package/src/tokens/macos-27/source/overlays-status.generated.css +931 -0
  204. package/src/tokens/macos-27/source/paint-styles.generated.css +81 -0
  205. package/src/tokens/macos-27/source/provenance.generated.json +62 -0
  206. package/src/tokens/macos-27/source/sizes.generated.css +385 -0
  207. package/src/tokens/macos-27/source/source.generated.json +554 -0
  208. package/src/tokens/macos-27/source/text-styles.generated.css +116 -0
  209. package/src/tokens/macos-27/source.css +17 -0
  210. package/src/tokens/macos-27/web-adaptations.css +98 -0
  211. package/src/tokens/macos-27/web-adaptations.json +163 -0
  212. package/src/tokens/macos-27/web-adaptations.md +117 -0
  213. package/src/tokens/moldable/brand.css +165 -0
  214. package/src/tokens/moldable/index.css +1 -0
  215. package/src/tokens/platform-contract.test.ts +294 -0
@@ -10,6 +10,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover.js';
10
10
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '../ui/tooltip.js';
11
11
  import { ChatInput, } from './chat-input.js';
12
12
  import { CompactingMessage, ThinkingMessage, getChatMessageRenderKey, } from './chat-message.js';
13
+ import { ChatImageLightboxProvider } from './chat-image-lightbox.js';
13
14
  import { MessageRow, computeIsThinking, } from './chat-messages.js';
14
15
  import { ConversationHistory, ConversationSidebar, } from './conversation-history.js';
15
16
  import { ModelEffortSelector, } from './model-effort-selector.js';
@@ -113,8 +114,11 @@ function MessageNavigationTrail({ items }) {
113
114
  }), "data-message-navigation-row": true, className: "flex h-2.5 w-9 shrink-0 cursor-pointer items-center outline-none", children: _jsx("span", { className: "flex h-0.5 w-[30px] items-center", children: _jsx("span", { "data-message-navigation-marker": true, className: cn('bg-muted-foreground rounded-pill h-0.5 w-[26px] origin-left opacity-40', isCurrent && 'bg-foreground opacity-60') }) }) }) }), _jsx(TooltipContent, { side: "right", className: "max-w-72", children: anchor.label })] }, anchor.id));
114
115
  }) }) }) }) }));
115
116
  }
116
- function ChatMessageViewport({ items, renderItem, toolProgress, onApprovalResponse, currentConversationId, scrollToBottomSignal, isBusy, hasOlderMessages = false, isLoadingOlderMessages = false, onLoadOlderMessages, showNavigationTrail = false, scrollAreaClassName, contentClassName, scrollButtonClassName, }) {
117
- const viewportRef = useRef(null);
117
+ function ChatMessageViewport({ currentConversationId, ...props }) {
118
+ return (_jsx(MessageScrollerProvider, { autoScroll: true, defaultScrollPosition: "end", scrollPreviousItemPeek: 64, children: _jsx(ChatImageLightboxProvider, { children: _jsx(ChatMessageViewportBody, { ...props }) }) }, currentConversationId ?? 'new-conversation'));
119
+ }
120
+ function ChatMessageViewportBody({ items, renderItem, toolProgress, onApprovalResponse, scrollToBottomSignal, isBusy, hasOlderMessages = false, isLoadingOlderMessages = false, onLoadOlderMessages, showNavigationTrail = false, scrollAreaClassName, contentClassName, scrollButtonClassName, }) {
121
+ const { beginHistoryLoad, finishHistoryLoad } = useMessageScroller();
118
122
  const loadingOlderRef = useRef(false);
119
123
  const handleViewportScroll = useCallback((event) => {
120
124
  const viewport = event.currentTarget;
@@ -126,27 +130,27 @@ function ChatMessageViewport({ items, renderItem, toolProgress, onApprovalRespon
126
130
  return;
127
131
  }
128
132
  loadingOlderRef.current = true;
129
- // A prepended page is position-preserved by the message scroller
130
- // itself, synchronously in the same mutation pass — compensating here
131
- // after a paint produced a visible two-step jump. Only a reset (the
132
- // cursor raced a history rewrite) still needs an explicit landing.
133
+ beginHistoryLoad();
133
134
  void onLoadOlderMessages()
134
135
  .then((outcome) => {
135
- if (outcome !== 'reset')
136
- return;
137
- window.requestAnimationFrame(() => {
138
- if (viewportRef.current !== viewport)
139
- return;
140
- viewport.scrollTop = viewport.scrollHeight;
141
- });
136
+ finishHistoryLoad(outcome !== 'unchanged');
137
+ })
138
+ .catch(() => {
139
+ finishHistoryLoad(false);
142
140
  })
143
141
  .finally(() => {
144
142
  window.requestAnimationFrame(() => {
145
143
  loadingOlderRef.current = false;
146
144
  });
147
145
  });
148
- }, [hasOlderMessages, isLoadingOlderMessages, onLoadOlderMessages]);
149
- return (_jsx(MessageScrollerProvider, { autoScroll: true, defaultScrollPosition: "end", scrollPreviousItemPeek: 64, children: _jsxs(MessageScroller, { className: "relative min-h-0 min-w-0 flex-1", children: [_jsx(MessageScrollerCommandSync, { signal: scrollToBottomSignal }), _jsx(MessageScrollerViewport, { ref: viewportRef, className: scrollAreaClassName, "data-message-scroll-fade": true, "data-has-older-messages": hasOlderMessages ? 'true' : 'false', onScroll: handleViewportScroll, children: _jsx(MessageScrollerContent, { "aria-busy": isBusy || isLoadingOlderMessages, className: contentClassName, children: _jsx(ToolProgressProvider, { value: toolProgress, children: _jsx(ToolApprovalProvider, { onApprovalResponse: onApprovalResponse ?? null, children: items.map((item) => (_jsx(MessageScrollerItem, { messageId: item.key, scrollAnchor: item.kind === 'message' && item.message.role === 'user', children: renderItem(item) }, item.key))) }) }) }) }), items.length > 0 && (_jsx(MessageScrollerButton, { className: scrollButtonClassName })), showNavigationTrail && _jsx(MessageNavigationTrail, { items: items })] }) }, currentConversationId ?? 'new-conversation'));
146
+ }, [
147
+ beginHistoryLoad,
148
+ finishHistoryLoad,
149
+ hasOlderMessages,
150
+ isLoadingOlderMessages,
151
+ onLoadOlderMessages,
152
+ ]);
153
+ return (_jsxs(MessageScroller, { className: "relative min-h-0 min-w-0 flex-1", children: [_jsx(MessageScrollerCommandSync, { signal: scrollToBottomSignal }), _jsx(MessageScrollerViewport, { className: scrollAreaClassName, "data-message-scroll-fade": true, "data-has-older-messages": hasOlderMessages ? 'true' : 'false', onScroll: handleViewportScroll, children: _jsx(MessageScrollerContent, { "aria-busy": isBusy || isLoadingOlderMessages, className: contentClassName, children: _jsx(ToolProgressProvider, { value: toolProgress, children: _jsx(ToolApprovalProvider, { onApprovalResponse: onApprovalResponse ?? null, children: items.map((item) => (_jsx(MessageScrollerItem, { messageId: item.key, scrollAnchor: item.kind === 'message' && item.message.role === 'user', children: renderItem(item) }, item.key))) }) }) }) }), items.length > 0 && (_jsx(MessageScrollerButton, { className: scrollButtonClassName })), showNavigationTrail && _jsx(MessageNavigationTrail, { items: items })] }));
150
154
  }
151
155
  function clampPercent(value) {
152
156
  if (!Number.isFinite(value))
@@ -181,7 +185,7 @@ function ContextWindowIndicator({ usage, planUsage, }) {
181
185
  : 'var(--muted-foreground)';
182
186
  return (_jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "text-muted-foreground hover:text-foreground focus-visible:ring-ring rounded-pill flex size-8 cursor-pointer items-center justify-center bg-transparent p-0 focus-visible:outline-none focus-visible:ring-1", "aria-label": `Context window ${usedPercent}% full`, children: _jsx("span", { className: "rounded-pill relative size-4", style: {
183
187
  background: `conic-gradient(${ringColor} ${usedPercent}%, color-mix(in oklch, var(--muted-foreground) 22%, transparent) 0)`,
184
- }, children: _jsx("span", { className: "bg-background rounded-pill absolute inset-[3px]" }) }) }) }), _jsxs(PopoverContent, { align: "end", side: "top", sideOffset: 8, className: "w-80 p-3 shadow-lg", children: [_jsxs("div", { className: "text-ui-body flex items-center justify-between gap-3", children: [_jsx("span", { className: "text-muted-foreground", children: "Context window" }), _jsxs("span", { children: [formatTokenCount(usage.usedTokens), " /", ' ', formatTokenCount(usage.maxTokens), " (", usedPercent, "%)"] })] }), _jsx("div", { className: "bg-muted rounded-pill mt-2 h-1.5 overflow-hidden", children: _jsx("div", { className: "bg-primary rounded-pill h-full", style: { width: `${usedPercent}%` } }) }), usage.compactsAutomatically && (_jsx("div", { className: "text-muted-foreground text-11 mt-2 leading-4", children: "Moldable automatically compacts its context" })), planUsage && planUsage.windows.length > 0 && (_jsxs("div", { className: "border-border mt-3 flex flex-col gap-3 border-t pt-3", children: [_jsxs("div", { children: [_jsxs("div", { className: "text-muted-foreground text-ui-caption", children: ["Plan usage limits", planUsage.planLabel ? ` · ${planUsage.planLabel}` : ''] }), planUsage.accountLabel && (_jsx("div", { className: "text-ui-caption mt-0.5 truncate font-medium", children: planUsage.accountLabel }))] }), planUsage.windows.map((window) => {
188
+ }, children: _jsx("span", { className: "bg-background rounded-pill absolute inset-[3px]" }) }) }) }), _jsxs(PopoverContent, { align: "end", side: "top", sideOffset: 8, className: "w-80 p-3 shadow-lg", children: [_jsxs("div", { className: "text-ui-body flex items-center justify-between gap-3", children: [_jsx("span", { className: "text-muted-foreground", children: "Context window" }), _jsxs("span", { className: "text-ui-caption", children: [formatTokenCount(usage.usedTokens), " /", ' ', formatTokenCount(usage.maxTokens), " (", usedPercent, "%)"] })] }), _jsx("div", { className: "bg-muted rounded-pill mt-2 h-1.5 overflow-hidden", children: _jsx("div", { className: "bg-primary rounded-pill h-full", style: { width: `${usedPercent}%` } }) }), usage.compactsAutomatically && (_jsx("div", { className: "text-muted-foreground text-11 mt-2 leading-4", children: "Moldable automatically compacts its context" })), planUsage && planUsage.windows.length > 0 && (_jsxs("div", { className: "border-border mt-3 flex flex-col gap-3 border-t pt-3", children: [_jsxs("div", { children: [_jsxs("div", { className: "text-muted-foreground text-ui-caption", children: ["Plan usage limits", planUsage.planLabel ? ` · ${planUsage.planLabel}` : ''] }), planUsage.accountLabel && (_jsx("div", { className: "text-ui-caption mt-0.5 truncate font-medium", children: planUsage.accountLabel }))] }), planUsage.windows.map((window) => {
185
189
  const reset = formatUsageReset(window.resetAt);
186
190
  const percent = clampPercent(window.usedPercent);
187
191
  return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "text-ui-caption flex items-center justify-between gap-3", children: [_jsx("span", { children: window.label }), _jsxs("span", { className: "text-muted-foreground", children: [reset ? `Resets ${reset} · ` : '', percent, "%"] })] }), _jsx("div", { className: "bg-muted rounded-pill h-1.5 overflow-hidden", children: _jsx("div", { className: percent >= 90
@@ -217,7 +221,7 @@ function QueuedMessageRow({ message, isResponding, onRetryQueuedMessage, onSteer
217
221
  /**
218
222
  * Floating chat panel with model selector
219
223
  */
220
- export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChange, onSubmit, onStop, onNewChat, modelProviders, modelEffortPresets, selectedModel, selectedReasoningEffort, onModelEffortChange, conversations, currentConversationId, currentAppId, hasMoreConversations = false, isLoadingMoreConversations = false, onLoadMoreConversations, onResolveMessageContent, scrollToBottomSignal = 0, hasOlderMessages = false, isLoadingOlderMessages = false, onLoadOlderMessages, onSelectConversation, onArchiveConversation, onArchiveConversationGroup, placeholder = 'Ask anything...', welcomeMessage, isExpanded, onExpandedChange, isFullScreen = false, onFullScreenChange, isMinimized = false, onMinimizedChange, className, error, onRetryError, missingApiKey, authIssue, onConnectChatGPT, onAdvancedSetup, toolProgress = {}, onApprovalResponse, isEditingApp = true, onEditingAppChange, showEditingAppToggle = false, checkpoints, restoringMessageId, onRestoreCheckpoint, imageAttachments = [], onRemoveImageAttachment, isImageDropActive = false, queuedMessages = [], onRemoveQueuedMessage, onRetryQueuedMessage, onSteerQueuedMessage, onReorderQueuedMessages, onEditQueuedMessage, slashCommands, onSlashCommandSelect, isCompacting = false, compactedHistoryStartIndex, contextUsage, planUsage, }) {
224
+ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChange, onSubmit, onStop, onNewChat, modelProviders, modelEffortPresets, selectedModel, selectedReasoningEffort, onModelEffortChange, conversations, currentConversationId, currentAppId, groupConversationHistory = false, hasMoreConversations = false, isLoadingMoreConversations = false, onLoadMoreConversations, onResolveMessageContent, scrollToBottomSignal = 0, hasOlderMessages = false, isLoadingOlderMessages = false, onLoadOlderMessages, onSelectConversation, onArchiveConversation, onArchiveConversationGroup, placeholder = 'Ask anything...', welcomeMessage, isExpanded, onExpandedChange, isFullScreen = false, onFullScreenChange, isMinimized = false, onMinimizedChange, className, error, onRetryError, missingApiKey, authIssue, onConnectChatGPT, onAdvancedSetup, toolProgress = {}, onApprovalResponse, isEditingApp = true, onEditingAppChange, showEditingAppToggle = false, checkpoints, restoringMessageId, onRestoreCheckpoint, imageAttachments = [], onRemoveImageAttachment, isImageDropActive = false, queuedMessages = [], onRemoveQueuedMessage, onRetryQueuedMessage, onSteerQueuedMessage, onReorderQueuedMessages, onEditQueuedMessage, slashCommands, onSlashCommandSelect, isCompacting = false, compactedHistoryStartIndex, contextUsage, planUsage, }) {
221
225
  const inputRef = useRef(null);
222
226
  const fullScreenTriggerRef = useRef(null);
223
227
  const [isConnectingChatGPT, setIsConnectingChatGPT] = useState(false);
@@ -317,7 +321,7 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
317
321
  : 'Not editing app (click to enable)' }) })] }) })) : null;
318
322
  const composerStartControls = (_jsxs(_Fragment, { children: [conversations &&
319
323
  (conversations.length > 0 || onNewChat) &&
320
- onSelectConversation && (_jsx(ConversationHistory, { conversations: conversations, currentConversationId: currentConversationId, currentAppId: currentAppId, onSelect: onSelectConversation, onArchive: onArchiveConversation, onArchiveGroup: onArchiveConversationGroup, onNewChat: handleNewChat, newChatDisabled: false, disabled: false, dropdownAlign: "start", dropdownSide: "top", tooltipSide: "top", hasMoreConversations: hasMoreConversations, isLoadingMoreConversations: isLoadingMoreConversations, onLoadMoreConversations: onLoadMoreConversations })), editingAppControl] }));
324
+ onSelectConversation && (_jsx(ConversationHistory, { conversations: conversations, currentConversationId: currentConversationId, currentAppId: currentAppId, groupByScope: groupConversationHistory, onSelect: onSelectConversation, onArchive: onArchiveConversation, onArchiveGroup: onArchiveConversationGroup, onNewChat: handleNewChat, newChatDisabled: false, disabled: false, dropdownAlign: "start", dropdownSide: "top", tooltipSide: "top", hasMoreConversations: hasMoreConversations, isLoadingMoreConversations: isLoadingMoreConversations, onLoadMoreConversations: onLoadMoreConversations })), editingAppControl] }));
321
325
  // Keyboard shortcut: Cmd+Shift+O for new chat
322
326
  useEffect(() => {
323
327
  const handleKeyDown = (e) => {
@@ -439,40 +443,50 @@ export function ChatPanel({ messages, status, isTurnInFlight, input, onInputChan
439
443
  window.requestAnimationFrame(() => {
440
444
  fullScreenTriggerRef.current?.focus();
441
445
  });
442
- }, size: "full", className: "rounded-container flex gap-0 overflow-hidden border-0 p-0", children: [_jsx("div", { "data-tauri-drag-region": true, className: "absolute inset-x-0 top-0 z-20 h-9", "aria-hidden": "true" }), isImageDropActive && (_jsxs("div", { className: "bg-background/90 border-primary/50 text-foreground absolute inset-0 z-50 flex flex-col items-center justify-center gap-2 rounded-[inherit] border-2 border-dashed backdrop-blur-sm", children: [_jsx(ImageIcon, { className: "text-primary size-7" }), _jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-ui-body font-medium", children: "Drop images to attach" }), _jsx("p", { className: "text-muted-foreground text-ui-caption", children: "They stay on disk and are read when sent" })] })] })), _jsxs("aside", { className: "border-border bg-muted/50 rounded-r-container relative z-10 flex w-[280px] shrink-0 flex-col overflow-hidden border-r px-3 pb-4 pt-11", children: [_jsxs("button", { type: "button", onClick: () => onFullScreenChange?.(false), className: "text-muted-foreground hover:text-foreground rounded-container text-ui-body mb-4 flex h-8 cursor-pointer items-center gap-2 px-2 transition-colors", children: [_jsx(Minimize2, { className: "size-4" }), "Back to app"] }), _jsx(ConversationSidebar, { conversations: conversations ?? [], currentConversationId: currentConversationId, currentAppId: currentAppId, onSelect: onSelectConversation ?? (() => undefined), onArchive: onArchiveConversation, onArchiveGroup: onArchiveConversationGroup, onNewChat: handleNewChat, newChatDisabled: false, hideScrollbar: true, disabled: false, hasMoreConversations: hasMoreConversations, isLoadingMoreConversations: isLoadingMoreConversations, onLoadMoreConversations: onLoadMoreConversations, className: "min-h-0 flex-1" })] }), _jsxs("section", { className: "relative flex min-w-0 flex-1 flex-col", children: [_jsxs("header", { className: "border-border flex h-14 shrink-0 items-center justify-between border-b px-5", children: [_jsx(DialogTitle, { className: "text-ui-body truncate font-medium", children: selectedConversation?.title ?? 'New conversation' }), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: () => onFullScreenChange?.(false), "aria-label": "Exit full screen", className: "text-muted-foreground hover:text-foreground relative z-30 cursor-pointer", children: _jsx(Minimize2, { className: "size-4" }) }) }), _jsx(TooltipContent, { children: "Exit full screen" })] }) })] }), _jsx(ChatMessageViewport, { items: listItems, renderItem: renderVirtualItem, toolProgress: toolProgress, onApprovalResponse: onApprovalResponse, currentConversationId: currentConversationId, scrollToBottomSignal: scrollToBottomSignal, isBusy: isResponding, hasOlderMessages: hasOlderMessages, isLoadingOlderMessages: isLoadingOlderMessages, onLoadOlderMessages: onLoadOlderMessages, showNavigationTrail: true, contentClassName: "mx-auto w-full min-w-0 max-w-3xl overflow-hidden px-6 py-6", scrollButtonClassName: "absolute bottom-3 left-1/2 z-20 size-8 -translate-x-1/2 cursor-pointer rounded-pill shadow-lg" }), authPromptCopy && (_jsx("div", { className: "mx-auto w-full max-w-3xl shrink-0 px-6 pb-2", children: _jsx(ChatAuthPrompt, { copy: authPromptCopy, isConnecting: isConnectingChatGPT, onConnect: onConnectChatGPT ? handleConnectChatGPT : undefined, onAdvancedSetup: onAdvancedSetup }) })), _jsxs("div", { className: "mx-auto w-full max-w-3xl shrink-0 px-6 pb-2", children: [queuedMessages.length > 0 && (_jsxs("div", { className: "border-border bg-muted/30 rounded-container mb-2 border p-3", children: [_jsxs("div", { className: "text-muted-foreground text-ui-caption mb-2 flex items-center gap-2 font-medium", children: [_jsx(ListPlus, { className: "size-3.5" }), _jsx("span", { children: "Queued follow-ups" })] }), _jsx(DndContext, { sensors: queueSensors, collisionDetection: closestCenter, onDragEnd: handleQueuedDragEnd, children: _jsx(SortableContext, { items: queuedMessageIds, strategy: verticalListSortingStrategy, children: _jsx("div", { className: "space-y-1.5", children: queuedMessages.map((message) => (_jsx(QueuedMessageRow, { message: message, isResponding: isResponding, onRetryQueuedMessage: onRetryQueuedMessage, onSteerQueuedMessage: onSteerQueuedMessage, onRemoveQueuedMessage: onRemoveQueuedMessage, onEditQueuedMessage: handleEditQueuedMessage }, message.id))) }) }) })] })), _jsx("div", { className: "bg-background overflow-hidden rounded-[24px] shadow-[0_8px_40px_-12px_rgba(0,0,0,0.2)]", children: _jsx(ChatInput, { input: input, onInputChange: onInputChange, onSubmit: handleSubmit, isResponding: isResponding, placeholder: placeholder, inputRef: inputRef, onStop: onStop, imageAttachments: imageAttachments, onRemoveImageAttachment: onRemoveImageAttachment, footerStartControls: editingAppControl, footerControls: composerControls, submitDisabled: submitDisabled, slashCommands: slashCommands, onSlashCommandSelect: onSlashCommandSelect }) })] })] })] }) }));
446
+ }, size: "full", className: "rounded-container flex gap-0 overflow-hidden border-0 p-0", children: [_jsx("div", { "data-tauri-drag-region": true, className: "absolute inset-x-0 top-0 z-20 h-9", "aria-hidden": "true" }), isImageDropActive && (_jsxs("div", { className: "bg-background/90 border-primary/50 text-foreground absolute inset-0 z-50 flex flex-col items-center justify-center gap-2 rounded-[inherit] border-2 border-dashed backdrop-blur-sm", children: [_jsx(ImageIcon, { className: "text-primary size-7" }), _jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-ui-body font-medium", children: "Drop images to attach" }), _jsx("p", { className: "text-muted-foreground text-ui-caption", children: "They stay on disk and are read when sent" })] })] })), _jsxs("aside", { className: "border-border bg-muted/50 rounded-r-container relative z-10 flex w-[280px] shrink-0 flex-col overflow-hidden border-r px-3 pb-4 pt-11", children: [_jsxs("button", { type: "button", onClick: () => onFullScreenChange?.(false), className: "text-muted-foreground hover:text-foreground rounded-container text-ui-body mb-4 flex h-8 cursor-pointer items-center gap-2 px-2 transition-colors", children: [_jsx(Minimize2, { className: "size-4" }), "Back to app"] }), _jsx(ConversationSidebar, { conversations: conversations ?? [], currentConversationId: currentConversationId, currentAppId: currentAppId, groupByScope: groupConversationHistory, onSelect: onSelectConversation ?? (() => undefined), onArchive: onArchiveConversation, onArchiveGroup: onArchiveConversationGroup, onNewChat: handleNewChat, newChatDisabled: false, hideScrollbar: true, disabled: false, hasMoreConversations: hasMoreConversations, isLoadingMoreConversations: isLoadingMoreConversations, onLoadMoreConversations: onLoadMoreConversations, className: "min-h-0 flex-1" })] }), _jsxs("section", { className: "relative flex min-w-0 flex-1 flex-col", children: [_jsxs("header", { className: "border-border flex h-14 shrink-0 items-center justify-between border-b px-5", children: [_jsx(DialogTitle, { className: "text-ui-body truncate font-medium", children: selectedConversation?.title ?? 'New conversation' }), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: () => onFullScreenChange?.(false), "aria-label": "Exit full screen", className: "text-muted-foreground hover:text-foreground relative z-30 cursor-pointer", children: _jsx(Minimize2, { className: "size-4" }) }) }), _jsx(TooltipContent, { children: "Exit full screen" })] }) })] }), _jsx(ChatMessageViewport, { items: listItems, renderItem: renderVirtualItem, toolProgress: toolProgress, onApprovalResponse: onApprovalResponse, currentConversationId: currentConversationId, scrollToBottomSignal: scrollToBottomSignal, isBusy: isResponding, hasOlderMessages: hasOlderMessages, isLoadingOlderMessages: isLoadingOlderMessages, onLoadOlderMessages: onLoadOlderMessages, showNavigationTrail: true, contentClassName: "mx-auto w-full min-w-0 max-w-3xl overflow-hidden px-6 py-6", scrollButtonClassName: "absolute bottom-3 left-1/2 z-20 size-8 -translate-x-1/2 cursor-pointer rounded-pill shadow-lg" }), authPromptCopy && (_jsx("div", { className: "mx-auto w-full max-w-3xl shrink-0 px-6 pb-2", children: _jsx(ChatAuthPrompt, { copy: authPromptCopy, isConnecting: isConnectingChatGPT, onConnect: onConnectChatGPT ? handleConnectChatGPT : undefined, onAdvancedSetup: onAdvancedSetup }) })), _jsxs("div", { className: "mx-auto w-full max-w-3xl shrink-0 px-6 pb-2", children: [queuedMessages.length > 0 && (_jsxs("div", { className: "border-border bg-muted/30 rounded-container mb-2 border p-3", children: [_jsxs("div", { className: "text-muted-foreground text-ui-caption mb-2 flex items-center gap-2 font-medium", children: [_jsx(ListPlus, { className: "size-3.5" }), _jsx("span", { children: "Queued follow-ups" })] }), _jsx(DndContext, { sensors: queueSensors, collisionDetection: closestCenter, onDragEnd: handleQueuedDragEnd, children: _jsx(SortableContext, { items: queuedMessageIds, strategy: verticalListSortingStrategy, children: _jsx("div", { className: "space-y-1.5", children: queuedMessages.map((message) => (_jsx(QueuedMessageRow, { message: message, isResponding: isResponding, onRetryQueuedMessage: onRetryQueuedMessage, onSteerQueuedMessage: onSteerQueuedMessage, onRemoveQueuedMessage: onRemoveQueuedMessage, onEditQueuedMessage: handleEditQueuedMessage }, message.id))) }) }) })] })), _jsx("div", { className: "bg-background overflow-hidden rounded-[24px] shadow-[0_8px_40px_-12px_rgba(0,0,0,0.2)]", children: _jsx(ChatInput, { input: input, onInputChange: onInputChange, onSubmit: handleSubmit, isResponding: isResponding, placeholder: placeholder, inputRef: inputRef, onStop: onStop, imageAttachments: imageAttachments, onRemoveImageAttachment: onRemoveImageAttachment, footerStartControls: editingAppControl, footerControls: composerControls, submitDisabled: submitDisabled, slashCommands: slashCommands, onSlashCommandSelect: onSlashCommandSelect }) })] })] })] }) }));
443
447
  }
444
448
  return (_jsxs(_Fragment, { children: [_jsx(AnimatePresence, { children: isExpanded && !isMinimized && (_jsx(motion.div, { "data-moldable-chat-backdrop": true, initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.2 }, className: "fixed inset-0 z-40 bg-black/20", onClick: () => onExpandedChange(false) })) }), _jsx(motion.div, { "data-moldable-chat-panel": true, initial: false, animate: {
445
449
  width: isExpanded ? 'min(90vw, 640px)' : '400px',
446
450
  y: isMinimized ? 'calc(100% + 24px)' : 0,
447
451
  opacity: isMinimized ? 0 : 1,
448
- }, transition: { type: 'tween', duration: 0.25, ease: 'easeOut' }, className: cn('pointer-events-none fixed bottom-6 left-[calc(50%+var(--sidebar-width,0px)/2)] z-50 flex -translate-x-1/2 justify-center', isMinimized && 'pointer-events-none', className), children: _jsxs("div", { "data-chat-drop-target": true, className: cn('bg-background/80 pointer-events-auto relative w-full shadow-[0_8px_40px_-12px_rgba(0,0,0,0.15)]', isExpanded
449
- ? 'bg-background'
450
- : 'bg-background/80 supports-[backdrop-filter]:backdrop-blur-xl', 'rounded-[28px]', isExpanded ? 'overflow-hidden' : 'overflow-visible'), children: [!isExpanded && (_jsx("div", { className: "pointer-events-none absolute inset-0 z-0 rounded-[inherit]", style: {
451
- background: 'linear-gradient(134deg, rgba(255,255,255,0.08) 0%, rgba(255,255,255,0.02) 50%, transparent 55%)',
452
- } })), isImageDropActive && (_jsxs("div", { className: "bg-background/90 border-primary/50 text-foreground absolute inset-0 z-[70] flex flex-col items-center justify-center gap-2 rounded-[inherit] border-2 border-dashed backdrop-blur-sm", children: [_jsx(ImageIcon, { className: "text-primary size-7" }), _jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-ui-body font-medium", children: "Drop images to attach" }), _jsx("p", { className: "text-muted-foreground text-ui-caption", children: "They stay on disk and are read when sent" })] })] })), _jsx("div", { className: "pointer-events-none absolute inset-0 z-50 rounded-[inherit]", style: {
452
+ }, transition: { type: 'tween', duration: 0.25, ease: 'easeOut' }, className: cn('pointer-events-none fixed bottom-6 left-[calc(50%+var(--sidebar-width,0px)/2)] z-50 flex -translate-x-1/2 justify-center', isMinimized && 'pointer-events-none', className), children: _jsxs("div", { "data-chat-drop-target": true, className: cn('bg-background/80 pointer-events-auto relative isolate w-full transition-[background-color,border-radius,box-shadow] duration-200 ease-out', isExpanded
453
+ ? 'bg-background rounded-[28px] shadow-[0_8px_40px_-12px_rgba(0,0,0,0.15)]'
454
+ : 'bg-background/80 rounded-[36px] supports-[backdrop-filter]:backdrop-blur-xl', isExpanded ? 'overflow-hidden' : 'overflow-visible'), children: [_jsx(motion.div, { initial: false, animate: { opacity: isExpanded ? 0 : 1 }, transition: {
455
+ duration: isExpanded ? 0.08 : 0.12,
456
+ delay: isExpanded ? 0 : 0.18,
457
+ ease: 'easeOut',
458
+ }, className: "pointer-events-none absolute inset-0 z-0 rounded-[inherit]", style: {
459
+ background: 'linear-gradient(135deg, color-mix(in oklch, var(--foreground) 9%, transparent) 0%, color-mix(in oklch, var(--foreground) 2%, transparent) 42%, transparent 58%)',
460
+ } }), isImageDropActive && (_jsxs("div", { className: "bg-background/90 border-primary/50 text-foreground absolute inset-0 z-[70] flex flex-col items-center justify-center gap-2 rounded-[inherit] border-2 border-dashed backdrop-blur-sm", children: [_jsx(ImageIcon, { className: "text-primary size-7" }), _jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-ui-body font-medium", children: "Drop images to attach" }), _jsx("p", { className: "text-muted-foreground text-ui-caption", children: "They stay on disk and are read when sent" })] })] })), _jsx("div", { className: "pointer-events-none absolute inset-0 z-[49] rounded-[inherit]", style: {
461
+ padding: '1px',
462
+ background: 'linear-gradient(135deg, color-mix(in oklch, var(--foreground) 20%, transparent), color-mix(in oklch, var(--border) 65%, transparent) 48%, color-mix(in oklch, var(--foreground) 12%, transparent))',
463
+ mask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
464
+ WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
465
+ maskComposite: 'exclude',
466
+ WebkitMaskComposite: 'xor',
467
+ } }), _jsx(motion.div, { "data-moldable-chat-edge-treatment": true, initial: false, animate: { opacity: isExpanded ? 0 : 1 }, transition: {
468
+ duration: isExpanded ? 0.08 : 0.12,
469
+ delay: isExpanded ? 0 : 0.18,
470
+ ease: 'easeOut',
471
+ }, className: "pointer-events-none absolute inset-0 z-50 rounded-[inherit]", style: {
453
472
  padding: '1px',
454
- background: 'linear-gradient(135deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.1) 20%, rgba(255,255,255,0.05) 45%, rgba(255,255,255,0.05) 55%, rgba(255,255,255,0.1) 80%, rgba(255,255,255,0.25) 100%)',
473
+ background: 'conic-gradient(from 205deg at 50% 50%, color-mix(in oklch, var(--foreground) 16%, transparent) 0deg, color-mix(in oklch, var(--border) 72%, transparent) 72deg, color-mix(in oklch, var(--primary) 18%, transparent) 148deg, color-mix(in oklch, var(--foreground) 30%, transparent) 226deg, color-mix(in oklch, var(--border) 68%, transparent) 304deg, color-mix(in oklch, var(--foreground) 16%, transparent) 360deg)',
455
474
  mask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
456
475
  WebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',
457
476
  maskComposite: 'exclude',
458
477
  WebkitMaskComposite: 'xor',
459
- } }), !isExpanded && (_jsx(motion.div, { className: "rounded-4xl pointer-events-none absolute inset-0 z-0", animate: {
460
- boxShadow: [
461
- '0 0 0px 0px color-mix(in oklch, var(--primary) 0%, #ff8800 00%)',
462
- '0 0 20px 0px color-mix(in oklch, var(--primary) 20%, #ff8800 30%)',
463
- '0 0 30px 2px color-mix(in oklch, var(--primary) 0%, #ff8800 00%)',
464
- ],
465
- }, transition: {
466
- duration: 4,
467
- repeat: Infinity,
468
- repeatDelay: 5,
469
- ease: [0.4, 0, 0.2, 1],
470
- } })), _jsxs("div", { className: "relative z-10 overflow-hidden rounded-[inherit]", children: [_jsxs(motion.div, { initial: false, animate: {
478
+ } }), _jsx(motion.div, { "data-moldable-chat-edge-depth": true, initial: false, animate: { opacity: isExpanded ? 0 : 1 }, transition: {
479
+ duration: isExpanded ? 0.08 : 0.12,
480
+ delay: isExpanded ? 0 : 0.18,
481
+ ease: 'easeOut',
482
+ }, className: "pointer-events-none absolute inset-0 z-40 rounded-[inherit]", style: {
483
+ boxShadow: 'inset 0 1px 0 color-mix(in oklch, var(--foreground) 9%, transparent), inset 0 -1px 0 color-mix(in oklch, var(--background) 72%, transparent), 0 16px 38px -26px color-mix(in oklch, var(--foreground) 52%, transparent), 0 0 24px -17px color-mix(in oklch, var(--primary) 42%, transparent)',
484
+ } }), _jsxs("div", { className: "relative z-10 overflow-hidden rounded-[inherit]", children: [_jsxs(motion.div, { initial: false, animate: {
471
485
  height: isExpanded ? 'min(60vh, 480px)' : '0px',
472
486
  opacity: isExpanded ? 1 : 0,
473
- }, transition: { type: 'tween', duration: 0.2, ease: 'easeOut' }, className: "flex flex-col overflow-hidden", style: { pointerEvents: isExpanded ? 'auto' : 'none' }, children: [isExpanded && !isMinimized && (_jsxs("div", { className: "absolute right-2 top-2 z-20 grid grid-cols-1 gap-0.5", "data-moldable-chat-panel-controls": true, role: "toolbar", "aria-label": "Chat view controls", "aria-orientation": "vertical", children: [onFullScreenChange && (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { ref: fullScreenTriggerRef, "data-moldable-chat-enter-fullscreen": true, variant: "ghost", size: "icon-sm", onClick: () => onFullScreenChange(true), "aria-label": "Enter full screen", className: "text-muted-foreground hover:text-foreground cursor-pointer", children: _jsx(Maximize2, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: "Full screen" })] }) })), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: handleMinimize, className: "text-muted-foreground hover:text-foreground cursor-pointer", children: _jsx(ChevronDown, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "Minimize" }) })] }) })] })), _jsx(ChatMessageViewport, { items: listItems, renderItem: renderVirtualItem, toolProgress: toolProgress, onApprovalResponse: onApprovalResponse, currentConversationId: currentConversationId, scrollToBottomSignal: scrollToBottomSignal, isBusy: isResponding, hasOlderMessages: hasOlderMessages, isLoadingOlderMessages: isLoadingOlderMessages, onLoadOlderMessages: onLoadOlderMessages, scrollAreaClassName: "px-2", contentClassName: "w-full min-w-0 max-w-full overflow-hidden py-4 pr-12", scrollButtonClassName: "absolute bottom-3 left-auto right-4 z-20 size-8 translate-x-0 cursor-pointer rounded-pill shadow-lg" }), authPromptCopy && (_jsx("div", { className: "shrink-0 px-4 pb-2", children: _jsx(ChatAuthPrompt, { copy: authPromptCopy, isConnecting: isConnectingChatGPT, onConnect: onConnectChatGPT ? handleConnectChatGPT : undefined, onAdvancedSetup: onAdvancedSetup }) }))] }), _jsxs("div", { className: "relative z-10", onClick: () => {
487
+ }, transition: { type: 'tween', duration: 0.2, ease: 'easeOut' }, className: "flex flex-col overflow-hidden", style: { pointerEvents: isExpanded ? 'auto' : 'none' }, children: [isExpanded && !isMinimized && (_jsxs("div", { className: "absolute right-4 top-3 z-20 grid grid-cols-1 gap-1", "data-moldable-chat-panel-controls": true, role: "toolbar", "aria-label": "Chat view controls", "aria-orientation": "vertical", children: [onFullScreenChange && (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { ref: fullScreenTriggerRef, "data-moldable-chat-enter-fullscreen": true, variant: "ghost", size: "icon-xl", onClick: () => onFullScreenChange(true), "aria-label": "Enter full screen", className: "text-muted-foreground hover:text-foreground cursor-pointer", children: _jsx(Maximize2, { className: "size-5" }) }) }), _jsx(TooltipContent, { side: "bottom", children: "Full screen" })] }) })), _jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-xl", onClick: handleMinimize, className: "text-muted-foreground hover:text-foreground cursor-pointer", children: _jsx(ChevronDown, { className: "size-5" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "Minimize" }) })] }) })] })), _jsx(ChatMessageViewport, { items: listItems, renderItem: renderVirtualItem, toolProgress: toolProgress, onApprovalResponse: onApprovalResponse, currentConversationId: currentConversationId, scrollToBottomSignal: scrollToBottomSignal, isBusy: isResponding, hasOlderMessages: hasOlderMessages, isLoadingOlderMessages: isLoadingOlderMessages, onLoadOlderMessages: onLoadOlderMessages, scrollAreaClassName: "px-2", contentClassName: "w-full min-w-0 max-w-full overflow-hidden py-4 pr-12", scrollButtonClassName: "absolute bottom-3 left-auto right-4 z-20 size-8 translate-x-0 cursor-pointer rounded-pill shadow-lg" }), authPromptCopy && (_jsx("div", { className: "shrink-0 px-4 pb-2", children: _jsx(ChatAuthPrompt, { copy: authPromptCopy, isConnecting: isConnectingChatGPT, onConnect: onConnectChatGPT ? handleConnectChatGPT : undefined, onAdvancedSetup: onAdvancedSetup }) }))] }), _jsxs("div", { className: "relative z-10", onClick: () => {
474
488
  if (!isExpanded) {
475
489
  onExpandedChange(true);
476
490
  }
477
- }, children: [queuedMessages.length > 0 && (_jsxs("div", { className: "border-border/70 bg-background/95 border-t px-4 py-2", children: [_jsxs("div", { className: "text-muted-foreground text-ui-caption mb-2 flex items-center gap-2 font-medium", children: [_jsx(ListPlus, { className: "size-3.5" }), _jsx("span", { children: "Queued follow-ups" })] }), _jsx(DndContext, { sensors: queueSensors, collisionDetection: closestCenter, onDragEnd: handleQueuedDragEnd, children: _jsx(SortableContext, { items: queuedMessageIds, strategy: verticalListSortingStrategy, children: _jsx("div", { className: "space-y-1.5", children: queuedMessages.map((message) => (_jsx(QueuedMessageRow, { message: message, isResponding: isResponding, onRetryQueuedMessage: onRetryQueuedMessage, onSteerQueuedMessage: onSteerQueuedMessage, onRemoveQueuedMessage: onRemoveQueuedMessage, onEditQueuedMessage: handleEditQueuedMessage }, message.id))) }) }) })] })), _jsx(ChatInput, { input: input, onInputChange: onInputChange, onSubmit: handleSubmit, isResponding: isResponding, placeholder: placeholder, inputRef: inputRef, onStop: onStop, compact: !isExpanded, imageAttachments: imageAttachments, onRemoveImageAttachment: onRemoveImageAttachment, footerStartControls: isExpanded ? composerStartControls : null, footerControls: isExpanded ? composerControls : null, submitDisabled: submitDisabled, slashCommands: slashCommands, onSlashCommandSelect: onSlashCommandSelect })] })] })] }) })] }));
491
+ }, children: [queuedMessages.length > 0 && (_jsxs("div", { className: "border-border/70 bg-background/95 border-t px-4 py-2", children: [_jsxs("div", { className: "text-muted-foreground text-ui-caption mb-2 flex items-center gap-2 font-medium", children: [_jsx(ListPlus, { className: "size-3.5" }), _jsx("span", { children: "Queued follow-ups" })] }), _jsx(DndContext, { sensors: queueSensors, collisionDetection: closestCenter, onDragEnd: handleQueuedDragEnd, children: _jsx(SortableContext, { items: queuedMessageIds, strategy: verticalListSortingStrategy, children: _jsx("div", { className: "space-y-1.5", children: queuedMessages.map((message) => (_jsx(QueuedMessageRow, { message: message, isResponding: isResponding, onRetryQueuedMessage: onRetryQueuedMessage, onSteerQueuedMessage: onSteerQueuedMessage, onRemoveQueuedMessage: onRemoveQueuedMessage, onEditQueuedMessage: handleEditQueuedMessage }, message.id))) }) }) })] })), _jsx(ChatInput, { input: input, onInputChange: onInputChange, onSubmit: handleSubmit, isResponding: isResponding, placeholder: placeholder, inputRef: inputRef, onStop: onStop, compact: !isExpanded, surfaceBorderRadius: isExpanded ? 24 : undefined, imageAttachments: imageAttachments, onRemoveImageAttachment: onRemoveImageAttachment, footerStartControls: isExpanded ? composerStartControls : null, footerControls: isExpanded ? composerControls : null, submitDisabled: submitDisabled, slashCommands: slashCommands, onSlashCommandSelect: onSlashCommandSelect })] })] })] }) })] }));
478
492
  }
@@ -26,6 +26,8 @@ interface ConversationListProps {
26
26
  conversations: ConversationMeta[];
27
27
  currentConversationId?: string | null;
28
28
  currentAppId?: string | null;
29
+ /** Opts into app/scope disclosure groups for unified conversation surfaces. */
30
+ groupByScope?: boolean;
29
31
  onSelect: (id: string) => void;
30
32
  onArchive?: (id: string) => void;
31
33
  onArchiveGroup?: (group: ConversationGroup) => void;
@@ -51,7 +53,7 @@ interface ConversationSidebarProps extends ConversationListProps {
51
53
  onLoadMoreConversations?: () => void | Promise<unknown>;
52
54
  }
53
55
  export declare function groupConversations(conversations: ConversationMeta[], currentAppId?: string | null): ConversationGroup[];
54
- export declare function ConversationSidebar({ conversations, currentConversationId, currentAppId, onSelect, onArchive, onArchiveGroup, onNewChat, newChatDisabled, hideScrollbar, hasMoreConversations, isLoadingMoreConversations, onLoadMoreConversations, disabled, className, }: ConversationSidebarProps): import("react/jsx-runtime").JSX.Element;
55
- export declare function ConversationHistory({ conversations, currentConversationId, currentAppId, onSelect, onArchive, onArchiveGroup, onNewChat, newChatDisabled, disabled, className, dropdownAlign, dropdownSide, tooltipSide, hasMoreConversations, isLoadingMoreConversations, onLoadMoreConversations, }: ConversationHistoryProps): import("react/jsx-runtime").JSX.Element | null;
56
+ export declare function ConversationSidebar({ conversations, currentConversationId, currentAppId, groupByScope, onSelect, onArchive, onArchiveGroup, onNewChat, newChatDisabled, hideScrollbar, hasMoreConversations, isLoadingMoreConversations, onLoadMoreConversations, disabled, className, }: ConversationSidebarProps): import("react/jsx-runtime").JSX.Element;
57
+ export declare function ConversationHistory({ conversations, currentConversationId, currentAppId, groupByScope, onSelect, onArchive, onArchiveGroup, onNewChat, newChatDisabled, disabled, className, dropdownAlign, dropdownSide, tooltipSide, hasMoreConversations, isLoadingMoreConversations, onLoadMoreConversations, }: ConversationHistoryProps): import("react/jsx-runtime").JSX.Element | null;
56
58
  export {};
57
59
  //# sourceMappingURL=conversation-history.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-history.d.ts","sourceRoot":"","sources":["../../../src/components/chat/conversation-history.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conversation-history.d.ts","sourceRoot":"","sources":["../../../src/components/chat/conversation-history.tsx"],"names":[],"mappings":"AAiCA,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,sFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,aAAa,EAAE,gBAAgB,EAAE,CAAA;CAClC;AAOD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GACtB,MAAM,CAIR;AAED,wBAAgB,6BAA6B,CAC3C,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,IAAI,IAAI,MAAM,CAIhB;AAED,UAAU,qBAAqB;IAC7B,aAAa,EAAE,gBAAgB,EAAE,CAAA;IACjC,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,+EAA+E;IAC/E,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAA;IACnD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,UAAU,wBAAyB,SAAQ,qBAAqB;IAC9D,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,aAAa,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;IAC1C,YAAY,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IAClD,WAAW,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAA;IACjD,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,0BAA0B,CAAC,EAAE,OAAO,CAAA;IACpC,uBAAuB,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACxD;AAED,UAAU,wBAAyB,SAAQ,qBAAqB;IAC9D,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,0BAA0B,CAAC,EAAE,OAAO,CAAA;IACpC,uBAAuB,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACxD;AAED,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,gBAAgB,EAAE,EACjC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAC3B,iBAAiB,EAAE,CA8CrB;AAqPD,wBAAgB,mBAAmB,CAAC,EAClC,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,YAAoB,EACpB,QAAQ,EACR,SAAS,EACT,cAAc,EACd,SAAS,EACT,eAAuB,EACvB,aAAqB,EACrB,oBAA4B,EAC5B,0BAAkC,EAClC,uBAAuB,EACvB,QAAgB,EAChB,SAAS,GACV,EAAE,wBAAwB,2CAwH1B;AAED,wBAAgB,mBAAmB,CAAC,EAClC,aAAa,EACb,qBAAqB,EACrB,YAAY,EACZ,YAAoB,EACpB,QAAQ,EACR,SAAS,EACT,cAAc,EACd,SAAS,EACT,eAAuB,EACvB,QAAgB,EAChB,SAAS,EACT,aAAuB,EACvB,YAAuB,EACvB,WAAsB,EACtB,oBAA4B,EAC5B,0BAAkC,EAClC,uBAAuB,GACxB,EAAE,wBAAwB,kDAmG1B"}
@@ -69,18 +69,28 @@ function AppScopeIcon({ icon, isChats, }) {
69
69
  if (isChats)
70
70
  return _jsx(MessageCircle, { className: "size-4" });
71
71
  if (isConversationIconImageSource(icon)) {
72
- return (_jsx("img", { src: icon, alt: "", className: "rounded-control size-4 object-cover" }));
72
+ return _jsx("img", { src: icon, alt: "", className: "size-4 rounded object-cover" });
73
73
  }
74
74
  if (icon)
75
- return _jsx("span", { className: "text-ui-body leading-none", children: icon });
75
+ return _jsx("span", { className: "text-sm leading-none", children: icon });
76
76
  return _jsx(Folder, { className: "size-4" });
77
77
  }
78
- function ConversationList({ conversations, currentConversationId, currentAppId, onSelect, onArchive, onArchiveGroup, disabled = false, className, }) {
78
+ function ConversationRows({ conversations, currentConversationId, onSelect, onArchive, disabled, }) {
79
+ return conversations.map((conversation) => (_jsxs("li", { "data-conversation-id": conversation.id, className: cn('group/conversation relative flex min-w-0 items-center rounded-lg', conversation.id === currentConversationId && 'bg-accent'), children: [_jsx("button", { type: "button", disabled: disabled, onClick: () => onSelect(conversation.id), "aria-current": conversation.id === currentConversationId ? 'page' : undefined, className: "min-w-0 flex-1 cursor-pointer truncate rounded-lg py-1.5 pl-3 pr-8 text-left text-sm disabled:cursor-default", children: conversation.title }), conversation.isActive && (_jsx(LoaderCircle, { className: "text-muted-foreground absolute right-2 size-3.5 animate-spin", "aria-label": "Conversation is working" })), onArchive && !conversation.isActive && (_jsx("button", { type: "button", onClick: (event) => {
80
+ event.stopPropagation();
81
+ onArchive(conversation.id);
82
+ }, disabled: disabled, className: "text-muted-foreground hover:text-foreground focus-visible:ring-ring absolute right-1 flex size-7 cursor-pointer items-center justify-center rounded-md opacity-0 transition-opacity focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 disabled:cursor-default group-focus-within/conversation:opacity-100 group-hover/conversation:opacity-100", "aria-label": `Archive ${conversation.title}`, children: _jsx(Archive, { className: "size-3.5" }) }))] }, conversation.id)));
83
+ }
84
+ function ConversationList({ conversations, currentConversationId, currentAppId, groupByScope = false, onSelect, onArchive, onArchiveGroup, disabled = false, className, }) {
79
85
  const groups = useMemo(() => groupConversations(conversations, currentAppId), [conversations, currentAppId]);
80
86
  const [collapsedGroups, setCollapsedGroups] = useState(() => new Set());
81
87
  const [expandedGroups, setExpandedGroups] = useState(() => new Set());
82
88
  const [groupPendingArchive, setGroupPendingArchive] = useState(null);
83
89
  const listIdPrefix = useId();
90
+ const flatConversations = useMemo(() => [...conversations].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)), [conversations]);
91
+ if (!groupByScope) {
92
+ return (_jsx("nav", { "aria-label": "Conversations", className: className, children: _jsx("ul", { className: "space-y-0.5", children: _jsx(ConversationRows, { conversations: flatConversations, currentConversationId: currentConversationId, onSelect: onSelect, onArchive: onArchive, disabled: disabled }) }) }));
93
+ }
84
94
  return (_jsxs("nav", { "aria-label": "Conversations", className: cn('space-y-1', className), children: [groups.map((group) => {
85
95
  const isCollapsed = collapsedGroups.has(group.id);
86
96
  const isShowingAll = expandedGroups.has(group.id);
@@ -96,19 +106,14 @@ function ConversationList({ conversations, currentConversationId, currentAppId,
96
106
  else
97
107
  next.add(group.id);
98
108
  return next;
99
- }), className: "text-foreground hover:bg-accent/60 rounded-container text-ui-body flex h-9 min-w-0 flex-1 cursor-pointer items-center gap-2 px-2 pr-9 text-left font-medium transition-colors", children: [_jsx(ChevronDown, { className: cn('text-muted-foreground size-3.5 shrink-0 transition-transform', isCollapsed && '-rotate-90') }), _jsx("span", { className: "text-muted-foreground flex size-4 shrink-0 items-center justify-center", children: _jsx(AppScopeIcon, { icon: group.icon, isChats: group.id === CHATS_GROUP_ID }) }), _jsx("span", { className: "truncate", children: group.label })] }), onArchiveGroup && (_jsx("button", { type: "button", onClick: () => setGroupPendingArchive(group), className: "text-muted-foreground hover:text-foreground focus-visible:ring-ring rounded-control absolute right-1 flex size-7 cursor-pointer items-center justify-center opacity-0 transition-opacity focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-1 group-focus-within/conversation-scope:opacity-100 group-hover/conversation-scope:opacity-100", "aria-label": `Archive all chats in ${group.label}`, children: _jsx(Archive, { className: "size-3.5" }) }))] }), !isCollapsed && (_jsxs("ul", { id: listId, className: "ml-5 space-y-0.5 py-0.5", children: [visibleConversations.map((conversation) => (_jsxs("li", { "data-conversation-id": conversation.id, className: cn('group/conversation rounded-container relative flex min-w-0 items-center', conversation.id === currentConversationId && 'bg-accent'), children: [_jsx("button", { type: "button", disabled: disabled, onClick: () => onSelect(conversation.id), "aria-current": conversation.id === currentConversationId
100
- ? 'page'
101
- : undefined, className: "rounded-container text-ui-body min-w-0 flex-1 cursor-pointer truncate py-1.5 pl-3 pr-8 text-left disabled:cursor-default", children: conversation.title }), conversation.isActive && (_jsx(LoaderCircle, { className: "text-muted-foreground absolute right-2 size-3.5 animate-spin", "aria-label": "Conversation is working" })), onArchive && !conversation.isActive && (_jsx("button", { type: "button", onClick: (event) => {
102
- event.stopPropagation();
103
- onArchive(conversation.id);
104
- }, disabled: disabled, className: "text-muted-foreground hover:text-foreground focus-visible:ring-ring rounded-control absolute right-1 flex size-7 cursor-pointer items-center justify-center opacity-0 transition-opacity focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-1 disabled:cursor-default group-focus-within/conversation:opacity-100 group-hover/conversation:opacity-100", "aria-label": `Archive ${conversation.title}`, children: _jsx(Archive, { className: "size-3.5" }) }))] }, conversation.id))), hasMoreConversations && (_jsx("li", { children: _jsx("button", { type: "button", onClick: () => setExpandedGroups((current) => {
109
+ }), className: "text-foreground hover:bg-accent/60 flex h-9 min-w-0 flex-1 cursor-pointer items-center gap-2 rounded-lg px-2 pr-9 text-left text-sm font-medium transition-colors", children: [_jsx(ChevronDown, { className: cn('text-muted-foreground size-3.5 shrink-0 transition-transform', isCollapsed && '-rotate-90') }), _jsx("span", { className: "text-muted-foreground flex size-4 shrink-0 items-center justify-center", children: _jsx(AppScopeIcon, { icon: group.icon, isChats: group.id === CHATS_GROUP_ID }) }), _jsx("span", { className: "truncate", children: group.label })] }), onArchiveGroup && (_jsx("button", { type: "button", onClick: () => setGroupPendingArchive(group), className: "text-muted-foreground hover:text-foreground focus-visible:ring-ring absolute right-1 flex size-7 cursor-pointer items-center justify-center rounded-md opacity-0 transition-opacity focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 group-focus-within/conversation-scope:opacity-100 group-hover/conversation-scope:opacity-100", "aria-label": `Archive all chats in ${group.label}`, children: _jsx(Archive, { className: "size-3.5" }) }))] }), !isCollapsed && (_jsxs("ul", { id: listId, className: "ml-5 space-y-0.5 py-0.5", children: [_jsx(ConversationRows, { conversations: visibleConversations, currentConversationId: currentConversationId, onSelect: onSelect, onArchive: onArchive, disabled: disabled }), hasMoreConversations && (_jsx("li", { children: _jsx("button", { type: "button", onClick: () => setExpandedGroups((current) => {
105
110
  const next = new Set(current);
106
111
  if (next.has(group.id))
107
112
  next.delete(group.id);
108
113
  else
109
114
  next.add(group.id);
110
115
  return next;
111
- }), className: "text-muted-foreground hover:bg-accent/60 hover:text-foreground rounded-container text-ui-caption flex h-7 w-full cursor-pointer items-center px-3 text-left transition-colors", "aria-label": `${isShowingAll ? 'Show fewer' : 'Show all'} ${group.label} conversations`, children: isShowingAll ? 'Show less' : 'Show all' }) }))] }))] }, group.id));
116
+ }), className: "text-muted-foreground hover:bg-accent/60 hover:text-foreground flex h-7 w-full cursor-pointer items-center rounded-lg px-3 text-left text-xs transition-colors", "aria-label": `${isShowingAll ? 'Show fewer' : 'Show all'} ${group.label} conversations`, children: isShowingAll ? 'Show less' : 'Show all' }) }))] }))] }, group.id));
112
117
  }), _jsx(AlertDialog, { open: groupPendingArchive !== null, onOpenChange: (open) => {
113
118
  if (!open)
114
119
  setGroupPendingArchive(null);
@@ -119,7 +124,7 @@ function ConversationList({ conversations, currentConversationId, currentAppId,
119
124
  setGroupPendingArchive(null);
120
125
  }, children: "Archive all" })] })] }) })] }));
121
126
  }
122
- export function ConversationSidebar({ conversations, currentConversationId, currentAppId, onSelect, onArchive, onArchiveGroup, onNewChat, newChatDisabled = false, hideScrollbar = false, hasMoreConversations = false, isLoadingMoreConversations = false, onLoadMoreConversations, disabled = false, className, }) {
127
+ export function ConversationSidebar({ conversations, currentConversationId, currentAppId, groupByScope = false, onSelect, onArchive, onArchiveGroup, onNewChat, newChatDisabled = false, hideScrollbar = false, hasMoreConversations = false, isLoadingMoreConversations = false, onLoadMoreConversations, disabled = false, className, }) {
123
128
  const scrollContainerRef = useRef(null);
124
129
  const [scrollFadeEdges, setScrollFadeEdges] = useState({
125
130
  top: false,
@@ -172,16 +177,16 @@ export function ConversationSidebar({ conversations, currentConversationId, curr
172
177
  '[scrollbar-width:none] [&::-webkit-scrollbar]:hidden'), "data-conversation-scroll-fade": true, "data-scroll-fade-top": scrollFadeEdges.top || undefined, "data-scroll-fade-bottom": scrollFadeEdges.bottom || undefined, onScroll: (event) => handleScroll(event.currentTarget), style: {
173
178
  maskImage: scrollMask,
174
179
  WebkitMaskImage: scrollMask,
175
- }, children: [_jsx(ConversationList, { conversations: conversations, currentConversationId: currentConversationId, currentAppId: currentAppId, onSelect: onSelect, onArchive: onArchive, onArchiveGroup: hasMoreConversations ? undefined : onArchiveGroup, disabled: disabled }), hasMoreConversations && (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", disabled: isLoadingMoreConversations, onClick: () => void onLoadMoreConversations?.(), className: "text-muted-foreground mt-1 w-full cursor-pointer", children: [isLoadingMoreConversations && (_jsx(LoaderCircle, { className: "size-3.5 animate-spin" })), "Load older conversations"] }))] })] }));
180
+ }, children: [_jsx(ConversationList, { conversations: conversations, currentConversationId: currentConversationId, currentAppId: currentAppId, groupByScope: groupByScope, onSelect: onSelect, onArchive: onArchive, onArchiveGroup: hasMoreConversations ? undefined : onArchiveGroup, disabled: disabled }), hasMoreConversations && (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", disabled: isLoadingMoreConversations, onClick: () => void onLoadMoreConversations?.(), className: "text-muted-foreground mt-1 w-full cursor-pointer", children: [isLoadingMoreConversations && (_jsx(LoaderCircle, { className: "size-3.5 animate-spin" })), "Load older conversations"] }))] })] }));
176
181
  }
177
- export function ConversationHistory({ conversations, currentConversationId, currentAppId, onSelect, onArchive, onArchiveGroup, onNewChat, newChatDisabled = false, disabled = false, className, dropdownAlign = 'start', dropdownSide = 'bottom', tooltipSide = 'bottom', hasMoreConversations = false, isLoadingMoreConversations = false, onLoadMoreConversations, }) {
182
+ export function ConversationHistory({ conversations, currentConversationId, currentAppId, groupByScope = false, onSelect, onArchive, onArchiveGroup, onNewChat, newChatDisabled = false, disabled = false, className, dropdownAlign = 'start', dropdownSide = 'bottom', tooltipSide = 'bottom', hasMoreConversations = false, isLoadingMoreConversations = false, onLoadMoreConversations, }) {
178
183
  const [isOpen, setIsOpen] = useState(false);
179
184
  if (conversations.length === 0 && !onNewChat)
180
185
  return null;
181
186
  return (_jsx(TooltipProvider, { children: _jsxs(Popover, { open: isOpen, onOpenChange: setIsOpen, children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", disabled: disabled, className: cn('text-muted-foreground hover:text-foreground cursor-pointer', className), "aria-label": "Conversations", children: _jsx(MessageCircle, { className: "size-4" }) }) }) }), _jsx(TooltipContent, { side: tooltipSide, children: "Conversations" })] }), _jsxs(PopoverContent, { align: dropdownAlign, side: dropdownSide, className: "w-72 p-2", children: [onNewChat && (_jsxs(Button, { type: "button", variant: "ghost", onClick: () => {
182
187
  onNewChat();
183
188
  setIsOpen(false);
184
- }, disabled: newChatDisabled, title: "New conversation (Cmd+Shift+O)", className: "mb-1 flex w-full cursor-pointer items-center justify-start gap-2", children: [_jsx(Plus, { className: "size-4" }), _jsx("span", { children: "New conversation" }), _jsx("span", { className: "text-muted-foreground text-ui-caption ml-auto", children: "\u2318\u21E7O" })] })), _jsxs("div", { className: "max-h-80 overflow-y-auto", onScroll: (event) => {
189
+ }, disabled: newChatDisabled, title: "New conversation (Cmd+Shift+O)", className: "mb-1 flex w-full cursor-pointer items-center justify-start gap-2 rounded-lg text-sm [&_[data-slot=button-content]]:w-full", children: [_jsx(Plus, { className: "size-4" }), _jsx("span", { children: "New conversation" }), _jsx("span", { className: "text-muted-foreground ml-auto text-xs", children: "\u2318\u21E7O" })] })), _jsxs("div", { className: "max-h-80 overflow-y-auto", onScroll: (event) => {
185
190
  const element = event.currentTarget;
186
191
  if (hasMoreConversations &&
187
192
  !isLoadingMoreConversations &&
@@ -191,7 +196,7 @@ export function ConversationHistory({ conversations, currentConversationId, curr
191
196
  120) {
192
197
  void onLoadMoreConversations?.();
193
198
  }
194
- }, children: [_jsx(ConversationList, { conversations: conversations, currentConversationId: currentConversationId, currentAppId: currentAppId, onSelect: (id) => {
199
+ }, children: [_jsx(ConversationList, { conversations: conversations, currentConversationId: currentConversationId, currentAppId: currentAppId, groupByScope: groupByScope, onSelect: (id) => {
195
200
  onSelect(id);
196
201
  setIsOpen(false);
197
202
  }, onArchive: onArchive, onArchiveGroup: hasMoreConversations ? undefined : onArchiveGroup, disabled: disabled }), hasMoreConversations && (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", disabled: isLoadingMoreConversations, onClick: () => void onLoadMoreConversations?.(), className: "text-muted-foreground mt-1 w-full cursor-pointer", children: [isLoadingMoreConversations && (_jsx(LoaderCircle, { className: "size-3.5 animate-spin" })), "Load older conversations"] }))] })] })] }) }));
@@ -3,7 +3,7 @@ import { type IconButtonProps } from './icon-button.js';
3
3
  import { type VariantProps } from 'class-variance-authority';
4
4
  declare const alertVariants: (props?: ({
5
5
  tone?: "neutral" | "info" | "success" | "warning" | "danger" | null | undefined;
6
- density?: "compact" | "default" | null | undefined;
6
+ density?: "default" | "compact" | null | undefined;
7
7
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
8
8
  type AlertTone = NonNullable<VariantProps<typeof alertVariants>['tone']>;
9
9
  type AlertDensity = NonNullable<VariantProps<typeof alertVariants>['density']>;
@@ -1,6 +1,18 @@
1
1
  import * as React from 'react';
2
2
  import type { MaterialVariant } from './material.js';
3
3
  export type AppFrameMode = 'auto' | 'embedded' | 'window';
4
+ export interface AppFrameNativeMaterial {
5
+ /**
6
+ * Reveal the host-rendered frosted window background while keeping the
7
+ * portable canvas fill as the fallback.
8
+ */
9
+ background?: boolean;
10
+ /**
11
+ * Allow a compatible leading `Sidebar` to request host-rendered native
12
+ * material when the app manifest and host both advertise support.
13
+ */
14
+ sidebar?: boolean;
15
+ }
4
16
  declare function resolveAppFrameMode(mode: AppFrameMode): Exclude<AppFrameMode, 'auto'>;
5
17
  export interface AppFrameProps extends React.ComponentProps<'div'> {
6
18
  /**
@@ -8,14 +20,21 @@ export interface AppFrameProps extends React.ComponentProps<'div'> {
8
20
  * in catalogs, previews, and tests where the runtime environment is ambiguous.
9
21
  */
10
22
  mode?: AppFrameMode;
23
+ /**
24
+ * Explicitly opts this app frame into renderer-owned native material
25
+ * regions. The matching region must also be enabled in `moldable.json`.
26
+ */
27
+ nativeMaterial?: AppFrameNativeMaterial;
11
28
  }
12
29
  interface AppFrameRuntimeContextValue {
13
30
  mode: Exclude<AppFrameMode, 'auto'>;
31
+ backgroundMaterialActive: boolean;
14
32
  sidebarMaterialAvailable: boolean;
33
+ sidebarMaterialEnabled: boolean;
15
34
  sidebarMaterialRegionId: string | null;
16
35
  }
17
36
  export declare function useAppFrameRuntime(): AppFrameRuntimeContextValue | null;
18
- declare function AppFrame({ mode, className, style, ...props }: AppFrameProps): import("react/jsx-runtime").JSX.Element;
37
+ declare function AppFrame({ mode, nativeMaterial, className, style, ...props }: AppFrameProps): import("react/jsx-runtime").JSX.Element;
19
38
  export interface AppFrameTitlebarProps extends React.ComponentPropsWithoutRef<'header'> {
20
39
  material?: MaterialVariant | 'none';
21
40
  }
@@ -1 +1 @@
1
- {"version":3,"file":"app-frame.d.ts","sourceRoot":"","sources":["../../../src/components/ui/app-frame.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAMjD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;AAEzD,iBAAS,mBAAmB,CAC1B,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,CAM/B;AAED,MAAM,WAAW,aAAc,SAAQ,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC;IAChE;;;OAGG;IACH,IAAI,CAAC,EAAE,YAAY,CAAA;CACpB;AAED,UAAU,2BAA2B;IACnC,IAAI,EAAE,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,CAAA;IACnC,wBAAwB,EAAE,OAAO,CAAA;IACjC,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAA;CACvC;AAKD,wBAAgB,kBAAkB,IAAI,2BAA2B,GAAG,IAAI,CAEvE;AAsBD,iBAAS,QAAQ,CAAC,EAChB,IAAa,EACb,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,aAAa,2CAoEf;AAED,MAAM,WAAW,qBACf,SAAQ,KAAK,CAAC,wBAAwB,CAAC,QAAQ,CAAC;IAChD,QAAQ,CAAC,EAAE,eAAe,GAAG,MAAM,CAAA;CACpC;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,QAAoB,EACpB,aAAa,EACb,WAAW,EACX,KAAK,EACL,GAAG,KAAK,EACT,EAAE,qBAAqB,2CA+BvB;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,wBAAwB,CAAC,KAAK,CAAC,2CAWvC;AAED,MAAM,WAAW,oBACf,SAAQ,KAAK,CAAC,wBAAwB,CAAC,MAAM,CAAC;IAC9C,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,iBAAS,eAAe,CAAC,EACvB,UAAiB,EACjB,QAAe,EACf,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,oBAAoB,2CAoBtB;AAED,MAAM,WAAW,sBACf,SAAQ,KAAK,CAAC,wBAAwB,CAAC,QAAQ,CAAC;IAChD,QAAQ,CAAC,EAAE,eAAe,GAAG,MAAM,CAAA;CACpC;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,QAAoB,EACpB,KAAK,EACL,GAAG,KAAK,EACT,EAAE,sBAAsB,2CAmBxB;AAED,OAAO,EACL,QAAQ,EACR,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,GACpB,CAAA"}
1
+ {"version":3,"file":"app-frame.d.ts","sourceRoot":"","sources":["../../../src/components/ui/app-frame.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAMjD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;AAEzD,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,iBAAS,mBAAmB,CAC1B,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,CAM/B;AAED,MAAM,WAAW,aAAc,SAAQ,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC;IAChE;;;OAGG;IACH,IAAI,CAAC,EAAE,YAAY,CAAA;IACnB;;;OAGG;IACH,cAAc,CAAC,EAAE,sBAAsB,CAAA;CACxC;AAED,UAAU,2BAA2B;IACnC,IAAI,EAAE,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,CAAA;IACnC,wBAAwB,EAAE,OAAO,CAAA;IACjC,wBAAwB,EAAE,OAAO,CAAA;IACjC,sBAAsB,EAAE,OAAO,CAAA;IAC/B,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAA;CACvC;AAKD,wBAAgB,kBAAkB,IAAI,2BAA2B,GAAG,IAAI,CAEvE;AA8BD,iBAAS,QAAQ,CAAC,EAChB,IAAa,EACb,cAAc,EACd,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,aAAa,2CAuGf;AAED,MAAM,WAAW,qBACf,SAAQ,KAAK,CAAC,wBAAwB,CAAC,QAAQ,CAAC;IAChD,QAAQ,CAAC,EAAE,eAAe,GAAG,MAAM,CAAA;CACpC;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,QAAoB,EACpB,aAAa,EACb,WAAW,EACX,KAAK,EACL,GAAG,KAAK,EACT,EAAE,qBAAqB,2CA+BvB;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,wBAAwB,CAAC,KAAK,CAAC,2CAWvC;AAED,MAAM,WAAW,oBACf,SAAQ,KAAK,CAAC,wBAAwB,CAAC,MAAM,CAAC;IAC9C,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,iBAAS,eAAe,CAAC,EACvB,UAAiB,EACjB,QAAe,EACf,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE,oBAAoB,2CAoBtB;AAED,MAAM,WAAW,sBACf,SAAQ,KAAK,CAAC,wBAAwB,CAAC,QAAQ,CAAC;IAChD,QAAQ,CAAC,EAAE,eAAe,GAAG,MAAM,CAAA;CACpC;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,QAAoB,EACpB,KAAK,EACL,GAAG,KAAK,EACT,EAAE,sBAAsB,2CAmBxB;AAED,OAAO,EACL,QAAQ,EACR,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,EACf,mBAAmB,GACpB,CAAA"}
@@ -30,13 +30,20 @@ function sidebarMaterialAvailable() {
30
30
  document.documentElement.dataset.moldableNativeMaterialSidebar ===
31
31
  'available');
32
32
  }
33
- function AppFrame({ mode = 'auto', className, style, ...props }) {
33
+ function backgroundMaterialAvailable() {
34
+ return (typeof document !== 'undefined' &&
35
+ document.documentElement.dataset.moldableNativeMaterialBackground ===
36
+ 'available');
37
+ }
38
+ function AppFrame({ mode = 'auto', nativeMaterial, className, style, ...props }) {
34
39
  const [resolvedMode, setResolvedMode] = React.useState(() => resolveAppFrameMode(mode));
35
40
  const [sidebarMaterialRegionId, setSidebarMaterialRegionId] = React.useState(activeSidebarMaterialRegionId);
36
41
  const [nativeSidebarMaterialAvailable, setNativeSidebarMaterialAvailable] = React.useState(sidebarMaterialAvailable);
42
+ const [nativeBackgroundMaterialAvailable, setNativeBackgroundMaterialAvailable,] = React.useState(backgroundMaterialAvailable);
37
43
  React.useEffect(() => {
38
44
  const updateHostState = () => {
39
45
  setResolvedMode(resolveAppFrameMode(mode));
46
+ setNativeBackgroundMaterialAvailable(backgroundMaterialAvailable());
40
47
  setNativeSidebarMaterialAvailable(sidebarMaterialAvailable());
41
48
  setSidebarMaterialRegionId(activeSidebarMaterialRegionId());
42
49
  };
@@ -50,19 +57,42 @@ function AppFrame({ mode = 'auto', className, style, ...props }) {
50
57
  };
51
58
  }, [mode]);
52
59
  const sidebarMaterialActive = resolvedMode === 'window' && sidebarMaterialRegionId !== null;
60
+ const backgroundMaterialActive = resolvedMode === 'window' &&
61
+ nativeMaterial?.background === true &&
62
+ nativeBackgroundMaterialAvailable;
63
+ React.useEffect(() => {
64
+ if (!backgroundMaterialActive)
65
+ return;
66
+ const previousBackgroundColor = document.body.style.backgroundColor;
67
+ document.body.style.backgroundColor = 'transparent';
68
+ return () => {
69
+ if (previousBackgroundColor) {
70
+ document.body.style.backgroundColor = previousBackgroundColor;
71
+ }
72
+ else {
73
+ document.body.style.removeProperty('background-color');
74
+ }
75
+ };
76
+ }, [backgroundMaterialActive]);
53
77
  return (_jsx(AppFrameRuntimeContext.Provider, { value: {
54
78
  mode: resolvedMode,
79
+ backgroundMaterialActive,
55
80
  sidebarMaterialAvailable: nativeSidebarMaterialAvailable,
81
+ sidebarMaterialEnabled: nativeMaterial?.sidebar === true,
56
82
  sidebarMaterialRegionId,
57
- }, children: _jsx("div", { "data-slot": "app-frame", "data-mode": resolvedMode, "data-native-material-sidebar": sidebarMaterialActive ? 'active' : undefined, className: cn('text-foreground flex h-dvh min-h-0 min-w-0 flex-col overflow-hidden bg-[var(--surface-canvas,var(--background))]', className), style: {
83
+ }, children: _jsx("div", { "data-slot": "app-frame", "data-mode": resolvedMode, "data-native-material-background": backgroundMaterialActive ? 'active' : undefined, "data-native-material-sidebar": sidebarMaterialActive ? 'active' : undefined, "data-native-material-sidebar-enabled": nativeMaterial?.sidebar === true ? 'true' : undefined, className: cn('text-foreground flex h-dvh min-h-0 min-w-0 flex-col overflow-hidden', backgroundMaterialActive
84
+ ? 'bg-transparent'
85
+ : 'bg-[var(--surface-canvas,var(--background))]', className), style: {
58
86
  paddingInlineStart: 'var(--window-safe-area-inline-start, 0px)',
59
87
  paddingInlineEnd: 'var(--window-safe-area-inline-end, 0px)',
60
88
  ...style,
61
- ...(sidebarMaterialActive
62
- ? {
63
- background: 'linear-gradient(to right, transparent 0 var(--moldable-material-region-sidebar-width), var(--surface-canvas, var(--background)) var(--moldable-material-region-sidebar-width) 100%)',
64
- }
65
- : {}),
89
+ ...(backgroundMaterialActive
90
+ ? { background: 'transparent' }
91
+ : sidebarMaterialActive
92
+ ? {
93
+ background: 'linear-gradient(to right, transparent 0 var(--moldable-material-region-sidebar-width), var(--surface-canvas, var(--background)) var(--moldable-material-region-sidebar-width) 100%)',
94
+ }
95
+ : {}),
66
96
  }, ...props }) }));
67
97
  }
68
98
  function AppFrameTitlebar({ className, material = 'regular', onDoubleClick, onMouseDown, style, ...props }) {
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { type VariantProps } from 'class-variance-authority';
3
3
  declare const badgeVariants: (props?: ({
4
- variant?: "link" | "default" | "outline" | "destructive" | "secondary" | "ghost" | null | undefined;
4
+ variant?: "default" | "link" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
5
5
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
6
6
  declare function Badge({ className, variant, asChild, ...props }: React.ComponentProps<'span'> & VariantProps<typeof badgeVariants> & {
7
7
  asChild?: boolean;
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Slot } from '@radix-ui/react-slot';
4
4
  import { cn } from '../../lib/utils.js';
5
5
  import { cva } from 'class-variance-authority';
6
- const badgeVariants = cva('text-ui-caption inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-pill border border-transparent px-2 py-0.5 font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring aria-invalid:border-destructive [&>svg]:pointer-events-none [&>svg]:size-3', {
6
+ const badgeVariants = cva('inline-flex h-[var(--density-badge-height)] w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-pill border border-transparent px-[var(--density-badge-padding-inline)] py-0 text-[length:var(--type-badge-size)] leading-[var(--type-badge-line-height)] font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-1 focus-visible:ring-ring aria-invalid:border-destructive [&>svg]:pointer-events-none [&>svg]:size-3', {
7
7
  variants: {
8
8
  variant: {
9
9
  default: 'bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
@@ -1,13 +1,15 @@
1
1
  import * as React from 'react';
2
+ import type { ControlSize } from '../../tokens/contracts.js';
2
3
  import { type VariantProps } from 'class-variance-authority';
3
4
  declare const buttonVariants: (props?: ({
4
- variant?: "link" | "toolbar" | "default" | "outline" | "destructive" | "secondary" | "ghost" | null | undefined;
5
- size?: "default" | "sm" | "xs" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
5
+ variant?: "link" | "toolbar" | "outline" | "default" | "prominent" | "bordered" | "borderless" | "tinted" | "destructive" | "destructive-tinted" | "secondary" | "ghost" | null | undefined;
6
+ size?: "small" | "sm" | "default" | "lg" | "xs" | "xl" | "mini" | "medium" | "large" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | "icon-xl" | null | undefined;
6
7
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
8
  type ButtonProps = React.ComponentProps<'button'> & VariantProps<typeof buttonVariants> & {
8
9
  asChild?: boolean;
10
+ controlSize?: ControlSize;
9
11
  loading?: boolean;
10
12
  };
11
- declare function Button({ 'aria-busy': ariaBusy, children, className, disabled, loading, onClick, onKeyDown, variant, size, asChild, ...props }: ButtonProps): import("react/jsx-runtime").JSX.Element;
13
+ declare function Button({ 'aria-busy': ariaBusy, children, className, controlSize, disabled, loading, onClick, onKeyDown, variant, size, asChild, ...props }: ButtonProps): import("react/jsx-runtime").JSX.Element;
12
14
  export { Button, buttonVariants };
13
15
  //# sourceMappingURL=button.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../../src/components/ui/button.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAG9B,OAAO,EAAE,KAAK,YAAY,EAAO,MAAM,0BAA0B,CAAA;AAEjE,QAAA,MAAM,cAAc;;;8EAoCnB,CAAA;AAED,KAAK,WAAW,GAAG,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAC/C,YAAY,CAAC,OAAO,cAAc,CAAC,GAAG;IACpC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AA4CH,iBAAS,MAAM,CAAC,EACd,WAAW,EAAE,QAAQ,EACrB,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,OAAe,EACf,OAAO,EACP,SAAS,EACT,OAAmB,EACnB,IAAgB,EAChB,OAAe,EACf,GAAG,KAAK,EACT,EAAE,WAAW,2CAqDb;AAED,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA"}
1
+ {"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../../src/components/ui/button.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAEzD,OAAO,EAAE,KAAK,YAAY,EAAO,MAAM,0BAA0B,CAAA;AAEjE,QAAA,MAAM,cAAc;;;8EAwDnB,CAAA;AAED,KAAK,WAAW,GAAG,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAC/C,YAAY,CAAC,OAAO,cAAc,CAAC,GAAG;IACpC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB,CAAA;AA4CH,iBAAS,MAAM,CAAC,EACd,WAAW,EAAE,QAAQ,EACrB,QAAQ,EACR,SAAS,EACT,WAAW,EACX,QAAQ,EACR,OAAe,EACf,OAAO,EACP,SAAS,EACT,OAAmB,EACnB,IAAgB,EAChB,OAAe,EACf,GAAG,KAAK,EACT,EAAE,WAAW,2CA0Eb;AAED,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA"}