privateboard 0.1.29 → 0.1.32

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/version.d.ts CHANGED
@@ -12,6 +12,6 @@
12
12
  * number ends up surfaced in the user-facing footer or banner. Keep
13
13
  * this file as the canonical source — every callsite reads from here.
14
14
  */
15
- declare const VERSION = "0.1.29";
15
+ declare const VERSION = "0.1.32";
16
16
 
17
17
  export { VERSION };
package/dist/version.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/version.ts
4
- var VERSION = "0.1.29";
4
+ var VERSION = "0.1.32";
5
5
  export {
6
6
  VERSION
7
7
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts"],"sourcesContent":["/**\n * Single source of truth for the app version.\n *\n * Imported by `cli.ts` (CLI banner / `--version`), `server.ts` (the\n * `/health` payload + the `/api/version` endpoint), and bundled into\n * the frontend via the version endpoint. Bump alongside `package.json`\n * on every release — the existing `npm version <patch|minor|major>`\n * + commit pattern updates package.json automatically; this file\n * needs the matching manual bump.\n *\n * If two strings drift (bumped one but not the other), the wrong\n * number ends up surfaced in the user-facing footer or banner. Keep\n * this file as the canonical source — every callsite reads from here.\n */\nexport const VERSION = \"0.1.29\";\n"],"mappings":";;;AAcO,IAAM,UAAU;","names":[]}
1
+ {"version":3,"sources":["../src/version.ts"],"sourcesContent":["/**\n * Single source of truth for the app version.\n *\n * Imported by `cli.ts` (CLI banner / `--version`), `server.ts` (the\n * `/health` payload + the `/api/version` endpoint), and bundled into\n * the frontend via the version endpoint. Bump alongside `package.json`\n * on every release — the existing `npm version <patch|minor|major>`\n * + commit pattern updates package.json automatically; this file\n * needs the matching manual bump.\n *\n * If two strings drift (bumped one but not the other), the wrong\n * number ends up surfaced in the user-facing footer or banner. Keep\n * this file as the canonical source — every callsite reads from here.\n */\nexport const VERSION = \"0.1.32\";\n"],"mappings":";;;AAcO,IAAM,UAAU;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "privateboard",
3
- "version": "0.1.29",
3
+ "version": "0.1.32",
4
4
  "description": "PrivateBoard · your private board meeting, on call. Local-first, multi-agent thinking amplifier.",
5
5
  "type": "module",
6
6
  "main": "electron-entry.cjs",
@@ -57,6 +57,7 @@
57
57
  "commander": "^12.1.0",
58
58
  "electron-updater": "^6.8.3",
59
59
  "hono": "^4.6.14",
60
+ "mp4-muxer": "^5.2.2",
60
61
  "open": "^10.1.0",
61
62
  "three": "^0.184.0",
62
63
  "tiny-pinyin": "^1.3.2",
@@ -469,14 +469,75 @@
469
469
  text-transform: uppercase;
470
470
  color: var(--text-soft, rgba(255, 255, 255, 0.55));
471
471
  }
472
+ /* Spine + house-style dropdowns · unified visual register that
473
+ matches the rest of the app's custom-dropdown vocabulary
474
+ (`.ap-model-trigger` in agent-profile, the `us-models-default`
475
+ row in user-settings). The wrapper provides the ▾ caret on the
476
+ right and a consistent dark surface; the native <select>
477
+ inside is `appearance: none` and inherits its frame from the
478
+ wrapper. Both spine and house-style dropdowns render
479
+ identically when closed — when open, the native option menu
480
+ takes over (browser-level chrome we can't easily restyle), but
481
+ the closed-state register matches everything else. */
482
+ .adjourn-render-select-wrap {
483
+ position: relative;
484
+ display: flex;
485
+ align-items: stretch;
486
+ width: 100%;
487
+ background: var(--bg);
488
+ border: 0.5px solid var(--line-bright, rgba(255, 255, 255, 0.18));
489
+ transition: border-color 0.12s, background 0.12s;
490
+ }
491
+ .adjourn-render-select-wrap:hover {
492
+ background: var(--panel);
493
+ border-color: var(--line-strong, rgba(255, 255, 255, 0.32));
494
+ }
495
+ .adjourn-render-select-wrap:focus-within {
496
+ border-color: var(--lime);
497
+ }
498
+ /* ▾ caret · same glyph + colour token as `.us-models-default-wrap`
499
+ so the visual matches across the app. `pointer-events: none`
500
+ lets clicks fall through to the <select> behind it. */
501
+ .adjourn-render-select-wrap::after {
502
+ content: "▾";
503
+ position: absolute;
504
+ right: 10px;
505
+ top: 50%;
506
+ transform: translateY(-50%);
507
+ color: var(--text-faint);
508
+ font-size: 10px;
509
+ line-height: 1;
510
+ pointer-events: none;
511
+ }
472
512
  .adjourn-render-select {
513
+ appearance: none;
514
+ -webkit-appearance: none;
515
+ -moz-appearance: none;
473
516
  width: 100%;
474
- font-family: var(--mono);
517
+ font-family: var(--mono, "Inter", system-ui, sans-serif);
475
518
  font-size: 11px;
476
- padding: 6px 8px;
519
+ font-weight: 600;
520
+ letter-spacing: 0.02em;
521
+ /* Right padding leaves room for the wrapper's ▾ caret. */
522
+ padding: 7px 26px 7px 10px;
477
523
  background: transparent;
478
524
  color: var(--text);
479
- border: 1px solid var(--line-strong, rgba(255, 255, 255, 0.2));
525
+ border: 0;
526
+ outline: none;
527
+ cursor: pointer;
528
+ }
529
+ .adjourn-render-select::-ms-expand { display: none; }
530
+ /* Option styling · most browsers ignore most rules here (the open
531
+ menu is OS-rendered), but Firefox + Safari respect a few — set
532
+ what we can so the menu reads as panel-surface dark when the
533
+ browser permits it. */
534
+ .adjourn-render-select option {
535
+ background: var(--panel);
536
+ color: var(--text);
537
+ }
538
+ .adjourn-render-select option:checked {
539
+ background: var(--panel-2);
540
+ color: var(--lime);
480
541
  }
