@monkey-mini-app/panel 0.1.1 → 0.1.5

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/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var React = require('react');
3
+ var React4 = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var lucideReact = require('lucide-react');
6
6
  var client = require('react-dom/client');
@@ -24,7 +24,7 @@ function _interopNamespace(e) {
24
24
  return Object.freeze(n);
25
25
  }
26
26
 
27
- var React__namespace = /*#__PURE__*/_interopNamespace(React);
27
+ var React4__namespace = /*#__PURE__*/_interopNamespace(React4);
28
28
 
29
29
  var __defProp = Object.defineProperty;
30
30
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -42,292 +42,17 @@ function capabilitiesOf(host) {
42
42
  };
43
43
  }
44
44
 
45
- // src/rest.ts
46
- function isRecord(value) {
47
- return typeof value === "object" && value !== null && !Array.isArray(value);
48
- }
49
- async function readJson(url, init) {
50
- let res;
51
- try {
52
- res = await fetch(url, init);
53
- } catch (cause) {
54
- throw new HostUnreachableError(url, cause);
55
- }
56
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
57
- return res.json();
58
- }
59
- var HostUnreachableError = class extends Error {
60
- constructor(url, cause) {
61
- super(`Cannot reach apps host: ${url}`);
62
- __publicField(this, "url");
63
- this.name = "HostUnreachableError";
64
- this.url = url;
65
- if (cause !== void 0) {
66
- Object.defineProperty(this, "cause", { value: cause, configurable: true, writable: true });
67
- }
68
- }
69
- };
70
- function isHostUnreachable(e) {
71
- return e instanceof HostUnreachableError;
72
- }
73
- function appFrameUrl(origin, appId, query) {
74
- return `${origin}/app/${encodeURIComponent(appId)}?${new URLSearchParams(query).toString()}`;
75
- }
76
- function parseAppTheme(raw) {
77
- if (raw === null) return null;
78
- if (!isRecord(raw)) return void 0;
79
- const theme = typeof raw.theme === "string" ? raw.theme : "";
80
- const palette = typeof raw.palette === "string" ? raw.palette : "";
81
- if (!theme && !palette) return null;
82
- return { theme, palette };
83
- }
84
- function parseAppsResponse(raw) {
85
- if (!isRecord(raw) || !Array.isArray(raw.apps)) return [];
86
- const out = [];
87
- for (const item of raw.apps) {
88
- if (!isRecord(item) || typeof item.id !== "string") continue;
89
- out.push({
90
- id: item.id,
91
- name: typeof item.name === "string" ? item.name : item.id,
92
- description: typeof item.description === "string" ? item.description : void 0,
93
- acronym: typeof item.acronym === "string" ? item.acronym : void 0,
94
- commits: typeof item.commits === "number" ? item.commits : void 0,
95
- version: typeof item.version === "string" ? item.version : void 0,
96
- theme: parseAppTheme(item.theme)
97
- });
98
- }
99
- return out;
100
- }
101
- function hostConfigToForm(raw, cardStyle) {
102
- const rec = isRecord(raw) ? raw : {};
103
- const llm = isRecord(rec.llm) ? rec.llm : null;
104
- const locale = typeof rec.locale === "string" ? rec.locale : "";
105
- const chatLanguage = typeof rec.chatLanguage === "string" ? rec.chatLanguage : "";
106
- return {
107
- hostPort: rec.hostPort != null ? String(rec.hostPort) : "",
108
- locale: locale || chatLanguage,
109
- chatLanguage: chatLanguage || locale,
110
- theme: typeof rec.theme === "string" ? rec.theme : "",
111
- palette: typeof rec.palette === "string" ? rec.palette : "",
112
- cardStyle,
113
- provider: llm && typeof llm.provider === "string" ? llm.provider : "",
114
- model: llm && typeof llm.model === "string" ? llm.model : ""
115
- };
116
- }
117
- function formToHostConfigBody(form) {
118
- const provider = (form.provider ?? "").trim();
119
- const model = (form.model ?? "").trim();
120
- const locale = form.locale || form.chatLanguage;
121
- const body = { locale, chatLanguage: form.chatLanguage || locale, theme: form.theme, palette: form.palette };
122
- const port = Number(form.hostPort);
123
- if (Number.isInteger(port) && port >= 0 && port <= 65535) body.hostPort = port;
124
- if (provider && model) body.llm = { provider, model };
125
- else if (!provider && !model) body.llm = null;
126
- return body;
127
- }
128
- function asCommit(raw) {
129
- if (!isRecord(raw) || typeof raw.id !== "string") return null;
130
- const files = Array.isArray(raw.files) ? raw.files.flatMap((f) => {
131
- if (!isRecord(f) || typeof f.path !== "string") return [];
132
- return [{ path: f.path, add: typeof f.add === "number" ? f.add : void 0, del: typeof f.del === "number" ? f.del : void 0, preview: typeof f.preview === "string" ? f.preview : void 0 }];
133
- }) : void 0;
134
- return { id: raw.id, message: typeof raw.message === "string" ? raw.message : "", time: typeof raw.time === "string" ? raw.time : "", files };
135
- }
136
- function parseCommitList(raw) {
137
- const rec = isRecord(raw) ? raw : {};
138
- const list = Array.isArray(rec.commits) ? rec.commits : Array.isArray(rec.nodes) ? rec.nodes : [];
139
- return list.map((c) => asCommit(c)).filter((c) => c !== null);
140
- }
141
- function parseCommitDetail(raw, id) {
142
- const rec = isRecord(raw) ? raw : {};
143
- const inner = isRecord(rec.commit) ? rec.commit : rec;
144
- return asCommit(inner) ?? { id, message: "", time: "", files: [] };
145
- }
146
- function parseStorageTables(raw) {
147
- const rec = isRecord(raw) ? raw : {};
148
- const list = Array.isArray(rec.tables) ? rec.tables : [];
149
- const out = [];
150
- for (const row of list) {
151
- if (!isRecord(row) || typeof row.name !== "string") continue;
152
- out.push({
153
- name: row.name,
154
- size: typeof row.size === "number" ? row.size : void 0,
155
- updatedAt: typeof row.updatedAt === "string" ? row.updatedAt : void 0
156
- });
157
- }
158
- return out;
159
- }
160
- function parsePalettes(raw) {
161
- const rec = isRecord(raw) ? raw : {};
162
- const list = Array.isArray(rec.palettes) ? rec.palettes : [];
163
- const out = [];
164
- for (const p of list) {
165
- if (!isRecord(p) || typeof p.id !== "string") continue;
166
- if (p.custom === false) continue;
167
- out.push({
168
- id: p.id,
169
- label: typeof p.label === "string" ? p.label : p.id,
170
- swatch: typeof p.swatch === "string" ? p.swatch : "#888",
171
- tokens: isRecord(p.tokens) ? p.tokens : void 0
172
- });
173
- }
174
- return out;
175
- }
176
- function createRestPanelHost(opts) {
177
- const storage = opts.storage ?? (typeof window !== "undefined" ? window.localStorage : null);
178
- function origin() {
179
- return (opts.getHostUrl ? opts.getHostUrl() : opts.hostUrl).replace(/\/$/, "");
180
- }
181
- const host = {
182
- locale: opts.locale,
183
- emptyText: opts.emptyText,
184
- fetchApps: async () => parseAppsResponse(await readJson(`${origin()}/api/apps`)),
185
- palettes: async () => {
186
- try {
187
- return parsePalettes(await readJson(`${origin()}/api/palettes`));
188
- } catch {
189
- return [];
190
- }
191
- },
192
- openPanel: () => opts.onOpen?.(),
193
- closePanel: () => opts.onClose?.(),
194
- persistTheme: (theme, palette) => {
195
- try {
196
- storage?.setItem("mma-theme-mode", theme);
197
- storage?.setItem("mma-palette", palette);
198
- } catch {
199
- }
200
- void fetch(`${origin()}/api/host-config`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ theme, palette }) }).catch(() => {
201
- });
202
- },
203
- appTheme: {
204
- save: async (appId, t) => {
205
- await fetch(`${origin()}/api/apps/${encodeURIComponent(appId)}/theme`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(t) }).catch(() => {
206
- });
207
- },
208
- clear: async (appId) => {
209
- await fetch(`${origin()}/api/apps/${encodeURIComponent(appId)}/theme`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ reset: true }) }).catch(() => {
210
- });
211
- }
212
- },
213
- config: {
214
- load: async () => hostConfigToForm(await readJson(`${origin()}/api/host-config`), opts.getCardStyle?.() ?? opts.cardStyle ?? "stamp"),
215
- save: async (cfg) => {
216
- const body = formToHostConfigBody(cfg);
217
- const res = await fetch(`${origin()}/api/host-config`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
218
- const raw = await res.json().catch(() => ({}));
219
- if (!res.ok || isRecord(raw) && raw.ok === false) {
220
- throw new Error(isRecord(raw) && typeof raw.error === "string" ? raw.error : `HTTP ${res.status}`);
221
- }
222
- const hostPort = isRecord(raw) ? raw.hostPort : void 0;
223
- if (typeof hostPort === "number" && Number.isInteger(hostPort) && hostPort > 0) {
224
- const next = `${new URL(origin()).protocol}//${new URL(origin()).hostname}:${hostPort}`;
225
- if (next !== origin()) opts.onHostChange?.(next);
226
- }
227
- opts.onConfigSaved?.(cfg);
228
- }
229
- },
230
- history: {
231
- list: async (appId) => parseCommitList(await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/history?limit=50`)),
232
- detail: async (appId, id) => parseCommitDetail(await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/history/${encodeURIComponent(id)}`), id)
233
- },
234
- storage: {
235
- listTables: async (appId) => parseStorageTables(await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/storage`)),
236
- readTable: async (appId, name) => {
237
- const raw = await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/storage/${encodeURIComponent(name)}`);
238
- return isRecord(raw) && "value" in raw ? raw.value : raw;
239
- }
240
- },
241
- deleteApp: opts.deleteApp ?? (async (appId) => {
242
- await fetch(`${origin()}/api/app/${encodeURIComponent(appId)}`, { method: "DELETE" }).catch(() => {
243
- });
244
- }),
245
- frame: opts.frameController ?? {
246
- url: (appId) => `${origin()}/app/${encodeURIComponent(appId)}`,
247
- mount: () => void 0,
248
- unmount: () => void 0,
249
- reload: () => void 0,
250
- syncEnv: () => void 0
251
- }
252
- };
253
- return host;
254
- }
255
- var none = {
256
- history: false,
257
- storage: false,
258
- config: false,
259
- appTheme: false,
260
- customPalettes: false,
261
- deleteApp: false
262
- };
263
- var initial = {
264
- tabs: [{ id: "all", title: "\u5168\u90E8", kind: "all" }],
265
- active: "all",
266
- apps: [],
267
- error: null,
268
- loading: false,
269
- query: "",
270
- dock: "fill",
271
- theme: "light",
272
- palette: "default",
273
- themeScope: "global",
274
- customPalettes: {},
275
- cardStyle: "stamp",
276
- visible: false,
277
- pendingDelete: null,
278
- themePopOpen: false,
279
- settingsOpen: false,
280
- cfgMsg: "",
281
- cfgVersion: 0,
282
- cfg: {},
283
- emptyText: void 0,
284
- capabilities: none,
285
- locale: "zh-CN",
286
- browseOpen: false,
287
- browseKind: "history",
288
- browseAppId: null,
289
- browseAppName: "",
290
- browseLoading: false,
291
- browseError: null,
292
- browseList: [],
293
- browseDetail: null,
294
- browseTable: null,
295
- browseTableValue: null,
296
- browseOpenFile: null
297
- };
298
- var state = { ...initial, capabilities: { ...none } };
299
- var listeners = /* @__PURE__ */ new Set();
300
- function getPanelState() {
301
- return state;
302
- }
303
- function setPanelState(patch) {
304
- state = { ...state, ...patch };
305
- for (const fn of listeners) fn();
306
- return state;
307
- }
308
- function subscribePanel(listener) {
309
- listeners.add(listener);
310
- return () => {
311
- listeners.delete(listener);
312
- };
313
- }
314
- function resetPanelState(seed) {
315
- state = {
316
- ...initial,
317
- capabilities: { ...none },
318
- customPalettes: {},
319
- cfg: {},
320
- tabs: [{ id: "all", title: "\u5168\u90E8", kind: "all" }],
321
- ...seed || {}
322
- };
323
- for (const fn of listeners) fn();
324
- return state;
45
+ // src/themes.ts
46
+ var LOCAL_PALETTE_ID = "__local__";
47
+ var GLOBAL_PALETTE_ID = "__global__";
48
+ function effectivePalette(stored, hasLocalTheme, hostPalette) {
49
+ if (stored && stored !== GLOBAL_PALETTE_ID) return stored;
50
+ if (!stored && hasLocalTheme) return LOCAL_PALETTE_ID;
51
+ return hostPalette;
325
52
  }
326
- function usePanelState() {
327
- return React.useSyncExternalStore(subscribePanel, getPanelState);
53
+ function selectedPalette(stored, hasLocalTheme) {
54
+ return stored ?? (hasLocalTheme ? LOCAL_PALETTE_ID : GLOBAL_PALETTE_ID);
328
55
  }
329
-
330
- // src/themes.ts
331
56
  var PALETTES = [
332
57
  { id: "default", label: "\u9ED8\u8BA4", swatch: "#2563eb" },
333
58
  { id: "tokyo", label: "\u4E1C\u4EAC\u591C", swatch: "#7aa2f7" },
@@ -692,167 +417,647 @@ var TOKENS = {
692
417
  }
693
418
  }
694
419
  };
695
- var THEME_VAR_KEYS = [
696
- "bg",
697
- "fg",
698
- "surface",
699
- "surface-fg",
700
- "border",
701
- "muted",
702
- "muted-fg",
703
- "primary",
704
- "primary-fg",
705
- "secondary",
706
- "secondary-fg",
707
- "accent",
708
- "accent-fg",
709
- "destructive",
710
- "destructive-fg",
711
- "ring",
712
- "input",
713
- "radius",
714
- "shadow"
715
- ];
716
- function kebabToCamel(k) {
717
- return k.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
420
+ var THEME_VAR_KEYS = [
421
+ "bg",
422
+ "fg",
423
+ "surface",
424
+ "surface-fg",
425
+ "border",
426
+ "muted",
427
+ "muted-fg",
428
+ "primary",
429
+ "primary-fg",
430
+ "secondary",
431
+ "secondary-fg",
432
+ "accent",
433
+ "accent-fg",
434
+ "destructive",
435
+ "destructive-fg",
436
+ "ring",
437
+ "input",
438
+ "radius",
439
+ "shadow"
440
+ ];
441
+ function kebabToCamel(k) {
442
+ return k.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
443
+ }
444
+ function parseThemeCss(css, _id) {
445
+ const out = {};
446
+ const blockRe = /:root\[data-mode="(light|dark)"\]\s*\{([^}]*)\}/g;
447
+ let m;
448
+ while (m = blockRe.exec(String(css || ""))) {
449
+ const mode = m[1];
450
+ const vars = {};
451
+ for (const key of THEME_VAR_KEYS) {
452
+ const vm = new RegExp(`--${key}\\s*:\\s*([^;\\n]+)`).exec(m[2]);
453
+ if (vm) vars[kebabToCamel(key)] = vm[1].trim();
454
+ }
455
+ if (vars.bg && vars.fg && vars.primary) out[mode] = vars;
456
+ }
457
+ if (!out.light || !out.dark) return null;
458
+ const base = {
459
+ surfaceFg: out.light.fg,
460
+ secondary: out.light.muted,
461
+ secondaryFg: out.light.fg,
462
+ destructiveFg: "#ffffff",
463
+ radius: "12px",
464
+ shadow: "rgba(15, 23, 42, 0.1)"
465
+ };
466
+ const fill = (v) => Object.assign({}, base, v);
467
+ return { light: fill(out.light), dark: fill(out.dark) };
468
+ }
469
+ function themeLabelFromCss(css, fallback) {
470
+ const m = /\/\*\s*name\s*:\s*([^*]+)\*\//.exec(String(css || ""));
471
+ return m && m[1].trim() || fallback;
472
+ }
473
+ function clampPalette(value) {
474
+ if (value === "tokyo" || value === "forest" || value === "matcha" || value === "yellow" || value === "zoro" || value === "hokage" || value === "slate" || value === "default") {
475
+ return value;
476
+ }
477
+ if (value === "ocean" || value === "mist") return "tokyo";
478
+ if (value === "violet" || value === "ink") return "matcha";
479
+ if (value === "paper" || value === "noir") return "slate";
480
+ return "default";
481
+ }
482
+ function clampMode(value) {
483
+ return value === "dark" ? "dark" : "light";
484
+ }
485
+ function resolveMode(value) {
486
+ if (value === "system") {
487
+ try {
488
+ if (typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches) {
489
+ return "dark";
490
+ }
491
+ } catch {
492
+ }
493
+ return "light";
494
+ }
495
+ return clampMode(value);
496
+ }
497
+ function tokensOf(palette, mode) {
498
+ return TOKENS[clampPalette(palette)][clampMode(mode)];
499
+ }
500
+ function cssVars(t) {
501
+ return [
502
+ `--background:${t.bg}`,
503
+ `--foreground:${t.fg}`,
504
+ `--card:${t.surface}`,
505
+ `--card-foreground:${t.surfaceFg}`,
506
+ `--primary:${t.primary}`,
507
+ `--primary-foreground:${t.primaryFg}`,
508
+ `--secondary:${t.secondary}`,
509
+ `--secondary-foreground:${t.secondaryFg}`,
510
+ `--muted:${t.muted}`,
511
+ `--muted-foreground:${t.mutedFg}`,
512
+ `--accent:${t.accent}`,
513
+ `--accent-foreground:${t.accentFg}`,
514
+ `--destructive:${t.destructive}`,
515
+ `--destructive-foreground:${t.destructiveFg}`,
516
+ `--border:${t.border}`,
517
+ `--input:${t.input}`,
518
+ `--ring:${t.ring}`,
519
+ `--radius:${t.radius}`,
520
+ `--shadow:${t.shadow}`,
521
+ `--color-background:var(--background)`,
522
+ `--color-surface:var(--card)`,
523
+ `--color-foreground:var(--foreground)`,
524
+ `--color-primary:var(--primary)`,
525
+ `--color-primary-foreground:var(--primary-foreground)`,
526
+ `--color-muted:var(--muted)`,
527
+ `--color-muted-foreground:var(--muted-foreground)`,
528
+ `--color-border:var(--border)`,
529
+ `--radius-md:var(--radius)`,
530
+ `--space-4:16px`,
531
+ `--font-sans:ui-sans-serif,system-ui,-apple-system,sans-serif`
532
+ ].join(";");
533
+ }
534
+ function runnerThemeCss() {
535
+ const blocks = [
536
+ `:root,html[data-theme="light"]:not([data-palette]),html[data-theme="light"][data-palette="default"]{${cssVars(TOKENS.default.light)}}`,
537
+ `html[data-theme="dark"]:not([data-palette]),html[data-theme="dark"][data-palette="default"]{${cssVars(TOKENS.default.dark)}}`
538
+ ];
539
+ ["tokyo", "forest", "matcha", "yellow", "zoro", "hokage", "slate"].forEach((id) => {
540
+ blocks.push(`html[data-theme="light"][data-palette="${id}"]{${cssVars(TOKENS[id].light)}}`);
541
+ blocks.push(`html[data-theme="dark"][data-palette="${id}"]{${cssVars(TOKENS[id].dark)}}`);
542
+ });
543
+ blocks.push(`html[data-theme="dark"] input[type="date"],html[data-theme="dark"] input[type="time"],html[data-theme="dark"] input[type="datetime-local"],html[data-theme="dark"] input[type="month"],html[data-theme="dark"] input[type="week"]{color-scheme:dark}`);
544
+ blocks.push(`html[data-theme="light"] input[type="date"],html[data-theme="light"] input[type="time"],html[data-theme="light"] input[type="datetime-local"],html[data-theme="light"] input[type="month"],html[data-theme="light"] input[type="week"]{color-scheme:light}`);
545
+ return blocks.join("\n ");
546
+ }
547
+ function themeCssVars(theme, palette, custom) {
548
+ const mode = clampMode(theme);
549
+ const c = custom && palette ? custom[String(palette)] : void 0;
550
+ const pal = c ? String(palette) : clampPalette(palette);
551
+ const t = c && c.tokens && c.tokens[mode] || tokensOf(pal, mode);
552
+ return {
553
+ "--dsw-alias-bg": t.bg,
554
+ "--dsw-alias-fg": t.fg,
555
+ "--dsw-alias-surface": t.surface,
556
+ "--dsw-alias-border": t.border,
557
+ "--dsw-alias-muted": t.muted,
558
+ "--dsw-alias-primary": t.primary,
559
+ "--dsw-alias-primary-fg": t.primaryFg,
560
+ "--dsw-alias-accent": t.accent,
561
+ "--dsw-alias-shadow": t.shadow,
562
+ "--background": t.bg,
563
+ "--foreground": t.fg,
564
+ "--card": t.surface,
565
+ "--card-foreground": t.surfaceFg,
566
+ "--primary": t.primary,
567
+ "--primary-foreground": t.primaryFg,
568
+ "--secondary": t.secondary,
569
+ "--secondary-foreground": t.secondaryFg,
570
+ "--muted": t.muted,
571
+ "--muted-foreground": t.mutedFg,
572
+ "--accent": t.accent,
573
+ "--accent-foreground": t.accentFg,
574
+ "--border": t.border,
575
+ "--input": t.input,
576
+ "--ring": t.ring,
577
+ "--destructive": t.destructive,
578
+ "--destructive-foreground": t.destructiveFg,
579
+ "--radius": t.radius,
580
+ "--shadow": t.shadow
581
+ };
582
+ }
583
+ function applyThemeTo(el, theme, palette, custom) {
584
+ const mode = resolveMode(theme);
585
+ const c = custom && palette ? custom[String(palette)] : void 0;
586
+ const pal = c ? String(palette) : clampPalette(palette);
587
+ const t = c && c.tokens && c.tokens[mode] || tokensOf(pal, mode);
588
+ el.setAttribute("data-theme", mode);
589
+ if (theme === "system") el.setAttribute("data-theme-pref", "system");
590
+ else el.removeAttribute("data-theme-pref");
591
+ el.setAttribute("data-palette", pal);
592
+ const map = themeCssVars(mode, palette, custom);
593
+ for (const k of Object.keys(map)) el.style.setProperty(k, map[k]);
594
+ el.style.background = t.bg;
595
+ el.style.color = t.fg;
596
+ return { theme: mode, palette: pal };
597
+ }
598
+
599
+ // src/rest.ts
600
+ function isRecord(value) {
601
+ return typeof value === "object" && value !== null && !Array.isArray(value);
602
+ }
603
+ async function readJson(url, init) {
604
+ let res;
605
+ try {
606
+ res = await fetch(url, init);
607
+ } catch (cause) {
608
+ throw new HostUnreachableError(url, cause);
609
+ }
610
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
611
+ return res.json();
612
+ }
613
+ var HostUnreachableError = class extends Error {
614
+ constructor(url, cause) {
615
+ super(`Cannot reach apps host: ${url}`);
616
+ __publicField(this, "url");
617
+ this.name = "HostUnreachableError";
618
+ this.url = url;
619
+ if (cause !== void 0) {
620
+ Object.defineProperty(this, "cause", { value: cause, configurable: true, writable: true });
621
+ }
622
+ }
623
+ };
624
+ function isHostUnreachable(e) {
625
+ return e instanceof HostUnreachableError;
718
626
  }
719
- function parseThemeCss(css, _id) {
720
- const out = {};
721
- const blockRe = /:root\[data-mode="(light|dark)"\]\s*\{([^}]*)\}/g;
722
- let m;
723
- while (m = blockRe.exec(String(css || ""))) {
724
- const mode = m[1];
725
- const vars = {};
726
- for (const key of THEME_VAR_KEYS) {
727
- const vm = new RegExp(`--${key}\\s*:\\s*([^;\\n]+)`).exec(m[2]);
728
- if (vm) vars[kebabToCamel(key)] = vm[1].trim();
627
+ function appFrameUrl(origin, appId, query) {
628
+ const params = new URLSearchParams();
629
+ for (const key of ["theme", "palette", "dock"]) {
630
+ const value = query[key];
631
+ if (value) params.set(key, value);
632
+ }
633
+ return `${origin}/app/${encodeURIComponent(appId)}?${params.toString()}`;
634
+ }
635
+ function relayViewEval(origin, frames, query) {
636
+ if (!query.requestId || !query.appId) return;
637
+ if (frames.postViewEval(query)) return;
638
+ const body = { appId: query.appId, requestId: query.requestId, view: "not-open" };
639
+ fetch(`${origin.replace(/\/$/, "")}/api/app/${encodeURIComponent(query.appId)}/view/eval`, {
640
+ method: "POST",
641
+ headers: { "content-type": "application/json" },
642
+ body: JSON.stringify(body)
643
+ }).catch(() => {
644
+ });
645
+ }
646
+ function subscribeHostEvents(origin, handlers) {
647
+ if (typeof EventSource === "undefined") return () => {
648
+ };
649
+ let es = null;
650
+ const listen = (fn) => {
651
+ return (raw) => {
652
+ const e = raw;
653
+ let data;
654
+ try {
655
+ data = JSON.parse(e.data || "{}");
656
+ } catch {
657
+ return;
658
+ }
659
+ if (!data || typeof data !== "object") return;
660
+ fn(data);
661
+ };
662
+ };
663
+ try {
664
+ es = new EventSource(`${origin.replace(/\/$/, "")}/api/events`);
665
+ es.addEventListener(
666
+ "app:open",
667
+ listen((d) => {
668
+ if (typeof d.appId === "string") handlers.onOpen?.(d.appId, typeof d.title === "string" ? d.title : void 0);
669
+ })
670
+ );
671
+ es.addEventListener(
672
+ "app:reload",
673
+ listen((d) => {
674
+ if (typeof d.appId === "string") handlers.onReload?.(d.appId);
675
+ })
676
+ );
677
+ es.addEventListener(
678
+ "app:eval",
679
+ listen((d) => {
680
+ if (typeof d.requestId !== "string" || typeof d.appId !== "string") return;
681
+ handlers.onEval?.({
682
+ requestId: d.requestId,
683
+ appId: d.appId,
684
+ code: typeof d.code === "string" ? d.code : "",
685
+ maxBytes: typeof d.maxBytes === "number" ? d.maxBytes : 0
686
+ });
687
+ })
688
+ );
689
+ es.addEventListener(
690
+ "app:storage-notice",
691
+ listen((d) => {
692
+ if (typeof d.appId !== "string" || typeof d.table !== "string" || typeof d.prompt !== "string") return;
693
+ const heavyRaw = Array.isArray(d.heavy) ? d.heavy : [];
694
+ const heavy = heavyRaw.flatMap((h) => {
695
+ if (!isRecord(h) || typeof h.key !== "string" || typeof h.bytes !== "number") return [];
696
+ const kind = h.kind === "list" || h.kind === "map" || h.kind === "value" ? h.kind : "value";
697
+ return [{
698
+ key: h.key,
699
+ bytes: h.bytes,
700
+ kind,
701
+ entries: typeof h.entries === "number" ? h.entries : void 0
702
+ }];
703
+ });
704
+ handlers.onStorageNotice?.({
705
+ appId: d.appId,
706
+ table: d.table,
707
+ bytes: typeof d.bytes === "number" ? d.bytes : 0,
708
+ keys: typeof d.keys === "number" ? d.keys : 0,
709
+ heavy,
710
+ prompt: d.prompt
711
+ });
712
+ })
713
+ );
714
+ } catch {
715
+ return () => {
716
+ };
717
+ }
718
+ return () => {
719
+ try {
720
+ es?.close();
721
+ } catch {
722
+ }
723
+ es = null;
724
+ };
725
+ }
726
+ function parseLocalPalette(css) {
727
+ if (typeof css !== "string" || !css.trim()) return null;
728
+ const tokens = parseThemeCss(css);
729
+ if (!tokens) return null;
730
+ return {
731
+ label: themeLabelFromCss(css, LOCAL_PALETTE_ID),
732
+ swatch: tokens.dark.primary || tokens.light.primary,
733
+ tokens
734
+ };
735
+ }
736
+ function parseAppTheme(raw) {
737
+ if (raw === null) return null;
738
+ if (!isRecord(raw)) return void 0;
739
+ const theme = typeof raw.theme === "string" ? raw.theme : "";
740
+ const palette = typeof raw.palette === "string" ? raw.palette : "";
741
+ if (!theme && !palette) return null;
742
+ return { theme, palette };
743
+ }
744
+ function parseAbout(raw) {
745
+ const rec = isRecord(raw) ? raw : {};
746
+ const packages = [];
747
+ if (Array.isArray(rec.packages)) {
748
+ for (const p of rec.packages) {
749
+ if (!isRecord(p) || typeof p.name !== "string" || typeof p.version !== "string") continue;
750
+ packages.push({ name: p.name, version: p.version });
751
+ }
752
+ }
753
+ return {
754
+ adapter: typeof rec.adapter === "string" ? rec.adapter : "host",
755
+ env: typeof rec.env === "string" ? rec.env : "unknown",
756
+ packages
757
+ };
758
+ }
759
+ function parseUpdateCheck(raw) {
760
+ const rec = isRecord(raw) ? raw : {};
761
+ return {
762
+ name: typeof rec.name === "string" ? rec.name : "",
763
+ current: typeof rec.current === "string" ? rec.current : "",
764
+ latest: typeof rec.latest === "string" ? rec.latest : null,
765
+ updateAvailable: rec.updateAvailable === true,
766
+ error: typeof rec.error === "string" ? rec.error : void 0
767
+ };
768
+ }
769
+ function parseAppsResponse(raw) {
770
+ if (!isRecord(raw) || !Array.isArray(raw.apps)) return [];
771
+ const out = [];
772
+ for (const item of raw.apps) {
773
+ if (!isRecord(item) || typeof item.id !== "string") continue;
774
+ out.push({
775
+ id: item.id,
776
+ name: typeof item.name === "string" ? item.name : item.id,
777
+ description: typeof item.description === "string" ? item.description : void 0,
778
+ acronym: typeof item.acronym === "string" ? item.acronym : void 0,
779
+ commits: typeof item.commits === "number" ? item.commits : void 0,
780
+ version: typeof item.version === "string" ? item.version : void 0,
781
+ theme: parseAppTheme(item.theme),
782
+ localPalette: parseLocalPalette(item.localThemeCss)
783
+ });
784
+ }
785
+ return out;
786
+ }
787
+ function hostConfigToForm(raw, cardStyle) {
788
+ const rec = isRecord(raw) ? raw : {};
789
+ const llm = isRecord(rec.llm) ? rec.llm : null;
790
+ const locale = typeof rec.locale === "string" ? rec.locale : "";
791
+ const chatLanguage = typeof rec.chatLanguage === "string" ? rec.chatLanguage : "";
792
+ return {
793
+ hostPort: rec.hostPort != null ? String(rec.hostPort) : "",
794
+ locale: locale || chatLanguage,
795
+ chatLanguage: chatLanguage || locale,
796
+ theme: typeof rec.theme === "string" ? rec.theme : "",
797
+ palette: typeof rec.palette === "string" ? rec.palette : "",
798
+ cardStyle,
799
+ provider: llm && typeof llm.provider === "string" ? llm.provider : "",
800
+ model: llm && typeof llm.model === "string" ? llm.model : ""
801
+ };
802
+ }
803
+ function formToHostConfigBody(form) {
804
+ const provider = (form.provider ?? "").trim();
805
+ const model = (form.model ?? "").trim();
806
+ const locale = form.locale || form.chatLanguage;
807
+ const body = { locale, chatLanguage: form.chatLanguage || locale, theme: form.theme, palette: form.palette };
808
+ const port = Number(form.hostPort);
809
+ if (Number.isInteger(port) && port >= 0 && port <= 65535) body.hostPort = port;
810
+ if (provider && model) body.llm = { provider, model };
811
+ else if (!provider && !model) body.llm = null;
812
+ return body;
813
+ }
814
+ function asCommit(raw) {
815
+ if (!isRecord(raw) || typeof raw.id !== "string") return null;
816
+ const files = Array.isArray(raw.files) ? raw.files.flatMap((f) => {
817
+ if (!isRecord(f) || typeof f.path !== "string") return [];
818
+ return [{ path: f.path, add: typeof f.add === "number" ? f.add : void 0, del: typeof f.del === "number" ? f.del : void 0, preview: typeof f.preview === "string" ? f.preview : void 0 }];
819
+ }) : void 0;
820
+ return { id: raw.id, message: typeof raw.message === "string" ? raw.message : "", time: typeof raw.time === "string" ? raw.time : "", files };
821
+ }
822
+ function parseCommitList(raw) {
823
+ const rec = isRecord(raw) ? raw : {};
824
+ const list = Array.isArray(rec.commits) ? rec.commits : Array.isArray(rec.nodes) ? rec.nodes : [];
825
+ return list.map((c) => asCommit(c)).filter((c) => c !== null);
826
+ }
827
+ function parseCommitDetail(raw, id) {
828
+ const rec = isRecord(raw) ? raw : {};
829
+ const inner = isRecord(rec.commit) ? rec.commit : rec;
830
+ return asCommit(inner) ?? { id, message: "", time: "", files: [] };
831
+ }
832
+ function parseStorageTables(raw) {
833
+ const rec = isRecord(raw) ? raw : {};
834
+ const list = Array.isArray(rec.tables) ? rec.tables : [];
835
+ const out = [];
836
+ for (const row of list) {
837
+ if (!isRecord(row) || typeof row.name !== "string") continue;
838
+ out.push({
839
+ name: row.name,
840
+ size: typeof row.size === "number" ? row.size : void 0,
841
+ updatedAt: typeof row.updatedAt === "string" ? row.updatedAt : void 0,
842
+ keys: typeof row.keys === "number" ? row.keys : void 0
843
+ });
844
+ }
845
+ return out;
846
+ }
847
+ function parseStorageNotices(raw, appId) {
848
+ const rec = isRecord(raw) ? raw : {};
849
+ const list = Array.isArray(rec.notices) ? rec.notices : [];
850
+ const out = [];
851
+ for (const row of list) {
852
+ if (!isRecord(row) || typeof row.table !== "string" || typeof row.prompt !== "string") continue;
853
+ const heavyRaw = Array.isArray(row.heavy) ? row.heavy : [];
854
+ const heavy = heavyRaw.flatMap((h) => {
855
+ if (!isRecord(h) || typeof h.key !== "string" || typeof h.bytes !== "number") return [];
856
+ const kind = h.kind === "list" || h.kind === "map" || h.kind === "value" ? h.kind : "value";
857
+ return [{
858
+ key: h.key,
859
+ bytes: h.bytes,
860
+ kind,
861
+ entries: typeof h.entries === "number" ? h.entries : void 0
862
+ }];
863
+ });
864
+ out.push({
865
+ appId: typeof row.appId === "string" ? row.appId : appId,
866
+ table: row.table,
867
+ bytes: typeof row.bytes === "number" ? row.bytes : 0,
868
+ keys: typeof row.keys === "number" ? row.keys : 0,
869
+ heavy,
870
+ prompt: row.prompt
871
+ });
872
+ }
873
+ return out;
874
+ }
875
+ function parsePalettes(raw) {
876
+ const rec = isRecord(raw) ? raw : {};
877
+ const list = Array.isArray(rec.palettes) ? rec.palettes : [];
878
+ const out = [];
879
+ for (const p of list) {
880
+ if (!isRecord(p) || typeof p.id !== "string") continue;
881
+ if (p.custom === false) continue;
882
+ out.push({
883
+ id: p.id,
884
+ label: typeof p.label === "string" ? p.label : p.id,
885
+ swatch: typeof p.swatch === "string" ? p.swatch : "#888",
886
+ tokens: isRecord(p.tokens) ? p.tokens : void 0
887
+ });
888
+ }
889
+ return out;
890
+ }
891
+ function createRestPanelHost(opts) {
892
+ const storage = opts.storage ?? (typeof window !== "undefined" ? window.localStorage : null);
893
+ function origin() {
894
+ return (opts.getHostUrl ? opts.getHostUrl() : opts.hostUrl).replace(/\/$/, "");
895
+ }
896
+ const host = {
897
+ locale: opts.locale,
898
+ emptyText: opts.emptyText,
899
+ fetchApps: async () => parseAppsResponse(await readJson(`${origin()}/api/apps`)),
900
+ palettes: async () => {
901
+ try {
902
+ return parsePalettes(await readJson(`${origin()}/api/palettes`));
903
+ } catch {
904
+ return [];
905
+ }
906
+ },
907
+ openPanel: () => opts.onOpen?.(),
908
+ closePanel: () => opts.onClose?.(),
909
+ persistTheme: (theme, palette) => {
910
+ try {
911
+ storage?.setItem("mma-theme-mode", theme);
912
+ storage?.setItem("mma-palette", palette);
913
+ } catch {
914
+ }
915
+ void fetch(`${origin()}/api/host-config`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ theme, palette }) }).catch(() => {
916
+ });
917
+ },
918
+ appTheme: {
919
+ save: async (appId, t) => {
920
+ await fetch(`${origin()}/api/apps/${encodeURIComponent(appId)}/theme`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(t) }).catch(() => {
921
+ });
922
+ },
923
+ clear: async (appId) => {
924
+ await fetch(`${origin()}/api/apps/${encodeURIComponent(appId)}/theme`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ reset: true }) }).catch(() => {
925
+ });
926
+ }
927
+ },
928
+ config: {
929
+ load: async () => hostConfigToForm(await readJson(`${origin()}/api/host-config`), opts.getCardStyle?.() ?? opts.cardStyle ?? "stamp"),
930
+ save: async (cfg) => {
931
+ const body = formToHostConfigBody(cfg);
932
+ const res = await fetch(`${origin()}/api/host-config`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
933
+ const raw = await res.json().catch(() => ({}));
934
+ if (!res.ok || isRecord(raw) && raw.ok === false) {
935
+ throw new Error(isRecord(raw) && typeof raw.error === "string" ? raw.error : `HTTP ${res.status}`);
936
+ }
937
+ const hostPort = isRecord(raw) ? raw.hostPort : void 0;
938
+ if (typeof hostPort === "number" && Number.isInteger(hostPort) && hostPort > 0) {
939
+ const next = `${new URL(origin()).protocol}//${new URL(origin()).hostname}:${hostPort}`;
940
+ if (next !== origin()) opts.onHostChange?.(next);
941
+ }
942
+ opts.onConfigSaved?.(cfg);
943
+ }
944
+ },
945
+ about: {
946
+ load: async () => parseAbout(await readJson(`${origin()}/api/about`)),
947
+ checkUpdates: async () => parseUpdateCheck(await readJson(`${origin()}/api/updates`))
948
+ },
949
+ history: {
950
+ list: async (appId) => parseCommitList(await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/history?limit=50`)),
951
+ detail: async (appId, id) => parseCommitDetail(await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/history/${encodeURIComponent(id)}`), id)
952
+ },
953
+ storage: {
954
+ listTables: async (appId) => {
955
+ const raw = await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/storage`);
956
+ return {
957
+ tables: parseStorageTables(raw),
958
+ notices: parseStorageNotices(raw, appId)
959
+ };
960
+ },
961
+ readTable: async (appId, name) => {
962
+ const raw = await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/storage/${encodeURIComponent(name)}`);
963
+ return isRecord(raw) && "value" in raw ? raw.value : raw;
964
+ }
965
+ },
966
+ deleteApp: opts.deleteApp ?? (async (appId) => {
967
+ await fetch(`${origin()}/api/app/${encodeURIComponent(appId)}`, { method: "DELETE" }).catch(() => {
968
+ });
969
+ }),
970
+ frame: opts.frameController ?? {
971
+ url: (appId) => `${origin()}/app/${encodeURIComponent(appId)}`,
972
+ mount: () => void 0,
973
+ unmount: () => void 0,
974
+ reload: () => void 0,
975
+ syncEnv: () => void 0
729
976
  }
