@12-apps/mcp 3.5.1 → 3.7.0
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/ADOPTING.md +69 -1
- package/dist/chunk-FBUSSQSK.js +22 -0
- package/dist/chunk-FBUSSQSK.js.map +1 -0
- package/dist/{chunk-FYEVBTDU.js → chunk-HRAQKMDC.js} +12 -20
- package/dist/chunk-HRAQKMDC.js.map +1 -0
- package/dist/{guide-DV5MQbCg.d.ts → guide-KQNcXlMG.d.ts} +32 -17
- package/dist/index.d.ts +2 -1
- package/dist/index.js +11 -7
- package/dist/index.js.map +1 -1
- package/dist/oauth/index.d.ts +1 -1
- package/dist/oauth/index.js +1 -1
- package/dist/pt-BR-e1RnV2v8.d.ts +25 -0
- package/dist/react/index.d.ts +208 -30
- package/dist/react/index.js +341 -151
- package/dist/react/index.js.map +1 -1
- package/package.json +4 -4
- package/src/guide.ts +33 -165
- package/src/index.ts +8 -3
- package/src/pt-BR.ts +195 -0
- package/src/react/ai-capabilities.tsx +10 -6
- package/src/react/ai-connection-utils.ts +18 -13
- package/src/react/ai-flow-steps.tsx +63 -43
- package/src/react/ai-landing.tsx +47 -21
- package/src/react/ai-onboarding.tsx +31 -25
- package/src/react/ai-status-board.tsx +31 -16
- package/src/react/ai-steps.tsx +60 -23
- package/src/react/copy.ts +165 -0
- package/src/react/host-connect-guide.tsx +24 -8
- package/src/react/host-select-step.tsx +4 -1
- package/src/react/index.ts +21 -3
- package/src/react/pt-BR.ts +114 -0
- package/dist/chunk-FYEVBTDU.js.map +0 -1
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import type { AiStatusBoardCopy } from "./copy";
|
|
3
5
|
import AddLinkOutlinedIcon from "@mui/icons-material/AddLinkOutlined";
|
|
4
6
|
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
|
|
5
7
|
import LinkOffOutlinedIcon from "@mui/icons-material/LinkOffOutlined";
|
|
6
8
|
import MenuBookOutlinedIcon from "@mui/icons-material/MenuBookOutlined";
|
|
7
9
|
|
|
8
|
-
import {
|
|
10
|
+
import { createConfirmButton } from "@12-apps/ui/feedback/ConfirmAction";
|
|
9
11
|
import { Button } from "@12-apps/ui/form/Button";
|
|
10
12
|
import { Box } from "@12-apps/ui/mui/Box";
|
|
11
13
|
import { Stack } from "@12-apps/ui/mui/Stack";
|
|
@@ -33,7 +35,7 @@ export interface HostStatus {
|
|
|
33
35
|
* The right-hand side of a CONNECTED box: the green pill, then the two things
|
|
34
36
|
* the pill alone left the owner unable to do.
|
|
35
37
|
*
|
|
36
|
-
* "
|
|
38
|
+
* "{copy.instructions}" exists because the setup steps were reachable ONLY from a RED
|
|
37
39
|
* card's "Conectar" — so on an all-green board there was no way to re-read how
|
|
38
40
|
* any of them was connected, which is exactly when someone re-doing the setup on
|
|
39
41
|
* a second machine needs them. It re-enters the same flow that button does.
|
|
@@ -45,11 +47,20 @@ function ConnectedControls({
|
|
|
45
47
|
host,
|
|
46
48
|
onConnect,
|
|
47
49
|
onDisconnect,
|
|
50
|
+
copy,
|
|
48
51
|
}: {
|
|
49
52
|
host: AiHostGuide;
|
|
50
53
|
onConnect: (hostId: string) => void;
|
|
51
54
|
onDisconnect?: DisconnectHandler;
|
|
55
|
+
copy: AiStatusBoardCopy;
|
|
52
56
|
}): React.JSX.Element {
|
|
57
|
+
// Built from the copy rather than at module scope: `createConfirmButton`
|
|
58
|
+
// takes the words, and they only arrive as a prop. Memoised so the wrapped
|
|
59
|
+
// component keeps its identity across renders and React does not remount it.
|
|
60
|
+
const DisconnectButton = useMemo(
|
|
61
|
+
() => createConfirmButton(copy.confirmAction, copy.disconnectError),
|
|
62
|
+
[copy.confirmAction, copy.disconnectError],
|
|
63
|
+
);
|
|
53
64
|
return (
|
|
54
65
|
<Stack
|
|
55
66
|
direction="row"
|
|
@@ -70,7 +81,7 @@ function ConnectedControls({
|
|
|
70
81
|
}}
|
|
71
82
|
>
|
|
72
83
|
<Text variant="caption" weight="bold" as="span">
|
|
73
|
-
|
|
84
|
+
{copy.connected}
|
|
74
85
|
</Text>
|
|
75
86
|
</Box>
|
|
76
87
|
<Button
|
|
@@ -80,27 +91,27 @@ function ConnectedControls({
|
|
|
80
91
|
data-testid={`ai-status-instructions-${host.id}`}
|
|
81
92
|
>
|
|
82
93
|
<MenuBookOutlinedIcon sx={{ fontSize: 16, mr: 0.5 }} />
|
|
83
|
-
|
|
94
|
+
{copy.instructions}
|
|
84
95
|
</Button>
|
|
85
96
|
{onDisconnect ? (
|
|
86
|
-
<
|
|
97
|
+
<DisconnectButton
|
|
87
98
|
variant="outline"
|
|
88
99
|
color="danger"
|
|
89
100
|
size="sm"
|
|
90
101
|
onClick={() => onDisconnect(host.id)}
|
|
91
102
|
confirm={{
|
|
92
|
-
title:
|
|
103
|
+
title: copy.disconnectTitle,
|
|
93
104
|
entityName: host.label,
|
|
94
105
|
description:
|
|
95
|
-
|
|
96
|
-
confirmText:
|
|
106
|
+
copy.disconnectBody,
|
|
107
|
+
confirmText: copy.disconnectConfirm,
|
|
97
108
|
dataTestId: `ai-status-disconnect-confirm-${host.id}`,
|
|
98
109
|
}}
|
|
99
110
|
data-testid={`ai-status-disconnect-${host.id}`}
|
|
100
111
|
>
|
|
101
112
|
<LinkOffOutlinedIcon sx={{ fontSize: 16, mr: 0.5 }} />
|
|
102
|
-
|
|
103
|
-
</
|
|
113
|
+
{copy.disconnect}
|
|
114
|
+
</DisconnectButton>
|
|
104
115
|
) : null}
|
|
105
116
|
</Stack>
|
|
106
117
|
);
|
|
@@ -111,10 +122,12 @@ function StatusBox({
|
|
|
111
122
|
status,
|
|
112
123
|
onConnect,
|
|
113
124
|
onDisconnect,
|
|
125
|
+
copy,
|
|
114
126
|
}: {
|
|
115
127
|
status: HostStatus;
|
|
116
128
|
onConnect: (hostId: string) => void;
|
|
117
129
|
onDisconnect?: DisconnectHandler;
|
|
130
|
+
copy: AiStatusBoardCopy;
|
|
118
131
|
}): React.JSX.Element {
|
|
119
132
|
const { host, connected, detail } = status;
|
|
120
133
|
return (
|
|
@@ -141,11 +154,11 @@ function StatusBox({
|
|
|
141
154
|
{connected ? <CheckCircleIcon sx={{ fontSize: 18, color: "success.main" }} /> : null}
|
|
142
155
|
</Stack>
|
|
143
156
|
<Text variant="caption" as="p" color="secondary">
|
|
144
|
-
{connected ? (detail ??
|
|
157
|
+
{connected ? (detail ?? copy.connected) : copy.notConnected}
|
|
145
158
|
</Text>
|
|
146
159
|
</Box>
|
|
147
160
|
{connected ? (
|
|
148
|
-
<ConnectedControls host={host} onConnect={onConnect} onDisconnect={onDisconnect} />
|
|
161
|
+
<ConnectedControls copy={copy} host={host} onConnect={onConnect} onDisconnect={onDisconnect} />
|
|
149
162
|
) : (
|
|
150
163
|
<Button
|
|
151
164
|
variant="outline"
|
|
@@ -169,7 +182,7 @@ function StatusBox({
|
|
|
169
182
|
* `announceAiConnection`), so several assistants can show connected at once.
|
|
170
183
|
*
|
|
171
184
|
* A connected box carries two more controls, because the pill it used to show
|
|
172
|
-
* alone was a dead end in both directions: "
|
|
185
|
+
* alone was a dead end in both directions: "{copy.instructions}" re-enters the host's
|
|
173
186
|
* setup steps (previously reachable only from a RED card, so an all-green board
|
|
174
187
|
* hid them entirely), and "Desconectar" — when the app passes `onDisconnect` —
|
|
175
188
|
* revokes access, which nothing in the UI could do at all.
|
|
@@ -178,20 +191,22 @@ export function AiStatusBoard({
|
|
|
178
191
|
statuses,
|
|
179
192
|
onConnect,
|
|
180
193
|
onDisconnect,
|
|
194
|
+
copy,
|
|
181
195
|
}: {
|
|
182
196
|
statuses: readonly HostStatus[];
|
|
183
197
|
onConnect: (hostId: string) => void;
|
|
184
198
|
/** Revoke this host's access. Omit to render a read-only board. */
|
|
185
199
|
onDisconnect?: DisconnectHandler;
|
|
200
|
+
copy: AiStatusBoardCopy;
|
|
186
201
|
}): React.JSX.Element {
|
|
187
202
|
return (
|
|
188
203
|
<Stack spacing={2} data-testid="ai-status-board">
|
|
189
204
|
<Box>
|
|
190
205
|
<Text variant="heading" size="sm" as="h2">
|
|
191
|
-
|
|
206
|
+
{copy.boardTitle}
|
|
192
207
|
</Text>
|
|
193
208
|
<Text variant="caption" as="p" color="secondary">
|
|
194
|
-
|
|
209
|
+
{copy.boardCaption}
|
|
195
210
|
</Text>
|
|
196
211
|
</Box>
|
|
197
212
|
<Box
|
|
@@ -202,7 +217,7 @@ export function AiStatusBoard({
|
|
|
202
217
|
}}
|
|
203
218
|
>
|
|
204
219
|
{statuses.map((status) => (
|
|
205
|
-
<StatusBox
|
|
220
|
+
<StatusBox copy={copy}
|
|
206
221
|
key={status.host.id}
|
|
207
222
|
status={status}
|
|
208
223
|
onConnect={onConnect}
|
package/src/react/ai-steps.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import type { McpAiCopy } from "./copy";
|
|
3
4
|
import { type GuidedNav, type GuidedStep } from "@12-apps/onboarding";
|
|
4
5
|
|
|
5
6
|
import { providerForHostId, type AiHostGuide } from "../guide";
|
|
@@ -24,11 +25,13 @@ export function StatusBoard({
|
|
|
24
25
|
connections,
|
|
25
26
|
hosts,
|
|
26
27
|
onDisconnect,
|
|
28
|
+
copy,
|
|
27
29
|
}: {
|
|
28
30
|
nav: GuidedNav;
|
|
29
31
|
connections: readonly AiConnection[];
|
|
30
32
|
hosts: readonly AiHostGuide[];
|
|
31
33
|
onDisconnect?: DisconnectHandler;
|
|
34
|
+
copy: McpAiCopy;
|
|
32
35
|
}): React.JSX.Element {
|
|
33
36
|
const legacyHostId = (nav.data.connectedHost ?? nav.data.selectedHost) as string | undefined;
|
|
34
37
|
|
|
@@ -43,7 +46,7 @@ export function StatusBoard({
|
|
|
43
46
|
return {
|
|
44
47
|
host,
|
|
45
48
|
connected: connection !== null,
|
|
46
|
-
detail: connection ? activeAgo(connection.lastActiveAt) : undefined,
|
|
49
|
+
detail: connection ? activeAgo(connection.lastActiveAt, copy.summary) : undefined,
|
|
47
50
|
};
|
|
48
51
|
});
|
|
49
52
|
|
|
@@ -78,19 +81,20 @@ export function StatusBoard({
|
|
|
78
81
|
: undefined;
|
|
79
82
|
|
|
80
83
|
return (
|
|
81
|
-
<AiStatusBoard statuses={statuses} onConnect={goToConnect} onDisconnect={disconnect} />
|
|
84
|
+
<AiStatusBoard copy={copy.statusBoard} statuses={statuses} onConnect={goToConnect} onDisconnect={disconnect} />
|
|
82
85
|
);
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
/** The first step (host picker) — picking a card advances to the right path. */
|
|
86
|
-
function selectStep(hosts: readonly AiHostGuide[]): GuidedStep {
|
|
89
|
+
function selectStep(hosts: readonly AiHostGuide[], copy: McpAiCopy): GuidedStep {
|
|
87
90
|
return {
|
|
88
91
|
id: "select",
|
|
89
|
-
label:
|
|
92
|
+
label: copy.flow.steps.select,
|
|
90
93
|
render: (nav: GuidedNav) => (
|
|
91
94
|
<HostSelectStep
|
|
92
95
|
hosts={hosts}
|
|
93
96
|
selectedId={(nav.data.selectedHost as string | undefined) ?? null}
|
|
97
|
+
copy={copy.hostSelect}
|
|
94
98
|
onSelect={(hostId) =>
|
|
95
99
|
nav.goTo(hosts.find((h) => h.id === hostId)?.pluginUrl ? "install" : "copy", {
|
|
96
100
|
selectedHost: hostId,
|
|
@@ -102,11 +106,11 @@ function selectStep(hosts: readonly AiHostGuide[]): GuidedStep {
|
|
|
102
106
|
}
|
|
103
107
|
|
|
104
108
|
/** The Copiar-URL step, shared by every manual flow. */
|
|
105
|
-
function copyStep(endpointUrl: string): GuidedStep {
|
|
109
|
+
function copyStep(endpointUrl: string, copy: McpAiCopy): GuidedStep {
|
|
106
110
|
return {
|
|
107
111
|
id: "copy",
|
|
108
|
-
label:
|
|
109
|
-
render: (nav: GuidedNav) => <CopyUrlStep nav={nav} endpointUrl={endpointUrl} />,
|
|
112
|
+
label: copy.flow.steps.copyUrl,
|
|
113
|
+
render: (nav: GuidedNav) => <CopyUrlStep copy={copy} nav={nav} endpointUrl={endpointUrl} />,
|
|
110
114
|
};
|
|
111
115
|
}
|
|
112
116
|
|
|
@@ -120,44 +124,70 @@ function manualMiddle(
|
|
|
120
124
|
hosts: readonly AiHostGuide[],
|
|
121
125
|
endpointUrl: string,
|
|
122
126
|
connectPrompt: string,
|
|
127
|
+
copy: McpAiCopy,
|
|
123
128
|
): GuidedStep[] {
|
|
124
129
|
if (host.configureStages && host.configureStages.length > 0) {
|
|
125
130
|
return [
|
|
126
|
-
copyStep(endpointUrl),
|
|
131
|
+
copyStep(endpointUrl, copy),
|
|
127
132
|
...host.configureStages.map((stage) => ({
|
|
128
133
|
id: stage.id,
|
|
129
134
|
label: stage.label,
|
|
130
135
|
render: (nav: GuidedNav) => (
|
|
131
|
-
<ConfigureStageStep
|
|
136
|
+
<ConfigureStageStep
|
|
137
|
+
nav={nav}
|
|
138
|
+
host={resolveHost(hosts, nav.data.selectedHost)}
|
|
139
|
+
stage={stage}
|
|
140
|
+
copy={copy}
|
|
141
|
+
/>
|
|
132
142
|
),
|
|
133
143
|
})),
|
|
134
144
|
];
|
|
135
145
|
}
|
|
136
146
|
return [
|
|
137
|
-
copyStep(endpointUrl),
|
|
147
|
+
copyStep(endpointUrl, copy),
|
|
138
148
|
{
|
|
139
149
|
id: "configure",
|
|
140
|
-
label:
|
|
141
|
-
render: (nav: GuidedNav) =>
|
|
150
|
+
label: copy.flow.steps.configure,
|
|
151
|
+
render: (nav: GuidedNav) => (
|
|
152
|
+
<ConfigureStep
|
|
153
|
+
nav={nav}
|
|
154
|
+
host={resolveHost(hosts, nav.data.selectedHost)}
|
|
155
|
+
copy={copy}
|
|
156
|
+
/>
|
|
157
|
+
),
|
|
142
158
|
},
|
|
143
159
|
{
|
|
144
160
|
id: "connect",
|
|
145
|
-
label:
|
|
161
|
+
label: copy.flow.steps.connect,
|
|
146
162
|
render: (nav: GuidedNav) => (
|
|
147
|
-
<ConnectStep
|
|
163
|
+
<ConnectStep
|
|
164
|
+
nav={nav}
|
|
165
|
+
host={resolveHost(hosts, nav.data.selectedHost)}
|
|
166
|
+
connectPrompt={connectPrompt}
|
|
167
|
+
copy={copy}
|
|
168
|
+
/>
|
|
148
169
|
),
|
|
149
170
|
},
|
|
150
171
|
];
|
|
151
172
|
}
|
|
152
173
|
|
|
153
174
|
/** The single middle step for the simplified plugin flow: install. */
|
|
154
|
-
function simpleMiddle(
|
|
175
|
+
function simpleMiddle(
|
|
176
|
+
hosts: readonly AiHostGuide[],
|
|
177
|
+
connectPrompt: string,
|
|
178
|
+
copy: McpAiCopy,
|
|
179
|
+
): GuidedStep[] {
|
|
155
180
|
return [
|
|
156
181
|
{
|
|
157
182
|
id: "install",
|
|
158
|
-
label:
|
|
183
|
+
label: copy.flow.steps.install,
|
|
159
184
|
render: (nav: GuidedNav) => (
|
|
160
|
-
<InstallStep
|
|
185
|
+
<InstallStep
|
|
186
|
+
nav={nav}
|
|
187
|
+
host={resolveHost(hosts, nav.data.selectedHost)}
|
|
188
|
+
connectPrompt={connectPrompt}
|
|
189
|
+
copy={copy}
|
|
190
|
+
/>
|
|
161
191
|
),
|
|
162
192
|
},
|
|
163
193
|
];
|
|
@@ -176,17 +206,24 @@ export function buildFlowSteps(opts: {
|
|
|
176
206
|
connectPrompt: string;
|
|
177
207
|
connections: readonly AiConnection[];
|
|
178
208
|
onRetest: () => void;
|
|
209
|
+
copy: McpAiCopy;
|
|
179
210
|
}): GuidedStep[] {
|
|
180
|
-
const { host, hosts, endpointUrl, connectPrompt, connections, onRetest } = opts;
|
|
211
|
+
const { host, hosts, endpointUrl, connectPrompt, connections, onRetest, copy } = opts;
|
|
181
212
|
const middle = host.pluginUrl
|
|
182
|
-
? simpleMiddle(hosts, connectPrompt)
|
|
183
|
-
: manualMiddle(host, hosts, endpointUrl, connectPrompt);
|
|
213
|
+
? simpleMiddle(hosts, connectPrompt, copy)
|
|
214
|
+
: manualMiddle(host, hosts, endpointUrl, connectPrompt, copy);
|
|
184
215
|
const confirm: GuidedStep = {
|
|
185
216
|
id: "confirm",
|
|
186
|
-
label:
|
|
217
|
+
label: copy.flow.steps.confirm,
|
|
187
218
|
render: (nav: GuidedNav) => (
|
|
188
|
-
<ConfirmStep
|
|
219
|
+
<ConfirmStep
|
|
220
|
+
nav={nav}
|
|
221
|
+
connections={connections}
|
|
222
|
+
hosts={hosts}
|
|
223
|
+
onRetest={onRetest}
|
|
224
|
+
copy={copy}
|
|
225
|
+
/>
|
|
189
226
|
),
|
|
190
227
|
};
|
|
191
|
-
return [selectStep(hosts), ...middle, confirm];
|
|
228
|
+
return [selectStep(hosts, copy), ...middle, confirm];
|
|
192
229
|
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { ConfirmActionCopy } from "@12-apps/ui/copy";
|
|
2
|
+
/**
|
|
3
|
+
* Every word the AI-integration screens render, as REQUIRED host config
|
|
4
|
+
* (FUT-760).
|
|
5
|
+
*
|
|
6
|
+
* These components used to take `capabilities` / `permissionModel` / `hosts` as
|
|
7
|
+
* OPTIONAL props defaulting to this package's own pt-BR, and to compile the rest
|
|
8
|
+
* of their sentences in outright. `guide.ts` even said so — "these are the
|
|
9
|
+
* shared defaults" — which is the anti-pattern named exactly: a host that
|
|
10
|
+
* configured nothing shipped one product's Portuguese, and nothing failed to
|
|
11
|
+
* say so.
|
|
12
|
+
*
|
|
13
|
+
* Split per screen rather than one flat map, because three of them
|
|
14
|
+
* (`AiLanding`, `AiCapabilities`, `AiStatusBoard`, `HostSelectStep`) are
|
|
15
|
+
* exported standalone and a host mounting only one should not have to supply
|
|
16
|
+
* words for screens it never renders.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** The marketing block's headline and its supporting line. */
|
|
20
|
+
export interface AiCapabilitiesCopy {
|
|
21
|
+
heading: string;
|
|
22
|
+
subheading: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** One reassurance chip on the landing: an icon's label and its caption. */
|
|
26
|
+
export interface AiTrustPoint {
|
|
27
|
+
/** Stable id — maps to the icon in the component. */
|
|
28
|
+
id: string;
|
|
29
|
+
label: string;
|
|
30
|
+
caption: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface AiLandingCopy {
|
|
34
|
+
/** Small line above the headline. */
|
|
35
|
+
eyebrow: string;
|
|
36
|
+
/** The headline's second half, after the assistant names. */
|
|
37
|
+
titleTail: string;
|
|
38
|
+
/** The paragraph under the headline. */
|
|
39
|
+
lede: string;
|
|
40
|
+
/** The reassurance strip. Order is the order rendered. */
|
|
41
|
+
trust: readonly AiTrustPoint[];
|
|
42
|
+
/** The call to action that starts the guided flow. */
|
|
43
|
+
start: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The wizard's step labels, in the order the stepper shows them. */
|
|
47
|
+
export interface AiFlowStepLabels {
|
|
48
|
+
select: string;
|
|
49
|
+
copyUrl: string;
|
|
50
|
+
configure: string;
|
|
51
|
+
connect: string;
|
|
52
|
+
install: string;
|
|
53
|
+
confirm: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The wizard's navigation words and each step's own sentences. */
|
|
57
|
+
export interface AiFlowCopy {
|
|
58
|
+
/** The stepper's labels. Not sentences, but still the host's words. */
|
|
59
|
+
steps: AiFlowStepLabels;
|
|
60
|
+
back: string;
|
|
61
|
+
next: string;
|
|
62
|
+
advance: string;
|
|
63
|
+
finish: string;
|
|
64
|
+
copyUrl: string;
|
|
65
|
+
copied: string;
|
|
66
|
+
copyMessage: string;
|
|
67
|
+
/** The copy-the-URL step's heading. Names the host's own noun for a tenant. */
|
|
68
|
+
copyUrlTitle: string;
|
|
69
|
+
/** Under the MCP URL: what this value is and what happens on copy. */
|
|
70
|
+
urlCaption: string;
|
|
71
|
+
/** Under the paste-in prompt: what the assistant will do with it. */
|
|
72
|
+
promptCaption: string;
|
|
73
|
+
/** The "ask the assistant to connect" step heading. */
|
|
74
|
+
askTitle: string;
|
|
75
|
+
/** The one-click install step's body, for a host with a published plugin. */
|
|
76
|
+
installBody: string;
|
|
77
|
+
/** The install button on that step. */
|
|
78
|
+
installAction: string;
|
|
79
|
+
/** Heading over the paste-in message on the manual path. */
|
|
80
|
+
pasteTitle: string;
|
|
81
|
+
/** Caption under the paste-in message on the install path. */
|
|
82
|
+
pasteInstallCaption: string;
|
|
83
|
+
/** Confirmation once a host reports in. */
|
|
84
|
+
connectedTo(hostLabel: string): string;
|
|
85
|
+
waitingTitle: string;
|
|
86
|
+
waitingBody(hostLabel: string): string;
|
|
87
|
+
testNow: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface AiStatusBoardCopy {
|
|
91
|
+
instructions: string;
|
|
92
|
+
connected: string;
|
|
93
|
+
notConnected: string;
|
|
94
|
+
connect: string;
|
|
95
|
+
disconnect: string;
|
|
96
|
+
disconnectTitle: string;
|
|
97
|
+
disconnectBody: string;
|
|
98
|
+
disconnectConfirm: string;
|
|
99
|
+
/** Shown when the disconnect write fails without a reason of its own. */
|
|
100
|
+
disconnectError: string;
|
|
101
|
+
boardTitle: string;
|
|
102
|
+
boardCaption: string;
|
|
103
|
+
/**
|
|
104
|
+
* The confirmation popup's own furniture — its cancel button and fallbacks.
|
|
105
|
+
* `@12-apps/ui` stopped shipping defaults for these (FUT-760), so the words
|
|
106
|
+
* arrive here rather than from the design system's Portuguese.
|
|
107
|
+
*/
|
|
108
|
+
confirmAction: ConfirmActionCopy;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The one-line summary of a connection's recency.
|
|
113
|
+
*
|
|
114
|
+
* Functions rather than strings because the number is interpolated and the
|
|
115
|
+
* plural rule is the host language's, not this package's.
|
|
116
|
+
*/
|
|
117
|
+
export interface AiConnectionSummaryCopy {
|
|
118
|
+
/** Active within the last minute — no number to interpolate. */
|
|
119
|
+
activeNow: string;
|
|
120
|
+
activeMinutes(minutes: number): string;
|
|
121
|
+
activeHours(hours: number): string;
|
|
122
|
+
activeDays(days: number): string;
|
|
123
|
+
/** Connected, but too long ago to state a recency for. */
|
|
124
|
+
configured: string;
|
|
125
|
+
/** Appended after a host's name: `Claude Conectado`. */
|
|
126
|
+
connectedSuffix: string;
|
|
127
|
+
/** Several assistants at once, from an already-joined list of names. */
|
|
128
|
+
connectedSeveral(names: string): string;
|
|
129
|
+
/** Connected, but the provider was never attributed to a known assistant. */
|
|
130
|
+
connectedGeneric: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface AiHostSelectCopy {
|
|
134
|
+
/** Why the owner is picking, under the host tiles. */
|
|
135
|
+
caption: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface AiConnectGuideCopy {
|
|
139
|
+
/** The label on the MCP URL field. Names the host's own noun for a tenant. */
|
|
140
|
+
urlLabel: string;
|
|
141
|
+
/** Above the MCP URL field. */
|
|
142
|
+
urlHint: string;
|
|
143
|
+
/** Lead-in to the vendor's official connector docs link. */
|
|
144
|
+
moreInfo: string;
|
|
145
|
+
connectOn(hostLabel: string): string;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface AiOnboardingCopy {
|
|
149
|
+
title: string;
|
|
150
|
+
/** The reveal toggle in the configured state, and its collapsed twin. */
|
|
151
|
+
editLabel: string;
|
|
152
|
+
collapseLabel: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Every screen's words, for a host mounting the whole flow. */
|
|
156
|
+
export interface McpAiCopy {
|
|
157
|
+
capabilities: AiCapabilitiesCopy;
|
|
158
|
+
landing: AiLandingCopy;
|
|
159
|
+
flow: AiFlowCopy;
|
|
160
|
+
statusBoard: AiStatusBoardCopy;
|
|
161
|
+
summary: AiConnectionSummaryCopy;
|
|
162
|
+
hostSelect: AiHostSelectCopy;
|
|
163
|
+
connectGuide: AiConnectGuideCopy;
|
|
164
|
+
onboarding: AiOnboardingCopy;
|
|
165
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import type { AiConnectGuideCopy, AiFlowCopy } from "./copy";
|
|
3
4
|
import CheckIcon from "@mui/icons-material/Check";
|
|
4
5
|
import ContentCopyOutlinedIcon from "@mui/icons-material/ContentCopyOutlined";
|
|
5
6
|
import OpenInNewIcon from "@mui/icons-material/OpenInNew";
|
|
@@ -24,10 +25,14 @@ export function EndpointCopyBlock({
|
|
|
24
25
|
endpointUrl,
|
|
25
26
|
copied,
|
|
26
27
|
onCopy,
|
|
28
|
+
flowCopy,
|
|
29
|
+
copy,
|
|
27
30
|
}: {
|
|
28
31
|
endpointUrl: string;
|
|
29
32
|
copied: boolean;
|
|
30
33
|
onCopy: () => void;
|
|
34
|
+
flowCopy: AiFlowCopy;
|
|
35
|
+
copy: AiConnectGuideCopy;
|
|
31
36
|
}): React.JSX.Element {
|
|
32
37
|
const handleClick = (): void => {
|
|
33
38
|
if (typeof navigator !== "undefined" && navigator.clipboard) {
|
|
@@ -47,10 +52,10 @@ export function EndpointCopyBlock({
|
|
|
47
52
|
}}
|
|
48
53
|
>
|
|
49
54
|
<Text variant="caption" as="p" weight="bold">
|
|
50
|
-
|
|
55
|
+
{copy.urlLabel}
|
|
51
56
|
</Text>
|
|
52
57
|
<Text variant="caption" as="p" color="secondary">
|
|
53
|
-
|
|
58
|
+
{copy.urlHint}
|
|
54
59
|
</Text>
|
|
55
60
|
<Box sx={{ mt: 1 }}>
|
|
56
61
|
<McpEndpointUrl url={endpointUrl} copyable={false} />
|
|
@@ -62,7 +67,7 @@ export function EndpointCopyBlock({
|
|
|
62
67
|
) : (
|
|
63
68
|
<ContentCopyOutlinedIcon sx={{ fontSize: 18, mr: 0.5 }} />
|
|
64
69
|
)}
|
|
65
|
-
{copied ?
|
|
70
|
+
{copied ? flowCopy.copied : flowCopy.copyUrl}
|
|
66
71
|
</Button>
|
|
67
72
|
</Box>
|
|
68
73
|
</Box>
|
|
@@ -78,10 +83,12 @@ export function PromptCopyBlock({
|
|
|
78
83
|
title,
|
|
79
84
|
caption,
|
|
80
85
|
message,
|
|
86
|
+
flowCopy,
|
|
81
87
|
}: {
|
|
82
88
|
title: string;
|
|
83
89
|
caption: string;
|
|
84
90
|
message: string;
|
|
91
|
+
flowCopy: AiFlowCopy;
|
|
85
92
|
}): React.JSX.Element {
|
|
86
93
|
const [copied, setCopied] = useState(false);
|
|
87
94
|
|
|
@@ -130,7 +137,7 @@ export function PromptCopyBlock({
|
|
|
130
137
|
) : (
|
|
131
138
|
<ContentCopyOutlinedIcon sx={{ fontSize: 18, mr: 0.5 }} />
|
|
132
139
|
)}
|
|
133
|
-
{copied ?
|
|
140
|
+
{copied ? flowCopy.copied : flowCopy.copyMessage}
|
|
134
141
|
</Button>
|
|
135
142
|
</Box>
|
|
136
143
|
</Box>
|
|
@@ -138,12 +145,18 @@ export function PromptCopyBlock({
|
|
|
138
145
|
}
|
|
139
146
|
|
|
140
147
|
/** The brand mark + "Conectar no <host>" header shared by the connect sub-steps. */
|
|
141
|
-
export function HostConnectHeader({
|
|
148
|
+
export function HostConnectHeader({
|
|
149
|
+
host,
|
|
150
|
+
copy,
|
|
151
|
+
}: {
|
|
152
|
+
host: AiHostGuide;
|
|
153
|
+
copy: AiConnectGuideCopy;
|
|
154
|
+
}): React.JSX.Element {
|
|
142
155
|
return (
|
|
143
156
|
<Stack direction="row" spacing={1.5} alignItems="center">
|
|
144
157
|
<HostBrandAvatar brand={host.brand} size={36} />
|
|
145
158
|
<Text variant="heading" size="sm" as="h3">
|
|
146
|
-
|
|
159
|
+
{copy.connectOn(host.label)}
|
|
147
160
|
</Text>
|
|
148
161
|
</Stack>
|
|
149
162
|
);
|
|
@@ -179,11 +192,14 @@ export function HostOpenButton({
|
|
|
179
192
|
}
|
|
180
193
|
|
|
181
194
|
/** A "Para mais informações" reference to the host's official connector docs. */
|
|
182
|
-
export function HostDocsLink({ host
|
|
195
|
+
export function HostDocsLink({ host,
|
|
196
|
+
copy,
|
|
197
|
+
}: { host: AiHostGuide;
|
|
198
|
+
copy: AiConnectGuideCopy;}): React.JSX.Element | null {
|
|
183
199
|
if (!host.docs) return null;
|
|
184
200
|
return (
|
|
185
201
|
<Text variant="caption" as="p" color="secondary">
|
|
186
|
-
|
|
202
|
+
{copy.moreInfo}{" "}
|
|
187
203
|
<Box
|
|
188
204
|
component="a"
|
|
189
205
|
href={host.docs.url}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import type { AiHostSelectCopy } from "./copy";
|
|
3
4
|
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
|
|
4
5
|
|
|
5
6
|
import { Box } from "@12-apps/ui/mui/Box";
|
|
@@ -72,10 +73,12 @@ export function HostSelectStep({
|
|
|
72
73
|
hosts,
|
|
73
74
|
selectedId,
|
|
74
75
|
onSelect,
|
|
76
|
+
copy,
|
|
75
77
|
}: {
|
|
76
78
|
hosts: readonly AiHostGuide[];
|
|
77
79
|
selectedId: string | null;
|
|
78
80
|
onSelect: (hostId: string) => void;
|
|
81
|
+
copy: AiHostSelectCopy;
|
|
79
82
|
}): React.JSX.Element {
|
|
80
83
|
return (
|
|
81
84
|
<Stack spacing={3} data-testid="ai-host-select">
|
|
@@ -84,7 +87,7 @@ export function HostSelectStep({
|
|
|
84
87
|
Escolha o seu assistente
|
|
85
88
|
</Text>
|
|
86
89
|
<Text variant="caption" as="p" color="secondary">
|
|
87
|
-
|
|
90
|
+
{copy.caption}
|
|
88
91
|
</Text>
|
|
89
92
|
</Box>
|
|
90
93
|
|
package/src/react/index.ts
CHANGED
|
@@ -29,9 +29,6 @@ export { McpEndpointUrl } from "./mcp-endpoint-url";
|
|
|
29
29
|
export { HostBrandAvatar, CapabilityIcon } from "./ai-icons";
|
|
30
30
|
export { FeatureBadge, type FeatureBadgeItem } from "./feature-badge";
|
|
31
31
|
export {
|
|
32
|
-
aiHostGuides,
|
|
33
|
-
AI_CAPABILITIES,
|
|
34
|
-
AI_PERMISSION_MODEL,
|
|
35
32
|
aiConnectPrompt,
|
|
36
33
|
type AiConnectPromptSpec,
|
|
37
34
|
providerForHostId,
|
|
@@ -42,3 +39,24 @@ export {
|
|
|
42
39
|
type AiProvider,
|
|
43
40
|
type AiCapability,
|
|
44
41
|
} from "../guide";
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
PT_BR_AI_CAPABILITIES,
|
|
45
|
+
PT_BR_AI_CONNECT_PROMPT,
|
|
46
|
+
PT_BR_AI_HOST_GUIDES,
|
|
47
|
+
PT_BR_AI_PERMISSION_MODEL,
|
|
48
|
+
} from "../pt-BR";
|
|
49
|
+
|
|
50
|
+
export type {
|
|
51
|
+
AiCapabilitiesCopy,
|
|
52
|
+
AiConnectGuideCopy,
|
|
53
|
+
AiConnectionSummaryCopy,
|
|
54
|
+
AiFlowCopy,
|
|
55
|
+
AiHostSelectCopy,
|
|
56
|
+
AiLandingCopy,
|
|
57
|
+
AiOnboardingCopy,
|
|
58
|
+
AiStatusBoardCopy,
|
|
59
|
+
AiTrustPoint,
|
|
60
|
+
McpAiCopy,
|
|
61
|
+
} from "./copy";
|
|
62
|
+
export { PT_BR_MCP_AI_COPY } from "./pt-BR";
|