@nocobase/portal-template-default 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/README.MD +10 -5
  2. package/package.json +17 -17
  3. package/registry/nocobase-ai/components/chat/chat-message.tsx +8 -6
  4. package/registry/nocobase-ai/components/chat/chat-messages.tsx +8 -3
  5. package/registry/nocobase-ai/components/chat/markdown-message.tsx +26 -0
  6. package/registry/nocobase-ai/components/chat/sub-agent-conversation.tsx +3 -3
  7. package/registry/nocobase-ai/demo/configuration-gate.tsx +83 -35
  8. package/registry/nocobase-i18n/README.md +23 -32
  9. package/registry/nocobase-i18n/components/language-switcher.tsx +1 -1
  10. package/registry/nocobase-i18n/demo/index.tsx +2 -2
  11. package/registry/nocobase-i18n/demo/prompt-generator.tsx +3 -2
  12. package/registry/nocobase-i18n/extension.tsx +3 -4
  13. package/registry/nocobase-i18n/index.ts +4 -3
  14. package/registry/nocobase-i18n/locales/en-US.ts +2 -52
  15. package/registry/nocobase-i18n/locales/index.ts +8 -0
  16. package/registry/nocobase-i18n/locales/zh-CN.ts +2 -52
  17. package/registry/nocobase-i18n/provider.tsx +18 -6
  18. package/registry/nocobase-i18n/server-resources.ts +13 -31
  19. package/registry/nocobase-i18n/tests/i18n-regression.mjs +14 -5
  20. package/registry/nocobase-route-surfaces/README.md +21 -0
  21. package/registry/nocobase-route-surfaces/demo/index.tsx +266 -0
  22. package/{src/extensions/nocobase-acl → registry/nocobase-route-surfaces}/demo/lazy-route.tsx +2 -1
  23. package/registry/nocobase-route-surfaces/demo/prompt-generator.tsx +90 -0
  24. package/registry/nocobase-route-surfaces/demo/scenarios.ts +95 -0
  25. package/registry/nocobase-route-surfaces/extension.tsx +119 -0
  26. package/registry/nocobase-route-surfaces/index.ts +7 -0
  27. package/registry/nocobase-route-surfaces/route-dialog.tsx +145 -0
  28. package/registry/nocobase-route-surfaces/route-drawer.tsx +186 -0
  29. package/registry/nocobase-route-surfaces/route-page.tsx +29 -0
  30. package/registry/nocobase-route-surfaces/route-surface-context.ts +13 -0
  31. package/registry/nocobase-route-surfaces/use-refine-unsaved-changes.tsx +106 -0
  32. package/registry/nocobase-route-surfaces/use-route-surface-close.ts +15 -0
  33. package/registry/nocobase-route-surfaces/use-route-surface-state.ts +65 -0
  34. package/registry.config.json +30 -3
  35. package/scripts/registry.mjs +0 -2
  36. package/src/App.tsx +65 -35
  37. package/src/app/extension.ts +1 -2
  38. package/src/app/extensions.tsx +0 -14
  39. package/src/components/app-shell/breadcrumb.tsx +67 -2
  40. package/src/components/app-shell/sidebar.tsx +2 -2
  41. package/src/components/resources/views/list-view.tsx +3 -2
  42. package/src/components/ui/sheet.tsx +22 -21
  43. package/src/lib/nocobase/client.ts +6 -1
  44. package/src/locales/en-US.ts +81 -0
  45. package/src/locales/zh-CN.ts +77 -0
  46. package/src/pages/users/create.tsx +76 -53
  47. package/src/pages/users/edit.tsx +87 -56
  48. package/src/pages/users/index.ts +3 -0
  49. package/src/pages/users/layout.tsx +15 -0
  50. package/src/pages/users/list.tsx +19 -21
  51. package/src/pages/users/role-badges.tsx +43 -0
  52. package/src/pages/users/role-detail.tsx +272 -0
  53. package/src/pages/users/role-utils.ts +6 -0
  54. package/src/pages/users/routes.ts +24 -0
  55. package/src/pages/users/show.tsx +195 -104
  56. package/src/pages/users/types.ts +13 -0
  57. package/src/providers/i18n/index.ts +3 -0
  58. package/{registry/nocobase-i18n → src/providers/i18n}/runtime.ts +63 -16
  59. package/src/providers/system-settings/context.ts +26 -0
  60. package/src/providers/system-settings/index.ts +2 -0
  61. package/src/providers/system-settings/provider.tsx +94 -0
  62. package/src/extensions/nocobase-acl/README.md +0 -19
  63. package/src/extensions/nocobase-acl/components/acl-boundary.tsx +0 -106
  64. package/src/extensions/nocobase-acl/components/index.ts +0 -3
  65. package/src/extensions/nocobase-acl/components/role-options.ts +0 -36
  66. package/src/extensions/nocobase-acl/components/role-switcher.tsx +0 -135
  67. package/src/extensions/nocobase-acl/demo/boundary-api.tsx +0 -186
  68. package/src/extensions/nocobase-acl/demo/components.tsx +0 -231
  69. package/src/extensions/nocobase-acl/demo/index.tsx +0 -505
  70. package/src/extensions/nocobase-acl/demo/prompt-generator.tsx +0 -134
  71. package/src/extensions/nocobase-acl/demo/role-switcher-prompt-generator.tsx +0 -198
  72. package/src/extensions/nocobase-acl/demo/scenario-section.tsx +0 -37
  73. package/src/extensions/nocobase-acl/extension.tsx +0 -71
  74. package/src/extensions/nocobase-acl/index.ts +0 -1
  75. package/src/extensions/nocobase-ai/README.md +0 -70
  76. package/src/extensions/nocobase-ai/adapters/react-hook-form.ts +0 -20
  77. package/src/extensions/nocobase-ai/assets/nocobase-ai-chat.svg +0 -50
  78. package/src/extensions/nocobase-ai/components/ai-root-provider.tsx +0 -32
  79. package/src/extensions/nocobase-ai/components/chat/ai-employee-avatar.tsx +0 -31
  80. package/src/extensions/nocobase-ai/components/chat/chat-attachment.tsx +0 -92
  81. package/src/extensions/nocobase-ai/components/chat/chat-compact.tsx +0 -90
  82. package/src/extensions/nocobase-ai/components/chat/chat-composer.tsx +0 -429
  83. package/src/extensions/nocobase-ai/components/chat/chat-empty-state.tsx +0 -51
  84. package/src/extensions/nocobase-ai/components/chat/chat-header.tsx +0 -88
  85. package/src/extensions/nocobase-ai/components/chat/chat-history-dialog.tsx +0 -77
  86. package/src/extensions/nocobase-ai/components/chat/chat-message.tsx +0 -252
  87. package/src/extensions/nocobase-ai/components/chat/chat-messages.tsx +0 -173
  88. package/src/extensions/nocobase-ai/components/chat/chat-window.tsx +0 -118
  89. package/src/extensions/nocobase-ai/components/chat/conversation-list.tsx +0 -343
  90. package/src/extensions/nocobase-ai/components/chat/markdown-message.tsx +0 -165
  91. package/src/extensions/nocobase-ai/components/chat/model-select-options.tsx +0 -30
  92. package/src/extensions/nocobase-ai/components/chat/reasoning-panel.tsx +0 -29
  93. package/src/extensions/nocobase-ai/components/chat/sub-agent-conversation.tsx +0 -142
  94. package/src/extensions/nocobase-ai/components/chat/tool-call-card.tsx +0 -316
  95. package/src/extensions/nocobase-ai/components/chat/user-prompt-editor.tsx +0 -99
  96. package/src/extensions/nocobase-ai/components/chat/work-context-chip.tsx +0 -114
  97. package/src/extensions/nocobase-ai/components/index.ts +0 -59
  98. package/src/extensions/nocobase-ai/components/page-elements/ai-form.tsx +0 -44
  99. package/src/extensions/nocobase-ai/components/page-elements/page-element-provider.tsx +0 -469
  100. package/src/extensions/nocobase-ai/components/surfaces/chat-dialog.tsx +0 -31
  101. package/src/extensions/nocobase-ai/components/surfaces/chat-inline.tsx +0 -18
  102. package/src/extensions/nocobase-ai/components/surfaces/chat-page.tsx +0 -18
  103. package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel-layout.tsx +0 -54
  104. package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel.tsx +0 -74
  105. package/src/extensions/nocobase-ai/components/surfaces/chat-surface-actions.tsx +0 -54
  106. package/src/extensions/nocobase-ai/components/tools/builtin-tool-renderers.tsx +0 -31
  107. package/src/extensions/nocobase-ai/components/tools/business-report-dialog.tsx +0 -364
  108. package/src/extensions/nocobase-ai/components/tools/business-report-renderer.tsx +0 -122
  109. package/src/extensions/nocobase-ai/components/tools/business-report-utils.tsx +0 -282
  110. package/src/extensions/nocobase-ai/components/tools/chart-renderer.tsx +0 -99
  111. package/src/extensions/nocobase-ai/components/tools/echarts-preview.tsx +0 -59
  112. package/src/extensions/nocobase-ai/components/tools/echarts-runtime-advanced.ts +0 -18
  113. package/src/extensions/nocobase-ai/components/tools/echarts-runtime-common.ts +0 -20
  114. package/src/extensions/nocobase-ai/components/tools/echarts-runtime-components.ts +0 -34
  115. package/src/extensions/nocobase-ai/components/tools/echarts-runtime-hierarchy.ts +0 -24
  116. package/src/extensions/nocobase-ai/components/tools/echarts-runtime.ts +0 -138
  117. package/src/extensions/nocobase-ai/components/tools/sub-agent-renderer.tsx +0 -67
  118. package/src/extensions/nocobase-ai/components/tools/suggestions-renderer.tsx +0 -59
  119. package/src/extensions/nocobase-ai/components/tools/tool-renderer-provider.tsx +0 -55
  120. package/src/extensions/nocobase-ai/components/tools/tool-renderer-utils.ts +0 -18
  121. package/src/extensions/nocobase-ai/components/tools/workflow-renderer.tsx +0 -108
  122. package/src/extensions/nocobase-ai/components/triggers/ai-chat-floating-trigger.tsx +0 -73
  123. package/src/extensions/nocobase-ai/components/triggers/ai-employee-shortcut.tsx +0 -149
  124. package/src/extensions/nocobase-ai/demo/configuration-gate.tsx +0 -49
  125. package/src/extensions/nocobase-ai/demo/container-showcase.tsx +0 -210
  126. package/src/extensions/nocobase-ai/demo/floating.tsx +0 -134
  127. package/src/extensions/nocobase-ai/demo/index.tsx +0 -346
  128. package/src/extensions/nocobase-ai/demo/interaction-showcase.tsx +0 -151
  129. package/src/extensions/nocobase-ai/demo/page-context-prompt-generator.tsx +0 -355
  130. package/src/extensions/nocobase-ai/demo/page-context.tsx +0 -884
  131. package/src/extensions/nocobase-ai/demo/page-element-showcase.tsx +0 -197
  132. package/src/extensions/nocobase-ai/demo/prompt-card.tsx +0 -41
  133. package/src/extensions/nocobase-ai/demo/prompt-generator.tsx +0 -452
  134. package/src/extensions/nocobase-ai/demo/shortcut.tsx +0 -1207
  135. package/src/extensions/nocobase-ai/demo/tool-cards.tsx +0 -530
  136. package/src/extensions/nocobase-ai/extension.tsx +0 -158
  137. package/src/extensions/nocobase-ai/global-ai-chat.tsx +0 -213
  138. package/src/extensions/nocobase-ai/index.ts +0 -3
  139. package/src/extensions/nocobase-ai/providers/ai-provider.tsx +0 -323
  140. package/src/extensions/nocobase-ai/providers/avatars.ts +0 -817
  141. package/src/extensions/nocobase-ai/providers/chat-context.tsx +0 -109
  142. package/src/extensions/nocobase-ai/providers/chat-controller.ts +0 -84
  143. package/src/extensions/nocobase-ai/providers/chat-message-utils.ts +0 -198
  144. package/src/extensions/nocobase-ai/providers/chat-provider.tsx +0 -871
  145. package/src/extensions/nocobase-ai/providers/chat-reducer.ts +0 -147
  146. package/src/extensions/nocobase-ai/providers/chat-task-utils.ts +0 -105
  147. package/src/extensions/nocobase-ai/providers/chat-transport.ts +0 -184
  148. package/src/extensions/nocobase-ai/providers/form-registry.tsx +0 -271
  149. package/src/extensions/nocobase-ai/providers/frontend-tool-registry.tsx +0 -231
  150. package/src/extensions/nocobase-ai/providers/index.ts +0 -61
  151. package/src/extensions/nocobase-ai/providers/model.ts +0 -32
  152. package/src/extensions/nocobase-ai/providers/page-context.tsx +0 -136
  153. package/src/extensions/nocobase-ai/providers/stream-coalescer.ts +0 -60
  154. package/src/extensions/nocobase-ai/providers/stream-event-utils.ts +0 -82
  155. package/src/extensions/nocobase-ai/providers/stream-parser.ts +0 -61
  156. package/src/extensions/nocobase-ai/providers/sub-agent-stream.ts +0 -316
  157. package/src/extensions/nocobase-ai/providers/types.ts +0 -183
  158. package/src/extensions/nocobase-ai/providers/ui-message-stream.ts +0 -463
  159. package/src/extensions/nocobase-ai/providers/use-automatic-tool-approval.ts +0 -94
  160. package/src/extensions/nocobase-ai/providers/use-chat-attachments.ts +0 -206
  161. package/src/extensions/nocobase-ai/providers/use-chat-message-actions.ts +0 -554
  162. package/src/extensions/nocobase-ai/providers/use-chat-runtime.ts +0 -210
  163. package/src/extensions/nocobase-ai/providers/use-chat-work-context.ts +0 -93
  164. package/src/extensions/nocobase-ai/providers/use-conversation-catalog.ts +0 -108
  165. package/src/extensions/nocobase-ai/providers/use-conversation-history.ts +0 -178
  166. package/src/extensions/nocobase-ai/services/index.ts +0 -7
  167. package/src/extensions/nocobase-ai/services/nocobase-ai-service.ts +0 -454
  168. package/src/extensions/nocobase-ai/services/types.ts +0 -82
  169. /package/{registry/nocobase-i18n → src/providers/i18n}/i18n-provider.ts +0 -0
  170. /package/{registry/nocobase-i18n → src/providers/i18n}/locale-store.ts +0 -0