730
- if (vars.bg && vars.fg && vars.primary) out[mode] = vars;
731
- }
732
- if (!out.light || !out.dark) return null;
733
- const base = {
734
- surfaceFg: out.light.fg,
735
- secondary: out.light.muted,
736
- secondaryFg: out.light.fg,
737
- destructiveFg: "#ffffff",
738
- radius: "12px",
739
- shadow: "rgba(15, 23, 42, 0.1)"
740
977
  };
741
- const fill = (v) => Object.assign({}, base, v);
742
- return { light: fill(out.light), dark: fill(out.dark) };
743
- }
744
- function themeLabelFromCss(css, fallback) {
745
- const m = /\/\*\s*name\s*:\s*([^*]+)\*\//.exec(String(css || ""));
746
- return m && m[1].trim() || fallback;
747
- }
748
- function clampPalette(value) {
749
- if (value === "tokyo" || value === "forest" || value === "matcha" || value === "yellow" || value === "zoro" || value === "hokage" || value === "slate" || value === "default") {
750
- return value;
751
- }
752
- if (value === "ocean" || value === "mist") return "tokyo";
753
- if (value === "violet" || value === "ink") return "matcha";
754
- if (value === "paper" || value === "noir") return "slate";
755
- return "default";
756
- }
757
- function clampMode(value) {
758
- return value === "dark" ? "dark" : "light";
978
+ return host;
759
979
  }
