@nocobase/portal-template-default 1.0.3 → 1.0.4

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 +1 -1
  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,530 +0,0 @@
1
- import {
2
- ToolCallCard,
3
- type ToolCallPart,
4
- } from "../components/chat/tool-call-card";
5
- import { Badge } from "@/components/ui/badge";
6
- import { Button } from "@/components/ui/button";
7
- import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
8
- import { Input } from "@/components/ui/input";
9
- import {
10
- Select,
11
- SelectContent,
12
- SelectItem,
13
- SelectTrigger,
14
- SelectValue,
15
- } from "@/components/ui/select";
16
- import { Switch } from "@/components/ui/switch";
17
- import { Textarea } from "@/components/ui/textarea";
18
- import { Check, Copy } from "lucide-react";
19
- import { useState } from "react";
20
-
21
- const tools = {
22
- suggestions: {
23
- type: "dynamic-tool",
24
- toolName: "suggestions",
25
- toolCallId: "tool-demo-suggestions",
26
- state: "input-available",
27
- input: {
28
- options: [
29
- "Summarize the operational risks",
30
- "Draft a weekly support report",
31
- "Propose the next automation",
32
- ],
33
- },
34
- },
35
- report: {
36
- type: "dynamic-tool",
37
- toolName: "businessReportGenerator",
38
- toolCallId: "tool-demo-report",
39
- state: "output-available",
40
- input: {
41
- title: "Customer support operations · Weekly review",
42
- summary:
43
- "Ticket volume remained stable while SLA exposure concentrated in two queues.",
44
- markdown: `# Weekly support review
45
-
46
- ## Executive summary
47
-
48
- Ticket volume remained stable, but unresolved priority requests increased in the onboarding and billing queues.
49
-
50
- {{ chart:1 }}
51
-
52
- {{ chart:2 }}
53
-
54
- ## Recommended actions
55
-
56
- - Reassign unowned priority tickets before the next shift.
57
- - Add an SLA warning automation for billing requests.
58
- - Review the onboarding knowledge-base gaps with the support lead.`,
59
- charts: [
60
- {
61
- title: "Open tickets by queue",
62
- options: {
63
- xAxis: {
64
- type: "category",
65
- data: ["Billing", "Onboarding", "API", "General"],
66
- },
67
- yAxis: { type: "value" },
68
- series: [{ type: "bar", data: [31, 26, 18, 14] }],
69
- },
70
- },
71
- {
72
- title: "SLA exposure trend",
73
- options: {
74
- xAxis: {
75
- type: "category",
76
- data: ["Mon", "Tue", "Wed", "Thu", "Fri"],
77
- },
78
- yAxis: { type: "value" },
79
- series: [{ type: "line", smooth: true, data: [8, 11, 9, 15, 12] }],
80
- },
81
- },
82
- ],
83
- },
84
- output: { ready: true },
85
- },
86
- chart: {
87
- type: "dynamic-tool",
88
- toolName: "chartGenerator",
89
- toolCallId: "tool-demo-chart",
90
- state: "output-available",
91
- input: {
92
- options: {
93
- tooltip: { trigger: "axis" },
94
- xAxis: {
95
- type: "category",
96
- data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
97
- },
98
- yAxis: { type: "value" },
99
- series: [
100
- { type: "bar", name: "Tickets", data: [42, 64, 51, 78, 58, 86, 72] },
101
- ],
102
- },
103
- },
104
- output: { ready: true },
105
- },
106
- subAgent: {
107
- type: "dynamic-tool",
108
- toolName: "dispatch-sub-agent-task",
109
- toolCallId: "tool-demo-sub-agent",
110
- state: "input-available",
111
- input: {
112
- username: "dex",
113
- question:
114
- "Review the current ticket categories and identify where automation would remove the most repetitive work.",
115
- },
116
- },
117
- workflow: {
118
- type: "dynamic-tool",
119
- toolName: "aiEmployeeWorkflowTaskOutput",
120
- toolCallId: "tool-demo-workflow",
121
- state: "input-available",
122
- input: {
123
- workflowTitle: "Publish weekly service review",
124
- result: {
125
- audience: "Support leadership",
126
- format: "Markdown report",
127
- records: 128,
128
- delivery: "Team workspace",
129
- },
130
- },
131
- },
132
- approval: {
133
- type: "dynamic-tool",
134
- toolName: "updateRecords",
135
- toolCallId: "tool-demo-approval",
136
- state: "input-available",
137
- input: {
138
- collection: "tickets",
139
- action: "Assign priority tickets to the on-call team",
140
- },
141
- },
142
- preparing: {
143
- type: "dynamic-tool",
144
- toolName: "inspectWorkspace",
145
- toolCallId: "tool-demo-preparing",
146
- state: "input-streaming",
147
- input: { scope: "support operations" },
148
- },
149
- running: {
150
- type: "dynamic-tool",
151
- toolName: "searchRecords",
152
- toolCallId: "tool-demo-running",
153
- state: "input-available",
154
- input: { collection: "tickets", filter: { status: "open" } },
155
- },
156
- completed: {
157
- type: "dynamic-tool",
158
- toolName: "inspectWorkspace",
159
- toolCallId: "tool-demo-completed",
160
- state: "output-available",
161
- input: { scope: "support operations" },
162
- output: { reviewed: true },
163
- },
164
- failed: {
165
- type: "dynamic-tool",
166
- toolName: "publishReport",
167
- toolCallId: "tool-demo-failed",
168
- state: "output-error",
169
- input: { destination: "leadership workspace" },
170
- errorText: "The current role cannot publish to this workspace.",
171
- },
172
- } satisfies Record<string, ToolCallPart>;
173
-
174
- const specializedCards = [
175
- [
176
- "Suggestions",
177
- "Let the user choose how the AI should continue.",
178
- tools.suggestions,
179
- ],
180
- [
181
- "Business report",
182
- "Open a generated report for preview and export.",
183
- tools.report,
184
- ],
185
- [
186
- "Chart",
187
- "Render generated visualization options as an inline preview.",
188
- tools.chart,
189
- ],
190
- [
191
- "Sub-agent",
192
- "Show delegated AI employee work without expanding the whole payload.",
193
- tools.subAgent,
194
- ],
195
- [
196
- "Workflow output",
197
- "Present structured workflow results in a business-friendly card.",
198
- tools.workflow,
199
- ],
200
- ] as const;
201
-
202
- export function ToolCardsPage() {
203
- const [decision, setDecision] = useState("Waiting for a decision");
204
- const [workflowDecision, setWorkflowDecision] = useState(
205
- "Waiting for workflow review"
206
- );
207
-
208
- return (
209
- <div className="space-y-10 pb-12">
210
- <section className="flex flex-wrap items-start justify-between gap-5 border-b pb-8">
211
- <div>
212
- <div className="flex items-center gap-2">
213
- <Badge variant="secondary">AI Components</Badge>
214
- <Badge variant="outline">Tool renderers</Badge>
215
- </div>
216
- <h1 className="mt-4 text-3xl font-semibold tracking-[-0.035em]">
217
- Tool Cards
218
- </h1>
219
- <p className="mt-3 max-w-3xl text-sm leading-6 text-muted-foreground">
220
- Specialized tools render their complete business interaction.
221
- Tools without a registered renderer fall back to the shared status,
222
- approval, error, and input disclosure card.
223
- </p>
224
- </div>
225
- </section>
226
-
227
- <section className="space-y-5">
228
- <SectionTitle
229
- eyebrow="Specialized renderers"
230
- title="Adapt NocoBase tool results to the job they represent"
231
- description="These cards follow the original AI employee patterns while using the starter's shadcn and Base UI component system."
232
- />
233
- <div className="grid gap-4 lg:grid-cols-2">
234
- {specializedCards.map(([title, description, part]) => (
235
- <Card key={title} className="gap-0 py-0">
236
- <CardHeader className="border-b py-4">
237
- <CardTitle className="text-base">{title}</CardTitle>
238
- <p className="text-xs leading-5 text-muted-foreground">
239
- {description}
240
- </p>
241
- </CardHeader>
242
- <CardContent className="p-4">
243
- <ToolCallCard
244
- part={part}
245
- approval={
246
- part.toolName === "aiEmployeeWorkflowTaskOutput"
247
- ? { required: true, status: "pending" }
248
- : undefined
249
- }
250
- onDecision={async (nextDecision) => {
251
- if (part.toolName !== "aiEmployeeWorkflowTaskOutput")
252
- return;
253
- setWorkflowDecision(
254
- nextDecision === "approve"
255
- ? "Workflow output approved"
256
- : nextDecision === "reject"
257
- ? "Workflow output rejected"
258
- : "Revision requested"
259
- );
260
- }}
261
- />
262
- {part.toolName === "aiEmployeeWorkflowTaskOutput" ? (
263
- <p className="mt-2 text-xs text-muted-foreground">
264
- {workflowDecision}
265
- </p>
266
- ) : null}
267
- </CardContent>
268
- </Card>
269
- ))}
270
- </div>
271
- </section>
272
-
273
- <section className="space-y-5">
274
- <SectionTitle
275
- eyebrow="Default Tool Card"
276
- title="One shared shell for every normal tool state"
277
- description="Tools without a specialized renderer still use the same compact status, input disclosure, error, and permission behavior."
278
- />
279
- <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
280
- {[
281
- ["Preparing input", tools.preparing],
282
- ["Running", tools.running],
283
- ["Completed", tools.completed],
284
- ["Failed", tools.failed],
285
- ].map(([label, part]) => (
286
- <Card key={label as string} className="gap-0 py-0">
287
- <CardHeader className="border-b py-3">
288
- <CardTitle className="text-sm">{label as string}</CardTitle>
289
- </CardHeader>
290
- <CardContent className="p-4">
291
- <ToolCallCard part={part as ToolCallPart} />
292
- </CardContent>
293
- </Card>
294
- ))}
295
- <Card className="gap-0 py-0 md:col-span-2 xl:col-span-2">
296
- <CardHeader className="border-b py-3">
297
- <CardTitle className="text-sm">Approval required</CardTitle>
298
- </CardHeader>
299
- <CardContent className="p-4">
300
- <ToolCallCard
301
- part={tools.approval}
302
- approval={{ required: true, status: "pending" }}
303
- onDecision={async (nextDecision) => {
304
- setDecision(
305
- nextDecision === "approve"
306
- ? "The tool was allowed."
307
- : "The tool was denied."
308
- );
309
- }}
310
- />
311
- <p className="mt-2 text-xs text-muted-foreground">{decision}</p>
312
- </CardContent>
313
- </Card>
314
- </div>
315
- </section>
316
-
317
- <section className="space-y-5">
318
- <SectionTitle
319
- eyebrow="Code prompt"
320
- title="Generate an implementation prompt for a specialized Tool Card"
321
- description="Choose the nearest existing renderer and describe the business interaction. The generated prompt tells the coding agent exactly where and how to implement it."
322
- />
323
- <ToolCardCodePrompt />
324
- </section>
325
- </div>
326
- );
327
- }
328
-
329
- type ReferenceRenderer =
330
- | "suggestions"
331
- | "business-report"
332
- | "chart"
333
- | "sub-agent"
334
- | "workflow";
335
-
336
- const referenceRenderers: Record<
337
- ReferenceRenderer,
338
- { label: string; guidance: string }
339
- > = {
340
- suggestions: {
341
- label: "Suggestions",
342
- guidance:
343
- "Follow SuggestionsRenderer for selectable options that call onEdit with the user's choice.",
344
- },
345
- "business-report": {
346
- label: "Business report",
347
- guidance:
348
- "Follow BusinessReportRenderer for an inline summary that opens a larger preview or export dialog.",
349
- },
350
- chart: {
351
- label: "Chart",
352
- guidance:
353
- "Follow ChartRenderer for a compact visual preview derived from structured tool arguments.",
354
- },
355
- "sub-agent": {
356
- label: "Sub-agent",
357
- guidance:
358
- "Follow SubAgentRenderer for a compact entity card with expandable task details.",
359
- },
360
- workflow: {
361
- label: "Workflow output",
362
- guidance:
363
- "Follow WorkflowRenderer for structured results and card-owned Reject, Revise, and Approve actions.",
364
- },
365
- };
366
-
367
- function ToolCardCodePrompt() {
368
- const [toolName, setToolName] = useState("reviewSupportQueue");
369
- const [reference, setReference] = useState<ReferenceRenderer>("workflow");
370
- const [behavior, setBehavior] = useState(
371
- "Show the affected support records, summarize the proposed changes, and let the user approve, request a revision, or reject the operation."
372
- );
373
- const [handlesApproval, setHandlesApproval] = useState(true);
374
- const [copied, setCopied] = useState(false);
375
- const referenceDefinition = referenceRenderers[reference];
376
- const prompt = `Implement a specialized NocoBase AI Tool Card for the tool "${
377
- toolName || "customTool"
378
- }" in the shadcn Registry source at registry/nocobase-ai.
379
-
380
- Business interaction:
381
- ${behavior || "Render the tool's business-specific interaction."}
382
-
383
- Reference implementation:
384
- - ${referenceDefinition.guidance}
385
- - Reuse the shared visual language from the existing specialized Tool Cards demo.
386
-
387
- Implementation requirements:
388
- - Create a focused renderer component under registry/nocobase-ai/components/tools. It renders the complete business-specific card.
389
- - Accept AIToolRendererProps: part, disabled, onEdit, onApprove, onReject, and onRevise.
390
- - Register the renderer for the exact tool name "${
391
- toolName || "customTool"
392
- }" in the built-in renderer map, or provide it through AIToolRendererProvider when it belongs to an external Registry package.
393
- - Set handlesApproval to ${handlesApproval}. ${
394
- handlesApproval
395
- ? "The specialized body must expose the complete decision UI and call onApprove, onReject, onEdit, or onRevise itself."
396
- : "Leave approval, status, errors, and disclosure to the shared ToolCallCard shell."
397
- }
398
- - Set standalone to true when the renderer replaces the Default Tool Card shell.
399
- - Do not add tool-name branches to ChatMessage or AIChatMessageList.
400
- - Do not render a separate raw Output section.
401
- - Preserve the actual message order: reasoning, assistant text, then the Tool Card in the position used by the NocoBase message renderer.
402
- - Use shadcn/Base UI components only; do not add Ant Design, Radix, or Zustand.
403
- - Add a fixed ToolCallPart example to registry/nocobase-ai/demo/tool-cards.tsx so every state and action can be reviewed without calling the backend.
404
- - Export any public renderer types or components from registry/nocobase-ai/components/index.ts when application code needs them.
405
-
406
- Verification:
407
- - Run pnpm registry:preview.
408
- - Run pnpm exec tsc --noEmit.
409
- - Run the scoped ESLint check and pnpm registry:build.
410
- - Verify the card in light and dark themes, narrow chat panels, expanded dialogs, pending approval, completed, and error states.`;
411
-
412
- return (
413
- <div className="grid items-start gap-5 xl:grid-cols-[380px_minmax(0,1fr)]">
414
- <Card className="gap-0 py-0">
415
- <CardHeader className="border-b py-4">
416
- <CardTitle className="text-base">Describe the Tool Card</CardTitle>
417
- <p className="text-xs leading-5 text-muted-foreground">
418
- The examples above remain the visual reference; these values define
419
- the implementation task.
420
- </p>
421
- </CardHeader>
422
- <CardContent className="space-y-5 p-4">
423
- <div className="space-y-2">
424
- <label className="text-xs font-medium" htmlFor="tool-name">
425
- Tool name
426
- </label>
427
- <Input
428
- id="tool-name"
429
- value={toolName}
430
- onChange={(event) => setToolName(event.target.value)}
431
- />
432
- </div>
433
- <div className="space-y-2">
434
- <div className="text-xs font-medium">Closest example</div>
435
- <Select
436
- value={reference}
437
- onValueChange={(value) => {
438
- if (!value) return;
439
- const nextReference = value as ReferenceRenderer;
440
- setReference(nextReference);
441
- setHandlesApproval(nextReference === "workflow");
442
- }}
443
- >
444
- <SelectTrigger className="w-full">
445
- <SelectValue>{referenceDefinition.label}</SelectValue>
446
- </SelectTrigger>
447
- <SelectContent>
448
- {Object.entries(referenceRenderers).map(([value, option]) => (
449
- <SelectItem key={value} value={value}>
450
- {option.label}
451
- </SelectItem>
452
- ))}
453
- </SelectContent>
454
- </Select>
455
- </div>
456
- <div className="space-y-2">
457
- <label className="text-xs font-medium" htmlFor="tool-behavior">
458
- Business interaction
459
- </label>
460
- <Textarea
461
- id="tool-behavior"
462
- className="min-h-28"
463
- value={behavior}
464
- onChange={(event) => setBehavior(event.target.value)}
465
- />
466
- </div>
467
- <label className="flex items-center justify-between gap-4 border-t pt-4 text-sm">
468
- <span>
469
- <span className="block font-medium">Card owns approval UI</span>
470
- <span className="block text-xs text-muted-foreground">
471
- Enable for actions such as Approve, Revise, and Reject.
472
- </span>
473
- </span>
474
- <Switch
475
- size="sm"
476
- checked={handlesApproval}
477
- onCheckedChange={setHandlesApproval}
478
- />
479
- </label>
480
- </CardContent>
481
- </Card>
482
- <Card className="gap-0 py-0">
483
- <div className="flex items-center justify-between border-b px-4 py-3">
484
- <div>
485
- <div className="text-sm font-medium">Coding prompt</div>
486
- <div className="text-xs text-muted-foreground">
487
- Ready to paste into an implementation task.
488
- </div>
489
- </div>
490
- <Button
491
- size="sm"
492
- onClick={async () => {
493
- await navigator.clipboard.writeText(prompt);
494
- setCopied(true);
495
- window.setTimeout(() => setCopied(false), 1500);
496
- }}
497
- >
498
- {copied ? <Check /> : <Copy />}
499
- {copied ? "Copied" : "Copy prompt"}
500
- </Button>
501
- </div>
502
- <pre className="max-h-[720px] min-h-[560px] overflow-auto whitespace-pre-wrap bg-muted/25 p-5 font-mono text-xs leading-5 text-muted-foreground">
503
- {prompt}
504
- </pre>
505
- </Card>
506
- </div>
507
- );
508
- }
509
-
510
- function SectionTitle({
511
- eyebrow,
512
- title,
513
- description,
514
- }: {
515
- eyebrow: string;
516
- title: string;
517
- description: string;
518
- }) {
519
- return (
520
- <div>
521
- <p className="text-xs font-semibold uppercase tracking-[0.16em] text-muted-foreground">
522
- {eyebrow}
523
- </p>
524
- <h2 className="mt-2 text-xl font-semibold tracking-tight">{title}</h2>
525
- <p className="mt-1.5 max-w-3xl text-sm leading-6 text-muted-foreground">
526
- {description}
527
- </p>
528
- </div>
529
- );
530
- }
@@ -1,158 +0,0 @@
1
- import { NocoBaseAIExtensionProvider } from "./global-ai-chat";
2
- import type { AppExtension } from "@/app/extension";
3
- import { LoadingState } from "@/components/app-shell/loading-state";
4
- import {
5
- Bot,
6
- MessageSquare,
7
- MousePointer2,
8
- PanelRight,
9
- Sparkles,
10
- Wrench,
11
- } from "lucide-react";
12
- import { lazy, Suspense, type ReactNode } from "react";
13
- import { Outlet, Route } from "react-router";
14
-
15
- const AIChatPage = lazy(() =>
16
- import("./demo").then((module) => ({ default: module.AIChatPage }))
17
- );
18
- const FloatingChatPage = lazy(() =>
19
- import("./demo/floating").then((module) => ({
20
- default: module.FloatingChatPage,
21
- }))
22
- );
23
- const ShortcutPage = lazy(() =>
24
- import("./demo/shortcut").then((module) => ({
25
- default: module.ShortcutPage,
26
- }))
27
- );
28
- const PageContextPage = lazy(() =>
29
- import("./demo/page-context").then((module) => ({
30
- default: module.PageContextPage,
31
- }))
32
- );
33
- const ToolCardsPage = lazy(() =>
34
- import("./demo/tool-cards").then((module) => ({
35
- default: module.ToolCardsPage,
36
- }))
37
- );
38
-
39
- function LazyDemoRoute({ children }: { children: ReactNode }) {
40
- return (
41
- <Suspense fallback={<LoadingState className="min-h-[320px]" />}>
42
- {children}
43
- </Suspense>
44
- );
45
- }
46
-
47
- const nocobaseAIExtension: AppExtension = {
48
- id: "nocobase-ai",
49
- Provider: NocoBaseAIExtensionProvider,
50
- resources: [
51
- {
52
- name: "ai-components",
53
- meta: {
54
- label: "AI Components",
55
- icon: <Bot />,
56
- acl: { type: "authenticated" },
57
- },
58
- },
59
- {
60
- name: "ai-chat-window",
61
- list: "/ai-chat",
62
- meta: {
63
- parent: "ai-components",
64
- label: "Chat window",
65
- icon: <MessageSquare />,
66
- description:
67
- "Build freely with AI while NocoBase keeps the application reliable.",
68
- acl: { type: "authenticated" },
69
- },
70
- },
71
- {
72
- name: "ai-floating-chat",
73
- list: "/ai-chat/floating",
74
- meta: {
75
- parent: "ai-components",
76
- label: "Floating chat",
77
- icon: <PanelRight />,
78
- acl: { type: "authenticated" },
79
- },
80
- },
81
- {
82
- name: "ai-employee-tasks",
83
- list: "/ai-chat/shortcut",
84
- meta: {
85
- parent: "ai-components",
86
- label: "Employee tasks",
87
- icon: <Sparkles />,
88
- acl: { type: "authenticated" },
89
- },
90
- },
91
- {
92
- name: "ai-page-context",
93
- list: "/ai-chat/context",
94
- meta: {
95
- parent: "ai-components",
96
- label: "Page context",
97
- icon: <MousePointer2 />,
98
- acl: { type: "authenticated" },
99
- },
100
- },
101
- {
102
- name: "ai-tool-cards",
103
- list: "/ai-chat/tools",
104
- meta: {
105
- parent: "ai-components",
106
- label: "Tool cards",
107
- icon: <Wrench />,
108
- acl: { type: "authenticated" },
109
- },
110
- },
111
- ],
112
- routes: (
113
- <Route key="nocobase-ai" path="/ai-chat" element={<Outlet />}>
114
- <Route
115
- index
116
- element={
117
- <LazyDemoRoute>
118
- <AIChatPage />
119
- </LazyDemoRoute>
120
- }
121
- />
122
- <Route
123
- path="floating"
124
- element={
125
- <LazyDemoRoute>
126
- <FloatingChatPage />
127
- </LazyDemoRoute>
128
- }
129
- />
130
- <Route
131
- path="shortcut"
132
- element={
133
- <LazyDemoRoute>
134
- <ShortcutPage />
135
- </LazyDemoRoute>
136
- }
137
- />
138
- <Route
139
- path="context"
140
- element={
141
- <LazyDemoRoute>
142
- <PageContextPage />
143
- </LazyDemoRoute>
144
- }
145
- />
146
- <Route
147
- path="tools"
148
- element={
149
- <LazyDemoRoute>
150
- <ToolCardsPage />
151
- </LazyDemoRoute>
152
- }
153
- />
154
- </Route>
155
- ),
156
- };
157
-
158
- export default nocobaseAIExtension;