reviewzy 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/package.json +2 -2
- package/src/dashboard/mount.ts +20 -3
- package/src/dashboard/static/components.css +1196 -451
- package/src/dashboard/static/cursor-rules.js +30 -34
- package/src/dashboard/static/cursor.css +118 -89
- package/src/dashboard/static/cursor.js +1636 -245
- package/src/dashboard/static/dashboard.css +275 -179
- package/src/dashboard/static/dashboard.js +330 -185
- package/src/dashboard/static/tokens.css +240 -54
- package/src/dashboard/static/ui.js +666 -171
- package/src/dashboard/views.tsx +1138 -841
- package/src/dashboard/word-diff.ts +18 -25
- package/src/db/migrations.ts +5 -6
- package/src/db/queries.ts +37 -0
- package/src/mcp/file-entries.ts +1 -1
- package/src/mcp/list-entries.ts +11 -2
|
@@ -1,244 +1,340 @@
|
|
|
1
1
|
/* ============================================================
|
|
2
2
|
reviewzy — dashboard page styles
|
|
3
|
+
The page layer: the sidebar shell, the list's own rhythm, and the
|
|
4
|
+
htmx behavior hooks. Every component surface is a shipped class;
|
|
5
|
+
nothing here repaints one.
|
|
3
6
|
============================================================ */
|
|
4
7
|
|
|
5
|
-
/* The
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
/* ---- The shell ----
|
|
9
|
+
The sidebar is a grid track, so the page column follows the animated
|
|
10
|
+
column width. A fixed 252px track would leave a 186px gap around the
|
|
11
|
+
66px rail. */
|
|
12
|
+
.layout { display: grid; grid-template-columns: auto minmax(0, 1fr); min-height: 100vh; }
|
|
8
13
|
|
|
9
|
-
/*
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
/* The page column takes the main track's width: the design drops the cap beside a sidebar,
|
|
15
|
+
because a capped column leaves a dead strip beside the content once the track outgrows
|
|
16
|
+
--page-w (268px on each side at 1920). The track is pinned rather than auto-placed: under
|
|
17
|
+
768 the sidebar is the fixed drawer and leaves the grid, so auto-placement would land the
|
|
18
|
+
page in the `auto` track, which sizes to the content's own intrinsic width — zero inside a
|
|
19
|
+
`container-type: inline-size` wrapper, collapsing the editor page to its padding. min-width
|
|
20
|
+
keeps the grid item from growing to its widest table's min-content width, which would widen
|
|
21
|
+
the document at 390. */
|
|
22
|
+
.layout > .page { grid-column: 2; min-width: 0; }
|
|
12
23
|
|
|
13
|
-
/*
|
|
14
|
-
.page
|
|
15
|
-
.page-lede { font-size: var(--t-3); color: var(--text-3); max-width: 540px; }
|
|
24
|
+
/* One spacing ladder: the page opens at --space-8 and closes at --space-16. */
|
|
25
|
+
.page { padding-block: var(--space-8) var(--space-16); }
|
|
16
26
|
|
|
17
|
-
/*
|
|
18
|
-
|
|
19
|
-
@media (
|
|
27
|
+
/* The rail's footer holds one control: the theme toggle keeps the slot, and
|
|
28
|
+
sign-out returns with the expanded column. */
|
|
29
|
+
@media (min-width: 769px) {
|
|
30
|
+
.sidebar.rail .sidebar-user form { display: none; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* ---- List page rhythm: header, banner, tabs, then the queue section ---- */
|
|
34
|
+
.page-header { margin-bottom: var(--space-8); }
|
|
35
|
+
|
|
36
|
+
/* The banner is the component's card form here, not the bleed form: it sits on
|
|
37
|
+
the page column under the header. */
|
|
38
|
+
#page-banner > .banner { margin-bottom: var(--space-4); }
|
|
39
|
+
|
|
40
|
+
/* The status strip is a row of submit buttons inside the filters form; the count
|
|
41
|
+
line rides the toolbar's end edge and the filter-bar's own block margin closes
|
|
42
|
+
the pair. Five tabs with counts never fit a 390px column, so the strip scrolls
|
|
43
|
+
in its own box the way the navbar's link band does; the row keeps its hairline
|
|
44
|
+
across the scrolled content. */
|
|
45
|
+
.status-scroll { margin-bottom: var(--space-4); overflow-x: auto; }
|
|
46
|
+
.status-scroll .tabs,
|
|
47
|
+
.style-guide-projects .tabs { width: max-content; min-width: 100%; }
|
|
48
|
+
.list-meta {
|
|
49
|
+
margin-left: auto;
|
|
50
|
+
display: flex;
|
|
51
|
+
align-items: center;
|
|
52
|
+
gap: var(--space-3);
|
|
53
|
+
}
|
|
20
54
|
|
|
21
55
|
/* ---- Entries region ---- */
|
|
22
56
|
/* The loading strip is hidden until htmx marks the request in flight (hx-indicator adds the
|
|
23
57
|
.htmx-request class); it sits beside the swap target, so a swap never removes it. */
|
|
24
58
|
#entries-loading { display: none; align-items: center; gap: var(--space-2); font-size: var(--t-2); color: var(--text-3); padding: var(--space-2) 0; }
|
|
25
59
|
#entries-loading.htmx-request { display: flex; }
|
|
60
|
+
/* A control mid-request ignores further activations: htmx marks the requesting element with
|
|
61
|
+
.htmx-request while the request is in flight, and pointer-events drops the duplicate click a
|
|
62
|
+
double-click would otherwise fire. */
|
|
63
|
+
.htmx-request { pointer-events: none; }
|
|
26
64
|
|
|
27
|
-
/* ----
|
|
28
|
-
|
|
29
|
-
.
|
|
65
|
+
/* ---- Bulk approve ----
|
|
66
|
+
The pinned batch action is the shipped section-sticky bar: transparent in flow so the sky
|
|
67
|
+
shows through, toned and hairlined only while it is stuck. Only its spacing is the page's. */
|
|
68
|
+
.batch-bar { margin-bottom: var(--space-4); }
|
|
30
69
|
|
|
31
|
-
/* ---- Project
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.project-
|
|
35
|
-
.
|
|
36
|
-
|
|
37
|
-
|
|
70
|
+
/* ---- Project sections: one heading and one table each ----
|
|
71
|
+
Batches stay a data concept behind the row selection and the one approve
|
|
72
|
+
action; nothing on the page discloses or groups by them. */
|
|
73
|
+
.project-section { margin-bottom: var(--space-8); }
|
|
74
|
+
.project-head {
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
align-items: flex-start;
|
|
78
|
+
gap: var(--space-1);
|
|
79
|
+
margin-bottom: var(--space-3);
|
|
80
|
+
}
|
|
81
|
+
.project-name {
|
|
82
|
+
font: 500 var(--t-5)/1.2 var(--font-sans);
|
|
83
|
+
letter-spacing: -0.01em;
|
|
84
|
+
color: var(--text);
|
|
85
|
+
margin: 0;
|
|
86
|
+
min-width: 0;
|
|
87
|
+
overflow-wrap: anywhere;
|
|
88
|
+
}
|
|
89
|
+
/* The project's count line: the drafts and entries of the rows shown in this group. */
|
|
90
|
+
.project-meta {
|
|
91
|
+
margin-top: 0;
|
|
92
|
+
font-size: var(--t-2);
|
|
93
|
+
max-width: none;
|
|
94
|
+
}
|
|
38
95
|
|
|
39
96
|
/* ---- Entry table cells ----
|
|
40
|
-
The table lays out
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
97
|
+
The table lays out with a fixed colgroup so the entry column takes the remainder and a long
|
|
98
|
+
title or file path truncates instead of widening the table; select, status, filed and actions
|
|
99
|
+
are the fixed columns. The table never compresses below 560px: the fixed columns sum to 422
|
|
100
|
+
and the entry column needs room to read, so a narrower wrap scrolls instead of collapsing it.
|
|
101
|
+
Truncation itself is the shipped .truncate utility. */
|
|
102
|
+
#entries-list table { table-layout: fixed; min-width: 560px; }
|
|
103
|
+
.col-select { width: 44px; }
|
|
104
|
+
.col-status { width: 108px; }
|
|
105
|
+
.col-filed { width: 150px; }
|
|
46
106
|
.col-actions { width: 120px; }
|
|
47
107
|
.entry-head {
|
|
48
108
|
display: flex;
|
|
49
|
-
|
|
50
|
-
gap: var(--space-
|
|
51
|
-
}
|
|
52
|
-
.entry-title {
|
|
53
|
-
flex: 1 1 auto;
|
|
109
|
+
flex-direction: column;
|
|
110
|
+
gap: var(--space-1);
|
|
54
111
|
min-width: 0;
|
|
112
|
+
}
|
|
113
|
+
/* The title is the row's link into the editor; the hover underline is the affordance. */
|
|
114
|
+
.entry-link {
|
|
115
|
+
display: block;
|
|
55
116
|
font: 400 var(--t-3)/1.4 var(--font-sans);
|
|
56
117
|
color: var(--text);
|
|
57
|
-
|
|
58
|
-
overflow: hidden;
|
|
59
|
-
text-overflow: ellipsis;
|
|
118
|
+
text-decoration: none;
|
|
60
119
|
}
|
|
61
|
-
.entry-
|
|
62
|
-
|
|
120
|
+
.entry-link:hover { text-decoration: underline; }
|
|
121
|
+
.entry-file {
|
|
122
|
+
display: block;
|
|
63
123
|
font-size: var(--t-2);
|
|
64
124
|
color: var(--text-4);
|
|
65
|
-
white-space: nowrap;
|
|
66
125
|
}
|
|
67
|
-
.cell-
|
|
68
|
-
font-family: var(--font-mono);
|
|
126
|
+
.cell-filed {
|
|
69
127
|
font-size: var(--t-2);
|
|
70
128
|
color: var(--text-4);
|
|
71
129
|
white-space: nowrap;
|
|
72
|
-
overflow: hidden;
|
|
73
|
-
text-overflow: ellipsis;
|
|
74
130
|
}
|
|
75
|
-
|
|
76
|
-
/* ---- Row actions and batch approve ----
|
|
77
|
-
A control mid-request ignores further activations: htmx marks the requesting element with
|
|
78
|
-
.htmx-request while the request is in flight, and pointer-events drops the duplicate click a
|
|
79
|
-
double-click would otherwise fire. */
|
|
80
|
-
.htmx-request { pointer-events: none; }
|
|
81
131
|
.cell-actions { white-space: nowrap; display: inline-flex; align-items: center; gap: var(--space-1); }
|
|
82
132
|
.table-wrap tbody td.cell-actions { padding-left: var(--space-1); padding-right: var(--space-1); }
|
|
83
|
-
|
|
133
|
+
/* The row's three icon actions sit at the small control step, so the 120px column holds them
|
|
134
|
+
without a 4px spill; the coarse floor is restated at the override's specificity, or a touch
|
|
135
|
+
row's tap targets would shrink with them. */
|
|
136
|
+
.cell-actions .btn-icon { width: var(--ctl-sm); min-width: var(--ctl-sm); height: var(--ctl-sm); min-height: var(--ctl-sm); }
|
|
137
|
+
@media (pointer: coarse) {
|
|
138
|
+
.cell-actions .btn-icon { width: var(--ctl-lg); min-width: var(--ctl-lg); height: var(--ctl-lg); min-height: var(--ctl-lg); }
|
|
139
|
+
}
|
|
140
|
+
/* The semantic icon actions: the shipped .btn-icon paints the label colour, these give the
|
|
141
|
+
approve and reject glyphs their status colours in every state a disabled slot sits in. */
|
|
142
|
+
.action-approve, .action-approve:hover { color: var(--success); }
|
|
143
|
+
.action-reject, .action-reject:hover { color: var(--danger); }
|
|
144
|
+
|
|
145
|
+
/* Under 900px the filed stamp (metadata, not a decision) leaves the row so the rest of the
|
|
146
|
+
table keeps its columns inside the wrap instead of scrolling wider. The column's width is
|
|
147
|
+
zeroed and its cells are dropped (Chromium ignores `visibility: collapse` on a col), and the
|
|
148
|
+
table's floor steps down with the column it lost. The wrap stays the named scroller for what
|
|
149
|
+
remains. */
|
|
150
|
+
@media (max-width: 900px) {
|
|
151
|
+
#entries-list table { min-width: 440px; }
|
|
152
|
+
#entries-list col.col-filed { width: 0; }
|
|
153
|
+
#entries-list .cell-filed { display: none; }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* ---- Notices ----
|
|
157
|
+
The refusal list rides inside the page banner's body, in the banner text's voice. */
|
|
158
|
+
.notice-list {
|
|
159
|
+
margin: var(--space-2) 0 0;
|
|
160
|
+
padding-left: var(--space-5);
|
|
84
161
|
display: flex;
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
The chevron points down expanded and turns when collapsed; the state is applied by dashboard.js
|
|
98
|
-
from localStorage, so the server ships everything open and the viewer's choice re-applies. */
|
|
99
|
-
.collapse-toggle { align-self: center; }
|
|
100
|
-
.collapse-toggle svg { transition: transform var(--dur-tint) var(--ease); }
|
|
101
|
-
.collapse-toggle[aria-expanded="false"] svg { transform: rotate(-90deg); }
|
|
102
|
-
.project-header .collapse-toggle, .batch-header .collapse-toggle { margin-left: auto; }
|
|
103
|
-
.notice-list { margin: 0; padding-left: var(--space-5); display: flex; flex-direction: column; gap: var(--space-1); }
|
|
104
|
-
|
|
105
|
-
/* ---- Diff view: the anchored snippet vs the entry's text, mono on the well, anchor distinct ---- */
|
|
106
|
-
.diff-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); margin-bottom: var(--space-8); }
|
|
107
|
-
@media (max-width: 900px) { .diff-grid { grid-template-columns: 1fr; } }
|
|
162
|
+
flex-direction: column;
|
|
163
|
+
gap: var(--space-1);
|
|
164
|
+
font-size: var(--t-3);
|
|
165
|
+
color: var(--text-2);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* ---- Empty-state actions: this page's filter-miss state holds two ways onward ---- */
|
|
169
|
+
.empty-actions { display: flex; gap: var(--space-2); justify-content: center; }
|
|
170
|
+
|
|
171
|
+
/* ---- The change: the anchored snippet as code on the well ----
|
|
172
|
+
The shipped pre owns the surface; the diff wraps instead of scrolling, which is the one
|
|
173
|
+
deviation this view adds. The anchor's annotation bar is the shipped .annot-mark. */
|
|
108
174
|
.diff-code {
|
|
109
|
-
|
|
110
|
-
background: var(--well);
|
|
111
|
-
border: 1px solid var(--line);
|
|
112
|
-
border-radius: var(--r-1);
|
|
113
|
-
padding: var(--space-4);
|
|
175
|
+
margin: 0;
|
|
114
176
|
white-space: pre-wrap;
|
|
115
177
|
word-break: break-word;
|
|
116
|
-
color: var(--text-2);
|
|
117
|
-
margin: 0;
|
|
118
178
|
}
|
|
119
179
|
.diff-line { display: block; }
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
padding-
|
|
180
|
+
/* A change with both removed and added words renders two rows: the file text with its removals,
|
|
181
|
+
then the proposal with its additions. The rule between them keeps each sentence whole. */
|
|
182
|
+
.diff-rows .diff-proposed {
|
|
183
|
+
margin-top: var(--space-2);
|
|
184
|
+
padding-top: var(--space-2);
|
|
185
|
+
border-top: 1px solid var(--line-soft);
|
|
125
186
|
}
|
|
126
187
|
|
|
127
|
-
/* ----
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
.
|
|
132
|
-
.
|
|
188
|
+
/* ---- Entry editor: one full-track workspace ----
|
|
189
|
+
The workspace, its header and its support card take the page column's whole track: a cap beside
|
|
190
|
+
the sidebar centres a column away from the nav it belongs to. The page is the query container,
|
|
191
|
+
so the title step, the support card's columns and the pair's order follow the track width, not
|
|
192
|
+
the viewport. Container queries sit inside the browser floor the readme states, so the title step
|
|
193
|
+
and the pair's order keep the query rather than an intrinsic fallback. Each card sizes to its own
|
|
194
|
+
content and its pane scrolls past the cap; one support card spans the workspace below. */
|
|
195
|
+
.editor-page { container-type: inline-size; }
|
|
196
|
+
/* The entry title runs one line on the 1920 and 1440 tracks; a narrow page column steps it down
|
|
197
|
+
so a long machine title does not wrap mid-hyphen. */
|
|
198
|
+
.editor-title { font-size: var(--t-7); }
|
|
199
|
+
@container (max-width: 800px) {
|
|
200
|
+
.editor-title { font-size: var(--t-6); }
|
|
201
|
+
}
|
|
202
|
+
.editor-grid {
|
|
203
|
+
display: flex;
|
|
204
|
+
flex-wrap: wrap;
|
|
205
|
+
gap: var(--space-4);
|
|
206
|
+
/* The pair's cards share one height: the taller card sets the row and the shorter one keeps its
|
|
207
|
+
own content at the top. The inner boxes stay content-sized; the slack is card surface. */
|
|
208
|
+
align-items: stretch;
|
|
209
|
+
}
|
|
210
|
+
/* Each card asks for its own readable measure and the pair wraps when the track cannot hold two,
|
|
211
|
+
so the collapse follows the track's own width instead of a breakpoint: it needs no container
|
|
212
|
+
query and holds in an engine without them. A terminal entry has no change card, so the settled
|
|
213
|
+
pane asks for the whole row, and one support card spans it below the pair. */
|
|
214
|
+
.editor-grid > * { flex: 1 1 320px; min-width: 0; }
|
|
215
|
+
#editor-region.editor-region-wide,
|
|
216
|
+
.editor-support { flex: 1 0 100%; }
|
|
217
|
+
.editor-pane { max-height: 60vh; overflow: auto; }
|
|
218
|
+
/* The proposal box sizes to its text through dashboard.js's autosizer, so a one-line entry gets a
|
|
219
|
+
one-line box and a long one grows to the viewport cap instead of scrolling from the start. */
|
|
220
|
+
/* One support card, two balanced columns: the sections keep their DOM order down each column and
|
|
221
|
+
each takes its own height, so a short Details never stretches to a taller Notes. The section
|
|
222
|
+
labels head their sections at the next type step; the nested "More" keeps the shipped size. The
|
|
223
|
+
320px floor is the same measure the pair asks for, so an engine without container queries still
|
|
224
|
+
meets one column on a phone instead of two slivers. */
|
|
225
|
+
.editor-support-card .card-content {
|
|
226
|
+
padding: var(--space-2);
|
|
227
|
+
columns: 320px 2;
|
|
228
|
+
column-gap: var(--space-5);
|
|
229
|
+
}
|
|
230
|
+
.editor-support-card .support-section {
|
|
231
|
+
break-inside: avoid;
|
|
232
|
+
margin-bottom: var(--space-3);
|
|
233
|
+
}
|
|
234
|
+
.editor-support-card .support-section > summary { font-size: var(--t-4); }
|
|
235
|
+
@container (max-width: 900px) {
|
|
236
|
+
.editor-support-card .card-content { columns: 1; }
|
|
237
|
+
}
|
|
238
|
+
/* The shipped empty state's block padding is card-scale; one line of it inside a section leaves
|
|
239
|
+
a tall blank band. It also centres its one line across the full card, which reads as a hole;
|
|
240
|
+
inside a section it lines up with the section's rows instead. */
|
|
241
|
+
.support-section .empty-state {
|
|
242
|
+
align-items: flex-start;
|
|
243
|
+
text-align: left;
|
|
244
|
+
padding-block: var(--space-2);
|
|
245
|
+
}
|
|
246
|
+
#editor-notice:empty { display: none; }
|
|
247
|
+
#editor-notice { margin-bottom: var(--space-4); }
|
|
248
|
+
/* The one part of the narrow layout the pair's own measure cannot carry: once it has wrapped, the
|
|
249
|
+
proposal leads, so the reader meets the words before the file's own text. An engine without
|
|
250
|
+
container queries keeps the file's text first. */
|
|
251
|
+
@container (max-width: 640px) {
|
|
252
|
+
#editor-region { order: -1; }
|
|
253
|
+
}
|
|
133
254
|
|
|
134
|
-
/* ----
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
.
|
|
139
|
-
@media (max-width: 900px) {
|
|
140
|
-
.editor-grid { grid-template-columns: 1fr; }
|
|
141
|
-
.editor-rail { position: static; }
|
|
142
|
-
}
|
|
143
|
-
/* The editor's title card header: the title falls back to the anchor text, which can be one long
|
|
144
|
-
unbroken string, so it wraps inside the card instead of overflowing or shoving the status pill. */
|
|
145
|
-
.editor-title { min-width: 0; overflow-wrap: anywhere; }
|
|
146
|
-
/* The entry text ships verbatim, often carrying markdown backticks and significant whitespace;
|
|
147
|
-
mono gives every glyph, space included, one full cell so both stay countable while editing. */
|
|
255
|
+
/* ---- Textareas: no manual resize ----
|
|
256
|
+
The shipped .input pins a control height and the shipped textarea rule adds a resize
|
|
257
|
+
handle; both fight the page's sizing. The editor box holds a viewport-clamped height and the
|
|
258
|
+
notes box a fixed one; the style-guide fields autosize to their content. */
|
|
259
|
+
main.page textarea.input { height: auto; resize: none; }
|
|
148
260
|
#editor-text { font-family: var(--font-mono); }
|
|
261
|
+
#editor-notes { height: 120px; min-height: 120px; max-height: 120px; overflow-y: auto; }
|
|
262
|
+
#style-guide-markdown { min-height: 200px; }
|
|
263
|
+
#style-guide-glossary { min-height: 140px; }
|
|
149
264
|
|
|
150
|
-
/*
|
|
151
|
-
|
|
152
|
-
a single click away while reading. The strip sits inside the page's inline padding, clear of content. */
|
|
153
|
-
.editor-nav-btn {
|
|
154
|
-
position: fixed;
|
|
155
|
-
top: var(--navbar-h);
|
|
156
|
-
bottom: 0;
|
|
157
|
-
width: 44px;
|
|
265
|
+
/* The live metrics row under the proposal's textarea: length, placeholders, tone. */
|
|
266
|
+
.constraint-row {
|
|
158
267
|
display: flex;
|
|
159
268
|
align-items: center;
|
|
160
|
-
|
|
269
|
+
flex-wrap: wrap;
|
|
270
|
+
gap: var(--space-2) var(--space-4);
|
|
271
|
+
margin-top: var(--space-3);
|
|
272
|
+
font-size: var(--t-2);
|
|
161
273
|
color: var(--text-3);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
transition: background var(--dur-tint) var(--ease), color var(--dur-tint) var(--ease);
|
|
165
|
-
}
|
|
166
|
-
.editor-nav-prev { left: 0; }
|
|
167
|
-
.editor-nav-next { right: 0; }
|
|
168
|
-
.editor-nav-btn:hover { background: var(--surface-2); color: var(--text); }
|
|
169
|
-
.editor-nav-btn.is-disabled { color: var(--text-4); opacity: 0.4; pointer-events: none; }
|
|
170
|
-
.editor-nav-btn svg { width: 18px; height: 18px; }
|
|
171
|
-
@media (max-width: 900px) { .editor-nav { display: none; } }
|
|
172
|
-
#editor-region > .callout { margin-bottom: var(--space-4); }
|
|
173
|
-
#editor-region > .btn { margin-top: var(--space-4); }
|
|
174
|
-
.editor-actions { margin-top: var(--space-4); display: flex; align-items: center; gap: var(--space-2); }
|
|
175
|
-
/* The in-flight indicator for an approve or reject from the editor: htmx's own rules fade it in. */
|
|
176
|
-
.editor-actions .htmx-indicator { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--t-2); color: var(--text-3); }
|
|
177
|
-
|
|
178
|
-
/* The notes card: the agent's row reuses the constraint row shape; the human's textarea and its
|
|
179
|
-
save button sit below it. */
|
|
180
|
-
.notes-field { margin-top: var(--space-2); }
|
|
181
|
-
.notes-actions { margin-top: var(--space-2); display: flex; justify-content: flex-end; }
|
|
182
|
-
|
|
183
|
-
/* Constraint panel: live count and placeholder checklist; the server owns the refusal. */
|
|
184
|
-
.constraint-row { display: flex; gap: var(--space-3); padding: 6px 0; }
|
|
185
|
-
.constraint-row + .constraint-row { border-top: 1px solid var(--line); }
|
|
186
|
-
.constraint-label { flex-shrink: 0; font: 500 11px/1.4 var(--font-sans); letter-spacing: var(--caps); text-transform: uppercase; color: var(--text-3); width: 96px; padding-top: 2px; }
|
|
187
|
-
.constraint-value { min-width: 0; color: var(--text-2); font-size: var(--t-2); }
|
|
188
|
-
.char-count { color: var(--text-2); font-size: var(--t-2); }
|
|
189
|
-
.char-count.over { color: var(--danger); }
|
|
190
|
-
.placeholder-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
|
|
274
|
+
}
|
|
275
|
+
.placeholder-list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-3); }
|
|
191
276
|
.placeholder-item { display: flex; align-items: center; gap: var(--space-2); }
|
|
192
277
|
.placeholder-state { font-size: var(--t-2); color: var(--text-3); min-width: 52px; }
|
|
193
278
|
.placeholder-state.is-present { color: var(--success); }
|
|
279
|
+
.char-count.over { color: var(--danger); }
|
|
194
280
|
|
|
195
|
-
|
|
196
|
-
|
|
281
|
+
.editor-actions { margin-top: var(--space-4); display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
|
|
282
|
+
/* The in-flight indicators for a reject from the editor and a style-guide save: htmx
|
|
283
|
+
fades the span in via .htmx-request, and display:none until then keeps the laid-out span from
|
|
284
|
+
widening the actions row past the viewport on a narrow screen. */
|
|
285
|
+
.editor-actions .htmx-indicator { display: none; }
|
|
286
|
+
.editor-actions .htmx-indicator.htmx-request { display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--t-2); color: var(--text-3); }
|
|
287
|
+
|
|
288
|
+
/* ---- Header prev/next ----
|
|
289
|
+
The neighbour pair lives in the page header's toolbar as secondary buttons; a boundary
|
|
290
|
+
keeps the slot with a disabled button so the pair never shifts. The meta row under the
|
|
291
|
+
title truncates the file path instead of letting a long one widen the page. */
|
|
292
|
+
.page-meta { min-width: 0; }
|
|
293
|
+
.page-meta-item { min-width: 0; }
|
|
294
|
+
.page-file { min-width: 0; max-width: 100%; }
|
|
295
|
+
|
|
296
|
+
/* ---- Notes ----
|
|
297
|
+
The notes card: the agent's row is the info-table readout; the human's textarea and its
|
|
298
|
+
save button sit below it. */
|
|
299
|
+
.notes-field { margin-top: var(--space-2); }
|
|
300
|
+
.notes-actions { margin-top: var(--space-2); display: flex; justify-content: flex-end; }
|
|
301
|
+
|
|
302
|
+
/* The details card's secondary block: the provenance rows (hash, filer, stale note) collapsed
|
|
303
|
+
behind a disclosure, so the agent-supplied context rows lead the card. */
|
|
197
304
|
.context-secondary { margin-top: var(--space-3); }
|
|
198
|
-
.context-secondary summary { cursor: pointer; font-size: var(--t-2); color: var(--text-3); }
|
|
199
305
|
.context-secondary .info-table { margin-top: var(--space-2); }
|
|
200
306
|
|
|
201
307
|
/* Repo row link: the brand icon beside the shortened owner/repo path, linking out to the host. */
|
|
202
|
-
.repo-link { display: inline-flex; align-items: center; gap:
|
|
203
|
-
.repo-link:hover { color: var(--accent-text); }
|
|
308
|
+
.repo-link { display: inline-flex; align-items: center; gap: var(--space-1); color: var(--text); text-decoration: none; }
|
|
309
|
+
.repo-link:hover { color: var(--accent-text); text-decoration: none; }
|
|
204
310
|
.repo-icon { width: 14px; height: 14px; flex-shrink: 0; fill: currentColor; opacity: var(--icon-dim); }
|
|
205
|
-
.placeholder-token { font: 400 var(--t-2)/1 var(--font-mono); color: var(--text); background: var(--well); border: 1px solid var(--line); border-radius: var(--r-1); padding: 2px 6px; }
|
|
206
311
|
|
|
207
|
-
/* Entry images:
|
|
208
|
-
|
|
209
|
-
.image-list {
|
|
312
|
+
/* Entry images: the dropzone is the upload control; the arrived images sit under it as the
|
|
313
|
+
shipped dropzone list, and the submit ends the block as its action row. */
|
|
314
|
+
.image-list { gap: var(--space-3); }
|
|
210
315
|
.entry-image { display: block; max-width: 100%; height: auto; border: 1px solid var(--line); border-radius: var(--r-1); }
|
|
211
316
|
.image-skip { margin: 0; font-size: var(--t-2); color: var(--text-3); }
|
|
212
317
|
.image-remove { margin-top: var(--space-1); }
|
|
213
|
-
.image-upload { display: flex;
|
|
214
|
-
.image-upload .input { flex: 1; min-width: 0; }
|
|
215
|
-
|
|
216
|
-
/* Revision history: newest first; the text clamps like the list's entry text. */
|
|
217
|
-
.history-empty { font-size: var(--t-2); color: var(--text-4); padding: var(--space-2) 0; }
|
|
218
|
-
.revision-list { list-style: none; margin: 0; padding: 0; }
|
|
219
|
-
.revision { padding: var(--space-3) 0; }
|
|
220
|
-
.revision + .revision { border-top: 1px solid var(--line); }
|
|
221
|
-
.revision-head { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-2); }
|
|
222
|
-
.revision-time { font-size: var(--t-2); color: var(--text-4); }
|
|
223
|
-
.revision-head .btn { margin-left: auto; }
|
|
224
|
-
.revision-text {
|
|
225
|
-
display: -webkit-box;
|
|
226
|
-
-webkit-line-clamp: 2;
|
|
227
|
-
-webkit-box-orient: vertical;
|
|
228
|
-
overflow: hidden;
|
|
229
|
-
font-size: var(--t-2);
|
|
230
|
-
color: var(--text-2);
|
|
231
|
-
}
|
|
318
|
+
.image-upload { display: flex; justify-content: flex-end; }
|
|
232
319
|
|
|
233
|
-
/* ---- Login
|
|
234
|
-
.login-
|
|
235
|
-
.login-main .page-header, .login-card { width: 100%; max-width: 400px; }
|
|
236
|
-
.login-card .card-content { display: flex; flex-direction: column; gap: var(--space-4); }
|
|
320
|
+
/* ---- Login: the page-state card's content column ---- */
|
|
321
|
+
.login-content { display: flex; flex-direction: column; gap: var(--space-4); }
|
|
237
322
|
.login-actions { display: flex; justify-content: flex-end; }
|
|
238
323
|
|
|
239
|
-
/* ---- Style guide editor: the section
|
|
240
|
-
|
|
241
|
-
|
|
324
|
+
/* ---- Style guide editor: the section tabs, then the one card ---- */
|
|
325
|
+
/* The project strip can hold every project's tab, so it scrolls in its own box like the status
|
|
326
|
+
strip does; the document never widens for it. */
|
|
327
|
+
.style-guide-projects { margin-bottom: var(--space-4); overflow-x: auto; }
|
|
328
|
+
/* The document default underlines every link on hover; the tab costume is a button's, so the
|
|
329
|
+
reset rides the anchor here. */
|
|
330
|
+
.style-guide-projects a.tab,
|
|
331
|
+
.style-guide-projects a.tab:hover { text-decoration: none; }
|
|
332
|
+
/* The settled notices sit on the same measure as the card below them; a full-width callout
|
|
333
|
+
over a half-width form read as two unrelated blocks. */
|
|
334
|
+
#style-guide-region > .callout { max-width: var(--page-narrow); margin-bottom: var(--space-4); }
|
|
242
335
|
#style-guide-region > .btn { margin-top: var(--space-4); }
|
|
243
|
-
|
|
336
|
+
/* The fields stack in one column on the narrow measure, so the guide's long inputs never
|
|
337
|
+
stretch the whole page column. */
|
|
338
|
+
.style-guide-card { max-width: var(--page-narrow); }
|
|
339
|
+
.style-guide-fields { display: flex; flex-direction: column; gap: var(--space-4); }
|
|
244
340
|
.style-guide-card .editor-actions { margin-top: 0; }
|