@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,779 @@
|
|
|
1
|
+
.prop-panel {
|
|
2
|
+
position: absolute;
|
|
3
|
+
top: 52px;
|
|
4
|
+
right: 8px;
|
|
5
|
+
background: var(--annot-bg-panel-deep);
|
|
6
|
+
border: 1px solid var(--annot-border-color);
|
|
7
|
+
border-radius: 8px;
|
|
8
|
+
padding: 10px 12px;
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
gap: 8px;
|
|
12
|
+
z-index: 20;
|
|
13
|
+
box-shadow: 0 4px 16px var(--annot-shadow);
|
|
14
|
+
min-width: 200px;
|
|
15
|
+
max-height: calc(100vh - 100px);
|
|
16
|
+
overflow-y: auto;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* Docked variant — used when PropertyPanel is embedded in a host
|
|
20
|
+
* sidebar instead of floating on the canvas. Strips the absolute
|
|
21
|
+
* positioning + chrome so the panel flows naturally inside its
|
|
22
|
+
* container. The host is responsible for sizing/positioning the
|
|
23
|
+
* container (e.g. #editor-right-panel). */
|
|
24
|
+
.prop-panel-docked {
|
|
25
|
+
position: static;
|
|
26
|
+
top: auto;
|
|
27
|
+
right: auto;
|
|
28
|
+
border: none;
|
|
29
|
+
border-radius: 0;
|
|
30
|
+
/* Tight inner pad — the outer `.editor-right-panel-section`
|
|
31
|
+
* already supplies its own 10 px vertical, so 6 px here keeps
|
|
32
|
+
* the docked-panel column compact without doubling up. The
|
|
33
|
+
* 16 px horizontal nests inside the section's 16 px to
|
|
34
|
+
* establish the level-2 column gutter. */
|
|
35
|
+
padding: 6px 16px;
|
|
36
|
+
box-shadow: none;
|
|
37
|
+
max-height: none;
|
|
38
|
+
min-width: 0;
|
|
39
|
+
background: transparent;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.prop-row {
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
gap: 6px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.prop-label {
|
|
49
|
+
font-size: 11px;
|
|
50
|
+
color: var(--annot-text-secondary);
|
|
51
|
+
width: 36px;
|
|
52
|
+
flex-shrink: 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.prop-color {
|
|
56
|
+
width: 26px;
|
|
57
|
+
height: 26px;
|
|
58
|
+
border: 1px solid var(--annot-input-border);
|
|
59
|
+
border-radius: 4px;
|
|
60
|
+
padding: 0;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
background: transparent;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.prop-color::-webkit-color-swatch-wrapper {
|
|
66
|
+
padding: 2px;
|
|
67
|
+
}
|
|
68
|
+
.prop-color::-webkit-color-swatch {
|
|
69
|
+
border: none;
|
|
70
|
+
border-radius: 2px;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.prop-btn {
|
|
74
|
+
width: 26px;
|
|
75
|
+
height: 26px;
|
|
76
|
+
border: 1px solid var(--annot-input-border);
|
|
77
|
+
border-radius: 4px;
|
|
78
|
+
background: var(--annot-chip-bg);
|
|
79
|
+
color: var(--annot-text-secondary);
|
|
80
|
+
font-size: 13px;
|
|
81
|
+
cursor: pointer;
|
|
82
|
+
display: flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
justify-content: center;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.prop-btn:hover {
|
|
88
|
+
border-color: var(--annot-accent);
|
|
89
|
+
color: var(--annot-accent);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Section with label + choice list */
|
|
93
|
+
.prop-section {
|
|
94
|
+
display: flex;
|
|
95
|
+
flex-direction: column;
|
|
96
|
+
gap: 4px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.prop-section-label {
|
|
100
|
+
font-size: 11px;
|
|
101
|
+
color: var(--annot-text-secondary);
|
|
102
|
+
padding-bottom: 2px;
|
|
103
|
+
border-bottom: 1px solid var(--annot-border-color);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* Vertical choice list (width / style) */
|
|
107
|
+
.prop-choice-list {
|
|
108
|
+
display: flex;
|
|
109
|
+
flex-direction: column;
|
|
110
|
+
gap: 1px;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.prop-choice-item {
|
|
114
|
+
display: flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
gap: 8px;
|
|
117
|
+
padding: 4px 8px;
|
|
118
|
+
border-radius: 4px;
|
|
119
|
+
cursor: pointer;
|
|
120
|
+
transition: background 0.1s;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.prop-choice-item:hover {
|
|
124
|
+
background: var(--annot-choice-hover);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.prop-choice-item.active {
|
|
128
|
+
background: var(--annot-choice-active);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.prop-choice-item svg {
|
|
132
|
+
flex-shrink: 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.prop-choice-label {
|
|
136
|
+
font-size: 11px;
|
|
137
|
+
color: var(--annot-text-secondary);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.prop-choice-item.active .prop-choice-label {
|
|
141
|
+
color: var(--annot-accent);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* Horizontal chip list (font size) */
|
|
145
|
+
.prop-choice-horizontal {
|
|
146
|
+
flex-direction: row;
|
|
147
|
+
flex-wrap: wrap;
|
|
148
|
+
gap: 4px;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* Variant-picker chip — sized + styled to match `.tool-flyout-chip` /
|
|
152
|
+
* `.tool-flyout-chip-svg` in the toolbar flyout so "next draw"
|
|
153
|
+
* (Tool mode) and "this element's variant" (Selection mode) share
|
|
154
|
+
* the same chip vocabulary across toolbar + property panel.
|
|
155
|
+
* The .pp-color-chip subclass overrides width/height inline (28×20)
|
|
156
|
+
* for Highlight color swatches — its inline styles win over these
|
|
157
|
+
* class defaults. */
|
|
158
|
+
.prop-choice-chip {
|
|
159
|
+
width: 32px;
|
|
160
|
+
height: 32px;
|
|
161
|
+
padding: 0;
|
|
162
|
+
border: 1px solid transparent;
|
|
163
|
+
border-radius: 6px;
|
|
164
|
+
background: transparent;
|
|
165
|
+
color: var(--annot-text-primary);
|
|
166
|
+
font-size: 22px;
|
|
167
|
+
cursor: pointer;
|
|
168
|
+
display: inline-flex;
|
|
169
|
+
align-items: center;
|
|
170
|
+
justify-content: center;
|
|
171
|
+
transition:
|
|
172
|
+
background 0.12s,
|
|
173
|
+
border-color 0.12s,
|
|
174
|
+
color 0.12s;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/* SVG glyphs render at a consistent 22×22 — same size as the
|
|
178
|
+
* ligature glyphs (font-size 22px) so SVG + ligature chips read as
|
|
179
|
+
* one family inside the same row. For tall/wide SVGs with explicit
|
|
180
|
+
* width/height attrs the `object-fit: contain`-like scaling is
|
|
181
|
+
* handled by preserveAspectRatio (SVG default), so non-square icons
|
|
182
|
+
* stay legible. */
|
|
183
|
+
.prop-choice-chip > svg {
|
|
184
|
+
width: 22px;
|
|
185
|
+
height: 22px;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.prop-choice-chip:hover {
|
|
189
|
+
background: var(--annot-hover-bg);
|
|
190
|
+
border-color: var(--annot-accent);
|
|
191
|
+
color: var(--annot-accent);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.prop-choice-chip.active {
|
|
195
|
+
/* "Filled" active state — solid `--annot-accent` background +
|
|
196
|
+
* contrasting glyph. Mirrors the variant flyout chip's
|
|
197
|
+
* active treatment (`.tool-flyout-chip.active` in
|
|
198
|
+
* `core/styles/toolbar.css`) so the right panel's TYPE
|
|
199
|
+
* picker reads with the same three-state hierarchy:
|
|
200
|
+
*
|
|
201
|
+
* Inactive — no border, default glyph
|
|
202
|
+
* Hover — accent border + tinted bg + accent glyph
|
|
203
|
+
* Active — accent fill + contrasting glyph (filled chip)
|
|
204
|
+
*
|
|
205
|
+
* The earlier "tinted bg + blue glyph" pattern shared every
|
|
206
|
+
* visual property with the hover state apart from a small
|
|
207
|
+
* delta in bg saturation (PWA dark: 18 % blue active vs 8 %
|
|
208
|
+
* white hover overlay). On screens / themes where the
|
|
209
|
+
* hover-bg / choice-active difference is subtle, the two
|
|
210
|
+
* states became visually indistinguishable — TYPE pickers
|
|
211
|
+
* lost their "what's currently selected" cue.
|
|
212
|
+
*
|
|
213
|
+
* VSCode contrast budget: bg = `--annot-accent` =
|
|
214
|
+
* `--vscode-button-background`; the inline override in
|
|
215
|
+
* `packages/vscode/src/webview/index.html` switches glyph to
|
|
216
|
+
* `--vscode-button-foreground` (the workbench-paired ≥4.5:1
|
|
217
|
+
* partner) so the white-on-blue contrast holds across
|
|
218
|
+
* every shipping theme. */
|
|
219
|
+
background: var(--annot-accent);
|
|
220
|
+
border-color: var(--annot-accent);
|
|
221
|
+
color: var(--annot-bg-panel);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.prop-number-input {
|
|
225
|
+
width: 52px;
|
|
226
|
+
height: 26px;
|
|
227
|
+
border: 1px solid var(--annot-input-border);
|
|
228
|
+
border-radius: 4px;
|
|
229
|
+
background: var(--annot-input-bg);
|
|
230
|
+
color: var(--annot-text-primary);
|
|
231
|
+
font-size: 13px;
|
|
232
|
+
padding: 0 6px;
|
|
233
|
+
text-align: center;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/* Preview line color adapts to theme */
|
|
237
|
+
.preview-line {
|
|
238
|
+
stroke: var(--annot-preview-line);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/* Color palette */
|
|
242
|
+
.color-palette {
|
|
243
|
+
display: flex;
|
|
244
|
+
flex-direction: column;
|
|
245
|
+
gap: 2px;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.color-palette-label {
|
|
249
|
+
font-size: 10px;
|
|
250
|
+
color: var(--annot-text-muted);
|
|
251
|
+
padding: 2px 0;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.color-palette-row {
|
|
255
|
+
display: flex;
|
|
256
|
+
gap: 2px;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.color-swatch {
|
|
260
|
+
width: 18px;
|
|
261
|
+
height: 18px;
|
|
262
|
+
border: 1px solid transparent;
|
|
263
|
+
border-radius: 2px;
|
|
264
|
+
cursor: pointer;
|
|
265
|
+
transition: transform 0.1s;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.color-swatch:hover {
|
|
269
|
+
transform: scale(1.25);
|
|
270
|
+
z-index: 1;
|
|
271
|
+
border-color: var(--annot-accent);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.color-swatch.active {
|
|
275
|
+
border: 2px solid var(--annot-accent);
|
|
276
|
+
border-radius: 3px;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.color-palette-custom {
|
|
280
|
+
margin-top: 4px;
|
|
281
|
+
display: flex;
|
|
282
|
+
align-items: center;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.color-palette-custom-btn {
|
|
286
|
+
font-size: 11px;
|
|
287
|
+
color: var(--annot-accent);
|
|
288
|
+
background: none;
|
|
289
|
+
border: none;
|
|
290
|
+
cursor: pointer;
|
|
291
|
+
padding: 2px 0;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.color-palette-custom-btn:hover {
|
|
295
|
+
text-decoration: underline;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.color-palette-hidden-input {
|
|
299
|
+
width: 0;
|
|
300
|
+
height: 0;
|
|
301
|
+
padding: 0;
|
|
302
|
+
border: none;
|
|
303
|
+
opacity: 0;
|
|
304
|
+
position: absolute;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/* ---- Category / row typography hierarchy ----
|
|
308
|
+
*
|
|
309
|
+
* Previously category headers (Type / Fill / Line / Label) and row
|
|
310
|
+
* labels (Color / Width / Dash type / …) used nearly identical
|
|
311
|
+
* styling — 13 px bold for headers, 12 px regular for rows — which
|
|
312
|
+
* read as "slightly bigger text" rather than a clear category /
|
|
313
|
+
* attribute distinction.
|
|
314
|
+
*
|
|
315
|
+
* New hierarchy (Figma / Linear / Notion-style):
|
|
316
|
+
* Category header → SMALL UPPERCASE, muted, letter-spaced
|
|
317
|
+
* → reads as a "section label" at a glance
|
|
318
|
+
* Row label → normal sentence case body text
|
|
319
|
+
* → reads as a "property name"
|
|
320
|
+
*
|
|
321
|
+
* The contrast in size + case + weight + spacing makes the grouping
|
|
322
|
+
* obvious without needing borders / boxes, keeping the panel flat
|
|
323
|
+
* and uncluttered.
|
|
324
|
+
*/
|
|
325
|
+
|
|
326
|
+
/* Vertical rhythm — Gestalt proximity principle, dense:
|
|
327
|
+
* Section → Section : 8px (clear category break)
|
|
328
|
+
* Row → Row : 4px (list items)
|
|
329
|
+
* Header → Body : 4px (parent-child grouping)
|
|
330
|
+
*
|
|
331
|
+
* Annot's right panel is a tool surface that competes with the
|
|
332
|
+
* canvas for screen real estate; users adjust properties dozens
|
|
333
|
+
* of times per session, so density-over-airiness wins. Original
|
|
334
|
+
* 20 : 6 : 4 ratio (PowerPoint-class) was halved on the section-
|
|
335
|
+
* to-section gap (Figma is closer to 8 px). The header-to-body
|
|
336
|
+
* gap stays 4 px because that proximity is already at the floor —
|
|
337
|
+
* making it any tighter would merge into the header.
|
|
338
|
+
*
|
|
339
|
+
* If you find yourself wanting more breathing room, the right
|
|
340
|
+
* fix is usually to drop a row, not to widen the gap. */
|
|
341
|
+
.pp-section {
|
|
342
|
+
margin: 8px 0 0 0;
|
|
343
|
+
}
|
|
344
|
+
.pp-section:first-child {
|
|
345
|
+
margin-top: 0;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.pp-section-header {
|
|
349
|
+
display: block;
|
|
350
|
+
user-select: none;
|
|
351
|
+
/* Small + uppercase + letter-spaced — canonical "section header"
|
|
352
|
+
* treatment in professional SaaS UIs (Figma right-panel, Linear,
|
|
353
|
+
* Notion databases). Uppercase + tracking give it visual weight
|
|
354
|
+
* despite the small font. */
|
|
355
|
+
font-size: 10px;
|
|
356
|
+
font-weight: 700;
|
|
357
|
+
letter-spacing: 0.7px;
|
|
358
|
+
text-transform: uppercase;
|
|
359
|
+
color: var(--annot-text-muted);
|
|
360
|
+
padding: 0 0 4px 0;
|
|
361
|
+
}
|
|
362
|
+
.pp-section-body {
|
|
363
|
+
display: flex;
|
|
364
|
+
flex-direction: column;
|
|
365
|
+
/* Was 6 px — tightened to 4 px (Figma-class density) so the
|
|
366
|
+
* Color / Width / Dash type / etc. property rows pack closer.
|
|
367
|
+
* Each row is already a complete visual unit (label + control
|
|
368
|
+
* with its own border/background) so it doesn't need much
|
|
369
|
+
* gap to stay readable. */
|
|
370
|
+
gap: 4px;
|
|
371
|
+
/* Indent rows so they hang off their category header. The
|
|
372
|
+
* uppercase tracked header stays flush at the section's left
|
|
373
|
+
* edge, establishing the outer column. */
|
|
374
|
+
padding-left: 10px;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/* Radio group — paint type choice (none / solid / gradient) */
|
|
378
|
+
.pp-radio-list {
|
|
379
|
+
display: flex;
|
|
380
|
+
flex-direction: column;
|
|
381
|
+
gap: 4px;
|
|
382
|
+
padding: 0 0 4px 12px;
|
|
383
|
+
}
|
|
384
|
+
.pp-radio {
|
|
385
|
+
display: flex;
|
|
386
|
+
align-items: center;
|
|
387
|
+
gap: 6px;
|
|
388
|
+
font-size: 13px;
|
|
389
|
+
color: var(--annot-text-primary);
|
|
390
|
+
cursor: pointer;
|
|
391
|
+
user-select: none;
|
|
392
|
+
}
|
|
393
|
+
.pp-radio input[type="radio"] {
|
|
394
|
+
accent-color: var(--annot-accent);
|
|
395
|
+
margin: 0;
|
|
396
|
+
cursor: pointer;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/* Two-column label-value row — the dominant layout in PowerPoint's
|
|
400
|
+
* side panel. Label hugs the left, control takes remaining width. */
|
|
401
|
+
.pp-row {
|
|
402
|
+
display: grid;
|
|
403
|
+
/* 120px label column accommodates the longest row labels
|
|
404
|
+
* ("Begin arrow type", "End arrow size", "Transparency") with a
|
|
405
|
+
* few px of breathing room. The remaining space goes to the
|
|
406
|
+
* control. Panel widened from 240→280 px in P1 gives us the
|
|
407
|
+
* extra width to work with. */
|
|
408
|
+
grid-template-columns: 120px 1fr;
|
|
409
|
+
align-items: center;
|
|
410
|
+
gap: 6px;
|
|
411
|
+
/* Was 28 px — tightened to 24 px to match the natural height
|
|
412
|
+
* of the in-row controls (`.pp-number` is 24 px, `.pp-color`
|
|
413
|
+
* 26 px). 28 px was adding dead vertical above/below each row
|
|
414
|
+
* for no visual benefit. */
|
|
415
|
+
min-height: 24px;
|
|
416
|
+
}
|
|
417
|
+
.pp-row-label {
|
|
418
|
+
/* Row / attribute label — subdued secondary color so the VALUE
|
|
419
|
+
* (color swatch, number, pulldown) reads as the primary content
|
|
420
|
+
* of each row. Paired with the uppercase category header above,
|
|
421
|
+
* the hierarchy reads as: CATEGORY (label) → attribute (value),
|
|
422
|
+
* matching professional design-tool property panels. */
|
|
423
|
+
font-size: 12px;
|
|
424
|
+
font-weight: 400;
|
|
425
|
+
color: var(--annot-text-secondary);
|
|
426
|
+
white-space: nowrap;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/* Numeric input with unit suffix — width (pt), transparency (%). */
|
|
430
|
+
.pp-number {
|
|
431
|
+
display: inline-flex;
|
|
432
|
+
align-items: center;
|
|
433
|
+
border: 1px solid var(--annot-border-color);
|
|
434
|
+
border-radius: 4px;
|
|
435
|
+
background: var(--bg-input, var(--annot-bg-panel));
|
|
436
|
+
overflow: hidden;
|
|
437
|
+
height: 24px;
|
|
438
|
+
min-width: 0;
|
|
439
|
+
}
|
|
440
|
+
.pp-number input {
|
|
441
|
+
border: none;
|
|
442
|
+
outline: none;
|
|
443
|
+
background: transparent;
|
|
444
|
+
color: var(--annot-text-primary);
|
|
445
|
+
font-size: 12px;
|
|
446
|
+
text-align: right;
|
|
447
|
+
width: 100%;
|
|
448
|
+
min-width: 0;
|
|
449
|
+
padding: 0 4px;
|
|
450
|
+
}
|
|
451
|
+
.pp-number input::-webkit-outer-spin-button,
|
|
452
|
+
.pp-number input::-webkit-inner-spin-button {
|
|
453
|
+
-webkit-appearance: none;
|
|
454
|
+
margin: 0;
|
|
455
|
+
}
|
|
456
|
+
.pp-number input[type="number"] {
|
|
457
|
+
-moz-appearance: textfield;
|
|
458
|
+
}
|
|
459
|
+
.pp-number-unit {
|
|
460
|
+
color: var(--annot-text-muted);
|
|
461
|
+
font-size: 11px;
|
|
462
|
+
padding-right: 4px;
|
|
463
|
+
user-select: none;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/* Slider paired with a numeric input — PowerPoint's transparency /
|
|
467
|
+
* brightness rows. The slider is the wide visual scrubber; the input
|
|
468
|
+
* gives precise values. */
|
|
469
|
+
.pp-slider-row {
|
|
470
|
+
display: grid;
|
|
471
|
+
/* Match the .pp-row label column so slider rows (Transparency, etc)
|
|
472
|
+
* line up with the rest of the label column. */
|
|
473
|
+
grid-template-columns: 108px 1fr 56px;
|
|
474
|
+
align-items: center;
|
|
475
|
+
gap: 6px;
|
|
476
|
+
min-height: 28px;
|
|
477
|
+
}
|
|
478
|
+
.pp-slider-row input[type="range"] {
|
|
479
|
+
width: 100%;
|
|
480
|
+
margin: 0;
|
|
481
|
+
accent-color: var(--annot-accent);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/* PowerPoint-style dropdown button — visual preview + caret. Popup
|
|
485
|
+
* content uses `.pp-select-popup` (inside the shared .tool-flyout). */
|
|
486
|
+
.pp-select {
|
|
487
|
+
display: inline-flex;
|
|
488
|
+
align-items: center;
|
|
489
|
+
justify-content: space-between;
|
|
490
|
+
gap: 4px;
|
|
491
|
+
height: 24px;
|
|
492
|
+
padding: 0 2px 0 6px;
|
|
493
|
+
background: var(--bg-input, var(--annot-bg-panel));
|
|
494
|
+
border: 1px solid var(--annot-border-color);
|
|
495
|
+
border-radius: 4px;
|
|
496
|
+
color: var(--annot-text-primary);
|
|
497
|
+
cursor: pointer;
|
|
498
|
+
font-size: 12px;
|
|
499
|
+
}
|
|
500
|
+
.pp-select:hover {
|
|
501
|
+
border-color: var(--annot-accent);
|
|
502
|
+
}
|
|
503
|
+
.pp-select:disabled {
|
|
504
|
+
opacity: 0.5;
|
|
505
|
+
cursor: not-allowed;
|
|
506
|
+
}
|
|
507
|
+
.pp-select-preview {
|
|
508
|
+
display: inline-flex;
|
|
509
|
+
align-items: center;
|
|
510
|
+
gap: 4px;
|
|
511
|
+
min-width: 0;
|
|
512
|
+
flex: 1;
|
|
513
|
+
}
|
|
514
|
+
.pp-select-preview svg {
|
|
515
|
+
display: block;
|
|
516
|
+
}
|
|
517
|
+
.pp-select-caret {
|
|
518
|
+
font-size: 16px;
|
|
519
|
+
color: var(--annot-text-muted);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/* Popup content for pp-select. Grid layout so icon galleries
|
|
523
|
+
* (arrow types) read as PowerPoint's visual pickers. */
|
|
524
|
+
.tool-flyout-pp-select {
|
|
525
|
+
padding: 6px;
|
|
526
|
+
/* Let the popup shrink to the widest row — PowerPoint's dropdowns
|
|
527
|
+
* size to content rather than a fixed min-width, which avoids the
|
|
528
|
+
* awkward empty band to the right of short previews (dashes, caps,
|
|
529
|
+
* joins). Callers can still opt into a larger minimum via the
|
|
530
|
+
* custom-select's `popupWidth` option when they need it. */
|
|
531
|
+
width: max-content;
|
|
532
|
+
}
|
|
533
|
+
.pp-select-grid {
|
|
534
|
+
display: grid;
|
|
535
|
+
grid-template-columns: 1fr;
|
|
536
|
+
gap: 2px;
|
|
537
|
+
}
|
|
538
|
+
.pp-select-item {
|
|
539
|
+
display: inline-flex;
|
|
540
|
+
align-items: center;
|
|
541
|
+
justify-content: flex-start;
|
|
542
|
+
gap: 6px;
|
|
543
|
+
padding: 4px 8px;
|
|
544
|
+
border: 1px solid transparent;
|
|
545
|
+
border-radius: 4px;
|
|
546
|
+
background: transparent;
|
|
547
|
+
color: var(--annot-text-primary);
|
|
548
|
+
font-size: 12px;
|
|
549
|
+
cursor: pointer;
|
|
550
|
+
text-align: left;
|
|
551
|
+
}
|
|
552
|
+
.pp-select-item:hover {
|
|
553
|
+
background: var(--annot-hover-bg);
|
|
554
|
+
}
|
|
555
|
+
.pp-select-item.active {
|
|
556
|
+
background: var(--annot-choice-hover, var(--annot-hover-bg));
|
|
557
|
+
border-color: var(--annot-accent);
|
|
558
|
+
color: var(--annot-accent);
|
|
559
|
+
}
|
|
560
|
+
.pp-select-item svg {
|
|
561
|
+
display: block;
|
|
562
|
+
flex-shrink: 0;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/* Color swatch button — the color pulldown affordance. */
|
|
566
|
+
.pp-color-btn {
|
|
567
|
+
width: 100%;
|
|
568
|
+
height: 24px;
|
|
569
|
+
padding: 2px;
|
|
570
|
+
background: var(--bg-input, var(--annot-bg-panel));
|
|
571
|
+
border: 1px solid var(--annot-border-color);
|
|
572
|
+
border-radius: 4px;
|
|
573
|
+
cursor: pointer;
|
|
574
|
+
display: flex;
|
|
575
|
+
align-items: center;
|
|
576
|
+
gap: 6px;
|
|
577
|
+
}
|
|
578
|
+
.pp-color-swatch {
|
|
579
|
+
width: 20px;
|
|
580
|
+
height: 16px;
|
|
581
|
+
border-radius: 2px;
|
|
582
|
+
border: 1px solid var(--annot-border-color);
|
|
583
|
+
flex-shrink: 0;
|
|
584
|
+
}
|
|
585
|
+
.pp-color-btn span:has(svg),
|
|
586
|
+
.pp-color-btn > span:last-child {
|
|
587
|
+
font-size: 16px;
|
|
588
|
+
color: var(--annot-text-muted);
|
|
589
|
+
margin-left: auto;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/* "No fill" state for color swatches — diagonal strike from top-right
|
|
593
|
+
* to bottom-left over a white background. Same visual as PowerPoint's
|
|
594
|
+
* "No fill" chip so the meaning reads without needing a label. */
|
|
595
|
+
.pp-color-swatch-none {
|
|
596
|
+
background:
|
|
597
|
+
linear-gradient(
|
|
598
|
+
to top left,
|
|
599
|
+
transparent 0 calc(50% - 0.8px),
|
|
600
|
+
var(--annot-accent, #e74c3c) calc(50% - 0.8px) calc(50% + 0.8px),
|
|
601
|
+
transparent calc(50% + 0.8px) 100%
|
|
602
|
+
),
|
|
603
|
+
#ffffff;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/* "No fill" button inside the color-pull popover. Shown ABOVE the
|
|
607
|
+
* palette grid for shape-fill pickers that support absence as a
|
|
608
|
+
* meaningful choice. */
|
|
609
|
+
.pp-color-none-btn {
|
|
610
|
+
display: block;
|
|
611
|
+
width: 100%;
|
|
612
|
+
padding: 6px 8px;
|
|
613
|
+
margin-bottom: 8px;
|
|
614
|
+
background: var(--bg-input, var(--annot-bg-panel));
|
|
615
|
+
color: var(--annot-text-primary);
|
|
616
|
+
border: 1px solid var(--annot-border-color);
|
|
617
|
+
border-radius: 4px;
|
|
618
|
+
cursor: pointer;
|
|
619
|
+
font-size: 12px;
|
|
620
|
+
text-align: left;
|
|
621
|
+
}
|
|
622
|
+
.pp-color-none-btn:hover {
|
|
623
|
+
background: var(--annot-hover-bg);
|
|
624
|
+
border-color: var(--annot-accent);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/* Chip-row container for variant/type pickers inside a pp-section.
|
|
628
|
+
* Mirrors the existing `prop-choice-horizontal` but lives inside a
|
|
629
|
+
* pp-section-body so the padding matches other pp-rows. */
|
|
630
|
+
.pp-type-row {
|
|
631
|
+
display: flex;
|
|
632
|
+
gap: 6px;
|
|
633
|
+
flex-wrap: wrap;
|
|
634
|
+
/* Leave headroom for the active-state outline ring (see
|
|
635
|
+
* .pp-color-chip.active below) so the ring doesn't clip against
|
|
636
|
+
* the section edge. */
|
|
637
|
+
padding: 3px;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/* Color-swatch chip — used by the Highlight Type picker. The chip
|
|
641
|
+
* frame inherits base .prop-choice-chip styling (32×32 transparent
|
|
642
|
+
* shell, accent border/bg on hover+active), matching other Type
|
|
643
|
+
* chips. The preview color lives on an INNER ::before square
|
|
644
|
+
* (--swatch-color driven by JS), sized smaller than the chip so
|
|
645
|
+
* the active-state accent border + bg tint show around it. This
|
|
646
|
+
* unifies the Highlight selection affordance with every other
|
|
647
|
+
* variant picker — no bespoke double-ring / checkmark needed. */
|
|
648
|
+
.prop-choice-chip.pp-color-chip::before {
|
|
649
|
+
content: "";
|
|
650
|
+
display: block;
|
|
651
|
+
width: 18px;
|
|
652
|
+
height: 18px;
|
|
653
|
+
border-radius: 3px;
|
|
654
|
+
background: var(--swatch-color, transparent);
|
|
655
|
+
/* Subtle inset stroke so light colors (e.g. yellow on a light
|
|
656
|
+
* theme) still have a visible edge against the chip frame. */
|
|
657
|
+
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12) inset;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/* Done button row — currently used by Freehand to explicitly end a
|
|
661
|
+
* continuous drawing session (draw.io-style). Full-width so it reads
|
|
662
|
+
* as a terminal action, distinct from the setting rows above. */
|
|
663
|
+
.pp-done-row {
|
|
664
|
+
display: flex;
|
|
665
|
+
padding: 8px 0 2px 0;
|
|
666
|
+
}
|
|
667
|
+
.pp-done-btn {
|
|
668
|
+
width: 100%;
|
|
669
|
+
padding: 8px 12px;
|
|
670
|
+
border: 1px solid var(--annot-accent);
|
|
671
|
+
border-radius: 6px;
|
|
672
|
+
background: var(--annot-accent);
|
|
673
|
+
color: var(--annot-bg-panel, #fff);
|
|
674
|
+
font-size: 13px;
|
|
675
|
+
font-weight: 600;
|
|
676
|
+
cursor: pointer;
|
|
677
|
+
transition:
|
|
678
|
+
opacity 0.12s,
|
|
679
|
+
transform 0.08s;
|
|
680
|
+
}
|
|
681
|
+
.pp-done-btn:hover {
|
|
682
|
+
opacity: 0.9;
|
|
683
|
+
}
|
|
684
|
+
.pp-done-btn:active {
|
|
685
|
+
transform: translateY(1px);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/* Safety: keep PP widgets inside the panel width even when the content
|
|
689
|
+
* would otherwise force horizontal overflow (long labels, wide
|
|
690
|
+
* previews, etc). `min-width: 0` is the well-known grid escape hatch
|
|
691
|
+
* that lets flex/grid children shrink below their content's natural
|
|
692
|
+
* width instead of overflowing their parent. */
|
|
693
|
+
.pp-row > *,
|
|
694
|
+
.pp-slider-row > * {
|
|
695
|
+
min-width: 0;
|
|
696
|
+
}
|
|
697
|
+
.pp-section,
|
|
698
|
+
.pp-section-body {
|
|
699
|
+
min-width: 0;
|
|
700
|
+
overflow: hidden;
|
|
701
|
+
}
|
|
702
|
+
.pp-select {
|
|
703
|
+
max-width: 100%;
|
|
704
|
+
min-width: 0;
|
|
705
|
+
}
|
|
706
|
+
.pp-select-preview svg {
|
|
707
|
+
max-width: 100%;
|
|
708
|
+
height: auto;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/* Icon-grid variant of the custom-select popup (arrow type / size
|
|
712
|
+
* pickers). PowerPoint renders these as uniform icon buttons — no
|
|
713
|
+
* text labels, centered previews, tighter padding. We apply these
|
|
714
|
+
* rules unconditionally since text-only options stay centered
|
|
715
|
+
* cleanly too. */
|
|
716
|
+
.pp-select-grid {
|
|
717
|
+
gap: 2px;
|
|
718
|
+
}
|
|
719
|
+
.pp-select-item {
|
|
720
|
+
justify-content: center;
|
|
721
|
+
padding: 6px;
|
|
722
|
+
min-height: 28px;
|
|
723
|
+
}
|
|
724
|
+
/* When a grid has multiple columns (set inline by JS), items become
|
|
725
|
+
* uniform square-ish cells matching PowerPoint's gallery look. */
|
|
726
|
+
.pp-select-grid[style*="repeat"] .pp-select-item {
|
|
727
|
+
aspect-ratio: auto;
|
|
728
|
+
min-width: 0;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/* Custom up/down spinner for pp-number (PowerPoint-style). The
|
|
732
|
+
* browser's native spin buttons are suppressed above so these
|
|
733
|
+
* stacked caret buttons take over. Chevrons are drawn with CSS
|
|
734
|
+
* borders so no extra font / SVG is needed. */
|
|
735
|
+
.pp-number-spinner {
|
|
736
|
+
display: flex;
|
|
737
|
+
flex-direction: column;
|
|
738
|
+
border-left: 1px solid var(--annot-border-color);
|
|
739
|
+
align-self: stretch;
|
|
740
|
+
}
|
|
741
|
+
.pp-number-spinner button {
|
|
742
|
+
flex: 1;
|
|
743
|
+
width: 14px;
|
|
744
|
+
min-height: 10px;
|
|
745
|
+
padding: 0;
|
|
746
|
+
margin: 0;
|
|
747
|
+
border: none;
|
|
748
|
+
background: transparent;
|
|
749
|
+
color: var(--annot-text-muted);
|
|
750
|
+
cursor: pointer;
|
|
751
|
+
display: flex;
|
|
752
|
+
align-items: center;
|
|
753
|
+
justify-content: center;
|
|
754
|
+
transition:
|
|
755
|
+
background 0.1s,
|
|
756
|
+
color 0.1s;
|
|
757
|
+
}
|
|
758
|
+
.pp-number-spinner button:hover {
|
|
759
|
+
background: var(--annot-hover-bg);
|
|
760
|
+
color: var(--annot-accent);
|
|
761
|
+
}
|
|
762
|
+
.pp-number-spinner button:active {
|
|
763
|
+
background: var(--annot-choice-hover, var(--annot-hover-bg));
|
|
764
|
+
}
|
|
765
|
+
.pp-number-spin-up::before,
|
|
766
|
+
.pp-number-spin-down::before {
|
|
767
|
+
content: "";
|
|
768
|
+
display: block;
|
|
769
|
+
width: 0;
|
|
770
|
+
height: 0;
|
|
771
|
+
border-left: 3px solid transparent;
|
|
772
|
+
border-right: 3px solid transparent;
|
|
773
|
+
}
|
|
774
|
+
.pp-number-spin-up::before {
|
|
775
|
+
border-bottom: 4px solid currentColor;
|
|
776
|
+
}
|
|
777
|
+
.pp-number-spin-down::before {
|
|
778
|
+
border-top: 4px solid currentColor;
|
|
779
|
+
}
|