smartrte-react 1.0.0-beta.1 → 1.0.0-beta.10
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 +45 -0
- package/README.md +99 -0
- package/dist/canonicalEditorRuntime.d.ts +12 -0
- package/dist/canonicalEditorRuntime.js +2 -1
- package/dist/components/CanonicalAuthorityEditor.d.ts +22 -1
- package/dist/components/CanonicalAuthorityEditor.js +271 -57
- package/dist/components/ColorPickerPopover.js +3 -1
- package/dist/components/MediaDetailsPopover.d.ts +35 -0
- package/dist/components/MediaDetailsPopover.js +136 -0
- package/dist/components/MediaManager.js +5 -5
- package/dist/components/MediaOverlay.d.ts +12 -21
- package/dist/components/MediaOverlay.js +129 -64
- package/dist/components/TableBorderPopover.js +3 -1
- package/dist/components/TableResizeHandles.js +22 -0
- package/dist/components/ToolbarPrimitives.d.ts +32 -0
- package/dist/components/ToolbarPrimitives.js +95 -4
- package/dist/components/fixedPositioning.d.ts +25 -0
- package/dist/components/fixedPositioning.js +42 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/standalone/classic-editor-embed.js +1 -1
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js +90 -33
- package/dist/toolbarTools.d.ts +95 -0
- package/dist/toolbarTools.js +17 -0
- package/package.json +2 -2
|
@@ -60,6 +60,9 @@ export declare const toolbarIcons: {
|
|
|
60
60
|
readonly undo: import("react/jsx-runtime").JSX.Element;
|
|
61
61
|
readonly redo: import("react/jsx-runtime").JSX.Element;
|
|
62
62
|
readonly more: import("react/jsx-runtime").JSX.Element;
|
|
63
|
+
readonly divider: import("react/jsx-runtime").JSX.Element;
|
|
64
|
+
readonly pageBreak: import("react/jsx-runtime").JSX.Element;
|
|
65
|
+
readonly lineHeight: import("react/jsx-runtime").JSX.Element;
|
|
63
66
|
};
|
|
64
67
|
export type ToolbarIconKey = keyof typeof toolbarIcons;
|
|
65
68
|
/** A single always-visible toolbar control: icon + plain-language label together, per the redesign's core principle. */
|
|
@@ -101,6 +104,21 @@ export declare function ToolbarMenuItem({ icon, label, ariaLabel, pressed, disab
|
|
|
101
104
|
* keyboard support (Enter/Space to open, native focus handling) without a
|
|
102
105
|
* new open/close state to manage; outside-click dismiss is handled by
|
|
103
106
|
* useDismissDetailsOnOutsideClick above.
|
|
107
|
+
*
|
|
108
|
+
* The panel itself renders `position: fixed` with JS-measured coordinates
|
|
109
|
+
* (docs/bugs/toolbar-dropdown-clipped-by-host-overflow-hidden.md), the same
|
|
110
|
+
* pattern ColorPickerPopover/TableBorderPopover already use - a real host
|
|
111
|
+
* can (and, reported live, does) wrap the editor in an `overflow: hidden`
|
|
112
|
+
* container for its own layout reasons; a `position: absolute` panel
|
|
113
|
+
* anchored inside that container gets visually clipped the moment it needs
|
|
114
|
+
* to extend past the container's own bounds, which is exactly when a
|
|
115
|
+
* narrow host is most likely to need the dropdown in the first place.
|
|
116
|
+
* `position: fixed`'s containing block is normally the viewport, so this
|
|
117
|
+
* escapes ANY ancestor's overflow clipping regardless of DOM depth - unless
|
|
118
|
+
* an ancestor has its own transform/perspective/filter, which becomes the
|
|
119
|
+
* containing block instead (see fixedPositioning.ts, and
|
|
120
|
+
* docs/bugs/toolbar-menu-misplaced-inside-transformed-ancestor.md); the
|
|
121
|
+
* placement math below accounts for that case too.
|
|
104
122
|
*/
|
|
105
123
|
export declare function ToolbarDropdown({ icon, label, priority, children }: {
|
|
106
124
|
icon?: ToolbarIconKey;
|
|
@@ -123,6 +141,20 @@ export declare function ToolbarGroup({ priority, children }: {
|
|
|
123
141
|
* primary row starts shedding groups - this is plain CSS visibility
|
|
124
142
|
* toggling of pre-rendered content, not JS media-query logic, matching how
|
|
125
143
|
* the rest of this responsive system already works.
|
|
144
|
+
*
|
|
145
|
+
* The panel itself renders `position: fixed` with JS-measured coordinates,
|
|
146
|
+
* the same fix and reasoning as `ToolbarDropdown` (docs/bugs/
|
|
147
|
+
* toolbar-dropdown-clipped-by-host-overflow-hidden.md) - this component was
|
|
148
|
+
* NOT covered by that fix at the time (incorrectly assumed to be
|
|
149
|
+
* unaffected since it already had its own separate mobile CSS), but it has
|
|
150
|
+
* the exact same `position: absolute` vulnerability to a host's own
|
|
151
|
+
* `overflow: hidden` container, and since this is the ONLY toolbar
|
|
152
|
+
* overflow affordance on narrow viewports (every ToolbarDropdown hides
|
|
153
|
+
* entirely below the mobile breakpoint), a clipped host is more likely to
|
|
154
|
+
* matter here, not less. Confirmed live: a real narrow host wrapping the
|
|
155
|
+
* editor in `overflow: hidden` computed this panel at `left: -214px` -
|
|
156
|
+
* mostly off-screen, functionally invisible despite being technically
|
|
157
|
+
* present in the DOM.
|
|
126
158
|
*/
|
|
127
159
|
export declare function MobileMoreMenu({ children }: {
|
|
128
160
|
children: React.ReactNode;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef } from "react";
|
|
3
|
-
import { AlignCenter, AlignJustify, AlignLeft, AlignRight, ALargeSmall, Baseline, Bold, CheckSquare, ChevronDown, Code, Columns3, Download, Eye, FileJson, History, Image as ImageIcon, IndentDecrease, IndentIncrease, Italic, Link2, ListChecks, ListOrdered, ListRestart, List as ListIcon, MessageSquare, MessageSquarePlus, MoreHorizontal, MoveDown, MoveLeft, MoveRight, MoveUp, Music, Omega, PanelTop, PenLine, Pilcrow, Quote, Redo2, Rows3, Square, Sigma, Strikethrough, Subscript, Superscript, Table2, Trash2, Type, Underline, Undo2, Unlink2, Upload, Video, Pencil, ZoomIn, ZoomOut, } from "lucide-react";
|
|
2
|
+
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
+
import { AlignCenter, AlignJustify, AlignLeft, AlignRight, AlignVerticalSpaceAround, ALargeSmall, Baseline, Bold, CheckSquare, ChevronDown, Code, Columns3, Download, Eye, FileJson, History, Image as ImageIcon, IndentDecrease, IndentIncrease, Italic, Link2, ListChecks, ListOrdered, ListRestart, List as ListIcon, MessageSquare, MessageSquarePlus, MoreHorizontal, MoveDown, MoveLeft, MoveRight, MoveUp, Music, Omega, PanelTop, PenLine, Pilcrow, Quote, Redo2, Rows3, ScissorsLineDashed, SeparatorHorizontal, Square, Sigma, Strikethrough, Subscript, Superscript, Table2, Trash2, Type, Underline, Undo2, Unlink2, Upload, Video, Pencil, ZoomIn, ZoomOut, } from "lucide-react";
|
|
4
|
+
import { getFixedPositioningOrigin } from "./fixedPositioning.js";
|
|
4
5
|
/**
|
|
5
6
|
* The toolbar's icon set (Phase: Direction B toolbar redesign, 2026-08-29).
|
|
6
7
|
* Lucide, per the design proposal's §11 recommendation - MIT-licensed,
|
|
@@ -74,6 +75,9 @@ export const toolbarIcons = {
|
|
|
74
75
|
undo: _jsx(Undo2, { ...stroke }),
|
|
75
76
|
redo: _jsx(Redo2, { ...stroke }),
|
|
76
77
|
more: _jsx(MoreHorizontal, { ...stroke }),
|
|
78
|
+
divider: _jsx(SeparatorHorizontal, { ...stroke }),
|
|
79
|
+
pageBreak: _jsx(ScissorsLineDashed, { ...stroke }),
|
|
80
|
+
lineHeight: _jsx(AlignVerticalSpaceAround, { ...stroke }),
|
|
77
81
|
};
|
|
78
82
|
/** A single always-visible toolbar control: icon + plain-language label together, per the redesign's core principle. */
|
|
79
83
|
export function ToolbarButton({ icon, label, ariaLabel, pressed, disabled, title, onClick, iconOnly, widePromote }) {
|
|
@@ -119,11 +123,53 @@ const useDismissDetailsOnOutsideClick = (ref) => {
|
|
|
119
123
|
* keyboard support (Enter/Space to open, native focus handling) without a
|
|
120
124
|
* new open/close state to manage; outside-click dismiss is handled by
|
|
121
125
|
* useDismissDetailsOnOutsideClick above.
|
|
126
|
+
*
|
|
127
|
+
* The panel itself renders `position: fixed` with JS-measured coordinates
|
|
128
|
+
* (docs/bugs/toolbar-dropdown-clipped-by-host-overflow-hidden.md), the same
|
|
129
|
+
* pattern ColorPickerPopover/TableBorderPopover already use - a real host
|
|
130
|
+
* can (and, reported live, does) wrap the editor in an `overflow: hidden`
|
|
131
|
+
* container for its own layout reasons; a `position: absolute` panel
|
|
132
|
+
* anchored inside that container gets visually clipped the moment it needs
|
|
133
|
+
* to extend past the container's own bounds, which is exactly when a
|
|
134
|
+
* narrow host is most likely to need the dropdown in the first place.
|
|
135
|
+
* `position: fixed`'s containing block is normally the viewport, so this
|
|
136
|
+
* escapes ANY ancestor's overflow clipping regardless of DOM depth - unless
|
|
137
|
+
* an ancestor has its own transform/perspective/filter, which becomes the
|
|
138
|
+
* containing block instead (see fixedPositioning.ts, and
|
|
139
|
+
* docs/bugs/toolbar-menu-misplaced-inside-transformed-ancestor.md); the
|
|
140
|
+
* placement math below accounts for that case too.
|
|
122
141
|
*/
|
|
123
142
|
export function ToolbarDropdown({ icon, label, priority, children }) {
|
|
124
143
|
const detailsRef = useRef(null);
|
|
125
144
|
useDismissDetailsOnOutsideClick(detailsRef);
|
|
126
|
-
|
|
145
|
+
const [placement, setPlacement] = useState(null);
|
|
146
|
+
useLayoutEffect(() => {
|
|
147
|
+
const details = detailsRef.current;
|
|
148
|
+
if (!details)
|
|
149
|
+
return;
|
|
150
|
+
const recompute = () => {
|
|
151
|
+
if (!details.open) {
|
|
152
|
+
setPlacement(null);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const margin = 8;
|
|
156
|
+
const triggerRect = details.getBoundingClientRect();
|
|
157
|
+
const menu = details.querySelector(":scope > .srte-menu");
|
|
158
|
+
const menuWidth = menu?.offsetWidth ?? 210;
|
|
159
|
+
const menuHeight = menu?.offsetHeight ?? 0;
|
|
160
|
+
const viewportWidth = window.innerWidth;
|
|
161
|
+
const viewportHeight = window.innerHeight;
|
|
162
|
+
const left = Math.min(Math.max(margin, triggerRect.left), Math.max(margin, viewportWidth - menuWidth - margin));
|
|
163
|
+
const overflowsBottom = triggerRect.bottom + 6 + menuHeight > viewportHeight - margin;
|
|
164
|
+
const top = overflowsBottom ? Math.max(margin, triggerRect.top - menuHeight - 6) : triggerRect.bottom + 6;
|
|
165
|
+
const origin = getFixedPositioningOrigin(details);
|
|
166
|
+
setPlacement({ left: left - origin.left, top: top - origin.top });
|
|
167
|
+
};
|
|
168
|
+
recompute();
|
|
169
|
+
details.addEventListener("toggle", recompute);
|
|
170
|
+
return () => details.removeEventListener("toggle", recompute);
|
|
171
|
+
}, []);
|
|
172
|
+
return _jsxs("details", { ref: detailsRef, className: "srte-toolbar-menu", ...(priority ? { "data-srte-priority": priority } : {}), children: [_jsxs("summary", { className: "srte-tool-button srte-menu-trigger", onMouseDown: (event) => event.preventDefault(), children: [icon ? toolbarIcons[icon] : null, _jsx("span", { children: label }), _jsx(ChevronDown, { width: 13, height: 13, strokeWidth: 2, "aria-hidden": "true" })] }), _jsx("div", { className: "srte-menu", role: "menu", "data-srte-menu-fixed": "true", style: { left: placement?.left ?? 0, top: placement?.top ?? 0, visibility: placement ? "visible" : "hidden" }, children: children })] });
|
|
127
173
|
}
|
|
128
174
|
/** A visually-separated cluster of controls, optionally collapsing as a whole unit on narrow viewports (theme.ts's `.srte-toolbar-group[data-srte-priority="3"]`). */
|
|
129
175
|
export function ToolbarGroup({ priority, children }) {
|
|
@@ -138,9 +184,54 @@ export function ToolbarGroup({ priority, children }) {
|
|
|
138
184
|
* primary row starts shedding groups - this is plain CSS visibility
|
|
139
185
|
* toggling of pre-rendered content, not JS media-query logic, matching how
|
|
140
186
|
* the rest of this responsive system already works.
|
|
187
|
+
*
|
|
188
|
+
* The panel itself renders `position: fixed` with JS-measured coordinates,
|
|
189
|
+
* the same fix and reasoning as `ToolbarDropdown` (docs/bugs/
|
|
190
|
+
* toolbar-dropdown-clipped-by-host-overflow-hidden.md) - this component was
|
|
191
|
+
* NOT covered by that fix at the time (incorrectly assumed to be
|
|
192
|
+
* unaffected since it already had its own separate mobile CSS), but it has
|
|
193
|
+
* the exact same `position: absolute` vulnerability to a host's own
|
|
194
|
+
* `overflow: hidden` container, and since this is the ONLY toolbar
|
|
195
|
+
* overflow affordance on narrow viewports (every ToolbarDropdown hides
|
|
196
|
+
* entirely below the mobile breakpoint), a clipped host is more likely to
|
|
197
|
+
* matter here, not less. Confirmed live: a real narrow host wrapping the
|
|
198
|
+
* editor in `overflow: hidden` computed this panel at `left: -214px` -
|
|
199
|
+
* mostly off-screen, functionally invisible despite being technically
|
|
200
|
+
* present in the DOM.
|
|
141
201
|
*/
|
|
142
202
|
export function MobileMoreMenu({ children }) {
|
|
143
203
|
const detailsRef = useRef(null);
|
|
144
204
|
useDismissDetailsOnOutsideClick(detailsRef);
|
|
145
|
-
|
|
205
|
+
const [placement, setPlacement] = useState(null);
|
|
206
|
+
useLayoutEffect(() => {
|
|
207
|
+
const details = detailsRef.current;
|
|
208
|
+
if (!details)
|
|
209
|
+
return;
|
|
210
|
+
const recompute = () => {
|
|
211
|
+
if (!details.open) {
|
|
212
|
+
setPlacement(null);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const margin = 8;
|
|
216
|
+
const triggerRect = details.getBoundingClientRect();
|
|
217
|
+
const menu = details.querySelector(":scope > .srte-menu");
|
|
218
|
+
const menuWidth = menu?.offsetWidth ?? Math.min(280, window.innerWidth - 16);
|
|
219
|
+
const menuHeight = menu?.offsetHeight ?? 0;
|
|
220
|
+
const viewportWidth = window.innerWidth;
|
|
221
|
+
const viewportHeight = window.innerHeight;
|
|
222
|
+
// Right-aligned to the trigger, matching this menu's original
|
|
223
|
+
// CSS-only `right: 0` intent - clamped into the viewport instead of
|
|
224
|
+
// being allowed to run off it or get clipped by an ancestor.
|
|
225
|
+
const preferredLeft = triggerRect.right - menuWidth;
|
|
226
|
+
const left = Math.min(Math.max(margin, preferredLeft), Math.max(margin, viewportWidth - menuWidth - margin));
|
|
227
|
+
const overflowsBottom = triggerRect.bottom + 6 + menuHeight > viewportHeight - margin;
|
|
228
|
+
const top = overflowsBottom ? Math.max(margin, triggerRect.top - menuHeight - 6) : triggerRect.bottom + 6;
|
|
229
|
+
const origin = getFixedPositioningOrigin(details);
|
|
230
|
+
setPlacement({ left: left - origin.left, top: top - origin.top });
|
|
231
|
+
};
|
|
232
|
+
recompute();
|
|
233
|
+
details.addEventListener("toggle", recompute);
|
|
234
|
+
return () => details.removeEventListener("toggle", recompute);
|
|
235
|
+
}, []);
|
|
236
|
+
return _jsxs("details", { ref: detailsRef, className: "srte-toolbar-menu srte-mobile-more", children: [_jsx("summary", { className: "srte-tool-button srte-menu-trigger", "aria-label": "More tools", onMouseDown: (event) => event.preventDefault(), children: toolbarIcons.more }), _jsx("div", { className: "srte-menu", role: "menu", "data-srte-menu-fixed": "true", style: { left: placement?.left ?? 0, top: placement?.top ?? 0, visibility: placement ? "visible" : "hidden" }, children: children })] });
|
|
146
237
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A CSS `transform`, `perspective`, `filter`, `backdrop-filter`, or a
|
|
3
|
+
* `will-change` naming one of those, on an ancestor makes that ancestor the
|
|
4
|
+
* containing block for `position: fixed` descendants instead of the
|
|
5
|
+
* viewport (CSS Transforms spec's rendering model) - most commonly a
|
|
6
|
+
* Radix/shadcn `Dialog`'s centering transform (`translate-x-[-50%]
|
|
7
|
+
* translate-y-[-50%]`), which any host may legitimately wrap this editor
|
|
8
|
+
* in. When that happens, `left`/`top` set on a fixed-positioned menu/popover
|
|
9
|
+
* are resolved against that ancestor's border box (as rendered,
|
|
10
|
+
* post-transform) instead of the viewport, silently producing coordinates
|
|
11
|
+
* that are internally consistent but land far from the trigger with no
|
|
12
|
+
* error - confirmed live via a Sootr host rendering the editor inside such
|
|
13
|
+
* a dialog.
|
|
14
|
+
*
|
|
15
|
+
* `getBoundingClientRect()` always returns viewport-relative coordinates
|
|
16
|
+
* regardless of transforms, so every placement calculation in this codebase
|
|
17
|
+
* computes coordinates in viewport space first, then subtracts this
|
|
18
|
+
* origin - the real containing block's own viewport-relative top-left - so
|
|
19
|
+
* the applied `left`/`top` land correctly under either containing block.
|
|
20
|
+
*/
|
|
21
|
+
export declare function findFixedPositioningContainer(el: HTMLElement): HTMLElement | null;
|
|
22
|
+
export declare function getFixedPositioningOrigin(el: HTMLElement): {
|
|
23
|
+
left: number;
|
|
24
|
+
top: number;
|
|
25
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A CSS `transform`, `perspective`, `filter`, `backdrop-filter`, or a
|
|
3
|
+
* `will-change` naming one of those, on an ancestor makes that ancestor the
|
|
4
|
+
* containing block for `position: fixed` descendants instead of the
|
|
5
|
+
* viewport (CSS Transforms spec's rendering model) - most commonly a
|
|
6
|
+
* Radix/shadcn `Dialog`'s centering transform (`translate-x-[-50%]
|
|
7
|
+
* translate-y-[-50%]`), which any host may legitimately wrap this editor
|
|
8
|
+
* in. When that happens, `left`/`top` set on a fixed-positioned menu/popover
|
|
9
|
+
* are resolved against that ancestor's border box (as rendered,
|
|
10
|
+
* post-transform) instead of the viewport, silently producing coordinates
|
|
11
|
+
* that are internally consistent but land far from the trigger with no
|
|
12
|
+
* error - confirmed live via a Sootr host rendering the editor inside such
|
|
13
|
+
* a dialog.
|
|
14
|
+
*
|
|
15
|
+
* `getBoundingClientRect()` always returns viewport-relative coordinates
|
|
16
|
+
* regardless of transforms, so every placement calculation in this codebase
|
|
17
|
+
* computes coordinates in viewport space first, then subtracts this
|
|
18
|
+
* origin - the real containing block's own viewport-relative top-left - so
|
|
19
|
+
* the applied `left`/`top` land correctly under either containing block.
|
|
20
|
+
*/
|
|
21
|
+
export function findFixedPositioningContainer(el) {
|
|
22
|
+
let node = el.parentElement;
|
|
23
|
+
while (node) {
|
|
24
|
+
const style = window.getComputedStyle(node);
|
|
25
|
+
if ((style.transform && style.transform !== "none") ||
|
|
26
|
+
(style.perspective && style.perspective !== "none") ||
|
|
27
|
+
(style.filter && style.filter !== "none") ||
|
|
28
|
+
(style.backdropFilter && style.backdropFilter !== "none") ||
|
|
29
|
+
(style.willChange && /transform|perspective|filter/.test(style.willChange))) {
|
|
30
|
+
return node;
|
|
31
|
+
}
|
|
32
|
+
node = node.parentElement;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
export function getFixedPositioningOrigin(el) {
|
|
37
|
+
const container = findFixedPositioningContainer(el);
|
|
38
|
+
if (!container)
|
|
39
|
+
return { left: 0, top: 0 };
|
|
40
|
+
const rect = container.getBoundingClientRect();
|
|
41
|
+
return { left: rect.left, top: rect.top };
|
|
42
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,9 @@ export { ClassicEditor } from './components/ClassicEditorAuthority.js';
|
|
|
2
2
|
export type { ClassicEditorProps } from './components/ClassicEditorAuthority.js';
|
|
3
3
|
export { CanonicalAuthorityEditor } from './components/CanonicalAuthorityEditor.js';
|
|
4
4
|
export type { CanonicalAuthorityEditorProps } from './components/CanonicalAuthorityEditor.js';
|
|
5
|
+
export type { EditorCapabilityPreset } from './capabilityPresets.js';
|
|
6
|
+
export type { ToolbarTools } from './toolbarTools.js';
|
|
7
|
+
export { DEFAULT_TOOLBAR_TOOLS, resolveToolbarTools } from './toolbarTools.js';
|
|
5
8
|
export { CanonicalEditorRuntime, createCanonicalEditorRuntime } from './canonicalEditorRuntime.js';
|
|
6
9
|
export type { SmartEditorChange, SmartEditorCheckpoint, SmartEditorHandle } from './canonicalEditorRuntime.js';
|
|
7
10
|
export type { MediaManagerAdapter, MediaItem, MediaSearchQuery } from './components/MediaManager.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { ClassicEditor } from './components/ClassicEditorAuthority.js';
|
|
2
2
|
export { CanonicalAuthorityEditor } from './components/CanonicalAuthorityEditor.js';
|
|
3
|
+
export { DEFAULT_TOOLBAR_TOOLS, resolveToolbarTools } from './toolbarTools.js';
|
|
3
4
|
export { CanonicalEditorRuntime, createCanonicalEditorRuntime } from './canonicalEditorRuntime.js';
|
|
4
5
|
export { DefaultMediaPicker } from './components/MediaPicker.js';
|
|
5
6
|
export { SRTE_DEFAULT_CSS, ensureStyleSheet } from './theme.js';
|
|
@@ -44,7 +44,7 @@ function ClassicEditorHost(props, ref) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
catch { }
|
|
47
|
-
}, placeholder: props.placeholder, minHeight: props.minHeight, maxHeight: props.maxHeight, readOnly: props.readOnly, table: props.table, media: props.media, formula: props.formula, showFontSize: props.showFontSize, theme: props.theme, className: props.className }) }));
|
|
47
|
+
}, placeholder: props.placeholder, minHeight: props.minHeight, maxHeight: props.maxHeight, readOnly: props.readOnly, table: props.table, media: props.media, formula: props.formula, showFontSize: props.showFontSize, tools: props.tools, theme: props.theme, className: props.className }) }));
|
|
48
48
|
}
|
|
49
49
|
const ClassicEditorHostWithRef = React.forwardRef(ClassicEditorHost);
|
|
50
50
|
function initClassicEditor(opts) {
|
package/dist/theme.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export type SrteTheme = 'light' | 'dark';
|
|
2
|
-
export declare const SRTE_DEFAULT_CSS = "\n.srte-editor {\n --srte-background: var(--card, #ffffff);\n --srte-canvas: var(--background, #ffffff);\n --srte-foreground: var(--foreground, #0f172a);\n --srte-muted: var(--muted, #f1f5f9);\n --srte-muted-foreground: var(--muted-foreground, #64748b);\n --srte-ring: var(--ring, #0284c7);\n --srte-radius: var(--radius, 0.625rem);\n --srte-bg: var(--srte-canvas);\n --srte-text: var(--srte-foreground);\n --srte-text-muted: var(--srte-muted-foreground);\n --srte-border: var(--border, #e2e8f0);\n --srte-border-light: var(--srte-border);\n --srte-toolbar-bg: var(--srte-background);\n --srte-input-bg: var(--srte-background);\n --srte-input-text: var(--srte-foreground);\n --srte-input-border: var(--srte-border);\n --srte-modal-backdrop: rgba(0, 0, 0, 0.35);\n --srte-modal-backdrop-filter: blur(2px);\n --srte-modal-bg: #ffffff;\n --srte-modal-text: #000000;\n --srte-menu-bg: var(--srte-background);\n --srte-menu-text: var(--srte-foreground);\n --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n --srte-accent: #0284c7;\n --srte-accent-bg: rgba(2, 132, 199, 0.12);\n --srte-danger: #dc2626;\n --srte-primary: #2563eb;\n /* --srte-primary on --srte-accent-bg measures 4.43:1, under WCAG AA's\n 4.5:1 for normal text - used only for the pressed toolbar-button state\n (see .srte-tool-button[aria-pressed=\"true\"]), not a replacement for\n --srte-primary generally. */\n --srte-primary-pressed: #1d4ed8;\n --srte-surface-subtle: #f3f4f6;\n --srte-on-primary: #ffffff;\n --srte-cancel-bg: #f3f4f6;\n --srte-code-bg: #f6f8fa;\n --srte-code-text: #24292f;\n}\n.srte-editor.srte-dark {\n --srte-background: var(--card, #1e293b);\n --srte-canvas: var(--background, #0f172a);\n --srte-foreground: var(--foreground, #f8fafc);\n --srte-muted: var(--muted, #334155);\n --srte-muted-foreground: var(--muted-foreground, #94a3b8);\n --srte-ring: var(--ring, #38bdf8);\n --srte-bg: var(--srte-canvas);\n --srte-text: var(--srte-foreground);\n --srte-text-muted: var(--srte-muted-foreground);\n --srte-border: var(--border, #334155);\n --srte-border-light: var(--srte-border);\n --srte-toolbar-bg: var(--srte-background);\n --srte-input-bg: var(--srte-background);\n --srte-input-text: var(--srte-foreground);\n --srte-input-border: var(--srte-border);\n --srte-modal-backdrop: rgba(0, 0, 0, 0.22);\n --srte-modal-backdrop-filter: blur(10px) saturate(0.9);\n --srte-modal-bg: #1e293b;\n --srte-modal-text: #e0e0e0;\n --srte-menu-bg: var(--srte-background);\n --srte-menu-text: var(--srte-foreground);\n --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);\n --srte-accent: #38bdf8;\n --srte-accent-bg: rgba(56, 189, 248, 0.16);\n --srte-danger: #ef4444;\n --srte-primary: #3b82f6;\n --srte-primary-pressed: var(--srte-primary);\n --srte-surface-subtle: #333333;\n --srte-on-primary: #ffffff;\n --srte-cancel-bg: #333333;\n --srte-code-bg: #111827;\n --srte-code-text: #e5e7eb;\n}\n.srte-editor {\n border-radius: var(--srte-radius);\n font-family: \"IBM Plex Sans\", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n container: srte-editor / inline-size;\n}\n.srte-canonical-authority > .srte-editor[contenteditable] {\n width: 100%;\n padding: 16px 20px;\n box-sizing: border-box;\n border: 1px solid var(--srte-border);\n border-radius: 0 0 var(--srte-radius) var(--srte-radius);\n outline: none;\n background: var(--srte-canvas);\n color: var(--srte-foreground);\n caret-color: var(--srte-foreground);\n line-height: 1.6;\n}\n.srte-toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n width: 100%;\n min-height: 48px;\n padding: 8px;\n box-sizing: border-box;\n border-bottom: 1px solid var(--srte-border);\n background: var(--srte-background);\n color: var(--srte-foreground);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n.srte-toolbar-group {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n min-width: 0;\n}\n.srte-toolbar-group + .srte-toolbar-group::before {\n content: \"\";\n width: 1px;\n height: 20px;\n margin: 0 5px 0 3px;\n background: var(--srte-border);\n}\n.srte-tool-button,\n.srte-toolbar select {\n height: 32px;\n min-width: 32px;\n box-sizing: border-box;\n border: 1px solid transparent;\n border-radius: 8px;\n background: transparent;\n color: var(--srte-foreground);\n font: 500 13px/1 \"IBM Plex Sans\", ui-sans-serif, system-ui, sans-serif;\n}\n.srte-tool-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 3px;\n padding: 0 7px;\n cursor: pointer;\n}\n.srte-toolbar select {\n max-width: 132px;\n padding: 0 26px 0 9px;\n border-color: var(--srte-border);\n background: var(--srte-input-bg);\n cursor: pointer;\n}\n.srte-tool-button:hover,\n.srte-toolbar select:hover,\n.srte-toolbar-menu[open] > .srte-menu-trigger {\n background: var(--srte-muted);\n border-color: var(--srte-border);\n}\n.srte-tool-button.srte-active,\n.srte-tool-button[aria-pressed=\"true\"] {\n /* var(--srte-primary) (#2563eb) on var(--srte-accent-bg) measured 4.43:1,\n just under WCAG AA's 4.5:1 for normal text (axe-core, Phase 11 Tier 3) -\n a dedicated, darker pressed-state color keeps the same hue family\n while clearing the threshold, without changing --srte-primary's other,\n already-compliant usages (e.g. against solid backgrounds). */\n color: var(--srte-primary-pressed);\n background: var(--srte-accent-bg);\n border-color: color-mix(in srgb, var(--srte-primary) 35%, transparent);\n}\n.srte-tool-button:focus-visible,\n.srte-toolbar select:focus-visible,\n.srte-menu-item:focus-visible {\n outline: 2px solid var(--srte-ring);\n outline-offset: 1px;\n}\n.srte-tool-button:disabled,\n.srte-menu-item:disabled,\n.srte-toolbar select:disabled {\n cursor: not-allowed;\n opacity: .4;\n}\n.srte-toolbar-menu {\n position: relative;\n}\n.srte-toolbar-menu > summary {\n list-style: none;\n}\n.srte-toolbar-menu > summary::-webkit-details-marker {\n display: none;\n}\n.srte-menu {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n z-index: 80;\n min-width: 210px;\n padding: 4px;\n overflow: hidden;\n border: 1px solid var(--srte-border);\n border-radius: 12px;\n background: var(--srte-menu-bg);\n color: var(--srte-menu-text);\n box-shadow: var(--srte-menu-shadow);\n}\n.srte-menu-item {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n padding: 0 8px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font: 500 13px/1 \"IBM Plex Sans\", ui-sans-serif, system-ui, sans-serif;\n text-align: left;\n white-space: nowrap;\n}\n.srte-menu-item:hover {\n background: var(--srte-muted);\n}\n.srte-menu-check {\n margin-left: auto;\n color: var(--srte-primary);\n font-weight: 700;\n}\n.srte-menu-separator {\n height: 1px;\n margin: 4px;\n background: var(--srte-border);\n}\n.srte-mobile-more { display: none; }\n.srte-toolbar .srte-command-proxy { display: none; }\n.srte-split-control {\n display: inline-flex;\n}\n.srte-split-control > .srte-tool-button:first-child {\n border-radius: 8px 0 0 8px;\n}\n.srte-split-control > select {\n width: 27px;\n padding: 0;\n border-radius: 0 8px 8px 0;\n border-left: 0;\n appearance: none;\n -webkit-appearance: none;\n color: transparent;\n background-color: var(--srte-muted);\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m7 10 5 5 5-5'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: center;\n cursor: pointer;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li {\n display: grid;\n grid-template-columns: 1.1em minmax(0, 1fr);\n column-gap: .45em;\n align-items: start;\n padding-left: 0;\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, decimal) \") \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \") \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \") \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, decimal) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, upper-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, upper-roman) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, upper-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, decimal) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, decimal-leading-zero) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \". \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"0\"] > li::marker {\n content: \"\u25CF \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"1\"] > li::marker {\n content: \"\u25CB \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"3\"] > li::marker {\n content: \"\u25A0 \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-outline\"] > li::marker {\n content: counters(list-item, \".\") \". \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-diamond\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u2756 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-diamond\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u27A2 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-diamond\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25A0 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-square\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u25A1 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-square\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25A3 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-square\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25AA \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u279C \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25C6 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25CF \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-star\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u2605 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-star\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25CB \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-star\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25A0 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow-circle\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u27A2 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow-circle\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25CB \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow-circle\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25A0 \"; }\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check] {\n display: inline-flex;\n grid-column: 1;\n grid-row: 1;\n align-items: center;\n justify-content: center;\n width: 1.1em;\n height: 1.6em;\n min-width: 0;\n box-sizing: border-box;\n padding: 0;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n cursor: pointer;\n appearance: none;\n outline: none;\n margin: 0;\n line-height: 1.6;\n vertical-align: top;\n position: relative;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check]:focus-visible {\n outline: 2px solid var(--srte-ring);\n outline-offset: 2px;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check]::before {\n content: \"\";\n width: .9em;\n height: .9em;\n box-sizing: border-box;\n border: 1.5px solid var(--srte-muted-foreground);\n border-radius: 3px;\n background: var(--srte-canvas);\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check][data-checked=\"true\"]::before {\n border-color: var(--srte-primary);\n background: var(--srte-primary);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--srte-on-primary) 25%, transparent);\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check][data-checked=\"true\"]::after {\n content: \"\";\n position: absolute;\n width: .42em;\n height: .22em;\n border-left: 1.5px solid var(--srte-on-primary);\n border-bottom: 1.5px solid var(--srte-on-primary);\n transform: translateY(-.08em) rotate(-45deg);\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > :is(p,h1,h2,h3,h4,h5,h6,blockquote,pre) {\n grid-column: 2;\n min-width: 0;\n margin-top: 0;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > :is(ul,ol) {\n grid-column: 2;\n}\n.srte-editor:focus-within {\n border-color: var(--srte-ring) !important;\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--srte-ring) 18%, transparent);\n}\n@media (max-width: 639px) {\n .srte-toolbar { gap: 3px; padding: 6px; }\n .srte-tool-button, .srte-toolbar select { height: 40px; min-width: 40px; }\n .srte-toolbar-group[data-srte-priority=\"3\"] { display: none; }\n .srte-toolbar-menu[data-srte-priority=\"2\"] { display: none; }\n .srte-mobile-more { display: block; }\n .srte-mobile-more .srte-menu {\n left: auto;\n right: 0;\n width: min(280px, calc(100vw - 16px));\n min-width: 0;\n max-height: min(70dvh, 480px);\n overflow-x: hidden;\n overflow-y: auto;\n overscroll-behavior: contain;\n -webkit-overflow-scrolling: touch;\n }\n .srte-menu-item { height: 40px; }\n}\n@container srte-editor (max-width: 639px) {\n .srte-toolbar { gap: 3px; padding: 6px; }\n .srte-tool-button, .srte-toolbar select { height: 40px; min-width: 40px; }\n .srte-toolbar-group[data-srte-priority=\"3\"],\n .srte-toolbar-menu[data-srte-priority=\"2\"] { display: none; }\n .srte-mobile-more { display: block; }\n .srte-mobile-more .srte-menu {\n left: auto;\n right: 0;\n width: min(280px, calc(100vw - 16px));\n min-width: 0;\n max-height: min(70dvh, 480px);\n overflow-x: hidden;\n overflow-y: auto;\n overscroll-behavior: contain;\n -webkit-overflow-scrolling: touch;\n }\n .srte-menu-item { height: 40px; }\n}\n/*\n * Wide-viewport promotion (docs/bugs/toolbar-priority-collapse-fixed-threshold-no-wide-promotion.md):\n * below this, the single 639px breakpoint above was this system's ONLY\n * threshold - every dropdown-grouped tool stayed hidden inside its dropdown\n * at every width from 640px up to and past a 2200px+ desktop, since nothing\n * ever measured or scaled with the extra room. A handful of tools frequent\n * enough to matter once there's genuinely spare room (Superscript,\n * Subscript, Text colour, Background colour, Font size, Font family, Remove\n * link, Insert formula, Special characters) get a standalone always-visible\n * ToolbarButton copy (data-srte-wide-promote, rendered directly in the\n * toolbar row) that appears past this breakpoint, while their existing\n * dropdown/mobile-menu ToolbarMenuItem copy (the exact same\n * data-srte-wide-promote attribute, on the .srte-menu-item element instead)\n * hides so the tool isn't offered twice. Below this breakpoint - including\n * all of mobile AND the 640-1439px tablet/typical-laptop band, which keeps\n * today's grouped-dropdown layout unchanged - the standalone copy stays\n * hidden and the dropdown copy is what's reachable.\n *\n * 1440px, not 1280px: Playwright's own default test viewport is exactly\n * 1280x720, and the vast majority of this suite's toolbar interactions run\n * at that default without ever calling page.setViewportSize - a 1280px\n * threshold would have flipped nearly every existing toolbar test's\n * dropdown-item locators (role=menuitem) over to CSS display:none out from\n * under them. 1440px clears that default with room to spare while still\n * comfortably covering \"wide desktop\" (the ~2264px width the original\n * report was filed against).\n */\n.srte-tool-button[data-srte-wide-promote=\"true\"] { display: none; }\n@media (min-width: 1440px) {\n .srte-tool-button[data-srte-wide-promote=\"true\"] { display: inline-flex; }\n .srte-menu-item[data-srte-wide-promote=\"true\"] { display: none; }\n}\n@container srte-editor (min-width: 1440px) {\n .srte-tool-button[data-srte-wide-promote=\"true\"] { display: inline-flex; }\n .srte-menu-item[data-srte-wide-promote=\"true\"] { display: none; }\n}\n.srte-editor [contenteditable] blockquote {\n border-left: 4px solid var(--srte-accent);\n margin: 0.75em 0;\n padding: 0.5em 1em;\n background: var(--srte-surface-subtle);\n color: var(--srte-text);\n}\n.srte-editor [contenteditable] p,\n.srte-editor [contenteditable] h1,\n.srte-editor [contenteditable] h2,\n.srte-editor [contenteditable] h3 {\n color: inherit;\n}\n.srte-editor [contenteditable] p {\n display: block;\n margin: 0 0 0.75em;\n font-size: 1em;\n font-weight: 400;\n line-height: 1.6;\n}\n.srte-editor [contenteditable] p[data-srte-caret-boundary=\"true\"] {\n min-height: 1.6em;\n}\n.srte-editor [contenteditable] h1,\n.srte-editor [contenteditable] h2,\n.srte-editor [contenteditable] h3 {\n display: block;\n margin: 0.75em 0 0.4em;\n font-weight: 700;\n line-height: 1.25;\n}\n.srte-editor [contenteditable] h1 {\n font-size: 2em;\n}\n.srte-editor [contenteditable] h2 {\n font-size: 1.5em;\n}\n.srte-editor [contenteditable] h3 {\n font-size: 1.25em;\n}\n.srte-editor [contenteditable] > :first-child {\n margin-top: 0;\n}\n.srte-editor [contenteditable] ul {\n list-style-type: disc;\n list-style-position: outside;\n margin: 0.75em 0;\n padding-left: 1.75em;\n}\n.srte-editor [contenteditable] ol {\n list-style-type: decimal;\n list-style-position: outside;\n margin: 0.75em 0;\n padding-left: 1.75em;\n}\n.srte-editor [contenteditable] li {\n display: list-item;\n margin: 0.25em 0;\n padding-left: 0.25em;\n}\n.srte-editor [contenteditable] li::marker {\n color: currentColor;\n}\n.srte-editor [contenteditable] table {\n width: 100%;\n margin: 0.75em 0;\n border-collapse: collapse;\n}\n.srte-editor [contenteditable] th,\n.srte-editor [contenteditable] td {\n padding: 8px;\n border: 1px solid var(--srte-border);\n vertical-align: top;\n}\n.srte-editor [contenteditable] [data-smart-cell-selected=\"true\"] {\n background: color-mix(in srgb, var(--srte-primary) 12%, var(--srte-canvas));\n box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--srte-primary) 65%, transparent);\n}\n.srte-editor [contenteditable][data-smart-cell-selection-active] ::selection {\n background: transparent;\n color: inherit;\n}\n.srte-editor [contenteditable] th {\n background: var(--srte-surface-subtle);\n font-weight: 600;\n text-align: left;\n}\n.srte-editor [contenteditable] td > h1,\n.srte-editor [contenteditable] td > h2,\n.srte-editor [contenteditable] td > h3,\n.srte-editor [contenteditable] th > h1,\n.srte-editor [contenteditable] th > h2,\n.srte-editor [contenteditable] th > h3 {\n margin: 0 0 0.4em;\n overflow-wrap: anywhere;\n}\n.srte-editor [contenteditable] pre {\n display: block;\n margin: 0.75em 0;\n padding: 12px 14px;\n overflow-x: auto;\n border: 1px solid var(--srte-border);\n border-radius: 6px;\n background: var(--srte-code-bg);\n color: var(--srte-code-text);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace;\n font-size: 0.9em;\n line-height: 1.55;\n white-space: pre-wrap;\n}\n.srte-editor [contenteditable] pre code {\n padding: 0;\n border: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n}\n.srte-editor [contenteditable] code:not(pre code) {\n padding: 0.1em 0.35em;\n border-radius: 3px;\n background: var(--srte-code-bg);\n color: var(--srte-code-text);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace;\n font-size: 0.9em;\n}\n.srte-editor [contenteditable] a,\n.srte-editor [contenteditable] a:visited {\n color: var(--srte-primary) !important;\n text-decoration: underline !important;\n text-decoration-thickness: 1px !important;\n text-underline-offset: 2px !important;\n cursor: pointer;\n}\n.srte-editor [contenteditable] a:hover {\n color: var(--srte-accent) !important;\n}\n.srte-editor [contenteditable] a:focus-visible {\n outline: 2px solid var(--srte-accent);\n outline-offset: 2px;\n}\n.srte-editor [contenteditable] sub,\n.srte-editor [contenteditable] sup {\n line-height: 0;\n}\n";
|
|
2
|
+
export declare const SRTE_DEFAULT_CSS = "\n.srte-editor {\n --srte-background: var(--card, #ffffff);\n --srte-canvas: var(--background, #ffffff);\n --srte-foreground: var(--foreground, #0f172a);\n --srte-muted: var(--muted, #f1f5f9);\n --srte-muted-foreground: var(--muted-foreground, #64748b);\n --srte-ring: var(--ring, #0284c7);\n --srte-radius: var(--radius, 0.625rem);\n --srte-bg: var(--srte-canvas);\n --srte-text: var(--srte-foreground);\n --srte-text-muted: var(--srte-muted-foreground);\n --srte-border: var(--border, #e2e8f0);\n --srte-border-light: var(--srte-border);\n --srte-toolbar-bg: var(--srte-background);\n --srte-input-bg: var(--srte-background);\n --srte-input-text: var(--srte-foreground);\n --srte-input-border: var(--srte-border);\n --srte-modal-backdrop: rgba(0, 0, 0, 0.35);\n --srte-modal-backdrop-filter: blur(2px);\n --srte-modal-bg: var(--card, #ffffff);\n --srte-modal-text: var(--foreground, #000000);\n --srte-menu-bg: var(--srte-background);\n --srte-menu-text: var(--srte-foreground);\n --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);\n --srte-accent: #0284c7;\n --srte-accent-bg: rgba(2, 132, 199, 0.12);\n --srte-danger: #dc2626;\n --srte-primary: #2563eb;\n /* --srte-primary on --srte-accent-bg measures 4.43:1, under WCAG AA's\n 4.5:1 for normal text - used only for the pressed toolbar-button state\n (see .srte-tool-button[aria-pressed=\"true\"]), not a replacement for\n --srte-primary generally. */\n --srte-primary-pressed: #1d4ed8;\n --srte-on-primary: #ffffff;\n --srte-cancel-bg: #f3f4f6;\n --srte-code-bg: #f6f8fa;\n --srte-code-text: #24292f;\n}\n.srte-editor.srte-dark {\n --srte-background: var(--card, #1e293b);\n --srte-canvas: var(--background, #0f172a);\n --srte-foreground: var(--foreground, #f8fafc);\n --srte-muted: var(--muted, #334155);\n --srte-muted-foreground: var(--muted-foreground, #94a3b8);\n --srte-ring: var(--ring, #38bdf8);\n --srte-bg: var(--srte-canvas);\n --srte-text: var(--srte-foreground);\n --srte-text-muted: var(--srte-muted-foreground);\n --srte-border: var(--border, #334155);\n --srte-border-light: var(--srte-border);\n --srte-toolbar-bg: var(--srte-background);\n --srte-input-bg: var(--srte-background);\n --srte-input-text: var(--srte-foreground);\n --srte-input-border: var(--srte-border);\n --srte-modal-backdrop: rgba(0, 0, 0, 0.22);\n --srte-modal-backdrop-filter: blur(10px) saturate(0.9);\n --srte-modal-bg: var(--card, #1e293b);\n --srte-modal-text: var(--foreground, #e0e0e0);\n --srte-menu-bg: var(--srte-background);\n --srte-menu-text: var(--srte-foreground);\n --srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);\n --srte-accent: #38bdf8;\n --srte-accent-bg: rgba(56, 189, 248, 0.16);\n --srte-danger: #ef4444;\n --srte-primary: #3b82f6;\n --srte-primary-pressed: var(--srte-primary);\n --srte-on-primary: #ffffff;\n --srte-cancel-bg: #333333;\n --srte-code-bg: #111827;\n --srte-code-text: #e5e7eb;\n}\n.srte-editor {\n border-radius: var(--srte-radius);\n font-family: \"IBM Plex Sans\", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n container: srte-editor / inline-size;\n}\n.srte-canonical-authority > .srte-editor[contenteditable] {\n width: 100%;\n padding: 16px 20px;\n box-sizing: border-box;\n border: 1px solid var(--srte-border);\n border-radius: 0 0 var(--srte-radius) var(--srte-radius);\n outline: none;\n background: var(--srte-canvas);\n color: var(--srte-foreground);\n caret-color: var(--srte-foreground);\n line-height: 1.6;\n}\n.srte-toolbar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n width: 100%;\n min-height: 48px;\n padding: 8px;\n box-sizing: border-box;\n border-bottom: 1px solid var(--srte-border);\n background: var(--srte-background);\n color: var(--srte-foreground);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n.srte-toolbar-group {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n min-width: 0;\n}\n.srte-toolbar-group + .srte-toolbar-group::before {\n content: \"\";\n width: 1px;\n height: 20px;\n margin: 0 5px 0 3px;\n background: var(--srte-border);\n}\n.srte-tool-button,\n.srte-toolbar select {\n height: 32px;\n min-width: 32px;\n box-sizing: border-box;\n border: 1px solid transparent;\n border-radius: 8px;\n background: transparent;\n color: var(--srte-foreground);\n font: 500 13px/1 \"IBM Plex Sans\", ui-sans-serif, system-ui, sans-serif;\n}\n.srte-tool-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 3px;\n padding: 0 7px;\n cursor: pointer;\n}\n.srte-toolbar select {\n max-width: 132px;\n padding: 0 26px 0 9px;\n border-color: var(--srte-border);\n background: var(--srte-input-bg);\n cursor: pointer;\n}\n.srte-tool-button:hover,\n.srte-toolbar select:hover,\n.srte-toolbar-menu[open] > .srte-menu-trigger {\n background: var(--srte-muted);\n border-color: var(--srte-border);\n}\n.srte-tool-button.srte-active,\n.srte-tool-button[aria-pressed=\"true\"] {\n /* var(--srte-primary) (#2563eb) on var(--srte-accent-bg) measured 4.43:1,\n just under WCAG AA's 4.5:1 for normal text (axe-core, Phase 11 Tier 3) -\n a dedicated, darker pressed-state color keeps the same hue family\n while clearing the threshold, without changing --srte-primary's other,\n already-compliant usages (e.g. against solid backgrounds). */\n color: var(--srte-primary-pressed);\n background: var(--srte-accent-bg);\n border-color: color-mix(in srgb, var(--srte-primary) 35%, transparent);\n}\n.srte-tool-button:focus-visible,\n.srte-toolbar select:focus-visible,\n.srte-menu-item:focus-visible {\n outline: 2px solid var(--srte-ring);\n outline-offset: 1px;\n}\n.srte-tool-button:disabled,\n.srte-menu-item:disabled,\n.srte-toolbar select:disabled {\n cursor: not-allowed;\n opacity: .4;\n}\n.srte-toolbar-menu {\n position: relative;\n}\n.srte-toolbar-menu > summary {\n list-style: none;\n}\n.srte-toolbar-menu > summary::-webkit-details-marker {\n display: none;\n}\n.srte-menu {\n position: absolute;\n top: calc(100% + 6px);\n left: 0;\n z-index: 80;\n min-width: 210px;\n padding: 4px;\n overflow: hidden;\n border: 1px solid var(--srte-border);\n border-radius: 12px;\n background: var(--srte-menu-bg);\n color: var(--srte-menu-text);\n box-shadow: var(--srte-menu-shadow);\n}\n/*\n * ToolbarDropdown's own menu panel (docs/bugs/\n * toolbar-dropdown-clipped-by-host-overflow-hidden.md): position:fixed with\n * JS-measured left/top (ToolbarPrimitives.tsx) instead of the default\n * position:absolute above, so it always escapes an ancestor host container's\n * own overflow:hidden regardless of DOM depth - MobileMoreMenu's overflow\n * menu (no data-srte-menu-fixed attribute) is unaffected and keeps its\n * existing position:absolute/right-anchored behavior.\n */\n.srte-menu[data-srte-menu-fixed=\"true\"] {\n position: fixed;\n top: 0;\n left: 0;\n}\n.srte-menu-item {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n height: 36px;\n padding: 0 8px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n font: 500 13px/1 \"IBM Plex Sans\", ui-sans-serif, system-ui, sans-serif;\n text-align: left;\n white-space: nowrap;\n}\n.srte-menu-item:hover {\n background: var(--srte-muted);\n}\n.srte-menu-check {\n margin-left: auto;\n color: var(--srte-primary);\n font-weight: 700;\n}\n.srte-menu-separator {\n height: 1px;\n margin: 4px;\n background: var(--srte-border);\n}\n.srte-mobile-more { display: none; }\n.srte-toolbar .srte-command-proxy { display: none; }\n.srte-split-control {\n display: inline-flex;\n}\n.srte-split-control > .srte-tool-button:first-child {\n border-radius: 8px 0 0 8px;\n}\n.srte-split-control > select {\n width: 27px;\n padding: 0;\n border-radius: 0 8px 8px 0;\n border-left: 0;\n appearance: none;\n -webkit-appearance: none;\n color: transparent;\n background-color: var(--srte-muted);\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m7 10 5 5 5-5'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: center;\n cursor: pointer;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li {\n display: grid;\n grid-template-columns: 1.1em minmax(0, 1fr);\n column-gap: .45em;\n align-items: start;\n padding-left: 0;\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, decimal) \") \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \") \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal-paren\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \") \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, decimal) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-decimal\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, upper-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-alpha\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, upper-roman) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, upper-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-upper-roman\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, decimal) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"0\"] > li::marker {\n content: counter(list-item, decimal-leading-zero) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"1\"] > li::marker {\n content: counter(list-item, lower-alpha) \". \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-leading-zero\"][data-srte-list-depth=\"3\"] > li::marker {\n content: counter(list-item, lower-roman) \". \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"0\"] > li::marker {\n content: \"\u25CF \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"1\"] > li::marker {\n content: \"\u25CB \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"2\"] > li::marker,\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-disc\"][data-srte-list-depth=\"3\"] > li::marker {\n content: \"\u25A0 \";\n}\n.srte-editor [contenteditable] ol[data-srte-list-preset=\"ordered-outline\"] > li::marker {\n content: counters(list-item, \".\") \". \";\n}\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-diamond\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u2756 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-diamond\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u27A2 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-diamond\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25A0 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-square\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u25A1 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-square\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25A3 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-square\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25AA \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u279C \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25C6 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25CF \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-star\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u2605 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-star\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25CB \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-star\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25A0 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow-circle\"][data-srte-list-depth=\"0\"] > li::marker { content: \"\u27A2 \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow-circle\"][data-srte-list-depth=\"1\"] > li::marker { content: \"\u25CB \"; }\n.srte-editor [contenteditable] ul[data-srte-list-preset=\"bullet-arrow-circle\"][data-srte-list-depth=\"2\"] > li::marker { content: \"\u25A0 \"; }\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check] {\n display: inline-flex;\n grid-column: 1;\n grid-row: 1;\n align-items: center;\n justify-content: center;\n width: 1.1em;\n height: 1.6em;\n min-width: 0;\n box-sizing: border-box;\n padding: 0;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n cursor: pointer;\n appearance: none;\n outline: none;\n margin: 0;\n line-height: 1.6;\n vertical-align: top;\n position: relative;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check]:focus-visible {\n outline: 2px solid var(--srte-ring);\n outline-offset: 2px;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check]::before {\n content: \"\";\n width: .9em;\n height: .9em;\n box-sizing: border-box;\n border: 1.5px solid var(--srte-muted-foreground);\n border-radius: 3px;\n background: var(--srte-canvas);\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check][data-checked=\"true\"]::before {\n border-color: var(--srte-primary);\n background: var(--srte-primary);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--srte-on-primary) 25%, transparent);\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > [data-srte-check][data-checked=\"true\"]::after {\n content: \"\";\n position: absolute;\n width: .42em;\n height: .22em;\n border-left: 1.5px solid var(--srte-on-primary);\n border-bottom: 1.5px solid var(--srte-on-primary);\n transform: translateY(-.08em) rotate(-45deg);\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > :is(p,h1,h2,h3,h4,h5,h6,blockquote,pre) {\n grid-column: 2;\n min-width: 0;\n margin-top: 0;\n}\n.srte-editor [contenteditable] ul[data-srte-checklist=\"true\"] > li > :is(ul,ol) {\n grid-column: 2;\n}\n.srte-editor:focus-within {\n border-color: var(--srte-ring) !important;\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--srte-ring) 18%, transparent);\n}\n/*\n * @container (element width), not @media (viewport width): a host can embed\n * the editor at any fraction of the browser window - a split pane next to a\n * preview panel is the case that surfaced this (docs/bugs/\n * toolbar-wide-promotion-fires-on-viewport-width-not-container-width.md). A\n * plain @media breakpoint here would fire based on the *window*, wrongly\n * switching tiers for an editor instance that's actually much narrower (or\n * wider) than the window - .srte-editor already establishes its own\n * container: srte-editor / inline-size (see the .srte-editor rule above),\n * so @container always reflects this specific instance's real rendered width regardless of host\n * layout, with no @media fallback needed (container queries have full\n * support across this project's three target engines).\n */\n@container srte-editor (max-width: 639px) {\n .srte-toolbar { gap: 3px; padding: 6px; }\n .srte-tool-button, .srte-toolbar select { height: 40px; min-width: 40px; }\n .srte-toolbar-group[data-srte-priority=\"3\"],\n .srte-toolbar-menu[data-srte-priority=\"2\"] { display: none; }\n .srte-mobile-more { display: block; }\n /* left/top are no longer set here - ToolbarPrimitives.tsx now computes\n them via inline style (JS-measured, position:fixed, viewport-clamped;\n see MobileMoreMenu's own doc comment), which always wins over any\n value set here regardless of specificity or source order. Sizing/\n scroll behavior stays defined here since inline positioning doesn't\n touch those properties. */\n .srte-mobile-more .srte-menu {\n width: min(280px, calc(100vw - 16px));\n min-width: 0;\n max-height: min(70dvh, 480px);\n overflow-x: hidden;\n overflow-y: auto;\n overscroll-behavior: contain;\n -webkit-overflow-scrolling: touch;\n }\n .srte-menu-item { height: 40px; }\n}\n/*\n * Wide-viewport promotion (docs/bugs/toolbar-priority-collapse-fixed-threshold-no-wide-promotion.md):\n * below this, the single 639px breakpoint above was this system's ONLY\n * threshold - every dropdown-grouped tool stayed hidden inside its dropdown\n * at every width from 640px up to and past a 2200px+ desktop, since nothing\n * ever measured or scaled with the extra room. A handful of tools frequent\n * enough to matter once there's genuinely spare room (Superscript,\n * Subscript, Text colour, Background colour, Font size, Font family, Remove\n * link, Insert formula, Special characters) get a standalone always-visible\n * ToolbarButton copy (data-srte-wide-promote, rendered directly in the\n * toolbar row) that appears past this breakpoint, while their existing\n * dropdown/mobile-menu ToolbarMenuItem copy (the exact same\n * data-srte-wide-promote attribute, on the .srte-menu-item element instead)\n * hides so the tool isn't offered twice. Below this breakpoint - including\n * all of mobile AND the 640-1439px tablet/typical-laptop band, which keeps\n * today's grouped-dropdown layout unchanged - the standalone copy stays\n * hidden and the dropdown copy is what's reachable.\n *\n * 1440px, not 1280px: Playwright's own default test viewport is exactly\n * 1280x720, and the vast majority of this suite's toolbar interactions run\n * at that default without ever calling page.setViewportSize - a 1280px\n * threshold would have flipped nearly every existing toolbar test's\n * dropdown-item locators (role=menuitem) over to CSS display:none out from\n * under them. 1440px clears that default with room to spare while still\n * comfortably covering \"wide desktop\" (the ~2264px width the original\n * report was filed against).\n *\n * @container only, no @media fallback - same reasoning as the mobile tier\n * above (docs/bugs/toolbar-wide-promotion-fires-on-viewport-width-not-container-width.md):\n * a plain @media(min-width:1440px) fired whenever the *browser window* was\n * wide, even when this editor instance was embedded in a much narrower\n * split pane - promoting 9 extra buttons into a group with no room for\n * them, which squeezed/overlapped the group's existing buttons instead of\n * wrapping cleanly.\n */\n.srte-tool-button[data-srte-wide-promote=\"true\"] { display: none; }\n@container srte-editor (min-width: 1440px) {\n .srte-tool-button[data-srte-wide-promote=\"true\"] { display: inline-flex; }\n .srte-menu-item[data-srte-wide-promote=\"true\"] { display: none; }\n}\n.srte-editor [contenteditable] blockquote {\n border-left: 4px solid var(--srte-accent);\n margin: 0.75em 0;\n padding: 0.5em 1em;\n /* --srte-muted, not the retired --srte-surface-subtle (docs/bugs/\n srte-surface-subtle-not-host-theme-aware.md): every other neutral\n surface/text token here (--srte-background, --srte-foreground,\n --srte-muted, --srte-border) is defined as var(--host-token, fallback)\n so it correctly follows a host's OWN light/dark state even if\n .srte-dark never gets applied for some host-side wiring reason -\n --srte-surface-subtle was the one exception, a bare hardcoded hex with\n no host integration, which is exactly what let a real host (dark mode,\n .srte-dark not reaching this editor instance) render this blockquote\n with a light-mode background and dark-mode text simultaneously. */\n background: var(--srte-muted);\n color: var(--srte-text);\n}\n.srte-editor [contenteditable] p,\n.srte-editor [contenteditable] h1,\n.srte-editor [contenteditable] h2,\n.srte-editor [contenteditable] h3 {\n color: inherit;\n}\n.srte-editor [contenteditable] p {\n display: block;\n margin: 0 0 0.75em;\n font-size: 1em;\n font-weight: 400;\n line-height: 1.6;\n}\n.srte-editor [contenteditable] p[data-srte-caret-boundary=\"true\"] {\n min-height: 1.6em;\n}\n.srte-editor [contenteditable] h1,\n.srte-editor [contenteditable] h2,\n.srte-editor [contenteditable] h3 {\n display: block;\n margin: 0.75em 0 0.4em;\n font-weight: 700;\n line-height: 1.25;\n}\n.srte-editor [contenteditable] h1 {\n font-size: 2em;\n}\n.srte-editor [contenteditable] h2 {\n font-size: 1.5em;\n}\n.srte-editor [contenteditable] h3 {\n font-size: 1.25em;\n}\n.srte-editor [contenteditable] > :first-child {\n margin-top: 0;\n}\n.srte-editor [contenteditable] ul {\n list-style-type: disc;\n list-style-position: outside;\n margin: 0.75em 0;\n padding-left: 1.75em;\n}\n.srte-editor [contenteditable] ol {\n list-style-type: decimal;\n list-style-position: outside;\n margin: 0.75em 0;\n padding-left: 1.75em;\n}\n.srte-editor [contenteditable] li {\n display: list-item;\n margin: 0.25em 0;\n padding-left: 0.25em;\n}\n.srte-editor [contenteditable] li::marker {\n color: currentColor;\n}\n.srte-editor [contenteditable] table {\n width: 100%;\n margin: 0.75em 0;\n border-collapse: collapse;\n}\n.srte-editor [contenteditable] th,\n.srte-editor [contenteditable] td {\n padding: 8px;\n border: 1px solid var(--srte-border);\n vertical-align: top;\n}\n.srte-editor [contenteditable] [data-smart-cell-selected=\"true\"] {\n background: color-mix(in srgb, var(--srte-primary) 12%, var(--srte-canvas));\n box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--srte-primary) 65%, transparent);\n}\n.srte-editor [contenteditable][data-smart-cell-selection-active] ::selection {\n background: transparent;\n color: inherit;\n}\n.srte-editor [contenteditable] th {\n background: var(--srte-muted);\n font-weight: 600;\n text-align: left;\n}\n.srte-editor [contenteditable] td > h1,\n.srte-editor [contenteditable] td > h2,\n.srte-editor [contenteditable] td > h3,\n.srte-editor [contenteditable] th > h1,\n.srte-editor [contenteditable] th > h2,\n.srte-editor [contenteditable] th > h3 {\n margin: 0 0 0.4em;\n overflow-wrap: anywhere;\n}\n.srte-editor [contenteditable] pre {\n display: block;\n margin: 0.75em 0;\n padding: 12px 14px;\n overflow-x: auto;\n border: 1px solid var(--srte-border);\n border-radius: 6px;\n background: var(--srte-code-bg);\n color: var(--srte-code-text);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace;\n font-size: 0.9em;\n line-height: 1.55;\n white-space: pre-wrap;\n}\n.srte-editor [contenteditable] pre code {\n padding: 0;\n border: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n}\n.srte-editor [contenteditable] code:not(pre code) {\n padding: 0.1em 0.35em;\n border-radius: 3px;\n background: var(--srte-code-bg);\n color: var(--srte-code-text);\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace;\n font-size: 0.9em;\n}\n.srte-editor [contenteditable] a,\n.srte-editor [contenteditable] a:visited {\n color: var(--srte-primary) !important;\n text-decoration: underline !important;\n text-decoration-thickness: 1px !important;\n text-underline-offset: 2px !important;\n cursor: pointer;\n}\n.srte-editor [contenteditable] a:hover {\n color: var(--srte-accent) !important;\n}\n.srte-editor [contenteditable] a:focus-visible {\n outline: 2px solid var(--srte-accent);\n outline-offset: 2px;\n}\n.srte-editor [contenteditable] sub,\n.srte-editor [contenteditable] sup {\n line-height: 0;\n}\n/*\n * A page break has no native visual meaning in a continuously-scrolling\n * web editor - its entire purpose is what happens at export/print time\n * (see atom/formats.ts's atomToHtml doc comment). This gives it a clearly\n * distinct live-editor appearance from the plain solid <hr> a horizontal\n * line renders as by default, so the two are never confused: a dashed\n * line with an explicit \"Page break\" label, per the feature's own design\n * decision.\n */\n.srte-editor [contenteditable] [data-smart-type=\"page_break\"] {\n display: block;\n position: relative;\n height: 0;\n margin: 1.5em 0;\n border: none;\n border-top: 2px dashed var(--srte-border);\n cursor: default;\n}\n.srte-editor [contenteditable] [data-smart-type=\"page_break\"]::after {\n content: \"Page break\";\n position: absolute;\n top: -0.7em;\n left: 50%;\n transform: translateX(-50%);\n background: var(--srte-canvas);\n padding: 0 0.6em;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--srte-text-muted);\n white-space: nowrap;\n}\n";
|
|
3
3
|
export declare function ensureStyleSheet(): void;
|
package/dist/theme.js
CHANGED
|
@@ -18,8 +18,8 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
18
18
|
--srte-input-border: var(--srte-border);
|
|
19
19
|
--srte-modal-backdrop: rgba(0, 0, 0, 0.35);
|
|
20
20
|
--srte-modal-backdrop-filter: blur(2px);
|
|
21
|
-
--srte-modal-bg: #ffffff;
|
|
22
|
-
--srte-modal-text: #000000;
|
|
21
|
+
--srte-modal-bg: var(--card, #ffffff);
|
|
22
|
+
--srte-modal-text: var(--foreground, #000000);
|
|
23
23
|
--srte-menu-bg: var(--srte-background);
|
|
24
24
|
--srte-menu-text: var(--srte-foreground);
|
|
25
25
|
--srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
|
|
@@ -32,7 +32,6 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
32
32
|
(see .srte-tool-button[aria-pressed="true"]), not a replacement for
|
|
33
33
|
--srte-primary generally. */
|
|
34
34
|
--srte-primary-pressed: #1d4ed8;
|
|
35
|
-
--srte-surface-subtle: #f3f4f6;
|
|
36
35
|
--srte-on-primary: #ffffff;
|
|
37
36
|
--srte-cancel-bg: #f3f4f6;
|
|
38
37
|
--srte-code-bg: #f6f8fa;
|
|
@@ -56,8 +55,8 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
56
55
|
--srte-input-border: var(--srte-border);
|
|
57
56
|
--srte-modal-backdrop: rgba(0, 0, 0, 0.22);
|
|
58
57
|
--srte-modal-backdrop-filter: blur(10px) saturate(0.9);
|
|
59
|
-
--srte-modal-bg: #1e293b;
|
|
60
|
-
--srte-modal-text: #e0e0e0;
|
|
58
|
+
--srte-modal-bg: var(--card, #1e293b);
|
|
59
|
+
--srte-modal-text: var(--foreground, #e0e0e0);
|
|
61
60
|
--srte-menu-bg: var(--srte-background);
|
|
62
61
|
--srte-menu-text: var(--srte-foreground);
|
|
63
62
|
--srte-menu-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
|
@@ -66,7 +65,6 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
66
65
|
--srte-danger: #ef4444;
|
|
67
66
|
--srte-primary: #3b82f6;
|
|
68
67
|
--srte-primary-pressed: var(--srte-primary);
|
|
69
|
-
--srte-surface-subtle: #333333;
|
|
70
68
|
--srte-on-primary: #ffffff;
|
|
71
69
|
--srte-cancel-bg: #333333;
|
|
72
70
|
--srte-code-bg: #111827;
|
|
@@ -196,6 +194,20 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
196
194
|
color: var(--srte-menu-text);
|
|
197
195
|
box-shadow: var(--srte-menu-shadow);
|
|
198
196
|
}
|
|
197
|
+
/*
|
|
198
|
+
* ToolbarDropdown's own menu panel (docs/bugs/
|
|
199
|
+
* toolbar-dropdown-clipped-by-host-overflow-hidden.md): position:fixed with
|
|
200
|
+
* JS-measured left/top (ToolbarPrimitives.tsx) instead of the default
|
|
201
|
+
* position:absolute above, so it always escapes an ancestor host container's
|
|
202
|
+
* own overflow:hidden regardless of DOM depth - MobileMoreMenu's overflow
|
|
203
|
+
* menu (no data-srte-menu-fixed attribute) is unaffected and keeps its
|
|
204
|
+
* existing position:absolute/right-anchored behavior.
|
|
205
|
+
*/
|
|
206
|
+
.srte-menu[data-srte-menu-fixed="true"] {
|
|
207
|
+
position: fixed;
|
|
208
|
+
top: 0;
|
|
209
|
+
left: 0;
|
|
210
|
+
}
|
|
199
211
|
.srte-menu-item {
|
|
200
212
|
display: flex;
|
|
201
213
|
align-items: center;
|
|
@@ -395,34 +407,32 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
395
407
|
border-color: var(--srte-ring) !important;
|
|
396
408
|
box-shadow: 0 0 0 2px color-mix(in srgb, var(--srte-ring) 18%, transparent);
|
|
397
409
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
overflow-y: auto;
|
|
412
|
-
overscroll-behavior: contain;
|
|
413
|
-
-webkit-overflow-scrolling: touch;
|
|
414
|
-
}
|
|
415
|
-
.srte-menu-item { height: 40px; }
|
|
416
|
-
}
|
|
410
|
+
/*
|
|
411
|
+
* @container (element width), not @media (viewport width): a host can embed
|
|
412
|
+
* the editor at any fraction of the browser window - a split pane next to a
|
|
413
|
+
* preview panel is the case that surfaced this (docs/bugs/
|
|
414
|
+
* toolbar-wide-promotion-fires-on-viewport-width-not-container-width.md). A
|
|
415
|
+
* plain @media breakpoint here would fire based on the *window*, wrongly
|
|
416
|
+
* switching tiers for an editor instance that's actually much narrower (or
|
|
417
|
+
* wider) than the window - .srte-editor already establishes its own
|
|
418
|
+
* container: srte-editor / inline-size (see the .srte-editor rule above),
|
|
419
|
+
* so @container always reflects this specific instance's real rendered width regardless of host
|
|
420
|
+
* layout, with no @media fallback needed (container queries have full
|
|
421
|
+
* support across this project's three target engines).
|
|
422
|
+
*/
|
|
417
423
|
@container srte-editor (max-width: 639px) {
|
|
418
424
|
.srte-toolbar { gap: 3px; padding: 6px; }
|
|
419
425
|
.srte-tool-button, .srte-toolbar select { height: 40px; min-width: 40px; }
|
|
420
426
|
.srte-toolbar-group[data-srte-priority="3"],
|
|
421
427
|
.srte-toolbar-menu[data-srte-priority="2"] { display: none; }
|
|
422
428
|
.srte-mobile-more { display: block; }
|
|
429
|
+
/* left/top are no longer set here - ToolbarPrimitives.tsx now computes
|
|
430
|
+
them via inline style (JS-measured, position:fixed, viewport-clamped;
|
|
431
|
+
see MobileMoreMenu's own doc comment), which always wins over any
|
|
432
|
+
value set here regardless of specificity or source order. Sizing/
|
|
433
|
+
scroll behavior stays defined here since inline positioning doesn't
|
|
434
|
+
touch those properties. */
|
|
423
435
|
.srte-mobile-more .srte-menu {
|
|
424
|
-
left: auto;
|
|
425
|
-
right: 0;
|
|
426
436
|
width: min(280px, calc(100vw - 16px));
|
|
427
437
|
min-width: 0;
|
|
428
438
|
max-height: min(70dvh, 480px);
|
|
@@ -459,12 +469,16 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
459
469
|
* under them. 1440px clears that default with room to spare while still
|
|
460
470
|
* comfortably covering "wide desktop" (the ~2264px width the original
|
|
461
471
|
* report was filed against).
|
|
472
|
+
*
|
|
473
|
+
* @container only, no @media fallback - same reasoning as the mobile tier
|
|
474
|
+
* above (docs/bugs/toolbar-wide-promotion-fires-on-viewport-width-not-container-width.md):
|
|
475
|
+
* a plain @media(min-width:1440px) fired whenever the *browser window* was
|
|
476
|
+
* wide, even when this editor instance was embedded in a much narrower
|
|
477
|
+
* split pane - promoting 9 extra buttons into a group with no room for
|
|
478
|
+
* them, which squeezed/overlapped the group's existing buttons instead of
|
|
479
|
+
* wrapping cleanly.
|
|
462
480
|
*/
|
|
463
481
|
.srte-tool-button[data-srte-wide-promote="true"] { display: none; }
|
|
464
|
-
@media (min-width: 1440px) {
|
|
465
|
-
.srte-tool-button[data-srte-wide-promote="true"] { display: inline-flex; }
|
|
466
|
-
.srte-menu-item[data-srte-wide-promote="true"] { display: none; }
|
|
467
|
-
}
|
|
468
482
|
@container srte-editor (min-width: 1440px) {
|
|
469
483
|
.srte-tool-button[data-srte-wide-promote="true"] { display: inline-flex; }
|
|
470
484
|
.srte-menu-item[data-srte-wide-promote="true"] { display: none; }
|
|
@@ -473,7 +487,17 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
473
487
|
border-left: 4px solid var(--srte-accent);
|
|
474
488
|
margin: 0.75em 0;
|
|
475
489
|
padding: 0.5em 1em;
|
|
476
|
-
|
|
490
|
+
/* --srte-muted, not the retired --srte-surface-subtle (docs/bugs/
|
|
491
|
+
srte-surface-subtle-not-host-theme-aware.md): every other neutral
|
|
492
|
+
surface/text token here (--srte-background, --srte-foreground,
|
|
493
|
+
--srte-muted, --srte-border) is defined as var(--host-token, fallback)
|
|
494
|
+
so it correctly follows a host's OWN light/dark state even if
|
|
495
|
+
.srte-dark never gets applied for some host-side wiring reason -
|
|
496
|
+
--srte-surface-subtle was the one exception, a bare hardcoded hex with
|
|
497
|
+
no host integration, which is exactly what let a real host (dark mode,
|
|
498
|
+
.srte-dark not reaching this editor instance) render this blockquote
|
|
499
|
+
with a light-mode background and dark-mode text simultaneously. */
|
|
500
|
+
background: var(--srte-muted);
|
|
477
501
|
color: var(--srte-text);
|
|
478
502
|
}
|
|
479
503
|
.srte-editor [contenteditable] p,
|
|
@@ -552,7 +576,7 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
552
576
|
color: inherit;
|
|
553
577
|
}
|
|
554
578
|
.srte-editor [contenteditable] th {
|
|
555
|
-
background: var(--srte-
|
|
579
|
+
background: var(--srte-muted);
|
|
556
580
|
font-weight: 600;
|
|
557
581
|
text-align: left;
|
|
558
582
|
}
|
|
@@ -613,6 +637,39 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
613
637
|
.srte-editor [contenteditable] sup {
|
|
614
638
|
line-height: 0;
|
|
615
639
|
}
|
|
640
|
+
/*
|
|
641
|
+
* A page break has no native visual meaning in a continuously-scrolling
|
|
642
|
+
* web editor - its entire purpose is what happens at export/print time
|
|
643
|
+
* (see atom/formats.ts's atomToHtml doc comment). This gives it a clearly
|
|
644
|
+
* distinct live-editor appearance from the plain solid <hr> a horizontal
|
|
645
|
+
* line renders as by default, so the two are never confused: a dashed
|
|
646
|
+
* line with an explicit "Page break" label, per the feature's own design
|
|
647
|
+
* decision.
|
|
648
|
+
*/
|
|
649
|
+
.srte-editor [contenteditable] [data-smart-type="page_break"] {
|
|
650
|
+
display: block;
|
|
651
|
+
position: relative;
|
|
652
|
+
height: 0;
|
|
653
|
+
margin: 1.5em 0;
|
|
654
|
+
border: none;
|
|
655
|
+
border-top: 2px dashed var(--srte-border);
|
|
656
|
+
cursor: default;
|
|
657
|
+
}
|
|
658
|
+
.srte-editor [contenteditable] [data-smart-type="page_break"]::after {
|
|
659
|
+
content: "Page break";
|
|
660
|
+
position: absolute;
|
|
661
|
+
top: -0.7em;
|
|
662
|
+
left: 50%;
|
|
663
|
+
transform: translateX(-50%);
|
|
664
|
+
background: var(--srte-canvas);
|
|
665
|
+
padding: 0 0.6em;
|
|
666
|
+
font-size: 11px;
|
|
667
|
+
font-weight: 600;
|
|
668
|
+
text-transform: uppercase;
|
|
669
|
+
letter-spacing: 0.05em;
|
|
670
|
+
color: var(--srte-text-muted);
|
|
671
|
+
white-space: nowrap;
|
|
672
|
+
}
|
|
616
673
|
`;
|
|
617
674
|
const SRTE_STYLE_ID = 'srte-theme-defaults';
|
|
618
675
|
export function ensureStyleSheet() {
|