@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,210 +0,0 @@
1
- import { Chat } from "@ai-sdk/react";
2
- import {
3
- useCallback,
4
- useRef,
5
- type Dispatch,
6
- type MutableRefObject,
7
- } from "react";
8
- import type { AIChatAction, AIChatState } from "./chat-reducer";
9
- import { NocoBaseChatTransport } from "./chat-transport";
10
- import { findAIModel, getAIModelKey } from "./model";
11
- import type { useAI } from "./ai-provider";
12
- import {
13
- AI_DRAFT_CONVERSATION_ID,
14
- type AIChatMessage,
15
- type AIChatTaskRuntime,
16
- } from "./types";
17
-
18
- type AIContextValue = ReturnType<typeof useAI>;
19
-
20
- export type AIConversationRuntimeContext = {
21
- employeeUsername: string;
22
- model: string;
23
- task?: AIChatTaskRuntime;
24
- };
25
-
26
- export function useChatRuntime({
27
- id,
28
- ai,
29
- stateRef,
30
- taskRuntimeRef,
31
- webSearchRef,
32
- conversationFinishedHandlerRef,
33
- moveAttachments,
34
- moveWorkContext,
35
- dispatch,
36
- }: {
37
- id: string;
38
- ai: AIContextValue;
39
- stateRef: MutableRefObject<AIChatState>;
40
- taskRuntimeRef: MutableRefObject<AIChatTaskRuntime | undefined>;
41
- webSearchRef: MutableRefObject<boolean>;
42
- conversationFinishedHandlerRef: MutableRefObject<
43
- | ((conversationId: string, chat: Chat<AIChatMessage>) => Promise<void>)
44
- | undefined
45
- >;
46
- moveAttachments: (from: string, to: string) => void;
47
- moveWorkContext: (from: string, to: string) => void;
48
- dispatch: Dispatch<AIChatAction>;
49
- }) {
50
- const chatsRef = useRef(new Map<string, Chat<AIChatMessage>>());
51
- const transportsRef = useRef(new Map<string, NocoBaseChatTransport>());
52
- const runtimeContextsRef = useRef(
53
- new Map<string, AIConversationRuntimeContext>()
54
- );
55
-
56
- const getRuntimeContext = useCallback(
57
- (conversationId: string): AIConversationRuntimeContext => {
58
- const existing = runtimeContextsRef.current.get(conversationId);
59
- if (existing) return existing;
60
-
61
- const latestState = stateRef.current;
62
- const conversation = latestState.conversations.find(
63
- (item) => item.id === conversationId
64
- );
65
- const conversationModel = conversation?.model
66
- ? ai.models.find(
67
- (item) =>
68
- item.value === conversation.model?.model &&
69
- (!conversation.model.llmService ||
70
- item.llmService === conversation.model.llmService)
71
- )
72
- : undefined;
73
- const context = {
74
- employeeUsername:
75
- conversation?.employeeUsername ??
76
- latestState.selectedEmployeeUsername,
77
- model: conversationModel
78
- ? getAIModelKey(conversationModel)
79
- : latestState.selectedModel,
80
- task:
81
- conversationId === AI_DRAFT_CONVERSATION_ID
82
- ? taskRuntimeRef.current
83
- : undefined,
84
- };
85
- runtimeContextsRef.current.set(conversationId, context);
86
- return context;
87
- },
88
- [ai.models, stateRef, taskRuntimeRef]
89
- );
90
-
91
- const getChat = useCallback(
92
- (conversationId: string) => {
93
- const existing = chatsRef.current.get(conversationId);
94
- if (existing) return existing;
95
-
96
- let runtimeConversationId = conversationId;
97
- const transport = ai.createTransport({
98
- chatId: `${id}:${conversationId}`,
99
- getContext: () => {
100
- const runtimeContext = getRuntimeContext(runtimeConversationId);
101
- const employee =
102
- ai.employees.find(
103
- (item) => item.username === runtimeContext.employeeUsername
104
- ) ?? ai.employees[0];
105
- const model =
106
- findAIModel(ai.models, runtimeContext.model) ?? ai.models[0];
107
- if (!employee || !model) {
108
- throw new Error(
109
- "AIProvider requires at least one employee and model"
110
- );
111
- }
112
- const task = runtimeContext.task;
113
- return {
114
- sessionId:
115
- runtimeConversationId === AI_DRAFT_CONVERSATION_ID
116
- ? undefined
117
- : runtimeConversationId,
118
- employee,
119
- model,
120
- task: task
121
- ? {
122
- ...task,
123
- webSearch: task.webSearch ?? webSearchRef.current,
124
- }
125
- : webSearchRef.current
126
- ? { workContext: [], webSearch: true }
127
- : undefined,
128
- };
129
- },
130
- onSessionCreated: (sessionId) => {
131
- const previousConversationId = runtimeConversationId;
132
- chatsRef.current.delete(runtimeConversationId);
133
- chatsRef.current.set(sessionId, chat);
134
- if (transport instanceof NocoBaseChatTransport) {
135
- transportsRef.current.delete(previousConversationId);
136
- transportsRef.current.set(sessionId, transport);
137
- }
138
- const runtimeContext = runtimeContextsRef.current.get(
139
- previousConversationId
140
- );
141
- if (runtimeContext) {
142
- runtimeContextsRef.current.delete(previousConversationId);
143
- runtimeContextsRef.current.set(sessionId, runtimeContext);
144
- }
145
- moveAttachments(previousConversationId, sessionId);
146
- moveWorkContext(previousConversationId, sessionId);
147
- dispatch({
148
- type: "replace-conversation-id",
149
- from: runtimeConversationId,
150
- to: sessionId,
151
- });
152
- runtimeConversationId = sessionId;
153
- },
154
- });
155
- const chat = new Chat<AIChatMessage>({
156
- id: `${id}:${conversationId}`,
157
- messages: [],
158
- onFinish: () => {
159
- const finishedConversationId = runtimeConversationId;
160
- if (finishedConversationId === AI_DRAFT_CONVERSATION_ID) {
161
- return;
162
- }
163
- queueMicrotask(() => {
164
- void conversationFinishedHandlerRef.current?.(
165
- finishedConversationId,
166
- chat
167
- );
168
- });
169
- },
170
- transport,
171
- });
172
- chatsRef.current.set(conversationId, chat);
173
- if (transport instanceof NocoBaseChatTransport) {
174
- transportsRef.current.set(conversationId, transport);
175
- }
176
- return chat;
177
- },
178
- [
179
- ai,
180
- conversationFinishedHandlerRef,
181
- dispatch,
182
- getRuntimeContext,
183
- id,
184
- moveAttachments,
185
- moveWorkContext,
186
- webSearchRef,
187
- ]
188
- );
189
-
190
- const getTransport = useCallback(
191
- (conversationId: string) => transportsRef.current.get(conversationId),
192
- []
193
- );
194
-
195
- const remove = useCallback((conversationId: string) => {
196
- chatsRef.current.delete(conversationId);
197
- transportsRef.current.delete(conversationId);
198
- runtimeContextsRef.current.delete(conversationId);
199
- }, []);
200
-
201
- return {
202
- chatsRef,
203
- transportsRef,
204
- runtimeContextsRef,
205
- getRuntimeContext,
206
- getChat,
207
- getTransport,
208
- remove,
209
- };
210
- }
@@ -1,93 +0,0 @@
1
- import { useCallback, useRef, useState } from "react";
2
- import type { AIWorkContextItem } from "./types";
3
-
4
- const EMPTY_CONTEXT: AIWorkContextItem[] = [];
5
-
6
- const contextItemKey = (item: AIWorkContextItem) =>
7
- `${item.type}:${item.id ?? item.title ?? "context"}`;
8
-
9
- export function useChatWorkContext(activeConversationId: string) {
10
- const [drafts, setDrafts] = useState<Record<string, AIWorkContextItem[]>>({});
11
- const draftsRef = useRef(drafts);
12
- draftsRef.current = drafts;
13
-
14
- const setConversationWorkContext = useCallback(
15
- (
16
- conversationId: string,
17
- value:
18
- | AIWorkContextItem[]
19
- | ((current: AIWorkContextItem[]) => AIWorkContextItem[])
20
- ) => {
21
- setDrafts((current) => ({
22
- ...current,
23
- [conversationId]:
24
- typeof value === "function"
25
- ? value(current[conversationId] ?? [])
26
- : value,
27
- }));
28
- },
29
- []
30
- );
31
-
32
- const addWorkContext = useCallback(
33
- (item: AIWorkContextItem) => {
34
- setConversationWorkContext(activeConversationId, (current) => {
35
- const key = contextItemKey(item);
36
- return [
37
- ...current.filter((entry) => contextItemKey(entry) !== key),
38
- item,
39
- ];
40
- });
41
- },
42
- [activeConversationId, setConversationWorkContext]
43
- );
44
-
45
- const removeWorkContext = useCallback(
46
- (item: AIWorkContextItem) => {
47
- const key = contextItemKey(item);
48
- setConversationWorkContext(activeConversationId, (current) =>
49
- current.filter((entry) => contextItemKey(entry) !== key)
50
- );
51
- },
52
- [activeConversationId, setConversationWorkContext]
53
- );
54
-
55
- const moveWorkContext = useCallback((from: string, to: string) => {
56
- setDrafts((current) => {
57
- if (!current[from]) return current;
58
- const next = { ...current, [to]: current[from] };
59
- delete next[from];
60
- return next;
61
- });
62
- }, []);
63
-
64
- const removeConversationWorkContext = useCallback(
65
- (conversationId: string) => {
66
- setDrafts((current) => {
67
- if (!current[conversationId]) return current;
68
- const next = { ...current };
69
- delete next[conversationId];
70
- return next;
71
- });
72
- },
73
- []
74
- );
75
-
76
- const clearWorkContext = useCallback(() => setDrafts({}), []);
77
- const getConversationWorkContext = useCallback(
78
- (conversationId: string) => draftsRef.current[conversationId] ?? [],
79
- []
80
- );
81
- const workContext = drafts[activeConversationId] ?? EMPTY_CONTEXT;
82
-
83
- return {
84
- workContext,
85
- addWorkContext,
86
- removeWorkContext,
87
- setConversationWorkContext,
88
- moveWorkContext,
89
- removeConversationWorkContext,
90
- clearWorkContext,
91
- getConversationWorkContext,
92
- };
93
- }
@@ -1,108 +0,0 @@
1
- import { useCallback, useEffect, useRef, useState } from "react";
2
- import type { AIConfigurationStatus, AIConversation } from "./types";
3
-
4
- export function useConversationCatalog({
5
- configurationStatus,
6
- listConversations,
7
- onChange,
8
- onError,
9
- }: {
10
- configurationStatus: AIConfigurationStatus;
11
- listConversations: (keyword?: string) => Promise<AIConversation[]>;
12
- onChange: (conversations: AIConversation[]) => void;
13
- onError: (error?: Error) => void;
14
- }) {
15
- const [loading, setLoading] = useState(false);
16
- const [search, setSearch] = useState("");
17
- const searchRef = useRef("");
18
- const requestRef = useRef(0);
19
- const catalogRef = useRef<AIConversation[]>([]);
20
-
21
- const apply = useCallback(
22
- (conversations: AIConversation[], keyword = searchRef.current) => {
23
- if (!keyword) catalogRef.current = conversations;
24
- onChange(conversations);
25
- },
26
- [onChange]
27
- );
28
-
29
- const refresh = useCallback(async () => {
30
- const requestId = requestRef.current + 1;
31
- requestRef.current = requestId;
32
- const keyword = searchRef.current;
33
- const conversations = await listConversations(keyword);
34
- if (requestRef.current === requestId) apply(conversations, keyword);
35
- return conversations;
36
- }, [apply, listConversations]);
37
-
38
- useEffect(() => {
39
- if (configurationStatus !== "ready") return;
40
- let active = true;
41
- setLoading(true);
42
- onError(undefined);
43
- void listConversations()
44
- .then((conversations) => {
45
- if (!active || searchRef.current) return;
46
- apply(conversations, "");
47
- })
48
- .catch((error: unknown) => {
49
- if (!active) return;
50
- onError(
51
- error instanceof Error
52
- ? error
53
- : new Error("Unable to load conversations")
54
- );
55
- })
56
- .finally(() => {
57
- if (active) setLoading(false);
58
- });
59
- return () => {
60
- active = false;
61
- };
62
- }, [apply, configurationStatus, listConversations, onError]);
63
-
64
- const searchConversations = useCallback(
65
- async (keyword: string) => {
66
- const normalizedKeyword = keyword.trim();
67
- setSearch(keyword);
68
- searchRef.current = normalizedKeyword;
69
-
70
- const requestId = requestRef.current + 1;
71
- requestRef.current = requestId;
72
- setLoading(true);
73
- onError(undefined);
74
- try {
75
- const conversations = await listConversations(normalizedKeyword);
76
- if (requestRef.current !== requestId) return;
77
- apply(conversations, normalizedKeyword);
78
- } catch (error) {
79
- if (requestRef.current !== requestId) return;
80
- const resolvedError =
81
- error instanceof Error
82
- ? error
83
- : new Error("Unable to search conversations");
84
- onError(resolvedError);
85
- throw resolvedError;
86
- } finally {
87
- if (requestRef.current === requestId) setLoading(false);
88
- }
89
- },
90
- [apply, listConversations, onError]
91
- );
92
-
93
- const updateCatalog = useCallback(
94
- (updater: (conversations: AIConversation[]) => AIConversation[]) => {
95
- catalogRef.current = updater(catalogRef.current);
96
- },
97
- []
98
- );
99
-
100
- return {
101
- loading,
102
- search,
103
- searchRef,
104
- refresh,
105
- searchConversations,
106
- updateCatalog,
107
- };
108
- }
@@ -1,178 +0,0 @@
1
- import type { Chat } from "@ai-sdk/react";
2
- import { useCallback, useEffect, useRef, useState } from "react";
3
- import type { NocoBaseChatTransport } from "./chat-transport";
4
- import { reconcileRefreshedMessages } from "./chat-message-utils";
5
- import { AI_DRAFT_CONVERSATION_ID, type AIChatMessage } from "./types";
6
-
7
- const isChatRunning = (chat: Chat<AIChatMessage>) =>
8
- chat.status === "streaming" || chat.status === "submitted";
9
-
10
- export function useConversationHistory({
11
- chatSurfaceOpen,
12
- activeConversationId,
13
- getActiveConversationId,
14
- getChat,
15
- getTransport,
16
- getConversationMessages,
17
- getConversationActiveState,
18
- onMarkRead,
19
- onError,
20
- }: {
21
- chatSurfaceOpen: boolean;
22
- activeConversationId: string;
23
- getActiveConversationId: () => string;
24
- getChat: (conversationId: string) => Chat<AIChatMessage>;
25
- getTransport: (conversationId: string) => NocoBaseChatTransport | undefined;
26
- getConversationMessages: (
27
- conversationId: string,
28
- options?: { updateRead?: boolean }
29
- ) => Promise<AIChatMessage[]>;
30
- getConversationActiveState: (
31
- conversationId: string
32
- ) => Promise<string | undefined>;
33
- onMarkRead: (conversationId: string) => void;
34
- onError: (error?: Error) => void;
35
- }) {
36
- const surfaceOpenRef = useRef(chatSurfaceOpen);
37
- const openVersionRef = useRef(0);
38
- const pendingRequestsRef = useRef(
39
- new Map<string, Promise<AIChatMessage[]>>()
40
- );
41
- const [loadingId, setLoadingId] = useState<string>();
42
- surfaceOpenRef.current = chatSurfaceOpen;
43
-
44
- const refresh = useCallback(
45
- (
46
- conversationId: string,
47
- targetChat: Chat<AIChatMessage>,
48
- options: { updateRead?: boolean } = {}
49
- ) => {
50
- const updateRead = options.updateRead === true;
51
- const requestKey = `${conversationId}:${updateRead ? "read" : "peek"}`;
52
- const pending = pendingRequestsRef.current.get(requestKey);
53
- if (pending) return pending;
54
-
55
- const request = getConversationMessages(conversationId, { updateRead })
56
- .then((messages) => {
57
- const reconciled = reconcileRefreshedMessages(
58
- targetChat.messages,
59
- messages
60
- );
61
- targetChat.messages = reconciled;
62
- return reconciled;
63
- })
64
- .finally(() => pendingRequestsRef.current.delete(requestKey));
65
- pendingRequestsRef.current.set(requestKey, request);
66
- return request;
67
- },
68
- [getConversationMessages]
69
- );
70
-
71
- const load = useCallback(
72
- async (conversationId: string) => {
73
- const targetChat = getChat(conversationId);
74
- if (isChatRunning(targetChat)) return;
75
-
76
- const openVersion = openVersionRef.current + 1;
77
- openVersionRef.current = openVersion;
78
- setLoadingId(conversationId);
79
- onError(undefined);
80
- try {
81
- await refresh(conversationId, targetChat, { updateRead: true });
82
- onMarkRead(conversationId);
83
- } catch (error) {
84
- onError(
85
- error instanceof Error
86
- ? error
87
- : new Error("Unable to load conversation messages")
88
- );
89
- return;
90
- } finally {
91
- setLoadingId((current) =>
92
- current === conversationId ? undefined : current
93
- );
94
- }
95
-
96
- if (
97
- openVersionRef.current !== openVersion ||
98
- getActiveConversationId() !== conversationId ||
99
- isChatRunning(targetChat)
100
- ) {
101
- return;
102
- }
103
-
104
- try {
105
- const activeState = await getConversationActiveState(conversationId);
106
- if (
107
- openVersionRef.current !== openVersion ||
108
- getActiveConversationId() !== conversationId ||
109
- isChatRunning(targetChat) ||
110
- activeState !== "streaming"
111
- ) {
112
- return;
113
- }
114
- getTransport(conversationId)?.prepareConversationResume(
115
- targetChat.messages
116
- );
117
- await targetChat.resumeStream();
118
- } catch (error) {
119
- onError(
120
- error instanceof Error
121
- ? error
122
- : new Error("Unable to resume the conversation stream")
123
- );
124
- }
125
- },
126
- [
127
- getActiveConversationId,
128
- getChat,
129
- getConversationActiveState,
130
- getTransport,
131
- onError,
132
- onMarkRead,
133
- refresh,
134
- ]
135
- );
136
-
137
- useEffect(() => {
138
- if (
139
- !chatSurfaceOpen ||
140
- activeConversationId === AI_DRAFT_CONVERSATION_ID
141
- ) {
142
- return;
143
- }
144
- const targetChat = getChat(activeConversationId);
145
- if (isChatRunning(targetChat)) return;
146
-
147
- void refresh(activeConversationId, targetChat, { updateRead: true })
148
- .then(() => {
149
- if (
150
- surfaceOpenRef.current &&
151
- getActiveConversationId() === activeConversationId
152
- ) {
153
- onMarkRead(activeConversationId);
154
- }
155
- })
156
- .catch((error: unknown) => {
157
- onError(
158
- error instanceof Error
159
- ? error
160
- : new Error("Unable to mark the conversation as read")
161
- );
162
- });
163
- }, [
164
- activeConversationId,
165
- chatSurfaceOpen,
166
- getActiveConversationId,
167
- getChat,
168
- onError,
169
- onMarkRead,
170
- refresh,
171
- ]);
172
-
173
- const invalidate = useCallback(() => {
174
- openVersionRef.current += 1;
175
- }, []);
176
-
177
- return { invalidate, load, loadingId, refresh };
178
- }
@@ -1,7 +0,0 @@
1
- export { NocoBaseAIService, nocobaseAIService } from "./nocobase-ai-service";
2
- export type {
3
- AIService,
4
- CreateAIConversationOptions,
5
- UpdateToolCallDecisionOptions,
6
- UpdatedToolCall,
7
- } from "./types";