privateboard 0.1.38 → 0.1.41

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.
Files changed (78) hide show
  1. package/dist/boot.js +327 -50
  2. package/dist/boot.js.map +1 -1
  3. package/dist/cli.js +327 -50
  4. package/dist/cli.js.map +1 -1
  5. package/dist/server.js +201 -40
  6. package/dist/server.js.map +1 -1
  7. package/dist/version.d.ts +1 -1
  8. package/dist/version.js +1 -1
  9. package/dist/version.js.map +1 -1
  10. package/package.json +1 -1
  11. package/public/__avatar3d_test.html +156 -0
  12. package/public/agent-overlay.css +14 -6
  13. package/public/agent-overlay.js +6 -6
  14. package/public/agent-profile.css +9 -12
  15. package/public/agent-profile.js +91 -38
  16. package/public/app.js +471 -528
  17. package/public/avatar-3d-snap.js +205 -0
  18. package/public/avatar-3d.js +836 -0
  19. package/public/avatar-customizer.html +274 -0
  20. package/public/avatar3d-editor.css +240 -0
  21. package/public/avatar3d-editor.js +484 -0
  22. package/public/avatars/3d/chair.png +0 -0
  23. package/public/avatars/3d/first-principles.png +0 -0
  24. package/public/avatars/3d/historian.png +0 -0
  25. package/public/avatars/3d/long-horizon.png +0 -0
  26. package/public/avatars/3d/phenomenologist.png +0 -0
  27. package/public/avatars/3d/socrates.png +0 -0
  28. package/public/avatars/3d/user-empathy.png +0 -0
  29. package/public/avatars/3d/value-investor.png +0 -0
  30. package/public/core-avatars.js +86 -0
  31. package/public/home-3d-loader.js +15 -4
  32. package/public/home-3d-mock.js +25 -14
  33. package/public/home.html +78 -16
  34. package/public/i18n.js +8 -4
  35. package/public/icons/avatar_1779855104027.glb +0 -0
  36. package/public/icons/logo.png +0 -0
  37. package/public/icons/new-style.glb +0 -0
  38. package/public/icons/new-style2.glb +0 -0
  39. package/public/icons/new-style3.glb +0 -0
  40. package/public/icons/new-style4.glb +0 -0
  41. package/public/icons/new-style5.glb +0 -0
  42. package/public/icons/new-style6.glb +0 -0
  43. package/public/icons/office.glb +0 -0
  44. package/public/icons/stuff.glb +0 -0
  45. package/public/index.html +169 -141
  46. package/public/magazine.html +1 -1
  47. package/public/new-agent.js +46 -20
  48. package/public/newspaper.html +1 -1
  49. package/public/office-viewer.html +340 -0
  50. package/public/ppt.html +1 -1
  51. package/public/stuff-viewer.html +330 -0
  52. package/public/thread.css +16 -15
  53. package/public/user-settings.css +7 -31
  54. package/public/user-settings.js +75 -89
  55. package/public/vendor/BufferGeometryUtils.js +1434 -0
  56. package/public/vendor/DRACOLoader.js +739 -0
  57. package/public/vendor/GLTFLoader.js +4860 -0
  58. package/public/vendor/RoomEnvironment.js +185 -0
  59. package/public/vendor/SkeletonUtils.js +496 -0
  60. package/public/vendor/draco/draco_decoder.js +34 -0
  61. package/public/vendor/draco/draco_decoder.wasm +0 -0
  62. package/public/vendor/draco/draco_encoder.js +33 -0
  63. package/public/vendor/draco/draco_wasm_wrapper.js +117 -0
  64. package/public/vendor/meshopt_decoder.module.js +196 -0
  65. package/public/voice-3d-banner.js +19 -7
  66. package/public/voice-3d.js +1407 -432
  67. package/public/voice-replay.js +21 -0
  68. package/public/avatar-skill.js +0 -629
  69. package/public/avatars/chair-blink.svg +0 -1
  70. package/public/avatars/chair.svg +0 -1
  71. package/public/avatars/first-principles.svg +0 -1
  72. package/public/avatars/historian.svg +0 -1
  73. package/public/avatars/long-horizon.svg +0 -1
  74. package/public/avatars/phenomenologist.svg +0 -1
  75. package/public/avatars/socrates.svg +0 -1
  76. package/public/avatars/user-empathy.svg +0 -1
  77. package/public/avatars/value-investor.svg +0 -1
  78. package/public/icons/folded-sidebar.png +0 -0
