privateboard 0.1.36 → 0.1.38
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/boot.js +1142 -50
- package/dist/boot.js.map +1 -1
- package/dist/cli.js +1142 -50
- package/dist/cli.js.map +1 -1
- package/dist/server.js +1121 -50
- package/dist/server.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +3 -2
- package/public/adjourn-overlay.css +2 -2
- package/public/agent-overlay.css +27 -15
- package/public/agent-overlay.js +3 -1
- package/public/agent-profile.css +328 -32
- package/public/agent-profile.js +414 -43
- package/public/app-updater.css +1 -1
- package/public/app.js +1807 -35
- package/public/avatars/chair-blink.svg +1 -0
- package/public/home-3d-loader.js +6 -0
- package/public/home.html +3 -3
- package/public/i18n.js +279 -0
- package/public/icons/folded-sidebar.png +0 -0
- package/public/index.html +410 -147
- package/public/mention-picker.js +1 -1
- package/public/new-agent.css +7 -7
- package/public/onboarding.css +5 -5
- package/public/quote-cta.css +5 -4
- package/public/quote-cta.js +50 -5
- package/public/report.html +27 -7
- package/public/room-settings.css +24 -9
- package/public/thread.css +1211 -0
- package/public/user-settings.css +6 -6
- package/public/user-settings.js +37 -20
- package/public/voice-3d.js +167 -3
- package/public/voice-clone.css +875 -0
- package/public/voice-clone.js +1351 -0
- package/public/voice-replay.css +3 -3
- package/public/icons/search.png +0 -0
package/public/mention-picker.js
CHANGED
package/public/new-agent.css
CHANGED
|
@@ -304,7 +304,7 @@
|
|
|
304
304
|
}
|
|
305
305
|
.na-avatar-regen .icon {
|
|
306
306
|
font-family: var(--mono);
|
|
307
|
-
font-size:
|
|
307
|
+
font-size: 14px;
|
|
308
308
|
font-weight: 400;
|
|
309
309
|
line-height: 1;
|
|
310
310
|
}
|
|
@@ -362,7 +362,7 @@
|
|
|
362
362
|
color: var(--lime);
|
|
363
363
|
}
|
|
364
364
|
.na-model-trigger .trigger-l .name {
|
|
365
|
-
font-size:
|
|
365
|
+
font-size: 14px;
|
|
366
366
|
font-weight: 700;
|
|
367
367
|
color: var(--text);
|
|
368
368
|
letter-spacing: -0.003em;
|
|
@@ -779,7 +779,7 @@
|
|
|
779
779
|
border: none;
|
|
780
780
|
background: transparent;
|
|
781
781
|
font-family: var(--font-human);
|
|
782
|
-
font-size:
|
|
782
|
+
font-size: 14px;
|
|
783
783
|
line-height: 1.5;
|
|
784
784
|
color: var(--text);
|
|
785
785
|
outline: none;
|
|
@@ -951,7 +951,7 @@
|
|
|
951
951
|
margin-top: 6px;
|
|
952
952
|
align-self: flex-start;
|
|
953
953
|
}
|
|
954
|
-
.new-agent-overlay .na-rule-add .plus { color: var(--lime); font-size:
|
|
954
|
+
.new-agent-overlay .na-rule-add .plus { color: var(--lime); font-size: 14px; line-height: 1; }
|
|
955
955
|
.new-agent-overlay .na-rule-add:hover { border-color: var(--lime); color: var(--lime); border-style: solid; }
|
|
956
956
|
.new-agent-overlay .na-rule-add[disabled],
|
|
957
957
|
.new-agent-overlay .na-rule-add.at-cap {
|
|
@@ -1119,7 +1119,7 @@
|
|
|
1119
1119
|
border: 0.5px solid var(--line-bright);
|
|
1120
1120
|
color: var(--text-faint);
|
|
1121
1121
|
font-family: var(--mono);
|
|
1122
|
-
font-size:
|
|
1122
|
+
font-size: 14px;
|
|
1123
1123
|
line-height: 1;
|
|
1124
1124
|
cursor: pointer;
|
|
1125
1125
|
display: flex;
|
|
@@ -1352,7 +1352,7 @@
|
|
|
1352
1352
|
.new-agent-overlay .na-textarea-wrap.tall textarea {
|
|
1353
1353
|
min-height: 220px;
|
|
1354
1354
|
font-family: var(--mono);
|
|
1355
|
-
font-size:
|
|
1355
|
+
font-size: 14px;
|
|
1356
1356
|
line-height: 1.5;
|
|
1357
1357
|
resize: vertical;
|
|
1358
1358
|
overflow-y: auto;
|
|
@@ -1382,7 +1382,7 @@
|
|
|
1382
1382
|
font-weight: 700;
|
|
1383
1383
|
letter-spacing: -0.005em;
|
|
1384
1384
|
font-family: var(--font-human);
|
|
1385
|
-
font-size:
|
|
1385
|
+
font-size: 14px;
|
|
1386
1386
|
}
|
|
1387
1387
|
.na-summary-handle { color: var(--lime); }
|
|
1388
1388
|
.na-foot-sep { color: var(--text-faint); }
|
package/public/onboarding.css
CHANGED
|
@@ -109,7 +109,7 @@
|
|
|
109
109
|
}
|
|
110
110
|
.onb-deck {
|
|
111
111
|
font-family: var(--font-human, var(--mono));
|
|
112
|
-
font-size:
|
|
112
|
+
font-size: 14px;
|
|
113
113
|
color: var(--text-soft);
|
|
114
114
|
line-height: 1.55;
|
|
115
115
|
margin-top: 8px;
|
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
content: ">";
|
|
180
180
|
color: var(--lime);
|
|
181
181
|
font-weight: 700;
|
|
182
|
-
font-size:
|
|
182
|
+
font-size: 14px;
|
|
183
183
|
font-family: var(--mono);
|
|
184
184
|
padding: 9px 0 0 11px;
|
|
185
185
|
align-self: flex-start;
|
|
@@ -332,7 +332,7 @@
|
|
|
332
332
|
}
|
|
333
333
|
.onb-voice-pitch {
|
|
334
334
|
font-family: var(--font-human, "Inter", system-ui, sans-serif);
|
|
335
|
-
font-size:
|
|
335
|
+
font-size: 14px;
|
|
336
336
|
line-height: 1.6;
|
|
337
337
|
color: var(--text-soft);
|
|
338
338
|
margin: 0;
|
|
@@ -393,7 +393,7 @@
|
|
|
393
393
|
}
|
|
394
394
|
.onb-cast-next-arrow {
|
|
395
395
|
font-family: var(--mono);
|
|
396
|
-
font-size:
|
|
396
|
+
font-size: 14px;
|
|
397
397
|
font-weight: 700;
|
|
398
398
|
color: var(--text-faint);
|
|
399
399
|
align-self: center;
|
|
@@ -592,7 +592,7 @@
|
|
|
592
592
|
color: var(--text-dim);
|
|
593
593
|
cursor: pointer;
|
|
594
594
|
font-family: var(--font-human, "Inter", system-ui, sans-serif);
|
|
595
|
-
font-size:
|
|
595
|
+
font-size: 14px;
|
|
596
596
|
font-weight: 500;
|
|
597
597
|
letter-spacing: -0.005em;
|
|
598
598
|
line-height: 1.2;
|
package/public/quote-cta.css
CHANGED
|
@@ -80,9 +80,10 @@
|
|
|
80
80
|
white-space: nowrap;
|
|
81
81
|
}
|
|
82
82
|
/* Adjourned-room state · Probe / Second hide (they post to a
|
|
83
|
-
closed room), but Save
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
closed room), but Save and Thread stay — bookmarking and opening a
|
|
84
|
+
private 1:1 follow-up thread are both primary review-mode uses
|
|
85
|
+
(a thread spawns its own live room, independent of the parent). */
|
|
86
|
+
.qcta.qcta-readonly .qcta-btn:not(.qcta-btn-save):not(.qcta-btn-thread) { display: none; }
|
|
86
87
|
.qcta.qcta-readonly .qcta-hint { display: inline-flex; align-items: center; }
|
|
87
88
|
|
|
88
89
|
/* ─── Save toast ─────────────────────────────────────────────
|
|
@@ -220,7 +221,7 @@
|
|
|
220
221
|
content: ">";
|
|
221
222
|
color: var(--lime);
|
|
222
223
|
font-weight: 700;
|
|
223
|
-
font-size:
|
|
224
|
+
font-size: 14px;
|
|
224
225
|
font-family: var(--mono, "Inter", system-ui, sans-serif);
|
|
225
226
|
padding: 9px 0 0 11px;
|
|
226
227
|
align-self: flex-start;
|
package/public/quote-cta.js
CHANGED
|
@@ -160,8 +160,8 @@
|
|
|
160
160
|
cta.className = "qcta";
|
|
161
161
|
cta.setAttribute("role", "toolbar");
|
|
162
162
|
const t = lang() === "zh"
|
|
163
|
-
? { ask: "追问", love: "附议", save: "收藏" }
|
|
164
|
-
: { ask: "Probe", love: "Second", save: "Save" };
|
|
163
|
+
? { ask: "追问", love: "附议", save: "收藏", thread: "私聊" }
|
|
164
|
+
: { ask: "Probe", love: "Second", save: "Save", thread: "Thread" };
|
|
165
165
|
// Inline chat-bubble SVG · uses currentColor so it inherits the
|
|
166
166
|
// hover lime / base text colour like the ★ glyph does.
|
|
167
167
|
const askIcon = `
|
|
@@ -176,6 +176,17 @@
|
|
|
176
176
|
<path d="M3.5 1.5 H10.5 V12.5 L7 9.5 L3.5 12.5 Z"/>
|
|
177
177
|
</svg>
|
|
178
178
|
`;
|
|
179
|
+
// Reply curve · matches `_threadTriggerIconSvg` in app.js so the
|
|
180
|
+
// selection bar's Thread button reads as the same action as the
|
|
181
|
+
// per-bubble msg-toolbar reply icon. Both surfaces route into
|
|
182
|
+
// `openThreadWith` — selection bar additionally seeds the
|
|
183
|
+
// composer with a quote of the selected text.
|
|
184
|
+
const threadIcon = `
|
|
185
|
+
<svg viewBox="0 0 14 14" width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
186
|
+
<path d="M5 10 L2 7 L5 4"/>
|
|
187
|
+
<path d="M2 7 H10 a2 2 0 0 1 2 2 V11"/>
|
|
188
|
+
</svg>
|
|
189
|
+
`;
|
|
179
190
|
cta.innerHTML = `
|
|
180
191
|
<button type="button" class="qcta-btn" data-qcta="ask">
|
|
181
192
|
<span class="ico">${askIcon}</span><span>${t.ask}</span>
|
|
@@ -183,6 +194,9 @@
|
|
|
183
194
|
<button type="button" class="qcta-btn" data-qcta="second">
|
|
184
195
|
<span class="ico">★</span><span>${t.love}</span>
|
|
185
196
|
</button>
|
|
197
|
+
<button type="button" class="qcta-btn qcta-btn-thread" data-qcta="thread" title="${t.thread}">
|
|
198
|
+
<span class="ico">${threadIcon}</span><span>${t.thread}</span>
|
|
199
|
+
</button>
|
|
186
200
|
<button type="button" class="qcta-btn qcta-btn-save" data-qcta="save" title="Save to Notes · S">
|
|
187
201
|
<span class="ico">${saveIcon}</span><span>${t.save}</span>
|
|
188
202
|
</button>
|
|
@@ -197,15 +211,18 @@
|
|
|
197
211
|
if (!btn) return;
|
|
198
212
|
const action = btn.getAttribute("data-qcta");
|
|
199
213
|
// Read-only state (adjourned rooms) blocks Probe / Second since
|
|
200
|
-
// they post messages to
|
|
201
|
-
//
|
|
202
|
-
|
|
214
|
+
// they post messages to the closed parent room. Save and Thread
|
|
215
|
+
// are exempt — Save is a personal bookmark, and a Thread spawns
|
|
216
|
+
// its own live 1:1 room (server doesn't gate thread-create on the
|
|
217
|
+
// parent's status), so review-mode follow-ups stay possible.
|
|
218
|
+
if (cta.classList.contains("qcta-readonly") && action !== "save" && action !== "thread") return;
|
|
203
219
|
const sel = lastSelection;
|
|
204
220
|
hideCTA();
|
|
205
221
|
if (!sel || !sel.text) return;
|
|
206
222
|
if (action === "ask") openAskOverlay(sel);
|
|
207
223
|
else if (action === "second") submitSecond(sel);
|
|
208
224
|
else if (action === "save") submitSave(sel);
|
|
225
|
+
else if (action === "thread") openSelectionThread(sel);
|
|
209
226
|
});
|
|
210
227
|
document.body.appendChild(cta);
|
|
211
228
|
return cta;
|
|
@@ -434,6 +451,34 @@
|
|
|
434
451
|
routeSend(body, mentions);
|
|
435
452
|
}
|
|
436
453
|
|
|
454
|
+
// ── Selection → private thread ──────────────────────────────
|
|
455
|
+
// Opens (or restores) a 1:1 thread window with the quoted director
|
|
456
|
+
// and pre-seeds the thread's composer textarea with the markdown
|
|
457
|
+
// blockquote of the selected text + a blank line. The user then
|
|
458
|
+
// types their follow-up question and sends; the director's reply
|
|
459
|
+
// sees the quote inline as the user's first thread message.
|
|
460
|
+
// Threads stay private to the user + this one director — the rest
|
|
461
|
+
// of the room never sees what was selected or discussed below.
|
|
462
|
+
async function openSelectionThread(sel) {
|
|
463
|
+
const app = window.app;
|
|
464
|
+
if (!app || typeof app.openThreadWith !== "function") return;
|
|
465
|
+
if (!sel.directorId) return;
|
|
466
|
+
// Stash the selected quote so the thread window mount can find
|
|
467
|
+
// it AFTER the async POST + DOM build settle. Keyed by director
|
|
468
|
+
// id so concurrent clicks on different bubbles don't overwrite
|
|
469
|
+
// each other. Consumed (and cleared) by mountThreadWindow.
|
|
470
|
+
app._threadPendingQuote = app._threadPendingQuote || {};
|
|
471
|
+
app._threadPendingQuote[sel.directorId] = {
|
|
472
|
+
text: sel.text,
|
|
473
|
+
directorName: sel.directorName || "",
|
|
474
|
+
};
|
|
475
|
+
try {
|
|
476
|
+
await app.openThreadWith(sel.directorId);
|
|
477
|
+
} catch (e) {
|
|
478
|
+
try { console.warn("[qcta-thread] open failed:", e); } catch (_) {}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
437
482
|
// ── Save to Notes ─────────────────────────────────────────────
|
|
438
483
|
// POST /api/notes with quote + sentence-based context + char
|
|
439
484
|
// offsets. No room interaction — this is a personal bookmark.
|
package/public/report.html
CHANGED
|
@@ -5041,19 +5041,20 @@
|
|
|
5041
5041
|
const intro = typeof parsed.intro === "string" ? parsed.intro.trim() : "";
|
|
5042
5042
|
const chairSynthesis = typeof parsed.chairSynthesis === "string" ? parsed.chairSynthesis.trim() : "";
|
|
5043
5043
|
|
|
5044
|
-
// Resolve director id → display name from the
|
|
5045
|
-
//
|
|
5046
|
-
//
|
|
5047
|
-
//
|
|
5044
|
+
// Resolve director id → display name from the global stash that
|
|
5045
|
+
// `load()` populates from state.members + historicalMembers +
|
|
5046
|
+
// chair. Falls back to scanning `[data-director-id]` chips for
|
|
5047
|
+
// legacy / forward-compat (none currently rendered, but cheap),
|
|
5048
|
+
// and finally to the id itself when nothing matches.
|
|
5048
5049
|
const directorNameById = (() => {
|
|
5049
|
-
const out = {};
|
|
5050
|
+
const out = Object.assign({}, window.__reportAgentNames || {});
|
|
5050
5051
|
try {
|
|
5051
5052
|
const chips = document.querySelectorAll("[data-director-id]");
|
|
5052
5053
|
chips.forEach((el) => {
|
|
5053
5054
|
const id = el.getAttribute("data-director-id");
|
|
5054
|
-
if (id && el.textContent) out[id] = el.textContent.trim();
|
|
5055
|
+
if (id && el.textContent && !out[id]) out[id] = el.textContent.trim();
|
|
5055
5056
|
});
|
|
5056
|
-
} catch (_) { /*
|
|
5057
|
+
} catch (_) { /* defensive · DOM may not be ready */ }
|
|
5057
5058
|
return out;
|
|
5058
5059
|
})();
|
|
5059
5060
|
const dn = (id) => directorNameById[id] || id || "—";
|
|
@@ -6420,6 +6421,25 @@
|
|
|
6420
6421
|
const j = await stateRes.json();
|
|
6421
6422
|
room = j.room;
|
|
6422
6423
|
members = j.members || [];
|
|
6424
|
+
// Author-id → display-name map · drives `renderViewsCompared`'s
|
|
6425
|
+
// director chips ("Every director on the table", alignment
|
|
6426
|
+
// cards, divergence sides). Earlier the helper scraped
|
|
6427
|
+
// `[data-director-id]` chips off the rendered DOM, but no
|
|
6428
|
+
// chip ever carried that attribute — the lookup always fell
|
|
6429
|
+
// through and the chips printed raw agent ids like
|
|
6430
|
+
// `1khezh9da65g`. Stash a real lookup on window from BOTH
|
|
6431
|
+
// active members and historicalMembers (excused directors
|
|
6432
|
+
// still appear in views-compared if they participated) plus
|
|
6433
|
+
// the chair (occasionally referenced too).
|
|
6434
|
+
const nameMap = {};
|
|
6435
|
+
for (const m of (j.members || [])) {
|
|
6436
|
+
if (m && m.id && m.name) nameMap[m.id] = m.name;
|
|
6437
|
+
}
|
|
6438
|
+
for (const m of (j.historicalMembers || [])) {
|
|
6439
|
+
if (m && m.id && m.name && !nameMap[m.id]) nameMap[m.id] = m.name;
|
|
6440
|
+
}
|
|
6441
|
+
if (j.chair && j.chair.id && j.chair.name) nameMap[j.chair.id] = j.chair.name;
|
|
6442
|
+
window.__reportAgentNames = nameMap;
|
|
6423
6443
|
} else {
|
|
6424
6444
|
members = [];
|
|
6425
6445
|
}
|
package/public/room-settings.css
CHANGED
|
@@ -218,7 +218,7 @@ html.is-electron-mac .room-settings-overlay * {
|
|
|
218
218
|
}
|
|
219
219
|
.rs-config-row-name {
|
|
220
220
|
font-family: var(--font-human);
|
|
221
|
-
font-size:
|
|
221
|
+
font-size: 14px;
|
|
222
222
|
font-weight: 600;
|
|
223
223
|
color: var(--text);
|
|
224
224
|
letter-spacing: -0.005em;
|
|
@@ -1096,12 +1096,11 @@ html.is-electron-mac .room-settings-overlay * {
|
|
|
1096
1096
|
0%, 100% { opacity: 1; }
|
|
1097
1097
|
50% { opacity: 0.4; }
|
|
1098
1098
|
}
|
|
1099
|
-
/* Generate-Report variant · adjourned room with no brief yet.
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
}
|
|
1099
|
+
/* Generate-Report variant · adjourned room with no brief yet. No
|
|
1100
|
+
colour accent — the icon rests at the shared `.head-icon-btn`
|
|
1101
|
+
text-soft tone (lime only on hover, like every other head icon)
|
|
1102
|
+
so it doesn't read as a yellow/gold highlight (`--lime` is the
|
|
1103
|
+
warm gold #C9A46B in the default theme). */
|
|
1105
1104
|
/* Count chip for multi-brief popover trigger · pinned to the icon's
|
|
1106
1105
|
top-right corner so the user sees "there's more than one report"
|
|
1107
1106
|
without taking horizontal space. Mono pill, same micro-tag register
|
|
@@ -1133,10 +1132,21 @@ html.is-electron-mac .room-settings-overlay * {
|
|
|
1133
1132
|
/* Edit cast · Lucide UserPlus. Sits between the cast avatars and the
|
|
1134
1133
|
primary state action so the user reads it as a direct affordance on
|
|
1135
1134
|
the cast strip ("add a member to this group"). Hidden on adjourned
|
|
1136
|
-
rooms (the cast is frozen once the brief is filed).
|
|
1135
|
+
rooms (the cast is frozen once the brief is filed).
|
|
1136
|
+
The glyph (user silhouette + plus) sits in opposite quadrants of
|
|
1137
|
+
the 24×24 viewBox · at the shared 16×16 mask size the symbol reads
|
|
1138
|
+
visually smaller than dense siblings like `.head-divergence`. Bump
|
|
1139
|
+
the mask + ::before box to 18×18 so it sits at the same perceived
|
|
1140
|
+
weight as the cast avatars (26px) next to it. */
|
|
1137
1141
|
.head-add-cast {
|
|
1138
1142
|
--icon: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2'/><circle cx='9' cy='7' r='4'/><line x1='19' x2='19' y1='8' y2='14'/><line x1='22' x2='16' y1='11' y2='11'/></svg>");
|
|
1139
1143
|
}
|
|
1144
|
+
.head-add-cast::before {
|
|
1145
|
+
width: 18px;
|
|
1146
|
+
height: 18px;
|
|
1147
|
+
-webkit-mask-size: 18px 18px;
|
|
1148
|
+
mask-size: 18px 18px;
|
|
1149
|
+
}
|
|
1140
1150
|
html[data-status="adjourned"] .head-add-cast { display: none; }
|
|
1141
1151
|
/* Adjourn · identical glyph to the input-bar's `.ib-adjourn` (Lucide
|
|
1142
1152
|
log-out: door + arrow exit). The head and input-bar icons share the
|
|
@@ -1195,7 +1205,12 @@ html[data-status="adjourned"] .head-add-cast { display: none; }
|
|
|
1195
1205
|
.rec-countdown-overlay {
|
|
1196
1206
|
position: absolute;
|
|
1197
1207
|
inset: 0;
|
|
1198
|
-
z-index:
|
|
1208
|
+
/* Sit above the live caption · `.rt-subtitle` is z-index: 12 (see
|
|
1209
|
+
index.html line ~9554), so the countdown overlay must be
|
|
1210
|
+
higher or else the subtitle bar punches through "3 / 2 / 1 /
|
|
1211
|
+
READY" and ruins the movie-trailer intro. 30 leaves headroom
|
|
1212
|
+
for any future stage chrome we want to slip between. */
|
|
1213
|
+
z-index: 30;
|
|
1199
1214
|
display: flex;
|
|
1200
1215
|
align-items: center;
|
|
1201
1216
|
justify-content: center;
|