@monkey-mini-app/panel 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1923 @@
1
+ import { __publicField, applyThemeTo, clampPalette, PALETTES } from './chunk-X3QW77RD.js';
2
+ export { PALETTES, applyThemeTo, clampMode, clampPalette, cssVars, parseThemeCss, runnerThemeCss, themeLabelFromCss, tokensOf } from './chunk-X3QW77RD.js';
3
+ import * as React from 'react';
4
+ import { useSyncExternalStore } from 'react';
5
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
+ import { RefreshCw, LayoutGrid, Clock, Database, Settings as Settings$1, PanelRight, X } from 'lucide-react';
7
+ import { createRoot } from 'react-dom/client';
8
+ import { createInstance } from 'i18next';
9
+
10
+ // src/panel-host.ts
11
+ function capabilitiesOf(host) {
12
+ return {
13
+ history: !!host.history,
14
+ storage: !!host.storage,
15
+ config: !!host.config,
16
+ appTheme: !!host.appTheme,
17
+ customPalettes: !!host.palettes,
18
+ deleteApp: !!host.deleteApp
19
+ };
20
+ }
21
+
22
+ // src/rest.ts
23
+ function isRecord(value) {
24
+ return typeof value === "object" && value !== null && !Array.isArray(value);
25
+ }
26
+ async function readJson(url, init) {
27
+ let res;
28
+ try {
29
+ res = await fetch(url, init);
30
+ } catch (cause) {
31
+ throw new HostUnreachableError(url, cause);
32
+ }
33
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
34
+ return res.json();
35
+ }
36
+ var HostUnreachableError = class extends Error {
37
+ constructor(url, cause) {
38
+ super(`Cannot reach apps host: ${url}`);
39
+ __publicField(this, "url");
40
+ this.name = "HostUnreachableError";
41
+ this.url = url;
42
+ if (cause !== void 0) {
43
+ Object.defineProperty(this, "cause", { value: cause, configurable: true, writable: true });
44
+ }
45
+ }
46
+ };
47
+ function isHostUnreachable(e) {
48
+ return e instanceof HostUnreachableError;
49
+ }
50
+ function appFrameUrl(origin, appId, query) {
51
+ return `${origin}/app/${encodeURIComponent(appId)}?${new URLSearchParams(query).toString()}`;
52
+ }
53
+ function parseAppTheme(raw) {
54
+ if (raw === null) return null;
55
+ if (!isRecord(raw)) return void 0;
56
+ const theme = typeof raw.theme === "string" ? raw.theme : "";
57
+ const palette = typeof raw.palette === "string" ? raw.palette : "";
58
+ if (!theme && !palette) return null;
59
+ return { theme, palette };
60
+ }
61
+ function parseAppsResponse(raw) {
62
+ if (!isRecord(raw) || !Array.isArray(raw.apps)) return [];
63
+ const out = [];
64
+ for (const item of raw.apps) {
65
+ if (!isRecord(item) || typeof item.id !== "string") continue;
66
+ out.push({
67
+ id: item.id,
68
+ name: typeof item.name === "string" ? item.name : item.id,
69
+ description: typeof item.description === "string" ? item.description : void 0,
70
+ acronym: typeof item.acronym === "string" ? item.acronym : void 0,
71
+ commits: typeof item.commits === "number" ? item.commits : void 0,
72
+ version: typeof item.version === "string" ? item.version : void 0,
73
+ theme: parseAppTheme(item.theme)
74
+ });
75
+ }
76
+ return out;
77
+ }
78
+ function hostConfigToForm(raw, cardStyle) {
79
+ const rec = isRecord(raw) ? raw : {};
80
+ const llm = isRecord(rec.llm) ? rec.llm : null;
81
+ const locale = typeof rec.locale === "string" ? rec.locale : "";
82
+ const chatLanguage = typeof rec.chatLanguage === "string" ? rec.chatLanguage : "";
83
+ return {
84
+ hostPort: rec.hostPort != null ? String(rec.hostPort) : "",
85
+ locale: locale || chatLanguage,
86
+ chatLanguage: chatLanguage || locale,
87
+ theme: typeof rec.theme === "string" ? rec.theme : "",
88
+ palette: typeof rec.palette === "string" ? rec.palette : "",
89
+ cardStyle,
90
+ provider: llm && typeof llm.provider === "string" ? llm.provider : "",
91
+ model: llm && typeof llm.model === "string" ? llm.model : ""
92
+ };
93
+ }
94
+ function formToHostConfigBody(form) {
95
+ const provider = (form.provider ?? "").trim();
96
+ const model = (form.model ?? "").trim();
97
+ const locale = form.locale || form.chatLanguage;
98
+ const body = { locale, chatLanguage: form.chatLanguage || locale, theme: form.theme, palette: form.palette };
99
+ const port = Number(form.hostPort);
100
+ if (Number.isInteger(port) && port >= 0 && port <= 65535) body.hostPort = port;
101
+ if (provider && model) body.llm = { provider, model };
102
+ else if (!provider && !model) body.llm = null;
103
+ return body;
104
+ }
105
+ function asCommit(raw) {
106
+ if (!isRecord(raw) || typeof raw.id !== "string") return null;
107
+ const files = Array.isArray(raw.files) ? raw.files.flatMap((f) => {
108
+ if (!isRecord(f) || typeof f.path !== "string") return [];
109
+ 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 }];
110
+ }) : void 0;
111
+ return { id: raw.id, message: typeof raw.message === "string" ? raw.message : "", time: typeof raw.time === "string" ? raw.time : "", files };
112
+ }
113
+ function parseCommitList(raw) {
114
+ const rec = isRecord(raw) ? raw : {};
115
+ const list = Array.isArray(rec.commits) ? rec.commits : Array.isArray(rec.nodes) ? rec.nodes : [];
116
+ return list.map((c) => asCommit(c)).filter((c) => c !== null);
117
+ }
118
+ function parseCommitDetail(raw, id) {
119
+ const rec = isRecord(raw) ? raw : {};
120
+ const inner = isRecord(rec.commit) ? rec.commit : rec;
121
+ return asCommit(inner) ?? { id, message: "", time: "", files: [] };
122
+ }
123
+ function parseStorageTables(raw) {
124
+ const rec = isRecord(raw) ? raw : {};
125
+ const list = Array.isArray(rec.tables) ? rec.tables : [];
126
+ const out = [];
127
+ for (const row of list) {
128
+ if (!isRecord(row) || typeof row.name !== "string") continue;
129
+ out.push({
130
+ name: row.name,
131
+ size: typeof row.size === "number" ? row.size : void 0,
132
+ updatedAt: typeof row.updatedAt === "string" ? row.updatedAt : void 0
133
+ });
134
+ }
135
+ return out;
136
+ }
137
+ function parsePalettes(raw) {
138
+ const rec = isRecord(raw) ? raw : {};
139
+ const list = Array.isArray(rec.palettes) ? rec.palettes : [];
140
+ const out = [];
141
+ for (const p of list) {
142
+ if (!isRecord(p) || typeof p.id !== "string") continue;
143
+ if (p.custom === false) continue;
144
+ out.push({
145
+ id: p.id,
146
+ label: typeof p.label === "string" ? p.label : p.id,
147
+ swatch: typeof p.swatch === "string" ? p.swatch : "#888",
148
+ tokens: isRecord(p.tokens) ? p.tokens : void 0
149
+ });
150
+ }
151
+ return out;
152
+ }
153
+ function createRestPanelHost(opts) {
154
+ const storage = opts.storage ?? (typeof window !== "undefined" ? window.localStorage : null);
155
+ function origin() {
156
+ return (opts.getHostUrl ? opts.getHostUrl() : opts.hostUrl).replace(/\/$/, "");
157
+ }
158
+ const host = {
159
+ locale: opts.locale,
160
+ emptyText: opts.emptyText,
161
+ fetchApps: async () => parseAppsResponse(await readJson(`${origin()}/api/apps`)),
162
+ palettes: async () => {
163
+ try {
164
+ return parsePalettes(await readJson(`${origin()}/api/palettes`));
165
+ } catch {
166
+ return [];
167
+ }
168
+ },
169
+ openPanel: () => opts.onOpen?.(),
170
+ closePanel: () => opts.onClose?.(),
171
+ persistTheme: (theme, palette) => {
172
+ try {
173
+ storage?.setItem("mma-theme-mode", theme);
174
+ storage?.setItem("mma-palette", palette);
175
+ } catch {
176
+ }
177
+ void fetch(`${origin()}/api/host-config`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ theme, palette }) }).catch(() => {
178
+ });
179
+ },
180
+ appTheme: {
181
+ save: async (appId, t) => {
182
+ await fetch(`${origin()}/api/apps/${encodeURIComponent(appId)}/theme`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(t) }).catch(() => {
183
+ });
184
+ },
185
+ clear: async (appId) => {
186
+ await fetch(`${origin()}/api/apps/${encodeURIComponent(appId)}/theme`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ reset: true }) }).catch(() => {
187
+ });
188
+ }
189
+ },
190
+ config: {
191
+ load: async () => hostConfigToForm(await readJson(`${origin()}/api/host-config`), opts.getCardStyle?.() ?? opts.cardStyle ?? "stamp"),
192
+ save: async (cfg) => {
193
+ const body = formToHostConfigBody(cfg);
194
+ const res = await fetch(`${origin()}/api/host-config`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
195
+ const raw = await res.json().catch(() => ({}));
196
+ if (!res.ok || isRecord(raw) && raw.ok === false) {
197
+ throw new Error(isRecord(raw) && typeof raw.error === "string" ? raw.error : `HTTP ${res.status}`);
198
+ }
199
+ const hostPort = isRecord(raw) ? raw.hostPort : void 0;
200
+ if (typeof hostPort === "number" && Number.isInteger(hostPort) && hostPort > 0) {
201
+ const next = `${new URL(origin()).protocol}//${new URL(origin()).hostname}:${hostPort}`;
202
+ if (next !== origin()) opts.onHostChange?.(next);
203
+ }
204
+ opts.onConfigSaved?.(cfg);
205
+ }
206
+ },
207
+ history: {
208
+ list: async (appId) => parseCommitList(await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/history?limit=50`)),
209
+ detail: async (appId, id) => parseCommitDetail(await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/history/${encodeURIComponent(id)}`), id)
210
+ },
211
+ storage: {
212
+ listTables: async (appId) => parseStorageTables(await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/storage`)),
213
+ readTable: async (appId, name) => {
214
+ const raw = await readJson(`${origin()}/api/apps/${encodeURIComponent(appId)}/storage/${encodeURIComponent(name)}`);
215
+ return isRecord(raw) && "value" in raw ? raw.value : raw;
216
+ }
217
+ },
218
+ deleteApp: opts.deleteApp ?? (async (appId) => {
219
+ await fetch(`${origin()}/api/app/${encodeURIComponent(appId)}`, { method: "DELETE" }).catch(() => {
220
+ });
221
+ }),
222
+ frame: opts.frameController ?? {
223
+ url: (appId) => `${origin()}/app/${encodeURIComponent(appId)}`,
224
+ mount: () => void 0,
225
+ unmount: () => void 0,
226
+ reload: () => void 0,
227
+ syncEnv: () => void 0
228
+ }
229
+ };
230
+ return host;
231
+ }
232
+ var none = {
233
+ history: false,
234
+ storage: false,
235
+ config: false,
236
+ appTheme: false,
237
+ customPalettes: false,
238
+ deleteApp: false
239
+ };
240
+ var initial = {
241
+ tabs: [{ id: "all", title: "\u5168\u90E8", kind: "all" }],
242
+ active: "all",
243
+ apps: [],
244
+ error: null,
245
+ loading: false,
246
+ query: "",
247
+ dock: "fill",
248
+ theme: "light",
249
+ palette: "default",
250
+ themeScope: "global",
251
+ customPalettes: {},
252
+ cardStyle: "stamp",
253
+ visible: false,
254
+ pendingDelete: null,
255
+ themePopOpen: false,
256
+ settingsOpen: false,
257
+ cfgMsg: "",
258
+ cfgVersion: 0,
259
+ cfg: {},
260
+ emptyText: void 0,
261
+ capabilities: none,
262
+ locale: "zh-CN",
263
+ browseOpen: false,
264
+ browseKind: "history",
265
+ browseAppId: null,
266
+ browseAppName: "",
267
+ browseLoading: false,
268
+ browseError: null,
269
+ browseList: [],
270
+ browseDetail: null,
271
+ browseTable: null,
272
+ browseTableValue: null,
273
+ browseOpenFile: null
274
+ };
275
+ var state = { ...initial, capabilities: { ...none } };
276
+ var listeners = /* @__PURE__ */ new Set();
277
+ function getPanelState() {
278
+ return state;
279
+ }
280
+ function setPanelState(patch) {
281
+ state = { ...state, ...patch };
282
+ for (const fn of listeners) fn();
283
+ return state;
284
+ }
285
+ function subscribePanel(listener) {
286
+ listeners.add(listener);
287
+ return () => {
288
+ listeners.delete(listener);
289
+ };
290
+ }
291
+ function resetPanelState(seed) {
292
+ state = {
293
+ ...initial,
294
+ capabilities: { ...none },
295
+ customPalettes: {},
296
+ cfg: {},
297
+ tabs: [{ id: "all", title: "\u5168\u90E8", kind: "all" }],
298
+ ...seed || {}
299
+ };
300
+ for (const fn of listeners) fn();
301
+ return state;
302
+ }
303
+ function usePanelState() {
304
+ return useSyncExternalStore(subscribePanel, getPanelState);
305
+ }
306
+
307
+ // src/actions.ts
308
+ function activeAppFrom(state2) {
309
+ const tab = state2.tabs.find((t) => t.kind === "app" && t.id === state2.active) || state2.tabs.find((t) => t.kind === "app");
310
+ return tab?.app ?? null;
311
+ }
312
+ function errorMessage(err) {
313
+ return err instanceof Error ? err.message : String(err);
314
+ }
315
+ function pushAppTab(app) {
316
+ const s = getPanelState();
317
+ const id = "app:" + app.id;
318
+ if (!s.tabs.some((t) => t.id === id)) {
319
+ setPanelState({ tabs: [...s.tabs, { id, title: app.name || app.id, kind: "app", app }] });
320
+ }
321
+ return id;
322
+ }
323
+ function asCustomPalettes(value) {
324
+ return value;
325
+ }
326
+ function createPanelActions(host, getRootEl, i18n) {
327
+ setPanelState({ capabilities: capabilitiesOf(host), locale: i18n.locale, emptyText: host.emptyText });
328
+ return {
329
+ openAppTab: (app) => {
330
+ const id = pushAppTab(app);
331
+ setPanelState({ active: id });
332
+ host.frame.mount(app.id);
333
+ },
334
+ closeTab: (id) => {
335
+ if (id === "all") return;
336
+ const s = getPanelState();
337
+ const tab = s.tabs.find((t) => t.id === id);
338
+ setPanelState({
339
+ tabs: s.tabs.filter((t) => t.id !== id),
340
+ active: s.active === id ? "all" : s.active
341
+ });
342
+ if (tab?.app) host.frame.unmount(tab.app.id);
343
+ },
344
+ switchTab: (id) => {
345
+ setPanelState({ active: id });
346
+ const s = getPanelState();
347
+ const tab = s.tabs.find((t) => t.id === id && t.kind === "app");
348
+ if (tab?.app) host.frame.mount(tab.app.id);
349
+ },
350
+ setDock: (next) => {
351
+ setPanelState({ dock: next });
352
+ try {
353
+ localStorage.setItem("mma-dock", next);
354
+ } catch {
355
+ }
356
+ },
357
+ setQuery: (q) => setPanelState({ query: q }),
358
+ toggleThemePop: () => setPanelState({ themePopOpen: !getPanelState().themePopOpen }),
359
+ setAppearance: (next, scope) => {
360
+ const s = getPanelState();
361
+ const theme = next.theme ? String(next.theme) : s.theme;
362
+ const palette = next.palette ? String(next.palette) : s.palette;
363
+ setPanelState({ theme, palette });
364
+ const root = getRootEl();
365
+ if (root) {
366
+ applyThemeTo(root, theme, palette, asCustomPalettes(s.customPalettes));
367
+ }
368
+ if (scope === "app") {
369
+ const app = activeAppFrom(getPanelState());
370
+ if (app && host.appTheme) {
371
+ const nextTheme = { theme, palette };
372
+ host.appTheme.save(app.id, nextTheme).catch(() => {
373
+ });
374
+ const cur = getPanelState();
375
+ setPanelState({
376
+ 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 }],
377
+ tabs: cur.tabs.map(
378
+ (t) => t.app?.id === app.id ? { ...t, app: { ...t.app, theme: nextTheme } } : t
379
+ )
380
+ });
381
+ }
382
+ } else {
383
+ host.persistTheme?.(theme, palette);
384
+ }
385
+ host.frame.syncEnv?.();
386
+ },
387
+ setThemeScope: (scope) => setPanelState({ themeScope: scope }),
388
+ clearAppTheme: () => {
389
+ const app = activeAppFrom(getPanelState());
390
+ if (app && host.appTheme) {
391
+ host.appTheme.clear(app.id).catch(() => {
392
+ });
393
+ const cur = getPanelState();
394
+ setPanelState({
395
+ apps: cur.apps.map((a) => a.id === app.id ? { ...a, theme: null } : a),
396
+ tabs: cur.tabs.map(
397
+ (t) => t.app?.id === app.id ? { ...t, app: { ...t.app, theme: null } } : t
398
+ )
399
+ });
400
+ host.frame.syncEnv?.();
401
+ }
402
+ },
403
+ getActiveApp: () => activeAppFrom(getPanelState()),
404
+ toggleSettings: (open) => {
405
+ setPanelState({ settingsOpen: open, cfgMsg: open ? "" : getPanelState().cfgMsg });
406
+ if (open && host.config) {
407
+ host.config.load().then((cfg) => {
408
+ setPanelState({ cfg, cfgVersion: getPanelState().cfgVersion + 1 });
409
+ }).catch(() => {
410
+ });
411
+ }
412
+ },
413
+ getCfg: () => getPanelState().cfg,
414
+ saveHostConfig: (form) => {
415
+ if (!host.config) return;
416
+ host.config.save(form).then(() => {
417
+ setPanelState({
418
+ cfg: { ...form },
419
+ cfgMsg: i18n.t("config.saved"),
420
+ cfgVersion: getPanelState().cfgVersion + 1
421
+ });
422
+ }).catch((e) => {
423
+ setPanelState({ cfgMsg: i18n.t("config.error", { message: errorMessage(e) }) });
424
+ });
425
+ },
426
+ toggleBrowse: (kind) => {
427
+ const s = getPanelState();
428
+ if (s.browseOpen) {
429
+ setPanelState({ browseOpen: false });
430
+ return;
431
+ }
432
+ const app = activeAppFrom(s);
433
+ if (!app) return;
434
+ const k = kind === "storage" ? "storage" : "history";
435
+ if (k === "history" && !host.history) return;
436
+ if (k === "storage" && !host.storage) return;
437
+ setPanelState({
438
+ browseOpen: true,
439
+ browseKind: k,
440
+ browseAppId: app.id,
441
+ browseAppName: app.name || app.id,
442
+ browseDetail: null,
443
+ browseTable: null,
444
+ browseTableValue: null,
445
+ browseOpenFile: null,
446
+ browseError: null,
447
+ browseList: [],
448
+ browseLoading: true
449
+ });
450
+ if (k === "history" && host.history) {
451
+ host.history.list(app.id).then((list) => {
452
+ setPanelState({ browseList: list, browseLoading: false });
453
+ }).catch((e) => setPanelState({ browseError: errorMessage(e), browseLoading: false }));
454
+ } else if (host.storage) {
455
+ host.storage.listTables(app.id).then((list) => {
456
+ setPanelState({ browseList: list, browseLoading: false });
457
+ }).catch((e) => setPanelState({ browseError: errorMessage(e), browseLoading: false }));
458
+ }
459
+ },
460
+ loadCommitDetail: (id) => {
461
+ const s = getPanelState();
462
+ if (!host.history || !s.browseAppId) return;
463
+ setPanelState({ browseDetail: { id, loading: true } });
464
+ host.history.detail(s.browseAppId, id).then((c) => {
465
+ setPanelState({ browseDetail: c });
466
+ }).catch((e) => setPanelState({ browseDetail: { id, error: errorMessage(e), files: [] } }));
467
+ },
468
+ loadTable: (name) => {
469
+ const s = getPanelState();
470
+ if (!host.storage || !s.browseAppId) return;
471
+ setPanelState({ browseTable: name, browseTableValue: null, browseLoading: true });
472
+ host.storage.readTable(s.browseAppId, name).then((v) => {
473
+ setPanelState({ browseTableValue: v, browseLoading: false });
474
+ }).catch(
475
+ (e) => setPanelState({ browseTableValue: { __error__: errorMessage(e) }, browseLoading: false })
476
+ );
477
+ },
478
+ browseBack: () => setPanelState({ browseDetail: null, browseTable: null, browseTableValue: null, browseOpenFile: null }),
479
+ browseFile: (path) => setPanelState({ browseOpenFile: getPanelState().browseOpenFile === path ? null : path }),
480
+ reloadActive: () => {
481
+ const app = activeAppFrom(getPanelState());
482
+ if (app) host.frame.reload(app.id);
483
+ },
484
+ askDelete: () => {
485
+ const app = activeAppFrom(getPanelState());
486
+ if (app) setPanelState({ pendingDelete: app.id });
487
+ },
488
+ hideModal: () => setPanelState({ pendingDelete: null }),
489
+ confirmDelete: async () => {
490
+ const s = getPanelState();
491
+ const id = s.pendingDelete;
492
+ if (!id) return;
493
+ setPanelState({ pendingDelete: null });
494
+ if (host.deleteApp) {
495
+ await host.deleteApp(id).catch(() => {
496
+ });
497
+ }
498
+ const tab = s.tabs.find((t) => t.id === "app:" + id);
499
+ setPanelState({
500
+ tabs: getPanelState().tabs.filter((t) => t.id !== "app:" + id),
501
+ active: getPanelState().active === "app:" + id ? "all" : getPanelState().active,
502
+ apps: getPanelState().apps.filter((a) => a.id !== id)
503
+ });
504
+ if (tab?.app) host.frame.unmount(tab.app.id);
505
+ },
506
+ closeDashboard: () => host.closePanel(),
507
+ fetchApps: () => {
508
+ setPanelState({ loading: true, error: null, emptyText: host.emptyText });
509
+ host.fetchApps().then((apps) => {
510
+ setPanelState({ apps, loading: false });
511
+ }).catch((e) => {
512
+ setPanelState({
513
+ error: isHostUnreachable(e) ? i18n.t("list.hostUnreachable", { url: e.url }) : errorMessage(e),
514
+ loading: false
515
+ });
516
+ });
517
+ },
518
+ setCardStyle: (v) => setPanelState({ cardStyle: v })
519
+ };
520
+ }
521
+ function defaultHideThemePop() {
522
+ if (!getPanelState().themePopOpen) return false;
523
+ setPanelState({ themePopOpen: false });
524
+ return true;
525
+ }
526
+ function clampPaletteId(v) {
527
+ return clampPalette(v);
528
+ }
529
+ var PanelActionsContext = React.createContext(null);
530
+ var PanelI18nContext = React.createContext(null);
531
+ function PanelProvider({
532
+ actions,
533
+ i18n,
534
+ children
535
+ }) {
536
+ return /* @__PURE__ */ jsx(PanelI18nContext.Provider, { value: i18n, children: /* @__PURE__ */ jsx(PanelActionsContext.Provider, { value: actions, children }) });
537
+ }
538
+ function usePanelActions() {
539
+ const ctx = React.useContext(PanelActionsContext);
540
+ if (!ctx) throw new Error("usePanelActions must be used inside <PanelProvider>");
541
+ return ctx;
542
+ }
543
+ function usePanelI18n() {
544
+ const ctx = React.useContext(PanelI18nContext);
545
+ if (!ctx) throw new Error("usePanelI18n must be used inside <PanelProvider>");
546
+ return ctx;
547
+ }
548
+
549
+ // src/lib.ts
550
+ function hue(id) {
551
+ let h = 0;
552
+ const s = String(id || "");
553
+ for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) % 360;
554
+ return h;
555
+ }
556
+ function appBlurb(a) {
557
+ return String(a.description || a.id || "");
558
+ }
559
+ function monoOf(a) {
560
+ return String(a.acronym || (a.name || "?").slice(0, 2) || "?");
561
+ }
562
+ function Loading() {
563
+ const { t } = usePanelI18n();
564
+ return /* @__PURE__ */ jsxs("div", { className: "mma-load", role: "status", "aria-label": t("load.label"), children: [
565
+ /* @__PURE__ */ jsx("div", { className: "mma-load-art", "aria-hidden": "true", children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 88 64", fill: "none", children: [
566
+ /* @__PURE__ */ jsx("rect", { x: "10", y: "8", width: "68", height: "48", rx: "10", stroke: "currentColor", strokeWidth: "1.6", opacity: ".35" }),
567
+ /* @__PURE__ */ jsx("rect", { x: "10", y: "8", width: "68", height: "12", rx: "10", fill: "currentColor", opacity: ".08" }),
568
+ /* @__PURE__ */ jsx("circle", { cx: "20", cy: "14", r: "2.2", fill: "currentColor", opacity: ".35" }),
569
+ /* @__PURE__ */ jsx("rect", { x: "26", y: "12.2", width: "18", height: "3.6", rx: "1.8", fill: "currentColor", opacity: ".22" }),
570
+ /* @__PURE__ */ jsx("rect", { x: "20", y: "28", width: "28", height: "4", rx: "2", fill: "currentColor", opacity: ".16" }),
571
+ /* @__PURE__ */ jsx("rect", { x: "20", y: "36", width: "40", height: "4", rx: "2", fill: "currentColor", opacity: ".1" }),
572
+ /* @__PURE__ */ jsx("rect", { x: "20", y: "44", width: "22", height: "4", rx: "2", fill: "currentColor", opacity: ".08" }),
573
+ /* @__PURE__ */ jsx("path", { d: "M62 40c6 0 10 5 10 10", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", opacity: ".85" }),
574
+ /* @__PURE__ */ jsx("circle", { cx: "72", cy: "50", r: "3.2", fill: "currentColor", opacity: ".9" })
575
+ ] }) }),
576
+ /* @__PURE__ */ jsxs("div", { className: "mma-load-dots", children: [
577
+ /* @__PURE__ */ jsx("i", {}),
578
+ /* @__PURE__ */ jsx("i", {}),
579
+ /* @__PURE__ */ jsx("i", {})
580
+ ] })
581
+ ] });
582
+ }
583
+ function Mark({ app, style }) {
584
+ const mono = monoOf(app);
585
+ if (style === "etch") {
586
+ return /* @__PURE__ */ jsx("span", { className: "mma-etch", style: { "--h": hue(app.id) }, children: mono });
587
+ }
588
+ if (style === "stamp") {
589
+ return /* @__PURE__ */ jsx("span", { className: "mma-stamp", style: { "--h": hue(app.id) }, children: mono });
590
+ }
591
+ return /* @__PURE__ */ jsx("span", { className: "mma-mono", style: { "--h": hue(app.id) }, children: mono });
592
+ }
593
+ function AppCard({ app }) {
594
+ const s = usePanelState();
595
+ const actions = usePanelActions();
596
+ const { t } = usePanelI18n();
597
+ const open = s.tabs.some((tab) => tab.id === "app:" + app.id);
598
+ const commits = Number(app.commits || 0);
599
+ return /* @__PURE__ */ jsxs(
600
+ "button",
601
+ {
602
+ type: "button",
603
+ className: "mma-card",
604
+ style: { "--h": hue(app.id) },
605
+ onClick: () => actions.openAppTab(app),
606
+ children: [
607
+ /* @__PURE__ */ jsx(Mark, { app, style: s.cardStyle }),
608
+ /* @__PURE__ */ jsx("h3", { children: app.name || app.id }),
609
+ /* @__PURE__ */ jsx("p", { children: appBlurb(app) }),
610
+ commits > 0 || open ? /* @__PURE__ */ jsxs("span", { className: "mma-meta", children: [
611
+ commits > 0 ? /* @__PURE__ */ jsx("span", { className: "mma-ver", children: t("list.commits", { count: commits }) }) : null,
612
+ open ? /* @__PURE__ */ jsxs("span", { className: "mma-open", children: [
613
+ /* @__PURE__ */ jsx("i", {}),
614
+ t("list.open")
615
+ ] }) : null
616
+ ] }) : null
617
+ ]
618
+ }
619
+ );
620
+ }
621
+ function AppRow({ app }) {
622
+ const s = usePanelState();
623
+ const actions = usePanelActions();
624
+ const { t } = usePanelI18n();
625
+ const open = s.tabs.some((tab) => tab.id === "app:" + app.id);
626
+ const commits = Number(app.commits || 0);
627
+ return /* @__PURE__ */ jsxs(
628
+ "button",
629
+ {
630
+ type: "button",
631
+ className: "mma-row",
632
+ style: { "--h": hue(app.id) },
633
+ onClick: () => actions.openAppTab(app),
634
+ children: [
635
+ /* @__PURE__ */ jsx(Mark, { app, style: s.cardStyle }),
636
+ /* @__PURE__ */ jsxs("span", { className: "mma-twrap", children: [
637
+ /* @__PURE__ */ jsx("span", { className: "mma-t", children: app.name || app.id }),
638
+ /* @__PURE__ */ jsx("small", { children: appBlurb(app) })
639
+ ] }),
640
+ /* @__PURE__ */ jsxs("span", { className: "mma-right", children: [
641
+ open ? /* @__PURE__ */ jsxs("span", { className: "mma-open", children: [
642
+ /* @__PURE__ */ jsx("i", {}),
643
+ t("list.open")
644
+ ] }) : commits > 0 ? /* @__PURE__ */ jsx("span", { className: "mma-ver", children: t("list.commits", { count: commits }) }) : null,
645
+ /* @__PURE__ */ jsx("svg", { className: "mma-chev", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: "M9.5 6l6 6-6 6" }) })
646
+ ] })
647
+ ]
648
+ }
649
+ );
650
+ }
651
+ function Search() {
652
+ const s = usePanelState();
653
+ const actions = usePanelActions();
654
+ const { t } = usePanelI18n();
655
+ return /* @__PURE__ */ jsxs("div", { className: "mma-search", children: [
656
+ /* @__PURE__ */ jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
657
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "7" }),
658
+ /* @__PURE__ */ jsx("path", { d: "M20 20l-3-3" })
659
+ ] }),
660
+ /* @__PURE__ */ jsx("input", { type: "search", placeholder: t("list.search"), value: s.query, onChange: (e) => actions.setQuery(e.target.value) })
661
+ ] });
662
+ }
663
+ function AppList() {
664
+ const s = usePanelState();
665
+ const actions = usePanelActions();
666
+ const { t } = usePanelI18n();
667
+ const q = String(s.query || "").trim().toLowerCase();
668
+ const apps = q ? s.apps.filter((a) => [a.name, a.id, a.description].join(" ").toLowerCase().includes(q)) : s.apps;
669
+ if (s.loading && !s.apps.length) return /* @__PURE__ */ jsx(Loading, {});
670
+ if (s.error) {
671
+ return /* @__PURE__ */ jsxs("div", { className: "mma-error", children: [
672
+ t("list.loadError", { message: s.error }),
673
+ " ",
674
+ /* @__PURE__ */ jsx("button", { type: "button", className: "mma-textbtn", onClick: () => actions.fetchApps(), children: t("list.retry") })
675
+ ] });
676
+ }
677
+ if (!s.apps.length) {
678
+ return /* @__PURE__ */ jsx("div", { className: "mma-empty", children: s.emptyText || t("list.empty") });
679
+ }
680
+ if (!apps.length) return /* @__PURE__ */ jsx("div", { className: "mma-empty", children: t("list.noMatch", { query: s.query }) });
681
+ const Item = s.dock === "side" ? AppRow : AppCard;
682
+ return /* @__PURE__ */ jsx("div", { className: "mma-grid", children: apps.map((app) => /* @__PURE__ */ jsx(Item, { app }, app.id)) });
683
+ }
684
+ function ListRegion() {
685
+ const s = usePanelState();
686
+ const { t } = usePanelI18n();
687
+ return /* @__PURE__ */ jsxs("div", { className: "mma-list", id: "mma-list", children: [
688
+ /* @__PURE__ */ jsxs("div", { className: "mma-list-head", children: [
689
+ /* @__PURE__ */ jsx("h2", { children: t("list.title") }),
690
+ /* @__PURE__ */ jsx("span", { id: "mma-app-count", children: t("list.count", { count: s.apps.length }) })
691
+ ] }),
692
+ /* @__PURE__ */ jsx(Search, {}),
693
+ /* @__PURE__ */ jsx("div", { id: "mma-list-body", children: /* @__PURE__ */ jsx(AppList, {}) })
694
+ ] });
695
+ }
696
+ function isCommit(value) {
697
+ return typeof value === "object" && value !== null && "id" in value && "message" in value;
698
+ }
699
+ function isStorageTable(value) {
700
+ return typeof value === "object" && value !== null && "name" in value;
701
+ }
702
+ function DiffPreview({ text }) {
703
+ const lines = text.replace(/\n$/, "").split("\n");
704
+ return /* @__PURE__ */ jsx("pre", { className: "mma-preview mma-diff-preview", children: lines.map((line, i) => {
705
+ const kind = line.startsWith("+") ? "add" : line.startsWith("-") ? "del" : "ctx";
706
+ return /* @__PURE__ */ jsxs("span", { className: `mma-diff-line mma-diff-line--${kind}`, children: [
707
+ line || " ",
708
+ "\n"
709
+ ] }, i);
710
+ }) });
711
+ }
712
+ function CommitItem({ c }) {
713
+ const actions = usePanelActions();
714
+ const { t } = usePanelI18n();
715
+ const files = (c.files || []).length;
716
+ let add = 0;
717
+ let del = 0;
718
+ for (const f of c.files || []) {
719
+ if ((f.add || 0) > 0) add += f.add || 0;
720
+ if ((f.del || 0) > 0) del += f.del || 0;
721
+ }
722
+ return /* @__PURE__ */ jsxs("button", { type: "button", className: "mma-bitem", onClick: () => actions.loadCommitDetail(c.id), title: c.message, children: [
723
+ /* @__PURE__ */ jsx("b", { className: "mma-commit-msg mma-commit-msg--clamp2", children: c.message }),
724
+ /* @__PURE__ */ jsxs("span", { className: "meta", children: [
725
+ /* @__PURE__ */ jsx("code", { children: String(c.id).slice(0, 7) }),
726
+ /* @__PURE__ */ jsx("span", { children: c.time }),
727
+ /* @__PURE__ */ jsx("span", { children: t("browse.files", { count: files }) }),
728
+ add ? /* @__PURE__ */ jsxs("span", { className: "mma-plus", children: [
729
+ "+",
730
+ add
731
+ ] }) : null,
732
+ del ? /* @__PURE__ */ jsxs("span", { className: "mma-minus", children: [
733
+ "-",
734
+ del
735
+ ] }) : null
736
+ ] })
737
+ ] });
738
+ }
739
+ function StorageItem({ table }) {
740
+ const actions = usePanelActions();
741
+ return /* @__PURE__ */ jsxs("button", { type: "button", className: "mma-bitem", onClick: () => actions.loadTable(table.name), children: [
742
+ /* @__PURE__ */ jsx("b", { children: table.name }),
743
+ /* @__PURE__ */ jsxs("span", { className: "meta", children: [
744
+ /* @__PURE__ */ jsxs("span", { children: [
745
+ table.size || 0,
746
+ " B"
747
+ ] }),
748
+ /* @__PURE__ */ jsx("span", { children: table.updatedAt || "" })
749
+ ] })
750
+ ] });
751
+ }
752
+ function CommitDetail() {
753
+ const s = usePanelState();
754
+ const actions = usePanelActions();
755
+ const { t } = usePanelI18n();
756
+ const [msgExpanded, setMsgExpanded] = React.useState(false);
757
+ const d = s.browseDetail;
758
+ React.useEffect(() => {
759
+ setMsgExpanded(false);
760
+ }, [d?.id]);
761
+ if (!d) return null;
762
+ if (d.loading) return /* @__PURE__ */ jsx("div", { className: "mma-bempty", children: t("browse.loading") });
763
+ if (d.error) return /* @__PURE__ */ jsx("div", { className: "mma-berr", children: d.error });
764
+ const longMsg = (d.message || "").length > 80 || (d.message || "").includes("\n");
765
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
766
+ /* @__PURE__ */ jsx("div", { className: "mma-browse-head mma-browse-head--tools", children: /* @__PURE__ */ jsx("div", { className: "mma-btns", children: /* @__PURE__ */ jsx("button", { type: "button", onClick: () => actions.browseBack(), children: t("browse.back") }) }) }),
767
+ /* @__PURE__ */ jsxs("div", { className: "mma-commit-meta", children: [
768
+ /* @__PURE__ */ jsx(
769
+ "p",
770
+ {
771
+ className: msgExpanded ? "mma-commit-msg" : "mma-commit-msg mma-commit-msg--clamp3",
772
+ title: d.message,
773
+ children: d.message
774
+ }
775
+ ),
776
+ longMsg ? /* @__PURE__ */ jsx("button", { type: "button", className: "mma-commit-msg-toggle", onClick: () => setMsgExpanded((v) => !v), children: msgExpanded ? t("browse.collapseMsg") : t("browse.expandMsg") }) : null,
777
+ /* @__PURE__ */ jsxs("div", { className: "meta mma-commit-ids", children: [
778
+ /* @__PURE__ */ jsx("code", { children: String(d.id || "").slice(0, 7) }),
779
+ /* @__PURE__ */ jsx("span", { children: d.time })
780
+ ] })
781
+ ] }),
782
+ /* @__PURE__ */ jsxs("div", { className: "mma-files", children: [
783
+ (d.files || []).map((f) => {
784
+ const open = s.browseOpenFile === f.path;
785
+ const add = f.add || 0;
786
+ const del = f.del || 0;
787
+ return /* @__PURE__ */ jsxs("div", { children: [
788
+ /* @__PURE__ */ jsxs(
789
+ "button",
790
+ {
791
+ type: "button",
792
+ className: "mma-fitem",
793
+ "data-open": open ? "1" : void 0,
794
+ onClick: () => actions.browseFile(f.path),
795
+ children: [
796
+ /* @__PURE__ */ jsxs("span", { className: "mma-diff", children: [
797
+ /* @__PURE__ */ jsx("span", { className: add > 0 ? "mma-plus" : "mma-diff-zero", children: add > 0 ? `+${add}` : "\xB7" }),
798
+ /* @__PURE__ */ jsx("span", { className: del > 0 ? "mma-minus" : "mma-diff-zero", children: del > 0 ? `-${del}` : "\xB7" })
799
+ ] }),
800
+ /* @__PURE__ */ jsx("span", { className: "p", children: f.path }),
801
+ /* @__PURE__ */ jsx("svg", { className: "mma-chevron", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "M9.5 6l6 6-6 6" }) })
802
+ ]
803
+ }
804
+ ),
805
+ open && f.preview ? /* @__PURE__ */ jsx(DiffPreview, { text: f.preview }) : null
806
+ ] }, f.path);
807
+ }),
808
+ !(d.files || []).length ? /* @__PURE__ */ jsx("div", { className: "mma-bempty", children: t("browse.noFiles") }) : null
809
+ ] })
810
+ ] });
811
+ }
812
+ function TableDetail() {
813
+ const s = usePanelState();
814
+ const actions = usePanelActions();
815
+ const { t } = usePanelI18n();
816
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
817
+ /* @__PURE__ */ jsxs("div", { className: "mma-browse-head", children: [
818
+ /* @__PURE__ */ jsx("div", { className: "mma-btns", children: /* @__PURE__ */ jsx("button", { type: "button", onClick: () => actions.browseBack(), children: t("browse.back") }) }),
819
+ /* @__PURE__ */ jsx("h3", { children: s.browseTable })
820
+ ] }),
821
+ s.browseLoading ? /* @__PURE__ */ jsx("div", { className: "mma-bempty", children: t("browse.loading") }) : /* @__PURE__ */ jsx("pre", { className: "mma-preview", style: { maxHeight: "none", whiteSpace: "pre-wrap", wordBreak: "break-all" }, children: JSON.stringify(s.browseTableValue, null, 2) })
822
+ ] });
823
+ }
824
+ function Browse() {
825
+ const s = usePanelState();
826
+ const actions = usePanelActions();
827
+ const { t } = usePanelI18n();
828
+ if (!s.browseOpen) return null;
829
+ let content;
830
+ if (s.browseKind === "history" && s.browseDetail) content = /* @__PURE__ */ jsx(CommitDetail, {});
831
+ else if (s.browseKind === "storage" && s.browseTable !== null) content = /* @__PURE__ */ jsx(TableDetail, {});
832
+ else {
833
+ content = /* @__PURE__ */ jsxs(Fragment, { children: [
834
+ /* @__PURE__ */ jsxs("div", { className: "mma-browse-head", children: [
835
+ /* @__PURE__ */ jsxs("h3", { children: [
836
+ s.browseKind === "history" ? t("browse.history") : t("browse.storage"),
837
+ /* @__PURE__ */ jsx("span", { className: "sub", children: s.browseAppName })
838
+ ] }),
839
+ /* @__PURE__ */ jsx("div", { className: "mma-btns", children: /* @__PURE__ */ jsx("button", { type: "button", onClick: () => actions.toggleBrowse(""), children: t("browse.close") }) })
840
+ ] }),
841
+ s.browseLoading && !s.browseList.length ? /* @__PURE__ */ jsx("div", { className: "mma-bempty", children: t("browse.loading") }) : s.browseError ? /* @__PURE__ */ jsx("div", { className: "mma-berr", children: s.browseError }) : !s.browseList.length ? /* @__PURE__ */ jsx("div", { className: "mma-bempty", children: s.browseKind === "history" ? t("browse.noCommits") : t("browse.noStorage") }) : /* @__PURE__ */ jsx("div", { className: "mma-blist", children: s.browseList.map(
842
+ (item, i) => s.browseKind === "history" && isCommit(item) ? /* @__PURE__ */ jsx(CommitItem, { c: item }, i) : isStorageTable(item) ? /* @__PURE__ */ jsx(StorageItem, { table: item }, i) : null
843
+ ) })
844
+ ] });
845
+ }
846
+ return /* @__PURE__ */ jsx("div", { className: "mma-browse", id: "mma-browse", "data-open": "1", children: /* @__PURE__ */ jsx("div", { className: "mma-browse-body", id: "mma-browse-body", children: content }) });
847
+ }
848
+ function Modal() {
849
+ const s = usePanelState();
850
+ const actions = usePanelActions();
851
+ const { t } = usePanelI18n();
852
+ if (!s.pendingDelete) return null;
853
+ const app = s.apps.find((a) => a.id === s.pendingDelete) || { id: s.pendingDelete, name: s.pendingDelete };
854
+ return /* @__PURE__ */ jsx("div", { className: "mma-modal", id: "mma-modal", children: /* @__PURE__ */ jsxs("div", { className: "mma-dialog", children: [
855
+ /* @__PURE__ */ jsx("h3", { children: t("modal.title") }),
856
+ /* @__PURE__ */ jsx("p", { children: t("modal.body", { name: app.name || app.id }) }),
857
+ /* @__PURE__ */ jsxs("div", { className: "mma-dialog-actions", children: [
858
+ /* @__PURE__ */ jsx("button", { type: "button", id: "mma-dialog-cancel", onClick: () => actions.hideModal(), children: t("modal.cancel") }),
859
+ /* @__PURE__ */ jsx("button", { type: "button", id: "mma-dialog-ok", className: "go", onClick: () => void actions.confirmDelete(), children: t("modal.confirm") })
860
+ ] })
861
+ ] }) });
862
+ }
863
+ function Settings() {
864
+ const s = usePanelState();
865
+ const actions = usePanelActions();
866
+ const { t } = usePanelI18n();
867
+ const [form, setForm] = React.useState({});
868
+ React.useEffect(() => {
869
+ if (!s.settingsOpen) return;
870
+ setForm(s.cfg && Object.keys(s.cfg).length ? s.cfg : actions.getCfg());
871
+ }, [s.settingsOpen, s.cfgVersion, s.cfg, actions]);
872
+ if (!s.capabilities.config) return null;
873
+ const set = (k) => (e) => setForm((prev) => ({ ...prev, [k]: e.target.value }));
874
+ return /* @__PURE__ */ jsxs("div", { className: "mma-settings", id: "mma-settings", "data-open": s.settingsOpen ? "1" : "0", children: [
875
+ /* @__PURE__ */ jsxs("div", { className: "mma-settings-head", children: [
876
+ /* @__PURE__ */ jsx("h3", { children: t("settings.title") }),
877
+ /* @__PURE__ */ jsx("button", { type: "button", className: "mma-iconbtn", id: "mma-cfg-close", onClick: () => actions.toggleSettings(false), children: "\u2715" })
878
+ ] }),
879
+ /* @__PURE__ */ jsxs("label", { children: [
880
+ t("settings.hostPort"),
881
+ /* @__PURE__ */ jsx("input", { id: "mma-cfg-port", type: "number", value: form.hostPort || "", onChange: set("hostPort") })
882
+ ] }),
883
+ /* @__PURE__ */ jsxs("label", { children: [
884
+ t("settings.language"),
885
+ /* @__PURE__ */ jsxs("select", { id: "mma-cfg-lang", value: form.locale || form.chatLanguage || "zh-CN", onChange: set("locale"), children: [
886
+ /* @__PURE__ */ jsx("option", { value: "zh-CN", children: t("settings.langZh") }),
887
+ /* @__PURE__ */ jsx("option", { value: "en", children: t("settings.langEn") })
888
+ ] })
889
+ ] }),
890
+ /* @__PURE__ */ jsxs("label", { children: [
891
+ t("settings.theme"),
892
+ /* @__PURE__ */ jsxs("select", { id: "mma-cfg-theme", value: form.theme || "light", onChange: set("theme"), children: [
893
+ /* @__PURE__ */ jsx("option", { value: "light", children: t("theme.light") }),
894
+ /* @__PURE__ */ jsx("option", { value: "dark", children: t("theme.dark") })
895
+ ] })
896
+ ] }),
897
+ /* @__PURE__ */ jsxs("label", { children: [
898
+ t("settings.palette"),
899
+ /* @__PURE__ */ jsx("select", { id: "mma-cfg-palette", value: form.palette || "default", onChange: set("palette"), children: PALETTES.map((p) => /* @__PURE__ */ jsx("option", { value: p.id, children: t(`palette.${p.id}`) }, p.id)) })
900
+ ] }),
901
+ /* @__PURE__ */ jsxs("label", { children: [
902
+ t("settings.cardStyle"),
903
+ /* @__PURE__ */ jsxs("select", { id: "mma-cfg-cardstyle", value: form.cardStyle || "stamp", onChange: set("cardStyle"), children: [
904
+ /* @__PURE__ */ jsx("option", { value: "stamp", children: t("settings.cardStamp") }),
905
+ /* @__PURE__ */ jsx("option", { value: "etch", children: t("settings.cardEtch") }),
906
+ /* @__PURE__ */ jsx("option", { value: "hero", children: t("settings.cardHero") }),
907
+ /* @__PURE__ */ jsx("option", { value: "list", children: t("settings.cardList") })
908
+ ] })
909
+ ] }),
910
+ /* @__PURE__ */ jsxs("label", { children: [
911
+ t("settings.llmProvider"),
912
+ /* @__PURE__ */ jsx("input", { id: "mma-cfg-provider", value: form.provider || "", onChange: set("provider") })
913
+ ] }),
914
+ /* @__PURE__ */ jsxs("label", { children: [
915
+ t("settings.llmModel"),
916
+ /* @__PURE__ */ jsx("input", { id: "mma-cfg-model", value: form.model || "", onChange: set("model") })
917
+ ] }),
918
+ /* @__PURE__ */ jsxs("div", { className: "mma-settings-actions", children: [
919
+ /* @__PURE__ */ jsx("button", { type: "button", className: "mma-textbtn", id: "mma-cfg-save", onClick: () => actions.saveHostConfig(form), children: t("settings.save") }),
920
+ /* @__PURE__ */ jsx("span", { className: "mma-settings-msg", id: "mma-cfg-msg", children: s.cfgMsg })
921
+ ] })
922
+ ] });
923
+ }
924
+ function Tabs() {
925
+ const s = usePanelState();
926
+ const actions = usePanelActions();
927
+ const { t } = usePanelI18n();
928
+ return /* @__PURE__ */ jsx("div", { className: "mma-tabs", id: "mma-tabs", children: s.tabs.map((tb) => {
929
+ const active = tb.id === s.active;
930
+ const close = tb.id === "all" ? null : /* @__PURE__ */ jsx(
931
+ "span",
932
+ {
933
+ className: "mma-tab-x",
934
+ role: "button",
935
+ "aria-label": t("tabs.close", { title: tb.title }),
936
+ onClick: (e) => {
937
+ e.stopPropagation();
938
+ actions.closeTab(tb.id);
939
+ },
940
+ children: "\xD7"
941
+ }
942
+ );
943
+ return /* @__PURE__ */ jsxs(
944
+ "button",
945
+ {
946
+ type: "button",
947
+ className: "mma-tab",
948
+ "data-active": active ? "1" : "0",
949
+ title: tb.id === "all" ? t("tabs.allTitle") : tb.title,
950
+ onClick: () => actions.switchTab(tb.id),
951
+ children: [
952
+ /* @__PURE__ */ jsx("span", { children: tb.title }),
953
+ close
954
+ ]
955
+ },
956
+ tb.id
957
+ );
958
+ }) });
959
+ }
960
+ function ThemePop() {
961
+ const s = usePanelState();
962
+ const actions = usePanelActions();
963
+ const { t } = usePanelI18n();
964
+ const app = actions.getActiveApp();
965
+ const appTheme = app ? app.theme : null;
966
+ const customs = s.customPalettes || {};
967
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
968
+ s.themePopOpen ? /* @__PURE__ */ jsx(
969
+ "div",
970
+ {
971
+ className: "mma-pop-scrim",
972
+ "aria-hidden": "true",
973
+ onPointerDown: (e) => {
974
+ e.preventDefault();
975
+ e.stopPropagation();
976
+ defaultHideThemePop();
977
+ }
978
+ }
979
+ ) : null,
980
+ /* @__PURE__ */ jsxs(
981
+ "div",
982
+ {
983
+ className: "mma-pop",
984
+ id: "mma-theme-pop",
985
+ role: "menu",
986
+ "data-open": s.themePopOpen ? "1" : "0",
987
+ onClick: (e) => e.stopPropagation(),
988
+ onPointerDown: (e) => e.stopPropagation(),
989
+ children: [
990
+ /* @__PURE__ */ jsx("div", { className: "mma-pop-seg", children: ["light", "dark"].map((mode) => /* @__PURE__ */ jsx(
991
+ "button",
992
+ {
993
+ type: "button",
994
+ "data-mode": mode,
995
+ "data-on": s.theme === mode ? "1" : "0",
996
+ onClick: () => actions.setAppearance({ theme: mode }, s.themeScope),
997
+ children: t(`theme.${mode}`)
998
+ },
999
+ mode
1000
+ )) }),
1001
+ /* @__PURE__ */ jsxs("div", { className: "mma-pop-list", children: [
1002
+ PALETTES.map((p) => /* @__PURE__ */ jsxs(
1003
+ "button",
1004
+ {
1005
+ type: "button",
1006
+ className: "mma-swatch",
1007
+ "data-palette": p.id,
1008
+ role: "menuitem",
1009
+ "data-on": s.palette === p.id ? "1" : "0",
1010
+ onClick: () => actions.setAppearance({ palette: p.id }, s.themeScope),
1011
+ children: [
1012
+ /* @__PURE__ */ jsx("i", { className: "mma-dot", style: { background: p.swatch } }),
1013
+ /* @__PURE__ */ jsx("span", { children: t(`palette.${p.id}`) })
1014
+ ]
1015
+ },
1016
+ p.id
1017
+ )),
1018
+ Object.keys(customs).map((id) => /* @__PURE__ */ jsxs(
1019
+ "button",
1020
+ {
1021
+ type: "button",
1022
+ className: "mma-swatch",
1023
+ "data-palette": id,
1024
+ "data-custom": "1",
1025
+ role: "menuitem",
1026
+ "data-on": s.palette === id ? "1" : "0",
1027
+ onClick: () => actions.setAppearance({ palette: id }, s.themeScope),
1028
+ children: [
1029
+ /* @__PURE__ */ jsx("i", { className: "mma-dot", style: { background: customs[id].swatch || "#888" } }),
1030
+ /* @__PURE__ */ jsx("span", { children: customs[id].label || id }),
1031
+ /* @__PURE__ */ jsx("i", { className: "mma-custom-badge", children: t("theme.custom") })
1032
+ ]
1033
+ },
1034
+ id
1035
+ ))
1036
+ ] }),
1037
+ /* @__PURE__ */ jsxs("div", { className: "mma-pop-seg mma-scope-seg", children: [
1038
+ /* @__PURE__ */ jsx(
1039
+ "button",
1040
+ {
1041
+ type: "button",
1042
+ "data-scope": "global",
1043
+ "data-on": s.themeScope === "global" ? "1" : "0",
1044
+ onClick: () => actions.setThemeScope("global"),
1045
+ children: t("theme.global")
1046
+ }
1047
+ ),
1048
+ /* @__PURE__ */ jsx(
1049
+ "button",
1050
+ {
1051
+ type: "button",
1052
+ "data-scope": "app",
1053
+ id: "mma-scope-app",
1054
+ title: app ? t("theme.saveTo", { name: app.name }) : t("theme.openAppFirst"),
1055
+ "data-on": s.themeScope === "app" ? "1" : "0",
1056
+ disabled: !app || !s.capabilities.appTheme,
1057
+ onClick: () => actions.setThemeScope("app"),
1058
+ children: app ? app.name : t("theme.currentApp")
1059
+ }
1060
+ )
1061
+ ] }),
1062
+ s.themeScope === "app" && appTheme ? /* @__PURE__ */ jsx("button", { type: "button", className: "mma-textbtn", id: "mma-clear-app-theme", onClick: () => actions.clearAppTheme(), children: t("theme.followGlobal") }) : null
1063
+ ]
1064
+ }
1065
+ )
1066
+ ] });
1067
+ }
1068
+ var iconProps = { size: 16, strokeWidth: 2, className: "mma-ico" };
1069
+ function Toolbar() {
1070
+ const s = usePanelState();
1071
+ const actions = usePanelActions();
1072
+ const { t } = usePanelI18n();
1073
+ const side = s.dock === "side";
1074
+ const appTab = s.tabs.some((tab) => tab.id === s.active && tab.kind === "app");
1075
+ const anyApp = s.tabs.some((tab) => tab.kind === "app");
1076
+ return /* @__PURE__ */ jsxs("div", { className: "mma-toolbar", children: [
1077
+ appTab && s.capabilities.deleteApp ? /* @__PURE__ */ jsx(
1078
+ "button",
1079
+ {
1080
+ type: "button",
1081
+ className: "mma-textbtn danger",
1082
+ id: "mma-delete",
1083
+ title: t("toolbar.delete"),
1084
+ onClick: () => actions.askDelete(),
1085
+ children: t("toolbar.delete")
1086
+ }
1087
+ ) : null,
1088
+ appTab ? /* @__PURE__ */ jsx(
1089
+ "button",
1090
+ {
1091
+ type: "button",
1092
+ className: "mma-iconbtn",
1093
+ id: "mma-reload",
1094
+ title: t("toolbar.reload"),
1095
+ "aria-label": t("toolbar.reload"),
1096
+ onClick: () => actions.reloadActive(),
1097
+ children: /* @__PURE__ */ jsx(RefreshCw, { ...iconProps })
1098
+ }
1099
+ ) : null,
1100
+ /* @__PURE__ */ jsxs("div", { className: "mma-theme-wrap", id: "mma-theme-wrap", children: [
1101
+ /* @__PURE__ */ jsx(
1102
+ "button",
1103
+ {
1104
+ type: "button",
1105
+ className: "mma-iconbtn",
1106
+ id: "mma-theme-btn",
1107
+ title: t("toolbar.theme"),
1108
+ "aria-label": t("toolbar.theme"),
1109
+ "aria-haspopup": "menu",
1110
+ onClick: () => actions.toggleThemePop(),
1111
+ children: /* @__PURE__ */ jsx(LayoutGrid, { ...iconProps })
1112
+ }
1113
+ ),
1114
+ /* @__PURE__ */ jsx(ThemePop, {})
1115
+ ] }),
1116
+ anyApp && s.capabilities.history ? /* @__PURE__ */ jsx(
1117
+ "button",
1118
+ {
1119
+ type: "button",
1120
+ className: "mma-iconbtn",
1121
+ id: "mma-history-btn",
1122
+ title: t("toolbar.history"),
1123
+ "aria-label": t("toolbar.history"),
1124
+ onClick: () => actions.toggleBrowse("history"),
1125
+ children: /* @__PURE__ */ jsx(Clock, { ...iconProps })
1126
+ }
1127
+ ) : null,
1128
+ anyApp && s.capabilities.storage ? /* @__PURE__ */ jsx(
1129
+ "button",
1130
+ {
1131
+ type: "button",
1132
+ className: "mma-iconbtn",
1133
+ id: "mma-storage-btn",
1134
+ title: t("toolbar.storage"),
1135
+ "aria-label": t("toolbar.storage"),
1136
+ onClick: () => actions.toggleBrowse("storage"),
1137
+ children: /* @__PURE__ */ jsx(Database, { ...iconProps })
1138
+ }
1139
+ ) : null,
1140
+ s.capabilities.config ? /* @__PURE__ */ jsx(
1141
+ "button",
1142
+ {
1143
+ type: "button",
1144
+ className: "mma-iconbtn",
1145
+ id: "mma-settings-btn",
1146
+ title: t("toolbar.settings"),
1147
+ "aria-label": t("toolbar.settings"),
1148
+ onClick: () => actions.toggleSettings(true),
1149
+ children: /* @__PURE__ */ jsx(Settings$1, { ...iconProps })
1150
+ }
1151
+ ) : null,
1152
+ /* @__PURE__ */ jsx(
1153
+ "button",
1154
+ {
1155
+ type: "button",
1156
+ className: "mma-iconbtn",
1157
+ id: "mma-dock-host",
1158
+ title: side ? t("toolbar.dockFill") : t("toolbar.dockSide"),
1159
+ "aria-label": side ? t("toolbar.dockFill") : t("toolbar.dockSide"),
1160
+ onClick: () => actions.setDock(side ? "fill" : "side"),
1161
+ children: /* @__PURE__ */ jsx(PanelRight, { ...iconProps })
1162
+ }
1163
+ ),
1164
+ /* @__PURE__ */ jsx(
1165
+ "button",
1166
+ {
1167
+ type: "button",
1168
+ className: "mma-iconbtn",
1169
+ id: "mma-close-host",
1170
+ title: t("toolbar.close"),
1171
+ "aria-label": t("toolbar.close"),
1172
+ onClick: () => actions.closeDashboard(),
1173
+ children: /* @__PURE__ */ jsx(X, { ...iconProps })
1174
+ }
1175
+ )
1176
+ ] });
1177
+ }
1178
+ function MiniAppPanel() {
1179
+ const s = usePanelState();
1180
+ const listVisible = s.active === "all";
1181
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1182
+ /* @__PURE__ */ jsxs("div", { className: "mma-chrome", children: [
1183
+ /* @__PURE__ */ jsx(Tabs, {}),
1184
+ /* @__PURE__ */ jsx(Toolbar, {})
1185
+ ] }),
1186
+ /* @__PURE__ */ jsxs("div", { className: "mma-stage", children: [
1187
+ listVisible ? /* @__PURE__ */ jsx(ListRegion, {}) : null,
1188
+ /* @__PURE__ */ jsx("div", { id: "mma-frames", className: "mma-frames", style: { display: listVisible ? "none" : "block" } })
1189
+ ] }),
1190
+ /* @__PURE__ */ jsx(Settings, {}),
1191
+ /* @__PURE__ */ jsx(Browse, {}),
1192
+ /* @__PURE__ */ jsx(Modal, {})
1193
+ ] });
1194
+ }
1195
+
1196
+ // src/errors.ts
1197
+ var PanelError = class extends Error {
1198
+ constructor(code, message, options) {
1199
+ super(message, options?.cause !== void 0 ? { cause: options.cause } : void 0);
1200
+ __publicField(this, "code");
1201
+ this.name = new.target.name;
1202
+ this.code = code;
1203
+ }
1204
+ };
1205
+
1206
+ // src/frame.ts
1207
+ function escapeHtml(s) {
1208
+ return String(s == null ? "" : s).replace(/[&<>"']/g, (c) => {
1209
+ switch (c) {
1210
+ case "&":
1211
+ return "&amp;";
1212
+ case "<":
1213
+ return "&lt;";
1214
+ case ">":
1215
+ return "&gt;";
1216
+ case '"':
1217
+ return "&quot;";
1218
+ case "'":
1219
+ return "&#39;";
1220
+ default:
1221
+ return c;
1222
+ }
1223
+ });
1224
+ }
1225
+ function loadingMarkup() {
1226
+ return `<div class="mma-load" role="status" aria-label="\u52A0\u8F7D\u4E2D">
1227
+ <div class="mma-load-art" aria-hidden="true">
1228
+ <svg viewBox="0 0 88 64" fill="none">
1229
+ <rect x="10" y="8" width="68" height="48" rx="10" stroke="currentColor" stroke-width="1.6" opacity=".35"/>
1230
+ <rect x="10" y="8" width="68" height="12" rx="10" fill="currentColor" opacity=".08"/>
1231
+ <circle cx="20" cy="14" r="2.2" fill="currentColor" opacity=".35"/>
1232
+ <rect x="26" y="12.2" width="18" height="3.6" rx="1.8" fill="currentColor" opacity=".22"/>
1233
+ <rect x="20" y="28" width="28" height="4" rx="2" fill="currentColor" opacity=".16"/>
1234
+ <rect x="20" y="36" width="40" height="4" rx="2" fill="currentColor" opacity=".1"/>
1235
+ <rect x="20" y="44" width="22" height="4" rx="2" fill="currentColor" opacity=".08"/>
1236
+ <path d="M62 40c6 0 10 5 10 10" stroke="var(--primary,#3b82f6)" stroke-width="1.8" stroke-linecap="round" opacity=".85"/>
1237
+ <circle cx="72" cy="50" r="3.2" fill="var(--primary,#3b82f6)" opacity=".9"/>
1238
+ </svg>
1239
+ <div class="mma-load-dots"><i></i><i></i><i></i></div></div></div>`;
1240
+ }
1241
+ function createFrameController(opts) {
1242
+ const { container: initial2, urlOf, envOf } = opts;
1243
+ let container = initial2 ?? null;
1244
+ const map = /* @__PURE__ */ new Map();
1245
+ return {
1246
+ map,
1247
+ url: (appId) => urlOf(appId),
1248
+ setContainer(el) {
1249
+ container = el;
1250
+ },
1251
+ postEnv(appId) {
1252
+ const rec = map.get(appId);
1253
+ const w = rec?.iframe.contentWindow;
1254
+ if (!w) return;
1255
+ const env = envOf(appId);
1256
+ try {
1257
+ w.postMessage({ type: "mma-set-env", theme: env.theme, palette: env.palette, dock: env.dock }, "*");
1258
+ } catch {
1259
+ }
1260
+ },
1261
+ postEnvAll() {
1262
+ for (const id of map.keys()) this.postEnv(id);
1263
+ },
1264
+ mount(appId, title) {
1265
+ for (const rec2 of map.values()) rec2.wrap.style.display = "none";
1266
+ let rec = map.get(appId);
1267
+ if (!rec) {
1268
+ const wrap = document.createElement("div");
1269
+ wrap.className = "mma-frame";
1270
+ wrap.setAttribute("data-app", appId);
1271
+ wrap.innerHTML = `${loadingMarkup()}<iframe title="${escapeHtml(title || appId)}"></iframe>`;
1272
+ if (!container) return;
1273
+ container.appendChild(wrap);
1274
+ const iframe = wrap.querySelector("iframe");
1275
+ if (!iframe) return;
1276
+ iframe.src = urlOf(appId);
1277
+ iframe.addEventListener("load", () => {
1278
+ const overlay = wrap.querySelector(".mma-load");
1279
+ overlay?.parentNode?.removeChild(overlay);
1280
+ this.postEnvAll();
1281
+ });
1282
+ rec = { wrap, iframe };
1283
+ map.set(appId, rec);
1284
+ }
1285
+ rec.wrap.style.display = "flex";
1286
+ },
1287
+ unmount(appId) {
1288
+ const rec = map.get(appId);
1289
+ if (!rec) return;
1290
+ rec.wrap.parentNode?.removeChild(rec.wrap);
1291
+ map.delete(appId);
1292
+ },
1293
+ unmountAll() {
1294
+ for (const id of [...map.keys()]) this.unmount(id);
1295
+ },
1296
+ reload(appId) {
1297
+ const rec = map.get(appId);
1298
+ if (!rec) return;
1299
+ if (!rec.wrap.querySelector(".mma-load")) rec.wrap.insertAdjacentHTML("afterbegin", loadingMarkup());
1300
+ rec.iframe.src = `${urlOf(appId)}&_=${Date.now()}`;
1301
+ }
1302
+ };
1303
+ }
1304
+
1305
+ // src/locales/en.json
1306
+ var en_default = {
1307
+ tabs: {
1308
+ all: "All",
1309
+ allTitle: "All mini apps",
1310
+ close: "Close {{title}}"
1311
+ },
1312
+ toolbar: {
1313
+ delete: "Delete",
1314
+ reload: "Reload",
1315
+ theme: "Theme",
1316
+ history: "History",
1317
+ storage: "Storage",
1318
+ settings: "Settings",
1319
+ dockFill: "Fill main area",
1320
+ dockSide: "Pin to chat right",
1321
+ close: "Close"
1322
+ },
1323
+ theme: {
1324
+ light: "Light",
1325
+ dark: "Dark",
1326
+ custom: "Custom",
1327
+ global: "Global",
1328
+ currentApp: "Current app",
1329
+ openAppFirst: "Open an app first",
1330
+ saveTo: 'Save to "{{name}}"',
1331
+ followGlobal: "Follow global (clear app theme)"
1332
+ },
1333
+ palette: {
1334
+ default: "Default",
1335
+ tokyo: "Tokyo Night",
1336
+ forest: "Everforest",
1337
+ matcha: "Strawberry Matcha",
1338
+ yellow: "Yellow Pill",
1339
+ zoro: "Three Swords",
1340
+ hokage: "Hokage Dawn",
1341
+ slate: "Slate"
1342
+ },
1343
+ list: {
1344
+ title: "Mini apps",
1345
+ count: "{{count}} apps",
1346
+ search: "Search mini apps",
1347
+ open: "Open",
1348
+ commits: "{{count}} commits",
1349
+ loadError: "Failed to load list: {{message}}",
1350
+ hostUnreachable: "Cannot reach the mini-app host ({{url}}): the host is not running or the port is taken. Free the port, or set hostPort in runtime/host.json and restart.",
1351
+ retry: "Retry",
1352
+ empty: "No mini apps yet.",
1353
+ noMatch: 'No mini apps matching "{{query}}".'
1354
+ },
1355
+ settings: {
1356
+ title: "Settings",
1357
+ hostPort: "Host port",
1358
+ language: "Language",
1359
+ langZh: "Chinese",
1360
+ langEn: "English",
1361
+ theme: "Theme",
1362
+ palette: "Palette",
1363
+ cardStyle: "Card style",
1364
+ cardStamp: "Stamp",
1365
+ cardEtch: "Etch",
1366
+ cardHero: "Hero",
1367
+ cardList: "List",
1368
+ llmProvider: "LLM Provider",
1369
+ llmModel: "LLM model",
1370
+ save: "Save"
1371
+ },
1372
+ modal: {
1373
+ title: "Delete mini app",
1374
+ body: 'This will delete "{{name}}" and its local data. This cannot be undone.',
1375
+ cancel: "Cancel",
1376
+ confirm: "Delete"
1377
+ },
1378
+ browse: {
1379
+ history: "History",
1380
+ storage: "Storage",
1381
+ close: "Close",
1382
+ loading: "Loading\u2026",
1383
+ files: "{{count}} files",
1384
+ back: "\u2190 Back",
1385
+ noFiles: "No file changes",
1386
+ noCommits: "No commits yet",
1387
+ noStorage: "No storage files",
1388
+ expandMsg: "Show more",
1389
+ collapseMsg: "Show less"
1390
+ },
1391
+ load: {
1392
+ label: "Loading"
1393
+ },
1394
+ config: {
1395
+ saved: "Saved",
1396
+ error: "\u2717 {{message}}"
1397
+ }
1398
+ };
1399
+
1400
+ // src/locales/zh-CN.json
1401
+ var zh_CN_default = {
1402
+ tabs: {
1403
+ all: "\u5168\u90E8",
1404
+ allTitle: "\u5168\u90E8\u5C0F\u7A0B\u5E8F",
1405
+ close: "\u5173\u95ED {{title}}"
1406
+ },
1407
+ toolbar: {
1408
+ delete: "\u5220\u9664",
1409
+ reload: "\u91CD\u65B0\u52A0\u8F7D",
1410
+ theme: "\u4E3B\u9898",
1411
+ history: "\u63D0\u4EA4\u5386\u53F2",
1412
+ storage: "\u5B58\u50A8",
1413
+ settings: "\u8BBE\u7F6E",
1414
+ dockFill: "\u94FA\u6EE1\u4E3B\u533A",
1415
+ dockSide: "\u9489\u5230\u804A\u5929\u53F3\u4FA7",
1416
+ close: "\u5173\u95ED"
1417
+ },
1418
+ theme: {
1419
+ light: "\u6D45\u8272",
1420
+ dark: "\u6DF1\u8272",
1421
+ custom: "\u81EA\u5B9A\u4E49",
1422
+ global: "\u5168\u5C40",
1423
+ currentApp: "\u5F53\u524D\u5C0F\u7A0B\u5E8F",
1424
+ openAppFirst: "\u6253\u5F00\u5C0F\u7A0B\u5E8F\u540E\u53EF\u7528",
1425
+ saveTo: "\u4FDD\u5B58\u5230\u300C{{name}}\u300D",
1426
+ followGlobal: "\u8DDF\u968F\u5168\u5C40\uFF08\u6E05\u9664\u672C\u5E94\u7528\u4E3B\u9898\uFF09"
1427
+ },
1428
+ palette: {
1429
+ default: "\u9ED8\u8BA4",
1430
+ tokyo: "\u4E1C\u4EAC\u591C",
1431
+ forest: "\u82D4\u539F",
1432
+ matcha: "\u8349\u8393\u62B9\u8336",
1433
+ yellow: "\u836F\u4E38\u9EC4",
1434
+ zoro: "\u4E09\u5200\u6D41",
1435
+ hokage: "\u706B\u5F71\u9ECE\u660E",
1436
+ slate: "\u77F3\u58A8"
1437
+ },
1438
+ list: {
1439
+ title: "\u5C0F\u7A0B\u5E8F",
1440
+ count: "{{count}} \u4E2A",
1441
+ search: "\u641C\u7D22\u5C0F\u7A0B\u5E8F",
1442
+ open: "\u5DF2\u6253\u5F00",
1443
+ commits: "{{count}} commits",
1444
+ loadError: "\u5217\u8868\u52A0\u8F7D\u5931\u8D25\uFF1A{{message}}",
1445
+ hostUnreachable: "\u65E0\u6CD5\u8FDE\u63A5\u5C0F\u7A0B\u5E8F host\uFF08{{url}}\uFF09\uFF1Ahost \u672A\u542F\u52A8\u6216\u7AEF\u53E3\u88AB\u5360\u7528\u3002\u8BF7\u91CA\u653E\u7AEF\u53E3\uFF0C\u6216\u7F16\u8F91 runtime/host.json \u7684 hostPort \u540E\u91CD\u542F\u3002",
1446
+ retry: "\u91CD\u8BD5",
1447
+ empty: "\u8FD8\u6CA1\u6709\u5C0F\u7A0B\u5E8F\u3002",
1448
+ noMatch: "\u6CA1\u6709\u5339\u914D\u300C{{query}}\u300D\u7684\u5C0F\u7A0B\u5E8F\u3002"
1449
+ },
1450
+ settings: {
1451
+ title: "\u8BBE\u7F6E",
1452
+ hostPort: "Host \u7AEF\u53E3",
1453
+ language: "\u754C\u9762\u8BED\u8A00",
1454
+ langZh: "\u4E2D\u6587",
1455
+ langEn: "English",
1456
+ theme: "\u4E3B\u9898",
1457
+ palette: "\u8C03\u8272\u677F",
1458
+ cardStyle: "\u5361\u7247\u6837\u5F0F",
1459
+ cardStamp: "\u5370\u7AE0",
1460
+ cardEtch: "\u8680\u523B",
1461
+ cardHero: "\u6D77\u62A5",
1462
+ cardList: "\u5217\u8868",
1463
+ llmProvider: "LLM Provider",
1464
+ llmModel: "LLM \u6A21\u578B",
1465
+ save: "\u4FDD\u5B58"
1466
+ },
1467
+ modal: {
1468
+ title: "\u5220\u9664\u5C0F\u7A0B\u5E8F",
1469
+ body: "\u5C06\u5220\u9664\u300C{{name}}\u300D\u53CA\u5176\u672C\u5730\u6570\u636E\uFF0C\u65E0\u6CD5\u64A4\u9500\u3002",
1470
+ cancel: "\u53D6\u6D88",
1471
+ confirm: "\u5220\u9664"
1472
+ },
1473
+ browse: {
1474
+ history: "\u63D0\u4EA4\u5386\u53F2",
1475
+ storage: "\u5B58\u50A8",
1476
+ close: "\u5173\u95ED",
1477
+ loading: "\u52A0\u8F7D\u4E2D\u2026",
1478
+ files: "{{count}} \u4E2A\u6587\u4EF6",
1479
+ back: "\u2190 \u8FD4\u56DE",
1480
+ noFiles: "\u65E0\u6587\u4EF6\u6539\u52A8",
1481
+ noCommits: "\u6682\u65E0\u63D0\u4EA4\u8BB0\u5F55",
1482
+ noStorage: "\u6682\u65E0\u5B58\u50A8\u6587\u4EF6",
1483
+ expandMsg: "\u5C55\u5F00\u5168\u6587",
1484
+ collapseMsg: "\u6536\u8D77"
1485
+ },
1486
+ load: {
1487
+ label: "\u52A0\u8F7D\u4E2D"
1488
+ },
1489
+ config: {
1490
+ saved: "\u5DF2\u4FDD\u5B58",
1491
+ error: "\u2717 {{message}}"
1492
+ }
1493
+ };
1494
+
1495
+ // src/types.ts
1496
+ var LOCALE_IDS = ["zh-CN", "en"];
1497
+
1498
+ // src/i18n.ts
1499
+ var resources = {
1500
+ "zh-CN": { translation: zh_CN_default },
1501
+ en: { translation: en_default }
1502
+ };
1503
+ function isLocaleId(value) {
1504
+ return LOCALE_IDS.includes(value);
1505
+ }
1506
+ function nodeEnv() {
1507
+ const g = globalThis;
1508
+ return g.process?.env?.NODE_ENV;
1509
+ }
1510
+ function failOnMissingKey() {
1511
+ return nodeEnv() !== "production";
1512
+ }
1513
+ function createPanelI18n(locale) {
1514
+ if (!isLocaleId(locale)) {
1515
+ throw new PanelError("I18N_INVALID_LOCALE", `unsupported locale: ${String(locale)}`);
1516
+ }
1517
+ const i18n = createInstance();
1518
+ void i18n.init({
1519
+ lng: locale,
1520
+ fallbackLng: false,
1521
+ supportedLngs: [...LOCALE_IDS],
1522
+ nonExplicitSupportedLngs: false,
1523
+ load: "currentOnly",
1524
+ defaultNS: "translation",
1525
+ ns: ["translation"],
1526
+ resources,
1527
+ interpolation: { escapeValue: false },
1528
+ returnNull: false,
1529
+ returnEmptyString: false,
1530
+ initImmediate: false,
1531
+ showSupportNotice: false
1532
+ });
1533
+ if (!i18n.isInitialized) {
1534
+ throw new PanelError("I18N_INIT_FAILED", `i18n failed to initialize for locale: ${locale}`);
1535
+ }
1536
+ return {
1537
+ locale,
1538
+ t(key, params) {
1539
+ if (!i18n.exists(key)) {
1540
+ if (failOnMissingKey()) {
1541
+ throw new PanelError("I18N_MISSING_KEY", `missing i18n key: ${key}`);
1542
+ }
1543
+ return key;
1544
+ }
1545
+ const value = i18n.t(key, params ?? {});
1546
+ if (typeof value !== "string") {
1547
+ throw new PanelError("I18N_INVALID_VALUE", `i18n key did not resolve to a string: ${key}`);
1548
+ }
1549
+ return value;
1550
+ }
1551
+ };
1552
+ }
1553
+ function resolvePanelLocale(value) {
1554
+ if (value === void 0) return "zh-CN";
1555
+ if (!isLocaleId(value)) {
1556
+ throw new PanelError("I18N_INVALID_LOCALE", `unsupported locale: ${String(value)}`);
1557
+ }
1558
+ return value;
1559
+ }
1560
+
1561
+ // src/styles.ts
1562
+ var PANEL_CSS_TAG = "panel";
1563
+ var CSS = [
1564
+ "#mma-host{color:var(--dsw-alias-fg,#111);background:var(--dsw-alias-bg,#f7f7f8);overflow:hidden;}",
1565
+ "#mma-host button,#mma-host input,#mma-host select{color:inherit;font:inherit;}",
1566
+ "#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;}",
1567
+ "#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;}",
1568
+ "#mma-host .mma-tab{display:inline-flex;align-items:center;flex:0 0 auto;max-width:140px;height:32px;padding:0 10px;border:0;border-bottom:2px solid transparent;background:transparent;cursor:pointer;font-size:13px;font-weight:500;color:inherit;opacity:.7;white-space:nowrap;border-radius:8px 8px 0 0;}",
1569
+ "#mma-host .mma-tab[data-active='1']{font-weight:600;opacity:1;border-bottom-color:var(--dsw-alias-primary,#3b82f6);color:var(--dsw-alias-primary,#3b82f6);}",
1570
+ "#mma-host .mma-tab>span{display:inline-flex;align-items:center;gap:5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
1571
+ "#mma-host .mma-tab-x{margin-left:6px;border:0;background:transparent;cursor:pointer;opacity:.55;color:inherit;padding:0 2px;line-height:1;font-size:14px;flex:0 0 auto;}",
1572
+ "#mma-host [hidden]{display:none !important;}",
1573
+ "#mma-host .mma-iconbtn{width:32px;height:32px;border:0;border-radius:8px;background:transparent;cursor:pointer;color:inherit;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;}",
1574
+ "#mma-host .mma-iconbtn:hover{background:var(--dsw-alias-muted,#f3f4f6);}",
1575
+ "#mma-host .mma-toolbar{display:flex;align-items:center;gap:2px;flex:0 0 auto;}",
1576
+ "#mma-host .mma-toolbar .mma-iconbtn{width:30px;height:30px;}",
1577
+ "#mma-host .mma-toolbar .mma-ico{display:block;}",
1578
+ "#mma-host .mma-theme-wrap{position:relative;flex:0 0 auto;}",
1579
+ "#mma-host .mma-pop-scrim{position:fixed;inset:0;z-index:7;background:transparent;}",
1580
+ "#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));}",
1581
+ "#mma-host .mma-pop[data-open='1']{display:block;}",
1582
+ "#mma-host .mma-pop-seg{display:flex;gap:4px;margin:0 0 12px;padding:3px;border-radius:9px;background:var(--dsw-alias-muted,#f3f4f6);}",
1583
+ "#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;}",
1584
+ "#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);}",
1585
+ "#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;}",
1586
+ "#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;}",
1587
+ "#mma-host .mma-swatch:hover{background:var(--dsw-alias-accent,var(--dsw-alias-muted,#f3f4f6));}",
1588
+ "#mma-host .mma-swatch[data-on='1']{background:var(--dsw-alias-accent,var(--dsw-alias-muted,#f3f4f6));font-weight:600;}",
1589
+ "#mma-host .mma-dot{display:inline-block;width:14px;height:14px;border-radius:99px;border:1px solid var(--dsw-alias-border,#e5e7eb);flex:0 0 14px;box-shadow:inset 0 0 0 1px rgba(255,255,255,.25);}",
1590
+ "#mma-host .mma-custom-badge{margin-left:auto;font-size:10px;padding:1px 6px;border-radius:99px;background:var(--dsw-alias-muted,#f3f4f6);color:var(--dsw-alias-fg,#111);opacity:.7;flex:0 0 auto;}",
1591
+ "#mma-host .mma-swatch span{overflow:hidden;text-overflow:ellipsis;}",
1592
+ "#mma-host .mma-swatch{border-bottom:1px solid var(--dsw-alias-border,#e5e7eb);}",
1593
+ "#mma-host .mma-swatch:last-child{border-bottom:none;}",
1594
+ "#mma-host .mma-swatch + .mma-swatch{border-top:0;}",
1595
+ "#mma-host .mma-swatch:hover{border-radius:8px;}",
1596
+ "#mma-host .mma-scope-seg{margin-top:0;padding:4px 5px 5px;border-top:1px solid var(--dsw-alias-border,#e5e7eb);}",
1597
+ "#mma-host .mma-scope-seg button:disabled{opacity:.45;cursor:not-allowed;}",
1598
+ "#mma-host .mma-textbtn{height:28px;padding:0 8px;border:0;border-radius:6px;background:transparent;cursor:pointer;color:inherit;opacity:.7;font-size:12px;}",
1599
+ "#mma-host .mma-textbtn:hover{opacity:1;background:var(--dsw-alias-muted,#f3f4f6);}",
1600
+ "#mma-host .mma-toolbar .mma-delete-btn{font-size:13px;font-weight:400;color:inherit;opacity:.8;}",
1601
+ "#mma-host .mma-toolbar .mma-delete-btn:hover{opacity:1;color:#dc2626;background:transparent;}",
1602
+ "#mma-host .mma-textbtn.danger:hover{color:#dc2626;}",
1603
+ "#mma-host .mma-stage{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden;position:relative;}",
1604
+ "#mma-host .mma-list,#mma-host .mma-frames{flex:1;min-height:0;overflow:auto;}",
1605
+ "#mma-host .mma-frames{display:none;position:relative;}",
1606
+ "#mma-host .mma-frame{position:absolute;inset:0;display:none;flex-direction:column;}",
1607
+ "#mma-host .mma-frame>iframe{flex:1;height:100%;min-height:0;width:100%;border:0;background:transparent;display:block;}",
1608
+ "#mma-host .mma-list-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;padding:18px 18px 8px;}",
1609
+ "#mma-host .mma-list-head h2{margin:0;font-size:16px;font-weight:650;}",
1610
+ "#mma-host .mma-list-head span{font-size:12px;opacity:.5;}",
1611
+ "#mma-host .mma-search{margin:0 18px 12px;position:relative;}",
1612
+ "#mma-host .mma-search input{width:100%;height:34px;box-sizing:border-box;padding:0 12px 0 32px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:8px;background:var(--dsw-alias-surface,#fff);color:inherit;font-size:13px;outline:none;}",
1613
+ "#mma-host .mma-search input:focus{border-color:var(--dsw-alias-primary,#3b82f6);}",
1614
+ "#mma-host .mma-search svg{position:absolute;left:10px;top:9px;opacity:.45;pointer-events:none;}",
1615
+ "#mma-host .mma-grid{padding:8px 16px 20px;display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px;}",
1616
+ "#mma-host[data-dock='side'] .mma-grid{grid-template-columns:minmax(0,1fr);}",
1617
+ "#mma-host .mma-card,#mma-host .mma-row{text-align:left;border-radius:13px;border:1px solid var(--dsw-alias-border,#e5e7eb);background:var(--dsw-alias-surface,#fff);color:var(--dsw-alias-fg,#111);cursor:pointer;transition:border-color .16s ease,box-shadow .16s ease,transform .16s ease,background-color .22s ease;}",
1618
+ "#mma-host .mma-card{display:flex;flex-direction:column;align-items:flex-start;gap:0;padding:16px 15px 13px;position:relative;overflow:hidden;}",
1619
+ "#mma-host .mma-row{display:flex;align-items:center;gap:12px;padding:10px 12px;position:relative;overflow:hidden;width:100%;}",
1620
+ "#mma-host .mma-card h3,#mma-host .mma-row .mma-t{font-size:14px;font-weight:650;margin:0;letter-spacing:.1px;position:relative;z-index:1;}",
1621
+ "#mma-host .mma-card p,#mma-host .mma-row small{font-size:12px;color:var(--muted-foreground,inherit);opacity:.85;line-height:1.45;}",
1622
+ "#mma-host .mma-card p{margin:0;position:relative;z-index:1;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}",
1623
+ "#mma-host .mma-row .mma-twrap{flex:1;min-width:0;position:relative;z-index:1;}",
1624
+ "#mma-host .mma-row small{display:block;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}",
1625
+ "#mma-host .mma-row .mma-right{margin-left:auto;display:flex;align-items:center;gap:7px;flex:0 0 auto;position:relative;z-index:1;}",
1626
+ "#mma-host .mma-chev{color:var(--muted-foreground,inherit);opacity:.5;width:14px;height:14px;}",
1627
+ "#mma-host .mma-meta{display:flex;align-items:center;gap:10px;margin-top:9px;position:relative;z-index:1;}",
1628
+ "#mma-host .mma-ver{font-size:10.5px;color:var(--muted-foreground,inherit);opacity:.8;letter-spacing:.2px;white-space:nowrap;}",
1629
+ "#mma-host .mma-open{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:600;color:var(--dsw-alias-primary,#3b82f6);white-space:nowrap;}",
1630
+ "#mma-host .mma-open i{width:6px;height:6px;border-radius:99px;background:var(--dsw-alias-primary,#3b82f6);}",
1631
+ "#mma-host[data-cardstyle='hero'] .mma-card::before,#mma-host[data-cardstyle='hero'] .mma-row::before{content:'';position:absolute;top:-28px;right:-20px;width:140px;height:120px;background:radial-gradient(62% 62% at 62% 40%,hsl(var(--h,215) 80% 60% / .20),transparent 72%);pointer-events:none;}",
1632
+ "#mma-host[data-cardstyle='hero'] .mma-card:hover,#mma-host[data-cardstyle='hero'] .mma-row:hover{background:linear-gradient(hsl(var(--h,215) 82% 60% / .10),hsl(var(--h,215) 82% 60% / .10)),var(--dsw-alias-surface,#fff);border-color:hsl(var(--h,215) 70% 60% / .5);box-shadow:0 8px 22px var(--dsw-alias-shadow,rgba(0,0,0,.12));transform:translateY(-1px);}",
1633
+ "#mma-host[data-cardstyle='hero'] .mma-mono{font-size:52px;font-weight:800;letter-spacing:2px;line-height:1.05;position:relative;z-index:1;margin-bottom:11px;color:hsl(var(--h,215) 60% 40%);}",
1634
+ "@supports (-webkit-background-clip:text){#mma-host[data-cardstyle='hero'] .mma-mono{background:linear-gradient(135deg,hsl(var(--h,215) 72% 42%),hsl(var(--h,215) 72% 62%));-webkit-background-clip:text;background-clip:text;color:transparent;}}",
1635
+ "#mma-host[data-cardstyle='etch'] .mma-etch{font-size:46px;font-weight:800;letter-spacing:2px;line-height:1.02;color:transparent;-webkit-text-stroke:1.5px hsl(var(--h,215) 65% 45%);margin-bottom:10px;position:relative;z-index:1;}",
1636
+ "#mma-host[data-cardstyle='etch'] .mma-card:hover,#mma-host[data-cardstyle='etch'] .mma-row:hover{background:linear-gradient(hsl(var(--h,215) 80% 55% / .08),hsl(var(--h,215) 80% 55% / .08)),var(--dsw-alias-surface,#fff);border-color:hsl(var(--h,215) 65% 50% / .55);box-shadow:0 8px 22px var(--dsw-alias-shadow,rgba(0,0,0,.12));transform:translateY(-1px);}",
1637
+ "#mma-host[data-cardstyle='etch'] .mma-card:hover .mma-etch,#mma-host[data-cardstyle='etch'] .mma-row:hover .mma-etch{background:linear-gradient(135deg,hsl(var(--h,215) 72% 42%),hsl(var(--h,215) 72% 62%));-webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-stroke:0;}",
1638
+ "#mma-host[data-cardstyle='stamp'] .mma-stamp{position:absolute;top:13px;right:13px;width:44px;height:44px;border:1.5px solid var(--dsw-alias-fg,#111);border-radius:9px;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:800;letter-spacing:1px;color:var(--dsw-alias-fg,#111);background:transparent;transition:background-color .18s ease,color .18s ease,transform .18s ease;}",
1639
+ "#mma-host[data-cardstyle='stamp'] .mma-card:hover .mma-stamp,#mma-host[data-cardstyle='stamp'] .mma-row:hover .mma-stamp{background:var(--dsw-alias-fg,#111);color:var(--dsw-alias-surface,#fff);transform:scale(1.05);}",
1640
+ "#mma-host[data-cardstyle='stamp'] .mma-card:hover,#mma-host[data-cardstyle='stamp'] .mma-row:hover{background:linear-gradient(hsl(var(--h,215) 30% 55% / .07),hsl(var(--h,215) 30% 55% / .07)),var(--dsw-alias-surface,#fff);border-color:var(--dsw-alias-fg,#111);box-shadow:0 8px 22px var(--dsw-alias-shadow,rgba(0,0,0,.12));transform:translateY(-1px);}",
1641
+ "#mma-host[data-cardstyle='stamp'] .mma-card h3,#mma-host[data-cardstyle='stamp'] .mma-card p{padding-right:52px;}",
1642
+ "#mma-host[data-cardstyle='stamp'] .mma-row .mma-stamp{position:static;width:36px;height:36px;font-size:12px;border-radius:8px;flex:0 0 36px;}",
1643
+ "#mma-host .mma-open-dot{width:7px;height:7px;border-radius:99px;background:var(--dsw-alias-primary,#3b82f6);display:inline-block;}",
1644
+ "#mma-host .mma-empty{padding:24px;opacity:.75;line-height:1.6;}",
1645
+ "#mma-host .mma-error{padding:24px;color:#b91c1c;}",
1646
+ ".mma-load{flex:1;min-height:180px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;color:var(--dsw-alias-fg,#111);}",
1647
+ ".mma-load-art{position:relative;width:88px;height:72px;}",
1648
+ ".mma-load-art svg{display:block;width:88px;height:64px;}",
1649
+ ".mma-load-dots{display:flex;gap:5px;justify-content:center;margin-top:2px;}",
1650
+ ".mma-load-dots i{width:6px;height:6px;border-radius:50%;background:var(--dsw-alias-primary,#3b82f6);opacity:.35;animation:mma-dot 1s ease-in-out infinite;}",
1651
+ ".mma-load-dots i:nth-child(2){animation-delay:.15s;}",
1652
+ ".mma-load-dots i:nth-child(3){animation-delay:.3s;}",
1653
+ "@keyframes mma-dot{0%,80%,100%{transform:translateY(0);opacity:.3}40%{transform:translateY(-5px);opacity:1}}",
1654
+ ".mma-iframe-wrap{position:relative;flex:1 1 auto;min-height:0;height:100%;display:flex;flex-direction:column;}",
1655
+ ".mma-iframe-wrap .mma-load,#mma-host .mma-frame .mma-load{position:absolute;inset:0;background:var(--dsw-alias-bg,#f7f7f8);z-index:1;}",
1656
+ "#mma-host .mma-modal{position:absolute;inset:0;background:rgba(0,0,0,.35);z-index:5;display:flex;align-items:center;justify-content:center;padding:24px;}",
1657
+ "#mma-host .mma-modal[hidden]{display:none;}",
1658
+ "#mma-host .mma-dialog{width:min(360px,100%);background:var(--dsw-alias-surface,#fff);color:var(--dsw-alias-fg,#111);border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:var(--radius,12px);padding:18px;box-shadow:0 12px 40px var(--dsw-alias-shadow,rgba(0,0,0,.18));}",
1659
+ "#mma-host .mma-dialog h3{margin:0 0 8px;font-size:15px;}",
1660
+ "#mma-host .mma-dialog p{margin:0 0 16px;font-size:13px;opacity:.75;line-height:1.5;}",
1661
+ "#mma-host .mma-dialog-actions{display:flex;justify-content:flex-end;gap:8px;}",
1662
+ "#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;}",
1663
+ "#mma-host .mma-dialog-actions .go{background:#dc2626;color:#fff;border-color:#dc2626;}",
1664
+ "#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;}",
1665
+ "#mma-host .mma-settings[data-open='1']{display:block;}",
1666
+ "#mma-host .mma-settings h3{margin:0 0 6px;font-size:16px;}",
1667
+ "#mma-host .mma-settings-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px;}",
1668
+ "#mma-host .mma-settings label{display:block;margin-bottom:12px;font-size:12px;opacity:.75;}",
1669
+ "#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;}",
1670
+ "#mma-host .mma-settings-actions{display:flex;flex-direction:column;align-items:flex-end;gap:8px;margin-top:18px;}",
1671
+ "#mma-host .mma-settings-msg{font-size:12px;opacity:.75;line-height:1.4;text-align:right;width:100%;}",
1672
+ "#mma-host .mma-settings-msg.err{color:#dc2626;}",
1673
+ "#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;}",
1674
+ "#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);}",
1675
+ "#mma-host .mma-browse{position:absolute;inset:0;background:var(--dsw-alias-bg,#f7f7f8);z-index:3;overflow:auto;padding:16px 18px;display:none;}",
1676
+ "#mma-host .mma-browse[data-open='1']{display:block;}",
1677
+ "#mma-host .mma-browse-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:12px;}",
1678
+ "#mma-host .mma-browse-head--tools{justify-content:flex-start;margin-bottom:8px;}",
1679
+ "#mma-host .mma-browse-head h3{margin:0;font-size:15px;}",
1680
+ "#mma-host .mma-browse-head .sub{margin-left:8px;font-size:12px;opacity:.55;font-weight:400;}",
1681
+ "#mma-host .mma-btns button{border:0;background:transparent;cursor:pointer;color:inherit;opacity:.7;font-size:12px;padding:4px 6px;}",
1682
+ "#mma-host .mma-commit-meta{margin:0 0 12px;}",
1683
+ "#mma-host .mma-commit-msg{margin:0 0 6px;font-size:13px;font-weight:600;line-height:1.45;word-break:break-word;}",
1684
+ "#mma-host .mma-commit-msg--clamp2{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}",
1685
+ "#mma-host .mma-commit-msg--clamp3{display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;}",
1686
+ "#mma-host .mma-commit-msg-toggle{border:0;background:transparent;cursor:pointer;color:inherit;opacity:.65;font-size:12px;padding:0 0 6px;}",
1687
+ "#mma-host .mma-commit-msg-toggle:hover{opacity:1;}",
1688
+ "#mma-host .mma-commit-ids{display:flex;gap:10px;flex-wrap:wrap;font-size:11px;opacity:.75;}",
1689
+ "#mma-host .mma-blist{display:flex;flex-direction:column;gap:4px;}",
1690
+ "#mma-host .mma-bitem{text-align:left;padding:10px 12px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:10px;background:var(--dsw-alias-surface,#fff);cursor:pointer;}",
1691
+ "#mma-host .mma-bitem b,#mma-host .mma-bitem .mma-commit-msg{font-size:13px;display:block;margin-bottom:4px;font-weight:600;}",
1692
+ "#mma-host .mma-bitem .meta{display:flex;gap:10px;font-size:11px;opacity:.65;flex-wrap:wrap;}",
1693
+ "#mma-host .mma-plus{color:#16a34a;font-variant-numeric:tabular-nums;}",
1694
+ "#mma-host .mma-minus{color:#dc2626;font-variant-numeric:tabular-nums;}",
1695
+ "#mma-host .mma-diff-zero{opacity:.35;}",
1696
+ "#mma-host .mma-files{display:flex;flex-direction:column;gap:6px;}",
1697
+ "#mma-host .mma-fitem{display:flex;align-items:center;gap:10px;width:100%;box-sizing:border-box;padding:10px 12px;border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:10px;background:var(--dsw-alias-surface,#fff);cursor:pointer;font-size:12px;color:inherit;text-align:left;}",
1698
+ "#mma-host .mma-fitem:hover{background:var(--dsw-alias-muted,#f3f4f6);border-color:var(--dsw-alias-border,#d1d5db);}",
1699
+ "#mma-host .mma-fitem[data-open='1']{border-color:var(--dsw-alias-primary,#2563eb);background:var(--dsw-alias-muted,#f3f4f6);}",
1700
+ "#mma-host .mma-fitem .mma-diff{display:inline-flex;gap:6px;flex:0 0 auto;min-width:4.5em;font-family:ui-monospace,monospace;font-size:11px;}",
1701
+ "#mma-host .mma-fitem .p{flex:1;min-width:0;font-family:ui-monospace,monospace;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}",
1702
+ "#mma-host .mma-fitem .mma-chevron{flex:0 0 12px;opacity:.45;transition:transform .15s ease;}",
1703
+ "#mma-host .mma-fitem[data-open='1'] .mma-chevron{transform:rotate(90deg);opacity:.7;}",
1704
+ "#mma-host .mma-preview{margin:0 0 2px;padding:8px 0;background:var(--dsw-alias-surface,#fff);border:1px solid var(--dsw-alias-border,#e5e7eb);border-radius:8px;font-size:11px;max-height:220px;overflow:auto;white-space:pre;line-height:1.55;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;}",
1705
+ "#mma-host .mma-diff-preview{padding:6px 0;}",
1706
+ "#mma-host .mma-diff-line{display:block;padding:0 12px;}",
1707
+ "#mma-host .mma-diff-line--add{color:#166534;background:rgba(22,163,74,.12);}",
1708
+ "#mma-host .mma-diff-line--del{color:#991b1b;background:rgba(220,38,38,.10);}",
1709
+ "#mma-host .mma-diff-line--ctx{color:inherit;opacity:.85;}",
1710
+ "#mma-host .mma-bempty{padding:20px;opacity:.6;text-align:center;font-size:13px;}",
1711
+ "#mma-host .mma-berr{padding:16px;color:#b91c1c;font-size:13px;}"
1712
+ ];
1713
+ function injectPanelCss() {
1714
+ if (typeof document === "undefined") return;
1715
+ let tag = document.querySelector(`style[data-plugin-css="${PANEL_CSS_TAG}"]`);
1716
+ if (!tag) {
1717
+ tag = document.createElement("style");
1718
+ tag.dataset.plugin = "panel";
1719
+ tag.dataset.pluginCss = PANEL_CSS_TAG;
1720
+ document.head.appendChild(tag);
1721
+ }
1722
+ tag.textContent = CSS.join("\n");
1723
+ }
1724
+ function loadCustomPalettes(host) {
1725
+ if (!host.palettes) return;
1726
+ host.palettes().then((list) => {
1727
+ const custom = {};
1728
+ for (const p of list) {
1729
+ custom[p.id] = { label: p.label, swatch: p.swatch, tokens: p.tokens };
1730
+ }
1731
+ setPanelState({ customPalettes: custom });
1732
+ }).catch(() => {
1733
+ });
1734
+ }
1735
+ function createMiniAppPanel(host, options) {
1736
+ const locale = resolvePanelLocale(options?.locale ?? host.locale);
1737
+ const i18n = createPanelI18n(locale);
1738
+ resetPanelState({
1739
+ tabs: [{ id: "all", title: i18n.t("tabs.all"), kind: "all" }],
1740
+ capabilities: capabilitiesOf(host),
1741
+ locale,
1742
+ emptyText: host.emptyText
1743
+ });
1744
+ let rootEl = null;
1745
+ let root = null;
1746
+ const actions = createPanelActions(host, () => rootEl, i18n);
1747
+ return {
1748
+ actions,
1749
+ open: () => host.openPanel(),
1750
+ close: () => host.closePanel(),
1751
+ mount(el) {
1752
+ rootEl = el;
1753
+ injectPanelCss();
1754
+ const s = getPanelState();
1755
+ applyThemeTo(el, s.theme, s.palette, s.customPalettes);
1756
+ loadCustomPalettes(host);
1757
+ if (!root) root = createRoot(el);
1758
+ root.render(
1759
+ /* @__PURE__ */ jsx(PanelProvider, { actions, i18n, children: /* @__PURE__ */ jsx(MiniAppPanel, {}) })
1760
+ );
1761
+ },
1762
+ unmount() {
1763
+ if (root) {
1764
+ root.unmount();
1765
+ root = null;
1766
+ }
1767
+ rootEl = null;
1768
+ }
1769
+ };
1770
+ }
1771
+
1772
+ // src/host-shell.ts
1773
+ function safeStorage(storage) {
1774
+ if (storage) return storage;
1775
+ try {
1776
+ if (typeof window !== "undefined") return window.localStorage;
1777
+ } catch {
1778
+ }
1779
+ return null;
1780
+ }
1781
+ function readThemePrefs(storage) {
1782
+ const get = (k) => {
1783
+ try {
1784
+ return storage?.getItem(k) ?? null;
1785
+ } catch {
1786
+ return null;
1787
+ }
1788
+ };
1789
+ return { theme: get("mma-theme-mode") || "light", palette: get("mma-palette") || "default" };
1790
+ }
1791
+ function createHostShell(opts) {
1792
+ const storage = safeStorage(opts.storage);
1793
+ const themePrefs = readThemePrefs(storage);
1794
+ const frame = createFrameController({
1795
+ urlOf: (appId) => appFrameUrl(opts.hostUrl, appId, envFor(appId)),
1796
+ envOf: (appId) => envFor(appId)
1797
+ });
1798
+ function envFor(appId) {
1799
+ const s = getPanelState();
1800
+ const app = s.apps.find((a) => a.id === appId);
1801
+ return { theme: app?.theme?.theme || s.theme, palette: app?.theme?.palette || s.palette, dock: s.dock };
1802
+ }
1803
+ const host = createRestPanelHost({
1804
+ hostUrl: opts.hostUrl,
1805
+ storage,
1806
+ cardStyle: opts.cardStyle,
1807
+ locale: opts.locale ?? void 0,
1808
+ emptyText: opts.emptyText,
1809
+ onOpen: () => optOnOpen(),
1810
+ onClose: () => optOnClose(),
1811
+ onHostChange: (next) => opts.onHostChange?.(next),
1812
+ frameController: {
1813
+ url: (appId) => frame.url(appId),
1814
+ // Lazy-bind the frames container (React may not have rendered #mma-frames yet).
1815
+ mount: (appId) => {
1816
+ ensureFrameContainer();
1817
+ frame.mount(appId);
1818
+ },
1819
+ unmount: (appId) => frame.unmount(appId),
1820
+ reload: (appId) => {
1821
+ ensureFrameContainer();
1822
+ frame.reload(appId);
1823
+ },
1824
+ syncEnv: () => frame.postEnvAll()
1825
+ }
1826
+ });
1827
+ let framesBound = false;
1828
+ function ensureFrameContainer() {
1829
+ if (framesBound) return;
1830
+ const el = containerEl?.querySelector("#mma-frames") ?? document.getElementById("mma-frames");
1831
+ if (el) {
1832
+ frame.setContainer(el);
1833
+ framesBound = true;
1834
+ }
1835
+ }
1836
+ let panel = null;
1837
+ let containerEl = null;
1838
+ function optOnOpen() {
1839
+ setPanelState({ visible: true });
1840
+ opts.onOpen?.();
1841
+ }
1842
+ function optOnClose() {
1843
+ setPanelState({ visible: false });
1844
+ opts.onClose?.();
1845
+ }
1846
+ function persistTheme(theme, palette) {
1847
+ setPanelState({ theme, palette });
1848
+ try {
1849
+ storage?.setItem("mma-theme-mode", theme);
1850
+ storage?.setItem("mma-palette", palette);
1851
+ } catch {
1852
+ }
1853
+ if (containerEl) applyThemeTo(containerEl, theme, palette);
1854
+ frame.postEnvAll();
1855
+ }
1856
+ return {
1857
+ host,
1858
+ frames: frame,
1859
+ get panel() {
1860
+ if (!panel) panel = createMiniAppPanel(host);
1861
+ return panel;
1862
+ },
1863
+ mount(el) {
1864
+ containerEl = document.createElement("div");
1865
+ containerEl.id = "mma-host";
1866
+ containerEl.setAttribute("data-ready", "1");
1867
+ containerEl.setAttribute("data-dock", getPanelState().dock);
1868
+ containerEl.setAttribute("data-cardstyle", getPanelState().cardStyle);
1869
+ Object.assign(containerEl.style, {
1870
+ position: "fixed",
1871
+ top: "0",
1872
+ left: "0",
1873
+ right: "0",
1874
+ bottom: "0",
1875
+ zIndex: "40",
1876
+ display: "flex",
1877
+ flexDirection: "column",
1878
+ background: "var(--dsw-alias-bg, #f7f7f8)",
1879
+ color: "var(--dsw-alias-fg, #111)",
1880
+ fontFamily: "ui-sans-serif, system-ui, -apple-system, sans-serif"
1881
+ });
1882
+ const target = el ?? document.body;
1883
+ target.appendChild(containerEl);
1884
+ setPanelState({ theme: themePrefs.theme, palette: themePrefs.palette, visible: true });
1885
+ applyThemeTo(containerEl, themePrefs.theme, themePrefs.palette);
1886
+ this.panel.mount(containerEl);
1887
+ const framesEl = containerEl.querySelector("#mma-frames");
1888
+ if (framesEl) frame.setContainer(framesEl);
1889
+ void this.panel.actions.fetchApps();
1890
+ },
1891
+ unmount() {
1892
+ panel?.unmount();
1893
+ panel = null;
1894
+ frame.unmountAll();
1895
+ containerEl?.parentNode?.removeChild(containerEl);
1896
+ containerEl = null;
1897
+ },
1898
+ openPanel: optOnOpen,
1899
+ closePanel: optOnClose,
1900
+ toggle() {
1901
+ if (getPanelState().visible) optOnClose();
1902
+ else optOnOpen();
1903
+ },
1904
+ persistTheme,
1905
+ setCardStyle(v) {
1906
+ setPanelState({ cardStyle: v });
1907
+ try {
1908
+ storage?.setItem("mma-card-style", v);
1909
+ } catch {
1910
+ }
1911
+ containerEl?.setAttribute("data-cardstyle", v);
1912
+ },
1913
+ setDock(v) {
1914
+ setPanelState({ dock: v });
1915
+ opts.layout?.setDock?.(v);
1916
+ }
1917
+ };
1918
+ }
1919
+
1920
+ // src/index.ts
1921
+ var packageName = "@monkey-mini-app/panel";
1922
+
1923
+ export { AppList, Browse, HostUnreachableError, LOCALE_IDS, ListRegion, Loading, MiniAppPanel, Modal, PANEL_CSS_TAG, PanelError, PanelProvider, Settings, Tabs, ThemePop, Toolbar, activeAppFrom, appBlurb, appFrameUrl, capabilitiesOf, clampPaletteId, createFrameController, createHostShell, createMiniAppPanel, createPanelActions, createPanelI18n, createRestPanelHost, defaultHideThemePop, formToHostConfigBody, getPanelState, hostConfigToForm, hue, injectPanelCss, isHostUnreachable, monoOf, packageName, parseAppsResponse, parseCommitDetail, parseCommitList, parsePalettes, parseStorageTables, readJson, resetPanelState, resolvePanelLocale, setPanelState, subscribePanel, usePanelActions, usePanelI18n, usePanelState };