@@ -166,6 +166,8 @@
166
166
  name: typeof data.name === "string" ? data.name : "You",
167
167
  intro: typeof data.intro === "string" ? data.intro : "",
168
168
  avatarSeed: data.avatarSeed ?? null,
169
+ avatar3d: data.avatar3d ?? null,
170
+ avatarUrl: data.avatarUrl ?? null,
169
171
  webSearchProvider: data.webSearchProvider === "tavily" ? "tavily" : "brave",
170
172
  minimaxRegion: data.minimaxRegion === "intl" ? "intl" : "cn",
171
173
  };
@@ -187,11 +189,22 @@
187
189
  body: JSON.stringify({
188
190
  name: u.name,
189
191
  intro: u.intro,
190
- avatarSeed: u.avatarSeed
192
+ avatarSeed: u.avatarSeed,
193
+ ...("avatarUrl" in u ? { avatarUrl: u.avatarUrl } : {}),
194
+ ...("avatar3d" in u ? { avatar3d: u.avatar3d } : {}),
191
195
  })
192
196
  }).catch(() => { /* offline → cache stays, retry on next edit */ });
193
197
  }
194
198
 
199
+ // The 3D-avatar editor (avatar3d-editor.js) saves the user's portrait via
200
+ // PUT /api/prefs and fires this event. Sync our cache + repaint the frame so
201
+ // the settings avatar updates without reopening the pane.
202
+ window.addEventListener("pb:user-avatar-updated", (e) => {
203
+ const url = e && e.detail && e.detail.avatarUrl;
204
+ _prefsCache = { ...(_prefsCache || {}), avatarUrl: url || null };
205
+ try { paintUserAvatar(); } catch (_) {}
206
+ });
207
+
195
208
  // Provider keys · canonical state lives in keys-store.js (loaded as a
196
209
  // module script before this file). All reads/writes go through that store;
197
210
  // _keysMeta is a live accessor so the rest of this file needs no changes.
@@ -325,9 +338,9 @@
325
338
  <div class="us-row-label">${tr("us_avatar")}</div>
326
339
  <div class="us-row-field us-avatar-row">
327
340
  <div class="us-avatar-frame" data-us-avatar></div>
328
- <button type="button" class="us-mini-btn" data-us-regen-avatar>
329
- <span class="us-mini-btn-mark">◆</span>
330
- <span>${tr("us_regen_avatar")}</span>
341
+ <button type="button" class="us-mini-btn" data-us-avatar3d>
342
+ <span class="us-mini-btn-mark">◈</span>
343
+ <span>${tr("us_avatar3d")}</span>
331
344
  </button>
332
345
  </div>
333
346
  </div>
@@ -367,30 +380,13 @@
367
380
  }).join("");
368
381
  }
369
382
 
370
- /* ── Room style toggle (3D voxel boardroom vs 2D flat round-table)
371
- Persists to `localStorage["boardroom.stage3d"]` ("on" | "off")
372
- same key voice-3d.js and renderRoundTable already gate on.
373
- Default "on" matches the existing implicit default. */
374
- const STAGE3D_KEY = "boardroom.stage3d";
375
- function getStage3d() {
376
- try { return localStorage.getItem(STAGE3D_KEY) !== "off"; }
377
- catch (_) { return true; }
378
- }
379
- function setStage3d(on) {
380
- try { localStorage.setItem(STAGE3D_KEY, on ? "on" : "off"); } catch (_) {}
381
- }
382
- function stageStyleSegmentsHTML() {
383
- const cur = getStage3d() ? "3d" : "2d";
384
- const items = [
385
- { key: "3d", labelKey: "us_stage_3d" },
386
- { key: "2d", labelKey: "us_stage_2d" },
387
- ];
388
- return items.map(({ key, labelKey }) => {
389
- const label = tr(labelKey);
390
- const cls = "us-seg-btn" + (key === cur ? " active" : "");
391
- return `<button type="button" class="${cls}" data-stage="${key}" role="radio" aria-checked="${key === cur ? "true" : "false"}">${escape(label)}</button>`;
392
- }).join("");
393
- }
383
+ /* The 2D / 3D stage toggle was retired (2026-05) · the voice room
384
+ is 3D-only now. `STAGE3D_KEY` / `getStage3d` / `setStage3d` /
385
+ `stageStyleSegmentsHTML` used to live here and have been removed
386
+ along with their row in `otherSettingsSectionHTML` and the click
387
+ handler below. The localStorage key the toggle persisted to
388
+ (`boardroom.stage3d`) is also no longer consulted anywhere; old
389
+ values are inert. */
394
390
 
