@mokuyoaxis/dsh-iris 0.2.6 → 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 (177) hide show
  1. package/CHANGELOG.md +103 -1
  2. package/README.en.md +104 -304
  3. package/README.md +101 -302
  4. package/bin/dsh-iris.js +446 -64
  5. package/docs/AGENT_CORE_QUICKSTART.md +64 -0
  6. package/docs/AGENT_QUICKSTART.md +7 -5
  7. package/docs/ARCHITECTURE.md +20 -4
  8. package/docs/AUDIO_FLOWS.md +71 -0
  9. package/docs/CLI_MANAGEMENT.md +1 -1
  10. package/docs/CONTACT_SHEET.md +62 -0
  11. package/docs/CORE_CONNECTIONS.md +99 -0
  12. package/docs/CORE_QUICKSTART.md +108 -0
  13. package/docs/CORE_RUNTIME_CONTRACT.md +41 -5
  14. package/docs/CORE_SERVICE.md +97 -0
  15. package/docs/CORE_SKILLS.md +94 -0
  16. package/docs/DSH_COMPATIBILITY.md +14 -4
  17. package/docs/DSH_CORE_MIGRATION.md +4 -2
  18. package/docs/DSH_PROVIDER_CONNECTIONS.md +1 -1
  19. package/docs/DSH_RC2_ADAPTATION.md +1 -1
  20. package/docs/DSH_RUNTIME.md +40 -0
  21. package/docs/FUNCTION_MODEL_POOLS.md +1 -1
  22. package/docs/HEADLESS_CLI.md +12 -4
  23. package/docs/HOST_DOCTOR.md +13 -1
  24. package/docs/IMAGE_FLOW.md +75 -0
  25. package/docs/IMAGE_GENERATION.md +127 -0
  26. package/docs/LOCAL_API.md +62 -0
  27. package/docs/LOCAL_API_CONTRACT.md +140 -0
  28. package/docs/LOCAL_CREATION.md +56 -0
  29. package/docs/LOCAL_OPERATIONS.md +46 -0
  30. package/docs/LOCAL_SELECTION.md +57 -0
  31. package/docs/LOCAL_WEB.md +201 -0
  32. package/docs/MCP.md +191 -0
  33. package/docs/MUSIC.md +2 -0
  34. package/docs/PANEL_EXTENSIONS.md +64 -0
  35. package/docs/PLUGINS.md +189 -0
  36. package/docs/PLUGIN_STARTUP.md +100 -0
  37. package/docs/PROVIDER_ADAPTER_CONTRACT.md +1 -1
  38. package/docs/PROVIDER_EXTENSIONS.md +78 -0
  39. package/docs/PROVIDER_HEALTH.md +4 -2
  40. package/docs/PROVIDER_SETTINGS.md +85 -0
  41. package/docs/PROVIDER_SUBMISSION_CONTRACT.md +6 -0
  42. package/docs/ROADMAP.md +28 -6
  43. package/docs/SECURITY.md +9 -1
  44. package/docs/SYNC_DELIVERY_RECOVERY.md +2 -0
  45. package/docs/VISION_CLI.md +2 -2
  46. package/docs/WEB_PROTOTYPE.md +62 -0
  47. package/docs/WEB_RECOVERY.md +46 -0
  48. package/docs/WORKBENCH_LOGIN.md +63 -0
  49. package/docs/assets/screenshots/core-connections-preview.webp +0 -0
  50. package/docs/assets/screenshots/core-model-pools-preview.webp +0 -0
  51. package/docs/assets/screenshots/core-overview-preview.webp +0 -0
  52. package/docs/assets/screenshots/core-spectrum-preview.webp +0 -0
  53. package/docs/assets/screenshots/core-studio-dark-preview.webp +0 -0
  54. package/docs/assets/screenshots/core-studio-mobile-preview.webp +0 -0
  55. package/docs/assets/screenshots/core-studio-preview.webp +0 -0
  56. package/docs/releases/0.2.7.md +62 -0
  57. package/docs/releases/0.3.0-alpha.1.md +51 -0
  58. package/docs/screenshots.md +41 -1
  59. package/lib/actions.js +132 -70
  60. package/lib/adapters.js +20 -11
  61. package/lib/api.js +38 -38
  62. package/lib/application-runtime.js +213 -0
  63. package/lib/audio-flow-input.js +37 -0
  64. package/lib/client.js +434 -55
  65. package/lib/comfyui-adapter.js +94 -0
  66. package/lib/comfyui-client.js +51 -0
  67. package/lib/comfyui-inspection.js +73 -0
  68. package/lib/comfyui-workflow.js +94 -0
  69. package/lib/config.js +36 -1
  70. package/lib/configured-image-runtime.js +50 -0
  71. package/lib/connection-skill.js +89 -0
  72. package/lib/connection-template.js +23 -0
  73. package/lib/core-artifact-store.js +21 -2
  74. package/lib/core-bridge-selection.js +31 -0
  75. package/lib/core-bridge-tools.js +83 -0
  76. package/lib/core-capability-catalog.js +182 -0
  77. package/lib/core-connection-diagnostics.js +49 -0
  78. package/lib/core-connection-store.js +277 -0
  79. package/lib/core-connections.js +225 -0
  80. package/lib/core-maintenance.js +31 -2
  81. package/lib/core-runtime.js +20 -0
  82. package/lib/core-service-client.js +170 -0
  83. package/lib/core-tasks.js +84 -13
  84. package/lib/core-writer-lease.js +16 -0
  85. package/lib/creation-request-store.js +133 -0
  86. package/lib/doctor.js +12 -1
  87. package/lib/dsh-core-adapter.js +666 -572
  88. package/lib/dsh-host-adapter.js +8 -2
  89. package/lib/dsh-service-bridge.js +66 -0
  90. package/lib/execution-ownership.js +43 -0
  91. package/lib/flow-store.js +290 -0
  92. package/lib/generation-input.js +10 -1
  93. package/lib/headless-vision.js +7 -7
  94. package/lib/image-edit-input.js +1 -1
  95. package/lib/image-flow.js +232 -0
  96. package/lib/index.js +94 -68
  97. package/lib/local-api-projection.js +32 -0
  98. package/lib/local-api-token-directory.js +31 -0
  99. package/lib/local-api.js +1083 -0
  100. package/lib/local-creation.js +150 -0
  101. package/lib/local-operations.js +36 -0
  102. package/lib/local-request-root.js +86 -0
  103. package/lib/local-web-assets.js +37 -0
  104. package/lib/mcp-bridge.js +58 -0
  105. package/lib/mcp-check.js +26 -0
  106. package/lib/mcp-config.js +42 -0
  107. package/lib/mcp-delivery.js +46 -0
  108. package/lib/mcp-http.js +101 -0
  109. package/lib/mcp-server.js +47 -0
  110. package/lib/model-directory-service.js +16 -6
  111. package/lib/model-directory.js +3 -3
  112. package/lib/models.js +2 -2
  113. package/lib/music-transport.js +2 -1
  114. package/lib/operation-runner.js +236 -0
  115. package/lib/operation-store.js +216 -0
  116. package/lib/panel-extension.js +36 -0
  117. package/lib/plugin-contract.js +194 -0
  118. package/lib/plugin-execution.js +51 -0
  119. package/lib/plugin-launch.js +86 -0
  120. package/lib/plugin-operation-store.js +101 -0
  121. package/lib/plugin-operations.js +28 -0
  122. package/lib/plugin-registry.js +109 -0
  123. package/lib/plugin-selection.js +15 -0
  124. package/lib/plugin-source-image.js +5 -0
  125. package/lib/plugin-web-assets.js +54 -0
  126. package/lib/plugins.d.ts +288 -0
  127. package/lib/plugins.js +58 -0
  128. package/lib/private-storage.js +12 -0
  129. package/lib/provider-adapter.js +5 -1
  130. package/lib/provider-adapters.js +56 -18
  131. package/lib/provider-catalog.js +40 -29
  132. package/lib/provider-config-service.js +105 -23
  133. package/lib/provider-diagnostics.js +56 -0
  134. package/lib/provider-extensions.js +29 -0
  135. package/lib/provider-health.js +8 -1
  136. package/lib/provider-monitor.js +79 -0
  137. package/lib/provider-protocol.js +70 -20
  138. package/lib/provider-registry.js +118 -0
  139. package/lib/provider-response.js +28 -0
  140. package/lib/provider-settings.js +134 -0
  141. package/lib/provider-task-runner.js +40 -7
  142. package/lib/render-address.js +35 -0
  143. package/lib/render.js +52 -13
  144. package/lib/skill-installation.js +211 -0
  145. package/lib/vision-model-routing.js +3 -0
  146. package/lib/web/app.js +642 -0
  147. package/lib/web/appearance.js +111 -0
  148. package/lib/web/comfyui.js +146 -0
  149. package/lib/web/connections.js +377 -0
  150. package/lib/web/creation.js +355 -0
  151. package/lib/web/flow-view.js +33 -0
  152. package/lib/web/gallery.js +132 -0
  153. package/lib/web/index.html +291 -0
  154. package/lib/web/media-client.js +204 -0
  155. package/lib/web/plugin-panel.js +221 -0
  156. package/lib/web/plugin-surface.css +17 -0
  157. package/lib/web/providers.js +492 -0
  158. package/lib/web/recovery.js +105 -0
  159. package/lib/web/startup.js +18 -0
  160. package/lib/web/styles.css +518 -0
  161. package/lib/web/transport.js +96 -0
  162. package/lib/web/workbench-login.js +165 -0
  163. package/lib/workbench-artifacts.js +1 -2
  164. package/lib/workbench-auth-cli.js +47 -0
  165. package/lib/workbench-auth.js +200 -0
  166. package/lib/workbench-management.js +8 -9
  167. package/package.json +22 -6
  168. package/plugins/contact-sheet/index.d.mts +4 -0
  169. package/plugins/contact-sheet/index.mjs +33 -0
  170. package/plugins/contact-sheet/web/board.css +14 -0
  171. package/plugins/contact-sheet/web/board.mjs +93 -0
  172. package/skills/iris-core/SKILL.md +44 -0
  173. package/skills/iris-core/agents/openai.yaml +4 -0
  174. package/skills/iris-core/references/cli.md +95 -0
  175. package/skills/iris-core/references/comfyui.md +69 -0
  176. package/skills/iris-core/references/tasks.md +60 -0
  177. package/user_guide.md +19 -9
@@ -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%; }