@mokuyoaxis/dsh-iris 0.2.7 → 0.3.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/CHANGELOG.md +81 -1
  2. package/README.en.md +103 -312
  3. package/README.md +100 -310
  4. package/bin/dsh-iris.js +333 -65
  5. package/docs/AGENT_CORE_QUICKSTART.md +64 -0
  6. package/docs/AGENT_QUICKSTART.md +2 -0
  7. package/docs/ARCHITECTURE.md +13 -3
  8. package/docs/AUDIO_FLOWS.md +71 -0
  9. package/docs/CONTACT_SHEET.md +62 -0
  10. package/docs/CORE_CONNECTIONS.md +99 -0
  11. package/docs/CORE_QUICKSTART.md +108 -0
  12. package/docs/CORE_RUNTIME_CONTRACT.md +7 -2
  13. package/docs/CORE_SERVICE.md +97 -0
  14. package/docs/CORE_SKILLS.md +35 -3
  15. package/docs/DSH_COMPATIBILITY.md +10 -0
  16. package/docs/DSH_RC2_ADAPTATION.md +1 -1
  17. package/docs/DSH_RUNTIME.md +6 -2
  18. package/docs/HEADLESS_CLI.md +2 -2
  19. package/docs/IMAGE_FLOW.md +4 -0
  20. package/docs/IMAGE_GENERATION.md +127 -0
  21. package/docs/LOCAL_API.md +4 -2
  22. package/docs/LOCAL_API_CONTRACT.md +39 -14
  23. package/docs/LOCAL_CREATION.md +7 -1
  24. package/docs/LOCAL_OPERATIONS.md +46 -0
  25. package/docs/LOCAL_SELECTION.md +57 -0
  26. package/docs/LOCAL_WEB.md +201 -0
  27. package/docs/MCP.md +191 -0
  28. package/docs/MUSIC.md +2 -0
  29. package/docs/PANEL_EXTENSIONS.md +64 -0
  30. package/docs/PLUGINS.md +189 -0
  31. package/docs/PLUGIN_STARTUP.md +100 -0
  32. package/docs/PROVIDER_ADAPTER_CONTRACT.md +1 -1
  33. package/docs/PROVIDER_EXTENSIONS.md +78 -0
  34. package/docs/PROVIDER_HEALTH.md +3 -1
  35. package/docs/PROVIDER_SETTINGS.md +85 -0
  36. package/docs/PROVIDER_SUBMISSION_CONTRACT.md +6 -0
  37. package/docs/ROADMAP.md +17 -7
  38. package/docs/VISION_CLI.md +1 -1
  39. package/docs/WEB_PROTOTYPE.md +21 -6
  40. package/docs/WEB_RECOVERY.md +46 -0
  41. package/docs/WORKBENCH_LOGIN.md +63 -0
  42. package/docs/assets/screenshots/core-connections-preview.webp +0 -0
  43. package/docs/assets/screenshots/core-model-pools-preview.webp +0 -0
  44. package/docs/assets/screenshots/core-overview-preview.webp +0 -0
  45. package/docs/assets/screenshots/core-spectrum-preview.webp +0 -0
  46. package/docs/assets/screenshots/core-studio-dark-preview.webp +0 -0
  47. package/docs/assets/screenshots/core-studio-mobile-preview.webp +0 -0
  48. package/docs/assets/screenshots/core-studio-preview.webp +0 -0
  49. package/docs/releases/0.3.0-alpha.1.md +51 -0
  50. package/docs/screenshots.md +41 -1
  51. package/lib/actions.js +53 -2
  52. package/lib/adapters.js +20 -11
  53. package/lib/application-runtime.js +34 -7
  54. package/lib/audio-flow-input.js +37 -0
  55. package/lib/client.js +346 -53
  56. package/lib/comfyui-adapter.js +94 -0
  57. package/lib/comfyui-client.js +51 -0
  58. package/lib/comfyui-inspection.js +73 -0
  59. package/lib/comfyui-workflow.js +94 -0
  60. package/lib/config.js +36 -1
  61. package/lib/configured-image-runtime.js +50 -0
  62. package/lib/connection-skill.js +89 -0
  63. package/lib/connection-template.js +23 -0
  64. package/lib/core-artifact-store.js +16 -2
  65. package/lib/core-bridge-selection.js +31 -0
  66. package/lib/core-bridge-tools.js +83 -0
  67. package/lib/core-capability-catalog.js +182 -0
  68. package/lib/core-connection-diagnostics.js +49 -0
  69. package/lib/core-connection-store.js +277 -0
  70. package/lib/core-connections.js +225 -0
  71. package/lib/core-maintenance.js +9 -3
  72. package/lib/core-runtime.js +2 -1
  73. package/lib/core-service-client.js +170 -0
  74. package/lib/core-tasks.js +29 -12
  75. package/lib/core-writer-lease.js +16 -0
  76. package/lib/creation-request-store.js +38 -64
  77. package/lib/dsh-host-adapter.js +4 -1
  78. package/lib/dsh-service-bridge.js +66 -0
  79. package/lib/execution-ownership.js +43 -0
  80. package/lib/flow-store.js +105 -10
  81. package/lib/generation-input.js +10 -1
  82. package/lib/headless-vision.js +7 -7
  83. package/lib/image-edit-input.js +1 -1
  84. package/lib/image-flow.js +73 -29
  85. package/lib/index.js +37 -17
  86. package/lib/local-api-projection.js +10 -5
  87. package/lib/local-api-token-directory.js +31 -0
  88. package/lib/local-api.js +769 -67
  89. package/lib/local-creation.js +89 -24
  90. package/lib/local-operations.js +36 -0
  91. package/lib/local-request-root.js +86 -0
  92. package/lib/local-web-assets.js +37 -0
  93. package/lib/mcp-bridge.js +58 -0
  94. package/lib/mcp-check.js +26 -0
  95. package/lib/mcp-config.js +42 -0
  96. package/lib/mcp-delivery.js +46 -0
  97. package/lib/mcp-http.js +101 -0
  98. package/lib/mcp-server.js +47 -0
  99. package/lib/model-directory-service.js +16 -6
  100. package/lib/model-directory.js +3 -3
  101. package/lib/models.js +2 -2
  102. package/lib/music-transport.js +2 -1
  103. package/lib/operation-runner.js +236 -0
  104. package/lib/operation-store.js +216 -0
  105. package/lib/panel-extension.js +36 -0
  106. package/lib/plugin-contract.js +194 -0
  107. package/lib/plugin-execution.js +51 -0
  108. package/lib/plugin-launch.js +86 -0
  109. package/lib/plugin-operation-store.js +101 -0
  110. package/lib/plugin-operations.js +28 -0
  111. package/lib/plugin-registry.js +109 -0
  112. package/lib/plugin-selection.js +15 -0
  113. package/lib/plugin-source-image.js +5 -0
  114. package/lib/plugin-web-assets.js +54 -0
  115. package/lib/plugins.d.ts +288 -0
  116. package/lib/plugins.js +58 -0
  117. package/lib/provider-adapter.js +5 -1
  118. package/lib/provider-adapters.js +56 -18
  119. package/lib/provider-catalog.js +40 -29
  120. package/lib/provider-config-service.js +105 -23
  121. package/lib/provider-diagnostics.js +56 -0
  122. package/lib/provider-extensions.js +29 -0
  123. package/lib/provider-health.js +8 -1
  124. package/lib/provider-monitor.js +79 -0
  125. package/lib/provider-protocol.js +70 -20
  126. package/lib/provider-registry.js +118 -0
  127. package/lib/provider-response.js +28 -0
  128. package/lib/provider-settings.js +134 -0
  129. package/lib/provider-task-runner.js +22 -9
  130. package/lib/skill-installation.js +23 -11
  131. package/lib/vision-model-routing.js +3 -0
  132. package/lib/web/app.js +642 -0
  133. package/lib/web/appearance.js +111 -0
  134. package/lib/web/comfyui.js +146 -0
  135. package/lib/web/connections.js +377 -0
  136. package/lib/web/creation.js +355 -0
  137. package/lib/web/flow-view.js +33 -0
  138. package/lib/web/gallery.js +132 -0
  139. package/lib/web/index.html +291 -0
  140. package/lib/web/media-client.js +204 -0
  141. package/lib/web/plugin-panel.js +221 -0
  142. package/lib/web/plugin-surface.css +17 -0
  143. package/lib/web/providers.js +492 -0
  144. package/lib/web/recovery.js +105 -0
  145. package/lib/web/startup.js +18 -0
  146. package/lib/web/styles.css +518 -0
  147. package/lib/web/transport.js +96 -0
  148. package/lib/web/workbench-login.js +165 -0
  149. package/lib/workbench-auth-cli.js +47 -0
  150. package/lib/workbench-auth.js +200 -0
  151. package/package.json +20 -5
  152. package/plugins/contact-sheet/index.d.mts +4 -0
  153. package/plugins/contact-sheet/index.mjs +33 -0
  154. package/plugins/contact-sheet/web/board.css +14 -0
  155. package/plugins/contact-sheet/web/board.mjs +93 -0
  156. package/skills/iris-core/SKILL.md +8 -4
  157. package/skills/iris-core/references/cli.md +2 -2
  158. package/skills/iris-core/references/comfyui.md +69 -0
  159. package/skills/iris-core/references/tasks.md +2 -0
  160. package/user_guide.md +5 -1
