@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.
- package/CHANGELOG.md +103 -1
- package/README.en.md +104 -304
- package/README.md +101 -302
- package/bin/dsh-iris.js +446 -64
- package/docs/AGENT_CORE_QUICKSTART.md +64 -0
- package/docs/AGENT_QUICKSTART.md +7 -5
- package/docs/ARCHITECTURE.md +20 -4
- package/docs/AUDIO_FLOWS.md +71 -0
- package/docs/CLI_MANAGEMENT.md +1 -1
- package/docs/CONTACT_SHEET.md +62 -0
- package/docs/CORE_CONNECTIONS.md +99 -0
- package/docs/CORE_QUICKSTART.md +108 -0
- package/docs/CORE_RUNTIME_CONTRACT.md +41 -5
- package/docs/CORE_SERVICE.md +97 -0
- package/docs/CORE_SKILLS.md +94 -0
- package/docs/DSH_COMPATIBILITY.md +14 -4
- package/docs/DSH_CORE_MIGRATION.md +4 -2
- package/docs/DSH_PROVIDER_CONNECTIONS.md +1 -1
- package/docs/DSH_RC2_ADAPTATION.md +1 -1
- package/docs/DSH_RUNTIME.md +40 -0
- package/docs/FUNCTION_MODEL_POOLS.md +1 -1
- package/docs/HEADLESS_CLI.md +12 -4
- package/docs/HOST_DOCTOR.md +13 -1
- package/docs/IMAGE_FLOW.md +75 -0
- package/docs/IMAGE_GENERATION.md +127 -0
- package/docs/LOCAL_API.md +62 -0
- package/docs/LOCAL_API_CONTRACT.md +140 -0
- package/docs/LOCAL_CREATION.md +56 -0
- package/docs/LOCAL_OPERATIONS.md +46 -0
- package/docs/LOCAL_SELECTION.md +57 -0
- package/docs/LOCAL_WEB.md +201 -0
- package/docs/MCP.md +191 -0
- package/docs/MUSIC.md +2 -0
- package/docs/PANEL_EXTENSIONS.md +64 -0
- package/docs/PLUGINS.md +189 -0
- package/docs/PLUGIN_STARTUP.md +100 -0
- package/docs/PROVIDER_ADAPTER_CONTRACT.md +1 -1
- package/docs/PROVIDER_EXTENSIONS.md +78 -0
- package/docs/PROVIDER_HEALTH.md +4 -2
- package/docs/PROVIDER_SETTINGS.md +85 -0
- package/docs/PROVIDER_SUBMISSION_CONTRACT.md +6 -0
- package/docs/ROADMAP.md +28 -6
- package/docs/SECURITY.md +9 -1
- package/docs/SYNC_DELIVERY_RECOVERY.md +2 -0
- package/docs/VISION_CLI.md +2 -2
- package/docs/WEB_PROTOTYPE.md +62 -0
- package/docs/WEB_RECOVERY.md +46 -0
- package/docs/WORKBENCH_LOGIN.md +63 -0
- package/docs/assets/screenshots/core-connections-preview.webp +0 -0
- package/docs/assets/screenshots/core-model-pools-preview.webp +0 -0
- package/docs/assets/screenshots/core-overview-preview.webp +0 -0
- package/docs/assets/screenshots/core-spectrum-preview.webp +0 -0
- package/docs/assets/screenshots/core-studio-dark-preview.webp +0 -0
- package/docs/assets/screenshots/core-studio-mobile-preview.webp +0 -0
- package/docs/assets/screenshots/core-studio-preview.webp +0 -0
- package/docs/releases/0.2.7.md +62 -0
- package/docs/releases/0.3.0-alpha.1.md +51 -0
- package/docs/screenshots.md +41 -1
- package/lib/actions.js +132 -70
- package/lib/adapters.js +20 -11
- package/lib/api.js +38 -38
- package/lib/application-runtime.js +213 -0
- package/lib/audio-flow-input.js +37 -0
- package/lib/client.js +434 -55
- package/lib/comfyui-adapter.js +94 -0
- package/lib/comfyui-client.js +51 -0
- package/lib/comfyui-inspection.js +73 -0
- package/lib/comfyui-workflow.js +94 -0
- package/lib/config.js +36 -1
- package/lib/configured-image-runtime.js +50 -0
- package/lib/connection-skill.js +89 -0
- package/lib/connection-template.js +23 -0
- package/lib/core-artifact-store.js +21 -2
- package/lib/core-bridge-selection.js +31 -0
- package/lib/core-bridge-tools.js +83 -0
- package/lib/core-capability-catalog.js +182 -0
- package/lib/core-connection-diagnostics.js +49 -0
- package/lib/core-connection-store.js +277 -0
- package/lib/core-connections.js +225 -0
- package/lib/core-maintenance.js +31 -2
- package/lib/core-runtime.js +20 -0
- package/lib/core-service-client.js +170 -0
- package/lib/core-tasks.js +84 -13
- package/lib/core-writer-lease.js +16 -0
- package/lib/creation-request-store.js +133 -0
- package/lib/doctor.js +12 -1
- package/lib/dsh-core-adapter.js +666 -572
- package/lib/dsh-host-adapter.js +8 -2
- package/lib/dsh-service-bridge.js +66 -0
- package/lib/execution-ownership.js +43 -0
- package/lib/flow-store.js +290 -0
- package/lib/generation-input.js +10 -1
- package/lib/headless-vision.js +7 -7
- package/lib/image-edit-input.js +1 -1
- package/lib/image-flow.js +232 -0
- package/lib/index.js +94 -68
- package/lib/local-api-projection.js +32 -0
- package/lib/local-api-token-directory.js +31 -0
- package/lib/local-api.js +1083 -0
- package/lib/local-creation.js +150 -0
- package/lib/local-operations.js +36 -0
- package/lib/local-request-root.js +86 -0
- package/lib/local-web-assets.js +37 -0
- package/lib/mcp-bridge.js +58 -0
- package/lib/mcp-check.js +26 -0
- package/lib/mcp-config.js +42 -0
- package/lib/mcp-delivery.js +46 -0
- package/lib/mcp-http.js +101 -0
- package/lib/mcp-server.js +47 -0
- package/lib/model-directory-service.js +16 -6
- package/lib/model-directory.js +3 -3
- package/lib/models.js +2 -2
- package/lib/music-transport.js +2 -1
- package/lib/operation-runner.js +236 -0
- package/lib/operation-store.js +216 -0
- package/lib/panel-extension.js +36 -0
- package/lib/plugin-contract.js +194 -0
- package/lib/plugin-execution.js +51 -0
- package/lib/plugin-launch.js +86 -0
- package/lib/plugin-operation-store.js +101 -0
- package/lib/plugin-operations.js +28 -0
- package/lib/plugin-registry.js +109 -0
- package/lib/plugin-selection.js +15 -0
- package/lib/plugin-source-image.js +5 -0
- package/lib/plugin-web-assets.js +54 -0
- package/lib/plugins.d.ts +288 -0
- package/lib/plugins.js +58 -0
- package/lib/private-storage.js +12 -0
- package/lib/provider-adapter.js +5 -1
- package/lib/provider-adapters.js +56 -18
- package/lib/provider-catalog.js +40 -29
- package/lib/provider-config-service.js +105 -23
- package/lib/provider-diagnostics.js +56 -0
- package/lib/provider-extensions.js +29 -0
- package/lib/provider-health.js +8 -1
- package/lib/provider-monitor.js +79 -0
- package/lib/provider-protocol.js +70 -20
- package/lib/provider-registry.js +118 -0
- package/lib/provider-response.js +28 -0
- package/lib/provider-settings.js +134 -0
- package/lib/provider-task-runner.js +40 -7
- package/lib/render-address.js +35 -0
- package/lib/render.js +52 -13
- package/lib/skill-installation.js +211 -0
- package/lib/vision-model-routing.js +3 -0
- package/lib/web/app.js +642 -0
- package/lib/web/appearance.js +111 -0
- package/lib/web/comfyui.js +146 -0
- package/lib/web/connections.js +377 -0
- package/lib/web/creation.js +355 -0
- package/lib/web/flow-view.js +33 -0
- package/lib/web/gallery.js +132 -0
- package/lib/web/index.html +291 -0
- package/lib/web/media-client.js +204 -0
- package/lib/web/plugin-panel.js +221 -0
- package/lib/web/plugin-surface.css +17 -0
- package/lib/web/providers.js +492 -0
- package/lib/web/recovery.js +105 -0
- package/lib/web/startup.js +18 -0
- package/lib/web/styles.css +518 -0
- package/lib/web/transport.js +96 -0
- package/lib/web/workbench-login.js +165 -0
- package/lib/workbench-artifacts.js +1 -2
- package/lib/workbench-auth-cli.js +47 -0
- package/lib/workbench-auth.js +200 -0
- package/lib/workbench-management.js +8 -9
- package/package.json +22 -6
- package/plugins/contact-sheet/index.d.mts +4 -0
- package/plugins/contact-sheet/index.mjs +33 -0
- package/plugins/contact-sheet/web/board.css +14 -0
- package/plugins/contact-sheet/web/board.mjs +93 -0
- package/skills/iris-core/SKILL.md +44 -0
- package/skills/iris-core/agents/openai.yaml +4 -0
- package/skills/iris-core/references/cli.md +95 -0
- package/skills/iris-core/references/comfyui.md +69 -0
- package/skills/iris-core/references/tasks.md +60 -0
- 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%; }
|