@bakapiano/ccsm 0.5.0 → 0.8.3
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/README.md +172 -38
- package/bin/ccsm.js +194 -0
- package/lib/config.js +1 -0
- package/lib/favorites.js +23 -45
- package/lib/focus.js +90 -14
- package/lib/jsonStore.js +60 -0
- package/lib/labels.js +29 -0
- package/lib/webTerminal.js +173 -0
- package/lib/workspace.js +8 -4
- package/package.json +11 -3
- package/public/css/base.css +82 -0
- package/public/css/cards.css +149 -0
- package/public/css/feedback.css +219 -0
- package/public/css/forms.css +282 -0
- package/public/css/layout.css +107 -0
- package/public/css/modal.css +169 -0
- package/public/css/responsive.css +10 -0
- package/public/css/sidebar.css +165 -0
- package/public/css/tables.css +266 -0
- package/public/css/terminals.css +112 -0
- package/public/css/tokens.css +63 -0
- package/public/css/wco.css +70 -0
- package/public/css/widgets.css +204 -0
- package/public/favicon.svg +18 -0
- package/public/index.html +53 -379
- package/public/js/actions.js +87 -0
- package/public/js/api.js +103 -0
- package/public/js/backend.js +28 -0
- package/public/js/components/App.js +45 -0
- package/public/js/components/Card.js +24 -0
- package/public/js/components/DialogHost.js +45 -0
- package/public/js/components/Fab.js +11 -0
- package/public/js/components/FavoritesTable.js +81 -0
- package/public/js/components/Footer.js +12 -0
- package/public/js/components/NewSessionModal.js +142 -0
- package/public/js/components/OfflineBanner.js +52 -0
- package/public/js/components/PageHead.js +33 -0
- package/public/js/components/Pagination.js +27 -0
- package/public/js/components/ProgressList.js +32 -0
- package/public/js/components/RecentTable.js +68 -0
- package/public/js/components/RepoPicker.js +40 -0
- package/public/js/components/ReposEditor.js +74 -0
- package/public/js/components/ServerStatus.js +18 -0
- package/public/js/components/SessionsTable.js +71 -0
- package/public/js/components/Sidebar.js +52 -0
- package/public/js/components/SnapshotPanel.js +77 -0
- package/public/js/components/TerminalView.js +108 -0
- package/public/js/components/TitleCell.js +40 -0
- package/public/js/components/Toast.js +8 -0
- package/public/js/components/WorkspacePicker.js +19 -0
- package/public/js/components/WorkspacesGrid.js +41 -0
- package/public/js/dialog.js +59 -0
- package/public/js/html.js +6 -0
- package/public/js/icons.js +114 -0
- package/public/js/main.js +81 -0
- package/public/js/pages/AboutPage.js +85 -0
- package/public/js/pages/ConfigurePage.js +194 -0
- package/public/js/pages/LaunchPage.js +117 -0
- package/public/js/pages/SessionsPage.js +47 -0
- package/public/js/pages/TerminalsPage.js +74 -0
- package/public/js/state.js +87 -0
- package/public/js/streaming.js +96 -0
- package/public/js/toast.js +14 -0
- package/public/js/util.js +24 -0
- package/public/manifest.webmanifest +14 -0
- package/scripts/install.js +111 -0
- package/scripts/uninstall.js +56 -0
- package/server.js +314 -31
- package/public/app.js +0 -894
- package/public/styles.css +0 -1204
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/* App grid · main column · page header · tab panels · footer status line */
|
|
2
|
+
|
|
3
|
+
.app {
|
|
4
|
+
display: grid;
|
|
5
|
+
grid-template-columns: var(--sidebar-w) 1fr;
|
|
6
|
+
min-height: 100vh;
|
|
7
|
+
transition: grid-template-columns .25s cubic-bezier(.4, 0, .2, 1);
|
|
8
|
+
}
|
|
9
|
+
.app:has(.sidebar[data-collapsed="true"]) {
|
|
10
|
+
grid-template-columns: var(--sidebar-w-collapsed) 1fr;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.main {
|
|
14
|
+
display: flex;
|
|
15
|
+
flex-direction: column;
|
|
16
|
+
min-width: 0;
|
|
17
|
+
padding: var(--s-8) var(--s-10) var(--s-6);
|
|
18
|
+
gap: var(--s-6);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.page-head {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: flex-start;
|
|
24
|
+
justify-content: space-between;
|
|
25
|
+
gap: var(--s-8);
|
|
26
|
+
padding-bottom: var(--s-5);
|
|
27
|
+
border-bottom: 1px solid var(--border);
|
|
28
|
+
}
|
|
29
|
+
.page-head-inner { min-width: 0; }
|
|
30
|
+
|
|
31
|
+
.page-title {
|
|
32
|
+
font-size: 26px;
|
|
33
|
+
font-weight: 600;
|
|
34
|
+
letter-spacing: -0.024em;
|
|
35
|
+
color: var(--ink);
|
|
36
|
+
line-height: 1.1;
|
|
37
|
+
}
|
|
38
|
+
.page-subtitle {
|
|
39
|
+
margin-top: 4px;
|
|
40
|
+
font-size: 13.5px;
|
|
41
|
+
color: var(--ink-mid);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.page-head-meta {
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: baseline;
|
|
47
|
+
gap: var(--s-3);
|
|
48
|
+
flex-shrink: 0;
|
|
49
|
+
white-space: nowrap;
|
|
50
|
+
font-family: var(--mono);
|
|
51
|
+
font-size: 11px;
|
|
52
|
+
color: var(--ink-muted);
|
|
53
|
+
}
|
|
54
|
+
.ph-stat { display: inline-flex; gap: 6px; align-items: baseline; }
|
|
55
|
+
.ph-key {
|
|
56
|
+
font-family: var(--body);
|
|
57
|
+
font-size: 10.5px;
|
|
58
|
+
letter-spacing: 0.06em;
|
|
59
|
+
text-transform: uppercase;
|
|
60
|
+
color: var(--ink-faint);
|
|
61
|
+
}
|
|
62
|
+
.ph-val { color: var(--ink-mid); }
|
|
63
|
+
.ph-divider { color: var(--ink-faint); }
|
|
64
|
+
|
|
65
|
+
.content {
|
|
66
|
+
flex: 1;
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-direction: column;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.tab-panel {
|
|
72
|
+
display: none;
|
|
73
|
+
flex-direction: column;
|
|
74
|
+
gap: var(--s-6);
|
|
75
|
+
}
|
|
76
|
+
.tab-panel[data-active] {
|
|
77
|
+
display: flex;
|
|
78
|
+
animation: panel-in .35s cubic-bezier(.4, 0, .2, 1);
|
|
79
|
+
}
|
|
80
|
+
@keyframes panel-in {
|
|
81
|
+
from { opacity: 0; transform: translateY(6px); }
|
|
82
|
+
to { opacity: 1; transform: translateY(0); }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.footer-status {
|
|
86
|
+
margin-top: auto;
|
|
87
|
+
padding-top: var(--s-4);
|
|
88
|
+
border-top: 1px solid var(--border-soft);
|
|
89
|
+
display: flex;
|
|
90
|
+
flex-wrap: wrap;
|
|
91
|
+
align-items: baseline;
|
|
92
|
+
gap: var(--s-2);
|
|
93
|
+
font-size: 11px;
|
|
94
|
+
color: var(--ink-muted);
|
|
95
|
+
}
|
|
96
|
+
.footer-status .fs-key {
|
|
97
|
+
text-transform: uppercase;
|
|
98
|
+
letter-spacing: 0.08em;
|
|
99
|
+
font-size: 10px;
|
|
100
|
+
color: var(--ink-faint);
|
|
101
|
+
}
|
|
102
|
+
.footer-status .fs-val {
|
|
103
|
+
font-family: var(--mono);
|
|
104
|
+
font-size: 11px;
|
|
105
|
+
color: var(--ink-mid);
|
|
106
|
+
}
|
|
107
|
+
.footer-status .fs-divider { color: var(--ink-faint); margin: 0 var(--s-1); }
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/* Floating action button · modal backdrop / shell / sections ·
|
|
2
|
+
nested repos-inline-config disclosure · ad-hoc confirm/prompt dialog */
|
|
3
|
+
|
|
4
|
+
.fab {
|
|
5
|
+
position: fixed;
|
|
6
|
+
bottom: var(--s-6);
|
|
7
|
+
right: var(--s-6);
|
|
8
|
+
z-index: 90;
|
|
9
|
+
width: 48px;
|
|
10
|
+
height: 48px;
|
|
11
|
+
border-radius: 50%;
|
|
12
|
+
background: var(--ink);
|
|
13
|
+
color: var(--bg-elev);
|
|
14
|
+
border: 0;
|
|
15
|
+
cursor: pointer;
|
|
16
|
+
display: inline-flex;
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: center;
|
|
19
|
+
box-shadow:
|
|
20
|
+
0 6px 16px -6px rgba(26, 24, 21, 0.25),
|
|
21
|
+
0 1px 0 rgba(26, 24, 21, 0.06);
|
|
22
|
+
transition: background .12s ease, transform .15s ease, box-shadow .15s ease;
|
|
23
|
+
}
|
|
24
|
+
.fab:hover {
|
|
25
|
+
background: #000;
|
|
26
|
+
transform: translateY(-1px);
|
|
27
|
+
box-shadow:
|
|
28
|
+
0 10px 22px -6px rgba(26, 24, 21, 0.26),
|
|
29
|
+
0 2px 0 rgba(26, 24, 21, 0.08);
|
|
30
|
+
}
|
|
31
|
+
.fab:active { transform: scale(0.96); }
|
|
32
|
+
|
|
33
|
+
.modal-backdrop {
|
|
34
|
+
position: fixed;
|
|
35
|
+
inset: 0;
|
|
36
|
+
z-index: 200;
|
|
37
|
+
background: rgba(26, 24, 21, 0.48);
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
justify-content: center;
|
|
41
|
+
padding: var(--s-6);
|
|
42
|
+
animation: backdrop-in .18s ease;
|
|
43
|
+
}
|
|
44
|
+
@keyframes backdrop-in { from { opacity: 0; } to { opacity: 1; } }
|
|
45
|
+
|
|
46
|
+
.modal {
|
|
47
|
+
background: var(--bg-elev);
|
|
48
|
+
border: 1px solid var(--border);
|
|
49
|
+
border-radius: var(--r-md);
|
|
50
|
+
width: min(560px, 100%);
|
|
51
|
+
max-height: 90vh;
|
|
52
|
+
display: flex;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
overflow: hidden;
|
|
55
|
+
box-shadow:
|
|
56
|
+
0 24px 64px -16px rgba(26, 24, 21, 0.35),
|
|
57
|
+
0 4px 12px -2px rgba(26, 24, 21, 0.15);
|
|
58
|
+
animation: modal-in .22s cubic-bezier(.4, 0, .2, 1);
|
|
59
|
+
}
|
|
60
|
+
@keyframes modal-in {
|
|
61
|
+
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
|
62
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.modal-head {
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
justify-content: space-between;
|
|
69
|
+
padding: var(--s-4) var(--s-6);
|
|
70
|
+
border-bottom: 1px solid var(--border-soft);
|
|
71
|
+
}
|
|
72
|
+
.modal-head h2 {
|
|
73
|
+
font-size: 16px;
|
|
74
|
+
font-weight: 600;
|
|
75
|
+
color: var(--ink);
|
|
76
|
+
}
|
|
77
|
+
.modal-close {
|
|
78
|
+
appearance: none;
|
|
79
|
+
background: transparent;
|
|
80
|
+
border: 0;
|
|
81
|
+
padding: 4px;
|
|
82
|
+
margin: -4px;
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
color: var(--ink-muted);
|
|
85
|
+
border-radius: 4px;
|
|
86
|
+
display: inline-flex;
|
|
87
|
+
transition: color .12s ease, background .12s ease;
|
|
88
|
+
}
|
|
89
|
+
.modal-close:hover { color: var(--ink); background: var(--bg); }
|
|
90
|
+
|
|
91
|
+
.modal-body {
|
|
92
|
+
padding: var(--s-5) var(--s-6);
|
|
93
|
+
overflow-y: auto;
|
|
94
|
+
flex: 1;
|
|
95
|
+
}
|
|
96
|
+
.modal-hint {
|
|
97
|
+
font-size: 13px;
|
|
98
|
+
color: var(--ink-muted);
|
|
99
|
+
margin-bottom: var(--s-4);
|
|
100
|
+
}
|
|
101
|
+
.modal-hint code {
|
|
102
|
+
font-family: var(--mono);
|
|
103
|
+
font-size: 11.5px;
|
|
104
|
+
background: var(--bg);
|
|
105
|
+
padding: 1px 5px;
|
|
106
|
+
border-radius: 4px;
|
|
107
|
+
border: 1px solid var(--border-soft);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.modal-foot {
|
|
111
|
+
display: flex;
|
|
112
|
+
justify-content: flex-end;
|
|
113
|
+
gap: var(--s-3);
|
|
114
|
+
padding: var(--s-3) var(--s-6);
|
|
115
|
+
border-top: 1px solid var(--border-soft);
|
|
116
|
+
background: var(--bg);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* Nested "Edit repos" disclosure inside the new-session modal */
|
|
120
|
+
.repos-inline-config {
|
|
121
|
+
margin: var(--s-3) 0;
|
|
122
|
+
border: 1px solid var(--border);
|
|
123
|
+
border-radius: var(--r-sm);
|
|
124
|
+
background: var(--bg);
|
|
125
|
+
}
|
|
126
|
+
.repos-inline-config summary {
|
|
127
|
+
cursor: pointer;
|
|
128
|
+
padding: 8px var(--s-3);
|
|
129
|
+
font-size: 12.5px;
|
|
130
|
+
color: var(--ink-mid);
|
|
131
|
+
font-weight: 500;
|
|
132
|
+
user-select: none;
|
|
133
|
+
}
|
|
134
|
+
.repos-inline-config summary::marker { color: var(--ink-mid); }
|
|
135
|
+
.repos-inline-config summary:hover { color: var(--ink); }
|
|
136
|
+
.repos-inline-config[open] summary { border-bottom: 1px solid var(--border); }
|
|
137
|
+
.repos-inline-body {
|
|
138
|
+
padding: var(--s-3);
|
|
139
|
+
background: var(--bg-elev);
|
|
140
|
+
border-radius: 0 0 var(--r-sm) var(--r-sm);
|
|
141
|
+
}
|
|
142
|
+
.repos-inline-actions {
|
|
143
|
+
display: flex;
|
|
144
|
+
gap: var(--s-3);
|
|
145
|
+
align-items: center;
|
|
146
|
+
margin-top: var(--s-3);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* Narrower variant for confirm/prompt — no border under head, tighter padding */
|
|
150
|
+
.modal-dialog { width: min(440px, 100%); }
|
|
151
|
+
.modal-dialog .modal-head { padding: var(--s-4) var(--s-5) var(--s-3); border-bottom: 0; }
|
|
152
|
+
.modal-dialog .modal-head h2 {
|
|
153
|
+
font-size: 14.5px;
|
|
154
|
+
font-weight: 600;
|
|
155
|
+
color: var(--ink);
|
|
156
|
+
line-height: 1.4;
|
|
157
|
+
}
|
|
158
|
+
.modal-dialog .modal-body { padding: 0 var(--s-5) var(--s-4); }
|
|
159
|
+
.modal-dialog .modal-foot { padding: var(--s-3) var(--s-5); }
|
|
160
|
+
.modal-dialog input[type="text"] {
|
|
161
|
+
width: 100%;
|
|
162
|
+
max-width: none;
|
|
163
|
+
margin-top: var(--s-2);
|
|
164
|
+
}
|
|
165
|
+
.dialog-msg {
|
|
166
|
+
font-size: 13.5px;
|
|
167
|
+
color: var(--ink-mid);
|
|
168
|
+
line-height: 1.55;
|
|
169
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* Narrow viewports: force-collapse sidebar, single-col config grid */
|
|
2
|
+
|
|
3
|
+
@media (max-width: 900px) {
|
|
4
|
+
.app { grid-template-columns: var(--sidebar-w-collapsed) 1fr !important; }
|
|
5
|
+
.sidebar { padding: var(--s-4) var(--s-2); }
|
|
6
|
+
.brand-name, .nav-label, .nav-badge { opacity: 0; pointer-events: none; }
|
|
7
|
+
.main { padding: var(--s-6) var(--s-5) var(--s-5); }
|
|
8
|
+
.page-head { flex-direction: column; gap: var(--s-3); }
|
|
9
|
+
.config-grid { grid-template-columns: 1fr; }
|
|
10
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/* Left collapsible sidebar nav · brand mark · util items · collapse toggle */
|
|
2
|
+
|
|
3
|
+
.sidebar {
|
|
4
|
+
position: sticky;
|
|
5
|
+
top: 0;
|
|
6
|
+
height: 100vh;
|
|
7
|
+
background: var(--sidebar-bg);
|
|
8
|
+
border-right: 1px solid var(--border);
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
padding: var(--s-4) var(--s-3);
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
transition: padding .25s cubic-bezier(.4, 0, .2, 1);
|
|
14
|
+
}
|
|
15
|
+
.sidebar[data-collapsed="true"] {
|
|
16
|
+
padding: var(--s-4) var(--s-2);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.sidebar-brand {
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
gap: var(--s-2);
|
|
23
|
+
/* Top padding matches .main's so the brand mark sits on the same
|
|
24
|
+
horizontal band as the page-title. */
|
|
25
|
+
padding: var(--s-4) var(--s-2);
|
|
26
|
+
min-height: 56px;
|
|
27
|
+
}
|
|
28
|
+
.brand-mark {
|
|
29
|
+
display: inline-flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
width: 32px;
|
|
33
|
+
height: 32px;
|
|
34
|
+
flex: 0 0 32px;
|
|
35
|
+
background: transparent;
|
|
36
|
+
}
|
|
37
|
+
.brand-mark svg { display: block; }
|
|
38
|
+
.brand-name {
|
|
39
|
+
font-size: 19px;
|
|
40
|
+
font-weight: 600;
|
|
41
|
+
letter-spacing: -0.02em;
|
|
42
|
+
color: var(--ink);
|
|
43
|
+
white-space: nowrap;
|
|
44
|
+
opacity: 1;
|
|
45
|
+
transition: opacity .15s ease;
|
|
46
|
+
}
|
|
47
|
+
.brand-dot { color: var(--accent); }
|
|
48
|
+
.sidebar[data-collapsed="true"] .brand-name { opacity: 0; pointer-events: none; }
|
|
49
|
+
|
|
50
|
+
.sidebar-nav {
|
|
51
|
+
display: flex;
|
|
52
|
+
flex-direction: column;
|
|
53
|
+
gap: 2px;
|
|
54
|
+
flex: 0 0 auto;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.nav-item, .util-item {
|
|
58
|
+
appearance: none;
|
|
59
|
+
background: transparent;
|
|
60
|
+
border: 0;
|
|
61
|
+
display: flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
gap: var(--s-3);
|
|
64
|
+
width: 100%;
|
|
65
|
+
padding: 0 12px;
|
|
66
|
+
min-height: 42px;
|
|
67
|
+
border-radius: var(--r-sm);
|
|
68
|
+
cursor: pointer;
|
|
69
|
+
color: var(--ink-mid);
|
|
70
|
+
font-family: var(--body);
|
|
71
|
+
font-size: 14.5px;
|
|
72
|
+
font-weight: 500;
|
|
73
|
+
text-align: left;
|
|
74
|
+
transition: background .12s ease, color .12s ease;
|
|
75
|
+
position: relative;
|
|
76
|
+
}
|
|
77
|
+
.nav-item:hover, .util-item:hover {
|
|
78
|
+
background: var(--sidebar-hover);
|
|
79
|
+
color: var(--ink);
|
|
80
|
+
}
|
|
81
|
+
.nav-item[aria-selected="true"] {
|
|
82
|
+
background: var(--sidebar-active);
|
|
83
|
+
color: var(--ink);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* Unsaved-changes dot next to nav label */
|
|
87
|
+
.nav-item.has-changes::after {
|
|
88
|
+
content: "";
|
|
89
|
+
position: absolute;
|
|
90
|
+
right: 10px;
|
|
91
|
+
top: 50%;
|
|
92
|
+
transform: translateY(-50%);
|
|
93
|
+
width: 7px;
|
|
94
|
+
height: 7px;
|
|
95
|
+
border-radius: 50%;
|
|
96
|
+
background: var(--ink);
|
|
97
|
+
box-shadow: 0 0 0 0 rgba(26, 24, 21, 0.30);
|
|
98
|
+
animation: dirty-pulse 2s ease-in-out infinite;
|
|
99
|
+
}
|
|
100
|
+
@keyframes dirty-pulse {
|
|
101
|
+
0%, 100% { box-shadow: 0 0 0 0 rgba(26, 24, 21, 0.22); }
|
|
102
|
+
50% { box-shadow: 0 0 0 4px rgba(26, 24, 21, 0); }
|
|
103
|
+
}
|
|
104
|
+
.sidebar[data-collapsed="true"] .nav-item.has-changes::after {
|
|
105
|
+
right: auto;
|
|
106
|
+
top: 6px;
|
|
107
|
+
left: 28px;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.nav-icon {
|
|
111
|
+
display: inline-flex;
|
|
112
|
+
width: 20px;
|
|
113
|
+
height: 20px;
|
|
114
|
+
flex: 0 0 20px;
|
|
115
|
+
color: currentColor;
|
|
116
|
+
}
|
|
117
|
+
.nav-icon svg { width: 100%; height: 100%; }
|
|
118
|
+
.nav-label {
|
|
119
|
+
white-space: nowrap;
|
|
120
|
+
opacity: 1;
|
|
121
|
+
transition: opacity .15s ease;
|
|
122
|
+
flex: 1;
|
|
123
|
+
}
|
|
124
|
+
.sidebar[data-collapsed="true"] .nav-label { opacity: 0; pointer-events: none; }
|
|
125
|
+
|
|
126
|
+
.nav-badge {
|
|
127
|
+
font-family: var(--mono);
|
|
128
|
+
font-size: 10.5px;
|
|
129
|
+
background: var(--border-soft);
|
|
130
|
+
color: var(--ink-muted);
|
|
131
|
+
padding: 1px 6px;
|
|
132
|
+
border-radius: 4px;
|
|
133
|
+
font-variant-numeric: tabular-nums;
|
|
134
|
+
opacity: 1;
|
|
135
|
+
transition: opacity .15s ease;
|
|
136
|
+
}
|
|
137
|
+
.sidebar[data-collapsed="true"] .nav-badge { opacity: 0; }
|
|
138
|
+
.nav-item[aria-selected="true"] .nav-badge {
|
|
139
|
+
background: var(--bg-elev);
|
|
140
|
+
color: var(--ink-mid);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.sidebar-divider {
|
|
144
|
+
margin: var(--s-3) var(--s-2);
|
|
145
|
+
border-top: 1px solid var(--border);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.sidebar-utility {
|
|
149
|
+
display: flex;
|
|
150
|
+
flex-direction: column;
|
|
151
|
+
gap: 2px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.sidebar-foot {
|
|
155
|
+
margin-top: auto;
|
|
156
|
+
padding-top: var(--s-2);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* chevron flips when the sidebar is collapsed; the rest is util-item styling */
|
|
160
|
+
.collapse-toggle .nav-icon {
|
|
161
|
+
transition: transform .25s cubic-bezier(.4, 0, .2, 1);
|
|
162
|
+
}
|
|
163
|
+
.sidebar[data-collapsed="true"] .collapse-toggle .nav-icon {
|
|
164
|
+
transform: rotate(180deg);
|
|
165
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/* Data tables · sticky actions column · status mark · title/path cells ·
|
|
2
|
+
star + rename inline buttons · row stagger animation */
|
|
3
|
+
|
|
4
|
+
.table-scroll {
|
|
5
|
+
overflow-x: auto;
|
|
6
|
+
/* tactile: scroll-padding so the actions column edges aren't flush
|
|
7
|
+
against the viewport when you scroll right to reach the buttons */
|
|
8
|
+
scroll-padding-right: var(--s-6);
|
|
9
|
+
}
|
|
10
|
+
.table-scroll::-webkit-scrollbar { height: 8px; }
|
|
11
|
+
.table-scroll .data {
|
|
12
|
+
min-width: 960px;
|
|
13
|
+
table-layout: auto;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.data {
|
|
17
|
+
width: 100%;
|
|
18
|
+
border-collapse: collapse;
|
|
19
|
+
font-family: var(--body);
|
|
20
|
+
font-size: 13.5px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.data thead th {
|
|
24
|
+
padding: 10px var(--s-5) 10px;
|
|
25
|
+
text-align: left;
|
|
26
|
+
font-family: var(--body);
|
|
27
|
+
font-size: 11px;
|
|
28
|
+
font-weight: 600;
|
|
29
|
+
letter-spacing: 0.04em;
|
|
30
|
+
color: var(--ink-muted);
|
|
31
|
+
border-bottom: 1px solid var(--border);
|
|
32
|
+
background: var(--bg);
|
|
33
|
+
vertical-align: bottom;
|
|
34
|
+
white-space: nowrap;
|
|
35
|
+
}
|
|
36
|
+
.data thead th.num { text-align: right; }
|
|
37
|
+
/* shrink-to-fit pattern: width:1% with nowrap → the cell takes only
|
|
38
|
+
what it needs; title / path cells get the remainder */
|
|
39
|
+
.data thead th.col-mark { width: 28px; padding-left: 0; padding-right: 0; }
|
|
40
|
+
.data thead th.col-star { width: 28px; padding-left: 0; padding-right: 0; }
|
|
41
|
+
.data thead th.col-actions { width: 1%; white-space: nowrap; }
|
|
42
|
+
.data thead th.num { width: 1%; white-space: nowrap; }
|
|
43
|
+
.data tbody td.num { white-space: nowrap; }
|
|
44
|
+
.data tbody td:last-child { white-space: nowrap; }
|
|
45
|
+
.data thead th:first-child { padding-left: var(--s-6); }
|
|
46
|
+
.data thead th:last-child { padding-right: var(--s-6); }
|
|
47
|
+
|
|
48
|
+
/* Last column pins to the right edge of the .table-scroll container during
|
|
49
|
+
horizontal scroll. Backgrounds must be solid because sticky cells overlay
|
|
50
|
+
the content scrolling beneath them. */
|
|
51
|
+
.data thead th:last-child,
|
|
52
|
+
.data tbody td:last-child {
|
|
53
|
+
position: sticky;
|
|
54
|
+
right: 0;
|
|
55
|
+
z-index: 1;
|
|
56
|
+
box-shadow: -10px 0 10px -8px rgba(26, 24, 21, 0.08);
|
|
57
|
+
}
|
|
58
|
+
.data thead th:last-child { background: var(--bg); }
|
|
59
|
+
.data tbody td:last-child { background: var(--bg-elev); }
|
|
60
|
+
.data tbody tr:hover td:last-child { background: var(--bg); }
|
|
61
|
+
|
|
62
|
+
.data tbody tr {
|
|
63
|
+
border-bottom: 1px solid var(--border-soft);
|
|
64
|
+
transition: background .12s ease;
|
|
65
|
+
}
|
|
66
|
+
.data tbody tr:last-child { border-bottom: 0; }
|
|
67
|
+
.data tbody tr:hover { background: var(--bg); }
|
|
68
|
+
|
|
69
|
+
.data tbody td {
|
|
70
|
+
padding: 12px var(--s-5);
|
|
71
|
+
vertical-align: middle;
|
|
72
|
+
color: var(--ink);
|
|
73
|
+
}
|
|
74
|
+
.data tbody td:first-child { padding-left: var(--s-6); }
|
|
75
|
+
.data tbody td:last-child { padding-right: var(--s-6); }
|
|
76
|
+
.data tbody td.num {
|
|
77
|
+
text-align: right;
|
|
78
|
+
font-family: var(--mono);
|
|
79
|
+
font-size: 11.5px;
|
|
80
|
+
color: var(--ink-mid);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/* JS adds .no-anim after the first render so 5s auto-refresh doesn't
|
|
84
|
+
restage the stagger and strobe. */
|
|
85
|
+
.data tbody:not(.no-anim) tr { animation: row-in .3s ease-out backwards; }
|
|
86
|
+
.data tbody:not(.no-anim) tr:nth-child(1) { animation-delay: 0ms; }
|
|
87
|
+
.data tbody:not(.no-anim) tr:nth-child(2) { animation-delay: 20ms; }
|
|
88
|
+
.data tbody:not(.no-anim) tr:nth-child(3) { animation-delay: 40ms; }
|
|
89
|
+
.data tbody:not(.no-anim) tr:nth-child(4) { animation-delay: 60ms; }
|
|
90
|
+
.data tbody:not(.no-anim) tr:nth-child(5) { animation-delay: 80ms; }
|
|
91
|
+
.data tbody:not(.no-anim) tr:nth-child(6) { animation-delay: 100ms; }
|
|
92
|
+
.data tbody:not(.no-anim) tr:nth-child(7) { animation-delay: 120ms; }
|
|
93
|
+
.data tbody:not(.no-anim) tr:nth-child(8) { animation-delay: 140ms; }
|
|
94
|
+
.data tbody:not(.no-anim) tr:nth-child(n+9) { animation-delay: 160ms; }
|
|
95
|
+
@keyframes row-in {
|
|
96
|
+
from { opacity: 0; transform: translateY(3px); }
|
|
97
|
+
to { opacity: 1; transform: translateY(0); }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* Status indicator dot — claude-like. Base color is the muted gray so any
|
|
101
|
+
unrecognized status string Claude writes ("compacting", "thinking", a
|
|
102
|
+
transient state, etc.) falls back to "unknown-looking" rather than a
|
|
103
|
+
stark black dot. .busy / .idle override. */
|
|
104
|
+
.status-mark {
|
|
105
|
+
display: inline-block;
|
|
106
|
+
width: 8px;
|
|
107
|
+
height: 8px;
|
|
108
|
+
border-radius: 50%;
|
|
109
|
+
background: currentColor;
|
|
110
|
+
vertical-align: middle;
|
|
111
|
+
position: relative;
|
|
112
|
+
color: var(--ink-faint);
|
|
113
|
+
}
|
|
114
|
+
.status-mark.busy {
|
|
115
|
+
color: var(--blue);
|
|
116
|
+
box-shadow: 0 0 0 0 currentColor;
|
|
117
|
+
animation: pulse 1.5s ease-in-out infinite;
|
|
118
|
+
}
|
|
119
|
+
.status-mark.idle { color: var(--green); }
|
|
120
|
+
.status-mark.unknown { color: var(--ink-faint); }
|
|
121
|
+
@keyframes pulse {
|
|
122
|
+
0% { box-shadow: 0 0 0 0 rgba(74, 115, 165, 0.4); }
|
|
123
|
+
70% { box-shadow: 0 0 0 7px rgba(74, 115, 165, 0); }
|
|
124
|
+
100% { box-shadow: 0 0 0 0 rgba(74, 115, 165, 0); }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* Title and path are fixed-width with overflow ellipsis on the inner
|
|
128
|
+
spans so the sticky actions column is always visible at typical widths;
|
|
129
|
+
long values truncate, the title attr shows the full value on hover. */
|
|
130
|
+
.title-cell {
|
|
131
|
+
width: 300px;
|
|
132
|
+
max-width: 300px;
|
|
133
|
+
min-width: 0;
|
|
134
|
+
}
|
|
135
|
+
.title-cell .title-row {
|
|
136
|
+
display: flex;
|
|
137
|
+
align-items: center;
|
|
138
|
+
gap: 6px;
|
|
139
|
+
min-width: 0;
|
|
140
|
+
width: 100%;
|
|
141
|
+
}
|
|
142
|
+
.title-cell .primary {
|
|
143
|
+
color: var(--ink);
|
|
144
|
+
font-weight: 500;
|
|
145
|
+
white-space: nowrap;
|
|
146
|
+
overflow: hidden;
|
|
147
|
+
text-overflow: ellipsis;
|
|
148
|
+
font-size: 13.5px;
|
|
149
|
+
min-width: 0;
|
|
150
|
+
flex: 0 1 auto;
|
|
151
|
+
}
|
|
152
|
+
.title-cell .secondary {
|
|
153
|
+
font-family: var(--mono);
|
|
154
|
+
font-size: 10.5px;
|
|
155
|
+
color: var(--ink-muted);
|
|
156
|
+
letter-spacing: 0.02em;
|
|
157
|
+
margin-top: 2px;
|
|
158
|
+
white-space: nowrap;
|
|
159
|
+
overflow: hidden;
|
|
160
|
+
text-overflow: ellipsis;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.path-cell {
|
|
164
|
+
font-family: var(--mono);
|
|
165
|
+
font-size: 11.5px;
|
|
166
|
+
color: var(--ink-mid);
|
|
167
|
+
width: 260px;
|
|
168
|
+
max-width: 260px;
|
|
169
|
+
min-width: 0;
|
|
170
|
+
white-space: nowrap;
|
|
171
|
+
overflow: hidden;
|
|
172
|
+
text-overflow: ellipsis;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.branch-tag {
|
|
176
|
+
display: inline-block;
|
|
177
|
+
font-family: var(--mono);
|
|
178
|
+
font-size: 11px;
|
|
179
|
+
color: var(--ink-mid);
|
|
180
|
+
background: var(--bg);
|
|
181
|
+
padding: 2px 7px;
|
|
182
|
+
border-radius: 4px;
|
|
183
|
+
border: 1px solid var(--border-soft);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.row-actions {
|
|
187
|
+
display: flex;
|
|
188
|
+
gap: var(--s-2);
|
|
189
|
+
justify-content: flex-end;
|
|
190
|
+
}
|
|
191
|
+
/* Equalised width so Focus / Continue / Resume align cleanly down the
|
|
192
|
+
actions column (last column is sticky, so width drift is visible).
|
|
193
|
+
.action is inline-flex with justify-content:center, so the icon+text
|
|
194
|
+
cluster sits in the middle. */
|
|
195
|
+
.row-actions .action.small {
|
|
196
|
+
min-width: 96px;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* Star toggle — inline next to the title text. Outline by default;
|
|
200
|
+
filled in ink when pinned. */
|
|
201
|
+
.star-btn {
|
|
202
|
+
appearance: none;
|
|
203
|
+
background: transparent;
|
|
204
|
+
border: 0;
|
|
205
|
+
padding: 2px;
|
|
206
|
+
margin: 0;
|
|
207
|
+
cursor: pointer;
|
|
208
|
+
color: var(--ink-muted);
|
|
209
|
+
display: inline-flex;
|
|
210
|
+
align-items: center;
|
|
211
|
+
justify-content: center;
|
|
212
|
+
border-radius: 4px;
|
|
213
|
+
transition: color .12s ease, background .12s ease, transform .15s ease;
|
|
214
|
+
line-height: 0;
|
|
215
|
+
flex: 0 0 auto;
|
|
216
|
+
}
|
|
217
|
+
.data tbody tr:hover .star-btn { color: var(--ink-mid); }
|
|
218
|
+
.star-btn:hover {
|
|
219
|
+
color: #e3b341 !important;
|
|
220
|
+
background: var(--bg);
|
|
221
|
+
}
|
|
222
|
+
.star-btn:active { transform: scale(0.88); }
|
|
223
|
+
.star-btn.is-fav { color: #e3b341; }
|
|
224
|
+
.star-btn svg { display: block; }
|
|
225
|
+
|
|
226
|
+
/* Rename (pencil) — only visible on row hover so it doesn't compete with
|
|
227
|
+
the star. When a label is set (.has-label), it stays subtly visible to
|
|
228
|
+
indicate the title is overridden. */
|
|
229
|
+
.rename-btn {
|
|
230
|
+
appearance: none;
|
|
231
|
+
background: transparent;
|
|
232
|
+
border: 0;
|
|
233
|
+
padding: 2px;
|
|
234
|
+
margin: 0;
|
|
235
|
+
cursor: pointer;
|
|
236
|
+
color: var(--ink-faint);
|
|
237
|
+
opacity: 0;
|
|
238
|
+
display: inline-flex;
|
|
239
|
+
align-items: center;
|
|
240
|
+
justify-content: center;
|
|
241
|
+
border-radius: 4px;
|
|
242
|
+
transition: opacity .12s ease, color .12s ease, background .12s ease, transform .15s ease;
|
|
243
|
+
line-height: 0;
|
|
244
|
+
flex: 0 0 auto;
|
|
245
|
+
}
|
|
246
|
+
.data tbody tr:hover .rename-btn {
|
|
247
|
+
opacity: 1;
|
|
248
|
+
color: var(--ink-muted);
|
|
249
|
+
}
|
|
250
|
+
.rename-btn:hover {
|
|
251
|
+
color: var(--ink) !important;
|
|
252
|
+
background: var(--bg);
|
|
253
|
+
}
|
|
254
|
+
.rename-btn:active { transform: scale(0.88); }
|
|
255
|
+
.rename-btn.has-label {
|
|
256
|
+
opacity: 0.8;
|
|
257
|
+
color: var(--ink);
|
|
258
|
+
}
|
|
259
|
+
.rename-btn svg { display: block; }
|
|
260
|
+
|
|
261
|
+
#favoritesEmpty {
|
|
262
|
+
padding: var(--s-6) var(--s-6);
|
|
263
|
+
text-align: center;
|
|
264
|
+
font-size: 12.5px;
|
|
265
|
+
color: var(--ink-muted);
|
|
266
|
+
}
|