smartrte-react 0.3.4 → 1.0.0-beta.1
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 +19 -0
- package/README.md +23 -0
- package/dist/adapters/domChecklistCommandBridge.d.ts +6 -0
- package/dist/adapters/domChecklistCommandBridge.js +33 -0
- package/dist/adapters/domCommandBridge.d.ts +19 -0
- package/dist/adapters/domCommandBridge.js +33 -0
- package/dist/adapters/domInlineAtomCommandBridge.d.ts +15 -0
- package/dist/adapters/domInlineAtomCommandBridge.js +205 -0
- package/dist/adapters/domInlineImageCommandBridge.d.ts +14 -0
- package/dist/adapters/domInlineImageCommandBridge.js +6 -0
- package/dist/adapters/domMoveCommandBridge.d.ts +7 -0
- package/dist/adapters/domMoveCommandBridge.js +54 -0
- package/dist/adapters/domSelectionBridge.d.ts +5 -3
- package/dist/adapters/domSelectionBridge.js +96 -44
- package/dist/adapters/domSmartDocument.d.ts +4 -4
- package/dist/adapters/domSmartDocument.js +159 -17
- package/dist/adapters/domTableCommandBridge.d.ts +61 -0
- package/dist/adapters/domTableCommandBridge.js +231 -0
- package/dist/adapters/internalFlags.d.ts +0 -8
- package/dist/adapters/internalFlags.js +0 -24
- package/dist/adapters/legacyListShadowComparator.d.ts +26 -0
- package/dist/adapters/legacyListShadowComparator.js +316 -0
- package/dist/adapters/pdfPrint.d.ts +8 -0
- package/dist/adapters/pdfPrint.js +22 -0
- package/dist/adapters/shadowMode.d.ts +3 -3
- package/dist/adapters/shadowMode.js +1 -1
- package/dist/canonicalEditorRuntime.d.ts +129 -0
- package/dist/canonicalEditorRuntime.js +331 -0
- package/dist/capabilityPresets.d.ts +27 -0
- package/dist/capabilityPresets.js +21 -0
- package/dist/collabTransport.d.ts +66 -0
- package/dist/collabTransport.js +1 -0
- package/dist/commentProvider.d.ts +24 -0
- package/dist/commentProvider.js +1 -0
- package/dist/components/CanonicalAuthorityEditor.d.ts +59 -0
- package/dist/components/CanonicalAuthorityEditor.js +1492 -0
- package/dist/components/ClassicEditorAuthority.d.ts +109 -0
- package/dist/components/ClassicEditorAuthority.js +17 -0
- package/dist/components/ColorPickerPopover.d.ts +39 -0
- package/dist/components/ColorPickerPopover.js +285 -0
- package/dist/components/CommentMarkers.d.ts +25 -0
- package/dist/components/CommentMarkers.js +105 -0
- package/dist/components/CommentThreadPanel.d.ts +26 -0
- package/dist/components/CommentThreadPanel.js +47 -0
- package/dist/components/ContextMenu.d.ts +23 -0
- package/dist/components/ContextMenu.js +141 -0
- package/dist/components/FormulaLibraryPopover.d.ts +16 -0
- package/dist/components/FormulaLibraryPopover.js +107 -0
- package/dist/components/LinkEditorPopover.d.ts +3 -1
- package/dist/components/LinkEditorPopover.js +22 -6
- package/dist/components/MediaManager.d.ts +4 -20
- package/dist/components/MediaManager.js +35 -6
- package/dist/components/MediaOverlay.d.ts +36 -0
- package/dist/components/MediaOverlay.js +163 -0
- package/dist/components/MediaPicker.d.ts +10 -0
- package/dist/components/MediaPicker.js +16 -0
- package/dist/components/SpecialCharacterPopover.d.ts +18 -0
- package/dist/components/SpecialCharacterPopover.js +99 -0
- package/dist/components/StructuralSuggestionMarkers.d.ts +20 -0
- package/dist/components/StructuralSuggestionMarkers.js +56 -0
- package/dist/components/SuggestionPanel.d.ts +26 -0
- package/dist/components/SuggestionPanel.js +33 -0
- package/dist/components/TableBorderPopover.d.ts +59 -0
- package/dist/components/TableBorderPopover.js +147 -0
- package/dist/components/TableResizeHandles.d.ts +25 -0
- package/dist/components/TableResizeHandles.js +301 -0
- package/dist/components/TableSizePickerPopover.d.ts +16 -0
- package/dist/components/TableSizePickerPopover.js +126 -0
- package/dist/components/ToolbarPrimitives.d.ts +129 -0
- package/dist/components/ToolbarPrimitives.js +146 -0
- package/dist/components/VersionHistoryPanel.d.ts +9 -0
- package/dist/components/VersionHistoryPanel.js +157 -0
- package/dist/formulaLibrary.d.ts +31 -0
- package/dist/formulaLibrary.js +90 -0
- package/dist/index.d.ts +23 -1
- package/dist/index.js +14 -1
- package/dist/mediaManagerAdapter.d.ts +12 -0
- package/dist/mediaManagerAdapter.js +34 -0
- package/dist/mediaProvider.d.ts +59 -0
- package/dist/mediaProvider.js +1 -0
- package/dist/specialCharacters.d.ts +22 -0
- package/dist/specialCharacters.js +86 -0
- package/dist/standalone/classic-editor-embed.js +19 -12
- package/dist/suggestionProvider.d.ts +17 -0
- package/dist/suggestionProvider.js +1 -0
- package/dist/theme.d.ts +1 -1
- package/dist/theme.js +161 -1
- package/dist/versionProvider.d.ts +29 -0
- package/dist/versionProvider.js +1 -0
- package/package.json +28 -14
- package/dist/adapters/coreBold.d.ts +0 -5
- package/dist/adapters/coreBold.js +0 -3
- package/dist/adapters/inlineMarkCoreExecution.d.ts +0 -9
- package/dist/adapters/inlineMarkCoreExecution.js +0 -25
- package/dist/components/ClassicEditor.d.ts +0 -66
- package/dist/components/ClassicEditor.js +0 -6617
- package/dist/standalone/editor.js +0 -841
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 11.5 §2.1: MediaManager.tsx was already built (upload/library/
|
|
3
|
+
* duplicate-detection/info panel) against its own MediaManagerAdapter
|
|
4
|
+
* shape, not the real MediaProvider interface - this translates one into
|
|
5
|
+
* the other. No MediaProvider change needed (confirmed in
|
|
6
|
+
* docs/PHASE_11_5_MEDIAPROVIDER_FINDINGS.md): the shapes just differ
|
|
7
|
+
* (batch vs. single upload; bundled vs. positional search query), not the
|
|
8
|
+
* capabilities.
|
|
9
|
+
*/
|
|
10
|
+
export const mediaManagerAdapterFrom = (provider) => ({
|
|
11
|
+
async upload(files) {
|
|
12
|
+
const uploaded = [];
|
|
13
|
+
for (const file of files) {
|
|
14
|
+
const result = await provider.upload(file);
|
|
15
|
+
uploaded.push({
|
|
16
|
+
id: result.id,
|
|
17
|
+
url: result.url,
|
|
18
|
+
title: file.name,
|
|
19
|
+
mimeType: file.type || undefined,
|
|
20
|
+
sizeBytes: file.size,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return uploaded;
|
|
24
|
+
},
|
|
25
|
+
search(query) {
|
|
26
|
+
return provider.search(query.q || "", {
|
|
27
|
+
mimePrefix: query.mimePrefix,
|
|
28
|
+
tags: query.tags,
|
|
29
|
+
hashHex: query.hashHex,
|
|
30
|
+
pageSize: query.pageSize,
|
|
31
|
+
}, query.page);
|
|
32
|
+
},
|
|
33
|
+
remove: (id) => provider.remove(id),
|
|
34
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export type MediaKind = "image" | "video" | "audio";
|
|
2
|
+
export interface UploadOptions {
|
|
3
|
+
readonly signal?: AbortSignal;
|
|
4
|
+
}
|
|
5
|
+
export interface MediaFilters {
|
|
6
|
+
readonly mimePrefix?: string;
|
|
7
|
+
readonly tags?: readonly string[];
|
|
8
|
+
readonly hashHex?: string;
|
|
9
|
+
readonly pageSize?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface MediaItem {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly url: string;
|
|
14
|
+
readonly width?: number;
|
|
15
|
+
readonly height?: number;
|
|
16
|
+
readonly sizeBytes?: number;
|
|
17
|
+
readonly mimeType?: string;
|
|
18
|
+
readonly hashHex?: string;
|
|
19
|
+
readonly createdAt?: string;
|
|
20
|
+
readonly title?: string;
|
|
21
|
+
readonly alt?: string;
|
|
22
|
+
readonly tags?: readonly string[];
|
|
23
|
+
/**
|
|
24
|
+
* How many times this asset is referenced across a host's content.
|
|
25
|
+
* Phase 11.5 §2.1: optional and additive - the package has no way to
|
|
26
|
+
* compute cross-document usage itself (it only ever sees one document at
|
|
27
|
+
* a time), so a host that wants this populates it from their own
|
|
28
|
+
* tracking; when absent, the UI simply omits the count rather than
|
|
29
|
+
* showing 0/misleading data.
|
|
30
|
+
*/
|
|
31
|
+
readonly usageCount?: number;
|
|
32
|
+
readonly license?: {
|
|
33
|
+
readonly author?: string;
|
|
34
|
+
readonly licenseType?: string;
|
|
35
|
+
readonly licenseText?: string;
|
|
36
|
+
readonly sourceUrl?: string;
|
|
37
|
+
readonly workName?: string;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Host-owned media boundary. The editor never receives storage credentials.
|
|
42
|
+
*
|
|
43
|
+
* The editor performs no server-side-equivalent validation of `upload`'s
|
|
44
|
+
* `file` - `CanonicalAuthorityEditor` applies only a best-effort client-side
|
|
45
|
+
* extension/MIME allow-list check before calling this method, which is a UX
|
|
46
|
+
* nicety, not a security boundary (a client can always be bypassed). The
|
|
47
|
+
* host's `upload` implementation MUST independently validate file type,
|
|
48
|
+
* size, and content server-side before persisting or serving whatever URL
|
|
49
|
+
* it returns - the same way it must never hand the editor real storage
|
|
50
|
+
* credentials.
|
|
51
|
+
*/
|
|
52
|
+
export interface MediaProvider {
|
|
53
|
+
upload(file: File, opts?: UploadOptions): Promise<{
|
|
54
|
+
url: string;
|
|
55
|
+
id: string;
|
|
56
|
+
}>;
|
|
57
|
+
search(query: string, filters?: MediaFilters, page?: number): Promise<MediaItem[]>;
|
|
58
|
+
remove(id: string): Promise<void>;
|
|
59
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Special character picker data (Direction B scope, per the owner-approved
|
|
3
|
+
* proposal - see the "Formula Library & Special Character Picker" scoping
|
|
4
|
+
* artifact): six categories, ~200 characters total. Unlike the formula
|
|
5
|
+
* library, this set is drawn from standardized Unicode blocks, so "cover
|
|
6
|
+
* this category" is a genuinely completable goal, not an open-ended one.
|
|
7
|
+
*
|
|
8
|
+
* Every entry inserts as plain text (see `insertSpecialCharacter` in
|
|
9
|
+
* CanonicalAuthorityEditor.tsx) - special characters are ordinary
|
|
10
|
+
* characters, never schema-level atoms.
|
|
11
|
+
*/
|
|
12
|
+
export type SpecialCharacterCategory = "greek" | "operators" | "arrows" | "currency" | "punctuation" | "accented";
|
|
13
|
+
export interface SpecialCharacterEntry {
|
|
14
|
+
readonly char: string;
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly category: SpecialCharacterCategory;
|
|
17
|
+
}
|
|
18
|
+
export declare const SPECIAL_CHARACTER_CATEGORIES: readonly {
|
|
19
|
+
readonly id: SpecialCharacterCategory;
|
|
20
|
+
readonly label: string;
|
|
21
|
+
}[];
|
|
22
|
+
export declare const SPECIAL_CHARACTERS: readonly SpecialCharacterEntry[];
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Special character picker data (Direction B scope, per the owner-approved
|
|
3
|
+
* proposal - see the "Formula Library & Special Character Picker" scoping
|
|
4
|
+
* artifact): six categories, ~200 characters total. Unlike the formula
|
|
5
|
+
* library, this set is drawn from standardized Unicode blocks, so "cover
|
|
6
|
+
* this category" is a genuinely completable goal, not an open-ended one.
|
|
7
|
+
*
|
|
8
|
+
* Every entry inserts as plain text (see `insertSpecialCharacter` in
|
|
9
|
+
* CanonicalAuthorityEditor.tsx) - special characters are ordinary
|
|
10
|
+
* characters, never schema-level atoms.
|
|
11
|
+
*/
|
|
12
|
+
export const SPECIAL_CHARACTER_CATEGORIES = [
|
|
13
|
+
{ id: "greek", label: "Greek" },
|
|
14
|
+
{ id: "operators", label: "Operators" },
|
|
15
|
+
{ id: "arrows", label: "Arrows" },
|
|
16
|
+
{ id: "currency", label: "Currency" },
|
|
17
|
+
{ id: "punctuation", label: "Punctuation" },
|
|
18
|
+
{ id: "accented", label: "Accented" },
|
|
19
|
+
];
|
|
20
|
+
const greekLower = [
|
|
21
|
+
["α", "alpha"], ["β", "beta"], ["γ", "gamma"], ["δ", "delta"], ["ε", "epsilon"], ["ζ", "zeta"],
|
|
22
|
+
["η", "eta"], ["θ", "theta"], ["ι", "iota"], ["κ", "kappa"], ["λ", "lambda"], ["μ", "mu"],
|
|
23
|
+
["ν", "nu"], ["ξ", "xi"], ["ο", "omicron"], ["π", "pi"], ["ρ", "rho"], ["σ", "sigma"],
|
|
24
|
+
["τ", "tau"], ["υ", "upsilon"], ["φ", "phi"], ["χ", "chi"], ["ψ", "psi"], ["ω", "omega"],
|
|
25
|
+
];
|
|
26
|
+
const greekUpper = [
|
|
27
|
+
["Α", "Alpha"], ["Β", "Beta"], ["Γ", "Gamma"], ["Δ", "Delta"], ["Ε", "Epsilon"], ["Ζ", "Zeta"],
|
|
28
|
+
["Η", "Eta"], ["Θ", "Theta"], ["Ι", "Iota"], ["Κ", "Kappa"], ["Λ", "Lambda"], ["Μ", "Mu"],
|
|
29
|
+
["Ν", "Nu"], ["Ξ", "Xi"], ["Ο", "Omicron"], ["Π", "Pi"], ["Ρ", "Rho"], ["Σ", "Sigma"],
|
|
30
|
+
["Τ", "Tau"], ["Υ", "Upsilon"], ["Φ", "Phi"], ["Χ", "Chi"], ["Ψ", "Psi"], ["Ω", "Omega"],
|
|
31
|
+
];
|
|
32
|
+
const operators = [
|
|
33
|
+
["±", "plus-minus"], ["∓", "minus-plus"], ["×", "multiplication"], ["÷", "division"], ["⋅", "dot"],
|
|
34
|
+
["≈", "approximately equal"], ["≠", "not equal"], ["≡", "identical to"], ["≤", "less than or equal"],
|
|
35
|
+
["≥", "greater than or equal"], ["≪", "much less than"], ["≫", "much greater than"], ["∞", "infinity"],
|
|
36
|
+
["√", "square root"], ["∛", "cube root"], ["∑", "summation"], ["∏", "product"], ["∫", "integral"],
|
|
37
|
+
["∬", "double integral"], ["∮", "contour integral"], ["∂", "partial derivative"], ["∇", "nabla"],
|
|
38
|
+
["∆", "delta (increment)"], ["∈", "element of"], ["∉", "not an element of"], ["⊂", "subset of"],
|
|
39
|
+
["⊃", "superset of"], ["⊆", "subset or equal"], ["⊇", "superset or equal"], ["∪", "union"],
|
|
40
|
+
["∩", "intersection"], ["∅", "empty set"], ["∀", "for all"], ["∃", "there exists"], ["¬", "not"],
|
|
41
|
+
["∧", "logical and"], ["∨", "logical or"], ["⊕", "circled plus"], ["⊗", "circled times"],
|
|
42
|
+
["∝", "proportional to"], ["∥", "parallel to"], ["⊥", "perpendicular to"], ["°", "degree"],
|
|
43
|
+
["′", "prime"], ["″", "double prime"], ["ℏ", "reduced Planck constant"],
|
|
44
|
+
];
|
|
45
|
+
const arrows = [
|
|
46
|
+
["←", "left arrow"], ["↑", "up arrow"], ["→", "right arrow"], ["↓", "down arrow"],
|
|
47
|
+
["↔", "left-right arrow"], ["↕", "up-down arrow"], ["↖", "up-left arrow"], ["↗", "up-right arrow"],
|
|
48
|
+
["↘", "down-right arrow"], ["↙", "down-left arrow"], ["⇐", "left double arrow"], ["⇑", "up double arrow"],
|
|
49
|
+
["⇒", "right double arrow"], ["⇓", "down double arrow"], ["⇔", "left-right double arrow"],
|
|
50
|
+
["⇕", "up-down double arrow"], ["⇌", "equilibrium arrows"], ["⇋", "reverse equilibrium arrows"],
|
|
51
|
+
["↦", "maps to"], ["↩", "hook left arrow"], ["↪", "hook right arrow"], ["⤴", "arrow curving up"],
|
|
52
|
+
["⤵", "arrow curving down"], ["⇢", "dashed right arrow"], ["⇠", "dashed left arrow"],
|
|
53
|
+
];
|
|
54
|
+
const currency = [
|
|
55
|
+
["$", "dollar sign"], ["¢", "cent sign"], ["£", "pound sign"], ["¤", "generic currency sign"],
|
|
56
|
+
["¥", "yen sign"], ["€", "euro sign"], ["₹", "rupee sign"], ["₩", "won sign"], ["₽", "ruble sign"],
|
|
57
|
+
["₺", "lira sign"], ["₴", "hryvnia sign"], ["₫", "dong sign"], ["₦", "naira sign"], ["฿", "baht sign"],
|
|
58
|
+
["₱", "peso sign"],
|
|
59
|
+
];
|
|
60
|
+
const punctuation = [
|
|
61
|
+
["–", "en dash"], ["—", "em dash"], ["…", "ellipsis"], ["“", "left double quote"], ["”", "right double quote"],
|
|
62
|
+
["‘", "left single quote"], ["’", "right single quote"], ["„", "low double quote"], ["«", "left guillemet"],
|
|
63
|
+
["»", "right guillemet"], ["§", "section sign"], ["¶", "pilcrow"], ["©", "copyright"], ["®", "registered trademark"],
|
|
64
|
+
["™", "trademark"], ["†", "dagger"], ["‡", "double dagger"], ["•", "bullet"], ["‰", "per mille"], ["¦", "broken bar"],
|
|
65
|
+
];
|
|
66
|
+
const accented = [
|
|
67
|
+
["à", "a grave"], ["á", "a acute"], ["â", "a circumflex"], ["ã", "a tilde"], ["ä", "a diaeresis"], ["å", "a ring"],
|
|
68
|
+
["æ", "ae ligature"], ["ç", "c cedilla"], ["è", "e grave"], ["é", "e acute"], ["ê", "e circumflex"], ["ë", "e diaeresis"],
|
|
69
|
+
["ì", "i grave"], ["í", "i acute"], ["î", "i circumflex"], ["ï", "i diaeresis"], ["ñ", "n tilde"], ["ò", "o grave"],
|
|
70
|
+
["ó", "o acute"], ["ô", "o circumflex"], ["õ", "o tilde"], ["ö", "o diaeresis"], ["ø", "o stroke"], ["ù", "u grave"],
|
|
71
|
+
["ú", "u acute"], ["û", "u circumflex"], ["ü", "u diaeresis"], ["ý", "y acute"], ["ÿ", "y diaeresis"], ["ß", "sharp s"],
|
|
72
|
+
["À", "A grave"], ["Á", "A acute"], ["Â", "A circumflex"], ["Ã", "A tilde"], ["Ä", "A diaeresis"], ["Å", "A ring"],
|
|
73
|
+
["Æ", "AE ligature"], ["Ç", "C cedilla"], ["È", "E grave"], ["É", "E acute"], ["Ê", "E circumflex"], ["Ë", "E diaeresis"],
|
|
74
|
+
["Ì", "I grave"], ["Í", "I acute"], ["Ñ", "N tilde"], ["Ò", "O grave"], ["Ó", "O acute"], ["Ö", "O diaeresis"],
|
|
75
|
+
["Ù", "U grave"], ["Ü", "U diaeresis"],
|
|
76
|
+
];
|
|
77
|
+
const toEntries = (pairs, category) => pairs.map(([char, name]) => ({ char, name, category }));
|
|
78
|
+
export const SPECIAL_CHARACTERS = [
|
|
79
|
+
...toEntries(greekLower, "greek"),
|
|
80
|
+
...toEntries(greekUpper, "greek"),
|
|
81
|
+
...toEntries(operators, "operators"),
|
|
82
|
+
...toEntries(arrows, "arrows"),
|
|
83
|
+
...toEntries(currency, "currency"),
|
|
84
|
+
...toEntries(punctuation, "punctuation"),
|
|
85
|
+
...toEntries(accented, "accented"),
|
|
86
|
+
];
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import React, { useImperativeHandle, useRef
|
|
2
|
+
import React, { useImperativeHandle, useRef } from "react";
|
|
3
3
|
import { createRoot } from "react-dom/client";
|
|
4
|
-
import {
|
|
4
|
+
import { foundationSchema, parseCanonicalListHtml, serializeCanonicalListHtml } from "smartrte-core/foundation";
|
|
5
|
+
import { ClassicEditor as ClassicEditorComponent } from "../components/ClassicEditorAuthority.js";
|
|
5
6
|
function ClassicEditorHost(props, ref) {
|
|
6
|
-
const [html, setHtml] = useState(props.value || "");
|
|
7
7
|
const containerRef = useRef(null);
|
|
8
|
+
const editorRef = useRef(null);
|
|
8
9
|
useImperativeHandle(ref, () => ({
|
|
9
10
|
setHtml(next) {
|
|
10
|
-
|
|
11
|
+
const current = editorRef.current;
|
|
12
|
+
if (!current)
|
|
13
|
+
return;
|
|
14
|
+
current.replaceValue({
|
|
15
|
+
schemaVersion: foundationSchema.version,
|
|
16
|
+
revision: current.getRevision() + 1,
|
|
17
|
+
document: parseCanonicalListHtml(next || "<p></p>"),
|
|
18
|
+
});
|
|
11
19
|
},
|
|
12
20
|
getHtml() {
|
|
13
|
-
|
|
14
|
-
|
|
21
|
+
const value = editorRef.current?.getValue();
|
|
22
|
+
return value ? serializeCanonicalListHtml(value.document, { clean: true }) : "";
|
|
15
23
|
},
|
|
16
24
|
focus() {
|
|
17
25
|
const el = containerRef.current?.querySelector('[contenteditable="true"]');
|
|
@@ -24,20 +32,19 @@ function ClassicEditorHost(props, ref) {
|
|
|
24
32
|
destroy() {
|
|
25
33
|
// No-op here; actual unmount happens in init
|
|
26
34
|
},
|
|
27
|
-
}), [
|
|
28
|
-
return (_jsx("div", { ref: containerRef, style: { height: "100%", width: "100%" }, children: _jsx(ClassicEditorComponent, {
|
|
29
|
-
|
|
30
|
-
props.onChange?.(v);
|
|
35
|
+
}), []);
|
|
36
|
+
return (_jsx("div", { ref: containerRef, style: { height: "100%", width: "100%" }, children: _jsx(ClassicEditorComponent, { ref: editorRef, defaultValue: props.value || "<p></p>", onChange: (html) => {
|
|
37
|
+
props.onChange?.(html);
|
|
31
38
|
try {
|
|
32
39
|
// Bridge to Flutter if present
|
|
33
40
|
// @ts-ignore
|
|
34
41
|
const ch = window.ToFlutter;
|
|
35
42
|
if (ch && typeof ch.postMessage === "function") {
|
|
36
|
-
ch.postMessage(JSON.stringify({ type: "change", html
|
|
43
|
+
ch.postMessage(JSON.stringify({ type: "change", html }));
|
|
37
44
|
}
|
|
38
45
|
}
|
|
39
46
|
catch { }
|
|
40
|
-
}, placeholder: props.placeholder, minHeight: props.minHeight, maxHeight: props.maxHeight, readOnly: props.readOnly, table: props.table, media: props.media, formula: props.formula, showFontSize: props.showFontSize,
|
|
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 }) }));
|
|
41
48
|
}
|
|
42
49
|
const ClassicEditorHostWithRef = React.forwardRef(ClassicEditorHost);
|
|
43
50
|
function initClassicEditor(opts) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { StructuralSuggestion } from "smartrte-core/foundation";
|
|
2
|
+
/**
|
|
3
|
+
* Host-owned persistence boundary for *structural* suggestions only,
|
|
4
|
+
* mirroring CommentProvider's shape. Inline suggestions (Phase 12a §2.3's
|
|
5
|
+
* mark-based half - see suggestions/inline.ts) need no equivalent provider:
|
|
6
|
+
* they are real marks on real document text, so they already ride along in
|
|
7
|
+
* whatever the host persists for the document itself (PersistedEditorDocument)
|
|
8
|
+
* - a separate side-channel for them would just be a second, redundant
|
|
9
|
+
* source of truth. Structural suggestions (a proposed whole-node removal)
|
|
10
|
+
* are NOT part of the document the way a comment thread isn't, so they need
|
|
11
|
+
* their own out-of-band store, exactly like comments.
|
|
12
|
+
*/
|
|
13
|
+
export interface SuggestionProvider {
|
|
14
|
+
list(): Promise<readonly StructuralSuggestion[]>;
|
|
15
|
+
save(suggestion: StructuralSuggestion): Promise<void>;
|
|
16
|
+
remove(suggestionId: string): Promise<void>;
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
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-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-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-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 color: var(--srte-primary);\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] 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 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]::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.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] 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: #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";
|
|
3
3
|
export declare function ensureStyleSheet(): void;
|
package/dist/theme.js
CHANGED
|
@@ -27,6 +27,11 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
27
27
|
--srte-accent-bg: rgba(2, 132, 199, 0.12);
|
|
28
28
|
--srte-danger: #dc2626;
|
|
29
29
|
--srte-primary: #2563eb;
|
|
30
|
+
/* --srte-primary on --srte-accent-bg measures 4.43:1, under WCAG AA's
|
|
31
|
+
4.5:1 for normal text - used only for the pressed toolbar-button state
|
|
32
|
+
(see .srte-tool-button[aria-pressed="true"]), not a replacement for
|
|
33
|
+
--srte-primary generally. */
|
|
34
|
+
--srte-primary-pressed: #1d4ed8;
|
|
30
35
|
--srte-surface-subtle: #f3f4f6;
|
|
31
36
|
--srte-on-primary: #ffffff;
|
|
32
37
|
--srte-cancel-bg: #f3f4f6;
|
|
@@ -60,6 +65,7 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
60
65
|
--srte-accent-bg: rgba(56, 189, 248, 0.16);
|
|
61
66
|
--srte-danger: #ef4444;
|
|
62
67
|
--srte-primary: #3b82f6;
|
|
68
|
+
--srte-primary-pressed: var(--srte-primary);
|
|
63
69
|
--srte-surface-subtle: #333333;
|
|
64
70
|
--srte-on-primary: #ffffff;
|
|
65
71
|
--srte-cancel-bg: #333333;
|
|
@@ -71,6 +77,18 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
71
77
|
font-family: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
72
78
|
container: srte-editor / inline-size;
|
|
73
79
|
}
|
|
80
|
+
.srte-canonical-authority > .srte-editor[contenteditable] {
|
|
81
|
+
width: 100%;
|
|
82
|
+
padding: 16px 20px;
|
|
83
|
+
box-sizing: border-box;
|
|
84
|
+
border: 1px solid var(--srte-border);
|
|
85
|
+
border-radius: 0 0 var(--srte-radius) var(--srte-radius);
|
|
86
|
+
outline: none;
|
|
87
|
+
background: var(--srte-canvas);
|
|
88
|
+
color: var(--srte-foreground);
|
|
89
|
+
caret-color: var(--srte-foreground);
|
|
90
|
+
line-height: 1.6;
|
|
91
|
+
}
|
|
74
92
|
.srte-toolbar {
|
|
75
93
|
display: flex;
|
|
76
94
|
flex-wrap: wrap;
|
|
@@ -134,7 +152,12 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
134
152
|
}
|
|
135
153
|
.srte-tool-button.srte-active,
|
|
136
154
|
.srte-tool-button[aria-pressed="true"] {
|
|
137
|
-
|
|
155
|
+
/* var(--srte-primary) (#2563eb) on var(--srte-accent-bg) measured 4.43:1,
|
|
156
|
+
just under WCAG AA's 4.5:1 for normal text (axe-core, Phase 11 Tier 3) -
|
|
157
|
+
a dedicated, darker pressed-state color keeps the same hue family
|
|
158
|
+
while clearing the threshold, without changing --srte-primary's other,
|
|
159
|
+
already-compliant usages (e.g. against solid backgrounds). */
|
|
160
|
+
color: var(--srte-primary-pressed);
|
|
138
161
|
background: var(--srte-accent-bg);
|
|
139
162
|
border-color: color-mix(in srgb, var(--srte-primary) 35%, transparent);
|
|
140
163
|
}
|
|
@@ -231,6 +254,84 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
231
254
|
align-items: start;
|
|
232
255
|
padding-left: 0;
|
|
233
256
|
}
|
|
257
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal-paren"][data-srte-list-depth="0"] > li::marker {
|
|
258
|
+
content: counter(list-item, decimal) ") ";
|
|
259
|
+
}
|
|
260
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal-paren"][data-srte-list-depth="1"] > li::marker {
|
|
261
|
+
content: counter(list-item, lower-alpha) ") ";
|
|
262
|
+
}
|
|
263
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal-paren"][data-srte-list-depth="2"] > li::marker,
|
|
264
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal-paren"][data-srte-list-depth="3"] > li::marker {
|
|
265
|
+
content: counter(list-item, lower-roman) ") ";
|
|
266
|
+
}
|
|
267
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal"][data-srte-list-depth="0"] > li::marker {
|
|
268
|
+
content: counter(list-item, decimal) ". ";
|
|
269
|
+
}
|
|
270
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal"][data-srte-list-depth="1"] > li::marker {
|
|
271
|
+
content: counter(list-item, lower-alpha) ". ";
|
|
272
|
+
}
|
|
273
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal"][data-srte-list-depth="2"] > li::marker,
|
|
274
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-decimal"][data-srte-list-depth="3"] > li::marker {
|
|
275
|
+
content: counter(list-item, lower-roman) ". ";
|
|
276
|
+
}
|
|
277
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-alpha"][data-srte-list-depth="0"] > li::marker {
|
|
278
|
+
content: counter(list-item, upper-alpha) ". ";
|
|
279
|
+
}
|
|
280
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-alpha"][data-srte-list-depth="1"] > li::marker {
|
|
281
|
+
content: counter(list-item, lower-alpha) ". ";
|
|
282
|
+
}
|
|
283
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-alpha"][data-srte-list-depth="2"] > li::marker,
|
|
284
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-alpha"][data-srte-list-depth="3"] > li::marker {
|
|
285
|
+
content: counter(list-item, lower-roman) ". ";
|
|
286
|
+
}
|
|
287
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-roman"][data-srte-list-depth="0"] > li::marker {
|
|
288
|
+
content: counter(list-item, upper-roman) ". ";
|
|
289
|
+
}
|
|
290
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-roman"][data-srte-list-depth="1"] > li::marker {
|
|
291
|
+
content: counter(list-item, upper-alpha) ". ";
|
|
292
|
+
}
|
|
293
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-roman"][data-srte-list-depth="2"] > li::marker,
|
|
294
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-upper-roman"][data-srte-list-depth="3"] > li::marker {
|
|
295
|
+
content: counter(list-item, decimal) ". ";
|
|
296
|
+
}
|
|
297
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-leading-zero"][data-srte-list-depth="0"] > li::marker {
|
|
298
|
+
content: counter(list-item, decimal-leading-zero) ". ";
|
|
299
|
+
}
|
|
300
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-leading-zero"][data-srte-list-depth="1"] > li::marker {
|
|
301
|
+
content: counter(list-item, lower-alpha) ". ";
|
|
302
|
+
}
|
|
303
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-leading-zero"][data-srte-list-depth="2"] > li::marker,
|
|
304
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-leading-zero"][data-srte-list-depth="3"] > li::marker {
|
|
305
|
+
content: counter(list-item, lower-roman) ". ";
|
|
306
|
+
}
|
|
307
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-disc"][data-srte-list-depth="0"] > li::marker {
|
|
308
|
+
content: "● ";
|
|
309
|
+
}
|
|
310
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-disc"][data-srte-list-depth="1"] > li::marker {
|
|
311
|
+
content: "○ ";
|
|
312
|
+
}
|
|
313
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-disc"][data-srte-list-depth="2"] > li::marker,
|
|
314
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-disc"][data-srte-list-depth="3"] > li::marker {
|
|
315
|
+
content: "■ ";
|
|
316
|
+
}
|
|
317
|
+
.srte-editor [contenteditable] ol[data-srte-list-preset="ordered-outline"] > li::marker {
|
|
318
|
+
content: counters(list-item, ".") ". ";
|
|
319
|
+
}
|
|
320
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-diamond"][data-srte-list-depth="0"] > li::marker { content: "❖ "; }
|
|
321
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-diamond"][data-srte-list-depth="1"] > li::marker { content: "➢ "; }
|
|
322
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-diamond"][data-srte-list-depth="2"] > li::marker { content: "■ "; }
|
|
323
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-square"][data-srte-list-depth="0"] > li::marker { content: "□ "; }
|
|
324
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-square"][data-srte-list-depth="1"] > li::marker { content: "▣ "; }
|
|
325
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-square"][data-srte-list-depth="2"] > li::marker { content: "▪ "; }
|
|
326
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-arrow"][data-srte-list-depth="0"] > li::marker { content: "➜ "; }
|
|
327
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-arrow"][data-srte-list-depth="1"] > li::marker { content: "◆ "; }
|
|
328
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-arrow"][data-srte-list-depth="2"] > li::marker { content: "● "; }
|
|
329
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-star"][data-srte-list-depth="0"] > li::marker { content: "★ "; }
|
|
330
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-star"][data-srte-list-depth="1"] > li::marker { content: "○ "; }
|
|
331
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-star"][data-srte-list-depth="2"] > li::marker { content: "■ "; }
|
|
332
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-arrow-circle"][data-srte-list-depth="0"] > li::marker { content: "➢ "; }
|
|
333
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-arrow-circle"][data-srte-list-depth="1"] > li::marker { content: "○ "; }
|
|
334
|
+
.srte-editor [contenteditable] ul[data-srte-list-preset="bullet-arrow-circle"][data-srte-list-depth="2"] > li::marker { content: "■ "; }
|
|
234
335
|
.srte-editor [contenteditable] ul[data-srte-checklist="true"] > li > [data-srte-check] {
|
|
235
336
|
display: inline-flex;
|
|
236
337
|
grid-column: 1;
|
|
@@ -239,11 +340,26 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
239
340
|
justify-content: center;
|
|
240
341
|
width: 1.1em;
|
|
241
342
|
height: 1.6em;
|
|
343
|
+
min-width: 0;
|
|
344
|
+
box-sizing: border-box;
|
|
345
|
+
padding: 0;
|
|
346
|
+
border: 0;
|
|
347
|
+
border-radius: 0;
|
|
348
|
+
background: transparent;
|
|
349
|
+
color: inherit;
|
|
350
|
+
font: inherit;
|
|
351
|
+
cursor: pointer;
|
|
352
|
+
appearance: none;
|
|
353
|
+
outline: none;
|
|
242
354
|
margin: 0;
|
|
243
355
|
line-height: 1.6;
|
|
244
356
|
vertical-align: top;
|
|
245
357
|
position: relative;
|
|
246
358
|
}
|
|
359
|
+
.srte-editor [contenteditable] ul[data-srte-checklist="true"] > li > [data-srte-check]:focus-visible {
|
|
360
|
+
outline: 2px solid var(--srte-ring);
|
|
361
|
+
outline-offset: 2px;
|
|
362
|
+
}
|
|
247
363
|
.srte-editor [contenteditable] ul[data-srte-checklist="true"] > li > [data-srte-check]::before {
|
|
248
364
|
content: "";
|
|
249
365
|
width: .9em;
|
|
@@ -317,6 +433,42 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
317
433
|
}
|
|
318
434
|
.srte-menu-item { height: 40px; }
|
|
319
435
|
}
|
|
436
|
+
/*
|
|
437
|
+
* Wide-viewport promotion (docs/bugs/toolbar-priority-collapse-fixed-threshold-no-wide-promotion.md):
|
|
438
|
+
* below this, the single 639px breakpoint above was this system's ONLY
|
|
439
|
+
* threshold - every dropdown-grouped tool stayed hidden inside its dropdown
|
|
440
|
+
* at every width from 640px up to and past a 2200px+ desktop, since nothing
|
|
441
|
+
* ever measured or scaled with the extra room. A handful of tools frequent
|
|
442
|
+
* enough to matter once there's genuinely spare room (Superscript,
|
|
443
|
+
* Subscript, Text colour, Background colour, Font size, Font family, Remove
|
|
444
|
+
* link, Insert formula, Special characters) get a standalone always-visible
|
|
445
|
+
* ToolbarButton copy (data-srte-wide-promote, rendered directly in the
|
|
446
|
+
* toolbar row) that appears past this breakpoint, while their existing
|
|
447
|
+
* dropdown/mobile-menu ToolbarMenuItem copy (the exact same
|
|
448
|
+
* data-srte-wide-promote attribute, on the .srte-menu-item element instead)
|
|
449
|
+
* hides so the tool isn't offered twice. Below this breakpoint - including
|
|
450
|
+
* all of mobile AND the 640-1439px tablet/typical-laptop band, which keeps
|
|
451
|
+
* today's grouped-dropdown layout unchanged - the standalone copy stays
|
|
452
|
+
* hidden and the dropdown copy is what's reachable.
|
|
453
|
+
*
|
|
454
|
+
* 1440px, not 1280px: Playwright's own default test viewport is exactly
|
|
455
|
+
* 1280x720, and the vast majority of this suite's toolbar interactions run
|
|
456
|
+
* at that default without ever calling page.setViewportSize - a 1280px
|
|
457
|
+
* threshold would have flipped nearly every existing toolbar test's
|
|
458
|
+
* dropdown-item locators (role=menuitem) over to CSS display:none out from
|
|
459
|
+
* under them. 1440px clears that default with room to spare while still
|
|
460
|
+
* comfortably covering "wide desktop" (the ~2264px width the original
|
|
461
|
+
* report was filed against).
|
|
462
|
+
*/
|
|
463
|
+
.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
|
+
@container srte-editor (min-width: 1440px) {
|
|
469
|
+
.srte-tool-button[data-srte-wide-promote="true"] { display: inline-flex; }
|
|
470
|
+
.srte-menu-item[data-srte-wide-promote="true"] { display: none; }
|
|
471
|
+
}
|
|
320
472
|
.srte-editor [contenteditable] blockquote {
|
|
321
473
|
border-left: 4px solid var(--srte-accent);
|
|
322
474
|
margin: 0.75em 0;
|
|
@@ -391,6 +543,14 @@ export const SRTE_DEFAULT_CSS = `
|
|
|
391
543
|
border: 1px solid var(--srte-border);
|
|
392
544
|
vertical-align: top;
|
|
393
545
|
}
|
|
546
|
+
.srte-editor [contenteditable] [data-smart-cell-selected="true"] {
|
|
547
|
+
background: color-mix(in srgb, var(--srte-primary) 12%, var(--srte-canvas));
|
|
548
|
+
box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--srte-primary) 65%, transparent);
|
|
549
|
+
}
|
|
550
|
+
.srte-editor [contenteditable][data-smart-cell-selection-active] ::selection {
|
|
551
|
+
background: transparent;
|
|
552
|
+
color: inherit;
|
|
553
|
+
}
|
|
394
554
|
.srte-editor [contenteditable] th {
|
|
395
555
|
background: var(--srte-surface-subtle);
|
|
396
556
|
font-weight: 600;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { DocumentVersion } from "smartrte-core/foundation";
|
|
2
|
+
/**
|
|
3
|
+
* The listing-cheap projection of a `DocumentVersion` - a host's backend
|
|
4
|
+
* shouldn't have to ship every saved version's full document body just to
|
|
5
|
+
* render a history list. Mirrors `MediaProvider.search` returning
|
|
6
|
+
* `MediaItem` (metadata) rather than raw asset bytes.
|
|
7
|
+
*/
|
|
8
|
+
export interface VersionListEntry {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
readonly label?: string;
|
|
11
|
+
readonly createdAt: number;
|
|
12
|
+
readonly authorId?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Host-owned version-storage boundary, mirroring `MediaProvider`'s shape
|
|
16
|
+
* and its "the editor never persists anything itself" contract: the host
|
|
17
|
+
* backend is the source of truth, and `save`/`list`/`load`/`remove` are
|
|
18
|
+
* the entire boundary. There is deliberately no shipped default
|
|
19
|
+
* implementation, exactly like `MediaProvider` - when a host doesn't
|
|
20
|
+
* supply one, the version-history feature is simply unavailable, the same
|
|
21
|
+
* way the media toolbar buttons are disabled when `mediaProvider` is
|
|
22
|
+
* absent.
|
|
23
|
+
*/
|
|
24
|
+
export interface VersionProvider {
|
|
25
|
+
save(version: DocumentVersion): Promise<VersionListEntry>;
|
|
26
|
+
list(): Promise<readonly VersionListEntry[]>;
|
|
27
|
+
load(id: string): Promise<DocumentVersion>;
|
|
28
|
+
remove(id: string): Promise<void>;
|
|
29
|
+
}
|