semantic-code-review 0.24.2__py3-none-any.whl
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.
- semantic_code_review/__init__.py +5 -0
- semantic_code_review/augment/__init__.py +3 -0
- semantic_code_review/augment/agents.py +95 -0
- semantic_code_review/augment/console.py +440 -0
- semantic_code_review/augment/extra_review.py +237 -0
- semantic_code_review/augment/fold_summary.py +447 -0
- semantic_code_review/augment/hunks.py +264 -0
- semantic_code_review/augment/mcp_server.py +156 -0
- semantic_code_review/augment/overview.py +233 -0
- semantic_code_review/augment/pass_.py +171 -0
- semantic_code_review/augment/pipeline.py +571 -0
- semantic_code_review/augment/progress.py +223 -0
- semantic_code_review/augment/prompts.py +93 -0
- semantic_code_review/augment/schemas.py +408 -0
- semantic_code_review/augment/tools.py +475 -0
- semantic_code_review/augment/trace_adapter.py +369 -0
- semantic_code_review/backends/__init__.py +95 -0
- semantic_code_review/backends/_cli_driver.py +571 -0
- semantic_code_review/backends/anthropic_sdk.py +56 -0
- semantic_code_review/backends/base.py +104 -0
- semantic_code_review/backends/claude_cli.py +421 -0
- semantic_code_review/backends/google_sdk.py +73 -0
- semantic_code_review/backends/openai_compat.py +42 -0
- semantic_code_review/cache/__init__.py +3 -0
- semantic_code_review/cache/store.py +87 -0
- semantic_code_review/cli/__init__.py +70 -0
- semantic_code_review/cli/_shared.py +157 -0
- semantic_code_review/cli/augment.py +74 -0
- semantic_code_review/cli/config_cmd.py +266 -0
- semantic_code_review/cli/fetch.py +33 -0
- semantic_code_review/cli/lint.py +27 -0
- semantic_code_review/cli/pr.py +98 -0
- semantic_code_review/cli/review.py +104 -0
- semantic_code_review/cli/runs_cmd.py +17 -0
- semantic_code_review/cli/show.py +19 -0
- semantic_code_review/cli/strip.py +20 -0
- semantic_code_review/config.py +563 -0
- semantic_code_review/config_template.py +154 -0
- semantic_code_review/fetch/__init__.py +61 -0
- semantic_code_review/fetch/anchor.py +244 -0
- semantic_code_review/fetch/github.py +229 -0
- semantic_code_review/fetch/github_comments.py +404 -0
- semantic_code_review/fetch/local.py +443 -0
- semantic_code_review/fetch/run_source.py +70 -0
- semantic_code_review/format/__init__.py +3 -0
- semantic_code_review/format/emit.py +163 -0
- semantic_code_review/format/lint.py +74 -0
- semantic_code_review/format/parse.py +574 -0
- semantic_code_review/format/sidecar.py +19 -0
- semantic_code_review/format/strip.py +20 -0
- semantic_code_review/git_ops.py +252 -0
- semantic_code_review/paths.py +61 -0
- semantic_code_review/review/__init__.py +3 -0
- semantic_code_review/review/comments.py +176 -0
- semantic_code_review/review/github.py +284 -0
- semantic_code_review/review/github_graphql.py +433 -0
- semantic_code_review/review/pr_flow.py +299 -0
- semantic_code_review/review/runner.py +407 -0
- semantic_code_review/review/server.py +1043 -0
- semantic_code_review/structural/__init__.py +41 -0
- semantic_code_review/structural/diff.py +102 -0
- semantic_code_review/structural/parse.py +330 -0
- semantic_code_review/structural/symbols.py +43 -0
- semantic_code_review/viewer/__init__.py +3 -0
- semantic_code_review/viewer/assets/annotations.ts +547 -0
- semantic_code_review/viewer/assets/boot.ts +273 -0
- semantic_code_review/viewer/assets/comment_store.ts +121 -0
- semantic_code_review/viewer/assets/comments.ts +624 -0
- semantic_code_review/viewer/assets/console.ts +426 -0
- semantic_code_review/viewer/assets/console_render.ts +213 -0
- semantic_code_review/viewer/assets/console_selection.ts +102 -0
- semantic_code_review/viewer/assets/data_store.ts +156 -0
- semantic_code_review/viewer/assets/file_rows.ts +49 -0
- semantic_code_review/viewer/assets/folds.ts +560 -0
- semantic_code_review/viewer/assets/index.html +62 -0
- semantic_code_review/viewer/assets/post_modal.ts +383 -0
- semantic_code_review/viewer/assets/progress.ts +138 -0
- semantic_code_review/viewer/assets/render.ts +937 -0
- semantic_code_review/viewer/assets/sidebar.ts +429 -0
- semantic_code_review/viewer/assets/sse.ts +78 -0
- semantic_code_review/viewer/assets/text_highlight.ts +215 -0
- semantic_code_review/viewer/assets/types.d.ts +388 -0
- semantic_code_review/viewer/assets/vendor/LICENSE +29 -0
- semantic_code_review/viewer/assets/vendor/VENDOR.md +55 -0
- semantic_code_review/viewer/assets/vendor/github-dark.min.css +10 -0
- semantic_code_review/viewer/assets/vendor/github.min.css +10 -0
- semantic_code_review/viewer/assets/vendor/highlight.min.js +1244 -0
- semantic_code_review/viewer/assets/vendor/mermaid.LICENSE +21 -0
- semantic_code_review/viewer/assets/vendor/mermaid.min.js +3587 -0
- semantic_code_review/viewer/assets/vendor/refresh.sh +65 -0
- semantic_code_review/viewer/assets/viewer.css +1202 -0
- semantic_code_review/viewer/assets/viewer.js +10588 -0
- semantic_code_review/viewer/build_json.py +546 -0
- semantic_code_review/viewer/hunk_layout.py +471 -0
- semantic_code_review-0.24.2.dist-info/METADATA +348 -0
- semantic_code_review-0.24.2.dist-info/RECORD +100 -0
- semantic_code_review-0.24.2.dist-info/WHEEL +5 -0
- semantic_code_review-0.24.2.dist-info/entry_points.txt +2 -0
- semantic_code_review-0.24.2.dist-info/licenses/LICENSE +21 -0
- semantic_code_review-0.24.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1202 @@
|
|
|
1
|
+
/* Semantic Code Review — viewer styles. */
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
color-scheme: dark light;
|
|
5
|
+
--bg: #0d1117;
|
|
6
|
+
--bg-alt: #161b22;
|
|
7
|
+
--bg-panel: #21262d;
|
|
8
|
+
--fg: #c9d1d9;
|
|
9
|
+
--fg-muted: #8b949e;
|
|
10
|
+
--fg-dim: #6e7681;
|
|
11
|
+
--border: #30363d;
|
|
12
|
+
--accent: #58a6ff;
|
|
13
|
+
--ok: #3fb950;
|
|
14
|
+
--warn: #d29922;
|
|
15
|
+
--err: #f85149;
|
|
16
|
+
--crit: #f85149;
|
|
17
|
+
--major: #d29922;
|
|
18
|
+
--minor: #8b949e;
|
|
19
|
+
--info: #58a6ff;
|
|
20
|
+
--mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@media (prefers-color-scheme: light) {
|
|
24
|
+
:root {
|
|
25
|
+
--bg: #ffffff;
|
|
26
|
+
--bg-alt: #f6f8fa;
|
|
27
|
+
--bg-panel: #eaeef2;
|
|
28
|
+
--fg: #1f2328;
|
|
29
|
+
--fg-muted: #59636e;
|
|
30
|
+
--fg-dim: #848d97;
|
|
31
|
+
--border: #d0d7de;
|
|
32
|
+
--accent: #0969da;
|
|
33
|
+
--ok: #1a7f37;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
* { box-sizing: border-box; }
|
|
38
|
+
html, body { margin: 0; padding: 0; background: var(--bg); color: var(--fg); }
|
|
39
|
+
body {
|
|
40
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
41
|
+
font-size: 14px; line-height: 1.5;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* --- Top bar -------------------------------------------------------------- */
|
|
45
|
+
.pr-bar {
|
|
46
|
+
position: sticky; top: 0; z-index: 50;
|
|
47
|
+
background: var(--bg-alt); border-bottom: 1px solid var(--border);
|
|
48
|
+
padding: 10px 16px; display: flex; gap: 14px; align-items: center;
|
|
49
|
+
flex-wrap: wrap;
|
|
50
|
+
}
|
|
51
|
+
.pr-title { font-weight: 600; margin-right: auto; }
|
|
52
|
+
.pr-title .pr-meta { color: var(--fg-muted); margin-left: 8px; font-weight: 400; font-size: 12px; }
|
|
53
|
+
.fold-slider { display: inline-flex; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
|
|
54
|
+
.fold-slider button {
|
|
55
|
+
background: transparent; color: var(--fg-muted); border: none;
|
|
56
|
+
padding: 4px 10px; font-size: 12px; cursor: pointer;
|
|
57
|
+
}
|
|
58
|
+
.fold-slider button.active { background: var(--accent); color: #fff; }
|
|
59
|
+
.fold-slider button:hover:not(.active) { background: var(--bg-panel); color: var(--fg); }
|
|
60
|
+
.help-btn, .reset-btn {
|
|
61
|
+
background: transparent; border: 1px solid var(--border); color: var(--fg-muted);
|
|
62
|
+
padding: 4px 10px; font-size: 12px; border-radius: 6px; cursor: pointer;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* --- Layout: sidebar + main ---------------------------------------------- */
|
|
66
|
+
.layout {
|
|
67
|
+
display: flex;
|
|
68
|
+
align-items: flex-start;
|
|
69
|
+
}
|
|
70
|
+
.group-sidebar {
|
|
71
|
+
flex: 0 0 220px;
|
|
72
|
+
position: sticky;
|
|
73
|
+
top: 56px; /* just below the sticky .pr-bar */
|
|
74
|
+
align-self: flex-start;
|
|
75
|
+
max-height: calc(100vh - 56px);
|
|
76
|
+
overflow-y: auto;
|
|
77
|
+
padding: 12px 10px 12px 16px;
|
|
78
|
+
border-right: 1px solid var(--border);
|
|
79
|
+
background: var(--bg);
|
|
80
|
+
}
|
|
81
|
+
.group-sidebar.empty { display: none; }
|
|
82
|
+
.group-sidebar #app + & { /* never triggered, but documents: sidebar left of #app */ }
|
|
83
|
+
.layout > main#app { flex: 1 1 auto; min-width: 0; }
|
|
84
|
+
|
|
85
|
+
.group-sidebar-header h3,
|
|
86
|
+
.group-axis-header h3 {
|
|
87
|
+
margin: 0 0 8px 2px;
|
|
88
|
+
font-size: 11px;
|
|
89
|
+
text-transform: uppercase;
|
|
90
|
+
letter-spacing: 0.06em;
|
|
91
|
+
color: var(--fg-muted);
|
|
92
|
+
font-weight: 600;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* Each grouping axis lives in its own stacked section so reviewers
|
|
96
|
+
* can scan every axis at once without flipping a tab. Themes sits
|
|
97
|
+
* above Files; the "Show all" button sits above both axes and
|
|
98
|
+
* clears whichever pill is active. */
|
|
99
|
+
.group-axis { margin-bottom: 14px; }
|
|
100
|
+
.group-axis:last-child { margin-bottom: 0; }
|
|
101
|
+
.group-axis-header h3 { margin-top: 6px; }
|
|
102
|
+
.group-btn {
|
|
103
|
+
display: flex;
|
|
104
|
+
align-items: center;
|
|
105
|
+
gap: 6px;
|
|
106
|
+
width: 100%;
|
|
107
|
+
padding: 6px 8px;
|
|
108
|
+
margin-bottom: 4px;
|
|
109
|
+
background: transparent;
|
|
110
|
+
border: 1px solid transparent;
|
|
111
|
+
border-radius: 4px;
|
|
112
|
+
color: var(--fg);
|
|
113
|
+
font-size: 12px;
|
|
114
|
+
text-align: left;
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
font-family: inherit;
|
|
117
|
+
}
|
|
118
|
+
.group-btn:hover { background: var(--bg-alt); border-color: var(--border); }
|
|
119
|
+
.group-btn.active {
|
|
120
|
+
background: var(--bg-panel);
|
|
121
|
+
border-color: var(--accent);
|
|
122
|
+
color: var(--fg);
|
|
123
|
+
}
|
|
124
|
+
.group-btn-label {
|
|
125
|
+
flex: 1 1 auto;
|
|
126
|
+
overflow: hidden;
|
|
127
|
+
text-overflow: ellipsis;
|
|
128
|
+
white-space: nowrap;
|
|
129
|
+
}
|
|
130
|
+
.group-btn-count {
|
|
131
|
+
flex: 0 0 auto;
|
|
132
|
+
background: var(--bg-alt);
|
|
133
|
+
color: var(--fg-muted);
|
|
134
|
+
padding: 1px 6px;
|
|
135
|
+
border-radius: 10px;
|
|
136
|
+
font-size: 11px;
|
|
137
|
+
min-width: 20px;
|
|
138
|
+
text-align: center;
|
|
139
|
+
}
|
|
140
|
+
.group-btn.active .group-btn-count {
|
|
141
|
+
background: var(--accent);
|
|
142
|
+
color: var(--bg);
|
|
143
|
+
}
|
|
144
|
+
/* Comment-thread tally on Files-axis pills: unresolved/total. Sits
|
|
145
|
+
* next to the hunk-count badge. Muted when every thread is resolved;
|
|
146
|
+
* amber when there's outstanding discussion to chase. */
|
|
147
|
+
.group-btn-comments {
|
|
148
|
+
flex: 0 0 auto;
|
|
149
|
+
background: var(--bg-alt);
|
|
150
|
+
color: var(--fg-muted);
|
|
151
|
+
padding: 1px 6px;
|
|
152
|
+
border-radius: 10px;
|
|
153
|
+
font-size: 11px;
|
|
154
|
+
min-width: 28px;
|
|
155
|
+
text-align: center;
|
|
156
|
+
}
|
|
157
|
+
.group-btn-comments.has-unresolved {
|
|
158
|
+
background: transparent;
|
|
159
|
+
color: var(--warn);
|
|
160
|
+
border: 1px solid var(--warn);
|
|
161
|
+
padding: 0 5px;
|
|
162
|
+
}
|
|
163
|
+
.group-btn.active .group-btn-comments {
|
|
164
|
+
background: var(--bg);
|
|
165
|
+
color: var(--fg);
|
|
166
|
+
}
|
|
167
|
+
.group-btn-all {
|
|
168
|
+
margin-bottom: 10px;
|
|
169
|
+
color: var(--fg-muted);
|
|
170
|
+
font-style: italic;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Symbols axis: nested class ▸ method tree. The toggle sits in a fixed
|
|
174
|
+
* column to the left of each pill so leaves (toggle hidden) stay aligned
|
|
175
|
+
* with their parents; children indent under the row. */
|
|
176
|
+
.group-tree-node { margin-bottom: 0; }
|
|
177
|
+
.group-tree-row { display: flex; align-items: center; gap: 2px; }
|
|
178
|
+
.group-tree-row .group-btn { margin-bottom: 4px; }
|
|
179
|
+
.group-tree-toggle {
|
|
180
|
+
flex: 0 0 auto;
|
|
181
|
+
width: 14px;
|
|
182
|
+
align-self: stretch;
|
|
183
|
+
margin-bottom: 4px;
|
|
184
|
+
padding: 0;
|
|
185
|
+
background: transparent;
|
|
186
|
+
border: none;
|
|
187
|
+
color: var(--fg-muted);
|
|
188
|
+
font-size: 9px;
|
|
189
|
+
line-height: 1;
|
|
190
|
+
cursor: pointer;
|
|
191
|
+
}
|
|
192
|
+
.group-tree-toggle:hover { color: var(--fg); }
|
|
193
|
+
.group-tree-toggle-leaf { cursor: default; visibility: hidden; }
|
|
194
|
+
.group-tree-children { padding-left: 12px; }
|
|
195
|
+
|
|
196
|
+
/* Subtle tell for hunks that no semantic group claimed, visible in
|
|
197
|
+
* the unfiltered view only. Left border gets a dotted treatment so
|
|
198
|
+
* the eye can pick them out at a glance without them feeling alarming. */
|
|
199
|
+
.hunk.ungrouped {
|
|
200
|
+
border-left-style: dotted !important;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* --- PR overview panel ---------------------------------------------------- */
|
|
204
|
+
.pr-panel {
|
|
205
|
+
margin: 12px 16px; background: var(--bg-alt); border: 1px solid var(--border);
|
|
206
|
+
border-radius: 8px; padding: 12px 16px;
|
|
207
|
+
}
|
|
208
|
+
.pr-panel h2 { margin: 0 0 6px 0; font-size: 14px; font-weight: 600; }
|
|
209
|
+
.pr-panel .themes span {
|
|
210
|
+
display: inline-block; background: var(--bg-panel); color: var(--fg-muted);
|
|
211
|
+
padding: 2px 8px; border-radius: 10px; font-size: 11px; margin-right: 4px;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* --- Files ---------------------------------------------------------------- */
|
|
215
|
+
.file {
|
|
216
|
+
margin: 12px 16px; background: var(--bg-alt); border: 1px solid var(--border);
|
|
217
|
+
border-radius: 8px; overflow: hidden;
|
|
218
|
+
}
|
|
219
|
+
.file-header {
|
|
220
|
+
display: flex; align-items: center; gap: 10px; padding: 10px 14px; cursor: pointer;
|
|
221
|
+
background: var(--bg-panel); user-select: none;
|
|
222
|
+
}
|
|
223
|
+
.file.folded .file-header { background: var(--bg-alt); }
|
|
224
|
+
.chevron {
|
|
225
|
+
width: 14px; height: 14px;
|
|
226
|
+
flex-shrink: 0;
|
|
227
|
+
color: var(--fg-muted);
|
|
228
|
+
transform-origin: 50% 50%;
|
|
229
|
+
transform: rotate(0deg);
|
|
230
|
+
transition: transform 140ms cubic-bezier(0.4, 0.0, 0.2, 1), color 140ms ease;
|
|
231
|
+
cursor: pointer;
|
|
232
|
+
vertical-align: middle;
|
|
233
|
+
}
|
|
234
|
+
.chevron.open { transform: rotate(90deg); }
|
|
235
|
+
.file-header:hover .chevron,
|
|
236
|
+
.hunk-header:hover .chevron,
|
|
237
|
+
.segment:hover .chevron { color: var(--fg); }
|
|
238
|
+
.file-path {
|
|
239
|
+
font-family: var(--mono); font-weight: 600;
|
|
240
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
|
|
241
|
+
}
|
|
242
|
+
.file-summary { color: var(--fg-muted); flex: 1; font-size: 13px; min-width: 0; }
|
|
243
|
+
.file-meta { display: flex; gap: 6px; font-size: 12px; color: var(--fg-muted); }
|
|
244
|
+
.file-meta .adds { color: var(--ok); }
|
|
245
|
+
.file-meta .dels { color: var(--err); }
|
|
246
|
+
|
|
247
|
+
.file-body { padding: 12px 14px; }
|
|
248
|
+
.file-overview {
|
|
249
|
+
font-size: 12px; color: var(--fg-muted);
|
|
250
|
+
padding: 6px 8px; background: var(--bg); border-radius: 4px; margin-bottom: 12px;
|
|
251
|
+
}
|
|
252
|
+
.file-overview .label { color: var(--fg-dim); margin-right: 4px; }
|
|
253
|
+
|
|
254
|
+
/* --- Hunks ---------------------------------------------------------------- */
|
|
255
|
+
.hunk {
|
|
256
|
+
margin: 12px 0; border: 1px solid var(--border); border-radius: 6px; overflow: hidden;
|
|
257
|
+
background: var(--bg);
|
|
258
|
+
}
|
|
259
|
+
.hunk.folded { border-left-width: 3px; }
|
|
260
|
+
.hunk-header {
|
|
261
|
+
display: grid;
|
|
262
|
+
grid-template-columns: auto 1fr auto;
|
|
263
|
+
grid-template-areas:
|
|
264
|
+
"chev pos meta"
|
|
265
|
+
". intent intent";
|
|
266
|
+
column-gap: 10px;
|
|
267
|
+
row-gap: 4px;
|
|
268
|
+
padding: 8px 12px;
|
|
269
|
+
cursor: pointer;
|
|
270
|
+
background: var(--bg-panel); user-select: none;
|
|
271
|
+
}
|
|
272
|
+
.hunk-header > .chevron { grid-area: chev; align-self: start; margin-top: 2px; }
|
|
273
|
+
.hunk-pos {
|
|
274
|
+
grid-area: pos;
|
|
275
|
+
font-family: var(--mono); color: var(--fg-dim); font-size: 12px;
|
|
276
|
+
align-self: start;
|
|
277
|
+
}
|
|
278
|
+
.hunk-intent {
|
|
279
|
+
grid-area: intent;
|
|
280
|
+
color: var(--fg); font-size: 13px;
|
|
281
|
+
align-self: start;
|
|
282
|
+
}
|
|
283
|
+
.hunk-intent.empty { color: var(--fg-dim); font-style: italic; }
|
|
284
|
+
.hunk-intent.pending {
|
|
285
|
+
color: var(--fg-dim); font-style: italic;
|
|
286
|
+
animation: scr-pulse 1.4s ease-in-out infinite;
|
|
287
|
+
}
|
|
288
|
+
.hunk-intent.queued { color: var(--fg-dim); font-style: italic; opacity: 0.7; }
|
|
289
|
+
@keyframes scr-pulse {
|
|
290
|
+
0%, 100% { opacity: 0.55; }
|
|
291
|
+
50% { opacity: 1; }
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* Progress strip — shown only while augmentation is streaming. */
|
|
295
|
+
.scr-progress {
|
|
296
|
+
display: flex; flex-direction: column; gap: 4px;
|
|
297
|
+
padding: 6px 14px;
|
|
298
|
+
background: var(--bg-alt); border-bottom: 1px solid var(--border);
|
|
299
|
+
font-size: 12px; color: var(--fg-muted);
|
|
300
|
+
}
|
|
301
|
+
.scr-progress.hidden { display: none; }
|
|
302
|
+
.scr-progress-summary { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
|
|
303
|
+
.scr-progress-overview {
|
|
304
|
+
display: inline-block; padding: 1px 8px; border-radius: 10px;
|
|
305
|
+
border: 1px solid var(--border); background: var(--bg);
|
|
306
|
+
}
|
|
307
|
+
.scr-progress-overview[data-state="pending"] { color: var(--fg-muted); }
|
|
308
|
+
.scr-progress-overview[data-state="running"] {
|
|
309
|
+
color: var(--accent); border-color: var(--accent);
|
|
310
|
+
animation: scr-pulse 1.4s ease-in-out infinite;
|
|
311
|
+
}
|
|
312
|
+
.scr-progress-overview[data-state="ok"] { color: var(--ok); border-color: var(--ok); }
|
|
313
|
+
.scr-progress-overview[data-state="failed"] { color: var(--err); border-color: var(--err); }
|
|
314
|
+
.scr-progress-hunks { color: var(--fg); font-weight: 500; }
|
|
315
|
+
.scr-progress-detail { color: var(--fg-dim); }
|
|
316
|
+
.scr-progress-grid {
|
|
317
|
+
display: flex; flex-wrap: wrap; gap: 3px;
|
|
318
|
+
padding-top: 2px;
|
|
319
|
+
}
|
|
320
|
+
.scr-progress-grid .sq {
|
|
321
|
+
width: 10px; height: 10px; border-radius: 2px;
|
|
322
|
+
background: var(--bg); border: 1px solid var(--border);
|
|
323
|
+
cursor: pointer;
|
|
324
|
+
}
|
|
325
|
+
.scr-progress-grid .sq[data-state="queued"] { background: var(--bg); }
|
|
326
|
+
.scr-progress-grid .sq[data-state="running"] {
|
|
327
|
+
background: var(--accent); border-color: var(--accent);
|
|
328
|
+
animation: scr-pulse 1.4s ease-in-out infinite;
|
|
329
|
+
}
|
|
330
|
+
.scr-progress-grid .sq[data-state="ok"] { background: var(--ok); border-color: var(--ok); }
|
|
331
|
+
.scr-progress-grid .sq[data-state="failed"] { background: var(--err); border-color: var(--err); }
|
|
332
|
+
.scr-progress-grid .sq:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
333
|
+
.hunk-meta { grid-area: meta; display: flex; gap: 6px; align-items: center; flex-shrink: 0; }
|
|
334
|
+
/* Subtle 3-star confidence indicator at the top-right of the hunk
|
|
335
|
+
* header. One filled star = low (the LLM hedged); three = high. The
|
|
336
|
+
* old `c=NN` chip was loud and numeric; stars read at a glance and
|
|
337
|
+
* stay out of the way. */
|
|
338
|
+
.hunk-confidence {
|
|
339
|
+
display: inline-flex;
|
|
340
|
+
gap: 1px;
|
|
341
|
+
font-size: 10px;
|
|
342
|
+
color: var(--fg-muted);
|
|
343
|
+
letter-spacing: 0.5px;
|
|
344
|
+
}
|
|
345
|
+
.hunk-confidence .conf-star.on { color: var(--fg-muted); }
|
|
346
|
+
.hunk-confidence[data-level="1"] .conf-star.on { color: var(--err); }
|
|
347
|
+
.hunk-confidence[data-level="1"] { opacity: 1; }
|
|
348
|
+
|
|
349
|
+
.context-icon {
|
|
350
|
+
font-size: 13px; color: var(--accent); cursor: help;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/* --- Segments ------------------------------------------------------------- */
|
|
354
|
+
.seg-list { padding: 8px 12px; background: var(--bg); }
|
|
355
|
+
.segment {
|
|
356
|
+
margin: 6px 0; padding: 6px 10px; background: var(--bg-alt);
|
|
357
|
+
border-left: 3px solid var(--border); border-radius: 3px;
|
|
358
|
+
display: flex; gap: 10px; align-items: flex-start; cursor: pointer;
|
|
359
|
+
}
|
|
360
|
+
.segment .chevron { margin-top: 1px; }
|
|
361
|
+
.segment-range {
|
|
362
|
+
font-family: var(--mono); color: var(--fg-dim); font-size: 12px; min-width: 90px;
|
|
363
|
+
flex-shrink: 0;
|
|
364
|
+
}
|
|
365
|
+
.segment-intent { flex: 1; font-size: 13px; }
|
|
366
|
+
.segment-intent.empty { color: var(--fg-dim); font-style: italic; }
|
|
367
|
+
|
|
368
|
+
.context-note, .refs {
|
|
369
|
+
padding: 6px 12px; background: var(--bg-alt); border-top: 1px solid var(--border);
|
|
370
|
+
font-size: 12px; color: var(--fg-muted);
|
|
371
|
+
}
|
|
372
|
+
.context-note { border-left: 3px solid var(--accent); padding-left: 16px; }
|
|
373
|
+
.refs a.ref-link {
|
|
374
|
+
display: inline-block; padding: 1px 6px; margin: 1px 4px 1px 0;
|
|
375
|
+
border: 1px solid var(--border); border-radius: 4px;
|
|
376
|
+
font-family: var(--mono); font-size: 11px;
|
|
377
|
+
color: var(--accent); text-decoration: none; background: var(--bg);
|
|
378
|
+
}
|
|
379
|
+
.refs a.ref-link:hover { background: var(--bg-panel); border-color: var(--accent); }
|
|
380
|
+
.refs .ref-reason { color: var(--fg-muted); font-size: 11px; }
|
|
381
|
+
|
|
382
|
+
/* --- Smells --------------------------------------------------------------- */
|
|
383
|
+
.smell {
|
|
384
|
+
display: inline-block; font-size: 11px; padding: 2px 8px;
|
|
385
|
+
border-radius: 10px; color: #fff; margin-left: 4px; cursor: help;
|
|
386
|
+
white-space: nowrap;
|
|
387
|
+
}
|
|
388
|
+
.smell.sev-info { background: #3b82f680; }
|
|
389
|
+
.smell.sev-minor { background: #64748b80; }
|
|
390
|
+
.smell.sev-major { background: #d2992280; }
|
|
391
|
+
.smell.sev-critical{ background: #f8514980; }
|
|
392
|
+
|
|
393
|
+
/* --- Diff grid -----------------------------------------------------------
|
|
394
|
+
* Two halves side by side, each its own horizontal-scroll container and
|
|
395
|
+
* its own 2-column grid (lineno + content). One shared scrollbar per
|
|
396
|
+
* side, zero mid-hunk scrollbars.
|
|
397
|
+
*
|
|
398
|
+
* Vertical alignment across halves: code rows use a uniform line-height
|
|
399
|
+
* and an enforced min-height on every cell, so every code row on both
|
|
400
|
+
* sides is exactly one line tall. Annotation rows break that symmetry
|
|
401
|
+
* (prose wraps); the JS inserts an invisible placeholder row on the
|
|
402
|
+
* opposite side and syncs its height to the real annotation via a
|
|
403
|
+
* ResizeObserver. Subgrid would do this for free but requires explicit
|
|
404
|
+
* outer row tracks, which we don't know at CSS compile time.
|
|
405
|
+
*/
|
|
406
|
+
.diff {
|
|
407
|
+
display: grid;
|
|
408
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
409
|
+
font-family: var(--mono);
|
|
410
|
+
font-size: 12.5px;
|
|
411
|
+
line-height: 1.5;
|
|
412
|
+
border: 1px solid var(--border);
|
|
413
|
+
border-radius: 4px;
|
|
414
|
+
overflow: hidden;
|
|
415
|
+
background: var(--bg);
|
|
416
|
+
}
|
|
417
|
+
.diff .half {
|
|
418
|
+
display: grid;
|
|
419
|
+
/* `max-content` as the track *min* sizes the content column to the widest
|
|
420
|
+
* line, so every row's grid area — and thus its background tint — is as
|
|
421
|
+
* wide as the longest line and stays painted when a long sibling scrolls
|
|
422
|
+
* into view. `1fr` as the max lets the track grow to fill the column when
|
|
423
|
+
* the content is narrower than the viewport. A plain `minmax(0, 1fr)`
|
|
424
|
+
* floors the track at 0, so the widest line overflows its own area while
|
|
425
|
+
* shorter rows' tint stops at the viewport edge. */
|
|
426
|
+
grid-template-columns: 3.5em minmax(max-content, 1fr);
|
|
427
|
+
overflow-x: auto;
|
|
428
|
+
min-width: 0;
|
|
429
|
+
/* overflow-y is implicitly clipped (non-visible) when x is auto.
|
|
430
|
+
* The annotation arrow's upward bleed stays inside the same scroll
|
|
431
|
+
* container as its anchor row, so nothing should actually overflow
|
|
432
|
+
* vertically in practice. */
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/* Each `.row` is a real grid box spanning both of `.half`'s columns,
|
|
436
|
+
* and uses `subgrid` so its two cells land in `.half`'s column tracks
|
|
437
|
+
* (matching widths across every row). Making the row an actual box
|
|
438
|
+
* rather than `display: contents` is required for `position: sticky`
|
|
439
|
+
* on `.cell-lineno` to work reliably in Chromium — sticky grandchildren
|
|
440
|
+
* of a display:contents parent have quirks that make them scroll along
|
|
441
|
+
* with content. */
|
|
442
|
+
.diff .row {
|
|
443
|
+
display: grid;
|
|
444
|
+
grid-template-columns: subgrid;
|
|
445
|
+
grid-column: 1 / -1;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.diff .cell {
|
|
449
|
+
padding: 0 8px;
|
|
450
|
+
white-space: pre;
|
|
451
|
+
min-width: 0;
|
|
452
|
+
/* Force one line-height per cell so empty cells on the opposite side
|
|
453
|
+
* of a pure-insert/delete row still occupy a full row, keeping the
|
|
454
|
+
* two halves vertically aligned line-for-line. */
|
|
455
|
+
min-height: calc(1.5em);
|
|
456
|
+
line-height: 1.5;
|
|
457
|
+
}
|
|
458
|
+
.diff .cell-lineno {
|
|
459
|
+
/* Pin line numbers to the left of their half so they stay visible
|
|
460
|
+
* when the content cells pan horizontally. z-index keeps them above
|
|
461
|
+
* the scrolling code; the background eliminates bleed-through. */
|
|
462
|
+
position: sticky;
|
|
463
|
+
left: 0;
|
|
464
|
+
z-index: 1;
|
|
465
|
+
color: var(--fg-dim);
|
|
466
|
+
text-align: right;
|
|
467
|
+
user-select: none;
|
|
468
|
+
background: var(--bg-alt);
|
|
469
|
+
border-right: 1px solid var(--border);
|
|
470
|
+
padding: 0 6px;
|
|
471
|
+
}
|
|
472
|
+
.diff .cell-lineno.empty { background: transparent; border-right-color: transparent; }
|
|
473
|
+
.diff .cell-content {
|
|
474
|
+
/* Width (and thus the row-kind background tint) comes from the content
|
|
475
|
+
* column track — `minmax(max-content, 1fr)` on `.half` above — so every
|
|
476
|
+
* cell stretches to the widest line. No per-cell `min-width` needed. */
|
|
477
|
+
/* Reserve a left gutter for the fold chevron so it can float outside
|
|
478
|
+
* the text flow without displacing the code. Every line — chevron or
|
|
479
|
+
* not — pays the same gutter so indentation reads consistently.
|
|
480
|
+
* `position: relative` makes the chevron's absolute positioning
|
|
481
|
+
* anchor here rather than on the scroll container. */
|
|
482
|
+
position: relative;
|
|
483
|
+
padding-left: 20px;
|
|
484
|
+
}
|
|
485
|
+
.diff .cell-content code { background: transparent !important; padding: 0 !important; }
|
|
486
|
+
.diff .cell-content.empty { background: transparent; min-width: 0; }
|
|
487
|
+
|
|
488
|
+
/* Per-row kind tints */
|
|
489
|
+
.diff .row-ctx .cell-content { background: var(--bg); }
|
|
490
|
+
.diff .row-ins .cell-content-new { background: rgba(63, 185, 80, 0.16); }
|
|
491
|
+
.diff .row-del .cell-content-old { background: rgba(248, 81, 73, 0.16); }
|
|
492
|
+
.diff .row-pair .cell-content-old { background: rgba(248, 81, 73, 0.16); }
|
|
493
|
+
.diff .row-pair .cell-content-new { background: rgba(63, 185, 80, 0.16); }
|
|
494
|
+
|
|
495
|
+
/* Intra-line sub-diff: the changed block within a replacement, over the
|
|
496
|
+
* light whole-line row tint. A subtle fill plus a coloured underline marks
|
|
497
|
+
* it. Underline (not a full ring) because a block that crosses highlight.js
|
|
498
|
+
* token spans is rendered as several adjacent `.char-chg` spans — a ring
|
|
499
|
+
* would draw a vertical edge at every join, breaking up joined text; an
|
|
500
|
+
* underline stays continuous across them. Applies to any changed row
|
|
501
|
+
* (pair, or a del/ins line inside a multi-line replacement block), so the
|
|
502
|
+
* side determines the colour. */
|
|
503
|
+
.diff .cell-content-old .char-chg {
|
|
504
|
+
background: rgba(248, 81, 73, 0.13);
|
|
505
|
+
border-bottom: 2px solid rgba(248, 81, 73, 0.7);
|
|
506
|
+
}
|
|
507
|
+
.diff .cell-content-new .char-chg {
|
|
508
|
+
background: rgba(63, 185, 80, 0.13);
|
|
509
|
+
border-bottom: 2px solid rgba(63, 185, 80, 0.7);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/* Symbol-focus search highlight: every occurrence of the focused symbol's
|
|
513
|
+
* name, painted search-style. Amber reads on both themes and over the
|
|
514
|
+
* green/red row + char-chg tints; the ring keeps it legible regardless. */
|
|
515
|
+
.diff .cell-content code .symbol-hit {
|
|
516
|
+
background: rgba(255, 196, 0, 0.4);
|
|
517
|
+
box-shadow: 0 0 0 1px rgba(255, 196, 0, 0.7);
|
|
518
|
+
border-radius: 2px;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
/* Dim the empty placeholder on the opposite side of an ins/del */
|
|
522
|
+
.diff .row-ins .cell-content-old.empty,
|
|
523
|
+
.diff .row-del .cell-content-new.empty {
|
|
524
|
+
background: rgba(110, 118, 129, 0.05);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/* Indent-based fold control ---------------------------------------------- */
|
|
528
|
+
/* The chevron floats in the left gutter of .cell-content — it does NOT
|
|
529
|
+
* take text-flow space, so lines with chevrons read at the same indent
|
|
530
|
+
* as their siblings without. Without this, fold-header lines appeared
|
|
531
|
+
* ~2 chars more indented than their bodies and the nesting structure
|
|
532
|
+
* was visually illegible. */
|
|
533
|
+
.diff .fold-chev {
|
|
534
|
+
position: absolute;
|
|
535
|
+
left: 4px;
|
|
536
|
+
top: 2px;
|
|
537
|
+
width: 12px; height: 12px;
|
|
538
|
+
color: var(--fg-dim);
|
|
539
|
+
cursor: pointer;
|
|
540
|
+
outline: none;
|
|
541
|
+
}
|
|
542
|
+
.diff .fold-chev:hover { color: var(--accent); }
|
|
543
|
+
.diff .fold-chev:focus-visible {
|
|
544
|
+
outline: 1px dotted var(--accent);
|
|
545
|
+
outline-offset: 1px;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/* Annotation rows — emacs-flycheck-style boxed callouts linked to a line
|
|
549
|
+
* in the diff via an L-shaped arrow. The arrow emerges from the first
|
|
550
|
+
* non-whitespace column of the anchor line, drops down, and turns right
|
|
551
|
+
* into the box. Two-line height by default; box overflow clipped. */
|
|
552
|
+
.diff .cell-annotation {
|
|
553
|
+
/* An annotation lives in its half's grid and spans the two columns
|
|
554
|
+
* (gutter + content) of that half. Same rule on both sides — the
|
|
555
|
+
* annotation's parent determines which half it lands in. */
|
|
556
|
+
grid-column: 1 / -1;
|
|
557
|
+
display: flex;
|
|
558
|
+
align-items: flex-start;
|
|
559
|
+
gap: 6px;
|
|
560
|
+
padding: 0 8px 6px;
|
|
561
|
+
color: var(--fg-muted);
|
|
562
|
+
background: transparent;
|
|
563
|
+
overflow: visible; /* arrow top can extend above the cell */
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/* Placeholder row on the opposite half, mirroring an annotation's
|
|
567
|
+
* height via JS-applied inline height so the two halves stay aligned
|
|
568
|
+
* even though only one side is populated. */
|
|
569
|
+
.diff .row-placeholder {
|
|
570
|
+
grid-column: 1 / -1;
|
|
571
|
+
visibility: hidden;
|
|
572
|
+
}
|
|
573
|
+
.diff .annot-arrow {
|
|
574
|
+
flex-shrink: 0;
|
|
575
|
+
/* margin-left + margin-top set inline by JS so the arrow's vertical
|
|
576
|
+
* segment lands at the character midpoint of the anchor line and the
|
|
577
|
+
* arrow's top reaches the line's vertical midline. */
|
|
578
|
+
color: var(--accent);
|
|
579
|
+
pointer-events: none;
|
|
580
|
+
}
|
|
581
|
+
.diff .annot-box {
|
|
582
|
+
flex: 0 1 auto;
|
|
583
|
+
padding: 4px 10px;
|
|
584
|
+
background: var(--bg-alt);
|
|
585
|
+
border: 1px solid var(--border);
|
|
586
|
+
border-left: 3px solid var(--accent);
|
|
587
|
+
border-radius: 4px;
|
|
588
|
+
font-size: 13px;
|
|
589
|
+
line-height: 1.4;
|
|
590
|
+
font-style: italic;
|
|
591
|
+
color: var(--fg);
|
|
592
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
593
|
+
font-weight: 400;
|
|
594
|
+
white-space: normal;
|
|
595
|
+
max-width: 64ch;
|
|
596
|
+
/* Fold summaries clamp to ~3 lines; line-notes and comments get to
|
|
597
|
+
* wrap freely. The annotations module applies the actual per-variant
|
|
598
|
+
* sizing inline via `layout.maxHeight`; this CSS default is only a
|
|
599
|
+
* pre-JS fallback. */
|
|
600
|
+
max-height: 5em;
|
|
601
|
+
overflow: hidden;
|
|
602
|
+
}
|
|
603
|
+
.diff .annot-box.missing {
|
|
604
|
+
color: var(--fg-dim);
|
|
605
|
+
border-left-color: var(--fg-dim);
|
|
606
|
+
}
|
|
607
|
+
.diff .annot-box.pending {
|
|
608
|
+
color: var(--fg-muted);
|
|
609
|
+
border-left-color: var(--accent);
|
|
610
|
+
font-style: italic;
|
|
611
|
+
animation: scr-pulse 1.4s ease-in-out infinite;
|
|
612
|
+
}
|
|
613
|
+
.diff .annot-box.failed {
|
|
614
|
+
color: var(--err);
|
|
615
|
+
border-left-color: var(--err);
|
|
616
|
+
cursor: pointer;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/* Reviewer comment variant (amber/warn border so it stands out from the
|
|
620
|
+
* blue LLM fold descriptions). */
|
|
621
|
+
.diff .annot-comment .annot-arrow { color: var(--warn); }
|
|
622
|
+
.diff .annot-comment .annot-box {
|
|
623
|
+
border-left-color: var(--warn);
|
|
624
|
+
font-style: normal;
|
|
625
|
+
max-height: none; /* comments can wrap freely */
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/* Line-note variant: same palette as fold summaries, but the body is
|
|
629
|
+
* often several sentences and should not be clipped. */
|
|
630
|
+
.diff .annot-note .annot-box {
|
|
631
|
+
max-height: none;
|
|
632
|
+
}
|
|
633
|
+
.comment-body { white-space: pre-wrap; font-size: 100%; }
|
|
634
|
+
|
|
635
|
+
/* Header strip on ingested comments: avatar, @author, permalink. Pure
|
|
636
|
+
* local comments don't get a header (no `author`), so this only shows
|
|
637
|
+
* up on PR-sourced rows. */
|
|
638
|
+
.comment-header {
|
|
639
|
+
display: flex;
|
|
640
|
+
align-items: center;
|
|
641
|
+
gap: 6px;
|
|
642
|
+
margin-bottom: 4px;
|
|
643
|
+
font-size: 11px;
|
|
644
|
+
color: var(--fg-muted);
|
|
645
|
+
}
|
|
646
|
+
.comment-avatar {
|
|
647
|
+
width: 16px;
|
|
648
|
+
height: 16px;
|
|
649
|
+
border-radius: 50%;
|
|
650
|
+
display: block;
|
|
651
|
+
}
|
|
652
|
+
.comment-author { font-weight: 600; color: var(--fg); }
|
|
653
|
+
.comment-permalink {
|
|
654
|
+
margin-left: auto;
|
|
655
|
+
text-decoration: none;
|
|
656
|
+
color: var(--fg-muted);
|
|
657
|
+
}
|
|
658
|
+
.comment-permalink:hover { color: var(--accent); }
|
|
659
|
+
|
|
660
|
+
/* Anchor-status chip on the thread root. Surfaces when the original
|
|
661
|
+
* (commit_id, line) had to be propagated to head — "was line 42",
|
|
662
|
+
* "line removed since abc1234", etc. Compact + low-key so it doesn't
|
|
663
|
+
* compete with the author name. */
|
|
664
|
+
.comment-anchor-chip {
|
|
665
|
+
display: inline-flex;
|
|
666
|
+
align-items: center;
|
|
667
|
+
padding: 0 6px;
|
|
668
|
+
font-size: 10px;
|
|
669
|
+
text-transform: lowercase;
|
|
670
|
+
border-radius: 3px;
|
|
671
|
+
border: 1px solid var(--border);
|
|
672
|
+
color: var(--fg-muted);
|
|
673
|
+
background: var(--bg-panel);
|
|
674
|
+
}
|
|
675
|
+
.comment-anchor-chip.chip-shifted { border-color: var(--accent); color: var(--accent); }
|
|
676
|
+
.comment-anchor-chip.chip-orphaned,
|
|
677
|
+
.comment-anchor-chip.chip-file_gone {
|
|
678
|
+
border-color: var(--warn);
|
|
679
|
+
color: var(--warn);
|
|
680
|
+
}
|
|
681
|
+
.comment-anchor-chip.chip-commit_unavailable {
|
|
682
|
+
border-color: var(--err);
|
|
683
|
+
color: var(--err);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/* GitHub-rendered body: drop the pre-wrap white-space rule so block
|
|
687
|
+
* elements (<p>, <pre>, <ul>) lay out naturally. */
|
|
688
|
+
.comment-body.comment-body-html { white-space: normal; }
|
|
689
|
+
.comment-body.comment-body-html p { margin: 0 0 6px; }
|
|
690
|
+
.comment-body.comment-body-html p:last-child { margin-bottom: 0; }
|
|
691
|
+
.comment-body.comment-body-html pre {
|
|
692
|
+
background: var(--bg-panel);
|
|
693
|
+
border: 1px solid var(--border);
|
|
694
|
+
border-radius: 3px;
|
|
695
|
+
padding: 4px 6px;
|
|
696
|
+
overflow-x: auto;
|
|
697
|
+
font-size: 12px;
|
|
698
|
+
}
|
|
699
|
+
.comment-body.comment-body-html code {
|
|
700
|
+
font-family: var(--mono);
|
|
701
|
+
background: var(--bg-panel);
|
|
702
|
+
padding: 0 3px;
|
|
703
|
+
border-radius: 2px;
|
|
704
|
+
}
|
|
705
|
+
.comment-body.comment-body-html pre code {
|
|
706
|
+
background: transparent;
|
|
707
|
+
padding: 0;
|
|
708
|
+
}
|
|
709
|
+
.comment-body.comment-body-html blockquote {
|
|
710
|
+
margin: 0 0 6px;
|
|
711
|
+
padding-left: 8px;
|
|
712
|
+
border-left: 2px solid var(--border);
|
|
713
|
+
color: var(--fg-muted);
|
|
714
|
+
}
|
|
715
|
+
.comment-body.comment-body-html a { color: var(--accent); }
|
|
716
|
+
.comment-body.comment-body-html img { max-width: 100%; }
|
|
717
|
+
|
|
718
|
+
/* Thread block: all comments at one anchor share a single annotation
|
|
719
|
+
* box and arrow. Replies are indented inside that box rather than
|
|
720
|
+
* occupying their own annotation rows. */
|
|
721
|
+
.comment-thread { display: flex; flex-direction: column; gap: 8px; }
|
|
722
|
+
.comment-thread-entry {
|
|
723
|
+
/* Stacked entries get a divider between them so individual comments
|
|
724
|
+
* read as distinct without each needing its own box. */
|
|
725
|
+
border-top: 1px solid var(--border);
|
|
726
|
+
padding-top: 6px;
|
|
727
|
+
}
|
|
728
|
+
.comment-thread-entry:first-child {
|
|
729
|
+
border-top: none;
|
|
730
|
+
padding-top: 0;
|
|
731
|
+
}
|
|
732
|
+
/* Replies aren't visually indented — the thread is a flat stack and
|
|
733
|
+
* the divider between entries is enough. (The .comment-thread-reply
|
|
734
|
+
* class still attaches for any future treatment that wants to target
|
|
735
|
+
* replies specifically.) */
|
|
736
|
+
.comment-thread-actions {
|
|
737
|
+
display: flex;
|
|
738
|
+
justify-content: flex-end;
|
|
739
|
+
gap: 6px;
|
|
740
|
+
margin-top: 4px;
|
|
741
|
+
padding-top: 6px;
|
|
742
|
+
border-top: 1px solid var(--border);
|
|
743
|
+
}
|
|
744
|
+
.comment-btn-reply { padding: 2px 10px; }
|
|
745
|
+
|
|
746
|
+
/* LLM line-note annotation — the LLM observation plus a small
|
|
747
|
+
* "Add as comment" affordance. Clicking the button opens the comment
|
|
748
|
+
* editor pre-filled with the line-note text and the source annotation
|
|
749
|
+
* row is removed once a comment is saved. */
|
|
750
|
+
.line-note-body { display: flex; flex-direction: column; gap: 4px; }
|
|
751
|
+
.line-note-text { white-space: pre-wrap; }
|
|
752
|
+
.line-note-actions {
|
|
753
|
+
display: flex;
|
|
754
|
+
justify-content: flex-end;
|
|
755
|
+
margin-top: 2px;
|
|
756
|
+
}
|
|
757
|
+
.comment-btn-promote {
|
|
758
|
+
padding: 1px 8px;
|
|
759
|
+
font-size: 10px;
|
|
760
|
+
opacity: 0.7;
|
|
761
|
+
}
|
|
762
|
+
.comment-btn-promote:hover { opacity: 1; }
|
|
763
|
+
|
|
764
|
+
/* Smell pills that are click-to-promote get a subtle hover cue —
|
|
765
|
+
* pointer cursor + a hint underline. Non-promotable smells (the
|
|
766
|
+
* file-overview aggregator) stay static. */
|
|
767
|
+
.smell.smell-promotable { cursor: pointer; }
|
|
768
|
+
.smell.smell-promotable:hover {
|
|
769
|
+
outline: 1px solid currentColor;
|
|
770
|
+
outline-offset: -1px;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/* Resolved thread: collapsed header bar at the top of the thread block.
|
|
774
|
+
* The annot-box still surrounds the whole thread; the header is just
|
|
775
|
+
* the first child, with a softer tone so a wall of resolved threads
|
|
776
|
+
* doesn't shout. */
|
|
777
|
+
.diff .annot-comment-resolved .annot-box {
|
|
778
|
+
border-left-color: var(--fg-muted);
|
|
779
|
+
}
|
|
780
|
+
.comment-thread-resolved-header {
|
|
781
|
+
display: flex;
|
|
782
|
+
align-items: center;
|
|
783
|
+
gap: 8px;
|
|
784
|
+
background: transparent;
|
|
785
|
+
border: none;
|
|
786
|
+
color: var(--fg-muted);
|
|
787
|
+
font: inherit;
|
|
788
|
+
font-size: 12px;
|
|
789
|
+
padding: 2px 0;
|
|
790
|
+
cursor: pointer;
|
|
791
|
+
text-align: left;
|
|
792
|
+
}
|
|
793
|
+
.comment-thread-resolved-header:hover {
|
|
794
|
+
color: var(--fg);
|
|
795
|
+
}
|
|
796
|
+
.comment-thread-chev { width: 1em; display: inline-block; text-align: center; }
|
|
797
|
+
.comment-thread-resolved-tag {
|
|
798
|
+
color: var(--ok, #6cf09e);
|
|
799
|
+
font-weight: 600;
|
|
800
|
+
}
|
|
801
|
+
.comment-thread-resolved-meta { color: var(--fg-muted); }
|
|
802
|
+
|
|
803
|
+
/* The comment editor should not inherit the annot-box width/height
|
|
804
|
+
* clamps — it grows freely with the user's text. */
|
|
805
|
+
.diff .annot-editor .annot-box.comment-editor-box {
|
|
806
|
+
max-width: none;
|
|
807
|
+
max-height: none;
|
|
808
|
+
width: 100%;
|
|
809
|
+
}
|
|
810
|
+
.comment-editor-box textarea.comment-editor-input {
|
|
811
|
+
display: block;
|
|
812
|
+
width: 100%;
|
|
813
|
+
min-width: 32ch;
|
|
814
|
+
box-sizing: border-box;
|
|
815
|
+
background: var(--bg);
|
|
816
|
+
color: var(--fg);
|
|
817
|
+
border: 1px solid var(--border);
|
|
818
|
+
border-radius: 3px;
|
|
819
|
+
padding: 4px 6px;
|
|
820
|
+
font-family: var(--mono);
|
|
821
|
+
font-size: 13px;
|
|
822
|
+
/* Start at a single line; JS autosizes height on input. User can still
|
|
823
|
+
* drag the handle horizontally to widen if they want a wider box. */
|
|
824
|
+
resize: horizontal;
|
|
825
|
+
overflow: hidden;
|
|
826
|
+
outline: none;
|
|
827
|
+
}
|
|
828
|
+
.comment-editor-box textarea.comment-editor-input:focus {
|
|
829
|
+
border-color: var(--accent);
|
|
830
|
+
}
|
|
831
|
+
.comment-editor-bar, .comment-actions {
|
|
832
|
+
display: flex;
|
|
833
|
+
gap: 6px;
|
|
834
|
+
margin-top: 6px;
|
|
835
|
+
justify-content: flex-end;
|
|
836
|
+
}
|
|
837
|
+
.comment-btn {
|
|
838
|
+
background: var(--bg-panel);
|
|
839
|
+
color: var(--fg-muted);
|
|
840
|
+
border: 1px solid var(--border);
|
|
841
|
+
border-radius: 3px;
|
|
842
|
+
padding: 2px 8px;
|
|
843
|
+
font-size: 11px;
|
|
844
|
+
cursor: pointer;
|
|
845
|
+
font-family: inherit;
|
|
846
|
+
}
|
|
847
|
+
.comment-btn:hover { color: var(--fg); border-color: var(--accent); }
|
|
848
|
+
.comment-btn-save { background: var(--warn); color: #000; border-color: var(--warn); }
|
|
849
|
+
.comment-btn-save:hover { color: #000; background: #e0ab2c; }
|
|
850
|
+
.comment-btn-del:hover { color: var(--err); border-color: var(--err); }
|
|
851
|
+
|
|
852
|
+
/* Gutter hover affordance — show a faint + in the line-number column on
|
|
853
|
+
* hover so reviewers know the number is clickable. */
|
|
854
|
+
.diff .cell-lineno:not(.empty) { cursor: pointer; position: relative; }
|
|
855
|
+
.diff .cell-lineno:not(.empty):hover {
|
|
856
|
+
background: var(--bg-panel);
|
|
857
|
+
color: var(--accent);
|
|
858
|
+
}
|
|
859
|
+
.diff .cell-lineno:not(.empty):hover::before {
|
|
860
|
+
content: "+";
|
|
861
|
+
position: absolute;
|
|
862
|
+
left: 2px;
|
|
863
|
+
top: 50%;
|
|
864
|
+
transform: translateY(-50%);
|
|
865
|
+
font-size: 10px;
|
|
866
|
+
color: var(--warn);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/* "Done" button in the PR bar (only present when a session endpoint
|
|
870
|
+
* was baked into the HTML). */
|
|
871
|
+
.done-btn {
|
|
872
|
+
background: var(--warn);
|
|
873
|
+
color: #000;
|
|
874
|
+
border: 1px solid var(--warn);
|
|
875
|
+
border-radius: 6px;
|
|
876
|
+
padding: 4px 12px;
|
|
877
|
+
font-size: 12px;
|
|
878
|
+
font-weight: 600;
|
|
879
|
+
cursor: pointer;
|
|
880
|
+
margin-left: 8px;
|
|
881
|
+
}
|
|
882
|
+
.done-btn:hover:not(:disabled) { background: #e0ab2c; }
|
|
883
|
+
.done-btn:disabled { opacity: 0.7; cursor: default; }
|
|
884
|
+
|
|
885
|
+
@media (prefers-color-scheme: light) {
|
|
886
|
+
.diff .row-ins .cell-content-new { background: rgba(26, 127, 55, 0.10); }
|
|
887
|
+
.diff .row-del .cell-content-old { background: rgba(207, 34, 46, 0.10); }
|
|
888
|
+
.diff .row-pair .cell-content-old { background: rgba(207, 34, 46, 0.10); }
|
|
889
|
+
.diff .row-pair .cell-content-new { background: rgba(26, 127, 55, 0.10); }
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/* --- Gap expansion chips (inter-hunk context) ---------------------------- */
|
|
893
|
+
.gap-chip {
|
|
894
|
+
margin: 8px 0;
|
|
895
|
+
padding: 6px 12px;
|
|
896
|
+
background: var(--bg-alt);
|
|
897
|
+
border: 1px dashed var(--border);
|
|
898
|
+
border-radius: 4px;
|
|
899
|
+
color: var(--fg-muted);
|
|
900
|
+
font-size: 12px;
|
|
901
|
+
cursor: pointer;
|
|
902
|
+
user-select: none;
|
|
903
|
+
text-align: center;
|
|
904
|
+
font-family: var(--mono);
|
|
905
|
+
transition: background 0.1s, color 0.1s, border-color 0.1s;
|
|
906
|
+
}
|
|
907
|
+
.gap-chip:hover {
|
|
908
|
+
background: var(--bg-panel);
|
|
909
|
+
color: var(--fg);
|
|
910
|
+
border-color: var(--accent);
|
|
911
|
+
border-style: solid;
|
|
912
|
+
}
|
|
913
|
+
.gap-chip .gap-icon { margin-right: 6px; color: var(--accent); }
|
|
914
|
+
|
|
915
|
+
.gap-expansion { position: relative; margin: 8px 0; }
|
|
916
|
+
.gap-expansion .diff { border-style: dashed; opacity: 0.92; }
|
|
917
|
+
.gap-expansion .gap-collapse {
|
|
918
|
+
position: absolute; top: 4px; right: 4px; z-index: 2;
|
|
919
|
+
background: var(--bg-panel); color: var(--fg-muted);
|
|
920
|
+
border: 1px solid var(--border); border-radius: 4px;
|
|
921
|
+
padding: 2px 8px; font-size: 11px; cursor: pointer;
|
|
922
|
+
font-family: var(--mono);
|
|
923
|
+
}
|
|
924
|
+
.gap-expansion .gap-collapse:hover { color: var(--fg); border-color: var(--accent); }
|
|
925
|
+
|
|
926
|
+
/* --- Status bar ----------------------------------------------------------- */
|
|
927
|
+
/* `position: sticky; bottom: 0` only pins relative to a scrolling
|
|
928
|
+
* ancestor, not the viewport — for a body-level footer it just sits
|
|
929
|
+
* at the end of the document flow. Use `fixed` to anchor it to the
|
|
930
|
+
* viewport bottom regardless of scroll position, and reserve space
|
|
931
|
+
* with body padding so the last content row isn't hidden behind it. */
|
|
932
|
+
.status-bar {
|
|
933
|
+
position: fixed; bottom: 0; left: 0; right: 0;
|
|
934
|
+
background: var(--bg-alt); border-top: 1px solid var(--border);
|
|
935
|
+
padding: 6px 16px; color: var(--fg-muted); font-size: 12px;
|
|
936
|
+
display: flex; gap: 16px; align-items: flex-end;
|
|
937
|
+
z-index: 40;
|
|
938
|
+
}
|
|
939
|
+
body { padding-bottom: 30px; }
|
|
940
|
+
|
|
941
|
+
/* The status counts sit on the right; when the console mounts, the
|
|
942
|
+
* prompt input takes the flexible left and pushes them over. */
|
|
943
|
+
.status-counts { margin-left: auto; white-space: nowrap; }
|
|
944
|
+
|
|
945
|
+
/* --- Review console ------------------------------------------------------- */
|
|
946
|
+
.console-input {
|
|
947
|
+
flex: 1 1 auto; min-width: 0;
|
|
948
|
+
resize: none; overflow-y: auto;
|
|
949
|
+
font: inherit; line-height: 1.4;
|
|
950
|
+
color: var(--fg); background: var(--bg-panel);
|
|
951
|
+
border: 1px solid var(--border); border-radius: 6px;
|
|
952
|
+
padding: 3px 8px;
|
|
953
|
+
}
|
|
954
|
+
.console-input::placeholder { color: var(--fg-muted); }
|
|
955
|
+
.console-input:focus { outline: none; border-color: var(--fg-muted); }
|
|
956
|
+
.console-input.busy { opacity: 0.7; }
|
|
957
|
+
|
|
958
|
+
/* Selection chip: the reviewer's pinned selection (Slice 4). Sits at the
|
|
959
|
+
* far left of the footer; visible only while a selection is pinned. */
|
|
960
|
+
.console-chip {
|
|
961
|
+
flex: 0 1 auto; min-width: 0;
|
|
962
|
+
display: inline-flex; align-items: center; gap: 4px;
|
|
963
|
+
max-width: 40%;
|
|
964
|
+
font-size: 12px; line-height: 1.2;
|
|
965
|
+
color: var(--fg); background: var(--bg-panel);
|
|
966
|
+
border: 1px solid var(--border); border-radius: 6px;
|
|
967
|
+
padding: 2px 4px 2px 8px;
|
|
968
|
+
}
|
|
969
|
+
.console-chip.hidden { display: none; }
|
|
970
|
+
.console-chip-label {
|
|
971
|
+
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
972
|
+
}
|
|
973
|
+
.console-chip-clear {
|
|
974
|
+
flex: 0 0 auto;
|
|
975
|
+
font: inherit; line-height: 1; color: var(--fg-muted);
|
|
976
|
+
background: none; border: none; cursor: pointer; padding: 0 2px;
|
|
977
|
+
}
|
|
978
|
+
.console-chip-clear:hover { color: var(--fg); }
|
|
979
|
+
|
|
980
|
+
/* Stop affordance: visible only while a turn streams. */
|
|
981
|
+
.console-stop {
|
|
982
|
+
flex: 0 0 auto; align-self: center;
|
|
983
|
+
font: inherit; font-size: 12px; line-height: 1.2;
|
|
984
|
+
color: var(--fg); background: var(--bg-panel);
|
|
985
|
+
border: 1px solid var(--border); border-radius: 6px;
|
|
986
|
+
padding: 3px 10px; cursor: pointer;
|
|
987
|
+
}
|
|
988
|
+
.console-stop:hover { border-color: var(--fg-muted); }
|
|
989
|
+
.console-stop:disabled { opacity: 0.5; cursor: default; }
|
|
990
|
+
.console-stop.hidden { display: none; }
|
|
991
|
+
|
|
992
|
+
/* Transcript drawer: anchored above the footer, grows upward to a cap
|
|
993
|
+
* then scrolls. `bottom` clears the fixed footer's reserved strip. */
|
|
994
|
+
.console-drawer {
|
|
995
|
+
position: fixed; left: 0; right: 0; bottom: 30px;
|
|
996
|
+
max-height: 58vh; overflow-y: auto;
|
|
997
|
+
background: var(--bg-alt); border-top: 1px solid var(--border);
|
|
998
|
+
z-index: 39;
|
|
999
|
+
padding: 12px 16px;
|
|
1000
|
+
}
|
|
1001
|
+
.console-drawer.hidden { display: none; }
|
|
1002
|
+
.console-transcript {
|
|
1003
|
+
display: flex; flex-direction: column; gap: 16px;
|
|
1004
|
+
/* Left-aligned (not centred) so the transcript lines up under the
|
|
1005
|
+
* prompt input, the way a terminal/Claude-Code log reads. The
|
|
1006
|
+
* max-width just caps line length for readability. */
|
|
1007
|
+
max-width: 980px; margin: 0;
|
|
1008
|
+
}
|
|
1009
|
+
/* Both question and answer read top-to-bottom, left-aligned and
|
|
1010
|
+
* full-width — no chat bubbles. The question is a quieter left-bordered
|
|
1011
|
+
* echo of the prompt; the answer below it is the prominent prose. */
|
|
1012
|
+
.console-q {
|
|
1013
|
+
max-width: 100%;
|
|
1014
|
+
white-space: pre-wrap; word-break: break-word;
|
|
1015
|
+
font-size: 13px; color: var(--fg-muted);
|
|
1016
|
+
border-left: 2px solid var(--border); padding-left: 10px;
|
|
1017
|
+
}
|
|
1018
|
+
.console-a {
|
|
1019
|
+
max-width: 100%;
|
|
1020
|
+
white-space: pre-wrap; word-break: break-word;
|
|
1021
|
+
font-size: 13px; color: var(--fg); line-height: 1.5;
|
|
1022
|
+
/* Pull the answer up under its question so a turn reads as one
|
|
1023
|
+
* block, with the larger transcript gap separating turns. */
|
|
1024
|
+
margin-top: -8px;
|
|
1025
|
+
}
|
|
1026
|
+
.console-a.console-pending .console-text { color: var(--fg-muted); }
|
|
1027
|
+
.console-a.console-error .console-text { color: var(--danger, #e5534b); }
|
|
1028
|
+
/* A cancelled turn keeps its partial text but dims it. */
|
|
1029
|
+
.console-a.console-cancelled .console-text { opacity: 0.65; }
|
|
1030
|
+
|
|
1031
|
+
/* Tool-activity lines stream above the answer text while the agent
|
|
1032
|
+
* reaches for tools; muted + monospaced to read as machine activity. */
|
|
1033
|
+
.console-activity {
|
|
1034
|
+
display: flex; flex-direction: column; gap: 2px;
|
|
1035
|
+
margin-bottom: 4px;
|
|
1036
|
+
}
|
|
1037
|
+
.console-activity:empty { display: none; }
|
|
1038
|
+
.console-tool {
|
|
1039
|
+
font-family: var(--mono, ui-monospace, monospace);
|
|
1040
|
+
font-size: 11px; color: var(--fg-muted);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
/* --- Rendered markdown answers (Slice 3) ---------------------------------
|
|
1044
|
+
* `.console-text` holds markdown-it output (sanitised); it manages its
|
|
1045
|
+
* own block whitespace, so override the pre-wrap inherited from the
|
|
1046
|
+
* plain-text era. Code fences keep their own `pre` whitespace. */
|
|
1047
|
+
.console-text { white-space: normal; }
|
|
1048
|
+
.console-text > :first-child { margin-top: 0; }
|
|
1049
|
+
.console-text > :last-child { margin-bottom: 0; }
|
|
1050
|
+
.console-text p { margin: 0 0 8px; }
|
|
1051
|
+
.console-text h1, .console-text h2, .console-text h3,
|
|
1052
|
+
.console-text h4, .console-text h5, .console-text h6 {
|
|
1053
|
+
margin: 14px 0 6px; line-height: 1.3; font-weight: 600;
|
|
1054
|
+
}
|
|
1055
|
+
.console-text h1 { font-size: 18px; }
|
|
1056
|
+
.console-text h2 { font-size: 16px; }
|
|
1057
|
+
.console-text h3 { font-size: 14px; }
|
|
1058
|
+
.console-text h4, .console-text h5, .console-text h6 { font-size: 13px; }
|
|
1059
|
+
.console-text ul, .console-text ol { margin: 0 0 8px; padding-left: 22px; }
|
|
1060
|
+
.console-text li { margin: 2px 0; }
|
|
1061
|
+
.console-text a { color: var(--accent); text-decoration: none; }
|
|
1062
|
+
.console-text a:hover { text-decoration: underline; }
|
|
1063
|
+
.console-text code {
|
|
1064
|
+
font-family: var(--mono, ui-monospace, monospace);
|
|
1065
|
+
font-size: 0.92em;
|
|
1066
|
+
background: var(--bg-panel); border-radius: 4px;
|
|
1067
|
+
padding: 0.1em 0.35em;
|
|
1068
|
+
}
|
|
1069
|
+
.console-text pre {
|
|
1070
|
+
background: var(--bg-alt); border: 1px solid var(--border);
|
|
1071
|
+
border-radius: 6px; padding: 10px 12px; margin: 0 0 8px;
|
|
1072
|
+
overflow-x: auto;
|
|
1073
|
+
}
|
|
1074
|
+
.console-text pre code {
|
|
1075
|
+
background: none; padding: 0; font-size: 12px; line-height: 1.45;
|
|
1076
|
+
}
|
|
1077
|
+
.console-text blockquote {
|
|
1078
|
+
margin: 0 0 8px; padding: 0 12px;
|
|
1079
|
+
border-left: 3px solid var(--border); color: var(--fg-muted);
|
|
1080
|
+
}
|
|
1081
|
+
.console-text table {
|
|
1082
|
+
border-collapse: collapse; margin: 0 0 8px; font-size: 12px;
|
|
1083
|
+
}
|
|
1084
|
+
.console-text th, .console-text td {
|
|
1085
|
+
border: 1px solid var(--border); padding: 4px 8px;
|
|
1086
|
+
}
|
|
1087
|
+
.console-text hr {
|
|
1088
|
+
border: none; border-top: 1px solid var(--border); margin: 12px 0;
|
|
1089
|
+
}
|
|
1090
|
+
/* A rendered mermaid diagram (its source `pre` is swapped out for this).
|
|
1091
|
+
* Centred, with the SVG scaled to fit the transcript width. */
|
|
1092
|
+
.console-mermaid {
|
|
1093
|
+
margin: 0 0 8px; text-align: center;
|
|
1094
|
+
}
|
|
1095
|
+
.console-mermaid svg { max-width: 100%; height: auto; }
|
|
1096
|
+
|
|
1097
|
+
/* --- Help overlay --------------------------------------------------------- */
|
|
1098
|
+
.help-overlay {
|
|
1099
|
+
position: fixed; inset: 0; background: rgba(0,0,0,0.6); z-index: 100;
|
|
1100
|
+
display: flex; align-items: center; justify-content: center;
|
|
1101
|
+
}
|
|
1102
|
+
.help-overlay.hidden { display: none; }
|
|
1103
|
+
.help-card {
|
|
1104
|
+
background: var(--bg-alt); border: 1px solid var(--border); border-radius: 8px;
|
|
1105
|
+
padding: 24px; max-width: 480px; color: var(--fg);
|
|
1106
|
+
}
|
|
1107
|
+
.help-card h3 { margin-top: 0; }
|
|
1108
|
+
.help-card kbd {
|
|
1109
|
+
display: inline-block; padding: 1px 6px; background: var(--bg-panel);
|
|
1110
|
+
border: 1px solid var(--border); border-radius: 4px; font-family: var(--mono);
|
|
1111
|
+
font-size: 11px; margin: 0 2px;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
/* --- Post-confirm modal (scr pr only) ------------------------------------ */
|
|
1115
|
+
/* Shown when the reviewer clicks Done in posting mode. Each row is a
|
|
1116
|
+
* comment that would be posted; per-row checkbox + delete give the
|
|
1117
|
+
* reviewer one last pass before the post fires. Reuses the help
|
|
1118
|
+
* overlay's backdrop pattern (fixed full-screen, centred card). */
|
|
1119
|
+
.post-modal {
|
|
1120
|
+
position: fixed; inset: 0; background: rgba(0,0,0,0.6); z-index: 110;
|
|
1121
|
+
display: flex; align-items: center; justify-content: center;
|
|
1122
|
+
}
|
|
1123
|
+
.post-modal.hidden { display: none; }
|
|
1124
|
+
.post-modal-card {
|
|
1125
|
+
background: var(--bg-alt); border: 1px solid var(--border); border-radius: 8px;
|
|
1126
|
+
padding: 20px 24px; width: min(720px, 90vw); max-height: 80vh;
|
|
1127
|
+
display: flex; flex-direction: column; color: var(--fg);
|
|
1128
|
+
}
|
|
1129
|
+
.post-modal-header { margin: 0 0 6px; font-size: 16px; }
|
|
1130
|
+
.post-modal-meta {
|
|
1131
|
+
margin: 0 0 14px; font-size: 12px; color: var(--fg-muted);
|
|
1132
|
+
font-family: var(--mono);
|
|
1133
|
+
}
|
|
1134
|
+
.post-modal-list {
|
|
1135
|
+
list-style: none; padding: 0; margin: 0; overflow-y: auto;
|
|
1136
|
+
border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
|
|
1137
|
+
max-height: 50vh;
|
|
1138
|
+
}
|
|
1139
|
+
.post-modal-empty {
|
|
1140
|
+
padding: 20px; text-align: center; color: var(--fg-muted); font-style: italic;
|
|
1141
|
+
}
|
|
1142
|
+
.post-row {
|
|
1143
|
+
display: flex; align-items: flex-start; gap: 8px;
|
|
1144
|
+
padding: 10px 0; border-bottom: 1px solid var(--border);
|
|
1145
|
+
}
|
|
1146
|
+
.post-row:last-child { border-bottom: 0; }
|
|
1147
|
+
.post-row-label {
|
|
1148
|
+
flex: 1; display: grid;
|
|
1149
|
+
grid-template-columns: auto 1fr auto; gap: 4px 10px;
|
|
1150
|
+
align-items: baseline; cursor: pointer;
|
|
1151
|
+
}
|
|
1152
|
+
.post-row-check { grid-row: 1 / span 2; margin-top: 2px; }
|
|
1153
|
+
.post-row-loc {
|
|
1154
|
+
font-family: var(--mono); font-size: 12px; color: var(--fg-muted);
|
|
1155
|
+
grid-column: 2; grid-row: 1;
|
|
1156
|
+
}
|
|
1157
|
+
.post-row-kind {
|
|
1158
|
+
font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em;
|
|
1159
|
+
color: var(--accent); grid-column: 3; grid-row: 1;
|
|
1160
|
+
}
|
|
1161
|
+
.post-row-body {
|
|
1162
|
+
font-size: 13px; white-space: pre-wrap; word-break: break-word;
|
|
1163
|
+
grid-column: 2 / span 2; grid-row: 2;
|
|
1164
|
+
}
|
|
1165
|
+
.post-row-delete {
|
|
1166
|
+
background: transparent; border: 1px solid var(--border); border-radius: 4px;
|
|
1167
|
+
width: 22px; height: 22px; padding: 0; line-height: 1;
|
|
1168
|
+
color: var(--fg-muted); cursor: pointer; font-size: 16px;
|
|
1169
|
+
}
|
|
1170
|
+
.post-row-delete:hover:not(:disabled) {
|
|
1171
|
+
color: var(--warn); border-color: var(--warn);
|
|
1172
|
+
}
|
|
1173
|
+
.post-row-delete:disabled { opacity: 0.5; cursor: default; }
|
|
1174
|
+
.post-modal-status {
|
|
1175
|
+
margin: 10px 0 0; font-size: 12px; color: var(--warn); min-height: 1.2em;
|
|
1176
|
+
}
|
|
1177
|
+
.post-modal-footer {
|
|
1178
|
+
display: flex; gap: 10px; justify-content: flex-end; margin-top: 14px;
|
|
1179
|
+
}
|
|
1180
|
+
.post-modal-footer button {
|
|
1181
|
+
background: var(--bg-panel); color: var(--fg);
|
|
1182
|
+
border: 1px solid var(--border); border-radius: 6px;
|
|
1183
|
+
padding: 6px 14px; font-size: 13px; cursor: pointer;
|
|
1184
|
+
}
|
|
1185
|
+
.post-modal-footer .post-confirm {
|
|
1186
|
+
background: var(--accent); border-color: var(--accent); color: #000; font-weight: 600;
|
|
1187
|
+
}
|
|
1188
|
+
.post-modal-footer .post-confirm:hover:not(:disabled) { filter: brightness(1.1); }
|
|
1189
|
+
.post-modal-footer button:disabled { opacity: 0.6; cursor: default; }
|
|
1190
|
+
.post-modal-success {
|
|
1191
|
+
display: flex; gap: 12px; align-items: flex-start;
|
|
1192
|
+
padding: 16px 0; margin-bottom: 4px;
|
|
1193
|
+
}
|
|
1194
|
+
.post-modal-tick {
|
|
1195
|
+
font-size: 24px; color: var(--ok, #2ea043); line-height: 1;
|
|
1196
|
+
}
|
|
1197
|
+
.post-modal-success-text { font-size: 14px; }
|
|
1198
|
+
.post-modal-link {
|
|
1199
|
+
display: inline-block; margin-top: 4px;
|
|
1200
|
+
color: var(--accent); text-decoration: none;
|
|
1201
|
+
}
|
|
1202
|
+
.post-modal-link:hover { text-decoration: underline; }
|