paseo-form-render 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +115 -2
- package/client/container.tsx +289 -0
- package/client/host-markdown.tsx +24 -0
- package/client/markdown.tsx +470 -0
- package/client/messages.ts +147 -0
- package/client/project-file-modal.tsx +690 -0
- package/client/react-native-dataset.d.ts +10 -0
- package/client/renderer.tsx +1056 -0
- package/client/rpc-bridge.tsx +27 -0
- package/client/settings-screen.tsx +163 -0
- package/index.client.tsx +103 -0
- package/index.server.ts +38 -0
- package/package.json +50 -4
- package/paseo-plugin.json +8 -0
- package/server/auto-fix.ts +217 -0
- package/server/demo.ts +20 -0
- package/server/file-picker.ts +253 -0
- package/server/settings.ts +26 -0
- package/shared/demo.ts +96 -0
- package/shared/form-errors.ts +390 -0
- package/shared/form-fix.ts +77 -0
- package/shared/form-helpers.ts +214 -0
- package/shared/parser.ts +119 -0
- package/shared/pickers.ts +50 -0
- package/shared/prompt.ts +96 -0
- package/shared/schema.ts +330 -0
- package/shared/settings.ts +18 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
import type { PluginHostProps } from "@getpaseo/plugin/client";
|
|
2
|
+
import React, { useMemo, type ReactNode } from "react";
|
|
3
|
+
import { Platform, StyleSheet, Text, View } from "react-native";
|
|
4
|
+
|
|
5
|
+
interface MarkdownBlock {
|
|
6
|
+
type: "heading" | "paragraph" | "list-item" | "code" | "table";
|
|
7
|
+
text?: string;
|
|
8
|
+
level?: number;
|
|
9
|
+
bullet?: string;
|
|
10
|
+
code?: string;
|
|
11
|
+
lang?: string;
|
|
12
|
+
headers?: string[];
|
|
13
|
+
rows?: string[][];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function isTableSeparatorLine(line: string): boolean {
|
|
17
|
+
const trimmed = line.trim();
|
|
18
|
+
if (!trimmed.includes("|") || !trimmed.includes("-")) return false;
|
|
19
|
+
const cells = parseTableRow(trimmed);
|
|
20
|
+
if (cells.length === 0) return false;
|
|
21
|
+
return cells.every((c) => /^:?-{1,}:?$/.test(c.trim()));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseTableRow(line: string): string[] {
|
|
25
|
+
let trimmed = line.trim();
|
|
26
|
+
if (trimmed.startsWith("|")) trimmed = trimmed.slice(1);
|
|
27
|
+
if (trimmed.endsWith("|")) trimmed = trimmed.slice(0, -1);
|
|
28
|
+
return trimmed.split("|").map((cell) => cell.trim());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function parseBlocks(src: string): MarkdownBlock[] {
|
|
32
|
+
const lines = src.replace(/\r\n/g, "\n").split("\n");
|
|
33
|
+
const blocks: MarkdownBlock[] = [];
|
|
34
|
+
let inCode = false;
|
|
35
|
+
let codeLines: string[] = [];
|
|
36
|
+
let codeLang = "";
|
|
37
|
+
let paragraph: string[] = [];
|
|
38
|
+
|
|
39
|
+
const flushParagraph = () => {
|
|
40
|
+
if (paragraph.length > 0) {
|
|
41
|
+
blocks.push({ type: "paragraph", text: paragraph.join("\n") });
|
|
42
|
+
paragraph = [];
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < lines.length; i++) {
|
|
47
|
+
const line = lines[i];
|
|
48
|
+
if (line.trim().startsWith("```")) {
|
|
49
|
+
if (inCode) {
|
|
50
|
+
blocks.push({ type: "code", code: codeLines.join("\n"), lang: codeLang });
|
|
51
|
+
codeLines = [];
|
|
52
|
+
inCode = false;
|
|
53
|
+
} else {
|
|
54
|
+
flushParagraph();
|
|
55
|
+
inCode = true;
|
|
56
|
+
codeLang = line.trim().slice(3).trim();
|
|
57
|
+
}
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (inCode) {
|
|
61
|
+
codeLines.push(line);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (!line.trim()) {
|
|
65
|
+
flushParagraph();
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (
|
|
69
|
+
line.includes("|") &&
|
|
70
|
+
i + 1 < lines.length &&
|
|
71
|
+
isTableSeparatorLine(lines[i + 1])
|
|
72
|
+
) {
|
|
73
|
+
flushParagraph();
|
|
74
|
+
const headers = parseTableRow(line);
|
|
75
|
+
const rows: string[][] = [];
|
|
76
|
+
i += 2;
|
|
77
|
+
while (i < lines.length && lines[i].trim().includes("|")) {
|
|
78
|
+
rows.push(parseTableRow(lines[i]));
|
|
79
|
+
i++;
|
|
80
|
+
}
|
|
81
|
+
i--;
|
|
82
|
+
blocks.push({ type: "table", headers, rows });
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const hMatch = line.match(/^(#{1,6})\s+(.*)$/);
|
|
86
|
+
if (hMatch) {
|
|
87
|
+
flushParagraph();
|
|
88
|
+
blocks.push({ type: "heading", level: hMatch[1].length, text: hMatch[2] });
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const listMatch = line.match(/^(\*|-|\d+\.)\s+(.*)$/);
|
|
92
|
+
if (listMatch) {
|
|
93
|
+
flushParagraph();
|
|
94
|
+
blocks.push({ type: "list-item", bullet: listMatch[1], text: listMatch[2] });
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
paragraph.push(line);
|
|
98
|
+
}
|
|
99
|
+
flushParagraph();
|
|
100
|
+
if (inCode && codeLines.length > 0) {
|
|
101
|
+
blocks.push({ type: "code", code: codeLines.join("\n"), lang: codeLang });
|
|
102
|
+
}
|
|
103
|
+
return blocks;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const WEB_TEXT_SELECTABLE = Platform.OS === "web" ? { userSelect: "text" as const } : {};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Typography that mirrors Paseo's own markdown scale: body text uses `fontSize.content` (15) with a
|
|
110
|
+
* 1.4 line height, inline/code blocks use `fontSize.code` (12) and headings are scaled from the same
|
|
111
|
+
* base (h1 28, h2 24, h3 21, h4 19, h5/h6 17 with a 1.3 line height).
|
|
112
|
+
*
|
|
113
|
+
* This matters because a message that contains a pfr-form block is rendered by this plugin instead
|
|
114
|
+
* of the host's markdown renderer. With smaller values the surrounding text of such a reply looked
|
|
115
|
+
* shrunk compared to the same text in a plain assistant message.
|
|
116
|
+
*/
|
|
117
|
+
const CONTENT_FONT_SIZE = 15;
|
|
118
|
+
const CONTENT_LINE_HEIGHT = 21;
|
|
119
|
+
const CODE_FONT_SIZE = 12;
|
|
120
|
+
const CODE_LINE_HEIGHT = 22;
|
|
121
|
+
const HEADING_FONT_SIZES = [28, 24, 21, 19, 17, 17];
|
|
122
|
+
const HEADING_LINE_HEIGHTS = [36, 31, 27, 25, 22, 22];
|
|
123
|
+
/** Host spacing scale (spacing[2] = 8, spacing[3] = 12, spacing[4] = 16, spacing[6] = 24). */
|
|
124
|
+
const SPACE_1 = 4;
|
|
125
|
+
const SPACE_2 = 8;
|
|
126
|
+
const SPACE_3 = 12;
|
|
127
|
+
const SPACE_4 = 16;
|
|
128
|
+
const SPACE_6 = 24;
|
|
129
|
+
|
|
130
|
+
function parseInline(text: string, color: string, codeBg: string): ReactNode[] {
|
|
131
|
+
const regex = /(`[^`]+`|\*\*[^*]+\*\*|\*[^*]+\*|\[[^\]]+\]\([^)]+\))/g;
|
|
132
|
+
const parts: ReactNode[] = [];
|
|
133
|
+
let lastIndex = 0;
|
|
134
|
+
let match: RegExpExecArray | null;
|
|
135
|
+
|
|
136
|
+
while ((match = regex.exec(text)) !== null) {
|
|
137
|
+
if (match.index > lastIndex) {
|
|
138
|
+
parts.push(text.slice(lastIndex, match.index));
|
|
139
|
+
}
|
|
140
|
+
const token = match[0];
|
|
141
|
+
if (token.startsWith("`") && token.endsWith("`")) {
|
|
142
|
+
parts.push(
|
|
143
|
+
<Text
|
|
144
|
+
key={match.index}
|
|
145
|
+
selectable
|
|
146
|
+
dataSet={{ paseoMarkdownTag: "code" }}
|
|
147
|
+
style={{
|
|
148
|
+
color,
|
|
149
|
+
fontFamily: Platform.select({ ios: "Menlo", default: "monospace" }),
|
|
150
|
+
fontSize: CODE_FONT_SIZE,
|
|
151
|
+
lineHeight: CODE_LINE_HEIGHT,
|
|
152
|
+
backgroundColor: codeBg,
|
|
153
|
+
paddingHorizontal: SPACE_1,
|
|
154
|
+
paddingVertical: 2,
|
|
155
|
+
borderRadius: 4,
|
|
156
|
+
...WEB_TEXT_SELECTABLE,
|
|
157
|
+
}}
|
|
158
|
+
>
|
|
159
|
+
{token.slice(1, -1)}
|
|
160
|
+
</Text>,
|
|
161
|
+
);
|
|
162
|
+
} else if (token.startsWith("**") && token.endsWith("**")) {
|
|
163
|
+
parts.push(
|
|
164
|
+
<Text
|
|
165
|
+
key={match.index}
|
|
166
|
+
selectable
|
|
167
|
+
dataSet={{ paseoMarkdownTag: "strong" }}
|
|
168
|
+
style={{
|
|
169
|
+
fontWeight: "bold",
|
|
170
|
+
color,
|
|
171
|
+
...WEB_TEXT_SELECTABLE,
|
|
172
|
+
}}
|
|
173
|
+
>
|
|
174
|
+
{token.slice(2, -2)}
|
|
175
|
+
</Text>,
|
|
176
|
+
);
|
|
177
|
+
} else if (token.startsWith("*") && token.endsWith("*")) {
|
|
178
|
+
parts.push(
|
|
179
|
+
<Text
|
|
180
|
+
key={match.index}
|
|
181
|
+
selectable
|
|
182
|
+
dataSet={{ paseoMarkdownTag: "em" }}
|
|
183
|
+
style={{
|
|
184
|
+
fontStyle: "italic",
|
|
185
|
+
color,
|
|
186
|
+
...WEB_TEXT_SELECTABLE,
|
|
187
|
+
}}
|
|
188
|
+
>
|
|
189
|
+
{token.slice(1, -1)}
|
|
190
|
+
</Text>,
|
|
191
|
+
);
|
|
192
|
+
} else if (token.startsWith("[") && token.includes("](")) {
|
|
193
|
+
const closeBracket = token.indexOf("]");
|
|
194
|
+
const linkText = token.slice(1, closeBracket);
|
|
195
|
+
parts.push(
|
|
196
|
+
<Text
|
|
197
|
+
key={match.index}
|
|
198
|
+
selectable
|
|
199
|
+
dataSet={{ paseoMarkdownTag: "a" }}
|
|
200
|
+
style={{
|
|
201
|
+
color,
|
|
202
|
+
textDecorationLine: "underline",
|
|
203
|
+
...WEB_TEXT_SELECTABLE,
|
|
204
|
+
}}
|
|
205
|
+
>
|
|
206
|
+
{linkText}
|
|
207
|
+
</Text>,
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
lastIndex = regex.lastIndex;
|
|
211
|
+
}
|
|
212
|
+
if (lastIndex < text.length) {
|
|
213
|
+
parts.push(text.slice(lastIndex));
|
|
214
|
+
}
|
|
215
|
+
return parts;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function MarkdownText({
|
|
219
|
+
text,
|
|
220
|
+
theme,
|
|
221
|
+
}: Pick<PluginHostProps, "theme"> & { text: string }) {
|
|
222
|
+
const blocks = useMemo(() => {
|
|
223
|
+
try {
|
|
224
|
+
return parseBlocks(text);
|
|
225
|
+
} catch {
|
|
226
|
+
return [{ type: "paragraph" as const, text }];
|
|
227
|
+
}
|
|
228
|
+
}, [text]);
|
|
229
|
+
const color = theme.colors.foreground;
|
|
230
|
+
const codeBg = theme.colors.surface2;
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<View style={styles.container}>
|
|
234
|
+
{blocks.map((block, index) => {
|
|
235
|
+
if (block.type === "heading") {
|
|
236
|
+
const level = Math.min(Math.max(block.level || 1, 1), 6);
|
|
237
|
+
const fontSize = HEADING_FONT_SIZES[level - 1];
|
|
238
|
+
const lineHeight = HEADING_LINE_HEIGHTS[level - 1];
|
|
239
|
+
return (
|
|
240
|
+
<Text
|
|
241
|
+
key={index}
|
|
242
|
+
selectable
|
|
243
|
+
dataSet={{ paseoMarkdownTag: `h${level}` }}
|
|
244
|
+
style={{
|
|
245
|
+
color,
|
|
246
|
+
fontSize,
|
|
247
|
+
lineHeight,
|
|
248
|
+
fontWeight: level <= 2 ? "bold" : "600",
|
|
249
|
+
marginTop: level === 1 ? SPACE_6 : level <= 4 ? SPACE_4 : SPACE_3,
|
|
250
|
+
marginBottom: level === 1 ? SPACE_3 : level <= 4 ? SPACE_2 : SPACE_1,
|
|
251
|
+
...(level === 1
|
|
252
|
+
? {
|
|
253
|
+
borderBottomWidth: 1,
|
|
254
|
+
borderBottomColor: theme.colors.border,
|
|
255
|
+
paddingBottom: SPACE_2,
|
|
256
|
+
}
|
|
257
|
+
: null),
|
|
258
|
+
...(level === 6 ? { textTransform: "uppercase" as const, letterSpacing: 0.5 } : null),
|
|
259
|
+
...WEB_TEXT_SELECTABLE,
|
|
260
|
+
}}
|
|
261
|
+
>
|
|
262
|
+
{parseInline(block.text ?? "", color, codeBg)}
|
|
263
|
+
</Text>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
if (block.type === "paragraph") {
|
|
267
|
+
return (
|
|
268
|
+
<Text
|
|
269
|
+
key={index}
|
|
270
|
+
selectable
|
|
271
|
+
dataSet={{ paseoMarkdownTag: "p" }}
|
|
272
|
+
style={{
|
|
273
|
+
color,
|
|
274
|
+
lineHeight: CONTENT_LINE_HEIGHT,
|
|
275
|
+
fontSize: CONTENT_FONT_SIZE,
|
|
276
|
+
marginBottom: SPACE_3,
|
|
277
|
+
...WEB_TEXT_SELECTABLE,
|
|
278
|
+
}}
|
|
279
|
+
>
|
|
280
|
+
{parseInline(block.text ?? "", color, codeBg)}
|
|
281
|
+
</Text>
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
if (block.type === "list-item") {
|
|
285
|
+
return (
|
|
286
|
+
<View
|
|
287
|
+
key={index}
|
|
288
|
+
dataSet={{ paseoMarkdownTag: "li" }}
|
|
289
|
+
style={styles.listRow}
|
|
290
|
+
>
|
|
291
|
+
<Text
|
|
292
|
+
selectable
|
|
293
|
+
dataSet={{ paseoMarkdownIgnore: "true", paseoMarkdownListMarker: "true" }}
|
|
294
|
+
style={{
|
|
295
|
+
color: theme.colors.foregroundMuted,
|
|
296
|
+
lineHeight: CONTENT_LINE_HEIGHT,
|
|
297
|
+
fontSize: CONTENT_FONT_SIZE,
|
|
298
|
+
width: 18,
|
|
299
|
+
}}
|
|
300
|
+
>
|
|
301
|
+
{block.bullet?.endsWith(".") ? block.bullet : "•"}
|
|
302
|
+
</Text>
|
|
303
|
+
<Text
|
|
304
|
+
selectable
|
|
305
|
+
style={{
|
|
306
|
+
color,
|
|
307
|
+
lineHeight: CONTENT_LINE_HEIGHT,
|
|
308
|
+
fontSize: CONTENT_FONT_SIZE,
|
|
309
|
+
flex: 1,
|
|
310
|
+
...WEB_TEXT_SELECTABLE,
|
|
311
|
+
}}
|
|
312
|
+
>
|
|
313
|
+
{parseInline(block.text ?? "", color, codeBg)}
|
|
314
|
+
</Text>
|
|
315
|
+
</View>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
if (block.type === "table") {
|
|
319
|
+
const headers = block.headers ?? [];
|
|
320
|
+
const rows = block.rows ?? [];
|
|
321
|
+
return (
|
|
322
|
+
<View
|
|
323
|
+
key={index}
|
|
324
|
+
dataSet={{ paseoMarkdownTag: "table" }}
|
|
325
|
+
style={[
|
|
326
|
+
styles.tableContainer,
|
|
327
|
+
{ borderColor: theme.colors.border },
|
|
328
|
+
]}
|
|
329
|
+
>
|
|
330
|
+
<View
|
|
331
|
+
dataSet={{ paseoMarkdownTag: "thead" }}
|
|
332
|
+
style={[
|
|
333
|
+
styles.tableHeaderRow,
|
|
334
|
+
{
|
|
335
|
+
backgroundColor: theme.colors.surface2,
|
|
336
|
+
borderBottomColor: theme.colors.border,
|
|
337
|
+
},
|
|
338
|
+
]}
|
|
339
|
+
>
|
|
340
|
+
{headers.map((header, colIdx) => (
|
|
341
|
+
<View
|
|
342
|
+
key={colIdx}
|
|
343
|
+
dataSet={{ paseoMarkdownTag: "th" }}
|
|
344
|
+
style={styles.tableCell}
|
|
345
|
+
>
|
|
346
|
+
<Text
|
|
347
|
+
selectable
|
|
348
|
+
style={{
|
|
349
|
+
color,
|
|
350
|
+
fontSize: CONTENT_FONT_SIZE,
|
|
351
|
+
lineHeight: CONTENT_LINE_HEIGHT,
|
|
352
|
+
fontWeight: "600",
|
|
353
|
+
...WEB_TEXT_SELECTABLE,
|
|
354
|
+
}}
|
|
355
|
+
>
|
|
356
|
+
{parseInline(header, color, codeBg)}
|
|
357
|
+
</Text>
|
|
358
|
+
</View>
|
|
359
|
+
))}
|
|
360
|
+
</View>
|
|
361
|
+
{rows.map((row, rowIdx) => (
|
|
362
|
+
<View
|
|
363
|
+
key={rowIdx}
|
|
364
|
+
dataSet={{ paseoMarkdownTag: "tr" }}
|
|
365
|
+
style={[
|
|
366
|
+
styles.tableRow,
|
|
367
|
+
rowIdx < rows.length - 1
|
|
368
|
+
? {
|
|
369
|
+
borderBottomWidth: 1,
|
|
370
|
+
borderBottomColor: theme.colors.border,
|
|
371
|
+
}
|
|
372
|
+
: null,
|
|
373
|
+
]}
|
|
374
|
+
>
|
|
375
|
+
{headers.map((_, colIdx) => (
|
|
376
|
+
<View
|
|
377
|
+
key={colIdx}
|
|
378
|
+
dataSet={{ paseoMarkdownTag: "td" }}
|
|
379
|
+
style={styles.tableCell}
|
|
380
|
+
>
|
|
381
|
+
<Text
|
|
382
|
+
selectable
|
|
383
|
+
style={{
|
|
384
|
+
color,
|
|
385
|
+
fontSize: CONTENT_FONT_SIZE,
|
|
386
|
+
lineHeight: CONTENT_LINE_HEIGHT,
|
|
387
|
+
...WEB_TEXT_SELECTABLE,
|
|
388
|
+
}}
|
|
389
|
+
>
|
|
390
|
+
{parseInline(row[colIdx] ?? "", color, codeBg)}
|
|
391
|
+
</Text>
|
|
392
|
+
</View>
|
|
393
|
+
))}
|
|
394
|
+
</View>
|
|
395
|
+
))}
|
|
396
|
+
</View>
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
if (block.type === "code") {
|
|
400
|
+
return (
|
|
401
|
+
<View
|
|
402
|
+
key={index}
|
|
403
|
+
dataSet={{
|
|
404
|
+
paseoMarkdownTag: "pre",
|
|
405
|
+
paseoMarkdownLanguage: block.lang || undefined,
|
|
406
|
+
}}
|
|
407
|
+
style={[
|
|
408
|
+
styles.codeBlock,
|
|
409
|
+
{ backgroundColor: codeBg },
|
|
410
|
+
]}
|
|
411
|
+
>
|
|
412
|
+
<Text
|
|
413
|
+
selectable
|
|
414
|
+
dataSet={{ paseoMarkdownTag: "code" }}
|
|
415
|
+
style={{
|
|
416
|
+
color,
|
|
417
|
+
fontFamily: Platform.select({ ios: "Menlo", default: "monospace" }),
|
|
418
|
+
fontSize: CODE_FONT_SIZE,
|
|
419
|
+
lineHeight: CODE_LINE_HEIGHT,
|
|
420
|
+
...WEB_TEXT_SELECTABLE,
|
|
421
|
+
}}
|
|
422
|
+
>
|
|
423
|
+
{block.code?.trimEnd() ?? ""}
|
|
424
|
+
</Text>
|
|
425
|
+
</View>
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
return null;
|
|
429
|
+
})}
|
|
430
|
+
</View>
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const styles = StyleSheet.create({
|
|
435
|
+
container: {
|
|
436
|
+
marginVertical: 2,
|
|
437
|
+
...(Platform.OS === "web" ? { userSelect: "text" as const } : {}),
|
|
438
|
+
},
|
|
439
|
+
listRow: {
|
|
440
|
+
flexDirection: "row",
|
|
441
|
+
gap: 6,
|
|
442
|
+
marginVertical: 2,
|
|
443
|
+
},
|
|
444
|
+
tableContainer: {
|
|
445
|
+
borderWidth: 1,
|
|
446
|
+
borderRadius: 6,
|
|
447
|
+
overflow: "hidden",
|
|
448
|
+
marginVertical: SPACE_2,
|
|
449
|
+
},
|
|
450
|
+
tableHeaderRow: {
|
|
451
|
+
flexDirection: "row",
|
|
452
|
+
borderBottomWidth: 1,
|
|
453
|
+
paddingVertical: SPACE_2,
|
|
454
|
+
paddingHorizontal: SPACE_2,
|
|
455
|
+
},
|
|
456
|
+
tableRow: {
|
|
457
|
+
flexDirection: "row",
|
|
458
|
+
paddingVertical: SPACE_2,
|
|
459
|
+
paddingHorizontal: SPACE_2,
|
|
460
|
+
},
|
|
461
|
+
tableCell: {
|
|
462
|
+
flex: 1,
|
|
463
|
+
paddingRight: SPACE_2,
|
|
464
|
+
},
|
|
465
|
+
codeBlock: {
|
|
466
|
+
borderRadius: 6,
|
|
467
|
+
padding: SPACE_3,
|
|
468
|
+
marginVertical: SPACE_2,
|
|
469
|
+
},
|
|
470
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { defineTranslations } from "@getpaseo/plugin/client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Labels shown in the plugin UI. English is the source of truth; every other locale must use the
|
|
5
|
+
* same keys and placeholders (checked by the SDK and by `client/messages.test.ts`). The host picks the
|
|
6
|
+
* locale from the app language.
|
|
7
|
+
*/
|
|
8
|
+
const en = {
|
|
9
|
+
slash: {
|
|
10
|
+
form: "Instruct the agent to use pfr-form protocol",
|
|
11
|
+
formDemo: "Show a demo interactive form",
|
|
12
|
+
},
|
|
13
|
+
errorBox: {
|
|
14
|
+
title: "[pfr-form Syntax Error]",
|
|
15
|
+
askFix: "Ask agent to fix",
|
|
16
|
+
sending: "Sending…",
|
|
17
|
+
fixRequested: "✓ Fix requested; the agent will reply with a corrected form",
|
|
18
|
+
},
|
|
19
|
+
form: {
|
|
20
|
+
submitted: "✓ Form Submitted",
|
|
21
|
+
},
|
|
22
|
+
dropdown: {
|
|
23
|
+
placeholder: "Select an option...",
|
|
24
|
+
multiPlaceholder: "Select options...",
|
|
25
|
+
close: "Close dropdown menu",
|
|
26
|
+
},
|
|
27
|
+
file: {
|
|
28
|
+
folderPlaceholder: "Enter or select folder...",
|
|
29
|
+
filePlaceholder: "Enter or select file...",
|
|
30
|
+
browse: "Browse...",
|
|
31
|
+
fullPath: "Full path:",
|
|
32
|
+
},
|
|
33
|
+
picker: {
|
|
34
|
+
selectFolder: "Select Folder",
|
|
35
|
+
selectFile: "Select File",
|
|
36
|
+
chooseFolder: "Choose Folder",
|
|
37
|
+
chooseFile: "Choose File",
|
|
38
|
+
quick: "Quick:",
|
|
39
|
+
searchPlaceholder: "Search files/folders in directory...",
|
|
40
|
+
newFolderPlaceholder: "New folder name...",
|
|
41
|
+
create: "Create",
|
|
42
|
+
upToParent: ".. (Up to parent)",
|
|
43
|
+
loading: "Loading files...",
|
|
44
|
+
noMatches: "No matching entries found",
|
|
45
|
+
selected: "✓ Selected",
|
|
46
|
+
selectedLabel: "Selected:",
|
|
47
|
+
cancel: "Cancel",
|
|
48
|
+
projectRoot: "Project Root",
|
|
49
|
+
project: "Project",
|
|
50
|
+
invalidFolderName: 'Folder name contains invalid characters (/ \\ : * ? " < > |).',
|
|
51
|
+
createFailed: "Failed to create folder.",
|
|
52
|
+
},
|
|
53
|
+
settings: {
|
|
54
|
+
loadError: "Could not load settings: {{error}}",
|
|
55
|
+
retry: "Retry",
|
|
56
|
+
resetDefaults: "Reset to defaults",
|
|
57
|
+
autoFixTitle: "Automatic form fixes",
|
|
58
|
+
autoFixDescription:
|
|
59
|
+
'When an agent replies with a pfr-form block that fails validation, ask it to re-send a corrected block automatically. This sets how many times in a row the agent is asked; 0 turns automatic fixes off. The "Ask agent to fix" button on an error box always works.',
|
|
60
|
+
maxRetries: "Maximum retries",
|
|
61
|
+
decreaseRetries: "Decrease retries",
|
|
62
|
+
increaseRetries: "Increase retries",
|
|
63
|
+
hintOff: "Automatic fixes are off.",
|
|
64
|
+
hint_one: "Up to {{count}} automatic request per reply.",
|
|
65
|
+
hint_other: "Up to {{count}} automatic requests per reply.",
|
|
66
|
+
saving: "Saving…",
|
|
67
|
+
preferFormsTitle: "Prefer forms",
|
|
68
|
+
preferFormsDescription:
|
|
69
|
+
"By default the agent is only told it can use pfr-form. Turn this on to require it to ask for choices, parameters, and confirmations with a form instead of plain text. Applies to agents created after the change.",
|
|
70
|
+
requireForms: "Require forms for user input",
|
|
71
|
+
saveError: "Could not save: {{error}}",
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const zhCN = {
|
|
76
|
+
slash: {
|
|
77
|
+
form: "让智能体使用 pfr-form 协议",
|
|
78
|
+
formDemo: "显示一个交互表单示例",
|
|
79
|
+
},
|
|
80
|
+
errorBox: {
|
|
81
|
+
title: "[pfr-form 语法错误]",
|
|
82
|
+
askFix: "请求智能体修复",
|
|
83
|
+
sending: "发送中…",
|
|
84
|
+
fixRequested: "✓ 已请求修复,智能体将回复更正后的表单",
|
|
85
|
+
},
|
|
86
|
+
form: {
|
|
87
|
+
submitted: "✓ 表单已提交",
|
|
88
|
+
},
|
|
89
|
+
dropdown: {
|
|
90
|
+
placeholder: "请选择...",
|
|
91
|
+
multiPlaceholder: "请选择多项...",
|
|
92
|
+
close: "关闭下拉菜单",
|
|
93
|
+
},
|
|
94
|
+
file: {
|
|
95
|
+
folderPlaceholder: "输入或选择文件夹...",
|
|
96
|
+
filePlaceholder: "输入或选择文件...",
|
|
97
|
+
browse: "浏览...",
|
|
98
|
+
fullPath: "完整路径:",
|
|
99
|
+
},
|
|
100
|
+
picker: {
|
|
101
|
+
selectFolder: "选择文件夹",
|
|
102
|
+
selectFile: "选择文件",
|
|
103
|
+
chooseFolder: "选择此文件夹",
|
|
104
|
+
chooseFile: "选择此文件",
|
|
105
|
+
quick: "快捷:",
|
|
106
|
+
searchPlaceholder: "在目录中搜索文件/文件夹...",
|
|
107
|
+
newFolderPlaceholder: "新文件夹名称...",
|
|
108
|
+
create: "创建",
|
|
109
|
+
upToParent: ".. (返回上级)",
|
|
110
|
+
loading: "正在加载文件...",
|
|
111
|
+
noMatches: "没有匹配的条目",
|
|
112
|
+
selected: "✓ 已选择",
|
|
113
|
+
selectedLabel: "已选择:",
|
|
114
|
+
cancel: "取消",
|
|
115
|
+
projectRoot: "项目根目录",
|
|
116
|
+
project: "项目",
|
|
117
|
+
invalidFolderName: '文件夹名称包含非法字符 (/ \\ : * ? " < > |)。',
|
|
118
|
+
createFailed: "创建文件夹失败。",
|
|
119
|
+
},
|
|
120
|
+
settings: {
|
|
121
|
+
loadError: "无法加载设置:{{error}}",
|
|
122
|
+
retry: "重试",
|
|
123
|
+
resetDefaults: "恢复默认值",
|
|
124
|
+
autoFixTitle: "自动修复表单",
|
|
125
|
+
autoFixDescription:
|
|
126
|
+
"当智能体回复的 pfr-form 代码块未通过校验时,自动请求它重新发送更正后的代码块。此处设置连续请求的最多次数;设为 0 则关闭自动修复。错误框上的“请求智能体修复”按钮始终可用。",
|
|
127
|
+
maxRetries: "最大重试次数",
|
|
128
|
+
decreaseRetries: "减少重试次数",
|
|
129
|
+
increaseRetries: "增加重试次数",
|
|
130
|
+
hintOff: "自动修复已关闭。",
|
|
131
|
+
hint_other: "每条回复最多自动请求 {{count}} 次。",
|
|
132
|
+
saving: "保存中…",
|
|
133
|
+
preferFormsTitle: "优先使用表单",
|
|
134
|
+
preferFormsDescription:
|
|
135
|
+
"默认情况下,智能体只被告知可以使用 pfr-form。开启后,它必须通过表单询问选项、参数和确认,而不是纯文本。仅对之后创建的智能体生效。",
|
|
136
|
+
requireForms: "用户输入必须使用表单",
|
|
137
|
+
saveError: "无法保存:{{error}}",
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export const messages = defineTranslations({
|
|
142
|
+
resources: {
|
|
143
|
+
en,
|
|
144
|
+
"zh-CN": zhCN,
|
|
145
|
+
zh: zhCN,
|
|
146
|
+
},
|
|
147
|
+
});
|