@mokuyoaxis/dsh-iris 0.2.7 → 0.3.0-beta.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 +143 -1
- package/README.en.md +109 -311
- package/README.md +106 -309
- package/bin/dsh-iris.js +452 -92
- package/docs/AGENT_CORE_QUICKSTART.md +64 -0
- package/docs/AGENT_QUICKSTART.md +2 -0
- package/docs/ARCHITECTURE.md +13 -3
- package/docs/ARTIFACT_MANIFEST.md +4 -0
- package/docs/AUDIO_FLOWS.md +87 -0
- package/docs/BETA_SCOPE.md +42 -0
- package/docs/CONTACT_SHEET.md +62 -0
- package/docs/CORE_CONNECTIONS.md +101 -0
- package/docs/CORE_QUICKSTART.md +110 -0
- package/docs/CORE_RUNTIME_CONTRACT.md +7 -2
- package/docs/CORE_SERVICE.md +101 -0
- package/docs/CORE_SKILLS.md +35 -3
- package/docs/DSH_COMPATIBILITY.md +21 -1
- package/docs/DSH_CORE_MIGRATION.md +6 -0
- package/docs/DSH_RC2_ADAPTATION.md +1 -1
- package/docs/DSH_RUNTIME.md +6 -2
- package/docs/HEADLESS_CLI.md +30 -5
- package/docs/HOST_ADAPTER_CONTRACT.md +5 -3
- package/docs/IMAGE_FLOW.md +4 -0
- package/docs/IMAGE_GENERATION.md +127 -0
- package/docs/LOCAL_API.md +4 -2
- package/docs/LOCAL_API_CONTRACT.md +42 -15
- package/docs/LOCAL_CREATION.md +7 -1
- package/docs/LOCAL_OPERATIONS.md +46 -0
- package/docs/LOCAL_SELECTION.md +57 -0
- package/docs/LOCAL_WEB.md +207 -0
- package/docs/MCP.md +212 -0
- package/docs/MEDIA_SCHEDULING.md +72 -0
- package/docs/MUSIC.md +2 -0
- package/docs/NETWORK_RESOURCES.md +106 -0
- package/docs/NETWORK_ROUTING.md +103 -0
- package/docs/NETWORK_WORKBENCH.md +65 -0
- package/docs/PANEL_EXTENSIONS.md +64 -0
- package/docs/PLUGINS.md +193 -0
- package/docs/PLUGIN_DEVELOPMENT.md +39 -0
- package/docs/PLUGIN_STARTUP.md +113 -0
- package/docs/PROVIDER_ADAPTER_CONTRACT.md +8 -2
- package/docs/PROVIDER_CREDENTIALS.md +67 -0
- package/docs/PROVIDER_EXTENSIONS.md +82 -0
- package/docs/PROVIDER_HEALTH.md +5 -1
- package/docs/PROVIDER_NETWORK.md +45 -0
- package/docs/PROVIDER_PLUGIN_CONTRACT.md +24 -0
- package/docs/PROVIDER_SETTINGS.md +91 -0
- package/docs/PROVIDER_SUBMISSION_CONTRACT.md +8 -0
- package/docs/ROADMAP.md +18 -8
- package/docs/SECURITY.md +1 -1
- package/docs/SYNC_DELIVERY_RECOVERY.md +5 -1
- package/docs/VIDEO_FLOWS.md +89 -0
- package/docs/VISION_CLI.md +1 -1
- package/docs/VISION_MODEL.md +2 -0
- package/docs/WEB_PROTOTYPE.md +21 -6
- package/docs/WEB_RECOVERY.md +46 -0
- package/docs/WORKBENCH_ARTIFACTS.md +6 -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.3.0-alpha.1.md +51 -0
- package/docs/releases/0.3.0-beta.1.md +34 -0
- package/docs/screenshots.md +41 -1
- package/lib/actions.js +56 -4
- package/lib/adapters.js +75 -49
- package/lib/api.js +23 -13
- package/lib/application-runtime.js +73 -17
- package/lib/application-writer.js +193 -0
- package/lib/audio-flow-input.js +37 -0
- package/lib/client.d.ts +3 -3
- package/lib/client.js +402 -72
- package/lib/comfyui-adapter.js +95 -0
- package/lib/comfyui-client.js +60 -0
- package/lib/comfyui-inspection.js +73 -0
- package/lib/comfyui-workflow.js +94 -0
- package/lib/command-service.js +18 -5
- package/lib/config.js +49 -10
- package/lib/configured-image-runtime.js +69 -0
- package/lib/connection-skill.js +98 -0
- package/lib/connection-template.js +23 -0
- package/lib/core-artifact-store.js +84 -28
- package/lib/core-bridge-selection.js +31 -0
- package/lib/core-bridge-tools.js +85 -0
- package/lib/core-capability-catalog.js +192 -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-delivery-cache.js +8 -3
- package/lib/core-maintenance.js +9 -3
- package/lib/core-runtime.js +2 -1
- package/lib/core-service-client.js +174 -0
- package/lib/core-tasks.js +113 -21
- package/lib/core-writer-lease.js +16 -0
- package/lib/creation-request-store.js +41 -64
- package/lib/doctor.js +2 -2
- package/lib/dsh-core-adapter.js +61 -9
- package/lib/dsh-host-adapter.js +4 -1
- package/lib/dsh-provider-config.js +1 -1
- package/lib/dsh-service-bridge.js +67 -0
- package/lib/execution-ownership.js +43 -0
- package/lib/flow-store.js +156 -14
- 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 +97 -31
- package/lib/index.js +47 -22
- package/lib/local-api-projection.js +15 -6
- package/lib/local-api-token-directory.js +31 -0
- package/lib/local-api.js +861 -71
- package/lib/local-creation.js +96 -24
- package/lib/local-operations.js +36 -0
- package/lib/local-request-root.js +86 -0
- package/lib/local-web-assets.js +39 -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 +174 -0
- package/lib/mcp-server.js +69 -0
- package/lib/media-scheduler.js +194 -0
- package/lib/model-directory-service.js +37 -10
- package/lib/model-directory.js +3 -3
- package/lib/models.js +2 -2
- package/lib/music-transport.js +6 -3
- package/lib/network-config-service.js +123 -0
- package/lib/network-config.js +127 -0
- package/lib/network-doctor.js +94 -0
- package/lib/network-management.js +51 -0
- package/lib/network-policy.js +19 -0
- package/lib/network-route-evidence.js +38 -0
- package/lib/network-routing.js +283 -0
- package/lib/operation-runner.js +281 -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 +307 -0
- package/lib/plugins.js +59 -0
- package/lib/provider-adapter.js +7 -3
- package/lib/provider-adapters.js +92 -22
- package/lib/provider-catalog.js +64 -36
- package/lib/provider-config-format.js +51 -0
- package/lib/provider-config-service.js +143 -31
- package/lib/provider-context.d.ts +90 -0
- package/lib/provider-context.js +42 -0
- package/lib/provider-credentials.js +97 -0
- package/lib/provider-diagnostics.js +56 -0
- package/lib/provider-extensions.js +29 -0
- package/lib/provider-health.js +18 -6
- package/lib/provider-monitor.js +81 -0
- package/lib/provider-network.js +52 -0
- package/lib/provider-protocol.js +73 -22
- package/lib/provider-registry.js +128 -0
- package/lib/provider-response.js +38 -0
- package/lib/provider-settings.js +136 -0
- package/lib/provider-task-runner.js +132 -43
- package/lib/provider-transport.js +16 -0
- package/lib/render.js +11 -1
- package/lib/scheduling-policy.js +66 -0
- package/lib/skill-installation.js +23 -11
- package/lib/tasks.js +3 -2
- package/lib/video-flow-input.js +16 -0
- package/lib/video-input.js +12 -5
- package/lib/vision-model-routing.js +5 -0
- package/lib/web/app.js +703 -0
- package/lib/web/appearance.js +111 -0
- package/lib/web/comfyui.js +146 -0
- package/lib/web/connections.js +516 -0
- package/lib/web/creation.js +359 -0
- package/lib/web/flow-view.js +34 -0
- package/lib/web/gallery.js +133 -0
- package/lib/web/index.html +344 -0
- package/lib/web/media-client.js +218 -0
- package/lib/web/network.js +232 -0
- package/lib/web/plugin-panel.js +223 -0
- package/lib/web/plugin-status.js +140 -0
- package/lib/web/plugin-surface.css +17 -0
- package/lib/web/providers.js +513 -0
- package/lib/web/recovery.js +105 -0
- package/lib/web/startup.js +18 -0
- package/lib/web/styles.css +571 -0
- package/lib/web/transport.js +101 -0
- package/lib/web/workbench-login.js +165 -0
- package/lib/workbench-artifacts.js +3 -2
- package/lib/workbench-auth-cli.js +47 -0
- package/lib/workbench-auth.js +200 -0
- package/package.json +24 -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 +8 -4
- package/skills/iris-core/references/cli.md +2 -2
- package/skills/iris-core/references/comfyui.md +69 -0
- package/skills/iris-core/references/tasks.md +2 -0
- package/templates/plugin-starter/LICENSE +21 -0
- package/templates/plugin-starter/README.md +74 -0
- package/templates/plugin-starter/assets/peach.png +0 -0
- package/templates/plugin-starter/assets/violet.png +0 -0
- package/templates/plugin-starter/index.mjs +27 -0
- package/templates/plugin-starter/package.json +18 -0
- package/templates/plugin-starter/scripts/core-cli.mjs +20 -0
- package/templates/plugin-starter/scripts/environment.mjs +65 -0
- package/templates/plugin-starter/scripts/mcp-probe.mjs +22 -0
- package/templates/plugin-starter/scripts/setup.mjs +42 -0
- package/templates/plugin-starter/scripts/smoke.mjs +76 -0
- package/templates/plugin-starter/web/board.css +27 -0
- package/templates/plugin-starter/web/board.mjs +160 -0
- package/templates/plugin-starter/web/png.mjs +23 -0
- package/templates/provider-starter/LICENSE +21 -0
- package/templates/provider-starter/README.md +50 -0
- package/templates/provider-starter/index.d.mts +5 -0
- package/templates/provider-starter/index.mjs +165 -0
- package/templates/provider-starter/package.json +13 -0
- package/templates/provider-starter/scripts/core-cli.mjs +22 -0
- package/templates/provider-starter/scripts/environment.mjs +122 -0
- package/templates/provider-starter/scripts/fixture.mjs +54 -0
- package/templates/provider-starter/scripts/mcp-probe.mjs +21 -0
- package/templates/provider-starter/scripts/protocol-smoke.mjs +48 -0
- package/templates/provider-starter/scripts/setup.mjs +44 -0
- package/templates/provider-starter/scripts/smoke.mjs +145 -0
- package/user_guide.md +5 -1
package/lib/client.js
CHANGED
|
@@ -21,7 +21,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
21
21
|
var exports = module.exports;
|
|
22
22
|
|
|
23
23
|
var React = require('react');
|
|
24
|
-
var IRIS_CLIENT_VERSION = '0.
|
|
24
|
+
var IRIS_CLIENT_VERSION = '0.3.0-beta.1';
|
|
25
25
|
var HOST_CLIENT_PROTOCOL_VERSION = 0;
|
|
26
26
|
var reportedSeats = [];
|
|
27
27
|
|
|
@@ -109,6 +109,27 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
109
109
|
'.iris-workbench-page :is(input, textarea, select) { font-family: inherit; accent-color: var(--iris-workbench-accent); }',
|
|
110
110
|
'.iris-workbench-page input:not([type=checkbox]):not([type=radio]):not([type=file]), .iris-workbench-page textarea, .iris-workbench-page select { min-width: 0; max-width: 100%; min-height: 34px; padding: 5px 8px; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 8px; background: var(--dsw-alias-bg-layer-2, #32323c); color: var(--dsw-alias-label-primary, #e6e6e6); font-size: 12px; }',
|
|
111
111
|
'.iris-workbench-page .iris-pm-note { line-height: 1.65; }',
|
|
112
|
+
'.iris-comfy-panel { position: relative; isolation: isolate; overflow: hidden; min-width: 0; display: grid; gap: 10px; padding: 12px; border: 1px solid color-mix(in srgb, #6F69B8 30%, var(--dsw-alias-border-l1, #3a3a44)); border-radius: 12px; background: linear-gradient(105deg, rgba(111,105,184,.07), rgba(229,155,118,.04)), var(--dsw-alias-bg-layer-1, #26262e); }',
|
|
113
|
+
'.iris-comfy-panel::before { content: ""; position: absolute; z-index: -1; pointer-events: none; inset: 0; background: linear-gradient(105deg, transparent 20%, #45317D18, #6F69B830, #BB78AD30, #E59B7630, #ED9C9120, #F0D09C30, transparent 80%); transform: translateX(110%); animation: iris-comfy-arrive .65s ease-out 1; }',
|
|
114
|
+
'@keyframes iris-comfy-arrive { from { transform: translateX(-110%); opacity: 0; } 25% { opacity: 1; } to { transform: translateX(110%); opacity: 0; } }',
|
|
115
|
+
'.iris-comfy-head { display: flex; gap: 8px; align-items: center; justify-content: space-between; flex-wrap: wrap; }',
|
|
116
|
+
'.iris-comfy-head strong { font-size: 13px; }',
|
|
117
|
+
'.iris-comfy-tag { border: 1px solid color-mix(in srgb, #BB78AD 30%, var(--dsw-alias-border-l1, #3a3a44)); border-radius: 20px; padding: 2px 8px; font-size: 11px; color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
118
|
+
'.iris-comfy-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }',
|
|
119
|
+
'.iris-comfy-panel label, .iris-comfy-grid label { display: grid; min-width: 0; gap: 5px; font-size: 12px; }',
|
|
120
|
+
'.iris-comfy-panel :is(input,select,textarea) { min-width: 0; max-width: 100%; width: 100%; box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 8px; padding: 6px 8px; background: var(--dsw-alias-bg-layer-2, #32323c); color: var(--dsw-alias-label-primary, #e6e6e6); font: inherit; }',
|
|
121
|
+
'.iris-comfy-panel input[type=checkbox] { width: auto; min-height: 0; }',
|
|
122
|
+
'.iris-comfy-panel summary { cursor: pointer; font-size: 12px; padding: 4px 0; }',
|
|
123
|
+
'.iris-comfy-panel details > div { margin-top: 8px; }',
|
|
124
|
+
'.iris-comfy-slots { display: grid; gap: 6px; max-height: 280px; overflow: auto; }',
|
|
125
|
+
'.iris-comfy-slot { display: flex; gap: 8px; align-items: center; min-width: 0; padding: 7px 0; border-bottom: 1px solid var(--dsw-alias-border-l1, #3a3a44); }',
|
|
126
|
+
'.iris-comfy-slot label { flex: 1; display: flex; align-items: center; gap: 7px; overflow-wrap: anywhere; }',
|
|
127
|
+
'.iris-comfy-slot select { width: auto; max-width: 100px; }',
|
|
128
|
+
'.iris-comfy-result { font-size: 12px; line-height: 1.65; overflow-wrap: anywhere; }',
|
|
129
|
+
'.iris-comfy-result ul { padding-left: 20px; margin: 5px 0; }',
|
|
130
|
+
'.iris-comfy-panel .iris-act-run, .iris-comfy-card .iris-act-run { background: #6F69B8; color: #fff; border-color: transparent; }',
|
|
131
|
+
'@media (max-width: 640px) { .iris-comfy-grid { grid-template-columns: minmax(0, 1fr); } .iris-comfy-panel { padding: 10px; } }',
|
|
132
|
+
'@media (prefers-reduced-motion: reduce) { .iris-comfy-panel::before { animation: none; opacity: 0; } }',
|
|
112
133
|
'.iris-workbench-page .iris-pm-mrows { max-height: 400px; gap: 8px; }',
|
|
113
134
|
'.iris-workbench-page .iris-pm-mrow { min-width: 0; padding: 10px; gap: 6px; flex-wrap: wrap; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 10px; }',
|
|
114
135
|
'.iris-workbench-page .iris-pm-mname { flex-basis: 100%; white-space: normal; overflow-wrap: anywhere; font-size: 13px; }',
|
|
@@ -183,7 +204,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
183
204
|
/* ---- 操作卡片组(阶段 5 GUI 直连) ---- */
|
|
184
205
|
'.iris-act-group { display: grid; gap: 8px; }',
|
|
185
206
|
'.iris-act-card { background: var(--dsw-alias-bg-layer-1, #26262e); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 8px; overflow: hidden; }',
|
|
186
|
-
'.iris-act-head { display: flex; align-items: center; gap: 8px; padding: 8px 10px; cursor: pointer; user-select: none; }',
|
|
207
|
+
'.iris-act-head { display: flex; align-items: center; width:100%; gap: 8px; padding: 8px 10px; border:0; background:transparent; color:inherit; font:inherit; text-align:left; cursor: pointer; user-select: none; }',
|
|
187
208
|
'.iris-act-head:hover { background: var(--dsw-alias-bg-layer-2, #32323c); }',
|
|
188
209
|
'.iris-act-title { font-size: 13px; font-weight: 600; flex: 1; }',
|
|
189
210
|
'.iris-act-arrow { color: var(--dsw-alias-label-secondary, #9a9a9a); font-size: 10px; transition: transform 0.15s; }',
|
|
@@ -317,7 +338,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
317
338
|
'.iris-bubble-more { font-size: 11px; color: var(--dsw-alias-brand-primary, #7aa2f7); background: transparent; border: none; cursor: pointer; padding: 4px 2px; text-align: left; }',
|
|
318
339
|
/* 历史浏览器(设置页按日期分组折叠) */
|
|
319
340
|
'.iris-hist-group { margin-bottom: 4px; }',
|
|
320
|
-
'.iris-hist-head { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-secondary, #9a9a9a); cursor: pointer; padding: 4px 2px; }',
|
|
341
|
+
'.iris-hist-head { display: flex; align-items: center; width:100%; gap: 6px; border:0; background:transparent; text-align:left; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-secondary, #9a9a9a); cursor: pointer; padding: 4px 2px; }',
|
|
321
342
|
'.iris-hist-head:hover { color: var(--dsw-alias-label-primary, #e6e6e6); }',
|
|
322
343
|
/* 常用卡片选择器 + 清理区 */
|
|
323
344
|
'.iris-pick-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 10px; }',
|
|
@@ -396,7 +417,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
396
417
|
'.iris-ff-val { font-size: 11px; color: var(--dsw-alias-label-secondary, #9a9a9a); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }',
|
|
397
418
|
'.iris-ff-val.set { color: var(--dsw-alias-state-success-primary, #6fcf6f); }',
|
|
398
419
|
'.iris-ff-list { display: grid; gap: 2px; max-height: 160px; overflow: auto; border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 6px; padding: 4px; background: var(--dsw-alias-bg-layer-2, #32323c); }',
|
|
399
|
-
'.iris-ff-item { display: flex; align-items: center; gap: 6px; font-size: 11px; cursor: pointer; padding: 2px 4px; border-radius: 4px; }',
|
|
420
|
+
'.iris-ff-item { display: flex; align-items: center; width:100%; gap: 6px; border:0; background:transparent; color:inherit; text-align:left; font-size: 11px; cursor: pointer; padding: 2px 4px; border-radius: 4px; }',
|
|
400
421
|
'.iris-ff-item:hover { background: var(--dsw-alias-bg-layer-1, #26262e); }',
|
|
401
422
|
'.iris-ff-item .iris-ff-src { color: var(--dsw-alias-brand-primary, #7aa2f7); font-size: 10px; }',
|
|
402
423
|
/* 对话框级提示词优化器:桌面浮层 + 窄屏底部 Sheet */
|
|
@@ -546,6 +567,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
546
567
|
for (var i = 0; i < sharedListeners.length; i++) {
|
|
547
568
|
sharedListeners[i](data);
|
|
548
569
|
}
|
|
570
|
+
window.dispatchEvent(new CustomEvent('iris-core-refresh-tick'));
|
|
549
571
|
}
|
|
550
572
|
function load() {
|
|
551
573
|
fetch('/iris/api/state')
|
|
@@ -553,7 +575,6 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
553
575
|
.then(function (data) {
|
|
554
576
|
if (data) {
|
|
555
577
|
apply(data);
|
|
556
|
-
window.dispatchEvent(new CustomEvent('iris-core-refresh-tick'));
|
|
557
578
|
}
|
|
558
579
|
})
|
|
559
580
|
.catch(function () { /* 网络抖动静默 */ });
|
|
@@ -676,7 +697,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
676
697
|
: option.protocolSupported === false ? option.reason || '当前接口协议不支持此功能。'
|
|
677
698
|
: option.unavailable && !health.rateLimited && !health.modelUnavailable && !option.modelUnavailable ? option.reason || '连接尚未就绪,请先检查供应商设置。' : '';
|
|
678
699
|
return { ...option, providerName: provider ? provider.name : 'DSH 宿主', health: health,
|
|
679
|
-
taskOnly: row.taskOnly || host, reason: reason, canTest: !reason };
|
|
700
|
+
taskOnly: row.taskOnly || host || option.imageRouting && option.imageRouting.mediaProtocol === 'comfyui', reason: reason, canTest: !reason };
|
|
680
701
|
});
|
|
681
702
|
}
|
|
682
703
|
function CapabilityHealthOverview() {
|
|
@@ -745,7 +766,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
745
766
|
if (field) { revealWorkbenchItem(field); field.scrollIntoView({ block: 'center', behavior: 'smooth' }); field.focus({ preventScroll: true }); }
|
|
746
767
|
}
|
|
747
768
|
function openTask(row) {
|
|
748
|
-
var card = document.querySelector('[data-iris-action="' + row.action + '"]');
|
|
769
|
+
var card = document.querySelector('[data-iris-action="' + (row.cap === 'image-gen' ? 'image' : row.action) + '"]');
|
|
749
770
|
if (!card) return;
|
|
750
771
|
revealWorkbenchItem(card);
|
|
751
772
|
if (!card.classList.contains('open')) card.querySelector('.iris-act-head').click();
|
|
@@ -841,7 +862,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
841
862
|
React.createElement('button', { className: 'iris-health-btn' + (!option.taskOnly ? ' primary' : ''),
|
|
842
863
|
disabled: !fresh || !option.canTest || !!busy || !!(result && result.phase === 'unknown'),
|
|
843
864
|
onClick: function () { if (running.current) return; if (option.taskOnly) openTask(row); else confirmPair[1]({ cap: row.cap, ref: option.ref }); } },
|
|
844
|
-
option.taskOnly ? '前往功能验证 ↗' : result && result.phase === 'running' ? '正在实测…' : '实测此模型')),
|
|
865
|
+
option.taskOnly ? option.comfyWorkflow ? '前往画图选择工作流 ↗' : '前往功能验证 ↗' : result && result.phase === 'running' ? '正在实测…' : '实测此模型')),
|
|
845
866
|
option.reason ? React.createElement('p', { className: 'iris-health-caption' }, option.reason) : null,
|
|
846
867
|
option.ref === 'host:current-vision' ? React.createElement('p', { className: 'iris-health-caption' },
|
|
847
868
|
'宿主视觉通过真实图片任务验证,请在打开的表单中选择 DSH 当前默认模型。') : null,
|
|
@@ -1176,6 +1197,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1176
1197
|
React.createElement('option', { value: 'dashscope' }, 'DashScope'),
|
|
1177
1198
|
React.createElement('option', { value: 'openai-images' }, 'OpenAI Images'),
|
|
1178
1199
|
React.createElement('option', { value: 'openai-chat-images' }, '聊天生图(网关兼容)'),
|
|
1200
|
+
p.mediaProtocol === 'comfyui' || m.imageProtocol === 'comfyui' ? React.createElement('option', { value: 'comfyui' }, 'ComfyUI · API 工作流') : null,
|
|
1179
1201
|
React.createElement('option', { value: 'openai-responses-images' }, 'Responses 生图')),
|
|
1180
1202
|
m.imageRouting && m.imageRouting.protocolInferred ? ' · 协议待确认' : null) : null,
|
|
1181
1203
|
!m.directoryOnly && caps.includes('music-gen') ? React.createElement('label', { className: 'iris-pm-note' }, '音乐协议 ',
|
|
@@ -1190,9 +1212,10 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1190
1212
|
return React.createElement('div', { className: 'iris-pm-pool' },
|
|
1191
1213
|
React.createElement('div', { className: 'iris-pm-note' }, '模型池(' + models.filter(function (m) { return !m.directoryOnly; }).length + ')'
|
|
1192
1214
|
+ (models.some(function (m) { return m.directoryOnly; }) ? ' · 目录待确认 ' + models.filter(function (m) { return m.directoryOnly; }).length + ' 项' : '') + ':'),
|
|
1193
|
-
rows.length ? React.createElement('div', { className: 'iris-pm-mrows' }, rows) : React.createElement('div', { className: 'iris-pm-note' },
|
|
1194
|
-
|
|
1195
|
-
|
|
1215
|
+
rows.length ? React.createElement('div', { className: 'iris-pm-mrows' }, rows) : React.createElement('div', { className: 'iris-pm-note' }, p.mediaProtocol === 'comfyui'
|
|
1216
|
+
? '尚无工作流,请在上方导入 API 图并保存。' : '暂无模型 — 展开「模型目录」同步,或在下方手动添加'),
|
|
1217
|
+
p.mediaProtocol !== 'comfyui' ? React.createElement(ModelUsageForm, { provider: p, setupSuggested: props.setupSuggested,
|
|
1218
|
+
onNote: props.onNote, onDone: props.onDone }) : null);
|
|
1196
1219
|
}
|
|
1197
1220
|
|
|
1198
1221
|
function ModelUsageForm(props) {
|
|
@@ -1343,7 +1366,172 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1343
1366
|
React.createElement('div', { className: 'iris-wb-row' },
|
|
1344
1367
|
React.createElement('input', { value: value, placeholder: '留空则沿用 Base URL', onChange: function (e) { setValue(e.target.value); } }),
|
|
1345
1368
|
React.createElement('button', { className: 'iris-pm-btn', onClick: save }, '保存')),
|
|
1346
|
-
React.createElement('span', { className: 'iris-pm-note' },
|
|
1369
|
+
React.createElement('span', { className: 'iris-pm-note' }, p.mediaProtocol === 'comfyui' ? '留空沿用已保存地址;反向代理有独立媒体地址时才填写。'
|
|
1370
|
+
: '百炼 Workspace 可填 …maas.aliyuncs.com/compatible-mode/v1;视觉仍使用普通 Base URL。'));
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
function ProviderAuthenticationEditor(props) {
|
|
1374
|
+
var p = props.provider, authPair = React.useState(p.auth), keyPair = React.useState(''), notePair = React.useState('');
|
|
1375
|
+
var busyPair = React.useState(false), active = React.useRef(null), unknownPair = React.useState(false);
|
|
1376
|
+
React.useEffect(function () { authPair[1](p.auth); }, [p.auth]);
|
|
1377
|
+
React.useEffect(function () { return function () { if (active.current) active.current.abort(); }; }, []);
|
|
1378
|
+
async function save() {
|
|
1379
|
+
if (active.current || unknownPair[0]) return;
|
|
1380
|
+
var controller = new AbortController(), timer = setTimeout(function () { controller.abort(); }, 15000);
|
|
1381
|
+
active.current = controller; busyPair[1](true);
|
|
1382
|
+
try {
|
|
1383
|
+
var result = await postAction('providers_upsert', { id: p.id, auth: authPair[0], ...(authPair[0] === 'bearer' && keyPair[0].trim() ? { apiKey: keyPair[0].trim() } : {}) }, { signal: controller.signal });
|
|
1384
|
+
if (controller.signal.aborted) throw new Error('请求已结束');
|
|
1385
|
+
if (!result.ok) { notePair[1](result.d.error || '保存失败'); return; }
|
|
1386
|
+
keyPair[1](''); notePair[1]('认证设置已保存'); props.onDone();
|
|
1387
|
+
} catch (_) { unknownPair[1](true); notePair[1]('保存结果未知,请重新打开此供应商核对;未自动重试。'); }
|
|
1388
|
+
finally { clearTimeout(timer); active.current = null; busyPair[1](false); }
|
|
1389
|
+
}
|
|
1390
|
+
return React.createElement('details', {}, React.createElement('summary', {}, '认证设置'),
|
|
1391
|
+
React.createElement('fieldset', { disabled: busyPair[0] || unknownPair[0], style: { border: 0, padding: 0, margin: '8px 0', minWidth: 0 } },
|
|
1392
|
+
React.createElement('label', { className: 'iris-pm-field' }, '认证方式', React.createElement('select', { value: authPair[0], onChange: function (event) { authPair[1](event.target.value); } },
|
|
1393
|
+
React.createElement('option', { value: 'none' }, '无需认证'), React.createElement('option', { value: 'bearer' }, 'Bearer · API Key'))),
|
|
1394
|
+
authPair[0] === 'bearer' ? React.createElement('label', { className: 'iris-pm-field' }, '替换 API Key(留空保留)', React.createElement('input', { type: 'password', value: keyPair[0], onChange: function (event) { keyPair[1](event.target.value); } })) : null,
|
|
1395
|
+
React.createElement('p', { className: 'iris-pm-note' }, '无需认证时不会发送保留的 Key;更改认证会使旧的健康证据失效。'),
|
|
1396
|
+
React.createElement('button', { className: 'iris-pm-btn', onClick: save }, busyPair[0] ? '保存中…' : '保存认证')),
|
|
1397
|
+
notePair[0] ? React.createElement('div', { role: 'status', className: 'iris-pm-note' }, notePair[0]) : null);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
function ComfyCheckResult(props) {
|
|
1401
|
+
var report = props.report;
|
|
1402
|
+
if (!report) return null;
|
|
1403
|
+
return React.createElement('div', { className: 'iris-comfy-result', role: 'status' },
|
|
1404
|
+
React.createElement('strong', {}, report.ok ? '静态检查通过 · 未生成图片' : '需要修正 ' + report.issues.length + ' 项 · 未生成图片'),
|
|
1405
|
+
report.issues.length ? React.createElement('ul', {}, report.issues.map(function (issue, i) {
|
|
1406
|
+
return React.createElement('li', { key: i }, issue.message);
|
|
1407
|
+
})) : null,
|
|
1408
|
+
React.createElement('div', { className: 'iris-pm-note' }, '只读取节点定义;不证明 GPU、显存、模型家族或自定义节点能够实际运行。'));
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
function comfySlots(draft) {
|
|
1412
|
+
var result = [], labels = { lora_name: 'LoRA', strength_model: '模型权重', strength_clip: 'CLIP 权重',
|
|
1413
|
+
ckpt_name: 'Checkpoint', seed: '种子', steps: '步数', cfg: 'CFG', sampler_name: '采样器', scheduler: '调度器', text: '文本' };
|
|
1414
|
+
Object.entries(draft.graph).forEach(function (pair) {
|
|
1415
|
+
var id = pair[0], node = pair[1];
|
|
1416
|
+
Object.entries(node.inputs).forEach(function (field) {
|
|
1417
|
+
var input = field[0], value = field[1];
|
|
1418
|
+
if (!['string', 'boolean', 'number'].includes(typeof value) || id === draft.promptNode && input === 'text'
|
|
1419
|
+
|| id === draft.sizeNode && ['width', 'height'].includes(input)) return;
|
|
1420
|
+
var parameter = draft.parameters.find(function (item) { return item.node === id && item.input === input; });
|
|
1421
|
+
result.push({ key: id + '/' + input, value: value, selected: !!parameter, parameter: parameter || {
|
|
1422
|
+
id: ('n' + id.replace(/[^A-Za-z0-9_]/g, '_') + '_' + input.replace(/[^A-Za-z0-9_]/g, '_')).slice(0, 54) + '_' + result.length,
|
|
1423
|
+
label: (labels[input] || input) + ' · ' + node.class_type + ' #' + id, node: id, input: input,
|
|
1424
|
+
type: typeof value === 'number' ? Number.isSafeInteger(value) && !/^(strength_|cfg$|denoise$)/.test(input) ? 'integer' : 'number' : typeof value
|
|
1425
|
+
} });
|
|
1426
|
+
});
|
|
1427
|
+
});
|
|
1428
|
+
return result;
|
|
1429
|
+
}
|
|
1430
|
+
function comfyDraftWorkflow(draft) {
|
|
1431
|
+
if (!draft.promptNode || !draft.outputNode) throw new Error('请明确选择正向提示词与图片输出节点');
|
|
1432
|
+
var parameters = draft.parameters;
|
|
1433
|
+
return { version: parameters.length ? 1 : 0, graph: draft.graph, promptNode: draft.promptNode, outputNode: draft.outputNode,
|
|
1434
|
+
...(draft.sizeNode ? { sizeNode: draft.sizeNode } : {}), ...(parameters.length ? { parameters: parameters } : {}) };
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
function ComfyWorkflowEditor(props) {
|
|
1438
|
+
var p = props.provider, draftPair = React.useState(null), draft = draftPair[0], namePair = React.useState('');
|
|
1439
|
+
var autoPair = React.useState(props.setupSuggested === true), reportPair = React.useState(null), notePair = React.useState('');
|
|
1440
|
+
var busyPair = React.useState(''), unknownPair = React.useState(false), replacePair = React.useState(false);
|
|
1441
|
+
var active = React.useRef(null), importVersion = React.useRef(0), original = React.useRef({});
|
|
1442
|
+
React.useEffect(function () { return function () { importVersion.current++; if (active.current) active.current.abort(); }; }, []);
|
|
1443
|
+
function change(field, value) {
|
|
1444
|
+
var next = { ...draft, [field]: value };
|
|
1445
|
+
if (field === 'promptNode' || field === 'sizeNode') next.parameters = next.parameters.filter(function (item) {
|
|
1446
|
+
return !(item.node === next.promptNode && item.input === 'text' || item.node === next.sizeNode && ['width', 'height'].includes(item.input));
|
|
1447
|
+
});
|
|
1448
|
+
draftPair[1](next); reportPair[1](null); replacePair[1](false); notePair[1]('');
|
|
1449
|
+
}
|
|
1450
|
+
async function read(event) {
|
|
1451
|
+
var file = event.target.files[0], ticket = ++importVersion.current;
|
|
1452
|
+
if (!file) return;
|
|
1453
|
+
try {
|
|
1454
|
+
if (file.size > 40 * 1024) throw new Error('工作流最多 40 KiB');
|
|
1455
|
+
var raw = JSON.parse(await file.text()), template = raw && raw.graph && [0, 1].includes(raw.version) ? raw : null;
|
|
1456
|
+
if (ticket !== importVersion.current) return;
|
|
1457
|
+
var graph = template ? template.graph : raw, entries = graph && typeof graph === 'object' && !Array.isArray(graph) ? Object.entries(graph) : [];
|
|
1458
|
+
if (!entries.length || entries.length > 256 || entries.some(function (entry) { var node = entry[1]; return !node || typeof node.class_type !== 'string'
|
|
1459
|
+
|| !node.inputs || typeof node.inputs !== 'object' || Array.isArray(node.inputs); })) throw new Error('请使用 Export (API) 节点图或 Iris 参数模板,不支持画布 JSON');
|
|
1460
|
+
var outputs = entries.filter(function (entry) { return entry[1].class_type === 'SaveImage'; });
|
|
1461
|
+
if (template && (template.version === 0 && template.parameters !== undefined || template.version === 1 && (!Array.isArray(template.parameters) || !template.parameters.length))
|
|
1462
|
+
|| template && template.parameters !== undefined && (!Array.isArray(template.parameters) || template.parameters.some(function (item) {
|
|
1463
|
+
return !item || typeof item.id !== 'string' || typeof item.node !== 'string' || typeof item.input !== 'string' || !['string', 'integer', 'number', 'boolean'].includes(item.type);
|
|
1464
|
+
}))) throw new Error('参数声明格式不正确');
|
|
1465
|
+
draftPair[1]({ graph: graph, promptNode: template ? template.promptNode : '', outputNode: template ? template.outputNode : outputs.length === 1 ? outputs[0][0] : '',
|
|
1466
|
+
sizeNode: template && template.sizeNode || '', parameters: template && template.parameters || [] });
|
|
1467
|
+
original.current = Object.fromEntries((p.models || []).map(function (model) { return [model.id, model.comfyWorkflow && model.comfyWorkflow.sha256 || null]; }));
|
|
1468
|
+
namePair[1](file.name.replace(/\.json$/i, '').slice(0, 120)); reportPair[1](null); replacePair[1](false);
|
|
1469
|
+
notePair[1]('已读取 ' + entries.length + ' 个节点。请核对绑定,再勾选希望在画图时调整的参数。');
|
|
1470
|
+
} catch (error) { if (ticket === importVersion.current) notePair[1]('导入失败:' + error.message + ';原草稿保留。'); }
|
|
1471
|
+
}
|
|
1472
|
+
async function operate(kind, confirmed) {
|
|
1473
|
+
if (active.current || unknownPair[0]) return;
|
|
1474
|
+
var workflow, name = namePair[0].trim();
|
|
1475
|
+
try { workflow = comfyDraftWorkflow(draft); if (kind === 'save' && !name) throw new Error('请填写工作流名称'); }
|
|
1476
|
+
catch (error) { notePair[1](error.message); return; }
|
|
1477
|
+
var exists = Object.hasOwn(original.current, name);
|
|
1478
|
+
if (kind === 'save' && exists && !confirmed) { replacePair[1](true); return; }
|
|
1479
|
+
var controller = new AbortController(); active.current = controller; busyPair[1](kind); notePair[1]('');
|
|
1480
|
+
var timer = setTimeout(function () { controller.abort(); }, 20000);
|
|
1481
|
+
try {
|
|
1482
|
+
var result = await postAction(kind === 'check' ? 'providers_check_comfy_workflow' : 'providers_save_comfy_workflow', kind === 'check'
|
|
1483
|
+
? { id: p.id, workflow: workflow } : { id: p.id, model_id: name, workflow: workflow,
|
|
1484
|
+
expectedSha: exists ? original.current[name] : null, enableAutomatic: autoPair[0] }, { signal: controller.signal });
|
|
1485
|
+
if (controller.signal.aborted) throw new Error('请求已结束');
|
|
1486
|
+
if (!result.ok || result.d.ok !== true) { notePair[1](result.d.error || '操作未完成'); return; }
|
|
1487
|
+
if (kind === 'check') reportPair[1](result.d.report);
|
|
1488
|
+
else {
|
|
1489
|
+
original.current[name] = result.d.comfyWorkflow.sha256; replacePair[1](false); notePair[1](result.d.text);
|
|
1490
|
+
props.onDone(); window.dispatchEvent(new CustomEvent('iris-function-configuration-changed'));
|
|
1491
|
+
}
|
|
1492
|
+
} catch (_) {
|
|
1493
|
+
if (kind === 'save') { unknownPair[1](true); notePair[1]('保存结果未知,未自动重试。请关闭并重新打开此供应商,核对已保存工作流;草稿仍在此处。'); }
|
|
1494
|
+
else { reportPair[1](null); notePair[1]('检查未完成,请核对连接后重试;未发起生成。'); }
|
|
1495
|
+
} finally { clearTimeout(timer); active.current = null; busyPair[1](''); }
|
|
1496
|
+
}
|
|
1497
|
+
var entries = draft ? Object.entries(draft.graph) : [], slots = draft ? comfySlots(draft) : [];
|
|
1498
|
+
function select(field, label, options) {
|
|
1499
|
+
return React.createElement('label', {}, label, React.createElement('select', { value: draft[field], onChange: function (event) { change(field, event.target.value); } },
|
|
1500
|
+
React.createElement('option', { value: '' }, field === 'sizeNode' ? '沿用工作流尺寸' : '请选择'),
|
|
1501
|
+
options.map(function (entry) { return React.createElement('option', { key: entry[0], value: entry[0] }, '#' + entry[0] + ' · ' + entry[1].class_type); })));
|
|
1502
|
+
}
|
|
1503
|
+
return React.createElement('section', { className: 'iris-comfy-panel', 'aria-label': 'ComfyUI 工作流' },
|
|
1504
|
+
React.createElement('div', { className: 'iris-comfy-head' }, React.createElement('strong', {}, 'ComfyUI · 工作流'), React.createElement('span', { className: 'iris-comfy-tag' }, '已有服务 · 单张文生图')),
|
|
1505
|
+
React.createElement('p', { className: 'iris-pm-note' }, '导入 API 工作流,保留已有节点与 LoRA。检查和保存不会生成图片。'),
|
|
1506
|
+
React.createElement('fieldset', { disabled: !!busyPair[0] || unknownPair[0], style: { border: 0, padding: 0, margin: 0, minWidth: 0, display: 'grid', gap: 10 } },
|
|
1507
|
+
React.createElement('label', {}, '导入工作流 JSON', React.createElement('input', { type: 'file', accept: '.json,application/json', onChange: read, 'data-comfy-file': p.id })),
|
|
1508
|
+
draft ? React.createElement(React.Fragment, {},
|
|
1509
|
+
React.createElement('label', {}, '工作流名称', React.createElement('input', { value: namePair[0], maxLength: 120, onChange: function (event) { namePair[1](event.target.value); replacePair[1](false); } })),
|
|
1510
|
+
React.createElement('div', { className: 'iris-comfy-grid' },
|
|
1511
|
+
select('promptNode', '正向提示词节点', entries.filter(function (entry) { return typeof entry[1].inputs.text === 'string'; })),
|
|
1512
|
+
select('outputNode', '图片输出节点', entries),
|
|
1513
|
+
select('sizeNode', '尺寸节点', entries.filter(function (entry) { return ['width', 'height'].every(function (key) { return Number.isSafeInteger(entry[1].inputs[key]) && entry[1].inputs[key] > 0; }); }))),
|
|
1514
|
+
React.createElement('details', {}, React.createElement('summary', {}, '公开参数 · 已选 ' + slots.filter(function (slot) { return slot.selected; }).length + ' / 32'),
|
|
1515
|
+
React.createElement('div', { className: 'iris-comfy-slots' }, slots.map(function (slot) {
|
|
1516
|
+
return React.createElement('div', { key: slot.key, className: 'iris-comfy-slot' },
|
|
1517
|
+
React.createElement('label', {}, React.createElement('input', { type: 'checkbox', checked: slot.selected, onChange: function (event) {
|
|
1518
|
+
if (event.target.checked && draft.parameters.length >= 32) { notePair[1]('最多公开 32 个参数'); return; }
|
|
1519
|
+
change('parameters', draft.parameters.filter(function (item) { return item.node !== slot.parameter.node || item.input !== slot.parameter.input; }).concat(event.target.checked ? [slot.parameter] : []));
|
|
1520
|
+
} }), slot.parameter.label || slot.parameter.id),
|
|
1521
|
+
slot.selected ? React.createElement('select', { 'aria-label': (slot.parameter.label || slot.parameter.id) + '类型', value: slot.parameter.type, onChange: function (event) {
|
|
1522
|
+
change('parameters', draft.parameters.map(function (item) { return item.id === slot.parameter.id ? { id: item.id, node: item.node, input: item.input, label: item.label, type: event.target.value } : item; }));
|
|
1523
|
+
} }, (typeof slot.value === 'number' ? [['integer', '整数'], ['number', '数值']] : [[typeof slot.value, typeof slot.value === 'boolean' ? '开关' : '文本']]).map(function (pair) { return React.createElement('option', { key: pair[0], value: pair[0] }, pair[1]); })) : null);
|
|
1524
|
+
}))),
|
|
1525
|
+
React.createElement('label', { style: { display: 'flex', alignItems: 'center' } }, React.createElement('input', { type: 'checkbox', checked: autoPair[0], onChange: function (event) { autoPair[1](event.target.checked); } }), '保存时将图片功能设为自动池'),
|
|
1526
|
+
React.createElement('div', { className: 'iris-wb-row' },
|
|
1527
|
+
React.createElement('button', { className: 'iris-pm-btn', onClick: function () { operate('check'); } }, busyPair[0] === 'check' ? '检查中…' : '只读检查'),
|
|
1528
|
+
React.createElement('button', { className: 'iris-act-run', onClick: function () { operate('save'); } }, busyPair[0] === 'save' ? '保存中…' : '保存工作流')),
|
|
1529
|
+
replacePair[0] ? React.createElement('div', { className: 'iris-comfy-result', role: 'group', 'aria-label': '确认替换工作流' },
|
|
1530
|
+
React.createElement('p', {}, '替换「' + namePair[0] + '」?旧任务不会改绑,更换模板可能影响旧任务的继续观察。'),
|
|
1531
|
+
React.createElement('button', { className: 'iris-pm-btn', onClick: function () { operate('save', true); } }, '确认替换'),
|
|
1532
|
+
React.createElement('button', { className: 'iris-pm-btn', onClick: function () { replacePair[1](false); } }, '取消')) : null) : null),
|
|
1533
|
+
notePair[0] ? React.createElement('div', { className: 'iris-comfy-result', role: 'status' }, notePair[0]) : null,
|
|
1534
|
+
React.createElement(ComfyCheckResult, { report: reportPair[0] }));
|
|
1347
1535
|
}
|
|
1348
1536
|
|
|
1349
1537
|
function ProviderManager() {
|
|
@@ -1355,7 +1543,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1355
1543
|
var addingPair = React.useState(false);
|
|
1356
1544
|
var adding = addingPair[0];
|
|
1357
1545
|
var setAdding = addingPair[1];
|
|
1358
|
-
var newForm = React.useState({ name: '', baseUrl: '', mediaBaseUrl: '', apiKey: '', type: 'openai', mediaProtocol: 'auto' });
|
|
1546
|
+
var newForm = React.useState({ name: '', baseUrl: '', mediaBaseUrl: '', apiKey: '', auth: 'bearer', type: 'openai', mediaProtocol: 'auto' });
|
|
1547
|
+
var savingRef = React.useRef(false), savingPair = React.useState(false), unknownPair = React.useState(false);
|
|
1359
1548
|
var hostConnections = React.useState([]);
|
|
1360
1549
|
var hostChoice = React.useState('');
|
|
1361
1550
|
var setupPair = React.useState(false);
|
|
@@ -1379,23 +1568,25 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1379
1568
|
return function () { clearInterval(timer); };
|
|
1380
1569
|
}, []);
|
|
1381
1570
|
function addProvider() {
|
|
1571
|
+
if (savingRef.current || unknownPair[0]) return;
|
|
1382
1572
|
var f = newForm[0];
|
|
1573
|
+
var payload;
|
|
1383
1574
|
if (hostChoice[0]) {
|
|
1384
1575
|
var connection = hostConnections[0].find(function (item) { return JSON.stringify([item.settingsNs, item.provider]) === hostChoice[0]; });
|
|
1385
1576
|
if (!connection || !connection.available) { setNote('请重新选择可用的 DSH 连接'); return; }
|
|
1386
|
-
|
|
1387
|
-
mediaProtocol: f.mediaProtocol, dshProvider: { provider: connection.provider, settingsNs: connection.settingsNs } }
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
});
|
|
1392
|
-
return;
|
|
1577
|
+
payload = { name: f.name || connection.name, type: 'openai', enabled: true,
|
|
1578
|
+
mediaProtocol: f.mediaProtocol, dshProvider: { provider: connection.provider, settingsNs: connection.settingsNs } };
|
|
1579
|
+
} else {
|
|
1580
|
+
if (!f.baseUrl.trim() || (f.auth !== 'none' && !f.apiKey.trim())) { setNote('请填写 Base URL;Bearer 认证还需要 API Key'); return; }
|
|
1581
|
+
payload = { ...f, ...(f.auth === 'none' ? { apiKey: '' } : {}) };
|
|
1393
1582
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1583
|
+
savingRef.current = true; savingPair[1](true);
|
|
1584
|
+
var controller = new AbortController(), timer = setTimeout(function () { controller.abort(); }, 15000);
|
|
1585
|
+
postAction('providers_upsert', payload, { signal: controller.signal }).then(function (r) {
|
|
1586
|
+
if (r.ok) { setAdding(false); openPair[1](r.d.providerId); setNote(f.mediaProtocol === 'comfyui' ? '连接已保存,请导入 API 工作流;尚未生成图片' : '已连接,请在下方确认模型用途'); refresh(); }
|
|
1397
1587
|
else setNote(r.d.error || '添加失败');
|
|
1398
|
-
});
|
|
1588
|
+
}).catch(function () { unknownPair[1](true); setNote('保存结果未知,请关闭添加表单并核对供应商列表;未自动重试。'); })
|
|
1589
|
+
.finally(function () { clearTimeout(timer); savingRef.current = false; savingPair[1](false); });
|
|
1399
1590
|
}
|
|
1400
1591
|
function toggle(p) {
|
|
1401
1592
|
postAction('providers_upsert', { id: p.id, enabled: !p.enabled }).then(function (r) {
|
|
@@ -1426,19 +1617,26 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1426
1617
|
var body = null;
|
|
1427
1618
|
if (open) {
|
|
1428
1619
|
body = React.createElement('div', { className: 'iris-pm-body', id: 'iris-provider-' + p.id },
|
|
1429
|
-
React.createElement('div', { className: 'iris-pm-note' }, 'Key ' + (p.apiKeyHint || '未配置') + ' · ' + (p.type || '') + ' · ' + (p.mediaProtocol || '')),
|
|
1620
|
+
React.createElement('div', { className: 'iris-pm-note' }, (p.auth === 'none' ? '无需认证' : 'Key ' + (p.apiKeyHint || '未配置')) + ' · ' + (p.type || '') + ' · ' + (p.mediaProtocol || '')),
|
|
1430
1621
|
p.dshProvider ? React.createElement('div', { className: 'iris-pm-note' }, '引用 DSH:' + p.dshProvider.provider
|
|
1431
1622
|
+ ' · 地址和密钥由宿主管理;' + (p.connection && p.connection.available ? '连接可用' : ((p.connection && p.connection.reason) || '连接不可用'))) : null,
|
|
1432
1623
|
p.protocolInferred ? React.createElement('div', { className: 'iris-pm-note' }, '协议为推断值,请确认') : null,
|
|
1433
|
-
React.createElement(
|
|
1434
|
-
|
|
1624
|
+
p.mediaProtocol === 'comfyui' ? React.createElement('details', {}, React.createElement('summary', {}, '连接设置'),
|
|
1625
|
+
React.createElement('div', { className: 'iris-pm-note' }, '服务地址:' + (p.mediaBaseUrl || p.baseUrl)),
|
|
1626
|
+
!p.dshProvider ? React.createElement(ProviderEndpointEditor, { provider: p, onNote: setNote, onDone: refresh }) : null,
|
|
1627
|
+
!p.dshProvider ? React.createElement(ProviderAuthenticationEditor, { provider: p, onDone: refresh }) : null)
|
|
1628
|
+
: React.createElement(React.Fragment, {}, !p.dshProvider ? React.createElement(ProviderEndpointEditor, { provider: p, onNote: setNote, onDone: refresh }) : null,
|
|
1629
|
+
!p.dshProvider ? React.createElement(ProviderAuthenticationEditor, { provider: p, onDone: refresh }) : null,
|
|
1630
|
+
React.createElement(VisionInputEditor, { provider: p, onNote: setNote, onDone: refresh })),
|
|
1435
1631
|
React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '媒体协议'), React.createElement('select', { value: p.protocolInferred ? 'auto' : (p.mediaProtocol || 'auto'), onChange: function (e) { setProtocol(p, e.target.value); } },
|
|
1436
1632
|
React.createElement('option', { value: 'auto' }, '自动'),
|
|
1437
|
-
p.mediaProtocol && !['dashscope', 'openai-images', 'openai-chat-images', 'openai-responses-images', 'auto'].includes(p.mediaProtocol) ? React.createElement('option', { value: p.mediaProtocol }, p.mediaProtocol + '(未支持)') : null,
|
|
1633
|
+
p.mediaProtocol && !['dashscope', 'openai-images', 'openai-chat-images', 'openai-responses-images', 'comfyui', 'auto'].includes(p.mediaProtocol) ? React.createElement('option', { value: p.mediaProtocol }, p.mediaProtocol + '(未支持)') : null,
|
|
1438
1634
|
React.createElement('option', { value: 'dashscope' }, 'DashScope(仅阿里云官方 HTTPS)'),
|
|
1439
1635
|
React.createElement('option', { value: 'openai-images' }, 'OpenAI Images 兼容'),
|
|
1440
1636
|
React.createElement('option', { value: 'openai-chat-images' }, '聊天生图(网关兼容)'),
|
|
1441
|
-
React.createElement('option', { value: 'openai-responses-images' }, 'Responses 生图')
|
|
1637
|
+
React.createElement('option', { value: 'openai-responses-images' }, 'Responses 生图'),
|
|
1638
|
+
!p.dshProvider ? React.createElement('option', { value: 'comfyui' }, 'ComfyUI · API 工作流') : null)),
|
|
1639
|
+
p.mediaProtocol === 'comfyui' ? React.createElement(ComfyWorkflowEditor, { key: p.id, provider: p, setupSuggested: setupPair[0], onDone: refresh }) : null,
|
|
1442
1640
|
React.createElement('div', { className: 'iris-pm-note' }, '能力: ' + ((p.capabilities || []).join(' / ') || '无')),
|
|
1443
1641
|
React.createElement(ProviderDirectory, { provider: p, onDone: refresh }),
|
|
1444
1642
|
React.createElement(ModelPool, { provider: p, setupSuggested: setupPair[0], onNote: setNote, onDone: refresh, testModel: testModel, removeModel: removeModel }),
|
|
@@ -1455,9 +1653,17 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1455
1653
|
});
|
|
1456
1654
|
var addBlock = null;
|
|
1457
1655
|
if (adding) {
|
|
1458
|
-
addBlock = React.createElement('
|
|
1656
|
+
addBlock = React.createElement('fieldset', { className: 'iris-pm-card' + (newForm[0].mediaProtocol === 'comfyui' ? ' iris-comfy-panel' : ''), disabled: savingPair[0] || unknownPair[0] },
|
|
1657
|
+
!hostChoice[0] ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '连接预设'),
|
|
1658
|
+
React.createElement('select', { value: newForm[0].mediaProtocol === 'comfyui' ? 'comfyui' : 'custom', onChange: function (e) {
|
|
1659
|
+
newForm[1](e.target.value === 'comfyui' ? { name: 'ComfyUI', baseUrl: 'http://127.0.0.1:8188', mediaBaseUrl: '', apiKey: '', type: 'openai', auth: 'none', mediaProtocol: 'comfyui' }
|
|
1660
|
+
: { name: '', baseUrl: '', mediaBaseUrl: '', apiKey: '', type: 'openai', auth: 'bearer', mediaProtocol: 'auto' });
|
|
1661
|
+
} }, React.createElement('option', { value: 'custom' }, '自定义供应商'), React.createElement('option', { value: 'comfyui' }, 'ComfyUI · 已有服务'))) : null,
|
|
1459
1662
|
React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '连接来源'),
|
|
1460
|
-
React.createElement('select', { value: hostChoice[0], onChange: function (e) {
|
|
1663
|
+
React.createElement('select', { value: hostChoice[0], onChange: function (e) {
|
|
1664
|
+
hostChoice[1](e.target.value);
|
|
1665
|
+
if (e.target.value && newForm[0].mediaProtocol === 'comfyui') newForm[1]({ name: '', baseUrl: '', mediaBaseUrl: '', apiKey: '', auth: 'bearer', type: 'openai', mediaProtocol: 'auto' });
|
|
1666
|
+
} },
|
|
1461
1667
|
React.createElement('option', { value: '' }, '手动填写供应商'),
|
|
1462
1668
|
hostConnections[0].map(function (connection) { return React.createElement('option', {
|
|
1463
1669
|
key: JSON.stringify([connection.settingsNs, connection.provider]), value: JSON.stringify([connection.settingsNs, connection.provider]), disabled: !connection.available
|
|
@@ -1466,26 +1672,82 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1466
1672
|
React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '名称'), React.createElement('input', { value: newForm[0].name, placeholder: '如 阿里云百炼', onChange: function (e) { newForm[1]({ ...newForm[0], name: e.target.value }); } })),
|
|
1467
1673
|
!hostChoice[0] ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, 'Base URL *'), React.createElement('input', { value: newForm[0].baseUrl, placeholder: 'https://dashscope.aliyuncs.com/compatible-mode/v1', onChange: function (e) { newForm[1]({ ...newForm[0], baseUrl: e.target.value }); } })) : null,
|
|
1468
1674
|
!hostChoice[0] ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '媒体 Base URL(可选)'), React.createElement('input', { value: newForm[0].mediaBaseUrl, placeholder: 'Workspace 媒体端点;留空沿用 Base URL', onChange: function (e) { newForm[1]({ ...newForm[0], mediaBaseUrl: e.target.value }); } })) : null,
|
|
1469
|
-
!hostChoice[0] ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '
|
|
1675
|
+
!hostChoice[0] ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '认证方式'), React.createElement('select', { value: newForm[0].auth, onChange: function (e) { newForm[1]({ ...newForm[0], auth: e.target.value }); } },
|
|
1676
|
+
React.createElement('option', { value: 'bearer' }, 'Bearer · API Key'), React.createElement('option', { value: 'none' }, '无需认证(本地或内网服务)'))) : null,
|
|
1677
|
+
!hostChoice[0] && newForm[0].auth !== 'none' ? React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, 'API Key *'), React.createElement('input', { type: 'password', value: newForm[0].apiKey, placeholder: 'sk-...', onChange: function (e) { newForm[1]({ ...newForm[0], apiKey: e.target.value }); } })) : null,
|
|
1470
1678
|
React.createElement('div', { className: 'iris-pm-field' }, React.createElement('label', {}, '媒体协议'), React.createElement('select', { value: newForm[0].mediaProtocol, onChange: function (e) { newForm[1]({ ...newForm[0], mediaProtocol: e.target.value }); } },
|
|
1471
1679
|
React.createElement('option', { value: 'auto' }, hostChoice[0] ? '请选择该服务支持的媒体协议' : '自动(按媒体 Base URL 安全判断)'),
|
|
1472
1680
|
React.createElement('option', { value: 'dashscope' }, 'DashScope(阿里云官方)'),
|
|
1473
1681
|
React.createElement('option', { value: 'openai-images' }, 'OpenAI Images 兼容'),
|
|
1474
1682
|
React.createElement('option', { value: 'openai-chat-images' }, '聊天生图(网关兼容)'),
|
|
1475
|
-
React.createElement('option', { value: 'openai-responses-images' }, 'Responses 生图')
|
|
1683
|
+
React.createElement('option', { value: 'openai-responses-images' }, 'Responses 生图'),
|
|
1684
|
+
!hostChoice[0] ? React.createElement('option', { value: 'comfyui' }, 'ComfyUI · API 工作流') : null)),
|
|
1685
|
+
newForm[0].mediaProtocol === 'comfyui' ? React.createElement('p', { className: 'iris-pm-note' }, '连接已有 ComfyUI 服务。先保存连接,再导入 Export (API) 工作流;Iris 不安装服务或模型。') : null,
|
|
1476
1686
|
React.createElement('div', { className: 'iris-wb-row' },
|
|
1477
|
-
React.createElement('button', { className: 'iris-act-run', onClick: addProvider }, '保存'),
|
|
1687
|
+
React.createElement('button', { className: 'iris-act-run', onClick: addProvider }, savingPair[0] ? '保存中…' : '保存'),
|
|
1478
1688
|
React.createElement('button', { className: 'iris-pm-btn', onClick: function () { setAdding(false); } }, '取消')));
|
|
1479
1689
|
}
|
|
1480
1690
|
return React.createElement('div', { className: 'iris-pm' },
|
|
1481
1691
|
React.createElement('div', { className: 'iris-pm-head' },
|
|
1482
1692
|
React.createElement('span', { className: 'iris-wb-muted' }, (list ? list.length : 0) + ' 个供应商'),
|
|
1483
|
-
React.createElement('button', { className: 'iris-pm-add', onClick: function () { if (!adding) { hostChoice[1](''); loadHostConnections(); } setAdding(!adding); } }, adding ? '收起' : '+ 添加供应商')),
|
|
1693
|
+
React.createElement('button', { className: 'iris-pm-add', disabled: savingPair[0], onClick: function () { if (!adding) { hostChoice[1](''); loadHostConnections(); } unknownPair[1](false); setAdding(!adding); } }, adding ? '收起' : '+ 添加供应商')),
|
|
1484
1694
|
addBlock,
|
|
1485
1695
|
cards.length ? cards : React.createElement('div', { className: 'iris-wb-empty' }, '暂无供应商 — 点右上角「+ 添加供应商」'),
|
|
1486
1696
|
note ? React.createElement('div', { className: 'iris-pm-act' }, note) : null);
|
|
1487
1697
|
}
|
|
1488
1698
|
|
|
1699
|
+
function comfyParameterLabel(parameter) {
|
|
1700
|
+
return parameter.label || ({ lora_name: 'LoRA', strength_model: '模型权重', strength_clip: 'CLIP 权重',
|
|
1701
|
+
ckpt_name: 'Checkpoint', seed: '种子', steps: '步数', sampler_name: '采样器', scheduler: '调度器', cfg: 'CFG' })[parameter.input]
|
|
1702
|
+
|| (parameter.id === 'negative' && parameter.input === 'text' ? '负面提示词' : parameter.id);
|
|
1703
|
+
}
|
|
1704
|
+
function ComfyParameters(props) {
|
|
1705
|
+
var option = props.option, parameters = option.comfyWorkflow.parameters || [];
|
|
1706
|
+
var reportPair = React.useState(null), choicesPair = React.useState([]), notePair = React.useState(''), busyPair = React.useState(false);
|
|
1707
|
+
var active = React.useRef(null), sequence = React.useRef(0);
|
|
1708
|
+
React.useEffect(function () { return function () { sequence.current++; if (active.current) active.current.abort(); }; }, []);
|
|
1709
|
+
function change(id, value) {
|
|
1710
|
+
sequence.current++; if (active.current) active.current.abort(); active.current = null; busyPair[1](false);
|
|
1711
|
+
reportPair[1](null); notePair[1]('参数已更改,提交前将重新预检。'); props.onChange({ ...props.values, [id]: value });
|
|
1712
|
+
}
|
|
1713
|
+
async function check() {
|
|
1714
|
+
if (active.current || props.disabled) return;
|
|
1715
|
+
var controller = new AbortController(), ticket = ++sequence.current; active.current = controller; busyPair[1](true); notePair[1]('');
|
|
1716
|
+
var timer = setTimeout(function () { controller.abort(); }, 20000);
|
|
1717
|
+
try {
|
|
1718
|
+
var result = await postAction('providers_check_comfy_workflow', { id: option.providerId, model_id: option.id,
|
|
1719
|
+
...(Object.keys(props.values).length ? { workflow_parameters: props.values } : {}) }, { signal: controller.signal });
|
|
1720
|
+
if (ticket !== sequence.current) return;
|
|
1721
|
+
if (!result.ok || result.d.ok !== true) { reportPair[1](null); notePair[1](result.d.error || '检查失败'); return; }
|
|
1722
|
+
reportPair[1](result.d.report); choicesPair[1](result.d.report.parameters || []);
|
|
1723
|
+
} catch (_) { if (ticket === sequence.current) { reportPair[1](null); notePair[1]('检查未完成;未发起生成。可核对服务后重试。'); } }
|
|
1724
|
+
finally { clearTimeout(timer); if (ticket === sequence.current) { active.current = null; busyPair[1](false); } }
|
|
1725
|
+
}
|
|
1726
|
+
return React.createElement('section', { className: 'iris-comfy-panel', 'aria-label': 'ComfyUI 画图参数' },
|
|
1727
|
+
React.createElement('div', { className: 'iris-comfy-head' }, React.createElement('strong', {}, 'ComfyUI · ' + option.id), React.createElement('span', { className: 'iris-comfy-tag' }, '单张文生图')),
|
|
1728
|
+
React.createElement('div', { className: 'iris-pm-note' }, '沿用工作流默认值,只调整已公开的参数。LoRA 候选可通过只读检查获取。'),
|
|
1729
|
+
parameters.length ? React.createElement('details', {}, React.createElement('summary', {}, '工作流参数 · ' + parameters.length),
|
|
1730
|
+
React.createElement('fieldset', { className: 'iris-comfy-grid', disabled: props.disabled, style: { border: 0, padding: 0, margin: '8px 0 0', minWidth: 0 } }, parameters.map(function (parameter) {
|
|
1731
|
+
var value = Object.hasOwn(props.values, parameter.id) ? props.values[parameter.id] : parameter.default;
|
|
1732
|
+
var checked = choicesPair[0].find(function (item) { return item.id === parameter.id; });
|
|
1733
|
+
var choices = checked && checked.availableChoices || parameter.choices, input;
|
|
1734
|
+
if (choices) input = React.createElement('select', { value: JSON.stringify(value), onChange: function (event) { change(parameter.id, JSON.parse(event.target.value)); } },
|
|
1735
|
+
!choices.includes(value) ? React.createElement('option', { value: JSON.stringify(value) }, String(value) + '(当前未列出)') : null,
|
|
1736
|
+
choices.map(function (choice) { return React.createElement('option', { key: JSON.stringify(choice), value: JSON.stringify(choice) }, String(choice)); }));
|
|
1737
|
+
else if (parameter.type === 'boolean') input = React.createElement('input', { type: 'checkbox', checked: value, onChange: function (event) { change(parameter.id, event.target.checked); } });
|
|
1738
|
+
else input = React.createElement(parameter.type === 'string' && parameter.input === 'text' ? 'textarea' : 'input', {
|
|
1739
|
+
type: parameter.type === 'string' ? 'text' : 'number', value: value, min: parameter.min, max: parameter.max, step: parameter.type === 'integer' ? 1 : 'any',
|
|
1740
|
+
onChange: function (event) { var text = event.target.value; change(parameter.id, parameter.type === 'string' || text === '' ? text : Number(text)); }
|
|
1741
|
+
});
|
|
1742
|
+
return React.createElement('label', { key: parameter.id }, comfyParameterLabel(parameter), input,
|
|
1743
|
+
checked && checked.choicesTruncated ? React.createElement('span', { className: 'iris-pm-note' }, '选项仅列前 256 项,可手填名称:',
|
|
1744
|
+
React.createElement('input', { type: 'text', value: value, onChange: function (event) { change(parameter.id, event.target.value); } })) : null);
|
|
1745
|
+
}))) : React.createElement('div', { className: 'iris-pm-note' }, '此模板未公开额外参数。'),
|
|
1746
|
+
React.createElement('div', {}, React.createElement('button', { className: 'iris-pm-btn', disabled: props.disabled || busyPair[0], onClick: check }, busyPair[0] ? '检查中…' : '只读检查并读取选项')),
|
|
1747
|
+
notePair[0] ? React.createElement('div', { className: 'iris-comfy-result', role: 'status' }, notePair[0]) : null,
|
|
1748
|
+
React.createElement(ComfyCheckResult, { report: reportPair[0] }));
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1489
1751
|
/* ---- 文件字段(阶段 10):让用户"看见并选文件",统一产出一个宿主路径 ---- */
|
|
1490
1752
|
function FileField(props) {
|
|
1491
1753
|
var value = props.value || '';
|
|
@@ -1546,7 +1808,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1546
1808
|
manual ? React.createElement('input', { className: 'iris-pm-madd', placeholder: '高级:粘贴 DSH 宿主上的绝对路径,如 /home/user/pic.png', value: value, onChange: function (e) { onChange(e.target.value); } }) : null,
|
|
1547
1809
|
menu === 'att' ? React.createElement('div', { className: 'iris-ff-list' },
|
|
1548
1810
|
(atts && atts.length) ? atts.map(function (a) {
|
|
1549
|
-
return React.createElement('
|
|
1811
|
+
return React.createElement('button', { type: 'button', key: a.attachmentId, className: 'iris-ff-item', onClick: function () { pickAttachment(a); } },
|
|
1550
1812
|
React.createElement('span', { className: 'iris-ff-src' }, a.source === 'iris' ? '🫧' : '🖼'),
|
|
1551
1813
|
React.createElement('span', {}, a.name || a.attachmentId.slice(0, 18)));
|
|
1552
1814
|
}) : React.createElement('div', { className: 'iris-wb-empty' }, '无可选附件(会话无图或 iris 未生成过)')) : null);
|
|
@@ -1570,6 +1832,9 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1570
1832
|
var modelPair = React.useState(null); // { assigned, options:[{id,providerId}], tested }
|
|
1571
1833
|
var modelInfo = modelPair[0];
|
|
1572
1834
|
var setModelInfo = modelPair[1];
|
|
1835
|
+
var submitting = React.useRef(false);
|
|
1836
|
+
var selectedOption = modelInfo && modelInfo.options.find(function (option) { return option.ref === vals.model; });
|
|
1837
|
+
var selectedComfy = action === 'image' && selectedOption && selectedOption.comfyWorkflow;
|
|
1573
1838
|
var irisState = useIrisState(5000);
|
|
1574
1839
|
function setVal(key, value) { var nv = {}; Object.keys(vals).forEach(function (k) { nv[k] = vals[k]; }); nv[key] = value; setVals(nv); }
|
|
1575
1840
|
function loadModels(event) {
|
|
@@ -1595,19 +1860,30 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1595
1860
|
return function () { window.removeEventListener('iris-function-configuration-changed', loadModels); };
|
|
1596
1861
|
}, [open]);
|
|
1597
1862
|
function submit() {
|
|
1598
|
-
if (run === 'running') return;
|
|
1863
|
+
if (submitting.current || run === 'running' || run && run.unknown) return;
|
|
1864
|
+
var payload = vals;
|
|
1865
|
+
if (selectedComfy) {
|
|
1866
|
+
var values = vals.workflow_parameters || {}, declared = selectedComfy.parameters || [];
|
|
1867
|
+
if (Object.keys(values).some(function (id) { var field = declared.find(function (item) { return item.id === id; }), value = values[id];
|
|
1868
|
+
return !field || (field.type === 'integer' ? !Number.isSafeInteger(value) : field.type === 'number' ? typeof value !== 'number' || !Number.isFinite(value) : typeof value !== field.type)
|
|
1869
|
+
|| field.min !== undefined && value < field.min || field.max !== undefined && value > field.max || field.choices && !field.choices.includes(value);
|
|
1870
|
+
})) { setRun({ ok: false, text: '请检查工作流参数的类型、范围和选项;尚未发起生成。' }); return; }
|
|
1871
|
+
payload = { ...vals, workflow_sha: selectedComfy.sha256 };
|
|
1872
|
+
}
|
|
1873
|
+
submitting.current = true;
|
|
1599
1874
|
setRun('running');
|
|
1600
1875
|
fetch('/iris/api/actions/' + action, {
|
|
1601
1876
|
method: 'POST',
|
|
1602
1877
|
headers: { 'Content-Type': 'application/json' },
|
|
1603
|
-
body: JSON.stringify(
|
|
1878
|
+
body: JSON.stringify(payload)
|
|
1604
1879
|
})
|
|
1605
1880
|
.then(function (r) { return r.json().then(function (d) { return { ok: r.ok, d: d }; }); })
|
|
1606
1881
|
.then(function (res) {
|
|
1607
1882
|
setRun(res.ok ? { ok: true, text: res.d.text || '', img: res.d.imageUrl || res.d.imageDataUrl || null, audio: res.d.audioUrl || null } : { ok: false, text: res.d.error || '执行失败' });
|
|
1608
1883
|
if (res.ok && res.d && res.d.storage === 'core') window.dispatchEvent(new CustomEvent('iris-core-artifacts-changed'));
|
|
1609
1884
|
})
|
|
1610
|
-
.catch(function () { setRun({ ok: false, text: '网络错误' }); })
|
|
1885
|
+
.catch(function () { setRun({ ok: false, unknown: !!selectedComfy, text: selectedComfy ? '提交结果未知,请到任务列表核对;未自动重试。' : '网络错误' }); })
|
|
1886
|
+
.finally(function () { submitting.current = false; });
|
|
1611
1887
|
}
|
|
1612
1888
|
var cardHealth = capability ? capabilityHealth(irisState, capability) : { status: 'verified' };
|
|
1613
1889
|
// Host references can be ready without raw endpoint/key fields in the state snapshot.
|
|
@@ -1620,7 +1896,9 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1620
1896
|
var inputs = fields.map(function (f) {
|
|
1621
1897
|
var node;
|
|
1622
1898
|
if (f.key === 'model' && capability) {
|
|
1623
|
-
node = React.createElement('select', { value: vals.model || '',
|
|
1899
|
+
node = React.createElement('select', { value: vals.model || '', disabled: run === 'running', onChange: function (e) {
|
|
1900
|
+
var next = { ...vals, model: e.target.value }; delete next.workflow_parameters; delete next.workflow_sha; setVals(next);
|
|
1901
|
+
} },
|
|
1624
1902
|
React.createElement('option', { value: '' }, modelInfo && modelInfo.mode === 'auto' ? '按功能池顺序自动选择' : '按所选模型顺序'),
|
|
1625
1903
|
((modelInfo && modelInfo.options) || []).map(function (option) {
|
|
1626
1904
|
return React.createElement('option', { key: option.ref, value: option.ref, disabled: option.unavailable }, option.label + (option.unavailable ? ' · 当前不可用' : ''));
|
|
@@ -1678,16 +1956,18 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1678
1956
|
body = React.createElement('div', { className: 'iris-act-body' },
|
|
1679
1957
|
modelBar,
|
|
1680
1958
|
inputs,
|
|
1681
|
-
React.createElement(
|
|
1682
|
-
run === 'running'
|
|
1959
|
+
selectedComfy ? React.createElement(ComfyParameters, { key: selectedOption.ref + '@' + selectedComfy.sha256, option: selectedOption,
|
|
1960
|
+
values: vals.workflow_parameters || {}, disabled: run === 'running', onChange: function (values) { setVal('workflow_parameters', values); } }) : null,
|
|
1961
|
+
React.createElement('button', { className: 'iris-act-run', disabled: run === 'running' || !!(run && run.unknown), onClick: submit },
|
|
1962
|
+
run === 'running' ? '执行中…' : (props.submitLabel || (selectedComfy ? '生成图片' : '执行'))),
|
|
1683
1963
|
result);
|
|
1684
1964
|
}
|
|
1685
1965
|
var headStatus = capability ? cardHealth.status : 'verified';
|
|
1686
1966
|
var headTitle = capability ? healthText(cardHealth) : '本地功能';
|
|
1687
1967
|
return React.createElement('div', {
|
|
1688
|
-
className: 'iris-act-card' + (open ? ' open' : '') + (capability ? ' health-' + headStatus : ''), 'data-iris-action': action
|
|
1968
|
+
className: 'iris-act-card' + (open ? ' open' : '') + (selectedComfy ? ' iris-comfy-card' : '') + (capability ? ' health-' + headStatus : ''), 'data-iris-action': action
|
|
1689
1969
|
},
|
|
1690
|
-
React.createElement('
|
|
1970
|
+
React.createElement('button', { type: 'button', className: 'iris-act-head', 'aria-expanded': open, onClick: function () { setOpen(!open); } },
|
|
1691
1971
|
React.createElement('span', { className: 'iris-act-title' }, title),
|
|
1692
1972
|
capability ? React.createElement('span', {
|
|
1693
1973
|
className: 'iris-pm-dot ' + healthClass(cardHealth),
|
|
@@ -1987,8 +2267,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1987
2267
|
if (!rows.length) return null;
|
|
1988
2268
|
var isCollapsed = !!collapsed[name];
|
|
1989
2269
|
return React.createElement('div', { key: name, className: 'iris-hist-group' },
|
|
1990
|
-
React.createElement('
|
|
1991
|
-
className: 'iris-hist-head',
|
|
2270
|
+
React.createElement('button', {
|
|
2271
|
+
type: 'button', className: 'iris-hist-head', 'aria-expanded': !isCollapsed,
|
|
1992
2272
|
onClick: function () { var n = {}; Object.keys(collapsed).forEach(function (k) { n[k] = collapsed[k]; }); n[name] = !isCollapsed; setCollapsed(n); }
|
|
1993
2273
|
}, React.createElement('span', {}, isCollapsed ? '▶' : '▼'), name + ' · ' + rows.length),
|
|
1994
2274
|
isCollapsed ? null : React.createElement('div', { className: 'iris-wb-box' },
|
|
@@ -2070,11 +2350,12 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2070
2350
|
var pair = React.useState([]);
|
|
2071
2351
|
var setItems = pair[1];
|
|
2072
2352
|
React.useEffect(function () {
|
|
2073
|
-
var alive = true;
|
|
2353
|
+
var alive = true, sequence = 0;
|
|
2074
2354
|
function load() {
|
|
2355
|
+
var current = ++sequence;
|
|
2075
2356
|
fetch('/iris/api/core/snapshot?limit=' + Math.max(1, Math.min(200, Number(limit) || 6)))
|
|
2076
2357
|
.then(function (res) { return res.ok ? res.json() : null; })
|
|
2077
|
-
.then(function (data) { if (alive && data) setItems(coreGalleryItems(data, limit)); })
|
|
2358
|
+
.then(function (data) { if (alive && current === sequence && data) setItems(coreGalleryItems(data, limit)); })
|
|
2078
2359
|
.catch(function () { /* Core 不可用不影响旧作品 */ });
|
|
2079
2360
|
}
|
|
2080
2361
|
function changed() { load(); }
|
|
@@ -2395,8 +2676,11 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2395
2676
|
.then(function (value) {
|
|
2396
2677
|
if (request.current !== controller || controller.signal.aborted) return;
|
|
2397
2678
|
setData(value);
|
|
2398
|
-
|
|
2399
|
-
|
|
2679
|
+
var notes = [];
|
|
2680
|
+
if (value.coreError) notes.push('Core 作品暂时不可用,当前计数仅含可读取作品。');
|
|
2681
|
+
if (value.droppedCore) notes.push('已跳过 ' + value.droppedCore + ' 条损坏、缺失或不支持格式的 Core 作品。');
|
|
2682
|
+
if (value.legacyCoreSkipped) notes.push(value.legacyCoreSkipped + ' 条早期 Core 作品尚未编入当前目录;可按 ID 读取,或显式重建索引后显示。');
|
|
2683
|
+
setNote(notes.join(' '));
|
|
2400
2684
|
})
|
|
2401
2685
|
.catch(function (error) { if (request.current === controller && !controller.signal.aborted) setNote(error.message); })
|
|
2402
2686
|
.finally(function () { if (request.current === controller) { request.current = null; setBusy(false); } });
|
|
@@ -2587,32 +2871,46 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2587
2871
|
var EMPTY_CORE_USER = Object.freeze({ rows: [], available: false, degraded: false, droppedTasks: 0 });
|
|
2588
2872
|
|
|
2589
2873
|
function useCoreUserTasks() {
|
|
2590
|
-
var
|
|
2874
|
+
var store = useCoreUserTasks.shared;
|
|
2875
|
+
if (!store) store = useCoreUserTasks.shared = { data: EMPTY_CORE_USER, listeners: [], sequence: 0, refresh: null };
|
|
2876
|
+
var pair = React.useState(store.data);
|
|
2591
2877
|
var setData = pair[1];
|
|
2592
2878
|
React.useEffect(function () {
|
|
2593
|
-
|
|
2879
|
+
function listener(data) { setData(data); }
|
|
2880
|
+
store.listeners.push(listener);
|
|
2881
|
+
setData(store.data);
|
|
2594
2882
|
function load() {
|
|
2883
|
+
var current = ++store.sequence;
|
|
2595
2884
|
fetch('/iris/api/core/snapshot?limit=200')
|
|
2596
2885
|
.then(function (res) { return res.ok ? res.json() : null; })
|
|
2597
2886
|
.then(function (data) {
|
|
2598
|
-
if (!
|
|
2599
|
-
|
|
2887
|
+
if (!store.listeners.length || current !== store.sequence || !data || !Array.isArray(data.userTasks)) return;
|
|
2888
|
+
store.data = {
|
|
2600
2889
|
rows: data.userTasks,
|
|
2890
|
+
activity: data.activity,
|
|
2601
2891
|
available: !!data.available,
|
|
2602
2892
|
degraded: !!data.degraded,
|
|
2603
2893
|
droppedTasks: Number(data.droppedTasks || 0)
|
|
2604
|
-
}
|
|
2894
|
+
};
|
|
2895
|
+
store.listeners.forEach(function (notify) { notify(store.data); });
|
|
2605
2896
|
})
|
|
2606
2897
|
.catch(function () { /* Core 不可用不阻塞 legacy 任务区 */ });
|
|
2607
2898
|
}
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2899
|
+
if (store.listeners.length === 1) {
|
|
2900
|
+
store.refresh = load;
|
|
2901
|
+
load();
|
|
2902
|
+
window.addEventListener('iris-core-artifacts-changed', load);
|
|
2903
|
+
window.addEventListener('iris-core-refresh-tick', load);
|
|
2904
|
+
}
|
|
2612
2905
|
return function () {
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2906
|
+
var index = store.listeners.indexOf(listener);
|
|
2907
|
+
if (index >= 0) store.listeners.splice(index, 1);
|
|
2908
|
+
if (!store.listeners.length) {
|
|
2909
|
+
store.sequence++;
|
|
2910
|
+
window.removeEventListener('iris-core-artifacts-changed', store.refresh);
|
|
2911
|
+
window.removeEventListener('iris-core-refresh-tick', store.refresh);
|
|
2912
|
+
store.refresh = null; store.data = EMPTY_CORE_USER;
|
|
2913
|
+
}
|
|
2616
2914
|
};
|
|
2617
2915
|
}, []);
|
|
2618
2916
|
return pair[0];
|
|
@@ -2736,11 +3034,16 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2736
3034
|
if (!props.row || props.row.retryable !== true) return null;
|
|
2737
3035
|
return React.createElement(CoreTaskManualButton, {
|
|
2738
3036
|
id: props.row.id, action: 'retry',
|
|
2739
|
-
idleLabel: '重试为新任务', busyLabel: '创建中…', failPrefix: '重试',
|
|
3037
|
+
idleLabel: props.row.capability === 'video' ? '重新创建文生视频' : '重试为新任务', busyLabel: '创建中…', failPrefix: '重试',
|
|
2740
3038
|
title: '创建一个新任务重新生成,可能产生重复费用;生成指令需要重新输入',
|
|
2741
3039
|
confirm: '将创建一个新任务并可能产生重复生成费用,确定继续?',
|
|
2742
3040
|
createdPrefix: '已创建新任务:',
|
|
2743
3041
|
buildBody: function () {
|
|
3042
|
+
var videoMode;
|
|
3043
|
+
if (props.row.capability === 'video') {
|
|
3044
|
+
videoMode = window.prompt('旧视频 Task 不保存模式、首帧和音频。本按钮仅创建新的文生视频;图生视频或数字人视频请取消,并通过视频工具或 CLI 重新提供首帧/音频。\n如明确选择文生视频,请输入 t2v:');
|
|
3045
|
+
if (videoMode === null || String(videoMode).trim() !== 't2v') return null;
|
|
3046
|
+
}
|
|
2744
3047
|
var isTts = props.row.capability === 'tts';
|
|
2745
3048
|
var isTranscribe = props.row.capability === 'transcribe';
|
|
2746
3049
|
var draft = window.prompt(isTts
|
|
@@ -2758,7 +3061,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2758
3061
|
}
|
|
2759
3062
|
return isTts ? { text: draft, confirmBilling: true }
|
|
2760
3063
|
: isTranscribe ? { audio_url: draft, confirmBilling: true }
|
|
2761
|
-
: { prompt: draft, confirmBilling: true, ...(
|
|
3064
|
+
: { prompt: draft, confirmBilling: true, ...(videoMode ? { video_mode: 't2v' } : {}),
|
|
3065
|
+
...(sourceArtifactId ? { source_artifact_id: String(sourceArtifactId).trim() } : {}) };
|
|
2762
3066
|
}
|
|
2763
3067
|
});
|
|
2764
3068
|
}
|
|
@@ -2885,17 +3189,20 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2885
3189
|
var copyPair = React.useState('');
|
|
2886
3190
|
var copyNote = copyPair[0];
|
|
2887
3191
|
var setCopyNote = copyPair[1];
|
|
3192
|
+
var requestRef = React.useRef({ active: false, sequence: 0 });
|
|
2888
3193
|
function load() {
|
|
2889
|
-
|
|
3194
|
+
var request = requestRef.current;
|
|
3195
|
+
if (busy || !request.active) return;
|
|
3196
|
+
var current = ++request.sequence;
|
|
2890
3197
|
setBusy(true);
|
|
2891
3198
|
setError('');
|
|
2892
3199
|
fetch('/iris/api/core/snapshot?limit=12')
|
|
2893
3200
|
.then(function (res) {
|
|
2894
3201
|
if (res.status === 404) return { available: false, backendReloadRequired: true, tasks: { total: 0, recent: [] }, artifacts: { total: 0, recent: [] } };
|
|
2895
3202
|
return res.ok ? res.json() : Promise.reject(new Error('读取失败(HTTP ' + res.status + ')'));
|
|
2896
|
-
}).then(setData)
|
|
2897
|
-
.catch(function (caught) { setError('Core 运行事实读取失败:' + caught.message); })
|
|
2898
|
-
.finally(function () { setBusy(false); });
|
|
3203
|
+
}).then(function (value) { if (request.active && current === request.sequence) setData(value); })
|
|
3204
|
+
.catch(function (caught) { if (request.active && current === request.sequence) setError('Core 运行事实读取失败:' + caught.message); })
|
|
3205
|
+
.finally(function () { if (request.active && current === request.sequence) setBusy(false); });
|
|
2899
3206
|
}
|
|
2900
3207
|
function copyCoreId(value, label) {
|
|
2901
3208
|
setCopyNote('');
|
|
@@ -2909,11 +3216,14 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
2909
3216
|
.catch(function () { setCopyNote('复制失败;请长按上方 ID 复制。'); });
|
|
2910
3217
|
}
|
|
2911
3218
|
React.useEffect(function () {
|
|
3219
|
+
requestRef.current.active = true;
|
|
2912
3220
|
function changed() { load(); }
|
|
2913
3221
|
load();
|
|
2914
3222
|
window.addEventListener('iris-core-artifacts-changed', changed);
|
|
2915
3223
|
window.addEventListener('iris-core-refresh-tick', changed);
|
|
2916
3224
|
return function () {
|
|
3225
|
+
requestRef.current.active = false;
|
|
3226
|
+
requestRef.current.sequence++;
|
|
2917
3227
|
window.removeEventListener('iris-core-artifacts-changed', changed);
|
|
2918
3228
|
window.removeEventListener('iris-core-refresh-tick', changed);
|
|
2919
3229
|
};
|
|
@@ -3957,13 +4267,21 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
3957
4267
|
function ProgressDock() {
|
|
3958
4268
|
var state = useIrisState(5000);
|
|
3959
4269
|
var running = (state && state.tasks && state.tasks.running) || [];
|
|
3960
|
-
|
|
4270
|
+
var coreUser = useCoreUserTasks();
|
|
4271
|
+
var coreRows = coreUser.activity && coreUser.activity.recent || [];
|
|
4272
|
+
if (!running.length && !coreRows.length) return null;
|
|
3961
4273
|
return React.createElement('div', { className: 'iris-progress-dock' },
|
|
3962
4274
|
running.map(function (t) {
|
|
3963
4275
|
return React.createElement('div', { key: t.id, className: 'iris-progress-row' },
|
|
3964
4276
|
React.createElement('span', {}, '🫧 ' + t.cap),
|
|
3965
4277
|
React.createElement('span', {}, t.model || ''),
|
|
3966
4278
|
React.createElement('span', {}, String(t.progress || '')));
|
|
4279
|
+
}),
|
|
4280
|
+
coreRows.map(function (row) {
|
|
4281
|
+
return React.createElement('div', { key: 'core:' + row.id, className: 'iris-progress-row' },
|
|
4282
|
+
React.createElement('span', {}, 'Core · ' + row.capability),
|
|
4283
|
+
React.createElement('span', {}, row.model),
|
|
4284
|
+
React.createElement('span', {}, row.label));
|
|
3967
4285
|
}));
|
|
3968
4286
|
}
|
|
3969
4287
|
|
|
@@ -3992,6 +4310,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
3992
4310
|
|
|
3993
4311
|
function FloatingBubble() {
|
|
3994
4312
|
var state = useIrisState(5000);
|
|
4313
|
+
var coreUser = useCoreUserTasks();
|
|
4314
|
+
var activity = coreUser.activity;
|
|
3995
4315
|
var providers = (state && state.providers) || [];
|
|
3996
4316
|
var fallbackConfigured = providers.some(function (p) { return p.enabled && p.apiKeyHint; });
|
|
3997
4317
|
var overallState = state && state.health && HEALTH_META[state.health.overall]
|
|
@@ -4098,9 +4418,14 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
4098
4418
|
style = { right: DEFAULT_X + 'px', bottom: DEFAULT_Y + 'px' };
|
|
4099
4419
|
}
|
|
4100
4420
|
|
|
4101
|
-
var
|
|
4102
|
-
var
|
|
4103
|
-
|
|
4421
|
+
var coreCount = activity ? activity.running + activity.paused + activity.attention : 0;
|
|
4422
|
+
var badgeCount = running.length + attention.length + coreCount;
|
|
4423
|
+
var badgeTitle = '旧任务:' + running.length + ' 运行中,' + attention.length + ' 待处理';
|
|
4424
|
+
if (activity) badgeTitle += ';Core:' + activity.running + ' 运行中,' + activity.paused + ' 观察暂停,' + activity.attention + ' 待处理'
|
|
4425
|
+
+ (activity.truncated ? '(部分较早的 Core 记录尚未统计)' : '');
|
|
4426
|
+
var badge = badgeCount || activity && activity.truncated
|
|
4427
|
+
? React.createElement('span', { className: 'iris-bubble-badge', title: badgeTitle, 'aria-label': badgeTitle },
|
|
4428
|
+
badgeCount ? String(badgeCount) + (activity && activity.truncated ? '+' : '') : '…')
|
|
4104
4429
|
: null;
|
|
4105
4430
|
|
|
4106
4431
|
var panel = null;
|
|
@@ -4118,6 +4443,11 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
4118
4443
|
|
|
4119
4444
|
return React.createElement('div', {
|
|
4120
4445
|
className: 'iris-bubble health-' + overallHealth + (dragRef.current ? ' dragging' : ''),
|
|
4446
|
+
role: 'button', tabIndex: 0, 'aria-label': 'Iris 快捷面板', 'aria-expanded': open,
|
|
4447
|
+
onKeyDown: function (event) {
|
|
4448
|
+
if (event.target !== event.currentTarget || (event.key !== 'Enter' && event.key !== ' ')) return;
|
|
4449
|
+
event.preventDefault(); setOpen(!open);
|
|
4450
|
+
},
|
|
4121
4451
|
style: style,
|
|
4122
4452
|
onPointerDown: onPointerDown,
|
|
4123
4453
|
onPointerMove: onPointerMove,
|