@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,210 +0,0 @@
1
- import {
2
- AIChatWindow,
3
- ChatInline,
4
- ChatPage,
5
- type AIChatWindowProps,
6
- } from "../components";
7
- import { Button } from "@/components/ui/button";
8
- import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
9
- import {
10
- AppWindow,
11
- Columns3,
12
- Maximize2,
13
- PanelRight,
14
- Smartphone,
15
- } from "lucide-react";
16
-
17
- export type ChatContainer =
18
- | "embedded"
19
- | "page"
20
- | "side-panel"
21
- | "dialog"
22
- | "mobile";
23
-
24
- const containerOptions: Array<{
25
- value: ChatContainer;
26
- title: string;
27
- description: string;
28
- action: string;
29
- icon: typeof AppWindow;
30
- }> = [
31
- {
32
- value: "embedded",
33
- title: "Embedded block",
34
- description:
35
- "Place chat inside a dashboard, record page, or workspace region.",
36
- action: "Preview block",
37
- icon: Columns3,
38
- },
39
- {
40
- value: "page",
41
- title: "Dedicated page",
42
- description:
43
- "Give the conversation a full route and the largest working area.",
44
- action: "Preview page",
45
- icon: AppWindow,
46
- },
47
- {
48
- value: "side-panel",
49
- title: "Push side panel",
50
- description: "Keep the page operable while the content narrows for chat.",
51
- action: "Open panel",
52
- icon: PanelRight,
53
- },
54
- {
55
- value: "dialog",
56
- title: "Dialog",
57
- description:
58
- "Open a focused conversation from an action without changing route.",
59
- action: "Open dialog",
60
- icon: Maximize2,
61
- },
62
- {
63
- value: "mobile",
64
- title: "Mobile container",
65
- description: "Use the same component in a narrow, touch-friendly viewport.",
66
- action: "Preview mobile",
67
- icon: Smartphone,
68
- },
69
- ];
70
-
71
- export function ContainerShowcase({
72
- value,
73
- onValueChange,
74
- windowProps,
75
- }: {
76
- value: ChatContainer;
77
- onValueChange: (value: ChatContainer) => void;
78
- windowProps: AIChatWindowProps;
79
- }) {
80
- return (
81
- <div className="space-y-5">
82
- <div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-5">
83
- {containerOptions.map((option) => {
84
- const Icon = option.icon;
85
- const active = option.value === value;
86
- return (
87
- <Card
88
- key={option.value}
89
- data-active={active}
90
- className="gap-0 py-0 transition-colors data-active:border-foreground/35 data-active:bg-muted/20"
91
- >
92
- <CardHeader className="p-4">
93
- <div className="mb-3 flex size-9 items-center justify-center rounded-lg border bg-background">
94
- <Icon className="size-4 text-muted-foreground" />
95
- </div>
96
- <CardTitle className="text-sm">{option.title}</CardTitle>
97
- <p className="min-h-15 text-xs leading-5 text-muted-foreground">
98
- {option.description}
99
- </p>
100
- </CardHeader>
101
- <CardContent className="border-t p-3">
102
- <Button
103
- variant={active ? "secondary" : "ghost"}
104
- size="sm"
105
- className="w-full"
106
- onClick={() => onValueChange(option.value)}
107
- >
108
- {option.action}
109
- </Button>
110
- </CardContent>
111
- </Card>
112
- );
113
- })}
114
- </div>
115
-
116
- <Card className="gap-0 py-0">
117
- <div className="flex min-h-12 items-center justify-between border-b px-4">
118
- <div>
119
- <div className="text-sm font-medium">Container preview</div>
120
- <div className="text-xs text-muted-foreground">
121
- {containerOptions.find((option) => option.value === value)?.title}
122
- </div>
123
- </div>
124
- <code className="rounded-md bg-muted px-2 py-1 text-xs text-muted-foreground">
125
- {containerComponent(value)}
126
- </code>
127
- </div>
128
- <div className="flex min-h-[650px] justify-center bg-muted/15 p-3 sm:p-5">
129
- {value === "embedded" ? (
130
- <ChatInline className="h-[610px] w-full max-w-3xl shadow-sm">
131
- <AIChatWindow {...windowProps} />
132
- </ChatInline>
133
- ) : null}
134
- {value === "page" ? (
135
- <ChatPage className="h-[610px] min-h-0 w-full">
136
- <AIChatWindow {...windowProps} />
137
- </ChatPage>
138
- ) : null}
139
- {value === "mobile" ? (
140
- <div className="w-full max-w-[390px] rounded-[2rem] border-4 border-foreground/10 bg-background p-1 shadow-xl">
141
- <ChatInline className="h-[610px] rounded-[1.55rem] border-0">
142
- <AIChatWindow {...windowProps} />
143
- </ChatInline>
144
- </div>
145
- ) : null}
146
- {value === "side-panel" || value === "dialog" ? (
147
- <ContainerLaunchPlaceholder
148
- mode={value}
149
- onOpen={() => onValueChange(value)}
150
- />
151
- ) : null}
152
- </div>
153
- </Card>
154
- </div>
155
- );
156
- }
157
-
158
- function ContainerLaunchPlaceholder({
159
- mode,
160
- onOpen,
161
- }: {
162
- mode: "side-panel" | "dialog";
163
- onOpen: () => void;
164
- }) {
165
- return (
166
- <div className="grid w-full max-w-4xl grid-cols-[200px_minmax(0,1fr)] overflow-hidden rounded-xl border bg-background shadow-sm">
167
- <div className="border-r bg-muted/30 p-4">
168
- <div className="h-5 w-24 rounded bg-muted" />
169
- <div className="mt-6 space-y-3">
170
- <div className="h-8 rounded bg-muted" />
171
- <div className="h-8 rounded bg-muted/70" />
172
- <div className="h-8 rounded bg-muted/70" />
173
- </div>
174
- </div>
175
- <div className="flex items-center justify-center p-8">
176
- <div className="max-w-sm text-center">
177
- <div className="mx-auto mb-4 h-24 rounded-xl border bg-muted/20" />
178
- <h3 className="text-sm font-medium">
179
- {mode === "side-panel"
180
- ? "Chat opens beside the current page"
181
- : "Chat opens above the current page"}
182
- </h3>
183
- <p className="mt-2 text-xs leading-5 text-muted-foreground">
184
- {mode === "side-panel"
185
- ? "The live preview pushes this page narrower instead of blocking interaction."
186
- : "The live preview uses a focused dialog while preserving the page underneath."}
187
- </p>
188
- <Button className="mt-4" size="sm" onClick={onOpen}>
189
- {mode === "side-panel" ? "Open side panel" : "Open dialog"}
190
- </Button>
191
- </div>
192
- </div>
193
- </div>
194
- );
195
- }
196
-
197
- const containerComponent = (value: ChatContainer) => {
198
- switch (value) {
199
- case "page":
200
- return "<ChatPage />";
201
- case "side-panel":
202
- return "<ChatSidePanelLayout />";
203
- case "dialog":
204
- return "<ChatDialog />";
205
- case "mobile":
206
- return "<ChatInline /> · 390px";
207
- default:
208
- return "<ChatInline />";
209
- }
210
- };
@@ -1,134 +0,0 @@
1
- import {
2
- AIChatFloatingTrigger,
3
- AIChatWindow,
4
- ChatSidePanelLayout,
5
- } from "../components";
6
- import { Badge } from "@/components/ui/badge";
7
- import { Card } from "@/components/ui/card";
8
- import {
9
- AIChatProvider,
10
- useAIChatController,
11
- useAIChatControllerState,
12
- } from "../providers";
13
- import { PromptCard } from "./prompt-card";
14
- import { AIConfigurationGate } from "./configuration-gate";
15
-
16
- const floatingPrompt = `Add the standard NocoBase AI floating chat entry to the application layout.
17
-
18
- Requirements:
19
- - Render AIChatFloatingTrigger at the lower-right edge of the application.
20
- - Bind it to the global AI chat controller.
21
- - Clicking the trigger opens a 450px ChatSidePanelLayout on the right.
22
- - The side panel must push the desktop page narrower instead of blocking page interaction.
23
- - Mobile may switch to an overlay presentation.
24
- - Hide the floating trigger while the side panel is open.
25
- - Support an unread-count badge.
26
- - Start a new conversation with the configured default AI employee when opened.
27
- - Keep the trigger and side-panel surface separate from AIChatWindow.
28
- - Use shadcn/Base UI components and the existing AIProvider/AIChatProvider runtime.`;
29
-
30
- export function FloatingChatPage() {
31
- return (
32
- <AIConfigurationGate>
33
- <FloatingChatPageContent />
34
- </AIConfigurationGate>
35
- );
36
- }
37
-
38
- function FloatingChatPageContent() {
39
- const controller = useAIChatController();
40
- const { open } = useAIChatControllerState(controller);
41
-
42
- return (
43
- <AIChatProvider id="floating-chat-demo" controller={controller}>
44
- <ChatSidePanelLayout
45
- open={open}
46
- onOpenChange={controller.setOpen}
47
- width={450}
48
- panel={<AIChatWindow />}
49
- >
50
- <div className="space-y-12 pb-12">
51
- <section className="flex flex-wrap items-start justify-between gap-5 border-b pb-8">
52
- <div>
53
- <div className="flex items-center gap-2">
54
- <Badge variant="secondary">AI Components</Badge>
55
- <Badge variant="outline">Global entry</Badge>
56
- </div>
57
- <h1 className="mt-4 text-3xl font-semibold tracking-[-0.035em]">
58
- Floating AI Chat
59
- </h1>
60
- <p className="mt-3 max-w-3xl text-sm leading-6 text-muted-foreground">
61
- A global lower-right entry for the shared AI conversation. It is
62
- intentionally coupled to the push side-panel surface rather than
63
- treated as a generic chat container.
64
- </p>
65
- </div>
66
- </section>
67
-
68
- <section className="space-y-5">
69
- <div>
70
- <p className="text-xs font-semibold uppercase tracking-[0.16em] text-muted-foreground">
71
- Interactive preview
72
- </p>
73
- <h2 className="mt-2 text-xl font-semibold tracking-tight">
74
- Global launcher and push side panel
75
- </h2>
76
- <p className="mt-1.5 max-w-3xl text-sm leading-6 text-muted-foreground">
77
- The trigger is constrained to this simulated application in the
78
- demo. In production its default positioning is fixed to the
79
- viewport.
80
- </p>
81
- </div>
82
- <Card className="gap-0 overflow-hidden py-0">
83
- <div className="flex h-12 items-center justify-between border-b px-4">
84
- <span className="text-sm font-medium">Application shell</span>
85
- <code className="rounded-md bg-muted px-2 py-1 text-xs text-muted-foreground">
86
- AIChatFloatingTrigger → ChatSidePanelLayout
87
- </code>
88
- </div>
89
- <div className="relative grid min-h-[560px] grid-cols-[190px_minmax(0,1fr)] overflow-hidden bg-background">
90
- <div className="border-r bg-muted/25 p-4">
91
- <div className="h-7 w-28 rounded bg-muted" />
92
- <div className="mt-8 space-y-3">
93
- <div className="h-9 rounded-lg bg-muted" />
94
- <div className="h-9 rounded-lg bg-muted/65" />
95
- <div className="h-9 rounded-lg bg-muted/65" />
96
- </div>
97
- </div>
98
- <div className="p-6">
99
- <div className="grid gap-4 sm:grid-cols-3">
100
- <div className="h-28 rounded-xl border bg-muted/15" />
101
- <div className="h-28 rounded-xl border bg-muted/15" />
102
- <div className="h-28 rounded-xl border bg-muted/15" />
103
- </div>
104
- <div className="mt-5 h-72 rounded-xl border bg-muted/10" />
105
- </div>
106
- <AIChatFloatingTrigger
107
- controller={controller}
108
- unreadCount={3}
109
- position="absolute"
110
- />
111
- </div>
112
- </Card>
113
- </section>
114
-
115
- <section className="space-y-5">
116
- <div>
117
- <p className="text-xs font-semibold uppercase tracking-[0.16em] text-muted-foreground">
118
- Floating chat prompt
119
- </p>
120
- <h2 className="mt-2 text-xl font-semibold tracking-tight">
121
- Generate the global launcher separately
122
- </h2>
123
- </div>
124
- <PromptCard
125
- title="Add a lower-right AI floating entry"
126
- description="This prompt only configures the global trigger and its side panel."
127
- prompt={floatingPrompt}
128
- />
129
- </section>
130
- </div>
131
- </ChatSidePanelLayout>
132
- </AIChatProvider>
133
- );
134
- }
@@ -1,346 +0,0 @@
1
- import {
2
- AIChatWindow,
3
- ChatDialog,
4
- ChatSurfaceActions,
5
- ChatSidePanelLayout,
6
- useAIPageElementPicker,
7
- type AIChatComposerAction,
8
- type AIChatWindowProps,
9
- } from "../components";
10
- import { Badge } from "@/components/ui/badge";
11
- import { Card } from "@/components/ui/card";
12
- import {
13
- Table,
14
- TableBody,
15
- TableCell,
16
- TableHead,
17
- TableHeader,
18
- TableRow,
19
- } from "@/components/ui/table";
20
- import { AIChatProvider, useAIChatBase } from "../providers";
21
- import { Globe2, MousePointer2 } from "lucide-react";
22
- import { useMemo, useState } from "react";
23
- import { ContainerShowcase, type ChatContainer } from "./container-showcase";
24
- import { InteractionShowcase } from "./interaction-showcase";
25
- import { AIConfigurationGate } from "./configuration-gate";
26
- import { PromptGenerator } from "./prompt-generator";
27
-
28
- const propRows = [
29
- [
30
- "className",
31
- "string",
32
- "undefined",
33
- "Adds layout or sizing classes to the root conversation window.",
34
- ],
35
- [
36
- "headerActions",
37
- "ReactNode",
38
- "undefined",
39
- "Adds surface actions such as expand, collapse, or close to the header.",
40
- ],
41
- [
42
- "composerActions",
43
- "AIChatComposerAction[]",
44
- "[]",
45
- "Application-specific buttons rendered in the composer toolbar.",
46
- ],
47
- [
48
- "showConversationToggle",
49
- "boolean",
50
- "true",
51
- "Shows the conversation-list control.",
52
- ],
53
- [
54
- "showNewConversation",
55
- "boolean",
56
- "true",
57
- "Shows the new-conversation action.",
58
- ],
59
- [
60
- "showEmployeeSelector",
61
- "boolean",
62
- "true",
63
- "Shows the AI employee selector in the composer.",
64
- ],
65
- [
66
- "showModelSelector",
67
- "boolean",
68
- "true",
69
- "Shows the model selector in the composer.",
70
- ],
71
- [
72
- "showUserPrompt",
73
- "boolean",
74
- "true",
75
- "Shows the personalized AI employee prompt editor.",
76
- ],
77
- [
78
- "enableAttachments",
79
- "boolean",
80
- "false",
81
- "Enables file picker, drag-and-drop, and pasted-image uploads.",
82
- ],
83
- [
84
- "attachmentActionIndex",
85
- "number",
86
- "0",
87
- "Places the attachment action at a specific position in the composer toolbar.",
88
- ],
89
- [
90
- "onToolCallDecision",
91
- "(decision: AIToolCallDecision) => void | Promise<void>",
92
- "undefined",
93
- "Observes approve, reject, or edit decisions after AIChatProvider has processed them; use it for application side effects or telemetry.",
94
- ],
95
- [
96
- "placeholder",
97
- "string",
98
- "Message your AI employee…",
99
- "Composer placeholder text.",
100
- ],
101
- [
102
- "disclaimer",
103
- "ReactNode | false",
104
- "Default disclaimer",
105
- "Customizes or hides the footer disclaimer.",
106
- ],
107
- ];
108
-
109
- export function AIChatPage() {
110
- return (
111
- <AIConfigurationGate>
112
- <AIChatProvider id="ai-chat-demo">
113
- <AIChatPageContent />
114
- </AIChatProvider>
115
- </AIConfigurationGate>
116
- );
117
- }
118
-
119
- function AIChatPageContent() {
120
- const [container, setContainer] = useState<ChatContainer>("embedded");
121
- const [sidePanelOpen, setSidePanelOpen] = useState(false);
122
- const [dialogOpen, setDialogOpen] = useState(false);
123
- const [webSearch, setWebSearch] = useState(false);
124
- const { id: chatId, addWorkContext, focusComposer } = useAIChatBase();
125
- const { registeredCount, startPicking } = useAIPageElementPicker();
126
-
127
- const composerActions = useMemo<AIChatComposerAction[]>(
128
- () => [
129
- {
130
- key: "pick-page-element",
131
- label: "Pick page element",
132
- icon: <MousePointer2 />,
133
- disabled: registeredCount === 0,
134
- onClick: () => {
135
- if (dialogOpen) {
136
- setDialogOpen(false);
137
- setSidePanelOpen(true);
138
- setContainer("side-panel");
139
- }
140
- startPicking({
141
- chatId,
142
- onSelect: (item) => {
143
- addWorkContext(item);
144
- focusComposer();
145
- },
146
- });
147
- },
148
- },
149
- {
150
- key: "web-search",
151
- label: "Web search",
152
- icon: <Globe2 />,
153
- active: webSearch,
154
- onClick: () => {
155
- setWebSearch((active) => !active);
156
- },
157
- },
158
- ],
159
- [
160
- addWorkContext,
161
- chatId,
162
- dialogOpen,
163
- focusComposer,
164
- registeredCount,
165
- startPicking,
166
- webSearch,
167
- ]
168
- );
169
-
170
- const windowProps = useMemo<AIChatWindowProps>(
171
- () => ({
172
- composerActions,
173
- enableAttachments: true,
174
- attachmentActionIndex: 1,
175
- onToolCallDecision: async () => undefined,
176
- }),
177
- [composerActions]
178
- );
179
-
180
- const closeSurface = () => {
181
- setSidePanelOpen(false);
182
- setDialogOpen(false);
183
- };
184
-
185
- const sidePanelWindowProps: AIChatWindowProps = {
186
- ...windowProps,
187
- headerActions: (
188
- <ChatSurfaceActions
189
- expanded={false}
190
- onExpandedChange={(expanded) => {
191
- if (!expanded) return;
192
- setSidePanelOpen(false);
193
- setDialogOpen(true);
194
- }}
195
- onClose={closeSurface}
196
- />
197
- ),
198
- };
199
-
200
- const dialogWindowProps: AIChatWindowProps = {
201
- ...windowProps,
202
- headerActions: (
203
- <ChatSurfaceActions
204
- expanded={true}
205
- onExpandedChange={(nextExpanded) => {
206
- if (nextExpanded) return;
207
- setDialogOpen(false);
208
- setSidePanelOpen(true);
209
- setContainer("side-panel");
210
- }}
211
- onClose={closeSurface}
212
- />
213
- ),
214
- };
215
-
216
- const selectContainer = (nextContainer: ChatContainer) => {
217
- setContainer(nextContainer);
218
- setSidePanelOpen(nextContainer === "side-panel");
219
- setDialogOpen(nextContainer === "dialog");
220
- };
221
-
222
- return (
223
- <ChatSidePanelLayout
224
- open={sidePanelOpen}
225
- onOpenChange={setSidePanelOpen}
226
- width={450}
227
- panel={<AIChatWindow {...sidePanelWindowProps} />}
228
- showCloseHandle={false}
229
- >
230
- <div className="space-y-14 pb-12">
231
- <section className="flex flex-wrap items-start justify-between gap-5 border-b pb-8">
232
- <div>
233
- <div className="flex items-center gap-2">
234
- <Badge variant="secondary">AI Components</Badge>
235
- <Badge variant="outline">Preview</Badge>
236
- </div>
237
- <h1 className="mt-4 text-3xl font-semibold tracking-[-0.035em]">
238
- AI Chat Window
239
- </h1>
240
- <p className="mt-3 max-w-3xl text-sm leading-6 text-muted-foreground">
241
- A position-independent NocoBase AI employee conversation
242
- component. Explore message interactions independently, place the
243
- same chat in different containers, then generate an implementation
244
- prompt for a target page.
245
- </p>
246
- </div>
247
- </section>
248
-
249
- <section className="space-y-5">
250
- <SectionTitle
251
- eyebrow="Container patterns"
252
- title="Use the same conversation window wherever the product needs it"
253
- description="The provider owns conversation state. Page, embedded block, push side panel, dialog, and mobile containers only decide placement and dimensions."
254
- />
255
- <ContainerShowcase
256
- value={container}
257
- onValueChange={selectContainer}
258
- windowProps={windowProps}
259
- />
260
- </section>
261
-
262
- <section className="space-y-5">
263
- <SectionTitle
264
- eyebrow="Message presentation"
265
- title="Choose how much conversation history the page should expose"
266
- description="Use the complete transcript for conversational work, or a compact worker surface that opens message history only when the user asks for it."
267
- />
268
- <InteractionShowcase />
269
- </section>
270
-
271
- <section className="space-y-5">
272
- <SectionTitle
273
- eyebrow="Prompt generator"
274
- title="Describe where chat belongs, then copy an implementation prompt"
275
- description="This replaces a generic prop configuration panel with a task-oriented generator: choose the target area, placement mode, and required capabilities."
276
- />
277
- <PromptGenerator />
278
- </section>
279
-
280
- <section className="space-y-5">
281
- <SectionTitle
282
- eyebrow="Component API"
283
- title="AIChatWindow props"
284
- description="The core window stays reusable while business pages provide placement, composer actions, and tool-approval behavior."
285
- />
286
- <Card className="gap-0 overflow-hidden py-0">
287
- <Table>
288
- <TableHeader>
289
- <TableRow>
290
- <TableHead>Prop</TableHead>
291
- <TableHead>Type</TableHead>
292
- <TableHead>Default</TableHead>
293
- <TableHead>Description</TableHead>
294
- </TableRow>
295
- </TableHeader>
296
- <TableBody>
297
- {propRows.map(([name, type, defaultValue, description]) => (
298
- <TableRow key={name}>
299
- <TableCell className="font-mono text-xs font-medium">
300
- {name}
301
- </TableCell>
302
- <TableCell className="font-mono text-xs text-muted-foreground">
303
- {type}
304
- </TableCell>
305
- <TableCell className="font-mono text-xs text-muted-foreground">
306
- {defaultValue}
307
- </TableCell>
308
- <TableCell className="min-w-80 whitespace-normal text-muted-foreground">
309
- {description}
310
- </TableCell>
311
- </TableRow>
312
- ))}
313
- </TableBody>
314
- </Table>
315
- </Card>
316
- </section>
317
-
318
- <ChatDialog open={dialogOpen} onOpenChange={setDialogOpen}>
319
- <AIChatWindow {...dialogWindowProps} />
320
- </ChatDialog>
321
- </div>
322
- </ChatSidePanelLayout>
323
- );
324
- }
325
-
326
- function SectionTitle({
327
- eyebrow,
328
- title,
329
- description,
330
- }: {
331
- eyebrow: string;
332
- title: string;
333
- description: string;
334
- }) {
335
- return (
336
- <div>
337
- <p className="text-xs font-semibold uppercase tracking-[0.16em] text-muted-foreground">
338
- {eyebrow}
339
- </p>
340
- <h2 className="mt-2 text-xl font-semibold tracking-tight">{title}</h2>
341
- <p className="mt-1.5 max-w-3xl text-sm leading-6 text-muted-foreground">
342
- {description}
343
- </p>
344
- </div>
345
- );
346
- }