760
- function tokensOf(palette, mode) {
761
- return TOKENS[clampPalette(palette)][clampMode(mode)];
980
+ var none = {
981
+ history: false,
982
+ storage: false,
983
+ config: false,
984
+ appTheme: false,
985
+ customPalettes: false,
986
+ deleteApp: false
987
+ };
988
+ var initial = {
989
+ tabs: [{ id: "all", title: "\u5168\u90E8", kind: "all" }],
990
+ active: "all",
991
+ apps: [],
992
+ error: null,
993
+ loading: false,
994
+ query: "",
995
+ dock: "fill",
996
+ theme: "light",
997
+ palette: "default",
998
+ themeScope: "global",
999
+ customPalettes: {},
1000
+ cardStyle: "stamp",
1001
+ visible: false,
1002
+ pendingDelete: null,
1003
+ themePopOpen: false,
1004
+ settingsOpen: false,
1005
+ cfgMsg: "",
1006
+ cfgVersion: 0,
1007
+ cfg: {},
1008
+ about: null,
1009
+ updateCheck: null,
1010
+ emptyText: void 0,
1011
+ capabilities: none,
1012
+ locale: "zh-CN",
1013
+ browseOpen: false,
1014
+ browseKind: "history",
1015
+ browseAppId: null,
1016
+ browseAppName: "",
1017
+ browseLoading: false,
1018
+ browseError: null,
1019
+ browseList: [],
1020
+ browseDetail: null,
1021
+ browseTable: null,
1022
+ browseTableValue: null,
1023
+ browseOpenFile: null,
1024
+ storageNotice: null
1025
+ };
1026
+ var state = { ...initial, capabilities: { ...none } };
1027
+ var listeners = /* @__PURE__ */ new Set();
1028
+ function getPanelState() {
1029
+ return state;
762
1030
  }
763
- function cssVars(t) {
764
- return [
765
- `--background:${t.bg}`,
766
- `--foreground:${t.fg}`,
767
- `--card:${t.surface}`,
768
- `--card-foreground:${t.surfaceFg}`,
769
- `--primary:${t.primary}`,
770
- `--primary-foreground:${t.primaryFg}`,
771
- `--secondary:${t.secondary}`,
772
- `--secondary-foreground:${t.secondaryFg}`,
773
- `--muted:${t.muted}`,
774
- `--muted-foreground:${t.mutedFg}`,
775
- `--accent:${t.accent}`,
776
- `--accent-foreground:${t.accentFg}`,
777
- `--destructive:${t.destructive}`,
778
- `--destructive-foreground:${t.destructiveFg}`,
779
- `--border:${t.border}`,
780
- `--input:${t.input}`,
781
- `--ring:${t.ring}`,
782
- `--radius:${t.radius}`,
783
- `--shadow:${t.shadow}`,
784
- `--color-background:var(--background)`,
785
- `--color-surface:var(--card)`,
786
- `--color-foreground:var(--foreground)`,
787
- `--color-primary:var(--primary)`,
788
- `--color-primary-foreground:var(--primary-foreground)`,
789
- `--color-muted:var(--muted)`,
790
- `--color-muted-foreground:var(--muted-foreground)`,
791
- `--color-border:var(--border)`,
792
- `--radius-md:var(--radius)`,
793
- `--space-4:16px`,
794
- `--font-sans:ui-sans-serif,system-ui,-apple-system,sans-serif`
795
- ].join(";");
1031
+ function setPanelState(patch) {
1032
+ state = { ...state, ...patch };
1033
+ for (const fn of listeners) fn();
1034
+ return state;
796
1035
  }
797
- function runnerThemeCss() {
798
- const blocks = [
799
- `:root,html[data-theme="light"]:not([data-palette]),html[data-theme="light"][data-palette="default"]{${cssVars(TOKENS.default.light)}}`,
800
- `html[data-theme="dark"]:not([data-palette]),html[data-theme="dark"][data-palette="default"]{${cssVars(TOKENS.default.dark)}}`
801
- ];
802
- ["tokyo", "forest", "matcha", "yellow", "zoro", "hokage", "slate"].forEach((id) => {
803
- blocks.push(`html[data-theme="light"][data-palette="${id}"]{${cssVars(TOKENS[id].light)}}`);
804
- blocks.push(`html[data-theme="dark"][data-palette="${id}"]{${cssVars(TOKENS[id].dark)}}`);
805
- });
806
- blocks.push(`html[data-theme="dark"] input[type="date"],html[data-theme="dark"] input[type="time"],html[data-theme="dark"] input[type="datetime-local"],html[data-theme="dark"] input[type="month"],html[data-theme="dark"] input[type="week"]{color-scheme:dark}`);
807
- blocks.push(`html[data-theme="light"] input[type="date"],html[data-theme="light"] input[type="time"],html[data-theme="light"] input[type="datetime-local"],html[data-theme="light"] input[type="month"],html[data-theme="light"] input[type="week"]{color-scheme:light}`);
808
- return blocks.join("\n ");
1036
+ function subscribePanel(listener) {
1037
+ listeners.add(listener);
1038
+ return () => {
1039
+ listeners.delete(listener);
1040
+ };
809
1041
  }
810
- function applyThemeTo(el, theme, palette, custom) {
811
- const mode = clampMode(theme);
812
- const c = custom && palette ? custom[String(palette)] : void 0;
813
- const pal = c ? String(palette) : clampPalette(palette);
814
- const t = c && c.tokens && c.tokens[mode] || tokensOf(pal, mode);
815
- el.setAttribute("data-theme", mode);
816
- el.setAttribute("data-palette", pal);
817
- const map = {
818
- "--dsw-alias-bg": t.bg,
819
- "--dsw-alias-fg": t.fg,
820
- "--dsw-alias-surface": t.surface,
821
- "--dsw-alias-border": t.border,
822
- "--dsw-alias-muted": t.muted,
823
- "--dsw-alias-primary": t.primary,
824
- "--dsw-alias-primary-fg": t.primaryFg,
825
- "--dsw-alias-accent": t.accent,
826
- "--dsw-alias-shadow": t.shadow,
827
- "--background": t.bg,
828
- "--foreground": t.fg,
829
- "--card": t.surface,
830
- "--card-foreground": t.surfaceFg,
831
- "--primary": t.primary,
832
- "--primary-foreground": t.primaryFg,
833
- "--secondary": t.secondary,
834
- "--secondary-foreground": t.secondaryFg,
835
- "--muted": t.muted,
836
- "--muted-foreground": t.mutedFg,
837
- "--accent": t.accent,
838
- "--accent-foreground": t.accentFg,
839
- "--border": t.border,
840
- "--input": t.input,
841
- "--ring": t.ring,
842
- "--destructive": t.destructive,
843
- "--destructive-foreground": t.destructiveFg,
844
- "--radius": t.radius,
845
- "--shadow": t.shadow
1042
+ function resetPanelState(seed) {
1043
+ state = {
1044
+ ...initial,
1045
+ capabilities: { ...none },
1046
+ customPalettes: {},
1047
+ cfg: {},
1048
+ tabs: [{ id: "all", title: "\u5168\u90E8", kind: "all" }],
1049
+ ...seed || {}
846
1050
  };
847
- for (const k of Object.keys(map)) el.style.setProperty(k, map[k]);
848
- el.style.background = t.bg;
849
- el.style.color = t.fg;
850
- return { theme: mode, palette: pal };
1051
+ for (const fn of listeners) fn();
1052
+ return state;
1053
+ }
1054
+ function usePanelState() {
1055
+ return React4.useSyncExternalStore(subscribePanel, getPanelState);
851
1056
  }
852
1057
 
853
1058
  // src/actions.ts
854
1059
  function activeAppFrom(state2) {
855
- const tab = state2.tabs.find((t) => t.kind === "app" && t.id === state2.active) || state2.tabs.find((t) => t.kind === "app");
1060
+ const tab = state2.tabs.find((t) => t.kind === "app" && t.id === state2.active);
856
1061
  return tab?.app ?? null;
857
1062
  }
858
1063
  function errorMessage(err) {
@@ -869,6 +1074,17 @@ function pushAppTab(app) {
869
1074
  function asCustomPalettes(value) {
870
1075
  return value;
871
1076
  }
1077
+ function loadCustomPalettes(host) {
1078
+ if (!host.palettes) return;
1079
+ host.palettes().then((list) => {
1080
+ const custom = {};
1081
+ for (const p of list) {
1082
+ custom[p.id] = { label: p.label, swatch: p.swatch, tokens: p.tokens };
1083
+ }
1084
+ setPanelState({ customPalettes: custom });
1085
+ }).catch(() => {
1086
+ });
1087
+ }
872
1088
  function createPanelActions(host, getRootEl, i18n) {
873
1089
  setPanelState({ capabilities: capabilitiesOf(host), locale: i18n.locale, emptyText: host.emptyText });
874
1090
  return {
@@ -883,14 +1099,18 @@ function createPanelActions(host, getRootEl, i18n) {
883
1099
  const tab = s.tabs.find((t) => t.id === id);
884
1100
  setPanelState({
885
1101
  tabs: s.tabs.filter((t) => t.id !== id),
886
- active: s.active === id ? "all" : s.active
1102
+ active: s.active === id ? "all" : s.active,
1103
+ ...s.active === id ? { themeScope: "global" } : {}
887
1104
  });
888
1105
  if (tab?.app) host.frame.unmount(tab.app.id);
889
1106
  },
890
1107
  switchTab: (id) => {
891
- setPanelState({ active: id });
892
1108
  const s = getPanelState();
893
1109
  const tab = s.tabs.find((t) => t.id === id && t.kind === "app");
1110
+ setPanelState({
1111
+ active: id,
1112
+ ...!tab?.app ? { themeScope: "global" } : {}
1113
+ });
894
1114
  if (tab?.app) host.frame.mount(tab.app.id);
895
1115
  },
896
1116
  setDock: (next) => {
@@ -901,31 +1121,38 @@ function createPanelActions(host, getRootEl, i18n) {
901
1121
  }
902
1122
  },
903
1123
  setQuery: (q) => setPanelState({ query: q }),
904
- toggleThemePop: () => setPanelState({ themePopOpen: !getPanelState().themePopOpen }),
1124
+ toggleThemePop: () => {
1125
+ const next = !getPanelState().themePopOpen;
1126
+ setPanelState({ themePopOpen: next });
1127
+ if (next) loadCustomPalettes(host);
1128
+ },
905
1129
  setAppearance: (next, scope) => {
906
1130
  const s = getPanelState();
907
- const theme = next.theme ? String(next.theme) : s.theme;
908
- const palette = next.palette ? String(next.palette) : s.palette;
909
- setPanelState({ theme, palette });
910
- const root = getRootEl();
911
- if (root) {
912
- applyThemeTo(root, theme, palette, asCustomPalettes(s.customPalettes));
913
- }
914
1131
  if (scope === "app") {
915
- const app = activeAppFrom(getPanelState());
1132
+ const app = activeAppFrom(s);
916
1133
  if (app && host.appTheme) {
917
- const nextTheme = { theme, palette };
1134
+ const base = app.theme ?? { theme: s.theme, palette: s.palette };
1135
+ const nextTheme = {
1136
+ theme: next.theme ? String(next.theme) : base.theme,
1137
+ palette: next.palette ? String(next.palette) : base.palette
1138
+ };
918
1139
  host.appTheme.save(app.id, nextTheme).catch(() => {
919
1140
  });
920
1141
  const cur = getPanelState();
1142
+ const patchApp = (a) => a.id === app.id ? { ...a, theme: nextTheme } : a;
921
1143
  setPanelState({
922
- apps: cur.apps.some((a) => a.id === app.id) ? cur.apps.map((a) => a.id === app.id ? { ...a, theme: nextTheme } : a) : [...cur.apps, { ...app, theme: nextTheme }],
923
- tabs: cur.tabs.map(
924
- (t) => t.app?.id === app.id ? { ...t, app: { ...t.app, theme: nextTheme } } : t
925
- )
1144
+ apps: cur.apps.some((a) => a.id === app.id) ? cur.apps.map(patchApp) : [...cur.apps, { ...app, theme: nextTheme }],
1145
+ tabs: cur.tabs.map((t) => t.app?.id === app.id ? { ...t, app: { ...t.app, theme: nextTheme } } : t)
926
1146
  });
927
1147
  }
928
1148
  } else {
1149
+ const theme = next.theme ? String(next.theme) : s.theme;
1150
+ const palette = next.palette ? String(next.palette) : s.palette;
1151
+ setPanelState({ theme, palette });
1152
+ const root = getRootEl();
1153
+ if (root) {
1154
+ applyThemeTo(root, theme, palette, asCustomPalettes(s.customPalettes));
1155
+ }
929
1156
  host.persistTheme?.(theme, palette);
930
1157
  }
931
1158
  host.frame.syncEnv?.();
@@ -948,23 +1175,71 @@ function createPanelActions(host, getRootEl, i18n) {
948
1175
  },
949
1176
  getActiveApp: () => activeAppFrom(getPanelState()),
950
1177
  toggleSettings: (open) => {
951
- setPanelState({ settingsOpen: open, cfgMsg: open ? "" : getPanelState().cfgMsg });
1178
+ setPanelState({
1179
+ settingsOpen: open,
1180
+ cfgMsg: open ? "" : getPanelState().cfgMsg,
1181
+ updateCheck: open ? null : getPanelState().updateCheck
1182
+ });
952
1183
  if (open && host.config) {
953
1184
  host.config.load().then((cfg) => {
954
1185
  setPanelState({ cfg, cfgVersion: getPanelState().cfgVersion + 1 });
955
1186
  }).catch(() => {
956
1187
  });
957
1188
  }
1189
+ if (open && host.about) {
1190
+ host.about.load().then((about) => setPanelState({ about })).catch(() => setPanelState({ about: null }));
1191
+ }
1192
+ },
1193
+ getCfg: () => getPanelState().cfg,
1194
+ checkUpdates: () => {
1195
+ if (!host.about) return;
1196
+ setPanelState({
1197
+ updateCheck: {
1198
+ name: "",
1199
+ current: "",
1200
+ latest: null,
1201
+ updateAvailable: false,
1202
+ status: "loading"
1203
+ }
1204
+ });
1205
+ host.about.checkUpdates().then((check) => setPanelState({ updateCheck: { ...check, status: "done" } })).catch(
1206
+ (err) => setPanelState({
1207
+ updateCheck: {
1208
+ name: "",
1209
+ current: "",
1210
+ latest: null,
1211
+ updateAvailable: false,
1212
+ status: "done",
1213
+ error: errorMessage(err)
1214
+ }
1215
+ })
1216
+ );
958
1217
  },
959
- getCfg: () => getPanelState().cfg,
960
1218
  saveHostConfig: (form) => {
961
1219
  if (!host.config) return;
962
1220
  host.config.save(form).then(() => {
1221
+ const style = form.cardStyle || getPanelState().cardStyle;
1222
+ const theme = form.theme || getPanelState().theme;
1223
+ const palette = form.palette || getPanelState().palette;
963
1224
  setPanelState({
964
1225
  cfg: { ...form },
965
1226
  cfgMsg: i18n.t("config.saved"),
966
- cfgVersion: getPanelState().cfgVersion + 1
1227
+ cfgVersion: getPanelState().cfgVersion + 1,
1228
+ cardStyle: style,
1229
+ theme,
1230
+ palette
967
1231
  });
1232
+ const root = getRootEl();
1233
+ if (root) {
1234
+ applyThemeTo(root, theme, palette, asCustomPalettes(getPanelState().customPalettes));
1235
+ root.setAttribute("data-cardstyle", style);
1236
+ }
1237
+ host.persistTheme?.(theme, palette);
1238
+ host.frame.syncEnv?.();
1239
+ try {
1240
+ localStorage.setItem("mma-card-style", style);
1241
+ } catch {
1242
+ }
968
1243
  }).catch((e) => {
969
1244
  setPanelState({ cfgMsg: i18n.t("config.error", { message: errorMessage(e) }) });
970
1245
  });
@@ -998,11 +1273,22 @@ function createPanelActions(host, getRootEl, i18n) {
998
1273
  setPanelState({ browseList: list, browseLoading: false });
999
1274
  }).catch((e) => setPanelState({ browseError: errorMessage(e), browseLoading: false }));
1000
1275
  } else if (host.storage) {
1001
- host.storage.listTables(app.id).then((list) => {
1002
- setPanelState({ browseList: list, browseLoading: false });
1276
+ host.storage.listTables(app.id).then(({ tables, notices }) => {
1277
+ const notice = notices[0] ?? null;
1278
+ setPanelState({
1279
+ browseList: tables,
1280
+ browseLoading: false,
1281
+ ...notice ? { storageNotice: notice } : {}
1282
+ });
1003
1283
  }).catch((e) => setPanelState({ browseError: errorMessage(e), browseLoading: false }));
1004
1284
  }
1005
1285
  },
1286
+ dismissStorageNotice: () => {
1287
+ setPanelState({ storageNotice: null });
1288
+ },
1289
+ applyStorageNotice: (notice) => {
1290
+ setPanelState({ storageNotice: notice });
1291
+ },
1006
1292
  loadCommitDetail: (id) => {
1007
1293
  const s = getPanelState();
1008
1294
  if (!host.history || !s.browseAppId) return;
@@ -1061,7 +1347,14 @@ function createPanelActions(host, getRootEl, i18n) {
1061
1347
  });
1062
1348
  });
1063
1349
  },
1064
- setCardStyle: (v) => setPanelState({ cardStyle: v })
1350
+ setCardStyle: (v) => {
1351
+ setPanelState({ cardStyle: v });
1352
+ getRootEl()?.setAttribute("data-cardstyle", v);
1353
+ try {
1354
+ localStorage.setItem("mma-card-style", v);
1355
+ } catch {
1356
+ }
1357
+ }
1065
1358
  };
1066
1359
  }
1067
1360
  function defaultHideThemePop() {
@@ -1072,8 +1365,8 @@ function defaultHideThemePop() {
1072
1365
  function clampPaletteId(v) {
1073
1366
  return clampPalette(v);
1074
1367
  }
1075
- var PanelActionsContext = React__namespace.createContext(null);
1076
- var PanelI18nContext = React__namespace.createContext(null);
1368
+ var PanelActionsContext = React4__namespace.createContext(null);
1369
+ var PanelI18nContext = React4__namespace.createContext(null);
1077
1370
  function PanelProvider({
1078
1371
  actions,
1079
1372
  i18n,
@@ -1082,12 +1375,12 @@ function PanelProvider({
1082
1375
  return /* @__PURE__ */ jsxRuntime.jsx(PanelI18nContext.Provider, { value: i18n, children: /* @__PURE__ */ jsxRuntime.jsx(PanelActionsContext.Provider, { value: actions, children }) });
1083
1376
  }
1084
1377
  function usePanelActions() {
1085
- const ctx = React__namespace.useContext(PanelActionsContext);
1378
+ const ctx = React4__namespace.useContext(PanelActionsContext);
1086
1379
  if (!ctx) throw new Error("usePanelActions must be used inside <PanelProvider>");
1087
1380
  return ctx;
1088
1381
  }
1089
1382
  function usePanelI18n() {
1090
- const ctx = React__namespace.useContext(PanelI18nContext);
1383
+ const ctx = React4__namespace.useContext(PanelI18nContext);
1091
1384
  if (!ctx) throw new Error("usePanelI18n must be used inside <PanelProvider>");
1092
1385
  return ctx;
1093
1386
  }
@@ -1147,6 +1440,7 @@ function AppCard({ app }) {
1147
1440
  {
1148
1441
  type: "button",
1149
1442
  className: "mma-card",
1443
+ "data-app-id": app.id,
1150
1444
  style: { "--h": hue(app.id) },
1151
1445
  onClick: () => actions.openAppTab(app),
1152
1446
  children: [
@@ -1175,6 +1469,7 @@ function AppRow({ app }) {
1175
1469
  {
1176
1470
  type: "button",
1177
1471
  className: "mma-row",
1472
+ "data-app-id": app.id,
1178
1473
  style: { "--h": hue(app.id) },
1179
1474
  onClick: () => actions.openAppTab(app),
1180
1475
  children: [
@@ -1282,26 +1577,60 @@ function CommitItem({ c }) {
1282
1577
  ] })
1283
1578
  ] });
1284
1579
  }
1580
+ function humanBytes(n, t) {
1581
+ if (n < 1024) return `${n} B`;
1582
+ if (n < 1024 * 1024) return t("browse.sizeKb", { value: (n / 1024).toFixed(1) });
1583
+ return t("browse.sizeMb", { value: (n / (1024 * 1024)).toFixed(1) });
1584
+ }
1285
1585
  function StorageItem({ table }) {
1286
1586
  const actions = usePanelActions();
1587
+ const { t } = usePanelI18n();
1287
1588
  return /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", className: "mma-bitem", onClick: () => actions.loadTable(table.name), children: [
1288
1589
  /* @__PURE__ */ jsxRuntime.jsx("b", { children: table.name }),
1289
1590
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "meta", children: [
1290
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
1291
- table.size || 0,
1292
- " B"
1293
- ] }),
1591
+ typeof table.keys === "number" ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("browse.entries", { count: table.keys }) }) : null,
1592
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: humanBytes(table.size || 0, t) }),
1294
1593
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: table.updatedAt || "" })
1295
1594
  ] })