395
391
  function otherSettingsSectionHTML() {
396
392
  return `
@@ -410,16 +406,6 @@
410
406
  </div>
411
407
  </div>
412
408
 
413
- <div class="us-row">
414
- <div class="us-row-label">${tr("us_stage_label")}</div>
415
- <div class="us-row-field">
416
- <div class="us-seg" role="radiogroup" aria-label="${escape(tr("us_stage_label"))}" data-us-stage>
417
- ${stageStyleSegmentsHTML()}
418
- </div>
419
- <p class="us-locale-deck">${escape(tr("us_stage_deck"))}</p>
420
- </div>
421
- </div>
422
-
423
409
  <div class="us-row">
424
410
  <div class="us-row-label">${tr("us_locale_label")}</div>
425
411
  <div class="us-row-field">
@@ -493,25 +479,8 @@
493
479
  // the app to re-render the current round-table so the swap is
494
480
  // visible immediately for anyone currently sitting in a voice
495
481
  // room (instead of "have to leave + re-enter to see it").
496
- const stGroup = paneEl.querySelector("[data-us-stage]");
497
- if (stGroup) {
498
- stGroup.addEventListener("click", (e) => {
499
- const btn = e.target.closest(".us-seg-btn[data-stage]");
500
- if (!btn) return;
501
- const next = btn.dataset.stage; // "3d" | "2d"
502
- setStage3d(next === "3d");
503
- stGroup.querySelectorAll(".us-seg-btn").forEach((el) => {
504
- const on = el.dataset.stage === next;
505
- el.classList.toggle("active", on);
506
- el.setAttribute("aria-checked", on ? "true" : "false");
507
- });
508
- try {
509
- if (window.app && typeof window.app.renderRoundTable === "function") {
510
- window.app.renderRoundTable();
511
- }
512
- } catch (_) { /* room may not be a voice room · ignore */ }
513
- });
514
- }
482
+ // [removed 2026-05] The 2D/3D stage toggle wire-up used to live
483
+ // here · the voice room is 3D-only now, no toggle to bind.
515
484
  // Typing-sound toggle · the persistence + audio context lives in
516
485
  // window.boardroomTypingSfx (typing-sfx.js); this row only mirrors
517
486
  // the current state and proxies clicks. Reading inside wire-up
@@ -1840,11 +1809,14 @@
1840
1809
 
1841
1810
  /* Avatar generation · same flow as the agent profile's regenerate
1842
1811
  button (see agent-profile.js / regenerateProfileAvatar): each
1843
- click pulls a fresh seed from AvatarSkill.randomSeed(), saves it
1844
- to the user prefs (`avatarSeed`), and re-paints. The SVG is
1845
- rendered from that seed via AvatarSkill.generate(). */
1846
- function generateAvatar(seed) {
1847
- return window.AvatarSkill.generate(seed);
1812
+ click pulls a fresh seed from Avatar3DSnap.randomSeed(), saves
1813
+ it to the user prefs (`avatarSeed`), and re-paints. Renders a
1814
+ 3D voxel head-and-shoulders portrait (the legacy AvatarSkill
1815
+ 8-bit SVG generator was retired). */
1816
+ // Synchronous "still rendering" placeholder used while the async
1817
+ // 3D snap is in flight · pure CSS via the .us-avatar-frame parent.
1818
+ function loadingHtml() {
1819
+ return '<div class="us-avatar-loading" aria-hidden="true">…</div>';
1848
1820
  }
1849
1821
 
1850
1822
  /* ── Modal shell ──────────────────────────────────────────── */
@@ -1918,21 +1890,44 @@
1918
1890
  const frame = paneEl.querySelector("[data-us-avatar]");
1919
1891
  if (!frame) return;
1920
1892
  const u = getUser();
1921
- // Mirror the agent profile flow · the avatar is whatever seed is
1922
- // saved on the user prefs. If none has ever been generated, mint
1923
- // one now so the avatar is stable across reloads.
1893
+ // 3D portrait takes precedence · if the user customised a 3D
1894
+ // avatar in the editor (avatarUrl is the rendered PNG), show
1895
+ // that. Otherwise render an async 3D snap from the user's seed.
1896
+ if (u.avatarUrl) {
1897
+ frame.innerHTML = `<img src="${u.avatarUrl}" alt="">`;
1898
+ return;
1899
+ }
1900
+ const snap = window.Avatar3DSnap;
1924
1901
  let seed = u.avatarSeed;
1925
- if (!seed && window.AvatarSkill) {
1926
- seed = window.AvatarSkill.randomSeed();
1902
+ if (!seed && snap) {
1903
+ seed = snap.randomSeed();
1927
1904
  saveUser({ avatarSeed: seed });
1928
- // Cascade the freshly-minted seed to app.prefs so the sidebar
1929
- // foot picks it up on the same paint.
1930
1905
  if (window.app) {
1931
1906
  window.app.prefs = { ...(window.app.prefs || {}), avatarSeed: seed };
1932
1907
  if (typeof window.app.renderUserBlock === "function") window.app.renderUserBlock();
1933
1908
  }
1934
1909
  }
1935
- frame.innerHTML = generateAvatar(seed || "default");
1910
+ if (!seed || !snap) {
1911
+ // No snap helper / no seed · clear the frame so the underlying
1912
+ // CSS placeholder shows (the .us-avatar-frame already has its
1913
+ // own initial styling).
1914
+ frame.innerHTML = "";
1915
+ return;
1916
+ }
1917
+ const cached = typeof snap.cacheGet === "function" ? snap.cacheGet(seed) : null;
1918
+ if (cached) {
1919
+ frame.innerHTML = `<img src="${cached}" alt="">`;
1920
+ return;
1921
+ }
1922
+ frame.innerHTML = loadingHtml();
1923
+ snap.generate(seed).then((url) => {
1924
+ if (!url) return;
1925
+ // Only paint if the frame is still in the DOM and showing the
1926
+ // same loading state (user may have already swapped avatars).
1927
+ const f2 = paneEl.querySelector("[data-us-avatar]");
1928
+ if (!f2) return;
1929
+ f2.innerHTML = `<img src="${url}" alt="">`;
1930
+ }).catch(() => { /* */ });
1936
1931
  }
1937
1932
 
1938
1933
  function wireUserSection() {
@@ -1959,25 +1954,16 @@
1959
1954
  });
1960
1955
  introCount.textContent = introInput.value.length;
1961
1956
 
1962
- // Regenerate avatar · same pattern as agent-profile's
1963
- // regenerateProfileAvatar: pull a fresh randomSeed, persist it to
1964
- // the user prefs, repaint. No counter, no name/intro composition —
1965
- // the seed is the only thing that determines the avatar.
1966
- paneEl.querySelector("[data-us-regen-avatar]").addEventListener("click", (e) => {
1967
- e.preventDefault();
1968
- if (!window.AvatarSkill) return;
1969
- const seed = window.AvatarSkill.randomSeed();
1970
- saveUser({ avatarSeed: seed });
1971
- paintUserAvatar();
1972
- // Push the new seed into app.prefs so the sidebar foot's user
1973
- // avatar repaints with the same SVG. Without this, the settings
1974
- // overlay shows the new face but the sidebar keeps the old one
1975
- // until the next reload.
1976
- if (window.app) {
1977
- window.app.prefs = { ...(window.app.prefs || {}), avatarSeed: seed };
1978
- if (typeof window.app.renderUserBlock === "function") window.app.renderUserBlock();
1979
- }
1980
- });
1957
+ // Customize 3D avatar · opens the shared editor in "user" mode. The
1958
+ // editor saves the rendered PNG + config to prefs (PUT /api/prefs) and
1959
+ // fires "pb:user-avatar-updated"; we repaint the frame on that event.
1960
+ const a3dBtn = paneEl.querySelector("[data-us-avatar3d]");
1961
+ if (a3dBtn) {
1962
+ a3dBtn.addEventListener("click", (e) => {
1963
+ e.preventDefault();
1964
+ if (typeof window.openAvatar3DEditor === "function") window.openAvatar3DEditor({ kind: "user" });
1965
+ });
1966
+ }
1981
1967
 
1982
1968
  paintUserAvatar();
1983
1969
  }