@12-apps/mcp 3.5.0 → 3.6.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 +197 -30
- package/dist/react/index.js +327 -147
- package/dist/react/index.js.map +1 -1
- package/package.json +6 -6
- 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 +30 -25
- package/src/react/ai-status-board.tsx +18 -11
- package/src/react/ai-steps.tsx +60 -23
- package/src/react/copy.ts +154 -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 +109 -0
- package/dist/chunk-FYEVBTDU.js.map +0 -1
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,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every word the AI-integration screens render, as REQUIRED host config
|
|
3
|
+
* (FUT-760).
|
|
4
|
+
*
|
|
5
|
+
* These components used to take `capabilities` / `permissionModel` / `hosts` as
|
|
6
|
+
* OPTIONAL props defaulting to this package's own pt-BR, and to compile the rest
|
|
7
|
+
* of their sentences in outright. `guide.ts` even said so — "these are the
|
|
8
|
+
* shared defaults" — which is the anti-pattern named exactly: a host that
|
|
9
|
+
* configured nothing shipped one product's Portuguese, and nothing failed to
|
|
10
|
+
* say so.
|
|
11
|
+
*
|
|
12
|
+
* Split per screen rather than one flat map, because three of them
|
|
13
|
+
* (`AiLanding`, `AiCapabilities`, `AiStatusBoard`, `HostSelectStep`) are
|
|
14
|
+
* exported standalone and a host mounting only one should not have to supply
|
|
15
|
+
* words for screens it never renders.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** The marketing block's headline and its supporting line. */
|
|
19
|
+
export interface AiCapabilitiesCopy {
|
|
20
|
+
heading: string;
|
|
21
|
+
subheading: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** One reassurance chip on the landing: an icon's label and its caption. */
|
|
25
|
+
export interface AiTrustPoint {
|
|
26
|
+
/** Stable id — maps to the icon in the component. */
|
|
27
|
+
id: string;
|
|
28
|
+
label: string;
|
|
29
|
+
caption: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface AiLandingCopy {
|
|
33
|
+
/** Small line above the headline. */
|
|
34
|
+
eyebrow: string;
|
|
35
|
+
/** The headline's second half, after the assistant names. */
|
|
36
|
+
titleTail: string;
|
|
37
|
+
/** The paragraph under the headline. */
|
|
38
|
+
lede: string;
|
|
39
|
+
/** The reassurance strip. Order is the order rendered. */
|
|
40
|
+
trust: readonly AiTrustPoint[];
|
|
41
|
+
/** The call to action that starts the guided flow. */
|
|
42
|
+
start: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The wizard's step labels, in the order the stepper shows them. */
|
|
46
|
+
export interface AiFlowStepLabels {
|
|
47
|
+
select: string;
|
|
48
|
+
copyUrl: string;
|
|
49
|
+
configure: string;
|
|
50
|
+
connect: string;
|
|
51
|
+
install: string;
|
|
52
|
+
confirm: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The wizard's navigation words and each step's own sentences. */
|
|
56
|
+
export interface AiFlowCopy {
|
|
57
|
+
/** The stepper's labels. Not sentences, but still the host's words. */
|
|
58
|
+
steps: AiFlowStepLabels;
|
|
59
|
+
back: string;
|
|
60
|
+
next: string;
|
|
61
|
+
advance: string;
|
|
62
|
+
finish: string;
|
|
63
|
+
copyUrl: string;
|
|
64
|
+
copied: string;
|
|
65
|
+
copyMessage: string;
|
|
66
|
+
/** The copy-the-URL step's heading. Names the host's own noun for a tenant. */
|
|
67
|
+
copyUrlTitle: string;
|
|
68
|
+
/** Under the MCP URL: what this value is and what happens on copy. */
|
|
69
|
+
urlCaption: string;
|
|
70
|
+
/** Under the paste-in prompt: what the assistant will do with it. */
|
|
71
|
+
promptCaption: string;
|
|
72
|
+
/** The "ask the assistant to connect" step heading. */
|
|
73
|
+
askTitle: string;
|
|
74
|
+
/** The one-click install step's body, for a host with a published plugin. */
|
|
75
|
+
installBody: string;
|
|
76
|
+
/** The install button on that step. */
|
|
77
|
+
installAction: string;
|
|
78
|
+
/** Heading over the paste-in message on the manual path. */
|
|
79
|
+
pasteTitle: string;
|
|
80
|
+
/** Caption under the paste-in message on the install path. */
|
|
81
|
+
pasteInstallCaption: string;
|
|
82
|
+
/** Confirmation once a host reports in. */
|
|
83
|
+
connectedTo(hostLabel: string): string;
|
|
84
|
+
waitingTitle: string;
|
|
85
|
+
waitingBody(hostLabel: string): string;
|
|
86
|
+
testNow: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface AiStatusBoardCopy {
|
|
90
|
+
instructions: string;
|
|
91
|
+
connected: string;
|
|
92
|
+
notConnected: string;
|
|
93
|
+
connect: string;
|
|
94
|
+
disconnect: string;
|
|
95
|
+
disconnectTitle: string;
|
|
96
|
+
disconnectBody: string;
|
|
97
|
+
disconnectConfirm: string;
|
|
98
|
+
boardTitle: string;
|
|
99
|
+
boardCaption: string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The one-line summary of a connection's recency.
|
|
104
|
+
*
|
|
105
|
+
* Functions rather than strings because the number is interpolated and the
|
|
106
|
+
* plural rule is the host language's, not this package's.
|
|
107
|
+
*/
|
|
108
|
+
export interface AiConnectionSummaryCopy {
|
|
109
|
+
/** Active within the last minute — no number to interpolate. */
|
|
110
|
+
activeNow: string;
|
|
111
|
+
activeMinutes(minutes: number): string;
|
|
112
|
+
activeHours(hours: number): string;
|
|
113
|
+
activeDays(days: number): string;
|
|
114
|
+
/** Connected, but too long ago to state a recency for. */
|
|
115
|
+
configured: string;
|
|
116
|
+
/** Appended after a host's name: `Claude Conectado`. */
|
|
117
|
+
connectedSuffix: string;
|
|
118
|
+
/** Several assistants at once, from an already-joined list of names. */
|
|
119
|
+
connectedSeveral(names: string): string;
|
|
120
|
+
/** Connected, but the provider was never attributed to a known assistant. */
|
|
121
|
+
connectedGeneric: string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface AiHostSelectCopy {
|
|
125
|
+
/** Why the owner is picking, under the host tiles. */
|
|
126
|
+
caption: string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface AiConnectGuideCopy {
|
|
130
|
+
/** The label on the MCP URL field. Names the host's own noun for a tenant. */
|
|
131
|
+
urlLabel: string;
|
|
132
|
+
/** Above the MCP URL field. */
|
|
133
|
+
urlHint: string;
|
|
134
|
+
/** Lead-in to the vendor's official connector docs link. */
|
|
135
|
+
moreInfo: string;
|
|
136
|
+
connectOn(hostLabel: string): string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface AiOnboardingCopy {
|
|
140
|
+
title: string;
|
|
141
|
+
editLabel: string;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Every screen's words, for a host mounting the whole flow. */
|
|
145
|
+
export interface McpAiCopy {
|
|
146
|
+
capabilities: AiCapabilitiesCopy;
|
|
147
|
+
landing: AiLandingCopy;
|
|
148
|
+
flow: AiFlowCopy;
|
|
149
|
+
statusBoard: AiStatusBoardCopy;
|
|
150
|
+
summary: AiConnectionSummaryCopy;
|
|
151
|
+
hostSelect: AiHostSelectCopy;
|
|
152
|
+
connectGuide: AiConnectGuideCopy;
|
|
153
|
+
onboarding: AiOnboardingCopy;
|
|
154
|
+
}
|
|
@@ -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";
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { McpAiCopy } from "./copy";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The pt-BR pack for the AI-integration screens — a NAMED constant a host
|
|
5
|
+
* passes by hand, never a default.
|
|
6
|
+
*
|
|
7
|
+
* The filename is what exempts this file from the copy-portability gate:
|
|
8
|
+
* Portuguese may ship, it may not be silent. Every sentence is VERBATIM what
|
|
9
|
+
* the components used to render, so a host adopting it sees no change on
|
|
10
|
+
* screen — what changes is that the words are chosen in a diff.
|
|
11
|
+
*/
|
|
12
|
+
export const PT_BR_MCP_AI_COPY: McpAiCopy = {
|
|
13
|
+
capabilities: {
|
|
14
|
+
heading: "O que o assistente faz por você",
|
|
15
|
+
subheading: "Sem planilhas, sem cliques — é só perguntar na conversa.",
|
|
16
|
+
},
|
|
17
|
+
landing: {
|
|
18
|
+
eyebrow: "Integração com IA",
|
|
19
|
+
titleTail: "à sua loja",
|
|
20
|
+
lede:
|
|
21
|
+
"Deixe o Claude, o ChatGPT e outros assistentes responderem sobre o seu cardápio, estoque e " +
|
|
22
|
+
"pedidos — e executarem ações por você, direto na conversa. Com segurança e sem instalar nada.",
|
|
23
|
+
trust: [
|
|
24
|
+
{
|
|
25
|
+
id: "login",
|
|
26
|
+
label: "Usa o seu próprio login",
|
|
27
|
+
caption: "Sem chaves ou credenciais extras",
|
|
28
|
+
},
|
|
29
|
+
{ id: "install", label: "Sem instalar nada", caption: "Conecta em poucos minutos" },
|
|
30
|
+
{
|
|
31
|
+
id: "permissions",
|
|
32
|
+
label: "Só o que você pode",
|
|
33
|
+
caption: "As suas permissões, nada além",
|
|
34
|
+
},
|
|
35
|
+
{ id: "surface", label: "Navegador ou app", caption: "Claude, ChatGPT, Codex" },
|
|
36
|
+
],
|
|
37
|
+
start: "Começar",
|
|
38
|
+
},
|
|
39
|
+
flow: {
|
|
40
|
+
steps: {
|
|
41
|
+
select: "Escolher",
|
|
42
|
+
copyUrl: "Copiar URL",
|
|
43
|
+
configure: "Configurar",
|
|
44
|
+
connect: "Conectar",
|
|
45
|
+
install: "Instalar",
|
|
46
|
+
confirm: "Confirmar",
|
|
47
|
+
},
|
|
48
|
+
back: "Voltar",
|
|
49
|
+
next: "Próximo",
|
|
50
|
+
advance: "Continuar",
|
|
51
|
+
finish: "Concluir",
|
|
52
|
+
copyUrl: "Copiar URL",
|
|
53
|
+
copied: "Copiado",
|
|
54
|
+
copyMessage: "Copiar mensagem",
|
|
55
|
+
copyUrlTitle: "Copie a URL da sua loja",
|
|
56
|
+
urlCaption:
|
|
57
|
+
"É o único dado que você cola no assistente — ao copiar, seguimos para o próximo passo.",
|
|
58
|
+
promptCaption:
|
|
59
|
+
"Assim ele se conecta, se identifica (Claude, ChatGPT…) e registramos a conexão.",
|
|
60
|
+
askTitle: "Peça ao assistente para conectar",
|
|
61
|
+
installBody:
|
|
62
|
+
"Abra o plugin da sua loja, clique em Instalar e autorize o acesso — sem copiar URL nem gerar credenciais.",
|
|
63
|
+
installAction: "Instalar o plugin da loja",
|
|
64
|
+
pasteTitle: "Cole esta mensagem no assistente",
|
|
65
|
+
pasteInstallCaption:
|
|
66
|
+
"Cole no assistente para ele se conectar, se identificar e confirmar o acesso.",
|
|
67
|
+
connectedTo: (hostLabel) => `${hostLabel} está conectado à sua loja.`,
|
|
68
|
+
waitingTitle: "Esperando conexão",
|
|
69
|
+
waitingBody: (hostLabel) =>
|
|
70
|
+
`Assim que você autorizar o acesso no ${hostLabel}, ela aparece aqui automaticamente.`,
|
|
71
|
+
testNow: "Testar agora",
|
|
72
|
+
},
|
|
73
|
+
statusBoard: {
|
|
74
|
+
instructions: "Instruções",
|
|
75
|
+
connected: "Conectado",
|
|
76
|
+
notConnected: "Ainda não conectado",
|
|
77
|
+
connect: "Conectar",
|
|
78
|
+
disconnect: "Desconectar",
|
|
79
|
+
disconnectTitle: "Desconectar o assistente?",
|
|
80
|
+
disconnectBody:
|
|
81
|
+
"Ele perde o acesso à sua loja na hora. Para voltar a usar, será preciso conectar de novo.",
|
|
82
|
+
disconnectConfirm: "Desconectar",
|
|
83
|
+
boardTitle: "Assistentes conectados",
|
|
84
|
+
boardCaption: "Em verde os que já operam a sua loja; em vermelho os que faltam conectar.",
|
|
85
|
+
},
|
|
86
|
+
summary: {
|
|
87
|
+
activeNow: "ativo agora",
|
|
88
|
+
activeMinutes: (minutes) => `ativo há ${minutes} min`,
|
|
89
|
+
activeHours: (hours) => `ativo há ${hours} h`,
|
|
90
|
+
activeDays: (days) => `ativo há ${days} dias`,
|
|
91
|
+
configured: "Integração configurada",
|
|
92
|
+
connectedSuffix: "Conectado",
|
|
93
|
+
connectedSeveral: (names) => `${names} conectados`,
|
|
94
|
+
connectedGeneric: "IA conectada",
|
|
95
|
+
},
|
|
96
|
+
hostSelect: {
|
|
97
|
+
caption: "Selecione onde você usa a IA — o passo a passo é feito sob medida para ele.",
|
|
98
|
+
},
|
|
99
|
+
connectGuide: {
|
|
100
|
+
urlLabel: "URL do servidor da sua loja",
|
|
101
|
+
urlHint: "Copie esta URL e cole no seu assistente para conectá-lo à loja.",
|
|
102
|
+
moreInfo: "Para mais informações, consulte a",
|
|
103
|
+
connectOn: (hostLabel) => `Conectar no ${hostLabel}`,
|
|
104
|
+
},
|
|
105
|
+
onboarding: {
|
|
106
|
+
title: "Conecte assistentes de IA à sua loja",
|
|
107
|
+
editLabel: "Conectar IA",
|
|
108
|
+
},
|
|
109
|
+
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/guide.ts"],"sourcesContent":["/**\n * AI-integration guide content — the reusable copy for a store's \"Integração com\n * IA\" onboarding. Pure, public, non-sensitive text (no React) so the on-page\n * walkthrough stays a portable, testable content module. Host brand marks/icons\n * and colours live in the components. Apps may pass their own overrides to\n * `<AiIntegrationOnboarding>`; these are the shared defaults.\n *\n * The MCP endpoint URL is NOT hardcoded here — the app derives it per request\n * from the deployment origin and passes it in.\n */\n\n/** Which brand a host belongs to — drives the icon/accent in the UI. */\nexport type AiHostBrand = \"claude\" | \"openai\";\n\nexport interface AiHostLink {\n /** Deep-link straight to the host's connector settings, when one exists. */\n url: string;\n label: string;\n}\n\n/**\n * One stage of the \"configure the connector\" part of the flow. A host can split\n * configuration across several stages, each with its OWN deep link, step label\n * and instructions — e.g. ChatGPT needs \"enable developer mode\" (Security\n * settings) and then \"configurar\" (create the connector). When a host declares\n * `configureStages`, the wizard renders one wizard step per stage (between\n * \"Copiar URL\" and \"Confirmar\") instead of the default Configurar/Conectar\n * split, and the sign-in happens inside a stage — no prompt to paste.\n */\nexport interface AiHostConfigureStage {\n /** Stable id — also the wizard step id + test id suffix (`ai-stage-${id}`). */\n id: string;\n /** Step label shown in the stepper. */\n label: string;\n /** Optional deep link opened on this stage (unlocks \"Próximo\" once opened). */\n link?: AiHostLink;\n /** The instructions shown for this stage. */\n steps: readonly string[];\n}\n\n/**\n * The AI providers a connection can be attributed to — derived server-side from\n * the OAuth client's redirect URIs (and confirmed by the `announceAiConnection`\n * tool). Drives which host card lights up green on the status board.\n */\nexport type AiProvider = \"claude\" | \"chatgpt\" | \"codex\";\n\n/** The provider a host id belongs to (claude-desktop shares Claude's provider). */\nexport function providerForHostId(hostId: string): AiProvider | null {\n if (hostId === \"claude\" || hostId === \"claude-desktop\") return \"claude\";\n if (hostId === \"chatgpt\") return \"chatgpt\";\n if (hostId === \"codex\") return \"codex\";\n return null;\n}\n\nexport interface AiHostGuide {\n /** Stable id — also the panel test id suffix (`ai-setup-${id}`). */\n id: string;\n /** Tab label shown to the owner. */\n label: string;\n brand: AiHostBrand;\n /** One-line hint of what this host is (web app, desktop app, CLI…). */\n kind: string;\n /** Optional direct link button (desktop apps have no deep link). */\n link?: AiHostLink;\n /**\n * Optional link to the host's OFFICIAL connector docs — a stable vendor URL\n * shown as a \"Para mais informações\" reference (never a deep link with a\n * volatile connector id / UI params).\n */\n docs?: AiHostLink;\n steps: readonly string[];\n /**\n * Optional per-stage configuration (each with its own deep link + label). When\n * present, the wizard renders one step per stage in place of the default\n * Configurar → Conectar split, and `steps` mirrors the flattened stage\n * instructions so the MCP guide stays a single source of truth.\n */\n configureStages?: readonly AiHostConfigureStage[];\n /**\n * Optional deep link to a PUBLISHED one-click plugin/connector for this host\n * (set per app via env, e.g. `CHATGPT_PLUGIN_URL` / `CLAUDE_PLUGIN_URL`). When\n * present, the wizard swaps the manual \"copy URL + configure connector\" path\n * for a simplified \"open → Install → authorize\" flow: the owner never copies\n * the MCP URL. Absent → the full manual flow.\n */\n pluginUrl?: string;\n}\n\nconst CONNECTOR_TAIL: readonly string[] = [\n \"Deixe OAuth Client ID e Client Secret em branco — não é preciso gerar credenciais: a loja registra o conector automaticamente no primeiro acesso.\",\n \"Confirme e clique em Connect: abre a tela de login da loja — entre com a SUA conta de lojista e autorize o acesso.\",\n \"Pronto: ative o conector na conversa para o assistente consultar e operar a sua loja.\",\n];\n\n/**\n * ChatGPT's two-stage configuration (Developer mode is now required before a\n * connector can be created). Stage 1 enables Developer mode in Security & login;\n * stage 2 creates the connector and signs in — which registers the connection on\n * the store side, so no prompt needs to be pasted afterwards.\n */\nfunction chatgptConfigureStages(\n platformName: string,\n): readonly AiHostConfigureStage[] {\n return [\n {\n id: \"enable-dev-mode\",\n label: \"enable developer mode\",\n link: {\n url: \"https://chatgpt.com/plugins#settings/Security\",\n label: \"Abrir Segurança e login\",\n },\n steps: [\n \"Ative o Modo desenvolvedor em Settings › Security and login (Segurança e login).\",\n ],\n },\n {\n id: \"configurar\",\n label: \"configurar\",\n link: {\n url: \"https://chatgpt.com/plugins#settings/Connectors?create-connector=true&redirectAfter=%2Fplugins\",\n label: \"Criar o conector\",\n },\n steps: [\n \"Isso vai abrir um popup para você criar um plugin novo. Coloque como nome o nome da sua loja e, no campo MCP, o link copiado no passo anterior.\",\n 'Marque a caixa \"I understand and want to continue\" — a OpenAI não revisou este servidor MCP; ela avisa que sites podem tentar roubar seus dados ou induzir o modelo a ações indevidas, incluindo destruir dados.',\n `Clique em \"Sign in with ${platformName}\" e entre com a sua conta de lojista para autorizar o acesso. Pronto: a conexão é registrada automaticamente.`,\n ],\n },\n ];\n}\n\n/**\n * The AI hosts a store owner can connect, in recommended order. Same OAuth flow\n * everywhere (the host drives it) — only the menu path differs per app.\n *\n * A FUNCTION of the platform's name, because one step is not generic: the\n * ChatGPT connector's consent screen shows an OAuth button labelled with\n * whoever operates the server, and the owner is told which button to click. It\n * used to name one particular STORE on one particular deployment — not even the\n * product, a tenant of it — so every other adopter instructed its owners to\n * click a button that does not exist.\n */\nexport function aiHostGuides(platformName: string): readonly AiHostGuide[] {\n const chatgptStages = chatgptConfigureStages(platformName);\n return [\n {\n id: \"claude\",\n label: \"Claude.ai\",\n brand: \"claude\",\n kind: \"No navegador\",\n link: {\n url: \"https://claude.ai/new?modal=add-custom-connector#settings/customize-connectors\",\n label: \"Abrir os conectores do Claude\",\n },\n docs: {\n url: \"https://support.anthropic.com/en/articles/11175166-how-do-i-connect-mcp-servers-to-claude-ai\",\n label: \"documentação oficial da Anthropic — conectores personalizados\",\n },\n steps: [\n \"Clique no botão acima (ou vá em Settings › Customize › Connectors) e escolha Add custom connector.\",\n \"Dê um nome ao conector (ex.: o nome da sua loja) e cole a URL do servidor MCP da sua loja (copie acima) no campo de URL.\",\n ...CONNECTOR_TAIL,\n ],\n },\n {\n id: \"claude-desktop\",\n label: \"Claude Desktop\",\n brand: \"claude\",\n kind: \"Aplicativo (Windows/Mac)\",\n docs: {\n url: \"https://support.anthropic.com/en/articles/11175166-how-do-i-connect-mcp-servers-to-claude-ai\",\n label: \"documentação oficial da Anthropic — conectores personalizados\",\n },\n steps: [\n \"Abra o Claude Desktop e vá em Settings (⚙️) › Connectors.\",\n \"Clique em Add custom connector e cole a URL do servidor MCP da sua loja (copie acima).\",\n ...CONNECTOR_TAIL,\n ],\n },\n {\n id: \"chatgpt\",\n label: \"ChatGPT\",\n brand: \"openai\",\n kind: \"No navegador\",\n link: {\n url: \"https://chatgpt.com/plugins\",\n label: \"Abrir os plugins do ChatGPT\",\n },\n docs: {\n url: \"https://developers.openai.com/apps-sdk/deploy/connect-chatgpt\",\n label:\n \"documentação oficial da OpenAI — conectar um servidor MCP ao ChatGPT\",\n },\n configureStages: chatgptStages,\n // Mirrors the flattened stage instructions so the MCP connect guide\n // (`connectToChatGpt`) can never drift from what owners see in the wizard.\n steps: chatgptStages.flatMap((stage) => stage.steps),\n },\n {\n id: \"codex\",\n label: \"Codex\",\n brand: \"openai\",\n kind: \"App / CLI de desenvolvedor\",\n link: {\n url: \"https://developers.openai.com/codex\",\n label: \"Documentação do Codex\",\n },\n docs: {\n url: \"https://developers.openai.com/apps-sdk/deploy/connect-chatgpt\",\n label: \"documentação oficial da OpenAI — conectar um servidor MCP\",\n },\n steps: [\n \"No Codex, abra as configurações de MCP (Settings › MCP no app, ou o arquivo de configuração na CLI).\",\n \"Adicione um servidor MCP e cole a URL do servidor MCP da sua loja (copie acima) como um conector remoto (HTTP).\",\n ...CONNECTOR_TAIL,\n ],\n },\n ];\n}\n\nexport interface AiCapability {\n /** Stable id — maps to an icon in the component. */\n id: string;\n /** Card headline. */\n title: string;\n /** Short supporting line, usually an example prompt in quotes. */\n detail: string;\n}\n\n/** What a connected assistant does for the owner — one marketing card each. */\nexport const AI_CAPABILITIES: readonly AiCapability[] = [\n {\n id: \"orders\",\n title: \"Acompanhe seus pedidos\",\n detail: '\"Quais pedidos entraram hoje?\"',\n },\n {\n id: \"inventory\",\n title: \"Controle o estoque\",\n detail:\n '\"Quanto ainda tenho do produto X? Registre a entrada de 20 unidades.\"',\n },\n {\n id: \"catalog\",\n title: \"Gerencie o catálogo\",\n detail: \"Crie e edite produtos e categorias conversando.\",\n },\n {\n id: \"sales\",\n title: \"Entenda suas vendas\",\n detail: '\"Qual foi o faturamento da semana?\"',\n },\n];\n\n/**\n * The permission model in one line, shown prominently: the assistant acts AS\n * the signed-in owner (auth-passthrough) — it can do exactly what the owner\n * can, nothing more, and no extra credential/API key is ever created.\n */\nexport const AI_PERMISSION_MODEL =\n \"O assistente age em seu nome, com exatamente as suas permissões: ele pode fazer o que você pode fazer na sua loja — nada além disso. Não é preciso criar nenhuma chave ou credencial extra; a autorização usa o seu próprio login.\";\n\n/** The two tools the paste-in prompt drives, and what to call the store id. */\nexport interface AiConnectPromptSpec {\n /**\n * The tool that REGISTERS the connection server-side, so the store learns\n * which assistant connected.\n */\n announceTool: string;\n /** A real READ tool, called straight after, to prove the access works. */\n probeTool: string;\n /** What that read returns, in the owner's own words (\"o estoque da loja\"). */\n probeSubject: string;\n /** What the assistant should ask for if it needs to identify the store. */\n identifierName: string;\n}\n\n/**\n * The message the owner pastes into the assistant's chat right after\n * connecting: announce the connection, then read something real to prove it\n * works. The owner types nothing.\n *\n * BUILT from the host's tool names rather than shipped with them. This was a\n * constant naming two tools — `announceAiConnection` and `listInventory` — that\n * THIS PACKAGE does not define or serve; they belong to one adopter's surface.\n * Any other host handed its owner a prompt instructing the assistant to call\n * two tools that do not exist, and because nothing registered the connection,\n * the wizard's confirm step then waited forever for a state that could never\n * arrive.\n */\nexport function aiConnectPrompt(spec: AiConnectPromptSpec): string {\n return (\n \"Você agora tem acesso ao conector MCP da minha loja. Faça, nesta ordem:\\n\" +\n `1) Execute a ferramenta ${spec.announceTool} informando qual assistente você é (host: \"chatgpt\", \"claude\" ou \"codex\") para registrar a conexão com a minha loja.\\n` +\n `2) Execute a ferramenta ${spec.probeTool} para confirmar o acesso a ${spec.probeSubject}.\\n` +\n `Se precisar do identificador da loja, me pergunte o ${spec.identifierName}.`\n );\n}\n"],"mappings":"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","names":[]}
|