@mokuyoaxis/dsh-iris 0.1.3 → 0.1.4
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/.dsh/skills/iris-compose-media/SKILL.md +23 -49
- package/.dsh/skills/iris-compose-media/references/task-v2-recovery.md +30 -0
- package/.dsh/skills/iris-compose-media/references/workflows.md +38 -0
- package/.dsh/skills/iris-verify-ui/SKILL.md +3 -25
- package/.dsh/skills/iris-verify-ui/agents/openai.yaml +4 -0
- package/.dsh/skills/iris-verify-ui/references/verdict-and-degradation.md +31 -0
- package/CHANGELOG.md +24 -2
- package/README.en.md +29 -7
- package/README.md +29 -7
- package/docs/ARCHITECTURE.md +27 -13
- package/docs/ARTIFACT_LIBRARY.md +24 -0
- package/docs/HOST_ADAPTER_CONTRACT.md +86 -0
- package/docs/HOST_DOCTOR.md +47 -0
- package/docs/PROVIDER_ADAPTER_CONTRACT.md +97 -0
- package/docs/PROVIDER_HEALTH.md +39 -0
- package/docs/PROVIDER_SUBMISSION_CONTRACT.md +2 -2
- package/docs/ROADMAP.md +18 -8
- package/docs/SECURITY.md +6 -2
- package/docs/TASK_SEMANTICS.md +3 -2
- package/docs/assets/diagrams/iris-architecture.png +0 -0
- package/docs/assets/diagrams/iris-architecture.svg +5 -5
- package/docs/releases/0.1.4.md +59 -0
- package/lib/actions.js +357 -221
- package/lib/adapters.js +7 -7
- package/lib/api.js +30 -11
- package/lib/artifacts.js +239 -0
- package/lib/bundled-skills.js +5 -5
- package/lib/client.js +370 -55
- package/lib/config.js +192 -9
- package/lib/doctor.js +175 -4
- package/lib/dsh-host-adapter.js +189 -0
- package/lib/host-contract.js +131 -0
- package/lib/host-runtime.js +156 -0
- package/lib/index.js +97 -113
- package/lib/locate.js +1 -1
- package/lib/media.js +16 -6
- package/lib/prompt-optimizer.js +10 -20
- package/lib/provider-adapter.js +371 -0
- package/lib/provider-adapters.js +279 -0
- package/lib/provider-health.js +59 -0
- package/lib/render.js +43 -12
- package/lib/tasks.js +29 -5
- package/lib/vision.js +23 -49
- package/package.json +14 -8
- package/user_guide.md +42 -2
- package/docs/assets/diagrams/iris-architecture.drawio +0 -1
- package/docs/assets/diagrams/iris-task-lifecycle.drawio +0 -1
- package/docs/assets/diagrams/iris-workflow-compose.drawio +0 -1
- package/docs/releases/0.1.1.md +0 -51
- package/docs/releases/0.1.2.md +0 -26
- package/docs/releases/0.1.3.md +0 -30
package/lib/client.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* DSH web client bundle 标准形态(与 dsh-at-file / dsh-better-sidebar 同构):
|
|
5
5
|
* window.__ModuleLoader__.load({ id, factory }) → CJS 工厂,require 解析宿主依赖,
|
|
6
6
|
* `inject` 声明本 bundle 需要的 ctx 服务 key(这里是 slots),
|
|
7
|
-
* `apply(ctx)`
|
|
7
|
+
* `apply(ctx)` 通过本地 clientSlots 适配端口注册座位。
|
|
8
8
|
*
|
|
9
9
|
* 三个零替换风险的座位:
|
|
10
10
|
* - settings.section(id: 'iris-workbench'):Iris 工作台整页 ——
|
|
@@ -21,6 +21,42 @@ 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.1.4';
|
|
25
|
+
var HOST_CLIENT_PROTOCOL_VERSION = 0;
|
|
26
|
+
var reportedSeats = [];
|
|
27
|
+
|
|
28
|
+
function reportClientSeat(seat) {
|
|
29
|
+
if (reportedSeats.indexOf(seat) < 0) reportedSeats.push(seat);
|
|
30
|
+
if (!window || typeof window.fetch !== 'function') return;
|
|
31
|
+
try {
|
|
32
|
+
Promise.resolve(window.fetch('/iris/api/host-client', {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: { 'content-type': 'application/json' },
|
|
35
|
+
credentials: 'same-origin',
|
|
36
|
+
body: JSON.stringify({
|
|
37
|
+
pluginId: '@mokuyoaxis/dsh-iris',
|
|
38
|
+
version: IRIS_CLIENT_VERSION,
|
|
39
|
+
protocolVersion: HOST_CLIENT_PROTOCOL_VERSION,
|
|
40
|
+
seats: reportedSeats.slice()
|
|
41
|
+
})
|
|
42
|
+
})).catch(function () {});
|
|
43
|
+
} catch (_) { /* Doctor handshake 不影响 UI */ }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Client bundle 不能导入 server-side Host contract;只在此边界读取 DSH slots 服务。
|
|
47
|
+
function clientSlotsPort(ctx) {
|
|
48
|
+
var slots;
|
|
49
|
+
try { slots = ctx && ctx.slots; } catch (_) { slots = null; }
|
|
50
|
+
if (!slots || typeof slots.inject !== 'function' || typeof slots.register !== 'function') {
|
|
51
|
+
var error = new Error('Iris clientSlots 不可用:DSH slots 需要 inject/register');
|
|
52
|
+
error.code = slots ? 'IRIS_HOST_CAPABILITY_INCOMPATIBLE' : 'IRIS_HOST_CAPABILITY_UNAVAILABLE';
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
return Object.freeze({
|
|
56
|
+
inject: function (seat, callback) { return slots.inject(seat, callback); },
|
|
57
|
+
register: function (definition, component) { return slots.register(definition, component); }
|
|
58
|
+
});
|
|
59
|
+
}
|
|
24
60
|
|
|
25
61
|
var STYLE_ID = 'iris-wb-css';
|
|
26
62
|
var cssText = [
|
|
@@ -57,6 +93,15 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
57
93
|
'.iris-wb-drawer .iris-wb-prompt { white-space: pre-wrap; word-break: break-word; font-size: 12px; }',
|
|
58
94
|
'.iris-wb-drawer .iris-wb-err { color: var(--dsw-alias-state-error-primary, #e06c6c); white-space: pre-wrap; word-break: break-word; font-size: 12px; }',
|
|
59
95
|
'.iris-wb-loading { color: var(--dsw-alias-label-secondary, #9a9a9a); font-size: 12px; }',
|
|
96
|
+
'.iris-doctor-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }',
|
|
97
|
+
'.iris-doctor-score { font-weight: 600; flex: 1; }',
|
|
98
|
+
'.iris-doctor-list { display: grid; gap: 4px; margin-top: 7px; }',
|
|
99
|
+
'.iris-doctor-row { display: grid; grid-template-columns: 14px 1fr; gap: 6px; font-size: 11px; }',
|
|
100
|
+
'.iris-doctor-row.ok { color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
101
|
+
'.iris-doctor-row.warn { color: var(--dsw-alias-state-warn-primary, #d9a941); }',
|
|
102
|
+
'.iris-doctor-row.error { color: var(--dsw-alias-state-error-primary, #e06c6c); }',
|
|
103
|
+
'.iris-doctor-details { margin-top: 7px; color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
104
|
+
'.iris-doctor-details summary { cursor: pointer; font-size: 11px; }',
|
|
60
105
|
/* ---- 操作卡片组(阶段 5 GUI 直连) ---- */
|
|
61
106
|
'.iris-act-group { display: grid; gap: 8px; }',
|
|
62
107
|
'.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; }',
|
|
@@ -96,8 +141,17 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
96
141
|
'.iris-pm-field { display: grid; gap: 3px; }',
|
|
97
142
|
'.iris-pm-field input, .iris-pm-field textarea { background: var(--dsw-alias-bg-layer-2, #32323c); color: var(--dsw-alias-label-primary, #e6e6e6); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 4px; padding: 4px 6px; font-size: 12px; width: 100%; box-sizing: border-box; }',
|
|
98
143
|
'.iris-pm-act { font-size: 11px; color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
99
|
-
'.iris-pm-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--dsw-alias-label-
|
|
100
|
-
'.iris-pm-dot.
|
|
144
|
+
'.iris-pm-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--dsw-alias-label-tertiary, #73737d); margin-right: 4px; }',
|
|
145
|
+
'.iris-pm-dot.configured { background: #6f9df6; box-shadow: 0 0 7px rgba(111,157,246,.55); }',
|
|
146
|
+
'.iris-pm-dot.verified { background: var(--dsw-alias-state-success-primary, #6fcf6f); box-shadow: 0 0 7px var(--dsw-alias-state-success-primary, #6fcf6f); }',
|
|
147
|
+
'.iris-pm-dot.failed { background: #b9656d; box-shadow: 0 0 6px rgba(185,101,109,.38); opacity: .82; }',
|
|
148
|
+
'.iris-health-grid { display: grid; gap: 5px; }',
|
|
149
|
+
'.iris-health-row { display: grid; grid-template-columns: minmax(72px, auto) 1fr auto; align-items: center; gap: 7px; padding: 6px 9px; border-radius: 9px; background: var(--dsw-alias-bg-layer-1, #26262e); font-size: 11px; }',
|
|
150
|
+
'.iris-health-name { font-weight: 600; }',
|
|
151
|
+
'.iris-health-state { color: var(--dsw-alias-label-secondary, #9a9a9a); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }',
|
|
152
|
+
'.iris-health-row.failed .iris-health-state { color: #c87a81; }',
|
|
153
|
+
'.iris-health-row.configured .iris-health-state { color: #7da8fa; }',
|
|
154
|
+
'.iris-health-row.verified .iris-health-state { color: var(--dsw-alias-state-success-primary, #6fcf6f); }',
|
|
101
155
|
'.iris-cap-grid { display: grid; gap: 6px; }',
|
|
102
156
|
'.iris-cap-row { display: flex; align-items: center; flex-wrap: wrap; gap: 5px; background: var(--dsw-alias-bg-layer-1, #26262e); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 8px; padding: 6px 9px; font-size: 12px; }',
|
|
103
157
|
'.iris-cap-name { font-weight: 600; min-width: 64px; }',
|
|
@@ -109,10 +163,12 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
109
163
|
'.iris-bubble { position: fixed; z-index: 9999; cursor: grab; user-select: none; touch-action: none; }',
|
|
110
164
|
'.iris-bubble.dragging { cursor: grabbing; }',
|
|
111
165
|
'.iris-bubble-btn { width: 44px; height: 44px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 20px; background: var(--dsw-alias-bg-layer-2, #32323c); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35); transition: box-shadow 0.2s, border-color 0.2s, opacity 0.2s; }',
|
|
112
|
-
'.iris-bubble.
|
|
113
|
-
'.iris-bubble.
|
|
166
|
+
'.iris-bubble.health-configured .iris-bubble-btn { border-color: #6f9df6; box-shadow: 0 0 14px rgba(111,157,246,.55); }',
|
|
167
|
+
'.iris-bubble.health-verified .iris-bubble-btn { border-color: var(--dsw-alias-state-success-primary, #6fcf6f); box-shadow: 0 0 14px var(--dsw-alias-state-success-primary, #6fcf6f); }',
|
|
168
|
+
'.iris-bubble.health-failed .iris-bubble-btn { opacity: .72; border-color: #b9656d; box-shadow: 0 0 11px rgba(185,101,109,.34); }',
|
|
169
|
+
'.iris-bubble.health-unconfigured .iris-bubble-btn { opacity: 0.55; border-color: var(--dsw-alias-border-l1, #3a3a44); }',
|
|
114
170
|
'.iris-bubble-badge { position: absolute; top: -2px; right: -2px; min-width: 16px; height: 16px; border-radius: 8px; background: var(--dsw-alias-state-warn-primary, #d9a941); color: #1a1a1e; font-size: 10px; font-weight: 700; display: flex; align-items: center; justify-content: center; padding: 0 3px; }',
|
|
115
|
-
'.iris-bubble-panel { position: fixed; z-index: 9998; width: 360px; max-height: min(520px,
|
|
171
|
+
'.iris-bubble-panel { position: fixed; z-index: 9998; box-sizing: border-box; width: min(360px, calc(100vw - 16px)); max-height: min(520px, calc(100dvh - 16px)); overflow: auto; overscroll-behavior: contain; background: var(--dsw-alias-bg-layer-1, #26262e); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 12px; padding: 12px 14px; font-size: 12px; line-height: 1.45; box-shadow: 0 8px 30px rgba(0, 0, 0, 0.45); }',
|
|
116
172
|
'.iris-bubble-panel-head { position: absolute; top: 6px; right: 6px; z-index: 2; }',
|
|
117
173
|
'.iris-bubble-close { border: none; background: transparent; color: var(--dsw-alias-label-secondary, #9a9a9a); font-size: 14px; cursor: pointer; padding: 4px 6px; }',
|
|
118
174
|
'.iris-bubble-close:hover { color: var(--dsw-alias-label-primary, #e6e6e6); }',
|
|
@@ -136,6 +192,22 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
136
192
|
'.iris-clean-btn:hover { border-color: var(--dsw-alias-brand-primary, #7aa2f7); }',
|
|
137
193
|
'.iris-clean-btn.danger:hover { color: var(--dsw-alias-state-error-primary, #e06c6c); border-color: var(--dsw-alias-state-error-primary, #e06c6c); }',
|
|
138
194
|
'.iris-clean-note { font-size: 11px; color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
195
|
+
/* 独立作品库 v0:任务清理后仍可浏览,窄屏自动收缩。 */
|
|
196
|
+
'.iris-gallery-tools { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }',
|
|
197
|
+
'.iris-gallery-tools .iris-clean-note { flex: 1; min-width: 100px; }',
|
|
198
|
+
'.iris-gallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr)); gap: 10px; }',
|
|
199
|
+
'.iris-gallery-card { min-width: 0; overflow: hidden; border: 1px solid rgba(150,164,215,.16); border-radius: 13px; background: color-mix(in srgb, var(--dsw-alias-bg-layer-1, #26262e) 92%, transparent); box-shadow: 0 7px 22px rgba(0,0,0,.12); }',
|
|
200
|
+
'.iris-gallery-preview { display: flex; align-items: center; justify-content: center; width: 100%; aspect-ratio: 1 / 1; overflow: hidden; background: rgba(8,10,16,.28); }',
|
|
201
|
+
'.iris-gallery-preview img, .iris-gallery-preview video { width: 100%; height: 100%; object-fit: contain; }',
|
|
202
|
+
'.iris-gallery-preview audio { width: calc(100% - 16px); }',
|
|
203
|
+
'.iris-gallery-file { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; font-weight: 600; }',
|
|
204
|
+
'.iris-gallery-meta { color: var(--dsw-alias-label-tertiary, #7a7a85); font-size: 10px; }',
|
|
205
|
+
'.iris-gallery-info { display: grid; gap: 4px; padding: 8px 9px 9px; }',
|
|
206
|
+
'.iris-gallery-actions { display: flex; align-items: center; gap: 8px; }',
|
|
207
|
+
'.iris-artifact-mini { display: flex; align-items: center; gap: 7px; min-width: 0; padding: 4px 6px; border-radius: 7px; color: var(--dsw-alias-label-secondary, #9a9a9a); text-decoration: none; }',
|
|
208
|
+
'.iris-artifact-mini:hover { background: var(--dsw-alias-bg-layer-2, #32323c); color: var(--dsw-alias-label-primary, #e6e6e6); }',
|
|
209
|
+
'.iris-artifact-mini span:nth-child(2) { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }',
|
|
210
|
+
'@media (max-width: 360px) { .iris-gallery-grid { grid-template-columns: 1fr; } }',
|
|
139
211
|
/* 模型池(阶段 9 P4) */
|
|
140
212
|
'.iris-pm-pool { display: grid; gap: 4px; }',
|
|
141
213
|
'.iris-pm-mrows { display: grid; gap: 3px; max-height: 220px; overflow: auto; }',
|
|
@@ -144,8 +216,10 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
144
216
|
'.iris-pm-mtag { font-size: 9px; color: var(--dsw-alias-brand-primary, #7aa2f7); border: 1px solid var(--dsw-alias-brand-primary, #7aa2f7); border-radius: 3px; padding: 0 3px; margin-left: 4px; }',
|
|
145
217
|
'.iris-pm-mcaps { display: flex; gap: 3px; }',
|
|
146
218
|
'.iris-pm-ver { font-size: 10px; padding: 0 4px; border-radius: 3px; background: var(--dsw-alias-bg-layer-2, #32323c); color: var(--dsw-alias-label-secondary, #9a9a9a); }',
|
|
147
|
-
'.iris-pm-ver.
|
|
148
|
-
'.iris-pm-ver.
|
|
219
|
+
'.iris-pm-ver.configured { color: #7da8fa; }',
|
|
220
|
+
'.iris-pm-ver.verified { color: var(--dsw-alias-state-success-primary, #6fcf6f); }',
|
|
221
|
+
'.iris-pm-ver.failed { color: #c87a81; border-color: rgba(185,101,109,.5); }',
|
|
222
|
+
'.iris-pm-ver.unconfigured { opacity: .55; }',
|
|
149
223
|
'.iris-pm-madd { flex: 1; background: var(--dsw-alias-bg-layer-2, #32323c); color: var(--dsw-alias-label-primary, #e6e6e6); border: 1px solid var(--dsw-alias-border-l1, #3a3a44); border-radius: 4px; padding: 3px 6px; font-size: 12px; }',
|
|
150
224
|
/* 文件选择器(阶段 10) */
|
|
151
225
|
'.iris-ff { display: grid; gap: 4px; }',
|
|
@@ -294,13 +368,60 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
294
368
|
return sharedState;
|
|
295
369
|
}
|
|
296
370
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
371
|
+
var HEALTH_META = {
|
|
372
|
+
unconfigured: { label: '未配置', symbol: '○' },
|
|
373
|
+
configured: { label: '已配置,待验证', symbol: '◌' },
|
|
374
|
+
verified: { label: '近期验证成功', symbol: '●' },
|
|
375
|
+
failed: { label: '认证失败', symbol: '×' }
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
function capabilityHealth(state, capability) {
|
|
379
|
+
var health = state && state.health && state.health.capabilities
|
|
380
|
+
&& state.health.capabilities[capability];
|
|
381
|
+
if (health && HEALTH_META[health.status]) return health;
|
|
300
382
|
var providers = (state && state.providers) || [];
|
|
301
|
-
|
|
302
|
-
return p.enabled && p.apiKeyHint && Array.isArray(p.capabilities)
|
|
383
|
+
var configured = providers.some(function (p) {
|
|
384
|
+
return p.enabled && p.apiKeyHint && Array.isArray(p.capabilities)
|
|
385
|
+
&& p.capabilities.indexOf(capability) >= 0;
|
|
303
386
|
});
|
|
387
|
+
return { status: configured ? 'configured' : 'unconfigured', candidateCount: configured ? 1 : 0 };
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function relativeHealthTime(value) {
|
|
391
|
+
var ms = Date.now() - Date.parse(String(value || ''));
|
|
392
|
+
if (!Number.isFinite(ms) || ms < 0) return '';
|
|
393
|
+
if (ms < 60000) return '刚刚';
|
|
394
|
+
if (ms < 3600000) return Math.floor(ms / 60000) + ' 分钟前';
|
|
395
|
+
if (ms < 86400000) return Math.floor(ms / 3600000) + ' 小时前';
|
|
396
|
+
return Math.floor(ms / 86400000) + ' 天前';
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function healthText(health) {
|
|
400
|
+
var status = health && HEALTH_META[health.status] ? health.status : 'unconfigured';
|
|
401
|
+
var text = HEALTH_META[status].label;
|
|
402
|
+
var age = relativeHealthTime(health && health.observedAt);
|
|
403
|
+
return text + (age ? ' · ' + age : '');
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function CapabilityHealthOverview() {
|
|
407
|
+
var state = useIrisState(5000);
|
|
408
|
+
var rows = [
|
|
409
|
+
['image-gen', '图片'],
|
|
410
|
+
['video-gen', '视频'],
|
|
411
|
+
['tts', '语音'],
|
|
412
|
+
['transcribe', '转写'],
|
|
413
|
+
['vision', '视觉']
|
|
414
|
+
];
|
|
415
|
+
return React.createElement('div', { className: 'iris-health-grid' }, rows.map(function (row) {
|
|
416
|
+
var health = capabilityHealth(state, row[0]);
|
|
417
|
+
var meta = HEALTH_META[health.status] || HEALTH_META.unconfigured;
|
|
418
|
+
return React.createElement('div', { key: row[0], className: 'iris-health-row ' + health.status },
|
|
419
|
+
React.createElement('span', { className: 'iris-health-name' },
|
|
420
|
+
React.createElement('span', { className: 'iris-pm-dot ' + health.status }, meta.symbol),
|
|
421
|
+
row[1]),
|
|
422
|
+
React.createElement('span', { className: 'iris-health-state', title: healthText(health) }, healthText(health)),
|
|
423
|
+
React.createElement('span', { className: 'iris-wb-muted' }, String(health.candidateCount || 0) + ' 路'));
|
|
424
|
+
}));
|
|
304
425
|
}
|
|
305
426
|
|
|
306
427
|
/* ---- 任务详情抽屉:按需拉取 /iris/api/task/:id ---- */
|
|
@@ -571,10 +692,17 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
571
692
|
var setNewId = addPair[1];
|
|
572
693
|
var models = p.models || [];
|
|
573
694
|
function verBadge(m, c) {
|
|
574
|
-
var
|
|
575
|
-
var
|
|
576
|
-
var
|
|
577
|
-
|
|
695
|
+
var health = m.health && m.health[c];
|
|
696
|
+
var status = health && HEALTH_META[health.status] ? health.status : 'configured';
|
|
697
|
+
var cls = 'iris-pm-ver ' + status;
|
|
698
|
+
var title = healthText(health || { status: status });
|
|
699
|
+
var symbol = status === 'verified' ? '✓' : (status === 'failed' ? '×' : (status === 'unconfigured' ? '○' : '?'));
|
|
700
|
+
return React.createElement('button', {
|
|
701
|
+
key: c,
|
|
702
|
+
className: cls,
|
|
703
|
+
title: title + ';点击单独验证此能力',
|
|
704
|
+
onClick: function () { props.testModel(p, m, c); }
|
|
705
|
+
}, c + symbol);
|
|
578
706
|
}
|
|
579
707
|
var rows = models.map(function (m) {
|
|
580
708
|
var caps = m.capabilities || [];
|
|
@@ -811,6 +939,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
811
939
|
.then(function (res) { setRun(res.ok ? { ok: true, text: res.d.text || '', img: res.d.imageDataUrl || null } : { ok: false, text: res.d.error || '执行失败' }); })
|
|
812
940
|
.catch(function () { setRun({ ok: false, text: '网络错误' }); });
|
|
813
941
|
}
|
|
942
|
+
var cardHealth = capability ? capabilityHealth(irisState, capability) : { status: 'verified' };
|
|
814
943
|
var body = null;
|
|
815
944
|
if (open) {
|
|
816
945
|
var inputs = fields.map(function (f) {
|
|
@@ -846,16 +975,17 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
846
975
|
result = React.createElement('div', { className: 'iris-act-result' + (run.ok ? '' : ' err') }, run.text);
|
|
847
976
|
}
|
|
848
977
|
}
|
|
849
|
-
//
|
|
978
|
+
// 只读模型提示:当前能力解析到的模型与持久健康事实。
|
|
850
979
|
var modelBar = null;
|
|
851
980
|
if (capability) {
|
|
852
|
-
var
|
|
981
|
+
var available = modelInfo && modelInfo.loaded && modelInfo.options.length > 0;
|
|
853
982
|
var shown = (vals.model && vals.model.trim()) || (modelInfo && modelInfo.assigned) || '';
|
|
854
983
|
modelBar = React.createElement('div', { className: 'iris-act-field' },
|
|
855
984
|
React.createElement('label', {},
|
|
856
|
-
React.createElement('span', { className:
|
|
857
|
-
' 模型:' + (shown || '自动') + (
|
|
858
|
-
React.createElement('div', { className: 'iris-wb-muted' },
|
|
985
|
+
React.createElement('span', { className: 'iris-pm-dot ' + cardHealth.status }, HEALTH_META[cardHealth.status].symbol),
|
|
986
|
+
' 模型:' + (shown || '自动') + (available ? '' : '(无可用模型)')),
|
|
987
|
+
React.createElement('div', { className: 'iris-wb-muted' },
|
|
988
|
+
healthText(cardHealth) + ' · 改分配 → 上方「能力分配(failover 顺序)」'));
|
|
859
989
|
}
|
|
860
990
|
body = React.createElement('div', { className: 'iris-act-body' },
|
|
861
991
|
modelBar,
|
|
@@ -864,13 +994,17 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
864
994
|
run === 'running' ? '执行中…' : '执行'),
|
|
865
995
|
result);
|
|
866
996
|
}
|
|
867
|
-
var
|
|
868
|
-
var
|
|
869
|
-
|
|
870
|
-
|
|
997
|
+
var headStatus = capability ? cardHealth.status : 'verified';
|
|
998
|
+
var headTitle = capability ? healthText(cardHealth) : '本地功能';
|
|
999
|
+
return React.createElement('div', {
|
|
1000
|
+
className: 'iris-act-card' + (open ? ' open' : '') + (capability ? ' health-' + headStatus : '')
|
|
1001
|
+
},
|
|
871
1002
|
React.createElement('div', { className: 'iris-act-head', onClick: function () { setOpen(!open); } },
|
|
872
1003
|
React.createElement('span', { className: 'iris-act-title' }, title),
|
|
873
|
-
capability ? React.createElement('span', {
|
|
1004
|
+
capability ? React.createElement('span', {
|
|
1005
|
+
className: 'iris-pm-dot ' + headStatus,
|
|
1006
|
+
title: headTitle
|
|
1007
|
+
}, HEALTH_META[headStatus].symbol) : null,
|
|
874
1008
|
React.createElement('span', { className: 'iris-act-arrow' }, '▶')),
|
|
875
1009
|
body);
|
|
876
1010
|
}
|
|
@@ -1054,6 +1188,105 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1054
1188
|
}));
|
|
1055
1189
|
}
|
|
1056
1190
|
|
|
1191
|
+
function fmtBytes(bytes) {
|
|
1192
|
+
var value = Number(bytes || 0);
|
|
1193
|
+
if (value < 1024) return value + 'B';
|
|
1194
|
+
if (value < 1048576) return (value / 1024).toFixed(0) + 'KB';
|
|
1195
|
+
return (value / 1048576).toFixed(1) + 'MB';
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
function artifactIcon(item) {
|
|
1199
|
+
var mime = String(item && item.mime || '');
|
|
1200
|
+
if (mime.indexOf('image/') === 0) return '🖼';
|
|
1201
|
+
if (mime.indexOf('video/') === 0) return '🎬';
|
|
1202
|
+
if (mime.indexOf('audio/') === 0) return '🔊';
|
|
1203
|
+
return '📄';
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
function artifactRowMini(item) {
|
|
1207
|
+
return React.createElement('a', { key: item.id, className: 'iris-artifact-mini', href: item.url, target: '_blank', rel: 'noreferrer', title: '打开作品' },
|
|
1208
|
+
React.createElement('span', {}, artifactIcon(item)),
|
|
1209
|
+
React.createElement('span', {}, item.file),
|
|
1210
|
+
React.createElement('span', { className: 'iris-gallery-meta' }, fmtBytes(item.size)));
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
function artifactPreview(item) {
|
|
1214
|
+
var mime = String(item.mime || '');
|
|
1215
|
+
if (mime.indexOf('image/') === 0) {
|
|
1216
|
+
return React.createElement('a', { className: 'iris-gallery-preview', href: item.url, target: '_blank', rel: 'noreferrer', title: '打开原图' },
|
|
1217
|
+
React.createElement('img', { src: item.url, alt: 'Iris 作品 ' + item.file, loading: 'lazy' }));
|
|
1218
|
+
}
|
|
1219
|
+
if (mime.indexOf('video/') === 0) {
|
|
1220
|
+
return React.createElement('div', { className: 'iris-gallery-preview' },
|
|
1221
|
+
React.createElement('video', { src: item.url, controls: true, playsInline: true, preload: 'metadata' }));
|
|
1222
|
+
}
|
|
1223
|
+
if (mime.indexOf('audio/') === 0) {
|
|
1224
|
+
return React.createElement('div', { className: 'iris-gallery-preview' },
|
|
1225
|
+
React.createElement('audio', { src: item.url, controls: true, preload: 'metadata' }));
|
|
1226
|
+
}
|
|
1227
|
+
return React.createElement('a', { className: 'iris-gallery-preview iris-wb-link', href: item.url, target: '_blank', rel: 'noreferrer' }, '打开文件');
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
function ArtifactGallery(props) {
|
|
1231
|
+
var itemsPair = React.useState([]);
|
|
1232
|
+
var items = itemsPair[0];
|
|
1233
|
+
var setItems = itemsPair[1];
|
|
1234
|
+
var totalPair = React.useState(Number(props.total || 0));
|
|
1235
|
+
var total = totalPair[0];
|
|
1236
|
+
var setTotal = totalPair[1];
|
|
1237
|
+
var busyPair = React.useState(false);
|
|
1238
|
+
var busy = busyPair[0];
|
|
1239
|
+
var setBusy = busyPair[1];
|
|
1240
|
+
var notePair = React.useState('');
|
|
1241
|
+
var note = notePair[0];
|
|
1242
|
+
var setNote = notePair[1];
|
|
1243
|
+
|
|
1244
|
+
function load(append) {
|
|
1245
|
+
if (busy) return;
|
|
1246
|
+
setBusy(true);
|
|
1247
|
+
var offset = append ? items.length : 0;
|
|
1248
|
+
fetch('/iris/api/artifacts?offset=' + offset + '&limit=24')
|
|
1249
|
+
.then(function (res) { return res.ok ? res.json() : Promise.reject(new Error('读取失败')); })
|
|
1250
|
+
.then(function (data) {
|
|
1251
|
+
setItems(append ? items.concat(data.items || []) : (data.items || []));
|
|
1252
|
+
setTotal(Number(data.total || 0));
|
|
1253
|
+
})
|
|
1254
|
+
.catch(function (error) { setNote('作品库读取失败:' + error.message); })
|
|
1255
|
+
.finally(function () { setBusy(false); });
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
function run(action, body, confirmText) {
|
|
1259
|
+
if (confirmText && !window.confirm(confirmText)) return;
|
|
1260
|
+
setBusy(true);
|
|
1261
|
+
postAction(action, body || {}).then(function (result) {
|
|
1262
|
+
var succeeded = result.ok && result.d && result.d.ok !== false;
|
|
1263
|
+
setNote(succeeded ? (result.d.text || '完成') : ('失败:' + ((result.d && (result.d.error || result.d.text)) || '未知错误')));
|
|
1264
|
+
if (succeeded) load(false);
|
|
1265
|
+
else setBusy(false);
|
|
1266
|
+
}).catch(function (error) { setBusy(false); setNote('失败:' + error.message); });
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
React.useEffect(function () { load(false); }, [props.total]);
|
|
1270
|
+
|
|
1271
|
+
return React.createElement('div', {},
|
|
1272
|
+
React.createElement('div', { className: 'iris-gallery-tools' },
|
|
1273
|
+
React.createElement('span', { className: 'iris-clean-note' }, '共 ' + total + ' 个作品 · 清任务历史不会删除'),
|
|
1274
|
+
React.createElement('button', { className: 'iris-clean-btn', disabled: busy, onClick: function () { run('artifacts_reindex'); } }, '找回本地作品'),
|
|
1275
|
+
total ? React.createElement('button', { className: 'iris-clean-btn danger', disabled: busy, onClick: function () { run('artifacts_clear', {}, '永久删除作品库中的全部媒体文件?任务历史会保留,但相关媒体将无法打开。此操作不可逆。'); } }, '清空作品库') : null),
|
|
1276
|
+
items.length ? React.createElement('div', { className: 'iris-gallery-grid' }, items.map(function (item) {
|
|
1277
|
+
return React.createElement('div', { key: item.id, className: 'iris-gallery-card' },
|
|
1278
|
+
artifactPreview(item),
|
|
1279
|
+
React.createElement('div', { className: 'iris-gallery-info' },
|
|
1280
|
+
React.createElement('div', { className: 'iris-gallery-file', title: item.file }, item.file),
|
|
1281
|
+
React.createElement('div', { className: 'iris-gallery-meta' }, fmtTime(item.createdAt) + ' · ' + fmtBytes(item.size)),
|
|
1282
|
+
React.createElement('div', { className: 'iris-gallery-actions' },
|
|
1283
|
+
React.createElement('a', { className: 'iris-wb-link', href: item.url, target: '_blank', rel: 'noreferrer' }, '打开'),
|
|
1284
|
+
React.createElement('button', { className: 'iris-pm-btn danger', disabled: busy, onClick: function () { run('artifacts_delete', { artifact_id: item.id }, '永久删除作品“' + item.file + '”?任务历史会保留,但媒体将无法打开。'); } }, '删除'))));
|
|
1285
|
+
})) : React.createElement('div', { className: 'iris-wb-empty' }, busy ? '正在读取作品…' : '尚无作品;如 outputs/ 中已有文件,可点“找回本地作品”。'),
|
|
1286
|
+
items.length < total ? React.createElement('button', { className: 'iris-clean-btn', disabled: busy, onClick: function () { load(true); } }, busy ? '加载中…' : '加载更多') : null,
|
|
1287
|
+
note ? React.createElement('div', { className: 'iris-clean-note', style: { marginTop: '7px' } }, note) : null);
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1057
1290
|
/* ---- 泡泡常用卡片选择器(设置页:多项勾选 → 出现在悬浮窗「常用」标签) ---- */
|
|
1058
1291
|
function BubbleCardPicker() {
|
|
1059
1292
|
var pair = useBubbleCards();
|
|
@@ -1141,11 +1374,60 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1141
1374
|
note ? React.createElement('div', { className: 'iris-clean-note' }, note) : null);
|
|
1142
1375
|
}
|
|
1143
1376
|
|
|
1377
|
+
function HostDoctorPanel() {
|
|
1378
|
+
var pair = React.useState(null);
|
|
1379
|
+
var report = pair[0];
|
|
1380
|
+
var setReport = pair[1];
|
|
1381
|
+
var busyPair = React.useState(false);
|
|
1382
|
+
var busy = busyPair[0];
|
|
1383
|
+
var setBusy = busyPair[1];
|
|
1384
|
+
var errorPair = React.useState('');
|
|
1385
|
+
var error = errorPair[0];
|
|
1386
|
+
var setError = errorPair[1];
|
|
1387
|
+
|
|
1388
|
+
function load() {
|
|
1389
|
+
setBusy(true);
|
|
1390
|
+
setError('');
|
|
1391
|
+
fetch('/iris/api/doctor')
|
|
1392
|
+
.then(function (res) { return res.json().then(function (data) { if (!res.ok) throw new Error(data.error || '诊断失败'); return data; }); })
|
|
1393
|
+
.then(function (data) { setReport(data); })
|
|
1394
|
+
.catch(function (err) { setError(String(err && err.message || err)); })
|
|
1395
|
+
.finally(function () { setBusy(false); });
|
|
1396
|
+
}
|
|
1397
|
+
React.useEffect(function () { load(); }, []);
|
|
1398
|
+
|
|
1399
|
+
var checks = report && Array.isArray(report.checks) ? report.checks : [];
|
|
1400
|
+
var issues = checks.filter(function (item) { return item.status !== 'ok'; });
|
|
1401
|
+
var mark = { ok: '✓', warn: '!', error: '×' };
|
|
1402
|
+
function rows(list) {
|
|
1403
|
+
return React.createElement('div', { className: 'iris-doctor-list' }, list.map(function (item) {
|
|
1404
|
+
return React.createElement('div', { key: item.id, className: 'iris-doctor-row ' + item.status },
|
|
1405
|
+
React.createElement('span', {}, mark[item.status] || '·'),
|
|
1406
|
+
React.createElement('span', {}, item.summary));
|
|
1407
|
+
}));
|
|
1408
|
+
}
|
|
1409
|
+
var score = report
|
|
1410
|
+
? report.summary.errors + ' 错误 · ' + report.summary.warnings + ' 警告'
|
|
1411
|
+
: (busy ? '正在检查宿主…' : '尚未运行');
|
|
1412
|
+
return React.createElement('div', { className: 'iris-wb-card' },
|
|
1413
|
+
React.createElement('div', { className: 'iris-doctor-top' },
|
|
1414
|
+
React.createElement('span', { className: 'iris-doctor-score' }, score),
|
|
1415
|
+
report && report.host ? React.createElement('span', { className: 'iris-wb-muted' }, 'DSH ' + report.host.version) : null,
|
|
1416
|
+
React.createElement('button', { className: 'iris-pm-btn', disabled: busy, onClick: load }, busy ? '检查中…' : '刷新')),
|
|
1417
|
+
error ? React.createElement('div', { className: 'iris-doctor-row error' }, React.createElement('span', {}, '×'), React.createElement('span', {}, error)) : null,
|
|
1418
|
+
issues.length ? rows(issues) : (report ? React.createElement('div', { className: 'iris-wb-muted' }, '当前可观察项全部健康') : null),
|
|
1419
|
+
checks.length ? React.createElement('details', { className: 'iris-doctor-details' },
|
|
1420
|
+
React.createElement('summary', {}, '查看全部 ' + checks.length + ' 项'),
|
|
1421
|
+
rows(checks)) : null,
|
|
1422
|
+
React.createElement('div', { className: 'iris-wb-muted' }, '只读本机注册证据;不调用 Browser、模型或供应商。'));
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1144
1425
|
function WorkbenchPanel() {
|
|
1145
1426
|
var state = useIrisState(5000);
|
|
1146
1427
|
var running = (state && state.tasks && state.tasks.running) || [];
|
|
1147
1428
|
var recent = (state && state.tasks && state.tasks.recent) || [];
|
|
1148
1429
|
var attention = (state && state.tasks && state.tasks.attention) || [];
|
|
1430
|
+
var artifactTotal = (state && state.artifacts && state.artifacts.total) || 0;
|
|
1149
1431
|
var selPair = React.useState(null);
|
|
1150
1432
|
var selectedTask = selPair[0];
|
|
1151
1433
|
var setSelectedTask = selPair[1];
|
|
@@ -1156,12 +1438,17 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1156
1438
|
React.createElement('span', { className: 'iris-wb-date' }, state ? '刷新于 ' + new Date().toLocaleTimeString() : '加载中…')),
|
|
1157
1439
|
React.createElement('div', { className: 'iris-wb-sec' }, '供应商'),
|
|
1158
1440
|
React.createElement(ProviderManager, {}),
|
|
1441
|
+
React.createElement('div', { className: 'iris-wb-sec' }, '能力健康'),
|
|
1442
|
+
React.createElement(CapabilityHealthOverview, {}),
|
|
1443
|
+
React.createElement('div', { className: 'iris-wb-muted' }, '绿色有效 7 天;真实成功或显式实测会刷新。不会后台探测,也不会因 429、网络或供应商 5xx 变红。'),
|
|
1159
1444
|
React.createElement('div', { className: 'iris-wb-sec' }, '能力分配(failover 顺序)'),
|
|
1160
1445
|
React.createElement(CapabilityAssigner, {}),
|
|
1161
1446
|
React.createElement('div', { className: 'iris-wb-sec' }, 'Iris 泡泡快捷卡片(勾选后显示在「⚡ 常用」标签)'),
|
|
1162
1447
|
React.createElement(BubbleCardPicker, {}),
|
|
1163
1448
|
React.createElement('div', { className: 'iris-wb-sec' }, '对话框提示词优化'),
|
|
1164
1449
|
React.createElement(PromptOptimizerSettings, {}),
|
|
1450
|
+
React.createElement('div', { className: 'iris-wb-sec' }, '宿主诊断'),
|
|
1451
|
+
React.createElement(HostDoctorPanel, {}),
|
|
1165
1452
|
React.createElement('div', { className: 'iris-wb-sec' }, '运行中任务'),
|
|
1166
1453
|
React.createElement('div', { className: 'iris-wb-box' },
|
|
1167
1454
|
running.length ? running.map(function (t) { return taskRow(t, true, selectedTask === t.id, onSelect); })
|
|
@@ -1170,6 +1457,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1170
1457
|
React.createElement('div', { className: 'iris-wb-sec' }, '需要处理'),
|
|
1171
1458
|
React.createElement('div', { className: 'iris-wb-box' },
|
|
1172
1459
|
attention.map(function (t) { return taskRow(t, false, selectedTask === t.id, onSelect); }))) : null,
|
|
1460
|
+
React.createElement('div', { className: 'iris-wb-sec' }, '作品库'),
|
|
1461
|
+
React.createElement(ArtifactGallery, { total: artifactTotal }),
|
|
1173
1462
|
React.createElement('div', { className: 'iris-wb-sec' }, '历史任务'),
|
|
1174
1463
|
React.createElement(CleanupBar, {}),
|
|
1175
1464
|
React.createElement(HistoryBrowser, { recent: recent, selected: selectedTask, onSelect: onSelect }),
|
|
@@ -1184,6 +1473,8 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1184
1473
|
var recent = (state && state.tasks && state.tasks.recent) || [];
|
|
1185
1474
|
var attention = (state && state.tasks && state.tasks.attention) || [];
|
|
1186
1475
|
var recentTotal = (state && state.tasks && state.tasks.recentTotal) || recent.length;
|
|
1476
|
+
var works = (state && state.artifacts && state.artifacts.recent) || [];
|
|
1477
|
+
var artifactTotal = (state && state.artifacts && state.artifacts.total) || works.length;
|
|
1187
1478
|
var cards = useBubbleCards()[0];
|
|
1188
1479
|
var tabPair = React.useState('tasks');
|
|
1189
1480
|
var tab = tabPair[0];
|
|
@@ -1194,11 +1485,7 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1194
1485
|
function onSelect(id) { setSelectedTask(id); }
|
|
1195
1486
|
var body;
|
|
1196
1487
|
if (tab === 'tasks') {
|
|
1197
|
-
//
|
|
1198
|
-
var succeeded = recent.filter(function (t) {
|
|
1199
|
-
return t.status === 'succeeded' && Array.isArray(t.media) && t.media.length > 0;
|
|
1200
|
-
});
|
|
1201
|
-
var shown = succeeded.slice(0, 6);
|
|
1488
|
+
// 快捷面板承载当前任务、需要处理的异常事实与独立作品库中的最近作品。
|
|
1202
1489
|
body = React.createElement('div', { className: 'iris-wb-box' },
|
|
1203
1490
|
running.length ? React.createElement('div', {},
|
|
1204
1491
|
React.createElement('div', { className: 'iris-wb-sec' }, '运行中'),
|
|
@@ -1206,12 +1493,12 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1206
1493
|
attention.length ? React.createElement('div', {},
|
|
1207
1494
|
React.createElement('div', { className: 'iris-wb-sec' }, '需要处理'),
|
|
1208
1495
|
attention.slice(0, 4).map(function (t) { return taskRowMini(t, selectedTask === t.id, onSelect); })) : null,
|
|
1209
|
-
React.createElement('div', { className: 'iris-wb-sec' }, '
|
|
1210
|
-
|
|
1211
|
-
: React.createElement('div', { className: 'iris-wb-empty' }, '
|
|
1212
|
-
|
|
1496
|
+
React.createElement('div', { className: 'iris-wb-sec' }, '最近作品'),
|
|
1497
|
+
works.length ? works.map(artifactRowMini)
|
|
1498
|
+
: React.createElement('div', { className: 'iris-wb-empty' }, '尚无作品'),
|
|
1499
|
+
artifactTotal > works.length || recentTotal > 0
|
|
1213
1500
|
? React.createElement('div', { className: 'iris-bubble-more', style: { cursor: 'default' } },
|
|
1214
|
-
'
|
|
1501
|
+
'完整作品库、失败与取消历史请到 Iris 工作台查看') : null);
|
|
1215
1502
|
} else {
|
|
1216
1503
|
var defs = CARD_DEFS.filter(function (d) { return cards.indexOf(d.action) >= 0; });
|
|
1217
1504
|
body = defs.length
|
|
@@ -1470,7 +1757,9 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1470
1757
|
function FloatingBubble() {
|
|
1471
1758
|
var state = useIrisState(5000);
|
|
1472
1759
|
var providers = (state && state.providers) || [];
|
|
1473
|
-
var
|
|
1760
|
+
var fallbackConfigured = providers.some(function (p) { return p.enabled && p.apiKeyHint; });
|
|
1761
|
+
var overallHealth = state && state.health && HEALTH_META[state.health.overall]
|
|
1762
|
+
? state.health.overall : (fallbackConfigured ? 'configured' : 'unconfigured');
|
|
1474
1763
|
var running = (state && state.tasks && state.tasks.running) || [];
|
|
1475
1764
|
var attention = (state && state.tasks && state.tasks.attention) || [];
|
|
1476
1765
|
|
|
@@ -1538,17 +1827,30 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1538
1827
|
}
|
|
1539
1828
|
}
|
|
1540
1829
|
|
|
1830
|
+
function persistCurrentPosition() {
|
|
1831
|
+
setPos(function (current) {
|
|
1832
|
+
if (current && current.x != null && current.y != null) {
|
|
1833
|
+
try { localStorage.setItem('iris-bubble-pos', JSON.stringify(current)); } catch (_) {}
|
|
1834
|
+
}
|
|
1835
|
+
return current;
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1541
1839
|
function onPointerUp(e) {
|
|
1542
1840
|
var drag = dragRef.current;
|
|
1543
1841
|
if (!drag) return;
|
|
1544
1842
|
try { e.currentTarget.releasePointerCapture(e.pointerId); } catch (_) {}
|
|
1545
1843
|
var moved = drag.moved;
|
|
1546
1844
|
dragRef.current = null;
|
|
1547
|
-
if (moved)
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1845
|
+
if (moved) persistCurrentPosition();
|
|
1846
|
+
else setOpen(!open);
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
function onPointerCancel(e) {
|
|
1850
|
+
if (!dragRef.current) return;
|
|
1851
|
+
try { e.currentTarget.releasePointerCapture(e.pointerId); } catch (_) {}
|
|
1852
|
+
dragRef.current = null;
|
|
1853
|
+
persistCurrentPosition();
|
|
1552
1854
|
}
|
|
1553
1855
|
|
|
1554
1856
|
var style;
|
|
@@ -1566,8 +1868,10 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1566
1868
|
var panel = null;
|
|
1567
1869
|
if (open) {
|
|
1568
1870
|
var xy = bubbleXY();
|
|
1569
|
-
var
|
|
1570
|
-
var
|
|
1871
|
+
var panelWidth = Math.min(360, Math.max(0, window.innerWidth - 16));
|
|
1872
|
+
var panelHeight = Math.min(520, Math.max(0, window.innerHeight - 16));
|
|
1873
|
+
var panelLeft = Math.max(8, Math.min(xy.x - panelWidth + 30, window.innerWidth - panelWidth - 8));
|
|
1874
|
+
var panelTop = Math.max(8, Math.min(xy.y - 260, window.innerHeight - panelHeight - 8));
|
|
1571
1875
|
panel = React.createElement('div', { className: 'iris-bubble-panel', style: { left: panelLeft + 'px', top: panelTop + 'px' } },
|
|
1572
1876
|
React.createElement('div', { className: 'iris-bubble-panel-head' },
|
|
1573
1877
|
React.createElement('button', { className: 'iris-bubble-close', onClick: function () { setOpen(false); } }, '✕')),
|
|
@@ -1575,52 +1879,63 @@ window.__ModuleLoader__.load({ id: '@mokuyoaxis/dsh-iris', factory: (require) =>
|
|
|
1575
1879
|
}
|
|
1576
1880
|
|
|
1577
1881
|
return React.createElement('div', {
|
|
1578
|
-
className: 'iris-bubble' +
|
|
1882
|
+
className: 'iris-bubble health-' + overallHealth + (dragRef.current ? ' dragging' : ''),
|
|
1579
1883
|
style: style,
|
|
1580
1884
|
onPointerDown: onPointerDown,
|
|
1581
1885
|
onPointerMove: onPointerMove,
|
|
1582
1886
|
onPointerUp: onPointerUp,
|
|
1583
|
-
|
|
1887
|
+
onPointerCancel: onPointerCancel,
|
|
1888
|
+
title: 'Iris ' + healthText({ status: overallHealth }) + ':点击打开快捷面板'
|
|
1584
1889
|
},
|
|
1585
1890
|
React.createElement('div', { className: 'iris-bubble-btn' }, '🫧'),
|
|
1586
1891
|
badge,
|
|
1587
1892
|
panel);
|
|
1588
1893
|
}
|
|
1589
1894
|
|
|
1590
|
-
|
|
1591
|
-
|
|
1895
|
+
var clientSlots = clientSlotsPort(ctx);
|
|
1896
|
+
|
|
1897
|
+
clientSlots.inject('settings.section', function () {
|
|
1898
|
+
var dispose = clientSlots.register({
|
|
1592
1899
|
name: 'settings.section',
|
|
1593
1900
|
id: 'iris-workbench',
|
|
1594
1901
|
order: 140,
|
|
1595
1902
|
label: function () { return 'Iris 工作台'; }
|
|
1596
1903
|
}, WorkbenchPanel);
|
|
1904
|
+
reportClientSeat('settings.section');
|
|
1905
|
+
return dispose;
|
|
1597
1906
|
});
|
|
1598
1907
|
|
|
1599
|
-
|
|
1600
|
-
|
|
1908
|
+
clientSlots.inject('conversation.input.right', function () {
|
|
1909
|
+
var dispose = clientSlots.register({
|
|
1601
1910
|
name: 'conversation.input.right',
|
|
1602
1911
|
id: 'iris-prompt-optimizer',
|
|
1603
1912
|
order: 40,
|
|
1604
1913
|
label: function () { return 'Iris 提示词优化'; }
|
|
1605
1914
|
}, PromptOptimizerControl);
|
|
1915
|
+
reportClientSeat('conversation.input.right');
|
|
1916
|
+
return dispose;
|
|
1606
1917
|
});
|
|
1607
1918
|
|
|
1608
|
-
|
|
1609
|
-
|
|
1919
|
+
clientSlots.inject('conversation.input.dock', function () {
|
|
1920
|
+
var dispose = clientSlots.register({
|
|
1610
1921
|
name: 'conversation.input.dock',
|
|
1611
1922
|
id: 'iris-progress',
|
|
1612
1923
|
order: 0,
|
|
1613
1924
|
label: function () { return 'Iris 任务进度'; }
|
|
1614
1925
|
}, ProgressDock);
|
|
1926
|
+
reportClientSeat('conversation.input.dock');
|
|
1927
|
+
return dispose;
|
|
1615
1928
|
});
|
|
1616
1929
|
|
|
1617
|
-
|
|
1618
|
-
|
|
1930
|
+
clientSlots.inject('shell.overlay', function () {
|
|
1931
|
+
var dispose = clientSlots.register({
|
|
1619
1932
|
name: 'shell.overlay',
|
|
1620
1933
|
id: 'iris-bubble',
|
|
1621
1934
|
order: 200,
|
|
1622
1935
|
label: function () { return 'Iris 泡泡'; }
|
|
1623
1936
|
}, FloatingBubble);
|
|
1937
|
+
reportClientSeat('shell.overlay');
|
|
1938
|
+
return dispose;
|
|
1624
1939
|
});
|
|
1625
1940
|
console.log('[iris] client slots registered');
|
|
1626
1941
|
} catch (err) {
|