@codemem/server 0.28.1 → 0.29.1
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 +368 -45
- package/dist/index.js.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 +2 -2
- package/static/app.js +2053 -952
- package/static/index.html +518 -23
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;
|
|
@@ -1575,7 +1827,9 @@
|
|
|
1575
1827
|
border-radius: var(--radius-sm);
|
|
1576
1828
|
background: var(--surface-2);
|
|
1577
1829
|
}
|
|
1578
|
-
|
|
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); }
|
|
1579
1833
|
|
|
1580
1834
|
.health-action-text, .sync-action-text { font-size: 13px; color: var(--text-primary); flex: 1; min-width: 0; }
|
|
1581
1835
|
.sync-section-heading {
|
|
@@ -1634,11 +1888,26 @@
|
|
|
1634
1888
|
|
|
1635
1889
|
/* ── Peers ─────────────────────────────────────────────── */
|
|
1636
1890
|
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
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);
|
|
1640
1910
|
}
|
|
1641
|
-
.peer-card { border-radius: var(--radius-lg); padding: var(--sp-4); display: grid; gap: var(--sp-2); }
|
|
1642
1911
|
.peer-title { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); flex-wrap: wrap; }
|
|
1643
1912
|
.peer-title strong { font-size: 14px; font-weight: 600; }
|
|
1644
1913
|
/* Direction glyph rendered when a peer is in mutual-trust state.
|
|
@@ -1670,9 +1939,189 @@
|
|
|
1670
1939
|
.peer-actor-row { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
|
|
1671
1940
|
.peer-scope-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); }
|
|
1672
1941
|
.peer-scope-editor { display: grid; gap: 8px; }
|
|
1673
|
-
.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); }
|
|
1674
1943
|
.peer-scope-chip { padding: 4px 8px; background: rgba(43, 122, 176, 0.12); color: var(--accent); font-size: 12px; }
|
|
1675
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
|
+
}
|
|
1676
2125
|
.peer-scope-chip-remove { border: none; background: transparent; color: inherit; cursor: pointer; padding: 0; font: inherit; opacity: 0.75; }
|
|
1677
2126
|
.peer-scope-chip-remove:hover { opacity: 1; }
|
|
1678
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; }
|
|
@@ -2583,7 +3032,8 @@
|
|
|
2583
3032
|
align-items: center;
|
|
2584
3033
|
}
|
|
2585
3034
|
.sync-dialog-input {
|
|
2586
|
-
width: 100
|
|
3035
|
+
width: calc(100% - 6px);
|
|
3036
|
+
margin: 3px;
|
|
2587
3037
|
padding: var(--sp-2) var(--sp-3);
|
|
2588
3038
|
border-radius: var(--radius-sm);
|
|
2589
3039
|
border: 1px solid var(--border);
|
|
@@ -2591,11 +3041,44 @@
|
|
|
2591
3041
|
color: var(--text-primary);
|
|
2592
3042
|
font-family: var(--font-sans);
|
|
2593
3043
|
font-size: 13px;
|
|
3044
|
+
box-sizing: border-box;
|
|
2594
3045
|
}
|
|
2595
3046
|
.sync-dialog-input:focus {
|
|
2596
|
-
outline: none;
|
|
2597
3047
|
border-color: var(--accent);
|
|
2598
|
-
|
|
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);
|
|
2599
3082
|
}
|
|
2600
3083
|
.sync-dialog-confirm.danger {
|
|
2601
3084
|
background: rgba(182, 75, 47, 0.12);
|
|
@@ -3026,6 +3509,10 @@
|
|
|
3026
3509
|
<!-- ── Tab: Sync ───────────────────────────────────────── -->
|
|
3027
3510
|
<div class="tab-panel" id="tab-sync" hidden>
|
|
3028
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
|
+
|
|
3029
3516
|
<!-- 1. Needs attention + team setup/status -->
|
|
3030
3517
|
<div class="card" id="syncTeamCard">
|
|
3031
3518
|
<div class="section-header">
|
|
@@ -3035,6 +3522,7 @@
|
|
|
3035
3522
|
</div>
|
|
3036
3523
|
<div class="section-actions">
|
|
3037
3524
|
<button class="settings-button" id="syncNowButton">Sync now</button>
|
|
3525
|
+
<a class="sync-subview-link" href="#sync/diagnostics">View diagnostics →</a>
|
|
3038
3526
|
</div>
|
|
3039
3527
|
</div>
|
|
3040
3528
|
<div class="section-meta" id="syncTeamMeta">Start with the next step below, then review team status and device details.</div>
|
|
@@ -3049,22 +3537,20 @@
|
|
|
3049
3537
|
<h3 class="settings-group-title">Join a team</h3>
|
|
3050
3538
|
<div class="section-meta">Have an invite? Paste it here.</div>
|
|
3051
3539
|
<div class="actor-create-row" id="syncJoinPanel">
|
|
3052
|
-
<label for="syncJoinInvite" class="sr-only">Team invite</label>
|
|
3053
|
-
<textarea class="feed-search" id="syncJoinInvite" placeholder="Paste team invite or
|
|
3054
|
-
<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>
|
|
3055
3543
|
</div>
|
|
3056
3544
|
</div>
|
|
3057
3545
|
<div id="syncAdminSection">
|
|
3058
3546
|
<div id="syncAdminDisclosureMount" style="display: contents"></div>
|
|
3059
3547
|
</div>
|
|
3060
3548
|
</div>
|
|
3061
|
-
<h3 class="settings-group-title" style="margin-top: 20px">Team status</h3>
|
|
3062
|
-
<div class="actor-list" id="syncTeamStatus" aria-live="polite"></div>
|
|
3063
3549
|
<div class="actor-list" id="syncJoinRequests"></div>
|
|
3064
3550
|
<div id="syncCoordinatorDiscovered" hidden>
|
|
3065
|
-
<h3 class="settings-group-title" style="margin-top: 20px">
|
|
3551
|
+
<h3 class="settings-group-title" style="margin-top: 20px">Team devices to pair</h3>
|
|
3066
3552
|
<div class="section-meta" id="syncCoordinatorDiscoveredMeta"></div>
|
|
3067
|
-
<div class="
|
|
3553
|
+
<div class="peer-list" id="syncCoordinatorDiscoveredList"></div>
|
|
3068
3554
|
</div>
|
|
3069
3555
|
</div>
|
|
3070
3556
|
|
|
@@ -3089,9 +3575,10 @@
|
|
|
3089
3575
|
|
|
3090
3576
|
<h3 class="settings-group-title" style="margin-top: 20px">Devices</h3>
|
|
3091
3577
|
<div class="peer-list" id="syncPeers"></div>
|
|
3092
|
-
<div class="
|
|
3093
|
-
<div class="
|
|
3094
|
-
<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>
|
|
3095
3582
|
</div>
|
|
3096
3583
|
|
|
3097
3584
|
<h3 class="settings-group-title" style="margin-top: 20px">Connect another device</h3>
|
|
@@ -3118,10 +3605,17 @@
|
|
|
3118
3605
|
</div>
|
|
3119
3606
|
</div>
|
|
3120
3607
|
|
|
3121
|
-
|
|
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>
|
|
3122
3613
|
<div class="card" style="animation-delay: 0.06s">
|
|
3123
3614
|
<div class="section-header">
|
|
3124
|
-
<
|
|
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>
|
|
3125
3619
|
<div class="section-actions">
|
|
3126
3620
|
<label class="small sync-redact-control">
|
|
3127
3621
|
<span id="syncRedactMount"></span>
|
|
@@ -3129,7 +3623,7 @@
|
|
|
3129
3623
|
</label>
|
|
3130
3624
|
</div>
|
|
3131
3625
|
</div>
|
|
3132
|
-
<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>
|
|
3133
3627
|
<div class="sync-skeleton" id="syncDiagSkeleton" role="status" aria-label="Loading diagnostics">
|
|
3134
3628
|
<div class="sync-skeleton-line long"></div>
|
|
3135
3629
|
<div class="sync-skeleton-line medium"></div>
|
|
@@ -3138,9 +3632,10 @@
|
|
|
3138
3632
|
<div class="sync-actions" id="syncActions"></div>
|
|
3139
3633
|
<div class="grid-2" id="syncStatusGrid"></div>
|
|
3140
3634
|
|
|
3141
|
-
<h3 class="settings-group-title" style="margin-top: 20px">Recent sync attempts
|
|
3635
|
+
<h3 class="settings-group-title" style="margin-top: 20px">Recent sync attempts</h3>
|
|
3142
3636
|
<div class="attempts-list" id="syncAttempts"></div>
|
|
3143
3637
|
</div>
|
|
3638
|
+
</div><!-- /#syncDiagnosticsView -->
|
|
3144
3639
|
</div>
|
|
3145
3640
|
|
|
3146
3641
|
<div class="tab-panel" id="tab-coordinator-admin" hidden>
|