@huanlin/dsh-plugin-aigc-canvas 0.1.10 → 0.2.1

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/lib/client.js CHANGED
@@ -8,9 +8,6 @@ window.__ModuleLoader__.load({
8
8
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
9
  let react_jsx_runtime = require("react/jsx-runtime");
10
10
  //#region src/client/api.ts
11
- /**
12
- * Typed fetch wrapper over the /aigc-canvas JSON API.
13
- */
14
11
  /** One wire failure. */
15
12
  var AigcApiError = class extends Error {
16
13
  code;
@@ -64,22 +61,6 @@ window.__ModuleLoader__.load({
64
61
  ...opts
65
62
  }, signal);
66
63
  }
67
- /** Fetch the full runtime config (providers + global settings). */
68
- function fetchConfig(signal) {
69
- return call("config.get", {}, signal);
70
- }
71
- /** Add a new provider. */
72
- function addProvider(provider, signal) {
73
- return call("providers.add", { provider }, signal);
74
- }
75
- /** Update an existing provider. */
76
- function updateProvider(provider, signal) {
77
- return call("providers.update", { provider }, signal);
78
- }
79
- /** Remove a provider by id. */
80
- function removeProvider(id, signal) {
81
- return call("providers.remove", { id }, signal);
82
- }
83
64
  /** Build the media URL for one element's media file (by uuid; the host resolves to the file). */
84
65
  function mediaUrlOf(sessionId, uuid, download = false) {
85
66
  const params = new URLSearchParams({
@@ -1058,8 +1039,28 @@ window.__ModuleLoader__.load({
1058
1039
  });
1059
1040
  }
1060
1041
  //#endregion
1042
+ //#region src/provider-shape.ts
1043
+ /**
1044
+ * Pure provider-id validation shared by the host half (Config / ProviderStore)
1045
+ * and the client settings page. Kept free of schemastery imports so the
1046
+ * client bundle (tsdown purity gate) can inline it.
1047
+ *
1048
+ * @module @huanlin/dsh-plugin-aigc-canvas/provider-shape
1049
+ */
1050
+ /** Provider id pattern: lowercase letters, digits, hyphens; must start with a letter. */
1051
+ const PROVIDER_ID_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
1052
+ /**
1053
+ * Validate a provider id.
1054
+ * @param id - candidate id (as typed into the settings page or passed to the store).
1055
+ * @returns an error message, or undefined when valid.
1056
+ */
1057
+ function validateProviderId(id) {
1058
+ if (id === "") return "provider id is required";
1059
+ if (!PROVIDER_ID_PATTERN.test(id)) return `invalid provider id: ${JSON.stringify(id)} (must be lowercase, hyphenated, start with a letter)`;
1060
+ }
1061
+ //#endregion
1061
1062
  //#region \0dsh-css:D:\Projects\deepseek-harness\dsh-aigc-canvas\src\client\SettingsPage.module.css.mjs
1062
- const css = "/* AIGC canvas settings section, in the settings-panel design language shared\r\n * with ModelsSection / GeneralSection / yet-another-subagent: 14/22 body,\r\n * 12/18 caption, 16/24 title, capsule controls (h36 r18 primary, h28 r14\r\n * secondary), 32px fields, border-l2 hairlines, and the editor as a filled\r\n * module on the panel fill.\r\n *\r\n * Every color resolves through a --dsw-alias-* token (no literal colors). */\r\n\r\n.o6nt8s_section {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 12px;\r\n max-width: 720px;\r\n color: var(--dsw-alias-label-primary);\r\n}\r\n\r\n.o6nt8s_title {\r\n margin: 0;\r\n font-size: 16px;\r\n line-height: 24px;\r\n font-weight: 500;\r\n color: var(--dsw-alias-label-primary);\r\n}\r\n\r\n.o6nt8s_intro {\r\n margin: 0;\r\n font-size: 14px;\r\n line-height: 22px;\r\n color: var(--dsw-alias-label-tertiary);\r\n}\r\n\r\n.o6nt8s_error {\r\n margin: 0;\r\n padding: 8px 12px;\r\n border: 1px solid var(--dsw-alias-state-error-primary);\r\n border-radius: 8px;\r\n background: var(--dsw-alias-interactive-bg-hover-danger);\r\n font-size: 12px;\r\n line-height: 18px;\r\n color: var(--dsw-alias-state-error-primary);\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: 8px;\r\n}\r\n\r\n.o6nt8s_errorDismiss {\r\n flex: none;\r\n border: none;\r\n background: transparent;\r\n color: inherit;\r\n font-size: 16px;\r\n line-height: 1;\r\n cursor: pointer;\r\n padding: 0 4px;\r\n}\r\n\r\n.o6nt8s_rows {\r\n list-style: none;\r\n margin: 12px 0 0;\r\n padding: 0;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n}\r\n\r\n/* A configured provider: outlined on the panel fill, matching the rowCard\r\n * chrome in ModelsSection. */\r\n.o6nt8s_rowCard {\r\n border: 1px solid var(--dsw-alias-border-l2);\r\n border-radius: 12px;\r\n padding: 12px 14px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 12px;\r\n}\r\n\r\n.o6nt8s_rowHead {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n}\r\n\r\n/* Chevron toggle: a small square with two borders, rotated to point right\r\n * (collapsed) or down (expanded). Pure CSS, no icon font. */\r\n.o6nt8s_chevronButton {\r\n flex: none;\r\n width: 24px;\r\n height: 24px;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n border: none;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary);\r\n cursor: pointer;\r\n padding: 0;\r\n border-radius: 4px;\r\n}\r\n\r\n.o6nt8s_chevronButton:hover {\r\n background: var(--dsw-alias-interactive-bg-hover-solid);\r\n color: var(--dsw-alias-label-primary);\r\n}\r\n\r\n/* Spacer that occupies the chevron slot on cards without a toggle (e.o6nt8s_g. the\r\n * new-draft card, which is always expanded). Keeps row-head alignment. */\r\n.o6nt8s_chevronSpacer {\r\n flex: none;\r\n width: 24px;\r\n height: 24px;\r\n}\r\n\r\n.o6nt8s_chevron {\r\n width: 7px;\r\n height: 7px;\r\n border-right: 1.5px solid currentColor;\r\n border-bottom: 1.5px solid currentColor;\r\n transform: rotate(-45deg);\r\n transition: transform 0.15s ease;\r\n}\r\n\r\n.o6nt8s_chevronExpanded {\r\n transform: rotate(45deg);\r\n}\r\n\r\n.o6nt8s_rowIdentity {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 6px;\r\n min-width: 0;\r\n flex: 1 1 auto;\r\n flex-wrap: wrap;\r\n}\r\n\r\n.o6nt8s_rowName {\r\n font-size: 14px;\r\n line-height: 22px;\r\n font-weight: 500;\r\n color: var(--dsw-alias-label-primary);\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.o6nt8s_rowNamePlaceholder {\r\n font-size: 14px;\r\n line-height: 22px;\r\n font-weight: 500;\r\n color: var(--dsw-alias-label-tertiary);\r\n font-style: italic;\r\n}\r\n\r\n/* Builtin badge: uses DSH <Pill> with a brand-colored override to mark\r\n * seed providers. */\r\n.o6nt8s_builtinBadge {\r\n height: 18px;\r\n padding: 0 6px;\r\n border-radius: 4px;\r\n font-size: 11px;\r\n line-height: 16px;\r\n font-weight: 500;\r\n background: var(--dsw-alias-brand-primary);\r\n color: var(--dsw-alias-label-primary-foreground);\r\n letter-spacing: 0.02em;\r\n}\r\n\r\n/* Default provider badge. */\r\n.o6nt8s_defaultBadge {\r\n height: 18px;\r\n padding: 0 6px;\r\n border-radius: 4px;\r\n font-size: 11px;\r\n line-height: 16px;\r\n font-weight: 500;\r\n background: var(--dsw-alias-bg-layer-2);\r\n color: var(--dsw-alias-label-secondary);\r\n}\r\n\r\n/* Stub/real mode badge. */\r\n.o6nt8s_stubBadge {\r\n height: 18px;\r\n padding: 0 6px;\r\n border-radius: 4px;\r\n font-size: 11px;\r\n line-height: 16px;\r\n font-weight: 500;\r\n background: var(--dsw-alias-bg-layer-2);\r\n color: var(--dsw-alias-label-tertiary);\r\n}\r\n\r\n.o6nt8s_realBadge {\r\n height: 18px;\r\n padding: 0 6px;\r\n border-radius: 4px;\r\n font-size: 11px;\r\n line-height: 16px;\r\n font-weight: 500;\r\n background: var(--dsw-alias-state-success-primary);\r\n color: var(--dsw-alias-label-primary-foreground);\r\n}\r\n\r\n.o6nt8s_rowId {\r\n flex: none;\r\n padding: 1px 6px;\r\n border: 1px solid var(--dsw-alias-border-l3);\r\n border-radius: 4px;\r\n font-size: 11px;\r\n line-height: 16px;\r\n font-family: var(--dsw-font-markdown-code-block-small, monospace);\r\n color: var(--dsw-alias-label-secondary);\r\n}\r\n\r\n.o6nt8s_rowActions {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 4px;\r\n margin-left: auto;\r\n flex: none;\r\n}\r\n\r\n/* Editor surface: a filled module on the panel, matching ModelsSection's\r\n * editor chrome (bg-module-platform, r12, p14/16). */\r\n.o6nt8s_editor {\r\n border-radius: 12px;\r\n background: var(--dsw-alias-bg-module-platform);\r\n padding: 14px 16px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 14px;\r\n}\r\n\r\n.o6nt8s_field {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n}\r\n\r\n.o6nt8s_fieldLabel {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 10px;\r\n font-size: 12px;\r\n line-height: 18px;\r\n font-weight: 500;\r\n color: var(--dsw-alias-label-secondary);\r\n}\r\n\r\n/* Input: matching ModelsSection .o6nt8s_input — h32, r8, border-l2, bg-layer-1. */\r\n.o6nt8s_input {\r\n box-sizing: border-box;\r\n width: 100%;\r\n height: 32px;\r\n padding: 0 10px;\r\n border: 1px solid var(--dsw-alias-border-l2);\r\n border-radius: 8px;\r\n font: inherit;\r\n font-size: 14px;\r\n line-height: 22px;\r\n background: var(--dsw-alias-bg-layer-1);\r\n color: var(--dsw-alias-label-primary);\r\n}\r\n\r\n.o6nt8s_input:focus {\r\n outline: none;\r\n border-color: var(--dsw-alias-brand-primary);\r\n}\r\n\r\n.o6nt8s_input::placeholder {\r\n color: var(--dsw-alias-label-dimmed);\r\n}\r\n\r\n.o6nt8s_input:disabled {\r\n opacity: 0.6;\r\n cursor: default;\r\n}\r\n\r\n.o6nt8s_textarea {\r\n box-sizing: border-box;\r\n width: 100%;\r\n min-height: 64px;\r\n padding: 6px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2);\r\n border-radius: 8px;\r\n font: inherit;\r\n font-size: 14px;\r\n line-height: 22px;\r\n background: var(--dsw-alias-bg-layer-1);\r\n color: var(--dsw-alias-label-primary);\r\n resize: vertical;\r\n}\r\n\r\n.o6nt8s_textarea:focus {\r\n outline: none;\r\n border-color: var(--dsw-alias-brand-primary);\r\n}\r\n\r\n.o6nt8s_textarea::placeholder {\r\n color: var(--dsw-alias-label-dimmed);\r\n}\r\n\r\n/* Auth scheme row: scheme select + optional name input side by side. */\r\n.o6nt8s_authRow {\r\n display: flex;\r\n gap: 8px;\r\n}\r\n\r\n.o6nt8s_authRow .o6nt8s_input {\r\n flex: 1 1 auto;\r\n}\r\n\r\n/* Select: same chrome as .o6nt8s_input. */\r\n.o6nt8s_select {\r\n box-sizing: border-box;\r\n flex: 0 0 auto;\r\n min-width: 130px;\r\n height: 32px;\r\n padding: 0 10px;\r\n border: 1px solid var(--dsw-alias-border-l2);\r\n border-radius: 8px;\r\n font: inherit;\r\n font-size: 14px;\r\n line-height: 22px;\r\n background: var(--dsw-alias-bg-layer-1);\r\n color: var(--dsw-alias-label-primary);\r\n}\r\n\r\n.o6nt8s_select:focus {\r\n outline: none;\r\n border-color: var(--dsw-alias-brand-primary);\r\n}\r\n\r\n/* Caption labels under fields. */\r\n.o6nt8s_desc {\r\n font-size: 12px;\r\n line-height: 18px;\r\n color: var(--dsw-alias-label-tertiary);\r\n}\r\n\r\n.o6nt8s_hint {\r\n font-size: 12px;\r\n line-height: 18px;\r\n color: var(--dsw-alias-label-secondary);\r\n}\r\n\r\n/* Buttons: capsule controls matching ModelsSection (h36 r18 primary,\r\n * h28 r14 secondary in row context). */\r\n.o6nt8s_primaryButton,\r\n.o6nt8s_secondaryButton,\r\n.o6nt8s_dangerButton,\r\n.o6nt8s_addBlockButton {\r\n box-sizing: border-box;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 4px;\r\n height: 36px;\r\n padding: 0 14px;\r\n border: none;\r\n border-radius: 18px;\r\n font: inherit;\r\n font-size: 14px;\r\n line-height: 22px;\r\n cursor: pointer;\r\n}\r\n\r\n.o6nt8s_primaryButton {\r\n background: var(--dsw-alias-button-primary-fill);\r\n color: var(--dsw-alias-label-primary-foreground);\r\n}\r\n\r\n.o6nt8s_primaryButton:hover:not(:disabled) {\r\n background: var(--dsw-alias-button-primary-hover);\r\n}\r\n\r\n.o6nt8s_secondaryButton,\r\n.o6nt8s_addBlockButton {\r\n border: 1px solid var(--dsw-alias-border-l2);\r\n background: transparent;\r\n color: var(--dsw-alias-label-primary);\r\n}\r\n\r\n.o6nt8s_secondaryButton:hover:not(:disabled),\r\n.o6nt8s_addBlockButton:hover:not(:disabled) {\r\n background: var(--dsw-alias-interactive-bg-hover-solid);\r\n}\r\n\r\n.o6nt8s_dangerButton {\r\n background: transparent;\r\n color: var(--dsw-alias-state-error-primary);\r\n}\r\n\r\n.o6nt8s_dangerButton:hover:not(:disabled) {\r\n background: var(--dsw-alias-interactive-bg-hover-danger);\r\n}\r\n\r\n/* Row-context buttons go dense (h28 r14, 12/18). */\r\n.o6nt8s_rowActions .o6nt8s_secondaryButton,\r\n.o6nt8s_rowActions .o6nt8s_dangerButton {\r\n height: 28px;\r\n padding: 0 10px;\r\n border-radius: 14px;\r\n font-size: 12px;\r\n line-height: 18px;\r\n}\r\n\r\n.o6nt8s_rowActions .o6nt8s_primaryButton {\r\n height: 28px;\r\n padding: 0 10px;\r\n border-radius: 14px;\r\n font-size: 12px;\r\n line-height: 18px;\r\n}\r\n\r\n.o6nt8s_primaryButton:disabled,\r\n.o6nt8s_secondaryButton:disabled,\r\n.o6nt8s_dangerButton:disabled,\r\n.o6nt8s_addBlockButton:disabled {\r\n opacity: 0.4;\r\n cursor: default;\r\n}\r\n\r\n.o6nt8s_primaryButton:focus-visible,\r\n.o6nt8s_secondaryButton:focus-visible,\r\n.o6nt8s_dangerButton:focus-visible,\r\n.o6nt8s_addBlockButton:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\r\n}\r\n\r\n/* Add-provider action: a full-width dashed-outline place card matching\r\n * ModelsSection's addBlock. */\r\n.o6nt8s_addBlockButton {\r\n width: 100%;\r\n margin-top: 12px;\r\n border-style: dashed;\r\n border-radius: 12px;\r\n height: 40px;\r\n color: var(--dsw-alias-label-secondary);\r\n}\r\n\r\n.o6nt8s_addBlockButton:hover:not(:disabled) {\r\n color: var(--dsw-alias-label-primary);\r\n border-color: var(--dsw-alias-brand-primary);\r\n}\r\n\r\n/* Empty state. */\r\n.o6nt8s_empty {\r\n margin: 0;\r\n padding: 24px 12px;\r\n text-align: center;\r\n font-size: 14px;\r\n line-height: 22px;\r\n color: var(--dsw-alias-label-tertiary);\r\n}\r\n\r\n/* Modal confirm body text. */\r\n.o6nt8s_confirmText {\r\n margin: 0;\r\n font-size: 14px;\r\n line-height: 22px;\r\n color: var(--dsw-alias-label-secondary);\r\n}\r\n\r\n.o6nt8s_loading {\r\n padding: 12px;\r\n font-size: 14px;\r\n line-height: 22px;\r\n color: var(--dsw-alias-label-tertiary);\r\n}\r\n";
1063
+ const css = "/* AIGC canvas row-configuration page, in the design language shared with\r\n * ModelsSection / GeneralSection / yet-another-subagent: 14/22 body,\r\n * 12/18 caption, capsule controls (h36 r18 primary, h28 r14\r\n * secondary), 32px fields, border-l2 hairlines, and the editor as a filled\r\n * module on the panel fill.\r\n *\r\n * Every color resolves through a --dsw-alias-* token (no literal colors). */\r\n\r\n.o6nt8s_section {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 12px;\r\n max-width: 720px;\r\n color: var(--dsw-alias-label-primary);\r\n}\r\n\r\n.o6nt8s_intro {\r\n margin: 0;\r\n font-size: 14px;\r\n line-height: 22px;\r\n color: var(--dsw-alias-label-tertiary);\r\n}\r\n\r\n.o6nt8s_error {\r\n margin: 0;\r\n padding: 8px 12px;\r\n border: 1px solid var(--dsw-alias-state-error-primary);\r\n border-radius: 8px;\r\n background: var(--dsw-alias-interactive-bg-hover-danger);\r\n font-size: 12px;\r\n line-height: 18px;\r\n color: var(--dsw-alias-state-error-primary);\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: 8px;\r\n}\r\n\r\n.o6nt8s_errorDismiss {\r\n flex: none;\r\n border: none;\r\n background: transparent;\r\n color: inherit;\r\n font-size: 16px;\r\n line-height: 1;\r\n cursor: pointer;\r\n padding: 0 4px;\r\n}\r\n\r\n.o6nt8s_rows {\r\n list-style: none;\r\n margin: 12px 0 0;\r\n padding: 0;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n}\r\n\r\n/* A configured provider: outlined on the panel fill, matching the rowCard\r\n * chrome in ModelsSection. */\r\n.o6nt8s_rowCard {\r\n border: 1px solid var(--dsw-alias-border-l2);\r\n border-radius: 12px;\r\n padding: 12px 14px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 12px;\r\n}\r\n\r\n.o6nt8s_rowHead {\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n}\r\n\r\n/* Chevron toggle: a small square with two borders, rotated to point right\r\n * (collapsed) or down (expanded). Pure CSS, no icon font. */\r\n.o6nt8s_chevronButton {\r\n flex: none;\r\n width: 24px;\r\n height: 24px;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n border: none;\r\n background: transparent;\r\n color: var(--dsw-alias-label-secondary);\r\n cursor: pointer;\r\n padding: 0;\r\n border-radius: 4px;\r\n}\r\n\r\n.o6nt8s_chevronButton:hover {\r\n background: var(--dsw-alias-interactive-bg-hover-solid);\r\n color: var(--dsw-alias-label-primary);\r\n}\r\n\r\n/* Spacer that occupies the chevron slot on cards without a toggle (e.o6nt8s_g. the\r\n * new-draft card, which is always expanded). Keeps row-head alignment. */\r\n.o6nt8s_chevronSpacer {\r\n flex: none;\r\n width: 24px;\r\n height: 24px;\r\n}\r\n\r\n.o6nt8s_chevron {\r\n width: 7px;\r\n height: 7px;\r\n border-right: 1.5px solid currentColor;\r\n border-bottom: 1.5px solid currentColor;\r\n transform: rotate(-45deg);\r\n transition: transform 0.15s ease;\r\n}\r\n\r\n.o6nt8s_chevronExpanded {\r\n transform: rotate(45deg);\r\n}\r\n\r\n.o6nt8s_rowIdentity {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 6px;\r\n min-width: 0;\r\n flex: 1 1 auto;\r\n flex-wrap: wrap;\r\n}\r\n\r\n.o6nt8s_rowName {\r\n font-size: 14px;\r\n line-height: 22px;\r\n font-weight: 500;\r\n color: var(--dsw-alias-label-primary);\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.o6nt8s_rowNamePlaceholder {\r\n font-size: 14px;\r\n line-height: 22px;\r\n font-weight: 500;\r\n color: var(--dsw-alias-label-tertiary);\r\n font-style: italic;\r\n}\r\n\r\n/* Builtin badge: uses DSH <Pill> with a brand-colored override to mark\r\n * seed providers. */\r\n.o6nt8s_builtinBadge {\r\n height: 18px;\r\n padding: 0 6px;\r\n border-radius: 4px;\r\n font-size: 11px;\r\n line-height: 16px;\r\n font-weight: 500;\r\n background: var(--dsw-alias-brand-primary);\r\n color: var(--dsw-alias-label-primary-foreground);\r\n letter-spacing: 0.02em;\r\n}\r\n\r\n/* Default provider badge. */\r\n.o6nt8s_defaultBadge {\r\n height: 18px;\r\n padding: 0 6px;\r\n border-radius: 4px;\r\n font-size: 11px;\r\n line-height: 16px;\r\n font-weight: 500;\r\n background: var(--dsw-alias-bg-layer-2);\r\n color: var(--dsw-alias-label-secondary);\r\n}\r\n\r\n/* Stub/real mode badge. */\r\n.o6nt8s_stubBadge {\r\n height: 18px;\r\n padding: 0 6px;\r\n border-radius: 4px;\r\n font-size: 11px;\r\n line-height: 16px;\r\n font-weight: 500;\r\n background: var(--dsw-alias-bg-layer-2);\r\n color: var(--dsw-alias-label-tertiary);\r\n}\r\n\r\n.o6nt8s_realBadge {\r\n height: 18px;\r\n padding: 0 6px;\r\n border-radius: 4px;\r\n font-size: 11px;\r\n line-height: 16px;\r\n font-weight: 500;\r\n background: var(--dsw-alias-state-success-primary);\r\n color: var(--dsw-alias-label-primary-foreground);\r\n}\r\n\r\n.o6nt8s_rowId {\r\n flex: none;\r\n padding: 1px 6px;\r\n border: 1px solid var(--dsw-alias-border-l3);\r\n border-radius: 4px;\r\n font-size: 11px;\r\n line-height: 16px;\r\n font-family: var(--dsw-font-markdown-code-block-small, monospace);\r\n color: var(--dsw-alias-label-secondary);\r\n}\r\n\r\n.o6nt8s_rowActions {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 4px;\r\n margin-left: auto;\r\n flex: none;\r\n}\r\n\r\n/* Editor surface: a filled module on the panel, matching ModelsSection's\r\n * editor chrome (bg-module-platform, r12, p14/16). */\r\n.o6nt8s_editor {\r\n border-radius: 12px;\r\n background: var(--dsw-alias-bg-module-platform);\r\n padding: 14px 16px;\r\n display: flex;\r\n flex-direction: column;\r\n gap: 14px;\r\n}\r\n\r\n.o6nt8s_field {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n}\r\n\r\n.o6nt8s_fieldLabel {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: 10px;\r\n font-size: 12px;\r\n line-height: 18px;\r\n font-weight: 500;\r\n color: var(--dsw-alias-label-secondary);\r\n}\r\n\r\n/* Input: matching ModelsSection .o6nt8s_input — h32, r8, border-l2, bg-layer-1. */\r\n.o6nt8s_input {\r\n box-sizing: border-box;\r\n width: 100%;\r\n height: 32px;\r\n padding: 0 10px;\r\n border: 1px solid var(--dsw-alias-border-l2);\r\n border-radius: 8px;\r\n font: inherit;\r\n font-size: 14px;\r\n line-height: 22px;\r\n background: var(--dsw-alias-bg-layer-1);\r\n color: var(--dsw-alias-label-primary);\r\n}\r\n\r\n.o6nt8s_input:focus {\r\n outline: none;\r\n border-color: var(--dsw-alias-brand-primary);\r\n}\r\n\r\n.o6nt8s_input::placeholder {\r\n color: var(--dsw-alias-label-dimmed);\r\n}\r\n\r\n.o6nt8s_input:disabled {\r\n opacity: 0.6;\r\n cursor: default;\r\n}\r\n\r\n.o6nt8s_textarea {\r\n box-sizing: border-box;\r\n width: 100%;\r\n min-height: 64px;\r\n padding: 6px 10px;\r\n border: 1px solid var(--dsw-alias-border-l2);\r\n border-radius: 8px;\r\n font: inherit;\r\n font-size: 14px;\r\n line-height: 22px;\r\n background: var(--dsw-alias-bg-layer-1);\r\n color: var(--dsw-alias-label-primary);\r\n resize: vertical;\r\n}\r\n\r\n.o6nt8s_textarea:focus {\r\n outline: none;\r\n border-color: var(--dsw-alias-brand-primary);\r\n}\r\n\r\n.o6nt8s_textarea::placeholder {\r\n color: var(--dsw-alias-label-dimmed);\r\n}\r\n\r\n/* Auth scheme row: scheme select + optional name input side by side. */\r\n.o6nt8s_authRow {\r\n display: flex;\r\n gap: 8px;\r\n}\r\n\r\n.o6nt8s_authRow .o6nt8s_input {\r\n flex: 1 1 auto;\r\n}\r\n\r\n/* Select: same chrome as .o6nt8s_input. */\r\n.o6nt8s_select {\r\n box-sizing: border-box;\r\n flex: 0 0 auto;\r\n min-width: 130px;\r\n height: 32px;\r\n padding: 0 10px;\r\n border: 1px solid var(--dsw-alias-border-l2);\r\n border-radius: 8px;\r\n font: inherit;\r\n font-size: 14px;\r\n line-height: 22px;\r\n background: var(--dsw-alias-bg-layer-1);\r\n color: var(--dsw-alias-label-primary);\r\n}\r\n\r\n.o6nt8s_select:focus {\r\n outline: none;\r\n border-color: var(--dsw-alias-brand-primary);\r\n}\r\n\r\n/* Caption labels under fields. */\r\n.o6nt8s_desc {\r\n font-size: 12px;\r\n line-height: 18px;\r\n color: var(--dsw-alias-label-tertiary);\r\n}\r\n\r\n.o6nt8s_hint {\r\n font-size: 12px;\r\n line-height: 18px;\r\n color: var(--dsw-alias-label-secondary);\r\n}\r\n\r\n/* Buttons: capsule controls matching ModelsSection (h36 r18 primary,\r\n * h28 r14 secondary in row context). */\r\n.o6nt8s_primaryButton,\r\n.o6nt8s_secondaryButton,\r\n.o6nt8s_dangerButton,\r\n.o6nt8s_addBlockButton {\r\n box-sizing: border-box;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n gap: 4px;\r\n height: 36px;\r\n padding: 0 14px;\r\n border: none;\r\n border-radius: 18px;\r\n font: inherit;\r\n font-size: 14px;\r\n line-height: 22px;\r\n cursor: pointer;\r\n}\r\n\r\n.o6nt8s_primaryButton {\r\n background: var(--dsw-alias-button-primary-fill);\r\n color: var(--dsw-alias-label-primary-foreground);\r\n}\r\n\r\n.o6nt8s_primaryButton:hover:not(:disabled) {\r\n background: var(--dsw-alias-button-primary-hover);\r\n}\r\n\r\n.o6nt8s_secondaryButton,\r\n.o6nt8s_addBlockButton {\r\n border: 1px solid var(--dsw-alias-border-l2);\r\n background: transparent;\r\n color: var(--dsw-alias-label-primary);\r\n}\r\n\r\n.o6nt8s_secondaryButton:hover:not(:disabled),\r\n.o6nt8s_addBlockButton:hover:not(:disabled) {\r\n background: var(--dsw-alias-interactive-bg-hover-solid);\r\n}\r\n\r\n.o6nt8s_dangerButton {\r\n background: transparent;\r\n color: var(--dsw-alias-state-error-primary);\r\n}\r\n\r\n.o6nt8s_dangerButton:hover:not(:disabled) {\r\n background: var(--dsw-alias-interactive-bg-hover-danger);\r\n}\r\n\r\n/* Row-context buttons go dense (h28 r14, 12/18). */\r\n.o6nt8s_rowActions .o6nt8s_secondaryButton,\r\n.o6nt8s_rowActions .o6nt8s_dangerButton {\r\n height: 28px;\r\n padding: 0 10px;\r\n border-radius: 14px;\r\n font-size: 12px;\r\n line-height: 18px;\r\n}\r\n\r\n.o6nt8s_rowActions .o6nt8s_primaryButton {\r\n height: 28px;\r\n padding: 0 10px;\r\n border-radius: 14px;\r\n font-size: 12px;\r\n line-height: 18px;\r\n}\r\n\r\n.o6nt8s_primaryButton:disabled,\r\n.o6nt8s_secondaryButton:disabled,\r\n.o6nt8s_dangerButton:disabled,\r\n.o6nt8s_addBlockButton:disabled {\r\n opacity: 0.4;\r\n cursor: default;\r\n}\r\n\r\n.o6nt8s_primaryButton:focus-visible,\r\n.o6nt8s_secondaryButton:focus-visible,\r\n.o6nt8s_dangerButton:focus-visible,\r\n.o6nt8s_addBlockButton:focus-visible {\r\n outline: none;\r\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\r\n}\r\n\r\n/* Add-provider action: a full-width dashed-outline place card matching\r\n * ModelsSection's addBlock. */\r\n.o6nt8s_addBlockButton {\r\n width: 100%;\r\n margin-top: 12px;\r\n border-style: dashed;\r\n border-radius: 12px;\r\n height: 40px;\r\n color: var(--dsw-alias-label-secondary);\r\n}\r\n\r\n.o6nt8s_addBlockButton:hover:not(:disabled) {\r\n color: var(--dsw-alias-label-primary);\r\n border-color: var(--dsw-alias-brand-primary);\r\n}\r\n\r\n/* Empty state. */\r\n.o6nt8s_empty {\r\n margin: 0;\r\n padding: 24px 12px;\r\n text-align: center;\r\n font-size: 14px;\r\n line-height: 22px;\r\n color: var(--dsw-alias-label-tertiary);\r\n}\r\n\r\n/* Modal confirm body text. */\r\n.o6nt8s_confirmText {\r\n margin: 0;\r\n font-size: 14px;\r\n line-height: 22px;\r\n color: var(--dsw-alias-label-secondary);\r\n}\r\n\r\n.o6nt8s_loading {\r\n padding: 12px;\r\n font-size: 14px;\r\n line-height: 22px;\r\n color: var(--dsw-alias-label-tertiary);\r\n}\r\n";
1063
1064
  const tagId = "@huanlin/dsh-plugin-aigc-canvas/SettingsPage.module.css";
1064
1065
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
1065
1066
  const tag = document.createElement("style");
@@ -1070,7 +1071,6 @@ window.__ModuleLoader__.load({
1070
1071
  }
1071
1072
  var SettingsPage_module_css_default = {
1072
1073
  "section": "o6nt8s_section",
1073
- "title": "o6nt8s_title",
1074
1074
  "intro": "o6nt8s_intro",
1075
1075
  "error": "o6nt8s_error",
1076
1076
  "errorDismiss": "o6nt8s_errorDismiss",
@@ -1111,7 +1111,23 @@ window.__ModuleLoader__.load({
1111
1111
  //#endregion
1112
1112
  //#region src/client/SettingsPage.tsx
1113
1113
  /**
1114
- * SettingsPage — the AIGC canvas settings section: provider list CRUD.
1114
+ * SettingsPage — the AIGC canvas row-configuration page: provider list CRUD.
1115
+ *
1116
+ * Mounts as the bundle row's `plugins.row.config` entry on the Plugins page
1117
+ * (dsh 0.1.7-rc.1): `view: 'summary'` renders the one-line description the
1118
+ * row page falls back to when the row declares none; `view: 'page'` renders
1119
+ * the editor under the page's own title/breadcrumb. The optional owner
1120
+ * `form` is the page-assembled ConfigPageForm; this editor keeps its own
1121
+ * reactive binding (below), and an undefined `form` means the entry's config
1122
+ * is not served (row freshly disabled) — the editor reports that instead.
1123
+ *
1124
+ * Transport (dsh 0.1.7-rc.1 DSH-0.1.7-J1-27): reads/writes ride the entry's
1125
+ * shared config form (`ctx.configForms.get('dsh-aigc-canvas')`, provided by
1126
+ * the ui-settings base and bound reactively into this component as the
1127
+ * `useAigcSettings` hook through the registration's `hooks` compartment).
1128
+ * Saves are whole-list writes through `form.set('providers', …)`; the host
1129
+ * persists them into the active profile's `cordis.patch.yml` under the
1130
+ * entry id.
1115
1131
  *
1116
1132
  * Visual language: matches ModelsSection / GeneralSection / yet-another-subagent —
1117
1133
  * outlined rowCard per provider (border-l2, r12, p12/14), filled editor surface
@@ -1149,82 +1165,107 @@ window.__ModuleLoader__.load({
1149
1165
  builtin: false
1150
1166
  };
1151
1167
  }
1168
+ /** Clone the committed list into editable drafts (auth object detached). */
1169
+ function toDrafts(providers) {
1170
+ return providers.map((p) => ({
1171
+ ...p,
1172
+ auth: { ...p.auth }
1173
+ }));
1174
+ }
1175
+ /**
1176
+ * Render the AIGC provider configuration entry.
1177
+ *
1178
+ * `view: 'summary'` returns the one-line description; `view: 'page'` renders
1179
+ * the CRUD editor (with its own hook set, in {@link ProviderEditor} below).
1180
+ * Branching before any hook keeps both arms hook-stable.
1181
+ *
1182
+ * @param props - plugins.row.config runtime share + locale + inject + form hook.
1183
+ * @returns the summary one-liner or the page element.
1184
+ */
1185
+ function SettingsPage(props) {
1186
+ if (props.view === "summary") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: props.t("rowSummary") });
1187
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderEditor, { ...props });
1188
+ }
1152
1189
  /**
1153
- * Render the AIGC provider settings page.
1154
- * @param props - settings.section runtime share + locale + inject.
1155
- * @returns the page element.
1190
+ * Render the provider CRUD editor (the `view: 'page'` arm).
1191
+ *
1192
+ * Reads the committed provider list through the entry's config form
1193
+ * (dsh 0.1.7-rc.1 DSH-0.1.7-J1-27: `ctx.configForms.get('dsh-aigc-canvas')`,
1194
+ * bound reactively as `useAigcSettings`) and writes whole-list replacements
1195
+ * through `saveProviders`. Local drafts re-seed from the committed list on
1196
+ * every revision bump — own saves land there, and so do external writers
1197
+ * (e.g. the model's aigc_provider_set_instructions tool).
1198
+ *
1199
+ * @param props - plugins.row.config runtime share + locale + inject + form hook.
1200
+ * @returns the editor element.
1156
1201
  */
1157
- function SettingsPage({ t, send }) {
1158
- const [providers, setProviders] = (0, react.useState)([]);
1202
+ function ProviderEditor({ form, t, send, useAigcSettings, saveProviders }) {
1203
+ const snapshot = useAigcSettings((s) => s);
1204
+ const providers = snapshot.status === "ready" && snapshot.value !== void 0 ? snapshot.value.providers : [];
1159
1205
  const [drafts, setDrafts] = (0, react.useState)([]);
1160
1206
  const [expanded, setExpanded] = (0, react.useState)(/* @__PURE__ */ new Set());
1161
1207
  const [addingNew, setAddingNew] = (0, react.useState)(false);
1162
1208
  const [newDraft, setNewDraft] = (0, react.useState)(emptyDraft());
1163
- const [loading, setLoading] = (0, react.useState)(true);
1164
1209
  const [error, setError] = (0, react.useState)(void 0);
1165
1210
  const [confirmDelete, setConfirmDelete] = (0, react.useState)(void 0);
1166
- const refresh = (0, react.useCallback)(async () => {
1167
- setLoading(true);
1211
+ const { status, revision, value } = snapshot;
1212
+ (0, react.useEffect)(() => {
1213
+ if (status !== "ready" || value === void 0) return;
1214
+ setDrafts(toDrafts(value.providers));
1215
+ }, [status, revision]);
1216
+ const commit = (0, react.useCallback)(async (next) => {
1168
1217
  setError(void 0);
1169
1218
  try {
1170
- const result = await fetchConfig();
1171
- setProviders(result.providers);
1172
- setDrafts(result.providers.map((p) => ({
1173
- ...p,
1174
- auth: { ...p.auth }
1175
- })));
1219
+ if (!await saveProviders(next)) {
1220
+ setError(t("settingsSaveFailed"));
1221
+ return false;
1222
+ }
1223
+ return true;
1176
1224
  } catch (err) {
1177
1225
  setError(err instanceof Error ? err.message : String(err));
1178
- } finally {
1179
- setLoading(false);
1226
+ return false;
1180
1227
  }
1181
- }, []);
1182
- (0, react.useEffect)(() => {
1183
- refresh();
1184
- }, [refresh]);
1228
+ }, [saveProviders, t]);
1185
1229
  const add = (0, react.useCallback)(async () => {
1186
1230
  if (newDraft.id === "") return;
1187
- try {
1188
- const result = await addProvider(newDraft);
1189
- setProviders(result.providers);
1190
- setDrafts(result.providers.map((p) => ({
1191
- ...p,
1192
- auth: { ...p.auth }
1193
- })));
1231
+ const idError = validateProviderId(newDraft.id);
1232
+ if (idError !== void 0) {
1233
+ setError(idError);
1234
+ return;
1235
+ }
1236
+ if (providers.some((p) => p.id === newDraft.id)) {
1237
+ setError(`provider id already exists: ${newDraft.id}`);
1238
+ return;
1239
+ }
1240
+ const stored = {
1241
+ ...newDraft,
1242
+ builtin: false
1243
+ };
1244
+ if (await commit([...providers, stored])) {
1194
1245
  setExpanded(/* @__PURE__ */ new Set([...expanded, newDraft.id]));
1195
1246
  setAddingNew(false);
1196
1247
  setNewDraft(emptyDraft());
1197
- } catch (err) {
1198
- setError(err instanceof Error ? err.message : String(err));
1199
1248
  }
1200
- }, [expanded, newDraft]);
1249
+ }, [
1250
+ commit,
1251
+ expanded,
1252
+ newDraft,
1253
+ providers
1254
+ ]);
1201
1255
  const update = (0, react.useCallback)(async (draft) => {
1202
- try {
1203
- const result = await updateProvider(draft);
1204
- setProviders(result.providers);
1205
- setDrafts(result.providers.map((p) => ({
1206
- ...p,
1207
- auth: { ...p.auth }
1208
- })));
1209
- } catch (err) {
1210
- setError(err instanceof Error ? err.message : String(err));
1211
- }
1212
- }, []);
1256
+ await commit(providers.map((p) => p.id === draft.id ? draft : p));
1257
+ }, [commit, providers]);
1213
1258
  const remove = (0, react.useCallback)(async (id) => {
1214
- try {
1215
- const result = await removeProvider(id);
1216
- setProviders(result.providers);
1217
- setDrafts(result.providers.map((p) => ({
1218
- ...p,
1219
- auth: { ...p.auth }
1220
- })));
1259
+ if (await commit(providers.filter((p) => p.id !== id))) {
1221
1260
  const next = new Set(expanded);
1222
1261
  next.delete(id);
1223
1262
  setExpanded(next);
1224
- } catch (err) {
1225
- setError(err instanceof Error ? err.message : String(err));
1226
1263
  }
1227
- }, [expanded]);
1264
+ }, [
1265
+ commit,
1266
+ expanded,
1267
+ providers
1268
+ ]);
1228
1269
  const init = (0, react.useCallback)(async (provider) => {
1229
1270
  const label = provider.name === "" ? provider.id : provider.name;
1230
1271
  const text = t("row.initPrompt").replace("{name}", label).replace("{id}", provider.id);
@@ -1251,13 +1292,16 @@ window.__ModuleLoader__.load({
1251
1292
  setNewDraft(emptyDraft());
1252
1293
  };
1253
1294
  const defaultId = providers.length > 0 ? providers[0]?.id : void 0;
1295
+ if (form === void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
1296
+ className: SettingsPage_module_css_default.section,
1297
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1298
+ className: SettingsPage_module_css_default.empty,
1299
+ children: t("settingsUnavailable")
1300
+ })
1301
+ });
1254
1302
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1255
1303
  className: SettingsPage_module_css_default.section,
1256
1304
  children: [
1257
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1258
- className: SettingsPage_module_css_default.title,
1259
- children: t("settingsTitle")
1260
- }),
1261
1305
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1262
1306
  className: SettingsPage_module_css_default.intro,
1263
1307
  children: t("settingsIntro")
@@ -1271,9 +1315,16 @@ window.__ModuleLoader__.load({
1271
1315
  children: "×"
1272
1316
  })]
1273
1317
  }),
