privateboard 0.1.31 → 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/boot.js +5 -1
- package/dist/boot.js.map +1 -1
- package/dist/cli.js +5 -1
- package/dist/cli.js.map +1 -1
- package/dist/server.js +5 -1
- 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 +2 -1
- package/public/app.js +9 -108
- package/public/home.html +1 -1
- package/public/i18n.js +8 -8
- package/public/index.html +88 -168
- package/public/onboarding.css +8 -0
- package/public/themes.css +37 -6
- package/public/typing-sfx.js +32 -2
- package/public/vendor/mp4-muxer.min.js +1904 -0
- package/public/voice-3d-banner.js +21 -0
- package/public/voice-onboarding.css +7 -0
- package/public/voice-recorder.js +427 -81
package/public/index.html
CHANGED
|
@@ -713,6 +713,7 @@
|
|
|
713
713
|
padding: 0;
|
|
714
714
|
background: transparent;
|
|
715
715
|
border: none;
|
|
716
|
+
opacity: 0;
|
|
716
717
|
pointer-events: none;
|
|
717
718
|
-webkit-app-region: drag;
|
|
718
719
|
z-index: 50;
|
|
@@ -1031,7 +1032,6 @@
|
|
|
1031
1032
|
padding: 6px 6px 8px;
|
|
1032
1033
|
flex-shrink: 0;
|
|
1033
1034
|
background: var(--panel);
|
|
1034
|
-
border-bottom: 0.5px solid var(--line);
|
|
1035
1035
|
}
|
|
1036
1036
|
.sidebar-tab {
|
|
1037
1037
|
flex: 1;
|
|
@@ -1067,13 +1067,6 @@
|
|
|
1067
1067
|
font-weight: 600;
|
|
1068
1068
|
}
|
|
1069
1069
|
.sidebar-tab.active .ico { opacity: 1; }
|
|
1070
|
-
.sidebar-tab .count {
|
|
1071
|
-
font-size: 10px;
|
|
1072
|
-
color: var(--text-faint);
|
|
1073
|
-
font-weight: 400;
|
|
1074
|
-
margin-left: 1px;
|
|
1075
|
-
}
|
|
1076
|
-
.sidebar-tab.active .count { color: var(--text-dim); }
|
|
1077
1070
|
|
|
1078
1071
|
/* Sidebar tab panels */
|
|
1079
1072
|
.sidebar-panel {
|
|
@@ -1115,13 +1108,6 @@
|
|
|
1115
1108
|
height: 1px;
|
|
1116
1109
|
background: var(--line-bright);
|
|
1117
1110
|
}
|
|
1118
|
-
.agents-section-header .badge {
|
|
1119
|
-
background: var(--bg);
|
|
1120
|
-
border: 0.5px solid var(--line-bright);
|
|
1121
|
-
padding: 0 5px;
|
|
1122
|
-
font-size: 8px;
|
|
1123
|
-
color: var(--text-dim);
|
|
1124
|
-
}
|
|
1125
1111
|
|
|
1126
1112
|
/* Shell wraps the agent-row link + delete button as siblings — same
|
|
1127
1113
|
pattern as session-row-shell · the X click never gets absorbed by
|
|
@@ -1150,7 +1136,6 @@
|
|
|
1150
1136
|
}
|
|
1151
1137
|
.agent-row:hover { background: var(--panel-2); }
|
|
1152
1138
|
.agent-row.active { background: var(--panel-3); }
|
|
1153
|
-
.agent-row.active .agent-row-title { color: var(--lime); }
|
|
1154
1139
|
|
|
1155
1140
|
/* The inline X delete button on custom agent rows is gone · Delete
|
|
1156
1141
|
now lives in the agent profile's ⋯ overflow menu (see
|
|
@@ -1159,18 +1144,16 @@
|
|
|
1159
1144
|
.agent-row .agent-row-av {
|
|
1160
1145
|
width: 32px;
|
|
1161
1146
|
height: 32px;
|
|
1162
|
-
background: var(--bg);
|
|
1163
|
-
border: 0.5px solid var(--line-bright);
|
|
1164
1147
|
image-rendering: pixelated;
|
|
1165
1148
|
image-rendering: crisp-edges;
|
|
1166
1149
|
flex-shrink: 0;
|
|
1167
1150
|
}
|
|
1168
1151
|
.agent-row .agent-row-content { min-width: 0; }
|
|
1169
|
-
/* Top-line ·
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1152
|
+
/* Top-line · flex container so the agent name + trailing
|
|
1153
|
+
hover-revealed pin button (and chair-badge / pb-row tag in
|
|
1154
|
+
their variants) share one baseline. `flex: 1` on the title
|
|
1155
|
+
pushes the trailing button to the right edge without needing
|
|
1156
|
+
`space-between`. */
|
|
1174
1157
|
.agent-row .agent-row-top-line {
|
|
1175
1158
|
display: flex;
|
|
1176
1159
|
align-items: baseline;
|
|
@@ -1256,7 +1239,6 @@
|
|
|
1256
1239
|
.agent-row.is-chair:hover { background: var(--panel-2); }
|
|
1257
1240
|
.agent-row.is-chair.active { background: var(--panel-3); }
|
|
1258
1241
|
.agent-row.is-chair .agent-row-av.chair-av {
|
|
1259
|
-
border: 0.5px solid var(--line-bright);
|
|
1260
1242
|
position: relative;
|
|
1261
1243
|
}
|
|
1262
1244
|
.agent-row.is-chair .agent-row-av.chair-av::after {
|
|
@@ -1323,8 +1305,6 @@
|
|
|
1323
1305
|
}
|
|
1324
1306
|
.agent-row .agent-row-chair-role { color: var(--text-soft); font-weight: 600; }
|
|
1325
1307
|
.agent-row .agent-row-chair-note { color: var(--text-faint); }
|
|
1326
|
-
/* Hide the Chair section's count badge — there's only ever one. */
|
|
1327
|
-
.agents-section-header.chair .badge { display: none; }
|
|
1328
1308
|
|
|
1329
1309
|
/* "Building" section · in-flight Full persona build placeholder.
|
|
1330
1310
|
Lime accent on the header to distinguish from inert pinned /
|
|
@@ -1336,10 +1316,6 @@
|
|
|
1336
1316
|
background: var(--lime-dim, var(--line));
|
|
1337
1317
|
opacity: 0.4;
|
|
1338
1318
|
}
|
|
1339
|
-
.agents-section-header.building .badge {
|
|
1340
|
-
color: var(--lime);
|
|
1341
|
-
border-color: var(--lime-dim, var(--line));
|
|
1342
|
-
}
|
|
1343
1319
|
/* Building row · same dimensions as a normal agent row but the
|
|
1344
1320
|
avatar slot is a pulsing CRT-style frame instead of a face,
|
|
1345
1321
|
and the row carries a small "BUILD" / "READY" tag chip.
|
|
@@ -1428,8 +1404,12 @@
|
|
|
1428
1404
|
|
|
1429
1405
|
/* Refined "+ New" — list-item style, sentence case, subtle */
|
|
1430
1406
|
.new-btn {
|
|
1431
|
-
margin
|
|
1432
|
-
|
|
1407
|
+
/* Horizontal margin = 6 (session-row-shell margin) + 4
|
|
1408
|
+
(sessions-scroll padding) so the hover background spans the
|
|
1409
|
+
exact same width as the room rows below — without it, the
|
|
1410
|
+
nav buttons hovered 8px wider than the list rows. */
|
|
1411
|
+
margin: 4px 10px 2px;
|
|
1412
|
+
padding: 5px 10px;
|
|
1433
1413
|
background: transparent;
|
|
1434
1414
|
color: var(--text);
|
|
1435
1415
|
border: none;
|
|
@@ -1506,10 +1486,7 @@
|
|
|
1506
1486
|
.new-btn.nav-search::before {
|
|
1507
1487
|
--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'><circle cx='11' cy='11' r='8'/><path d='m21 21-4.3-4.3'/></svg>");
|
|
1508
1488
|
}
|
|
1509
|
-
/* All Reports / All Notes / Search nav-button shape ·
|
|
1510
|
-
badge floats to the right of the label, so the link's flex
|
|
1511
|
-
layout grows to fill the row. `.nav-label` flex-grows,
|
|
1512
|
-
`.nav-count` is auto-sized and hugs the trailing edge. */
|
|
1489
|
+
/* All Reports / All Notes / Search nav-button shape · label-only. */
|
|
1513
1490
|
.new-btn.nav-notes,
|
|
1514
1491
|
.new-btn.nav-reports,
|
|
1515
1492
|
.new-btn.nav-search {
|
|
@@ -1521,49 +1498,20 @@
|
|
|
1521
1498
|
flex: 1;
|
|
1522
1499
|
min-width: 0;
|
|
1523
1500
|
}
|
|
1524
|
-
/* Count badge · subtle mono micro-tag. Uses --text-faint on its own
|
|
1525
|
-
and inherits the link's color cascade on hover/active so the badge
|
|
1526
|
-
tracks the label's lime shift. Hidden until at least one item
|
|
1527
|
-
exists (the `hidden` attr is removed once count > 0). Same visual
|
|
1528
|
-
vocabulary as the section-header .badge below — both render as
|
|
1529
|
-
mono pills so the sidebar reads as one consistent counting
|
|
1530
|
-
system. */
|
|
1531
|
-
.new-btn.nav-notes .nav-count,
|
|
1532
|
-
.new-btn.nav-reports .nav-count,
|
|
1533
|
-
.new-btn.nav-search .nav-count {
|
|
1534
|
-
font-family: var(--mono);
|
|
1535
|
-
font-size: 10px;
|
|
1536
|
-
font-weight: 600;
|
|
1537
|
-
letter-spacing: 0.04em;
|
|
1538
|
-
color: var(--text-faint);
|
|
1539
|
-
padding: 1px 6px;
|
|
1540
|
-
background: var(--panel-3);
|
|
1541
|
-
border-radius: 3px;
|
|
1542
|
-
flex-shrink: 0;
|
|
1543
|
-
transition: color 0.12s, background 0.12s;
|
|
1544
|
-
}
|
|
1545
|
-
.new-btn.nav-notes:hover .nav-count,
|
|
1546
|
-
.new-btn.nav-notes.active .nav-count,
|
|
1547
|
-
.new-btn.nav-reports:hover .nav-count,
|
|
1548
|
-
.new-btn.nav-reports.active .nav-count,
|
|
1549
|
-
.new-btn.nav-search:hover .nav-count,
|
|
1550
|
-
.new-btn.nav-search.active .nav-count {
|
|
1551
|
-
color: var(--lime);
|
|
1552
|
-
background: rgba(190, 242, 100, 0.08);
|
|
1553
|
-
}
|
|
1554
1501
|
.new-btn:hover {
|
|
1555
1502
|
background: var(--panel-2);
|
|
1556
|
-
color: var(--
|
|
1503
|
+
color: var(--text);
|
|
1557
1504
|
}
|
|
1558
1505
|
/* The ::before icon inherits via currentColor — no explicit
|
|
1559
1506
|
hover override needed; setting the parent's color cascades. */
|
|
1560
|
-
/* Active state ·
|
|
1561
|
-
|
|
1507
|
+
/* Active state · same panel as session-row-shell.active, but text
|
|
1508
|
+
stays at the default `--text` (no accent shift) — selection
|
|
1509
|
+
reads as a settled state via the background alone. */
|
|
1562
1510
|
.new-btn.active {
|
|
1563
1511
|
background: var(--panel-3);
|
|
1564
|
-
color: var(--
|
|
1512
|
+
color: var(--text);
|
|
1565
1513
|
}
|
|
1566
|
-
.new-btn.active::before { color: var(--
|
|
1514
|
+
.new-btn.active::before { color: var(--text); }
|
|
1567
1515
|
|
|
1568
1516
|
.sessions-scroll {
|
|
1569
1517
|
flex: 1;
|
|
@@ -1596,11 +1544,11 @@
|
|
|
1596
1544
|
set this to 16px and made the section badges drift 4px
|
|
1597
1545
|
further left than the nav badges. */
|
|
1598
1546
|
padding: 8px 12px 4px 10px;
|
|
1599
|
-
font-size:
|
|
1547
|
+
font-size: 10px;
|
|
1600
1548
|
color: var(--text-dim);
|
|
1601
1549
|
text-transform: uppercase;
|
|
1602
1550
|
letter-spacing: 0.14em;
|
|
1603
|
-
font-weight:
|
|
1551
|
+
font-weight: 600;
|
|
1604
1552
|
/* Sticky · the LIVE / PAUSED / ADJOURNED dividers stay pinned
|
|
1605
1553
|
to the top of the scroll viewport as the user scrolls down
|
|
1606
1554
|
a long room list. Parent `.sessions-scroll` is the scroll
|
|
@@ -1624,7 +1572,6 @@
|
|
|
1624
1572
|
}
|
|
1625
1573
|
.section-header.live { color: var(--lime); }
|
|
1626
1574
|
.section-header.draft { color: var(--amber); }
|
|
1627
|
-
.section-header.paused { color: var(--amber); }
|
|
1628
1575
|
.section-header.adjourned { color: var(--text-dim); }
|
|
1629
1576
|
.section-header.pinned { color: var(--text-soft); }
|
|
1630
1577
|
.row-status {
|
|
@@ -1636,33 +1583,7 @@
|
|
|
1636
1583
|
margin-right: 6px;
|
|
1637
1584
|
}
|
|
1638
1585
|
.row-status.paused { color: var(--amber); }
|
|
1639
|
-
.section-header .line { flex: 1;
|
|
1640
|
-
/* Section badge · same visual vocabulary as .new-btn.nav-* .nav-count
|
|
1641
|
-
above so the sidebar reads as one consistent counting system.
|
|
1642
|
-
Mono pill, panel-3 bg, faint text, rounded — overrides the
|
|
1643
|
-
.section-header parent's sans uppercase letter-spacing because a
|
|
1644
|
-
count is a number, not a label. The .live variant still takes its
|
|
1645
|
-
pulsing dot via ::before. */
|
|
1646
|
-
.section-header .badge {
|
|
1647
|
-
font-family: var(--mono);
|
|
1648
|
-
font-size: 10px;
|
|
1649
|
-
font-weight: 600;
|
|
1650
|
-
letter-spacing: 0.04em;
|
|
1651
|
-
text-transform: none;
|
|
1652
|
-
color: var(--text-faint);
|
|
1653
|
-
padding: 1px 6px;
|
|
1654
|
-
background: var(--panel-3);
|
|
1655
|
-
border-radius: 3px;
|
|
1656
|
-
flex-shrink: 0;
|
|
1657
|
-
}
|
|
1658
|
-
.section-header.live .badge {
|
|
1659
|
-
color: var(--lime);
|
|
1660
|
-
background: rgba(190, 242, 100, 0.08);
|
|
1661
|
-
}
|
|
1662
|
-
.section-header.live .badge::before {
|
|
1663
|
-
content: "● ";
|
|
1664
|
-
animation: pulse 1.5s infinite;
|
|
1665
|
-
}
|
|
1586
|
+
.section-header .line { flex: 1; }
|
|
1666
1587
|
.pin-glyph {
|
|
1667
1588
|
width: 10px;
|
|
1668
1589
|
height: 10px;
|
|
@@ -1677,7 +1598,6 @@
|
|
|
1677
1598
|
reserve 22px and shrink the title). The row-shell ancestor
|
|
1678
1599
|
needs `position: relative` for this to anchor correctly. */
|
|
1679
1600
|
.agent-row-shell, .session-row-shell { position: relative; }
|
|
1680
|
-
.row-top-line .pin-toggle,
|
|
1681
1601
|
.agent-row-top-line .pin-toggle {
|
|
1682
1602
|
position: absolute;
|
|
1683
1603
|
top: 8px;
|
|
@@ -1695,16 +1615,13 @@
|
|
|
1695
1615
|
opacity: 0;
|
|
1696
1616
|
transition: opacity 0.12s, color 0.12s;
|
|
1697
1617
|
}
|
|
1698
|
-
.row-top-line .pin-toggle svg,
|
|
1699
1618
|
.agent-row-top-line .pin-toggle svg {
|
|
1700
1619
|
width: 11px;
|
|
1701
1620
|
height: 11px;
|
|
1702
1621
|
fill: currentColor;
|
|
1703
1622
|
}
|
|
1704
|
-
.session-row:hover .pin-toggle,
|
|
1705
1623
|
.agent-row:hover .pin-toggle { opacity: 0.55; }
|
|
1706
1624
|
.pin-toggle:hover { opacity: 1 !important; color: var(--lime); }
|
|
1707
|
-
.session-row.pinned .pin-toggle,
|
|
1708
1625
|
.agent-row.pinned .pin-toggle {
|
|
1709
1626
|
opacity: 1;
|
|
1710
1627
|
color: var(--lime);
|
|
@@ -1718,21 +1635,20 @@
|
|
|
1718
1635
|
X button all clip cleanly to rounded corners. */
|
|
1719
1636
|
.session-row-shell {
|
|
1720
1637
|
position: relative;
|
|
1721
|
-
margin:
|
|
1638
|
+
margin: 2px 6px;
|
|
1722
1639
|
border-radius: 4px;
|
|
1723
1640
|
overflow: hidden;
|
|
1724
1641
|
}
|
|
1725
1642
|
.session-row-shell.active .session-row { background: var(--panel-3); }
|
|
1726
|
-
.session-row-shell.active .row-title { color: var(--lime); }
|
|
1727
1643
|
|
|
1728
1644
|
.session-row {
|
|
1729
1645
|
display: block;
|
|
1730
|
-
/*
|
|
1731
|
-
rooms-tab lists share the same horizontal rhythm in the
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
padding:
|
|
1646
|
+
/* Horizontal padding kept in sync with .agent-row so the agents-tab
|
|
1647
|
+
and rooms-tab lists share the same horizontal rhythm in the
|
|
1648
|
+
sidebar. Vertical padding 5px — single-line title with the
|
|
1649
|
+
subtitle row removed; tight hitbox matching the .new-btn nav
|
|
1650
|
+
buttons above. */
|
|
1651
|
+
padding: 5px 10px;
|
|
1736
1652
|
text-decoration: none;
|
|
1737
1653
|
color: var(--text);
|
|
1738
1654
|
cursor: pointer;
|
|
@@ -1741,35 +1657,42 @@
|
|
|
1741
1657
|
.session-row:hover { background: var(--panel-2); }
|
|
1742
1658
|
|
|
1743
1659
|
/* Per-row delete button · sibling of the anchor, so its click never
|
|
1744
|
-
gets routed to the link. Hover-revealed in the top-right band.
|
|
1660
|
+
gets routed to the link. Hover-revealed in the top-right band.
|
|
1661
|
+
Icon-only Lucide X painted via `mask-image + currentColor` —
|
|
1662
|
+
same recipe as the `.new-btn::before` nav glyphs, no border or
|
|
1663
|
+
chip chrome. Vertical centring via the symmetric top/bottom +
|
|
1664
|
+
auto margin trick so the glyph stays mid-row regardless of row
|
|
1665
|
+
padding. */
|
|
1745
1666
|
.row-delete {
|
|
1746
1667
|
position: absolute;
|
|
1747
|
-
top:
|
|
1668
|
+
top: 0;
|
|
1669
|
+
bottom: 0;
|
|
1748
1670
|
right: 10px;
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1671
|
+
margin: auto 0;
|
|
1672
|
+
width: 16px;
|
|
1673
|
+
height: 16px;
|
|
1674
|
+
border: 0;
|
|
1675
|
+
padding: 0;
|
|
1676
|
+
background-color: currentColor;
|
|
1752
1677
|
color: var(--text-faint);
|
|
1753
|
-
font-family: var(--mono);
|
|
1754
|
-
font-size: 10px;
|
|
1755
|
-
line-height: 1;
|
|
1756
1678
|
cursor: pointer;
|
|
1757
1679
|
display: none;
|
|
1758
|
-
|
|
1759
|
-
justify-content: center;
|
|
1760
|
-
padding: 0;
|
|
1761
|
-
transition: all 0.12s;
|
|
1680
|
+
transition: color 0.12s;
|
|
1762
1681
|
z-index: 2;
|
|
1682
|
+
-webkit-mask-image: 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='M18 6 6 18'/><path d='m6 6 12 12'/></svg>");
|
|
1683
|
+
mask-image: 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='M18 6 6 18'/><path d='m6 6 12 12'/></svg>");
|
|
1684
|
+
-webkit-mask-repeat: no-repeat;
|
|
1685
|
+
mask-repeat: no-repeat;
|
|
1686
|
+
-webkit-mask-position: center;
|
|
1687
|
+
mask-position: center;
|
|
1688
|
+
-webkit-mask-size: 16px 16px;
|
|
1689
|
+
mask-size: 16px 16px;
|
|
1763
1690
|
}
|
|
1764
|
-
.session-row-shell:hover .row-delete { display:
|
|
1765
|
-
/*
|
|
1691
|
+
.session-row-shell:hover .row-delete { display: block; }
|
|
1692
|
+
/* Row title reserves right padding so its ellipsis stops before
|
|
1766
1693
|
the absolutely-positioned X delete button on hover. */
|
|
1767
|
-
.session-row-shell .row-
|
|
1768
|
-
.row-delete:hover {
|
|
1769
|
-
border-color: var(--red);
|
|
1770
|
-
color: var(--red);
|
|
1771
|
-
background: var(--bg);
|
|
1772
|
-
}
|
|
1694
|
+
.session-row-shell:hover .row-title { padding-right: 26px; }
|
|
1695
|
+
.row-delete:hover { color: var(--red); }
|
|
1773
1696
|
.session-row-shell.removing { opacity: 0; transition: opacity 0.16s; }
|
|
1774
1697
|
|
|
1775
1698
|
/* Empty state for the Adjourned section */
|
|
@@ -1833,40 +1756,17 @@
|
|
|
1833
1756
|
flex-shrink: 0;
|
|
1834
1757
|
}
|
|
1835
1758
|
|
|
1836
|
-
.row-content { min-width: 0; overflow: hidden; }
|
|
1837
|
-
.row-top-line {
|
|
1838
|
-
display: flex;
|
|
1839
|
-
align-items: baseline;
|
|
1840
|
-
gap: 4px;
|
|
1841
|
-
margin-bottom: 1px;
|
|
1842
|
-
}
|
|
1843
1759
|
.row-title {
|
|
1760
|
+
display: block;
|
|
1844
1761
|
font-size: 14px;
|
|
1845
1762
|
font-weight: 400;
|
|
1846
1763
|
color: var(--text);
|
|
1847
1764
|
overflow: hidden;
|
|
1848
1765
|
text-overflow: ellipsis;
|
|
1849
1766
|
white-space: nowrap;
|
|
1850
|
-
flex: 1;
|
|
1851
|
-
min-width: 0;
|
|
1852
1767
|
font-family: var(--sans);
|
|
1853
1768
|
letter-spacing: -0.005em;
|
|
1854
1769
|
}
|
|
1855
|
-
.row-subtitle {
|
|
1856
|
-
font-size: 12px;
|
|
1857
|
-
color: var(--text-dim);
|
|
1858
|
-
overflow: hidden;
|
|
1859
|
-
text-overflow: ellipsis;
|
|
1860
|
-
white-space: nowrap;
|
|
1861
|
-
font-family: var(--sans);
|
|
1862
|
-
letter-spacing: -0.003em;
|
|
1863
|
-
}
|
|
1864
|
-
.row-subtitle .report-style {
|
|
1865
|
-
color: var(--lime);
|
|
1866
|
-
font-weight: 700;
|
|
1867
|
-
margin-right: 3px;
|
|
1868
|
-
}
|
|
1869
|
-
.row-subtitle .sep { color: var(--text-faint); margin: 0 3px; }
|
|
1870
1770
|
|
|
1871
1771
|
/* Bottom-of-list pagination hint (Slack-style) — appears
|
|
1872
1772
|
briefly when the user scrolls to the bottom of the list. */
|
|
@@ -10719,7 +10619,15 @@
|
|
|
10719
10619
|
margin: 0 auto;
|
|
10720
10620
|
border: 0.5px solid var(--line-strong);
|
|
10721
10621
|
border-radius: 12px;
|
|
10722
|
-
|
|
10622
|
+
/* Frosted glass · same recipe as `.input-bar` directly below so the
|
|
10623
|
+
two conjoined cards share one continuous surface treatment. The
|
|
10624
|
+
chat stream behind softens through the haze; the queue chrome
|
|
10625
|
+
stays legible. `.queue-head` / `.queue-list` rows below are
|
|
10626
|
+
`background: transparent` already, so the outer glass dominates. */
|
|
10627
|
+
background: color-mix(in srgb, var(--panel-3) 78%, var(--bg) 22%);
|
|
10628
|
+
background: color-mix(in srgb, color-mix(in srgb, var(--panel-3) 78%, var(--bg) 22%) 88%, transparent);
|
|
10629
|
+
backdrop-filter: blur(24px) saturate(180%);
|
|
10630
|
+
-webkit-backdrop-filter: blur(24px) saturate(180%);
|
|
10723
10631
|
box-shadow: 0 6px 18px -16px rgba(0, 0, 0, 0.45);
|
|
10724
10632
|
position: relative;
|
|
10725
10633
|
/* `overflow: visible` (was `hidden`) so hover tooltips on header
|
|
@@ -10771,7 +10679,14 @@
|
|
|
10771
10679
|
border-bottom: none;
|
|
10772
10680
|
border-top-left-radius: 12px;
|
|
10773
10681
|
border-top-right-radius: 12px;
|
|
10774
|
-
|
|
10682
|
+
/* Frosted glass · mirror `.speaking-queue` so the paused-state
|
|
10683
|
+
chrome reads identically to the live queue strip — same haze,
|
|
10684
|
+
same conjoin with `.input-bar` underneath. Without this the
|
|
10685
|
+
strip pops as a solid card the moment the user hits Pause. */
|
|
10686
|
+
background: color-mix(in srgb, var(--panel-3) 78%, var(--bg) 22%);
|
|
10687
|
+
background: color-mix(in srgb, color-mix(in srgb, var(--panel-3) 78%, var(--bg) 22%) 88%, transparent);
|
|
10688
|
+
backdrop-filter: blur(24px) saturate(180%);
|
|
10689
|
+
-webkit-backdrop-filter: blur(24px) saturate(180%);
|
|
10775
10690
|
box-shadow: 0 6px 18px -16px rgba(0, 0, 0, 0.45);
|
|
10776
10691
|
overflow: hidden;
|
|
10777
10692
|
}
|
|
@@ -10836,7 +10751,15 @@
|
|
|
10836
10751
|
border-bottom: none;
|
|
10837
10752
|
border-top-left-radius: 12px;
|
|
10838
10753
|
border-top-right-radius: 12px;
|
|
10839
|
-
|
|
10754
|
+
/* Frosted glass · third sibling in the queue/paused/adjourned trio
|
|
10755
|
+
that shares this slot above the input-bar. Same recipe as
|
|
10756
|
+
`.speaking-queue` / `.paused-strip` so the live → paused →
|
|
10757
|
+
adjourned chrome transitions read as a state-of-the-same-surface
|
|
10758
|
+
rather than three different cards. */
|
|
10759
|
+
background: color-mix(in srgb, var(--panel-3) 78%, var(--bg) 22%);
|
|
10760
|
+
background: color-mix(in srgb, color-mix(in srgb, var(--panel-3) 78%, var(--bg) 22%) 88%, transparent);
|
|
10761
|
+
backdrop-filter: blur(24px) saturate(180%);
|
|
10762
|
+
-webkit-backdrop-filter: blur(24px) saturate(180%);
|
|
10840
10763
|
box-shadow: 0 6px 18px -16px rgba(0, 0, 0, 0.45);
|
|
10841
10764
|
/* `overflow: visible` (was `hidden`) so hover tooltips on header
|
|
10842
10765
|
controls — the mirrored `.rt-sub-restore::after` for the
|
|
@@ -16107,7 +16030,10 @@
|
|
|
16107
16030
|
<!-- voice-recorder · meeting capture. Lazy module · only allocates
|
|
16108
16031
|
anything on first start(). Loaded BEFORE app.js so the global
|
|
16109
16032
|
`window.BoardroomRecorder` exists by the time app.js init() wires
|
|
16110
|
-
up the onStateChange subscription.
|
|
16033
|
+
up the onStateChange subscription. mp4-muxer vendored in
|
|
16034
|
+
/vendor/ provides the H.264+AAC mp4 container · classic-script
|
|
16035
|
+
wrapper emits `window.Mp4Muxer`. Must load before voice-recorder. -->
|
|
16036
|
+
<script src="vendor/mp4-muxer.min.js" defer></script>
|
|
16111
16037
|
<script src="voice-recorder.js" defer></script>
|
|
16112
16038
|
<script src="app.js" defer></script>
|
|
16113
16039
|
<script>
|
|
@@ -16271,12 +16197,10 @@
|
|
|
16271
16197
|
<a href="#" class="sidebar-tab active" data-sidebar-tab="rooms" role="tab" aria-selected="true">
|
|
16272
16198
|
<i class="ic ic-rooms ico"></i>
|
|
16273
16199
|
<span data-i18n="sidebar_tab_rooms">Rooms</span>
|
|
16274
|
-
<span class="count" data-sidebar-tab-count="rooms">0</span>
|
|
16275
16200
|
</a>
|
|
16276
16201
|
<a href="#" class="sidebar-tab" data-sidebar-tab="agents" role="tab" aria-selected="false">
|
|
16277
16202
|
<i class="ic ic-agents ico"></i>
|
|
16278
16203
|
<span data-i18n="sidebar_tab_agents">Agents</span>
|
|
16279
|
-
<span class="count" data-sidebar-tab-count="agents">0</span>
|
|
16280
16204
|
</a>
|
|
16281
16205
|
</div>
|
|
16282
16206
|
|
|
@@ -16289,7 +16213,6 @@
|
|
|
16289
16213
|
did <X> come up?"). Same .new-btn chrome as siblings. -->
|
|
16290
16214
|
<a href="#" class="new-btn nav-search" data-search-trigger>
|
|
16291
16215
|
<span class="nav-label">Search</span>
|
|
16292
|
-
<span class="nav-count" data-search-count hidden></span>
|
|
16293
16216
|
</a>
|
|
16294
16217
|
<!-- "All Reports" cross-room index · opens a Perplexity-Discover-
|
|
16295
16218
|
style grid of every brief any room has produced. Sits below
|
|
@@ -16298,17 +16221,14 @@
|
|
|
16298
16221
|
elsewhere in the sidebar. -->
|
|
16299
16222
|
<a href="#" class="new-btn nav-reports" data-reports-trigger>
|
|
16300
16223
|
<span class="nav-label" data-i18n="sidebar_all_reports">All Reports</span>
|
|
16301
|
-
<span class="nav-count" data-reports-count hidden></span>
|
|
16302
16224
|
</a>
|
|
16303
16225
|
<!-- "All Notes" · chairman's-notes index. Sits directly below
|
|
16304
16226
|
All Reports because both are cross-cutting aggregation views
|
|
16305
16227
|
(read-only collections that span every room), distinct from
|
|
16306
16228
|
the room list below. Bookmark glyph differentiates from
|
|
16307
|
-
Reports' FileText glyph.
|
|
16308
|
-
live as new notes are saved or deleted. -->
|
|
16229
|
+
Reports' FileText glyph. -->
|
|
16309
16230
|
<a href="#" class="new-btn nav-notes" data-notes-trigger>
|
|
16310
16231
|
<span class="nav-label" data-i18n="sidebar_all_notes">All Notes</span>
|
|
16311
|
-
<span class="nav-count" data-notes-count hidden></span>
|
|
16312
16232
|
</a>
|
|
16313
16233
|
|
|
16314
16234
|
<div class="sessions-scroll">
|
package/public/onboarding.css
CHANGED
|
@@ -300,6 +300,14 @@
|
|
|
300
300
|
height: 100% !important;
|
|
301
301
|
display: block;
|
|
302
302
|
}
|
|
303
|
+
/* Director nameplates pile up on this 180 px banner · directors sit
|
|
304
|
+
on a tight arc and their plate width plus mono caps cause adjacent
|
|
305
|
+
names to overlap into an unreadable blob. Hide them on this surface
|
|
306
|
+
and let the "speaking" pulse + rotating speaker badge carry the
|
|
307
|
+
carousel motion instead. Chair plate stays · single seat, no collision.
|
|
308
|
+
Mirrors `.hero-3d .rt-name-director { display: none !important }`
|
|
309
|
+
on the marketing homepage. */
|
|
310
|
+
.onb-voice-banner-stage .rt-name-director { display: none !important; }
|
|
303
311
|
/* 3D round-table poster · static fallback when WebGL/motion is
|
|
304
312
|
unavailable. Matches the marketing homepage hero scene so the
|
|
305
313
|
visual is consistent across surfaces. <picture> chooses WebP /
|
package/public/themes.css
CHANGED
|
@@ -71,6 +71,19 @@
|
|
|
71
71
|
/* --sans defaults to the human face — chat input, topic question,
|
|
72
72
|
replies, and live notes use it unless an agent override applies. */
|
|
73
73
|
--sans: var(--font-human);
|
|
74
|
+
|
|
75
|
+
/* ─── Radius scale ───
|
|
76
|
+
One axis-of-truth for corner softness. The earlier codebase had
|
|
77
|
+
border-radius values inlined as 4 / 6 / 8 / 12 — bumping the look
|
|
78
|
+
meant find-and-replacing per call-site. These tokens are the new
|
|
79
|
+
single dial: turn one knob, the whole product rounds in lockstep.
|
|
80
|
+
Theme-agnostic (radius doesn't change between dark / light), so
|
|
81
|
+
they live on plain `:root` rather than per-theme. */
|
|
82
|
+
--radius-sm: 6px; /* small chrome · sidebar rows, hover pills */
|
|
83
|
+
--radius-md: 8px; /* buttons, inputs, badges with text */
|
|
84
|
+
--radius-lg: 12px; /* cards, modal panels, search card */
|
|
85
|
+
--radius-xl: 16px; /* large surfaces (main raised panel) */
|
|
86
|
+
--radius-pill: 9999px; /* pure pill / circle */
|
|
74
87
|
}
|
|
75
88
|
|
|
76
89
|
/* ─── DARK · regent · warm gold on near-black ───
|
|
@@ -79,14 +92,26 @@
|
|
|
79
92
|
primary keeps working — the actual hue is gold (#C9A46B).
|
|
80
93
|
See `docs/theme-system.md` for the full token reference. */
|
|
81
94
|
:root[data-theme="dark"] {
|
|
95
|
+
/* ─── Surface ladder · structural panels ───
|
|
96
|
+
Used for solid raised cards, sub-cards, and the chrome shell.
|
|
97
|
+
For *transient* states (hover / active / pressed) prefer the
|
|
98
|
+
alpha overlay tokens below — they layer cleanly over vibrancy
|
|
99
|
+
and stay theme-agnostic at the call site. */
|
|
82
100
|
--bg: #0A0A0A;
|
|
83
101
|
--panel: #131312;
|
|
84
|
-
--panel-2: #
|
|
85
|
-
--panel-3: #
|
|
86
|
-
--hi: #2A2A26;
|
|
102
|
+
--panel-2: #21211F;
|
|
103
|
+
--panel-3: #2A2A27;
|
|
87
104
|
--strip-bg: color-mix(in srgb, var(--lime) 6%, var(--bg)); /* topbar / bottombar — very subtle brand tint */
|
|
88
105
|
--accent-line: color-mix(in srgb, var(--lime) 22%, var(--bg)); /* brand-tinted hairlines for composer chrome */
|
|
89
106
|
|
|
107
|
+
/* ─── Alpha overlay system · macOS / Linear-style ───
|
|
108
|
+
Semantic interaction layers. The values resolve to white-alpha
|
|
109
|
+
on dark themes and black-alpha on light, so a single rule
|
|
110
|
+
(`background: var(--surface-hover)`) reads correctly in both. */
|
|
111
|
+
--surface-hover: rgba(255, 255, 255, 0.10);
|
|
112
|
+
--surface-active: rgba(255, 255, 255, 0.15);
|
|
113
|
+
--surface-strong: rgba(255, 255, 255, 0.20);
|
|
114
|
+
|
|
90
115
|
--line: #26241F;
|
|
91
116
|
--line-bright: #3A3934;
|
|
92
117
|
--line-strong: #5A5852; /* raised from #4D4B45 so input frames separate from line-bright */
|
|
@@ -95,6 +120,7 @@
|
|
|
95
120
|
--text-soft: #8E8B83;
|
|
96
121
|
--text-dim: #7E7B70; /* was #5C5A52; raised to ~4.3:1 vs bg, near WCAG AA */
|
|
97
122
|
--text-faint: #5A5848; /* was #5C5A4D; clearly fainter than dim, OK for hints */
|
|
123
|
+
--text-muted: rgba(255, 255, 255, 0.60); /* alpha-derived · pairs with surface overlays */
|
|
98
124
|
|
|
99
125
|
--lime: #C9A46B;
|
|
100
126
|
--lime-deep: #9A7B40;
|
|
@@ -102,7 +128,6 @@
|
|
|
102
128
|
--amber: #A57843;
|
|
103
129
|
--amber-dim: #5E441F;
|
|
104
130
|
--red: #B5706A;
|
|
105
|
-
--red-dim: #6B4540;
|
|
106
131
|
--cyan: #6A9B97;
|
|
107
132
|
--magenta: #8E7A8E;
|
|
108
133
|
}
|
|
@@ -127,10 +152,16 @@
|
|
|
127
152
|
--panel: #FAFAFA; /* was #F9F9F9 — micro-adjust for ladder spacing */
|
|
128
153
|
--panel-2: #EEEEEE; /* was #F1F1F1 — clearer 1-step pop vs panel */
|
|
129
154
|
--panel-3: #E4E4E4; /* was #ECECEC — clearer 2-step */
|
|
130
|
-
--hi: #D4D4D4; /* was #E5E5E5 — visible hover surface */
|
|
131
155
|
--strip-bg: color-mix(in srgb, var(--lime) 6%, var(--bg)); /* topbar / bottombar — very subtle brand tint */
|
|
132
156
|
--accent-line: color-mix(in srgb, var(--lime) 22%, var(--bg)); /* brand-tinted hairlines for composer chrome */
|
|
133
157
|
|
|
158
|
+
/* ─── Alpha overlay system (light theme · black-on-white) ───
|
|
159
|
+
Mirrors the dark-theme set but inverted polarity, so the same
|
|
160
|
+
`var(--surface-hover)` rule looks correct in both modes. */
|
|
161
|
+
--surface-hover: rgba(0, 0, 0, 0.05);
|
|
162
|
+
--surface-active: rgba(0, 0, 0, 0.08);
|
|
163
|
+
--surface-strong: rgba(0, 0, 0, 0.12);
|
|
164
|
+
|
|
134
165
|
--line: #E5E5E5; /* was #ECECEC — actually visible on white (1.10:1) */
|
|
135
166
|
--line-bright: #C8C8CD; /* was #D4D4D8 — clearer everyday border */
|
|
136
167
|
--line-strong: #A6A6AC; /* was #CCCCCC — structural divider / input frame */
|
|
@@ -139,6 +170,7 @@
|
|
|
139
170
|
--text-soft: #5D5D5D; /* secondary — 7.0:1 (AAA for text > 18pt) */
|
|
140
171
|
--text-dim: #717181; /* was #8E8EA0; lifted to 4.7:1 (AA) for readable */
|
|
141
172
|
--text-faint: #9A9A9A; /* hint level ~2.8:1 — for disabled/decoration */
|
|
173
|
+
--text-muted: rgba(0, 0, 0, 0.55); /* alpha-derived · pairs with surface overlays */
|
|
142
174
|
|
|
143
175
|
--lime: #10A37F; /* OpenAI green — primary accent */
|
|
144
176
|
--lime-deep: #0D8868; /* deeper green — hover / active depth */
|
|
@@ -147,7 +179,6 @@
|
|
|
147
179
|
--amber: #C2410C;
|
|
148
180
|
--amber-dim: #FED7AA;
|
|
149
181
|
--red: #DC2626;
|
|
150
|
-
--red-dim: #FEE2E2;
|
|
151
182
|
--cyan: #0891B2;
|
|
152
183
|
--magenta: #BE185D;
|
|
153
184
|
}
|
package/public/typing-sfx.js
CHANGED
|
@@ -127,7 +127,13 @@
|
|
|
127
127
|
* blips, gavel, countdown beeps) all flow into the meeting
|
|
128
128
|
* recording's audio mix alongside director TTS. Safe to call
|
|
129
129
|
* before ensureContext fires — we connect now if possible and
|
|
130
|
-
* also stash so a later ensureContext call wires it up.
|
|
130
|
+
* also stash so a later ensureContext call wires it up.
|
|
131
|
+
*
|
|
132
|
+
* NOTE · this only works when `dest` is a node in the SAME
|
|
133
|
+
* AudioContext as typing-sfx. The recorder runs in its own
|
|
134
|
+
* context so this path is a silent no-op for it; the recorder
|
|
135
|
+
* uses `getRecorderStream()` below to bridge across contexts
|
|
136
|
+
* via a MediaStream. Kept for any future intra-context callers. */
|
|
131
137
|
let _pendingRecorderDest = null;
|
|
132
138
|
function connectRecorderDestination(dest) {
|
|
133
139
|
if (!dest) return;
|
|
@@ -139,6 +145,30 @@
|
|
|
139
145
|
}
|
|
140
146
|
}
|
|
141
147
|
|
|
148
|
+
/** Cross-context recorder bridge · creates a MediaStreamDestination
|
|
149
|
+
* in OUR AudioContext and returns its `.stream` so the recorder's
|
|
150
|
+
* separate AudioContext can pull our SFX output in via
|
|
151
|
+
* `recorderCtx.createMediaStreamSource(stream)`. AudioNodes cannot
|
|
152
|
+
* connect across AudioContexts directly · MediaStream is the only
|
|
153
|
+
* Web Audio bridge between two graphs. The destination node is
|
|
154
|
+
* cached so repeated calls return the same stream. */
|
|
155
|
+
let _recorderStreamDest = null;
|
|
156
|
+
function getRecorderStream() {
|
|
157
|
+
const ctx = ensureContext();
|
|
158
|
+
if (!ctx) return null;
|
|
159
|
+
if (!_recorderStreamDest) {
|
|
160
|
+
try {
|
|
161
|
+
_recorderStreamDest = ctx.createMediaStreamDestination();
|
|
162
|
+
if (_outputNode) _outputNode.connect(_recorderStreamDest);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
console.warn("[typing-sfx] getRecorderStream failed", e && e.message);
|
|
165
|
+
_recorderStreamDest = null;
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return _recorderStreamDest.stream;
|
|
170
|
+
}
|
|
171
|
+
|
|
142
172
|
/** Suspend the AudioContext shortly after `setThinking(false)` so
|
|
143
173
|
* the system audio session is released for the HTMLAudioElement
|
|
144
174
|
* TTS path. Browsers (Safari / iOS especially) treat a "running"
|
|
@@ -445,5 +475,5 @@
|
|
|
445
475
|
|
|
446
476
|
// Public surface · attached to window so app.js (and the
|
|
447
477
|
// user-settings toggle) can reach it without an import.
|
|
448
|
-
window.boardroomTypingSfx = { tick, speakerChange, setThinking, gavel, countdownTick, setEnabled, isEnabled, connectRecorderDestination };
|
|
478
|
+
window.boardroomTypingSfx = { tick, speakerChange, setThinking, gavel, countdownTick, setEnabled, isEnabled, connectRecorderDestination, getRecorderStream };
|
|
449
479
|
})();
|