privateboard 0.1.9 → 0.1.11
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/dist/cli.js +3732 -2160
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/public/agent-overlay.js +59 -36
- package/public/agent-profile.css +392 -100
- package/public/agent-profile.js +551 -59
- package/public/app.js +1341 -681
- package/public/i18n.js +1990 -0
- package/public/index.html +251 -94
- package/public/keys-store.js +63 -0
- package/public/new-agent.css +60 -0
- package/public/new-agent.js +121 -53
- package/public/room-settings.js +2 -1
- package/public/user-settings.css +68 -0
- package/public/user-settings.js +241 -87
- package/public/voice-replay.css +607 -0
- package/public/voice-replay.js +533 -0
package/public/new-agent.css
CHANGED
|
@@ -1193,6 +1193,66 @@
|
|
|
1193
1193
|
cursor: progress;
|
|
1194
1194
|
}
|
|
1195
1195
|
.na-avatar-regen-mark { font-size: 11px; color: var(--lime); line-height: 1; }
|
|
1196
|
+
/* Model picker chip · sits in the avatar column right under the
|
|
1197
|
+
regen button, so the user sees the active model the moment they
|
|
1198
|
+
open the modal (the foot-bottom placement was almost invisible).
|
|
1199
|
+
Sized to match .na-avatar-regen so the two reads as a paired
|
|
1200
|
+
control set: regen the face, switch the model. Same .cmp-dd
|
|
1201
|
+
markup as the AI composer's chip — these overrides only adjust
|
|
1202
|
+
the surface fit (padding / type scale / alignment). */
|
|
1203
|
+
.new-agent-overlay .na-avatar-model {
|
|
1204
|
+
display: inline-flex;
|
|
1205
|
+
align-items: center;
|
|
1206
|
+
gap: 8px;
|
|
1207
|
+
padding: 7px 12px;
|
|
1208
|
+
font-size: 10px;
|
|
1209
|
+
letter-spacing: 0.14em;
|
|
1210
|
+
text-transform: uppercase;
|
|
1211
|
+
border: 0.5px solid var(--line-bright);
|
|
1212
|
+
background: transparent;
|
|
1213
|
+
color: var(--text-soft);
|
|
1214
|
+
cursor: pointer;
|
|
1215
|
+
transition: border-color 0.12s, color 0.12s, background 0.12s;
|
|
1216
|
+
max-width: 220px;
|
|
1217
|
+
}
|
|
1218
|
+
.new-agent-overlay .na-avatar-model:hover {
|
|
1219
|
+
border-color: var(--lime);
|
|
1220
|
+
color: var(--lime);
|
|
1221
|
+
}
|
|
1222
|
+
.new-agent-overlay .na-avatar-model.open {
|
|
1223
|
+
border-color: var(--lime);
|
|
1224
|
+
color: var(--lime);
|
|
1225
|
+
background: var(--bg);
|
|
1226
|
+
}
|
|
1227
|
+
.new-agent-overlay .na-avatar-model .cmp-dd-label {
|
|
1228
|
+
color: var(--text-faint);
|
|
1229
|
+
font-weight: 500;
|
|
1230
|
+
letter-spacing: 0.14em;
|
|
1231
|
+
}
|
|
1232
|
+
.new-agent-overlay .na-avatar-model .cmp-dd-value {
|
|
1233
|
+
color: var(--text);
|
|
1234
|
+
font-weight: 700;
|
|
1235
|
+
letter-spacing: 0.04em;
|
|
1236
|
+
text-transform: none;
|
|
1237
|
+
font-size: 11px;
|
|
1238
|
+
/* Long model names (e.g. "Claude Opus 4.7 · 1M context") truncate
|
|
1239
|
+
instead of wrapping the chip into a second line. */
|
|
1240
|
+
white-space: nowrap;
|
|
1241
|
+
overflow: hidden;
|
|
1242
|
+
text-overflow: ellipsis;
|
|
1243
|
+
max-width: 140px;
|
|
1244
|
+
}
|
|
1245
|
+
.new-agent-overlay .na-avatar-model.open .cmp-dd-value { color: var(--lime); }
|
|
1246
|
+
.new-agent-overlay .na-avatar-model .cmp-dd-chevron {
|
|
1247
|
+
color: var(--text-faint);
|
|
1248
|
+
font-size: 9px;
|
|
1249
|
+
line-height: 1;
|
|
1250
|
+
transition: transform 0.12s;
|
|
1251
|
+
}
|
|
1252
|
+
.new-agent-overlay .na-avatar-model.open .cmp-dd-chevron {
|
|
1253
|
+
color: var(--lime);
|
|
1254
|
+
transform: rotate(180deg);
|
|
1255
|
+
}
|
|
1196
1256
|
/* Vibe caption · the LLM's one-line description that seeded the avatar. */
|
|
1197
1257
|
.na-avatar-vibe {
|
|
1198
1258
|
font-family: var(--mono);
|
package/public/new-agent.js
CHANGED
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
{ provider: "xai", models: [
|
|
28
28
|
{ v: "grok-4-3", name: "Grok 4.3", deck: "flagship · 1M ctx" },
|
|
29
29
|
{ v: "grok-4-1-fast", name: "Grok 4.1 Fast", deck: "fast · 256k ctx" }
|
|
30
|
+
]},
|
|
31
|
+
{ provider: "deepseek", models: [
|
|
32
|
+
{ v: "deepseek-v4-pro", name: "DeepSeek V4 Pro", deck: "reasoning · open weights" },
|
|
33
|
+
{ v: "deepseek-v4-flash", name: "DeepSeek Lite", deck: "V4 Flash · fast · 1M ctx" }
|
|
30
34
|
]}
|
|
31
35
|
];
|
|
32
36
|
const ALL_MODELS = MODEL_GROUPS.flatMap((g) => g.models);
|
|
@@ -55,6 +59,34 @@
|
|
|
55
59
|
}[c]));
|
|
56
60
|
}
|
|
57
61
|
|
|
62
|
+
function t(k, vars) {
|
|
63
|
+
return (window.I18n && window.I18n.t(k, vars)) || k;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function applyNewAgentI18n() {
|
|
67
|
+
if (!modal) return;
|
|
68
|
+
modal.querySelectorAll("[data-i18n-na-html]").forEach((el) => {
|
|
69
|
+
const key = el.getAttribute("data-i18n-na-html");
|
|
70
|
+
if (key) el.innerHTML = t(key);
|
|
71
|
+
});
|
|
72
|
+
modal.querySelectorAll("[data-i18n-na]").forEach((el) => {
|
|
73
|
+
const key = el.getAttribute("data-i18n-na");
|
|
74
|
+
if (key && !el.hasAttribute("data-i18n-na-html")) el.textContent = t(key);
|
|
75
|
+
});
|
|
76
|
+
modal.querySelectorAll("[data-i18n-na-placeholder]").forEach((el) => {
|
|
77
|
+
const key = el.getAttribute("data-i18n-na-placeholder");
|
|
78
|
+
if (key) el.setAttribute("placeholder", t(key));
|
|
79
|
+
});
|
|
80
|
+
modal.querySelectorAll("[data-i18n-na-title]").forEach((el) => {
|
|
81
|
+
const key = el.getAttribute("data-i18n-na-title");
|
|
82
|
+
if (key) el.setAttribute("title", t(key));
|
|
83
|
+
});
|
|
84
|
+
modal.querySelectorAll("[data-i18n-na-aria]").forEach((el) => {
|
|
85
|
+
const key = el.getAttribute("data-i18n-na-aria");
|
|
86
|
+
if (key) el.setAttribute("aria-label", t(key));
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
58
90
|
/* ───── 8-bit avatar generator ─────
|
|
59
91
|
Pure function: same seed → same avatar.
|
|
60
92
|
placeholder=true returns a neutral grey silhouette.
|
|
@@ -122,16 +154,16 @@
|
|
|
122
154
|
<div class="new-agent-overlay" id="new-agent-overlay" role="dialog" aria-modal="true" aria-hidden="true">
|
|
123
155
|
<div class="new-agent-modal" role="document">
|
|
124
156
|
<div class="na-classification">
|
|
125
|
-
<span><span class="dot">●</span>
|
|
126
|
-
<span class="right"
|
|
157
|
+
<span><span class="dot">●</span> <span data-i18n-na="na_class_left"></span></span>
|
|
158
|
+
<span class="right" data-i18n-na="na_class_right"></span>
|
|
127
159
|
</div>
|
|
128
160
|
|
|
129
161
|
<header class="na-head">
|
|
130
162
|
<div>
|
|
131
|
-
<div class="na-step-num"
|
|
132
|
-
<div class="na-step-title"
|
|
163
|
+
<div class="na-step-num" data-i18n-na-html="na_step_kicker_html"></div>
|
|
164
|
+
<div class="na-step-title" data-i18n-na="na_step_title"></div>
|
|
133
165
|
</div>
|
|
134
|
-
<button type="button" class="na-close" aria-label="
|
|
166
|
+
<button type="button" class="na-close" data-i18n-na-aria="na_close_aria" aria-label="">✕</button>
|
|
135
167
|
</header>
|
|
136
168
|
|
|
137
169
|
<div class="na-body">
|
|
@@ -143,7 +175,23 @@
|
|
|
143
175
|
</div>
|
|
144
176
|
<button type="button" class="na-avatar-regen" data-na-regen>
|
|
145
177
|
<span class="na-avatar-regen-mark">◆</span>
|
|
146
|
-
<span class="na-avatar-regen-label"
|
|
178
|
+
<span class="na-avatar-regen-label" data-i18n-na="na_avatar_regen"></span>
|
|
179
|
+
</button>
|
|
180
|
+
<!-- Model picker · reuses the cmp-dd chip + dropdown
|
|
181
|
+
machinery from the AI agent composer's model
|
|
182
|
+
dropdown. Reads + writes app.loadAgentComposerModel
|
|
183
|
+
so the user's last-picked model is shared across
|
|
184
|
+
both flows. Sized up via .na-avatar-model in
|
|
185
|
+
new-agent.css to match the regen button's visual
|
|
186
|
+
weight (the toolbar-sized chip is too small to
|
|
187
|
+
notice in a full-screen modal). The popover is
|
|
188
|
+
opened by app.openComposerDropdown via the
|
|
189
|
+
document-level [data-cmp-dropdown] click delegate
|
|
190
|
+
in app.js. -->
|
|
191
|
+
<button type="button" class="cmp-dd na-avatar-model" data-cmp-dropdown="agent-model" data-i18n-na-title="na_field_model">
|
|
192
|
+
<span class="cmp-dd-label" data-i18n-na="na_field_model"></span>
|
|
193
|
+
<span class="cmp-dd-value" data-cmp-dd-value="agent-model"></span>
|
|
194
|
+
<span class="cmp-dd-chevron">▾</span>
|
|
147
195
|
</button>
|
|
148
196
|
<div class="na-avatar-vibe" data-na-vibe></div>
|
|
149
197
|
</div>
|
|
@@ -152,42 +200,35 @@
|
|
|
152
200
|
|
|
153
201
|
<div class="na-field">
|
|
154
202
|
<label class="na-field-label">
|
|
155
|
-
<span
|
|
203
|
+
<span data-i18n-na="na_field_name"></span>
|
|
156
204
|
<span class="na-field-meta"><span class="na-name-count">0</span>/32</span>
|
|
157
205
|
</label>
|
|
158
206
|
<div class="na-input-wrap na-name-wrap">
|
|
159
|
-
<input type="text" class="na-name-input" placeholder="
|
|
207
|
+
<input type="text" class="na-name-input" data-i18n-na-placeholder="na_name_ph" placeholder="" maxlength="32">
|
|
160
208
|
</div>
|
|
161
|
-
<div class="na-field-hint">
|
|
209
|
+
<div class="na-field-hint"><span data-i18n-na="na_hint_handle"></span> <span class="na-handle-preview">/new_agent</span></div>
|
|
162
210
|
</div>
|
|
163
211
|
|
|
164
212
|
<div class="na-field">
|
|
165
213
|
<label class="na-field-label">
|
|
166
|
-
<span
|
|
214
|
+
<span data-i18n-na="na_field_intro"></span>
|
|
167
215
|
<span class="na-field-meta"><span class="na-desc-count">0</span>/280</span>
|
|
168
216
|
</label>
|
|
169
217
|
<div class="na-textarea-wrap intro">
|
|
170
|
-
<textarea class="na-desc-input" placeholder="
|
|
218
|
+
<textarea class="na-desc-input" data-i18n-na-placeholder="na_intro_ph" placeholder="" maxlength="280"></textarea>
|
|
171
219
|
</div>
|
|
172
|
-
<div class="na-field-hint"
|
|
220
|
+
<div class="na-field-hint" data-i18n-na="na_hint_bio"></div>
|
|
173
221
|
</div>
|
|
174
222
|
|
|
175
223
|
<div class="na-field">
|
|
176
224
|
<label class="na-field-label">
|
|
177
|
-
<span
|
|
178
|
-
<span class="na-field-meta"><span class="na-instr-count">0</span>
|
|
225
|
+
<span data-i18n-na="na_field_instruction"></span>
|
|
226
|
+
<span class="na-field-meta"><span class="na-instr-count">0</span> · <span data-i18n-na="na_instr_meta"></span></span>
|
|
179
227
|
</label>
|
|
180
228
|
<div class="na-textarea-wrap tall">
|
|
181
|
-
<textarea class="na-instr-input" spellcheck="false" placeholder="
|
|
182
|
-
You are __, the room's __. Your job is to ___.
|
|
183
|
-
|
|
184
|
-
### Voice
|
|
185
|
-
Demand ___. Don't ___. Cite ___ when ___.
|
|
186
|
-
|
|
187
|
-
### Boundaries
|
|
188
|
-
When the room ___, raise an objection."></textarea>
|
|
229
|
+
<textarea class="na-instr-input" spellcheck="false" data-i18n-na-placeholder="na_instr_ph" placeholder=""></textarea>
|
|
189
230
|
</div>
|
|
190
|
-
<div class="na-field-hint"
|
|
231
|
+
<div class="na-field-hint" data-i18n-na="na_hint_instr"></div>
|
|
191
232
|
</div>
|
|
192
233
|
|
|
193
234
|
</div>
|
|
@@ -195,12 +236,12 @@ When the room ___, raise an objection."></textarea>
|
|
|
195
236
|
</div>
|
|
196
237
|
|
|
197
238
|
<footer class="na-foot">
|
|
198
|
-
<div class="na-foot-meta"
|
|
239
|
+
<div class="na-foot-meta" data-i18n-na="na_foot_meta"></div>
|
|
199
240
|
<div class="na-foot-actions">
|
|
200
|
-
<button type="button" class="na-cancel"
|
|
241
|
+
<button type="button" class="na-cancel" data-i18n-na="na_cancel"></button>
|
|
201
242
|
<button type="button" class="na-create" disabled>
|
|
202
243
|
<span class="na-create-mark">◆</span>
|
|
203
|
-
<span
|
|
244
|
+
<span data-i18n-na="na_create"></span>
|
|
204
245
|
</button>
|
|
205
246
|
</div>
|
|
206
247
|
</footer>
|
|
@@ -229,13 +270,13 @@ When the room ___, raise an objection."></textarea>
|
|
|
229
270
|
const addBtn = modal && modal.querySelector("[data-na-rule-add]");
|
|
230
271
|
if (!list) return;
|
|
231
272
|
if (rulesState.length === 0) {
|
|
232
|
-
list.innerHTML = `<li class="na-rule-empty"
|
|
273
|
+
list.innerHTML = `<li class="na-rule-empty">${escape(t("na_rule_empty"))}</li>`;
|
|
233
274
|
} else {
|
|
234
275
|
list.innerHTML = rulesState.map((body, i) => `
|
|
235
276
|
<li class="na-rule" data-rule-idx="${i}">
|
|
236
277
|
<span class="na-rule-num">${i + 1}</span>
|
|
237
|
-
<input type="text" class="na-rule-input" placeholder="
|
|
238
|
-
<button type="button" class="na-rule-rm" data-na-rule-rm="${i}" title="
|
|
278
|
+
<input type="text" class="na-rule-input" placeholder="${escape(t("na_rule_ph"))}" maxlength="120" value="${escape(body)}">
|
|
279
|
+
<button type="button" class="na-rule-rm" data-na-rule-rm="${i}" title="${escape(t("na_rule_rm"))}">✕</button>
|
|
239
280
|
</li>
|
|
240
281
|
`).join("");
|
|
241
282
|
}
|
|
@@ -282,9 +323,9 @@ When the room ___, raise an objection."></textarea>
|
|
|
282
323
|
`);
|
|
283
324
|
} else {
|
|
284
325
|
slots.push(`
|
|
285
|
-
<button type="button" class="na-skill-slot empty" data-na-skill-slot="${i}" title="
|
|
326
|
+
<button type="button" class="na-skill-slot empty" data-na-skill-slot="${i}" title="${escape(t("na_skill_install"))}">
|
|
286
327
|
<span class="na-skill-icon">+</span>
|
|
287
|
-
<span class="na-skill-name"
|
|
328
|
+
<span class="na-skill-name">${escape(t("na_skill_empty"))}</span>
|
|
288
329
|
</button>
|
|
289
330
|
`);
|
|
290
331
|
}
|
|
@@ -363,9 +404,9 @@ When the room ___, raise an objection."></textarea>
|
|
|
363
404
|
|
|
364
405
|
function getProviderStatus(provider) {
|
|
365
406
|
const keys = (typeof window.boardroomKeys === "function" ? window.boardroomKeys() : {}) || {};
|
|
366
|
-
if (keys[provider]) return { label: "
|
|
367
|
-
if (keys.openrouter) return { label: "
|
|
368
|
-
return { label: "
|
|
407
|
+
if (keys[provider]) return { label: t("na_key_direct"), cls: "direct" };
|
|
408
|
+
if (keys.openrouter) return { label: t("na_key_via"), cls: "via" };
|
|
409
|
+
return { label: t("na_key_none"), cls: "none" };
|
|
369
410
|
}
|
|
370
411
|
|
|
371
412
|
function refreshProviderStatus() {
|
|
@@ -391,10 +432,37 @@ When the room ___, raise an objection."></textarea>
|
|
|
391
432
|
paintAvatar();
|
|
392
433
|
refreshAll();
|
|
393
434
|
|
|
435
|
+
// Paint the model dropdown's current label · the chip uses the
|
|
436
|
+
// same agent-model state as the AI composer (loadAgentComposerModel),
|
|
437
|
+
// so re-opening the overlay reflects the user's last pick.
|
|
438
|
+
paintModelLabel();
|
|
439
|
+
|
|
394
440
|
overlay.classList.add("open");
|
|
395
441
|
overlay.setAttribute("aria-hidden", "false");
|
|
396
442
|
document.body.style.overflow = "hidden";
|
|
397
443
|
setTimeout(() => modal.querySelector(".na-name-input").focus(), 80);
|
|
444
|
+
applyNewAgentI18n();
|
|
445
|
+
refreshProviderStatus();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/** Read the user's current agent-model selection (shared with the
|
|
449
|
+
* AI composer) and paint it into the overlay's `cmp-dd-value` span.
|
|
450
|
+
* Falls back to a dash when app or its model resolver isn't ready. */
|
|
451
|
+
function paintModelLabel() {
|
|
452
|
+
const span = modal && modal.querySelector('[data-cmp-dd-value="agent-model"]');
|
|
453
|
+
if (!span) return;
|
|
454
|
+
let label = "—";
|
|
455
|
+
try {
|
|
456
|
+
if (window.app && typeof window.app.loadAgentComposerModel === "function") {
|
|
457
|
+
const v = window.app.loadAgentComposerModel();
|
|
458
|
+
if (v) {
|
|
459
|
+
label = (typeof window.app.modelLabel === "function")
|
|
460
|
+
? (window.app.modelLabel(v) || v)
|
|
461
|
+
: v;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
} catch (_) { /* keep dash */ }
|
|
465
|
+
span.textContent = label;
|
|
398
466
|
}
|
|
399
467
|
|
|
400
468
|
function close() {
|
|
@@ -511,8 +579,8 @@ When the room ___, raise an objection."></textarea>
|
|
|
511
579
|
}
|
|
512
580
|
|
|
513
581
|
if (btn) btn.disabled = true;
|
|
514
|
-
const originalLabel = labelEl?.textContent || "
|
|
515
|
-
if (labelEl) labelEl.textContent = "
|
|
582
|
+
const originalLabel = labelEl?.textContent || t("na_avatar_regen");
|
|
583
|
+
if (labelEl) labelEl.textContent = t("na_avatar_thinking");
|
|
516
584
|
|
|
517
585
|
try {
|
|
518
586
|
const res = await fetch("/api/avatar/generate", {
|
|
@@ -595,6 +663,9 @@ When the room ___, raise an objection."></textarea>
|
|
|
595
663
|
// Initial paint of placeholder
|
|
596
664
|
paintAvatar();
|
|
597
665
|
|
|
666
|
+
document.addEventListener("boardroom:locale", applyNewAgentI18n);
|
|
667
|
+
applyNewAgentI18n();
|
|
668
|
+
|
|
598
669
|
// Create — POST to /api/agents and refresh the sidebar's agents list.
|
|
599
670
|
modal.querySelector(".na-create").addEventListener("click", async () => {
|
|
600
671
|
const create = modal.querySelector(".na-create");
|
|
@@ -603,20 +674,16 @@ When the room ___, raise an objection."></textarea>
|
|
|
603
674
|
const name = modal.querySelector(".na-name-input").value.trim();
|
|
604
675
|
const bio = modal.querySelector(".na-desc-input").value.trim();
|
|
605
676
|
const instruction = modal.querySelector(".na-instr-input").value.trim();
|
|
606
|
-
//
|
|
607
|
-
//
|
|
608
|
-
//
|
|
609
|
-
//
|
|
610
|
-
//
|
|
611
|
-
//
|
|
612
|
-
// first interaction); we fall back to `opus-4-7` only as a
|
|
613
|
-
// last resort and leave the runtime resolver to fix it up.
|
|
677
|
+
// Model · the foot dropdown writes the user's pick to the shared
|
|
678
|
+
// agent-composer model state (loadAgentComposerModel). It
|
|
679
|
+
// already does the reachability + default-resolver fallback
|
|
680
|
+
// chain (server defaultModelV → first reachable → DEFAULT_AGENT_MODEL),
|
|
681
|
+
// so this just reads it back. Hard fallback to "opus-4-7" only
|
|
682
|
+
// covers the edge case where window.app isn't initialized.
|
|
614
683
|
let modelV = "opus-4-7";
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
modelV =
|
|
618
|
-
} else if (cache && Array.isArray(cache.reachable) && cache.reachable.length > 0) {
|
|
619
|
-
modelV = cache.reachable[0].modelV;
|
|
684
|
+
if (window.app && typeof window.app.loadAgentComposerModel === "function") {
|
|
685
|
+
const picked = window.app.loadAgentComposerModel();
|
|
686
|
+
if (picked) modelV = picked;
|
|
620
687
|
}
|
|
621
688
|
|
|
622
689
|
// Avatar → data URL. If the user never clicked "regenerate", we
|
|
@@ -631,9 +698,8 @@ When the room ___, raise an objection."></textarea>
|
|
|
631
698
|
const avatarPath = "data:image/svg+xml;utf8," + encodeURIComponent(svg);
|
|
632
699
|
|
|
633
700
|
// Lock the button while the request is in flight.
|
|
634
|
-
const orig = create.textContent;
|
|
635
701
|
create.disabled = true;
|
|
636
|
-
create.
|
|
702
|
+
create.innerHTML = `<span class="na-create-mark">◆</span><span>${escape(t("na_creating"))}</span>`;
|
|
637
703
|
|
|
638
704
|
try {
|
|
639
705
|
const res = await fetch("/api/agents", {
|
|
@@ -658,9 +724,11 @@ When the room ___, raise an objection."></textarea>
|
|
|
658
724
|
} catch (_) { /* */ }
|
|
659
725
|
close();
|
|
660
726
|
} catch (e) {
|
|
661
|
-
|
|
727
|
+
const msg = e && e.message ? e.message : String(e);
|
|
728
|
+
alert(t("na_create_fail", { msg }));
|
|
662
729
|
create.disabled = false;
|
|
663
|
-
create.
|
|
730
|
+
create.innerHTML = `<span class="na-create-mark">◆</span><span data-i18n-na="na_create"></span>`;
|
|
731
|
+
applyNewAgentI18n();
|
|
664
732
|
}
|
|
665
733
|
});
|
|
666
734
|
}
|
package/public/room-settings.js
CHANGED
|
@@ -124,6 +124,7 @@
|
|
|
124
124
|
* new model the catalog doesn't know about yet). */
|
|
125
125
|
const MODEL_LABELS = {
|
|
126
126
|
"sonnet-4-6": "Sonnet 4.6",
|
|
127
|
+
"opus-4-6": "Opus 4.6",
|
|
127
128
|
"opus-4-7": "Opus 4.7",
|
|
128
129
|
"opus-4-6": "Opus 4.6",
|
|
129
130
|
"opus-4-6-fast": "Opus 4.6 Fast",
|
|
@@ -138,9 +139,9 @@
|
|
|
138
139
|
"gemini-3-1-flash": "Gemini 3.1 Flash Lite",
|
|
139
140
|
"grok-4-3": "Grok 4.3",
|
|
140
141
|
"grok-4-1-fast": "Grok 4.1 Fast",
|
|
141
|
-
"grok-4-3": "Grok 4.3",
|
|
142
142
|
"grok-4-20": "Grok 4.20",
|
|
143
143
|
"deepseek-v4-pro": "DeepSeek V4 Pro",
|
|
144
|
+
"deepseek-v4-flash": "DeepSeek Lite",
|
|
144
145
|
};
|
|
145
146
|
function modelLabelFor(v) {
|
|
146
147
|
if (!v) return "";
|
package/public/user-settings.css
CHANGED
|
@@ -343,6 +343,43 @@
|
|
|
343
343
|
min-width: 0;
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
+
/* Preferences → Other · EN / Zh (moved off the sidebar chrome) */
|
|
347
|
+
.us-pane-body .locale-switch {
|
|
348
|
+
display: inline-flex;
|
|
349
|
+
align-items: center;
|
|
350
|
+
padding: 1px;
|
|
351
|
+
border: 1px solid var(--line-bright, rgba(255, 255, 255, 0.09));
|
|
352
|
+
border-radius: 3px;
|
|
353
|
+
background: var(--panel-2, #151514);
|
|
354
|
+
}
|
|
355
|
+
.us-pane-body .locale-switch .locale-btn {
|
|
356
|
+
appearance: none;
|
|
357
|
+
border: 0;
|
|
358
|
+
background: transparent;
|
|
359
|
+
color: var(--text-dim);
|
|
360
|
+
font-family: var(--mono);
|
|
361
|
+
font-size: 10px;
|
|
362
|
+
font-weight: 600;
|
|
363
|
+
padding: 3px 8px;
|
|
364
|
+
cursor: pointer;
|
|
365
|
+
border-radius: 2px;
|
|
366
|
+
line-height: 1.2;
|
|
367
|
+
}
|
|
368
|
+
.us-pane-body .locale-switch .locale-btn:hover { color: var(--text-soft); }
|
|
369
|
+
.us-pane-body .locale-switch .locale-btn.active {
|
|
370
|
+
background: var(--panel-3);
|
|
371
|
+
color: var(--lime, #6fb572);
|
|
372
|
+
}
|
|
373
|
+
.us-locale-deck {
|
|
374
|
+
margin: 10px 0 0;
|
|
375
|
+
padding: 0;
|
|
376
|
+
max-width: 420px;
|
|
377
|
+
font-size: 11px;
|
|
378
|
+
color: var(--text-faint, #3a382f);
|
|
379
|
+
letter-spacing: 0.02em;
|
|
380
|
+
line-height: 1.5;
|
|
381
|
+
}
|
|
382
|
+
|
|
346
383
|
/* User avatar block */
|
|
347
384
|
.us-avatar-row {
|
|
348
385
|
display: flex;
|
|
@@ -639,6 +676,20 @@
|
|
|
639
676
|
border-color: var(--red, #B5706A);
|
|
640
677
|
color: var(--red, #B5706A);
|
|
641
678
|
}
|
|
679
|
+
/* Locked variant · the only configured LLM key cannot be removed —
|
|
680
|
+
the boardroom would land in a "no usable carrier" state and every
|
|
681
|
+
director / chair / brief writer would fail. Renders dimmed and
|
|
682
|
+
non-interactive; the title attribute carries the explanation
|
|
683
|
+
("Add another LLM key first — at least one must remain
|
|
684
|
+
configured."). The user-facing path to "delete this key" is to
|
|
685
|
+
add another LLM provider first, then come back to this row. */
|
|
686
|
+
.us-key-remove.is-locked,
|
|
687
|
+
.us-key-remove.is-locked:hover {
|
|
688
|
+
cursor: not-allowed;
|
|
689
|
+
opacity: 0.45;
|
|
690
|
+
border-color: var(--line-bright, #2A2A26);
|
|
691
|
+
color: var(--text-faint, #3A382F);
|
|
692
|
+
}
|
|
642
693
|
|
|
643
694
|
/* "Set as default" CTA · sits next to the remove button on configured
|
|
644
695
|
non-default LLM rows. Quiet outline by default, flips to lime on
|
|
@@ -1352,6 +1403,23 @@
|
|
|
1352
1403
|
margin: -4px 0 6px;
|
|
1353
1404
|
}
|
|
1354
1405
|
|
|
1406
|
+
.us-ws-backend-radios {
|
|
1407
|
+
display: flex;
|
|
1408
|
+
flex-wrap: wrap;
|
|
1409
|
+
gap: 14px 22px;
|
|
1410
|
+
}
|
|
1411
|
+
.us-ws-backend-label {
|
|
1412
|
+
display: inline-flex;
|
|
1413
|
+
align-items: center;
|
|
1414
|
+
gap: 8px;
|
|
1415
|
+
font-size: 12px;
|
|
1416
|
+
color: var(--text-soft, #8E8B83);
|
|
1417
|
+
cursor: pointer;
|
|
1418
|
+
}
|
|
1419
|
+
.us-ws-backend-label span {
|
|
1420
|
+
color: var(--text, #C8C5BE);
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1355
1423
|
/* ─── Available models summary + default picker ───
|
|
1356
1424
|
Lives at the bottom of the API Key section. Pure-list layout —
|
|
1357
1425
|
no shadows, no left accents. Provider tag + flat per-model rows
|