privateboard 0.1.5 → 0.1.7

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.
@@ -162,6 +162,43 @@
162
162
  .us-nav-item.danger { color: var(--text-faint, #3A382F); }
163
163
  .us-nav-item.danger:hover { color: var(--red, #B5706A); }
164
164
 
165
+ /* ─── Sidebar foot · app version stamp ───
166
+ Pinned to the bottom of the column flex via `margin-top: auto` so
167
+ it sits below every nav item even when the section list is short.
168
+ Tiny mono kicker · matches the section-tag typography elsewhere in
169
+ the overlay so it reads as quiet metadata, not a sixth nav item. */
170
+ .us-nav-foot {
171
+ margin-top: auto;
172
+ padding: 12px 12px 4px;
173
+ display: flex;
174
+ flex-direction: column;
175
+ gap: 2px;
176
+ align-items: flex-start;
177
+ border-top: 0.5px solid var(--line-bright, #2A2A26);
178
+ font-family: var(--mono);
179
+ }
180
+ .us-nav-foot-label {
181
+ font-size: 8.5px;
182
+ letter-spacing: 0.22em;
183
+ text-transform: uppercase;
184
+ color: var(--text-faint, #3A382F);
185
+ font-weight: 600;
186
+ }
187
+ .us-nav-foot-value {
188
+ font-size: 11px;
189
+ letter-spacing: 0.04em;
190
+ color: var(--text-soft, #8E8B83);
191
+ font-weight: 700;
192
+ font-variant-numeric: tabular-nums;
193
+ }
194
+ @media (max-width: 600px) {
195
+ /* On the horizontal-row mobile nav layout, the foot would steal a
196
+ slot in the scrollable tab strip — hide it there. The version
197
+ still surfaces via the running server's /api/version endpoint
198
+ for anyone who needs it. */
199
+ .us-nav-foot { display: none; }
200
+ }
201
+
165
202
  /* Right pane (scrollable section content) */
166
203
  .us-pane {
167
204
  padding: 18px 22px 18px;
@@ -224,6 +261,59 @@
224
261
  margin-top: 5px;
225
262
  }
226
263
 
264
+ /* ─── Toggle row · pill button + supporting deck text ───
265
+ Matches the visual vocabulary of the day-picker pills (mono
266
+ uppercase, hairline border, lime accent when on) so the User pane
267
+ doesn't introduce a third button style for one toggle. */
268
+ .us-toggle-row {
269
+ display: flex;
270
+ align-items: center;
271
+ gap: 12px;
272
+ flex-wrap: wrap;
273
+ }
274
+ .us-toggle-pill {
275
+ appearance: none;
276
+ background: transparent;
277
+ border: 0.5px solid var(--line-bright, #2A2A26);
278
+ color: var(--text-soft, #8E8B83);
279
+ font-family: var(--mono);
280
+ font-size: 10.5px;
281
+ letter-spacing: 0.18em;
282
+ text-transform: uppercase;
283
+ font-weight: 700;
284
+ padding: 6px 14px;
285
+ display: inline-flex;
286
+ align-items: center;
287
+ gap: 8px;
288
+ cursor: pointer;
289
+ transition: color 0.12s, border-color 0.12s, background 0.12s;
290
+ }
291
+ .us-toggle-pill:hover {
292
+ color: var(--text, #C8C5BE);
293
+ border-color: var(--text-faint, #3A382F);
294
+ }
295
+ .us-toggle-pill.on {
296
+ color: var(--lime, #6FB572);
297
+ border-color: var(--lime, #6FB572);
298
+ background: var(--panel-2, #1A1A18);
299
+ }
300
+ .us-toggle-dot {
301
+ width: 6px;
302
+ height: 6px;
303
+ border-radius: 50%;
304
+ background: var(--text-faint, #3A382F);
305
+ transition: background 0.12s;
306
+ }
307
+ .us-toggle-pill.on .us-toggle-dot { background: var(--lime, #6FB572); }
308
+ .us-toggle-deck {
309
+ font-size: 11px;
310
+ color: var(--text-faint, #3A382F);
311
+ letter-spacing: 0.02em;
312
+ line-height: 1.5;
313
+ flex: 1 1 220px;
314
+ min-width: 0;
315
+ }
316
+
227
317
  /* User avatar block */
228
318
  .us-avatar-row {
229
319
  display: flex;
@@ -225,6 +225,19 @@
225
225
  <div class="us-row-meta"><span data-us-intro-count>0</span> / 320 chars</div>
226
226
  </div>
227
227
  </div>
228
+
229
+ <div class="us-row">
230
+ <div class="us-row-label">Typing sound</div>
231
+ <div class="us-row-field">
232
+ <div class="us-toggle-row">
233
+ <button type="button" class="us-toggle-pill" data-us-sfx-typing aria-pressed="false">
234
+ <span class="us-toggle-dot"></span>
235
+ <span class="us-toggle-label" data-us-sfx-typing-label>off</span>
236
+ </button>
237
+ <span class="us-toggle-deck">a soft keyboard click as directors stream their replies. Persists locally.</span>
238
+ </div>
239
+ </div>
240
+ </div>
228
241
  </div>
229
242
  `;
230
243
  }
@@ -930,6 +943,10 @@
930
943
  <a href="#" class="us-nav-item" data-section="usage" role="tab" aria-selected="false">Usage</a>
931
944
  <a href="#" class="us-nav-item" data-section="keys" role="tab" aria-selected="false">API Key</a>
932
945
  <a href="#" class="us-nav-item" data-section="default" role="tab" aria-selected="false">Default Model</a>
946
+ <div class="us-nav-foot" data-us-version aria-label="App version">
947
+ <span class="us-nav-foot-label">version</span>
948
+ <span class="us-nav-foot-value" data-us-version-value>·</span>
949
+ </div>
933
950
  </nav>
934
951
 
935
952
  <div class="us-pane" data-us-pane></div>
@@ -1018,6 +1035,32 @@
1018
1035
  });
1019
1036
  introCount.textContent = introInput.value.length;
1020
1037
 
1038
+ // Typing-sound toggle · the persistence + audio context lives in
1039
+ // window.boardroomTypingSfx (typing-sfx.js), so this row only
1040
+ // mirrors the current state and proxies clicks. Reading inside the
1041
+ // wire-up call (not at HTML build time) means the pill always
1042
+ // reflects the LATEST stored state when the User pane re-mounts.
1043
+ const sfxBtn = paneEl.querySelector("[data-us-sfx-typing]");
1044
+ const sfxLabel = paneEl.querySelector("[data-us-sfx-typing-label]");
1045
+ if (sfxBtn && sfxLabel && window.boardroomTypingSfx) {
1046
+ const paint = () => {
1047
+ const on = window.boardroomTypingSfx.isEnabled();
1048
+ sfxBtn.classList.toggle("on", on);
1049
+ sfxBtn.setAttribute("aria-pressed", on ? "true" : "false");
1050
+ sfxLabel.textContent = on ? "on" : "off";
1051
+ };
1052
+ paint();
1053
+ sfxBtn.addEventListener("click", () => {
1054
+ const next = !window.boardroomTypingSfx.isEnabled();
1055
+ window.boardroomTypingSfx.setEnabled(next);
1056
+ paint();
1057
+ // Audible confirmation when turning ON · the click that just
1058
+ // toggled also serves as the gesture the AudioContext needs,
1059
+ // so this tick will actually be heard.
1060
+ if (next) window.boardroomTypingSfx.tick();
1061
+ });
1062
+ }
1063
+
1021
1064
  // Regenerate avatar · same pattern as agent-profile's
1022
1065
  // regenerateProfileAvatar: pull a fresh randomSeed, persist it to
1023
1066
  // the user prefs, repaint. No counter, no name/intro composition —
@@ -1219,6 +1262,34 @@
1219
1262
  overlay.classList.add("open");
1220
1263
  overlay.setAttribute("aria-hidden", "false");
1221
1264
  document.body.style.overflow = "hidden";
1265
+ // Lazy-fetch the version string on each open. Cheap (one tiny
1266
+ // request, no DB hit) and the user expects the foot to reflect
1267
+ // the running server, not a baked-in client constant — if they
1268
+ // upgrade the npm package and a tab is still open, the next
1269
+ // overlay open shows the new version.
1270
+ fetchAppVersion();
1271
+ }
1272
+
1273
+ let _versionCache = null;
1274
+ async function fetchAppVersion() {
1275
+ const slot = overlay && overlay.querySelector("[data-us-version-value]");
1276
+ if (!slot) return;
1277
+ // Use cache if we already have it · the version doesn't change
1278
+ // mid-process. First open does the network round-trip; subsequent
1279
+ // opens repaint from cache instantly.
1280
+ if (_versionCache) {
1281
+ slot.textContent = _versionCache;
1282
+ return;
1283
+ }
1284
+ try {
1285
+ const r = await fetch("/api/version");
1286
+ if (!r.ok) return;
1287
+ const j = await r.json();
1288
+ if (j && typeof j.version === "string") {
1289
+ _versionCache = `v${j.version}`;
1290
+ slot.textContent = _versionCache;
1291
+ }
1292
+ } catch { /* swallow · the foot just stays at "·" if offline */ }
1222
1293
  }
1223
1294
  function close() {
1224
1295
  if (!overlay) return;