1296
1595
  ] });
1297
1596
  }
1597
+ function StorageNoticeBanner() {
1598
+ const s = usePanelState();
1599
+ const actions = usePanelActions();
1600
+ const { t } = usePanelI18n();
1601
+ const notice = s.storageNotice;
1602
+ const [copied, setCopied] = React4__namespace.useState(false);
1603
+ if (!notice) return null;
1604
+ if (s.browseAppId && notice.appId !== s.browseAppId) return null;
1605
+ const top = notice.heavy[0];
1606
+ const summary = top ? t("browse.noticeTop", { key: top.key, size: humanBytes(top.bytes, t) }) : "";
1607
+ const copyPrompt = async () => {
1608
+ try {
1609
+ await navigator.clipboard.writeText(notice.prompt);
1610
+ setCopied(true);
1611
+ window.setTimeout(() => setCopied(false), 1500);
1612
+ } catch {
1613
+ }
1614
+ };
1615
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-storage-notice", role: "status", children: [
1616
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-storage-notice-body", children: [
1617
+ /* @__PURE__ */ jsxRuntime.jsx("b", { children: t("browse.noticeTitle", { table: notice.table, size: humanBytes(notice.bytes, t) }) }),
1618
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: t("browse.noticeBody") }),
1619
+ summary ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mma-storage-notice-top", children: summary }) : null
1620
+ ] }),
1621
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-storage-notice-actions", children: [
1622
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "mma-storage-notice-copy", onClick: () => void copyPrompt(), children: copied ? t("browse.noticeCopied") : t("browse.noticeCopy") }),
1623
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => actions.dismissStorageNotice(), children: t("browse.noticeDismiss") })
1624
+ ] })
1625
+ ] });
1626
+ }
1298
1627
  function CommitDetail() {
1299
1628
  const s = usePanelState();
1300
1629
  const actions = usePanelActions();
1301
1630
  const { t } = usePanelI18n();
1302
- const [msgExpanded, setMsgExpanded] = React__namespace.useState(false);
1631
+ const [msgExpanded, setMsgExpanded] = React4__namespace.useState(false);
1303
1632
  const d = s.browseDetail;
1304
- React__namespace.useEffect(() => {
1633
+ React4__namespace.useEffect(() => {
1305
1634
  setMsgExpanded(false);
1306
1635
  }, [d?.id]);
1307
1636
  if (!d) return null;
@@ -1389,7 +1718,10 @@ function Browse() {
1389
1718
  ) })
1390
1719
  ] });
1391
1720
  }
1392
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mma-browse", id: "mma-browse", "data-open": "1", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mma-browse-body", id: "mma-browse-body", children: content }) });
1721
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mma-browse", id: "mma-browse", "data-open": "1", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-browse-body", id: "mma-browse-body", children: [
1722
+ s.browseKind === "storage" ? /* @__PURE__ */ jsxRuntime.jsx(StorageNoticeBanner, {}) : null,
1723
+ content
1724
+ ] }) });
1393
1725
  }
1394
1726
  function Modal() {
1395
1727
  const s = usePanelState();
@@ -1406,66 +1738,676 @@ function Modal() {
1406
1738
  ] })
1407
1739
  ] }) });
1408
1740
  }
1741
+ function OptionCardGrid({
1742
+ options,
1743
+ value,
1744
+ onChange,
1745
+ columns = 3,
1746
+ "aria-label": ariaLabel,
1747
+ className,
1748
+ trailing
1749
+ }) {
1750
+ const refs = React4__namespace.useRef([]);
1751
+ const values = options.map((o) => o.value);
1752
+ const focusAt = (index) => {
1753
+ const el = refs.current[index];
1754
+ el?.focus();
1755
+ };
1756
+ const onKeyDown = (e, index) => {
1757
+ const enabled = options.map((o, i) => ({ o, i })).filter((x) => !x.o.disabled);
1758
+ if (!enabled.length) return;
1759
+ const pos = enabled.findIndex((x) => x.i === index);
1760
+ if (pos < 0) return;
1761
+ let nextPos = pos;
1762
+ if (e.key === "ArrowRight" || e.key === "ArrowDown") {
1763
+ e.preventDefault();
1764
+ nextPos = (pos + 1) % enabled.length;
1765
+ } else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
1766
+ e.preventDefault();
1767
+ nextPos = (pos - 1 + enabled.length) % enabled.length;
1768
+ } else if (e.key === "Home") {
1769
+ e.preventDefault();
1770
+ nextPos = 0;
1771
+ } else if (e.key === "End") {
1772
+ e.preventDefault();
1773
+ nextPos = enabled.length - 1;
1774
+ } else {
1775
+ return;
1776
+ }
1777
+ const next = enabled[nextPos];
1778
+ if (!next) return;
1779
+ focusAt(next.i);
1780
+ if (!next.o.disabled) onChange(next.o.value);
1781
+ };
1782
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1783
+ "div",
1784
+ {
1785
+ className: `mma-optgrid mma-optgrid--${columns}${className ? ` ${className}` : ""}`,
1786
+ role: "radiogroup",
1787
+ "aria-label": ariaLabel,
1788
+ children: [
1789
+ options.map((opt, index) => {
1790
+ const checked = value === opt.value;
1791
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1792
+ "button",
1793
+ {
1794
+ type: "button",
1795
+ role: "radio",
1796
+ "aria-checked": checked,
1797
+ "aria-label": opt.hint ? `${opt.label}. ${opt.hint}` : opt.label,
1798
+ disabled: opt.disabled,
1799
+ tabIndex: checked || !value && index === 0 ? 0 : -1,
1800
+ className: "mma-optcard",
1801
+ "data-on": checked ? "1" : "0",
1802
+ ref: (el) => {
1803
+ refs.current[index] = el;
1804
+ },
1805
+ onClick: () => {
1806
+ if (!opt.disabled) onChange(opt.value);
1807
+ },
1808
+ onKeyDown: (e) => onKeyDown(e, index),
1809
+ children: [
1810
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-optcard-preview", "aria-hidden": "true", children: opt.preview }),
1811
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-optcard-label", children: opt.label }),
1812
+ checked ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-optcard-check", "aria-hidden": "true", children: "\u2713" }) : null
1813
+ ]
1814
+ },
1815
+ opt.value
1816
+ );
1817
+ }),
1818
+ trailing ? /* @__PURE__ */ jsxRuntime.jsxs(
1819
+ "button",
1820
+ {
1821
+ type: "button",
1822
+ className: "mma-optcard mma-optcard--ghost",
1823
+ disabled: trailing.disabled !== false,
1824
+ title: trailing.hint,
1825
+ onClick: () => trailing.onClick?.(),
1826
+ children: [
1827
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-optcard-preview mma-optcard-preview--plus", "aria-hidden": "true", children: "+" }),
1828
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-optcard-label", children: trailing.label })
1829
+ ]
1830
+ }
1831
+ ) : null,
1832
+ /* @__PURE__ */ jsxRuntime.jsx("span", { hidden: true, children: values.join(",") })
1833
+ ]
1834
+ }
1835
+ );
1836
+ }
1837
+ var SECTIONS = ["appearance", "network", "model", "about"];
1838
+ var DEFAULTS = {
1839
+ hostPort: "17880",
1840
+ provider: "",
1841
+ model: "",
1842
+ locale: "zh-CN",
1843
+ theme: "light",
1844
+ palette: "default",
1845
+ cardStyle: "stamp"
1846
+ };
1847
+ function shortPkg(name) {
1848
+ return name.replace(/^@monkey-mini-app\//, "");
1849
+ }
1850
+ function validatePort(raw) {
1851
+ const trimmed = raw.trim();
1852
+ if (!trimmed) return "empty";
1853
+ if (!/^\d+$/.test(trimmed)) return "nan";
1854
+ const n = Number(trimmed);
1855
+ if (!Number.isInteger(n) || n < 1024 || n > 65535) return "range";
1856
+ return null;
1857
+ }
1858
+ function ThemePreview({ mode }) {
1859
+ if (mode === "system") {
1860
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mma-preview-theme mma-preview-theme--system", children: [
1861
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-preview-theme-half mma-preview-theme-half--light" }),
1862
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-preview-theme-half mma-preview-theme-half--dark" })
1863
+ ] });
1864
+ }
1865
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `mma-preview-theme mma-preview-theme--${mode}`, children: [
1866
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-preview-bar" }),
1867
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-preview-bar" })
1868
+ ] });
1869
+ }
1870
+ function PalettePreview({ id, mode }) {
1871
+ const t = tokensOf(id, mode);
1872
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mma-preview-swatches", children: [
1873
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { background: t.primary } }),
1874
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { background: t.secondary } }),
1875
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { background: t.bg } })
1876
+ ] });
1877
+ }
1878
+ function CardStylePreview({ style }) {
1879
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: `mma-preview-cardstyle mma-preview-cardstyle--${style}` });
1880
+ }
1409
1881
  function Settings() {
1410
1882
  const s = usePanelState();
1411
1883
  const actions = usePanelActions();
1412
1884
  const { t } = usePanelI18n();
1413
- const [form, setForm] = React__namespace.useState({});
1414
- React__namespace.useEffect(() => {
1885
+ const [form, setForm] = React4__namespace.useState({});
1886
+ const [snapshot, setSnapshot] = React4__namespace.useState(null);
1887
+ const [section, setSection] = React4__namespace.useState("appearance");
1888
+ const [portTouched, setPortTouched] = React4__namespace.useState(false);
1889
+ const [confirm, setConfirm] = React4__namespace.useState(null);
1890
+ const [lastSavedAt, setLastSavedAt] = React4__namespace.useState(null);
1891
+ const rootRef = React4__namespace.useRef(null);
1892
+ const contentRef = React4__namespace.useRef(null);
1893
+ const sectionRefs = React4__namespace.useRef({});
1894
+ const dirtyCountRef = React4__namespace.useRef(0);
1895
+ const confirmRef = React4__namespace.useRef(confirm);
1896
+ confirmRef.current = confirm;
1897
+ const [wide, setWide] = React4__namespace.useState(false);
1898
+ React4__namespace.useEffect(() => {
1899
+ if (!s.settingsOpen) return;
1900
+ const next = s.cfg && Object.keys(s.cfg).length ? { ...s.cfg } : { ...actions.getCfg() };
1901
+ const snap = {
1902
+ hostPort: next.hostPort || "",
1903
+ provider: next.provider || "",
1904
+ model: next.model || "",
1905
+ theme: next.theme || s.theme || "light",
1906
+ palette: next.palette || s.palette || "default",
1907
+ cardStyle: next.cardStyle || s.cardStyle || "stamp",
1908
+ locale: next.locale || next.chatLanguage || "zh-CN"
1909
+ };
1910
+ setForm({ ...next, ...snap, chatLanguage: snap.locale });
1911
+ setSnapshot(snap);
1912
+ setPortTouched(false);
1913
+ setConfirm(null);
1914
+ setSection("appearance");
1915
+ }, [s.settingsOpen, s.cfgVersion, s.cfg, actions, s.theme, s.palette, s.cardStyle]);
1916
+ React4__namespace.useEffect(() => {
1415
1917
  if (!s.settingsOpen) return;
1416
- setForm(s.cfg && Object.keys(s.cfg).length ? s.cfg : actions.getCfg());
1417
- }, [s.settingsOpen, s.cfgVersion, s.cfg, actions]);
1918
+ const host = rootRef.current?.closest("#mma-host") ?? rootRef.current;
1919
+ if (!host || typeof ResizeObserver === "undefined") return;
1920
+ const ro = new ResizeObserver((entries) => {
1921
+ const w = entries[0]?.contentRect.width ?? 0;
1922
+ setWide(w >= 720);
1923
+ });
1924
+ ro.observe(host);
1925
+ return () => ro.disconnect();
1926
+ }, [s.settingsOpen]);
1927
+ React4__namespace.useEffect(() => {
1928
+ if (!s.settingsOpen) return;
1929
+ const root = contentRef.current;
1930
+ if (!root) return;
1931
+ const nodes = SECTIONS.map((id) => sectionRefs.current[id]).filter(Boolean);
1932
+ if (!nodes.length) return;
1933
+ const obs = new IntersectionObserver(
1934
+ (entries) => {
1935
+ const visible = entries.filter((e) => e.isIntersecting).sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0];
1936
+ const id = visible?.target.getAttribute("data-section");
1937
+ if (id) setSection(id);
1938
+ },
1939
+ { root, threshold: [0.25, 0.5, 0.75], rootMargin: "-20% 0px -55% 0px" }
1940
+ );
1941
+ for (const n of nodes) obs.observe(n);
1942
+ return () => obs.disconnect();
1943
+ }, [s.settingsOpen, form]);
1944
+ React4__namespace.useEffect(() => {
1945
+ if (!s.settingsOpen) return;
1946
+ const onKey = (e) => {
1947
+ if (e.key !== "Escape") return;
1948
+ e.preventDefault();
1949
+ if (confirmRef.current) {
1950
+ setConfirm(null);
1951
+ return;
1952
+ }
1953
+ if (dirtyCountRef.current > 0) setConfirm("close");
1954
+ else actions.toggleSettings(false);
1955
+ };
1956
+ window.addEventListener("keydown", onKey);
1957
+ return () => window.removeEventListener("keydown", onKey);
1958
+ }, [s.settingsOpen, actions]);
1418
1959
  if (!s.capabilities.config) return null;
