@goodandready/dsh-moa 0.2.41 → 0.2.43
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/README.md +7 -0
- package/README.ru.md +7 -0
- package/README.zh.md +11 -0
- package/lib/client.js +421 -107
- package/lib/file-workspace.js +40 -43
- package/lib/history.js +8 -1
- package/lib/index.js +55 -4
- package/lib/live-canvas.js +39 -6
- package/lib/moa-budget.js +28 -19
- package/lib/moa-candidates.js +3 -0
- package/lib/moa-multi-judge.js +2 -1
- package/lib/moa-prompts.js +5 -1
- package/lib/moa-runner.js +48 -46
- package/lib/moa-stream.js +59 -1
- package/lib/moa-test-gate.js +11 -0
- package/lib/routes.js +10 -3
- package/lib/updater.js +23 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -101,6 +101,8 @@ window.__ModuleLoader__.load({
|
|
|
101
101
|
'proposers.add_btn': '+ Add Candidate Model',
|
|
102
102
|
'proposers.temp_label': 'Candidates Temperature',
|
|
103
103
|
'proposers.temp_hint': 'Recommended: 0.6 – 0.8 for creative, independent solution exploration.',
|
|
104
|
+
'proposers.temperature_gradient': 'Temperature Gradient Exploration (0.2 → 0.9 across candidates)',
|
|
105
|
+
'proposers.candidate_temp_tooltip': 'Candidate Temperature (leave empty for default/gradient)',
|
|
104
106
|
'stats.title': '📈 MoA Analytics & Telemetry',
|
|
105
107
|
'stats.desc': 'Session telemetry, historical runs, and model win-rate tracking.',
|
|
106
108
|
'stats.total_runs': 'Total Runs',
|
|
@@ -142,6 +144,64 @@ window.__ModuleLoader__.load({
|
|
|
142
144
|
'diff.curator': 'Curator Synthesis',
|
|
143
145
|
'diff.candidate': 'Candidate {num}',
|
|
144
146
|
'diff.empty': 'Selected files are identical (zero diff).',
|
|
147
|
+
// Multi-Judge
|
|
148
|
+
'aggregator.multi_judge_title': 'Multi-Judge Panel & Consensus Voting',
|
|
149
|
+
'aggregator.multi_judge_desc': 'Deploy multiple judge models to score candidates and determine winner via consensus voting.',
|
|
150
|
+
'aggregator.voting_strategy_label': 'Voting Strategy:',
|
|
151
|
+
'aggregator.voting_majority': 'Majority Vote',
|
|
152
|
+
'aggregator.voting_highest_score': 'Highest Score',
|
|
153
|
+
'aggregator.voting_unanimous': 'Unanimous',
|
|
154
|
+
'aggregator.judge_models_title': 'Judge Panel Models',
|
|
155
|
+
'aggregator.judge_models_desc': 'Specify 2 or more judge models for panel consensus.',
|
|
156
|
+
'aggregator.add_judge_btn': '+ Add Judge Model',
|
|
157
|
+
'aggregator.judge_badge': 'Judge #{num}',
|
|
158
|
+
'aggregator.judge_models_empty': 'At least 2 judge models are required for consensus voting.',
|
|
159
|
+
|
|
160
|
+
// Composite
|
|
161
|
+
'aggregator.composite_title': 'Composite Hybrid Synthesis (AST / Block Merge)',
|
|
162
|
+
'aggregator.composite_desc': 'Synthesize modular code blocks, types, and algorithms from multiple candidates into a unified composite.',
|
|
163
|
+
|
|
164
|
+
// Budget
|
|
165
|
+
'aggregator.budget_title': 'Cost Budget Guardrails & Auto-Fallback',
|
|
166
|
+
'aggregator.budget_desc': 'Cap maximum run cost; auto-trim candidate pool or abort to prevent accidental token spend.',
|
|
167
|
+
'aggregator.budget_max_usd': 'Max USD ($):',
|
|
168
|
+
'aggregator.budget_action': 'Action:',
|
|
169
|
+
'aggregator.budget_action_trim': 'Trim Pool',
|
|
170
|
+
'aggregator.budget_action_abort': 'Abort',
|
|
171
|
+
|
|
172
|
+
// Reports
|
|
173
|
+
'aggregator.report_title': 'Automated Benchmark & Post-Mortem Report',
|
|
174
|
+
'aggregator.report_desc': 'Generate downloadable Markdown & JSON reports detailing candidate latency, agreement, test gate, and cost breakdown.',
|
|
175
|
+
|
|
176
|
+
// Local Fallback
|
|
177
|
+
'aggregator.local_fallback_title': 'Graceful Degradation & Local Fallback',
|
|
178
|
+
'aggregator.local_fallback_desc': 'Automatically fall back to local Ollama / MiniPC models when online candidate APIs fail.',
|
|
179
|
+
'aggregator.local_models_title': 'Local Fallback Models',
|
|
180
|
+
'aggregator.local_models_desc': 'Local offline models used when cloud candidate APIs fail.',
|
|
181
|
+
'aggregator.add_local_btn': '+ Add Local Model',
|
|
182
|
+
'aggregator.local_badge': 'Local #{num}',
|
|
183
|
+
'aggregator.local_models_empty': 'Configure at least one local fallback model (e.g. Ollama).',
|
|
184
|
+
|
|
185
|
+
// Smart Routing Model
|
|
186
|
+
'config.smart_routing_title': 'Smart Prompt Routing',
|
|
187
|
+
'config.smart_routing_desc': 'Analyze task complexity and automatically select the optimal preset.',
|
|
188
|
+
'config.smart_routing_model': 'Routing Model:',
|
|
189
|
+
|
|
190
|
+
// Multi-Turn Context
|
|
191
|
+
'aggregator.multi_turn_title': 'Multi-Turn Context & Diff Pruning',
|
|
192
|
+
'aggregator.multi_turn_desc': 'Retain conversation context across turns while pruning duplicate code blocks.',
|
|
193
|
+
|
|
194
|
+
// UI States & Errors
|
|
195
|
+
'status.loading': 'Loading MoA settings…',
|
|
196
|
+
'status.unavailable': 'MoA service temporarily unavailable',
|
|
197
|
+
'status.retry': 'Retry',
|
|
198
|
+
'status.error_saving': 'Error saving: ',
|
|
199
|
+
'error_boundary.title': '⚠️ MoA UI Error:',
|
|
200
|
+
'error_boundary.retry': 'Retry',
|
|
201
|
+
|
|
202
|
+
// Diff Viewer
|
|
203
|
+
'diff.select_run': 'Run:',
|
|
204
|
+
'diff.latest_run': 'Latest / Active Workspace',
|
|
145
205
|
'updater.success': 'Successfully updated to v{version}. Restart DSH to apply.',
|
|
146
206
|
}
|
|
147
207
|
|
|
@@ -219,6 +279,8 @@ window.__ModuleLoader__.load({
|
|
|
219
279
|
'proposers.add_btn': '+ 添加候选模型',
|
|
220
280
|
'proposers.temp_label': '候选模型采样温度 (Temperature)',
|
|
221
281
|
'proposers.temp_hint': '推荐:0.6 – 0.8,鼓励创造性、发散性的独立探索。',
|
|
282
|
+
'proposers.temperature_gradient': '温度梯度探索(候选模型采样温度从 0.2 到 0.9 递增)',
|
|
283
|
+
'proposers.candidate_temp_tooltip': '候选模型采样温度(留空使用默认/梯度)',
|
|
222
284
|
'stats.title': '📈 MoA 数据分析与遥测',
|
|
223
285
|
'stats.desc': '会话遥测、历史运行追踪及模型胜率统计。',
|
|
224
286
|
'stats.total_runs': '总运行次数',
|
|
@@ -260,6 +322,64 @@ window.__ModuleLoader__.load({
|
|
|
260
322
|
'diff.curator': '裁判合成 (Curator Synthesis)',
|
|
261
323
|
'diff.candidate': '候选方案 {num}',
|
|
262
324
|
'diff.empty': '选中的文件完全一致(无差异)。',
|
|
325
|
+
// Multi-Judge
|
|
326
|
+
'aggregator.multi_judge_title': '多裁判委员会与共识投票',
|
|
327
|
+
'aggregator.multi_judge_desc': '部署多个裁判模型对候选方案打分并通过共识投票评定获胜者。',
|
|
328
|
+
'aggregator.voting_strategy_label': '投票策略:',
|
|
329
|
+
'aggregator.voting_majority': '多数决',
|
|
330
|
+
'aggregator.voting_highest_score': '最高分',
|
|
331
|
+
'aggregator.voting_unanimous': '全票通过',
|
|
332
|
+
'aggregator.judge_models_title': '裁判模型列表',
|
|
333
|
+
'aggregator.judge_models_desc': '指定 2 个或更多裁判模型以实现委员会共识。',
|
|
334
|
+
'aggregator.add_judge_btn': '+ 添加裁判模型',
|
|
335
|
+
'aggregator.judge_badge': '裁判 #{num}',
|
|
336
|
+
'aggregator.judge_models_empty': '共识投票至少需要配置 2 个裁判模型。',
|
|
337
|
+
|
|
338
|
+
// Composite
|
|
339
|
+
'aggregator.composite_title': '混合复合综合 (AST / 代码块合并)',
|
|
340
|
+
'aggregator.composite_desc': '从多个候选方案中提取模块化代码块、类型定义与算法并综合为统一方案。',
|
|
341
|
+
|
|
342
|
+
// Budget
|
|
343
|
+
'aggregator.budget_title': '成本预算护栏与自动分流',
|
|
344
|
+
'aggregator.budget_desc': '限制单次最高花费;自动削减候选模型或中止以防止超额消耗。',
|
|
345
|
+
'aggregator.budget_max_usd': '最高金额 ($):',
|
|
346
|
+
'aggregator.budget_action': '处置方式:',
|
|
347
|
+
'aggregator.budget_action_trim': '削减候选',
|
|
348
|
+
'aggregator.budget_action_abort': '中止运行',
|
|
349
|
+
|
|
350
|
+
// Reports
|
|
351
|
+
'aggregator.report_title': '自动化基准与复盘报告',
|
|
352
|
+
'aggregator.report_desc': '生成可下载的 Markdown 与 JSON 报告,详细记录候选方案耗时、一致性、测试门禁与成本分布。',
|
|
353
|
+
|
|
354
|
+
// Local Fallback
|
|
355
|
+
'aggregator.local_fallback_title': '优雅降级与本地备用模型',
|
|
356
|
+
'aggregator.local_fallback_desc': '在线候选模型 API 故障时自动降级到本地 Ollama / MiniPC 模型。',
|
|
357
|
+
'aggregator.local_models_title': '本地备用模型列表',
|
|
358
|
+
'aggregator.local_models_desc': '云端候选 API 异常时调用的本地离线模型。',
|
|
359
|
+
'aggregator.add_local_btn': '+ 添加本地模型',
|
|
360
|
+
'aggregator.local_badge': '本地 #{num}',
|
|
361
|
+
'aggregator.local_models_empty': '请至少配置一个本地备用模型(例如 Ollama)。',
|
|
362
|
+
|
|
363
|
+
// Smart Routing Model
|
|
364
|
+
'config.smart_routing_title': '智能提示词分流',
|
|
365
|
+
'config.smart_routing_desc': '分析任务复杂度并自动匹配最佳预设。',
|
|
366
|
+
'config.smart_routing_model': '分流决策模型:',
|
|
367
|
+
|
|
368
|
+
// Multi-Turn Context
|
|
369
|
+
'aggregator.multi_turn_title': '多轮对话上下文与差异剪枝',
|
|
370
|
+
'aggregator.multi_turn_desc': '跨轮次保留对话上下文并剪枝重复代码块。',
|
|
371
|
+
|
|
372
|
+
// UI States & Errors
|
|
373
|
+
'status.loading': '正在加载 MoA 设置…',
|
|
374
|
+
'status.unavailable': 'MoA 服务暂不可用',
|
|
375
|
+
'status.retry': '重试',
|
|
376
|
+
'status.error_saving': '保存失败:',
|
|
377
|
+
'error_boundary.title': '⚠️ MoA 界面错误:',
|
|
378
|
+
'error_boundary.retry': '重试',
|
|
379
|
+
|
|
380
|
+
// Diff Viewer
|
|
381
|
+
'diff.select_run': '运行记录:',
|
|
382
|
+
'diff.latest_run': '最新 / 当前工作区',
|
|
263
383
|
'updater.success': '成功更新至 v{version},请重启 DSH 使其生效。',
|
|
264
384
|
}
|
|
265
385
|
|
|
@@ -307,6 +427,55 @@ window.__ModuleLoader__.load({
|
|
|
307
427
|
Chevron = FallbackChevron
|
|
308
428
|
}
|
|
309
429
|
|
|
430
|
+
|
|
431
|
+
function renderSlotEditorList({ items, badgePrefix, onUpdate, onAdd, onRemove, addLabel, emptyWarning, availableModels, t }) {
|
|
432
|
+
return React.createElement(
|
|
433
|
+
'div',
|
|
434
|
+
{ style: { display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4 } },
|
|
435
|
+
(Array.isArray(items) ? items : []).map((item, idx) =>
|
|
436
|
+
React.createElement(
|
|
437
|
+
'div',
|
|
438
|
+
{ key: idx, style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
439
|
+
React.createElement('span', { className: 'moa-cand-badge' }, `${badgePrefix} #${idx + 1}`),
|
|
440
|
+
React.createElement(
|
|
441
|
+
'div',
|
|
442
|
+
{ style: { flex: 1 } },
|
|
443
|
+
React.createElement(SearchableModelPicker, {
|
|
444
|
+
provider: (item && item.provider) || '',
|
|
445
|
+
model: (item && item.model) || '',
|
|
446
|
+
onChange: (prov, mod) => onUpdate(idx, prov, mod),
|
|
447
|
+
availableModels,
|
|
448
|
+
t,
|
|
449
|
+
})
|
|
450
|
+
),
|
|
451
|
+
React.createElement(
|
|
452
|
+
'button',
|
|
453
|
+
{
|
|
454
|
+
type: 'button',
|
|
455
|
+
className: 'moa-btn moa-btn-danger moa-btn-sub',
|
|
456
|
+
onClick: () => onRemove(idx),
|
|
457
|
+
},
|
|
458
|
+
'✕'
|
|
459
|
+
)
|
|
460
|
+
)
|
|
461
|
+
),
|
|
462
|
+
React.createElement(
|
|
463
|
+
'button',
|
|
464
|
+
{
|
|
465
|
+
type: 'button',
|
|
466
|
+
className: 'moa-btn moa-btn-sub',
|
|
467
|
+
style: { marginTop: 4, alignSelf: 'flex-start' },
|
|
468
|
+
onClick: onAdd,
|
|
469
|
+
},
|
|
470
|
+
addLabel
|
|
471
|
+
),
|
|
472
|
+
emptyWarning ? React.createElement(
|
|
473
|
+
'div',
|
|
474
|
+
{ style: { fontSize: 11, color: 'var(--dsw-alias-state-warning-primary, #e6a23c)', marginTop: 2 } },
|
|
475
|
+
emptyWarning
|
|
476
|
+
) : null
|
|
477
|
+
)
|
|
478
|
+
}
|
|
310
479
|
const STYLES = `
|
|
311
480
|
/* dsh-moa modern clinebot-aligned styling */
|
|
312
481
|
.moa-page{display:flex;flex-direction:column;gap:18px;padding:4px 0 24px;max-width:960px}
|
|
@@ -635,8 +804,25 @@ window.__ModuleLoader__.load({
|
|
|
635
804
|
const [loading, setLoading] = React.useState(false)
|
|
636
805
|
const [error, setError] = React.useState(null)
|
|
637
806
|
const [hasFetched, setHasFetched] = React.useState(false)
|
|
807
|
+
const [historyRuns, setHistoryRuns] = React.useState([])
|
|
808
|
+
const [selectedRunId, setSelectedRunId] = React.useState('')
|
|
809
|
+
|
|
810
|
+
React.useEffect(() => {
|
|
811
|
+
fetch('/dsh-moa/history?limit=30', { cache: 'no-store' })
|
|
812
|
+
.then((r) => r.json())
|
|
813
|
+
.then((data) => {
|
|
814
|
+
if (data && data.ok && Array.isArray(data.runs)) {
|
|
815
|
+
setHistoryRuns(data.runs)
|
|
816
|
+
}
|
|
817
|
+
})
|
|
818
|
+
.catch(() => {})
|
|
819
|
+
}, [])
|
|
820
|
+
|
|
821
|
+
const selectedRun = historyRuns.find((r) => r.id === selectedRunId)
|
|
822
|
+
const candidateCount = selectedRun && Array.isArray(selectedRun.candidates) && selectedRun.candidates.length > 0
|
|
823
|
+
? selectedRun.candidates.length
|
|
824
|
+
: Math.max(currentRefs.length || 0, 2)
|
|
638
825
|
|
|
639
|
-
const candidateCount = Math.max(currentRefs.length || 0, 2)
|
|
640
826
|
const candidateOptions = []
|
|
641
827
|
for (let i = 1; i <= candidateCount; i++) {
|
|
642
828
|
candidateOptions.push({
|
|
@@ -649,6 +835,7 @@ window.__ModuleLoader__.load({
|
|
|
649
835
|
setLoading(true)
|
|
650
836
|
setError(null)
|
|
651
837
|
const params = new URLSearchParams()
|
|
838
|
+
if (selectedRunId) params.set('runId', selectedRunId)
|
|
652
839
|
if (fromCandidate) params.set('from', fromCandidate)
|
|
653
840
|
if (toCandidate) params.set('to', toCandidate)
|
|
654
841
|
if (targetFile) params.set('file', targetFile)
|
|
@@ -679,11 +866,11 @@ window.__ModuleLoader__.load({
|
|
|
679
866
|
setHasFetched(true)
|
|
680
867
|
setError(err.message || String(err))
|
|
681
868
|
})
|
|
682
|
-
}, [fromCandidate, toCandidate, selectedFile])
|
|
869
|
+
}, [fromCandidate, toCandidate, selectedFile, selectedRunId])
|
|
683
870
|
|
|
684
871
|
React.useEffect(() => {
|
|
685
872
|
loadDiff()
|
|
686
|
-
}, [fromCandidate, toCandidate])
|
|
873
|
+
}, [fromCandidate, toCandidate, selectedRunId])
|
|
687
874
|
|
|
688
875
|
const handleFileChange = (e) => {
|
|
689
876
|
const nextFile = e.target.value
|
|
@@ -707,6 +894,25 @@ window.__ModuleLoader__.load({
|
|
|
707
894
|
React.createElement(
|
|
708
895
|
'div',
|
|
709
896
|
{ className: 'moa-diff-controls' },
|
|
897
|
+
/* Run selector dropdown (#179) */
|
|
898
|
+
React.createElement(
|
|
899
|
+
'label',
|
|
900
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } },
|
|
901
|
+
t('diff.select_run') || 'Run:',
|
|
902
|
+
React.createElement(
|
|
903
|
+
'select',
|
|
904
|
+
{
|
|
905
|
+
className: 'moa-diff-select',
|
|
906
|
+
value: selectedRunId,
|
|
907
|
+
onChange: (e) => setSelectedRunId(e.target.value),
|
|
908
|
+
},
|
|
909
|
+
React.createElement('option', { value: '' }, t('diff.latest_run') || 'Latest / Active Workspace'),
|
|
910
|
+
historyRuns.map((r) => {
|
|
911
|
+
const label = `${r.preset || 'default'} [${(r.id || '').slice(0, 8)}] ${(r.prompt || '').slice(0, 24)}...`
|
|
912
|
+
return React.createElement('option', { key: r.id, value: r.id }, label)
|
|
913
|
+
})
|
|
914
|
+
)
|
|
915
|
+
),
|
|
710
916
|
React.createElement(
|
|
711
917
|
'label',
|
|
712
918
|
{ style: { display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } },
|
|
@@ -1331,7 +1537,7 @@ window.__ModuleLoader__.load({
|
|
|
1331
1537
|
})
|
|
1332
1538
|
)
|
|
1333
1539
|
),
|
|
1334
|
-
/* Multi-Judge Panel & Consensus Voting */
|
|
1540
|
+
/* Multi-Judge Panel & Consensus Voting (#175, #176) */
|
|
1335
1541
|
React.createElement(
|
|
1336
1542
|
'div',
|
|
1337
1543
|
{ style: { display: 'flex', flexDirection: 'column', gap: 6, padding: '8px 12px', background: 'var(--dsw-alias-bg-layer-2)', borderRadius: 8, border: '1px solid var(--dsw-alias-border-l2)', marginTop: 8 } },
|
|
@@ -1344,29 +1550,62 @@ window.__ModuleLoader__.load({
|
|
|
1344
1550
|
onChange: (e) => updateCurrentPreset((p) => ({ ...p, multi_judge_enabled: e.target.checked })),
|
|
1345
1551
|
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
1346
1552
|
}),
|
|
1347
|
-
'Multi-Judge Panel & Consensus Voting'
|
|
1553
|
+
t('aggregator.multi_judge_title') || 'Multi-Judge Panel & Consensus Voting'
|
|
1348
1554
|
),
|
|
1349
1555
|
React.createElement(
|
|
1350
1556
|
'div',
|
|
1351
1557
|
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginLeft: 22 } },
|
|
1352
|
-
'Deploy multiple judge models to score candidates and determine winner via consensus voting.'
|
|
1558
|
+
t('aggregator.multi_judge_desc') || 'Deploy multiple judge models to score candidates and determine winner via consensus voting.'
|
|
1353
1559
|
),
|
|
1354
1560
|
currentPreset.multi_judge_enabled && React.createElement(
|
|
1355
1561
|
'div',
|
|
1356
|
-
{ style: { marginLeft: 22, display: 'flex',
|
|
1357
|
-
React.createElement('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, 'Voting Strategy:'),
|
|
1562
|
+
{ style: { marginLeft: 22, display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4 } },
|
|
1358
1563
|
React.createElement(
|
|
1359
|
-
'
|
|
1360
|
-
{
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1564
|
+
'div',
|
|
1565
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
1566
|
+
React.createElement('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, t('aggregator.voting_strategy_label') || 'Voting Strategy:'),
|
|
1567
|
+
React.createElement(
|
|
1568
|
+
'select',
|
|
1569
|
+
{
|
|
1570
|
+
className: 'moa-select',
|
|
1571
|
+
style: { width: 140, height: 28, fontSize: 12, padding: '0 6px' },
|
|
1572
|
+
value: currentPreset.judge_voting_strategy || 'majority',
|
|
1573
|
+
onChange: (e) => updateCurrentPreset((p) => ({ ...p, judge_voting_strategy: e.target.value })),
|
|
1574
|
+
},
|
|
1575
|
+
React.createElement('option', { value: 'majority' }, t('aggregator.voting_majority') || 'Majority Vote'),
|
|
1576
|
+
React.createElement('option', { value: 'highest_score' }, t('aggregator.voting_highest_score') || 'Highest Score'),
|
|
1577
|
+
React.createElement('option', { value: 'unanimous' }, t('aggregator.voting_unanimous') || 'Unanimous')
|
|
1578
|
+
)
|
|
1579
|
+
),
|
|
1580
|
+
renderSlotEditorList({
|
|
1581
|
+
items: currentPreset.judge_models,
|
|
1582
|
+
badgePrefix: 'Judge',
|
|
1583
|
+
onUpdate: (idx, prov, mod) => {
|
|
1584
|
+
updateCurrentPreset((p) => {
|
|
1585
|
+
const list = [...(p.judge_models || [])]
|
|
1586
|
+
list[idx] = { provider: prov, model: mod }
|
|
1587
|
+
return { ...p, judge_models: list }
|
|
1588
|
+
})
|
|
1365
1589
|
},
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1590
|
+
onRemove: (idx) => {
|
|
1591
|
+
updateCurrentPreset((p) => ({
|
|
1592
|
+
...p,
|
|
1593
|
+
judge_models: (p.judge_models || []).filter((_, i) => i !== idx),
|
|
1594
|
+
}))
|
|
1595
|
+
},
|
|
1596
|
+
onAdd: () => {
|
|
1597
|
+
updateCurrentPreset((p) => ({
|
|
1598
|
+
...p,
|
|
1599
|
+
judge_models: [...(p.judge_models || []), { provider: (availableModels[0] && availableModels[0].provider) || '', model: (availableModels[0] && availableModels[0].model) || '' }],
|
|
1600
|
+
}))
|
|
1601
|
+
},
|
|
1602
|
+
addLabel: t('aggregator.add_judge_btn') || '+ Add Judge Model',
|
|
1603
|
+
emptyWarning: (!currentPreset.judge_models || currentPreset.judge_models.length < 2)
|
|
1604
|
+
? (t('aggregator.judge_models_empty') || 'At least 2 judge models are required for consensus voting.')
|
|
1605
|
+
: null,
|
|
1606
|
+
availableModels,
|
|
1607
|
+
t,
|
|
1608
|
+
})
|
|
1370
1609
|
)
|
|
1371
1610
|
),
|
|
1372
1611
|
|
|
@@ -1383,12 +1622,12 @@ window.__ModuleLoader__.load({
|
|
|
1383
1622
|
onChange: (e) => updateCurrentPreset((p) => ({ ...p, composite_merge_enabled: e.target.checked })),
|
|
1384
1623
|
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
1385
1624
|
}),
|
|
1386
|
-
'Composite Hybrid Synthesis (AST / Block Merge)'
|
|
1625
|
+
t('aggregator.composite_title') || 'Composite Hybrid Synthesis (AST / Block Merge)'
|
|
1387
1626
|
),
|
|
1388
1627
|
React.createElement(
|
|
1389
1628
|
'div',
|
|
1390
1629
|
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginLeft: 22 } },
|
|
1391
|
-
'Synthesize modular code blocks, types, and algorithms from multiple candidates into a unified composite.'
|
|
1630
|
+
t('aggregator.composite_desc') || 'Synthesize modular code blocks, types, and algorithms from multiple candidates into a unified composite.'
|
|
1392
1631
|
)
|
|
1393
1632
|
),
|
|
1394
1633
|
|
|
@@ -1405,12 +1644,12 @@ window.__ModuleLoader__.load({
|
|
|
1405
1644
|
onChange: (e) => updateCurrentPreset((p) => ({ ...p, budget_guard_enabled: e.target.checked })),
|
|
1406
1645
|
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
1407
1646
|
}),
|
|
1408
|
-
'Cost Budget Guardrails & Auto-Fallback'
|
|
1647
|
+
t('aggregator.budget_title') || 'Cost Budget Guardrails & Auto-Fallback'
|
|
1409
1648
|
),
|
|
1410
1649
|
React.createElement(
|
|
1411
1650
|
'div',
|
|
1412
1651
|
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginLeft: 22 } },
|
|
1413
|
-
'Cap maximum run cost; auto-trim candidate pool or abort to prevent accidental token spend.'
|
|
1652
|
+
t('aggregator.budget_desc') || 'Cap maximum run cost; auto-trim candidate pool or abort to prevent accidental token spend.'
|
|
1414
1653
|
),
|
|
1415
1654
|
currentPreset.budget_guard_enabled && React.createElement(
|
|
1416
1655
|
'div',
|
|
@@ -1418,7 +1657,7 @@ window.__ModuleLoader__.load({
|
|
|
1418
1657
|
React.createElement(
|
|
1419
1658
|
'div',
|
|
1420
1659
|
{ style: { display: 'flex', alignItems: 'center', gap: 6 } },
|
|
1421
|
-
React.createElement('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, 'Max USD ($):'),
|
|
1660
|
+
React.createElement('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, t('aggregator.budget_max_usd') || 'Max USD ($):'),
|
|
1422
1661
|
React.createElement('input', {
|
|
1423
1662
|
type: 'number',
|
|
1424
1663
|
step: '0.01',
|
|
@@ -1434,7 +1673,7 @@ window.__ModuleLoader__.load({
|
|
|
1434
1673
|
React.createElement(
|
|
1435
1674
|
'div',
|
|
1436
1675
|
{ style: { display: 'flex', alignItems: 'center', gap: 6 } },
|
|
1437
|
-
React.createElement('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, 'Action:'),
|
|
1676
|
+
React.createElement('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, t('aggregator.budget_action') || 'Action:'),
|
|
1438
1677
|
React.createElement(
|
|
1439
1678
|
'select',
|
|
1440
1679
|
{
|
|
@@ -1443,8 +1682,8 @@ window.__ModuleLoader__.load({
|
|
|
1443
1682
|
value: currentPreset.budget_action || 'trim',
|
|
1444
1683
|
onChange: (e) => updateCurrentPreset((p) => ({ ...p, budget_action: e.target.value })),
|
|
1445
1684
|
},
|
|
1446
|
-
React.createElement('option', { value: 'trim' }, 'Trim Pool'),
|
|
1447
|
-
React.createElement('option', { value: 'abort' }, 'Abort')
|
|
1685
|
+
React.createElement('option', { value: 'trim' }, t('aggregator.budget_action_trim') || 'Trim Pool'),
|
|
1686
|
+
React.createElement('option', { value: 'abort' }, t('aggregator.budget_action_abort') || 'Abort')
|
|
1448
1687
|
)
|
|
1449
1688
|
)
|
|
1450
1689
|
)
|
|
@@ -1463,16 +1702,16 @@ window.__ModuleLoader__.load({
|
|
|
1463
1702
|
onChange: (e) => updateCurrentPreset((p) => ({ ...p, report_generation_enabled: e.target.checked })),
|
|
1464
1703
|
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
1465
1704
|
}),
|
|
1466
|
-
'Automated Benchmark & Post-Mortem Report'
|
|
1705
|
+
t('aggregator.report_title') || 'Automated Benchmark & Post-Mortem Report'
|
|
1467
1706
|
),
|
|
1468
1707
|
React.createElement(
|
|
1469
1708
|
'div',
|
|
1470
1709
|
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginLeft: 22 } },
|
|
1471
|
-
'Generate downloadable Markdown & JSON reports detailing candidate latency, agreement, test gate, and cost breakdown.'
|
|
1710
|
+
t('aggregator.report_desc') || 'Generate downloadable Markdown & JSON reports detailing candidate latency, agreement, test gate, and cost breakdown.'
|
|
1472
1711
|
)
|
|
1473
1712
|
),
|
|
1474
1713
|
|
|
1475
|
-
/* Graceful Degradation & Local Fallback */
|
|
1714
|
+
/* Graceful Degradation & Local Fallback (#175, #176) */
|
|
1476
1715
|
React.createElement(
|
|
1477
1716
|
'div',
|
|
1478
1717
|
{ style: { display: 'flex', flexDirection: 'column', gap: 6, padding: '8px 12px', background: 'var(--dsw-alias-bg-layer-2)', borderRadius: 8, border: '1px solid var(--dsw-alias-border-l2)', marginTop: 8 } },
|
|
@@ -1485,86 +1724,154 @@ window.__ModuleLoader__.load({
|
|
|
1485
1724
|
onChange: (e) => updateCurrentPreset((p) => ({ ...p, local_fallback_enabled: e.target.checked })),
|
|
1486
1725
|
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
1487
1726
|
}),
|
|
1488
|
-
'Graceful Degradation & Local Fallback'
|
|
1727
|
+
t('aggregator.local_fallback_title') || 'Graceful Degradation & Local Fallback'
|
|
1489
1728
|
),
|
|
1490
1729
|
React.createElement(
|
|
1491
1730
|
'div',
|
|
1492
1731
|
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginLeft: 22 } },
|
|
1493
|
-
'Automatically fall back to local Ollama / MiniPC models when online candidate APIs fail.'
|
|
1732
|
+
t('aggregator.local_fallback_desc') || 'Automatically fall back to local Ollama / MiniPC models when online candidate APIs fail.'
|
|
1733
|
+
),
|
|
1734
|
+
currentPreset.local_fallback_enabled && React.createElement(
|
|
1735
|
+
'div',
|
|
1736
|
+
{ style: { marginLeft: 22, marginTop: 4 } },
|
|
1737
|
+
renderSlotEditorList({
|
|
1738
|
+
items: currentPreset.local_fallback_models,
|
|
1739
|
+
badgePrefix: 'Local',
|
|
1740
|
+
onUpdate: (idx, prov, mod) => {
|
|
1741
|
+
updateCurrentPreset((p) => {
|
|
1742
|
+
const list = [...(p.local_fallback_models || [])]
|
|
1743
|
+
list[idx] = { provider: prov, model: mod }
|
|
1744
|
+
return { ...p, local_fallback_models: list }
|
|
1745
|
+
})
|
|
1746
|
+
},
|
|
1747
|
+
onRemove: (idx) => {
|
|
1748
|
+
updateCurrentPreset((p) => ({
|
|
1749
|
+
...p,
|
|
1750
|
+
local_fallback_models: (p.local_fallback_models || []).filter((_, i) => i !== idx),
|
|
1751
|
+
}))
|
|
1752
|
+
},
|
|
1753
|
+
onAdd: () => {
|
|
1754
|
+
updateCurrentPreset((p) => ({
|
|
1755
|
+
...p,
|
|
1756
|
+
local_fallback_models: [...(p.local_fallback_models || []), { provider: 'ollama', model: '' }],
|
|
1757
|
+
}))
|
|
1758
|
+
},
|
|
1759
|
+
addLabel: t('aggregator.add_local_btn') || '+ Add Local Model',
|
|
1760
|
+
emptyWarning: (!currentPreset.local_fallback_models || currentPreset.local_fallback_models.length === 0)
|
|
1761
|
+
? (t('aggregator.local_models_empty') || 'Configure at least one local fallback model (e.g. Ollama).')
|
|
1762
|
+
: null,
|
|
1763
|
+
availableModels,
|
|
1764
|
+
t,
|
|
1765
|
+
})
|
|
1494
1766
|
)
|
|
1495
1767
|
),
|
|
1496
1768
|
|
|
1497
|
-
/*
|
|
1769
|
+
/* Smart Prompt Routing (#176) */
|
|
1498
1770
|
React.createElement(
|
|
1499
1771
|
'div',
|
|
1500
|
-
{ style: {
|
|
1772
|
+
{ style: { display: 'flex', flexDirection: 'column', gap: 6, padding: '8px 12px', background: 'var(--dsw-alias-bg-layer-2)', borderRadius: 8, border: '1px solid var(--dsw-alias-border-l2)', marginTop: 8 } },
|
|
1501
1773
|
React.createElement(
|
|
1502
|
-
'
|
|
1503
|
-
{ style: {
|
|
1504
|
-
|
|
1774
|
+
'label',
|
|
1775
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: 13, fontWeight: 500 } },
|
|
1776
|
+
React.createElement('input', {
|
|
1777
|
+
type: 'checkbox',
|
|
1778
|
+
checked: Boolean(currentPreset.smart_routing_enabled),
|
|
1779
|
+
onChange: (e) => updateCurrentPreset((p) => ({ ...p, smart_routing_enabled: e.target.checked })),
|
|
1780
|
+
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
1781
|
+
}),
|
|
1782
|
+
t('config.smart_routing_title') || 'Smart Prompt Routing'
|
|
1505
1783
|
),
|
|
1506
1784
|
React.createElement(
|
|
1507
1785
|
'div',
|
|
1508
|
-
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)',
|
|
1509
|
-
t('
|
|
1510
|
-
),
|
|
1511
|
-
(Array.isArray(currentPreset.aggregator_fallbacks) ? currentPreset.aggregator_fallbacks : []).map((fb, fbIdx) =>
|
|
1512
|
-
React.createElement(
|
|
1513
|
-
'div',
|
|
1514
|
-
{ key: fbIdx, style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
1515
|
-
React.createElement('span', { className: 'moa-cand-badge' }, `Fallback #${fbIdx + 1}`),
|
|
1516
|
-
React.createElement(
|
|
1517
|
-
'div',
|
|
1518
|
-
{ style: { flex: 1 } },
|
|
1519
|
-
React.createElement(SearchableModelPicker, {
|
|
1520
|
-
provider: (fb && fb.provider) || '',
|
|
1521
|
-
model: (fb && fb.model) || '',
|
|
1522
|
-
onChange: (prov, mod) => {
|
|
1523
|
-
updateCurrentPreset((p) => {
|
|
1524
|
-
const list = [...(p.aggregator_fallbacks || [])]
|
|
1525
|
-
list[fbIdx] = { provider: prov, model: mod }
|
|
1526
|
-
return { ...p, aggregator_fallbacks: list }
|
|
1527
|
-
})
|
|
1528
|
-
},
|
|
1529
|
-
availableModels,
|
|
1530
|
-
t,
|
|
1531
|
-
})
|
|
1532
|
-
),
|
|
1533
|
-
React.createElement(
|
|
1534
|
-
'button',
|
|
1535
|
-
{
|
|
1536
|
-
type: 'button',
|
|
1537
|
-
className: 'moa-btn moa-btn-danger moa-btn-sub',
|
|
1538
|
-
onClick: () => {
|
|
1539
|
-
updateCurrentPreset((p) => ({
|
|
1540
|
-
...p,
|
|
1541
|
-
aggregator_fallbacks: (p.aggregator_fallbacks || []).filter((_, i) => i !== fbIdx),
|
|
1542
|
-
}))
|
|
1543
|
-
},
|
|
1544
|
-
},
|
|
1545
|
-
'✕'
|
|
1546
|
-
)
|
|
1547
|
-
)
|
|
1786
|
+
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginLeft: 22 } },
|
|
1787
|
+
t('config.smart_routing_desc') || 'Analyze task complexity and automatically select the optimal preset.'
|
|
1548
1788
|
),
|
|
1549
|
-
React.createElement(
|
|
1789
|
+
currentPreset.smart_routing_enabled && React.createElement(
|
|
1550
1790
|
'div',
|
|
1551
|
-
|
|
1791
|
+
{ style: { marginLeft: 22, marginTop: 4, display: 'flex', alignItems: 'center', gap: 8 } },
|
|
1792
|
+
React.createElement('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, t('config.smart_routing_model') || 'Routing Model:'),
|
|
1552
1793
|
React.createElement(
|
|
1553
|
-
'
|
|
1554
|
-
{
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1794
|
+
'div',
|
|
1795
|
+
{ style: { flex: 1, maxWidth: 320 } },
|
|
1796
|
+
React.createElement(SearchableModelPicker, {
|
|
1797
|
+
provider: currentPreset.smart_routing_model?.provider || '',
|
|
1798
|
+
model: currentPreset.smart_routing_model?.model || '',
|
|
1799
|
+
onChange: (prov, mod) => {
|
|
1559
1800
|
updateCurrentPreset((p) => ({
|
|
1560
1801
|
...p,
|
|
1561
|
-
|
|
1802
|
+
smart_routing_model: { provider: prov, model: mod },
|
|
1562
1803
|
}))
|
|
1563
1804
|
},
|
|
1564
|
-
|
|
1565
|
-
|
|
1805
|
+
availableModels,
|
|
1806
|
+
t,
|
|
1807
|
+
})
|
|
1566
1808
|
)
|
|
1567
1809
|
)
|
|
1810
|
+
),
|
|
1811
|
+
|
|
1812
|
+
/* Multi-Turn Context & Diff Pruning (#176) */
|
|
1813
|
+
React.createElement(
|
|
1814
|
+
'div',
|
|
1815
|
+
{ style: { display: 'flex', flexDirection: 'column', gap: 6, padding: '8px 12px', background: 'var(--dsw-alias-bg-layer-2)', borderRadius: 8, border: '1px solid var(--dsw-alias-border-l2)', marginTop: 8 } },
|
|
1816
|
+
React.createElement(
|
|
1817
|
+
'label',
|
|
1818
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: 13, fontWeight: 500 } },
|
|
1819
|
+
React.createElement('input', {
|
|
1820
|
+
type: 'checkbox',
|
|
1821
|
+
checked: currentPreset.multi_turn_enabled !== false,
|
|
1822
|
+
onChange: (e) => updateCurrentPreset((p) => ({ ...p, multi_turn_enabled: e.target.checked })),
|
|
1823
|
+
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
1824
|
+
}),
|
|
1825
|
+
t('aggregator.multi_turn_title') || 'Multi-Turn Context & Diff Pruning'
|
|
1826
|
+
),
|
|
1827
|
+
React.createElement(
|
|
1828
|
+
'div',
|
|
1829
|
+
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginLeft: 22 } },
|
|
1830
|
+
t('aggregator.multi_turn_desc') || 'Retain conversation context across turns while pruning duplicate code blocks.'
|
|
1831
|
+
)
|
|
1832
|
+
),
|
|
1833
|
+
|
|
1834
|
+
/* Fallback Judges Chain */
|
|
1835
|
+
React.createElement(
|
|
1836
|
+
'div',
|
|
1837
|
+
{ style: { marginTop: 10, display: 'flex', flexDirection: 'column', gap: 6 } },
|
|
1838
|
+
React.createElement(
|
|
1839
|
+
'div',
|
|
1840
|
+
{ style: { fontSize: 13, fontWeight: 600, color: 'var(--dsw-alias-label-primary)' } },
|
|
1841
|
+
t('aggregator.fallbacks_title')
|
|
1842
|
+
),
|
|
1843
|
+
React.createElement(
|
|
1844
|
+
'div',
|
|
1845
|
+
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginTop: -4 } },
|
|
1846
|
+
t('aggregator.fallbacks_desc')
|
|
1847
|
+
),
|
|
1848
|
+
renderSlotEditorList({
|
|
1849
|
+
items: currentPreset.aggregator_fallbacks,
|
|
1850
|
+
badgePrefix: 'Fallback',
|
|
1851
|
+
onUpdate: (idx, prov, mod) => {
|
|
1852
|
+
updateCurrentPreset((p) => {
|
|
1853
|
+
const list = [...(p.aggregator_fallbacks || [])]
|
|
1854
|
+
list[idx] = { provider: prov, model: mod }
|
|
1855
|
+
return { ...p, aggregator_fallbacks: list }
|
|
1856
|
+
})
|
|
1857
|
+
},
|
|
1858
|
+
onRemove: (idx) => {
|
|
1859
|
+
updateCurrentPreset((p) => ({
|
|
1860
|
+
...p,
|
|
1861
|
+
aggregator_fallbacks: (p.aggregator_fallbacks || []).filter((_, i) => i !== idx),
|
|
1862
|
+
}))
|
|
1863
|
+
},
|
|
1864
|
+
onAdd: () => {
|
|
1865
|
+
updateCurrentPreset((p) => ({
|
|
1866
|
+
...p,
|
|
1867
|
+
aggregator_fallbacks: [...(p.aggregator_fallbacks || []), { provider: (availableModels[0] && availableModels[0].provider) || '', model: (availableModels[0] && availableModels[0].model) || '' }],
|
|
1868
|
+
}))
|
|
1869
|
+
},
|
|
1870
|
+
addLabel: t('aggregator.add_fallback_btn'),
|
|
1871
|
+
emptyWarning: null,
|
|
1872
|
+
availableModels,
|
|
1873
|
+
t,
|
|
1874
|
+
})
|
|
1568
1875
|
)
|
|
1569
1876
|
)
|
|
1570
1877
|
)
|
|
@@ -1659,7 +1966,7 @@ window.__ModuleLoader__.load({
|
|
|
1659
1966
|
onChange: (e) => updateCurrentPreset((p) => ({ ...p, temperature_gradient_enabled: e.target.checked })),
|
|
1660
1967
|
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
1661
1968
|
}),
|
|
1662
|
-
'Temperature Gradient Exploration (0.2 → 0.9 across candidates)'
|
|
1969
|
+
t('proposers.temperature_gradient') || 'Temperature Gradient Exploration (0.2 → 0.9 across candidates)'
|
|
1663
1970
|
)
|
|
1664
1971
|
),
|
|
1665
1972
|
React.createElement(
|
|
@@ -1687,7 +1994,7 @@ window.__ModuleLoader__.load({
|
|
|
1687
1994
|
min: '0',
|
|
1688
1995
|
max: '2',
|
|
1689
1996
|
placeholder: 'Temp',
|
|
1690
|
-
title: 'Candidate Temperature (leave empty for default/gradient)',
|
|
1997
|
+
title: t('proposers.candidate_temp_tooltip') || 'Candidate Temperature (leave empty for default/gradient)',
|
|
1691
1998
|
className: 'moa-input',
|
|
1692
1999
|
style: { width: 50, height: 32, fontSize: 12, padding: '0 4px', textAlign: 'center' },
|
|
1693
2000
|
value: (typeof ref.temperature === 'number' && ref.temperature >= 0) ? ref.temperature : '',
|
|
@@ -2021,6 +2328,9 @@ window.__ModuleLoader__.load({
|
|
|
2021
2328
|
)
|
|
2022
2329
|
}
|
|
2023
2330
|
|
|
2331
|
+
const UNAVAILABLE_SNAPSHOT = Object.freeze({ status: 'unavailable' })
|
|
2332
|
+
const LOADING_SNAPSHOT = Object.freeze({ status: 'loading' })
|
|
2333
|
+
|
|
2024
2334
|
function useMoASettings(props) {
|
|
2025
2335
|
const effectiveCtx = (props && props.ctx) || rootCtx
|
|
2026
2336
|
// English is canonical; prefer the core-provided translator for the
|
|
@@ -2033,12 +2343,12 @@ window.__ModuleLoader__.load({
|
|
|
2033
2343
|
return s && typeof s.get === 'function' ? s.get(NS) : undefined
|
|
2034
2344
|
}, [effectiveCtx])
|
|
2035
2345
|
|
|
2346
|
+
const getSnapshot = React.useCallback(() => (scope ? scope.getSnapshot() : UNAVAILABLE_SNAPSHOT), [scope])
|
|
2347
|
+
const getServerSnapshot = React.useCallback(() => LOADING_SNAPSHOT, [])
|
|
2348
|
+
const subscribe = React.useCallback((cb) => (scope ? scope.subscribe(cb) : () => {}), [scope])
|
|
2349
|
+
|
|
2036
2350
|
const snapshot = React.useSyncExternalStore
|
|
2037
|
-
? React.useSyncExternalStore(
|
|
2038
|
-
React.useMemo(() => (cb) => (scope ? scope.subscribe(cb) : () => {}), [scope]),
|
|
2039
|
-
React.useCallback(() => (scope ? scope.getSnapshot() : { status: 'unavailable' }), [scope]),
|
|
2040
|
-
React.useCallback(() => ({ status: 'loading' }), [])
|
|
2041
|
-
)
|
|
2351
|
+
? React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
|
|
2042
2352
|
: null
|
|
2043
2353
|
|
|
2044
2354
|
const [status, setStatus] = React.useState('loading')
|
|
@@ -2052,6 +2362,7 @@ window.__ModuleLoader__.load({
|
|
|
2052
2362
|
currentVersion: '0.2.16',
|
|
2053
2363
|
latestVersion: undefined,
|
|
2054
2364
|
updateAvailable: false,
|
|
2365
|
+
canAutoUpdate: true,
|
|
2055
2366
|
notice: null,
|
|
2056
2367
|
error: null,
|
|
2057
2368
|
})
|
|
@@ -2068,10 +2379,11 @@ window.__ModuleLoader__.load({
|
|
|
2068
2379
|
currentVersion: data.currentVersion,
|
|
2069
2380
|
latestVersion: data.latestVersion,
|
|
2070
2381
|
updateAvailable: Boolean(data.updateAvailable),
|
|
2382
|
+
canAutoUpdate: data.canAutoUpdate !== undefined ? Boolean(data.canAutoUpdate) : prev.canAutoUpdate,
|
|
2071
2383
|
error: data.latestCheckFailed ? (t('updater.checkFailed') || 'Registry check failed') : null,
|
|
2072
2384
|
}))
|
|
2073
2385
|
} else {
|
|
2074
|
-
setUpdateState((prev) => ({ ...prev, checking: false, error: data
|
|
2386
|
+
setUpdateState((prev) => ({ ...prev, checking: false, error: data?.error || 'Check failed' }))
|
|
2075
2387
|
}
|
|
2076
2388
|
} catch (err) {
|
|
2077
2389
|
setUpdateState((prev) => ({ ...prev, checking: false, error: String(err?.message || err) }))
|
|
@@ -2086,19 +2398,20 @@ window.__ModuleLoader__.load({
|
|
|
2086
2398
|
headers: { 'Content-Type': 'application/json', 'x-dsh-plugin-update': '1' },
|
|
2087
2399
|
})
|
|
2088
2400
|
const data = await res.json()
|
|
2089
|
-
if (data && data.updated) {
|
|
2401
|
+
if (data && (data.updated || data.updatedVersion || data.restartRequired || data.ok)) {
|
|
2402
|
+
const nextVer = data.updatedVersion || data.latestVersion || data.currentVersion
|
|
2090
2403
|
setUpdateState((prev) => ({
|
|
2091
2404
|
...prev,
|
|
2092
2405
|
updating: false,
|
|
2093
2406
|
updateAvailable: false,
|
|
2094
|
-
currentVersion:
|
|
2095
|
-
notice: (t('updater.success') || 'Successfully updated to v{version}. Restart DSH to apply.').replace('{version}',
|
|
2407
|
+
currentVersion: nextVer,
|
|
2408
|
+
notice: (t('updater.success') || 'Successfully updated to v{version}. Restart DSH to apply.').replace('{version}', nextVer),
|
|
2096
2409
|
}))
|
|
2097
2410
|
} else {
|
|
2098
2411
|
setUpdateState((prev) => ({
|
|
2099
2412
|
...prev,
|
|
2100
2413
|
updating: false,
|
|
2101
|
-
error: data
|
|
2414
|
+
error: data?.error || data?.message || 'Update failed',
|
|
2102
2415
|
}))
|
|
2103
2416
|
}
|
|
2104
2417
|
} catch (err) {
|
|
@@ -2258,7 +2571,7 @@ window.__ModuleLoader__.load({
|
|
|
2258
2571
|
if (res.defaultPreset) setDefaultPreset(res.defaultPreset)
|
|
2259
2572
|
setTimeout(() => setSaveStatus(''), 3000)
|
|
2260
2573
|
} else {
|
|
2261
|
-
setSaveStatus('Error saving: ' + (res?.error || 'failed'))
|
|
2574
|
+
setSaveStatus((t('status.error_saving') || 'Error saving: ') + (res?.error || 'failed'))
|
|
2262
2575
|
}
|
|
2263
2576
|
})
|
|
2264
2577
|
.catch((err) => {
|
|
@@ -2303,13 +2616,14 @@ window.__ModuleLoader__.load({
|
|
|
2303
2616
|
}
|
|
2304
2617
|
render() {
|
|
2305
2618
|
if (this.state.hasError) {
|
|
2619
|
+
const t = typeof this.props?.t === 'function' ? this.props.t : (typeof makeT === 'function' ? makeT(en, null) : ((k) => k))
|
|
2306
2620
|
return React.createElement(
|
|
2307
2621
|
'div',
|
|
2308
2622
|
{
|
|
2309
2623
|
className: 'moa-alert-err',
|
|
2310
2624
|
style: { margin: '12px 0', padding: '14px', borderRadius: '8px' },
|
|
2311
2625
|
},
|
|
2312
|
-
React.createElement('div', { style: { fontWeight: 600, marginBottom: '6px' } }, '⚠️ MoA UI Error:'),
|
|
2626
|
+
React.createElement('div', { style: { fontWeight: 600, marginBottom: '6px' } }, t('error_boundary.title') || '⚠️ MoA UI Error:'),
|
|
2313
2627
|
React.createElement('div', { style: { fontSize: '12px', wordBreak: 'break-all' } }, String(this.state.error?.message || this.state.error)),
|
|
2314
2628
|
React.createElement(
|
|
2315
2629
|
'button',
|
|
@@ -2319,7 +2633,7 @@ window.__ModuleLoader__.load({
|
|
|
2319
2633
|
style: { marginTop: '10px', fontSize: '12px', padding: '4px 10px' },
|
|
2320
2634
|
onClick: () => this.setState({ hasError: false, error: null }),
|
|
2321
2635
|
},
|
|
2322
|
-
'Retry'
|
|
2636
|
+
t('error_boundary.retry') || 'Retry'
|
|
2323
2637
|
)
|
|
2324
2638
|
)
|
|
2325
2639
|
}
|
|
@@ -2366,12 +2680,12 @@ window.__ModuleLoader__.load({
|
|
|
2366
2680
|
'div',
|
|
2367
2681
|
{ className: 'moa-body' },
|
|
2368
2682
|
state.status === 'loading'
|
|
2369
|
-
? React.createElement('div', { style: { padding: '16px 0', color: 'var(--dsw-alias-label-secondary)', fontSize: 13 } }, 'Loading MoA settings…')
|
|
2683
|
+
? React.createElement('div', { style: { padding: '16px 0', color: 'var(--dsw-alias-label-secondary)', fontSize: 13 } }, state.t('status.loading') || 'Loading MoA settings…')
|
|
2370
2684
|
: state.status === 'unavailable'
|
|
2371
2685
|
? React.createElement(
|
|
2372
2686
|
'div',
|
|
2373
2687
|
{ style: { padding: '16px 0', display: 'flex', flexDirection: 'column', gap: 10 } },
|
|
2374
|
-
React.createElement('div', { className: 'moa-alert-err' }, 'MoA service temporarily unavailable'),
|
|
2688
|
+
React.createElement('div', { className: 'moa-alert-err' }, state.t('status.unavailable') || 'MoA service temporarily unavailable'),
|
|
2375
2689
|
React.createElement(
|
|
2376
2690
|
'button',
|
|
2377
2691
|
{
|
|
@@ -2380,7 +2694,7 @@ window.__ModuleLoader__.load({
|
|
|
2380
2694
|
style: { width: 'fit-content' },
|
|
2381
2695
|
onClick: state.reload,
|
|
2382
2696
|
},
|
|
2383
|
-
state.t('actions.retry')
|
|
2697
|
+
state.t('status.retry') || state.t('actions.retry')
|
|
2384
2698
|
)
|
|
2385
2699
|
)
|
|
2386
2700
|
: React.createElement(MoAEditor, state)
|
|
@@ -2429,7 +2743,7 @@ window.__ModuleLoader__.load({
|
|
|
2429
2743
|
locale: NS,
|
|
2430
2744
|
inject: () => ({ ctx }),
|
|
2431
2745
|
},
|
|
2432
|
-
(props) => React.createElement(MoAErrorBoundary,
|
|
2746
|
+
(props) => React.createElement(MoAErrorBoundary, props, React.createElement(MoACard, props))
|
|
2433
2747
|
)
|
|
2434
2748
|
} catch (err) {
|
|
2435
2749
|
console.warn('[dsh-moa] Failed to register ' + slotName + ':', err)
|
|
@@ -2452,7 +2766,7 @@ window.__ModuleLoader__.load({
|
|
|
2452
2766
|
locale: NS,
|
|
2453
2767
|
inject: () => ({ ctx }),
|
|
2454
2768
|
},
|
|
2455
|
-
(props) => React.createElement(MoAErrorBoundary,
|
|
2769
|
+
(props) => React.createElement(MoAErrorBoundary, props, React.createElement(MoACard, props))
|
|
2456
2770
|
)
|
|
2457
2771
|
} catch (err) {
|
|
2458
2772
|
console.warn('[dsh-moa] Failed to register plugins.item:', err)
|