@codemem/server 0.28.0 → 0.29.0
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/index.js +377 -50
- package/dist/index.js.map +1 -1
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/memory.d.ts.map +1 -1
- package/dist/routes/stats.d.ts.map +1 -1
- package/dist/routes/sync.d.ts.map +1 -1
- package/package.json +7 -7
- package/static/app.js +2065 -978
- package/static/index.html +531 -30
package/static/index.html
CHANGED
|
@@ -469,6 +469,16 @@
|
|
|
469
469
|
flex-direction: column;
|
|
470
470
|
gap: var(--sp-5);
|
|
471
471
|
}
|
|
472
|
+
/* Sync tab wraps its cards in #syncMainView / #syncDiagnosticsView
|
|
473
|
+
sub-view containers, which breaks the .tab-panel flex gap inheritance.
|
|
474
|
+
Restore the gap inside each sub-view so Sync cards match the Feed
|
|
475
|
+
tab's card rhythm. */
|
|
476
|
+
#syncMainView,
|
|
477
|
+
#syncDiagnosticsView {
|
|
478
|
+
display: flex;
|
|
479
|
+
flex-direction: column;
|
|
480
|
+
gap: var(--sp-5);
|
|
481
|
+
}
|
|
472
482
|
|
|
473
483
|
/* ── Cards / sections ──────────────────────────────────── */
|
|
474
484
|
|
|
@@ -713,6 +723,84 @@
|
|
|
713
723
|
margin-left: 2px;
|
|
714
724
|
}
|
|
715
725
|
|
|
726
|
+
/* Scope-defaults drawer inside a group card. Reads top-down:
|
|
727
|
+
header · helper · auto-seed toggle (master) · include chips
|
|
728
|
+
· exclude chips · actions. Delimits itself as its own subsection
|
|
729
|
+
with a top border + breathing room so it doesn't run into the
|
|
730
|
+
peer-actions row above. Radix Collapsible.Content is the host,
|
|
731
|
+
so open/close is driven by data-state. */
|
|
732
|
+
.coordinator-admin-group-preferences {
|
|
733
|
+
display: grid;
|
|
734
|
+
gap: var(--sp-4);
|
|
735
|
+
padding-top: var(--sp-3);
|
|
736
|
+
border-top: 1px solid var(--border);
|
|
737
|
+
min-width: 0;
|
|
738
|
+
}
|
|
739
|
+
.coordinator-admin-group-preferences[data-state="closed"] { display: none; }
|
|
740
|
+
/* Rotate the trigger's chevron when the scope-defaults disclosure
|
|
741
|
+
is open. Matches the sync-tab device-row chevron affordance. */
|
|
742
|
+
.coordinator-admin-scope-trigger[data-state="open"] .device-row-chevron {
|
|
743
|
+
transform: rotate(90deg);
|
|
744
|
+
}
|
|
745
|
+
.coordinator-admin-scope-trigger .device-row-chevron {
|
|
746
|
+
display: inline-flex;
|
|
747
|
+
width: 14px;
|
|
748
|
+
height: 14px;
|
|
749
|
+
transition: transform 140ms ease;
|
|
750
|
+
}
|
|
751
|
+
.coordinator-admin-scope-trigger .device-row-chevron svg {
|
|
752
|
+
width: 100%;
|
|
753
|
+
height: 100%;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
.coordinator-admin-group-preferences > .coordinator-admin-field,
|
|
757
|
+
.coordinator-admin-group-preferences > .coordinator-admin-inline-filter {
|
|
758
|
+
min-width: 0;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
.coordinator-admin-drawer-title {
|
|
762
|
+
font-size: 13px;
|
|
763
|
+
font-weight: 600;
|
|
764
|
+
color: var(--text-primary);
|
|
765
|
+
margin: 0;
|
|
766
|
+
letter-spacing: 0.01em;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/* Tighten the helper text directly under the drawer title so they
|
|
770
|
+
read as one intro block, then let the sp-4 gap separate that
|
|
771
|
+
block from the toggle. */
|
|
772
|
+
.coordinator-admin-group-preferences > .coordinator-admin-drawer-title + .peer-submeta {
|
|
773
|
+
margin-top: calc(-1 * var(--sp-3));
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/* Help text that lives INSIDE a chip field (e.g. "Exact names,
|
|
777
|
+
leave empty to allow all") — looser than the row gap, tighter
|
|
778
|
+
than the inter-section gap. */
|
|
779
|
+
.coordinator-admin-group-preferences .coordinator-admin-field > .peer-submeta {
|
|
780
|
+
margin-top: calc(-1 * var(--sp-1));
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/* Dim the chip editor when auto-seed is off so it visually reads
|
|
784
|
+
as "configuration that isn't currently applied" rather than
|
|
785
|
+
"broken input". */
|
|
786
|
+
.coordinator-admin-group-preferences .peer-scope-input:disabled {
|
|
787
|
+
opacity: 0.55;
|
|
788
|
+
cursor: not-allowed;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/* Separate the action row (Save / Cancel) from the last field with
|
|
792
|
+
an explicit gap + top border so the terminating region reads as
|
|
793
|
+
distinct from the form controls above. */
|
|
794
|
+
.coordinator-admin-group-preferences > .peer-actions {
|
|
795
|
+
margin-top: var(--sp-1);
|
|
796
|
+
padding-top: var(--sp-3);
|
|
797
|
+
border-top: 1px solid color-mix(in srgb, var(--border) 55%, transparent);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
.coordinator-admin-group-preferences > .coordinator-admin-inline-filter {
|
|
801
|
+
gap: var(--sp-3);
|
|
802
|
+
}
|
|
803
|
+
|
|
716
804
|
.coordinator-admin-empty-state {
|
|
717
805
|
padding: var(--sp-3) 0 var(--sp-1);
|
|
718
806
|
}
|
|
@@ -746,6 +834,32 @@
|
|
|
746
834
|
align-items: center;
|
|
747
835
|
}
|
|
748
836
|
|
|
837
|
+
/* Utility link that navigates between sibling sub-surfaces (e.g.
|
|
838
|
+
main Sync view ↔ diagnostics sub-view). One treatment used for
|
|
839
|
+
both directions so header/back links read as the same vocabulary
|
|
840
|
+
— quieter than any real button nearby, since it's navigation,
|
|
841
|
+
not action. */
|
|
842
|
+
.sync-subview-link {
|
|
843
|
+
display: inline-flex;
|
|
844
|
+
align-items: center;
|
|
845
|
+
gap: 4px;
|
|
846
|
+
font-size: 13px;
|
|
847
|
+
font-weight: 500;
|
|
848
|
+
color: var(--text-secondary);
|
|
849
|
+
text-decoration: none;
|
|
850
|
+
padding: 4px 6px;
|
|
851
|
+
border-radius: var(--radius-sm);
|
|
852
|
+
transition: color 140ms ease;
|
|
853
|
+
}
|
|
854
|
+
.sync-subview-link:hover {
|
|
855
|
+
color: var(--accent);
|
|
856
|
+
}
|
|
857
|
+
.sync-subview-link:focus-visible {
|
|
858
|
+
color: var(--accent);
|
|
859
|
+
outline: 2px solid var(--accent);
|
|
860
|
+
outline-offset: 2px;
|
|
861
|
+
}
|
|
862
|
+
|
|
749
863
|
.section-meta {
|
|
750
864
|
color: var(--text-tertiary);
|
|
751
865
|
font-size: 12px;
|
|
@@ -860,6 +974,144 @@
|
|
|
860
974
|
box-shadow: 0 0 0 4px var(--focus-ring);
|
|
861
975
|
}
|
|
862
976
|
|
|
977
|
+
/* Device-row compact header. A non-interactive container holding
|
|
978
|
+
the status pip + name + chips + meta, with a dedicated disclosure
|
|
979
|
+
button (.device-row-toggle) for assistive tech and keyboard users.
|
|
980
|
+
The whole row still responds to click via a background click
|
|
981
|
+
handler on .peer-card, but the accessible name of the toggle stays
|
|
982
|
+
clean ("Expand device work").
|
|
983
|
+
See docs/plans/2026-04-23-sync-tab-redesign.md (PR 2). */
|
|
984
|
+
.device-row-head {
|
|
985
|
+
display: flex;
|
|
986
|
+
align-items: center;
|
|
987
|
+
gap: var(--sp-3);
|
|
988
|
+
width: 100%;
|
|
989
|
+
min-height: 56px;
|
|
990
|
+
padding: 10px var(--sp-4);
|
|
991
|
+
}
|
|
992
|
+
/* Row head doubles as the Collapsible trigger so clicking anywhere
|
|
993
|
+
on the band toggles the drawer. Button defaults are reset; the
|
|
994
|
+
chevron at the right is a visual indicator only. */
|
|
995
|
+
button.device-row-head--trigger {
|
|
996
|
+
background: transparent;
|
|
997
|
+
border: none;
|
|
998
|
+
color: inherit;
|
|
999
|
+
text-align: left;
|
|
1000
|
+
font: inherit;
|
|
1001
|
+
cursor: pointer;
|
|
1002
|
+
}
|
|
1003
|
+
button.device-row-head--trigger:hover {
|
|
1004
|
+
background: color-mix(in srgb, var(--surface-2) 60%, transparent);
|
|
1005
|
+
}
|
|
1006
|
+
button.device-row-head--trigger:focus-visible {
|
|
1007
|
+
outline: 2px solid var(--accent);
|
|
1008
|
+
outline-offset: -2px;
|
|
1009
|
+
}
|
|
1010
|
+
.device-row-chevron {
|
|
1011
|
+
margin-left: auto;
|
|
1012
|
+
flex-shrink: 0;
|
|
1013
|
+
display: inline-flex;
|
|
1014
|
+
align-items: center;
|
|
1015
|
+
justify-content: center;
|
|
1016
|
+
width: 32px;
|
|
1017
|
+
height: 32px;
|
|
1018
|
+
color: var(--text-secondary);
|
|
1019
|
+
}
|
|
1020
|
+
.device-row-chevron svg {
|
|
1021
|
+
width: 18px;
|
|
1022
|
+
height: 18px;
|
|
1023
|
+
transition: transform 140ms ease;
|
|
1024
|
+
}
|
|
1025
|
+
button.device-row-head--trigger[data-state="open"] .device-row-chevron svg {
|
|
1026
|
+
transform: rotate(90deg);
|
|
1027
|
+
}
|
|
1028
|
+
.device-row-head .device-row-name {
|
|
1029
|
+
flex: 1 1 auto;
|
|
1030
|
+
min-width: 0;
|
|
1031
|
+
font-size: 14px;
|
|
1032
|
+
font-weight: 600;
|
|
1033
|
+
color: var(--text-primary);
|
|
1034
|
+
overflow: hidden;
|
|
1035
|
+
text-overflow: ellipsis;
|
|
1036
|
+
white-space: nowrap;
|
|
1037
|
+
}
|
|
1038
|
+
.device-row-head .device-row-chips {
|
|
1039
|
+
display: inline-flex;
|
|
1040
|
+
gap: 6px;
|
|
1041
|
+
align-items: center;
|
|
1042
|
+
flex-wrap: wrap;
|
|
1043
|
+
}
|
|
1044
|
+
.device-row-head .device-row-meta {
|
|
1045
|
+
font-size: 12px;
|
|
1046
|
+
color: var(--text-tertiary);
|
|
1047
|
+
font-feature-settings: 'tnum' 1;
|
|
1048
|
+
white-space: nowrap;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
/* Drawer content region revealed when the row is expanded.
|
|
1052
|
+
Keeps the existing .peer-actions / .peer-scope styling intact —
|
|
1053
|
+
this is just the outer wrapper + separator line. */
|
|
1054
|
+
.device-row-drawer {
|
|
1055
|
+
padding: var(--sp-3) var(--sp-4) var(--sp-4);
|
|
1056
|
+
border-top: 1px solid color-mix(in srgb, var(--border) 55%, transparent);
|
|
1057
|
+
display: grid;
|
|
1058
|
+
gap: var(--sp-3);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
/* Device-row skeleton (shown during the initial sync load). Shape
|
|
1062
|
+
matches the real row so layout doesn't shift when data arrives.
|
|
1063
|
+
See docs/plans/2026-04-23-sync-tab-redesign.md (loading-states). */
|
|
1064
|
+
.device-row-skeleton-list {
|
|
1065
|
+
display: grid;
|
|
1066
|
+
gap: var(--sp-2);
|
|
1067
|
+
}
|
|
1068
|
+
.device-row-skeleton {
|
|
1069
|
+
display: flex;
|
|
1070
|
+
align-items: center;
|
|
1071
|
+
gap: var(--sp-3);
|
|
1072
|
+
min-height: 56px;
|
|
1073
|
+
padding: 10px var(--sp-3);
|
|
1074
|
+
border-radius: var(--radius-lg);
|
|
1075
|
+
border: 1px solid var(--border);
|
|
1076
|
+
background: var(--surface-1);
|
|
1077
|
+
}
|
|
1078
|
+
.device-row-skeleton .skel-pip,
|
|
1079
|
+
.device-row-skeleton .skel-name,
|
|
1080
|
+
.device-row-skeleton .skel-chip {
|
|
1081
|
+
display: inline-block;
|
|
1082
|
+
border-radius: var(--radius-sm);
|
|
1083
|
+
background: linear-gradient(90deg,
|
|
1084
|
+
color-mix(in srgb, var(--surface-2) 82%, var(--surface-1)) 25%,
|
|
1085
|
+
color-mix(in srgb, var(--surface-2) 60%, var(--surface-1)) 50%,
|
|
1086
|
+
color-mix(in srgb, var(--surface-2) 82%, var(--surface-1)) 75%);
|
|
1087
|
+
background-size: 200% 100%;
|
|
1088
|
+
animation: device-skel-shimmer 1.5s ease-in-out infinite;
|
|
1089
|
+
}
|
|
1090
|
+
.device-row-skeleton .skel-pip {
|
|
1091
|
+
width: 8px;
|
|
1092
|
+
height: 8px;
|
|
1093
|
+
border-radius: 50%;
|
|
1094
|
+
}
|
|
1095
|
+
.device-row-skeleton .skel-name {
|
|
1096
|
+
flex: 1;
|
|
1097
|
+
max-width: 180px;
|
|
1098
|
+
height: 14px;
|
|
1099
|
+
}
|
|
1100
|
+
.device-row-skeleton .skel-chip {
|
|
1101
|
+
width: 80px;
|
|
1102
|
+
height: 18px;
|
|
1103
|
+
border-radius: var(--radius-pill);
|
|
1104
|
+
}
|
|
1105
|
+
@keyframes device-skel-shimmer {
|
|
1106
|
+
0% { background-position: 200% 0; }
|
|
1107
|
+
100% { background-position: -200% 0; }
|
|
1108
|
+
}
|
|
1109
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1110
|
+
.device-row-skeleton .skel-pip,
|
|
1111
|
+
.device-row-skeleton .skel-name,
|
|
1112
|
+
.device-row-skeleton .skel-chip { animation: none; }
|
|
1113
|
+
}
|
|
1114
|
+
|
|
863
1115
|
.sync-redact-control {
|
|
864
1116
|
display: inline-flex;
|
|
865
1117
|
align-items: center;
|
|
@@ -968,12 +1220,14 @@
|
|
|
968
1220
|
|
|
969
1221
|
.feed-controls {
|
|
970
1222
|
display: flex;
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
gap: var(--sp-
|
|
974
|
-
flex-wrap: wrap;
|
|
1223
|
+
flex-direction: column;
|
|
1224
|
+
align-items: stretch;
|
|
1225
|
+
gap: var(--sp-2);
|
|
975
1226
|
margin-bottom: var(--sp-4);
|
|
976
1227
|
}
|
|
1228
|
+
.feed-controls > .section-meta {
|
|
1229
|
+
margin-bottom: 0;
|
|
1230
|
+
}
|
|
977
1231
|
|
|
978
1232
|
.feed-controls-right {
|
|
979
1233
|
display: flex;
|
|
@@ -1573,7 +1827,9 @@
|
|
|
1573
1827
|
border-radius: var(--radius-sm);
|
|
1574
1828
|
background: var(--surface-2);
|
|
1575
1829
|
}
|
|
1576
|
-
|
|
1830
|
+
/* General sibling, not adjacent: the invite/join panels sit in empty
|
|
1831
|
+
portal <div>s between action rows and the `+` selector would miss. */
|
|
1832
|
+
.sync-action ~ .sync-action { margin-top: var(--sp-2); }
|
|
1577
1833
|
|
|
1578
1834
|
.health-action-text, .sync-action-text { font-size: 13px; color: var(--text-primary); flex: 1; min-width: 0; }
|
|
1579
1835
|
.sync-section-heading {
|
|
@@ -1632,11 +1888,26 @@
|
|
|
1632
1888
|
|
|
1633
1889
|
/* ── Peers ─────────────────────────────────────────────── */
|
|
1634
1890
|
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1891
|
+
/* Device list: full-width rows so the row head reads as a single
|
|
1892
|
+
line and the expanded drawer uses the full card width. The old
|
|
1893
|
+
2-column grid above 900px forced expansion into a narrow sidebar. */
|
|
1894
|
+
.peer-list { display: grid; grid-template-columns: 1fr; gap: var(--sp-2); }
|
|
1895
|
+
.peer-card {
|
|
1896
|
+
border: 1px solid var(--border);
|
|
1897
|
+
border-radius: var(--radius-lg);
|
|
1898
|
+
background: var(--surface-1);
|
|
1899
|
+
padding: 0;
|
|
1900
|
+
display: block;
|
|
1901
|
+
overflow: hidden;
|
|
1902
|
+
}
|
|
1903
|
+
/* Padded variant for surfaces that reuse .peer-card as a generic
|
|
1904
|
+
content card (e.g. coordinator-admin group cards). Device rows
|
|
1905
|
+
keep padding:0 because their row-head owns its own interior. */
|
|
1906
|
+
.peer-card.peer-card--padded {
|
|
1907
|
+
padding: var(--sp-3);
|
|
1908
|
+
display: grid;
|
|
1909
|
+
gap: var(--sp-2);
|
|
1638
1910
|
}
|
|
1639
|
-
.peer-card { border-radius: var(--radius-lg); padding: var(--sp-4); display: grid; gap: var(--sp-2); }
|
|
1640
1911
|
.peer-title { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); flex-wrap: wrap; }
|
|
1641
1912
|
.peer-title strong { font-size: 14px; font-weight: 600; }
|
|
1642
1913
|
/* Direction glyph rendered when a peer is in mutual-trust state.
|
|
@@ -1668,9 +1939,189 @@
|
|
|
1668
1939
|
.peer-actor-row { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
|
|
1669
1940
|
.peer-scope-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); }
|
|
1670
1941
|
.peer-scope-editor { display: grid; gap: 8px; }
|
|
1671
|
-
.peer-scope-chips { display: flex; flex-wrap: wrap; gap: 6px; min-height: 32px; align-items: flex-start; padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); }
|
|
1942
|
+
.peer-scope-chips { display: flex; flex-wrap: wrap; gap: 6px; min-height: 32px; align-items: flex-start; padding: 8px 10px; margin: 0; list-style: none; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); }
|
|
1672
1943
|
.peer-scope-chip { padding: 4px 8px; background: rgba(43, 122, 176, 0.12); color: var(--accent); font-size: 12px; }
|
|
1673
1944
|
.peer-scope-chip.empty { background: transparent; color: var(--text-tertiary); border-style: dashed; border-color: var(--border); }
|
|
1945
|
+
|
|
1946
|
+
/* ProjectScopePicker — selected chips on top, an "Add project"
|
|
1947
|
+
button below that opens a Radix Popover with a searchable list.
|
|
1948
|
+
Typing an unmatched name turns the last row into
|
|
1949
|
+
`+ Create "foo"` so free-text entry folds into the same
|
|
1950
|
+
surface. Used by coordinator-admin scope defaults and Sync
|
|
1951
|
+
peer-scope drawers. */
|
|
1952
|
+
.project-scope-picker {
|
|
1953
|
+
display: grid;
|
|
1954
|
+
gap: var(--sp-2);
|
|
1955
|
+
position: relative;
|
|
1956
|
+
}
|
|
1957
|
+
.project-scope-picker-selected {
|
|
1958
|
+
display: flex;
|
|
1959
|
+
flex-wrap: wrap;
|
|
1960
|
+
gap: 6px;
|
|
1961
|
+
padding: 8px 10px;
|
|
1962
|
+
margin: 0;
|
|
1963
|
+
min-height: 36px;
|
|
1964
|
+
list-style: none;
|
|
1965
|
+
border: 1px solid var(--border);
|
|
1966
|
+
border-radius: var(--radius-sm);
|
|
1967
|
+
background: var(--surface-2);
|
|
1968
|
+
align-items: center;
|
|
1969
|
+
}
|
|
1970
|
+
.project-scope-picker-selected-empty {
|
|
1971
|
+
font-size: 12px;
|
|
1972
|
+
color: var(--text-tertiary);
|
|
1973
|
+
}
|
|
1974
|
+
.project-scope-picker-selected-chip {
|
|
1975
|
+
display: inline-flex;
|
|
1976
|
+
align-items: center;
|
|
1977
|
+
gap: 6px;
|
|
1978
|
+
padding: 4px 4px 4px 10px;
|
|
1979
|
+
background: rgba(43, 122, 176, 0.16);
|
|
1980
|
+
border: 1px solid color-mix(in srgb, var(--accent) 48%, var(--border));
|
|
1981
|
+
border-radius: var(--radius-pill);
|
|
1982
|
+
color: var(--accent);
|
|
1983
|
+
font-size: 12px;
|
|
1984
|
+
}
|
|
1985
|
+
.project-scope-picker-selected-remove {
|
|
1986
|
+
appearance: none;
|
|
1987
|
+
border: none;
|
|
1988
|
+
background: transparent;
|
|
1989
|
+
color: inherit;
|
|
1990
|
+
cursor: pointer;
|
|
1991
|
+
padding: 0 4px;
|
|
1992
|
+
font: inherit;
|
|
1993
|
+
font-size: 14px;
|
|
1994
|
+
line-height: 1;
|
|
1995
|
+
opacity: 0.7;
|
|
1996
|
+
}
|
|
1997
|
+
.project-scope-picker-selected-remove:hover { opacity: 1; }
|
|
1998
|
+
.project-scope-picker-trigger {
|
|
1999
|
+
justify-self: start;
|
|
2000
|
+
display: inline-flex;
|
|
2001
|
+
align-items: center;
|
|
2002
|
+
gap: 6px;
|
|
2003
|
+
padding: 6px 12px;
|
|
2004
|
+
font-size: 12px;
|
|
2005
|
+
}
|
|
2006
|
+
.project-scope-picker-popover {
|
|
2007
|
+
width: min(360px, 100%);
|
|
2008
|
+
max-height: 280px;
|
|
2009
|
+
display: grid;
|
|
2010
|
+
grid-template-rows: auto 1fr;
|
|
2011
|
+
gap: 4px;
|
|
2012
|
+
padding: 6px;
|
|
2013
|
+
background: var(--surface-1);
|
|
2014
|
+
border: 1px solid var(--border);
|
|
2015
|
+
border-radius: var(--radius-md);
|
|
2016
|
+
box-shadow: var(--shadow-lg, 0 10px 15px -3px rgba(0, 0, 0, 0.3));
|
|
2017
|
+
z-index: 50;
|
|
2018
|
+
}
|
|
2019
|
+
/* Vanilla-DOM popover (peer-scope) anchors inline under its trigger;
|
|
2020
|
+
the Preact ToggleGroup version portals via Radix. Both share the
|
|
2021
|
+
rest of the styling. */
|
|
2022
|
+
.project-scope-picker-popover--inline {
|
|
2023
|
+
position: absolute;
|
|
2024
|
+
left: 0;
|
|
2025
|
+
right: auto;
|
|
2026
|
+
top: 100%;
|
|
2027
|
+
margin-top: 6px;
|
|
2028
|
+
}
|
|
2029
|
+
.project-scope-picker-search {
|
|
2030
|
+
appearance: none;
|
|
2031
|
+
width: 100%;
|
|
2032
|
+
border: 1px solid var(--border);
|
|
2033
|
+
border-radius: var(--radius-sm);
|
|
2034
|
+
background: var(--surface-2);
|
|
2035
|
+
color: var(--text-primary);
|
|
2036
|
+
padding: 6px 10px;
|
|
2037
|
+
font: inherit;
|
|
2038
|
+
font-size: 13px;
|
|
2039
|
+
}
|
|
2040
|
+
.project-scope-picker-search:focus {
|
|
2041
|
+
outline: 2px solid var(--accent);
|
|
2042
|
+
outline-offset: 1px;
|
|
2043
|
+
}
|
|
2044
|
+
.project-scope-picker-results {
|
|
2045
|
+
margin: 0;
|
|
2046
|
+
padding: 4px 0;
|
|
2047
|
+
list-style: none;
|
|
2048
|
+
max-height: 220px;
|
|
2049
|
+
overflow-y: auto;
|
|
2050
|
+
display: grid;
|
|
2051
|
+
gap: 2px;
|
|
2052
|
+
}
|
|
2053
|
+
.project-scope-picker-empty-row {
|
|
2054
|
+
padding: 8px 10px;
|
|
2055
|
+
font-size: 12px;
|
|
2056
|
+
color: var(--text-tertiary);
|
|
2057
|
+
}
|
|
2058
|
+
.project-scope-picker-row {
|
|
2059
|
+
appearance: none;
|
|
2060
|
+
border: none;
|
|
2061
|
+
background: transparent;
|
|
2062
|
+
width: 100%;
|
|
2063
|
+
text-align: left;
|
|
2064
|
+
font: inherit;
|
|
2065
|
+
display: grid;
|
|
2066
|
+
grid-template-columns: 16px 1fr;
|
|
2067
|
+
gap: 6px;
|
|
2068
|
+
align-items: center;
|
|
2069
|
+
padding: 6px 10px;
|
|
2070
|
+
border-radius: var(--radius-sm);
|
|
2071
|
+
cursor: pointer;
|
|
2072
|
+
color: var(--text-secondary);
|
|
2073
|
+
font-size: 13px;
|
|
2074
|
+
}
|
|
2075
|
+
.project-scope-picker-row:focus-visible {
|
|
2076
|
+
outline: 2px solid var(--accent);
|
|
2077
|
+
outline-offset: -2px;
|
|
2078
|
+
}
|
|
2079
|
+
.project-scope-picker-row--active {
|
|
2080
|
+
background: color-mix(in srgb, var(--accent) 10%, transparent);
|
|
2081
|
+
color: var(--text-primary);
|
|
2082
|
+
}
|
|
2083
|
+
.project-scope-picker-row[aria-selected="true"] {
|
|
2084
|
+
color: var(--accent);
|
|
2085
|
+
}
|
|
2086
|
+
.project-scope-picker-row-check {
|
|
2087
|
+
display: inline-block;
|
|
2088
|
+
width: 16px;
|
|
2089
|
+
text-align: center;
|
|
2090
|
+
font-size: 12px;
|
|
2091
|
+
color: var(--accent);
|
|
2092
|
+
}
|
|
2093
|
+
.project-scope-picker-row--create .project-scope-picker-row-check {
|
|
2094
|
+
color: var(--text-tertiary);
|
|
2095
|
+
}
|
|
2096
|
+
/* PresencePip — atomic health indicator.
|
|
2097
|
+
See docs/plans/2026-04-23-sync-tab-redesign.md (loading-states). */
|
|
2098
|
+
.presence-pip {
|
|
2099
|
+
display: inline-block;
|
|
2100
|
+
width: var(--presence-pip-size, 8px);
|
|
2101
|
+
height: var(--presence-pip-size, 8px);
|
|
2102
|
+
border-radius: 50%;
|
|
2103
|
+
flex-shrink: 0;
|
|
2104
|
+
vertical-align: middle;
|
|
2105
|
+
}
|
|
2106
|
+
.presence-pip--online { background: var(--accent); }
|
|
2107
|
+
.presence-pip--offline { background: var(--accent-warm, var(--accent)); }
|
|
2108
|
+
.presence-pip--degraded { background: var(--accent-warm, var(--accent)); }
|
|
2109
|
+
.presence-pip--attention { background: var(--accent-warm-strong, var(--accent-warm, var(--accent))); animation: pip-attention 2.4s ease-in-out infinite; }
|
|
2110
|
+
.presence-pip--syncing { background: var(--accent); animation: pip-syncing 1.2s ease-in-out infinite; }
|
|
2111
|
+
.presence-pip--unknown { background: var(--text-tertiary); opacity: 0.6; }
|
|
2112
|
+
|
|
2113
|
+
@keyframes pip-attention {
|
|
2114
|
+
0%, 100% { opacity: 0.6; transform: scale(1); }
|
|
2115
|
+
50% { opacity: 1; transform: scale(1.15); }
|
|
2116
|
+
}
|
|
2117
|
+
@keyframes pip-syncing {
|
|
2118
|
+
0%, 100% { opacity: 0.55; }
|
|
2119
|
+
50% { opacity: 1; }
|
|
2120
|
+
}
|
|
2121
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2122
|
+
.presence-pip--attention,
|
|
2123
|
+
.presence-pip--syncing { animation: none; opacity: 1; transform: none; }
|
|
2124
|
+
}
|
|
1674
2125
|
.peer-scope-chip-remove { border: none; background: transparent; color: inherit; cursor: pointer; padding: 0; font: inherit; opacity: 0.75; }
|
|
1675
2126
|
.peer-scope-chip-remove:hover { opacity: 1; }
|
|
1676
2127
|
.peer-scope-input { width: 100%; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2); color: var(--text-primary); padding: 8px 10px; font: inherit; }
|
|
@@ -2581,7 +3032,8 @@
|
|
|
2581
3032
|
align-items: center;
|
|
2582
3033
|
}
|
|
2583
3034
|
.sync-dialog-input {
|
|
2584
|
-
width: 100
|
|
3035
|
+
width: calc(100% - 6px);
|
|
3036
|
+
margin: 3px;
|
|
2585
3037
|
padding: var(--sp-2) var(--sp-3);
|
|
2586
3038
|
border-radius: var(--radius-sm);
|
|
2587
3039
|
border: 1px solid var(--border);
|
|
@@ -2589,11 +3041,44 @@
|
|
|
2589
3041
|
color: var(--text-primary);
|
|
2590
3042
|
font-family: var(--font-sans);
|
|
2591
3043
|
font-size: 13px;
|
|
3044
|
+
box-sizing: border-box;
|
|
2592
3045
|
}
|
|
2593
3046
|
.sync-dialog-input:focus {
|
|
2594
|
-
outline: none;
|
|
2595
3047
|
border-color: var(--accent);
|
|
2596
|
-
|
|
3048
|
+
outline: 2px solid var(--focus-ring);
|
|
3049
|
+
outline-offset: 1px;
|
|
3050
|
+
}
|
|
3051
|
+
.autocomplete-popover {
|
|
3052
|
+
background: var(--surface-1, var(--bg-elevated));
|
|
3053
|
+
border: 1px solid var(--border);
|
|
3054
|
+
border-radius: var(--radius-sm);
|
|
3055
|
+
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
|
|
3056
|
+
max-height: 260px;
|
|
3057
|
+
overflow-y: auto;
|
|
3058
|
+
padding: 4px;
|
|
3059
|
+
z-index: 120;
|
|
3060
|
+
}
|
|
3061
|
+
.autocomplete-listbox {
|
|
3062
|
+
display: flex;
|
|
3063
|
+
flex-direction: column;
|
|
3064
|
+
list-style: none;
|
|
3065
|
+
margin: 0;
|
|
3066
|
+
padding: 0;
|
|
3067
|
+
}
|
|
3068
|
+
.autocomplete-option {
|
|
3069
|
+
padding: 6px 10px;
|
|
3070
|
+
border-radius: var(--radius-sm);
|
|
3071
|
+
color: var(--text-primary);
|
|
3072
|
+
font-family: var(--font-sans);
|
|
3073
|
+
font-size: 13px;
|
|
3074
|
+
cursor: pointer;
|
|
3075
|
+
user-select: none;
|
|
3076
|
+
}
|
|
3077
|
+
.autocomplete-option:hover,
|
|
3078
|
+
.autocomplete-option.active,
|
|
3079
|
+
.autocomplete-option[aria-selected="true"] {
|
|
3080
|
+
background: var(--surface-2, rgba(255, 255, 255, 0.06));
|
|
3081
|
+
color: var(--accent);
|
|
2597
3082
|
}
|
|
2598
3083
|
.sync-dialog-confirm.danger {
|
|
2599
3084
|
background: rgba(182, 75, 47, 0.12);
|
|
@@ -3024,6 +3509,10 @@
|
|
|
3024
3509
|
<!-- ── Tab: Sync ───────────────────────────────────────── -->
|
|
3025
3510
|
<div class="tab-panel" id="tab-sync" hidden>
|
|
3026
3511
|
|
|
3512
|
+
<!-- Sub-view: main Sync surface. Toggled by syncViewController
|
|
3513
|
+
based on #sync vs #sync/diagnostics in the URL hash. -->
|
|
3514
|
+
<div id="syncMainView">
|
|
3515
|
+
|
|
3027
3516
|
<!-- 1. Needs attention + team setup/status -->
|
|
3028
3517
|
<div class="card" id="syncTeamCard">
|
|
3029
3518
|
<div class="section-header">
|
|
@@ -3033,6 +3522,7 @@
|
|
|
3033
3522
|
</div>
|
|
3034
3523
|
<div class="section-actions">
|
|
3035
3524
|
<button class="settings-button" id="syncNowButton">Sync now</button>
|
|
3525
|
+
<a class="sync-subview-link" href="#sync/diagnostics">View diagnostics →</a>
|
|
3036
3526
|
</div>
|
|
3037
3527
|
</div>
|
|
3038
3528
|
<div class="section-meta" id="syncTeamMeta">Start with the next step below, then review team status and device details.</div>
|
|
@@ -3047,22 +3537,20 @@
|
|
|
3047
3537
|
<h3 class="settings-group-title">Join a team</h3>
|
|
3048
3538
|
<div class="section-meta">Have an invite? Paste it here.</div>
|
|
3049
3539
|
<div class="actor-create-row" id="syncJoinPanel">
|
|
3050
|
-
<label for="syncJoinInvite" class="sr-only">Team invite</label>
|
|
3051
|
-
<textarea class="feed-search" id="syncJoinInvite" placeholder="Paste team invite or
|
|
3052
|
-
<button class="settings-button" id="syncJoinButton">
|
|
3540
|
+
<label for="syncJoinInvite" class="sr-only">Team invite or pairing payload</label>
|
|
3541
|
+
<textarea class="feed-search" id="syncJoinInvite" placeholder="Paste a team invite or device pairing payload"></textarea>
|
|
3542
|
+
<button class="settings-button" id="syncJoinButton">Accept</button>
|
|
3053
3543
|
</div>
|
|
3054
3544
|
</div>
|
|
3055
3545
|
<div id="syncAdminSection">
|
|
3056
3546
|
<div id="syncAdminDisclosureMount" style="display: contents"></div>
|
|
3057
3547
|
</div>
|
|
3058
3548
|
</div>
|
|
3059
|
-
<h3 class="settings-group-title" style="margin-top: 20px">Team status</h3>
|
|
3060
|
-
<div class="actor-list" id="syncTeamStatus" aria-live="polite"></div>
|
|
3061
3549
|
<div class="actor-list" id="syncJoinRequests"></div>
|
|
3062
3550
|
<div id="syncCoordinatorDiscovered" hidden>
|
|
3063
|
-
<h3 class="settings-group-title" style="margin-top: 20px">
|
|
3551
|
+
<h3 class="settings-group-title" style="margin-top: 20px">Team devices to pair</h3>
|
|
3064
3552
|
<div class="section-meta" id="syncCoordinatorDiscoveredMeta"></div>
|
|
3065
|
-
<div class="
|
|
3553
|
+
<div class="peer-list" id="syncCoordinatorDiscoveredList"></div>
|
|
3066
3554
|
</div>
|
|
3067
3555
|
</div>
|
|
3068
3556
|
|
|
@@ -3087,11 +3575,19 @@
|
|
|
3087
3575
|
|
|
3088
3576
|
<h3 class="settings-group-title" style="margin-top: 20px">Devices</h3>
|
|
3089
3577
|
<div class="peer-list" id="syncPeers"></div>
|
|
3090
|
-
<div class="
|
|
3091
|
-
<div class="
|
|
3092
|
-
<div class="
|
|
3578
|
+
<div class="device-row-skeleton-list" id="syncPeersSkeleton" role="status" aria-label="Loading devices" aria-busy="true">
|
|
3579
|
+
<div class="device-row-skeleton"><span class="skel-pip"></span><span class="skel-name"></span><span class="skel-chip"></span></div>
|
|
3580
|
+
<div class="device-row-skeleton"><span class="skel-pip"></span><span class="skel-name"></span><span class="skel-chip"></span></div>
|
|
3581
|
+
<div class="device-row-skeleton"><span class="skel-pip"></span><span class="skel-name"></span><span class="skel-chip"></span></div>
|
|
3093
3582
|
</div>
|
|
3094
3583
|
|
|
3584
|
+
<h3 class="settings-group-title" style="margin-top: 20px">Connect another device</h3>
|
|
3585
|
+
<div class="section-meta">Share this command on the new device to join it to the team.</div>
|
|
3586
|
+
<div class="actor-create-row">
|
|
3587
|
+
<div id="syncPairingDisclosureMount" style="display: contents"></div>
|
|
3588
|
+
</div>
|
|
3589
|
+
<div id="syncPairingPanelMount" style="display: contents"></div>
|
|
3590
|
+
|
|
3095
3591
|
<div id="syncSharingReview" hidden>
|
|
3096
3592
|
<h3 class="settings-group-title" style="margin-top: 20px">What teammates will receive</h3>
|
|
3097
3593
|
<div class="section-meta" id="syncSharingReviewMeta"></div>
|
|
@@ -3109,19 +3605,25 @@
|
|
|
3109
3605
|
</div>
|
|
3110
3606
|
</div>
|
|
3111
3607
|
|
|
3112
|
-
|
|
3608
|
+
</div><!-- /#syncMainView -->
|
|
3609
|
+
|
|
3610
|
+
<!-- Sub-view: Advanced diagnostics. Reached via #sync/diagnostics;
|
|
3611
|
+
shown by syncViewController when that hash is active. -->
|
|
3612
|
+
<div id="syncDiagnosticsView" hidden>
|
|
3113
3613
|
<div class="card" style="animation-delay: 0.06s">
|
|
3114
3614
|
<div class="section-header">
|
|
3115
|
-
<
|
|
3615
|
+
<div class="section-header-title">
|
|
3616
|
+
<a class="sync-subview-link" href="#sync">← Back to sync</a>
|
|
3617
|
+
<h2>Sync diagnostics</h2>
|
|
3618
|
+
</div>
|
|
3116
3619
|
<div class="section-actions">
|
|
3117
|
-
<div id="syncPairingDisclosureMount" style="display: contents"></div>
|
|
3118
3620
|
<label class="small sync-redact-control">
|
|
3119
3621
|
<span id="syncRedactMount"></span>
|
|
3120
3622
|
<span id="syncRedactLabel">Redact</span>
|
|
3121
3623
|
</label>
|
|
3122
3624
|
</div>
|
|
3123
3625
|
</div>
|
|
3124
|
-
<div class="section-meta" id="syncMeta">Use this
|
|
3626
|
+
<div class="section-meta" id="syncMeta">Use this surface for sync debugging and recent attempt details.</div>
|
|
3125
3627
|
<div class="sync-skeleton" id="syncDiagSkeleton" role="status" aria-label="Loading diagnostics">
|
|
3126
3628
|
<div class="sync-skeleton-line long"></div>
|
|
3127
3629
|
<div class="sync-skeleton-line medium"></div>
|
|
@@ -3130,11 +3632,10 @@
|
|
|
3130
3632
|
<div class="sync-actions" id="syncActions"></div>
|
|
3131
3633
|
<div class="grid-2" id="syncStatusGrid"></div>
|
|
3132
3634
|
|
|
3133
|
-
<
|
|
3134
|
-
|
|
3135
|
-
<h3 class="settings-group-title" style="margin-top: 20px">Recent sync attempts (advanced)</h3>
|
|
3635
|
+
<h3 class="settings-group-title" style="margin-top: 20px">Recent sync attempts</h3>
|
|
3136
3636
|
<div class="attempts-list" id="syncAttempts"></div>
|
|
3137
3637
|
</div>
|
|
3638
|
+
</div><!-- /#syncDiagnosticsView -->
|
|
3138
3639
|
</div>
|
|
3139
3640
|
|
|
3140
3641
|
<div class="tab-panel" id="tab-coordinator-admin" hidden>
|