autumnnote 1.0.6 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/autumnnote.css +129 -1
- package/dist/autumnnote.es.js +401 -36
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +401 -36
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +3 -2
- package/src/js/Context.js +6 -0
- package/src/js/core/sanitise.js +20 -2
- package/src/js/editing/Style.js +120 -7
- package/src/js/module/Buttons.js +5 -4
- package/src/js/module/Clipboard.js +8 -0
- package/src/js/module/CodeTooltip.js +1 -0
- package/src/js/module/ContextMenu.js +146 -8
- package/src/js/module/Editor.js +61 -0
- package/src/js/module/ImageCropOverlay.js +2 -2
- package/src/js/module/ImageDialog.js +17 -2
- package/src/js/module/ImageResizer.js +2 -0
- package/src/js/module/ImageTooltip.js +9 -0
- package/src/js/module/LinkTooltip.js +4 -0
- package/src/js/module/Placeholder.js +2 -1
- package/src/js/module/TableTooltip.js +6 -0
- package/src/js/module/Toolbar.js +21 -3
- package/src/js/module/VideoDialog.js +5 -5
- package/src/js/module/VideoResizer.js +11 -0
- package/src/js/module/VideoTooltip.js +1 -0
- package/src/js/renderer.js +3 -0
- package/src/styles/autumnnote.scss +146 -9
|
@@ -121,9 +121,12 @@ export class ImageDialog {
|
|
|
121
121
|
accept: 'image/*',
|
|
122
122
|
});
|
|
123
123
|
this._fileInput = fileInput;
|
|
124
|
+
// Hint line shown below the file input for format errors
|
|
125
|
+
const fileHint = createElement('p', { class: 'an-dialog-hint' });
|
|
126
|
+
this._fileHint = fileHint;
|
|
124
127
|
const d = on(fileInput, 'change', () => this._onFileChange());
|
|
125
128
|
this._disposers.push(d);
|
|
126
|
-
box.append(fileLabel, fileInput);
|
|
129
|
+
box.append(fileLabel, fileInput, fileHint);
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
// Buttons
|
|
@@ -156,10 +159,22 @@ export class ImageDialog {
|
|
|
156
159
|
const file = this._fileInput && this._fileInput.files && this._fileInput.files[0];
|
|
157
160
|
if (!file || !file.type.startsWith('image/')) return;
|
|
158
161
|
|
|
162
|
+
// C2: Reject image formats browsers cannot display (TIFF, BMP, etc.).
|
|
163
|
+
const UNSUPPORTED = ['image/tiff', 'image/x-tiff', 'image/bmp', 'image/x-bmp', 'image/x-ms-bmp'];
|
|
164
|
+
if (UNSUPPORTED.includes(file.type)) {
|
|
165
|
+
const message = `Format "${file.type}" is not supported for display in web browsers. Please convert to PNG, JPEG, or WebP first.`;
|
|
166
|
+
if (this._fileHint) this._fileHint.textContent = message;
|
|
167
|
+
this.context.triggerEvent('imageError', { file, message });
|
|
168
|
+
this._fileInput.value = '';
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (this._fileHint) this._fileHint.textContent = '';
|
|
172
|
+
|
|
159
173
|
// Validate file size (max 5 MB by default)
|
|
160
174
|
const maxSize = (this.options.maxImageSize || 5) * 1024 * 1024;
|
|
161
175
|
if (file.size > maxSize) {
|
|
162
176
|
const message = `Image file is too large. Maximum allowed size is ${this.options.maxImageSize || 5} MB.`;
|
|
177
|
+
if (this._fileHint) this._fileHint.textContent = message;
|
|
163
178
|
console.warn('[AutumnNote] ImageDialog:', message);
|
|
164
179
|
this.context.triggerEvent('imageError', { file, message });
|
|
165
180
|
this._fileInput.value = '';
|
|
@@ -168,7 +183,7 @@ export class ImageDialog {
|
|
|
168
183
|
|
|
169
184
|
const reader = new FileReader();
|
|
170
185
|
reader.onload = (e) => {
|
|
171
|
-
this._urlInput.value = e.target.result;
|
|
186
|
+
this._urlInput.value = /** @type {string} */ (e.target.result);
|
|
172
187
|
};
|
|
173
188
|
reader.readAsDataURL(file);
|
|
174
189
|
}
|
|
@@ -50,6 +50,7 @@ export class ImageResizer {
|
|
|
50
50
|
on(editable, 'click', (e) => this._onEditorClick(e)),
|
|
51
51
|
// Also select on right-click so the highlight shows before the context menu
|
|
52
52
|
on(editable, 'contextmenu', (e) => {
|
|
53
|
+
if (this.context.layoutInfo.container.classList.contains('an-disabled')) return;
|
|
53
54
|
const img = e.target.closest('img');
|
|
54
55
|
if (img) this._select(img);
|
|
55
56
|
}),
|
|
@@ -127,6 +128,7 @@ export class ImageResizer {
|
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
_onEditorClick(e) {
|
|
131
|
+
if (this.context.layoutInfo.container.classList.contains('an-disabled')) return;
|
|
130
132
|
const img = e.target.closest('img');
|
|
131
133
|
if (img) {
|
|
132
134
|
// Prevent browser from deselecting the current text selection / opening native resize
|
|
@@ -38,6 +38,7 @@ export class ImageTooltip {
|
|
|
38
38
|
|
|
39
39
|
this._disposers.push(
|
|
40
40
|
on(editable, 'mouseover', (e) => {
|
|
41
|
+
if (this.context.layoutInfo.container.classList.contains('an-disabled')) return;
|
|
41
42
|
const img = e.target.closest('img');
|
|
42
43
|
// Skip when the image is inside a link — LinkTooltip takes priority there
|
|
43
44
|
if (img && editable.contains(img) && !img.closest('a[href]')) {
|
|
@@ -227,6 +228,8 @@ export class ImageTooltip {
|
|
|
227
228
|
const target = img.closest('figure.an-figure') || img;
|
|
228
229
|
target.style.float = value;
|
|
229
230
|
target.style.display = '';
|
|
231
|
+
// Clear explicit width set for centering so the figure is free to float
|
|
232
|
+
if (target !== img) target.style.width = '';
|
|
230
233
|
target.style.marginLeft = value === 'right' ? '12px' : '';
|
|
231
234
|
target.style.marginRight = value === 'left' ? '12px' : '';
|
|
232
235
|
this.context.invoke('editor.afterCommand');
|
|
@@ -240,6 +243,9 @@ export class ImageTooltip {
|
|
|
240
243
|
const target = img.closest('figure.an-figure') || img;
|
|
241
244
|
target.style.float = '';
|
|
242
245
|
target.style.display = 'block';
|
|
246
|
+
// C1/C3: use fit-content so the block figure shrinks to the image width;
|
|
247
|
+
// without an explicit width, margin:auto has no effect on a 100%-wide block.
|
|
248
|
+
if (target !== img) target.style.width = 'fit-content';
|
|
243
249
|
target.style.marginLeft = 'auto';
|
|
244
250
|
target.style.marginRight = 'auto';
|
|
245
251
|
this.context.invoke('editor.afterCommand');
|
|
@@ -335,7 +341,10 @@ export class ImageTooltip {
|
|
|
335
341
|
img.style.marginLeft = '';
|
|
336
342
|
img.style.marginRight = '';
|
|
337
343
|
} else if (img.style.display === 'block' && img.style.marginLeft === 'auto') {
|
|
344
|
+
// C1/C3: use fit-content so the centered block figure tightly wraps the
|
|
345
|
+
// image, making margin:auto actually centre it regardless of image width.
|
|
338
346
|
figure.style.display = 'block';
|
|
347
|
+
figure.style.width = 'fit-content';
|
|
339
348
|
figure.style.marginLeft = 'auto';
|
|
340
349
|
figure.style.marginRight = 'auto';
|
|
341
350
|
img.style.display = '';
|
|
@@ -125,9 +125,13 @@ export class LinkTooltip {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
_show(anchor) {
|
|
128
|
+
const isReadOnly = this.context.layoutInfo.container.classList.contains('an-disabled');
|
|
128
129
|
const url = anchor.getAttribute('href') || '';
|
|
129
130
|
this._urlLabel.textContent = this._truncateUrl(url);
|
|
130
131
|
this._urlLabel.title = url;
|
|
132
|
+
// In read-only mode keep only open + copy; hide edit and unlink
|
|
133
|
+
this._editBtn.style.display = isReadOnly ? 'none' : '';
|
|
134
|
+
this._unlinkBtn.style.display = isReadOnly ? 'none' : '';
|
|
131
135
|
this._el.style.display = 'flex';
|
|
132
136
|
this._positionNear(anchor);
|
|
133
137
|
}
|
|
@@ -39,7 +39,8 @@ export class Placeholder {
|
|
|
39
39
|
_update() {
|
|
40
40
|
const editable = this.context.layoutInfo.editable;
|
|
41
41
|
const isFocused = document.activeElement === editable;
|
|
42
|
-
const isEmpty = !editable.textContent.trim() &&
|
|
42
|
+
const isEmpty = !editable.textContent.trim() &&
|
|
43
|
+
!editable.querySelector('img, table, hr, .an-video-wrapper');
|
|
43
44
|
editable.classList.toggle('an-placeholder', isEmpty && !isFocused);
|
|
44
45
|
}
|
|
45
46
|
}
|
|
@@ -105,6 +105,7 @@ export class TableTooltip {
|
|
|
105
105
|
|
|
106
106
|
this._disposers.push(
|
|
107
107
|
on(editable, 'mouseover', (e) => {
|
|
108
|
+
if (this.context.layoutInfo.container.classList.contains('an-disabled')) return;
|
|
108
109
|
const table = e.target.closest('table');
|
|
109
110
|
if (table && editable.contains(table)) {
|
|
110
111
|
const cell = e.target.closest('td, th');
|
|
@@ -169,6 +170,10 @@ export class TableTooltip {
|
|
|
169
170
|
|
|
170
171
|
const onEditorMove = (e) => {
|
|
171
172
|
if (_resizing) return;
|
|
173
|
+
if (this.context.layoutInfo.container.classList.contains('an-disabled')) {
|
|
174
|
+
clearHover();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
172
177
|
// Throttle to one check per animation frame — getBoundingClientRect forces reflow
|
|
173
178
|
if (_rafEditorMove !== null) return;
|
|
174
179
|
const target = e.target;
|
|
@@ -195,6 +200,7 @@ export class TableTooltip {
|
|
|
195
200
|
};
|
|
196
201
|
|
|
197
202
|
const onEditorDown = (e) => {
|
|
203
|
+
if (this.context.layoutInfo.container.classList.contains('an-disabled')) return;
|
|
198
204
|
if (!_nearCell || !_nearEdge) return;
|
|
199
205
|
_resizing = true;
|
|
200
206
|
_edge = _nearEdge;
|
package/src/js/module/Toolbar.js
CHANGED
|
@@ -513,16 +513,34 @@ export class Toolbar {
|
|
|
513
513
|
select.appendChild(opt);
|
|
514
514
|
});
|
|
515
515
|
|
|
516
|
+
// Save the editor selection when the user starts interacting with the
|
|
517
|
+
// dropdown (mousedown fires before the editor loses focus). When the
|
|
518
|
+
// change handler runs, focus has moved to the <select>; we restore the
|
|
519
|
+
// saved range so execCommand / fontSize() act on the intended text.
|
|
520
|
+
/** @type {Range|null} */
|
|
521
|
+
let _savedRange = null;
|
|
522
|
+
const dMousedown = on(select, 'mousedown', () => {
|
|
523
|
+
const sel = window.getSelection();
|
|
524
|
+
_savedRange = (sel && sel.rangeCount) ? sel.getRangeAt(0).cloneRange() : null;
|
|
525
|
+
});
|
|
526
|
+
|
|
516
527
|
const disposer = on(select, 'change', (e) => {
|
|
517
|
-
const value = e.target.value;
|
|
518
|
-
const selectedOpt = e.target.options[e.target.selectedIndex];
|
|
528
|
+
const value = /** @type {HTMLSelectElement} */ (e.target).value;
|
|
529
|
+
const selectedOpt = /** @type {HTMLSelectElement} */ (e.target).options[/** @type {HTMLSelectElement} */ (e.target).selectedIndex];
|
|
519
530
|
if (!value || selectedOpt.disabled) return;
|
|
520
531
|
this.context.invoke('editor.focus');
|
|
532
|
+
// Restore selection saved on mousedown so the action targets the correct text.
|
|
533
|
+
if (_savedRange) {
|
|
534
|
+
try {
|
|
535
|
+
const sel = window.getSelection();
|
|
536
|
+
if (sel) { sel.removeAllRanges(); sel.addRange(_savedRange); }
|
|
537
|
+
} catch (_) { /* range may be stale if DOM changed */ }
|
|
538
|
+
}
|
|
521
539
|
def.action(this.context, value);
|
|
522
540
|
this.context.invoke('editor.afterCommand');
|
|
523
541
|
});
|
|
524
542
|
|
|
525
|
-
this._disposers.push(disposer);
|
|
543
|
+
this._disposers.push(dMousedown, disposer);
|
|
526
544
|
return select;
|
|
527
545
|
}
|
|
528
546
|
|
|
@@ -73,12 +73,12 @@ export class VideoDialog {
|
|
|
73
73
|
// URL input
|
|
74
74
|
const urlLabel = createElement('label', { class: 'an-label' });
|
|
75
75
|
urlLabel.textContent = 'Video URL';
|
|
76
|
-
const urlInput = createElement('input', {
|
|
76
|
+
const urlInput = /** @type {HTMLInputElement} */ (createElement('input', {
|
|
77
77
|
type: 'url',
|
|
78
78
|
class: 'an-input',
|
|
79
79
|
placeholder: 'YouTube, Vimeo, or direct .mp4 URL',
|
|
80
80
|
autocomplete: 'off',
|
|
81
|
-
});
|
|
81
|
+
}));
|
|
82
82
|
this._urlInput = urlInput;
|
|
83
83
|
|
|
84
84
|
// Hint (detected source)
|
|
@@ -88,14 +88,14 @@ export class VideoDialog {
|
|
|
88
88
|
// Width
|
|
89
89
|
const widthLabel = createElement('label', { class: 'an-label' });
|
|
90
90
|
widthLabel.textContent = 'Width (px)';
|
|
91
|
-
const widthInput = createElement('input', {
|
|
91
|
+
const widthInput = /** @type {HTMLInputElement} */ (createElement('input', {
|
|
92
92
|
type: 'number',
|
|
93
93
|
class: 'an-input',
|
|
94
94
|
placeholder: '560',
|
|
95
95
|
min: '80',
|
|
96
96
|
max: '1920',
|
|
97
97
|
value: '560',
|
|
98
|
-
});
|
|
98
|
+
}));
|
|
99
99
|
this._widthInput = widthInput;
|
|
100
100
|
|
|
101
101
|
// Buttons
|
|
@@ -119,7 +119,7 @@ export class VideoDialog {
|
|
|
119
119
|
const d1 = on(insertBtn, 'click', () => this._onInsert());
|
|
120
120
|
const d2 = on(cancelBtn, 'click', () => this._close());
|
|
121
121
|
const d3 = on(overlay, 'click', (e) => { if (e.target === overlay) this._close(); });
|
|
122
|
-
const d4 = on(urlInput, 'keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); this._onInsert(); } });
|
|
122
|
+
const d4 = on(urlInput, 'keydown', (e) => { if (/** @type {KeyboardEvent} */ (e).key === 'Enter') { e.preventDefault(); this._onInsert(); } });
|
|
123
123
|
this._disposers.push(d0, d1, d2, d3, d4);
|
|
124
124
|
|
|
125
125
|
return overlay;
|
|
@@ -41,6 +41,7 @@ export class VideoResizer {
|
|
|
41
41
|
this._disposers.push(
|
|
42
42
|
on(editable, 'click', (e) => this._onEditorClick(e)),
|
|
43
43
|
on(editable, 'contextmenu', (e) => {
|
|
44
|
+
if (this.context.layoutInfo.container.classList.contains('an-disabled')) return;
|
|
44
45
|
const wrapper = this._findWrapper(e.target);
|
|
45
46
|
if (wrapper) this._select(wrapper);
|
|
46
47
|
}),
|
|
@@ -48,6 +49,15 @@ export class VideoResizer {
|
|
|
48
49
|
on(window, 'scroll', () => this._updateOverlayPosition(), { passive: true }),
|
|
49
50
|
on(window, 'resize', onWindowResize),
|
|
50
51
|
on(editable, 'scroll', () => this._updateOverlayPosition(), { passive: true }),
|
|
52
|
+
// D1: Prevent native browser drag of video wrappers. Without this, a user
|
|
53
|
+
// can hold-and-drag to produce a "copy" that lands outside .an-editable,
|
|
54
|
+
// where the .an-video-shield CSS loses its containing context so the
|
|
55
|
+
// copied video becomes directly playable.
|
|
56
|
+
on(editable, 'dragstart', (e) => {
|
|
57
|
+
if (e.target instanceof Element && e.target.closest('.an-video-wrapper')) {
|
|
58
|
+
e.preventDefault();
|
|
59
|
+
}
|
|
60
|
+
}),
|
|
51
61
|
);
|
|
52
62
|
|
|
53
63
|
return this;
|
|
@@ -131,6 +141,7 @@ export class VideoResizer {
|
|
|
131
141
|
}
|
|
132
142
|
|
|
133
143
|
_onEditorClick(e) {
|
|
144
|
+
if (this.context.layoutInfo.container.classList.contains('an-disabled')) return;
|
|
134
145
|
const wrapper = this._findWrapper(e.target);
|
|
135
146
|
if (wrapper) {
|
|
136
147
|
e.preventDefault();
|
|
@@ -37,6 +37,7 @@ export class VideoTooltip {
|
|
|
37
37
|
|
|
38
38
|
this._disposers.push(
|
|
39
39
|
on(editable, 'mouseover', (e) => {
|
|
40
|
+
if (this.context.layoutInfo.container.classList.contains('an-disabled')) return;
|
|
40
41
|
// The shield div sits on top of iframes — we detect hover via it or the wrapper
|
|
41
42
|
const wrapper = e.target.closest('.an-video-wrapper');
|
|
42
43
|
if (wrapper && editable.contains(wrapper)) {
|
package/src/js/renderer.js
CHANGED
|
@@ -78,6 +78,9 @@ export function renderLayout(targetEl, options) {
|
|
|
78
78
|
// Read-only mode
|
|
79
79
|
if (options.readOnly) {
|
|
80
80
|
container.classList.add('an-disabled');
|
|
81
|
+
editable.querySelectorAll('ul.an-checklist input[type="checkbox"]').forEach((cb) => {
|
|
82
|
+
cb.setAttribute('disabled', '');
|
|
83
|
+
});
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
// Text direction
|
|
@@ -50,6 +50,20 @@
|
|
|
50
50
|
user-select: text;
|
|
51
51
|
// Subtle visual cue that the area is read-only.
|
|
52
52
|
background: #fafafa;
|
|
53
|
+
|
|
54
|
+
// Block checklist checkbox interaction in read-only mode.
|
|
55
|
+
ul.an-checklist input[type='checkbox'] {
|
|
56
|
+
pointer-events: none;
|
|
57
|
+
cursor: default;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Prevent images and videos from being dragged out.
|
|
61
|
+
img,
|
|
62
|
+
.an-video-wrapper {
|
|
63
|
+
-webkit-user-drag: none;
|
|
64
|
+
user-drag: none;
|
|
65
|
+
pointer-events: none;
|
|
66
|
+
}
|
|
53
67
|
}
|
|
54
68
|
}
|
|
55
69
|
|
|
@@ -87,6 +101,12 @@
|
|
|
87
101
|
align-items: center;
|
|
88
102
|
gap: 2px;
|
|
89
103
|
|
|
104
|
+
// Icon-only toolbar buttons should not have horizontal padding so all
|
|
105
|
+
// buttons appear consistently square (min-width = height = $an-btn-height).
|
|
106
|
+
// Dialog buttons (.an-dialog-actions .an-btn) keep their padding via the
|
|
107
|
+
// lower-specificity .an-btn rule.
|
|
108
|
+
> .an-btn { padding: 0; }
|
|
109
|
+
|
|
90
110
|
// Separator between groups
|
|
91
111
|
& + .an-btn-group {
|
|
92
112
|
position: relative;
|
|
@@ -388,6 +408,7 @@
|
|
|
388
408
|
// =============================================================================
|
|
389
409
|
|
|
390
410
|
.an-editable {
|
|
411
|
+
position: relative; // Establishes containing block for the ::before placeholder
|
|
391
412
|
flex: 1;
|
|
392
413
|
padding: $an-editable-pad;
|
|
393
414
|
min-height: 0;
|
|
@@ -397,18 +418,18 @@
|
|
|
397
418
|
overflow-y: auto;
|
|
398
419
|
word-break: break-word;
|
|
399
420
|
|
|
400
|
-
// Placeholder — only
|
|
401
|
-
//
|
|
402
|
-
//
|
|
403
|
-
//
|
|
404
|
-
// Avoid position:absolute here — without position:relative on the parent it
|
|
405
|
-
// would be positioned relative to .an-container (contain:layout) and overlap
|
|
406
|
-
// the toolbar.
|
|
421
|
+
// Placeholder — shown only when the editable is empty and not focused.
|
|
422
|
+
// Uses position:absolute so it never shifts the cursor or block content,
|
|
423
|
+
// and requires position:relative on this element (added above) so it is
|
|
424
|
+
// scoped to the editable area and never overlaps the toolbar.
|
|
407
425
|
&.an-placeholder:not(:focus)::before {
|
|
408
426
|
content: attr(data-placeholder);
|
|
409
427
|
color: $an-muted;
|
|
410
428
|
pointer-events: none;
|
|
411
|
-
|
|
429
|
+
position: absolute;
|
|
430
|
+
top: 12px; // matches top padding from $an-editable-pad (12px 14px)
|
|
431
|
+
left: 14px; // matches left padding
|
|
432
|
+
right: 14px;
|
|
412
433
|
}
|
|
413
434
|
|
|
414
435
|
// Content styles
|
|
@@ -472,9 +493,21 @@
|
|
|
472
493
|
inset: 0;
|
|
473
494
|
z-index: 2;
|
|
474
495
|
cursor: default;
|
|
496
|
+
pointer-events: all; // Explicitly block pointer events reaching the iframe
|
|
475
497
|
}
|
|
476
498
|
}
|
|
477
499
|
|
|
500
|
+
// Ensure video shield stays effective even when a video copy moves outside
|
|
501
|
+
// .an-editable temporarily (e.g. native drag-and-drop).
|
|
502
|
+
// This global rule mirrors the scoped one above.
|
|
503
|
+
@at-root .an-video-wrapper .an-video-shield {
|
|
504
|
+
position: absolute;
|
|
505
|
+
inset: 0;
|
|
506
|
+
z-index: 2;
|
|
507
|
+
cursor: default;
|
|
508
|
+
pointer-events: all;
|
|
509
|
+
}
|
|
510
|
+
|
|
478
511
|
// Table styles — target both classed and unclassed tables
|
|
479
512
|
table, .an-table {
|
|
480
513
|
border-collapse: collapse;
|
|
@@ -934,6 +967,91 @@
|
|
|
934
967
|
background: $an-border;
|
|
935
968
|
}
|
|
936
969
|
|
|
970
|
+
// Color-strip variant — SVG stacked above a thin color bar (matches toolbar)
|
|
971
|
+
.an-context-icon--color {
|
|
972
|
+
flex-direction: column;
|
|
973
|
+
gap: 2px;
|
|
974
|
+
width: 16px;
|
|
975
|
+
align-items: center;
|
|
976
|
+
|
|
977
|
+
.an-context-icon-svg {
|
|
978
|
+
display: flex;
|
|
979
|
+
align-items: center;
|
|
980
|
+
justify-content: center;
|
|
981
|
+
line-height: 0;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
.an-context-color-strip {
|
|
986
|
+
display: block;
|
|
987
|
+
width: 100%;
|
|
988
|
+
height: 3px;
|
|
989
|
+
border-radius: 1px;
|
|
990
|
+
flex-shrink: 0;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// Color palette inside context menu
|
|
994
|
+
.an-context-color-palette {
|
|
995
|
+
display: flex;
|
|
996
|
+
flex-wrap: wrap;
|
|
997
|
+
gap: 3px;
|
|
998
|
+
padding: 6px 12px 2px;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
.an-context-color-swatch {
|
|
1002
|
+
width: 15px;
|
|
1003
|
+
height: 15px;
|
|
1004
|
+
border-radius: 3px;
|
|
1005
|
+
border: 1px solid rgba(0, 0, 0, 0.12);
|
|
1006
|
+
cursor: pointer;
|
|
1007
|
+
flex-shrink: 0;
|
|
1008
|
+
position: relative;
|
|
1009
|
+
transition: transform 0.1s;
|
|
1010
|
+
|
|
1011
|
+
&:hover {
|
|
1012
|
+
transform: scale(1.28);
|
|
1013
|
+
z-index: 1;
|
|
1014
|
+
border-color: rgba(0, 0, 0, 0.4);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
.an-context-color-none {
|
|
1019
|
+
display: flex;
|
|
1020
|
+
align-items: center;
|
|
1021
|
+
justify-content: center;
|
|
1022
|
+
background: #fff !important;
|
|
1023
|
+
color: #999;
|
|
1024
|
+
|
|
1025
|
+
svg { display: block; }
|
|
1026
|
+
|
|
1027
|
+
&:hover { color: #333; }
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
.an-context-color-custom {
|
|
1031
|
+
display: flex;
|
|
1032
|
+
align-items: center;
|
|
1033
|
+
gap: 8px;
|
|
1034
|
+
padding: 4px 12px 8px;
|
|
1035
|
+
font-size: 12px;
|
|
1036
|
+
color: $an-muted;
|
|
1037
|
+
cursor: pointer;
|
|
1038
|
+
|
|
1039
|
+
input[type='color'] {
|
|
1040
|
+
width: 18px;
|
|
1041
|
+
height: 18px;
|
|
1042
|
+
padding: 0;
|
|
1043
|
+
border: 1px solid $an-border;
|
|
1044
|
+
border-radius: 3px;
|
|
1045
|
+
cursor: pointer;
|
|
1046
|
+
background: transparent;
|
|
1047
|
+
-webkit-appearance: none;
|
|
1048
|
+
appearance: none;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
span { flex: 1; }
|
|
1052
|
+
&:hover span { color: $an-text; }
|
|
1053
|
+
}
|
|
1054
|
+
|
|
937
1055
|
// ---------------------------------------------------------------------------
|
|
938
1056
|
// Image resize overlay
|
|
939
1057
|
// ---------------------------------------------------------------------------
|
|
@@ -1378,7 +1496,11 @@ $an-video-accent: #8b5cf6; // violet-500
|
|
|
1378
1496
|
margin: 0.75em 0;
|
|
1379
1497
|
text-align: center;
|
|
1380
1498
|
|
|
1381
|
-
img {
|
|
1499
|
+
img {
|
|
1500
|
+
display: block;
|
|
1501
|
+
max-width: 100%;
|
|
1502
|
+
margin: 0 auto; // Centers img when figure uses fit-content or explicit width
|
|
1503
|
+
}
|
|
1382
1504
|
}
|
|
1383
1505
|
|
|
1384
1506
|
figcaption.an-figcaption {
|
|
@@ -1650,6 +1772,21 @@ $an-video-accent: #8b5cf6; // violet-500
|
|
|
1650
1772
|
.an-context-item { color: #cdd6f4; }
|
|
1651
1773
|
.an-context-sep { background: #3f3f5f; }
|
|
1652
1774
|
|
|
1775
|
+
.an-context-color-swatch {
|
|
1776
|
+
border-color: rgba(255, 255, 255, 0.15);
|
|
1777
|
+
&:hover { border-color: rgba(255, 255, 255, 0.5); }
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
.an-context-color-none {
|
|
1781
|
+
background: #1e1e2e !important;
|
|
1782
|
+
color: #888;
|
|
1783
|
+
&:hover { color: #ccc; }
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
.an-context-color-custom {
|
|
1787
|
+
input[type='color'] { border-color: #3f3f5f; }
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1653
1790
|
.an-icon-cat {
|
|
1654
1791
|
border-color: #3f3f5f;
|
|
1655
1792
|
color: #a6adc8;
|