@@ -0,0 +1,221 @@
1
+ const $ = selector => document.querySelector(selector);
2
+ const node = (tag, text, className) => { const item = document.createElement(tag); if (text) item.textContent = text; if (className) item.className = className; return item; };
3
+ const freeze = value => { if (value && typeof value === 'object') { Object.values(value).forEach(freeze); Object.freeze(value); } return value; };
4
+ const ARTIFACT = /^artifact_[a-f0-9]{24}$/, RUN = /^run_[a-f0-9]{24}$/;
5
+ const MEDIA = { 'image/png': 'content', 'image/jpeg': 'content', 'image/webp': 'content',
6
+ 'audio/wav': 'audio', 'audio/mpeg': 'audio', 'audio/ogg': 'audio', 'audio/flac': 'audio', 'text/plain': 'text' };
7
+
8
+ /** Trusted same-origin modules. Shadow DOM isolates styles, not permissions or JavaScript. */
9
+ export function createPluginPanel({ navigate, describe, onProvidersSaved }) {
10
+ let client, session, manifest, page = '', artifacts = [], currentArtifact, active = new Map();
11
+ const themeListeners = new Set();
12
+ const themeValue = () => Object.freeze({ mode: document.documentElement.dataset.theme || 'light',
13
+ density: document.documentElement.dataset.density || 'comfortable', accent: 'iris' });
14
+ const themeObserver = new MutationObserver(() => {
15
+ for (const listener of themeListeners) listener(themeValue());
16
+ });
17
+ function report(item, stage, error, target = $('#plugin-feedback')) {
18
+ const message = item.id + ' · ' + stage + ' · ' + describe(error);
19
+ target.textContent = message; target.hidden = false; target.classList.add('error');
20
+ target.dataset.code = 'IRIS_PLUGIN_WEB_' + stage.toUpperCase();
21
+ }
22
+ function stop(slot) { const previous = active.get(slot); active.delete(slot); previous?.(); }
23
+ function stopAll() { for (const slot of [...active.keys()]) stop(slot); }
24
+ function mount(slot, item, extra = {}) {
25
+ stop(slot); slot.replaceChildren();
26
+ const lifetime = new AbortController(), signal = lifetime.signal, http = client;
27
+ const host = node('div', '', 'plugin-surface'); host.dataset.pluginContribution = item.id;
28
+ const status = node('p', '正在加载…', 'field-note'); status.setAttribute('role', 'status');
29
+ slot.append(host, status);
30
+ const shadow = host.attachShadow({ mode: 'open' }), container = node('div', '', 'plugin-content');
31
+ const unsubscriptions = new Set();
32
+ let cleanup, settled = false, released = false;
33
+ async function release() {
34
+ if (released || !settled) return; released = true;
35
+ try { await cleanup?.(); } catch (error) { report(item, 'cleanup', error); }
36
+ }
37
+ function close() {
38
+ signal.aborted || lifetime.abort();
39
+ for (const unsubscribe of unsubscriptions) unsubscribe();
40
+ host.remove(); status.remove(); void release();
41
+ }
42
+ active.set(slot, close);
43
+ function live() { signal.throwIfAborted(); if (http !== client) throw new DOMException('登录上下文已关闭', 'AbortError'); }
44
+ function options(value = {}) { live(); return { ...value, signal: AbortSignal.any([signal, ...(value.signal ? [value.signal] : [])]) }; }
45
+ const read = (path, value) => http.get(path, options(value));
46
+ const api = Object.freeze({
47
+ ...(session.pluginExecution ? { pluginOperations: Object.freeze({
48
+ catalog: (query = {}, value) => read('/api/v0/plugin-operations/catalog?' + new URLSearchParams(query), value),
49
+ history: value => read('/api/v0/plugin-operations', value),
50
+ execute: (request, value) => http.callPlugin(request, options(value)),
51
+ inspect: (id, value) => http.getOperation(id, options(value)),
52
+ lookup: (key, value) => http.lookupOperation(key, options(value)),
53
+ stop: (id, value) => http.stopOperation(id, options(value))
54
+ }) } : {}),
55
+ runs: (query = {}, value) => read('/api/v0/runs?' + new URLSearchParams(query), value),
56
+ run: (id, value) => { if (typeof id !== 'string' || !RUN.test(id)) throw new TypeError('流程 ID 无效'); return read('/api/v0/runs/' + id, value); },
57
+ models: value => read('/api/v0/models', value),
58
+ audioModels: value => read('/api/v0/audio-models', value),
59
+ artifact: (artifact, value) => {
60
+ if (typeof artifact?.id !== 'string' || !ARTIFACT.test(artifact.id) || !Object.hasOwn(MEDIA, artifact?.mediaType)) throw new TypeError('作品 ID 或媒体类型无效');
61
+ return read('/api/v0/artifacts/' + artifact.id + '/' + MEDIA[artifact.mediaType], { ...value, binary: true });
62
+ },
63
+ ...(session.providerManagement && item.dependency.kind === 'settings' ? { providers: Object.freeze({
64
+ read: value => read('/api/v0/provider-management/settings', value),
65
+ async save(input, value) {
66
+ const result = await http.postProvider('save', input, options(value));
67
+ live(); onProvidersSaved?.(result); return result;
68
+ }
69
+ }) } : {})
70
+ });
71
+ const theme = Object.freeze({ current: themeValue,
72
+ subscribe(listener) {
73
+ live();
74
+ if (typeof listener !== 'function') throw new TypeError('主题监听须为函数');
75
+ const subscription = value => { try { listener(value); } catch (error) { report(item, 'theme', error); } };
76
+ const off = () => { themeListeners.delete(subscription); unsubscriptions.delete(off); };
77
+ themeListeners.add(subscription); unsubscriptions.add(off); return off;
78
+ }
79
+ });
80
+ const context = Object.freeze({ container, signal, dependency: item.dependency, api, session, theme, ...extra });
81
+ let stage = 'styles';
82
+ void (async () => {
83
+ try {
84
+ await Promise.all(['/plugin-surface.css', ...item.styles].map(href => new Promise((resolve, reject) => {
85
+ const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = href;
86
+ const abort = () => reject(signal.reason);
87
+ link.addEventListener('load', () => { signal.removeEventListener('abort', abort); resolve(); }, { once: true });
88
+ link.addEventListener('error', () => { signal.removeEventListener('abort', abort); reject(new Error('样式资源加载失败')); }, { once: true });
89
+ signal.addEventListener('abort', abort, { once: true }); shadow.append(link);
90
+ })));
91
+ live(); stage = 'module';
92
+ const module = await import(item.entry);
93
+ live(); if (typeof module.mount !== 'function') throw new Error('入口须导出 mount(context)');
94
+ shadow.append(container); stage = 'mount';
95
+ cleanup = await module.mount(context);
96
+ if (cleanup !== undefined && typeof cleanup !== 'function') { cleanup = undefined; throw new Error('mount 只能返回释放函数或 undefined'); }
97
+ if (!signal.aborted) status.hidden = true;
98
+ } catch (error) {
99
+ if (!signal.aborted) {
100
+ // A failed mount has no live API/listeners; keep its diagnostic outside its removed host.
101
+ lifetime.abort(); for (const unsubscribe of unsubscriptions) unsubscribe(); host.remove();
102
+ report(item, stage, error, status);
103
+ }
104
+ } finally { settled = true; if (signal.aborted) await release(); }
105
+ })();
106
+ }
107
+ function renderPage() {
108
+ const item = manifest?.pages.find(value => 'plugin:' + value.id === page);
109
+ $('#plugin-page-panel').hidden = !item;
110
+ $('#plugin-settings-panel').hidden = page !== 'plugin-settings';
111
+ $('#plugin-history-panel').hidden = page !== 'plugin-history';
112
+ for (const button of $('#plugin-navigation').querySelectorAll('button')) button.setAttribute('aria-current', button.dataset.page === page ? 'page' : 'false');
113
+ if (item) {
114
+ $('#plugin-page-title').textContent = item.title;
115
+ $('#plugin-page-identity').textContent = item.dependency.pluginId + ' · v' + item.dependency.pluginVersion;
116
+ mount($('#plugin-page-slot'), item);
117
+ }
118
+ if (page === 'plugin-settings') {
119
+ for (const details of $('#plugin-settings-list').children) if (details.open) mount(details.querySelector('.plugin-slot'), manifest.settings.find(value => value.id === details.dataset.contribution));
120
+ }
121
+ if (page === 'plugin-history') showHistory();
122
+ }
123
+ function showHistory() {
124
+ const slot = $('#plugin-history-list'); stop(slot); slot.replaceChildren();
125
+ const controller = new AbortController(), signal = controller.signal, http = client, urls = new Set();
126
+ active.set(slot, () => { controller.abort(); for (const url of urls) URL.revokeObjectURL(url); });
127
+ $('#plugin-history-feedback').textContent = '正在读取保存的业务记录…';
128
+ void http.get('/api/v0/plugin-operations', { signal }).then(value => {
129
+ if (signal.aborted || http !== client) return;
130
+ $('#plugin-history-feedback').textContent = value.operations.length ? '显示最近业务;缺少插件仍可读取原记录。' : '尚无保存的业务记录。';
131
+ for (const operation of value.operations) {
132
+ const article = node('article', '', 'card plugin-history-item'), title = node('h2', operation.operationId);
133
+ const identity = node('p', operation.dependency.pluginId + ' · v' + operation.dependency.pluginVersion, 'field-note');
134
+ const status = node('p', operation.state + ' · ' + operation.id, 'field-note'); article.dataset.operationId = operation.id;
135
+ article.append(title, identity, status);
136
+ for (const id of operation.result?.artifactIds || []) {
137
+ const preview = node('button', '查看原作品', 'secondary'); preview.type = 'button';
138
+ preview.addEventListener('click', async () => {
139
+ preview.disabled = true;
140
+ try {
141
+ const blob = await http.get('/api/v0/artifacts/' + id + '/content', { signal, binary: true });
142
+ if (signal.aborted || http !== client) return;
143
+ const extension = { 'image/png': 'png', 'image/jpeg': 'jpg', 'image/webp': 'webp' }[blob.type];
144
+ if (!extension) throw new Error('此业务作品不是支持的图片类型');
145
+ const url = URL.createObjectURL(blob); urls.add(url); const image = node('img'); image.src = url; image.alt = '此业务保存的原始作品';
146
+ const link = node('a', '下载原图'); link.href = url; link.download = 'iris-' + id + '.' + extension; article.append(image, link); preview.hidden = true;
147
+ } catch (error) { if (!signal.aborted) { status.textContent = describe(error); preview.disabled = false; } }
148
+ }); article.append(preview);
149
+ }
150
+ slot.append(article);
151
+ }
152
+ }).catch(error => { if (!signal.aborted) $('#plugin-history-feedback').textContent = describe(error); });
153
+ }
154
+ $('#plugin-history-refresh').addEventListener('click', () => { if (client && page === 'plugin-history') showHistory(); });
155
+ function renderPreview() {
156
+ stop($('#plugin-preview-slot')); $('#plugin-preview-slot').replaceChildren();
157
+ const id = $('#plugin-renderer').value, item = manifest?.renderers.find(value => value.id === id && value.mediaTypes.includes(currentArtifact?.mediaType));
158
+ if (page === 'studio' && $('#plugin-preview').open && item) mount($('#plugin-preview-slot'), item, { artifact: currentArtifact });
159
+ }
160
+ function selectArtifact() {
161
+ stop($('#plugin-preview-slot'));
162
+ currentArtifact = artifacts.find(value => value.id === $('#plugin-artifact').value);
163
+ $('#plugin-renderer').replaceChildren(new Option('默认预览', ''), ...(manifest?.renderers || [])
164
+ .filter(item => item.mediaTypes.includes(currentArtifact?.mediaType)).map(item => new Option(item.title, item.id)));
165
+ renderPreview();
166
+ }
167
+ $('#plugin-renderer').addEventListener('change', renderPreview);
168
+ $('#plugin-artifact').addEventListener('change', selectArtifact);
169
+ $('#plugin-preview').addEventListener('toggle', renderPreview);
170
+ return Object.freeze({
171
+ connect(http, facts) {
172
+ client = http;
173
+ session = freeze({ contractVersion: facts.contractVersion, instanceId: facts.instanceId, rootId: facts.rootId,
174
+ operations: [...facts.operations], readOnly: facts.readOnly, audio: facts.audio === true, providerManagement: facts.providerManagement === true,
175
+ pluginExecution: facts.pluginExecution === true, pluginOperations: structuredClone(facts.pluginOperations || []) });
176
+ manifest = freeze(structuredClone(facts.panelContributions || { contractVersion: 0, pages: [], settings: [], renderers: [] }));
177
+ themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme', 'data-density', 'data-accent'] });
178
+ const nav = $('#plugin-navigation'); nav.replaceChildren(); nav.hidden = !manifest.pages.length && !manifest.settings.length && !session.pluginExecution;
179
+ for (const item of [...manifest.pages, ...(manifest.settings.length ? [{ id: '', title: '扩展设置' }] : []), ...(session.pluginExecution ? [{ id: 'history', title: '业务记录' }] : [])]) {
180
+ const button = node('button', item.title, 'nav-item'); button.type = 'button';
181
+ button.dataset.page = item.id === 'history' ? 'plugin-history' : item.id ? 'plugin:' + item.id : 'plugin-settings';
182
+ button.addEventListener('click', () => navigate(button.dataset.page)); nav.append(button);
183
+ }
184
+ $('#plugin-settings-list').replaceChildren(...manifest.settings.map(item => {
185
+ const details = node('details', '', 'card plugin-settings-card'); details.dataset.contribution = item.id;
186
+ const summary = node('summary'), label = node('span', item.title), disclosure = node('span', '展开', 'disclosure');
187
+ summary.append(label, disclosure);
188
+ const identity = node('p', item.dependency.pluginId + ' · v' + item.dependency.pluginVersion, 'field-note');
189
+ const slot = node('div', '', 'plugin-slot'); details.append(summary, identity, slot);
190
+ details.addEventListener('toggle', () => {
191
+ disclosure.textContent = details.open ? '收起' : '展开';
192
+ if (details.isConnected && details.open && page === 'plugin-settings' && client && manifest.settings.includes(item)) mount(slot, item); else stop(slot);
193
+ }); return details;
194
+ }));
195
+ },
196
+ show(next) {
197
+ if (page === next) return;
198
+ $('#plugin-feedback').hidden = true; stopAll(); page = next;
199
+ if (client) { renderPage(); renderPreview(); }
200
+ else { $('#plugin-page-panel').hidden = true; $('#plugin-settings-panel').hidden = true; $('#plugin-history-panel').hidden = true; }
201
+ },
202
+ previewArtifacts(values, preferredId) {
203
+ artifacts = values.filter(value => value && manifest?.renderers.some(item => item.mediaTypes.includes(value.mediaType))).map(value => freeze({ ...value }));
204
+ const selected = artifacts.find(value => value.id === preferredId) || artifacts[0];
205
+ $('#plugin-preview').hidden = !artifacts.length; $('#plugin-preview').open = false;
206
+ $('#plugin-artifact').replaceChildren(...artifacts.map(value => new Option(value.label, value.id)));
207
+ if (selected) $('#plugin-artifact').value = selected.id;
208
+ selectArtifact();
209
+ },
210
+ clearPreview() { stop($('#plugin-preview-slot')); artifacts = []; currentArtifact = null; $('#plugin-preview').hidden = true; $('#plugin-preview').open = false; $('#plugin-preview-slot').replaceChildren(); },
211
+ disconnect() {
212
+ $('#plugin-feedback').hidden = true;
213
+ stopAll(); themeObserver.disconnect(); themeListeners.clear(); client = session = manifest = null; page = '';
214
+ $('#plugin-navigation').replaceChildren(); $('#plugin-navigation').hidden = true;
215
+ $('#plugin-page-panel').hidden = $('#plugin-settings-panel').hidden = $('#plugin-history-panel').hidden = true;
216
+ $('#plugin-history-list').replaceChildren();
217
+ $('#plugin-page-slot').replaceChildren(); $('#plugin-settings-list').replaceChildren();
218
+ artifacts = []; currentArtifact = null; $('#plugin-preview').hidden = true; $('#plugin-preview').open = false;
219
+ }
220
+ });
221
+ }
@@ -0,0 +1,17 @@
1
+ :host { display:block; color:var(--ink); font:inherit; color-scheme:inherit; }
2
+ * { box-sizing:border-box; }
3
+ [hidden] { display:none!important; }
4
+ .plugin-content { min-width:0; overflow-wrap:anywhere; }
5
+ h1,h2,h3,p,figure { margin:0 0 12px; }
6
+ button,input,select,textarea { font:inherit; color:inherit; max-width:100%; }
7
+ button,input,select,textarea { border:1px solid var(--line); border-radius:8px; padding:8px 12px; background:var(--panel); min-height:40px; }
8
+ button { cursor:pointer; background:var(--accent); color:var(--on-accent); }
9
+ button:disabled { opacity:.5; cursor:default; }
10
+ button.secondary { background:var(--panel); color:var(--ink); }
11
+ label { display:grid; gap:6px; margin-bottom:12px; }
12
+ a { color:var(--accent); }
13
+ :focus-visible { outline:2px solid var(--purple); outline-offset:3px; }
14
+ .muted,.field-note { color:var(--muted); font-size:12px; }
15
+ .card { background:var(--panel); border:1px solid var(--line); border-radius:12px; padding:16px; }
16
+ .error { color:var(--error); }
17
+ img,video,audio { max-width:100%; }