1419
- const set = (k) => (e) => setForm((prev) => ({ ...prev, [k]: e.target.value }));
1420
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings", id: "mma-settings", "data-open": s.settingsOpen ? "1" : "0", children: [
1421
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-head", children: [
1422
- /* @__PURE__ */ jsxRuntime.jsx("h3", { children: t("settings.title") }),
1423
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "mma-iconbtn", id: "mma-cfg-close", onClick: () => actions.toggleSettings(false), children: "\u2715" })
1424
- ] }),
1425
- /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
1426
- t("settings.hostPort"),
1427
- /* @__PURE__ */ jsxRuntime.jsx("input", { id: "mma-cfg-port", type: "number", value: form.hostPort || "", onChange: set("hostPort") })
1428
- ] }),
1429
- /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
1430
- t("settings.language"),
1431
- /* @__PURE__ */ jsxRuntime.jsxs("select", { id: "mma-cfg-lang", value: form.locale || form.chatLanguage || "zh-CN", onChange: set("locale"), children: [
1432
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "zh-CN", children: t("settings.langZh") }),
1433
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "en", children: t("settings.langEn") })
1434
- ] })
1435
- ] }),
1436
- /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
1437
- t("settings.theme"),
1438
- /* @__PURE__ */ jsxRuntime.jsxs("select", { id: "mma-cfg-theme", value: form.theme || "light", onChange: set("theme"), children: [
1439
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "light", children: t("theme.light") }),
1440
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "dark", children: t("theme.dark") })
1441
- ] })
1442
- ] }),
1443
- /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
1444
- t("settings.palette"),
1445
- /* @__PURE__ */ jsxRuntime.jsx("select", { id: "mma-cfg-palette", value: form.palette || "default", onChange: set("palette"), children: PALETTES.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.id, children: t(`palette.${p.id}`) }, p.id)) })
1446
- ] }),
1447
- /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
1448
- t("settings.cardStyle"),
1449
- /* @__PURE__ */ jsxRuntime.jsxs("select", { id: "mma-cfg-cardstyle", value: form.cardStyle || "stamp", onChange: set("cardStyle"), children: [
1450
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "stamp", children: t("settings.cardStamp") }),
1451
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "etch", children: t("settings.cardEtch") }),
1452
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "hero", children: t("settings.cardHero") }),
1453
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "list", children: t("settings.cardList") })
1454
- ] })
1455
- ] }),
1456
- /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
1457
- t("settings.llmProvider"),
1458
- /* @__PURE__ */ jsxRuntime.jsx("input", { id: "mma-cfg-provider", value: form.provider || "", onChange: set("provider") })
1459
- ] }),
1460
- /* @__PURE__ */ jsxRuntime.jsxs("label", { children: [
1461
- t("settings.llmModel"),
1462
- /* @__PURE__ */ jsxRuntime.jsx("input", { id: "mma-cfg-model", value: form.model || "", onChange: set("model") })
1463
- ] }),
1464
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-actions", children: [
1465
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "mma-textbtn", id: "mma-cfg-save", onClick: () => actions.saveHostConfig(form), children: t("settings.save") }),
1466
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-settings-msg", id: "mma-cfg-msg", children: s.cfgMsg })
1467
- ] })
1468
- ] });
1960
+ const themeMode = form.theme === "system" || form.theme === "dark" || form.theme === "light" ? form.theme : s.theme === "system" || s.theme === "dark" || s.theme === "light" ? s.theme : "light";
1961
+ const resolvedMode = resolveMode(themeMode === "system" ? "system" : themeMode);
1962
+ const palette = form.palette || s.palette || "default";
1963
+ const cardStyle = form.cardStyle || s.cardStyle || "stamp";
1964
+ const locale = form.locale || form.chatLanguage || "zh-CN";
1965
+ const dirty = {
1966
+ hostPort: form.hostPort || "",
1967
+ provider: form.provider || "",
1968
+ model: form.model || "",
1969
+ theme: themeMode,
1970
+ palette,
1971
+ cardStyle,
1972
+ locale
1973
+ };
1974
+ const dirtyKeys = ["hostPort", "provider", "model", "theme", "palette", "cardStyle", "locale"].filter((k) => snapshot != null && dirty[k] !== snapshot[k]);
1975
+ const dirtyCount = dirtyKeys.length;
1976
+ dirtyCountRef.current = dirtyCount;
1977
+ const portDirty = snapshot != null && dirty.hostPort !== snapshot.hostPort;
1978
+ const portErrorKey = portTouched || portDirty ? validatePort(form.hostPort || "") : null;
1979
+ const portError = portErrorKey === "empty" ? t("settings.portEmpty") : portErrorKey === "nan" ? t("settings.portNan") : portErrorKey === "range" ? t("settings.portRange") : null;
1980
+ const canSave = dirtyCount > 0 && !portError;
1981
+ const hostUrl = `http://127.0.0.1:${(form.hostPort || "").trim() || "\u2014"}`;
1982
+ const paletteLabel = t(`palette.${clampPalette(palette)}`);
1983
+ const versionLabel = s.about?.packages?.[0]?.version ?? s.about?.packages?.find((p) => p.name.includes("dsh"))?.version ?? "\u2014";
1984
+ const patch = (partial) => setForm((prev) => ({ ...prev, ...partial }));
1985
+ const paintPreview = (next) => {
1986
+ const host = document.getElementById("mma-host");
1987
+ if (!host) return;
1988
+ const theme = next.theme ?? themeMode;
1989
+ const pal = next.palette ?? palette;
1990
+ const style = next.cardStyle ?? cardStyle;
1991
+ applyThemeTo(host, theme, pal, s.customPalettes);
1992
+ host.setAttribute("data-cardstyle", style);
1993
+ };
1994
+ const previewAppearance = (partial) => {
1995
+ const nextTheme = partial.theme ?? themeMode;
1996
+ const nextPalette = partial.palette ?? palette;
1997
+ const nextCard = partial.cardStyle ?? cardStyle;
1998
+ paintPreview({ theme: nextTheme, palette: nextPalette, cardStyle: nextCard });
1999
+ patch({
2000
+ ...partial.theme != null ? { theme: partial.theme } : {},
2001
+ ...partial.palette != null ? { palette: partial.palette } : {},
2002
+ ...partial.cardStyle != null ? { cardStyle: partial.cardStyle } : {},
2003
+ ...partial.locale != null ? { locale: partial.locale, chatLanguage: partial.locale } : {}
2004
+ });
2005
+ };
2006
+ const scrollTo = (id) => {
2007
+ setSection(id);
2008
+ sectionRefs.current[id]?.scrollIntoView({ behavior: "smooth", block: "start" });
2009
+ };
2010
+ const discard = () => {
2011
+ if (!snapshot) return;
2012
+ patch({ ...snapshot, chatLanguage: snapshot.locale });
2013
+ paintPreview({
2014
+ theme: snapshot.theme,
2015
+ palette: snapshot.palette,
2016
+ cardStyle: snapshot.cardStyle
2017
+ });
2018
+ setPortTouched(false);
2019
+ };
2020
+ const requestClose = () => {
2021
+ if (dirtyCount > 0) setConfirm("close");
2022
+ else actions.toggleSettings(false);
2023
+ };
2024
+ const restoreDefaults = () => {
2025
+ previewAppearance({
2026
+ theme: DEFAULTS.theme,
2027
+ palette: DEFAULTS.palette,
2028
+ cardStyle: DEFAULTS.cardStyle,
2029
+ locale: DEFAULTS.locale
2030
+ });
2031
+ patch({
2032
+ hostPort: DEFAULTS.hostPort,
2033
+ provider: DEFAULTS.provider,
2034
+ model: DEFAULTS.model,
2035
+ locale: DEFAULTS.locale,
2036
+ chatLanguage: DEFAULTS.locale,
2037
+ theme: DEFAULTS.theme,
2038
+ palette: DEFAULTS.palette,
2039
+ cardStyle: DEFAULTS.cardStyle
2040
+ });
2041
+ setPortTouched(true);
2042
+ setConfirm(null);
2043
+ };
2044
+ const save = () => {
2045
+ if (!canSave) return;
2046
+ const committed = { ...dirty };
2047
+ actions.saveHostConfig({
2048
+ ...form,
2049
+ theme: committed.theme,
2050
+ palette: committed.palette,
2051
+ cardStyle: committed.cardStyle,
2052
+ locale: committed.locale,
2053
+ chatLanguage: committed.locale
2054
+ });
2055
+ actions.setAppearance(
2056
+ { theme: committed.theme, palette: committed.palette },
2057
+ "global"
2058
+ );
2059
+ actions.setCardStyle(committed.cardStyle);
2060
+ setSnapshot(committed);
2061
+ const now = /* @__PURE__ */ new Date();
2062
+ setLastSavedAt(
2063
+ `${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`
2064
+ );
2065
+ };
2066
+ const copyUrl = async () => {
2067
+ try {
2068
+ await navigator.clipboard.writeText(hostUrl);
2069
+ } catch {
2070
+ }
2071
+ };
2072
+ const sectionLabel = (id) => id === "appearance" ? t("settings.sectionAppearance") : id === "network" ? t("settings.sectionNetwork") : id === "model" ? t("settings.sectionModel") : t("settings.sectionAbout");
2073
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2074
+ "div",
2075
+ {
2076
+ ref: rootRef,
2077
+ className: "mma-settings",
2078
+ id: "mma-settings",
2079
+ "data-open": s.settingsOpen ? "1" : "0",
2080
+ "data-wide": wide ? "1" : "0",
2081
+ children: [
2082
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "mma-settings-head", children: [
2083
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-head-left", children: [
2084
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-settings-head-icon", "aria-hidden": "true", children: "\u2699" }),
2085
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { children: t("settings.title") })
2086
+ ] }),
2087
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-head-right", children: [
2088
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mma-settings-meta", children: [
2089
+ versionLabel,
2090
+ " \xB7 ",
2091
+ paletteLabel
2092
+ ] }),
2093
+ /* @__PURE__ */ jsxRuntime.jsx(
2094
+ "button",
2095
+ {
2096
+ type: "button",
2097
+ className: "mma-iconbtn",
2098
+ id: "mma-cfg-close",
2099
+ "aria-label": t("settings.close"),
2100
+ onClick: requestClose,
2101
+ children: "\u2715"
2102
+ }
2103
+ )
2104
+ ] })
2105
+ ] }),
2106
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-body", children: [
2107
+ /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "mma-settings-rail", role: "tablist", "aria-label": t("settings.sectionsAria"), children: [
2108
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mma-settings-rail-title", children: t("settings.sectionNav") }),
2109
+ SECTIONS.map((id) => /* @__PURE__ */ jsxRuntime.jsx(
2110
+ "button",
2111
+ {
2112
+ type: "button",
2113
+ role: "tab",
2114
+ "aria-current": section === id ? "page" : void 0,
2115
+ "data-on": section === id ? "1" : "0",
2116
+ className: "mma-settings-rail-item",
2117
+ onClick: () => scrollTo(id),
2118
+ children: sectionLabel(id)
2119
+ },
2120
+ id
2121
+ ))
2122
+ ] }),
2123
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-content", ref: contentRef, children: [
2124
+ /* @__PURE__ */ jsxRuntime.jsxs(
2125
+ "section",
2126
+ {
2127
+ className: "mma-settings-section",
2128
+ "data-section": "appearance",
2129
+ ref: (el) => {
2130
+ sectionRefs.current.appearance = el;
2131
+ },
2132
+ children: [
2133
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { children: t("settings.sectionAppearance") }),
2134
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-field", children: [
2135
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "mma-settings-label", htmlFor: "mma-cfg-lang-seg", children: t("settings.language") }),
2136
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mma-seg", id: "mma-cfg-lang-seg", role: "group", "aria-label": t("settings.language"), children: [
2137
+ ["zh-CN", t("settings.langZh")],
2138
+ ["en", t("settings.langEn")]
2139
+ ].map(([id, label]) => /* @__PURE__ */ jsxRuntime.jsx(
2140
+ "button",
2141
+ {
2142
+ type: "button",
2143
+ "data-on": locale === id ? "1" : "0",
2144
+ onClick: () => previewAppearance({ locale: id }),
2145
+ children: label
2146
+ },
2147
+ id
2148
+ )) })
2149
+ ] }),
2150
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-field", children: [
2151
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-settings-label", children: t("settings.theme") }),
2152
+ /* @__PURE__ */ jsxRuntime.jsx(
2153
+ OptionCardGrid,
2154
+ {
2155
+ "aria-label": t("settings.theme"),
2156
+ columns: 3,
2157
+ value: themeMode,
2158
+ onChange: (v) => previewAppearance({ theme: v }),
2159
+ options: ["system", "light", "dark"].map((mode) => ({
2160
+ value: mode,
2161
+ label: t(`theme.${mode}`),
2162
+ preview: /* @__PURE__ */ jsxRuntime.jsx(ThemePreview, { mode })
2163
+ }))
2164
+ }
2165
+ )
2166
+ ] }),
2167
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-field", children: [
2168
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-settings-label", children: t("settings.palette") }),
2169
+ /* @__PURE__ */ jsxRuntime.jsx(
2170
+ OptionCardGrid,
2171
+ {
2172
+ "aria-label": t("settings.palette"),
2173
+ columns: 4,
2174
+ value: palette,
2175
+ onChange: (v) => previewAppearance({ palette: v }),
2176
+ options: PALETTES.map((p) => ({
2177
+ value: p.id,
2178
+ label: t(`palette.${p.id}`),
2179
+ preview: /* @__PURE__ */ jsxRuntime.jsx(PalettePreview, { id: p.id, mode: resolvedMode })
2180
+ }))
2181
+ }
2182
+ )
2183
+ ] }),
2184
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-field", children: [
2185
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-settings-label", children: t("settings.cardStyle") }),
2186
+ /* @__PURE__ */ jsxRuntime.jsx(
2187
+ OptionCardGrid,
2188
+ {
2189
+ "aria-label": t("settings.cardStyle"),
2190
+ columns: 3,
2191
+ value: cardStyle,
2192
+ onChange: (v) => previewAppearance({ cardStyle: v }),
2193
+ options: [
2194
+ ["stamp", t("settings.cardStamp")],
2195
+ ["etch", t("settings.cardEtch")],
2196
+ ["hero", t("settings.cardHero")],
2197
+ ["list", t("settings.cardList")]
2198
+ ].map(([value, label]) => ({
2199
+ value,
2200
+ label,
2201
+ preview: /* @__PURE__ */ jsxRuntime.jsx(CardStylePreview, { style: value })
2202
+ }))
2203
+ }
2204
+ )
2205
+ ] }),
2206
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mma-settings-hint", children: t("settings.appearanceHint") })
2207
+ ]
2208
+ }
2209
+ ),
2210
+ /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "mma-settings-rule" }),
2211
+ /* @__PURE__ */ jsxRuntime.jsxs(
2212
+ "section",
2213
+ {
2214
+ className: "mma-settings-section",
2215
+ "data-section": "network",
2216
+ ref: (el) => {
2217
+ sectionRefs.current.network = el;
2218
+ },
2219
+ children: [
2220
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { children: t("settings.sectionNetwork") }),
2221
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `mma-settings-field mma-settings-field--row${portError ? " is-error" : ""}`, children: [
2222
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "mma-settings-label", htmlFor: "mma-cfg-port", children: t("settings.hostPort") }),
2223
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-control", children: [
2224
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-port-row", children: [
2225
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-port-addon", "aria-hidden": "true", children: ":" }),
2226
+ /* @__PURE__ */ jsxRuntime.jsx(
2227
+ "input",
2228
+ {
2229
+ id: "mma-cfg-port",
2230
+ type: "text",
2231
+ inputMode: "numeric",
2232
+ autoComplete: "off",
2233
+ value: form.hostPort || "",
2234
+ "aria-invalid": !!portError,
2235
+ "aria-describedby": "mma-cfg-port-hint",
2236
+ onChange: (e) => {
2237
+ setPortTouched(true);
2238
+ patch({ hostPort: e.target.value });
2239
+ },
2240
+ onBlur: () => setPortTouched(true)
2241
+ }
2242
+ ),
2243
+ /* @__PURE__ */ jsxRuntime.jsx("code", { className: "mma-port-url", children: hostUrl }),
2244
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "mma-ghostbtn", onClick: () => void copyUrl(), children: t("settings.copyUrl") }),
2245
+ /* @__PURE__ */ jsxRuntime.jsx("a", { className: "mma-ghostbtn", href: hostUrl, target: "_blank", rel: "noreferrer", children: t("settings.openUrl") })
2246
+ ] }),
2247
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mma-settings-hint", id: "mma-cfg-port-hint", role: portError ? "alert" : void 0, children: portError ?? t("settings.portHint") })
2248
+ ] })
2249
+ ] })
2250
+ ]
2251
+ }
2252
+ ),
2253
+ /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "mma-settings-rule" }),
2254
+ /* @__PURE__ */ jsxRuntime.jsxs(
2255
+ "section",
2256
+ {
2257
+ className: "mma-settings-section",
2258
+ "data-section": "model",
2259
+ ref: (el) => {
2260
+ sectionRefs.current.model = el;
2261
+ },
2262
+ children: [
2263
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { children: t("settings.sectionModel") }),
2264
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-field mma-settings-field--row", children: [
2265
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "mma-settings-label", htmlFor: "mma-cfg-provider", children: t("settings.llmProvider") }),
2266
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-control mma-settings-control--model", children: [
2267
+ /* @__PURE__ */ jsxRuntime.jsx(
2268
+ "input",
2269
+ {
2270
+ id: "mma-cfg-provider",
2271
+ list: "mma-cfg-provider-list",
2272
+ placeholder: t("settings.providerPlaceholder"),
2273
+ value: form.provider || "",
2274
+ onChange: (e) => patch({ provider: e.target.value, model: "" })
2275
+ }
2276
+ ),
2277
+ /* @__PURE__ */ jsxRuntime.jsxs("datalist", { id: "mma-cfg-provider-list", children: [
2278
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "openai" }),
2279
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "anthropic" }),
2280
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "deepseek" })
2281
+ ] })
2282
+ ] })
2283
+ ] }),
2284
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-field mma-settings-field--row", children: [
2285
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "mma-settings-label", htmlFor: "mma-cfg-model", children: t("settings.llmModel") }),
2286
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mma-settings-control mma-settings-control--model", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-model-row", children: [
2287
+ /* @__PURE__ */ jsxRuntime.jsx(
2288
+ "input",
2289
+ {
2290
+ id: "mma-cfg-model",
2291
+ placeholder: form.provider ? t("settings.modelPlaceholder") : t("settings.modelNeedsProvider"),
2292
+ value: form.model || "",
2293
+ disabled: !form.provider,
2294
+ onChange: (e) => patch({ model: e.target.value })
2295
+ }
2296
+ ),
2297
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-status-pill", "data-state": "idle", children: t("settings.llmProbeIdle") })
2298
+ ] }) })
2299
+ ] })
2300
+ ]
2301
+ }
2302
+ ),
2303
+ /* @__PURE__ */ jsxRuntime.jsx("hr", { className: "mma-settings-rule" }),
2304
+ /* @__PURE__ */ jsxRuntime.jsxs(
2305
+ "section",
2306
+ {
2307
+ className: "mma-settings-section",
2308
+ "data-section": "about",
2309
+ ref: (el) => {
2310
+ sectionRefs.current.about = el;
2311
+ },
2312
+ children: [
2313
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { children: t("settings.sectionAbout") }),
2314
+ s.about ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2315
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-about-meta", children: [
2316
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-settings-label", children: t("settings.env") }),
2317
+ /* @__PURE__ */ jsxRuntime.jsxs("code", { children: [
2318
+ s.about.adapter,
2319
+ " \xB7 ",
2320
+ s.about.env
2321
+ ] })
2322
+ ] }),
2323
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mma-about-pkgs", children: s.about.packages.map((pkg) => /* @__PURE__ */ jsxRuntime.jsxs("li", { children: [
2324
+ /* @__PURE__ */ jsxRuntime.jsx("span", { title: pkg.name, children: shortPkg(pkg.name) }),
2325
+ /* @__PURE__ */ jsxRuntime.jsx("code", { children: pkg.version })
2326
+ ] }, pkg.name)) }),
2327
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-about-actions", children: [
2328
+ /* @__PURE__ */ jsxRuntime.jsx(
2329
+ "button",
2330
+ {
2331
+ type: "button",
2332
+ className: "mma-ghostbtn",
2333
+ id: "mma-cfg-check-update",
2334
+ disabled: s.updateCheck?.status === "loading",
2335
+ onClick: () => actions.checkUpdates(),
2336
+ children: s.updateCheck?.status === "loading" ? t("settings.checkingUpdate") : t("settings.checkUpdate")
2337
+ }
2338
+ ),
2339
+ s.updateCheck?.status === "done" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-settings-hint", children: s.updateCheck.error ? t("settings.updateError", { message: s.updateCheck.error }) : s.updateCheck.updateAvailable ? t("settings.updateAvailable", {
2340
+ latest: s.updateCheck.latest ?? "",
2341
+ current: s.updateCheck.current
2342
+ }) : t("settings.upToDate", {
2343
+ version: s.updateCheck.latest ?? s.updateCheck.current
2344
+ }) }) : null
2345
+ ] })
2346
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mma-settings-hint", children: t("settings.aboutLoading") })
2347
+ ]
2348
+ }
2349
+ )
2350
+ ] })
2351
+ ] }),
2352
+ /* @__PURE__ */ jsxRuntime.jsxs("footer", { className: "mma-settings-foot", children: [
2353
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-foot-left", "aria-live": "polite", children: [
2354
+ lastSavedAt ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mma-settings-hint", children: [
2355
+ t("settings.lastSaved", { time: lastSavedAt }),
2356
+ dirtyCount > 0 ? " \xB7 " : ""
2357
+ ] }) : null,
2358
+ dirtyCount > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-settings-dirty", children: t("settings.dirtyCount", { count: dirtyCount }) }) : null
2359
+ ] }),
2360
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-settings-foot-right", children: [
2361
+ dirtyCount > 0 ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "mma-ghostbtn", onClick: discard, children: t("settings.discard") }) : null,
2362
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "mma-ghostbtn", onClick: () => setConfirm("restore"), children: t("settings.restore") }),
2363
+ /* @__PURE__ */ jsxRuntime.jsx(
2364
+ "button",
2365
+ {
2366
+ type: "button",
2367
+ className: "mma-primarybtn",
2368
+ id: "mma-cfg-save",
2369
+ disabled: !canSave,
2370
+ onClick: save,
2371
+ children: t("settings.save")
2372
+ }
2373
+ )
2374
+ ] }),
2375
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mma-settings-msg", id: "mma-cfg-msg", children: s.cfgMsg })
2376
+ ] }),
2377
+ confirm ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mma-settings-confirm", role: "presentation", children: /* @__PURE__ */ jsxRuntime.jsxs(
2378
+ "div",
2379
+ {
2380
+ className: "mma-dialog",
2381
+ role: "dialog",
2382
+ "aria-modal": "true",
2383
+ "aria-labelledby": "mma-settings-confirm-title",
2384
+ children: [
2385
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { id: "mma-settings-confirm-title", children: confirm === "close" ? t("settings.confirmCloseTitle") : t("settings.confirmRestoreTitle") }),
2386
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: confirm === "close" ? t("settings.confirmCloseBody") : t("settings.confirmRestoreBody") }),
2387
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-dialog-actions", children: [
2388
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => setConfirm(null), children: t("modal.cancel") }),
2389
+ /* @__PURE__ */ jsxRuntime.jsx(
2390
+ "button",
2391
+ {
2392
+ type: "button",
2393
+ className: "go",
2394
+ onClick: () => {
2395
+ if (confirm === "close") {
2396
+ discard();
2397
+ actions.toggleSettings(false);
2398
+ } else restoreDefaults();
2399
+ setConfirm(null);
2400
+ },
2401
+ children: confirm === "close" ? t("settings.confirmDiscard") : t("settings.confirmRestoreAction")
2402
+ }
2403
+ )
2404
+ ] })
2405
+ ]
2406
+ }
2407
+ ) }) : null
2408
+ ]
2409
+ }
2410
+ );
1469
2411
  }
