@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.
- package/README.MD +10 -5
- package/package.json +1 -1
- package/registry/nocobase-ai/components/chat/chat-message.tsx +8 -6
- package/registry/nocobase-ai/components/chat/chat-messages.tsx +8 -3
- package/registry/nocobase-ai/components/chat/markdown-message.tsx +26 -0
- package/registry/nocobase-ai/components/chat/sub-agent-conversation.tsx +3 -3
- package/registry/nocobase-ai/demo/configuration-gate.tsx +83 -35
- package/registry/nocobase-i18n/README.md +23 -32
- package/registry/nocobase-i18n/components/language-switcher.tsx +1 -1
- package/registry/nocobase-i18n/demo/index.tsx +2 -2
- package/registry/nocobase-i18n/demo/prompt-generator.tsx +3 -2
- package/registry/nocobase-i18n/extension.tsx +3 -4
- package/registry/nocobase-i18n/index.ts +4 -3
- package/registry/nocobase-i18n/locales/en-US.ts +2 -52
- package/registry/nocobase-i18n/locales/index.ts +8 -0
- package/registry/nocobase-i18n/locales/zh-CN.ts +2 -52
- package/registry/nocobase-i18n/provider.tsx +18 -6
- package/registry/nocobase-i18n/server-resources.ts +13 -31
- package/registry/nocobase-i18n/tests/i18n-regression.mjs +14 -5
- package/registry/nocobase-route-surfaces/README.md +21 -0
- package/registry/nocobase-route-surfaces/demo/index.tsx +266 -0
- package/{src/extensions/nocobase-acl → registry/nocobase-route-surfaces}/demo/lazy-route.tsx +2 -1
- package/registry/nocobase-route-surfaces/demo/prompt-generator.tsx +90 -0
- package/registry/nocobase-route-surfaces/demo/scenarios.ts +95 -0
- package/registry/nocobase-route-surfaces/extension.tsx +119 -0
- package/registry/nocobase-route-surfaces/index.ts +7 -0
- package/registry/nocobase-route-surfaces/route-dialog.tsx +145 -0
- package/registry/nocobase-route-surfaces/route-drawer.tsx +186 -0
- package/registry/nocobase-route-surfaces/route-page.tsx +29 -0
- package/registry/nocobase-route-surfaces/route-surface-context.ts +13 -0
- package/registry/nocobase-route-surfaces/use-refine-unsaved-changes.tsx +106 -0
- package/registry/nocobase-route-surfaces/use-route-surface-close.ts +15 -0
- package/registry/nocobase-route-surfaces/use-route-surface-state.ts +65 -0
- package/registry.config.json +30 -3
- package/scripts/registry.mjs +0 -2
- package/src/App.tsx +65 -35
- package/src/app/extension.ts +1 -2
- package/src/app/extensions.tsx +0 -14
- package/src/components/app-shell/breadcrumb.tsx +67 -2
- package/src/components/app-shell/sidebar.tsx +2 -2
- package/src/components/resources/views/list-view.tsx +3 -2
- package/src/components/ui/sheet.tsx +22 -21
- package/src/lib/nocobase/client.ts +6 -1
- package/src/locales/en-US.ts +81 -0
- package/src/locales/zh-CN.ts +77 -0
- package/src/pages/users/create.tsx +76 -53
- package/src/pages/users/edit.tsx +87 -56
- package/src/pages/users/index.ts +3 -0
- package/src/pages/users/layout.tsx +15 -0
- package/src/pages/users/list.tsx +19 -21
- package/src/pages/users/role-badges.tsx +43 -0
- package/src/pages/users/role-detail.tsx +272 -0
- package/src/pages/users/role-utils.ts +6 -0
- package/src/pages/users/routes.ts +24 -0
- package/src/pages/users/show.tsx +195 -104
- package/src/pages/users/types.ts +13 -0
- package/src/providers/i18n/index.ts +3 -0
- package/{registry/nocobase-i18n → src/providers/i18n}/runtime.ts +63 -16
- package/src/providers/system-settings/context.ts +26 -0
- package/src/providers/system-settings/index.ts +2 -0
- package/src/providers/system-settings/provider.tsx +94 -0
- package/src/extensions/nocobase-acl/README.md +0 -19
- package/src/extensions/nocobase-acl/components/acl-boundary.tsx +0 -106
- package/src/extensions/nocobase-acl/components/index.ts +0 -3
- package/src/extensions/nocobase-acl/components/role-options.ts +0 -36
- package/src/extensions/nocobase-acl/components/role-switcher.tsx +0 -135
- package/src/extensions/nocobase-acl/demo/boundary-api.tsx +0 -186
- package/src/extensions/nocobase-acl/demo/components.tsx +0 -231
- package/src/extensions/nocobase-acl/demo/index.tsx +0 -505
- package/src/extensions/nocobase-acl/demo/prompt-generator.tsx +0 -134
- package/src/extensions/nocobase-acl/demo/role-switcher-prompt-generator.tsx +0 -198
- package/src/extensions/nocobase-acl/demo/scenario-section.tsx +0 -37
- package/src/extensions/nocobase-acl/extension.tsx +0 -71
- package/src/extensions/nocobase-acl/index.ts +0 -1
- package/src/extensions/nocobase-ai/README.md +0 -70
- package/src/extensions/nocobase-ai/adapters/react-hook-form.ts +0 -20
- package/src/extensions/nocobase-ai/assets/nocobase-ai-chat.svg +0 -50
- package/src/extensions/nocobase-ai/components/ai-root-provider.tsx +0 -32
- package/src/extensions/nocobase-ai/components/chat/ai-employee-avatar.tsx +0 -31
- package/src/extensions/nocobase-ai/components/chat/chat-attachment.tsx +0 -92
- package/src/extensions/nocobase-ai/components/chat/chat-compact.tsx +0 -90
- package/src/extensions/nocobase-ai/components/chat/chat-composer.tsx +0 -429
- package/src/extensions/nocobase-ai/components/chat/chat-empty-state.tsx +0 -51
- package/src/extensions/nocobase-ai/components/chat/chat-header.tsx +0 -88
- package/src/extensions/nocobase-ai/components/chat/chat-history-dialog.tsx +0 -77
- package/src/extensions/nocobase-ai/components/chat/chat-message.tsx +0 -252
- package/src/extensions/nocobase-ai/components/chat/chat-messages.tsx +0 -173
- package/src/extensions/nocobase-ai/components/chat/chat-window.tsx +0 -118
- package/src/extensions/nocobase-ai/components/chat/conversation-list.tsx +0 -343
- package/src/extensions/nocobase-ai/components/chat/markdown-message.tsx +0 -165
- package/src/extensions/nocobase-ai/components/chat/model-select-options.tsx +0 -30
- package/src/extensions/nocobase-ai/components/chat/reasoning-panel.tsx +0 -29
- package/src/extensions/nocobase-ai/components/chat/sub-agent-conversation.tsx +0 -142
- package/src/extensions/nocobase-ai/components/chat/tool-call-card.tsx +0 -316
- package/src/extensions/nocobase-ai/components/chat/user-prompt-editor.tsx +0 -99
- package/src/extensions/nocobase-ai/components/chat/work-context-chip.tsx +0 -114
- package/src/extensions/nocobase-ai/components/index.ts +0 -59
- package/src/extensions/nocobase-ai/components/page-elements/ai-form.tsx +0 -44
- package/src/extensions/nocobase-ai/components/page-elements/page-element-provider.tsx +0 -469
- package/src/extensions/nocobase-ai/components/surfaces/chat-dialog.tsx +0 -31
- package/src/extensions/nocobase-ai/components/surfaces/chat-inline.tsx +0 -18
- package/src/extensions/nocobase-ai/components/surfaces/chat-page.tsx +0 -18
- package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel-layout.tsx +0 -54
- package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel.tsx +0 -74
- package/src/extensions/nocobase-ai/components/surfaces/chat-surface-actions.tsx +0 -54
- package/src/extensions/nocobase-ai/components/tools/builtin-tool-renderers.tsx +0 -31
- package/src/extensions/nocobase-ai/components/tools/business-report-dialog.tsx +0 -364
- package/src/extensions/nocobase-ai/components/tools/business-report-renderer.tsx +0 -122
- package/src/extensions/nocobase-ai/components/tools/business-report-utils.tsx +0 -282
- package/src/extensions/nocobase-ai/components/tools/chart-renderer.tsx +0 -99
- package/src/extensions/nocobase-ai/components/tools/echarts-preview.tsx +0 -59
- package/src/extensions/nocobase-ai/components/tools/echarts-runtime-advanced.ts +0 -18
- package/src/extensions/nocobase-ai/components/tools/echarts-runtime-common.ts +0 -20
- package/src/extensions/nocobase-ai/components/tools/echarts-runtime-components.ts +0 -34
- package/src/extensions/nocobase-ai/components/tools/echarts-runtime-hierarchy.ts +0 -24
- package/src/extensions/nocobase-ai/components/tools/echarts-runtime.ts +0 -138
- package/src/extensions/nocobase-ai/components/tools/sub-agent-renderer.tsx +0 -67
- package/src/extensions/nocobase-ai/components/tools/suggestions-renderer.tsx +0 -59
- package/src/extensions/nocobase-ai/components/tools/tool-renderer-provider.tsx +0 -55
- package/src/extensions/nocobase-ai/components/tools/tool-renderer-utils.ts +0 -18
- package/src/extensions/nocobase-ai/components/tools/workflow-renderer.tsx +0 -108
- package/src/extensions/nocobase-ai/components/triggers/ai-chat-floating-trigger.tsx +0 -73
- package/src/extensions/nocobase-ai/components/triggers/ai-employee-shortcut.tsx +0 -149
- package/src/extensions/nocobase-ai/demo/configuration-gate.tsx +0 -49
- package/src/extensions/nocobase-ai/demo/container-showcase.tsx +0 -210
- package/src/extensions/nocobase-ai/demo/floating.tsx +0 -134
- package/src/extensions/nocobase-ai/demo/index.tsx +0 -346
- package/src/extensions/nocobase-ai/demo/interaction-showcase.tsx +0 -151
- package/src/extensions/nocobase-ai/demo/page-context-prompt-generator.tsx +0 -355
- package/src/extensions/nocobase-ai/demo/page-context.tsx +0 -884
- package/src/extensions/nocobase-ai/demo/page-element-showcase.tsx +0 -197
- package/src/extensions/nocobase-ai/demo/prompt-card.tsx +0 -41
- package/src/extensions/nocobase-ai/demo/prompt-generator.tsx +0 -452
- package/src/extensions/nocobase-ai/demo/shortcut.tsx +0 -1207
- package/src/extensions/nocobase-ai/demo/tool-cards.tsx +0 -530
- package/src/extensions/nocobase-ai/extension.tsx +0 -158
- package/src/extensions/nocobase-ai/global-ai-chat.tsx +0 -213
- package/src/extensions/nocobase-ai/index.ts +0 -3
- package/src/extensions/nocobase-ai/providers/ai-provider.tsx +0 -323
- package/src/extensions/nocobase-ai/providers/avatars.ts +0 -817
- package/src/extensions/nocobase-ai/providers/chat-context.tsx +0 -109
- package/src/extensions/nocobase-ai/providers/chat-controller.ts +0 -84
- package/src/extensions/nocobase-ai/providers/chat-message-utils.ts +0 -198
- package/src/extensions/nocobase-ai/providers/chat-provider.tsx +0 -871
- package/src/extensions/nocobase-ai/providers/chat-reducer.ts +0 -147
- package/src/extensions/nocobase-ai/providers/chat-task-utils.ts +0 -105
- package/src/extensions/nocobase-ai/providers/chat-transport.ts +0 -184
- package/src/extensions/nocobase-ai/providers/form-registry.tsx +0 -271
- package/src/extensions/nocobase-ai/providers/frontend-tool-registry.tsx +0 -231
- package/src/extensions/nocobase-ai/providers/index.ts +0 -61
- package/src/extensions/nocobase-ai/providers/model.ts +0 -32
- package/src/extensions/nocobase-ai/providers/page-context.tsx +0 -136
- package/src/extensions/nocobase-ai/providers/stream-coalescer.ts +0 -60
- package/src/extensions/nocobase-ai/providers/stream-event-utils.ts +0 -82
- package/src/extensions/nocobase-ai/providers/stream-parser.ts +0 -61
- package/src/extensions/nocobase-ai/providers/sub-agent-stream.ts +0 -316
- package/src/extensions/nocobase-ai/providers/types.ts +0 -183
- package/src/extensions/nocobase-ai/providers/ui-message-stream.ts +0 -463
- package/src/extensions/nocobase-ai/providers/use-automatic-tool-approval.ts +0 -94
- package/src/extensions/nocobase-ai/providers/use-chat-attachments.ts +0 -206
- package/src/extensions/nocobase-ai/providers/use-chat-message-actions.ts +0 -554
- package/src/extensions/nocobase-ai/providers/use-chat-runtime.ts +0 -210
- package/src/extensions/nocobase-ai/providers/use-chat-work-context.ts +0 -93
- package/src/extensions/nocobase-ai/providers/use-conversation-catalog.ts +0 -108
- package/src/extensions/nocobase-ai/providers/use-conversation-history.ts +0 -178
- package/src/extensions/nocobase-ai/services/index.ts +0 -7
- package/src/extensions/nocobase-ai/services/nocobase-ai-service.ts +0 -454
- package/src/extensions/nocobase-ai/services/types.ts +0 -82
- /package/{registry/nocobase-i18n → src/providers/i18n}/i18n-provider.ts +0 -0
- /package/{registry/nocobase-i18n → src/providers/i18n}/locale-store.ts +0 -0
package/README.MD
CHANGED
|
@@ -68,11 +68,10 @@ pnpm registry:install
|
|
|
68
68
|
This repository authors optional NocoBase integration entries. Internationalization
|
|
69
69
|
is delivered as one cohesive Registry entry:
|
|
70
70
|
|
|
71
|
-
- `i18n`
|
|
72
|
-
|
|
73
|
-
locale persistence,
|
|
74
|
-
|
|
75
|
-
prompt generator.
|
|
71
|
+
- `i18n` extends the Starter's built-in translation provider and shared System
|
|
72
|
+
Settings bootstrap with selected server-generated namespaces such as
|
|
73
|
+
`lm-collections`, user locale persistence, the language switcher, and the
|
|
74
|
+
lazy-loaded showcase with its prompt generator.
|
|
76
75
|
|
|
77
76
|
The AI integration is available through four incremental entries:
|
|
78
77
|
|
|
@@ -109,6 +108,12 @@ complete item through Shadcn:
|
|
|
109
108
|
pnpm dlx shadcn@latest add @nocobase/ai
|
|
110
109
|
```
|
|
111
110
|
|
|
111
|
+
Install the optional NocoBase i18n integration with:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pnpm dlx shadcn@latest add @nocobase/i18n
|
|
115
|
+
```
|
|
116
|
+
|
|
112
117
|
Shadcn does not keep a Registry item lock or merge local changes. Re-running
|
|
113
118
|
`add` downloads the current item, skips identical files, and asks before
|
|
114
119
|
replacing different files. Use `--diff` to inspect an update and `--overwrite`
|
package/package.json
CHANGED
|
@@ -64,8 +64,8 @@ function ChatMessageComponent({
|
|
|
64
64
|
const workContext = message.metadata?.workContext ?? [];
|
|
65
65
|
if (isUser) {
|
|
66
66
|
return (
|
|
67
|
-
<article className="group/message flex flex-col items-end px-4 py-2 sm:px-5">
|
|
68
|
-
<div className="flex max-w-[80%] flex-col items-end">
|
|
67
|
+
<article className="group/message flex min-w-0 max-w-full flex-col items-end px-4 py-2 sm:px-5">
|
|
68
|
+
<div className="flex min-w-0 max-w-[80%] flex-col items-end">
|
|
69
69
|
{workContext.length ? (
|
|
70
70
|
<div className="mb-1.5 flex max-w-full flex-wrap justify-end gap-1.5">
|
|
71
71
|
{workContext.map((item, index) => (
|
|
@@ -85,8 +85,10 @@ function ChatMessageComponent({
|
|
|
85
85
|
</div>
|
|
86
86
|
) : null}
|
|
87
87
|
{text ? (
|
|
88
|
-
<div className="rounded-2xl rounded-br-md bg-secondary px-4 py-2.5 text-sm leading-6 text-secondary-foreground">
|
|
89
|
-
<div className="whitespace-pre-wrap
|
|
88
|
+
<div className="min-w-0 max-w-full rounded-2xl rounded-br-md bg-secondary px-4 py-2.5 text-sm leading-6 text-secondary-foreground">
|
|
89
|
+
<div className="whitespace-pre-wrap [overflow-wrap:anywhere]">
|
|
90
|
+
{text}
|
|
91
|
+
</div>
|
|
90
92
|
</div>
|
|
91
93
|
) : null}
|
|
92
94
|
</div>
|
|
@@ -150,7 +152,7 @@ function ChatMessageComponent({
|
|
|
150
152
|
);
|
|
151
153
|
|
|
152
154
|
return (
|
|
153
|
-
<article className="group/message px-4 py-2 sm:px-5">
|
|
155
|
+
<article className="group/message min-w-0 max-w-full px-4 py-2 sm:px-5">
|
|
154
156
|
<div className="min-w-0 space-y-4">
|
|
155
157
|
{assistantParts.map((part, index) => {
|
|
156
158
|
if (part.type === "reasoning") {
|
|
@@ -167,7 +169,7 @@ function ChatMessageComponent({
|
|
|
167
169
|
return (
|
|
168
170
|
<div
|
|
169
171
|
key={`text-${index}`}
|
|
170
|
-
className="ai-markdown text-sm leading-6 text-foreground"
|
|
172
|
+
className="ai-markdown min-w-0 max-w-full [overflow-wrap:anywhere] text-sm leading-6 text-foreground"
|
|
171
173
|
>
|
|
172
174
|
<MarkdownMessage>{part.text}</MarkdownMessage>
|
|
173
175
|
</div>
|
|
@@ -94,12 +94,17 @@ export function AIChatMessageList({
|
|
|
94
94
|
}, [atBottom, messages, status]);
|
|
95
95
|
|
|
96
96
|
return (
|
|
97
|
-
<div
|
|
97
|
+
<div
|
|
98
|
+
className={cn(
|
|
99
|
+
"relative min-h-0 min-w-0 flex-1 overflow-hidden bg-background",
|
|
100
|
+
className
|
|
101
|
+
)}
|
|
102
|
+
>
|
|
98
103
|
<div
|
|
99
104
|
ref={viewportRef}
|
|
100
105
|
role="log"
|
|
101
106
|
aria-live="polite"
|
|
102
|
-
className="absolute inset-0 overflow-y-auto overscroll-contain"
|
|
107
|
+
className="absolute inset-0 overflow-x-hidden overflow-y-auto overscroll-contain"
|
|
103
108
|
onScroll={(event) => {
|
|
104
109
|
const element = event.currentTarget;
|
|
105
110
|
setAtBottom(
|
|
@@ -112,7 +117,7 @@ export function AIChatMessageList({
|
|
|
112
117
|
) : (
|
|
113
118
|
<>
|
|
114
119
|
{messages.length ? (
|
|
115
|
-
<div className="mx-auto w-full max-w-3xl py-2">
|
|
120
|
+
<div className="mx-auto min-w-0 w-full max-w-3xl py-2">
|
|
116
121
|
{messages.map((message) => (
|
|
117
122
|
<ChatMessage
|
|
118
123
|
key={message.id}
|
|
@@ -9,6 +9,22 @@ type MarkdownMessageProps = {
|
|
|
9
9
|
variant?: "chat" | "document";
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
function formatStandaloneJson(content: string) {
|
|
13
|
+
const trimmed = content.trim();
|
|
14
|
+
const isObject = trimmed.startsWith("{") && trimmed.endsWith("}");
|
|
15
|
+
const isArray = trimmed.startsWith("[") && trimmed.endsWith("]");
|
|
16
|
+
|
|
17
|
+
if (!isObject && !isArray) return null;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const value: unknown = JSON.parse(trimmed);
|
|
21
|
+
if (value === null || typeof value !== "object") return null;
|
|
22
|
+
return JSON.stringify(value, null, 2);
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
12
28
|
function createMarkdownComponents(variant: MarkdownMessageProps["variant"]): Components {
|
|
13
29
|
const document = variant === "document";
|
|
14
30
|
|
|
@@ -154,6 +170,16 @@ export function MarkdownMessage({
|
|
|
154
170
|
children,
|
|
155
171
|
variant = "chat",
|
|
156
172
|
}: MarkdownMessageProps) {
|
|
173
|
+
const formattedJson = formatStandaloneJson(children);
|
|
174
|
+
|
|
175
|
+
if (formattedJson) {
|
|
176
|
+
return (
|
|
177
|
+
<pre className="my-4 max-w-full overflow-x-auto rounded-lg bg-muted p-4 text-sm leading-6">
|
|
178
|
+
<code className="font-mono">{formattedJson}</code>
|
|
179
|
+
</pre>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
157
183
|
return (
|
|
158
184
|
<ReactMarkdown
|
|
159
185
|
remarkPlugins={[remarkGfm]}
|
|
@@ -45,7 +45,7 @@ export function SubAgentConversation({
|
|
|
45
45
|
const interactionPending = status === "streaming" || status === "submitted";
|
|
46
46
|
|
|
47
47
|
return (
|
|
48
|
-
<section className="rounded-xl border border-dashed bg-muted/15">
|
|
48
|
+
<section className="min-w-0 max-w-full rounded-xl border border-dashed bg-muted/15">
|
|
49
49
|
<Button
|
|
50
50
|
type="button"
|
|
51
51
|
variant="ghost"
|
|
@@ -67,7 +67,7 @@ export function SubAgentConversation({
|
|
|
67
67
|
</span>
|
|
68
68
|
</Button>
|
|
69
69
|
{expanded ? (
|
|
70
|
-
<div className="space-y-3 border-t border-dashed px-3 py-3">
|
|
70
|
+
<div className="min-w-0 space-y-3 border-t border-dashed px-3 py-3">
|
|
71
71
|
{messages.flatMap((message) =>
|
|
72
72
|
message.parts.map((part, index) => {
|
|
73
73
|
if (part.type === "reasoning") {
|
|
@@ -84,7 +84,7 @@ export function SubAgentConversation({
|
|
|
84
84
|
return (
|
|
85
85
|
<div
|
|
86
86
|
key={`${message.id}-text-${index}`}
|
|
87
|
-
className="ai-markdown text-sm leading-6 text-foreground"
|
|
87
|
+
className="ai-markdown min-w-0 max-w-full [overflow-wrap:anywhere] text-sm leading-6 text-foreground"
|
|
88
88
|
>
|
|
89
89
|
<MarkdownMessage>{part.text}</MarkdownMessage>
|
|
90
90
|
</div>
|
|
@@ -1,49 +1,97 @@
|
|
|
1
|
-
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
|
2
|
-
import { Badge } from "@/components/ui/badge";
|
|
3
|
-
import { LoadingState } from "@/components/app-shell/loading-state";
|
|
4
|
-
import { useAI } from "../providers";
|
|
5
|
-
import { CircleAlert } from "lucide-react";
|
|
6
1
|
import type { ReactNode } from "react";
|
|
2
|
+
import { CircleAlert } from "lucide-react";
|
|
7
3
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
4
|
+
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
|
5
|
+
import {
|
|
6
|
+
AIProvider,
|
|
7
|
+
useAI,
|
|
8
|
+
type AIEmployee,
|
|
9
|
+
type AIModel,
|
|
10
|
+
} from "../providers";
|
|
11
|
+
import type { AIService } from "../services";
|
|
12
|
+
|
|
13
|
+
const previewEmployees: AIEmployee[] = [
|
|
14
|
+
{
|
|
15
|
+
username: "atlas",
|
|
16
|
+
nickname: "Atlas",
|
|
17
|
+
position: "General assistant",
|
|
18
|
+
greeting: "How can I help with this page?",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
username: "viz",
|
|
22
|
+
nickname: "Viz",
|
|
23
|
+
position: "Insights analyst",
|
|
24
|
+
greeting: "What would you like to explore?",
|
|
25
|
+
},
|
|
26
|
+
];
|
|
15
27
|
|
|
16
|
-
|
|
28
|
+
const previewModels: AIModel[] = [
|
|
29
|
+
{
|
|
30
|
+
value: "__preview__",
|
|
31
|
+
label: "Preview only",
|
|
32
|
+
configured: false,
|
|
33
|
+
},
|
|
34
|
+
];
|
|
17
35
|
|
|
18
|
-
|
|
19
|
-
|
|
36
|
+
const emptyStream = () =>
|
|
37
|
+
new ReadableStream<Uint8Array>({
|
|
38
|
+
start(controller) {
|
|
39
|
+
controller.close();
|
|
40
|
+
},
|
|
41
|
+
});
|
|
20
42
|
|
|
21
|
-
|
|
43
|
+
const previewService: AIService = {
|
|
44
|
+
listEmployees: async () => previewEmployees,
|
|
45
|
+
listModels: async () => previewModels,
|
|
46
|
+
updateEmployeeUserPrompt: async () => undefined,
|
|
47
|
+
listConversations: async () => [],
|
|
48
|
+
getConversationMessages: async () => [],
|
|
49
|
+
getConversationActiveState: async () => "idle",
|
|
50
|
+
updateConversationTitle: async () => undefined,
|
|
51
|
+
destroyConversation: async () => undefined,
|
|
52
|
+
uploadFile: async (file) => ({
|
|
53
|
+
id: crypto.randomUUID(),
|
|
54
|
+
filename: file.name,
|
|
55
|
+
mimetype: file.type,
|
|
56
|
+
size: file.size,
|
|
57
|
+
}),
|
|
58
|
+
createConversation: async () => `preview-${crypto.randomUUID()}`,
|
|
59
|
+
sendMessagesStream: async () => emptyStream(),
|
|
60
|
+
resendMessagesStream: async () => emptyStream(),
|
|
61
|
+
updateToolCallDecision: async () => ({ updated: 0, toolCalls: [] }),
|
|
62
|
+
resumeToolCallStream: async () => emptyStream(),
|
|
63
|
+
resumeConversationStream: async () => emptyStream(),
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export function AIConfigurationGate({ children }: { children: ReactNode }) {
|
|
67
|
+
const { configurationStatus, hasEnabledModels, employees } = useAI();
|
|
68
|
+
const configured = configurationStatus === "ready" && hasEnabledModels;
|
|
69
|
+
|
|
70
|
+
if (configured) return children;
|
|
71
|
+
|
|
72
|
+
const content = employees.length ? (
|
|
73
|
+
children
|
|
74
|
+
) : (
|
|
75
|
+
<AIProvider
|
|
76
|
+
employees={previewEmployees}
|
|
77
|
+
models={previewModels}
|
|
78
|
+
service={previewService}
|
|
79
|
+
>
|
|
80
|
+
{children}
|
|
81
|
+
</AIProvider>
|
|
82
|
+
);
|
|
22
83
|
|
|
23
84
|
return (
|
|
24
|
-
<div className="space-y-6
|
|
25
|
-
<
|
|
26
|
-
<div className="flex items-center gap-2">
|
|
27
|
-
<Badge variant="secondary">AI Components</Badge>
|
|
28
|
-
<Badge variant="outline">Live API</Badge>
|
|
29
|
-
</div>
|
|
30
|
-
<h1 className="mt-4 text-3xl font-semibold tracking-[-0.035em]">
|
|
31
|
-
NocoBase AI
|
|
32
|
-
</h1>
|
|
33
|
-
<p className="mt-3 max-w-3xl text-sm leading-6 text-muted-foreground">
|
|
34
|
-
Conversations use the AI employees and enabled models configured in
|
|
35
|
-
the connected NocoBase application.
|
|
36
|
-
</p>
|
|
37
|
-
</section>
|
|
38
|
-
|
|
39
|
-
<Alert variant="destructive">
|
|
85
|
+
<div className="space-y-6">
|
|
86
|
+
<Alert>
|
|
40
87
|
<CircleAlert />
|
|
41
|
-
<AlertTitle>
|
|
88
|
+
<AlertTitle>Preview mode</AlertTitle>
|
|
42
89
|
<AlertDescription>
|
|
43
|
-
|
|
44
|
-
|
|
90
|
+
Component examples remain available. Sending messages requires an
|
|
91
|
+
enabled AI model in the connected NocoBase application.
|
|
45
92
|
</AlertDescription>
|
|
46
93
|
</Alert>
|
|
94
|
+
{content}
|
|
47
95
|
</div>
|
|
48
96
|
);
|
|
49
97
|
}
|
|
@@ -1,43 +1,37 @@
|
|
|
1
1
|
# NocoBase i18n
|
|
2
2
|
|
|
3
|
-
Optional
|
|
3
|
+
Optional NocoBase server-language integration and language controls for the
|
|
4
|
+
NocoBase Admin Starter.
|
|
4
5
|
|
|
5
|
-
The
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
requiring a second Registry item.
|
|
6
|
+
The Starter owns the Refine i18n provider, local i18next runtime, application
|
|
7
|
+
resources, locale selection, and the shared `systemSettings:get` bootstrap.
|
|
8
|
+
This Registry reuses those foundations and adds only the NocoBase-specific
|
|
9
|
+
remote capabilities:
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
- load registered dynamic namespaces from `app:getLang`;
|
|
12
|
+
- persist a signed-in user's selected language through `users:updateLang`;
|
|
13
|
+
- expose reusable page and user-menu language switchers;
|
|
14
|
+
- provide an integrated Demo and Prompt generator.
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
`
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
lazy features.
|
|
22
|
-
|
|
23
|
-
On NocoBase versions that support filtered language resources, the request uses
|
|
24
|
-
`app:getLang?ns=lm-collections,...`. Older servers ignore the query parameter;
|
|
25
|
-
the client still merges only registered namespaces, so the integration remains
|
|
26
|
-
backward compatible.
|
|
16
|
+
When this Registry is not installed, the Starter still uses the system default
|
|
17
|
+
language and all local translations normally. It does not request
|
|
18
|
+
`app:getLang`. When installed, the Registry reads the already-cached system
|
|
19
|
+
settings and requests only registered server namespaces. `lm-collections` is
|
|
20
|
+
registered by default for collection and field metadata.
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
Application-owned React translations belong in `src/locales`, outside the
|
|
23
|
+
installed Registry directory:
|
|
30
24
|
|
|
31
25
|
```ts
|
|
32
|
-
import { registerLocaleResources } from "@/
|
|
26
|
+
import { registerLocaleResources } from "@/providers/i18n";
|
|
33
27
|
|
|
34
|
-
registerLocaleResources("my-
|
|
28
|
+
registerLocaleResources("my-feature", {
|
|
35
29
|
"en-US": { title: "Orders" },
|
|
36
30
|
"zh-CN": { title: "订单" },
|
|
37
31
|
});
|
|
38
32
|
```
|
|
39
33
|
|
|
40
|
-
|
|
34
|
+
Other installed components can opt into another server-generated namespace:
|
|
41
35
|
|
|
42
36
|
```ts
|
|
43
37
|
import { registerServerResourceNamespace } from "@/extensions/nocobase-i18n";
|
|
@@ -45,9 +39,6 @@ import { registerServerResourceNamespace } from "@/extensions/nocobase-i18n";
|
|
|
45
39
|
registerServerResourceNamespace("my-dynamic-namespace");
|
|
46
40
|
```
|
|
47
41
|
|
|
48
|
-
Namespaces registered after
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
Use the application's `useTranslate` hook in React components. Existing exact
|
|
52
|
-
NocoBase-style expressions such as `{{t("Orders")}}` and expressions with a
|
|
53
|
-
string namespace are resolved through the Starter compatibility helper.
|
|
42
|
+
Namespaces registered after startup are loaded incrementally. Existing exact
|
|
43
|
+
NocoBase expressions such as `{{t("Orders")}}` remain supported by the
|
|
44
|
+
Starter's translation compatibility helper.
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
SelectValue,
|
|
19
19
|
} from "@/components/ui/select";
|
|
20
20
|
import { cn } from "@/lib/utils";
|
|
21
|
-
import { useEnabledLocales } from "
|
|
21
|
+
import { useEnabledLocales } from "@/providers/i18n";
|
|
22
22
|
|
|
23
23
|
export type LanguageSwitcherProps = {
|
|
24
24
|
className?: string;
|
|
@@ -37,7 +37,7 @@ export function I18nDemoPage() {
|
|
|
37
37
|
{t(
|
|
38
38
|
"demo.description",
|
|
39
39
|
{ ns: "nocobase-i18n" },
|
|
40
|
-
"
|
|
40
|
+
"Optional NocoBase server translations and language controls built on the Starter's internationalization runtime."
|
|
41
41
|
)}
|
|
42
42
|
</p>
|
|
43
43
|
</header>
|
|
@@ -98,7 +98,7 @@ export function I18nDemoPage() {
|
|
|
98
98
|
description={t(
|
|
99
99
|
"demo.frontend.description",
|
|
100
100
|
{ ns: "nocobase-i18n" },
|
|
101
|
-
"Starter and Registry components keep their
|
|
101
|
+
"The Starter provides the runtime while application and Registry components keep their messages in local resource files."
|
|
102
102
|
)}
|
|
103
103
|
>
|
|
104
104
|
<div className="divide-y rounded-lg border">
|
|
@@ -30,12 +30,13 @@ Application scene
|
|
|
30
30
|
- Translate the complete visible user flow, including empty, loading, validation, and error states.
|
|
31
31
|
|
|
32
32
|
Implementation contract
|
|
33
|
-
- Use the
|
|
33
|
+
- Use the Starter's @/providers/i18n runtime and the application's translation and locale hooks.
|
|
34
34
|
- Put application-owned React messages in src/locales and register the app namespace through registerTranslationResources from @/lib/i18n.
|
|
35
35
|
- Keep each Registry component's messages in its own namespace and register them with registerLocaleResources().
|
|
36
36
|
- Do not place application messages inside src/extensions/nocobase-i18n/locales because installed Registry files may be refreshed independently.
|
|
37
37
|
- Use the reusable LanguageSwitcher when the page needs an explicit language control; the signed-in user menu already includes one.
|
|
38
|
-
-
|
|
38
|
+
- Reuse the Starter's shared system settings instead of requesting systemSettings:get again.
|
|
39
|
+
- Keep Starter and Registry UI resources local. Use app:getLang only for registered dynamic server namespaces such as lm-collections when the NocoBase i18n Registry is installed.
|
|
39
40
|
- Let the shared NocoBase client send the selected locale through X-Locale.
|
|
40
41
|
- Preserve compatibility when server metadata contains exact {{t("...")}} expressions.
|
|
41
42
|
- Provide complete React components, locale resource files, route/resource integration, and realistic visible content.`,
|
|
@@ -4,8 +4,8 @@ import { Languages } from "lucide-react";
|
|
|
4
4
|
import { lazy, Suspense } from "react";
|
|
5
5
|
import { Route } from "react-router";
|
|
6
6
|
import { LanguageUserMenuItems } from "./components";
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
7
|
+
import "./locales";
|
|
8
|
+
import { NocoBaseI18nBootstrap } from "./provider";
|
|
9
9
|
|
|
10
10
|
const I18nDemoPage = lazy(() =>
|
|
11
11
|
import("./demo").then((module) => ({ default: module.I18nDemoPage }))
|
|
@@ -13,8 +13,7 @@ const I18nDemoPage = lazy(() =>
|
|
|
13
13
|
|
|
14
14
|
const nocobaseI18nExtension: AppExtension = {
|
|
15
15
|
id: "nocobase-i18n",
|
|
16
|
-
|
|
17
|
-
Provider: LocalePreferenceProvider,
|
|
16
|
+
Provider: NocoBaseI18nBootstrap,
|
|
18
17
|
UserMenuItems: LanguageUserMenuItems,
|
|
19
18
|
resources: [
|
|
20
19
|
{
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * from "./components";
|
|
2
|
-
export * from "./i18n-provider";
|
|
3
|
-
export * from "./locale-store";
|
|
4
2
|
export * from "./provider";
|
|
5
|
-
export * from "./runtime";
|
|
6
3
|
export * from "./server-resources";
|
|
4
|
+
export {
|
|
5
|
+
registerLocaleResources,
|
|
6
|
+
useEnabledLocales,
|
|
7
|
+
} from "@/providers/i18n";
|
|
@@ -1,53 +1,4 @@
|
|
|
1
1
|
const enUS = {
|
|
2
|
-
starter: {
|
|
3
|
-
"shell.workspace": "AI application workspace",
|
|
4
|
-
"shell.footer.freedom": "AI builds freely.",
|
|
5
|
-
"shell.footer.reliability": "NocoBase keeps it reliable.",
|
|
6
|
-
"auth.signOut": "Sign out",
|
|
7
|
-
"auth.signingOut": "Signing out...",
|
|
8
|
-
"buttons.apply": "Apply",
|
|
9
|
-
"buttons.clear": "Clear",
|
|
10
|
-
"buttons.undo": "Undo",
|
|
11
|
-
"pages.error.title": "Page not found.",
|
|
12
|
-
"pages.error.description": "The page you're looking for does not exist.",
|
|
13
|
-
"pages.error.backHome": "Back to homepage",
|
|
14
|
-
"table.filter.text.placeholder": "Filter by...",
|
|
15
|
-
"table.filter.numeric.placeholder": "Filter by...",
|
|
16
|
-
"table.filter.combobox.placeholder": "Select...",
|
|
17
|
-
"table.filter.combobox.search": "Search...",
|
|
18
|
-
"table.filter.combobox.noResults": "Results not found.",
|
|
19
|
-
"table.filter.operator.placeholder": "Search operator...",
|
|
20
|
-
"table.filter.operator.noResults": "No operator found.",
|
|
21
|
-
"table.filter.operator.eq": "Equals",
|
|
22
|
-
"table.filter.operator.ne": "Not equals",
|
|
23
|
-
"table.filter.operator.lt": "Less than",
|
|
24
|
-
"table.filter.operator.gt": "Greater than",
|
|
25
|
-
"table.filter.operator.lte": "Less than or equal",
|
|
26
|
-
"table.filter.operator.gte": "Greater than or equal",
|
|
27
|
-
"table.filter.operator.in": "Includes in an array",
|
|
28
|
-
"table.filter.operator.nin": "Not includes in an array",
|
|
29
|
-
"table.filter.operator.ina": "Includes in an array (case sensitive)",
|
|
30
|
-
"table.filter.operator.nina": "Not includes in an array (case sensitive)",
|
|
31
|
-
"table.filter.operator.contains": "Contains",
|
|
32
|
-
"table.filter.operator.ncontains": "Not contains",
|
|
33
|
-
"table.filter.operator.containss": "Contains (case sensitive)",
|
|
34
|
-
"table.filter.operator.ncontainss": "Not contains (case sensitive)",
|
|
35
|
-
"table.filter.operator.between": "Between",
|
|
36
|
-
"table.filter.operator.nbetween": "Not between",
|
|
37
|
-
"table.filter.operator.null": "Is null",
|
|
38
|
-
"table.filter.operator.nnull": "Is not null",
|
|
39
|
-
"table.filter.operator.startswith": "Starts with",
|
|
40
|
-
"table.filter.operator.nstartswith": "Not starts with",
|
|
41
|
-
"table.filter.operator.startswiths": "Starts with (case sensitive)",
|
|
42
|
-
"table.filter.operator.nstartswiths": "Not starts with (case sensitive)",
|
|
43
|
-
"table.filter.operator.endswith": "Ends with",
|
|
44
|
-
"table.filter.operator.nendswith": "Not ends with",
|
|
45
|
-
"table.filter.operator.endswiths": "Ends with (case sensitive)",
|
|
46
|
-
"table.filter.operator.nendswiths": "Not ends with (case sensitive)",
|
|
47
|
-
"table.filter.operator.eqs": "Equals (case sensitive)",
|
|
48
|
-
"table.filter.operator.nes": "Not equals (case sensitive)",
|
|
49
|
-
},
|
|
50
|
-
"nocobase-i18n": {
|
|
51
2
|
"language.label": "Language",
|
|
52
3
|
"language.current": "Current language",
|
|
53
4
|
"language.switchError": "Unable to switch language.",
|
|
@@ -55,7 +6,7 @@ const enUS = {
|
|
|
55
6
|
"navigation.demo": "Internationalization",
|
|
56
7
|
"demo.title": "Internationalization",
|
|
57
8
|
"demo.description":
|
|
58
|
-
"
|
|
9
|
+
"Optional NocoBase server translations and language controls built on the Starter's internationalization runtime.",
|
|
59
10
|
"demo.switcher.title": "Language switcher",
|
|
60
11
|
"demo.switcher.description":
|
|
61
12
|
"The same reusable component is also integrated into the signed-in user menu.",
|
|
@@ -64,7 +15,7 @@ const enUS = {
|
|
|
64
15
|
"Enable another language in NocoBase system settings to display the switcher.",
|
|
65
16
|
"demo.frontend.title": "Application UI translations",
|
|
66
17
|
"demo.frontend.description":
|
|
67
|
-
"Starter and Registry components keep their
|
|
18
|
+
"The Starter provides the runtime while application and Registry components keep their messages in local resource files.",
|
|
68
19
|
"demo.frontend.actions": "Common actions",
|
|
69
20
|
"demo.server.title": "NocoBase metadata translations",
|
|
70
21
|
"demo.server.description":
|
|
@@ -83,7 +34,6 @@ const enUS = {
|
|
|
83
34
|
"demo.prompt.output": "Generated prompt",
|
|
84
35
|
"demo.prompt.outputDescription":
|
|
85
36
|
"Updates as you change the scene and language requirements.",
|
|
86
|
-
},
|
|
87
37
|
} as const;
|
|
88
38
|
|
|
89
39
|
export default enUS;
|
|
@@ -1,53 +1,4 @@
|
|
|
1
1
|
const zhCN = {
|
|
2
|
-
starter: {
|
|
3
|
-
"shell.workspace": "AI 应用工作区",
|
|
4
|
-
"shell.footer.freedom": "AI 自由构建。",
|
|
5
|
-
"shell.footer.reliability": "NocoBase 保障可靠运行。",
|
|
6
|
-
"auth.signOut": "退出登录",
|
|
7
|
-
"auth.signingOut": "正在退出...",
|
|
8
|
-
"buttons.apply": "应用",
|
|
9
|
-
"buttons.clear": "清除",
|
|
10
|
-
"buttons.undo": "撤销",
|
|
11
|
-
"pages.error.title": "页面不存在",
|
|
12
|
-
"pages.error.description": "你访问的页面不存在。",
|
|
13
|
-
"pages.error.backHome": "返回首页",
|
|
14
|
-
"table.filter.text.placeholder": "输入筛选内容...",
|
|
15
|
-
"table.filter.numeric.placeholder": "输入筛选数值...",
|
|
16
|
-
"table.filter.combobox.placeholder": "请选择...",
|
|
17
|
-
"table.filter.combobox.search": "搜索...",
|
|
18
|
-
"table.filter.combobox.noResults": "没有找到结果。",
|
|
19
|
-
"table.filter.operator.placeholder": "搜索操作符...",
|
|
20
|
-
"table.filter.operator.noResults": "没有找到操作符。",
|
|
21
|
-
"table.filter.operator.eq": "等于",
|
|
22
|
-
"table.filter.operator.ne": "不等于",
|
|
23
|
-
"table.filter.operator.lt": "小于",
|
|
24
|
-
"table.filter.operator.gt": "大于",
|
|
25
|
-
"table.filter.operator.lte": "小于或等于",
|
|
26
|
-
"table.filter.operator.gte": "大于或等于",
|
|
27
|
-
"table.filter.operator.in": "包含于数组",
|
|
28
|
-
"table.filter.operator.nin": "不包含于数组",
|
|
29
|
-
"table.filter.operator.ina": "包含于数组(区分大小写)",
|
|
30
|
-
"table.filter.operator.nina": "不包含于数组(区分大小写)",
|
|
31
|
-
"table.filter.operator.contains": "包含",
|
|
32
|
-
"table.filter.operator.ncontains": "不包含",
|
|
33
|
-
"table.filter.operator.containss": "包含(区分大小写)",
|
|
34
|
-
"table.filter.operator.ncontainss": "不包含(区分大小写)",
|
|
35
|
-
"table.filter.operator.between": "介于",
|
|
36
|
-
"table.filter.operator.nbetween": "不介于",
|
|
37
|
-
"table.filter.operator.null": "为空",
|
|
38
|
-
"table.filter.operator.nnull": "不为空",
|
|
39
|
-
"table.filter.operator.startswith": "开头是",
|
|
40
|
-
"table.filter.operator.nstartswith": "开头不是",
|
|
41
|
-
"table.filter.operator.startswiths": "开头是(区分大小写)",
|
|
42
|
-
"table.filter.operator.nstartswiths": "开头不是(区分大小写)",
|
|
43
|
-
"table.filter.operator.endswith": "结尾是",
|
|
44
|
-
"table.filter.operator.nendswith": "结尾不是",
|
|
45
|
-
"table.filter.operator.endswiths": "结尾是(区分大小写)",
|
|
46
|
-
"table.filter.operator.nendswiths": "结尾不是(区分大小写)",
|
|
47
|
-
"table.filter.operator.eqs": "等于(区分大小写)",
|
|
48
|
-
"table.filter.operator.nes": "不等于(区分大小写)",
|
|
49
|
-
},
|
|
50
|
-
"nocobase-i18n": {
|
|
51
2
|
"language.label": "语言",
|
|
52
3
|
"language.current": "当前语言",
|
|
53
4
|
"language.switchError": "无法切换语言。",
|
|
@@ -55,7 +6,7 @@ const zhCN = {
|
|
|
55
6
|
"navigation.demo": "国际化",
|
|
56
7
|
"demo.title": "国际化",
|
|
57
8
|
"demo.description":
|
|
58
|
-
"
|
|
9
|
+
"基于 Starter 国际化运行时的可选 NocoBase 服务端翻译与语言切换能力。",
|
|
59
10
|
"demo.switcher.title": "语言切换器",
|
|
60
11
|
"demo.switcher.description": "同一个可复用组件也会集成到登录用户菜单中。",
|
|
61
12
|
"demo.switcher.unavailable.title": "当前仅启用一种语言",
|
|
@@ -63,7 +14,7 @@ const zhCN = {
|
|
|
63
14
|
"请在 NocoBase 系统设置中启用其他语言,语言切换器才会显示。",
|
|
64
15
|
"demo.frontend.title": "应用 UI 文案翻译",
|
|
65
16
|
"demo.frontend.description":
|
|
66
|
-
"Starter
|
|
17
|
+
"Starter 提供运行时,应用和 Registry 组件分别维护自己的本地语言资源。",
|
|
67
18
|
"demo.frontend.actions": "常用操作",
|
|
68
19
|
"demo.server.title": "NocoBase 元数据翻译",
|
|
69
20
|
"demo.server.description":
|
|
@@ -79,7 +30,6 @@ const zhCN = {
|
|
|
79
30
|
"demo.prompt.copyError": "无法访问剪贴板,请手动选中并复制提示词。",
|
|
80
31
|
"demo.prompt.output": "生成的提示词",
|
|
81
32
|
"demo.prompt.outputDescription": "修改场景和语言要求时会实时更新。",
|
|
82
|
-
},
|
|
83
33
|
} as const;
|
|
84
34
|
|
|
85
35
|
export default zhCN;
|