@@ -1,343 +0,0 @@
1
- import { Button } from "@/components/ui/button";
2
- import { LoadingState } from "@/components/app-shell/loading-state";
3
- import {
4
- Dialog,
5
- DialogContent,
6
- DialogDescription,
7
- DialogFooter,
8
- DialogHeader,
9
- DialogTitle,
10
- } from "@/components/ui/dialog";
11
- import {
12
- DropdownMenu,
13
- DropdownMenuContent,
14
- DropdownMenuItem,
15
- DropdownMenuTrigger,
16
- } from "@/components/ui/dropdown-menu";
17
- import { cn } from "@/lib/utils";
18
- import { Input } from "@/components/ui/input";
19
- import { Label } from "@/components/ui/label";
20
- import { useAIChatBase } from "../../providers";
21
- import {
22
- MoreHorizontal,
23
- PanelLeftClose,
24
- Pencil,
25
- Plus,
26
- Search,
27
- Trash2,
28
- X,
29
- LoaderCircle,
30
- } from "lucide-react";
31
- import { useEffect, useState, type FormEvent } from "react";
32
-
33
- export function ConversationList({
34
- onClose,
35
- showCloseButton = true,
36
- }: {
37
- onClose?: () => void;
38
- showCloseButton?: boolean;
39
- } = {}) {
40
- const {
41
- conversations,
42
- activeConversationId,
43
- selectConversation,
44
- renameConversation,
45
- removeConversation,
46
- startNewConversation,
47
- setConversationListOpen,
48
- conversationsLoading,
49
- conversationSearch,
50
- searchConversations,
51
- historyError,
52
- } = useAIChatBase();
53
- const [searchValue, setSearchValue] = useState(conversationSearch);
54
- const [renameTarget, setRenameTarget] = useState<{
55
- id: string;
56
- title: string;
57
- }>();
58
- const [renameTitle, setRenameTitle] = useState("");
59
- const [renaming, setRenaming] = useState(false);
60
- const [renameError, setRenameError] = useState<string>();
61
- const [deleteTarget, setDeleteTarget] = useState<{
62
- id: string;
63
- title: string;
64
- }>();
65
- const [deleting, setDeleting] = useState(false);
66
- const [deleteError, setDeleteError] = useState<string>();
67
-
68
- useEffect(() => {
69
- setRenameTitle(renameTarget?.title ?? "");
70
- setRenameError(undefined);
71
- }, [renameTarget]);
72
-
73
- useEffect(() => {
74
- setSearchValue(conversationSearch);
75
- }, [conversationSearch]);
76
-
77
- const submitRename = async (event: FormEvent) => {
78
- event.preventDefault();
79
- if (!renameTarget || !renameTitle.trim()) return;
80
- setRenaming(true);
81
- setRenameError(undefined);
82
- try {
83
- await renameConversation(renameTarget.id, renameTitle);
84
- setRenameTarget(undefined);
85
- } catch (error) {
86
- setRenameError(
87
- error instanceof Error ? error.message : "Unable to rename conversation"
88
- );
89
- } finally {
90
- setRenaming(false);
91
- }
92
- };
93
-
94
- const confirmDelete = async () => {
95
- if (!deleteTarget) return;
96
- setDeleting(true);
97
- setDeleteError(undefined);
98
- try {
99
- await removeConversation(deleteTarget.id);
100
- setDeleteTarget(undefined);
101
- } catch (error) {
102
- setDeleteError(
103
- error instanceof Error ? error.message : "Unable to delete conversation"
104
- );
105
- } finally {
106
- setDeleting(false);
107
- }
108
- };
109
-
110
- return (
111
- <div className="flex h-full min-w-0 flex-1 flex-col overflow-hidden overscroll-contain bg-card">
112
- <div className="flex h-12 shrink-0 items-center justify-between border-b px-3">
113
- <div className="flex min-w-0 items-center gap-1.5">
114
- {showCloseButton ? (
115
- <Button
116
- variant="ghost"
117
- size="icon-sm"
118
- aria-label="Close conversation list"
119
- onClick={() =>
120
- onClose ? onClose() : setConversationListOpen(false)
121
- }
122
- >
123
- <PanelLeftClose />
124
- </Button>
125
- ) : null}
126
- <span className="truncate text-sm font-semibold">Conversations</span>
127
- </div>
128
- <Button
129
- variant="ghost"
130
- size="icon-sm"
131
- aria-label="New conversation"
132
- onClick={startNewConversation}
133
- >
134
- <Plus />
135
- </Button>
136
- </div>
137
- <form
138
- className="shrink-0 border-b p-2.5"
139
- onSubmit={(event) => {
140
- event.preventDefault();
141
- void searchConversations(searchValue).catch(() => undefined);
142
- }}
143
- >
144
- <div className="relative">
145
- <Search className="pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
146
- <Input
147
- value={searchValue}
148
- className="h-8 pl-8 pr-8 text-sm"
149
- placeholder="Search conversations"
150
- aria-label="Search conversations"
151
- onChange={(event) => setSearchValue(event.target.value)}
152
- />
153
- {conversationsLoading && conversationSearch ? (
154
- <LoaderCircle className="absolute right-2.5 top-1/2 size-4 -translate-y-1/2 animate-spin text-muted-foreground" />
155
- ) : searchValue ? (
156
- <Button
157
- type="button"
158
- variant="ghost"
159
- size="icon-xs"
160
- className="absolute right-1.5 top-1/2 -translate-y-1/2"
161
- aria-label="Clear conversation search"
162
- onClick={() => {
163
- setSearchValue("");
164
- void searchConversations("").catch(() => undefined);
165
- }}
166
- >
167
- <X />
168
- </Button>
169
- ) : null}
170
- </div>
171
- </form>
172
- <div className="min-h-0 flex-1 overflow-y-auto overscroll-contain p-2">
173
- {conversationsLoading ? (
174
- <LoadingState className="py-8" />
175
- ) : conversations.length ? (
176
- <div className="space-y-1">
177
- {conversations.map((conversation) => {
178
- const active = conversation.id === activeConversationId;
179
- return (
180
- <div
181
- key={conversation.id}
182
- className={cn(
183
- "group/conversation flex items-start rounded-lg pr-1 transition-colors",
184
- active ? "bg-accent" : "hover:bg-muted/70"
185
- )}
186
- >
187
- <button
188
- type="button"
189
- className="flex min-w-0 flex-1 items-center gap-2 px-2.5 py-2.5 text-left"
190
- onClick={() => selectConversation(conversation.id)}
191
- >
192
- {conversation.unread && !active ? (
193
- <span
194
- className="size-2 shrink-0 rounded-full bg-destructive"
195
- aria-label="Unread conversation"
196
- />
197
- ) : null}
198
- <span className="block min-w-0 flex-1 truncate text-sm font-medium">
199
- {conversation.title}
200
- </span>
201
- </button>
202
- <DropdownMenu>
203
- <DropdownMenuTrigger
204
- render={
205
- <Button
206
- variant="ghost"
207
- size="icon-xs"
208
- className="mt-1.5 opacity-0 group-hover/conversation:opacity-100 data-popup-open:opacity-100"
209
- aria-label="Conversation actions"
210
- />
211
- }
212
- >
213
- <MoreHorizontal />
214
- </DropdownMenuTrigger>
215
- <DropdownMenuContent align="end" className="w-36">
216
- <DropdownMenuItem
217
- onClick={() =>
218
- setRenameTarget({
219
- id: conversation.id,
220
- title: conversation.title,
221
- })
222
- }
223
- >
224
- <Pencil />
225
- Rename
226
- </DropdownMenuItem>
227
- <DropdownMenuItem
228
- variant="destructive"
229
- onClick={() => {
230
- setDeleteError(undefined);
231
- setDeleteTarget({
232
- id: conversation.id,
233
- title: conversation.title,
234
- });
235
- }}
236
- >
237
- <Trash2 />
238
- Delete
239
- </DropdownMenuItem>
240
- </DropdownMenuContent>
241
- </DropdownMenu>
242
- </div>
243
- );
244
- })}
245
- </div>
246
- ) : (
247
- <div className="px-3 py-8 text-center text-sm text-muted-foreground">
248
- {conversationSearch
249
- ? "No matching conversations."
250
- : "No conversations yet."}
251
- </div>
252
- )}
253
- {historyError ? (
254
- <div className="mx-2 mt-2 rounded-lg border border-destructive/25 bg-destructive/5 px-3 py-2 text-xs text-destructive">
255
- {historyError.message}
256
- </div>
257
- ) : null}
258
- </div>
259
- <Dialog
260
- open={!!renameTarget}
261
- onOpenChange={(open) => {
262
- if (!open && !renaming) setRenameTarget(undefined);
263
- }}
264
- >
265
- <DialogContent>
266
- <form onSubmit={submitRename}>
267
- <DialogHeader>
268
- <DialogTitle>Rename conversation</DialogTitle>
269
- <DialogDescription>
270
- Choose a title that makes this conversation easy to find.
271
- </DialogDescription>
272
- </DialogHeader>
273
- <div className="mt-5 space-y-2">
274
- <Label htmlFor="conversation-title">Title</Label>
275
- <Input
276
- id="conversation-title"
277
- value={renameTitle}
278
- onChange={(event) => setRenameTitle(event.target.value)}
279
- autoFocus
280
- maxLength={120}
281
- />
282
- {renameError ? (
283
- <p className="text-xs text-destructive">{renameError}</p>
284
- ) : null}
285
- </div>
286
- <DialogFooter className="mt-5">
287
- <Button
288
- type="button"
289
- variant="outline"
290
- disabled={renaming}
291
- onClick={() => setRenameTarget(undefined)}
292
- >
293
- Cancel
294
- </Button>
295
- <Button type="submit" disabled={renaming || !renameTitle.trim()}>
296
- {renaming ? <LoaderCircle className="animate-spin" /> : null}
297
- Save
298
- </Button>
299
- </DialogFooter>
300
- </form>
301
- </DialogContent>
302
- </Dialog>
303
- <Dialog
304
- open={!!deleteTarget}
305
- onOpenChange={(open) => {
306
- if (!open && !deleting) setDeleteTarget(undefined);
307
- }}
308
- >
309
- <DialogContent>
310
- <DialogHeader>
311
- <DialogTitle>Delete conversation?</DialogTitle>
312
- <DialogDescription>
313
- “{deleteTarget?.title}” and its messages will be permanently
314
- deleted.
315
- </DialogDescription>
316
- </DialogHeader>
317
- {deleteError ? (
318
- <p className="mt-3 text-sm text-destructive">{deleteError}</p>
319
- ) : null}
320
- <DialogFooter className="mt-5">
321
- <Button
322
- type="button"
323
- variant="outline"
324
- disabled={deleting}
325
- onClick={() => setDeleteTarget(undefined)}
326
- >
327
- Cancel
328
- </Button>
329
- <Button
330
- type="button"
331
- variant="destructive"
332
- disabled={deleting}
333
- onClick={() => void confirmDelete()}
334
- >
335
- {deleting ? <LoaderCircle className="animate-spin" /> : null}
336
- Delete
337
- </Button>
338
- </DialogFooter>
339
- </DialogContent>
340
- </Dialog>
341
- </div>
342
- );
343
- }
@@ -1,165 +0,0 @@
1
- import ReactMarkdown from "react-markdown";
2
- import type { Components } from "react-markdown";
3
- import remarkGfm from "remark-gfm";
4
-
5
- import { cn } from "@/lib/utils";
6
-
7
- type MarkdownMessageProps = {
8
- children: string;
9
- variant?: "chat" | "document";
10
- };
11
-
12
- function createMarkdownComponents(variant: MarkdownMessageProps["variant"]): Components {
13
- const document = variant === "document";
14
-
15
- return {
16
- h1: ({ className, ...props }) => (
17
- <h1
18
- {...props}
19
- className={cn(
20
- "mb-4 mt-7 scroll-m-20 font-bold tracking-tight first:mt-0",
21
- document ? "text-3xl" : "text-xl",
22
- className
23
- )}
24
- />
25
- ),
26
- h2: ({ className, ...props }) => (
27
- <h2
28
- {...props}
29
- className={cn(
30
- "mb-3 mt-7 scroll-m-20 border-b pb-2 font-semibold tracking-tight first:mt-0",
31
- document ? "text-2xl" : "text-lg",
32
- className
33
- )}
34
- />
35
- ),
36
- h3: ({ className, ...props }) => (
37
- <h3
38
- {...props}
39
- className={cn(
40
- "mb-2 mt-6 scroll-m-20 font-semibold tracking-tight first:mt-0",
41
- document ? "text-xl" : "text-base",
42
- className
43
- )}
44
- />
45
- ),
46
- h4: ({ className, ...props }) => (
47
- <h4
48
- {...props}
49
- className={cn(
50
- "mb-2 mt-5 scroll-m-20 font-semibold first:mt-0",
51
- document ? "text-lg" : "text-sm",
52
- className
53
- )}
54
- />
55
- ),
56
- p: ({ className, ...props }) => (
57
- <p {...props} className={cn("my-3 leading-7", className)} />
58
- ),
59
- ul: ({ className, ...props }) => (
60
- <ul
61
- {...props}
62
- className={cn("my-3 ml-6 list-disc space-y-1", className)}
63
- />
64
- ),
65
- ol: ({ className, ...props }) => (
66
- <ol
67
- {...props}
68
- className={cn("my-3 ml-6 list-decimal space-y-1", className)}
69
- />
70
- ),
71
- li: ({ className, ...props }) => (
72
- <li {...props} className={cn("pl-1", className)} />
73
- ),
74
- blockquote: ({ className, ...props }) => (
75
- <blockquote
76
- {...props}
77
- className={cn(
78
- "my-4 border-l-4 border-border pl-4 italic text-muted-foreground",
79
- className
80
- )}
81
- />
82
- ),
83
- hr: ({ className, ...props }) => (
84
- <hr {...props} className={cn("my-6 border-border", className)} />
85
- ),
86
- table: ({ className, ...props }) => (
87
- <div className="my-4 w-full overflow-x-auto rounded-md border">
88
- <table
89
- {...props}
90
- className={cn("w-full border-collapse text-sm", className)}
91
- />
92
- </div>
93
- ),
94
- th: ({ className, ...props }) => (
95
- <th
96
- {...props}
97
- className={cn(
98
- "border-b border-r bg-muted/60 px-3 py-2 text-left font-semibold last:border-r-0",
99
- className
100
- )}
101
- />
102
- ),
103
- td: ({ className, ...props }) => (
104
- <td
105
- {...props}
106
- className={cn(
107
- "border-b border-r px-3 py-2 align-top last:border-r-0",
108
- className
109
- )}
110
- />
111
- ),
112
- tr: ({ className, ...props }) => (
113
- <tr
114
- {...props}
115
- className={cn("last:[&>td]:border-b-0", className)}
116
- />
117
- ),
118
- strong: ({ className, ...props }) => (
119
- <strong {...props} className={cn("font-semibold", className)} />
120
- ),
121
- a: ({ className, ...props }) => (
122
- <a
123
- {...props}
124
- className={cn("underline underline-offset-4", className)}
125
- target="_blank"
126
- rel="noreferrer"
127
- />
128
- ),
129
- pre: ({ className, ...props }) => (
130
- <pre
131
- {...props}
132
- className={cn(
133
- "my-4 overflow-x-auto rounded-lg bg-muted p-4 text-sm",
134
- className
135
- )}
136
- />
137
- ),
138
- code: ({ className, ...props }) => (
139
- <code
140
- {...props}
141
- className={cn(
142
- "rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em]",
143
- className
144
- )}
145
- />
146
- ),
147
- };
148
- }
149
-
150
- const chatComponents = createMarkdownComponents("chat");
151
- const documentComponents = createMarkdownComponents("document");
152
-
153
- export function MarkdownMessage({
154
- children,
155
- variant = "chat",
156
- }: MarkdownMessageProps) {
157
- return (
158
- <ReactMarkdown
159
- remarkPlugins={[remarkGfm]}
160
- components={variant === "document" ? documentComponents : chatComponents}
161
- >
162
- {children}
163
- </ReactMarkdown>
164
- );
165
- }
@@ -1,30 +0,0 @@
1
- import {
2
- SelectGroup,
3
- SelectItem,
4
- SelectLabel,
5
- SelectSeparator,
6
- } from "@/components/ui/select";
7
- import { getAIModelKey, groupAIModels, type AIModel } from "../../providers";
8
- import { Fragment } from "react";
9
-
10
- export function AIModelSelectOptions({ models }: { models: AIModel[] }) {
11
- const groups = groupAIModels(models);
12
-
13
- return groups.map((group, index) => (
14
- <Fragment key={group.key}>
15
- {index > 0 ? <SelectSeparator /> : null}
16
- <SelectGroup className="py-1">
17
- <SelectLabel className="px-2 py-1.5 font-medium">
18
- {group.label}
19
- </SelectLabel>
20
- {group.models.map((model) => (
21
- <SelectItem key={getAIModelKey(model)} value={getAIModelKey(model)}>
22
- <span className="block max-w-64 truncate" title={model.label}>
23
- {model.label}
24
- </span>
25
- </SelectItem>
26
- ))}
27
- </SelectGroup>
28
- </Fragment>
29
- ));
30
- }
@@ -1,29 +0,0 @@
1
- import {
2
- Collapsible,
3
- CollapsibleContent,
4
- CollapsibleTrigger,
5
- } from "@/components/ui/collapsible";
6
- import { Brain, ChevronDown } from "lucide-react";
7
-
8
- export function ReasoningPanel({
9
- children,
10
- streaming,
11
- }: {
12
- children: string;
13
- streaming?: boolean;
14
- }) {
15
- if (!children) return null;
16
-
17
- return (
18
- <Collapsible className="overflow-hidden rounded-lg border border-border/70 bg-muted/30">
19
- <CollapsibleTrigger className="group/reasoning flex w-full items-center gap-2 px-3 py-2 text-xs font-medium text-muted-foreground outline-none hover:bg-muted/40 hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset">
20
- <Brain className="size-3.5" />
21
- <span>{streaming ? "Thinking…" : "Reasoning"}</span>
22
- <ChevronDown className="ml-auto size-3.5 transition-transform group-data-panel-open/reasoning:rotate-180" />
23
- </CollapsibleTrigger>
24
- <CollapsibleContent className="border-t px-3 py-2.5 text-xs leading-5 text-muted-foreground">
25
- {children}
26
- </CollapsibleContent>
27
- </Collapsible>
28
- );
29
- }