@ampless/admin 1.0.0-alpha.85 → 1.0.0-beta.101
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.ja.md +2 -2
- package/README.md +2 -2
- package/dist/api/index.d.ts +1 -1
- package/dist/api/index.js +4 -2
- package/dist/{chunk-SQ6LFPXH.js → chunk-2TBGPB5H.js} +2 -2
- package/dist/{chunk-K44QF3MV.js → chunk-4OFRX6QX.js} +1 -1
- package/dist/{chunk-HANIMWMN.js → chunk-4WYMEUYU.js} +301 -222
- package/dist/chunk-7HSWOQWV.js +172 -0
- package/dist/{chunk-J6R356H3.js → chunk-7OZ23MLD.js} +1 -1
- package/dist/{chunk-OFPNBZKP.js → chunk-7W7LAQXY.js} +76 -8
- package/dist/{chunk-HN3UFHP7.js → chunk-ANME5KDN.js} +1 -1
- package/dist/{chunk-GGHN2RKQ.js → chunk-E5VF3Y65.js} +69 -3
- package/dist/{chunk-7LT6YPIT.js → chunk-JLCTF5HY.js} +8 -2
- package/dist/{chunk-R3TEJ32M.js → chunk-LXY3BRKQ.js} +2 -2
- package/dist/{chunk-MKWYNK4J.js → chunk-NHMG2MRJ.js} +2 -2
- package/dist/{chunk-AYESATAM.js → chunk-OPVLQOOB.js} +44 -13
- package/dist/{chunk-SMI7Y77B.js → chunk-ORXOMW7M.js} +265 -190
- package/dist/{chunk-W4VHP2JW.js → chunk-SYAEM5DM.js} +1 -1
- package/dist/{chunk-7NQERXNY.js → chunk-TJZ4FTU6.js} +1 -1
- package/dist/{chunk-CFIOUXVE.js → chunk-V5XORHKX.js} +1 -1
- package/dist/chunk-ZDIDJCSU.js +40 -0
- package/dist/components/account-view.d.ts +8 -0
- package/dist/components/account-view.js +10 -0
- package/dist/components/admin-dashboard.js +3 -3
- package/dist/components/edit-post-view.js +5 -5
- package/dist/components/index.d.ts +4 -2
- package/dist/components/index.js +7 -7
- package/dist/components/login-view.d.ts +3 -1
- package/dist/components/login-view.js +4 -3
- package/dist/components/mcp-tokens-view.js +3 -3
- package/dist/components/media-view.js +5 -5
- package/dist/components/new-post-view.js +5 -5
- package/dist/components/plugin-settings-form.d.ts +2 -10
- package/dist/components/plugin-settings-form.js +5 -7
- package/dist/components/posts-list-view.js +3 -3
- package/dist/components/users-list-view.js +3 -3
- package/dist/{i18n-DA-aakRX.d.ts → i18n-Bd8q9aTl.d.ts} +108 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/metafile-esm.json +1 -1
- package/dist/pages/index.d.ts +21 -4
- package/dist/pages/index.js +54 -24
- package/package.json +5 -6
|
@@ -5,14 +5,14 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
useLocale,
|
|
7
7
|
useT
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-4OFRX6QX.js";
|
|
9
9
|
|
|
10
10
|
// src/components/plugin-settings-form.tsx
|
|
11
11
|
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
12
12
|
import {
|
|
13
13
|
resolveLocalized as resolveLocalized3
|
|
14
14
|
} from "ampless";
|
|
15
|
-
import { Button as Button2,
|
|
15
|
+
import { Button as Button2, Label as Label3 } from "@ampless/runtime/ui";
|
|
16
16
|
|
|
17
17
|
// src/lib/plugin-settings.ts
|
|
18
18
|
import {
|
|
@@ -38,6 +38,25 @@ async function setPluginPublicSetting(instanceId, field, rawValue) {
|
|
|
38
38
|
const store = getKvStore();
|
|
39
39
|
await store.put(SITE_CONFIG_PK, pluginSettingKey(instanceId, field.key), validated);
|
|
40
40
|
}
|
|
41
|
+
async function getPluginPublicSetting(instanceId, fieldKey) {
|
|
42
|
+
return getKvStore().get(SITE_CONFIG_PK, pluginSettingKey(instanceId, fieldKey));
|
|
43
|
+
}
|
|
44
|
+
function collectSettingWrites(fields, values, touched, parseFn) {
|
|
45
|
+
const writes = [];
|
|
46
|
+
const invalid = {};
|
|
47
|
+
for (const field of fields) {
|
|
48
|
+
if (!touched[field.key]) continue;
|
|
49
|
+
const raw = values[field.key] ?? "";
|
|
50
|
+
const parsed = parseFn(field, raw);
|
|
51
|
+
if (parsed === null && raw !== "") {
|
|
52
|
+
invalid[field.key] = true;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (parsed === null) continue;
|
|
56
|
+
writes.push({ field, parsed });
|
|
57
|
+
}
|
|
58
|
+
return { writes, invalid };
|
|
59
|
+
}
|
|
41
60
|
async function deletePluginPublicSetting(instanceId, field) {
|
|
42
61
|
if (!isValidPluginKey(instanceId)) {
|
|
43
62
|
throw new Error(`Invalid plugin instanceId: "${instanceId}"`);
|
|
@@ -145,6 +164,114 @@ import { useEffect, useState } from "react";
|
|
|
145
164
|
import { resolveLocalized } from "ampless";
|
|
146
165
|
import { Label } from "@ampless/runtime/ui";
|
|
147
166
|
|
|
167
|
+
// src/components/scalar-input.tsx
|
|
168
|
+
import { Input, Textarea } from "@ampless/runtime/ui";
|
|
169
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
170
|
+
function renderScalarInput(field, id, value, invalid, onChange) {
|
|
171
|
+
switch (field.type) {
|
|
172
|
+
case "text":
|
|
173
|
+
case "url":
|
|
174
|
+
return /* @__PURE__ */ jsx(
|
|
175
|
+
Input,
|
|
176
|
+
{
|
|
177
|
+
id,
|
|
178
|
+
value,
|
|
179
|
+
maxLength: field.type === "text" ? field.maxLength : void 0,
|
|
180
|
+
placeholder: "placeholder" in field ? field.placeholder : void 0,
|
|
181
|
+
onChange: (e) => onChange(e.target.value),
|
|
182
|
+
"aria-invalid": invalid,
|
|
183
|
+
type: field.type === "url" ? "url" : "text"
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
case "textarea":
|
|
187
|
+
return /* @__PURE__ */ jsx(
|
|
188
|
+
Textarea,
|
|
189
|
+
{
|
|
190
|
+
id,
|
|
191
|
+
value,
|
|
192
|
+
rows: field.rows ?? 4,
|
|
193
|
+
maxLength: field.maxLength,
|
|
194
|
+
placeholder: field.placeholder,
|
|
195
|
+
onChange: (e) => onChange(e.target.value),
|
|
196
|
+
"aria-invalid": invalid
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
case "code":
|
|
200
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
201
|
+
field.language && /* @__PURE__ */ jsx("p", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: field.language }),
|
|
202
|
+
/* @__PURE__ */ jsx(
|
|
203
|
+
Textarea,
|
|
204
|
+
{
|
|
205
|
+
id,
|
|
206
|
+
value,
|
|
207
|
+
rows: field.rows ?? 8,
|
|
208
|
+
maxLength: field.maxLength,
|
|
209
|
+
placeholder: field.placeholder,
|
|
210
|
+
onChange: (e) => onChange(e.target.value),
|
|
211
|
+
"aria-invalid": invalid,
|
|
212
|
+
className: "font-mono text-xs"
|
|
213
|
+
}
|
|
214
|
+
)
|
|
215
|
+
] });
|
|
216
|
+
case "boolean":
|
|
217
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
218
|
+
/* @__PURE__ */ jsx(
|
|
219
|
+
"input",
|
|
220
|
+
{
|
|
221
|
+
id,
|
|
222
|
+
type: "checkbox",
|
|
223
|
+
checked: value === "true",
|
|
224
|
+
onChange: (e) => onChange(e.target.checked ? "true" : "false"),
|
|
225
|
+
className: "h-4 w-4"
|
|
226
|
+
}
|
|
227
|
+
),
|
|
228
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: value === "true" ? "on" : "off" })
|
|
229
|
+
] });
|
|
230
|
+
case "number":
|
|
231
|
+
return /* @__PURE__ */ jsx(
|
|
232
|
+
Input,
|
|
233
|
+
{
|
|
234
|
+
id,
|
|
235
|
+
type: "number",
|
|
236
|
+
value,
|
|
237
|
+
min: field.min,
|
|
238
|
+
max: field.max,
|
|
239
|
+
step: field.step,
|
|
240
|
+
onChange: (e) => onChange(e.target.value),
|
|
241
|
+
"aria-invalid": invalid
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
case "select":
|
|
245
|
+
return /* @__PURE__ */ jsxs(
|
|
246
|
+
"select",
|
|
247
|
+
{
|
|
248
|
+
id,
|
|
249
|
+
className: "w-full rounded-md border bg-background px-2 py-1.5 text-sm",
|
|
250
|
+
value,
|
|
251
|
+
onChange: (e) => onChange(e.target.value),
|
|
252
|
+
"aria-invalid": invalid,
|
|
253
|
+
children: [
|
|
254
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "\u2014" }),
|
|
255
|
+
field.options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: typeof opt.label === "string" ? opt.label : opt.value }, opt.value))
|
|
256
|
+
]
|
|
257
|
+
}
|
|
258
|
+
);
|
|
259
|
+
case "json":
|
|
260
|
+
return /* @__PURE__ */ jsx(
|
|
261
|
+
Textarea,
|
|
262
|
+
{
|
|
263
|
+
id,
|
|
264
|
+
value,
|
|
265
|
+
rows: field.rows ?? 8,
|
|
266
|
+
placeholder: field.placeholder ?? "{}",
|
|
267
|
+
onChange: (e) => onChange(e.target.value),
|
|
268
|
+
"aria-invalid": invalid,
|
|
269
|
+
className: "font-mono text-xs"
|
|
270
|
+
}
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
148
275
|
// src/lib/repeatable-field.ts
|
|
149
276
|
function parseRepeatableValue(raw) {
|
|
150
277
|
if (!raw || raw.trim() === "") return [];
|
|
@@ -209,7 +336,7 @@ function canAddItem(field, currentCount) {
|
|
|
209
336
|
}
|
|
210
337
|
|
|
211
338
|
// src/components/repeatable-field-editor.tsx
|
|
212
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
339
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
213
340
|
function RepeatableFieldEditor({
|
|
214
341
|
field,
|
|
215
342
|
id,
|
|
@@ -245,7 +372,7 @@ function RepeatableFieldEditor({
|
|
|
245
372
|
commit([...items, makeEmptyItem(field)]);
|
|
246
373
|
}
|
|
247
374
|
const addLabelText = field.addLabel ? resolveLocalized(field.addLabel, locale) : "+ Add item";
|
|
248
|
-
return /* @__PURE__ */
|
|
375
|
+
return /* @__PURE__ */ jsxs2(
|
|
249
376
|
"div",
|
|
250
377
|
{
|
|
251
378
|
id,
|
|
@@ -255,17 +382,17 @@ function RepeatableFieldEditor({
|
|
|
255
382
|
invalid ? "border-destructive" : "border-border"
|
|
256
383
|
].join(" "),
|
|
257
384
|
children: [
|
|
258
|
-
items.length === 0 && /* @__PURE__ */
|
|
385
|
+
items.length === 0 && /* @__PURE__ */ jsx2("p", { className: "text-xs text-muted-foreground", children: "No items yet." }),
|
|
259
386
|
items.map((item, itemIdx) => {
|
|
260
387
|
const label = itemLabel(field, item, itemIdx);
|
|
261
|
-
return /* @__PURE__ */
|
|
388
|
+
return /* @__PURE__ */ jsxs2(
|
|
262
389
|
"div",
|
|
263
390
|
{
|
|
264
391
|
className: "space-y-2 rounded-md border border-border bg-muted/30 p-3",
|
|
265
392
|
children: [
|
|
266
|
-
/* @__PURE__ */
|
|
267
|
-
/* @__PURE__ */
|
|
268
|
-
/* @__PURE__ */
|
|
393
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between", children: [
|
|
394
|
+
/* @__PURE__ */ jsx2("span", { className: "text-xs font-medium", children: label }),
|
|
395
|
+
/* @__PURE__ */ jsx2(
|
|
269
396
|
"button",
|
|
270
397
|
{
|
|
271
398
|
type: "button",
|
|
@@ -276,13 +403,13 @@ function RepeatableFieldEditor({
|
|
|
276
403
|
}
|
|
277
404
|
)
|
|
278
405
|
] }),
|
|
279
|
-
/* @__PURE__ */
|
|
406
|
+
/* @__PURE__ */ jsx2("div", { className: "space-y-2", children: field.fields.map((sf) => {
|
|
280
407
|
const cellId = `${id}-${itemIdx}-${sf.key}`;
|
|
281
408
|
const formString = subFieldValueToFormString(sf, item[sf.key]);
|
|
282
|
-
return /* @__PURE__ */
|
|
283
|
-
/* @__PURE__ */
|
|
409
|
+
return /* @__PURE__ */ jsxs2("div", { className: "space-y-1", children: [
|
|
410
|
+
/* @__PURE__ */ jsxs2(Label, { htmlFor: cellId, className: "text-xs", children: [
|
|
284
411
|
resolveLocalized(sf.label, locale),
|
|
285
|
-
sf.required && /* @__PURE__ */
|
|
412
|
+
sf.required && /* @__PURE__ */ jsx2("span", { className: "ml-1 text-destructive", children: "*" })
|
|
286
413
|
] }),
|
|
287
414
|
renderScalarInput(
|
|
288
415
|
sf,
|
|
@@ -299,7 +426,7 @@ function RepeatableFieldEditor({
|
|
|
299
426
|
itemIdx
|
|
300
427
|
);
|
|
301
428
|
}),
|
|
302
|
-
/* @__PURE__ */
|
|
429
|
+
/* @__PURE__ */ jsx2(
|
|
303
430
|
"button",
|
|
304
431
|
{
|
|
305
432
|
type: "button",
|
|
@@ -317,7 +444,7 @@ function RepeatableFieldEditor({
|
|
|
317
444
|
// src/components/secret-field-input.tsx
|
|
318
445
|
import { useReducer, useId } from "react";
|
|
319
446
|
import { resolveLocalized as resolveLocalized2 } from "ampless";
|
|
320
|
-
import { Button, Input, Label as Label2 } from "@ampless/runtime/ui";
|
|
447
|
+
import { Button, Input as Input2, Label as Label2 } from "@ampless/runtime/ui";
|
|
321
448
|
|
|
322
449
|
// src/lib/secret-field-input.ts
|
|
323
450
|
function initialSecretFieldState(hasValue) {
|
|
@@ -379,7 +506,7 @@ function isBusy(state) {
|
|
|
379
506
|
}
|
|
380
507
|
|
|
381
508
|
// src/components/secret-field-input.tsx
|
|
382
|
-
import { jsx as
|
|
509
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
383
510
|
function SecretFieldInput({ field, hasValue, onSave, onClear }) {
|
|
384
511
|
const locale = useLocale();
|
|
385
512
|
const inputId = useId();
|
|
@@ -416,15 +543,15 @@ function SecretFieldInput({ field, hasValue, onSave, onClear }) {
|
|
|
416
543
|
const label = resolveLocalized2(field.label, locale);
|
|
417
544
|
const description = field.description ? resolveLocalized2(field.description, locale) : void 0;
|
|
418
545
|
const busy = isBusy(state);
|
|
419
|
-
return /* @__PURE__ */
|
|
420
|
-
/* @__PURE__ */
|
|
421
|
-
/* @__PURE__ */
|
|
546
|
+
return /* @__PURE__ */ jsxs3("div", { className: "space-y-1.5 rounded-md border border-amber-200 bg-amber-50 p-3 dark:border-amber-900 dark:bg-amber-950/30", children: [
|
|
547
|
+
/* @__PURE__ */ jsx3("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxs3(Label2, { htmlFor: inputId, className: "flex items-center gap-1.5 text-sm font-medium", children: [
|
|
548
|
+
/* @__PURE__ */ jsx3(LockIcon, {}),
|
|
422
549
|
label,
|
|
423
|
-
field.required && /* @__PURE__ */
|
|
550
|
+
field.required && /* @__PURE__ */ jsx3("span", { className: "ml-0.5 text-destructive", children: "*" })
|
|
424
551
|
] }) }),
|
|
425
|
-
description && /* @__PURE__ */
|
|
426
|
-
showStoredPlaceholder(state) && /* @__PURE__ */
|
|
427
|
-
/* @__PURE__ */
|
|
552
|
+
description && /* @__PURE__ */ jsx3("p", { className: "text-xs text-muted-foreground", children: description }),
|
|
553
|
+
showStoredPlaceholder(state) && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
554
|
+
/* @__PURE__ */ jsx3(
|
|
428
555
|
"span",
|
|
429
556
|
{
|
|
430
557
|
className: "flex-1 rounded-md border bg-background px-3 py-1.5 text-sm text-muted-foreground",
|
|
@@ -432,7 +559,7 @@ function SecretFieldInput({ field, hasValue, onSave, onClear }) {
|
|
|
432
559
|
children: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"
|
|
433
560
|
}
|
|
434
561
|
),
|
|
435
|
-
/* @__PURE__ */
|
|
562
|
+
/* @__PURE__ */ jsx3(
|
|
436
563
|
Button,
|
|
437
564
|
{
|
|
438
565
|
type: "button",
|
|
@@ -443,7 +570,7 @@ function SecretFieldInput({ field, hasValue, onSave, onClear }) {
|
|
|
443
570
|
children: "Replace"
|
|
444
571
|
}
|
|
445
572
|
),
|
|
446
|
-
/* @__PURE__ */
|
|
573
|
+
/* @__PURE__ */ jsx3(
|
|
447
574
|
Button,
|
|
448
575
|
{
|
|
449
576
|
type: "button",
|
|
@@ -456,9 +583,9 @@ function SecretFieldInput({ field, hasValue, onSave, onClear }) {
|
|
|
456
583
|
}
|
|
457
584
|
)
|
|
458
585
|
] }),
|
|
459
|
-
showInput(state) && /* @__PURE__ */
|
|
460
|
-
/* @__PURE__ */
|
|
461
|
-
|
|
586
|
+
showInput(state) && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
587
|
+
/* @__PURE__ */ jsx3(
|
|
588
|
+
Input2,
|
|
462
589
|
{
|
|
463
590
|
id: inputId,
|
|
464
591
|
type: "text",
|
|
@@ -474,7 +601,7 @@ function SecretFieldInput({ field, hasValue, onSave, onClear }) {
|
|
|
474
601
|
onChange: (e) => dispatch({ type: "CHANGE", value: e.target.value })
|
|
475
602
|
}
|
|
476
603
|
),
|
|
477
|
-
/* @__PURE__ */
|
|
604
|
+
/* @__PURE__ */ jsx3(
|
|
478
605
|
Button,
|
|
479
606
|
{
|
|
480
607
|
type: "button",
|
|
@@ -484,7 +611,7 @@ function SecretFieldInput({ field, hasValue, onSave, onClear }) {
|
|
|
484
611
|
children: state.status === "saving" ? "Saving\u2026" : "Save"
|
|
485
612
|
}
|
|
486
613
|
),
|
|
487
|
-
(state.status === "editing" || state.status === "error" && state.previousStatus === "editing") && /* @__PURE__ */
|
|
614
|
+
(state.status === "editing" || state.status === "error" && state.previousStatus === "editing") && /* @__PURE__ */ jsx3(
|
|
488
615
|
Button,
|
|
489
616
|
{
|
|
490
617
|
type: "button",
|
|
@@ -496,12 +623,12 @@ function SecretFieldInput({ field, hasValue, onSave, onClear }) {
|
|
|
496
623
|
}
|
|
497
624
|
)
|
|
498
625
|
] }),
|
|
499
|
-
state.status === "clearing" && /* @__PURE__ */
|
|
500
|
-
state.status === "error" && /* @__PURE__ */
|
|
626
|
+
state.status === "clearing" && /* @__PURE__ */ jsx3("p", { className: "text-xs text-muted-foreground", children: "Clearing\u2026" }),
|
|
627
|
+
state.status === "error" && /* @__PURE__ */ jsx3("p", { className: "text-xs text-destructive", children: state.message })
|
|
501
628
|
] });
|
|
502
629
|
}
|
|
503
630
|
function LockIcon() {
|
|
504
|
-
return /* @__PURE__ */
|
|
631
|
+
return /* @__PURE__ */ jsx3(
|
|
505
632
|
"svg",
|
|
506
633
|
{
|
|
507
634
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -509,7 +636,7 @@ function LockIcon() {
|
|
|
509
636
|
fill: "currentColor",
|
|
510
637
|
className: "h-3.5 w-3.5 text-amber-600 dark:text-amber-400",
|
|
511
638
|
"aria-hidden": "true",
|
|
512
|
-
children: /* @__PURE__ */
|
|
639
|
+
children: /* @__PURE__ */ jsx3(
|
|
513
640
|
"path",
|
|
514
641
|
{
|
|
515
642
|
fillRule: "evenodd",
|
|
@@ -522,7 +649,7 @@ function LockIcon() {
|
|
|
522
649
|
}
|
|
523
650
|
|
|
524
651
|
// src/components/plugin-settings-form.tsx
|
|
525
|
-
import { Fragment, jsx as
|
|
652
|
+
import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
526
653
|
var CACHE_REBUILD_DELAY_MS = 8e3;
|
|
527
654
|
function stringify(field, raw) {
|
|
528
655
|
if (raw === void 0 || raw === null) return "";
|
|
@@ -614,6 +741,56 @@ function PluginSettingsForm({
|
|
|
614
741
|
cancelled = true;
|
|
615
742
|
};
|
|
616
743
|
}, [instanceId]);
|
|
744
|
+
useEffect2(() => {
|
|
745
|
+
if (fields.length === 0) return;
|
|
746
|
+
let cancelled = false;
|
|
747
|
+
async function fetchStoredValues() {
|
|
748
|
+
const updates = {};
|
|
749
|
+
const newStoredKeys = [];
|
|
750
|
+
const clearedKeys = [];
|
|
751
|
+
for (const field of fields) {
|
|
752
|
+
try {
|
|
753
|
+
const stored = await getPluginPublicSetting(instanceId, field.key);
|
|
754
|
+
if (stored !== null && stored !== void 0) {
|
|
755
|
+
updates[field.key] = stringify(field, stored);
|
|
756
|
+
newStoredKeys.push(field.key);
|
|
757
|
+
} else {
|
|
758
|
+
clearedKeys.push(field.key);
|
|
759
|
+
}
|
|
760
|
+
} catch (err) {
|
|
761
|
+
console.warn("[plugin] mount fetch failed for field", field.key, err);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
if (cancelled) return;
|
|
765
|
+
if (Object.keys(updates).length > 0 || clearedKeys.length > 0) {
|
|
766
|
+
setState((prev) => {
|
|
767
|
+
const nextValues = { ...prev.values };
|
|
768
|
+
for (const [key, val] of Object.entries(updates)) {
|
|
769
|
+
if (!prev.touched[key]) {
|
|
770
|
+
nextValues[key] = val;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
for (const key of clearedKeys) {
|
|
774
|
+
if (!prev.touched[key]) {
|
|
775
|
+
const field = fields.find((f) => f.key === key);
|
|
776
|
+
if (field) nextValues[key] = defaultDisplay(field);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return { ...prev, values: nextValues };
|
|
780
|
+
});
|
|
781
|
+
setStoredKeys((prev) => {
|
|
782
|
+
const next = new Set(prev);
|
|
783
|
+
for (const k of newStoredKeys) next.add(k);
|
|
784
|
+
for (const k of clearedKeys) next.delete(k);
|
|
785
|
+
return next;
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
void fetchStoredValues();
|
|
790
|
+
return () => {
|
|
791
|
+
cancelled = true;
|
|
792
|
+
};
|
|
793
|
+
}, [instanceId]);
|
|
617
794
|
function defaultDisplay(field) {
|
|
618
795
|
return field.default !== void 0 ? stringify(field, field.default) : "";
|
|
619
796
|
}
|
|
@@ -676,38 +853,41 @@ function PluginSettingsForm({
|
|
|
676
853
|
setSaving(true);
|
|
677
854
|
setError(null);
|
|
678
855
|
setInfo(null);
|
|
679
|
-
const newInvalid =
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
const parsed = parse(field, raw);
|
|
686
|
-
if (parsed === null && raw !== "") {
|
|
687
|
-
newInvalid[field.key] = true;
|
|
688
|
-
continue;
|
|
689
|
-
}
|
|
690
|
-
if (parsed === null) continue;
|
|
691
|
-
writes.push(setPluginPublicSetting(instanceId, field, parsed));
|
|
692
|
-
writtenKeys.push(field.key);
|
|
693
|
-
}
|
|
856
|
+
const { writes: pendingWrites, invalid: newInvalid } = collectSettingWrites(
|
|
857
|
+
fields,
|
|
858
|
+
state.values,
|
|
859
|
+
state.touched,
|
|
860
|
+
parse
|
|
861
|
+
);
|
|
694
862
|
if (Object.keys(newInvalid).length > 0) {
|
|
695
863
|
setState((prev) => ({ ...prev, invalid: newInvalid }));
|
|
696
864
|
setSaving(false);
|
|
697
865
|
setError(t("plugins.invalidValue"));
|
|
698
866
|
return;
|
|
699
867
|
}
|
|
868
|
+
if (pendingWrites.length === 0) {
|
|
869
|
+
setInfo(t("plugins.noChanges"));
|
|
870
|
+
setSaving(false);
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
700
873
|
try {
|
|
701
|
-
await Promise.all(
|
|
874
|
+
await Promise.all(
|
|
875
|
+
pendingWrites.map(
|
|
876
|
+
({ field, parsed }) => setPluginPublicSetting(instanceId, field, parsed)
|
|
877
|
+
)
|
|
878
|
+
);
|
|
879
|
+
const writtenKeys = pendingWrites.map(({ field }) => field.key);
|
|
702
880
|
setInfo(t("plugins.saved"));
|
|
703
|
-
setState((prev) =>
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
881
|
+
setState((prev) => {
|
|
882
|
+
const nextTouched = { ...prev.touched };
|
|
883
|
+
for (const k of writtenKeys) delete nextTouched[k];
|
|
884
|
+
return { ...prev, touched: nextTouched, invalid: {} };
|
|
885
|
+
});
|
|
886
|
+
setStoredKeys((prev) => {
|
|
887
|
+
const next = new Set(prev);
|
|
888
|
+
for (const k of writtenKeys) next.add(k);
|
|
889
|
+
return next;
|
|
890
|
+
});
|
|
711
891
|
scheduleCacheInvalidation();
|
|
712
892
|
} catch (err) {
|
|
713
893
|
console.error("[plugin] save failed", err);
|
|
@@ -716,13 +896,13 @@ function PluginSettingsForm({
|
|
|
716
896
|
setSaving(false);
|
|
717
897
|
}
|
|
718
898
|
}
|
|
719
|
-
return /* @__PURE__ */
|
|
720
|
-
/* @__PURE__ */
|
|
721
|
-
/* @__PURE__ */
|
|
722
|
-
/* @__PURE__ */
|
|
899
|
+
return /* @__PURE__ */ jsxs4("form", { onSubmit: save, className: "max-w-2xl space-y-5 rounded-md border p-4", children: [
|
|
900
|
+
/* @__PURE__ */ jsxs4("div", { children: [
|
|
901
|
+
/* @__PURE__ */ jsx4("h2", { className: "text-base font-semibold", children: displayName ? resolveLocalized3(displayName, locale) : instanceId }),
|
|
902
|
+
/* @__PURE__ */ jsx4("p", { className: "text-xs text-muted-foreground", children: instanceId })
|
|
723
903
|
] }),
|
|
724
|
-
fields.length > 0 && /* @__PURE__ */
|
|
725
|
-
fields.map((field) => /* @__PURE__ */
|
|
904
|
+
fields.length > 0 && /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
905
|
+
fields.map((field) => /* @__PURE__ */ jsx4(
|
|
726
906
|
FieldRow,
|
|
727
907
|
{
|
|
728
908
|
field,
|
|
@@ -734,14 +914,14 @@ function PluginSettingsForm({
|
|
|
734
914
|
},
|
|
735
915
|
field.key
|
|
736
916
|
)),
|
|
737
|
-
info && /* @__PURE__ */
|
|
738
|
-
error && /* @__PURE__ */
|
|
739
|
-
/* @__PURE__ */
|
|
917
|
+
info && /* @__PURE__ */ jsx4("p", { className: "text-sm text-muted-foreground", children: info }),
|
|
918
|
+
error && /* @__PURE__ */ jsx4("p", { className: "text-sm text-destructive", children: error }),
|
|
919
|
+
/* @__PURE__ */ jsx4(Button2, { type: "submit", disabled: saving, children: saving ? t("plugins.saving") : t("plugins.save") })
|
|
740
920
|
] }),
|
|
741
|
-
secretFields && secretFields.length > 0 && /* @__PURE__ */
|
|
742
|
-
/* @__PURE__ */
|
|
743
|
-
/* @__PURE__ */
|
|
744
|
-
/* @__PURE__ */
|
|
921
|
+
secretFields && secretFields.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "mt-6 space-y-4 border-t pt-4", children: [
|
|
922
|
+
/* @__PURE__ */ jsxs4("div", { children: [
|
|
923
|
+
/* @__PURE__ */ jsxs4("h3", { className: "flex items-center gap-1.5 text-sm font-semibold text-amber-700 dark:text-amber-400", children: [
|
|
924
|
+
/* @__PURE__ */ jsx4(
|
|
745
925
|
"svg",
|
|
746
926
|
{
|
|
747
927
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -749,7 +929,7 @@ function PluginSettingsForm({
|
|
|
749
929
|
fill: "currentColor",
|
|
750
930
|
className: "h-3.5 w-3.5",
|
|
751
931
|
"aria-hidden": "true",
|
|
752
|
-
children: /* @__PURE__ */
|
|
932
|
+
children: /* @__PURE__ */ jsx4(
|
|
753
933
|
"path",
|
|
754
934
|
{
|
|
755
935
|
fillRule: "evenodd",
|
|
@@ -761,12 +941,12 @@ function PluginSettingsForm({
|
|
|
761
941
|
),
|
|
762
942
|
"Secret settings"
|
|
763
943
|
] }),
|
|
764
|
-
/* @__PURE__ */
|
|
944
|
+
/* @__PURE__ */ jsx4("p", { className: "text-xs text-muted-foreground", children: "Values are stored securely and never displayed after saving. They are only accessible by the trusted processor Lambda \u2014 not by the public site or admin UI." })
|
|
765
945
|
] }),
|
|
766
946
|
secretHasValue === null ? (
|
|
767
947
|
// Loading placeholder. Keep layout height stable so the
|
|
768
948
|
// section doesn't jump when the check resolves.
|
|
769
|
-
/* @__PURE__ */
|
|
949
|
+
/* @__PURE__ */ jsx4(
|
|
770
950
|
"div",
|
|
771
951
|
{
|
|
772
952
|
className: "h-10 animate-pulse rounded-md bg-muted/50",
|
|
@@ -774,7 +954,7 @@ function PluginSettingsForm({
|
|
|
774
954
|
"aria-label": t("plugins.loading")
|
|
775
955
|
}
|
|
776
956
|
)
|
|
777
|
-
) : secretFields.map((field) => /* @__PURE__ */
|
|
957
|
+
) : secretFields.map((field) => /* @__PURE__ */ jsx4(
|
|
778
958
|
SecretFieldInput,
|
|
779
959
|
{
|
|
780
960
|
field,
|
|
@@ -791,17 +971,17 @@ function FieldRow({ field, value, invalid, onChange, onReset, hasStoredValue })
|
|
|
791
971
|
const t = useT();
|
|
792
972
|
const locale = useLocale();
|
|
793
973
|
const id = `plugin-${field.key}`;
|
|
794
|
-
const labelEl = /* @__PURE__ */
|
|
974
|
+
const labelEl = /* @__PURE__ */ jsxs4(Label3, { htmlFor: id, className: invalid ? "text-destructive" : void 0, children: [
|
|
795
975
|
resolveLocalized3(field.label, locale),
|
|
796
|
-
field.required && /* @__PURE__ */
|
|
976
|
+
field.required && /* @__PURE__ */ jsx4("span", { className: "ml-1 text-destructive", children: "*" })
|
|
797
977
|
] });
|
|
798
|
-
const description = field.description ? /* @__PURE__ */
|
|
978
|
+
const description = field.description ? /* @__PURE__ */ jsx4("p", { className: "text-xs text-muted-foreground", children: resolveLocalized3(field.description, locale) }) : null;
|
|
799
979
|
const input = renderInput(field, id, value, invalid, onChange);
|
|
800
980
|
const placeholder = renderDefaultHint(field, locale);
|
|
801
|
-
return /* @__PURE__ */
|
|
802
|
-
/* @__PURE__ */
|
|
981
|
+
return /* @__PURE__ */ jsxs4("div", { className: "space-y-1.5", children: [
|
|
982
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between", children: [
|
|
803
983
|
labelEl,
|
|
804
|
-
hasStoredValue && /* @__PURE__ */
|
|
984
|
+
hasStoredValue && /* @__PURE__ */ jsx4(
|
|
805
985
|
"button",
|
|
806
986
|
{
|
|
807
987
|
type: "button",
|
|
@@ -830,116 +1010,12 @@ function renderDefaultHint(field, _locale) {
|
|
|
830
1010
|
}
|
|
831
1011
|
}
|
|
832
1012
|
if (!preview) return null;
|
|
833
|
-
return /* @__PURE__ */
|
|
834
|
-
}
|
|
835
|
-
function renderScalarInput(field, id, value, invalid, onChange) {
|
|
836
|
-
switch (field.type) {
|
|
837
|
-
case "text":
|
|
838
|
-
case "url":
|
|
839
|
-
return /* @__PURE__ */ jsx3(
|
|
840
|
-
Input2,
|
|
841
|
-
{
|
|
842
|
-
id,
|
|
843
|
-
value,
|
|
844
|
-
maxLength: field.type === "text" ? field.maxLength : void 0,
|
|
845
|
-
placeholder: "placeholder" in field ? field.placeholder : void 0,
|
|
846
|
-
onChange: (e) => onChange(e.target.value),
|
|
847
|
-
"aria-invalid": invalid,
|
|
848
|
-
type: field.type === "url" ? "url" : "text"
|
|
849
|
-
}
|
|
850
|
-
);
|
|
851
|
-
case "textarea":
|
|
852
|
-
return /* @__PURE__ */ jsx3(
|
|
853
|
-
Textarea,
|
|
854
|
-
{
|
|
855
|
-
id,
|
|
856
|
-
value,
|
|
857
|
-
rows: field.rows ?? 4,
|
|
858
|
-
maxLength: field.maxLength,
|
|
859
|
-
placeholder: field.placeholder,
|
|
860
|
-
onChange: (e) => onChange(e.target.value),
|
|
861
|
-
"aria-invalid": invalid
|
|
862
|
-
}
|
|
863
|
-
);
|
|
864
|
-
case "code":
|
|
865
|
-
return /* @__PURE__ */ jsxs3("div", { className: "space-y-1", children: [
|
|
866
|
-
field.language && /* @__PURE__ */ jsx3("p", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: field.language }),
|
|
867
|
-
/* @__PURE__ */ jsx3(
|
|
868
|
-
Textarea,
|
|
869
|
-
{
|
|
870
|
-
id,
|
|
871
|
-
value,
|
|
872
|
-
rows: field.rows ?? 8,
|
|
873
|
-
maxLength: field.maxLength,
|
|
874
|
-
placeholder: field.placeholder,
|
|
875
|
-
onChange: (e) => onChange(e.target.value),
|
|
876
|
-
"aria-invalid": invalid,
|
|
877
|
-
className: "font-mono text-xs"
|
|
878
|
-
}
|
|
879
|
-
)
|
|
880
|
-
] });
|
|
881
|
-
case "boolean":
|
|
882
|
-
return /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
883
|
-
/* @__PURE__ */ jsx3(
|
|
884
|
-
"input",
|
|
885
|
-
{
|
|
886
|
-
id,
|
|
887
|
-
type: "checkbox",
|
|
888
|
-
checked: value === "true",
|
|
889
|
-
onChange: (e) => onChange(e.target.checked ? "true" : "false"),
|
|
890
|
-
className: "h-4 w-4"
|
|
891
|
-
}
|
|
892
|
-
),
|
|
893
|
-
/* @__PURE__ */ jsx3("span", { className: "text-xs text-muted-foreground", children: value === "true" ? "on" : "off" })
|
|
894
|
-
] });
|
|
895
|
-
case "number":
|
|
896
|
-
return /* @__PURE__ */ jsx3(
|
|
897
|
-
Input2,
|
|
898
|
-
{
|
|
899
|
-
id,
|
|
900
|
-
type: "number",
|
|
901
|
-
value,
|
|
902
|
-
min: field.min,
|
|
903
|
-
max: field.max,
|
|
904
|
-
step: field.step,
|
|
905
|
-
onChange: (e) => onChange(e.target.value),
|
|
906
|
-
"aria-invalid": invalid
|
|
907
|
-
}
|
|
908
|
-
);
|
|
909
|
-
case "select":
|
|
910
|
-
return /* @__PURE__ */ jsxs3(
|
|
911
|
-
"select",
|
|
912
|
-
{
|
|
913
|
-
id,
|
|
914
|
-
className: "w-full rounded-md border bg-background px-2 py-1.5 text-sm",
|
|
915
|
-
value,
|
|
916
|
-
onChange: (e) => onChange(e.target.value),
|
|
917
|
-
"aria-invalid": invalid,
|
|
918
|
-
children: [
|
|
919
|
-
/* @__PURE__ */ jsx3("option", { value: "", children: "\u2014" }),
|
|
920
|
-
field.options.map((opt) => /* @__PURE__ */ jsx3("option", { value: opt.value, children: typeof opt.label === "string" ? opt.label : opt.value }, opt.value))
|
|
921
|
-
]
|
|
922
|
-
}
|
|
923
|
-
);
|
|
924
|
-
case "json":
|
|
925
|
-
return /* @__PURE__ */ jsx3(
|
|
926
|
-
Textarea,
|
|
927
|
-
{
|
|
928
|
-
id,
|
|
929
|
-
value,
|
|
930
|
-
rows: field.rows ?? 8,
|
|
931
|
-
placeholder: field.placeholder ?? "{}",
|
|
932
|
-
onChange: (e) => onChange(e.target.value),
|
|
933
|
-
"aria-invalid": invalid,
|
|
934
|
-
className: "font-mono text-xs"
|
|
935
|
-
}
|
|
936
|
-
);
|
|
937
|
-
}
|
|
1013
|
+
return /* @__PURE__ */ jsx4("p", { className: "text-xs text-muted-foreground", children: /* @__PURE__ */ jsx4("code", { children: preview }) });
|
|
938
1014
|
}
|
|
939
1015
|
function renderInput(field, id, value, invalid, onChange) {
|
|
940
1016
|
switch (field.type) {
|
|
941
1017
|
case "repeatable":
|
|
942
|
-
return /* @__PURE__ */
|
|
1018
|
+
return /* @__PURE__ */ jsx4(
|
|
943
1019
|
RepeatableFieldEditor,
|
|
944
1020
|
{
|
|
945
1021
|
field,
|
|
@@ -955,6 +1031,5 @@ function renderInput(field, id, value, invalid, onChange) {
|
|
|
955
1031
|
}
|
|
956
1032
|
|
|
957
1033
|
export {
|
|
958
|
-
PluginSettingsForm
|
|
959
|
-
renderScalarInput
|
|
1034
|
+
PluginSettingsForm
|
|
960
1035
|
};
|