@ingcreators/annot-core 0.1.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/CHANGELOG.md +7 -0
- package/LICENSE +201 -0
- package/README.md +56 -0
- package/dist/auto-capture-options.d.ts +78 -0
- package/dist/editor/arrow-markers.d.ts +142 -0
- package/dist/editor/bake-translate.d.ts +192 -0
- package/dist/editor/font-registry.d.ts +58 -0
- package/dist/editor/gradient-utils.d.ts +23 -0
- package/dist/editor/history-core.d.ts +48 -0
- package/dist/editor/icons/brand-icons.d.ts +53 -0
- package/dist/editor/icons/material-symbols.d.ts +105 -0
- package/dist/editor/icons/registry.d.ts +179 -0
- package/dist/editor/icons/render.d.ts +22 -0
- package/dist/editor/icons/sanitize.d.ts +60 -0
- package/dist/editor/index.d.ts +13 -0
- package/dist/editor/path-utils.d.ts +32 -0
- package/dist/editor/property-schema.d.ts +263 -0
- package/dist/editor/rich-text-mapper.d.ts +8 -0
- package/dist/editor/selection-geometry.d.ts +66 -0
- package/dist/editor/shape-utils.d.ts +27 -0
- package/dist/editor/svg-format.d.ts +68 -0
- package/dist/editor/svg-id-utils.d.ts +37 -0
- package/dist/editor/svg-to-annotation-shapes.d.ts +34 -0
- package/dist/editor/text-utils.d.ts +212 -0
- package/dist/editor/tool-lifecycle.d.ts +56 -0
- package/dist/editor/tool-options.d.ts +126 -0
- package/dist/editor/tool-panel-adapter.d.ts +105 -0
- package/dist/editor/tool-preset-serde.d.ts +43 -0
- package/dist/editor/tool-registry.d.ts +320 -0
- package/dist/editor/tool-style-reader.d.ts +36 -0
- package/dist/editor/tool-style-writer.d.ts +33 -0
- package/dist/editor/toolbar-icons.d.ts +84 -0
- package/dist/editor/transform-utils.d.ts +127 -0
- package/dist/editor/viewport-math.d.ts +69 -0
- package/dist/encode/index.d.ts +4 -0
- package/dist/encode/options.d.ts +79 -0
- package/dist/encode/png8.d.ts +10 -0
- package/dist/headless.d.ts +29 -0
- package/dist/icons/index.d.ts +15 -0
- package/dist/icons/types.d.ts +108 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3164 -0
- package/dist/storage/errors.d.ts +59 -0
- package/dist/storage/index.d.ts +6 -0
- package/dist/storage/metadata-cache.d.ts +231 -0
- package/dist/storage/path.d.ts +49 -0
- package/dist/storage/thumbnail-cache.d.ts +110 -0
- package/dist/storage/thumbnail.d.ts +31 -0
- package/dist/storage/types.d.ts +677 -0
- package/dist/utils/assert.d.ts +31 -0
- package/dist/utils/constants.d.ts +10 -0
- package/dist/utils/dash-utils.d.ts +6 -0
- package/dist/utils/desktop-bridge.d.ts +349 -0
- package/dist/utils/filename.d.ts +48 -0
- package/dist/utils/id.d.ts +21 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/types.d.ts +20 -0
- package/dist/xmp/xmp-browser.d.ts +39 -0
- package/dist/zip/zip-builder.d.ts +8 -0
- package/dist/zip/zip-bytes.d.ts +22 -0
- package/package.json +58 -0
- package/styles/editor.css +1912 -0
- package/styles/fonts.css +46 -0
- package/styles/property-panel.css +779 -0
- package/styles/toolbar.css +673 -0
|
@@ -0,0 +1,1912 @@
|
|
|
1
|
+
/* ingcreators brand theme — design tokens.
|
|
2
|
+
*
|
|
3
|
+
* The two `:root` blocks below define the dark (default) and light
|
|
4
|
+
* palettes. Tokens are grouped into five semantic sections in the
|
|
5
|
+
* SAME ORDER in both blocks so a side-by-side diff between palettes
|
|
6
|
+
* stays readable. Adding a new token? Pick the section that matches
|
|
7
|
+
* its USE, not its colour family. Adding a new section? Reflect it
|
|
8
|
+
* in `docs/design-system.md` and in `ThemeTokenName` in
|
|
9
|
+
* `packages/editor/src/theme-overrides.ts` (the symmetry test in
|
|
10
|
+
* `theme-overrides.test.ts` fails the build if you forget).
|
|
11
|
+
*
|
|
12
|
+
* Contrast targets (WCAG 2.1 AA):
|
|
13
|
+
* text-primary on bg-primary: ≥ 7:1 (AAA)
|
|
14
|
+
* text-secondary on bg-primary: ≥ 4.5:1 (AA)
|
|
15
|
+
* text-muted on bg-primary / bg-secondary: ≥ 4.5:1 (AA)
|
|
16
|
+
* accent (interactive) on bg-primary: ≥ 4.5:1
|
|
17
|
+
* borders: ≥ 3:1 for non-text UI
|
|
18
|
+
* focus ring: ≥ 3:1 and ≥ 2px thick
|
|
19
|
+
*
|
|
20
|
+
* See `docs/design-system.md` for the full token reference and
|
|
21
|
+
* `docs/plans/design-system-foundations.md` for the roadmap (the
|
|
22
|
+
* `--annot-*` Phase 2 namespace migration is what these grouping
|
|
23
|
+
* comments anticipate).
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/* Dark theme (default) ----------------------------------------- */
|
|
27
|
+
:root {
|
|
28
|
+
/* === Surface — page / panel backdrops + chromeless decoration === */
|
|
29
|
+
--annot-bg-primary: #0f1730;
|
|
30
|
+
--annot-bg-secondary: #0b1020;
|
|
31
|
+
--annot-bg-panel: #121933;
|
|
32
|
+
--annot-bg-panel-deep: #0f1730;
|
|
33
|
+
--annot-border-color: rgba(255, 255, 255, 0.22); /* bumped from 0.08 for ≥3:1 on interactive */
|
|
34
|
+
--annot-border-subtle: rgba(255, 255, 255, 0.08); /* keeps old value for decorative dividers */
|
|
35
|
+
--annot-shadow: 0 20px 50px rgba(0, 0, 0, 0.28);
|
|
36
|
+
|
|
37
|
+
/* === Content — foreground text + reading primitives === */
|
|
38
|
+
--annot-text-primary: #eef2ff; /* ~18:1 on bg-primary (AAA) */
|
|
39
|
+
--annot-text-secondary: #b7c0e0; /* ~10:1 on bg-primary (AAA) */
|
|
40
|
+
--annot-text-muted: #9097b8; /* ~6.2:1 on bg-primary (AA) — was 5.3:1 */
|
|
41
|
+
--annot-preview-line: #b7c0e0;
|
|
42
|
+
|
|
43
|
+
/* === Accent — brand colour, active highlight, focus indicator === */
|
|
44
|
+
--annot-accent: #7c9cff; /* ~7.5:1 on bg-primary (AAA) */
|
|
45
|
+
--annot-accent-2: #7ef0c5;
|
|
46
|
+
--annot-accent-bg: rgba(124, 156, 255, 0.18);
|
|
47
|
+
--annot-accent-hover: rgba(124, 156, 255, 0.26);
|
|
48
|
+
--annot-active-bg: rgba(124, 156, 255, 0.18);
|
|
49
|
+
--annot-active-border: #7c9cff;
|
|
50
|
+
--annot-chip-bg: rgba(124, 156, 255, 0.14);
|
|
51
|
+
--annot-focus-ring: #7ef0c5; /* accent-2 green contrasts against blue accent for clarity */
|
|
52
|
+
|
|
53
|
+
/* === Interaction — pointer + form states === */
|
|
54
|
+
--annot-hover-bg: rgba(255, 255, 255, 0.08);
|
|
55
|
+
--annot-hover-border: rgba(255, 255, 255, 0.22);
|
|
56
|
+
--annot-choice-bg: transparent;
|
|
57
|
+
--annot-choice-hover: rgba(124, 156, 255, 0.1);
|
|
58
|
+
--annot-choice-active: rgba(124, 156, 255, 0.18);
|
|
59
|
+
--annot-input-bg: rgba(255, 255, 255, 0.06);
|
|
60
|
+
--annot-input-border: rgba(255, 255, 255, 0.22); /* ≥3:1 for form borders */
|
|
61
|
+
|
|
62
|
+
/* === Canvas — editor backdrop + transparency-grid checkerboard === */
|
|
63
|
+
--annot-canvas-bg: #0b1020;
|
|
64
|
+
--annot-canvas-check: #121933;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Light theme -------------------------------------------------- */
|
|
68
|
+
:root.light {
|
|
69
|
+
/* === Surface === */
|
|
70
|
+
--annot-bg-primary: #f5f7ff;
|
|
71
|
+
--annot-bg-secondary: #ffffff;
|
|
72
|
+
--annot-bg-panel: #ffffff;
|
|
73
|
+
--annot-bg-panel-deep: #fafbff;
|
|
74
|
+
--annot-border-color: #bfc6de; /* ~3:1 against bg-primary for visible UI boundaries */
|
|
75
|
+
--annot-border-subtle: #d8ddef; /* fainter dividers */
|
|
76
|
+
--annot-shadow: rgba(0, 0, 0, 0.08);
|
|
77
|
+
|
|
78
|
+
/* === Content === */
|
|
79
|
+
--annot-text-primary: #0b1020; /* ~16:1 on bg-primary (AAA) */
|
|
80
|
+
--annot-text-secondary: #3a4568; /* ~8:1 on bg-primary (AAA) — was #4a5580 (~6:1) */
|
|
81
|
+
--annot-text-muted: #616984; /* ~4.6:1 on bg-primary (AA) — was #8890b0 (~3.2:1 FAIL) */
|
|
82
|
+
--annot-preview-line: #2a3050;
|
|
83
|
+
|
|
84
|
+
/* === Accent === */
|
|
85
|
+
--annot-accent: #3456c9; /* ~6.3:1 on bg-primary (AA+) — was #5b7ee5 (~4.2:1 marginal) */
|
|
86
|
+
--annot-accent-2: #188a5c; /* ~5.2:1 for secondary accent */
|
|
87
|
+
--annot-accent-bg: #edf1ff;
|
|
88
|
+
--annot-accent-hover: #dce4ff;
|
|
89
|
+
--annot-active-bg: #dce4ff;
|
|
90
|
+
--annot-active-border: #3456c9;
|
|
91
|
+
--annot-chip-bg: #edf1ff;
|
|
92
|
+
--annot-focus-ring: #3456c9;
|
|
93
|
+
|
|
94
|
+
/* === Interaction === */
|
|
95
|
+
--annot-hover-bg: #eef1fa;
|
|
96
|
+
--annot-hover-border: #8893b8;
|
|
97
|
+
--annot-choice-bg: transparent;
|
|
98
|
+
--annot-choice-hover: #eef1fa;
|
|
99
|
+
--annot-choice-active: #dce4ff;
|
|
100
|
+
--annot-input-bg: #ffffff;
|
|
101
|
+
--annot-input-border: #8893b8;
|
|
102
|
+
|
|
103
|
+
/* === Canvas === */
|
|
104
|
+
--annot-canvas-bg: #e8ecf5;
|
|
105
|
+
--annot-canvas-check: #dde2ef;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
* {
|
|
109
|
+
margin: 0;
|
|
110
|
+
padding: 0;
|
|
111
|
+
box-sizing: border-box;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
html,
|
|
115
|
+
body {
|
|
116
|
+
width: 100%;
|
|
117
|
+
height: 100%;
|
|
118
|
+
overflow: hidden;
|
|
119
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
120
|
+
background: var(--annot-bg-primary, #2a2a2a);
|
|
121
|
+
color: var(--annot-text-primary, #e0e0e0);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
#canvas-container {
|
|
125
|
+
position: absolute;
|
|
126
|
+
top: 48px;
|
|
127
|
+
left: 0;
|
|
128
|
+
right: 0;
|
|
129
|
+
bottom: 28px;
|
|
130
|
+
overflow: auto;
|
|
131
|
+
background: var(--annot-canvas-bg);
|
|
132
|
+
background-image:
|
|
133
|
+
linear-gradient(45deg, var(--annot-canvas-check) 25%, transparent 25%),
|
|
134
|
+
linear-gradient(-45deg, var(--annot-canvas-check) 25%, transparent 25%),
|
|
135
|
+
linear-gradient(45deg, transparent 75%, var(--annot-canvas-check) 75%),
|
|
136
|
+
linear-gradient(-45deg, transparent 75%, var(--annot-canvas-check) 75%);
|
|
137
|
+
background-size: 20px 20px;
|
|
138
|
+
background-position:
|
|
139
|
+
0 0,
|
|
140
|
+
0 10px,
|
|
141
|
+
10px -10px,
|
|
142
|
+
-10px 0;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* Editor SVG root.
|
|
146
|
+
*
|
|
147
|
+
* Two selectors — id `#svg-root` and attribute `[data-annot-shell-root]` —
|
|
148
|
+
* resolve to the same SVG element across hosts. The PWA + Tauri desktop
|
|
149
|
+
* ship a pre-baked `<svg id="svg-root">` in their index.html so first-
|
|
150
|
+
* render CSS hits the styled element before JS boots; the VSCode webview
|
|
151
|
+
* (and any future host that doesn't pre-bake the SVG) lets `EditorShell`
|
|
152
|
+
* create an anonymous `<svg data-annot-shell-root="1">` inside its
|
|
153
|
+
* container. Both surfaces match the same rules so editor styling stays
|
|
154
|
+
* consistent regardless of how the SVG was provisioned. */
|
|
155
|
+
#svg-root,
|
|
156
|
+
[data-annot-shell-root] {
|
|
157
|
+
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5);
|
|
158
|
+
background: #fff;
|
|
159
|
+
cursor: default;
|
|
160
|
+
display: block;
|
|
161
|
+
margin: 20px auto;
|
|
162
|
+
/* SVG `<text>` is selectable by default. During a drag — especially
|
|
163
|
+
when smart-guide overlays appear and the cursor crosses an
|
|
164
|
+
annotation's text — the browser would otherwise interpret the
|
|
165
|
+
pointer travel as a text-selection range and paint the blue
|
|
166
|
+
highlight on the just-touched run. The contentEditable text-edit
|
|
167
|
+
overlay opts back in via its own inline `user-select: text`. */
|
|
168
|
+
user-select: none;
|
|
169
|
+
-webkit-user-select: none;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/* The contentEditable overlay lives inside a `<foreignObject>` —
|
|
173
|
+
bring text selection back so the user can mark / replace runs
|
|
174
|
+
while editing. */
|
|
175
|
+
#svg-root foreignObject [contenteditable="true"],
|
|
176
|
+
[data-annot-shell-root] foreignObject [contenteditable="true"] {
|
|
177
|
+
user-select: text;
|
|
178
|
+
-webkit-user-select: text;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
#statusbar {
|
|
182
|
+
position: absolute;
|
|
183
|
+
bottom: 0;
|
|
184
|
+
left: 0;
|
|
185
|
+
right: 0;
|
|
186
|
+
height: 28px;
|
|
187
|
+
background: var(--annot-bg-panel, #1e1e1e);
|
|
188
|
+
border-top: 1px solid var(--annot-border-color, #333);
|
|
189
|
+
display: flex;
|
|
190
|
+
align-items: center;
|
|
191
|
+
padding: 0 12px;
|
|
192
|
+
gap: 16px;
|
|
193
|
+
font-size: 12px;
|
|
194
|
+
color: #888;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* Save toast notification */
|
|
198
|
+
.save-toast {
|
|
199
|
+
position: fixed;
|
|
200
|
+
bottom: 48px;
|
|
201
|
+
left: 50%;
|
|
202
|
+
transform: translateX(-50%) translateY(20px);
|
|
203
|
+
background: var(--annot-accent, #00d4ff);
|
|
204
|
+
color: #000;
|
|
205
|
+
padding: 6px 24px;
|
|
206
|
+
border-radius: 20px;
|
|
207
|
+
font-size: 13px;
|
|
208
|
+
font-weight: 600;
|
|
209
|
+
opacity: 0;
|
|
210
|
+
transition:
|
|
211
|
+
opacity 0.3s,
|
|
212
|
+
transform 0.3s;
|
|
213
|
+
z-index: 1000;
|
|
214
|
+
pointer-events: none;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.save-toast.show {
|
|
218
|
+
opacity: 1;
|
|
219
|
+
transform: translateX(-50%) translateY(0);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* ---- Tag editor ---- */
|
|
223
|
+
|
|
224
|
+
.tag-editor {
|
|
225
|
+
display: flex;
|
|
226
|
+
align-items: center;
|
|
227
|
+
gap: 4px;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.tag-chips {
|
|
231
|
+
display: flex;
|
|
232
|
+
align-items: center;
|
|
233
|
+
gap: 3px;
|
|
234
|
+
flex-wrap: wrap;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.tag-chip {
|
|
238
|
+
display: inline-flex;
|
|
239
|
+
align-items: center;
|
|
240
|
+
gap: 2px;
|
|
241
|
+
padding: 1px 6px;
|
|
242
|
+
border-radius: 10px;
|
|
243
|
+
background: var(--annot-chip-bg, #16213e);
|
|
244
|
+
color: var(--annot-text-primary);
|
|
245
|
+
font-size: 11px;
|
|
246
|
+
white-space: nowrap;
|
|
247
|
+
border: 1px solid var(--annot-border-color);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.tag-chip-label {
|
|
251
|
+
pointer-events: none;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.tag-chip-delete {
|
|
255
|
+
border: none;
|
|
256
|
+
background: none;
|
|
257
|
+
color: var(--annot-text-muted);
|
|
258
|
+
font-size: 12px;
|
|
259
|
+
cursor: pointer;
|
|
260
|
+
padding: 0 1px;
|
|
261
|
+
line-height: 1;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.tag-chip-delete:hover {
|
|
265
|
+
color: #e44;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.tag-add-btn {
|
|
269
|
+
border: 1px dashed var(--annot-border-color);
|
|
270
|
+
background: transparent;
|
|
271
|
+
color: var(--annot-text-muted);
|
|
272
|
+
font-size: 11px;
|
|
273
|
+
padding: 1px 8px;
|
|
274
|
+
border-radius: 10px;
|
|
275
|
+
cursor: pointer;
|
|
276
|
+
white-space: nowrap;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.tag-add-btn:hover {
|
|
280
|
+
border-color: var(--annot-accent);
|
|
281
|
+
color: var(--annot-accent);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.tag-input-row {
|
|
285
|
+
position: absolute;
|
|
286
|
+
bottom: 100%;
|
|
287
|
+
left: 0;
|
|
288
|
+
margin-bottom: 4px;
|
|
289
|
+
display: flex;
|
|
290
|
+
gap: 4px;
|
|
291
|
+
background: var(--annot-bg-panel-deep, #1e1e2e);
|
|
292
|
+
border: 1px solid var(--annot-border-color);
|
|
293
|
+
border-radius: 6px;
|
|
294
|
+
padding: 6px 8px;
|
|
295
|
+
box-shadow: 0 -4px 12px var(--annot-shadow);
|
|
296
|
+
z-index: 200;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.tag-input {
|
|
300
|
+
height: 24px;
|
|
301
|
+
border: 1px solid var(--annot-input-border, #444);
|
|
302
|
+
border-radius: 4px;
|
|
303
|
+
background: var(--annot-input-bg, #2a2a2a);
|
|
304
|
+
color: var(--annot-text-primary);
|
|
305
|
+
font-size: 12px;
|
|
306
|
+
padding: 0 6px;
|
|
307
|
+
outline: none;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.tag-input-key {
|
|
311
|
+
width: 80px;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.tag-input-value {
|
|
315
|
+
width: 120px;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.tag-input:focus {
|
|
319
|
+
border-color: var(--annot-accent);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.tag-ok-btn,
|
|
323
|
+
.tag-cancel-btn {
|
|
324
|
+
width: 24px;
|
|
325
|
+
height: 24px;
|
|
326
|
+
border: none;
|
|
327
|
+
border-radius: 4px;
|
|
328
|
+
cursor: pointer;
|
|
329
|
+
font-size: 14px;
|
|
330
|
+
display: flex;
|
|
331
|
+
align-items: center;
|
|
332
|
+
justify-content: center;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.tag-ok-btn {
|
|
336
|
+
background: var(--annot-accent);
|
|
337
|
+
color: #000;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.tag-ok-btn:hover {
|
|
341
|
+
opacity: 0.8;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.tag-cancel-btn {
|
|
345
|
+
background: transparent;
|
|
346
|
+
color: var(--annot-text-muted);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.tag-cancel-btn:hover {
|
|
350
|
+
color: #e44;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/* Tag chips in gallery cards */
|
|
354
|
+
.gallery-tag-chips {
|
|
355
|
+
display: flex;
|
|
356
|
+
flex-wrap: wrap;
|
|
357
|
+
gap: 2px;
|
|
358
|
+
margin-top: 3px;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.gallery-tag {
|
|
362
|
+
font-size: 10px;
|
|
363
|
+
padding: 0 5px;
|
|
364
|
+
border-radius: 8px;
|
|
365
|
+
background: var(--annot-chip-bg, #16213e);
|
|
366
|
+
color: var(--annot-text-secondary);
|
|
367
|
+
border: 1px solid var(--annot-border-color);
|
|
368
|
+
white-space: nowrap;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
#statusbar span {
|
|
372
|
+
white-space: nowrap;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/* Zoom controls */
|
|
376
|
+
#zoom-controls {
|
|
377
|
+
display: flex;
|
|
378
|
+
align-items: center;
|
|
379
|
+
gap: 2px;
|
|
380
|
+
background: var(--annot-input-bg, #2a2a2a);
|
|
381
|
+
border: 1px solid var(--annot-border-color, #444);
|
|
382
|
+
border-radius: 4px;
|
|
383
|
+
padding: 0 2px;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.zoom-btn {
|
|
387
|
+
width: 22px;
|
|
388
|
+
height: 22px;
|
|
389
|
+
border: none;
|
|
390
|
+
background: transparent;
|
|
391
|
+
color: var(--annot-text-secondary, #888);
|
|
392
|
+
font-size: 16px;
|
|
393
|
+
cursor: pointer;
|
|
394
|
+
display: flex;
|
|
395
|
+
align-items: center;
|
|
396
|
+
justify-content: center;
|
|
397
|
+
border-radius: 3px;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.zoom-btn:hover {
|
|
401
|
+
background: var(--annot-hover-bg, #333);
|
|
402
|
+
color: var(--annot-accent, #00d4ff);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.zoom-label {
|
|
406
|
+
min-width: 42px;
|
|
407
|
+
height: 22px;
|
|
408
|
+
border: none;
|
|
409
|
+
background: transparent;
|
|
410
|
+
color: var(--annot-text-primary, #ccc);
|
|
411
|
+
font-size: 11px;
|
|
412
|
+
cursor: pointer;
|
|
413
|
+
text-align: center;
|
|
414
|
+
padding: 0 4px;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.zoom-label:hover {
|
|
418
|
+
color: var(--annot-accent, #00d4ff);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.zoom-select-wrap {
|
|
422
|
+
position: relative;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.zoom-menu {
|
|
426
|
+
position: absolute;
|
|
427
|
+
bottom: 100%;
|
|
428
|
+
left: 50%;
|
|
429
|
+
transform: translateX(-50%);
|
|
430
|
+
margin-bottom: 4px;
|
|
431
|
+
background: var(--annot-bg-panel-deep, #1e1e2e);
|
|
432
|
+
border: 1px solid var(--annot-border-color, #444);
|
|
433
|
+
border-radius: 6px;
|
|
434
|
+
overflow: hidden;
|
|
435
|
+
box-shadow: 0 -4px 16px var(--annot-shadow, rgba(0, 0, 0, 0.4));
|
|
436
|
+
z-index: 200;
|
|
437
|
+
min-width: 90px;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.zoom-menu-item {
|
|
441
|
+
display: block;
|
|
442
|
+
width: 100%;
|
|
443
|
+
padding: 6px 14px;
|
|
444
|
+
border: none;
|
|
445
|
+
background: transparent;
|
|
446
|
+
color: var(--annot-text-primary, #ccc);
|
|
447
|
+
font-size: 12px;
|
|
448
|
+
text-align: left;
|
|
449
|
+
cursor: pointer;
|
|
450
|
+
white-space: nowrap;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.zoom-menu-item:hover {
|
|
454
|
+
background: var(--annot-choice-hover, #2a2a40);
|
|
455
|
+
color: var(--annot-accent, #00d4ff);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.zoom-menu-item.active {
|
|
459
|
+
color: var(--annot-accent, #00d4ff);
|
|
460
|
+
font-weight: bold;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.zoom-menu-sep {
|
|
464
|
+
height: 1px;
|
|
465
|
+
background: var(--annot-border-color, #444);
|
|
466
|
+
margin: 2px 0;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/* ---- Gallery toolbar ---- */
|
|
470
|
+
|
|
471
|
+
.gallery-toolbar-title {
|
|
472
|
+
font-size: 16px;
|
|
473
|
+
font-weight: 700;
|
|
474
|
+
color: var(--annot-accent);
|
|
475
|
+
padding: 0 4px;
|
|
476
|
+
white-space: nowrap;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.gallery-toolbar-search {
|
|
480
|
+
flex: 1;
|
|
481
|
+
max-width: 360px;
|
|
482
|
+
height: 32px;
|
|
483
|
+
padding: 0 10px;
|
|
484
|
+
border: 1px solid var(--annot-border-color);
|
|
485
|
+
border-radius: 6px;
|
|
486
|
+
background: var(--annot-input-bg);
|
|
487
|
+
color: var(--annot-text-primary);
|
|
488
|
+
font-size: 13px;
|
|
489
|
+
outline: none;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
.gallery-toolbar-search:focus {
|
|
493
|
+
border-color: var(--annot-accent);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/* ---- File details drawer ----
|
|
497
|
+
*
|
|
498
|
+
* Right-side slide-in panel that consolidates file metadata and full tag
|
|
499
|
+
* management. Non-modal (canvas stays interactive outside the drawer) and
|
|
500
|
+
* pinned below editor-header + toolbar and above the statusbar.
|
|
501
|
+
*/
|
|
502
|
+
.file-details-backdrop {
|
|
503
|
+
position: absolute;
|
|
504
|
+
top: 48px;
|
|
505
|
+
right: 0;
|
|
506
|
+
bottom: 28px;
|
|
507
|
+
left: 0;
|
|
508
|
+
background: rgba(0, 0, 0, 0);
|
|
509
|
+
pointer-events: none;
|
|
510
|
+
transition: background 0.2s ease-out;
|
|
511
|
+
z-index: 19;
|
|
512
|
+
}
|
|
513
|
+
.file-details-backdrop.open {
|
|
514
|
+
background: rgba(0, 0, 0, 0.2);
|
|
515
|
+
pointer-events: auto;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.file-details-drawer {
|
|
519
|
+
position: absolute;
|
|
520
|
+
top: 48px;
|
|
521
|
+
right: 0;
|
|
522
|
+
bottom: 28px;
|
|
523
|
+
width: 360px;
|
|
524
|
+
max-width: 80vw;
|
|
525
|
+
background: var(--annot-bg-panel);
|
|
526
|
+
border-left: 1px solid var(--annot-border-color);
|
|
527
|
+
box-shadow: -4px 0 28px rgba(0, 0, 0, 0.35);
|
|
528
|
+
transform: translateX(calc(100% + 28px)); /* include shadow in off-screen */
|
|
529
|
+
transition: transform 0.22s ease-out;
|
|
530
|
+
z-index: 20;
|
|
531
|
+
overflow-y: auto;
|
|
532
|
+
display: flex;
|
|
533
|
+
flex-direction: column;
|
|
534
|
+
}
|
|
535
|
+
.file-details-drawer.open {
|
|
536
|
+
transform: translateX(0);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.file-details-header {
|
|
540
|
+
display: flex;
|
|
541
|
+
align-items: center;
|
|
542
|
+
justify-content: space-between;
|
|
543
|
+
padding: 14px 18px;
|
|
544
|
+
border-bottom: 1px solid var(--annot-border-color);
|
|
545
|
+
flex-shrink: 0;
|
|
546
|
+
}
|
|
547
|
+
.file-details-title {
|
|
548
|
+
margin: 0;
|
|
549
|
+
font-size: 15px;
|
|
550
|
+
font-weight: 700;
|
|
551
|
+
letter-spacing: -0.02em;
|
|
552
|
+
color: var(--annot-text-primary);
|
|
553
|
+
}
|
|
554
|
+
.file-details-close {
|
|
555
|
+
width: 32px;
|
|
556
|
+
height: 32px;
|
|
557
|
+
border: none;
|
|
558
|
+
background: transparent;
|
|
559
|
+
color: var(--annot-text-secondary);
|
|
560
|
+
border-radius: 6px;
|
|
561
|
+
cursor: pointer;
|
|
562
|
+
font-size: 20px;
|
|
563
|
+
display: flex;
|
|
564
|
+
align-items: center;
|
|
565
|
+
justify-content: center;
|
|
566
|
+
}
|
|
567
|
+
.file-details-close:hover {
|
|
568
|
+
background: var(--annot-hover-bg);
|
|
569
|
+
color: var(--annot-text-primary);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.file-details-section {
|
|
573
|
+
padding: 14px 18px;
|
|
574
|
+
border-bottom: 1px solid var(--annot-border-subtle);
|
|
575
|
+
}
|
|
576
|
+
.file-details-section:last-child {
|
|
577
|
+
border-bottom: none;
|
|
578
|
+
}
|
|
579
|
+
.file-details-section-title {
|
|
580
|
+
margin: 0 0 10px;
|
|
581
|
+
font-size: 11px;
|
|
582
|
+
font-weight: 700;
|
|
583
|
+
text-transform: uppercase;
|
|
584
|
+
letter-spacing: 0.08em;
|
|
585
|
+
color: var(--annot-text-muted);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.file-details-row {
|
|
589
|
+
display: grid;
|
|
590
|
+
grid-template-columns: 92px 1fr;
|
|
591
|
+
gap: 12px;
|
|
592
|
+
padding: 5px 0;
|
|
593
|
+
font-size: 13px;
|
|
594
|
+
line-height: 1.45;
|
|
595
|
+
}
|
|
596
|
+
.file-details-row-label {
|
|
597
|
+
color: var(--annot-text-muted);
|
|
598
|
+
}
|
|
599
|
+
.file-details-row-value {
|
|
600
|
+
color: var(--annot-text-primary);
|
|
601
|
+
word-break: break-word;
|
|
602
|
+
min-width: 0;
|
|
603
|
+
}
|
|
604
|
+
.file-details-row-value.mono {
|
|
605
|
+
font-family:
|
|
606
|
+
SF Mono,
|
|
607
|
+
Menlo,
|
|
608
|
+
Consolas,
|
|
609
|
+
monospace;
|
|
610
|
+
font-size: 12px;
|
|
611
|
+
}
|
|
612
|
+
.file-details-row-value.selectable {
|
|
613
|
+
user-select: text;
|
|
614
|
+
}
|
|
615
|
+
a.file-details-row-value {
|
|
616
|
+
color: var(--annot-accent);
|
|
617
|
+
text-decoration: none;
|
|
618
|
+
}
|
|
619
|
+
a.file-details-row-value:hover {
|
|
620
|
+
text-decoration: underline;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
.file-details-tags-editor {
|
|
624
|
+
/* TagEditor renders its own layout; this wrapper just sizes it. */
|
|
625
|
+
min-height: 32px;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/* Author avatar inside the Last commit section. Rendered inline with
|
|
629
|
+
* the author name so the row height stays consistent with the other
|
|
630
|
+
* metadata rows. */
|
|
631
|
+
.file-details-avatar {
|
|
632
|
+
width: 16px;
|
|
633
|
+
height: 16px;
|
|
634
|
+
border-radius: 50%;
|
|
635
|
+
margin-right: 6px;
|
|
636
|
+
vertical-align: middle;
|
|
637
|
+
display: inline-block;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/* Abbreviated commit SHA shown alongside the message. Monospace so it
|
|
641
|
+
* reads as a git ref rather than prose. */
|
|
642
|
+
.file-details-sha {
|
|
643
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
644
|
+
font-size: 11px;
|
|
645
|
+
color: var(--annot-text-secondary);
|
|
646
|
+
padding: 1px 4px;
|
|
647
|
+
border-radius: 3px;
|
|
648
|
+
background: var(--annot-input-bg, rgba(255, 255, 255, 0.04));
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/* External-link section ("View on GitHub", etc). One link per row,
|
|
652
|
+
* icon + label, styled like a prominent action rather than a value. */
|
|
653
|
+
.file-details-link-row {
|
|
654
|
+
display: block;
|
|
655
|
+
}
|
|
656
|
+
.file-details-external-link,
|
|
657
|
+
.file-details-external-link:visited {
|
|
658
|
+
display: inline-flex;
|
|
659
|
+
align-items: center;
|
|
660
|
+
gap: 6px;
|
|
661
|
+
color: var(--annot-accent);
|
|
662
|
+
text-decoration: none;
|
|
663
|
+
font-size: 13px;
|
|
664
|
+
padding: 4px 0;
|
|
665
|
+
}
|
|
666
|
+
.file-details-external-link:hover {
|
|
667
|
+
text-decoration: underline;
|
|
668
|
+
}
|
|
669
|
+
.file-details-external-link annot-icon {
|
|
670
|
+
font-size: 16px;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/* Anchors inside a row value (e.g. the commit-message link in the
|
|
674
|
+
* Last commit section). Override the browser's default :visited
|
|
675
|
+
* color — on a dark panel it drops to a near-black purple that the
|
|
676
|
+
* user reported as unreadable. Keeping the accent color across
|
|
677
|
+
* states matches how `a.file-details-row-value` handles the
|
|
678
|
+
* top-level Source link. */
|
|
679
|
+
.file-details-row-value a,
|
|
680
|
+
.file-details-row-value a:visited {
|
|
681
|
+
color: var(--annot-accent);
|
|
682
|
+
text-decoration: underline;
|
|
683
|
+
}
|
|
684
|
+
.file-details-row-value a:hover {
|
|
685
|
+
opacity: 0.85;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/* Inline rename affordance on the Name row. The input occupies the
|
|
689
|
+
* same visual slot as a read-only span value — NO border or padding
|
|
690
|
+
* offset — so the text baseline and left edge line up perfectly with
|
|
691
|
+
* the "Location", "Dimensions", etc. rows below. Only a subtle box-
|
|
692
|
+
* shadow hover / focus state hints that the field is editable.
|
|
693
|
+
* Matches the Finder / macOS settings-panel pattern where values
|
|
694
|
+
* inline-edit without visible field chrome. */
|
|
695
|
+
.file-details-name-wrap {
|
|
696
|
+
min-width: 0;
|
|
697
|
+
}
|
|
698
|
+
.file-details-name-input {
|
|
699
|
+
display: block;
|
|
700
|
+
width: 100%;
|
|
701
|
+
border: none;
|
|
702
|
+
outline: none;
|
|
703
|
+
background: transparent;
|
|
704
|
+
color: var(--annot-text-primary);
|
|
705
|
+
font: inherit;
|
|
706
|
+
font-size: 13px;
|
|
707
|
+
line-height: 1.45;
|
|
708
|
+
padding: 0;
|
|
709
|
+
margin: 0;
|
|
710
|
+
border-radius: 3px;
|
|
711
|
+
transition:
|
|
712
|
+
background 0.15s,
|
|
713
|
+
box-shadow 0.15s;
|
|
714
|
+
min-width: 0;
|
|
715
|
+
text-overflow: ellipsis;
|
|
716
|
+
}
|
|
717
|
+
.file-details-name-input:hover:not(:focus) {
|
|
718
|
+
background: var(--annot-hover-bg);
|
|
719
|
+
box-shadow: inset 0 -1px 0 var(--annot-border-subtle);
|
|
720
|
+
}
|
|
721
|
+
.file-details-name-input:focus {
|
|
722
|
+
background: var(--annot-input-bg, rgba(255, 255, 255, 0.04));
|
|
723
|
+
box-shadow: inset 0 0 0 1px var(--annot-accent);
|
|
724
|
+
text-overflow: clip;
|
|
725
|
+
}
|
|
726
|
+
.file-details-name-input:disabled {
|
|
727
|
+
opacity: 0.55;
|
|
728
|
+
cursor: wait;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
.file-details-name-error {
|
|
732
|
+
color: var(--danger, #ff7070);
|
|
733
|
+
font-size: 11px;
|
|
734
|
+
line-height: 1.3;
|
|
735
|
+
margin-top: 4px;
|
|
736
|
+
min-height: 0; /* collapses when empty */
|
|
737
|
+
}
|
|
738
|
+
.file-details-name-error:empty {
|
|
739
|
+
display: none;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/* ---- Editor header ----
|
|
743
|
+
*
|
|
744
|
+
* #editor-header sits above #toolbar in editor mode and carries the
|
|
745
|
+
* per-file context (brand, breadcrumb + filename, save status, help,
|
|
746
|
+
* theme). It is hidden by default and revealed via body.editor-mode
|
|
747
|
+
* so that gallery mode keeps its single-row top (brand + search + …).
|
|
748
|
+
*
|
|
749
|
+
* When editor-header is visible, #toolbar is pushed down by 48px and
|
|
750
|
+
* #canvas-container by another 48px so both rows stay visible.
|
|
751
|
+
*/
|
|
752
|
+
#editor-header {
|
|
753
|
+
display: none;
|
|
754
|
+
}
|
|
755
|
+
/* Editor header — compact single-row layout, 48px:
|
|
756
|
+
*
|
|
757
|
+
* [A] Device › test › image-1234.png ⓘ [?] [☀]
|
|
758
|
+
*
|
|
759
|
+
* Industry-standard pattern used by Google Drive, Finder, VS Code, etc.
|
|
760
|
+
* The filename is the final breadcrumb segment (styled bold + active),
|
|
761
|
+
* so path and filename share one coherent unit. Tags moved to the
|
|
762
|
+
* file-details drawer (opened via ⓘ), leaving the header free for
|
|
763
|
+
* navigation + a pair of global actions.
|
|
764
|
+
*/
|
|
765
|
+
body.editor-mode #editor-header {
|
|
766
|
+
position: absolute;
|
|
767
|
+
top: 0;
|
|
768
|
+
left: 0;
|
|
769
|
+
right: 0;
|
|
770
|
+
height: 48px;
|
|
771
|
+
display: flex;
|
|
772
|
+
align-items: center;
|
|
773
|
+
padding: 0 14px;
|
|
774
|
+
gap: 10px;
|
|
775
|
+
/* One level deeper than the toolbar (--bg-panel) so the header
|
|
776
|
+
* reads as page-level context and the toolbar as the elevated
|
|
777
|
+
* "action surface". Same palette in both themes (dark: #0f1730
|
|
778
|
+
* vs #121933; light: #fafbff vs #ffffff) — subtle, just enough
|
|
779
|
+
* to establish hierarchy without adding visual noise. */
|
|
780
|
+
background: var(--annot-bg-panel-deep);
|
|
781
|
+
border-bottom: 1px solid var(--annot-border-color);
|
|
782
|
+
z-index: 11;
|
|
783
|
+
/* `overflow: visible` so the custom CSS tooltip on header buttons
|
|
784
|
+
* (positioned at `bottom: -40px`) can extend below the 48px header
|
|
785
|
+
* without being clipped. Long breadcrumb paths are already
|
|
786
|
+
* controlled by the `.editor-header-path` container's own
|
|
787
|
+
* `overflow: hidden` + per-segment `text-overflow: ellipsis`, so
|
|
788
|
+
* dropping the parent's clip has no effect on the breadcrumb
|
|
789
|
+
* layout. */
|
|
790
|
+
overflow: visible;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
.editor-header-brand {
|
|
794
|
+
width: 36px;
|
|
795
|
+
height: 36px;
|
|
796
|
+
border: none;
|
|
797
|
+
background: transparent;
|
|
798
|
+
padding: 0;
|
|
799
|
+
border-radius: 8px;
|
|
800
|
+
cursor: pointer;
|
|
801
|
+
display: flex;
|
|
802
|
+
align-items: center;
|
|
803
|
+
justify-content: center;
|
|
804
|
+
flex-shrink: 0;
|
|
805
|
+
transition: background 0.15s;
|
|
806
|
+
}
|
|
807
|
+
.editor-header-brand:hover {
|
|
808
|
+
background: var(--annot-hover-bg);
|
|
809
|
+
}
|
|
810
|
+
.editor-header-brand:focus-visible {
|
|
811
|
+
outline: 2px solid var(--annot-accent);
|
|
812
|
+
outline-offset: 2px;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/* Breadcrumb as full path + filename (the last, bold, non-clickable
|
|
816
|
+
* "active" segment).
|
|
817
|
+
*
|
|
818
|
+
* Flex-shrink priorities (from least important → most important):
|
|
819
|
+
* ancestor segments (Device, test, …) : flex-shrink: 1 → shrink first
|
|
820
|
+
* filename : flex-shrink: 0 → stays visible
|
|
821
|
+
* info button : flex-shrink: 0 → stays visible
|
|
822
|
+
*
|
|
823
|
+
* Result: when space is tight, only the ancestor path segments ellipsize.
|
|
824
|
+
* The filename and info button remain fully visible. When space is
|
|
825
|
+
* abundant (as in the usual case), filename renders at natural width
|
|
826
|
+
* with no artificial max-width cap.
|
|
827
|
+
*/
|
|
828
|
+
.editor-header-path {
|
|
829
|
+
flex: 0 1 auto;
|
|
830
|
+
min-width: 0;
|
|
831
|
+
overflow: hidden;
|
|
832
|
+
}
|
|
833
|
+
.editor-header-path .breadcrumb-item {
|
|
834
|
+
min-width: 0;
|
|
835
|
+
overflow: hidden;
|
|
836
|
+
text-overflow: ellipsis;
|
|
837
|
+
white-space: nowrap;
|
|
838
|
+
flex-shrink: 1;
|
|
839
|
+
}
|
|
840
|
+
.editor-header-path .breadcrumb-filename {
|
|
841
|
+
flex-shrink: 0;
|
|
842
|
+
font-weight: 600;
|
|
843
|
+
color: var(--annot-text-primary);
|
|
844
|
+
}
|
|
845
|
+
.editor-header-path .breadcrumb-filename:hover {
|
|
846
|
+
background: none;
|
|
847
|
+
color: var(--annot-text-primary);
|
|
848
|
+
cursor: default;
|
|
849
|
+
}
|
|
850
|
+
.editor-header-path .editor-header-info-btn {
|
|
851
|
+
flex-shrink: 0;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/* File actions cluster in the editor header right side (Open / Copy /
|
|
855
|
+
* Save ▼). Reuses .header-info-btn for baseline styling so these
|
|
856
|
+
* buttons match the help + theme toggle next to them. Grouped in their
|
|
857
|
+
* own container so there's subtle spacing between "file actions" and
|
|
858
|
+
* "global actions" (help, theme). */
|
|
859
|
+
.editor-header-file-actions {
|
|
860
|
+
display: flex;
|
|
861
|
+
align-items: center;
|
|
862
|
+
gap: 2px;
|
|
863
|
+
margin-right: 4px;
|
|
864
|
+
padding-right: 4px;
|
|
865
|
+
border-right: 1px solid var(--annot-border-subtle);
|
|
866
|
+
flex-shrink: 0;
|
|
867
|
+
}
|
|
868
|
+
/* The header Save dropdown arrow needs its own layout — the shared
|
|
869
|
+
* .tool-dropdown-arrow in toolbar.css is absolutely positioned below a
|
|
870
|
+
* tool icon (vertical-stack case), which doesn't fit horizontal-header
|
|
871
|
+
* placement. Reset it and let flex lay it out next to the save icon. */
|
|
872
|
+
.header-save-wrap {
|
|
873
|
+
display: inline-flex;
|
|
874
|
+
align-items: center;
|
|
875
|
+
gap: 0;
|
|
876
|
+
}
|
|
877
|
+
.header-save-wrap .tool-dropdown-arrow {
|
|
878
|
+
position: static;
|
|
879
|
+
transform: none;
|
|
880
|
+
width: 14px;
|
|
881
|
+
height: 28px;
|
|
882
|
+
background: transparent;
|
|
883
|
+
color: var(--annot-text-muted);
|
|
884
|
+
border: none;
|
|
885
|
+
font-size: 14px;
|
|
886
|
+
cursor: pointer;
|
|
887
|
+
display: flex;
|
|
888
|
+
align-items: center;
|
|
889
|
+
justify-content: center;
|
|
890
|
+
margin-left: -4px;
|
|
891
|
+
}
|
|
892
|
+
.header-save-wrap .tool-dropdown-arrow:hover {
|
|
893
|
+
color: var(--annot-accent);
|
|
894
|
+
background: var(--annot-hover-bg);
|
|
895
|
+
border-radius: 4px;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/* Inline rename input that replaces the filename span on double-click.
|
|
899
|
+
* Styled to match the filename's visual weight (same font-weight,
|
|
900
|
+
* size, line-height) so swapping in/out of edit mode doesn't shift
|
|
901
|
+
* the baseline or the row height. */
|
|
902
|
+
.breadcrumb-filename-input {
|
|
903
|
+
flex-shrink: 0;
|
|
904
|
+
min-width: 160px;
|
|
905
|
+
max-width: 420px;
|
|
906
|
+
font: inherit;
|
|
907
|
+
font-weight: 600;
|
|
908
|
+
font-size: 13px;
|
|
909
|
+
line-height: 1.4;
|
|
910
|
+
color: var(--annot-text-primary);
|
|
911
|
+
background: var(--annot-input-bg, rgba(255, 255, 255, 0.04));
|
|
912
|
+
border: 1px solid var(--annot-accent);
|
|
913
|
+
border-radius: 4px;
|
|
914
|
+
padding: 1px 6px;
|
|
915
|
+
outline: none;
|
|
916
|
+
}
|
|
917
|
+
.breadcrumb-filename-input:disabled {
|
|
918
|
+
opacity: 0.55;
|
|
919
|
+
cursor: wait;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/* In editor mode, the top #toolbar is replaced by the left
|
|
923
|
+
* #editor-sidebar. Hide the top toolbar entirely and make room for
|
|
924
|
+
* the sidebar on the left edge of the canvas area.
|
|
925
|
+
*
|
|
926
|
+
* Doc mode + split-editor mode follow the same chrome contract:
|
|
927
|
+
* each surface ships its own dedicated header (`<annot-doc-header>`
|
|
928
|
+
* / `.split-editor-header`) carrying brand + actions + theme
|
|
929
|
+
* toggle, so the gallery's search-bearing #toolbar is redundant
|
|
930
|
+
* and the search box (meaningless on a single-doc editor or a
|
|
931
|
+
* modal split-editor task) would otherwise leak into chrome. */
|
|
932
|
+
body.editor-mode #toolbar,
|
|
933
|
+
body.annot-doc-mode #toolbar,
|
|
934
|
+
body.split-editor-active #toolbar {
|
|
935
|
+
display: none;
|
|
936
|
+
}
|
|
937
|
+
#editor-sidebar {
|
|
938
|
+
display: none; /* hidden by default (gallery mode) */
|
|
939
|
+
}
|
|
940
|
+
body.editor-mode #editor-sidebar {
|
|
941
|
+
position: absolute;
|
|
942
|
+
top: 48px;
|
|
943
|
+
left: 0;
|
|
944
|
+
bottom: 28px;
|
|
945
|
+
width: 48px;
|
|
946
|
+
background: var(--annot-bg-panel);
|
|
947
|
+
border-right: 1px solid var(--annot-border-color);
|
|
948
|
+
display: flex;
|
|
949
|
+
flex-direction: column;
|
|
950
|
+
align-items: center;
|
|
951
|
+
padding: 6px 0;
|
|
952
|
+
gap: 2px;
|
|
953
|
+
z-index: 10;
|
|
954
|
+
/* `overflow: visible` (both axes) so tooltips positioned to the
|
|
955
|
+
* right of a tool button can escape the 48px strip into the canvas
|
|
956
|
+
* area. Per CSS spec, mixing `overflow-x: visible` with
|
|
957
|
+
* `overflow-y: auto` causes the browser to promote `visible` to
|
|
958
|
+
* `auto`, which would re-introduce the clipping. So we stay fully
|
|
959
|
+
* visible and accept that on extremely short viewports the
|
|
960
|
+
* bottom-most tools may overflow past the sidebar's visual end.
|
|
961
|
+
* With 9 tools × ~38 px = ~342 px total + 6 px top padding, the
|
|
962
|
+
* full stack fits in any viewport ≥ 396 px tall — well below the
|
|
963
|
+
* ~500 px minimum any modern laptop / tablet provides.
|
|
964
|
+
*
|
|
965
|
+
* The old "clip dropdown-arrow bleed" justification no longer
|
|
966
|
+
* applies: the caret element was removed when the variant badge
|
|
967
|
+
* took over the flyout-trigger role, and the badge's hit-zone
|
|
968
|
+
* expansion (::before inset: -6px) stays within the button's
|
|
969
|
+
* 36 px footprint. */
|
|
970
|
+
overflow: visible;
|
|
971
|
+
}
|
|
972
|
+
body.editor-mode #canvas-container {
|
|
973
|
+
top: 48px; /* below editor-header only */
|
|
974
|
+
left: 48px; /* right of the sidebar */
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/* Right property panel — context-aware:
|
|
978
|
+
* Tool active (not Select) → shows tool default properties
|
|
979
|
+
* Selection present → shows selection properties
|
|
980
|
+
* Otherwise → hidden (body.has-right-panel removed)
|
|
981
|
+
* Host (app.ts) toggles `body.has-right-panel` to show/hide.
|
|
982
|
+
*/
|
|
983
|
+
#editor-right-panel {
|
|
984
|
+
display: none;
|
|
985
|
+
}
|
|
986
|
+
body.editor-mode.has-right-panel #editor-right-panel {
|
|
987
|
+
position: absolute;
|
|
988
|
+
top: 48px;
|
|
989
|
+
right: 0;
|
|
990
|
+
bottom: 28px;
|
|
991
|
+
width: 280px;
|
|
992
|
+
background: var(--annot-bg-panel);
|
|
993
|
+
border-left: 1px solid var(--annot-border-color);
|
|
994
|
+
display: flex;
|
|
995
|
+
flex-direction: column;
|
|
996
|
+
z-index: 10;
|
|
997
|
+
overflow-y: auto;
|
|
998
|
+
}
|
|
999
|
+
body.editor-mode.has-right-panel #canvas-container {
|
|
1000
|
+
right: 280px; /* leave room for the right panel */
|
|
1001
|
+
}
|
|
1002
|
+
body.editor-mode.has-right-panel .file-details-drawer,
|
|
1003
|
+
body.editor-mode.has-right-panel .file-details-backdrop {
|
|
1004
|
+
/* The details drawer also lives on the right — let it slide over
|
|
1005
|
+
* the property panel so the user can still access file details
|
|
1006
|
+
* while a tool/selection is active. */
|
|
1007
|
+
z-index: 25;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
.editor-right-panel-section {
|
|
1011
|
+
/* Tightened from 14 px → 10 px vertical to densify the right
|
|
1012
|
+
* panel for the screenshot-annotation flow (Figma-class
|
|
1013
|
+
* density). Horizontal stays at 16 px so the level-2 column
|
|
1014
|
+
* alignment with `.prop-panel-docked` is preserved. */
|
|
1015
|
+
padding: 10px 16px;
|
|
1016
|
+
border-bottom: 1px solid var(--annot-border-subtle);
|
|
1017
|
+
}
|
|
1018
|
+
/* Dynamic section title (e.g. "Rectangle" / "Arrow" / "Callout").
|
|
1019
|
+
* Rendered as a readable, sentence-case heading rather than the
|
|
1020
|
+
* older "SECTION" uppercase pill — the title now names the element
|
|
1021
|
+
* the user is editing, so readability matters more than category
|
|
1022
|
+
* emphasis. Sentence case + slightly larger weight makes it read as
|
|
1023
|
+
* "this IS the thing" rather than "a section labeled …". */
|
|
1024
|
+
.editor-right-panel-section-title {
|
|
1025
|
+
/* Was 0 0 10px — tightened to 6 px so the section title binds
|
|
1026
|
+
* closer to the first level-2 sub-section header below it. */
|
|
1027
|
+
margin: 0 0 6px;
|
|
1028
|
+
font-size: 13px;
|
|
1029
|
+
font-weight: 600;
|
|
1030
|
+
color: var(--annot-text-primary);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/* Actions section — transform / arrange / align / group button
|
|
1034
|
+
* rows (rotate / flip / z-order / align / distribute / group /
|
|
1035
|
+
* ungroup). Shown only when selection is non-empty. Markup
|
|
1036
|
+
* mirrors the Selected card: the section's body wrapper is a
|
|
1037
|
+
* `.prop-panel.prop-panel-docked`, and each sub-group is a
|
|
1038
|
+
* `.pp-section` with a `.pp-section-header` label. That gives
|
|
1039
|
+
* Actions the same level-2 alignment as TYPE / FILL / LINE for
|
|
1040
|
+
* free, no Actions-specific overrides required. The only thing
|
|
1041
|
+
* still needing a custom class is the row container, where the
|
|
1042
|
+
* tight 2 px button gap (vs `.pp-section-body`'s 6 px) is
|
|
1043
|
+
* intentional for icon-button rows. */
|
|
1044
|
+
.editor-right-panel-actions-row {
|
|
1045
|
+
display: flex;
|
|
1046
|
+
gap: 2px;
|
|
1047
|
+
/* Indent the action row slightly so it visually hangs off the
|
|
1048
|
+
* `.pp-section-header` above, matching `.pp-section-body`'s 10
|
|
1049
|
+
* px indent in the property panel. */
|
|
1050
|
+
padding-left: 10px;
|
|
1051
|
+
}
|
|
1052
|
+
.editor-right-panel-actions-row:not(:last-child) {
|
|
1053
|
+
margin-bottom: 0;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/* Action buttons inherit the full 36×36 `.toolbar-btn` size + 20 px
|
|
1057
|
+
* font-size from the sidebar/toolbar rules so Icon weight matches
|
|
1058
|
+
* across surfaces. Panel is 208 px inner after padding; 4 buttons
|
|
1059
|
+
* × 36 + 3 × 2 gap = 150 px, leaving ~58 px margin — comfortably
|
|
1060
|
+
* fits without needing a compact-size override. */
|
|
1061
|
+
|
|
1062
|
+
/* Custom-SVG action buttons (flip / z-order) — SVG renders at 22 px
|
|
1063
|
+
* to match the optical weight of the 20 px Material-Symbols glyph
|
|
1064
|
+
* in a 36 px button. `currentColor` inside the SVG picks up the
|
|
1065
|
+
* button's color + hover / active states automatically. */
|
|
1066
|
+
.editor-right-panel-actions-row .action-btn-svg > svg {
|
|
1067
|
+
width: 22px;
|
|
1068
|
+
height: 22px;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/* Tooltips on action buttons sit INSIDE the 280 px right panel,
|
|
1072
|
+
* whose `overflow-y: auto` implicitly clips horizontal overflow too.
|
|
1073
|
+
* The default centered tooltip (left:50%, nowrap) extends past the
|
|
1074
|
+
* panel's left/right edges for the EXTREMITY buttons (leftmost
|
|
1075
|
+
* overflows left, rightmost overflows right) and gets clipped.
|
|
1076
|
+
*
|
|
1077
|
+
* Fix: keep the tooltip BELOW (matching the rest of the editor's
|
|
1078
|
+
* tooltip convention — all toolbar buttons drop downward), but
|
|
1079
|
+
* anchor to the INSIDE edge for the first / last buttons of each
|
|
1080
|
+
* row so the bubble always extends INTO the panel rather than out
|
|
1081
|
+
* of it. Middle buttons stay centered as usual. Multi-line wrapping
|
|
1082
|
+
* (`white-space: normal`) keeps the bubble narrow enough to fit the
|
|
1083
|
+
* column width. */
|
|
1084
|
+
.editor-right-panel-actions-row .toolbar-btn[data-tooltip]::after {
|
|
1085
|
+
bottom: auto;
|
|
1086
|
+
top: calc(100% + 6px);
|
|
1087
|
+
left: 50%;
|
|
1088
|
+
transform: translateX(-50%);
|
|
1089
|
+
max-width: 180px;
|
|
1090
|
+
white-space: normal;
|
|
1091
|
+
text-align: center;
|
|
1092
|
+
line-height: 1.3;
|
|
1093
|
+
}
|
|
1094
|
+
/* First button: align tooltip's LEFT edge to the button's left edge
|
|
1095
|
+
* so it extends rightward into the panel (never past panel-left). */
|
|
1096
|
+
.editor-right-panel-actions-row .toolbar-btn:first-child[data-tooltip]::after {
|
|
1097
|
+
left: 0;
|
|
1098
|
+
right: auto;
|
|
1099
|
+
transform: none;
|
|
1100
|
+
text-align: left;
|
|
1101
|
+
}
|
|
1102
|
+
/* Last button: mirror — align tooltip's RIGHT edge to the button's
|
|
1103
|
+
* right edge so it extends leftward (never past panel-right). */
|
|
1104
|
+
.editor-right-panel-actions-row .toolbar-btn:last-child[data-tooltip]::after {
|
|
1105
|
+
left: auto;
|
|
1106
|
+
right: 0;
|
|
1107
|
+
transform: none;
|
|
1108
|
+
text-align: right;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/* Apply-redactions-to-image button — labeled action surface in the
|
|
1112
|
+
* right-panel's apply-redactions section (`#renderApplyRedactions`
|
|
1113
|
+
* in `right-panel.ts`). Phase 3 of
|
|
1114
|
+
* `_done/redact-burn-into-image.md` initially reused the
|
|
1115
|
+
* `.toolbar-btn` class but that's a 36 × 36 icon-only chrome —
|
|
1116
|
+
* dropping a label inside overflows the box. This is the dedicated
|
|
1117
|
+
* labeled-button rule: full-width row, icon + label, opaque
|
|
1118
|
+
* destructive background so the user recognises it as the
|
|
1119
|
+
* irreversibility-confirming gesture. */
|
|
1120
|
+
.annot-apply-redactions-btn {
|
|
1121
|
+
display: flex;
|
|
1122
|
+
align-items: center;
|
|
1123
|
+
justify-content: center;
|
|
1124
|
+
gap: 8px;
|
|
1125
|
+
width: 100%;
|
|
1126
|
+
min-height: 36px;
|
|
1127
|
+
padding: 8px 12px;
|
|
1128
|
+
border: 1px solid var(--annot-border-color);
|
|
1129
|
+
border-radius: 8px;
|
|
1130
|
+
background: var(--annot-bg-elevated, var(--annot-bg-panel));
|
|
1131
|
+
color: var(--annot-text-primary);
|
|
1132
|
+
font-size: 13px;
|
|
1133
|
+
font-weight: 500;
|
|
1134
|
+
cursor: pointer;
|
|
1135
|
+
transition:
|
|
1136
|
+
background 0.1s,
|
|
1137
|
+
border-color 0.1s;
|
|
1138
|
+
}
|
|
1139
|
+
.annot-apply-redactions-btn:hover:not(:disabled) {
|
|
1140
|
+
background: var(--annot-hover-bg);
|
|
1141
|
+
border-color: var(--annot-hover-border);
|
|
1142
|
+
}
|
|
1143
|
+
.annot-apply-redactions-btn:focus-visible {
|
|
1144
|
+
outline: 2px solid var(--annot-focus-ring, var(--annot-accent));
|
|
1145
|
+
outline-offset: 2px;
|
|
1146
|
+
}
|
|
1147
|
+
.annot-apply-redactions-btn:disabled {
|
|
1148
|
+
opacity: 0.5;
|
|
1149
|
+
cursor: not-allowed;
|
|
1150
|
+
}
|
|
1151
|
+
.annot-apply-redactions-btn > annot-icon {
|
|
1152
|
+
font-size: 18px;
|
|
1153
|
+
flex-shrink: 0;
|
|
1154
|
+
}
|
|
1155
|
+
.annot-apply-redactions-btn > .annot-apply-redactions-label {
|
|
1156
|
+
flex: 1;
|
|
1157
|
+
min-width: 0;
|
|
1158
|
+
text-align: left;
|
|
1159
|
+
white-space: normal;
|
|
1160
|
+
/* Allow up to two lines for a graceful long-language wrap; tall
|
|
1161
|
+
* single-line containers (the common case) just stay one line. */
|
|
1162
|
+
line-height: 1.3;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
/* Empty-state shown when the panel has no active tool AND no
|
|
1166
|
+
* selection. Keeps the always-visible panel feeling intentional
|
|
1167
|
+
* rather than "broken / blank". */
|
|
1168
|
+
.editor-right-panel-empty {
|
|
1169
|
+
flex: 1;
|
|
1170
|
+
display: flex;
|
|
1171
|
+
flex-direction: column;
|
|
1172
|
+
align-items: center;
|
|
1173
|
+
justify-content: center;
|
|
1174
|
+
gap: 8px;
|
|
1175
|
+
padding: 48px 24px;
|
|
1176
|
+
text-align: center;
|
|
1177
|
+
color: var(--annot-text-muted);
|
|
1178
|
+
}
|
|
1179
|
+
.editor-right-panel-empty-icon {
|
|
1180
|
+
font-size: 36px;
|
|
1181
|
+
opacity: 0.4;
|
|
1182
|
+
font-variation-settings:
|
|
1183
|
+
"FILL" 0,
|
|
1184
|
+
"wght" 300,
|
|
1185
|
+
"GRAD" 0,
|
|
1186
|
+
"opsz" 40;
|
|
1187
|
+
}
|
|
1188
|
+
.editor-right-panel-empty-title {
|
|
1189
|
+
margin: 0;
|
|
1190
|
+
font-size: 13px;
|
|
1191
|
+
font-weight: 600;
|
|
1192
|
+
color: var(--annot-text-secondary);
|
|
1193
|
+
}
|
|
1194
|
+
.editor-right-panel-empty-hint {
|
|
1195
|
+
margin: 0;
|
|
1196
|
+
font-size: 12px;
|
|
1197
|
+
line-height: 1.5;
|
|
1198
|
+
max-width: 180px;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/* Scratchpad section — always-visible bottom area of the right panel.
|
|
1202
|
+
* Pinned to the bottom by margin-top:auto inside the flex-column
|
|
1203
|
+
* container so tool/selection content fills the top and scratchpad
|
|
1204
|
+
* stays anchored. Top border differentiates it as a distinct area. */
|
|
1205
|
+
.editor-right-panel-scratchpad {
|
|
1206
|
+
margin-top: auto;
|
|
1207
|
+
border-top: 1px solid var(--annot-border-color);
|
|
1208
|
+
border-bottom: none;
|
|
1209
|
+
background: var(--annot-bg-panel-deep);
|
|
1210
|
+
flex-shrink: 0;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
.scratchpad-section-header {
|
|
1214
|
+
display: flex;
|
|
1215
|
+
align-items: center;
|
|
1216
|
+
justify-content: space-between;
|
|
1217
|
+
margin-bottom: 10px;
|
|
1218
|
+
}
|
|
1219
|
+
.scratchpad-section-header .editor-right-panel-section-title {
|
|
1220
|
+
margin: 0;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
.scratchpad-save-btn {
|
|
1224
|
+
width: 22px;
|
|
1225
|
+
height: 22px;
|
|
1226
|
+
border: 1px solid var(--annot-border-color);
|
|
1227
|
+
border-radius: 4px;
|
|
1228
|
+
background: transparent;
|
|
1229
|
+
color: var(--annot-text-secondary);
|
|
1230
|
+
cursor: pointer;
|
|
1231
|
+
display: flex;
|
|
1232
|
+
align-items: center;
|
|
1233
|
+
justify-content: center;
|
|
1234
|
+
font-size: 14px;
|
|
1235
|
+
transition:
|
|
1236
|
+
background 0.15s,
|
|
1237
|
+
border-color 0.15s,
|
|
1238
|
+
color 0.15s;
|
|
1239
|
+
}
|
|
1240
|
+
.scratchpad-save-btn:not(:disabled):hover {
|
|
1241
|
+
background: var(--annot-accent-bg);
|
|
1242
|
+
border-color: var(--annot-accent);
|
|
1243
|
+
color: var(--annot-accent);
|
|
1244
|
+
}
|
|
1245
|
+
.scratchpad-save-btn:disabled {
|
|
1246
|
+
opacity: 0.35;
|
|
1247
|
+
cursor: not-allowed;
|
|
1248
|
+
}
|
|
1249
|
+
.scratchpad-save-btn annot-icon {
|
|
1250
|
+
font-size: 16px;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
.scratchpad-grid {
|
|
1254
|
+
display: grid;
|
|
1255
|
+
grid-template-columns: repeat(auto-fill, minmax(48px, 1fr));
|
|
1256
|
+
gap: 6px;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
.scratchpad-item {
|
|
1260
|
+
position: relative;
|
|
1261
|
+
aspect-ratio: 1 / 1;
|
|
1262
|
+
background: var(--annot-canvas-bg, #0b1020);
|
|
1263
|
+
border: 1px solid var(--annot-border-subtle);
|
|
1264
|
+
border-radius: 6px;
|
|
1265
|
+
cursor: pointer;
|
|
1266
|
+
overflow: hidden;
|
|
1267
|
+
display: flex;
|
|
1268
|
+
align-items: center;
|
|
1269
|
+
justify-content: center;
|
|
1270
|
+
transition:
|
|
1271
|
+
border-color 0.15s,
|
|
1272
|
+
transform 0.15s;
|
|
1273
|
+
}
|
|
1274
|
+
.scratchpad-item:hover {
|
|
1275
|
+
border-color: var(--annot-accent);
|
|
1276
|
+
transform: translateY(-1px);
|
|
1277
|
+
}
|
|
1278
|
+
/* Armed state — the user clicked this item and the paste tool is
|
|
1279
|
+
* waiting for a canvas click. Stronger accent so the "which item
|
|
1280
|
+
* will land next?" question is answered at a glance. */
|
|
1281
|
+
.scratchpad-item.active {
|
|
1282
|
+
border-color: var(--annot-accent);
|
|
1283
|
+
background: var(--annot-accent-bg);
|
|
1284
|
+
box-shadow: 0 0 0 1px var(--annot-accent) inset;
|
|
1285
|
+
}
|
|
1286
|
+
.scratchpad-item-thumb {
|
|
1287
|
+
max-width: 80%;
|
|
1288
|
+
max-height: 80%;
|
|
1289
|
+
object-fit: contain;
|
|
1290
|
+
pointer-events: none;
|
|
1291
|
+
user-select: none;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
.scratchpad-item-delete {
|
|
1295
|
+
position: absolute;
|
|
1296
|
+
top: 2px;
|
|
1297
|
+
right: 2px;
|
|
1298
|
+
width: 16px;
|
|
1299
|
+
height: 16px;
|
|
1300
|
+
border: none;
|
|
1301
|
+
border-radius: 50%;
|
|
1302
|
+
background: rgba(0, 0, 0, 0.6);
|
|
1303
|
+
color: #fff;
|
|
1304
|
+
font-size: 12px;
|
|
1305
|
+
display: flex;
|
|
1306
|
+
align-items: center;
|
|
1307
|
+
justify-content: center;
|
|
1308
|
+
cursor: pointer;
|
|
1309
|
+
opacity: 0;
|
|
1310
|
+
transition: opacity 0.15s;
|
|
1311
|
+
font-variation-settings:
|
|
1312
|
+
"FILL" 0,
|
|
1313
|
+
"wght" 500,
|
|
1314
|
+
"GRAD" 0,
|
|
1315
|
+
"opsz" 20;
|
|
1316
|
+
}
|
|
1317
|
+
.scratchpad-item:hover .scratchpad-item-delete {
|
|
1318
|
+
opacity: 1;
|
|
1319
|
+
}
|
|
1320
|
+
.scratchpad-item-delete:hover {
|
|
1321
|
+
background: var(--danger, #ff7070);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
.scratchpad-empty {
|
|
1325
|
+
margin: 0;
|
|
1326
|
+
font-size: 12px;
|
|
1327
|
+
color: var(--annot-text-muted);
|
|
1328
|
+
line-height: 1.5;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
/* ---- Save status indicator ----
|
|
1332
|
+
*
|
|
1333
|
+
* Sits between the filename and the right-edge help/theme actions in the
|
|
1334
|
+
* editor header. Reports the save lifecycle without blocking; matches
|
|
1335
|
+
* the Google Docs / Notion convention of a small, muted indicator that
|
|
1336
|
+
* briefly animates while writing.
|
|
1337
|
+
*/
|
|
1338
|
+
/* Padding matches .breadcrumb-item (2px 6px) so vertical rhythm aligns
|
|
1339
|
+
* with the filename segment it sits next to. Icon kept at 14px to keep
|
|
1340
|
+
* the indicator visually "inline metadata" rather than a chunky chip. */
|
|
1341
|
+
.save-status {
|
|
1342
|
+
display: inline-flex;
|
|
1343
|
+
align-items: center;
|
|
1344
|
+
gap: 4px;
|
|
1345
|
+
padding: 2px 8px;
|
|
1346
|
+
border-radius: 6px;
|
|
1347
|
+
font-size: 12px;
|
|
1348
|
+
font-weight: 500;
|
|
1349
|
+
line-height: 1;
|
|
1350
|
+
white-space: nowrap;
|
|
1351
|
+
user-select: none;
|
|
1352
|
+
flex-shrink: 0; /* stays intact next to the filename */
|
|
1353
|
+
color: var(--annot-text-muted);
|
|
1354
|
+
transition:
|
|
1355
|
+
color 0.15s,
|
|
1356
|
+
background 0.15s;
|
|
1357
|
+
}
|
|
1358
|
+
.save-status-icon {
|
|
1359
|
+
font-size: 14px;
|
|
1360
|
+
font-variation-settings:
|
|
1361
|
+
"FILL" 0,
|
|
1362
|
+
"wght" 400,
|
|
1363
|
+
"GRAD" 0,
|
|
1364
|
+
"opsz" 20;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
/* Saved — neutral, at rest */
|
|
1368
|
+
.save-status.save-status-saved {
|
|
1369
|
+
color: var(--annot-text-muted);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
/* Edited — slightly brighter than saved, signals "something's queued" */
|
|
1373
|
+
.save-status.save-status-pending {
|
|
1374
|
+
color: var(--annot-text-secondary);
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
/* Saving — accent color + spinning icon */
|
|
1378
|
+
.save-status.save-status-saving {
|
|
1379
|
+
color: var(--annot-accent);
|
|
1380
|
+
}
|
|
1381
|
+
.save-status.save-status-saving .save-status-icon {
|
|
1382
|
+
animation: save-status-spin 1s linear infinite;
|
|
1383
|
+
}
|
|
1384
|
+
@keyframes save-status-spin {
|
|
1385
|
+
from {
|
|
1386
|
+
transform: rotate(0deg);
|
|
1387
|
+
}
|
|
1388
|
+
to {
|
|
1389
|
+
transform: rotate(360deg);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
/* Error — danger color, stays visible until next save attempt */
|
|
1394
|
+
.save-status.save-status-error {
|
|
1395
|
+
color: var(--danger, #ff7070);
|
|
1396
|
+
background: rgba(255, 112, 112, 0.1);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
/* The descendant rule `.editor-header-path .editor-header-info-btn`
|
|
1400
|
+
* earlier in the file intentionally has higher specificity — it's a
|
|
1401
|
+
* contextual tweak for buttons inside the breadcrumb path. Keeping
|
|
1402
|
+
* the base class here with a documented suppression is cleaner than
|
|
1403
|
+
* fragmenting the "editor header info button" styling across two
|
|
1404
|
+
* sections just to satisfy the linter. */
|
|
1405
|
+
/* biome-ignore lint/style/noDescendingSpecificity: base rule deliberately follows contextual .editor-header-path descendant */
|
|
1406
|
+
.editor-header-info-btn {
|
|
1407
|
+
width: 28px;
|
|
1408
|
+
height: 28px;
|
|
1409
|
+
border: none;
|
|
1410
|
+
background: transparent;
|
|
1411
|
+
color: var(--annot-text-muted);
|
|
1412
|
+
border-radius: 4px;
|
|
1413
|
+
cursor: pointer;
|
|
1414
|
+
font-size: 18px;
|
|
1415
|
+
font-variation-settings:
|
|
1416
|
+
"FILL" 0,
|
|
1417
|
+
"wght" 400,
|
|
1418
|
+
"GRAD" 0,
|
|
1419
|
+
"opsz" 20;
|
|
1420
|
+
display: flex;
|
|
1421
|
+
align-items: center;
|
|
1422
|
+
justify-content: center;
|
|
1423
|
+
flex-shrink: 0;
|
|
1424
|
+
transition:
|
|
1425
|
+
background 0.15s,
|
|
1426
|
+
color 0.15s;
|
|
1427
|
+
}
|
|
1428
|
+
.editor-header-info-btn:hover {
|
|
1429
|
+
background: var(--annot-hover-bg);
|
|
1430
|
+
color: var(--annot-accent);
|
|
1431
|
+
}
|
|
1432
|
+
.editor-header-info-btn:focus-visible {
|
|
1433
|
+
outline: 2px solid var(--annot-accent);
|
|
1434
|
+
outline-offset: 2px;
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
/* ---- Breadcrumb ---- */
|
|
1438
|
+
|
|
1439
|
+
.breadcrumb {
|
|
1440
|
+
display: flex;
|
|
1441
|
+
align-items: center;
|
|
1442
|
+
gap: 2px;
|
|
1443
|
+
overflow: hidden;
|
|
1444
|
+
white-space: nowrap;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/* Breadcrumb items may be either <button> (clickable ancestors) or
|
|
1448
|
+
* <span> (the non-clickable filename segment). Without the font resets
|
|
1449
|
+
* below, <button> falls back to the UA system font while <span>
|
|
1450
|
+
* inherits Sora — the two have different metrics and the filename
|
|
1451
|
+
* appears visually lower than sibling clickable segments.
|
|
1452
|
+
*
|
|
1453
|
+
* `font: inherit` pulls the parent's full font specification into the
|
|
1454
|
+
* button, then we override the few properties we actually want to pin
|
|
1455
|
+
* (size, weight). Setting `display: inline-flex + align-items: center`
|
|
1456
|
+
* also normalizes vertical alignment between span and button so the
|
|
1457
|
+
* glyph baselines line up across element types.
|
|
1458
|
+
*
|
|
1459
|
+
* Same pattern as `.editor-header-info-btn` above: the descendant
|
|
1460
|
+
* rule `.editor-header-path .breadcrumb-item` earlier in the file
|
|
1461
|
+
* is a contextual override, and the base rule lives here with the
|
|
1462
|
+
* rest of the breadcrumb section. */
|
|
1463
|
+
/* biome-ignore lint/style/noDescendingSpecificity: base rule deliberately follows contextual .editor-header-path descendant */
|
|
1464
|
+
.breadcrumb-item {
|
|
1465
|
+
font: inherit;
|
|
1466
|
+
font-size: 13px;
|
|
1467
|
+
font-weight: 400;
|
|
1468
|
+
line-height: 1.4;
|
|
1469
|
+
display: inline-flex;
|
|
1470
|
+
align-items: center;
|
|
1471
|
+
border: none;
|
|
1472
|
+
background: none;
|
|
1473
|
+
color: var(--annot-text-secondary);
|
|
1474
|
+
cursor: pointer;
|
|
1475
|
+
padding: 2px 6px;
|
|
1476
|
+
border-radius: 4px;
|
|
1477
|
+
white-space: nowrap;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
.breadcrumb-item:hover {
|
|
1481
|
+
background: var(--annot-hover-bg);
|
|
1482
|
+
color: var(--annot-accent);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
.breadcrumb-item.active {
|
|
1486
|
+
color: var(--annot-text-primary);
|
|
1487
|
+
font-weight: 600;
|
|
1488
|
+
cursor: default;
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
.breadcrumb-item.active:hover {
|
|
1492
|
+
background: none;
|
|
1493
|
+
color: var(--annot-text-primary);
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
.breadcrumb-sep {
|
|
1497
|
+
color: var(--annot-text-muted);
|
|
1498
|
+
font-size: 16px;
|
|
1499
|
+
user-select: none;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
/* ---- Folder cards ---- */
|
|
1503
|
+
|
|
1504
|
+
.gallery-folder {
|
|
1505
|
+
border-style: dashed;
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
.gallery-folder:hover {
|
|
1509
|
+
border-style: solid;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
.gallery-folder-icon {
|
|
1513
|
+
display: flex;
|
|
1514
|
+
align-items: center;
|
|
1515
|
+
justify-content: center;
|
|
1516
|
+
background: var(--annot-bg-secondary);
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
.gallery-folder-name {
|
|
1520
|
+
font-weight: 600;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
/* ---- Gallery panel ---- */
|
|
1524
|
+
|
|
1525
|
+
/* #gallery-container removed — gallery now renders inside #file-manager > #main-content */
|
|
1526
|
+
|
|
1527
|
+
.gallery-panel {
|
|
1528
|
+
/* Block display is required because `.gallery-panel` is now applied
|
|
1529
|
+
* to `<annot-gallery-page>` (a custom element, defaults to inline);
|
|
1530
|
+
* without this the horizontal padding collapses around the block
|
|
1531
|
+
* `.gallery-grid` child and the left/right gutter disappears. */
|
|
1532
|
+
display: block;
|
|
1533
|
+
padding: 16px 24px;
|
|
1534
|
+
/* Fill the available main-content area — let the gallery grid wrap
|
|
1535
|
+
* naturally instead of clamping to a fixed max-width. */
|
|
1536
|
+
margin: 0 auto;
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
.gallery-grid {
|
|
1540
|
+
display: grid;
|
|
1541
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
1542
|
+
gap: 16px;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
.gallery-empty {
|
|
1546
|
+
grid-column: 1 / -1;
|
|
1547
|
+
text-align: center;
|
|
1548
|
+
padding: 60px 20px;
|
|
1549
|
+
color: var(--annot-text-muted);
|
|
1550
|
+
font-size: 14px;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
.gallery-item {
|
|
1554
|
+
border: 1px solid var(--annot-border-color);
|
|
1555
|
+
border-radius: 8px;
|
|
1556
|
+
overflow: hidden;
|
|
1557
|
+
background: var(--annot-bg-panel);
|
|
1558
|
+
cursor: pointer;
|
|
1559
|
+
transition:
|
|
1560
|
+
border-color 0.15s,
|
|
1561
|
+
transform 0.15s;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
.gallery-item:hover {
|
|
1565
|
+
border-color: var(--annot-accent);
|
|
1566
|
+
transform: translateY(-2px);
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
.gallery-thumb {
|
|
1570
|
+
aspect-ratio: 16 / 9;
|
|
1571
|
+
overflow: hidden;
|
|
1572
|
+
background: #111;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
.gallery-thumb img {
|
|
1576
|
+
width: 100%;
|
|
1577
|
+
height: 100%;
|
|
1578
|
+
/* Thumbnails are pre-rendered at 16:9 with letterboxing / top-crop
|
|
1579
|
+
* baked in, so the card just displays them 1:1. `contain` prevents
|
|
1580
|
+
* any further browser-side upscaling and keeps short images crisp. */
|
|
1581
|
+
object-fit: contain;
|
|
1582
|
+
object-position: center;
|
|
1583
|
+
display: block;
|
|
1584
|
+
/* Progressive enhancement: browsers that don't recognise `high-
|
|
1585
|
+
* quality` fall back to `-webkit-optimize-contrast` or the plain
|
|
1586
|
+
* `auto` declaration earlier in the stack. This is a deliberate
|
|
1587
|
+
* cross-browser pattern, not a typo. */
|
|
1588
|
+
/* biome-ignore-start lint/suspicious/noDuplicateProperties: deliberate CSS fallback stack */
|
|
1589
|
+
image-rendering: auto;
|
|
1590
|
+
image-rendering: -webkit-optimize-contrast;
|
|
1591
|
+
image-rendering: high-quality;
|
|
1592
|
+
/* biome-ignore-end lint/suspicious/noDuplicateProperties: deliberate CSS fallback stack */
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
/* Document-card fallback when `thumbnailDataUrl` is empty —
|
|
1596
|
+
* Phase 11 of `annot-html-document.md` shipped doc cards but
|
|
1597
|
+
* with no thumb-generation path, so the default `.gallery-thumb`
|
|
1598
|
+
* `background:#111` showed through as a black rectangle. The
|
|
1599
|
+
* `<annot-icon spec="article">` fallback rendered into the top-
|
|
1600
|
+
* left corner without sizing because nothing centered it. The
|
|
1601
|
+
* rules below: center the icon, give it a readable size, swap
|
|
1602
|
+
* the dark background for a friendlier panel-coloured tile so
|
|
1603
|
+
* docs feel like papers / preview tiles rather than missing
|
|
1604
|
+
* media. Documents with image blocks override this via the
|
|
1605
|
+
* pre-rendered `<img>` (sibling `.gallery-thumb img` rule
|
|
1606
|
+
* above). */
|
|
1607
|
+
.gallery-document-item .gallery-thumb {
|
|
1608
|
+
background: var(--annot-bg-panel, #2a2a2a);
|
|
1609
|
+
display: flex;
|
|
1610
|
+
align-items: center;
|
|
1611
|
+
justify-content: center;
|
|
1612
|
+
border: 1px solid var(--annot-border-color, transparent);
|
|
1613
|
+
}
|
|
1614
|
+
.gallery-document-item .gallery-thumb annot-icon {
|
|
1615
|
+
width: 56px;
|
|
1616
|
+
height: 56px;
|
|
1617
|
+
color: var(--annot-text-secondary, #9ca3af);
|
|
1618
|
+
}
|
|
1619
|
+
.gallery-document-item .gallery-thumb annot-icon svg {
|
|
1620
|
+
width: 100%;
|
|
1621
|
+
height: 100%;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
.gallery-item-info {
|
|
1625
|
+
padding: 8px 10px;
|
|
1626
|
+
position: relative;
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
.gallery-item-name {
|
|
1630
|
+
font-size: 12px;
|
|
1631
|
+
color: var(--annot-text-primary);
|
|
1632
|
+
white-space: nowrap;
|
|
1633
|
+
overflow: hidden;
|
|
1634
|
+
text-overflow: ellipsis;
|
|
1635
|
+
padding-right: 20px;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
.gallery-item-meta {
|
|
1639
|
+
font-size: 11px;
|
|
1640
|
+
color: var(--annot-text-muted);
|
|
1641
|
+
margin-top: 2px;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
.gallery-item-delete {
|
|
1645
|
+
position: absolute;
|
|
1646
|
+
top: 6px;
|
|
1647
|
+
right: 6px;
|
|
1648
|
+
width: 20px;
|
|
1649
|
+
height: 20px;
|
|
1650
|
+
border: none;
|
|
1651
|
+
border-radius: 50%;
|
|
1652
|
+
background: rgba(200, 0, 0, 0.6);
|
|
1653
|
+
color: #fff;
|
|
1654
|
+
font-size: 14px;
|
|
1655
|
+
line-height: 20px;
|
|
1656
|
+
text-align: center;
|
|
1657
|
+
cursor: pointer;
|
|
1658
|
+
opacity: 0;
|
|
1659
|
+
transition: opacity 0.15s;
|
|
1660
|
+
padding: 0;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
.gallery-item:hover .gallery-item-delete {
|
|
1664
|
+
opacity: 1;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
.gallery-item-delete:hover {
|
|
1668
|
+
background: rgba(220, 0, 0, 0.9);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
/* ---- Selection checkbox ---- */
|
|
1672
|
+
|
|
1673
|
+
.gallery-checkbox {
|
|
1674
|
+
position: absolute;
|
|
1675
|
+
top: 6px;
|
|
1676
|
+
left: 6px;
|
|
1677
|
+
width: 24px;
|
|
1678
|
+
height: 24px;
|
|
1679
|
+
display: flex;
|
|
1680
|
+
align-items: center;
|
|
1681
|
+
justify-content: center;
|
|
1682
|
+
border-radius: 50%;
|
|
1683
|
+
background: rgba(0, 0, 0, 0.4);
|
|
1684
|
+
color: #888;
|
|
1685
|
+
cursor: pointer;
|
|
1686
|
+
opacity: 0;
|
|
1687
|
+
transition: opacity 0.15s;
|
|
1688
|
+
z-index: 2;
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
.gallery-checkbox annot-icon {
|
|
1692
|
+
font-size: 18px;
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
.gallery-item:hover .gallery-checkbox,
|
|
1696
|
+
.gallery-item.selected .gallery-checkbox {
|
|
1697
|
+
opacity: 1;
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
.gallery-item.selected .gallery-checkbox {
|
|
1701
|
+
background: var(--annot-accent);
|
|
1702
|
+
color: #000;
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
.gallery-item.selected {
|
|
1706
|
+
border-color: var(--annot-accent);
|
|
1707
|
+
box-shadow: 0 0 0 2px var(--annot-accent);
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
.gallery-item {
|
|
1711
|
+
position: relative;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
/* ---- Selection action bar ---- */
|
|
1715
|
+
|
|
1716
|
+
.gallery-action-bar {
|
|
1717
|
+
display: flex;
|
|
1718
|
+
align-items: center;
|
|
1719
|
+
gap: 8px;
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
.gallery-footer-text {
|
|
1723
|
+
font-size: 12px;
|
|
1724
|
+
color: var(--annot-text-secondary);
|
|
1725
|
+
white-space: nowrap;
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
.gallery-action-btn {
|
|
1729
|
+
border: 1px solid var(--annot-border-color);
|
|
1730
|
+
background: var(--annot-bg-panel);
|
|
1731
|
+
color: var(--annot-text-primary);
|
|
1732
|
+
font-size: 12px;
|
|
1733
|
+
padding: 2px 10px;
|
|
1734
|
+
border-radius: 4px;
|
|
1735
|
+
cursor: pointer;
|
|
1736
|
+
white-space: nowrap;
|
|
1737
|
+
display: flex;
|
|
1738
|
+
align-items: center;
|
|
1739
|
+
gap: 3px;
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
.gallery-action-btn:hover {
|
|
1743
|
+
border-color: var(--annot-accent);
|
|
1744
|
+
color: var(--annot-accent);
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
.gallery-action-danger:hover {
|
|
1748
|
+
border-color: #e44;
|
|
1749
|
+
color: #e44;
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
/* ---- Folder picker dialog ---- */
|
|
1753
|
+
|
|
1754
|
+
.folder-picker-overlay {
|
|
1755
|
+
position: fixed;
|
|
1756
|
+
inset: 0;
|
|
1757
|
+
background: rgba(0, 0, 0, 0.5);
|
|
1758
|
+
display: flex;
|
|
1759
|
+
align-items: center;
|
|
1760
|
+
justify-content: center;
|
|
1761
|
+
z-index: 1000;
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
.folder-picker-dialog {
|
|
1765
|
+
background: var(--annot-bg-panel);
|
|
1766
|
+
border: 1px solid var(--annot-border-color);
|
|
1767
|
+
border-radius: 10px;
|
|
1768
|
+
padding: 16px;
|
|
1769
|
+
min-width: 300px;
|
|
1770
|
+
max-width: 450px;
|
|
1771
|
+
max-height: 60vh;
|
|
1772
|
+
display: flex;
|
|
1773
|
+
flex-direction: column;
|
|
1774
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
.folder-picker-title {
|
|
1778
|
+
font-size: 15px;
|
|
1779
|
+
font-weight: 600;
|
|
1780
|
+
color: var(--annot-text-primary);
|
|
1781
|
+
margin-bottom: 12px;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
.folder-picker-list {
|
|
1785
|
+
overflow-y: auto;
|
|
1786
|
+
flex: 1;
|
|
1787
|
+
display: flex;
|
|
1788
|
+
flex-direction: column;
|
|
1789
|
+
gap: 2px;
|
|
1790
|
+
margin-bottom: 12px;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
.folder-picker-item {
|
|
1794
|
+
display: block;
|
|
1795
|
+
width: 100%;
|
|
1796
|
+
padding: 8px 12px;
|
|
1797
|
+
border: none;
|
|
1798
|
+
border-radius: 6px;
|
|
1799
|
+
background: transparent;
|
|
1800
|
+
color: var(--annot-text-primary);
|
|
1801
|
+
font-size: 13px;
|
|
1802
|
+
text-align: left;
|
|
1803
|
+
cursor: pointer;
|
|
1804
|
+
white-space: nowrap;
|
|
1805
|
+
overflow: hidden;
|
|
1806
|
+
text-overflow: ellipsis;
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
.folder-picker-item:hover {
|
|
1810
|
+
background: var(--annot-choice-hover);
|
|
1811
|
+
color: var(--annot-accent);
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
.folder-picker-cancel {
|
|
1815
|
+
align-self: flex-end;
|
|
1816
|
+
border: 1px solid var(--annot-border-color);
|
|
1817
|
+
background: transparent;
|
|
1818
|
+
color: var(--annot-text-secondary);
|
|
1819
|
+
font-size: 13px;
|
|
1820
|
+
padding: 6px 16px;
|
|
1821
|
+
border-radius: 6px;
|
|
1822
|
+
cursor: pointer;
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
.folder-picker-cancel:hover {
|
|
1826
|
+
border-color: var(--annot-accent);
|
|
1827
|
+
color: var(--annot-accent);
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
/* =============================================================================
|
|
1831
|
+
* Elements panel — sidebar list of detected DOM elements (button /
|
|
1832
|
+
* link / input / heading / etc.) for a screenshot captured by the
|
|
1833
|
+
* browser extension. Rows show icon + label + sub-type. Hover →
|
|
1834
|
+
* highlight on canvas; click → insert a rectangle annotation.
|
|
1835
|
+
* Section is hidden entirely when the current image has no metadata.
|
|
1836
|
+
* ============================================================================= */
|
|
1837
|
+
.editor-right-panel-elements {
|
|
1838
|
+
/* Inherits .editor-right-panel-section padding/border. */
|
|
1839
|
+
}
|
|
1840
|
+
.editor-right-panel-elements-hint {
|
|
1841
|
+
margin: 0 0 8px 0;
|
|
1842
|
+
font-size: 11px;
|
|
1843
|
+
color: var(--annot-text-muted);
|
|
1844
|
+
}
|
|
1845
|
+
.editor-right-panel-elements-search {
|
|
1846
|
+
width: 100%;
|
|
1847
|
+
height: 28px;
|
|
1848
|
+
padding: 0 8px;
|
|
1849
|
+
border: 1px solid var(--annot-border-color);
|
|
1850
|
+
border-radius: 6px;
|
|
1851
|
+
background: var(--annot-bg-panel-deep);
|
|
1852
|
+
color: var(--annot-text-primary);
|
|
1853
|
+
font-size: 12px;
|
|
1854
|
+
margin-bottom: 8px;
|
|
1855
|
+
box-sizing: border-box;
|
|
1856
|
+
}
|
|
1857
|
+
.editor-right-panel-elements-search:focus {
|
|
1858
|
+
outline: none;
|
|
1859
|
+
border-color: var(--annot-accent);
|
|
1860
|
+
}
|
|
1861
|
+
.editor-right-panel-elements-list {
|
|
1862
|
+
display: flex;
|
|
1863
|
+
flex-direction: column;
|
|
1864
|
+
gap: 1px;
|
|
1865
|
+
max-height: 320px;
|
|
1866
|
+
overflow-y: auto;
|
|
1867
|
+
/* Negative margin pulls the list to the section padding edge so
|
|
1868
|
+
* scroll bar doesn't waste horizontal space. */
|
|
1869
|
+
margin: 0 -4px;
|
|
1870
|
+
}
|
|
1871
|
+
.editor-right-panel-element-row {
|
|
1872
|
+
display: flex;
|
|
1873
|
+
align-items: center;
|
|
1874
|
+
gap: 8px;
|
|
1875
|
+
padding: 6px 8px;
|
|
1876
|
+
border: none;
|
|
1877
|
+
background: transparent;
|
|
1878
|
+
border-radius: 4px;
|
|
1879
|
+
cursor: pointer;
|
|
1880
|
+
text-align: left;
|
|
1881
|
+
font-size: 12px;
|
|
1882
|
+
color: var(--annot-text-primary);
|
|
1883
|
+
transition: background 0.1s;
|
|
1884
|
+
width: 100%;
|
|
1885
|
+
}
|
|
1886
|
+
.editor-right-panel-element-row:hover {
|
|
1887
|
+
background: var(--annot-hover-bg);
|
|
1888
|
+
}
|
|
1889
|
+
.editor-right-panel-element-icon {
|
|
1890
|
+
font-size: 16px;
|
|
1891
|
+
color: var(--annot-text-secondary);
|
|
1892
|
+
flex-shrink: 0;
|
|
1893
|
+
}
|
|
1894
|
+
.editor-right-panel-element-label {
|
|
1895
|
+
flex: 1;
|
|
1896
|
+
overflow: hidden;
|
|
1897
|
+
text-overflow: ellipsis;
|
|
1898
|
+
white-space: nowrap;
|
|
1899
|
+
min-width: 0;
|
|
1900
|
+
}
|
|
1901
|
+
.editor-right-panel-element-sub {
|
|
1902
|
+
font-size: 10px;
|
|
1903
|
+
color: var(--annot-text-muted);
|
|
1904
|
+
text-transform: lowercase;
|
|
1905
|
+
flex-shrink: 0;
|
|
1906
|
+
}
|
|
1907
|
+
.editor-right-panel-elements-empty {
|
|
1908
|
+
padding: 12px 4px;
|
|
1909
|
+
font-size: 11px;
|
|
1910
|
+
color: var(--annot-text-muted);
|
|
1911
|
+
text-align: center;
|
|
1912
|
+
}
|