@goodandready/dsh-moa 0.2.8 → 0.2.10
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 +64 -37
- package/docs/README.ru.md +64 -37
- package/docs/README.zh.md +70 -27
- package/docs/design/DESIGN.md +43 -41
- package/docs/plans/47-client-js-split-plan.md +57 -0
- package/lib/client.js +666 -465
- package/lib/file-workspace.js +3 -1
- package/lib/history.js +4 -1
- package/lib/index.js +66 -12
- package/lib/live-canvas.js +34 -0
- package/lib/moa-runner.js +665 -351
- package/lib/pricing.js +17 -3
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1,135 +1,249 @@
|
|
|
1
|
+
// Plugin Settings Card (settings.plugin.item).
|
|
2
|
+
//
|
|
3
|
+
// Aligned with dsh-clinebot design language:
|
|
4
|
+
// - Header with real status chips (host online/offline/disabled via /dsh-moa/status)
|
|
5
|
+
// - Clean section cards (.moa-section-card) using native DSH design tokens (--dsw-alias-*)
|
|
6
|
+
// - Segmented preset selectors and count pills
|
|
7
|
+
// - Refined temperature sliders and searchable model picker
|
|
8
|
+
// - Telemetry summary grid fed by /dsh-moa/history
|
|
9
|
+
// - Robust error boundary and settingsScope reactivity
|
|
10
|
+
//
|
|
11
|
+
// English is the canonical source language; the ru translation is provided
|
|
12
|
+
// by the DSH translation plugin at runtime (no bundled ru duplicate).
|
|
13
|
+
|
|
1
14
|
window.__ModuleLoader__.load({
|
|
2
15
|
id: '@goodandready/dsh-moa',
|
|
3
16
|
factory: (require) => {
|
|
4
17
|
var module = { exports: {} }
|
|
5
18
|
var exports = module.exports
|
|
6
|
-
|
|
7
|
-
|
|
19
|
+
const React = require('react')
|
|
8
20
|
const NS = 'dsh-moa'
|
|
9
|
-
|
|
21
|
+
const TITLE = 'Mixture of Agents (MoA)'
|
|
22
|
+
const SUBTITLE = 'Ensemble parallel candidate generation with judge synthesis via /moa.'
|
|
10
23
|
|
|
11
|
-
let
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
let rootCtx = null
|
|
25
|
+
|
|
26
|
+
const en = {
|
|
27
|
+
title: 'Mixture of Agents (MoA)',
|
|
28
|
+
description: 'Ensemble model orchestration: parallel candidate proposals + judge synthesis via /moa <prompt>.',
|
|
29
|
+
'header.title': 'Mixture of Agents (MoA) Orchestration',
|
|
30
|
+
'header.sub': 'Parallel generation by candidate proposers with final evaluation and code promotion by a leading aggregator/judge. Returns to your session model automatically.',
|
|
31
|
+
'badge.online': 'MoA Active',
|
|
32
|
+
'badge.offline': 'Host Unreachable',
|
|
33
|
+
'badge.preset': 'Preset: {name}',
|
|
34
|
+
'badge.models': '{count} models available',
|
|
35
|
+
'badge.candidates': '{count} proposers',
|
|
36
|
+
'presets.title': '📋 Preset Management',
|
|
37
|
+
'presets.desc': 'Select active preset or create custom configurations for specific tasks (e.g. code review, math, quick triage).',
|
|
38
|
+
'presets.active_badge': 'Active Default',
|
|
39
|
+
'presets.set_default': 'Set as Default',
|
|
40
|
+
'presets.new_btn': '+ New Preset',
|
|
41
|
+
'presets.del_btn': 'Delete',
|
|
42
|
+
'presets.del_confirm': 'Are you sure you want to delete preset "{name}"?',
|
|
43
|
+
'presets.create': 'Create',
|
|
44
|
+
'presets.cancel': 'Cancel',
|
|
45
|
+
'presets.name_placeholder': 'New preset name (e.g. fast-audit)',
|
|
46
|
+
'aggregator.title': '⚖️ Leading Model / Judge (Synthesis & Audit)',
|
|
47
|
+
'aggregator.desc': 'Compares candidate proposals, resolves discrepancies, filters hallucination, and synthesizes final solution.',
|
|
48
|
+
'aggregator.temp_label': 'Judge Temperature',
|
|
49
|
+
'aggregator.temp_hint': 'Recommended: 0.2 – 0.4 for strict validation and deterministic synthesis.',
|
|
50
|
+
'aggregator.criteria_label': 'Judge Evaluation Criteria (Optional):',
|
|
51
|
+
'aggregator.criteria_placeholder': 'e.g. Priority: performance, zero external dependencies, robust edge-case handling...',
|
|
52
|
+
'aggregator.questions_label': 'Ask clarifying questions on broad tasks (Questionnaire)',
|
|
53
|
+
'aggregator.questions_hint': 'When enabled: for short/vague prompts, the judge synthesizes a 2-4 question checklist before code generation.',
|
|
54
|
+
'aggregator.curator_label': 'Curator Synthesis & Master Assembler Advice',
|
|
55
|
+
'aggregator.curator_hint': 'When enabled: the curator highlights the finest components of each candidate, applies the antipatterns rubric, and advises which agent model should assemble the solution.',
|
|
56
|
+
'aggregator.stream_label': 'Live Stream Curator/Judge Thinking',
|
|
57
|
+
'aggregator.stream_hint': 'Streams aggregator tokens directly to chat in real time for zero-latency initial response.',
|
|
58
|
+
'aggregator.fallbacks_title': 'Fallback Judges Chain',
|
|
59
|
+
'aggregator.fallbacks_desc': 'Sequential backup models invoked automatically if the primary judge experiences rate limits or outages.',
|
|
60
|
+
'aggregator.add_fallback_btn': '+ Add Fallback Judge',
|
|
61
|
+
'proposers.quorum_label': 'Quorum Fast-Forward (Straggler Mitigation)',
|
|
62
|
+
'proposers.quorum_hint': 'When 67%+ candidates finish, start a grace timer (default: 10s) and proceed without waiting for hanging providers.',
|
|
63
|
+
'proposers.grace_label': 'Grace Period (sec):',
|
|
64
|
+
'proposers.title': '👥 Candidate Models (Proposers)',
|
|
65
|
+
'proposers.desc': 'Generate independent solutions in parallel to maximize diversity and solution space coverage.',
|
|
66
|
+
'proposers.parallel_count': 'Parallel Proposers',
|
|
67
|
+
'proposers.candidate': 'Candidate',
|
|
68
|
+
'proposers.add_btn': '+ Add Candidate Model',
|
|
69
|
+
'proposers.temp_label': 'Candidates Temperature',
|
|
70
|
+
'proposers.temp_hint': 'Recommended: 0.6 – 0.8 for creative, independent solution exploration.',
|
|
71
|
+
'stats.title': '📈 MoA Analytics & Telemetry',
|
|
72
|
+
'stats.desc': 'Session telemetry, historical runs, and model win-rate tracking.',
|
|
73
|
+
'stats.total_runs': 'Total Runs',
|
|
74
|
+
'stats.avg_cost': 'Avg Run Cost',
|
|
75
|
+
'stats.active_models': 'Configured Models',
|
|
76
|
+
'stats.preset_count': 'Total Presets',
|
|
77
|
+
'picker.select': 'Select model…',
|
|
78
|
+
'picker.search_placeholder': 'Search model by name or provider…',
|
|
79
|
+
'picker.found': 'Found',
|
|
80
|
+
'picker.of': 'of',
|
|
81
|
+
'picker.clear': 'Clear',
|
|
82
|
+
'picker.none': 'No matching models found.',
|
|
83
|
+
'picker.custom': 'Use custom: ',
|
|
84
|
+
'actions.save': 'Save Configuration',
|
|
85
|
+
'actions.saving': 'Saving…',
|
|
86
|
+
'actions.saved': 'Configuration saved successfully!',
|
|
87
|
+
'actions.retry': 'Reload Configuration',
|
|
88
|
+
'badge.disabled': 'MoA Disabled',
|
|
89
|
+
'config.enabled_label': 'Enable MoA (slash command and turn routing)',
|
|
90
|
+
'slash.desc': 'Run turn with Mixture of Agents ensemble synthesis (/moa [preset] <prompt>)',
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function makeT(dict, fallback) {
|
|
94
|
+
return function t(key, vars) {
|
|
95
|
+
let val = (dict && dict[key]) || (fallback && fallback[key]) || key
|
|
96
|
+
if (vars && typeof val === 'string') {
|
|
97
|
+
for (const k of Object.keys(vars)) {
|
|
98
|
+
val = val.replace(new RegExp('\\{' + k + '\\}', 'g'), String(vars[k]))
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return val
|
|
102
|
+
}
|
|
17
103
|
}
|
|
18
104
|
|
|
19
|
-
function FallbackChevron({ className }) {
|
|
105
|
+
function FallbackChevron({ className, style }) {
|
|
20
106
|
return React.createElement(
|
|
21
107
|
'svg',
|
|
22
108
|
{
|
|
23
|
-
className,
|
|
24
109
|
width: 14,
|
|
25
110
|
height: 14,
|
|
26
111
|
viewBox: '0 0 14 14',
|
|
27
112
|
fill: 'none',
|
|
113
|
+
'aria-hidden': true,
|
|
114
|
+
className,
|
|
115
|
+
style,
|
|
116
|
+
},
|
|
117
|
+
React.createElement('path', {
|
|
118
|
+
d: 'M3.5 5.25L7 8.75L10.5 5.25',
|
|
28
119
|
stroke: 'currentColor',
|
|
29
120
|
strokeWidth: 1.5,
|
|
30
121
|
strokeLinecap: 'round',
|
|
31
122
|
strokeLinejoin: 'round',
|
|
32
|
-
}
|
|
33
|
-
React.createElement('path', { d: 'M3.5 5.25L7 8.75L10.5 5.25' })
|
|
123
|
+
})
|
|
34
124
|
)
|
|
35
125
|
}
|
|
36
|
-
|
|
37
|
-
|
|
126
|
+
let Chevron = FallbackChevron
|
|
127
|
+
try {
|
|
128
|
+
const primitives = require('@deepseek-ai/dsh-client-ui-primitives')
|
|
129
|
+
if (primitives && typeof primitives.IconChevronDownOutline14 === 'function') {
|
|
130
|
+
Chevron = primitives.IconChevronDownOutline14
|
|
131
|
+
}
|
|
132
|
+
} catch (noPrimitives) {
|
|
133
|
+
Chevron = FallbackChevron
|
|
134
|
+
}
|
|
38
135
|
|
|
39
136
|
const STYLES = `
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
137
|
+
/* dsh-moa modern clinebot-aligned styling */
|
|
138
|
+
.moa-page{display:flex;flex-direction:column;gap:18px;padding:4px 0 24px;max-width:960px}
|
|
139
|
+
.moa-header{display:flex;flex-direction:column;gap:8px;padding-bottom:14px;border-bottom:1px solid var(--dsw-alias-border-l2)}
|
|
140
|
+
.moa-page-title{font-size:20px;font-weight:700;color:var(--dsw-alias-label-primary);display:flex;align-items:center;gap:10px;flex-wrap:wrap}
|
|
141
|
+
.moa-page-sub{font-size:13px;color:var(--dsw-alias-label-secondary);line-height:1.5}
|
|
142
|
+
|
|
143
|
+
.moa-badge-row{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:2px}
|
|
144
|
+
.moa-badge{font-size:12px;padding:3px 10px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);display:inline-flex;align-items:center;gap:5px;font-weight:500}
|
|
145
|
+
.moa-badge-ok{border-color:var(--dsw-alias-state-success-primary);color:var(--dsw-alias-state-success-primary);background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent)}
|
|
146
|
+
.moa-badge-warn{border-color:var(--dsw-alias-state-warning-primary);color:var(--dsw-alias-state-warning-primary);background:color-mix(in srgb, var(--dsw-alias-state-warning-primary) 10%, transparent)}
|
|
147
|
+
.moa-badge-brand{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}
|
|
148
|
+
|
|
149
|
+
.moa-section-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;padding:18px 20px;display:flex;flex-direction:column;gap:14px}
|
|
150
|
+
.moa-section-title{font-size:15px;font-weight:600;color:var(--dsw-alias-label-primary);display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}
|
|
151
|
+
.moa-section-desc{font-size:13px;color:var(--dsw-alias-label-secondary);margin-top:-6px;line-height:1.4}
|
|
152
|
+
|
|
153
|
+
.moa-grid-2{display:grid;grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));gap:12px}
|
|
154
|
+
.moa-stat-box{padding:12px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2);display:flex;flex-direction:column;gap:4px}
|
|
155
|
+
.moa-stat-val{font-size:18px;font-weight:700;color:var(--dsw-alias-label-primary)}
|
|
156
|
+
.moa-stat-lbl{font-size:12px;color:var(--dsw-alias-label-secondary)}
|
|
157
|
+
|
|
158
|
+
.moa-input{height:36px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;width:100%;box-sizing:border-box}
|
|
159
|
+
.moa-input:focus{outline:none;border-color:var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))}
|
|
160
|
+
.moa-textarea{width:100%;box-sizing:border-box;padding:10px 12px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-size:13px;resize:vertical;outline:none;line-height:1.4}
|
|
161
|
+
.moa-textarea:focus{border-color:var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))}
|
|
162
|
+
|
|
163
|
+
.moa-btn{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:7px 14px;font-size:13px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-weight:500;display:inline-flex;align-items:center;justify-content:center;gap:6px;transition:all .15s ease}
|
|
164
|
+
.moa-btn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-4, var(--dsw-alias-bg-layer-1));border-color:var(--dsw-alias-label-secondary)}
|
|
165
|
+
.moa-btn-primary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3);border-color:transparent}
|
|
166
|
+
.moa-btn-primary:hover:not(:disabled){background:var(--dsw-alias-label-primary) !important;color:var(--dsw-alias-bg-layer-3) !important;opacity:0.9}
|
|
167
|
+
.moa-btn-danger{color:var(--dsw-alias-state-error-primary);border-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 35%, transparent)}
|
|
168
|
+
.moa-btn-danger:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent) !important;border-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 50%, transparent)}
|
|
169
|
+
.moa-btn-sub{padding:5px 12px;font-size:12px;border-radius:6px}
|
|
170
|
+
|
|
171
|
+
/* Segmented Buttons */
|
|
172
|
+
.moa-seg-group{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
|
|
173
|
+
.moa-seg-btn{appearance:none;font:inherit;padding:6px 14px;border-radius:8px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);font-size:13px;font-weight:500;cursor:pointer;display:inline-flex;align-items:center;gap:6px;transition:all .15s ease}
|
|
174
|
+
.moa-seg-btn:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-secondary)}
|
|
175
|
+
.moa-seg-btn-active{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3);border-color:transparent;font-weight:600}
|
|
176
|
+
.moa-seg-btn-active:hover{background:var(--dsw-alias-label-primary) !important;color:var(--dsw-alias-bg-layer-3) !important}
|
|
177
|
+
|
|
178
|
+
/* Sliders */
|
|
179
|
+
.moa-slider-row{display:flex;align-items:center;gap:14px}
|
|
180
|
+
.moa-slider{flex:1;accent-color:var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary));cursor:pointer;height:6px}
|
|
181
|
+
.moa-slider-badge{min-width:48px;height:30px;padding:0 8px;display:flex;align-items:center;justify-content:center;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:6px;font-size:13px;font-weight:700;color:var(--dsw-alias-label-primary)}
|
|
182
|
+
|
|
183
|
+
/* Candidate Rows */
|
|
184
|
+
.moa-cand-list{display:flex;flex-direction:column;gap:10px}
|
|
185
|
+
.moa-cand-row{display:flex;align-items:center;gap:10px}
|
|
186
|
+
.moa-cand-badge{font-size:12px;font-weight:600;color:var(--dsw-alias-label-secondary);min-width:92px;flex-shrink:0}
|
|
187
|
+
.moa-cand-picker{flex:1;min-width:0}
|
|
188
|
+
|
|
189
|
+
/* Searchable Model Picker */
|
|
190
|
+
.moa-picker-container{position:relative;width:100%}
|
|
191
|
+
.moa-picker-btn{width:100%;min-height:38px;padding:6px 12px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-primary);display:flex;align-items:center;justify-content:space-between;cursor:pointer;font-size:13px;text-align:left;box-sizing:border-box;gap:8px}
|
|
192
|
+
.moa-picker-btn:hover{border-color:var(--dsw-alias-label-secondary)}
|
|
193
|
+
.moa-picker-summary{display:flex;align-items:center;gap:8px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
194
|
+
.moa-prov-chip{font-size:11px;font-weight:600;text-transform:uppercase;padding:2px 6px;border-radius:4px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);flex-shrink:0}
|
|
195
|
+
.moa-model-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500}
|
|
196
|
+
.moa-picker-popover{position:absolute;top:calc(100% + 4px);left:0;right:0;background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;box-shadow:0 12px 36px color-mix(in srgb, var(--dsw-alias-label-tertiary) 45%, transparent);z-index:1000;max-height:360px;display:flex;flex-direction:column;overflow:hidden}
|
|
197
|
+
.moa-picker-search-bar{padding:8px 10px;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);display:flex;flex-direction:column;gap:4px}
|
|
198
|
+
.moa-search-input{width:100%;height:32px;padding:0 10px;background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;color:var(--dsw-alias-label-primary);font-size:13px;outline:none;box-sizing:border-box}
|
|
199
|
+
.moa-search-input:focus{border-color:var(--dsw-alias-label-primary)}
|
|
200
|
+
.moa-search-info{font-size:11px;color:var(--dsw-alias-label-tertiary);padding:0 2px;display:flex;justify-content:space-between}
|
|
201
|
+
.moa-picker-list{overflow-y:auto;max-height:280px;padding:6px;display:flex;flex-direction:column;gap:2px}
|
|
202
|
+
.moa-group-title{font-size:11px;font-weight:700;text-transform:uppercase;color:var(--dsw-alias-label-tertiary);padding:8px 8px 4px}
|
|
203
|
+
.moa-picker-item{display:flex;align-items:center;justify-content:space-between;padding:7px 10px;border-radius:6px;font-size:13px;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left;border:none;background:transparent;width:100%;gap:8px}
|
|
204
|
+
.moa-picker-item:hover, .moa-picker-item-selected{background:var(--dsw-alias-interactive-bg-hover, var(--dsw-alias-bg-layer-2))}
|
|
205
|
+
.moa-picker-check{color:var(--dsw-alias-state-success-primary);font-weight:bold;font-size:14px;flex-shrink:0}
|
|
206
|
+
.moa-custom-opt{padding:8px 10px;border-top:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);font-size:12px;color:var(--dsw-alias-label-primary);cursor:pointer;display:flex;align-items:center;gap:6px}
|
|
207
|
+
|
|
208
|
+
/* Alerts & Collapsible Card */
|
|
209
|
+
.moa-alert-ok{padding:10px 14px;border-radius:8px;background:color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent);color:var(--dsw-alias-state-success-primary);font-size:13px;font-weight:500}
|
|
210
|
+
.moa-alert-err{padding:10px 14px;border-radius:8px;background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);color:var(--dsw-alias-state-error-primary);font-size:13px;font-weight:500}
|
|
211
|
+
.moa-card{list-style:none;margin:0;padding:0;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;overflow:hidden;transition:border-color .15s ease}
|
|
212
|
+
.moa-head{width:100%;padding:14px 18px;display:flex;align-items:center;justify-content:space-between;background:transparent;border:none;color:inherit;cursor:pointer;text-align:left}
|
|
213
|
+
.moa-head:hover{background:var(--dsw-alias-bg-layer-2)}
|
|
214
|
+
.moa-title{font-size:15px;font-weight:600;color:var(--dsw-alias-label-primary)}
|
|
215
|
+
.moa-sub{font-size:12px;color:var(--dsw-alias-label-secondary);margin-top:2px}
|
|
216
|
+
.moa-chev{transition:transform .2s ease;color:var(--dsw-alias-label-secondary)}
|
|
217
|
+
.moa-chev-open{transform:rotate(180deg)}
|
|
218
|
+
.moa-body{padding:0 18px 20px;border-top:1px solid var(--dsw-alias-border-l2)}
|
|
219
|
+
`
|
|
112
220
|
|
|
113
221
|
let stylesInjected = false
|
|
114
222
|
function ensureStyles() {
|
|
115
223
|
if (stylesInjected || typeof document === 'undefined') return
|
|
116
224
|
stylesInjected = true
|
|
117
225
|
const style = document.createElement('style')
|
|
226
|
+
style.id = 'dsh-moa-full-css'
|
|
227
|
+
if (style.dataset) {
|
|
228
|
+
style.dataset.dshPlugin = NS
|
|
229
|
+
} else if (typeof style.setAttribute === 'function') {
|
|
230
|
+
style.setAttribute('data-dsh-plugin', NS)
|
|
231
|
+
}
|
|
118
232
|
style.textContent = STYLES
|
|
119
233
|
document.head.appendChild(style)
|
|
120
234
|
}
|
|
121
235
|
|
|
122
236
|
const PROVIDER_TITLES = {
|
|
123
|
-
codex: 'ChatGPT Codex
|
|
124
|
-
claude: 'Claude (Anthropic
|
|
125
|
-
grok: 'Grok (xAI
|
|
237
|
+
codex: 'ChatGPT Codex',
|
|
238
|
+
claude: 'Claude (Anthropic)',
|
|
239
|
+
grok: 'Grok (xAI)',
|
|
126
240
|
antigravity: 'Antigravity (Google)',
|
|
127
241
|
gemini: 'Google Gemini',
|
|
128
242
|
commandcode: 'CommandCode Provider',
|
|
129
243
|
'opencode-go': 'OpenCode-Go',
|
|
130
244
|
'qwen-token-plan': 'Qwen (Token Plan)',
|
|
131
245
|
groq: 'Groq Cloud',
|
|
132
|
-
zai: 'Zhipu GLM
|
|
246
|
+
zai: 'Zhipu GLM',
|
|
133
247
|
ollama: 'Ollama (Local / Cloud)',
|
|
134
248
|
'kimi-coding': 'Kimi Coding',
|
|
135
249
|
minimax: 'MiniMax',
|
|
@@ -141,7 +255,7 @@ window.__ModuleLoader__.load({
|
|
|
141
255
|
provider,
|
|
142
256
|
model,
|
|
143
257
|
onChange,
|
|
144
|
-
availableModels,
|
|
258
|
+
availableModels = [],
|
|
145
259
|
t,
|
|
146
260
|
}) {
|
|
147
261
|
const [open, setOpen] = React.useState(false)
|
|
@@ -150,7 +264,7 @@ window.__ModuleLoader__.load({
|
|
|
150
264
|
const inputRef = React.useRef(null)
|
|
151
265
|
|
|
152
266
|
const selectedLabel = React.useMemo(() => {
|
|
153
|
-
if (!provider && !model) return t('
|
|
267
|
+
if (!provider && !model) return t('picker.select')
|
|
154
268
|
const found = availableModels.find((m) => m.provider === provider && m.model === model)
|
|
155
269
|
return found ? (found.label || `${provider}: ${found.model}`) : `${provider}:${model}`
|
|
156
270
|
}, [availableModels, provider, model, t])
|
|
@@ -178,19 +292,18 @@ window.__ModuleLoader__.load({
|
|
|
178
292
|
const title = PROVIDER_TITLES[prov] || prov.toUpperCase()
|
|
179
293
|
groups.push({ provider: prov, title, items })
|
|
180
294
|
}
|
|
181
|
-
groups.sort((a, b) => a.title.localeCompare(b.title))
|
|
182
295
|
return groups
|
|
183
296
|
}, [filtered])
|
|
184
297
|
|
|
185
298
|
React.useEffect(() => {
|
|
186
299
|
if (!open) return
|
|
187
|
-
const
|
|
300
|
+
const handleClickOutside = (e) => {
|
|
188
301
|
if (rootRef.current && !rootRef.current.contains(e.target)) {
|
|
189
302
|
setOpen(false)
|
|
190
303
|
}
|
|
191
304
|
}
|
|
192
|
-
document.addEventListener('mousedown',
|
|
193
|
-
return () => document.removeEventListener('mousedown',
|
|
305
|
+
document.addEventListener('mousedown', handleClickOutside)
|
|
306
|
+
return () => document.removeEventListener('mousedown', handleClickOutside)
|
|
194
307
|
}, [open])
|
|
195
308
|
|
|
196
309
|
React.useEffect(() => {
|
|
@@ -206,21 +319,23 @@ window.__ModuleLoader__.load({
|
|
|
206
319
|
}
|
|
207
320
|
|
|
208
321
|
const handleCustom = () => {
|
|
209
|
-
const
|
|
210
|
-
if (!
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
322
|
+
const trimmed = query.trim()
|
|
323
|
+
if (!trimmed) return
|
|
324
|
+
let prov = provider || 'custom'
|
|
325
|
+
let mod = trimmed
|
|
326
|
+
if (trimmed.includes(':')) {
|
|
327
|
+
const [p, ...rest] = trimmed.split(':')
|
|
328
|
+
prov = p.trim()
|
|
329
|
+
mod = rest.join(':').trim()
|
|
216
330
|
}
|
|
331
|
+
onChange(prov, mod)
|
|
217
332
|
setOpen(false)
|
|
218
333
|
setQuery('')
|
|
219
334
|
}
|
|
220
335
|
|
|
221
336
|
return React.createElement(
|
|
222
337
|
'div',
|
|
223
|
-
{
|
|
338
|
+
{ className: 'moa-picker-container', ref: rootRef },
|
|
224
339
|
React.createElement(
|
|
225
340
|
'button',
|
|
226
341
|
{
|
|
@@ -234,7 +349,7 @@ window.__ModuleLoader__.load({
|
|
|
234
349
|
provider && React.createElement('span', { className: 'moa-prov-chip' }, provider),
|
|
235
350
|
React.createElement('span', { className: 'moa-model-name' }, selectedLabel)
|
|
236
351
|
),
|
|
237
|
-
React.createElement(Chevron, {
|
|
352
|
+
React.createElement(Chevron, { style: { transform: open ? 'rotate(180deg)' : 'none' } })
|
|
238
353
|
),
|
|
239
354
|
open &&
|
|
240
355
|
React.createElement(
|
|
@@ -247,7 +362,7 @@ window.__ModuleLoader__.load({
|
|
|
247
362
|
ref: inputRef,
|
|
248
363
|
type: 'text',
|
|
249
364
|
className: 'moa-search-input',
|
|
250
|
-
placeholder: t('
|
|
365
|
+
placeholder: t('picker.search_placeholder'),
|
|
251
366
|
value: query,
|
|
252
367
|
onChange: (e) => setQuery(e.target.value),
|
|
253
368
|
onKeyDown: (e) => {
|
|
@@ -261,13 +376,13 @@ window.__ModuleLoader__.load({
|
|
|
261
376
|
React.createElement(
|
|
262
377
|
'span',
|
|
263
378
|
null,
|
|
264
|
-
`${t('
|
|
379
|
+
`${t('picker.found')}: ${filtered.length} ${t('picker.of')} ${availableModels.length}`
|
|
265
380
|
),
|
|
266
381
|
query &&
|
|
267
382
|
React.createElement(
|
|
268
383
|
'span',
|
|
269
|
-
{ style: { cursor: 'pointer' }, onClick: () => setQuery('') },
|
|
270
|
-
t('clear')
|
|
384
|
+
{ style: { cursor: 'pointer', textDecoration: 'underline' }, onClick: () => setQuery('') },
|
|
385
|
+
t('picker.clear')
|
|
271
386
|
)
|
|
272
387
|
)
|
|
273
388
|
),
|
|
@@ -304,7 +419,7 @@ window.__ModuleLoader__.load({
|
|
|
304
419
|
React.createElement(
|
|
305
420
|
'div',
|
|
306
421
|
{ style: { padding: '12px 10px', fontSize: 13, color: 'var(--dsw-alias-label-secondary)' } },
|
|
307
|
-
t('
|
|
422
|
+
t('picker.none')
|
|
308
423
|
)
|
|
309
424
|
),
|
|
310
425
|
query.trim() &&
|
|
@@ -315,7 +430,7 @@ window.__ModuleLoader__.load({
|
|
|
315
430
|
React.createElement(
|
|
316
431
|
'span',
|
|
317
432
|
null,
|
|
318
|
-
`${t('
|
|
433
|
+
`${t('picker.custom')}"${query.trim()}"`
|
|
319
434
|
)
|
|
320
435
|
)
|
|
321
436
|
)
|
|
@@ -329,14 +444,18 @@ window.__ModuleLoader__.load({
|
|
|
329
444
|
defaultPreset,
|
|
330
445
|
setDefaultPreset,
|
|
331
446
|
availableModels,
|
|
447
|
+
enabled,
|
|
448
|
+
setEnabled,
|
|
449
|
+
hostStatus,
|
|
450
|
+
stats,
|
|
332
451
|
handleSave,
|
|
333
452
|
saveStatus,
|
|
453
|
+
reload,
|
|
334
454
|
}) {
|
|
335
455
|
const [selectedPresetName, setSelectedPresetName] = React.useState(defaultPreset || (presets[0] && presets[0].name) || 'default')
|
|
336
456
|
const [isCreating, setIsCreating] = React.useState(false)
|
|
337
457
|
const [newPresetName, setNewPresetName] = React.useState('')
|
|
338
458
|
|
|
339
|
-
// Sync active preset if selected one is removed
|
|
340
459
|
React.useEffect(() => {
|
|
341
460
|
if (!presets.some((p) => p.name === selectedPresetName)) {
|
|
342
461
|
setSelectedPresetName(defaultPreset || (presets[0] && presets[0].name) || 'default')
|
|
@@ -346,9 +465,11 @@ window.__ModuleLoader__.load({
|
|
|
346
465
|
const currentPreset = presets.find((p) => p.name === selectedPresetName) || presets[0] || {
|
|
347
466
|
name: 'default',
|
|
348
467
|
reference_models: [],
|
|
349
|
-
aggregator: { provider: '', model: '' },
|
|
468
|
+
aggregator: { provider: 'codex', model: 'gpt-5.6-sol' },
|
|
350
469
|
aggregator_temperature: 0.4,
|
|
351
470
|
reference_temperature: 0.6,
|
|
471
|
+
judge_criteria: '',
|
|
472
|
+
ask_clarifying_questions: true,
|
|
352
473
|
}
|
|
353
474
|
|
|
354
475
|
const updateCurrentPreset = (modifier) => {
|
|
@@ -363,10 +484,7 @@ window.__ModuleLoader__.load({
|
|
|
363
484
|
}
|
|
364
485
|
|
|
365
486
|
const setAggregatorModel = (prov, mod) => {
|
|
366
|
-
updateCurrentPreset((p) => ({
|
|
367
|
-
...p,
|
|
368
|
-
aggregator: { provider: prov, model: mod },
|
|
369
|
-
}))
|
|
487
|
+
updateCurrentPreset((p) => ({ ...p, aggregator: { provider: prov, model: mod } }))
|
|
370
488
|
}
|
|
371
489
|
|
|
372
490
|
const setAggregatorTemp = (temp) => {
|
|
@@ -441,115 +559,146 @@ window.__ModuleLoader__.load({
|
|
|
441
559
|
const created = {
|
|
442
560
|
name,
|
|
443
561
|
enabled: true,
|
|
562
|
+
ask_clarifying_questions: true,
|
|
444
563
|
reference_models: currentPreset?.reference_models?.length ? structuredClone(currentPreset.reference_models) : [
|
|
445
564
|
{ provider: 'opencode-go', model: 'deepseek-v4-flash' },
|
|
446
565
|
{ provider: 'codex', model: 'gpt-5.6-sol' },
|
|
447
566
|
],
|
|
448
567
|
aggregator: currentPreset?.aggregator?.provider ? structuredClone(currentPreset.aggregator) : {
|
|
449
|
-
provider: '
|
|
450
|
-
model: '
|
|
568
|
+
provider: 'codex',
|
|
569
|
+
model: 'gpt-5.6-sol',
|
|
451
570
|
},
|
|
452
|
-
reference_temperature:
|
|
453
|
-
aggregator_temperature:
|
|
454
|
-
max_tokens:
|
|
571
|
+
reference_temperature: 0.6,
|
|
572
|
+
aggregator_temperature: 0.4,
|
|
573
|
+
max_tokens: 4096,
|
|
574
|
+
judge_criteria: '',
|
|
455
575
|
}
|
|
456
|
-
setPresets((prev) => [...prev, created])
|
|
576
|
+
setPresets((prev) => [...(prev || []), created])
|
|
457
577
|
setSelectedPresetName(name)
|
|
458
578
|
setIsCreating(false)
|
|
459
579
|
setNewPresetName('')
|
|
460
580
|
}
|
|
461
581
|
|
|
462
|
-
const handleDeletePreset = () => {
|
|
582
|
+
const handleDeletePreset = (nameToDelete) => {
|
|
463
583
|
if (presets.length <= 1) return
|
|
464
|
-
if (
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
584
|
+
if (typeof window !== 'undefined' && window.confirm) {
|
|
585
|
+
if (!window.confirm(t('presets.del_confirm', { name: nameToDelete }))) return
|
|
586
|
+
}
|
|
587
|
+
setPresets((prev) => prev.filter((p) => p.name !== nameToDelete))
|
|
588
|
+
if (defaultPreset === nameToDelete) {
|
|
589
|
+
const nextDefault = presets.find((p) => p.name !== nameToDelete)?.name || 'default'
|
|
590
|
+
setDefaultPreset(nextDefault)
|
|
468
591
|
}
|
|
469
|
-
setPresets(remaining)
|
|
470
|
-
setSelectedPresetName(remaining[0].name)
|
|
471
592
|
}
|
|
472
593
|
|
|
594
|
+
const aggTemp = currentPreset.aggregator_temperature !== undefined ? currentPreset.aggregator_temperature : 0.4
|
|
595
|
+
const refTemp = currentPreset.reference_temperature !== undefined ? currentPreset.reference_temperature : 0.6
|
|
473
596
|
const currentRefs = currentPreset.reference_models || []
|
|
474
|
-
const aggTemp = currentPreset.aggregator_temperature ?? 0.4
|
|
475
|
-
const refTemp = currentPreset.reference_temperature ?? 0.6
|
|
476
597
|
const judgeCriteria = currentPreset.judge_criteria || ''
|
|
598
|
+
const isDefault = defaultPreset === currentPreset.name
|
|
599
|
+
const hostStatusBadge = hostStatus === 'online'
|
|
600
|
+
? React.createElement('span', { className: 'moa-badge moa-badge-ok' }, '🟢 ' + t('badge.online'))
|
|
601
|
+
: hostStatus === 'disabled'
|
|
602
|
+
? React.createElement('span', { className: 'moa-badge moa-badge-warn' }, '⏸ ' + t('badge.disabled'))
|
|
603
|
+
: React.createElement('span', { className: 'moa-badge moa-badge-warn' }, '🔴 ' + t('badge.offline'))
|
|
477
604
|
|
|
478
605
|
return React.createElement(
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
606
|
+
'div',
|
|
607
|
+
{ className: 'moa-page' },
|
|
608
|
+
|
|
609
|
+
/* ── HEADER WITH LIVE STATUS CHIPS ── */
|
|
482
610
|
React.createElement(
|
|
483
611
|
'div',
|
|
484
|
-
{ className: 'moa-
|
|
612
|
+
{ className: 'moa-header' },
|
|
485
613
|
React.createElement(
|
|
486
614
|
'div',
|
|
487
|
-
{ className: 'moa-
|
|
488
|
-
React.createElement('
|
|
615
|
+
{ className: 'moa-page-title' },
|
|
616
|
+
React.createElement('span', null, '🧠 ' + t('header.title')),
|
|
617
|
+
React.createElement(
|
|
618
|
+
'div',
|
|
619
|
+
{ className: 'moa-badge-row' },
|
|
620
|
+
hostStatusBadge,
|
|
621
|
+
React.createElement('span', { className: 'moa-badge moa-badge-brand' }, t('badge.preset', { name: currentPreset.name })),
|
|
622
|
+
React.createElement('span', { className: 'moa-badge moa-badge-brand' }, t('badge.models', { count: availableModels.length })),
|
|
623
|
+
React.createElement('span', { className: 'moa-badge moa-badge-brand' }, t('badge.candidates', { count: currentRefs.length }))
|
|
624
|
+
)
|
|
625
|
+
),
|
|
626
|
+
React.createElement('div', { className: 'moa-page-sub' }, t('header.sub'))
|
|
627
|
+
),
|
|
628
|
+
|
|
629
|
+
/* ── SECTION 1: PRESET MANAGEMENT ── */
|
|
630
|
+
React.createElement(
|
|
631
|
+
'div',
|
|
632
|
+
{ className: 'moa-section-card' },
|
|
633
|
+
React.createElement(
|
|
634
|
+
'div',
|
|
635
|
+
{ className: 'moa-section-title' },
|
|
636
|
+
React.createElement('span', null, t('presets.title')),
|
|
489
637
|
React.createElement(
|
|
490
638
|
'div',
|
|
491
|
-
{
|
|
492
|
-
|
|
493
|
-
'select',
|
|
494
|
-
{
|
|
495
|
-
className: 'moa-select',
|
|
496
|
-
style: { flex: 1, minWidth: 200 },
|
|
497
|
-
value: isCreating ? '__new__' : currentPreset.name,
|
|
498
|
-
onChange: (e) => {
|
|
499
|
-
if (e.target.value === '__new__') {
|
|
500
|
-
setIsCreating(true)
|
|
501
|
-
} else {
|
|
502
|
-
setIsCreating(false)
|
|
503
|
-
setSelectedPresetName(e.target.value)
|
|
504
|
-
}
|
|
505
|
-
},
|
|
506
|
-
},
|
|
507
|
-
presets.map((pr) =>
|
|
508
|
-
React.createElement(
|
|
509
|
-
'option',
|
|
510
|
-
{ key: pr.name, value: pr.name },
|
|
511
|
-
pr.name + (pr.name === defaultPreset ? ` ⭐ (${t('isDefault') || 'основной'})` : '')
|
|
512
|
-
)
|
|
513
|
-
),
|
|
514
|
-
React.createElement('option', { disabled: true }, '──────────────────'),
|
|
515
|
-
React.createElement(
|
|
516
|
-
'option',
|
|
517
|
-
{ value: '__new__' },
|
|
518
|
-
'+ ' + (t('createNewPreset') || 'Создать новый пресет...')
|
|
519
|
-
)
|
|
520
|
-
),
|
|
521
|
-
currentPreset.name !== defaultPreset &&
|
|
639
|
+
{ style: { display: 'flex', gap: 8, alignItems: 'center' } },
|
|
640
|
+
!isDefault &&
|
|
522
641
|
React.createElement(
|
|
523
642
|
'button',
|
|
524
643
|
{
|
|
525
644
|
type: 'button',
|
|
526
|
-
className: 'moa-btn-sub',
|
|
645
|
+
className: 'moa-btn moa-btn-sub',
|
|
527
646
|
onClick: () => setDefaultPreset(currentPreset.name),
|
|
528
|
-
title: t('makeDefaultHint') || 'Использовать этот пресет при вызове /moa без аргументов',
|
|
529
647
|
},
|
|
530
|
-
'
|
|
648
|
+
'★ ' + t('presets.set_default')
|
|
531
649
|
),
|
|
532
|
-
|
|
650
|
+
isDefault &&
|
|
651
|
+
React.createElement('span', { className: 'moa-badge moa-badge-ok' }, '✓ ' + t('presets.active_badge')),
|
|
652
|
+
!isCreating &&
|
|
533
653
|
React.createElement(
|
|
534
654
|
'button',
|
|
535
655
|
{
|
|
536
656
|
type: 'button',
|
|
537
|
-
className: 'moa-btn-
|
|
538
|
-
onClick:
|
|
539
|
-
title: t('deletePreset') || 'Удалить пресет',
|
|
657
|
+
className: 'moa-btn moa-btn-sub',
|
|
658
|
+
onClick: () => setIsCreating(true),
|
|
540
659
|
},
|
|
541
|
-
|
|
660
|
+
t('presets.new_btn')
|
|
542
661
|
)
|
|
543
662
|
)
|
|
544
663
|
),
|
|
664
|
+
React.createElement('div', { className: 'moa-section-desc' }, t('presets.desc')),
|
|
665
|
+
React.createElement(
|
|
666
|
+
'div',
|
|
667
|
+
{ className: 'moa-seg-group' },
|
|
668
|
+
presets.map((p) => {
|
|
669
|
+
const isCur = p.name === selectedPresetName
|
|
670
|
+
return React.createElement(
|
|
671
|
+
'button',
|
|
672
|
+
{
|
|
673
|
+
key: p.name,
|
|
674
|
+
type: 'button',
|
|
675
|
+
className: isCur ? 'moa-seg-btn moa-seg-btn-active' : 'moa-seg-btn',
|
|
676
|
+
onClick: () => setSelectedPresetName(p.name),
|
|
677
|
+
},
|
|
678
|
+
p.name === defaultPreset ? `★ ${p.name}` : p.name
|
|
679
|
+
)
|
|
680
|
+
}),
|
|
681
|
+
presets.length > 1 &&
|
|
682
|
+
React.createElement(
|
|
683
|
+
'button',
|
|
684
|
+
{
|
|
685
|
+
type: 'button',
|
|
686
|
+
className: 'moa-btn moa-btn-danger moa-btn-sub',
|
|
687
|
+
onClick: () => handleDeletePreset(currentPreset.name),
|
|
688
|
+
title: t('presets.del_btn'),
|
|
689
|
+
},
|
|
690
|
+
'🗑 ' + t('presets.del_btn')
|
|
691
|
+
)
|
|
692
|
+
),
|
|
545
693
|
isCreating &&
|
|
546
694
|
React.createElement(
|
|
547
695
|
'div',
|
|
548
|
-
{
|
|
696
|
+
{ style: { display: 'flex', gap: 8, alignItems: 'center', marginTop: 4 } },
|
|
549
697
|
React.createElement('input', {
|
|
550
698
|
type: 'text',
|
|
551
|
-
className: 'moa-
|
|
552
|
-
|
|
699
|
+
className: 'moa-input',
|
|
700
|
+
style: { maxWidth: 320 },
|
|
701
|
+
placeholder: t('presets.name_placeholder'),
|
|
553
702
|
value: newPresetName,
|
|
554
703
|
onChange: (e) => setNewPresetName(e.target.value),
|
|
555
704
|
onKeyDown: (e) => {
|
|
@@ -561,39 +710,34 @@ window.__ModuleLoader__.load({
|
|
|
561
710
|
'button',
|
|
562
711
|
{
|
|
563
712
|
type: 'button',
|
|
564
|
-
className: 'moa-btn-
|
|
713
|
+
className: 'moa-btn moa-btn-primary',
|
|
565
714
|
onClick: handleCreatePreset,
|
|
566
|
-
style: { background: 'var(--dsw-alias-label-primary)', color: 'var(--dsw-alias-bg-layer-3)' },
|
|
567
715
|
},
|
|
568
|
-
t('create')
|
|
716
|
+
t('presets.create')
|
|
569
717
|
),
|
|
570
718
|
React.createElement(
|
|
571
719
|
'button',
|
|
572
720
|
{
|
|
573
721
|
type: 'button',
|
|
574
|
-
className: 'moa-btn
|
|
722
|
+
className: 'moa-btn',
|
|
575
723
|
onClick: () => setIsCreating(false),
|
|
576
724
|
},
|
|
577
|
-
t('cancel')
|
|
725
|
+
t('presets.cancel')
|
|
578
726
|
)
|
|
579
727
|
)
|
|
580
728
|
),
|
|
581
|
-
|
|
729
|
+
|
|
730
|
+
/* ── SECTION 2: AGGREGATOR / JUDGE ── */
|
|
582
731
|
React.createElement(
|
|
583
732
|
'div',
|
|
584
|
-
{ className: 'moa-
|
|
733
|
+
{ className: 'moa-section-card' },
|
|
585
734
|
React.createElement(
|
|
586
735
|
'div',
|
|
587
|
-
{ className: 'moa-
|
|
588
|
-
React.createElement('span', null, t('
|
|
589
|
-
React.createElement(
|
|
590
|
-
'div',
|
|
591
|
-
{ style: { display: 'flex', alignItems: 'center', gap: 6 } },
|
|
592
|
-
React.createElement('span', { className: 'moa-hint' }, `${t('temperature') || 'Температура'}:`),
|
|
593
|
-
React.createElement('span', { style: { fontWeight: 600, fontSize: 13 } }, aggTemp)
|
|
594
|
-
)
|
|
736
|
+
{ className: 'moa-section-title' },
|
|
737
|
+
React.createElement('span', null, t('aggregator.title')),
|
|
738
|
+
React.createElement('span', { className: 'moa-badge moa-badge-brand' }, `Temp: ${aggTemp}`)
|
|
595
739
|
),
|
|
596
|
-
React.createElement('div', { className: 'moa-
|
|
740
|
+
React.createElement('div', { className: 'moa-section-desc' }, t('aggregator.desc')),
|
|
597
741
|
React.createElement(SearchableModelPicker, {
|
|
598
742
|
provider: currentPreset.aggregator?.provider || '',
|
|
599
743
|
model: currentPreset.aggregator?.model || '',
|
|
@@ -603,57 +747,42 @@ window.__ModuleLoader__.load({
|
|
|
603
747
|
}),
|
|
604
748
|
React.createElement(
|
|
605
749
|
'div',
|
|
606
|
-
{
|
|
750
|
+
{ style: { display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4 } },
|
|
751
|
+
React.createElement(
|
|
752
|
+
'div',
|
|
753
|
+
{ style: { display: 'flex', justifyContent: 'space-between', fontSize: 13, fontWeight: 500 } },
|
|
754
|
+
React.createElement('span', null, t('aggregator.temp_label')),
|
|
755
|
+
React.createElement('span', { style: { color: 'var(--dsw-alias-label-secondary)', fontSize: 12 } }, t('aggregator.temp_hint'))
|
|
756
|
+
),
|
|
607
757
|
React.createElement(
|
|
608
758
|
'div',
|
|
609
|
-
{ className: 'moa-
|
|
759
|
+
{ className: 'moa-slider-row' },
|
|
610
760
|
React.createElement('input', {
|
|
611
761
|
type: 'range',
|
|
612
|
-
className: 'moa-
|
|
762
|
+
className: 'moa-slider',
|
|
613
763
|
min: '0',
|
|
614
764
|
max: '2',
|
|
615
765
|
step: '0.05',
|
|
616
766
|
value: aggTemp,
|
|
617
767
|
onChange: (e) => setAggregatorTemp(e.target.value),
|
|
618
768
|
}),
|
|
619
|
-
React.createElement('
|
|
620
|
-
|
|
621
|
-
className: 'moa-temp-input',
|
|
622
|
-
min: '0',
|
|
623
|
-
max: '2',
|
|
624
|
-
step: '0.05',
|
|
625
|
-
value: aggTemp,
|
|
626
|
-
onChange: (e) => setAggregatorTemp(e.target.value),
|
|
627
|
-
})
|
|
628
|
-
),
|
|
629
|
-
React.createElement('div', { className: 'moa-hint' }, t('aggTempHint') || 'Рекомендуется 0.2 – 0.4 для точной проверки и синтеза без галлюцинаций.')
|
|
769
|
+
React.createElement('div', { className: 'moa-slider-badge' }, aggTemp)
|
|
770
|
+
)
|
|
630
771
|
),
|
|
631
772
|
React.createElement(
|
|
632
773
|
'div',
|
|
633
|
-
{
|
|
634
|
-
React.createElement('label', {
|
|
774
|
+
{ style: { display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4, borderTop: '1px solid var(--dsw-alias-border-l2)', paddingTop: 10 } },
|
|
775
|
+
React.createElement('label', { style: { fontSize: 13, fontWeight: 500 } }, t('aggregator.criteria_label')),
|
|
635
776
|
React.createElement('textarea', {
|
|
636
777
|
className: 'moa-textarea',
|
|
637
778
|
rows: 2,
|
|
638
|
-
|
|
639
|
-
width: '100%',
|
|
640
|
-
boxSizing: 'border-box',
|
|
641
|
-
padding: '8px 10px',
|
|
642
|
-
borderRadius: 8,
|
|
643
|
-
border: '1px solid var(--dsw-alias-border-l2)',
|
|
644
|
-
background: 'var(--dsw-alias-bg-layer-3)',
|
|
645
|
-
color: 'var(--dsw-alias-label-primary)',
|
|
646
|
-
fontSize: 13,
|
|
647
|
-
resize: 'vertical',
|
|
648
|
-
outline: 'none',
|
|
649
|
-
},
|
|
650
|
-
placeholder: t('judgeCriteriaPlaceholder') || 'например: Приоритет — производительность, чистый код, минимум зависимостей...',
|
|
779
|
+
placeholder: t('aggregator.criteria_placeholder'),
|
|
651
780
|
value: judgeCriteria,
|
|
652
781
|
onChange: (e) => setJudgeCriteria(e.target.value),
|
|
653
782
|
}),
|
|
654
783
|
React.createElement(
|
|
655
784
|
'div',
|
|
656
|
-
{ style: { marginTop:
|
|
785
|
+
{ style: { marginTop: 6, display: 'flex', flexDirection: 'column', gap: 3 } },
|
|
657
786
|
React.createElement(
|
|
658
787
|
'label',
|
|
659
788
|
{ style: { display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: 13, fontWeight: 500 } },
|
|
@@ -661,37 +790,141 @@ window.__ModuleLoader__.load({
|
|
|
661
790
|
type: 'checkbox',
|
|
662
791
|
checked: currentPreset.ask_clarifying_questions !== false,
|
|
663
792
|
onChange: (e) => updateCurrentPreset((p) => ({ ...p, ask_clarifying_questions: e.target.checked })),
|
|
664
|
-
style: { accentColor: 'var(--dsw-alias-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
793
|
+
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
665
794
|
}),
|
|
666
|
-
t('
|
|
795
|
+
t('aggregator.questions_label')
|
|
667
796
|
),
|
|
668
797
|
React.createElement(
|
|
669
798
|
'div',
|
|
670
|
-
{
|
|
671
|
-
t('
|
|
799
|
+
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginLeft: 22 } },
|
|
800
|
+
t('aggregator.questions_hint')
|
|
801
|
+
),
|
|
802
|
+
React.createElement(
|
|
803
|
+
'label',
|
|
804
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: 13, fontWeight: 500, marginTop: 8 } },
|
|
805
|
+
React.createElement('input', {
|
|
806
|
+
type: 'checkbox',
|
|
807
|
+
checked: Boolean(currentPreset.curator_synthesis),
|
|
808
|
+
onChange: (e) => updateCurrentPreset((p) => ({ ...p, curator_synthesis: e.target.checked })),
|
|
809
|
+
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
810
|
+
}),
|
|
811
|
+
t('aggregator.curator_label')
|
|
812
|
+
),
|
|
813
|
+
React.createElement(
|
|
814
|
+
'div',
|
|
815
|
+
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginLeft: 22 } },
|
|
816
|
+
t('aggregator.curator_hint')
|
|
817
|
+
),
|
|
818
|
+
React.createElement(
|
|
819
|
+
'label',
|
|
820
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: 13, fontWeight: 500, marginTop: 8 } },
|
|
821
|
+
React.createElement('input', {
|
|
822
|
+
type: 'checkbox',
|
|
823
|
+
checked: currentPreset.stream_aggregator !== false,
|
|
824
|
+
onChange: (e) => updateCurrentPreset((p) => ({ ...p, stream_aggregator: e.target.checked })),
|
|
825
|
+
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
826
|
+
}),
|
|
827
|
+
t('aggregator.stream_label')
|
|
828
|
+
),
|
|
829
|
+
React.createElement(
|
|
830
|
+
'div',
|
|
831
|
+
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginLeft: 22 } },
|
|
832
|
+
t('aggregator.stream_hint')
|
|
833
|
+
),
|
|
834
|
+
/* Fallback Judges Chain */
|
|
835
|
+
React.createElement(
|
|
836
|
+
'div',
|
|
837
|
+
{ style: { marginTop: 10, display: 'flex', flexDirection: 'column', gap: 6 } },
|
|
838
|
+
React.createElement(
|
|
839
|
+
'div',
|
|
840
|
+
{ style: { fontSize: 13, fontWeight: 600, color: 'var(--dsw-alias-label-primary)' } },
|
|
841
|
+
t('aggregator.fallbacks_title')
|
|
842
|
+
),
|
|
843
|
+
React.createElement(
|
|
844
|
+
'div',
|
|
845
|
+
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginTop: -4 } },
|
|
846
|
+
t('aggregator.fallbacks_desc')
|
|
847
|
+
),
|
|
848
|
+
(Array.isArray(currentPreset.aggregator_fallbacks) ? currentPreset.aggregator_fallbacks : []).map((fb, fbIdx) =>
|
|
849
|
+
React.createElement(
|
|
850
|
+
'div',
|
|
851
|
+
{ key: fbIdx, style: { display: 'flex', alignItems: 'center', gap: 8 } },
|
|
852
|
+
React.createElement('span', { className: 'moa-cand-badge' }, `Fallback #${fbIdx + 1}`),
|
|
853
|
+
React.createElement(
|
|
854
|
+
'div',
|
|
855
|
+
{ style: { flex: 1 } },
|
|
856
|
+
React.createElement(SearchableModelPicker, {
|
|
857
|
+
value: fb,
|
|
858
|
+
onChange: (val) => {
|
|
859
|
+
updateCurrentPreset((p) => {
|
|
860
|
+
const list = [...(p.aggregator_fallbacks || [])]
|
|
861
|
+
list[fbIdx] = val
|
|
862
|
+
return { ...p, aggregator_fallbacks: list }
|
|
863
|
+
})
|
|
864
|
+
},
|
|
865
|
+
allModels,
|
|
866
|
+
t,
|
|
867
|
+
})
|
|
868
|
+
),
|
|
869
|
+
React.createElement(
|
|
870
|
+
'button',
|
|
871
|
+
{
|
|
872
|
+
type: 'button',
|
|
873
|
+
className: 'moa-btn moa-btn-danger moa-btn-sub',
|
|
874
|
+
onClick: () => {
|
|
875
|
+
updateCurrentPreset((p) => ({
|
|
876
|
+
...p,
|
|
877
|
+
aggregator_fallbacks: (p.aggregator_fallbacks || []).filter((_, i) => i !== fbIdx),
|
|
878
|
+
}))
|
|
879
|
+
},
|
|
880
|
+
},
|
|
881
|
+
'✕'
|
|
882
|
+
)
|
|
883
|
+
)
|
|
884
|
+
),
|
|
885
|
+
React.createElement(
|
|
886
|
+
'div',
|
|
887
|
+
null,
|
|
888
|
+
React.createElement(
|
|
889
|
+
'button',
|
|
890
|
+
{
|
|
891
|
+
type: 'button',
|
|
892
|
+
className: 'moa-btn moa-btn-sub',
|
|
893
|
+
style: { marginTop: 4 },
|
|
894
|
+
onClick: () => {
|
|
895
|
+
updateCurrentPreset((p) => ({
|
|
896
|
+
...p,
|
|
897
|
+
aggregator_fallbacks: [...(p.aggregator_fallbacks || []), { provider: 'opencode-go', model: 'deepseek-v4-flash' }],
|
|
898
|
+
}))
|
|
899
|
+
},
|
|
900
|
+
},
|
|
901
|
+
t('aggregator.add_fallback_btn')
|
|
902
|
+
)
|
|
903
|
+
)
|
|
672
904
|
)
|
|
673
905
|
)
|
|
674
906
|
)
|
|
675
907
|
),
|
|
676
|
-
|
|
908
|
+
|
|
909
|
+
/* ── SECTION 3: CANDIDATE PROPOSERS ── */
|
|
677
910
|
React.createElement(
|
|
678
911
|
'div',
|
|
679
|
-
{ className: 'moa-
|
|
912
|
+
{ className: 'moa-section-card' },
|
|
680
913
|
React.createElement(
|
|
681
914
|
'div',
|
|
682
|
-
{ className: 'moa-
|
|
683
|
-
React.createElement('span', null, t('
|
|
915
|
+
{ className: 'moa-section-title' },
|
|
916
|
+
React.createElement('span', null, t('proposers.title')),
|
|
684
917
|
React.createElement(
|
|
685
918
|
'div',
|
|
686
|
-
{ className: 'moa-
|
|
687
|
-
React.createElement('span', {
|
|
919
|
+
{ className: 'moa-seg-group' },
|
|
920
|
+
React.createElement('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, t('proposers.parallel_count') + ':'),
|
|
688
921
|
[2, 3, 4, 5].map((cnt) =>
|
|
689
922
|
React.createElement(
|
|
690
923
|
'button',
|
|
691
924
|
{
|
|
692
925
|
key: cnt,
|
|
693
926
|
type: 'button',
|
|
694
|
-
className: currentRefs.length === cnt ? 'moa-
|
|
927
|
+
className: currentRefs.length === cnt ? 'moa-seg-btn moa-seg-btn-active moa-btn-sub' : 'moa-seg-btn moa-btn-sub',
|
|
695
928
|
onClick: () => setReferenceCount(cnt),
|
|
696
929
|
},
|
|
697
930
|
cnt
|
|
@@ -699,18 +932,55 @@ window.__ModuleLoader__.load({
|
|
|
699
932
|
)
|
|
700
933
|
)
|
|
701
934
|
),
|
|
702
|
-
React.createElement('div', { className: 'moa-
|
|
935
|
+
React.createElement('div', { className: 'moa-section-desc' }, t('proposers.desc')),
|
|
703
936
|
React.createElement(
|
|
704
937
|
'div',
|
|
705
|
-
{
|
|
938
|
+
{ 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)' } },
|
|
939
|
+
React.createElement(
|
|
940
|
+
'label',
|
|
941
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: 13, fontWeight: 500 } },
|
|
942
|
+
React.createElement('input', {
|
|
943
|
+
type: 'checkbox',
|
|
944
|
+
checked: Boolean(currentPreset.quorum_enabled),
|
|
945
|
+
onChange: (e) => updateCurrentPreset((p) => ({ ...p, quorum_enabled: e.target.checked })),
|
|
946
|
+
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
947
|
+
}),
|
|
948
|
+
t('proposers.quorum_label')
|
|
949
|
+
),
|
|
950
|
+
React.createElement(
|
|
951
|
+
'div',
|
|
952
|
+
{ style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)', marginLeft: 22 } },
|
|
953
|
+
t('proposers.quorum_hint')
|
|
954
|
+
),
|
|
955
|
+
currentPreset.quorum_enabled && React.createElement(
|
|
956
|
+
'div',
|
|
957
|
+
{ style: { marginLeft: 22, display: 'flex', alignItems: 'center', gap: 8, marginTop: 4 } },
|
|
958
|
+
React.createElement('span', { style: { fontSize: 12, color: 'var(--dsw-alias-label-secondary)' } }, t('proposers.grace_label')),
|
|
959
|
+
React.createElement('input', {
|
|
960
|
+
type: 'number',
|
|
961
|
+
className: 'moa-input',
|
|
962
|
+
style: { width: 70, height: 28, padding: '0 6px', fontSize: 12 },
|
|
963
|
+
min: 2,
|
|
964
|
+
max: 60,
|
|
965
|
+
value: currentPreset.grace_period_sec ?? 10,
|
|
966
|
+
onChange: (e) => {
|
|
967
|
+
const val = parseInt(e.target.value, 10)
|
|
968
|
+
updateCurrentPreset((p) => ({ ...p, grace_period_sec: isNaN(val) ? 10 : val }))
|
|
969
|
+
},
|
|
970
|
+
})
|
|
971
|
+
)
|
|
972
|
+
),
|
|
973
|
+
React.createElement(
|
|
974
|
+
'div',
|
|
975
|
+
{ className: 'moa-cand-list' },
|
|
706
976
|
currentRefs.map((ref, idx) =>
|
|
707
977
|
React.createElement(
|
|
708
978
|
'div',
|
|
709
|
-
{ key: idx, className: 'moa-
|
|
710
|
-
React.createElement('span', { className: 'moa-
|
|
979
|
+
{ key: idx, className: 'moa-cand-row' },
|
|
980
|
+
React.createElement('span', { className: 'moa-cand-badge' }, `${t('proposers.candidate')} #${idx + 1}`),
|
|
711
981
|
React.createElement(
|
|
712
982
|
'div',
|
|
713
|
-
{ className: 'moa-
|
|
983
|
+
{ className: 'moa-cand-picker' },
|
|
714
984
|
React.createElement(SearchableModelPicker, {
|
|
715
985
|
provider: ref.provider || '',
|
|
716
986
|
model: ref.model || '',
|
|
@@ -724,8 +994,8 @@ window.__ModuleLoader__.load({
|
|
|
724
994
|
'button',
|
|
725
995
|
{
|
|
726
996
|
type: 'button',
|
|
727
|
-
className: 'moa-btn-
|
|
728
|
-
title:
|
|
997
|
+
className: 'moa-btn moa-btn-danger moa-btn-sub',
|
|
998
|
+
title: 'Remove',
|
|
729
999
|
onClick: () => removeReference(idx),
|
|
730
1000
|
},
|
|
731
1001
|
'✕'
|
|
@@ -737,180 +1007,128 @@ window.__ModuleLoader__.load({
|
|
|
737
1007
|
'button',
|
|
738
1008
|
{
|
|
739
1009
|
type: 'button',
|
|
740
|
-
className: 'moa-btn
|
|
1010
|
+
className: 'moa-btn',
|
|
1011
|
+
style: { alignSelf: 'flex-start' },
|
|
741
1012
|
onClick: addReference,
|
|
742
1013
|
},
|
|
743
|
-
|
|
1014
|
+
t('proposers.add_btn')
|
|
744
1015
|
),
|
|
745
1016
|
React.createElement(
|
|
746
1017
|
'div',
|
|
747
|
-
{
|
|
1018
|
+
{ style: { display: 'flex', flexDirection: 'column', gap: 6, marginTop: 4, borderTop: '1px solid var(--dsw-alias-border-l2)', paddingTop: 10 } },
|
|
748
1019
|
React.createElement(
|
|
749
1020
|
'div',
|
|
750
|
-
{ style: { display: 'flex',
|
|
751
|
-
React.createElement('span',
|
|
752
|
-
React.createElement('span', { style: {
|
|
1021
|
+
{ style: { display: 'flex', justifyContent: 'space-between', fontSize: 13, fontWeight: 500 } },
|
|
1022
|
+
React.createElement('span', null, t('proposers.temp_label')),
|
|
1023
|
+
React.createElement('span', { style: { color: 'var(--dsw-alias-label-secondary)', fontSize: 12 } }, t('proposers.temp_hint'))
|
|
753
1024
|
),
|
|
754
1025
|
React.createElement(
|
|
755
1026
|
'div',
|
|
756
|
-
{ className: 'moa-
|
|
1027
|
+
{ className: 'moa-slider-row' },
|
|
757
1028
|
React.createElement('input', {
|
|
758
1029
|
type: 'range',
|
|
759
|
-
className: 'moa-
|
|
1030
|
+
className: 'moa-slider',
|
|
760
1031
|
min: '0',
|
|
761
1032
|
max: '2',
|
|
762
1033
|
step: '0.05',
|
|
763
1034
|
value: refTemp,
|
|
764
1035
|
onChange: (e) => setReferenceTemp(e.target.value),
|
|
765
1036
|
}),
|
|
766
|
-
React.createElement('
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
1037
|
+
React.createElement('div', { className: 'moa-slider-badge' }, refTemp)
|
|
1038
|
+
)
|
|
1039
|
+
)
|
|
1040
|
+
),
|
|
1041
|
+
|
|
1042
|
+
/* ── SECTION 4: TELEMETRY & STATS SUMMARY ── */
|
|
1043
|
+
React.createElement(
|
|
1044
|
+
'div',
|
|
1045
|
+
{ className: 'moa-section-card' },
|
|
1046
|
+
React.createElement('div', { className: 'moa-section-title' }, t('stats.title')),
|
|
1047
|
+
React.createElement('div', { className: 'moa-section-desc' }, t('stats.desc')),
|
|
1048
|
+
React.createElement(
|
|
1049
|
+
'div',
|
|
1050
|
+
{ className: 'moa-grid-2' },
|
|
1051
|
+
React.createElement(
|
|
1052
|
+
'div',
|
|
1053
|
+
{ className: 'moa-stat-box' },
|
|
1054
|
+
React.createElement('div', { className: 'moa-stat-val' }, String(presets.length)),
|
|
1055
|
+
React.createElement('div', { className: 'moa-stat-lbl' }, t('stats.preset_count'))
|
|
1056
|
+
),
|
|
1057
|
+
React.createElement(
|
|
1058
|
+
'div',
|
|
1059
|
+
{ className: 'moa-stat-box' },
|
|
1060
|
+
React.createElement('div', { className: 'moa-stat-val' }, (stats.totalRuns === null || stats.totalRuns === undefined) ? '—' : String(stats.totalRuns)),
|
|
1061
|
+
React.createElement('div', { className: 'moa-stat-lbl' }, t('stats.total_runs'))
|
|
775
1062
|
),
|
|
776
|
-
React.createElement(
|
|
1063
|
+
React.createElement(
|
|
1064
|
+
'div',
|
|
1065
|
+
{ className: 'moa-stat-box' },
|
|
1066
|
+
React.createElement('div', { className: 'moa-stat-val' }, (stats.avgCostUsd === null || stats.avgCostUsd === undefined) ? '—' : `~\$${stats.avgCostUsd.toFixed(4)}`),
|
|
1067
|
+
React.createElement('div', { className: 'moa-stat-lbl' }, t('stats.avg_cost'))
|
|
1068
|
+
),
|
|
1069
|
+
React.createElement(
|
|
1070
|
+
'div',
|
|
1071
|
+
{ className: 'moa-stat-box' },
|
|
1072
|
+
React.createElement('div', { className: 'moa-stat-val' }, String(currentRefs.length)),
|
|
1073
|
+
React.createElement('div', { className: 'moa-stat-lbl' }, t('stats.active_models'))
|
|
1074
|
+
)
|
|
777
1075
|
)
|
|
778
1076
|
),
|
|
779
|
-
|
|
1077
|
+
|
|
1078
|
+
/* ── ENABLED TOGGLE ── */
|
|
1079
|
+
React.createElement(
|
|
1080
|
+
'label',
|
|
1081
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 8, cursor: 'pointer', fontSize: 13, fontWeight: 500, alignSelf: 'flex-start' } },
|
|
1082
|
+
React.createElement('input', {
|
|
1083
|
+
type: 'checkbox',
|
|
1084
|
+
checked: enabled !== false,
|
|
1085
|
+
onChange: (e) => setEnabled(e.target.checked),
|
|
1086
|
+
style: { accentColor: 'var(--dsw-alias-state-brand-primary, var(--dsw-alias-label-primary))', cursor: 'pointer' },
|
|
1087
|
+
}),
|
|
1088
|
+
t('config.enabled_label')
|
|
1089
|
+
),
|
|
1090
|
+
|
|
1091
|
+
/* ── FOOTER ACTIONS & SAVE ── */
|
|
780
1092
|
React.createElement(
|
|
781
1093
|
'div',
|
|
782
|
-
{
|
|
783
|
-
saveStatus && React.createElement('span', { className: 'moa-status-text' }, saveStatus),
|
|
1094
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 12, marginTop: 4 } },
|
|
784
1095
|
React.createElement(
|
|
785
1096
|
'button',
|
|
786
1097
|
{
|
|
787
1098
|
type: 'button',
|
|
788
|
-
className: 'moa-
|
|
1099
|
+
className: 'moa-btn moa-btn-primary',
|
|
1100
|
+
style: { padding: '10px 24px', fontSize: 14, fontWeight: 600 },
|
|
789
1101
|
onClick: handleSave,
|
|
790
1102
|
},
|
|
791
|
-
t('save')
|
|
792
|
-
)
|
|
1103
|
+
saveStatus === t('actions.saving') ? t('actions.saving') : t('actions.save')
|
|
1104
|
+
),
|
|
1105
|
+
React.createElement(
|
|
1106
|
+
'button',
|
|
1107
|
+
{
|
|
1108
|
+
type: 'button',
|
|
1109
|
+
className: 'moa-btn',
|
|
1110
|
+
onClick: reload,
|
|
1111
|
+
},
|
|
1112
|
+
t('actions.retry')
|
|
1113
|
+
),
|
|
1114
|
+
saveStatus &&
|
|
1115
|
+
React.createElement(
|
|
1116
|
+
'span',
|
|
1117
|
+
{
|
|
1118
|
+
className: saveStatus.startsWith('Error') ? 'moa-alert-err' : 'moa-alert-ok',
|
|
1119
|
+
},
|
|
1120
|
+
saveStatus
|
|
1121
|
+
)
|
|
793
1122
|
)
|
|
794
1123
|
)
|
|
795
1124
|
}
|
|
796
1125
|
|
|
797
|
-
let rootCtx = null
|
|
798
|
-
|
|
799
|
-
const en = {
|
|
800
|
-
title: 'Mixture of Agents (MoA)',
|
|
801
|
-
description: 'Run tasks with multiple parallel proposer models and a synthesizing judge model',
|
|
802
|
-
slashMoa: 'Run task through Mixture of Agents (/moa <prompt>)',
|
|
803
|
-
defaultPreset: 'Default Preset (used on /moa without arguments)',
|
|
804
|
-
isDefault: 'default',
|
|
805
|
-
presetSelectLabel: 'Active Preset to Configure:',
|
|
806
|
-
createNewPreset: 'Create new preset...',
|
|
807
|
-
makeDefault: 'Make Default',
|
|
808
|
-
makeDefaultHint: 'Use this preset for /moa when no preset argument is given',
|
|
809
|
-
deletePreset: 'Delete preset',
|
|
810
|
-
delete: 'Delete',
|
|
811
|
-
confirmDeletePreset: 'Are you sure you want to delete preset',
|
|
812
|
-
newPresetPlaceholder: 'New preset name (e.g. fast-audit)',
|
|
813
|
-
create: 'Create',
|
|
814
|
-
cancel: 'Cancel',
|
|
815
|
-
aggregatorTitle: 'Judge / Aggregator Model (who reviews and synthesizes)',
|
|
816
|
-
aggregatorDesc: 'Evaluates candidate proposals, corrects hallucinations, and outputs the final unified solution.',
|
|
817
|
-
temperature: 'Temperature',
|
|
818
|
-
aggTempHint: 'Recommended 0.2 - 0.4 for rigorous verification and factual synthesis.',
|
|
819
|
-
proposersTitle: 'Candidate Models (Advisors)',
|
|
820
|
-
proposersDesc: 'Generates independent candidate answers in parallel to provide diverse perspectives.',
|
|
821
|
-
refTempLabel: 'Candidates Temperature (solution diversity):',
|
|
822
|
-
refTempHint: 'Recommended 0.6 - 0.8 for diverse creative hypotheses.',
|
|
823
|
-
parallelCount: 'Parallel workers',
|
|
824
|
-
candidate: 'Candidate',
|
|
825
|
-
addCandidate: 'Add parallel model slot',
|
|
826
|
-
remove: 'Remove',
|
|
827
|
-
save: 'Save Changes',
|
|
828
|
-
saved: 'Saved successfully',
|
|
829
|
-
loading: 'Saving…',
|
|
830
|
-
retry: 'Retry connection',
|
|
831
|
-
unavailable: 'MoA service temporarily unavailable (reconnecting...)',
|
|
832
|
-
searchPlaceholder: 'Search by model name or provider...',
|
|
833
|
-
modelsFound: 'Found',
|
|
834
|
-
of: 'of',
|
|
835
|
-
clear: 'Clear',
|
|
836
|
-
noModelsMatch: 'No models found matching query.',
|
|
837
|
-
useCustom: 'Use custom',
|
|
838
|
-
selectModel: 'Select model...',
|
|
839
|
-
judgeCriteriaLabel: 'Custom Judge Evaluation Criteria (optional):',
|
|
840
|
-
judgeCriteriaPlaceholder: 'e.g. Priority: performance, zero external dependencies, robust edge-case handling...',
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
const ru = {
|
|
844
|
-
title: 'Mixture of Agents (MoA)',
|
|
845
|
-
description: 'Запуск задач через ансамбль параллельных моделей-советников и модель-судью',
|
|
846
|
-
slashMoa: 'Запустить задачу через архитектуру Mixture of Agents (/moa <prompt>)',
|
|
847
|
-
defaultPreset: 'Основной пресет (при вызове /moa без параметров)',
|
|
848
|
-
isDefault: 'основной',
|
|
849
|
-
presetSelectLabel: 'Текущий пресет для настройки:',
|
|
850
|
-
createNewPreset: 'Создать новый пресет...',
|
|
851
|
-
makeDefault: 'Сделать основным',
|
|
852
|
-
makeDefaultHint: 'Использовать этот пресет для /moa при вызове без параметров',
|
|
853
|
-
deletePreset: 'Удалить пресет',
|
|
854
|
-
delete: 'Удалить',
|
|
855
|
-
confirmDeletePreset: 'Вы уверены, что хотите удалить пресет',
|
|
856
|
-
newPresetPlaceholder: 'Название нового пресета (например: fast-audit)',
|
|
857
|
-
create: 'Создать',
|
|
858
|
-
cancel: 'Отмена',
|
|
859
|
-
aggregatorTitle: 'Ведущая модель / Судья (кто проверяет и объединяет)',
|
|
860
|
-
aggregatorDesc: 'Сопоставляет варианты кандидатов, устраняет ошибки и синтезирует итоговое решение.',
|
|
861
|
-
temperature: 'Температура',
|
|
862
|
-
aggTempHint: 'Рекомендуется 0.2 – 0.4 для строгой проверки и синтеза без выдумок.',
|
|
863
|
-
proposersTitle: 'Модели-кандидаты (советники)',
|
|
864
|
-
proposersDesc: 'Параллельно формируют независимые варианты решения одной задачи.',
|
|
865
|
-
refTempLabel: 'Температура кандидатов (разнообразие решений):',
|
|
866
|
-
refTempHint: 'Рекомендуется 0.6 – 0.8 для получения независимых и креативных вариантов.',
|
|
867
|
-
parallelCount: 'Параллельных моделей',
|
|
868
|
-
candidate: 'Кандидат',
|
|
869
|
-
addCandidate: 'Добавить модель в параллель',
|
|
870
|
-
remove: 'Удалить',
|
|
871
|
-
save: 'Сохранить',
|
|
872
|
-
saved: 'Успешно сохранено',
|
|
873
|
-
loading: 'Сохранение…',
|
|
874
|
-
retry: 'Повторить подключение',
|
|
875
|
-
unavailable: 'Служба MoA временно недоступна (переподключение...)',
|
|
876
|
-
searchPlaceholder: 'Поиск модели по названию или провайдеру...',
|
|
877
|
-
modelsFound: 'Найдено',
|
|
878
|
-
of: 'из',
|
|
879
|
-
clear: 'Сбросить',
|
|
880
|
-
noModelsMatch: 'Модели не найдены.',
|
|
881
|
-
useCustom: 'Использовать кастомное',
|
|
882
|
-
selectModel: 'Выберите модель...',
|
|
883
|
-
judgeCriteriaLabel: 'Критерии оценки судьи (опционально):',
|
|
884
|
-
judgeCriteriaPlaceholder: 'например: Приоритет — производительность, минимум зависимостей, обработка граничных случаев...',
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
function makeT(ctx, props) {
|
|
888
|
-
return (key) => {
|
|
889
|
-
if (props && typeof props.t === 'function') {
|
|
890
|
-
try {
|
|
891
|
-
const res = props.t(key)
|
|
892
|
-
if (res && res !== key) return res
|
|
893
|
-
} catch (_) {}
|
|
894
|
-
}
|
|
895
|
-
const effectiveCtx = (props && props.ctx) || ctx || rootCtx
|
|
896
|
-
if (effectiveCtx && effectiveCtx.locale && typeof effectiveCtx.locale.bind === 'function') {
|
|
897
|
-
try {
|
|
898
|
-
const bound = effectiveCtx.locale.bind(NS)
|
|
899
|
-
const res = bound(key)
|
|
900
|
-
if (res && res !== key) return res
|
|
901
|
-
} catch (_) {}
|
|
902
|
-
}
|
|
903
|
-
const curLocale = (effectiveCtx && effectiveCtx.locale && effectiveCtx.locale.getSnapshot && effectiveCtx.locale.getSnapshot().active) || 'ru'
|
|
904
|
-
const dict = curLocale === 'en' ? en : ru
|
|
905
|
-
return dict[key] || ru[key] || en[key] || key
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
|
|
909
1126
|
function useMoASettings(props) {
|
|
910
|
-
const t = makeT(rootCtx, props)
|
|
911
1127
|
const effectiveCtx = (props && props.ctx) || rootCtx
|
|
1128
|
+
// English is canonical; prefer the core-provided translator for the
|
|
1129
|
+
// active UI language (the slot declares locale: NS), fall back to en.
|
|
1130
|
+
const t = typeof props?.t === 'function' ? props.t : makeT(en, null)
|
|
912
1131
|
|
|
913
|
-
// Reactive binding to settingsScope if available
|
|
914
1132
|
const scope = React.useMemo(() => {
|
|
915
1133
|
if (!effectiveCtx) return undefined
|
|
916
1134
|
const s = effectiveCtx.get ? effectiveCtx.get('settingsScope') : effectiveCtx.settingsScope
|
|
@@ -930,8 +1148,10 @@ window.__ModuleLoader__.load({
|
|
|
930
1148
|
const [defaultPreset, setDefaultPreset] = React.useState('default')
|
|
931
1149
|
const [availableModels, setAvailableModels] = React.useState([])
|
|
932
1150
|
const [saveStatus, setSaveStatus] = React.useState('')
|
|
1151
|
+
const [enabled, setEnabled] = React.useState(true)
|
|
1152
|
+
const [hostStatus, setHostStatus] = React.useState('connecting')
|
|
1153
|
+
const [stats, setStats] = React.useState({ totalRuns: null, avgCostUsd: null })
|
|
933
1154
|
|
|
934
|
-
// Sync from settingsScope snapshot when available
|
|
935
1155
|
React.useEffect(() => {
|
|
936
1156
|
if (!scope || !snapshot) return
|
|
937
1157
|
const snapStatus = snapshot.status || 'ready'
|
|
@@ -939,6 +1159,7 @@ window.__ModuleLoader__.load({
|
|
|
939
1159
|
const val = snapshot.value || {}
|
|
940
1160
|
if (Array.isArray(val.presets)) setPresets(val.presets)
|
|
941
1161
|
if (val.default_preset) setDefaultPreset(val.default_preset)
|
|
1162
|
+
if (typeof val.enabled === 'boolean') setEnabled(val.enabled)
|
|
942
1163
|
setStatus('ready')
|
|
943
1164
|
} else if (snapStatus === 'unavailable') {
|
|
944
1165
|
setStatus('unavailable')
|
|
@@ -948,6 +1169,35 @@ window.__ModuleLoader__.load({
|
|
|
948
1169
|
const reload = React.useCallback(() => {
|
|
949
1170
|
if (!scope) setStatus('loading')
|
|
950
1171
|
|
|
1172
|
+
fetch('/dsh-moa/status', { cache: 'no-store' })
|
|
1173
|
+
.then((r) => r.json())
|
|
1174
|
+
.then((data) => {
|
|
1175
|
+
if (data && data.ok) {
|
|
1176
|
+
setHostStatus(data.enabled === false ? 'disabled' : 'online')
|
|
1177
|
+
} else {
|
|
1178
|
+
setHostStatus('offline')
|
|
1179
|
+
}
|
|
1180
|
+
})
|
|
1181
|
+
.catch(() => {
|
|
1182
|
+
setHostStatus('offline')
|
|
1183
|
+
})
|
|
1184
|
+
|
|
1185
|
+
fetch('/dsh-moa/history?limit=100', { cache: 'no-store' })
|
|
1186
|
+
.then((r) => r.json())
|
|
1187
|
+
.then((data) => {
|
|
1188
|
+
if (data && data.ok && Array.isArray(data.runs)) {
|
|
1189
|
+
const runs = data.runs
|
|
1190
|
+
const withCost = runs.filter((run) => typeof run.totalCostUsd === 'number')
|
|
1191
|
+
const avgCostUsd = withCost.length > 0
|
|
1192
|
+
? withCost.reduce((acc, run) => acc + run.totalCostUsd, 0) / withCost.length
|
|
1193
|
+
: null
|
|
1194
|
+
setStats({ totalRuns: data.total || runs.length, avgCostUsd })
|
|
1195
|
+
}
|
|
1196
|
+
})
|
|
1197
|
+
.catch((err) => {
|
|
1198
|
+
console.warn('[dsh-moa] History fetch failed:', err)
|
|
1199
|
+
})
|
|
1200
|
+
|
|
951
1201
|
fetch('/dsh-moa/presets', { cache: 'no-store' })
|
|
952
1202
|
.then((r) => {
|
|
953
1203
|
if (!r.ok) throw new Error(`HTTP ${r.status}`)
|
|
@@ -984,16 +1234,17 @@ window.__ModuleLoader__.load({
|
|
|
984
1234
|
}, [reload])
|
|
985
1235
|
|
|
986
1236
|
const handleSave = () => {
|
|
987
|
-
setSaveStatus(t('
|
|
1237
|
+
setSaveStatus(t('actions.saving'))
|
|
988
1238
|
|
|
989
1239
|
if (scope && typeof scope.set === 'function') {
|
|
990
1240
|
Promise.all([
|
|
1241
|
+
scope.set('enabled', enabled),
|
|
991
1242
|
scope.set('presets', presets),
|
|
992
1243
|
scope.set('default_preset', defaultPreset),
|
|
993
1244
|
])
|
|
994
1245
|
.then(() => {
|
|
995
|
-
setSaveStatus(t('saved'))
|
|
996
|
-
setTimeout(() => setSaveStatus(''),
|
|
1246
|
+
setSaveStatus(t('actions.saved'))
|
|
1247
|
+
setTimeout(() => setSaveStatus(''), 3000)
|
|
997
1248
|
})
|
|
998
1249
|
.catch((err) => {
|
|
999
1250
|
console.warn('[dsh-moa] scope.set failed, trying REST fallback:', err)
|
|
@@ -1009,15 +1260,15 @@ window.__ModuleLoader__.load({
|
|
|
1009
1260
|
fetch('/dsh-moa/presets', {
|
|
1010
1261
|
method: 'POST',
|
|
1011
1262
|
headers: { 'Content-Type': 'application/json' },
|
|
1012
|
-
body: JSON.stringify({ presets, default_preset: defaultPreset }),
|
|
1263
|
+
body: JSON.stringify({ enabled, presets, default_preset: defaultPreset }),
|
|
1013
1264
|
})
|
|
1014
1265
|
.then((r) => r.json())
|
|
1015
1266
|
.then((res) => {
|
|
1016
1267
|
if (res && res.ok) {
|
|
1017
|
-
setSaveStatus(t('saved'))
|
|
1268
|
+
setSaveStatus(t('actions.saved'))
|
|
1018
1269
|
if (Array.isArray(res.presets)) setPresets(res.presets)
|
|
1019
1270
|
if (res.defaultPreset) setDefaultPreset(res.defaultPreset)
|
|
1020
|
-
setTimeout(() => setSaveStatus(''),
|
|
1271
|
+
setTimeout(() => setSaveStatus(''), 3000)
|
|
1021
1272
|
} else {
|
|
1022
1273
|
setSaveStatus('Error saving: ' + (res?.error || 'failed'))
|
|
1023
1274
|
}
|
|
@@ -1035,92 +1286,44 @@ window.__ModuleLoader__.load({
|
|
|
1035
1286
|
defaultPreset,
|
|
1036
1287
|
setDefaultPreset,
|
|
1037
1288
|
availableModels,
|
|
1289
|
+
enabled,
|
|
1290
|
+
setEnabled,
|
|
1291
|
+
hostStatus,
|
|
1292
|
+
stats,
|
|
1038
1293
|
handleSave,
|
|
1039
1294
|
saveStatus,
|
|
1040
1295
|
reload,
|
|
1041
1296
|
}
|
|
1042
1297
|
}
|
|
1043
1298
|
|
|
1044
|
-
function MoASection(props) {
|
|
1045
|
-
ensureStyles()
|
|
1046
|
-
const state = useMoASettings(props)
|
|
1047
|
-
|
|
1048
|
-
return React.createElement(
|
|
1049
|
-
'div',
|
|
1050
|
-
{ className: 'moa-section' },
|
|
1051
|
-
React.createElement('div', { className: 'moa-section-title' }, state.t('title')),
|
|
1052
|
-
React.createElement('div', { className: 'moa-section-sub' }, state.t('description')),
|
|
1053
|
-
state.status === 'loading'
|
|
1054
|
-
? React.createElement('div', { className: 'moa-hint' }, state.t('loading'))
|
|
1055
|
-
: state.status === 'unavailable'
|
|
1056
|
-
? React.createElement(
|
|
1057
|
-
'div',
|
|
1058
|
-
{ className: 'moa-field' },
|
|
1059
|
-
React.createElement('div', { className: 'moa-hint' }, state.t('unavailable')),
|
|
1060
|
-
React.createElement(
|
|
1061
|
-
'button',
|
|
1062
|
-
{
|
|
1063
|
-
type: 'button',
|
|
1064
|
-
className: 'moa-count-btn',
|
|
1065
|
-
style: { width: 'fit-content', marginTop: 8 },
|
|
1066
|
-
onClick: state.reload,
|
|
1067
|
-
},
|
|
1068
|
-
state.t('retry')
|
|
1069
|
-
)
|
|
1070
|
-
)
|
|
1071
|
-
: React.createElement(MoAEditor, state)
|
|
1072
|
-
)
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
// ── ERROR BOUNDARY ──────────────────────────────────────────────
|
|
1077
1299
|
class MoAErrorBoundary extends React.Component {
|
|
1078
1300
|
constructor(props) {
|
|
1079
1301
|
super(props)
|
|
1080
|
-
this.state = { hasError: false, error: null
|
|
1302
|
+
this.state = { hasError: false, error: null }
|
|
1081
1303
|
}
|
|
1082
1304
|
static getDerivedStateFromError(error) {
|
|
1083
1305
|
return { hasError: true, error }
|
|
1084
1306
|
}
|
|
1085
1307
|
componentDidCatch(error, errorInfo) {
|
|
1086
1308
|
console.error('[dsh-moa] MoAErrorBoundary caught error:', error, errorInfo)
|
|
1087
|
-
this.setState({ errorInfo })
|
|
1088
1309
|
}
|
|
1089
1310
|
render() {
|
|
1090
1311
|
if (this.state.hasError) {
|
|
1091
1312
|
return React.createElement(
|
|
1092
1313
|
'div',
|
|
1093
1314
|
{
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
borderRadius: '8px',
|
|
1097
|
-
border: '1px solid #ef4444',
|
|
1098
|
-
background: 'rgba(239,68,68,0.08)',
|
|
1099
|
-
color: '#ef4444',
|
|
1100
|
-
fontSize: '13px',
|
|
1101
|
-
fontFamily: 'monospace',
|
|
1102
|
-
whiteSpace: 'pre-wrap',
|
|
1103
|
-
wordBreak: 'break-word',
|
|
1104
|
-
},
|
|
1315
|
+
className: 'moa-alert-err',
|
|
1316
|
+
style: { margin: '12px 0', padding: '14px', borderRadius: '8px' },
|
|
1105
1317
|
},
|
|
1106
|
-
React.createElement('div', { style: { fontWeight:
|
|
1107
|
-
React.createElement('div',
|
|
1108
|
-
this.state.errorInfo && React.createElement(
|
|
1109
|
-
'details',
|
|
1110
|
-
{ style: { marginTop: 8 } },
|
|
1111
|
-
React.createElement('summary', null, 'Component stack'),
|
|
1112
|
-
React.createElement('pre', { style: { fontSize: 11 } }, this.state.errorInfo.componentStack || '')
|
|
1113
|
-
),
|
|
1318
|
+
React.createElement('div', { style: { fontWeight: 600, marginBottom: '6px' } }, '⚠️ MoA UI Error:'),
|
|
1319
|
+
React.createElement('div', { style: { fontSize: '12px', wordBreak: 'break-all' } }, String(this.state.error?.message || this.state.error)),
|
|
1114
1320
|
React.createElement(
|
|
1115
1321
|
'button',
|
|
1116
1322
|
{
|
|
1117
1323
|
type: 'button',
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
cursor: 'pointer', fontSize: 12,
|
|
1122
|
-
},
|
|
1123
|
-
onClick: () => this.setState({ hasError: false, error: null, errorInfo: null }),
|
|
1324
|
+
className: 'moa-btn',
|
|
1325
|
+
style: { marginTop: '10px', fontSize: '12px', padding: '4px 10px' },
|
|
1326
|
+
onClick: () => this.setState({ hasError: false, error: null }),
|
|
1124
1327
|
},
|
|
1125
1328
|
'Retry'
|
|
1126
1329
|
)
|
|
@@ -1149,8 +1352,8 @@ window.__ModuleLoader__.load({
|
|
|
1149
1352
|
React.createElement(
|
|
1150
1353
|
'div',
|
|
1151
1354
|
null,
|
|
1152
|
-
React.createElement('div', { className: 'moa-title' }, state.t('title')),
|
|
1153
|
-
React.createElement('div', { className: 'moa-sub' }, state.t('description'))
|
|
1355
|
+
React.createElement('div', { className: 'moa-title' }, '🧠 ' + (state.t('title') || TITLE)),
|
|
1356
|
+
React.createElement('div', { className: 'moa-sub' }, state.t('description') || SUBTITLE)
|
|
1154
1357
|
),
|
|
1155
1358
|
React.createElement(Chevron, {
|
|
1156
1359
|
className: open ? 'moa-chev moa-chev-open' : 'moa-chev',
|
|
@@ -1161,21 +1364,21 @@ window.__ModuleLoader__.load({
|
|
|
1161
1364
|
'div',
|
|
1162
1365
|
{ className: 'moa-body' },
|
|
1163
1366
|
state.status === 'loading'
|
|
1164
|
-
? React.createElement('div', {
|
|
1367
|
+
? React.createElement('div', { style: { padding: '16px 0', color: 'var(--dsw-alias-label-secondary)', fontSize: 13 } }, 'Loading MoA settings…')
|
|
1165
1368
|
: state.status === 'unavailable'
|
|
1166
1369
|
? React.createElement(
|
|
1167
1370
|
'div',
|
|
1168
|
-
{
|
|
1169
|
-
React.createElement('div', { className: 'moa-
|
|
1371
|
+
{ style: { padding: '16px 0', display: 'flex', flexDirection: 'column', gap: 10 } },
|
|
1372
|
+
React.createElement('div', { className: 'moa-alert-err' }, 'MoA service temporarily unavailable'),
|
|
1170
1373
|
React.createElement(
|
|
1171
1374
|
'button',
|
|
1172
1375
|
{
|
|
1173
1376
|
type: 'button',
|
|
1174
|
-
className: 'moa-
|
|
1175
|
-
style: { width: 'fit-content'
|
|
1377
|
+
className: 'moa-btn',
|
|
1378
|
+
style: { width: 'fit-content' },
|
|
1176
1379
|
onClick: state.reload,
|
|
1177
1380
|
},
|
|
1178
|
-
state.t('retry')
|
|
1381
|
+
state.t('actions.retry')
|
|
1179
1382
|
)
|
|
1180
1383
|
)
|
|
1181
1384
|
: React.createElement(MoAEditor, state)
|
|
@@ -1186,14 +1389,13 @@ window.__ModuleLoader__.load({
|
|
|
1186
1389
|
exports.inject = ['slots', 'locale', 'inputTriggers', 'settingsScope']
|
|
1187
1390
|
exports.apply = function apply(ctx) {
|
|
1188
1391
|
rootCtx = ctx
|
|
1189
|
-
|
|
1392
|
+
|
|
1190
1393
|
const addLocale = (locale, dictionary) => {
|
|
1191
1394
|
try {
|
|
1192
1395
|
if (ctx.locale && typeof ctx.locale.register === 'function') {
|
|
1193
1396
|
return ctx.locale.register(NS, locale, dictionary)
|
|
1194
1397
|
}
|
|
1195
1398
|
} catch {
|
|
1196
|
-
// Если язык уже занят (например, dsh-russian-lang) или зарегистрирован ранее — уступаем без ошибки
|
|
1197
1399
|
return () => {}
|
|
1198
1400
|
}
|
|
1199
1401
|
return () => {}
|
|
@@ -1201,7 +1403,7 @@ window.__ModuleLoader__.load({
|
|
|
1201
1403
|
|
|
1202
1404
|
if (typeof ctx.effect === 'function') {
|
|
1203
1405
|
ctx.effect(() => {
|
|
1204
|
-
const undo = [addLocale('en', en)
|
|
1406
|
+
const undo = [addLocale('en', en)]
|
|
1205
1407
|
return () => {
|
|
1206
1408
|
for (const off of undo) {
|
|
1207
1409
|
if (typeof off === 'function') {
|
|
@@ -1212,10 +1414,8 @@ window.__ModuleLoader__.load({
|
|
|
1212
1414
|
}, 'dsh-moa: dictionaries')
|
|
1213
1415
|
} else {
|
|
1214
1416
|
addLocale('en', en)
|
|
1215
|
-
addLocale('ru', ru)
|
|
1216
1417
|
}
|
|
1217
1418
|
|
|
1218
|
-
|
|
1219
1419
|
if (ctx.slots) {
|
|
1220
1420
|
const registerCard = () => {
|
|
1221
1421
|
try {
|
|
@@ -1250,7 +1450,7 @@ window.__ModuleLoader__.load({
|
|
|
1250
1450
|
trigger: '/',
|
|
1251
1451
|
name: 'moa',
|
|
1252
1452
|
order: 35,
|
|
1253
|
-
description:
|
|
1453
|
+
description: en['slash.desc'],
|
|
1254
1454
|
candidates: (_session, req) => {
|
|
1255
1455
|
if (req.position !== 'leading') return Promise.resolve([])
|
|
1256
1456
|
const q = req.query.trim().toLowerCase()
|
|
@@ -1258,7 +1458,7 @@ window.__ModuleLoader__.load({
|
|
|
1258
1458
|
return Promise.resolve([
|
|
1259
1459
|
{
|
|
1260
1460
|
name: 'moa',
|
|
1261
|
-
description:
|
|
1461
|
+
description: en['slash.desc'],
|
|
1262
1462
|
},
|
|
1263
1463
|
])
|
|
1264
1464
|
},
|
|
@@ -1283,3 +1483,4 @@ window.__ModuleLoader__.load({
|
|
|
1283
1483
|
return module.exports
|
|
1284
1484
|
},
|
|
1285
1485
|
})
|
|
1486
|
+
|