1470
2412
  function Tabs() {
1471
2413
  const s = usePanelState();
@@ -1508,8 +2450,11 @@ function ThemePop() {
1508
2450
  const actions = usePanelActions();
1509
2451
  const { t } = usePanelI18n();
1510
2452
  const app = actions.getActiveApp();
1511
- const appTheme = app ? app.theme : null;
2453
+ const appScope = Boolean(app) && s.themeScope === "app";
2454
+ const viewTheme = appScope ? app?.theme?.theme ?? s.theme : s.theme;
2455
+ const viewPalette = appScope ? selectedPalette(app?.theme?.palette, Boolean(app?.localPalette)) : s.palette;
1512
2456
  const customs = s.customPalettes || {};
2457
+ const canApp = Boolean(app && s.capabilities.appTheme);
1513
2458
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1514
2459
  s.themePopOpen ? /* @__PURE__ */ jsxRuntime.jsx(
1515
2460
  "div",
@@ -1533,18 +2478,61 @@ function ThemePop() {
1533
2478
  onClick: (e) => e.stopPropagation(),
1534
2479
  onPointerDown: (e) => e.stopPropagation(),
1535
2480
  children: [
1536
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mma-pop-seg", children: ["light", "dark"].map((mode) => /* @__PURE__ */ jsxRuntime.jsx(
2481
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mma-pop-lab", children: t("theme.apply") }),
2482
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-pop-seg", children: [
2483
+ /* @__PURE__ */ jsxRuntime.jsx(
2484
+ "button",
2485
+ {
2486
+ type: "button",
2487
+ "data-scope": "global",
2488
+ "data-on": s.themeScope === "global" ? "1" : "0",
2489
+ onClick: () => actions.setThemeScope("global"),
2490
+ children: t("theme.global")
2491
+ }
2492
+ ),
2493
+ /* @__PURE__ */ jsxRuntime.jsx(
2494
+ "button",
2495
+ {
2496
+ type: "button",
2497
+ "data-scope": "app",
2498
+ id: "mma-scope-app",
2499
+ title: app ? t("theme.saveTo", { name: app.name }) : t("theme.openAppFirst"),
2500
+ "data-on": appScope ? "1" : "0",
2501
+ disabled: !canApp,
2502
+ onClick: () => actions.setThemeScope("app"),
2503
+ children: app ? app.name : t("theme.currentApp")
2504
+ }
2505
+ )
2506
+ ] }),
2507
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mma-pop-lab", children: t("theme.appearance") }),
2508
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mma-pop-seg", children: ["system", "light", "dark"].map((mode) => /* @__PURE__ */ jsxRuntime.jsx(
1537
2509
  "button",
1538
2510
  {
1539
2511
  type: "button",
1540
2512
  "data-mode": mode,
1541
- "data-on": s.theme === mode ? "1" : "0",
2513
+ "data-on": viewTheme === mode ? "1" : "0",
1542
2514
  onClick: () => actions.setAppearance({ theme: mode }, s.themeScope),
1543
2515
  children: t(`theme.${mode}`)
1544
2516
  },
1545
2517
  mode
1546
2518
  )) }),
2519
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mma-pop-lab", children: t("theme.palettes") }),
1547
2520
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-pop-list", children: [
2521
+ appScope ? /* @__PURE__ */ jsxRuntime.jsxs(
2522
+ "button",
2523
+ {
2524
+ type: "button",
2525
+ className: "mma-swatch",
2526
+ id: "mma-follow-global",
2527
+ "data-on": viewPalette === GLOBAL_PALETTE_ID ? "1" : "0",
2528
+ role: "menuitem",
2529
+ onClick: () => actions.setAppearance({ palette: GLOBAL_PALETTE_ID }, "app"),
2530
+ children: [
2531
+ /* @__PURE__ */ jsxRuntime.jsx("i", { className: "mma-dot", style: { background: "linear-gradient(135deg,#888,#ddd)" } }),
2532
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("theme.followGlobal") })
2533
+ ]
2534
+ }
2535
+ ) : null,
1548
2536
  PALETTES.map((p) => /* @__PURE__ */ jsxRuntime.jsxs(
1549
2537
  "button",
1550
2538
  {
@@ -1552,11 +2540,12 @@ function ThemePop() {
1552
2540
  className: "mma-swatch",
1553
2541
  "data-palette": p.id,
1554
2542
  role: "menuitem",
1555
- "data-on": s.palette === p.id ? "1" : "0",
2543
+ "data-on": viewPalette === p.id ? "1" : "0",
1556
2544
  onClick: () => actions.setAppearance({ palette: p.id }, s.themeScope),
1557
2545
  children: [
1558
2546
  /* @__PURE__ */ jsxRuntime.jsx("i", { className: "mma-dot", style: { background: p.swatch } }),
1559
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: t(`palette.${p.id}`) })
2547
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t(`palette.${p.id}`) }),
2548
+ /* @__PURE__ */ jsxRuntime.jsx("i", { className: "mma-custom-badge", children: t("theme.chipSystem") })
1560
2549
  ]
1561
2550
  },
1562
2551
  p.id
@@ -1569,7 +2558,7 @@ function ThemePop() {
1569
2558
  "data-palette": id,
1570
2559
  "data-custom": "1",
1571
2560
  role: "menuitem",
1572
- "data-on": s.palette === id ? "1" : "0",
2561
+ "data-on": viewPalette === id ? "1" : "0",
1573
2562
  onClick: () => actions.setAppearance({ palette: id }, s.themeScope),
1574
2563
  children: [
1575
2564
  /* @__PURE__ */ jsxRuntime.jsx("i", { className: "mma-dot", style: { background: customs[id].swatch || "#888" } }),
@@ -1578,34 +2567,34 @@ function ThemePop() {
1578
2567
  ]
1579
2568
  },
1580
2569
  id
1581
- ))
1582
- ] }),
1583
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mma-pop-seg mma-scope-seg", children: [
1584
- /* @__PURE__ */ jsxRuntime.jsx(
1585
- "button",
1586
- {
1587
- type: "button",
1588
- "data-scope": "global",
1589
- "data-on": s.themeScope === "global" ? "1" : "0",
1590
- onClick: () => actions.setThemeScope("global"),
1591
- children: t("theme.global")
1592
- }
1593
- ),
1594
- /* @__PURE__ */ jsxRuntime.jsx(
2570
+ )),
2571
+ appScope && app?.localPalette ? /* @__PURE__ */ jsxRuntime.jsxs(
1595
2572
  "button",
1596
2573
  {
1597
2574
  type: "button",
1598
- "data-scope": "app",
1599
- id: "mma-scope-app",
1600
- title: app ? t("theme.saveTo", { name: app.name }) : t("theme.openAppFirst"),
1601
- "data-on": s.themeScope === "app" ? "1" : "0",
1602
- disabled: !app || !s.capabilities.appTheme,
1603
- onClick: () => actions.setThemeScope("app"),
1604
- children: app ? app.name : t("theme.currentApp")
2575
+ className: "mma-swatch",
2576
+ "data-palette": LOCAL_PALETTE_ID,
2577
+ role: "menuitem",
2578
+ "data-on": viewPalette === LOCAL_PALETTE_ID ? "1" : "0",
2579
+ onClick: () => actions.setAppearance({ palette: LOCAL_PALETTE_ID }, "app"),
2580
+ children: [
2581
+ /* @__PURE__ */ jsxRuntime.jsx("i", { className: "mma-dot", style: { background: app.localPalette.swatch } }),
2582
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: app.localPalette.label }),
2583
+ /* @__PURE__ */ jsxRuntime.jsx("i", { className: "mma-custom-badge", children: t("theme.chipApp") })
2584
+ ]
1605
2585
  }
1606
- )
2586
+ ) : null
1607
2587
  ] }),
1608
- s.themeScope === "app" && appTheme ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "mma-textbtn", id: "mma-clear-app-theme", onClick: () => actions.clearAppTheme(), children: t("theme.followGlobal") }) : null
2588
+ appScope && app?.theme ? /* @__PURE__ */ jsxRuntime.jsx(
2589
+ "button",
2590
+ {
2591
+ type: "button",
2592
+ className: "mma-textbtn",
2593
+ id: "mma-reset-app-theme",
2594
+ onClick: () => actions.clearAppTheme(),
2595
+ children: t("theme.resetApp")
2596
+ }
2597
+ ) : null
1609
2598
  ]
1610
2599
  }
1611
2600
  )
