@beyondwork/docx-react-component 1.0.18 → 1.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/package.json +24 -34
- package/src/api/README.md +5 -1
- package/src/api/public-types.ts +710 -4
- package/src/api/session-state.ts +60 -0
- package/src/core/commands/formatting-commands.ts +2 -1
- package/src/core/commands/image-commands.ts +147 -0
- package/src/core/commands/index.ts +19 -3
- package/src/core/commands/list-commands.ts +231 -36
- package/src/core/commands/paragraph-layout-commands.ts +339 -0
- package/src/core/commands/section-layout-commands.ts +680 -0
- package/src/core/commands/style-commands.ts +262 -0
- package/src/core/search/search-text.ts +357 -0
- package/src/core/selection/mapping.ts +41 -0
- package/src/core/state/editor-state.ts +4 -1
- package/src/index.ts +51 -0
- package/src/io/docx-session.ts +623 -56
- package/src/io/export/serialize-comments.ts +104 -34
- package/src/io/export/serialize-footnotes.ts +198 -1
- package/src/io/export/serialize-headers-footers.ts +203 -10
- package/src/io/export/serialize-main-document.ts +285 -8
- package/src/io/export/serialize-numbering.ts +28 -7
- package/src/io/export/split-review-boundaries.ts +181 -19
- package/src/io/normalize/normalize-text.ts +144 -32
- package/src/io/ooxml/highlight-colors.ts +39 -0
- package/src/io/ooxml/numbering-sentinels.ts +44 -0
- package/src/io/ooxml/parse-comments.ts +85 -19
- package/src/io/ooxml/parse-fields.ts +396 -0
- package/src/io/ooxml/parse-footnotes.ts +452 -22
- package/src/io/ooxml/parse-headers-footers.ts +657 -29
- package/src/io/ooxml/parse-inline-media.ts +30 -0
- package/src/io/ooxml/parse-main-document.ts +807 -20
- package/src/io/ooxml/parse-numbering.ts +7 -0
- package/src/io/ooxml/parse-revisions.ts +317 -38
- package/src/io/ooxml/parse-settings.ts +184 -0
- package/src/io/ooxml/parse-shapes.ts +25 -0
- package/src/io/ooxml/parse-styles.ts +463 -0
- package/src/io/ooxml/parse-theme.ts +32 -0
- package/src/legal/bookmarks.ts +44 -0
- package/src/legal/cross-references.ts +59 -1
- package/src/model/canonical-document.ts +250 -4
- package/src/model/cds-1.0.0.ts +13 -0
- package/src/model/snapshot.ts +87 -2
- package/src/review/store/revision-store.ts +6 -0
- package/src/review/store/revision-types.ts +1 -0
- package/src/runtime/document-layout.ts +332 -0
- package/src/runtime/document-navigation.ts +603 -0
- package/src/runtime/document-runtime.ts +1754 -78
- package/src/runtime/document-search.ts +145 -0
- package/src/runtime/numbering-prefix.ts +47 -26
- package/src/runtime/page-layout-estimation.ts +212 -0
- package/src/runtime/read-only-diagnostics-runtime.ts +9 -0
- package/src/runtime/session-capabilities.ts +35 -3
- package/src/runtime/story-context.ts +164 -0
- package/src/runtime/story-targeting.ts +162 -0
- package/src/runtime/surface-projection.ts +324 -36
- package/src/runtime/table-schema.ts +89 -7
- package/src/runtime/view-state.ts +477 -0
- package/src/runtime/workflow-markup.ts +349 -0
- package/src/ui/WordReviewEditor.tsx +2469 -1344
- package/src/ui/browser-export.ts +52 -0
- package/src/ui/editor-command-bag.ts +120 -0
- package/src/ui/editor-runtime-boundary.ts +1422 -0
- package/src/ui/editor-shell-view.tsx +134 -0
- package/src/ui/editor-surface-controller.tsx +51 -0
- package/src/ui/headless/preserve-editor-selection.ts +5 -0
- package/src/ui/headless/revision-decoration-model.ts +4 -4
- package/src/ui/headless/selection-helpers.ts +20 -0
- package/src/ui/headless/selection-toolbar-model.ts +22 -0
- package/src/ui/headless/use-editor-keyboard.ts +6 -1
- package/src/ui/runtime-snapshot-selectors.ts +197 -0
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +18 -2
- package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +129 -0
- package/src/ui-tailwind/chrome/tw-layout-panel.tsx +114 -0
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +34 -0
- package/src/ui-tailwind/chrome/tw-page-ruler.tsx +386 -0
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +150 -14
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +128 -0
- package/src/ui-tailwind/editor-surface/perf-probe.ts +179 -0
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +46 -7
- package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +31 -0
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +35 -0
- package/src/ui-tailwind/editor-surface/pm-position-map.ts +3 -3
- package/src/ui-tailwind/editor-surface/pm-schema.ts +186 -13
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +191 -68
- package/src/ui-tailwind/editor-surface/search-plugin.ts +19 -68
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +51 -0
- package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +11 -0
- package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +7 -1
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +528 -85
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +0 -1
- package/src/ui-tailwind/index.ts +2 -1
- package/src/ui-tailwind/page-chrome-model.ts +27 -0
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +277 -147
- package/src/ui-tailwind/review/tw-health-panel.tsx +31 -2
- package/src/ui-tailwind/review/tw-review-rail.tsx +8 -8
- package/src/ui-tailwind/review/tw-revision-sidebar.tsx +15 -15
- package/src/ui-tailwind/theme/editor-theme.css +127 -0
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +4 -0
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +829 -12
- package/src/ui-tailwind/tw-review-workspace.tsx +1238 -42
- package/src/validation/compatibility-engine.ts +119 -24
- package/src/validation/compatibility-report.ts +1 -0
- package/src/validation/diagnostics.ts +1 -0
- package/src/validation/docx-comment-proof.ts +707 -0
|
@@ -52,34 +52,34 @@ export function TwReviewRail(props: TwReviewRailProps) {
|
|
|
52
52
|
return (
|
|
53
53
|
<aside
|
|
54
54
|
aria-label="Review rail"
|
|
55
|
-
className="flex w-[
|
|
55
|
+
className="flex w-[304px] shrink-0 flex-col border-l border-border bg-canvas"
|
|
56
56
|
>
|
|
57
57
|
<Tabs.Root
|
|
58
58
|
value={props.activeTab}
|
|
59
59
|
onValueChange={(v: string) => props.onActiveTabChange(v as ReviewRailTab)}
|
|
60
60
|
className="flex flex-1 flex-col min-h-0"
|
|
61
61
|
>
|
|
62
|
-
<Tabs.List className="flex shrink-0 border-b border-border">
|
|
62
|
+
<Tabs.List className="flex shrink-0 border-b border-border px-2">
|
|
63
63
|
<Tabs.Trigger
|
|
64
64
|
value="comments"
|
|
65
|
-
className={`flex-1 py-2 text-xs text-tertiary transition-colors data-[state=active]:text-primary data-[state=active]:shadow-[inset_0_-2px_0_var(--color-accent)] outline-none ${focusRingClass}`}
|
|
65
|
+
className={`flex-1 py-2 text-xs text-tertiary font-medium transition-colors data-[state=active]:text-primary data-[state=active]:font-semibold data-[state=active]:shadow-[inset_0_-2px_0_var(--color-accent)] outline-none ${focusRingClass}`}
|
|
66
66
|
>
|
|
67
67
|
Comments{" "}
|
|
68
|
-
<span className="text-tertiary">{props.comments.totalCount}</span>
|
|
68
|
+
<span className="ml-1 inline-flex min-w-[14px] items-center justify-center rounded-full bg-subtle px-1.5 py-px text-[10px] font-medium text-tertiary">{props.comments.totalCount}</span>
|
|
69
69
|
</Tabs.Trigger>
|
|
70
70
|
<Tabs.Trigger
|
|
71
71
|
value="changes"
|
|
72
|
-
className={`flex-1 py-2 text-xs text-tertiary transition-colors data-[state=active]:text-primary data-[state=active]:shadow-[inset_0_-2px_0_var(--color-accent)] outline-none ${focusRingClass}`}
|
|
72
|
+
className={`flex-1 py-2 text-xs text-tertiary font-medium transition-colors data-[state=active]:text-primary data-[state=active]:font-semibold data-[state=active]:shadow-[inset_0_-2px_0_var(--color-accent)] outline-none ${focusRingClass}`}
|
|
73
73
|
>
|
|
74
74
|
Changes{" "}
|
|
75
|
-
<span className="text-tertiary">{props.trackedChanges.totalCount}</span>
|
|
75
|
+
<span className="ml-1 inline-flex min-w-[14px] items-center justify-center rounded-full bg-subtle px-1.5 py-px text-[10px] font-medium text-tertiary">{props.trackedChanges.totalCount}</span>
|
|
76
76
|
</Tabs.Trigger>
|
|
77
77
|
{/* Health moved to toolbar popover */}
|
|
78
78
|
</Tabs.List>
|
|
79
79
|
|
|
80
80
|
<ScrollArea.Root className="flex-1 min-h-0">
|
|
81
81
|
<ScrollArea.Viewport className="h-full w-full">
|
|
82
|
-
<Tabs.Content value="comments" className="p-
|
|
82
|
+
<Tabs.Content value="comments" className="p-2.5 outline-none">
|
|
83
83
|
<TwCommentSidebar
|
|
84
84
|
currentUserId={props.currentUserId}
|
|
85
85
|
comments={props.comments}
|
|
@@ -92,7 +92,7 @@ export function TwReviewRail(props: TwReviewRailProps) {
|
|
|
92
92
|
/>
|
|
93
93
|
</Tabs.Content>
|
|
94
94
|
|
|
95
|
-
<Tabs.Content value="changes" className="p-
|
|
95
|
+
<Tabs.Content value="changes" className="p-2.5 outline-none">
|
|
96
96
|
<TwRevisionSidebar
|
|
97
97
|
trackedChanges={props.trackedChanges}
|
|
98
98
|
markupDisplay={props.markupDisplay}
|
|
@@ -28,16 +28,16 @@ export function TwRevisionSidebar(props: TwRevisionSidebarProps) {
|
|
|
28
28
|
|
|
29
29
|
return (
|
|
30
30
|
<div className="outline-none">
|
|
31
|
-
<p className="text-
|
|
31
|
+
<p className="mb-2 text-[10px] text-tertiary">
|
|
32
32
|
{trackedChanges.pendingChangeIds.length} active · {trackedChanges.acceptedChangeIds.length} accepted · {trackedChanges.preserveOnlyChangeIds.length} preserve-only
|
|
33
33
|
</p>
|
|
34
34
|
|
|
35
35
|
{/* Bulk actions */}
|
|
36
|
-
<div className="flex gap-1
|
|
36
|
+
<div className="mb-2 flex gap-1">
|
|
37
37
|
<button
|
|
38
38
|
type="button"
|
|
39
39
|
disabled={actionablePendingCount === 0}
|
|
40
|
-
className="inline-flex items-center gap-1 rounded-md px-2
|
|
40
|
+
className="inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] font-semibold text-accent hover:bg-accent-soft transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
|
|
41
41
|
onClick={props.onAcceptAllChanges}
|
|
42
42
|
>
|
|
43
43
|
Accept all ({actionablePendingCount})
|
|
@@ -45,7 +45,7 @@ export function TwRevisionSidebar(props: TwRevisionSidebarProps) {
|
|
|
45
45
|
<button
|
|
46
46
|
type="button"
|
|
47
47
|
disabled={actionablePendingCount === 0}
|
|
48
|
-
className="inline-flex items-center gap-1 rounded-md px-2
|
|
48
|
+
className="inline-flex items-center gap-1 rounded-md px-2 py-1 text-[10px] text-secondary hover:bg-surface transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
|
|
49
49
|
onClick={props.onRejectAllChanges}
|
|
50
50
|
>
|
|
51
51
|
Reject all
|
|
@@ -76,14 +76,14 @@ export function TwRevisionSidebar(props: TwRevisionSidebarProps) {
|
|
|
76
76
|
: rev.kind === "deletion" ? "bg-danger"
|
|
77
77
|
: "bg-tertiary"
|
|
78
78
|
}`} />
|
|
79
|
-
<div className="p-2
|
|
80
|
-
<div className="flex items-start justify-between gap-2
|
|
81
|
-
<span className="text-
|
|
79
|
+
<div className="p-2 flex-1 min-w-0">
|
|
80
|
+
<div className="mb-0.5 flex items-start justify-between gap-2">
|
|
81
|
+
<span className="text-[11px] font-medium text-primary">{rev.anchorLabel}</span>
|
|
82
82
|
<RevisionBadge status={rev.status} actionability={rev.actionability} />
|
|
83
83
|
</div>
|
|
84
|
-
<p className="text-
|
|
84
|
+
<p className="mb-1 text-[10px] text-tertiary">{rev.authorId} · {rev.createdAt}</p>
|
|
85
85
|
{rev.excerpt ? (
|
|
86
|
-
<p className={`text-
|
|
86
|
+
<p className={`text-[11px] ${
|
|
87
87
|
rev.kind === "insertion" ? "text-insert"
|
|
88
88
|
: rev.kind === "deletion" ? "text-danger line-through"
|
|
89
89
|
: "text-secondary"
|
|
@@ -91,18 +91,18 @@ export function TwRevisionSidebar(props: TwRevisionSidebarProps) {
|
|
|
91
91
|
{rev.excerpt}
|
|
92
92
|
</p>
|
|
93
93
|
) : (
|
|
94
|
-
<p className="text-
|
|
94
|
+
<p className="text-[11px] text-secondary">{rev.label}</p>
|
|
95
95
|
)}
|
|
96
96
|
{rev.detail ? (
|
|
97
|
-
<p className="text-
|
|
97
|
+
<p className="mt-1 text-[10px] text-secondary">{rev.detail}</p>
|
|
98
98
|
) : null}
|
|
99
|
-
<div className="
|
|
99
|
+
<div className="mt-1.5 flex gap-1">
|
|
100
100
|
{rev.actionability === "actionable" ? (
|
|
101
101
|
<>
|
|
102
102
|
<button
|
|
103
103
|
type="button"
|
|
104
104
|
disabled={!rev.canAccept || rev.status === "accepted"}
|
|
105
|
-
className="inline-flex items-center gap-1 rounded-md px-
|
|
105
|
+
className="inline-flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[10px] text-insert hover:bg-insert-soft transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
|
|
106
106
|
onClick={(e) => {
|
|
107
107
|
e.stopPropagation();
|
|
108
108
|
props.onAcceptRevision?.(rev.revisionId);
|
|
@@ -113,7 +113,7 @@ export function TwRevisionSidebar(props: TwRevisionSidebarProps) {
|
|
|
113
113
|
<button
|
|
114
114
|
type="button"
|
|
115
115
|
disabled={!rev.canReject || rev.status === "rejected"}
|
|
116
|
-
className="inline-flex items-center gap-1 rounded-md px-
|
|
116
|
+
className="inline-flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[10px] text-danger hover:bg-delete-soft transition-colors disabled:opacity-30 disabled:cursor-not-allowed"
|
|
117
117
|
onClick={(e) => {
|
|
118
118
|
e.stopPropagation();
|
|
119
119
|
props.onRejectRevision?.(rev.revisionId);
|
|
@@ -123,7 +123,7 @@ export function TwRevisionSidebar(props: TwRevisionSidebarProps) {
|
|
|
123
123
|
</button>
|
|
124
124
|
</>
|
|
125
125
|
) : (
|
|
126
|
-
<span className="
|
|
126
|
+
<span className="px-1.5 py-0.5 text-[10px] text-tertiary">Preserve-only</span>
|
|
127
127
|
)}
|
|
128
128
|
</div>
|
|
129
129
|
</div>
|
|
@@ -68,6 +68,11 @@
|
|
|
68
68
|
--font-legal-serif: var(--font-legal-serif, "Source Serif 4", "Georgia", "Times New Roman", serif);
|
|
69
69
|
--font-legal-sans: var(--font-legal-sans, "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif);
|
|
70
70
|
--font-legal-mono: "JetBrains Mono", "SF Mono", "Fira Code", "Consolas", monospace;
|
|
71
|
+
|
|
72
|
+
/* Page-mode document surface */
|
|
73
|
+
--color-page-shadow: rgba(16, 24, 40, 0.06);
|
|
74
|
+
--color-page-border: rgba(0, 0, 0, 0.04);
|
|
75
|
+
--color-page-bg: #ffffff;
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
/* ─── Dark mode overrides ─── */
|
|
@@ -108,6 +113,96 @@
|
|
|
108
113
|
|
|
109
114
|
--color-shadow: rgba(0, 0, 0, 0.32);
|
|
110
115
|
--color-shadow-strong: rgba(0, 0, 0, 0.48);
|
|
116
|
+
|
|
117
|
+
--color-page-shadow: rgba(0, 0, 0, 0.18);
|
|
118
|
+
--color-page-border: rgba(255, 255, 255, 0.06);
|
|
119
|
+
--color-page-bg: #1e1e1e;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/*
|
|
123
|
+
* ─── Font substitution map ───
|
|
124
|
+
*
|
|
125
|
+
* Common Word font families mapped to redistributable metric-compatible or
|
|
126
|
+
* open substitutes. These CSS custom properties are consumed by the
|
|
127
|
+
* ProseMirror surface font_family mark and by the page-mode typography
|
|
128
|
+
* layer. Hosts can override individual properties to supply their own
|
|
129
|
+
* licensed fonts.
|
|
130
|
+
*
|
|
131
|
+
* Strategy:
|
|
132
|
+
* 1. Use metric-compatible open substitutes where available
|
|
133
|
+
* (e.g. Liberation Serif for Times New Roman, Carlito for Calibri).
|
|
134
|
+
* 2. Fall back through system-installed metric-compatible fonts
|
|
135
|
+
* (e.g. Cambria → system serif, Calibri → system sans).
|
|
136
|
+
* 3. Final fallback to the editor's legal-serif or legal-sans role font.
|
|
137
|
+
*
|
|
138
|
+
* This pipeline does NOT ship proprietary Microsoft fonts. It uses only
|
|
139
|
+
* redistributable or system-installed families.
|
|
140
|
+
*/
|
|
141
|
+
:root {
|
|
142
|
+
/* Serif substitutions */
|
|
143
|
+
--wre-font-times-new-roman: "Liberation Serif", "Times New Roman", "Source Serif 4", "Georgia", serif;
|
|
144
|
+
--wre-font-cambria: "Caladea", "Cambria", "Source Serif 4", "Georgia", serif;
|
|
145
|
+
--wre-font-garamond: "EB Garamond", "Garamond", "Source Serif 4", serif;
|
|
146
|
+
--wre-font-book-antiqua: "Palatino Linotype", "Book Antiqua", "Source Serif 4", serif;
|
|
147
|
+
--wre-font-georgia: "Georgia", "Source Serif 4", serif;
|
|
148
|
+
|
|
149
|
+
/* Sans-serif substitutions */
|
|
150
|
+
--wre-font-calibri: "Carlito", "Calibri", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
151
|
+
--wre-font-arial: "Liberation Sans", "Arial", "Inter", sans-serif;
|
|
152
|
+
--wre-font-helvetica: "Liberation Sans", "Helvetica Neue", "Helvetica", "Inter", sans-serif;
|
|
153
|
+
--wre-font-verdana: "Verdana", "Inter", sans-serif;
|
|
154
|
+
--wre-font-tahoma: "Tahoma", "Inter", sans-serif;
|
|
155
|
+
--wre-font-segoe-ui: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
156
|
+
--wre-font-century-gothic: "URW Gothic", "Century Gothic", "Inter", sans-serif;
|
|
157
|
+
|
|
158
|
+
/* Monospace substitutions */
|
|
159
|
+
--wre-font-courier-new: "Liberation Mono", "Courier New", "JetBrains Mono", monospace;
|
|
160
|
+
--wre-font-consolas: "JetBrains Mono", "Consolas", "SF Mono", monospace;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* ─── Page-mode typography ─── */
|
|
164
|
+
.wre-page-surface {
|
|
165
|
+
font-family: var(--font-legal-serif);
|
|
166
|
+
font-size: 15px;
|
|
167
|
+
line-height: 1.6;
|
|
168
|
+
color: var(--color-primary);
|
|
169
|
+
-webkit-font-smoothing: antialiased;
|
|
170
|
+
-moz-osx-font-smoothing: grayscale;
|
|
171
|
+
text-rendering: optimizeLegibility;
|
|
172
|
+
font-kerning: normal;
|
|
173
|
+
font-variant-ligatures: common-ligatures;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.wre-page-surface p {
|
|
177
|
+
margin: 0 0 0.5em 0;
|
|
178
|
+
orphans: 2;
|
|
179
|
+
widows: 2;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* Page chrome — shadow, border, and background for the page-mode document panel */
|
|
183
|
+
.wre-page-chrome {
|
|
184
|
+
background: var(--color-page-bg);
|
|
185
|
+
border: 1px solid var(--color-page-border);
|
|
186
|
+
border-radius: 2px;
|
|
187
|
+
box-shadow: 0 1px 4px var(--color-page-shadow);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* Canvas-mode typography — lighter, review-first baseline */
|
|
191
|
+
.wre-canvas-surface {
|
|
192
|
+
font-family: var(--font-legal-serif);
|
|
193
|
+
font-size: 15px;
|
|
194
|
+
line-height: 1.6;
|
|
195
|
+
color: var(--color-primary);
|
|
196
|
+
-webkit-font-smoothing: antialiased;
|
|
197
|
+
-moz-osx-font-smoothing: grayscale;
|
|
198
|
+
text-rendering: optimizeLegibility;
|
|
199
|
+
font-kerning: normal;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/* Wide-table readability in page mode */
|
|
203
|
+
.wre-page-surface table {
|
|
204
|
+
font-size: 14px;
|
|
205
|
+
line-height: 1.45;
|
|
111
206
|
}
|
|
112
207
|
|
|
113
208
|
/* ─── Base resets ─── */
|
|
@@ -171,6 +266,10 @@
|
|
|
171
266
|
margin: 0 0 0.5em 0;
|
|
172
267
|
}
|
|
173
268
|
|
|
269
|
+
.prosemirror-surface .ProseMirror p[data-numbered="true"][data-list-continuation="true"] {
|
|
270
|
+
margin-bottom: 0.25em;
|
|
271
|
+
}
|
|
272
|
+
|
|
174
273
|
.prosemirror-surface .ProseMirror [data-node-type="opaque_block"] {
|
|
175
274
|
user-select: none;
|
|
176
275
|
cursor: default;
|
|
@@ -188,3 +287,31 @@
|
|
|
188
287
|
.prosemirror-surface .ProseMirror ::selection {
|
|
189
288
|
background: var(--color-accent-soft);
|
|
190
289
|
}
|
|
290
|
+
|
|
291
|
+
.prosemirror-surface .ProseMirror .ProseMirror-selectednode {
|
|
292
|
+
outline: none;
|
|
293
|
+
background-color: var(--color-accent-soft);
|
|
294
|
+
border-radius: 4px;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/*
|
|
298
|
+
* ─── Preservation noise suppression ───
|
|
299
|
+
*
|
|
300
|
+
* Quiet-marker opaque inlines (w:proofErr, w:lastRenderedPageBreak, w:permStart,
|
|
301
|
+
* w:permEnd) are preserved in the canonical document for export safety but
|
|
302
|
+
* contribute zero visual weight to the reading/editing surface. They remain in
|
|
303
|
+
* the DOM as zero-dimension spans so round-trip export is unaffected.
|
|
304
|
+
*/
|
|
305
|
+
.prosemirror-surface .ProseMirror [data-inline-presentation="quiet-marker"] {
|
|
306
|
+
display: inline-block;
|
|
307
|
+
width: 0;
|
|
308
|
+
height: 0;
|
|
309
|
+
overflow: hidden;
|
|
310
|
+
vertical-align: baseline;
|
|
311
|
+
pointer-events: none;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/* ─── Page workspace zoom scaling ─── */
|
|
315
|
+
.wre-page-chrome[style*="scale"] {
|
|
316
|
+
will-change: transform;
|
|
317
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import * as Tooltip from "@radix-ui/react-tooltip";
|
|
3
3
|
|
|
4
|
+
import { preserveEditorSelectionMouseDown } from "../../ui/headless/preserve-editor-selection";
|
|
5
|
+
|
|
4
6
|
export interface TwToolbarIconButtonProps {
|
|
5
7
|
icon: React.ComponentType<{ className?: string }>;
|
|
6
8
|
label: string;
|
|
@@ -19,7 +21,9 @@ export function TwToolbarIconButton(props: TwToolbarIconButtonProps) {
|
|
|
19
21
|
<Tooltip.Trigger asChild>
|
|
20
22
|
<button
|
|
21
23
|
type="button"
|
|
24
|
+
aria-label={props.label}
|
|
22
25
|
disabled={props.disabled}
|
|
26
|
+
onMouseDown={preserveEditorSelectionMouseDown}
|
|
23
27
|
onClick={props.onClick}
|
|
24
28
|
className={[
|
|
25
29
|
"inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors outline-none",
|