privateboard 0.1.6 → 0.1.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "privateboard",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "PrivateBoard · your private board meeting, on call. Local-first, multi-agent thinking amplifier.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,7 +18,9 @@
18
18
  "start": "node dist/cli.js",
19
19
  "test": "vitest run",
20
20
  "test:watch": "vitest",
21
- "prepublishOnly": "npm run build"
21
+ "sync-version": "node scripts/sync-version.mjs",
22
+ "regen-avatars": "node scripts/regen-avatars.mjs",
23
+ "prepublishOnly": "npm run sync-version && npm run build"
22
24
  },
23
25
  "repository": {
24
26
  "type": "git",
@@ -168,6 +168,45 @@
168
168
  line-height: 1.4;
169
169
  word-break: break-word;
170
170
  }
171
+
172
+ /* Subject row · the subject text can run several lines on long room
173
+ titles. We clamp to 3 lines by default and reveal a Show more /
174
+ less toggle only when the text actually overflows (the toggle is
175
+ `hidden` at render time and unhidden by openAdjournOverlay's post-
176
+ mount measurement). */
177
+ .adjourn-subject-wrap {
178
+ display: flex;
179
+ flex-direction: column;
180
+ gap: 6px;
181
+ min-width: 0;
182
+ }
183
+ .adjourn-subject-text {
184
+ display: block;
185
+ white-space: pre-wrap;
186
+ }
187
+ .adjourn-subject-text.is-clamped {
188
+ display: -webkit-box;
189
+ -webkit-line-clamp: 3;
190
+ -webkit-box-orient: vertical;
191
+ overflow: hidden;
192
+ }
193
+ .adjourn-subject-toggle {
194
+ align-self: flex-start;
195
+ appearance: none;
196
+ background: transparent;
197
+ border: none;
198
+ font-family: var(--mono);
199
+ font-size: 9.5px;
200
+ letter-spacing: 0.14em;
201
+ text-transform: uppercase;
202
+ color: var(--text-soft);
203
+ cursor: pointer;
204
+ padding: 0;
205
+ transition: color 0.12s;
206
+ }
207
+ .adjourn-subject-toggle:hover { color: var(--lime); }
208
+ .adjourn-subject-toggle::before { content: "[ "; color: var(--text-faint); }
209
+ .adjourn-subject-toggle::after { content: " ]"; color: var(--text-faint); }
171
210
  .adjourn-summary-note {
172
211
  margin: 0;
173
212
  font-family: var(--font-human);
@@ -177,6 +216,161 @@
177
216
  letter-spacing: -0.003em;
178
217
  }
179
218
 