@@ -1788,6 +2777,13 @@ function createFrameController(opts) {
1788
2777
  const { container: initial2, urlOf, envOf } = opts;
1789
2778
  let container = initial2 ?? null;
1790
2779
  const map = /* @__PURE__ */ new Map();
2780
+ function targetOrigin(appId, rec) {
2781
+ try {
2782
+ return new URL(rec?.iframe.src || urlOf(appId)).origin;
2783
+ } catch {
2784
+ return "";
2785
+ }
2786
+ }
1791
2787
  return {
1792
2788
  map,
1793
2789
  url: (appId) => urlOf(appId),
@@ -1799,8 +2795,13 @@ function createFrameController(opts) {
1799
2795
  const w = rec?.iframe.contentWindow;
1800
2796
  if (!w) return;
1801
2797
  const env = envOf(appId);
2798
+ const target = targetOrigin(appId, rec);
2799
+ if (!target) return;
1802
2800
  try {
1803
- w.postMessage({ type: "mma-set-env", theme: env.theme, palette: env.palette, dock: env.dock }, "*");
2801
+ w.postMessage(
2802
+ { type: "mma-set-env", theme: env.theme, palette: env.palette, dock: env.dock, vars: env.vars },
2803
+ target
2804
+ );
1804
2805
  } catch {
1805
2806
  }
1806
2807
  },
@@ -1814,7 +2815,7 @@ function createFrameController(opts) {
1814
2815
  const wrap = document.createElement("div");
1815
2816
  wrap.className = "mma-frame";
1816
2817
  wrap.setAttribute("data-app", appId);
1817
- wrap.innerHTML = `${loadingMarkup()}<iframe title="${escapeHtml(title || appId)}"></iframe>`;
2818
+ wrap.innerHTML = `${loadingMarkup()}<iframe title="${escapeHtml(title || appId)}" allow="clipboard-write"></iframe>`;
1818
2819
  if (!container) return;
1819
2820
  container.appendChild(wrap);
1820
2821
  const iframe = wrap.querySelector("iframe");
@@ -1844,6 +2845,27 @@ function createFrameController(opts) {
1844
2845
  if (!rec) return;
1845
2846
  if (!rec.wrap.querySelector(".mma-load")) rec.wrap.insertAdjacentHTML("afterbegin", loadingMarkup());
1846
2847
  rec.iframe.src = `${urlOf(appId)}&_=${Date.now()}`;
2848
+ },
2849
+ postViewEval(query) {
2850
+ const rec = map.get(query.appId);
2851
+ const w = rec?.iframe.contentWindow;
2852
+ const target = rec ? targetOrigin(query.appId, rec) : "";
2853
+ if (!w || !target) return false;
2854
+ try {
2855
+ w.postMessage(
2856
+ {
2857
+ type: "mma-view-eval",
2858
+ requestId: query.requestId,
2859
+ appId: query.appId,
2860
+ code: query.code ?? "",
2861
+ maxBytes: query.maxBytes ?? 0
2862
+ },
2863
+ target
2864
+ );
2865
+ return true;
2866
+ } catch {
2867
+ return false;
2868
+ }
1847
2869
  }
1848
2870
  };
1849
2871
  }
@@ -1867,6 +2889,7 @@ var en_default = {
1867
2889
  close: "Close"
1868
2890
  },
1869
2891
  theme: {
2892
+ system: "System",
1870
2893
  light: "Light",
1871
2894
  dark: "Dark",
1872
2895
  custom: "Custom",
@@ -1874,7 +2897,13 @@ var en_default = {
1874
2897
  currentApp: "Current app",
1875
2898
  openAppFirst: "Open an app first",
1876
2899
  saveTo: 'Save to "{{name}}"',
1877
- followGlobal: "Follow global (clear app theme)"
2900
+ followGlobal: "Follow global",
2901
+ apply: "Apply to",
2902
+ appearance: "Appearance",
2903
+ palettes: "Palette",
2904
+ chipSystem: "System",
2905
+ chipApp: "This app",
2906
+ resetApp: "Restore this app default"
1878
2907
  },
1879
2908
  palette: {
1880
2909
  default: "Default",
@@ -1900,20 +2929,59 @@ var en_default = {
1900
2929
  },
1901
2930
  settings: {
1902
2931
  title: "Settings",
2932
+ close: "Close settings",
2933
+ sectionNav: "Sections",
2934
+ sectionsAria: "Settings sections",
2935
+ sectionAppearance: "Appearance",
2936
+ sectionNetwork: "Network",
2937
+ sectionModel: "Model",
2938
+ sectionAbout: "About",
2939
+ sectionHost: "Host",
1903
2940
  hostPort: "Host port",
1904
2941
  language: "Language",
1905
2942
  langZh: "Chinese",
1906
2943
  langEn: "English",
1907
2944
  theme: "Theme",
1908
2945
  palette: "Palette",
2946
+ dock: "Dock",
2947
+ dockFill: "Fill",
2948
+ dockSide: "Side",
1909
2949
  cardStyle: "Card style",
1910
2950
  cardStamp: "Stamp",
1911
2951
  cardEtch: "Etch",
1912
2952
  cardHero: "Hero",
1913
2953
  cardList: "List",
2954
+ appearanceHint: "Preview only. Click Save to persist; closing discards unsaved changes.",
2955
+ portHint: "Range 1024\u201365535. Restart the app after changing.",
2956
+ portEmpty: "Enter a port.",
2957
+ portNan: "Port must be a number.",
2958
+ portRange: "Port must be between 1024 and 65535.",
2959
+ copyUrl: "Copy",
2960
+ openUrl: "Open",
1914
2961
  llmProvider: "LLM Provider",
1915
2962
  llmModel: "LLM model",
1916
- save: "Save"
2963
+ providerPlaceholder: "Select a provider",
2964
+ modelPlaceholder: "Enter a model",
2965
+ modelNeedsProvider: "Select a provider first",
2966
+ llmProbeIdle: "Probe not wired",
2967
+ save: "Save",
2968
+ discard: "Discard",
2969
+ restore: "Restore defaults",
2970
+ lastSaved: "Saved {{time}}",
2971
+ dirtyCount: "{{count}} unsaved",
2972
+ confirmCloseTitle: "Discard unsaved changes?",
2973
+ confirmCloseBody: "Unsaved appearance, network, and model changes will be discarded and restored.",
2974
+ confirmDiscard: "Discard",
2975
+ confirmRestoreTitle: "Restore defaults?",
2976
+ confirmRestoreBody: "Resets appearance, port, and model fields (port/model still need Save).",
2977
+ confirmRestoreAction: "Restore",
2978
+ env: "Environment",
2979
+ aboutLoading: "Loading version info\u2026",
2980
+ checkUpdate: "Check for updates",
2981
+ checkingUpdate: "Checking\u2026",
2982
+ upToDate: "Up to date ({{version}})",
2983
+ updateAvailable: "Update available: {{latest}} (current {{current}})",
2984
+ updateError: "Check failed: {{message}}"
1917
2985
  },
1918
2986
  modal: {
1919
2987
  title: "Delete mini app",
@@ -1932,7 +3000,16 @@ var en_default = {
1932
3000
  noCommits: "No commits yet",
1933
3001
  noStorage: "No storage files",
1934
3002
  expandMsg: "Show more",
1935
- collapseMsg: "Show less"
3003
+ collapseMsg: "Show less",
3004
+ entries: "{{count}} entries",
3005
+ sizeKb: "{{value}} KB",
3006
+ sizeMb: "{{value}} MB",
3007
+ noticeTitle: '"{{table}}" is already {{size}}',
3008
+ noticeBody: "The bigger this file gets, the slower every save becomes. Move anything that keeps growing into its own table \u2014 copy the prompt below and paste it to an AI to do the split for you.",
3009
+ noticeTop: 'The heaviest piece right now is "{{key}}" (about {{size}}).',
3010
+ noticeCopy: "Copy split prompt",
3011
+ noticeCopied: "Copied",
3012
+ noticeDismiss: "Got it"
1936
3013
  },
1937
3014
  load: {
1938
3015
  label: "Loading"
@@ -1962,6 +3039,7 @@ var zh_CN_default = {
1962
3039
  close: "\u5173\u95ED"
1963
3040
  },
1964
3041
  theme: {
3042
+ system: "\u8DDF\u968F\u7CFB\u7EDF",
1965
3043
  light: "\u6D45\u8272",
1966
3044
  dark: "\u6DF1\u8272",
1967
3045
  custom: "\u81EA\u5B9A\u4E49",
@@ -1969,7 +3047,13 @@ var zh_CN_default = {
1969
3047
  currentApp: "\u5F53\u524D\u5C0F\u7A0B\u5E8F",
1970
3048
  openAppFirst: "\u6253\u5F00\u5C0F\u7A0B\u5E8F\u540E\u53EF\u7528",
1971
3049
  saveTo: "\u4FDD\u5B58\u5230\u300C{{name}}\u300D",
1972
- followGlobal: "\u8DDF\u968F\u5168\u5C40\uFF08\u6E05\u9664\u672C\u5E94\u7528\u4E3B\u9898\uFF09"
3050
+ followGlobal: "\u8DDF\u968F\u5168\u5C40",
3051
+ apply: "\u5E94\u7528\u5230",
3052
+ appearance: "\u5916\u89C2",
3053
+ palettes: "\u8272\u677F",
3054
+ chipSystem: "\u7CFB\u7EDF",
3055
+ chipApp: "\u672C\u5E94\u7528",
3056
+ resetApp: "\u6062\u590D\u8BE5\u5E94\u7528\u9ED8\u8BA4"
1973
3057
  },
1974
3058
  palette: {
1975
3059
  default: "\u9ED8\u8BA4",
@@ -1995,20 +3079,59 @@ var zh_CN_default = {
1995
3079
  },
1996
3080
  settings: {
1997
3081
  title: "\u8BBE\u7F6E",
3082
+ close: "\u5173\u95ED\u8BBE\u7F6E",
3083
+ sectionNav: "\u5206\u7EC4",
3084
+ sectionsAria: "\u8BBE\u7F6E\u5206\u7EC4",
3085
+ sectionAppearance: "\u5916\u89C2",
3086
+ sectionNetwork: "\u7F51\u7EDC",
3087
+ sectionModel: "\u6A21\u578B",
3088
+ sectionAbout: "\u5173\u4E8E",
3089
+ sectionHost: "\u5BBF\u4E3B",
1998
3090
  hostPort: "Host \u7AEF\u53E3",
1999
3091
  language: "\u754C\u9762\u8BED\u8A00",
2000
3092
  langZh: "\u4E2D\u6587",
2001
3093
  langEn: "English",
2002
3094
  theme: "\u4E3B\u9898",
2003
3095
  palette: "\u8C03\u8272\u677F",
3096
+ dock: "\u505C\u9760",
3097
+ dockFill: "\u94FA\u6EE1",
3098
+ dockSide: "\u4FA7\u680F",
2004
3099
  cardStyle: "\u5361\u7247\u6837\u5F0F",
2005
3100
  cardStamp: "\u5370\u7AE0",
2006
3101
  cardEtch: "\u8680\u523B",
2007
3102
  cardHero: "\u6D77\u62A5",
2008
3103
  cardList: "\u5217\u8868",
3104
+ appearanceHint: "\u4EC5\u9884\u89C8\uFF0C\u70B9\u4FDD\u5B58\u540E\u624D\u4F1A\u5199\u5165\uFF1B\u5173\u95ED\u672A\u4FDD\u5B58\u5C06\u6062\u590D\u539F\u6837\u3002",
3105
+ portHint: "\u8303\u56F4 1024\u201365535\uFF0C\u4FEE\u6539\u540E\u9700\u91CD\u542F\u5E94\u7528\u3002",
3106
+ portEmpty: "\u8BF7\u8F93\u5165\u7AEF\u53E3\u3002",
3107
+ portNan: "\u7AEF\u53E3\u5FC5\u987B\u662F\u6570\u5B57\u3002",
3108
+ portRange: "\u7AEF\u53E3\u9700\u5728 1024\u201365535 \u4E4B\u95F4\u3002",
3109
+ copyUrl: "\u590D\u5236",
3110
+ openUrl: "\u6253\u5F00",
2009
3111
  llmProvider: "LLM Provider",
2010
3112
  llmModel: "LLM \u6A21\u578B",
2011
- save: "\u4FDD\u5B58"
3113
+ providerPlaceholder: "\u9009\u62E9 Provider",
3114
+ modelPlaceholder: "\u8F93\u5165\u6A21\u578B\u540D",
3115
+ modelNeedsProvider: "\u5148\u9009\u62E9 Provider",
3116
+ llmProbeIdle: "\u672A\u63A5\u5165\u68C0\u6D4B",
3117
+ save: "\u4FDD\u5B58",
3118
+ discard: "\u653E\u5F03\u4FEE\u6539",
3119
+ restore: "\u6062\u590D\u9ED8\u8BA4",
3120
+ lastSaved: "\u4E0A\u6B21\u4FDD\u5B58 {{time}}",
3121
+ dirtyCount: "{{count}} \u9879\u672A\u4FDD\u5B58",
3122
+ confirmCloseTitle: "\u653E\u5F03\u672A\u4FDD\u5B58\u7684\u4FEE\u6539\uFF1F",
3123
+ confirmCloseBody: "\u5173\u95ED\u540E\uFF0C\u672A\u4FDD\u5B58\u7684\u5916\u89C2 / \u7F51\u7EDC / \u6A21\u578B\u6539\u52A8\u90FD\u4F1A\u4E22\u5F03\u5E76\u6062\u590D\u539F\u6837\u3002",
3124
+ confirmDiscard: "\u653E\u5F03",
3125
+ confirmRestoreTitle: "\u6062\u590D\u9ED8\u8BA4\u8BBE\u7F6E\uFF1F",
3126
+ confirmRestoreBody: "\u5C06\u91CD\u7F6E\u5916\u89C2\u3001\u7AEF\u53E3\u4E0E\u6A21\u578B\u5B57\u6BB5\u4E3A\u9ED8\u8BA4\u503C\uFF08\u4ECD\u9700\u4FDD\u5B58\u7AEF\u53E3/\u6A21\u578B\uFF09\u3002",
3127
+ confirmRestoreAction: "\u6062\u590D",
3128
+ env: "\u73AF\u5883",
3129
+ aboutLoading: "\u6B63\u5728\u8BFB\u53D6\u7248\u672C\u4FE1\u606F\u2026",
3130
+ checkUpdate: "\u68C0\u67E5\u66F4\u65B0",
3131
+ checkingUpdate: "\u68C0\u67E5\u4E2D\u2026",
3132
+ upToDate: "\u5DF2\u662F\u6700\u65B0\uFF08{{version}}\uFF09",
3133
+ updateAvailable: "\u6709\u65B0\u7248\u672C {{latest}}\uFF08\u5F53\u524D {{current}}\uFF09",
3134
+ updateError: "\u68C0\u67E5\u5931\u8D25\uFF1A{{message}}"
2012
3135
  },
2013
3136
  modal: {
2014
3137
  title: "\u5220\u9664\u5C0F\u7A0B\u5E8F",
@@ -2027,7 +3150,16 @@ var zh_CN_default = {
2027
3150
  noCommits: "\u6682\u65E0\u63D0\u4EA4\u8BB0\u5F55",
2028
3151
  noStorage: "\u6682\u65E0\u5B58\u50A8\u6587\u4EF6",
2029
3152
  expandMsg: "\u5C55\u5F00\u5168\u6587",
2030
- collapseMsg: "\u6536\u8D77"
3153
+ collapseMsg: "\u6536\u8D77",
3154
+ entries: "{{count}} \u6761",
3155
+ sizeKb: "{{value}} KB",
3156
+ sizeMb: "{{value}} MB",
3157
+ noticeTitle: "\u300C{{table}}\u300D\u5DF2\u7ECF\u6709 {{size}} \u4E86",
3158
+ noticeBody: "\u8FD9\u4E2A\u6587\u4EF6\u8D8A\u5927\uFF0C\u6BCF\u6B21\u4FDD\u5B58\u90FD\u4F1A\u8D8A\u6162\u3002\u5EFA\u8BAE\u628A\u4F1A\u4E0D\u65AD\u53D8\u591A\u7684\u6570\u636E\u62C6\u5230\u5355\u72EC\u7684\u8868\u91CC\u2014\u2014\u4F60\u53EF\u4EE5\u628A\u4E0B\u9762\u7684\u63D0\u793A\u8BCD\u590D\u5236\u7ED9 AI\uFF0C\u8BA9\u5B83\u5E2E\u4F60\u6539\u3002",
3159
+ noticeTop: "\u773C\u4E0B\u6700\u5927\u7684\u4E00\u5757\u662F\u300C{{key}}\u300D\uFF08\u7EA6 {{size}}\uFF09\u3002",
3160
+ noticeCopy: "\u590D\u5236\u62C6\u5206\u63D0\u793A\u8BCD",
3161
+ noticeCopied: "\u5DF2\u590D\u5236",
3162
+ noticeDismiss: "\u77E5\u9053\u4E86"
2031
3163
  },
2032
3164
  load: {
2033
3165
  label: "\u52A0\u8F7D\u4E2D"
@@ -2107,7 +3239,7 @@ function resolvePanelLocale(value) {
2107
3239
  // src/styles.ts
2108
3240
  var PANEL_CSS_TAG = "panel";
2109
3241
  var CSS = [
2110
- "#mma-host{color:var(--dsw-alias-fg,#111);background:var(--dsw-alias-bg,#f7f7f8);overflow:hidden;}",
3242
+ "#mma-host{color:var(--dsw-alias-fg,#111);background:var(--dsw-alias-bg,#f7f7f8);overflow:hidden;container-type:inline-size;container-name:mma-host;}",
2111
3243
  "#mma-host button,#mma-host input,#mma-host select{color:inherit;font:inherit;}",
2112
3244
  "#mma-host .mma-chrome{display:flex;align-items:center;gap:8px;height:46px;padding:0 10px;border-bottom:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);flex:0 0 auto;}",
2113
3245
  "#mma-host .mma-tabs{display:flex;align-items:center;gap:2px;flex:1;min-width:0;overflow-x:auto;flex-wrap:nowrap;scrollbar-width:thin;}",
@@ -2123,12 +3255,18 @@ var CSS = [
2123
3255
  "#mma-host .mma-toolbar .mma-ico{display:block;}",
2124
3256
  "#mma-host .mma-theme-wrap{position:relative;flex:0 0 auto;}",
2125
3257
  "#mma-host .mma-pop-scrim{position:fixed;inset:0;z-index:7;background:transparent;}",
2126
- "#mma-host .mma-pop{display:none;position:absolute;right:0;top:40px;z-index:8;width:232px;padding:12px 14px 14px;border-radius:12px;border:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);box-shadow:0 12px 32px var(--dsw-alias-shadow,rgba(0,0,0,.14));}",
3258
+ "#mma-host .mma-pop{display:none;position:absolute;right:0;top:40px;z-index:8;width:260px;padding:12px 14px 14px;border-radius:12px;border:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);box-shadow:0 12px 32px var(--dsw-alias-shadow,rgba(0,0,0,.14));}",
2127
3259
  "#mma-host .mma-pop[data-open='1']{display:block;}",
2128
3260
  "#mma-host .mma-pop-seg{display:flex;gap:4px;margin:0 0 12px;padding:3px;border-radius:9px;background:var(--dsw-alias-muted,#f3f4f6);}",
2129
3261
  "#mma-host .mma-pop-seg button{flex:1;height:28px;padding:0;line-height:1;border:0;border-radius:7px;background:transparent;cursor:pointer;font-size:12px;color:inherit;display:flex;align-items:center;justify-content:center;}",
2130
3262
  "#mma-host .mma-pop-seg button[data-on='1']{background:var(--dsw-alias-surface,#fff);font-weight:600;box-shadow:0 1px 2px var(--dsw-alias-shadow,rgba(0,0,0,.06));color:var(--dsw-alias-primary,#2563eb);}",
2131
- "#mma-host .mma-pop-list{margin:0 0 12px;display:flex;flex-direction:column;gap:6px!important;max-height:280px;overflow-y:auto;padding:4px 0;}",
3263
+ "#mma-host .mma-pop-seg button:disabled{opacity:.45;cursor:not-allowed;}",
3264
+ "#mma-host .mma-pop-lab{font-size:10px;letter-spacing:.12em;text-transform:uppercase;opacity:.55;margin:10px 2px 6px;}",
3265
+ "#mma-host .mma-pop-lab:first-child{margin-top:0;}",
3266
+ "#mma-host .mma-pop-list{margin:0;display:flex;flex-direction:column;gap:6px!important;max-height:280px;overflow-y:auto;padding:4px 0;scrollbar-width:thin;scrollbar-color:color-mix(in srgb,var(--dsw-alias-fg,#111) 35%,transparent) transparent;}",
3267
+ "#mma-host .mma-pop-list::-webkit-scrollbar{width:8px;}",
3268
+ "#mma-host .mma-pop-list::-webkit-scrollbar-track{background:transparent;}",
3269
+ "#mma-host .mma-pop-list::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--dsw-alias-fg,#111) 35%,transparent);border-radius:99px;}",
2132
3270
  "#mma-host .mma-swatch{display:flex;align-items:center;gap:10px;width:100%;min-height:34px;padding:0 10px;border:0;border-radius:8px;background:transparent;cursor:pointer;color:inherit;font-size:13px;text-align:left;white-space:nowrap;overflow:hidden;}",
2133
3271
  "#mma-host .mma-swatch:hover{background:var(--dsw-alias-accent,var(--dsw-alias-muted,#f3f4f6));}",
2134
3272
  "#mma-host .mma-swatch[data-on='1']{background:var(--dsw-alias-accent,var(--dsw-alias-muted,#f3f4f6));font-weight:600;}",
@@ -2207,17 +3345,106 @@ var CSS = [
2207
3345
  "#mma-host .mma-dialog-actions{display:flex;justify-content:flex-end;gap:8px;}",
2208
3346
  "#mma-host .mma-dialog-actions button{height:32px;padding:0 12px;border-radius:8px;border:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);cursor:pointer;}",
2209
3347
  "#mma-host .mma-dialog-actions .go{background:#dc2626;color:#fff;border-color:#dc2626;}",
2210
- "#mma-host .mma-settings{position:absolute;inset:0;background:var(--dsw-alias-bg,#f7f7f8);z-index:4;overflow:auto;padding:20px 22px 32px;display:none;}",
2211
- "#mma-host .mma-settings[data-open='1']{display:block;}",
2212
- "#mma-host .mma-settings h3{margin:0 0 6px;font-size:16px;}",
2213
- "#mma-host .mma-settings-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px;}",
2214
- "#mma-host .mma-settings label{display:block;margin-bottom:12px;font-size:12px;opacity:.75;}",
2215
- "#mma-host .mma-settings input,#mma-host .mma-settings select{display:block;width:100%;margin-top:4px;height:32px;padding:0 10px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:8px;background:var(--dsw-alias-surface,#fff);color:inherit;font-size:13px;box-sizing:border-box;}",
2216
- "#mma-host .mma-settings-actions{display:flex;flex-direction:column;align-items:flex-end;gap:8px;margin-top:18px;}",
2217
- "#mma-host .mma-settings-msg{font-size:12px;opacity:.75;line-height:1.4;text-align:right;width:100%;}",
2218
- "#mma-host .mma-settings-msg.err{color:#dc2626;}",
2219
- "#mma-host #mma-cfg-save{height:34px;padding:0 18px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:8px;background:var(--dsw-alias-surface,#fff);color:inherit;font-size:14px;font-weight:600;opacity:1;cursor:pointer;}",
2220
- "#mma-host #mma-cfg-save:hover{background:var(--dsw-alias-muted,#f3f4f6);border-color:var(--dsw-alias-primary,#2563eb);color:var(--dsw-alias-primary,#2563eb);}",
3348
+ "#mma-host .mma-settings{position:absolute;inset:0;z-index:4;display:none;flex-direction:column;background:var(--dsw-alias-bg,#f7f7f8);color:inherit;overflow:hidden;}",
3349
+ "#mma-host .mma-settings[data-open='1']{display:flex;}",
3350
+ "#mma-host .mma-settings-head{flex:0 0 auto;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border-bottom:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);}",
3351
+ "#mma-host .mma-settings-head-left{display:flex;align-items:center;gap:8px;min-width:0;}",
3352
+ "#mma-host .mma-settings-head-icon{opacity:.7;font-size:14px;}",
3353
+ "#mma-host .mma-settings-head h3{margin:0;font-size:15px;font-weight:650;letter-spacing:-.01em;}",
3354
+ "#mma-host .mma-settings-head-right{display:flex;align-items:center;gap:8px;}",
3355
+ "#mma-host .mma-settings-meta{font-size:11px;opacity:.55;white-space:nowrap;}",
3356
+ "#mma-host .mma-settings-body{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;}",
3357
+ "#mma-host .mma-settings-rail{flex:0 0 auto;display:flex;gap:4px;padding:8px 10px;border-bottom:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);overflow-x:auto;}",
3358
+ "#mma-host .mma-settings-rail-title{display:none;}",
3359
+ "#mma-host .mma-settings-rail-item{flex:0 0 auto;height:30px;padding:0 10px;border:0;border-radius:8px;background:transparent;color:inherit;opacity:.7;font-size:12.5px;cursor:pointer;}",
3360
+ "#mma-host .mma-settings-rail-item:hover{background:var(--dsw-alias-muted,#f3f4f6);opacity:1;}",
3361
+ "#mma-host .mma-settings-rail-item[data-on='1']{background:var(--dsw-alias-muted,#f3f4f6);opacity:1;font-weight:600;color:var(--dsw-alias-primary,#2563eb);box-shadow:inset 0 -2px 0 var(--dsw-alias-primary,#2563eb);}",
3362
+ "#mma-host .mma-settings-content{flex:1 1 auto;min-height:0;overflow:auto;padding:18px 16px 24px;scroll-behavior:smooth;}",
3363
+ "#mma-host .mma-settings-section{scroll-margin-top:12px;}",
3364
+ "#mma-host .mma-settings-section h4{margin:0 0 14px;font-size:12.5px;font-weight:600;letter-spacing:.5px;text-transform:uppercase;opacity:.55;}",
3365
+ "#mma-host .mma-settings-field{display:flex;flex-direction:column;gap:8px;margin-bottom:20px;}",
3366
+ "#mma-host .mma-settings-label{font-size:12.5px;opacity:.75;}",
3367
+ "#mma-host .mma-settings-control{min-width:0;}",
3368
+ "#mma-host .mma-settings-control--model{max-width:520px;}",
3369
+ "#mma-host .mma-settings input{display:block;width:100%;height:34px;padding:0 10px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:9px;background:var(--dsw-alias-surface,#fff);color:inherit;font-size:13px;box-sizing:border-box;}",
3370
+ "#mma-host .mma-settings input:focus{outline:none;border-color:var(--dsw-alias-primary,#2563eb);box-shadow:0 0 0 2px color-mix(in srgb, var(--dsw-alias-primary,#2563eb) 20%, transparent);}",
3371
+ "#mma-host .mma-settings input:disabled{opacity:.55;}",
3372
+ "#mma-host .mma-settings-field.is-error input{border-color:var(--dsw-alias-destructive,#dc2626);}",
3373
+ "#mma-host .mma-seg{display:flex;gap:4px;padding:3px;border-radius:10px;background:var(--dsw-alias-muted,#f3f4f6);max-width:280px;}",
3374
+ "#mma-host .mma-seg button{flex:1;height:30px;padding:0 8px;border:0;border-radius:8px;background:transparent;cursor:pointer;font-size:12.5px;color:inherit;}",
3375
+ "#mma-host .mma-seg button[data-on='1']{background:var(--dsw-alias-surface,#fff);font-weight:600;box-shadow:0 1px 2px var(--dsw-alias-shadow,rgba(0,0,0,.06));color:var(--dsw-alias-primary,#2563eb);}",
3376
+ "#mma-host .mma-optgrid{display:grid;gap:10px;}",
3377
+ "#mma-host .mma-optgrid--2{grid-template-columns:repeat(2,minmax(0,1fr));}",
3378
+ "#mma-host .mma-optgrid--3{grid-template-columns:repeat(2,minmax(0,1fr));}",
3379
+ "#mma-host .mma-optgrid--4{grid-template-columns:repeat(2,minmax(0,1fr));}",
3380
+ "#mma-host .mma-optcard{position:relative;display:flex;flex-direction:column;gap:8px;padding:10px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:10px;background:var(--dsw-alias-surface,#fff);color:inherit;cursor:pointer;text-align:left;}",
3381
+ "#mma-host .mma-optcard:hover{border-color:color-mix(in srgb, var(--dsw-alias-fg,#111) 28%, var(--dsw-alias-border,#e5e7eb));}",
3382
+ "#mma-host .mma-optcard:focus-visible{outline:2px solid var(--dsw-alias-primary,#2563eb);outline-offset:2px;}",
3383
+ "#mma-host .mma-optcard[data-on='1']{border-color:var(--dsw-alias-primary,#2563eb);background:color-mix(in srgb, var(--dsw-alias-primary,#2563eb) 8%, var(--dsw-alias-surface,#fff));}",
3384
+ "#mma-host .mma-optcard:disabled,.mma-optcard--ghost{opacity:.55;cursor:default;border-style:dashed;}",
3385
+ "#mma-host .mma-optcard-preview{display:block;height:42px;border-radius:8px;overflow:hidden;background:var(--dsw-alias-muted,#f3f4f6);}",
3386
+ "#mma-host .mma-optcard-preview--plus{display:flex;align-items:center;justify-content:center;font-size:20px;opacity:.7;}",
3387
+ "#mma-host .mma-optcard-label{font-size:12px;line-height:1.2;}",
3388
+ "#mma-host .mma-optcard-check{position:absolute;top:8px;right:8px;width:18px;height:18px;border-radius:999px;background:var(--dsw-alias-primary,#2563eb);color:var(--dsw-alias-primary-fg,#fff);font-size:11px;display:flex;align-items:center;justify-content:center;}",
3389
+ "#mma-host .mma-preview-theme{display:flex;flex-direction:column;justify-content:center;gap:6px;height:100%;padding:8px;box-sizing:border-box;}",
3390
+ "#mma-host .mma-preview-theme--light{background:var(--dsw-alias-bg,#f7f7f8);}",
3391
+ "#mma-host .mma-preview-theme--dark{background:color-mix(in srgb, var(--dsw-alias-fg,#111) 88%, transparent);}",
3392
+ "#mma-host .mma-preview-theme--system{display:flex;flex-direction:row;padding:0;}",
3393
+ "#mma-host .mma-preview-theme-half{flex:1;height:100%;display:flex;flex-direction:column;justify-content:center;gap:6px;padding:8px;box-sizing:border-box;}",
3394
+ "#mma-host .mma-preview-theme-half--light{background:var(--dsw-alias-bg,#f7f7f8);}",
3395
+ "#mma-host .mma-preview-theme-half--dark{background:color-mix(in srgb, var(--dsw-alias-fg,#111) 88%, transparent);}",
3396
+ "#mma-host .mma-preview-bar{display:block;height:4px;border-radius:999px;background:color-mix(in srgb, var(--dsw-alias-fg,#111) 25%, transparent);}",
3397
+ "#mma-host .mma-preview-theme--dark .mma-preview-bar,#mma-host .mma-preview-theme-half--dark .mma-preview-bar{background:color-mix(in srgb, #fff 45%, transparent);}",
3398
+ "#mma-host .mma-preview-swatches{display:flex;align-items:center;justify-content:center;gap:8px;height:100%;}",
3399
+ "#mma-host .mma-preview-swatches > span{width:14px;height:14px;border-radius:999px;border:1px solid color-mix(in srgb, var(--dsw-alias-fg,#111) 12%, transparent);}",
3400
+ "#mma-host .mma-preview-cardstyle{display:block;height:100%;margin:8px;border-radius:6px;border:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);box-sizing:border-box;}",
3401
+ "#mma-host .mma-preview-cardstyle--stamp{box-shadow:inset 0 0 0 1px var(--dsw-alias-border,#e5e7eb);position:relative;}",
3402
+ "#mma-host .mma-preview-cardstyle--stamp::after{content:'';position:absolute;top:4px;right:4px;width:18px;height:8px;border-radius:3px;background:var(--dsw-alias-muted,#f3f4f6);}",
3403
+ "#mma-host .mma-preview-cardstyle--etch{background:transparent;}",
3404
+ "#mma-host .mma-preview-cardstyle--hero{box-shadow:3px 3px 0 var(--dsw-alias-border,#e5e7eb);}",
3405
+ "#mma-host .mma-preview-cardstyle--list{border:0;border-bottom:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:0;margin:14px 8px;}",
3406
+ "#mma-host .mma-port-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;}",
3407
+ "#mma-host .mma-port-addon{display:inline-flex;align-items:center;justify-content:center;height:34px;padding:0 8px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-right:0;border-radius:9px 0 0 9px;background:var(--dsw-alias-muted,#f3f4f6);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;}",
3408
+ "#mma-host .mma-port-row input{width:120px;border-radius:0 9px 9px 0;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;}",
3409
+ "#mma-host .mma-port-url{font-size:11px;opacity:.65;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;}",
3410
+ "#mma-host .mma-model-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px;}",
3411
+ "#mma-host .mma-model-row input{flex:1 1 180px;min-width:0;}",
3412
+ "#mma-host .mma-status-pill{display:inline-flex;align-items:center;gap:6px;height:24px;padding:0 10px;border-radius:20px;border:1px solid var(--dsw-alias-border,#e5e7eb);font-size:11px;opacity:.75;white-space:nowrap;}",
3413
+ "#mma-host .mma-ghostbtn{height:30px;padding:0 10px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:8px;background:var(--dsw-alias-surface,#fff);color:inherit;font-size:12px;cursor:pointer;text-decoration:none;display:inline-flex;align-items:center;}",
3414
+ "#mma-host .mma-ghostbtn:hover{background:var(--dsw-alias-muted,#f3f4f6);}",
3415
+ "#mma-host .mma-ghostbtn:disabled{opacity:.55;cursor:default;}",
3416
+ "#mma-host .mma-primarybtn{height:34px;padding:0 16px;border:0;border-radius:8px;background:var(--dsw-alias-primary,#2563eb);color:var(--dsw-alias-primary-fg,#fff);font-size:13px;font-weight:600;cursor:pointer;}",
3417
+ "#mma-host .mma-primarybtn:disabled{opacity:.45;cursor:default;}",
3418
+ "#mma-host .mma-settings-hint{margin:0;font-size:11.5px;opacity:.6;line-height:1.45;}",
3419
+ "#mma-host .mma-settings-field.is-error .mma-settings-hint{color:var(--dsw-alias-destructive,#dc2626);opacity:1;}",
3420
+ "#mma-host .mma-settings-rule{border:0;border-top:1px solid var(--dsw-alias-border,#e5e7eb);margin:20px 0;}",
3421
+ "#mma-host .mma-about-meta{margin-bottom:10px;display:flex;flex-direction:column;gap:6px;}",
3422
+ "#mma-host .mma-about-meta code{display:inline-block;width:fit-content;font-size:12px;padding:3px 8px;border-radius:6px;background:var(--dsw-alias-muted,#f3f4f6);}",
3423
+ "#mma-host .mma-about-pkgs{list-style:none;margin:0 0 12px;padding:0;display:flex;flex-direction:column;gap:6px;}",
3424
+ "#mma-host .mma-about-pkgs li{display:flex;align-items:center;justify-content:space-between;gap:12px;font-size:12px;padding:8px 10px;border-radius:9px;background:var(--dsw-alias-surface,#fff);border:1px solid var(--dsw-alias-border,#e5e7eb);}",
3425
+ "#mma-host .mma-about-pkgs li span{opacity:.8;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
3426
+ "#mma-host .mma-about-pkgs li code{font-size:11px;font-variant-numeric:tabular-nums;opacity:.9;}",
3427
+ "#mma-host .mma-about-actions{display:flex;flex-direction:column;align-items:flex-start;gap:8px;}",
3428
+ "#mma-host .mma-settings-foot{flex:0 0 auto;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px;padding:10px 14px;border-top:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);}",
3429
+ "#mma-host .mma-settings-foot-left{display:flex;flex-wrap:wrap;align-items:center;gap:4px;min-width:0;}",
3430
+ "#mma-host .mma-settings-foot-right{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-left:auto;}",
3431
+ "#mma-host .mma-settings-dirty{font-size:11.5px;color:var(--dsw-alias-primary,#2563eb);}",
3432
+ "#mma-host .mma-settings-msg{flex-basis:100%;font-size:12px;opacity:.75;line-height:1.4;}",
3433
+ "#mma-host .mma-settings-msg.err{color:var(--dsw-alias-destructive,#dc2626);}",
3434
+ "#mma-host .mma-settings-confirm{position:absolute;inset:0;z-index:5;display:flex;align-items:center;justify-content:center;padding:16px;background:color-mix(in srgb, var(--dsw-alias-fg,#111) 35%, transparent);}",
3435
+ /* Wide layout: data-wide from ResizeObserver (reliable on fixed #mma-host) + container query fallback. */
3436
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-body{flex-direction:row;}",
3437
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-rail{flex:0 0 148px;flex-direction:column;align-items:stretch;gap:2px;border-bottom:0;border-right:1px solid var(--dsw-alias-border,#e5e7eb);padding:14px 10px;overflow:visible;}",
3438
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-rail-title{display:block;font-size:10px;letter-spacing:.08em;text-transform:uppercase;opacity:.45;margin:0 8px 8px;}",
3439
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-rail-item{width:100%;justify-content:flex-start;text-align:left;box-shadow:none;border-radius:8px;position:relative;}",
3440
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-rail-item[data-on='1']{box-shadow:none;background:var(--dsw-alias-muted,#f3f4f6);}",
3441
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-rail-item[data-on='1']::before{content:'';position:absolute;left:0;top:6px;bottom:6px;width:2px;border-radius:2px;background:var(--dsw-alias-primary,#2563eb);}",
3442
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-content{padding:22px 26px 28px;}",
3443
+ "#mma-host .mma-settings[data-wide='1'] .mma-optgrid--3{grid-template-columns:repeat(3,minmax(0,1fr));}",
3444
+ "#mma-host .mma-settings[data-wide='1'] .mma-optgrid--4{grid-template-columns:repeat(4,minmax(0,1fr));}",
3445
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-field--row{flex-direction:row;align-items:flex-start;gap:16px;}",
3446
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-field--row > .mma-settings-label{flex:0 0 112px;padding-top:8px;}",
3447
+ "#mma-host .mma-settings[data-wide='1'] .mma-settings-field--row > .mma-settings-control{flex:1 1 auto;}",
2221
3448
  "#mma-host .mma-browse{position:absolute;inset:0;background:var(--dsw-alias-bg,#f7f7f8);z-index:3;overflow:auto;padding:16px 18px;display:none;}",
2222
3449
  "#mma-host .mma-browse[data-open='1']{display:block;}",
2223
3450
  "#mma-host .mma-browse-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px;}",
@@ -2254,7 +3481,15 @@ var CSS = [
2254
3481
  "#mma-host .mma-diff-line--del{color:#991b1b;background:rgba(220,38,38,.10);}",
2255
3482
  "#mma-host .mma-diff-line--ctx{color:inherit;opacity:.85;}",
2256
3483
  "#mma-host .mma-bempty{padding:20px;opacity:.6;text-align:center;font-size:13px;}",
2257
- "#mma-host .mma-berr{padding:16px;color:#b91c1c;font-size:13px;}"
3484
+ "#mma-host .mma-berr{padding:16px;color:#b91c1c;font-size:13px;}",
3485
+ "#mma-host .mma-storage-notice{display:flex;gap:12px;align-items:flex-start;justify-content:space-between;padding:12px 14px;margin:0 0 12px;border-radius:10px;background:color-mix(in srgb, var(--dsw-alias-warning, #f59e0b) 14%, var(--dsw-alias-bg, #f7f7f8));border:1px solid color-mix(in srgb, var(--dsw-alias-warning, #f59e0b) 35%, transparent);}",
3486
+ "#mma-host .mma-storage-notice-body{min-width:0;flex:1;}",
3487
+ "#mma-host .mma-storage-notice-body b{display:block;font-size:13px;margin:0 0 4px;}",
3488
+ "#mma-host .mma-storage-notice-body p{margin:0;font-size:12px;line-height:1.45;opacity:.85;}",
3489
+ "#mma-host .mma-storage-notice-top{margin-top:6px !important;}",
3490
+ "#mma-host .mma-storage-notice-actions{display:flex;flex-direction:column;gap:6px;flex-shrink:0;}",
3491
+ "#mma-host .mma-storage-notice-actions button{font-size:12px;padding:6px 10px;border-radius:8px;border:1px solid color-mix(in srgb, currentColor 18%, transparent);background:var(--dsw-alias-bg, #fff);cursor:pointer;}",
3492
+ "#mma-host .mma-storage-notice-copy{font-weight:600;}"
2258
3493
  ];
2259
3494
  function injectPanelCss() {
2260
3495
  if (typeof document === "undefined") return;
@@ -2267,25 +3502,20 @@ function injectPanelCss() {
2267
3502
  }
2268
3503
  tag.textContent = CSS.join("\n");
2269
3504
  }
2270
- function loadCustomPalettes(host) {
2271
- if (!host.palettes) return;
2272
- host.palettes().then((list) => {
2273
- const custom = {};
2274
- for (const p of list) {
2275
- custom[p.id] = { label: p.label, swatch: p.swatch, tokens: p.tokens };
2276
- }
2277
- setPanelState({ customPalettes: custom });
2278
- }).catch(() => {
2279
- });
2280
- }
2281
3505
  function createMiniAppPanel(host, options) {
2282
3506
  const locale = resolvePanelLocale(options?.locale ?? host.locale);
2283
3507
  const i18n = createPanelI18n(locale);
3508
+ const prev = getPanelState();
2284
3509
  resetPanelState({
2285
3510
  tabs: [{ id: "all", title: i18n.t("tabs.all"), kind: "all" }],
2286
3511
  capabilities: capabilitiesOf(host),
2287
3512
  locale,
2288
- emptyText: host.emptyText
3513
+ emptyText: host.emptyText,
3514
+ theme: prev.theme,
3515
+ palette: prev.palette,
3516
+ dock: prev.dock,
3517
+ cardStyle: prev.cardStyle,
3518
+ visible: prev.visible
2289
3519
  });
2290
3520
  let rootEl = null;
2291
3521
  let root = null;
@@ -2332,29 +3562,98 @@ function readThemePrefs(storage) {
2332
3562
  return null;
2333
3563
  }
2334
3564
  };
2335
- return { theme: get("mma-theme-mode") || "light", palette: get("mma-palette") || "default" };
3565
+ return {
3566
+ theme: (() => {
3567
+ const m = get("mma-theme-mode");
3568
+ return m === "system" || m === "dark" || m === "light" ? m : "light";
3569
+ })(),
3570
+ palette: get("mma-palette") || "default",
3571
+ cardStyle: get("mma-card-style") || "stamp"
3572
+ };
2336
3573
  }
2337
3574
  function createHostShell(opts) {
2338
3575
  const storage = safeStorage(opts.storage);
2339
3576
  const themePrefs = readThemePrefs(storage);
3577
+ let currentOrigin = opts.hostUrl.replace(/\/$/, "");
2340
3578
  const frame = createFrameController({
2341
- urlOf: (appId) => appFrameUrl(opts.hostUrl, appId, envFor(appId)),
3579
+ urlOf: (appId) => appFrameUrl(currentOrigin, appId, envFor(appId)),
2342
3580
  envOf: (appId) => envFor(appId)
2343
3581
  });
3582
+ let unsubEvents = null;
3583
+ function bindHostEvents() {
3584
+ unsubEvents?.();
3585
+ unsubEvents = subscribeHostEvents(currentOrigin, {
3586
+ // `mini_app_open` asked to show an app. Without this the panel opens on the list and
3587
+ // the agent's follow-up (`mini_app_errors`, `mini_app_view_eval`) waits on a view that
3588
+ // was never mounted — the dsh client already does this in its own stream handler.
3589
+ onOpen: (appId, title) => {
3590
+ optOnOpen();
3591
+ void host.fetchApps().then((apps) => {
3592
+ const app = apps.find((a) => a.id === appId);
3593
+ if (app) panelRef()?.actions.openAppTab(app);
3594
+ else if (title) console.warn(`[mma] opened app ${appId} is not registered on this host`);
3595
+ });
3596
+ },
3597
+ onReload: (appId) => {
3598
+ if (frame.map.has(appId)) frame.reload(appId);
3599
+ },
3600
+ onEval: (query) => relayViewEval(currentOrigin, frame, query),
3601
+ onStorageNotice: (notice) => {
3602
+ setPanelState({ storageNotice: notice });
3603
+ }
3604
+ });
3605
+ }
2344
3606
  function envFor(appId) {
2345
3607
  const s = getPanelState();
2346
3608
  const app = s.apps.find((a) => a.id === appId);
2347
- return { theme: app?.theme?.theme || s.theme, palette: app?.theme?.palette || s.palette, dock: s.dock };
3609
+ const theme = app?.theme?.theme || s.theme;
3610
+ const palette = effectivePalette(app?.theme?.palette, Boolean(app?.localPalette), s.palette);
3611
+ const custom = { ...s.customPalettes };
3612
+ if (app?.localPalette) {
3613
+ custom[LOCAL_PALETTE_ID] = {
3614
+ label: app.localPalette.label,
3615
+ swatch: app.localPalette.swatch,
3616
+ tokens: app.localPalette.tokens
3617
+ };
3618
+ }
3619
+ return {
3620
+ theme,
3621
+ palette,
3622
+ dock: s.dock,
3623
+ vars: themeCssVars(resolveMode(theme), palette, custom)
3624
+ };
3625
+ }
3626
+ async function migrateOrigin(next) {
3627
+ const origin = next.replace(/\/$/, "");
3628
+ for (let i = 0; i < 30; i++) {
3629
+ try {
3630
+ const res = await fetch(`${origin}/health`);
3631
+ if (res.ok) break;
3632
+ } catch {
3633
+ }
3634
+ await new Promise((r) => setTimeout(r, 100));
3635
+ }
3636
+ currentOrigin = origin;
3637
+ try {
3638
+ storage?.setItem("mma-host-url", origin);
3639
+ } catch {
3640
+ }
3641
+ opts.onHostChange?.(origin);
3642
+ bindHostEvents();
3643
+ for (const id of [...frame.map.keys()]) frame.reload(id);
2348
3644
  }
2349
3645
  const host = createRestPanelHost({
2350
3646
  hostUrl: opts.hostUrl,
3647
+ getHostUrl: () => currentOrigin,
2351
3648
  storage,
2352
3649
  cardStyle: opts.cardStyle,
2353
3650
  locale: opts.locale ?? void 0,
2354
3651
  emptyText: opts.emptyText,
2355
3652
  onOpen: () => optOnOpen(),
2356
3653
  onClose: () => optOnClose(),
2357
- onHostChange: (next) => opts.onHostChange?.(next),
3654
+ onHostChange: (next) => {
3655
+ void migrateOrigin(next);
3656
+ },
2358
3657
  frameController: {
2359
3658
  url: (appId) => frame.url(appId),
2360
3659
  // Lazy-bind the frames container (React may not have rendered #mma-frames yet).
@@ -2381,6 +3680,10 @@ function createHostShell(opts) {
2381
3680
  }
2382
3681
  let panel = null;
2383
3682
  let containerEl = null;
3683
+ function panelRef() {
3684
+ if (!panel) panel = createMiniAppPanel(host);
3685
+ return panel;
3686
+ }
2384
3687
  function optOnOpen() {
2385
3688
  setPanelState({ visible: true });
2386
3689
  opts.onOpen?.();
@@ -2403,15 +3706,15 @@ function createHostShell(opts) {
2403
3706
  host,
2404
3707
  frames: frame,
2405
3708
  get panel() {
2406
- if (!panel) panel = createMiniAppPanel(host);
2407
- return panel;
3709
+ return panelRef();
2408
3710
  },
2409
3711
  mount(el) {
2410
3712
  containerEl = document.createElement("div");
2411
3713
  containerEl.id = "mma-host";
2412
3714
  containerEl.setAttribute("data-ready", "1");
2413
3715
  containerEl.setAttribute("data-dock", getPanelState().dock);
2414
- containerEl.setAttribute("data-cardstyle", getPanelState().cardStyle);
3716
+ containerEl.setAttribute("data-cardstyle", themePrefs.cardStyle);
3717
+ bindHostEvents();
2415
3718
  Object.assign(containerEl.style, {
2416
3719
  position: "fixed",
2417
3720
  top: "0",
@@ -2427,14 +3730,24 @@ function createHostShell(opts) {
2427
3730
  });
2428
3731
  const target = el ?? document.body;
2429
3732
  target.appendChild(containerEl);
2430
- setPanelState({ theme: themePrefs.theme, palette: themePrefs.palette, visible: true });
2431
- applyThemeTo(containerEl, themePrefs.theme, themePrefs.palette);
3733
+ const cardStyle = themePrefs.cardStyle;
3734
+ setPanelState({
3735
+ theme: themePrefs.theme,
3736
+ palette: themePrefs.palette,
3737
+ cardStyle,
3738
+ visible: true
3739
+ });
2432
3740
  this.panel.mount(containerEl);
3741
+ applyThemeTo(containerEl, themePrefs.theme, themePrefs.palette);
3742
+ setPanelState({ theme: themePrefs.theme, palette: themePrefs.palette, cardStyle });
3743
+ containerEl.setAttribute("data-cardstyle", cardStyle);
2433
3744
  const framesEl = containerEl.querySelector("#mma-frames");
2434
3745
  if (framesEl) frame.setContainer(framesEl);
2435
3746
  void this.panel.actions.fetchApps();
2436
3747
  },
2437
3748
  unmount() {
3749
+ unsubEvents?.();
3750
+ unsubEvents = null;
2438
3751
  panel?.unmount();
2439
3752
  panel = null;
2440
3753
  frame.unmountAll();
@@ -2468,8 +3781,10 @@ var packageName = "@monkey-mini-app/panel";
2468
3781
 
2469
3782
  exports.AppList = AppList;
2470
3783
  exports.Browse = Browse;
3784
+ exports.GLOBAL_PALETTE_ID = GLOBAL_PALETTE_ID;
2471
3785
  exports.HostUnreachableError = HostUnreachableError;
2472
3786
  exports.LOCALE_IDS = LOCALE_IDS;
3787
+ exports.LOCAL_PALETTE_ID = LOCAL_PALETTE_ID;
2473
3788
  exports.ListRegion = ListRegion;
2474
3789
  exports.Loading = Loading;
2475
3790
  exports.MiniAppPanel = MiniAppPanel;
@@ -2498,6 +3813,7 @@ exports.createPanelI18n = createPanelI18n;
2498
3813
  exports.createRestPanelHost = createRestPanelHost;
2499
3814
  exports.cssVars = cssVars;
2500
3815
  exports.defaultHideThemePop = defaultHideThemePop;
3816
+ exports.effectivePalette = effectivePalette;
2501
3817
  exports.formToHostConfigBody = formToHostConfigBody;
2502
3818
  exports.getPanelState = getPanelState;
2503
3819
  exports.hostConfigToForm = hostConfigToForm;
@@ -2513,11 +3829,16 @@ exports.parsePalettes = parsePalettes;
2513
3829
  exports.parseStorageTables = parseStorageTables;
2514
3830
  exports.parseThemeCss = parseThemeCss;
2515
3831
  exports.readJson = readJson;
3832
+ exports.relayViewEval = relayViewEval;
2516
3833
  exports.resetPanelState = resetPanelState;
3834
+ exports.resolveMode = resolveMode;
2517
3835
  exports.resolvePanelLocale = resolvePanelLocale;
2518
3836
  exports.runnerThemeCss = runnerThemeCss;
3837
+ exports.selectedPalette = selectedPalette;
2519
3838
  exports.setPanelState = setPanelState;
3839
+ exports.subscribeHostEvents = subscribeHostEvents;
2520
3840
  exports.subscribePanel = subscribePanel;
3841
+ exports.themeCssVars = themeCssVars;
2521
3842
  exports.themeLabelFromCss = themeLabelFromCss;
2522
3843
  exports.tokensOf = tokensOf;
2523
3844
  exports.usePanelActions = usePanelActions;