481
542
  .adjourn-render-hint {
482
543
  font-size: 11px;
@@ -1794,6 +1794,7 @@
1794
1794
  /* Intel · bio prose. The .ap-intel-view variant matches body but
1795
1795
  rendered inside the .ap-intel container so the edit toggle can swap
1796
1796
  it with a textarea (mirrors the Instruction edit pattern). */
1797
+ .ap-intel { display: flex; flex-direction: column; gap: 0; }
1797
1798
  .ap-intel-view,
1798
1799
  .ap-intel-body {
1799
1800
  font-family: var(--font-human);
@@ -1802,6 +1803,55 @@
1802
1803
  color: var(--text);
1803
1804
  letter-spacing: -0.003em;
1804
1805
  }
1806
+ /* Collapsed default · 3 lines of body copy (14px × 1.55 line-height
1807
+ ≈ 22 px/line → 68 px). JS adds an .overflowing class on the
1808
+ parent when scrollHeight exceeds this, which reveals the fade
1809
+ gradient and the show-more toggle. */
1810
+ .ap-intel-view {
1811
+ position: relative;
1812
+ max-height: 68px;
1813
+ overflow: hidden;
1814
+ transition: max-height 0.22s ease;
1815
+ }
1816
+ .ap-intel-view.expanded {
1817
+ max-height: none;
1818
+ }
1819
+ /* Fade-out gradient at the bottom edge while collapsed — fades to
1820
+ the parent .ap-block panel colour so the cut reads as "more
1821
+ below" rather than a sharp line. */
1822
+ .ap-intel-view::after {
1823
+ content: "";
1824
+ position: absolute;
1825
+ left: 0;
1826
+ right: 0;
1827
+ bottom: 0;
1828
+ height: 28px;
1829
+ pointer-events: none;
1830
+ opacity: 0;
1831
+ transition: opacity 0.15s ease;
1832
+ background: linear-gradient(to bottom, transparent, var(--panel-2));
1833
+ }
1834
+ .ap-intel.overflowing .ap-intel-view:not(.expanded)::after { opacity: 1; }
1835
+ /* Toggle button · hidden unless content actually overflows. Same
1836
+ mono-uppercase look as `.ap-instr-toggle` for visual consistency. */
1837
+ .ap-intel-toggle {
1838
+ display: none;
1839
+ align-self: flex-start;
1840
+ margin-top: 10px;
1841
+ font-family: var(--mono);
1842
+ font-size: 10px;
1843
+ font-weight: 700;
1844
+ letter-spacing: 0.14em;
1845
+ text-transform: uppercase;
1846
+ padding: 5px 10px;
1847
+ background: transparent;
1848
+ border: 0.5px solid var(--line-bright);
1849
+ color: var(--text-soft);
1850
+ cursor: pointer;
1851
+ transition: border-color 0.1s, color 0.1s;
1852
+ }
1853
+ .ap-intel-toggle:hover { border-color: var(--text-soft); color: var(--text); }
1854
+ .ap-intel.overflowing .ap-intel-toggle { display: inline-block; }
1805
1855
 
1806
1856
  /* Skills · 4 progress bars */
1807
1857
  .ap-skill-rows { display: flex; flex-direction: column; gap: 9px; }
@@ -929,13 +929,56 @@
929
929
  function repaintIntel(slug, p) {
930
930
  const block = document.querySelector(`[data-ap-intel][data-slug="${slug}"]`);
931
931
  if (!block) return;
932
+ block.classList.remove("overflowing");
932
933
  const bio = bioFor(slug, p);
933
934
  block.innerHTML = `
934
935
  <div class="ap-intel-view" data-ap-intel-view>${
935
936
  escape(bio) || `<span class="ap-empty">${escape(uiT("ap_intel_empty"))}</span>`
936
937
  }</div>
938
+ <button type="button" class="ap-intel-toggle" data-ap-intel-toggle aria-expanded="false">${escape(uiT("ap_show_more"))}</button>
937
939
  `;
940
+ evaluateIntelOverflow(slug);
941
+ }
942
+
943
+ /** After rendering, measure whether the intel view exceeds its
944
+ * collapsed 3-line max-height. If so, mark the block as
945
+ * overflowing — that reveals the toggle button + fade gradient
946
+ * via CSS. Mirrors `evaluateInstructionOverflow` but with the
947
+ * 3-line cap defined in `.ap-intel-view` CSS. Re-evaluated on
948
+ * every repaint and again on window resize (the rendered width
949
+ * changes, so the wrapped line count can change too). */
950
+ function evaluateIntelOverflow(slug) {
951
+ const block = document.querySelector(`[data-ap-intel][data-slug="${slug}"]`);
952
+ if (!block) return;
953
+ const view = block.querySelector("[data-ap-intel-view]");
954
+ const toggle = block.querySelector("[data-ap-intel-toggle]");
955
+ if (!view || !toggle) return;
956
+ // Reset to collapsed default before measuring — prevents stale
957
+ // 'expanded' state from a prior interaction shadowing the check.
958
+ view.classList.remove("expanded");
959
+ toggle.setAttribute("aria-expanded", "false");
960
+ toggle.textContent = uiT("ap_show_more");
961
+ if (view.scrollHeight - view.clientHeight > 4) {
962
+ block.classList.add("overflowing");
963
+ } else {
964
+ block.classList.remove("overflowing");
965
+ }
938
966
  }
967
+ // Re-evaluate every visible intel block when the layout reflows
968
+ // (sidebar resize, window resize). Debounced so resize storms
969
+ // don't trip us — one tick after the resize ends. Same pattern
970
+ // as the Instruction resize listener directly below the
971
+ // `evaluateInstructionOverflow` definition.
972
+ let _intelResizeTimer = null;
973
+ window.addEventListener("resize", () => {
974
+ if (_intelResizeTimer) clearTimeout(_intelResizeTimer);
975
+ _intelResizeTimer = setTimeout(() => {
976
+ document.querySelectorAll("[data-ap-intel]").forEach((b) => {
977
+ const slug = b.getAttribute("data-slug");
978
+ if (slug) evaluateIntelOverflow(slug);
979
+ });
980
+ }, 80);
981
+ });
939
982
 
940
983
  function openIntelEditor(slug, p) {
941
984
  const block = document.querySelector(`[data-ap-intel][data-slug="${slug}"]`);
@@ -3358,6 +3401,7 @@
3358
3401
  </header>
3359
3402
  <div class="ap-intel" data-ap-intel data-slug="${escape(slug)}">
3360
3403
  <div class="ap-intel-view" data-ap-intel-view>${escape(bioBody) || `<span class="ap-empty">${escape(uiT("ap_intel_empty"))}</span>`}</div>
3404
+ <button type="button" class="ap-intel-toggle" data-ap-intel-toggle aria-expanded="false">${escape(uiT("ap_show_more"))}</button>
3361
3405
  </div>
3362
3406
  </section>
3363
3407
 
@@ -3611,6 +3655,8 @@
3611
3655
  // Detect whether the instruction prose exceeds the collapsed cap.
3612
3656
  // Has to run AFTER innerHTML mounts (so layout/wrapping is real).
3613
3657
  evaluateInstructionOverflow(slug);
3658
+ // Same overflow detection for the Intel bio (3-line clamp).
3659
+ evaluateIntelOverflow(slug);
3614
3660
  // Lazy-load Track Record counters (rooms / rounds / tokens). Runs
3615
3661
  // off the main paint thread; placeholders ("—") show until the
3616
3662
  // fetch resolves so the layout never reflows.
@@ -4559,6 +4605,22 @@
4559
4605
  instrToggle.textContent = expanded ? uiT("ap_show_less") : uiT("ap_show_more");
4560
4606
  return;
4561
4607
  }
4608
+ // Intel · same show-more / show-less pattern as Instruction.
4609
+ // CSS clamps `.ap-intel-view` to a 3-line max-height by default;
4610
+ // the .expanded class lifts the cap. Toggle button visibility
4611
+ // is gated by `.ap-intel.overflowing` (set in
4612
+ // evaluateIntelOverflow).
4613
+ const intelToggle = e.target.closest("[data-ap-intel-toggle]");
4614
+ if (intelToggle) {
4615
+ e.preventDefault();
4616
+ const block = intelToggle.closest("[data-ap-intel]");
4617
+ const view = block?.querySelector("[data-ap-intel-view]");
4618
+ if (!view) return;
4619
+ const expanded = view.classList.toggle("expanded");
4620
+ intelToggle.setAttribute("aria-expanded", String(expanded));
4621
+ intelToggle.textContent = expanded ? uiT("ap_show_less") : uiT("ap_show_more");
4622
+ return;
4623
+ }
4562
4624
 
4563
4625
  // Rules · add a new empty row, then focus its input.
4564
4626
  const addRuleBtn = e.target.closest("[data-ap-rule-add]");