219
+ /* ─── Report-mode picker · two cards radio-style. Sits between the
220
+ summary note and the foot actions. Selected option gets a left
221
+ accent stripe + tinted bg; unselected stays quiet. The radio inputs
222
+ are visually hidden (the label IS the click target) but stay in the
223
+ DOM for accessibility / keyboard nav. ─────────────────────────── */
224
+ .adjourn-mode-picker {
225
+ margin-top: 16px;
226
+ padding-top: 14px;
227
+ border-top: 0.5px solid var(--line);
228
+ }
229
+ .adjourn-mode-label {
230
+ font-family: var(--mono);
231
+ font-size: 9.5px;
232
+ letter-spacing: 0.14em;
233
+ color: var(--text-faint);
234
+ margin-bottom: 8px;
235
+ text-transform: uppercase;
236
+ }
237
+ .adjourn-mode-options {
238
+ display: grid;
239
+ grid-template-columns: 1fr 1fr;
240
+ gap: 10px;
241
+ }
242
+ .adjourn-mode-options-3 {
243
+ grid-template-columns: 1fr 1fr 1fr;
244
+ }
245
+ .adjourn-mode-options-4 {
246
+ grid-template-columns: 1fr 1fr;
247
+ }
248
+ /* At desktop width the primary tile (Report · the long-form
249
+ markdown mode) takes 1.5× the width of the other three so it
250
+ reads as the default / recommended choice. The implementation
251
+ piggybacks on grid auto-placement: the primary is always the
252
+ first child, so giving column 1 a 1.5fr track is enough. */
253
+ @media (min-width: 800px) {
254
+ .adjourn-mode-options-4 { grid-template-columns: 1.5fr 1fr 1fr 1fr; }
255
+ }
256
+ @media (max-width: 720px) {
257
+ .adjourn-mode-options-3 { grid-template-columns: 1fr 1fr; }
258
+ }
259
+ @media (max-width: 540px) {
260
+ .adjourn-mode-options { grid-template-columns: 1fr; }
261
+ .adjourn-mode-options-3 { grid-template-columns: 1fr; }
262
+ .adjourn-mode-options-4 { grid-template-columns: 1fr; }
263
+ }
264
+ .adjourn-mode-option {
265
+ position: relative;
266
+ display: flex;
267
+ flex-direction: column;
268
+ align-items: stretch;
269
+ text-align: center;
270
+ gap: 8px;
271
+ padding: 14px 10px 12px;
272
+ background: var(--panel-2);
273
+ border: 1px solid var(--line);
274
+ cursor: pointer;
275
+ transition: border-color 0.12s, color 0.12s;
276
+ min-height: 130px;
277
+ }
278
+ .adjourn-mode-option:hover {
279
+ border-color: var(--line-bright);
280
+ }
281
+ /* Selected state · just the border + icon shift to lime. No
282
+ background tint, no transform · the visual delta against
283
+ neighbour tiles is enough on its own. */
284
+ .adjourn-mode-option.on {
285
+ border-color: var(--lime);
286
+ }
287
+ /* The native radio is the source of truth for `:checked` state and
288
+ keyboard navigation, but visually it's hidden — the whole tile IS
289
+ the click target. Keeping it in the DOM (at 1×1 absolutely-
290
+ positioned) preserves a11y without occupying layout space. */
291
+ .adjourn-mode-option input[type="radio"] {
292
+ position: absolute;
293
+ width: 1px;
294
+ height: 1px;
295
+ margin: 0;
296
+ padding: 0;
297
+ border: 0;
298
+ opacity: 0;
299
+ pointer-events: none;
300
+ }
301
+ /* Keyboard focus only · `:has(input:focus-visible)` skips the ring
302
+ on plain mouse clicks (which already get the lime border + icon
303
+ feedback) so a clicked tile doesn't show a doubled outline. */
304
+ .adjourn-mode-option:has(input:focus-visible) {
305
+ outline: 1px solid var(--lime);
306
+ outline-offset: 1px;
307
+ }
308
+
309
+ /* Icon block · 40px high, centered. Color inherits from the parent
310
+ so hover / selected states cascade through `currentColor` in the
311
+ SVG without per-mode overrides. */
312
+ .adjourn-mode-icon {
313
+ display: flex;
314
+ align-items: center;
315
+ justify-content: center;
316
+ height: 44px;
317
+ margin-bottom: 2px;
318
+ color: var(--text-soft);
319
+ transition: color 0.12s;
320
+ }
321
+ .adjourn-mode-icon svg {
322
+ width: 36px;
323
+ height: 36px;
324
+ display: block;
325
+ }
326
+ .adjourn-mode-option:hover .adjourn-mode-icon {
327
+ color: var(--text);
328
+ }
329
+ .adjourn-mode-option.on .adjourn-mode-icon {
330
+ color: var(--lime);
331
+ }
332
+
333
+ /* Primary tile · the Report mode is the recommended default, so it
334
+ gets a slightly larger icon + a touch more padding to read as the
335
+ "main" choice without changing its visual register. */
336
+ .adjourn-mode-option-primary {
337
+ padding: 16px 14px 14px;
338
+ }
339
+ .adjourn-mode-option-primary .adjourn-mode-icon {
340
+ height: 50px;
341
+ }
342
+ .adjourn-mode-option-primary .adjourn-mode-icon svg {
343
+ width: 42px;
344
+ height: 42px;
345
+ }
346
+
347
+ .adjourn-mode-body {
348
+ display: flex;
349
+ flex-direction: column;
350
+ gap: 3px;
351
+ min-width: 0;
352
+ }
353
+ .adjourn-mode-title {
354
+ font-family: var(--font-human);
355
+ font-size: 13px;
356
+ font-weight: 600;
357
+ color: var(--text);
358
+ letter-spacing: -0.005em;
359
+ line-height: 1.2;
360
+ }
361
+ .adjourn-mode-deck {
362
+ font-family: var(--font-human);
363
+ font-size: 11px;
364
+ line-height: 1.4;
365
+ color: var(--text-faint);
366
+ /* Clamp to 2 lines so 4 tiles align to the same height regardless
367
+ of deck length; longer decks ellipsis cleanly. */
368
+ display: -webkit-box;
369
+ -webkit-line-clamp: 2;
370
+ -webkit-box-orient: vertical;
371
+ overflow: hidden;
372
+ }
373
+
180
374
  /* ─── Footer ─── skip-button on the left, primary actions on the right.
181
375
  The skip moves out of the gallery-body and into a real button slot
182
376
  so the visual weight matches Cancel / Adjourn (peers in size and
@@ -2389,6 +2389,14 @@
2389
2389
  document.querySelectorAll("[data-notes-trigger].active").forEach((el) => el.classList.remove("active"));
2390
2390
  document.querySelectorAll("[data-reports-trigger].active").forEach((el) => el.classList.remove("active"));
2391
2391
  currentlyOpenSlug = null;
2392
+ // Clear the no-room flag IFF there's an actual room loaded · the
2393
+ // floating sidebar-expand button shouldn't show on top of a real
2394
+ // room view (the in-header expand button takes over there). When
2395
+ // showRoom fires without an active room (e.g. bouncing back to
2396
+ // the empty-state composer), keep no-room set so the expand
2397
+ // control stays reachable.
2398
+ const hasRoom = window.app && window.app.currentRoomId;
2399
+ if (hasRoom) document.documentElement.classList.remove("no-room");
2392
2400
  }
2393
2401
 
2394
2402
  /** Build a minimal profile object from a live /api/agents record so
@@ -2474,6 +2482,12 @@
2474
2482
  // notes empty-state through the agent view.
2475
2483
  if (v.reports) v.reports.setAttribute("hidden", "");
2476
2484
  if (v.notes) v.notes.setAttribute("hidden", "");
2485
+ // The floating sidebar-expand button is gated on `html.no-room`
2486
+ // — without setting it here, a user who collapses the sidebar
2487
+ // while on an agent profile loses the expand control and has to
2488
+ // navigate back to a room view to recover it. Same logic that
2489
+ // app.js applies in openAllReports / openAllNotes.
2490
+ document.documentElement.classList.add("no-room");
2477
2491
  document.querySelectorAll("[data-notes-trigger].active").forEach((el) => el.classList.remove("active"));
2478
2492
  document.querySelectorAll("[data-reports-trigger].active").forEach((el) => el.classList.remove("active"));
2479
2493
  v.agent.removeAttribute("hidden");