1274
- loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1318
+ status === "ready" && !snapshot.writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1319
+ className: SettingsPage_module_css_default.intro,
1320
+ children: t("settingsReadOnly")
1321
+ }),
1322
+ status === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1275
1323
  className: SettingsPage_module_css_default.loading,
1276
1324
  children: t("settingsLoading")
1325
+ }) : status === "unavailable" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1326
+ className: SettingsPage_module_css_default.empty,
1327
+ children: t("settingsUnavailable")
1277
1328
  }) : providers.length === 0 && !addingNew ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1278
1329
  className: SettingsPage_module_css_default.empty,
1279
1330
  children: t("settingsEmpty")
@@ -1303,7 +1354,7 @@ window.__ModuleLoader__.load({
1303
1354
  onCancel: cancelNew
1304
1355
  }, "__new__")]
1305
1356
  }),
1306
- !loading && !addingNew && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1357
+ status === "ready" && !addingNew && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1307
1358
  type: "button",
1308
1359
  className: SettingsPage_module_css_default.addBlockButton,
1309
1360
  onClick: () => setAddingNew(true),
@@ -1595,7 +1646,7 @@ window.__ModuleLoader__.load({
1595
1646
  tabTitle: "AIGC 画布",
1596
1647
  title: "AIGC 画布",
1597
1648
  empty: "画布是空的。模型通过 aigc_http_request 调用供应商 API 生成素材,再用 aigc_canvas_place 把文件放到画布的任意位置。",
1598
- emptyHint: "可在右侧设置页配置供应商,然后让模型开始生成。",
1649
+ emptyHint: "可在插件页(Plugins)的本插件行配置供应商,然后让模型开始生成。",
1599
1650
  prompt: "提示词",
1600
1651
  image: "图片",
1601
1652
  video: "视频",
@@ -1621,13 +1672,15 @@ window.__ModuleLoader__.load({
1621
1672
  deleteElement: "删除元素",
1622
1673
  dropHint: "拖放文件到画布",
1623
1674
  uploading: "上传中…",
1624
- settingsNav: "AIGC 画布",
1625
- settingsTitle: "AIGC 供应商",
1675
+ rowSummary: "配置 AIGC 生成供应商(API 地址、密钥、鉴权方式、调用说明)。",
1626
1676
  settingsIntro: "配置一个或多个 AIGC 供应商。每个供应商可独立设置名称、API 地址、密钥、鉴权方式和调用说明。模型通过 aigc_get_provider_info 读取供应商列表,用 aigc_http_request 调用 API(自动携带 endpoint 和 apiKey),生成的文件用 aigc_canvas_place 放到画布上。",
1627
1677
  settingsEmpty: "暂无供应商,请在下方添加。",
1628
1678
  settingsAdd: "+ 添加供应商",
1629
1679
  settingsLoading: "加载中…",
1630
1680
  settingsError: "错误",
1681
+ settingsUnavailable: "设置服务不可用(宿主未提供此插件的配置)。",
1682
+ settingsReadOnly: "当前连接不持久化配置,修改将不会保存。",
1683
+ settingsSaveFailed: "保存失败:配置已在其他位置修改,请稍后重试。",
1631
1684
  "row.id": "ID",
1632
1685
  "row.name": "名称",
1633
1686
  "row.endpoint": "API 地址",
@@ -1667,7 +1720,7 @@ window.__ModuleLoader__.load({
1667
1720
  tabTitle: "AIGC Canvas",
1668
1721
  title: "AIGC Canvas",
1669
1722
  empty: "Canvas is empty. The agent calls provider APIs via aigc_http_request and places the generated files anywhere on the canvas with aigc_canvas_place.",
1670
- emptyHint: "Configure a provider in the settings tab on the right, then ask the agent to generate something.",
1723
+ emptyHint: "Configure providers on the Plugins page (this plugin's row), then ask the agent to generate something.",
1671
1724
  prompt: "Prompt",
1672
1725
  image: "Image",
1673
1726
  video: "Video",
@@ -1693,13 +1746,15 @@ window.__ModuleLoader__.load({
1693
1746
  deleteElement: "Delete element",
1694
1747
  dropHint: "Drop files onto canvas",
1695
1748
  uploading: "Uploading…",
1696
- settingsNav: "AIGC Canvas",
1697
- settingsTitle: "AIGC Providers",
1749
+ rowSummary: "Configure AIGC generation providers (API endpoint, key, auth scheme, usage instructions).",
1698
1750
  settingsIntro: "Configure one or more AIGC providers. Each provider has its own name, API endpoint, key, auth scheme, and usage instructions. The agent reads the provider list via aigc_get_provider_info, calls the API via aigc_http_request (endpoint + apiKey attached automatically), and places generated files onto the canvas with aigc_canvas_place.",
1699
1751
  settingsEmpty: "No providers configured. Add one below.",
1700
1752
  settingsAdd: "+ Add provider",
1701
1753
  settingsLoading: "Loading…",
1702
1754
  settingsError: "Error",
1755
+ settingsUnavailable: "Settings are unavailable (the host does not serve this plugin's configuration).",
1756
+ settingsReadOnly: "This connection does not persist configuration; changes will not be saved.",
1757
+ settingsSaveFailed: "Save refused — the configuration changed elsewhere. Please retry.",
1703
1758
  "row.id": "ID",
1704
1759
  "row.name": "Name",
1705
1760
  "row.endpoint": "Endpoint",
@@ -1742,7 +1797,7 @@ window.__ModuleLoader__.load({
1742
1797
  tabTitle: "AIGC キャンバス",
1743
1798
  title: "AIGC キャンバス",
1744
1799
  empty: "キャンバスは空です。エージェントが aigc_http_request でプロバイダー API を呼び出して素材を生成し、aigc_canvas_place で生成されたファイルをキャンバスの任意の場所に配置します。",
1745
- emptyHint: "右側の設定タブでプロバイダーを設定してから、エージェントに生成を依頼してください。",
1800
+ emptyHint: "プラグインページ(Plugins)のこのプラグインの行でプロバイダーを設定してから、エージェントに生成を依頼してください。",
1746
1801
  prompt: "プロンプト",
1747
1802
  image: "画像",
1748
1803
  video: "動画",
@@ -1769,13 +1824,15 @@ window.__ModuleLoader__.load({
1769
1824
  deleteElement: "要素を削除",
1770
1825
  dropHint: "ファイルをキャンバスにドロップ",
1771
1826
  uploading: "アップロード中…",
1772
- settingsNav: "AIGC キャンバス",
1773
- settingsTitle: "AIGC プロバイダー",
1827
+ rowSummary: "AIGC 生成プロバイダーを設定します(API アドレス、キー、認証方式、呼び出し手順)。",
1774
1828
  settingsIntro: "1 つ以上の AIGC プロバイダーを設定します。各プロバイダーに名前、API アドレス、キー、認証方式、呼び出し手順を個別に設定できます。エージェントは aigc_get_provider_info でプロバイダー一覧を読み取り、aigc_http_request で API を呼び出し(endpoint と apiKey は自動的に付与されます)、生成されたファイルは aigc_canvas_place でキャンバスに配置します。",
1775
1829
  settingsEmpty: "プロバイダーが設定されていません。下から追加してください。",
1776
1830
  settingsAdd: "+ プロバイダーを追加",
1777
1831
  settingsLoading: "読み込み中…",
1778
1832
  settingsError: "エラー",
1833
+ settingsUnavailable: "設定サービスを利用できません(ホストがこのプラグインの設定を提供していません)。",
1834
+ settingsReadOnly: "この接続では設定を保存しないため、変更は保存されません。",
1835
+ settingsSaveFailed: "保存できませんでした。設定が他の場所で変更されました。再試行してください。",
1779
1836
  "row.id": "ID",
1780
1837
  "row.name": "名前",
1781
1838
  "row.endpoint": "API アドレス",
@@ -1814,7 +1871,7 @@ window.__ModuleLoader__.load({
1814
1871
  tabTitle: "AIGC-Leinwand",
1815
1872
  title: "AIGC-Leinwand",
1816
1873
  empty: "Die Leinwand ist leer. Der Agent ruft die API des Anbieters über aigc_http_request auf, um Assets zu generieren, und legt die erzeugten Dateien mit aigc_canvas_place an beliebiger Stelle auf der Leinwand ab.",
1817
- emptyHint: "Konfiguriere einen Anbieter im Einstellungs-Tab rechts und bitte den Agenten dann, etwas zu generieren.",
1874
+ emptyHint: "Konfiguriere Anbieter in der Zeile dieses Plugins auf der Plugins-Seite und bitte den Agenten dann, etwas zu generieren.",
1818
1875
  prompt: "Prompt",
1819
1876
  image: "Bild",
1820
1877
  video: "Video",
@@ -1841,13 +1898,15 @@ window.__ModuleLoader__.load({
1841
1898
  deleteElement: "Element löschen",
1842
1899
  dropHint: "Dateien auf die Leinwand ziehen",
1843
1900
  uploading: "Hochladen…",
1844
- settingsNav: "AIGC-Leinwand",
1845
- settingsTitle: "AIGC-Anbieter",
1901
+ rowSummary: "AIGC-Generierungsanbieter konfigurieren (API-Adresse, Schlüssel, Authentifizierung, Aufrufanweisungen).",
1846
1902
  settingsIntro: "Konfiguriere einen oder mehrere AIGC-Anbieter. Jeder Anbieter hat eigene Einstellungen für Name, API-Adresse, Schlüssel, Authentifizierung und Aufrufanweisungen. Der Agent liest die Anbieterliste über aigc_get_provider_info, ruft die API über aigc_http_request auf (endpoint + apiKey werden automatisch angehängt) und legt die generierten Dateien mit aigc_canvas_place auf der Leinwand ab.",
1847
1903
  settingsEmpty: "Keine Anbieter konfiguriert. Füge unten einen hinzu.",
1848
1904
  settingsAdd: "+ Anbieter hinzufügen",
1849
1905
  settingsLoading: "Wird geladen…",
1850
1906
  settingsError: "Fehler",
1907
+ settingsUnavailable: "Einstellungen sind nicht verfügbar (der Host stellt die Konfiguration dieses Plugins nicht bereit).",
1908
+ settingsReadOnly: "Diese Verbindung speichert die Konfiguration nicht; Änderungen werden nicht gespeichert.",
1909
+ settingsSaveFailed: "Speichern abgelehnt — die Konfiguration wurde anderweitig geändert. Bitte erneut versuchen.",
1851
1910
  "row.id": "ID",
1852
1911
  "row.name": "Name",
1853
1912
  "row.endpoint": "API-Adresse",
@@ -1886,7 +1945,7 @@ window.__ModuleLoader__.load({
1886
1945
  tabTitle: "Toile AIGC",
1887
1946
  title: "Toile AIGC",
1888
1947
  empty: "La toile est vide. L’agent appelle les API du fournisseur via aigc_http_request pour générer les ressources, puis place les fichiers générés n’importe où sur la toile avec aigc_canvas_place.",
1889
- emptyHint: "Configurez un fournisseur dans l’onglet des paramètres à droite, puis demandez à l’agent de générer quelque chose.",
1948
+ emptyHint: "Configurez les fournisseurs dans la ligne de ce plugin sur la page Plugins, puis demandez à l’agent de générer quelque chose.",
1890
1949
  prompt: "Invite",
1891
1950
  image: "Image",
1892
1951
  video: "Vidéo",
@@ -1913,13 +1972,15 @@ window.__ModuleLoader__.load({
1913
1972
  deleteElement: "Supprimer l’élément",
1914
1973
  dropHint: "Déposez des fichiers sur la toile",
1915
1974
  uploading: "Téléversement…",
1916
- settingsNav: "Toile AIGC",
1917
- settingsTitle: "Fournisseurs AIGC",
1975
+ rowSummary: "Configurer les fournisseurs de génération AIGC (endpoint API, clé, schéma d’authentification, instructions d’appel).",
1918
1976
  settingsIntro: "Configurez un ou plusieurs fournisseurs AIGC. Chaque fournisseur a son propre nom, endpoint API, clé, schéma d’authentification et instructions d’appel. L’agent lit la liste des fournisseurs via aigc_get_provider_info, appelle l’API via aigc_http_request (endpoint et apiKey attachés automatiquement) et place les fichiers générés sur la toile avec aigc_canvas_place.",
1919
1977
  settingsEmpty: "Aucun fournisseur configuré. Ajoutez-en un ci-dessous.",
1920
1978
  settingsAdd: "+ Ajouter un fournisseur",
1921
1979
  settingsLoading: "Chargement…",
1922
1980
  settingsError: "Erreur",
1981
+ settingsUnavailable: "Les paramètres sont indisponibles (l’hôte ne fournit pas la configuration de ce plugin).",
1982
+ settingsReadOnly: "Cette connexion ne persiste pas la configuration ; les modifications ne seront pas enregistrées.",
1983
+ settingsSaveFailed: "Enregistrement refusé — la configuration a été modifiée ailleurs. Veuillez réessayer.",
1923
1984
  "row.id": "ID",
1924
1985
  "row.name": "Nom",
1925
1986
  "row.endpoint": "Endpoint",
@@ -1958,7 +2019,7 @@ window.__ModuleLoader__.load({
1958
2019
  tabTitle: "Tela AIGC",
1959
2020
  title: "Tela AIGC",
1960
2021
  empty: "A tela está vazia. O agente chama as APIs do provedor via aigc_http_request para gerar os recursos e coloca os arquivos gerados em qualquer posição da tela com aigc_canvas_place.",
1961
- emptyHint: "Configure um provedor na aba de configurações à direita e peça ao agente para gerar algo.",
2022
+ emptyHint: "Configure provedores na linha deste plugin na página Plugins e peça ao agente para gerar algo.",
1962
2023
  prompt: "Prompt",
1963
2024
  image: "Imagem",
1964
2025
  video: "Vídeo",
@@ -1985,13 +2046,15 @@ window.__ModuleLoader__.load({
1985
2046
  deleteElement: "Excluir elemento",
1986
2047
  dropHint: "Solte arquivos na tela",
1987
2048
  uploading: "Enviando…",
1988
- settingsNav: "Tela AIGC",
1989
- settingsTitle: "Provedores AIGC",
2049
+ rowSummary: "Configurar provedores de geração AIGC (endereço da API, chave, autenticação, instruções de chamada).",
1990
2050
  settingsIntro: "Configure um ou mais provedores AIGC. Cada provedor tem configurações independentes de nome, endereço da API, chave, método de autenticação e instruções de chamada. O agente lê a lista de provedores via aigc_get_provider_info, chama a API via aigc_http_request (endpoint e apiKey anexados automaticamente) e coloca os arquivos gerados na tela com aigc_canvas_place.",
1991
2051
  settingsEmpty: "Nenhum provedor configurado. Adicione um abaixo.",
1992
2052
  settingsAdd: "+ Adicionar provedor",
1993
2053
  settingsLoading: "Carregando…",
1994
2054
  settingsError: "Erro",
2055
+ settingsUnavailable: "As configurações estão indisponíveis (o host não serve a configuração deste plugin).",
2056
+ settingsReadOnly: "Esta conexão não persiste a configuração; as alterações não serão salvas.",
2057
+ settingsSaveFailed: "Salvamento recusado — a configuração foi alterada em outro lugar. Tente novamente.",
1995
2058
  "row.id": "ID",
1996
2059
  "row.name": "Nome",
1997
2060
  "row.endpoint": "Endereço da API",
@@ -2030,7 +2093,7 @@ window.__ModuleLoader__.load({
2030
2093
  tabTitle: "AIGC 캔버스",
2031
2094
  title: "AIGC 캔버스",
2032
2095
  empty: "캔버스가 비어 있습니다. 에이전트가 aigc_http_request로 공급자 API를 호출해 자산을 생성한 후, aigc_canvas_place로 생성된 파일을 캔버스의 원하는 위치에 배치합니다.",
2033
- emptyHint: "오른쪽 설정 탭에서 공급자를 구성한 다음 에이전트에게 생성을 요청하세요.",
2096
+ emptyHint: "플러그인(Plugins) 페이지의 이 플러그인 행에서 공급자를 구성한 다음 에이전트에게 생성을 요청하세요.",
2034
2097
  prompt: "프롬프트",
2035
2098
  image: "이미지",
2036
2099
  video: "동영상",
@@ -2057,13 +2120,15 @@ window.__ModuleLoader__.load({
2057
2120
  deleteElement: "요소 삭제",
2058
2121
  dropHint: "캔버스에 파일을 끌어다 놓기",
2059
2122
  uploading: "업로드 중…",
2060
- settingsNav: "AIGC 캔버스",
2061
- settingsTitle: "AIGC 공급자",
2123
+ rowSummary: "AIGC 생성 공급자를 구성합니다(API 주소, 키, 인증 방식, 호출 지침).",
2062
2124
  settingsIntro: "하나 이상의 AIGC 공급자를 구성합니다. 각 공급자에 이름, API 주소, 키, 인증 방식, 호출 지침을 개별적으로 설정할 수 있습니다. 에이전트는 aigc_get_provider_info로 공급자 목록을 읽고, aigc_http_request로 API를 호출하며(endpoint와 apiKey 자동 첨부), 생성된 파일은 aigc_canvas_place로 캔버스에 배치합니다.",
2063
2125
  settingsEmpty: "구성된 공급자가 없습니다. 아래에서 추가하세요.",
2064
2126
  settingsAdd: "+ 공급자 추가",
2065
2127
  settingsLoading: "불러오는 중…",
2066
2128
  settingsError: "오류",
2129
+ settingsUnavailable: "설정 서비스를 사용할 수 없습니다(호스트가 이 플러그인의 구성을 제공하지 않음).",
2130
+ settingsReadOnly: "이 연결은 구성을 저장하지 않으므로 변경 사항이 저장되지 않습니다.",
2131
+ settingsSaveFailed: "저장이 거부되었습니다. 구성이 다른 곳에서 변경되었습니다. 다시 시도하세요.",
2067
2132
  "row.id": "ID",
2068
2133
  "row.name": "이름",
2069
2134
  "row.endpoint": "API 주소",
@@ -2102,7 +2167,7 @@ window.__ModuleLoader__.load({
2102
2167
  tabTitle: "لوحة AIGC",
2103
2168
  title: "لوحة AIGC",
2104
2169
  empty: "اللوحة فارغة. يستدعي الوكيل واجهات برمجة تطبيقات المزوّد عبر aigc_http_request لتوليد الموارد، ثم يضع الملفات المولَّدة في أي موضع على اللوحة باستخدام aigc_canvas_place.",
2105
- emptyHint: "قم بتكوين مزوّد في تبويب الإعدادات على اليمين، ثم اطلب من الوكيل توليد شيء.",
2170
+ emptyHint: "قم بتكوين المزوّدين في صف هذا البرنامج المساعد على صفحة الإضافات (Plugins)، ثم اطلب من الوكيل توليد شيء.",
2106
2171
  prompt: "الموجّه",
2107
2172
  image: "صورة",
2108
2173
  video: "فيديو",
@@ -2129,13 +2194,15 @@ window.__ModuleLoader__.load({
2129
2194
  deleteElement: "حذف العنصر",
2130
2195
  dropHint: "اسحب الملفات إلى اللوحة",
2131
2196
  uploading: "جارٍ الرفع…",
2132
- settingsNav: "لوحة AIGC",
2133
- settingsTitle: "مزوّدو AIGC",
2197
+ rowSummary: "تكوين مزوّدي توليد AIGC (عنوان API، المفتاح، المصادقة، تعليمات الاستدعاء).",
2134
2198
  settingsIntro: "قم بتكوين مزوّد واحد أو أكثر من مزوّدي AIGC. يمكن ضبط اسم وعنوان API ومفتاح وطريقة مصادقة وتعليمات الاستدعاء لكل مزوّد بشكل مستقل. يقرأ الوكيل قائمة المزوّدين عبر aigc_get_provider_info، ويستدعي واجهة API عبر aigc_http_request (مع إرفاق endpoint وapiKey تلقائيًا)، ويضع الملفات المولَّدة على اللوحة باستخدام aigc_canvas_place.",
2135
2199
  settingsEmpty: "لا توجد مزوّدات مكوّنة. أضف واحدًا أدناه.",
2136
2200
  settingsAdd: "+ إضافة مزوّد",
2137
2201
  settingsLoading: "جارٍ التحميل…",
2138
2202
  settingsError: "خطأ",
2203
+ settingsUnavailable: "الإعدادات غير متاحة (لا يوفر المضيف تكوين هذا البرنامج المساعد).",
2204
+ settingsReadOnly: "هذا الاتصال لا يحفظ التكوين؛ لن يتم حفظ التغييرات.",
2205
+ settingsSaveFailed: "تم رفض الحفظ — تم تغيير التكوين في مكان آخر. يرجى المحاولة مرة أخرى.",
2139
2206
  "row.id": "المعرّف",
2140
2207
  "row.name": "الاسم",
2141
2208
  "row.endpoint": "عنوان API",
@@ -2174,7 +2241,7 @@ window.__ModuleLoader__.load({
2174
2241
  tabTitle: "AIGC कैनवास",
2175
2242
  title: "AIGC कैनवास",
2176
2243
  empty: "कैनवास खाली है। एजेंट aigc_http_request के माध्यम से प्रदाता API को कॉल करके एसेट जनरेट करता है, फिर aigc_canvas_place से जनरेट की गई फ़ाइलों को कैनवास पर किसी भी स्थान पर रखता है।",
2177
- emptyHint: "दाईं ओर सेटिंग्स टैब में प्रदाता कॉन्फ़िगर करें, फिर एजेंट से कुछ जनरेट करने के लिए कहें।",
2244
+ emptyHint: "प्लगइन्स (Plugins) पेज पर इस प्लगइन की पंक्ति में प्रदाता कॉन्फ़िगर करें, फिर एजेंट से कुछ जनरेट करने के लिए कहें।",
2178
2245
  prompt: "प्रॉम्प्ट",
2179
2246
  image: "छवि",
2180
2247
  video: "वीडियो",
@@ -2201,13 +2268,15 @@ window.__ModuleLoader__.load({
2201
2268
  deleteElement: "तत्व हटाएं",
2202
2269
  dropHint: "फ़ाइलों को कैनवास पर छोड़ें",
2203
2270
  uploading: "अपलोड किया जा रहा…",
2204
- settingsNav: "AIGC कैनवास",
2205
- settingsTitle: "AIGC प्रदाता",
2271
+ rowSummary: "AIGC जनरेशन प्रदाता कॉन्फ़िगर करें (API पता, कुंजी, प्रमाणीकरण, कॉल निर्देश)।",
2206
2272
  settingsIntro: "एक या अधिक AIGC प्रदाता कॉन्फ़िगर करें। प्रत्येक प्रदाता के लिए नाम, API पता, कुंजी, प्रमाणीकरण विधि और कॉल निर्देश अलग से सेट किए जा सकते हैं। एजेंट aigc_get_provider_info से प्रदाता सूची पढ़ता है, aigc_http_request से API कॉल करता है (endpoint और apiKey स्वतः जुड़ जाते हैं), और जनरेट की गई फ़ाइलें aigc_canvas_place से कैनवास पर रखता है।",
2207
2273
  settingsEmpty: "कोई प्रदाता कॉन्फ़िगर नहीं है। नीचे जोड़ें।",
2208
2274
  settingsAdd: "+ प्रदाता जोड़ें",
2209
2275
  settingsLoading: "लोड हो रहा…",
2210
2276
  settingsError: "त्रुटि",
2277
+ settingsUnavailable: "सेटिंग्स अनुपलब्ध हैं (होस्ट इस प्लगइन का कॉन्फ़िगरेशन प्रदान नहीं करता है)।",
2278
+ settingsReadOnly: "यह कनेक्शन कॉन्फ़िगरेशन सहेजता नहीं है; परिवर्तन सहेजे नहीं जाएंगे।",
2279
+ settingsSaveFailed: "सहेजना अस्वीकार — कॉन्फ़िगरेशन कहीं और बदल गया। कृपया पुनः प्रयास करें।",
2211
2280
  "row.id": "आईडी",
2212
2281
  "row.name": "नाम",
2213
2282
  "row.endpoint": "API पता",
@@ -2246,7 +2315,7 @@ window.__ModuleLoader__.load({
2246
2315
  tabTitle: "Kanvas AIGC",
2247
2316
  title: "Kanvas AIGC",
2248
2317
  empty: "Kanvas kosong. Agen memanggil API penyedia melalui aigc_http_request untuk menghasilkan aset, lalu menempatkan file yang dihasilkan ke posisi mana pun di kanvas dengan aigc_canvas_place.",
2249
- emptyHint: "Konfigurasikan penyedia di tab pengaturan di kanan, lalu minta agen untuk menghasilkan sesuatu.",
2318
+ emptyHint: "Konfigurasikan penyedia di baris plugin ini pada halaman Plugins, lalu minta agen menghasilkan sesuatu.",
2250
2319
  prompt: "Prompt",
2251
2320
  image: "Gambar",
2252
2321
  video: "Video",
@@ -2273,13 +2342,15 @@ window.__ModuleLoader__.load({
2273
2342
  deleteElement: "Hapus elemen",
2274
2343
  dropHint: "Letakkan file di kanvas",
2275
2344
  uploading: "Mengunggah…",
2276
- settingsNav: "Kanvas AIGC",
2277
- settingsTitle: "Penyedia AIGC",
2345
+ rowSummary: "Konfigurasikan penyedia pembuatan AIGC (alamat API, kunci, autentikasi, instruksi pemanggilan).",
2278
2346
  settingsIntro: "Konfigurasikan satu atau lebih penyedia AIGC. Setiap penyedia dapat mengatur nama, alamat API, kunci, metode autentikasi, dan instruksi pemanggilan secara terpisah. Agen membaca daftar penyedia melalui aigc_get_provider_info, memanggil API melalui aigc_http_request (endpoint dan apiKey dilampirkan otomatis), dan file yang dihasilkan diletakkan di kanvas dengan aigc_canvas_place.",
2279
2347
  settingsEmpty: "Belum ada penyedia yang dikonfigurasi. Tambahkan di bawah.",
2280
2348
  settingsAdd: "+ Tambah penyedia",
2281
2349
  settingsLoading: "Memuat…",
2282
2350
  settingsError: "Kesalahan",
2351
+ settingsUnavailable: "Pengaturan tidak tersedia (host tidak menyajikan konfigurasi plugin ini).",
2352
+ settingsReadOnly: "Koneksi ini tidak menyimpan konfigurasi; perubahan tidak akan disimpan.",
2353
+ settingsSaveFailed: "Penyimpanan ditolak — konfigurasi berubah di tempat lain. Coba lagi.",
2283
2354
  "row.id": "ID",
2284
2355
  "row.name": "Nama",
2285
2356
  "row.endpoint": "Alamat API",
@@ -2318,7 +2389,7 @@ window.__ModuleLoader__.load({
2318
2389
  tabTitle: "AIGC Tuvali",
2319
2390
  title: "AIGC Tuvali",
2320
2391
  empty: "Tuval boş. Aracı, aigc_http_request ile sağlayıcı API’lerini çağırıp varlık üretir; ardından aigc_canvas_place ile dosyaları tuvale istediğiniz konuma yerleştirir.",
2321
- emptyHint: "Sağdaki ayarlar sekmesinde bir sağlayıcı yapılandırın, sonra aracıdan bir şey üretmesini isteyin.",
2392
+ emptyHint: "Eklentiler (Plugins) sayfasında bu eklentinin satırında sağlayıcıları yapılandırın, sonra aracıdan bir şey üretmesini isteyin.",
2322
2393
  prompt: "İstek",
2323
2394
  image: "Görsel",
2324
2395
  video: "Video",
@@ -2345,13 +2416,15 @@ window.__ModuleLoader__.load({
2345
2416
  deleteElement: "Öğeyi sil",
2346
2417
  dropHint: "Dosyaları tuvale bırakın",
2347
2418
  uploading: "Yükleniyor…",
2348
- settingsNav: "AIGC Tuvali",
2349
- settingsTitle: "AIGC Sağlayıcıları",
2419
+ rowSummary: "AIGC üretim sağlayıcılarını yapılandırın (API adresi, anahtar, kimlik doğrulama, çağrı talimatları).",
2350
2420
  settingsIntro: "Bir veya daha fazla AIGC sağlayıcısı yapılandırın. Her sağlayıcı için ad, API adresi, anahtar, kimlik doğrulama biçimi ve çağrı talimatları bağımsız olarak ayarlanabilir. Aracı, sağlayıcı listesini aigc_get_provider_info ile okur, API’yi aigc_http_request ile çağırır (endpoint ve apiKey otomatik eklenir) ve üretilen dosyaları aigc_canvas_place ile tuvale yerleştirir.",
2351
2421
  settingsEmpty: "Yapılandırılmış sağlayıcı yok. Aşağıdan ekleyin.",
2352
2422
  settingsAdd: "+ Sağlayıcı ekle",
2353
2423
  settingsLoading: "Yükleniyor…",
2354
2424
  settingsError: "Hata",
2425
+ settingsUnavailable: "Ayarlar kullanılamıyor (ana makine bu eklentinin yapılandırmasını sunmuyor).",
2426
+ settingsReadOnly: "Bu bağlantı yapılandırmayı kalıcı kılmaz; değişiklikler kaydedilmez.",
2427
+ settingsSaveFailed: "Kaydetme reddedildi — yapılandırma başka yerde değişti. Lütfen tekrar deneyin.",
2355
2428
  "row.id": "Kimlik",
2356
2429
  "row.name": "Ad",
2357
2430
  "row.endpoint": "API adresi",
@@ -2390,7 +2463,7 @@ window.__ModuleLoader__.load({
2390
2463
  tabTitle: "Canvas AIGC",
2391
2464
  title: "Canvas AIGC",
2392
2465
  empty: "Canvas đang trống. Agent gọi API của nhà cung cấp qua aigc_http_request để tạo tài nguyên, sau đó dùng aigc_canvas_place đặt tệp đã tạo vào bất kỳ vị trí nào trên canvas.",
2393
- emptyHint: "Cấu hình một nhà cung cấp trong tab cài đặt bên phải, rồi yêu cầu agent tạo nội dung.",
2466
+ emptyHint: "Cấu hình nhà cung cấp trong hàng plugin này trên trang Plugins, rồi yêu cầu agent tạo nội dung.",
2394
2467
  prompt: "Lời nhắc",
2395
2468
  image: "Hình ảnh",
2396
2469
  video: "Video",
@@ -2417,13 +2490,15 @@ window.__ModuleLoader__.load({
2417
2490
  deleteElement: "Xóa phần tử",
2418
2491
  dropHint: "Kéo thả tệp vào canvas",
2419
2492
  uploading: "Đang tải lên…",
2420
- settingsNav: "Canvas AIGC",
2421
- settingsTitle: "Nhà cung cấp AIGC",
2493
+ rowSummary: "Cấu hình nhà cung cấp tạo AIGC (địa chỉ API, khóa, xác thực, hướng dẫn gọi).",
2422
2494
  settingsIntro: "Cấu hình một hoặc nhiều nhà cung cấp AIGC. Mỗi nhà cung cấp có thể đặt riêng tên, địa chỉ API, khóa, phương thức xác thực và hướng dẫn gọi. Agent đọc danh sách nhà cung cấp qua aigc_get_provider_info, gọi API qua aigc_http_request (endpoint và apiKey tự động đính kèm) và đặt tệp đã tạo lên canvas bằng aigc_canvas_place.",
2423
2495
  settingsEmpty: "Chưa có nhà cung cấp nào được cấu hình. Thêm một nhà cung cấp bên dưới.",
2424
2496
  settingsAdd: "+ Thêm nhà cung cấp",
2425
2497
  settingsLoading: "Đang tải…",
2426
2498
  settingsError: "Lỗi",
2499
+ settingsUnavailable: "Cài đặt không khả dụng (máy chủ không cung cấp cấu hình của plugin này).",
2500
+ settingsReadOnly: "Kết nối này không lưu cấu hình; thay đổi sẽ không được lưu.",
2501
+ settingsSaveFailed: "Đã từ chối lưu — cấu hình đã thay đổi ở nơi khác. Vui lòng thử lại.",
2427
2502
  "row.id": "ID",
2428
2503
  "row.name": "Tên",
2429
2504
  "row.endpoint": "Địa chỉ API",
@@ -2462,7 +2537,7 @@ window.__ModuleLoader__.load({
2462
2537
  tabTitle: "แคนวาส AIGC",
2463
2538
  title: "แคนวาส AIGC",
2464
2539
  empty: "แคนวาสว่างเปล่า เอเจนต์เรียก API ของผู้ให้บริการผ่าน aigc_http_request เพื่อสร้างทรัพยากร แล้ววางไฟล์ที่สร้างแล้วบนตำแหน่งใดก็ได้บนแคนวาสด้วย aigc_canvas_place",
2465
- emptyHint: "กำหนดค่าผู้ให้บริการในแท็บการตั้งค่าทางขวา แล้วขอให้เอเจนต์สร้างบางอย่าง",
2540
+ emptyHint: "กำหนดค่าผู้ให้บริการในแถวของปลั๊กอินนี้บนหน้าปลั๊กอิน (Plugins) แล้วให้เอเจนต์สร้างสิ่งที่ต้องการ",
2466
2541
  prompt: "พรอมต์",
2467
2542
  image: "รูปภาพ",
2468
2543
  video: "วิดีโอ",
@@ -2489,13 +2564,15 @@ window.__ModuleLoader__.load({
2489
2564
  deleteElement: "ลบองค์ประกอบ",
2490
2565
  dropHint: "วางไฟล์ลงบนแคนวาส",
2491
2566
  uploading: "กำลังอัปโหลด…",
2492
- settingsNav: "แคนวาส AIGC",
2493
- settingsTitle: "ผู้ให้บริการ AIGC",
2567
+ rowSummary: "กำหนดค่าผู้ให้บริการสร้าง AIGC (ที่อยู่ API, คีย์, การยืนยันตัวตน, คำแนะนำการเรียก)",
2494
2568
  settingsIntro: "กำหนดค่าผู้ให้บริการ AIGC หนึ่งรายขึ้นไป แต่ละรายสามารถตั้งชื่อ ที่อยู่ API คีย์ วิธีการยืนยันตัวตน และคำแนะนำการเรียกได้ด้วยตัวเอง เอเจนต์อ่านรายชื่อผู้ให้บริการผ่าน aigc_get_provider_info เรียก API ผ่าน aigc_http_request (แนบ endpoint และ apiKey อัตโนมัติ) และวางไฟล์ที่สร้างแล้วบนแคนวาสด้วย aigc_canvas_place",
2495
2569
  settingsEmpty: "ยังไม่ได้กำหนดค่าผู้ให้บริการ เพิ่มด้านล่าง",
2496
2570
  settingsAdd: "+ เพิ่มผู้ให้บริการ",
2497
2571
  settingsLoading: "กำลังโหลด…",
2498
2572
  settingsError: "ข้อผิดพลาด",
2573
+ settingsUnavailable: "การตั้งค่าใช้งานไม่ได้ (โฮสต์ไม่ได้ให้ค่าตั้งค่าของปลั๊กอินนี้)",
2574
+ settingsReadOnly: "การเชื่อมต่อนี้ไม่บันทึกค่าตั้งค่า การเปลี่ยนแปลงจะไม่ถูกบันทึก",
2575
+ settingsSaveFailed: "บันทึกไม่สำเร็จ — ค่าตั้งค่าถูกเปลี่ยนที่อื่น โปรดลองอีกครั้ง",
2499
2576
  "row.id": "ID",
2500
2577
  "row.name": "ชื่อ",
2501
2578
  "row.endpoint": "ที่อยู่ API",
@@ -2534,7 +2611,7 @@ window.__ModuleLoader__.load({
2534
2611
  tabTitle: "Холст AIGC",
2535
2612
  title: "Холст AIGC",
2536
2613
  empty: "Холст пуст. Агент вызывает API провайдера через aigc_http_request для генерации ресурсов, затем помещает сгенерированные файлы в любое место холста с помощью aigc_canvas_place.",
2537
- emptyHint: "Настройте провайдера на вкладке настроек справа, затем попросите агента что-нибудь сгенерировать.",
2614
+ emptyHint: "Настройте провайдеров в строке этого плагина на странице Plugins, затем попросите агента что-нибудь сгенерировать.",
2538
2615
  prompt: "Промпт",
2539
2616
  image: "Изображение",
2540
2617
  video: "Видео",
@@ -2561,13 +2638,15 @@ window.__ModuleLoader__.load({
2561
2638
  deleteElement: "Удалить элемент",
2562
2639
  dropHint: "Перетащите файлы на холст",
2563
2640
  uploading: "Загрузка…",
2564
- settingsNav: "Холст AIGC",
2565
- settingsTitle: "Провайдеры AIGC",
2641
+ rowSummary: "Настройка провайдеров генерации AIGC (адрес API, ключ, аутентификация, инструкции вызова).",
2566
2642
  settingsIntro: "Настройте одного или нескольких провайдеров AIGC. Для каждого провайдера можно отдельно задать имя, адрес API, ключ, способ аутентификации и инструкции по вызову. Агент читает список провайдеров через aigc_get_provider_info, вызывает API через aigc_http_request (endpoint и apiKey прикрепляются автоматически) и помещает сгенерированные файлы на холст с помощью aigc_canvas_place.",
2567
2643
  settingsEmpty: "Провайдеры не настроены. Добавьте одного ниже.",
2568
2644
  settingsAdd: "+ Добавить провайдера",
2569
2645
  settingsLoading: "Загрузка…",
2570
2646
  settingsError: "Ошибка",
2647
+ settingsUnavailable: "Настройки недоступны (хост не предоставляет конфигурацию этого плагина).",
2648
+ settingsReadOnly: "Это соединение не сохраняет конфигурацию; изменения не будут сохранены.",
2649
+ settingsSaveFailed: "Сохранение отклонено — конфигурация изменилась в другом месте. Повторите попытку.",
2571
2650
  "row.id": "ID",
2572
2651
  "row.name": "Название",
2573
2652
  "row.endpoint": "Адрес API",
@@ -2606,7 +2685,7 @@ window.__ModuleLoader__.load({
2606
2685
  tabTitle: "Tela AIGC",
2607
2686
  title: "Tela AIGC",
2608
2687
  empty: "La tela è vuota. L’agente chiama le API del fornitore tramite aigc_http_request per generare le risorse, quindi posiziona i file generati in un punto qualsiasi della tela con aigc_canvas_place.",
2609
- emptyHint: "Configura un fornitore nella scheda impostazioni a destra, poi chiedi all’agente di generare qualcosa.",
2688
+ emptyHint: "Configura i provider nella riga di questo plugin nella pagina Plugin, poi chiedi all’agente di generare qualcosa.",
2610
2689
  prompt: "Prompt",
2611
2690
  image: "Immagine",
2612
2691
  video: "Video",
@@ -2633,13 +2712,15 @@ window.__ModuleLoader__.load({
2633
2712
  deleteElement: "Elimina elemento",
2634
2713
  dropHint: "Trascina i file sulla tela",
2635
2714
  uploading: "Caricamento…",
2636
- settingsNav: "Tela AIGC",
2637
- settingsTitle: "Fornitori AIGC",
2715
+ rowSummary: "Configura i provider di generazione AIGC (indirizzo API, chiave, autenticazione, istruzioni di chiamata).",
2638
2716
  settingsIntro: "Configura uno o più fornitori AIGC. Ogni fornitore ha nome, endpoint API, chiave, schema di autenticazione e istruzioni di chiamata propri. L’agente legge l’elenco dei fornitori tramite aigc_get_provider_info, chiama l’API tramite aigc_http_request (endpoint e apiKey allegati automaticamente) e posiziona i file generati sulla tela con aigc_canvas_place.",
2639
2717
  settingsEmpty: "Nessun fornitore configurato. Aggiungine uno qui sotto.",
2640
2718
  settingsAdd: "+ Aggiungi fornitore",
2641
2719
  settingsLoading: "Caricamento…",
2642
2720
  settingsError: "Errore",
2721
+ settingsUnavailable: "Le impostazioni non sono disponibili (l’host non serve la configurazione di questo plugin).",
2722
+ settingsReadOnly: "Questa connessione non salva la configurazione; le modifiche non verranno salvate.",
2723
+ settingsSaveFailed: "Salvataggio rifiutato — la configurazione è cambiata altrove. Riprova.",
2643
2724
  "row.id": "ID",
2644
2725
  "row.name": "Nome",
2645
2726
  "row.endpoint": "Endpoint",
@@ -2678,7 +2759,7 @@ window.__ModuleLoader__.load({
2678
2759
  tabTitle: "AIGC-canvas",
2679
2760
  title: "AIGC-canvas",
2680
2761
  empty: "Het canvas is leeg. De agent roept provider-API’s aan via aigc_http_request om assets te genereren en plaatst de gegenereerde bestanden met aigc_canvas_place op elke gewenste positie op het canvas.",
2681
- emptyHint: "Configureer een provider op het tabblad Instellingen rechts en vraag de agent vervolgens iets te genereren.",
2762
+ emptyHint: "Configureer providers in de rij van deze plugin op de Plugins-pagina en vraag de agent dan iets te genereren.",
2682
2763
  prompt: "Prompt",
2683
2764
  image: "Afbeelding",
2684
2765
  video: "Video",
@@ -2705,13 +2786,15 @@ window.__ModuleLoader__.load({
2705
2786
  deleteElement: "Element verwijderen",
2706
2787
  dropHint: "Sleep bestanden naar het canvas",
2707
2788
  uploading: "Bezig met uploaden…",
2708
- settingsNav: "AIGC-canvas",
2709
- settingsTitle: "AIGC-providers",
2789
+ rowSummary: "AIGC-generatieproviders configureren (API-adres, sleutel, authenticatie, aanroepinstructies).",
2710
2790
  settingsIntro: "Configureer een of meer AIGC-providers. Elke provider kan afzonderlijk worden ingesteld op naam, API-adres, sleutel, authenticatiemethode en aanroepinstructies. De agent leest de providerlijst via aigc_get_provider_info, roept de API aan via aigc_http_request (endpoint en apiKey automatisch toegevoegd) en plaatst de gegenereerde bestanden met aigc_canvas_place op het canvas.",
2711
2791
  settingsEmpty: "Geen providers geconfigureerd. Voeg er hieronder een toe.",
2712
2792
  settingsAdd: "+ Provider toevoegen",
2713
2793
  settingsLoading: "Laden…",
2714
2794
  settingsError: "Fout",
2795
+ settingsUnavailable: "Instellingen zijn niet beschikbaar (de host levert de configuratie van deze plugin niet).",
2796
+ settingsReadOnly: "Deze verbinding slaat de configuratie niet op; wijzigingen worden niet bewaard.",
2797
+ settingsSaveFailed: "Opslaan geweigerd — de configuratie is elders gewijzigd. Probeer het opnieuw.",
2715
2798
  "row.id": "ID",
2716
2799
  "row.name": "Naam",
2717
2800
  "row.endpoint": "API-adres",
@@ -2750,7 +2833,7 @@ window.__ModuleLoader__.load({
2750
2833
  tabTitle: "AIGC-canvas",
2751
2834
  title: "AIGC-canvas",
2752
2835
  empty: "Canvasen är tom. Agenten anropar leverantörens API:er via aigc_http_request för att generera tillgångar och placerar sedan de genererade filerna var som helst på canvasen med aigc_canvas_place.",
2753
- emptyHint: "Konfigurera en leverantör i inställningsfliken till höger och be sedan agenten att generera något.",
2836
+ emptyHint: "Konfigurera leverantörer i denna plugins rad på Plugins-sidan och be sedan agenten generera något.",
2754
2837
  prompt: "Prompt",
2755
2838
  image: "Bild",
2756
2839
  video: "Video",
@@ -2777,13 +2860,15 @@ window.__ModuleLoader__.load({
2777
2860
  deleteElement: "Ta bort element",
2778
2861
  dropHint: "Släpp filer på canvasen",
2779
2862
  uploading: "Laddar upp…",
2780
- settingsNav: "AIGC-canvas",
2781
- settingsTitle: "AIGC-leverantörer",
2863
+ rowSummary: "Konfigurera AIGC-genereringsleverantörer (API-adress, nyckel, autentisering, anropsinstruktioner).",
2782
2864
  settingsIntro: "Konfigurera en eller flera AIGC-leverantörer. Varje leverantör kan ställas in med eget namn, API-adress, nyckel, autentiseringsmetod och anropssinstruktioner. Agenten läser leverantörslistan via aigc_get_provider_info, anropar API:et via aigc_http_request (endpoint och apiKey läggs till automatiskt) och placerar de genererade filerna på canvasen med aigc_canvas_place.",
2783
2865
  settingsEmpty: "Inga leverantörer konfigurerade. Lägg till en nedan.",
2784
2866
  settingsAdd: "+ Lägg till leverantör",
2785
2867
  settingsLoading: "Läser in…",
2786
2868
  settingsError: "Fel",
2869
+ settingsUnavailable: "Inställningarna är inte tillgängliga (värden tillhandahåller inte pluginens konfiguration).",
2870
+ settingsReadOnly: "Denna anslutning sparar inte konfigurationen; ändringar sparas inte.",
2871
+ settingsSaveFailed: "Spara nekades — konfigurationen ändrades någon annanstans. Försök igen.",
2787
2872
  "row.id": "ID",
2788
2873
  "row.name": "Namn",
2789
2874
  "row.endpoint": "API-adress",
@@ -2822,7 +2907,7 @@ window.__ModuleLoader__.load({
2822
2907
  tabTitle: "Płótno AIGC",
2823
2908
  title: "Płótno AIGC",
2824
2909
  empty: "Płótno jest puste. Agent wywołuje API dostawcy przez aigc_http_request, aby wygenerować zasoby, a następnie umieszcza wygenerowane pliki w dowolnym miejscu na płótnie za pomocą aigc_canvas_place.",
2825
- emptyHint: "Skonfiguruj dostawcę w zakładce ustawień po prawej, a następnie poproś agenta o wygenerowanie czegoś.",
2910
+ emptyHint: "Skonfiguruj dostawców w wierszu tej wtyczki na stronie Wtyczki (Plugins), a następnie poproś agenta o wygenerowanie czegoś.",
2826
2911
  prompt: "Komunikat",
2827
2912
  image: "Obraz",
2828
2913
  video: "Wideo",
@@ -2849,13 +2934,15 @@ window.__ModuleLoader__.load({
2849
2934
  deleteElement: "Usuń element",
2850
2935
  dropHint: "Przeciągnij pliki na płótno",
2851
2936
  uploading: "Przesyłanie…",
2852
- settingsNav: "Płótno AIGC",
2853
- settingsTitle: "Dostawcy AIGC",
2937
+ rowSummary: "Skonfiguruj dostawców generowania AIGC (adres API, klucz, uwierzytelnianie, instrukcje wywołania).",
2854
2938
  settingsIntro: "Skonfiguruj jednego lub więcej dostawców AIGC. Dla każdego dostawcy można osobno ustawić nazwę, adres API, klucz, metodę uwierzytelniania i instrukcje wywołania. Agent odczytuje listę dostawców przez aigc_get_provider_info, wywołuje API przez aigc_http_request (endpoint i apiKey dołączane automatycznie) i umieszcza wygenerowane pliki na płótnie za pomocą aigc_canvas_place.",
2855
2939
  settingsEmpty: "Nie skonfigurowano żadnych dostawców. Dodaj jednego poniżej.",
2856
2940
  settingsAdd: "+ Dodaj dostawcę",
2857
2941
  settingsLoading: "Ładowanie…",
2858
2942
  settingsError: "Błąd",
2943
+ settingsUnavailable: "Ustawienia są niedostępne (host nie udostępnia konfiguracji tej wtyczki).",
2944
+ settingsReadOnly: "To połączenie nie zapisuje konfiguracji; zmiany nie zostaną zapisane.",
2945
+ settingsSaveFailed: "Odrzucono zapis — konfiguracja zmieniła się gdzie indziej. Spróbuj ponownie.",
2859
2946
  "row.id": "ID",
2860
2947
  "row.name": "Nazwa",
2861
2948
  "row.endpoint": "Adres API",
@@ -2894,7 +2981,7 @@ window.__ModuleLoader__.load({
2894
2981
  tabTitle: "AIGC 畫布",
2895
2982
  title: "AIGC 畫布",
2896
2983
  empty: "畫布是空的。模型透過 aigc_http_request 呼叫供應商 API 產生素材,再用 aigc_canvas_place 把檔案放到畫布的任意位置。",
2897
- emptyHint: "可在右側設定頁配置供應商,然後讓模型開始產生。",
2984
+ emptyHint: "可在外掛頁(Plugins)本外掛的行內設定供應商,然後讓模型開始生成。",
2898
2985
  prompt: "提示詞",
2899
2986
  image: "圖片",
2900
2987
  video: "影片",
@@ -2921,13 +3008,15 @@ window.__ModuleLoader__.load({
2921
3008
  deleteElement: "刪除元素",
2922
3009
  dropHint: "拖放檔案到畫布",
2923
3010
  uploading: "上載中…",
2924
- settingsNav: "AIGC 畫布",
2925
- settingsTitle: "AIGC 供應商",
3011
+ rowSummary: "設定 AIGC 生成供應商(API 位址、金鑰、鑑權方式、呼叫說明)。",
2926
3012
  settingsIntro: "設定一個或多個 AIGC 供應商。每個供應商可獨立設定名稱、API 位址、金鑰、驗證方式和呼叫說明。模型透過 aigc_get_provider_info 讀取供應商列表,用 aigc_http_request 呼叫 API(自動攜帶 endpoint 和 apiKey),產生的檔案用 aigc_canvas_place 放到畫布上。",
2927
3013
  settingsEmpty: "尚無供應商,請在下方新增。",
2928
3014
  settingsAdd: "+ 新增供應商",
2929
3015
  settingsLoading: "載入中…",
2930
3016
  settingsError: "錯誤",
3017
+ settingsUnavailable: "設定服務無法使用(主機未提供此外掛程式的設定)。",
3018
+ settingsReadOnly: "此連線不會保存設定,變更將不會儲存。",
3019
+ settingsSaveFailed: "儲存失敗:設定已在其他地方修改,請稍後重試。",
2931
3020
  "row.id": "ID",
2932
3021
  "row.name": "名稱",
2933
3022
  "row.endpoint": "API 位址",
@@ -2966,7 +3055,7 @@ window.__ModuleLoader__.load({
2966
3055
  tabTitle: "AIGC 畫布",
2967
3056
  title: "AIGC 畫布",
2968
3057
  empty: "畫布是空的。模型透過 aigc_http_request 呼叫供應商 API 產生素材,再用 aigc_canvas_place 把檔案放到畫布的任意位置。",
2969
- emptyHint: "可在右側設定頁配置供應商,然後讓模型開始產生。",
3058
+ emptyHint: "可在外掛頁(Plugins)本外掛的行內設定供應商,然後讓模型開始生成。",
2970
3059
  prompt: "提示詞",
2971
3060
  image: "圖片",
2972
3061
  video: "影片",
@@ -2993,13 +3082,15 @@ window.__ModuleLoader__.load({
2993
3082
  deleteElement: "刪除元素",
2994
3083
  dropHint: "拖放檔案到畫布",
2995
3084
  uploading: "上傳中…",
2996
- settingsNav: "AIGC 畫布",
2997
- settingsTitle: "AIGC 供應商",
3085
+ rowSummary: "設定 AIGC 生成供應商(API 位址、金鑰、鑑權方式、呼叫說明)。",
2998
3086
  settingsIntro: "設定一個或多個 AIGC 供應商。每個供應商可獨立設定名稱、API 位址、金鑰、驗證方式和呼叫說明。模型透過 aigc_get_provider_info 讀取供應商列表,用 aigc_http_request 呼叫 API(自動攜帶 endpoint 和 apiKey),產生的檔案用 aigc_canvas_place 放到畫布上。",
2999
3087
  settingsEmpty: "尚無供應商,請在下方新增。",
3000
3088
  settingsAdd: "+ 新增供應商",
3001
3089
  settingsLoading: "載入中…",
3002
3090
  settingsError: "錯誤",
3091
+ settingsUnavailable: "設定服務無法使用(主機未提供此外掛程式的設定)。",
3092
+ settingsReadOnly: "此連線不會保存設定,變更將不會儲存。",
3093
+ settingsSaveFailed: "儲存失敗:設定已在其他地方修改,請稍後重試。",
3003
3094
  "row.id": "ID",
3004
3095
  "row.name": "名稱",
3005
3096
  "row.endpoint": "API 位址",
@@ -3038,7 +3129,7 @@ window.__ModuleLoader__.load({
3038
3129
  tabTitle: "AIGC 畫布",
3039
3130
  title: "AIGC 畫布",
3040
3131
  empty: "畫布是空的。模型透過 aigc_http_request 呼叫供應商 API 產生素材,再用 aigc_canvas_place 把檔案放到畫布的任意位置。",
3041
- emptyHint: "可在右側設定頁配置供應商,然後讓模型開始產生。",
3132
+ emptyHint: "可在外掛頁(Plugins)本外掛的行內設定供應商,然後讓模型開始生成。",
3042
3133
  prompt: "提示詞",
3043
3134
  image: "圖片",
3044
3135
  video: "影片",
@@ -3065,13 +3156,15 @@ window.__ModuleLoader__.load({
3065
3156
  deleteElement: "刪除元素",
3066
3157
  dropHint: "拖放檔案到畫布",
3067
3158
  uploading: "上載中…",
3068
- settingsNav: "AIGC 畫布",
3069
- settingsTitle: "AIGC 供應商",
3159
+ rowSummary: "設定 AIGC 生成供應商(API 位址、金鑰、鑑權方式、呼叫說明)。",
3070
3160
  settingsIntro: "設定一個或多個 AIGC 供應商。每個供應商可獨立設定名稱、API 位址、金鑰、驗證方式和呼叫說明。模型透過 aigc_get_provider_info 讀取供應商列表,用 aigc_http_request 呼叫 API(自動攜帶 endpoint 和 apiKey),產生的檔案用 aigc_canvas_place 放到畫布上。",
3071
3161
  settingsEmpty: "尚無供應商,請在下方新增。",
3072
3162
  settingsAdd: "+ 新增供應商",
3073
3163
  settingsLoading: "載入中…",
3074
3164
  settingsError: "錯誤",
3165
+ settingsUnavailable: "設定服務無法使用(主機未提供此外掛程式的設定)。",
3166
+ settingsReadOnly: "此連線不會保存設定,變更將不會儲存。",
3167
+ settingsSaveFailed: "儲存失敗:設定已在其他地方修改,請稍後重試。",
3075
3168
  "row.id": "ID",
3076
3169
  "row.name": "名稱",
3077
3170
  "row.endpoint": "API 位址",
@@ -3108,14 +3201,38 @@ window.__ModuleLoader__.load({
3108
3201
  }
3109
3202
  };
3110
3203
  //#endregion
3204
+ //#region src/entry-identity.ts
3205
+ /**
3206
+ * entry-identity.ts — the identity triple that binds this plugin's three
3207
+ * naming surfaces together. Keeping them in one dependency-free module lets
3208
+ * a unit test pin them against `cordis.patch.yml` and `package.json`
3209
+ * (a mismatched `plugins.row.config` key fails silently: the row's configure
3210
+ * entry simply never appears).
3211
+ *
3212
+ * - ENTRY_ID: the profile entry id — the `id` of the insert row in
3213
+ * `cordis.patch.yml`. The Host serves the entry's volatile config under
3214
+ * it, so it keys `ctx.configForms.get` and `settings.update`.
3215
+ * - PACKAGE_NAME: the bundle's npm package name — the row's `name` field.
3216
+ * - ROW_CONFIG_KEY: the Plugins page `plugins.row.config` slot key,
3217
+ * `<package name>#<row id>` with the row id passed through verbatim.
3218
+ *
3219
+ * @module @huanlin/dsh-plugin-aigc-canvas/entry-identity
3220
+ */
3221
+ /** Profile entry id (= the `cordis.patch.yml` insert row id / settings namespace). */
3222
+ const ENTRY_ID = "dsh-aigc-canvas";
3223
+ /** `plugins.row.config` key: the bundle's package name `#` the patch row id. */
3224
+ const ROW_CONFIG_KEY = `@huanlin/dsh-plugin-aigc-canvas#${ENTRY_ID}`;
3225
+ //#endregion
3111
3226
  //#region src/client/index.tsx
3112
3227
  /** Services required before mounting. `betterSidebar` is intentionally NOT
3113
3228
  * listed here — this plugin extends `dsh-better-sidebar` when present, but
3114
- * must remain loadable without it (defensive lookup via `ctx.get(...)`). */
3229
+ * must remain loadable without it (defensive lookup via `ctx.get(...)`).
3230
+ * `configForms` is provided by the ui-settings base plugin. */
3115
3231
  const inject = [
3116
3232
  "slots",
3117
3233
  "locale",
3118
- "conversation"
3234
+ "conversation",
3235
+ "configForms"
3119
3236
  ];
3120
3237
  function apply(ctx) {
3121
3238
  ctx.effect(() => ctx.locale.register(NS, {
@@ -3162,18 +3279,24 @@ window.__ModuleLoader__.load({
3162
3279
  });
3163
3280
  }
3164
3281
  }));
3282
+ const form = ctx.configForms.get(ENTRY_ID);
3165
3283
  const settingsInjected = () => ({
3166
3284
  t,
3167
- send: (text) => ctx.conversation.send(text)
3285
+ send: (text) => ctx.conversation.send(text),
3286
+ hooks: { aigcSettings: form },
3287
+ saveProviders: (providers) => form.set("providers", providers.map((p) => ({
3288
+ ...p,
3289
+ auth: { ...p.auth }
3290
+ })))
3168
3291
  });
3169
- ctx.slots.inject("settings.section", () => ctx.slots.register({
3170
- name: "settings.section",
3171
- id: "aigc-canvas",
3172
- order: 60,
3173
- label: () => t("settingsNav"),
3174
- locale: NS,
3175
- inject: settingsInjected
3176
- }, SettingsPage));
3292
+ ctx.effect(() => ctx.configForms.whileServed([ENTRY_ID], () => ctx.slots.inject("plugins.row.config", function* () {
3293
+ yield ctx.slots.register({
3294
+ name: "plugins.row.config",
3295
+ key: ROW_CONFIG_KEY,
3296
+ locale: NS,
3297
+ inject: settingsInjected
3298
+ }, SettingsPage);
3299
+ })), "dsh-aigc-canvas: plugin row config page");
3177
3300
  }
3178
3301
  //#endregion
3179
3302
  exports.apply = apply;