@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,673 @@
|
|
|
1
|
+
#toolbar {
|
|
2
|
+
position: absolute;
|
|
3
|
+
top: 0;
|
|
4
|
+
left: 0;
|
|
5
|
+
right: 0;
|
|
6
|
+
height: 48px;
|
|
7
|
+
background: var(--annot-bg-panel);
|
|
8
|
+
border-bottom: 1px solid var(--annot-border-color);
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
padding: 0 8px;
|
|
12
|
+
gap: 4px;
|
|
13
|
+
z-index: 10;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.toolbar-group {
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
gap: 2px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.toolbar-separator {
|
|
23
|
+
width: 1px;
|
|
24
|
+
height: 28px;
|
|
25
|
+
background: var(--annot-border-color);
|
|
26
|
+
margin: 0 6px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.toolbar-btn {
|
|
30
|
+
width: 36px;
|
|
31
|
+
height: 36px;
|
|
32
|
+
border: 1px solid transparent;
|
|
33
|
+
border-radius: 6px;
|
|
34
|
+
background: transparent;
|
|
35
|
+
color: var(--annot-text-primary);
|
|
36
|
+
font-size: 20px;
|
|
37
|
+
cursor: pointer;
|
|
38
|
+
font-variation-settings:
|
|
39
|
+
"FILL" 0,
|
|
40
|
+
"wght" 300,
|
|
41
|
+
"GRAD" 0,
|
|
42
|
+
"opsz" 20;
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: center;
|
|
46
|
+
transition:
|
|
47
|
+
background 0.1s,
|
|
48
|
+
border-color 0.1s;
|
|
49
|
+
position: relative;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.toolbar-btn:hover {
|
|
53
|
+
background: var(--annot-hover-bg);
|
|
54
|
+
border-color: var(--annot-hover-border);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.toolbar-btn.active {
|
|
58
|
+
background: var(--annot-active-bg);
|
|
59
|
+
border-color: var(--annot-active-border);
|
|
60
|
+
color: var(--annot-accent);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Custom CSS tooltip — generic for any element with data-tooltip.
|
|
64
|
+
*
|
|
65
|
+
* Uses `[data-tooltip]` (NOT `[title]`) as the source attribute so
|
|
66
|
+
* the browser doesn't show a DUPLICATE native tooltip on top of our
|
|
67
|
+
* custom one. `title` triggers the OS's built-in yellow bubble
|
|
68
|
+
* (Windows) / gray bubble (macOS), which is styled differently per
|
|
69
|
+
* platform, can't be themed, and appears with a fixed ~500 ms delay.
|
|
70
|
+
* Modern SaaS tools (Figma, Notion, Linear, Miro, VS Code, etc.) all
|
|
71
|
+
* skip `title` and use a custom tooltip like this one.
|
|
72
|
+
*
|
|
73
|
+
* Accessibility: `aria-label` is set in parallel with `data-tooltip`
|
|
74
|
+
* (via the `setTooltip()` helper in utils/tooltip.ts), so screen
|
|
75
|
+
* readers still announce the control's name.
|
|
76
|
+
*
|
|
77
|
+
* Scope: any element with `data-tooltip`, NOT scoped to specific
|
|
78
|
+
* button classes — otherwise every new button or tooltip-bearing
|
|
79
|
+
* element requires adding its class to this selector. A generic
|
|
80
|
+
* rule means `setTooltip(anyEl, "…")` Just Works.
|
|
81
|
+
*
|
|
82
|
+
* `bottom: -40px` — wide enough to clear the extended body of an
|
|
83
|
+
* accessibility-enlarged mouse cursor. Default OS cursors (~16×20 px)
|
|
84
|
+
* don't need this much offset, but Windows' "Large" / "Extra Large"
|
|
85
|
+
* pointer presets (32–64 px) extend down-right from the tip and would
|
|
86
|
+
* obscure a closer tooltip. 40 px clears the "Large" preset. */
|
|
87
|
+
|
|
88
|
+
/* Positioning context for the ::after pseudo. `:where()` keeps the
|
|
89
|
+
* specificity at zero so consuming elements can still override
|
|
90
|
+
* `position` (e.g. a table cell that needs `position: static` for
|
|
91
|
+
* its own layout) without `!important`. Most elements that get
|
|
92
|
+
* data-tooltip are buttons/chips that are fine with relative. */
|
|
93
|
+
:where([data-tooltip]) {
|
|
94
|
+
position: relative;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
[data-tooltip]::after {
|
|
98
|
+
content: attr(data-tooltip);
|
|
99
|
+
position: absolute;
|
|
100
|
+
bottom: -40px;
|
|
101
|
+
left: 50%;
|
|
102
|
+
transform: translateX(-50%);
|
|
103
|
+
background: var(--annot-bg-panel-deep);
|
|
104
|
+
color: var(--annot-text-primary);
|
|
105
|
+
/* Force a plain system font. Without this, buttons that carry the
|
|
106
|
+
* `material-symbols-outlined` class would make the tooltip's text
|
|
107
|
+
* try to render as Material Symbols ligatures (tooltip text like
|
|
108
|
+
* "Help" isn't a valid ligature, so the browser falls back to
|
|
109
|
+
* whatever glyph coverage the font has — which looks weird /
|
|
110
|
+
* unstyled). */
|
|
111
|
+
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
112
|
+
font-variation-settings: normal;
|
|
113
|
+
font-weight: 400;
|
|
114
|
+
font-size: 11px;
|
|
115
|
+
line-height: 1.2;
|
|
116
|
+
padding: 3px 8px;
|
|
117
|
+
border-radius: 4px;
|
|
118
|
+
border: 1px solid var(--annot-border-color);
|
|
119
|
+
white-space: nowrap;
|
|
120
|
+
pointer-events: none;
|
|
121
|
+
opacity: 0;
|
|
122
|
+
transition: opacity 0.15s;
|
|
123
|
+
z-index: 100;
|
|
124
|
+
/* Subtle shadow so the bubble reads against the canvas background
|
|
125
|
+
* (which may be the screenshot itself, not a theme-panel color). */
|
|
126
|
+
box-shadow: 0 2px 6px var(--annot-shadow, rgba(0, 0, 0, 0.15));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
[data-tooltip]:hover::after {
|
|
130
|
+
opacity: 1;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.toolbar-input {
|
|
134
|
+
height: 28px;
|
|
135
|
+
border: 1px solid var(--annot-input-border);
|
|
136
|
+
border-radius: 4px;
|
|
137
|
+
background: var(--annot-input-bg);
|
|
138
|
+
color: var(--annot-text-primary);
|
|
139
|
+
font-size: 12px;
|
|
140
|
+
padding: 0 6px;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.toolbar-color {
|
|
144
|
+
width: 28px;
|
|
145
|
+
height: 28px;
|
|
146
|
+
border: 1px solid var(--annot-input-border);
|
|
147
|
+
border-radius: 4px;
|
|
148
|
+
padding: 0;
|
|
149
|
+
cursor: pointer;
|
|
150
|
+
background: transparent;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.toolbar-color::-webkit-color-swatch-wrapper {
|
|
154
|
+
padding: 2px;
|
|
155
|
+
}
|
|
156
|
+
.toolbar-color::-webkit-color-swatch {
|
|
157
|
+
border: none;
|
|
158
|
+
border-radius: 2px;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.toolbar-range {
|
|
162
|
+
width: 60px;
|
|
163
|
+
accent-color: var(--annot-accent);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.toolbar-label {
|
|
167
|
+
font-size: 11px;
|
|
168
|
+
color: var(--annot-text-secondary);
|
|
169
|
+
margin-right: 2px;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.toolbar-spacer {
|
|
173
|
+
flex: 1;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* ---- Vertical variant (left-side tool strip) ----
|
|
177
|
+
*
|
|
178
|
+
* Enabled via the `.toolbar-vertical` class on the Toolbar container.
|
|
179
|
+
* The same DOM structure is used; only layout-direction properties are
|
|
180
|
+
* overridden so tool buttons, groups, separators, and the spacer all
|
|
181
|
+
* rotate 90°.
|
|
182
|
+
*/
|
|
183
|
+
.toolbar-vertical {
|
|
184
|
+
/* `<annot-toolbar>` is a custom element with no UA stylesheet, so
|
|
185
|
+
* its default `display` is `inline`. The horizontal toolbar mode
|
|
186
|
+
* worked accidentally because inline-block children flowed
|
|
187
|
+
* horizontally on their own; the vertical mode worked accidentally
|
|
188
|
+
* because `<div class="tool-btn-wrap">` is block-level and stacks
|
|
189
|
+
* naturally. In both modes every flex-related declaration below
|
|
190
|
+
* (`flex-direction`, `gap`, `align-items`, `flex: 1` on the
|
|
191
|
+
* spacer) was silently no-op'd until `display: flex` was set on
|
|
192
|
+
* the host. #428 added `align-items: center` to fix the
|
|
193
|
+
* separator off-centre but it never took effect for that reason —
|
|
194
|
+
* the storybook DOM inspection in the follow-up PR confirmed
|
|
195
|
+
* `parentDisplay: "inline"` on the toolbar. Promoting to flex
|
|
196
|
+
* here turns every previously-silent rule on at once: gap
|
|
197
|
+
* spacing, centre-aligned narrower children, and the spacer
|
|
198
|
+
* actually pushing the history group to the bottom of the
|
|
199
|
+
* strip. */
|
|
200
|
+
display: flex;
|
|
201
|
+
flex-direction: column;
|
|
202
|
+
padding: 6px 0;
|
|
203
|
+
gap: 2px;
|
|
204
|
+
align-items: center;
|
|
205
|
+
}
|
|
206
|
+
.toolbar-vertical .toolbar-group {
|
|
207
|
+
flex-direction: column;
|
|
208
|
+
}
|
|
209
|
+
.toolbar-vertical .toolbar-separator {
|
|
210
|
+
width: 28px;
|
|
211
|
+
height: 1px;
|
|
212
|
+
margin: 6px 0;
|
|
213
|
+
}
|
|
214
|
+
.toolbar-vertical .toolbar-spacer {
|
|
215
|
+
flex: 1; /* flex: 1 already grows the correct axis in column layout */
|
|
216
|
+
}
|
|
217
|
+
/* Vertical mode: suppress the CSS ::after tooltip. It would need to
|
|
218
|
+
* extend to the right of the 48px sidebar, which (a) gets clipped by
|
|
219
|
+
* overflow-x: hidden and (b) pollutes the sidebar's scroll-area
|
|
220
|
+
* calculation causing a spurious horizontal scrollbar. Native browser
|
|
221
|
+
* `title` tooltips still appear on hover for accessibility — we just
|
|
222
|
+
* drop the custom styled bubble in this mode. */
|
|
223
|
+
/* Vertical toolbar: reposition the tooltip to the RIGHT of the
|
|
224
|
+
* button instead of below. Below-positioning makes no sense for a
|
|
225
|
+
* vertical strip — the tooltip would overlap the NEXT tool button
|
|
226
|
+
* stacked directly underneath. Rightward positioning keeps the
|
|
227
|
+
* tooltip in the canvas area beside the sidebar, which is empty
|
|
228
|
+
* space from the sidebar's perspective.
|
|
229
|
+
*
|
|
230
|
+
* Requires `#editor-sidebar { overflow-x: visible }` (see editor.css)
|
|
231
|
+
* so the tooltip can extend beyond the 48px strip. */
|
|
232
|
+
.toolbar-vertical .toolbar-btn[data-tooltip]::after {
|
|
233
|
+
bottom: auto;
|
|
234
|
+
top: 50%;
|
|
235
|
+
left: 100%;
|
|
236
|
+
transform: translateY(-50%);
|
|
237
|
+
margin-left: 12px;
|
|
238
|
+
}
|
|
239
|
+
/* Dropdown arrow — horizontal mode centers it below the icon; in
|
|
240
|
+
* vertical mode, put it on the right edge so it doesn't bleed into
|
|
241
|
+
* the next tool stacked below. */
|
|
242
|
+
/* Tool dropdown menu opens to the RIGHT of the tool in vertical mode
|
|
243
|
+
* instead of below it. */
|
|
244
|
+
.toolbar-vertical .tool-dropdown-menu {
|
|
245
|
+
top: 0;
|
|
246
|
+
left: 100%;
|
|
247
|
+
margin-top: 0;
|
|
248
|
+
margin-left: 6px;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* Copy dropdown */
|
|
252
|
+
.copy-dropdown {
|
|
253
|
+
position: relative;
|
|
254
|
+
display: flex;
|
|
255
|
+
align-items: center;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.copy-dropdown-arrow {
|
|
259
|
+
width: 20px;
|
|
260
|
+
font-size: 10px;
|
|
261
|
+
padding: 0;
|
|
262
|
+
margin-left: -2px;
|
|
263
|
+
border-top-left-radius: 0;
|
|
264
|
+
border-bottom-left-radius: 0;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.copy-dropdown-menu {
|
|
268
|
+
position: absolute;
|
|
269
|
+
top: 100%;
|
|
270
|
+
right: 0;
|
|
271
|
+
margin-top: 4px;
|
|
272
|
+
background: var(--annot-bg-panel);
|
|
273
|
+
border: 1px solid var(--annot-border-color);
|
|
274
|
+
border-radius: 6px;
|
|
275
|
+
flex-direction: column;
|
|
276
|
+
overflow: hidden;
|
|
277
|
+
box-shadow: 0 4px 12px var(--annot-shadow);
|
|
278
|
+
z-index: 100;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.copy-dropdown-item {
|
|
282
|
+
display: block;
|
|
283
|
+
width: 100%;
|
|
284
|
+
padding: 8px 16px;
|
|
285
|
+
border: none;
|
|
286
|
+
background: transparent;
|
|
287
|
+
color: var(--annot-text-primary);
|
|
288
|
+
font-size: 13px;
|
|
289
|
+
text-align: left;
|
|
290
|
+
cursor: pointer;
|
|
291
|
+
white-space: nowrap;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.copy-dropdown-item:hover {
|
|
295
|
+
background: var(--annot-choice-hover);
|
|
296
|
+
color: var(--annot-accent);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/* Tool button with dropdown */
|
|
300
|
+
.tool-btn-wrap {
|
|
301
|
+
position: relative;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* Note: the previous `.tool-dropdown-arrow` caret was removed when
|
|
305
|
+
* the variant badge took over the flyout-trigger role. Keeping the
|
|
306
|
+
* rules for `.tool-dropdown-menu` below because some other code
|
|
307
|
+
* paths may reuse the class name for non-toolbar popovers.
|
|
308
|
+
*
|
|
309
|
+
* The `.toolbar-vertical .tool-dropdown-menu` rule earlier in the
|
|
310
|
+
* file is a contextual override (different docking for vertical
|
|
311
|
+
* toolbars); the base styling lives here with the rest of the
|
|
312
|
+
* dropdown-menu rules, hence the descending-specificity suppression. */
|
|
313
|
+
/* biome-ignore lint/style/noDescendingSpecificity: base rule deliberately follows contextual .toolbar-vertical descendant */
|
|
314
|
+
.tool-dropdown-menu {
|
|
315
|
+
position: absolute;
|
|
316
|
+
top: 100%;
|
|
317
|
+
left: 0;
|
|
318
|
+
margin-top: 2px;
|
|
319
|
+
background: var(--annot-bg-panel-deep);
|
|
320
|
+
border: 1px solid var(--annot-border-color);
|
|
321
|
+
border-radius: 8px;
|
|
322
|
+
padding: 8px 10px;
|
|
323
|
+
display: flex;
|
|
324
|
+
flex-direction: column;
|
|
325
|
+
gap: 6px;
|
|
326
|
+
z-index: 200;
|
|
327
|
+
box-shadow: 0 4px 16px var(--annot-shadow);
|
|
328
|
+
min-width: 220px;
|
|
329
|
+
white-space: nowrap;
|
|
330
|
+
max-height: calc(100vh - 80px);
|
|
331
|
+
overflow-y: auto;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/* --- Tool flyout (variant picker opened from the ▼ arrow) ---
|
|
335
|
+
*
|
|
336
|
+
* Rendered into <body> with position: fixed so the popover escapes the
|
|
337
|
+
* toolbar / editor-header `overflow: hidden`. Placement (right/below)
|
|
338
|
+
* is computed in JS via getBoundingClientRect + viewport clamp.
|
|
339
|
+
*/
|
|
340
|
+
.tool-flyout {
|
|
341
|
+
background: var(--annot-bg-panel-deep);
|
|
342
|
+
border: 1px solid var(--annot-border-color);
|
|
343
|
+
border-radius: 8px;
|
|
344
|
+
padding: 6px;
|
|
345
|
+
box-shadow: 0 6px 20px var(--annot-shadow);
|
|
346
|
+
display: flex;
|
|
347
|
+
gap: 4px;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/* Compact variant picker — tight row of icon chips. The chips are the
|
|
351
|
+
* primary hit target (parent-button click uses the last picked variant). */
|
|
352
|
+
.tool-flyout-variant .tool-flyout-row {
|
|
353
|
+
display: flex;
|
|
354
|
+
gap: 4px;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.tool-flyout-chip {
|
|
358
|
+
width: 32px;
|
|
359
|
+
height: 32px;
|
|
360
|
+
border: 1px solid transparent;
|
|
361
|
+
border-radius: 6px;
|
|
362
|
+
background: transparent;
|
|
363
|
+
color: var(--annot-text-primary);
|
|
364
|
+
font-size: 22px;
|
|
365
|
+
font-variation-settings:
|
|
366
|
+
"FILL" 0,
|
|
367
|
+
"wght" 500,
|
|
368
|
+
"GRAD" 0,
|
|
369
|
+
"opsz" 24;
|
|
370
|
+
cursor: pointer;
|
|
371
|
+
display: inline-flex;
|
|
372
|
+
align-items: center;
|
|
373
|
+
justify-content: center;
|
|
374
|
+
transition:
|
|
375
|
+
background 0.12s,
|
|
376
|
+
border-color 0.12s,
|
|
377
|
+
color 0.12s;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.tool-flyout-chip:hover {
|
|
381
|
+
/* Border on hover mirrors `.tool-flyout-color-chip:hover` and gives
|
|
382
|
+
* the user a clear "this row is targetable" signal even when the
|
|
383
|
+
* `--annot-hover-bg` token resolves to a very subtle tint (e.g.
|
|
384
|
+
* VSCode's `--vscode-list-hoverBackground`). Without the border
|
|
385
|
+
* the bg-only delta looked nearly identical to the inactive chip
|
|
386
|
+
* under VSCode's theme map. */
|
|
387
|
+
background: var(--annot-hover-bg);
|
|
388
|
+
border-color: var(--annot-accent);
|
|
389
|
+
color: var(--annot-accent);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.tool-flyout-chip.active {
|
|
393
|
+
/* "Filled" active state — solid `--annot-accent` background +
|
|
394
|
+
* contrasting glyph. The earlier "tinted bg + blue glyph" pattern
|
|
395
|
+
* (`--annot-choice-active` 18 % blue tint vs the hover state's
|
|
396
|
+
* 6-10 % tint) left active and hover too close to read apart at a
|
|
397
|
+
* glance, especially in PWA Light where the hover token resolves
|
|
398
|
+
* to `#eef1fa` and the active token to `#dce4ff` — both pale
|
|
399
|
+
* blues. Switching to a solid fill makes active visually dominant
|
|
400
|
+
* across themes:
|
|
401
|
+
*
|
|
402
|
+
* PWA dark — bg `#7c9cff` (light blue) + glyph `#121933` (navy) ≈ 9:1
|
|
403
|
+
* PWA light — bg `#3456c9` (dark blue) + glyph `#ffffff` (white) ≈ 9:1
|
|
404
|
+
* VSCode — bg button-bg + glyph button-foreground (overridden
|
|
405
|
+
* in the webview's inline `<style>` so the paired
|
|
406
|
+
* tokens hold ≥ 4.5:1 even when the workbench theme
|
|
407
|
+
* picks a low-contrast button-background).
|
|
408
|
+
*
|
|
409
|
+
* Hover keeps the outlined-with-blue-glyph look — a clear
|
|
410
|
+
* three-state hierarchy (inactive → outlined hover → filled
|
|
411
|
+
* active). */
|
|
412
|
+
background: var(--annot-accent);
|
|
413
|
+
border-color: var(--annot-accent);
|
|
414
|
+
color: var(--annot-bg-panel);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/* Chips whose glyph is an inline SVG (rather than a Material Symbols
|
|
418
|
+
* ligature). The SVG inherits `currentColor` from the chip, so all
|
|
419
|
+
* the color / hover / active rules above Just Work without extra
|
|
420
|
+
* selectors. Size matches the optical weight of the 22px
|
|
421
|
+
* material-symbols glyph next to it so mixed flyouts (e.g. rect + SVG
|
|
422
|
+
* and ellipse + font glyph) read as one row. */
|
|
423
|
+
.tool-flyout-chip-svg {
|
|
424
|
+
/* Same box + interaction rules as .tool-flyout-chip — mirrored here
|
|
425
|
+
* because the chip omits the material-symbols class. */
|
|
426
|
+
width: 32px;
|
|
427
|
+
height: 32px;
|
|
428
|
+
border: 1px solid transparent;
|
|
429
|
+
border-radius: 6px;
|
|
430
|
+
background: transparent;
|
|
431
|
+
color: var(--annot-text-primary);
|
|
432
|
+
cursor: pointer;
|
|
433
|
+
display: inline-flex;
|
|
434
|
+
align-items: center;
|
|
435
|
+
justify-content: center;
|
|
436
|
+
transition:
|
|
437
|
+
background 0.12s,
|
|
438
|
+
border-color 0.12s,
|
|
439
|
+
color 0.12s;
|
|
440
|
+
}
|
|
441
|
+
.tool-flyout-chip-svg:hover {
|
|
442
|
+
/* Match `.tool-flyout-chip:hover` — border for hover affordance
|
|
443
|
+
* even when `--annot-hover-bg` resolves to a very subtle tint. */
|
|
444
|
+
background: var(--annot-hover-bg);
|
|
445
|
+
border-color: var(--annot-accent);
|
|
446
|
+
color: var(--annot-accent);
|
|
447
|
+
}
|
|
448
|
+
.tool-flyout-chip-svg.active {
|
|
449
|
+
/* Match `.tool-flyout-chip.active` — see the comment there. */
|
|
450
|
+
background: var(--annot-accent);
|
|
451
|
+
border-color: var(--annot-accent);
|
|
452
|
+
color: var(--annot-bg-panel);
|
|
453
|
+
}
|
|
454
|
+
.tool-flyout-chip-svg > svg {
|
|
455
|
+
width: 22px;
|
|
456
|
+
height: 22px;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/* Highlight color picker — a tight row of 6 colored circle chips.
|
|
460
|
+
* Each chip's background IS the preset color; a ring indicates the
|
|
461
|
+
* currently-selected swatch so the user sees at a glance which color
|
|
462
|
+
* a bare click on the Highlight button will draw with. */
|
|
463
|
+
.tool-flyout-highlight .tool-flyout-color-row {
|
|
464
|
+
display: flex;
|
|
465
|
+
gap: 6px;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.tool-flyout-color-chip {
|
|
469
|
+
/* Same 32×32 frame + hover / active vocabulary as .tool-flyout-chip-svg
|
|
470
|
+
* (glyph chips) so Highlight color selection reads as one entry in
|
|
471
|
+
* the same Type-picker family. Color lives on the inner ::before
|
|
472
|
+
* swatch; the frame handles selection affordance via border / bg. */
|
|
473
|
+
width: 32px;
|
|
474
|
+
height: 32px;
|
|
475
|
+
border: 1px solid transparent;
|
|
476
|
+
border-radius: 6px;
|
|
477
|
+
background: transparent;
|
|
478
|
+
cursor: pointer;
|
|
479
|
+
padding: 0;
|
|
480
|
+
display: inline-flex;
|
|
481
|
+
align-items: center;
|
|
482
|
+
justify-content: center;
|
|
483
|
+
transition:
|
|
484
|
+
background 0.12s,
|
|
485
|
+
border-color 0.12s;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.tool-flyout-color-chip::before {
|
|
489
|
+
content: "";
|
|
490
|
+
display: block;
|
|
491
|
+
width: 18px;
|
|
492
|
+
height: 18px;
|
|
493
|
+
border-radius: 3px;
|
|
494
|
+
background: var(--swatch-color, transparent);
|
|
495
|
+
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12) inset;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.tool-flyout-color-chip:hover {
|
|
499
|
+
background: var(--annot-hover-bg);
|
|
500
|
+
border-color: var(--annot-accent);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.tool-flyout-color-chip.active {
|
|
504
|
+
/* Match `.tool-flyout-chip.active` — see the comment there. */
|
|
505
|
+
background: var(--annot-choice-active, var(--annot-choice-hover, var(--annot-hover-bg)));
|
|
506
|
+
border-color: var(--annot-accent);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/* Variant-bearing tool buttons need positioning context so the
|
|
510
|
+
* bottom-right badge and / or the highlight color bar can anchor
|
|
511
|
+
* themselves. Applied to all toolbar buttons because the cost is
|
|
512
|
+
* zero when no pseudo-element / child badge exists. */
|
|
513
|
+
.toolbar-btn {
|
|
514
|
+
position: relative;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/* Variant badge — small icon tucked into the bottom-right corner to
|
|
518
|
+
* indicate which sub-variant the next bare click on the main button
|
|
519
|
+
* will create. Serves DOUBLE DUTY as the flyout trigger: clicking
|
|
520
|
+
* the badge opens the variant picker (see #ensureBadge in
|
|
521
|
+
* toolbar.ts). The MAIN icon (tool identity) stays fixed; the badge
|
|
522
|
+
* alone mutates. Pattern-matched to Affinity / Photoshop's
|
|
523
|
+
* sub-variant indicator, where the in-button indicator IS the flyout
|
|
524
|
+
* affordance — no separate caret needed.
|
|
525
|
+
*
|
|
526
|
+
* Sizing / placement notes:
|
|
527
|
+
* - 16×16 visual bounds (slightly larger than the old 14×14 so
|
|
528
|
+
* the badge is easier to see AND hit).
|
|
529
|
+
* - pointer-events: auto so the badge is clickable. Parent button's
|
|
530
|
+
* text-node (the main icon) stays the click target for everything
|
|
531
|
+
* EXCEPT the badge zone — so main click activates tool, badge
|
|
532
|
+
* click opens flyout.
|
|
533
|
+
* - Hover state (ring + slight zoom) signals interactivity. */
|
|
534
|
+
.tool-btn-badge {
|
|
535
|
+
position: absolute;
|
|
536
|
+
right: -2px;
|
|
537
|
+
bottom: -2px;
|
|
538
|
+
width: 12px;
|
|
539
|
+
height: 12px;
|
|
540
|
+
display: flex;
|
|
541
|
+
align-items: center;
|
|
542
|
+
justify-content: center;
|
|
543
|
+
font-size: 10px;
|
|
544
|
+
line-height: 1;
|
|
545
|
+
color: var(--annot-accent);
|
|
546
|
+
background: var(--annot-bg-panel);
|
|
547
|
+
border-radius: 50%;
|
|
548
|
+
box-shadow: 0 0 0 1.5px var(--annot-bg-panel);
|
|
549
|
+
pointer-events: auto;
|
|
550
|
+
cursor: pointer;
|
|
551
|
+
/* Visual badge stays small so it floats outside the main icon's
|
|
552
|
+
* visual bounds (right/bottom -2px) without occluding it. The
|
|
553
|
+
* `::before` rule below extends the hit area back out to the
|
|
554
|
+
* button corner so clickability matches the larger 16×16 era. */
|
|
555
|
+
padding: 0;
|
|
556
|
+
border: none;
|
|
557
|
+
transition:
|
|
558
|
+
transform 0.12s,
|
|
559
|
+
box-shadow 0.12s;
|
|
560
|
+
font-variation-settings:
|
|
561
|
+
"FILL" 1,
|
|
562
|
+
"wght" 600,
|
|
563
|
+
"GRAD" 0,
|
|
564
|
+
"opsz" 20;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.tool-btn-badge:hover {
|
|
568
|
+
transform: scale(1.15);
|
|
569
|
+
box-shadow:
|
|
570
|
+
0 0 0 1.5px var(--annot-bg-panel),
|
|
571
|
+
0 0 0 3px var(--annot-accent);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/* Expand the effective click zone beyond the visual circle without
|
|
575
|
+
* affecting layout. An invisible ::before covers the bottom-right
|
|
576
|
+
* quadrant of the parent button; the badge remains absolutely
|
|
577
|
+
* positioned within it. Users can click anywhere in that quadrant
|
|
578
|
+
* and hit the badge — forgiving for touch + hover-and-click. */
|
|
579
|
+
.tool-btn-badge::before {
|
|
580
|
+
content: "";
|
|
581
|
+
position: absolute;
|
|
582
|
+
inset: -6px;
|
|
583
|
+
border-radius: 50%;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/* When the parent button is active (tool selected), accentuate the
|
|
587
|
+
* badge so it stays visible against the active-state background. */
|
|
588
|
+
/* Active-state badge: blend the badge frame with the active button
|
|
589
|
+
* background so the badge feels integral to the active tool rather
|
|
590
|
+
* than a separate panel-coloured callout. The original token name
|
|
591
|
+
* was `--bg-active` but no such variable existed (typo of
|
|
592
|
+
* `--annot-active-bg`), so until now the rule fell through to
|
|
593
|
+
* `--annot-bg-panel` and the badge stayed panel-coloured under
|
|
594
|
+
* every state. Pointing at the real `--annot-active-bg` token
|
|
595
|
+
* restores the intended "blend" look:
|
|
596
|
+
*
|
|
597
|
+
* PWA dark — rgba(124,156,255,0.18) tint over the active
|
|
598
|
+
* button background; blue glyph reads fine.
|
|
599
|
+
* PWA light — pale-blue active tint; blue glyph reads fine.
|
|
600
|
+
* VSCode — list-activeSelectionBackground (strong selection
|
|
601
|
+
* blue); the `.tool-btn-badge` glyph stays
|
|
602
|
+
* `var(--annot-accent)` (also blue) and would
|
|
603
|
+
* vanish, so the inline VSCode override in
|
|
604
|
+
* `packages/vscode/src/webview/index.html` switches
|
|
605
|
+
* the active-badge foreground to
|
|
606
|
+
* `--vscode-list-activeSelectionForeground`. */
|
|
607
|
+
.toolbar-btn.active .tool-btn-badge {
|
|
608
|
+
background: var(--annot-active-bg, var(--annot-bg-panel));
|
|
609
|
+
box-shadow: 0 0 0 1.5px var(--annot-active-bg, var(--annot-bg-panel));
|
|
610
|
+
}
|
|
611
|
+
.toolbar-btn.active .tool-btn-badge:hover {
|
|
612
|
+
box-shadow:
|
|
613
|
+
0 0 0 1.5px var(--annot-active-bg, var(--annot-bg-panel)),
|
|
614
|
+
0 0 0 3px var(--annot-accent);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/* SVG-based badge (for rect / rounded variants where Material Symbols
|
|
618
|
+
* don't give enough visual distinction). Shares positioning + chrome
|
|
619
|
+
* with .tool-btn-badge, just swaps ligature font rendering for an
|
|
620
|
+
* inline SVG. Size scales with the badge's 16px bounds so visual
|
|
621
|
+
* weight matches the ligature glyphs next to it. */
|
|
622
|
+
.tool-btn-badge-svg > svg {
|
|
623
|
+
width: 10px;
|
|
624
|
+
height: 10px;
|
|
625
|
+
color: var(--annot-accent);
|
|
626
|
+
stroke: currentColor;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/* Highlight tool badge — circular panel frame containing a rounded-
|
|
630
|
+
* square color swatch. The outer circle keeps the badge shape
|
|
631
|
+
* consistent with every other tool's variant badge (shape/arrow/
|
|
632
|
+
* text/marker/redact all get a circle in the bottom-right); the
|
|
633
|
+
* inner square mirrors the Selection-panel / flyout swatch chips so
|
|
634
|
+
* the "color = variant" reading lines up visually across all three
|
|
635
|
+
* surfaces. `--swatch-color` is set inline by #syncToolButtonIcon. */
|
|
636
|
+
.tool-btn-badge-color {
|
|
637
|
+
/* Shares the variant-badge sizing + offset (12×12 floating outside
|
|
638
|
+
* the icon at right/bottom -2px) so the toolbar reads consistently
|
|
639
|
+
* across all tools. Only the inner swatch chip differs. */
|
|
640
|
+
font-size: 0;
|
|
641
|
+
color: transparent;
|
|
642
|
+
/* Inherits background (panel color) + border-radius (50%) from
|
|
643
|
+
* .tool-btn-badge. Nudge the outer ring thicker so light colors
|
|
644
|
+
* stay visible against the panel. */
|
|
645
|
+
box-shadow: 0 0 0 1.5px var(--annot-bg-panel);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
.tool-btn-badge-color::after {
|
|
649
|
+
/* Inner rounded square showing the current highlight color. Sized
|
|
650
|
+
* to fit the 12×12 frame with a thin ring of badge-bg visible all
|
|
651
|
+
* around. */
|
|
652
|
+
content: "";
|
|
653
|
+
display: block;
|
|
654
|
+
width: 8px;
|
|
655
|
+
height: 8px;
|
|
656
|
+
border-radius: 2px;
|
|
657
|
+
background: var(--swatch-color, transparent);
|
|
658
|
+
box-shadow: 0 0 0 0.5px rgba(0, 0, 0, 0.25);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
.toolbar-btn.active .tool-btn-badge-color {
|
|
662
|
+
box-shadow: 0 0 0 1.5px var(--annot-active-bg, var(--annot-bg-panel));
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/* Larger popover used by the Scratchpad button — holds a scrollable
|
|
666
|
+
* thumbnail grid plus a save-selection action. */
|
|
667
|
+
.tool-flyout-scratchpad {
|
|
668
|
+
flex-direction: column;
|
|
669
|
+
gap: 8px;
|
|
670
|
+
width: 264px;
|
|
671
|
+
max-height: min(420px, calc(100vh - 40px));
|
|
672
|
+
padding: 10px;
|
|
673
|
+
}
|