@bendyline/squisq-editor-react 1.5.3 → 1.6.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/README.md +57 -10
- package/dist/index.d.ts +1114 -52
- package/dist/index.js +18446 -8050
- package/dist/index.js.map +1 -1
- package/dist/styles/fa-brands-400-AHOAZHCU.woff2 +0 -0
- package/dist/styles/fa-regular-400-VRZYIBIZ.woff2 +0 -0
- package/dist/styles/fa-solid-900-MDEYK55F.woff2 +0 -0
- package/dist/styles/fa-v4compatibility-ETEVP6IB.woff2 +0 -0
- package/dist/styles/index.css +13867 -0
- package/package.json +15 -7
- package/src/BlockCardView.tsx +121 -0
- package/src/BlockPreviewPanel.tsx +69 -0
- package/src/BlockPropertiesPopover.tsx +191 -0
- package/src/EditorContext.tsx +165 -16
- package/src/EditorShell.tsx +265 -144
- package/src/FolderView.tsx +131 -0
- package/src/Icon.tsx +26 -0
- package/src/ImageEditor.tsx +69 -22
- package/src/OutlinePanel.tsx +64 -7
- package/src/PlainHtmlPreview.tsx +30 -3
- package/src/PreviewControls.tsx +491 -122
- package/src/PreviewPanel.tsx +15 -9
- package/src/RawEditor.tsx +179 -17
- package/src/RecorderEntry.tsx +9 -16
- package/src/TemplateAnnotation.ts +44 -0
- package/src/TemplatePicker.tsx +329 -54
- package/src/ThemeCustomizerPanel.tsx +30 -336
- package/src/ThemePicker.tsx +112 -3
- package/src/TimelineBlockPreview.tsx +37 -0
- package/src/TimelineTrack.tsx +671 -0
- package/src/Toolbar.tsx +544 -180
- package/src/Tooltip.tsx +22 -4
- package/src/TransitionPicker.tsx +351 -0
- package/src/VersionHistoryPanel.tsx +4 -16
- package/src/ViewMenuPanel.tsx +17 -14
- package/src/WysiwygEditor.tsx +161 -65
- package/src/__tests__/blockProperties.test.ts +92 -0
- package/src/__tests__/blockRange.test.ts +105 -0
- package/src/__tests__/buildPreviewDocTransition.test.ts +73 -0
- package/src/__tests__/codeContextSectionView.test.tsx +95 -0
- package/src/__tests__/codeContextZoneManager.test.ts +127 -0
- package/src/__tests__/createShapeLayer.test.ts +46 -0
- package/src/__tests__/diffContextSections.test.ts +39 -0
- package/src/__tests__/drawingShapeRoundTrip.test.ts +49 -0
- package/src/__tests__/editorShellCodeContext.test.tsx +86 -0
- package/src/__tests__/editorShellProps.test.tsx +96 -0
- package/src/__tests__/embeddedMedia.test.ts +48 -0
- package/src/__tests__/headingTransition.test.ts +138 -0
- package/src/__tests__/layoutChildRoundTrip.test.ts +71 -0
- package/src/__tests__/plainHtmlPreview.test.tsx +10 -8
- package/src/__tests__/previewControls.test.tsx +70 -0
- package/src/__tests__/recorderMediaInsert.test.ts +86 -0
- package/src/__tests__/templateAnnotationRoundTrip.test.ts +18 -0
- package/src/__tests__/templatePickerMetadata.test.ts +32 -0
- package/src/__tests__/timelineSource.test.ts +134 -0
- package/src/__tests__/tiptapBridge.test.ts +92 -0
- package/src/__tests__/tiptapBridgeConformance.test.ts +47 -0
- package/src/__tests__/tooltip.test.tsx +72 -0
- package/src/__tests__/transitionCatalog.test.ts +64 -0
- package/src/__tests__/useBlockNavigator.test.tsx +67 -0
- package/src/__tests__/useJsonEditorTokens.test.ts +59 -0
- package/src/__tests__/useMediaRecorder.test.ts +41 -0
- package/src/__tests__/useTimelineClock.test.ts +21 -0
- package/src/blockProperties.ts +88 -0
- package/src/blockRange.ts +132 -0
- package/src/buildPreviewDoc.ts +98 -9
- package/src/codeContext/CodeContextSectionView.tsx +124 -0
- package/src/codeContext/CodeContextZoneManager.ts +149 -0
- package/src/codeContext/CodeContextZones.tsx +121 -0
- package/src/codeContext/diffContextSections.ts +38 -0
- package/src/codeContext/types.ts +75 -0
- package/src/customTemplates/AddBin.tsx +126 -0
- package/src/customTemplates/CustomLayoutManager.tsx +233 -0
- package/src/customTemplates/CustomTemplateContext.tsx +182 -0
- package/src/customTemplates/LayerToolbar.tsx +580 -0
- package/src/customTemplates/ShapeGlyph.tsx +47 -0
- package/src/customTemplates/TemplateDesigner.tsx +430 -0
- package/src/customTemplates/__tests__/library.test.ts +88 -0
- package/src/customTemplates/__tests__/normalizePositions.test.ts +109 -0
- package/src/customTemplates/__tests__/shapeDefs.test.ts +49 -0
- package/src/customTemplates/__tests__/useMemoryLayerAdapter.test.ts +95 -0
- package/src/customTemplates/designer.css +673 -0
- package/src/customTemplates/index.ts +31 -0
- package/src/customTemplates/library.ts +97 -0
- package/src/customTemplates/normalizePositions.ts +75 -0
- package/src/customTemplates/shapeDefs.ts +131 -0
- package/src/customTemplates/thumbnail.tsx +63 -0
- package/src/customTemplates/tokenDefs.ts +60 -0
- package/src/customTemplates/useDocCustomTemplates.ts +52 -0
- package/src/customTemplates/useMemoryLayerAdapter.ts +123 -0
- package/src/customThemes/CustomThemeContext.tsx +179 -0
- package/src/customThemes/CustomThemeDialog.tsx +286 -0
- package/src/customThemes/__tests__/CustomThemeContext.test.tsx +64 -0
- package/src/customThemes/__tests__/CustomThemeDialog.test.tsx +47 -0
- package/src/customThemes/__tests__/customThemeLibrary.test.ts +51 -0
- package/src/customThemes/customThemeLibrary.ts +97 -0
- package/src/customThemes/index.ts +31 -0
- package/src/customThemes/themeControls.tsx +229 -0
- package/src/customThemes/themeDraft.ts +272 -0
- package/src/customThemes/useDocCustomThemes.ts +49 -0
- package/src/diagram/DiagramCanvas.tsx +240 -0
- package/src/diagram/DiagramExtension.ts +209 -0
- package/src/diagram/DiagramMaximizedOverlay.tsx +46 -0
- package/src/diagram/DiagramWidget.tsx +270 -0
- package/src/diagram/diagramCommands.ts +604 -0
- package/src/diagram/diagramConstants.ts +17 -0
- package/src/diagram/useDiagramData.ts +126 -0
- package/src/embeddedMedia.ts +78 -0
- package/src/frontmatter.ts +29 -0
- package/src/headingTransition.ts +231 -0
- package/src/imageEditor/CanvasSurface.tsx +383 -88
- package/src/imageEditor/PropertiesPanel.tsx +47 -1
- package/src/imageEditor/Toolbar.tsx +229 -16
- package/src/imageEditor/createShapeLayer.ts +280 -0
- package/src/imageEditor/icons.tsx +34 -114
- package/src/imageEditor/image-editor.css +54 -5
- package/src/imageEditor/state.ts +23 -3
- package/src/index.ts +109 -1
- package/src/jsonEditor/useJsonEditorTokens.ts +13 -43
- package/src/recorder/RecorderModal.tsx +120 -53
- package/src/recorder/RecorderPanel.tsx +2 -26
- package/src/recorder/hooks/useMediaRecorder.ts +17 -11
- package/src/recorder/insertMediaBlock.ts +30 -0
- package/src/resolveBlockVisual.ts +33 -0
- package/src/scene/Scene.tsx +540 -0
- package/src/scene/SceneBlockExtension.ts +198 -0
- package/src/scene/SceneBlockToolbar.tsx +201 -0
- package/src/scene/SceneBlockWidget.tsx +434 -0
- package/src/scene/ScenePropsBar.tsx +85 -0
- package/src/scene/SceneSelection.tsx +107 -0
- package/src/scene/SceneViewport.tsx +102 -0
- package/src/scene/ShapePalette.tsx +181 -0
- package/src/scene/__tests__/DiagramAdapter.test.ts +56 -0
- package/src/scene/__tests__/bezierEdit.test.ts +85 -0
- package/src/scene/__tests__/blockLayers.test.ts +57 -0
- package/src/scene/__tests__/shapeLayers.test.ts +106 -0
- package/src/scene/__tests__/useSceneHitTest.test.ts +90 -0
- package/src/scene/__tests__/useScenePanZoom.test.ts +103 -0
- package/src/scene/adapters/DiagramAdapter.ts +168 -0
- package/src/scene/adapters/DrawingAdapter.ts +415 -0
- package/src/scene/adapters/LayoutAdapter.ts +310 -0
- package/src/scene/adapters/blockLayers.ts +159 -0
- package/src/scene/commands/SceneCommand.ts +70 -0
- package/src/scene/commands/drawingCommands.ts +318 -0
- package/src/scene/commands/layoutCommands.ts +301 -0
- package/src/scene/hooks/useSceneHitTest.ts +105 -0
- package/src/scene/hooks/useScenePanZoom.ts +147 -0
- package/src/scene/hooks/useSceneSelection.ts +62 -0
- package/src/scene/index.ts +95 -0
- package/src/scene/layers/DiagramEdges.tsx +127 -0
- package/src/scene/layers/edgeGeometry.ts +77 -0
- package/src/scene/layers/nodeCard.tsx +145 -0
- package/src/scene/layers/renderLayer.tsx +70 -0
- package/src/scene/layers/shapeLayers.ts +201 -0
- package/src/scene/paths/bezierEdit.ts +208 -0
- package/src/scene/scene.css +649 -0
- package/src/scene/text/SceneTextOverlay.tsx +161 -0
- package/src/scene/text/sceneTextChannel.ts +40 -0
- package/src/scene/text/sceneTextConfig.ts +27 -0
- package/src/scene/text/sceneTiptap.ts +36 -0
- package/src/scene/text/useSceneTextEditing.ts +39 -0
- package/src/scene/tools/ConnectTool.ts +111 -0
- package/src/scene/tools/DrawingConnectTool.ts +161 -0
- package/src/scene/tools/PathTool.ts +158 -0
- package/src/scene/tools/PlaceTool.ts +47 -0
- package/src/scene/tools/SceneTool.ts +75 -0
- package/src/scene/tools/SelectTool.ts +284 -0
- package/src/scene/tools/ShapeTool.ts +144 -0
- package/src/scene/tools/TextTool.ts +72 -0
- package/src/scene/tools/TokenTool.ts +95 -0
- package/src/scene/tools/createDrawShapeTool.ts +82 -0
- package/src/styles/code-context.css +155 -0
- package/src/styles/diagram.css +183 -0
- package/src/styles/editor.css +1708 -150
- package/src/styles/folder-view.css +210 -0
- package/src/styles/image-edit-affordance.css +2 -2
- package/src/styles/index.css +5 -0
- package/src/timelineSource.ts +244 -0
- package/src/tiptapBridge.ts +115 -34
- package/src/tooltipPlacement.ts +13 -0
- package/src/transitionCatalog.ts +159 -0
- package/src/types/monaco-shims.d.ts +10 -0
- package/src/useBlockNavigator.ts +153 -0
- package/src/useMonacoLoader.ts +23 -1
- package/src/useTimelineClock.ts +76 -0
- package/src/utils/dropUtils.ts +1 -1
package/src/EditorContext.tsx
CHANGED
|
@@ -32,6 +32,8 @@ import type { Editor as TiptapEditor } from '@tiptap/core';
|
|
|
32
32
|
import type { editor as MonacoEditorNs } from 'monaco-editor';
|
|
33
33
|
import { markdownToTiptap } from './tiptapBridge';
|
|
34
34
|
import { resolveFileKind } from './fileKind';
|
|
35
|
+
import { useBlockNavigator } from './useBlockNavigator';
|
|
36
|
+
import { sceneTextChannel, type SceneTextHandle } from './scene/text/sceneTextChannel';
|
|
35
37
|
|
|
36
38
|
/** Monaco standalone code editor instance type */
|
|
37
39
|
type MonacoEditor = MonacoEditorNs.IStandaloneCodeEditor;
|
|
@@ -89,7 +91,22 @@ export type DocumentLinkProvider = (query: string) => Promise<DocumentLinkCandid
|
|
|
89
91
|
// ─── Types ───────────────────────────────────────────────
|
|
90
92
|
|
|
91
93
|
export type EditorView = 'raw' | 'wysiwyg' | 'preview';
|
|
92
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Light/dark chrome mode for the editor shell (toolbar, tabs, status bar,
|
|
96
|
+
* side panes). This is the editor's *UI color scheme* — distinct from a
|
|
97
|
+
* Squisq `Theme` object, which styles the rendered document. Renamed from
|
|
98
|
+
* the former `EditorTheme` to remove that ambiguity.
|
|
99
|
+
*/
|
|
100
|
+
export type EditorColorScheme = 'light' | 'dark';
|
|
101
|
+
/**
|
|
102
|
+
* Document layout mode. `'document'` shows the whole markdown document in
|
|
103
|
+
* the active view (the historical behavior). `'block'` is the
|
|
104
|
+
* block-at-a-time view — one heading-defined block on a card at a time,
|
|
105
|
+
* with the editor scoped to just that block. `'timeline'` is block mode plus
|
|
106
|
+
* a horizontal timeline track for editing block durations and media slices.
|
|
107
|
+
* See {@link useBlockNavigator}.
|
|
108
|
+
*/
|
|
109
|
+
export type LayoutMode = 'document' | 'block' | 'timeline';
|
|
93
110
|
/**
|
|
94
111
|
* How much of the active Squisq theme the WYSIWYG editing surface
|
|
95
112
|
* mirrors. `'fonts'` is the historical default — body and heading
|
|
@@ -117,8 +134,8 @@ export interface EditorState {
|
|
|
117
134
|
parseError: string | null;
|
|
118
135
|
/** Whether a parse is pending */
|
|
119
136
|
isParsing: boolean;
|
|
120
|
-
/** Current color
|
|
121
|
-
|
|
137
|
+
/** Current light/dark chrome color scheme for the editor shell. */
|
|
138
|
+
colorScheme: EditorColorScheme;
|
|
122
139
|
/** Operating mode — 'markdown' for the full shell, 'code' for Monaco-only. */
|
|
123
140
|
editorMode: EditorMode;
|
|
124
141
|
/** Monaco language ID for the Raw editor. */
|
|
@@ -179,11 +196,48 @@ export interface EditorState {
|
|
|
179
196
|
* shell.
|
|
180
197
|
*/
|
|
181
198
|
allowRecording: boolean;
|
|
199
|
+
/**
|
|
200
|
+
* Document layout mode. `'document'` (default) edits the whole document;
|
|
201
|
+
* `'block'` activates the block-at-a-time card view. Initialized from the
|
|
202
|
+
* EditorShell `layoutMode` prop; the View menu can toggle it at runtime.
|
|
203
|
+
*/
|
|
204
|
+
layoutMode: LayoutMode;
|
|
205
|
+
/**
|
|
206
|
+
* The markdown the active text editor should bind to: the full source in
|
|
207
|
+
* `'document'` mode, or just the active block's slice in `'block'` mode.
|
|
208
|
+
* Editors read this instead of `markdownSource` so the same surfaces work
|
|
209
|
+
* in both layouts.
|
|
210
|
+
*/
|
|
211
|
+
editorSource: string;
|
|
212
|
+
/** Number of navigable blocks (cards) in the current document. */
|
|
213
|
+
blockCount: number;
|
|
214
|
+
/** Index of the block currently shown on the card (block mode). */
|
|
215
|
+
activeBlockKey: number;
|
|
216
|
+
/** 1-based source line where the active block begins, or null. */
|
|
217
|
+
activeBlockStartLine: number | null;
|
|
182
218
|
}
|
|
183
219
|
|
|
184
220
|
export interface EditorActions {
|
|
185
221
|
/** Set markdown source and trigger re-parse */
|
|
186
222
|
setMarkdownSource: (source: string) => void;
|
|
223
|
+
/**
|
|
224
|
+
* Write through the active editor channel. In `'document'` mode this is
|
|
225
|
+
* `setMarkdownSource`; in `'block'` mode it splices the edited block back
|
|
226
|
+
* into the full document. Editors call this instead of `setMarkdownSource`.
|
|
227
|
+
*/
|
|
228
|
+
setEditorSource: (source: string) => void;
|
|
229
|
+
/** Switch between Document and Block-at-a-time layouts. */
|
|
230
|
+
setLayoutMode: (mode: LayoutMode) => void;
|
|
231
|
+
/** Show a block by index in block mode (clamped to range). */
|
|
232
|
+
goToBlock: (key: number) => void;
|
|
233
|
+
/** Show the block that owns a given 1-based source line (used by the outline). */
|
|
234
|
+
goToBlockByLine: (line: number) => void;
|
|
235
|
+
/** Move the card to the previous block. */
|
|
236
|
+
prevBlock: () => void;
|
|
237
|
+
/** Move the card to the next block. */
|
|
238
|
+
nextBlock: () => void;
|
|
239
|
+
/** Insert a new heading block after the active one and move to it. */
|
|
240
|
+
addBlock: () => void;
|
|
187
241
|
/** Set markdown from a MarkdownDocument (e.g. from WYSIWYG) */
|
|
188
242
|
setMarkdownDoc: (doc: MarkdownDocument) => void;
|
|
189
243
|
/** Switch the active view */
|
|
@@ -192,8 +246,8 @@ export interface EditorActions {
|
|
|
192
246
|
setTiptapEditor: (editor: TiptapEditor | null) => void;
|
|
193
247
|
/** Register / unregister the Monaco editor instance (called by RawEditor) */
|
|
194
248
|
setMonacoEditor: (editor: MonacoEditor | null) => void;
|
|
195
|
-
/** Set the color
|
|
196
|
-
|
|
249
|
+
/** Set the light/dark chrome color scheme for the editor shell. */
|
|
250
|
+
setColorScheme: (colorScheme: EditorColorScheme) => void;
|
|
197
251
|
/** Show or hide the inline preview gutter at runtime (driven by the View menu). */
|
|
198
252
|
setInlinePreviewVisible: (visible: boolean) => void;
|
|
199
253
|
/** Show or hide the bottom status bar at runtime (driven by the View menu). */
|
|
@@ -230,6 +284,14 @@ export interface EditorContextValue extends EditorState, EditorActions {
|
|
|
230
284
|
tiptapEditor: TiptapEditor | null;
|
|
231
285
|
/** The live Monaco editor instance (null when Raw is not mounted) */
|
|
232
286
|
monacoEditor: MonacoEditor | null;
|
|
287
|
+
/**
|
|
288
|
+
* The focused canvas textbox editor, if any — a small Tiptap instance for
|
|
289
|
+
* a diagram/drawing/layout textbox being edited inline. Published via
|
|
290
|
+
* `sceneTextChannel` (the canvas renders in a detached React root). The
|
|
291
|
+
* top formatting toolbar retargets to this when set. `level` gates which
|
|
292
|
+
* buttons apply (`inline` = marks only; `rich` = headings/lists too).
|
|
293
|
+
*/
|
|
294
|
+
activeSceneText: SceneTextHandle | null;
|
|
233
295
|
/**
|
|
234
296
|
* Workspace-scoped `ContentContainer` for this document — the folder
|
|
235
297
|
* holding the doc, its `_files/` sidecar, sibling documents, and any
|
|
@@ -304,8 +366,8 @@ export interface EditorProviderProps {
|
|
|
304
366
|
initialView?: EditorView;
|
|
305
367
|
/** Article ID used when generating the Doc */
|
|
306
368
|
articleId?: string;
|
|
307
|
-
/**
|
|
308
|
-
|
|
369
|
+
/** Light/dark chrome color scheme for the editor shell. */
|
|
370
|
+
colorScheme?: EditorColorScheme;
|
|
309
371
|
/**
|
|
310
372
|
* Workspace-scoped `ContentContainer` for this document — the folder
|
|
311
373
|
* holding the doc, its `_files/` sidecar, sibling documents, and any
|
|
@@ -396,6 +458,12 @@ export interface EditorProviderProps {
|
|
|
396
458
|
* toolbar's View menu can change it at runtime.
|
|
397
459
|
*/
|
|
398
460
|
themeInheritance?: ThemeInheritance;
|
|
461
|
+
/**
|
|
462
|
+
* Initial layout mode. Defaults to `'document'` (whole-document editing).
|
|
463
|
+
* `'block'` boots into the block-at-a-time card view. The toolbar's View
|
|
464
|
+
* menu can toggle it at runtime.
|
|
465
|
+
*/
|
|
466
|
+
layoutMode?: LayoutMode;
|
|
399
467
|
/**
|
|
400
468
|
* Bundled view preferences — a serializable JSON blob covering all
|
|
401
469
|
* runtime-toggleable view options. When provided, individual values
|
|
@@ -432,6 +500,8 @@ export interface ViewPreferences {
|
|
|
432
500
|
blockTags?: boolean;
|
|
433
501
|
/** How much of the active Squisq theme the WYSIWYG surface mirrors. */
|
|
434
502
|
themeInheritance?: ThemeInheritance;
|
|
503
|
+
/** Document vs. block-at-a-time layout. */
|
|
504
|
+
layoutMode?: LayoutMode;
|
|
435
505
|
}
|
|
436
506
|
|
|
437
507
|
/**
|
|
@@ -445,7 +515,7 @@ export function EditorProvider({
|
|
|
445
515
|
initialMarkdown = '',
|
|
446
516
|
initialView = 'raw',
|
|
447
517
|
articleId = 'untitled',
|
|
448
|
-
|
|
518
|
+
colorScheme: initialColorScheme = 'light',
|
|
449
519
|
workspaceContainer = null,
|
|
450
520
|
allowVersioning = false,
|
|
451
521
|
versionBasename,
|
|
@@ -464,6 +534,7 @@ export function EditorProvider({
|
|
|
464
534
|
outline = false,
|
|
465
535
|
blockTags = true,
|
|
466
536
|
themeInheritance = 'fonts',
|
|
537
|
+
layoutMode = 'document',
|
|
467
538
|
viewPreferences,
|
|
468
539
|
onViewPreferencesChange,
|
|
469
540
|
children,
|
|
@@ -476,6 +547,7 @@ export function EditorProvider({
|
|
|
476
547
|
const effectiveOutline = viewPreferences?.outline ?? outline;
|
|
477
548
|
const effectiveBlockTags = viewPreferences?.blockTags ?? blockTags;
|
|
478
549
|
const effectiveThemeInheritance = viewPreferences?.themeInheritance ?? themeInheritance;
|
|
550
|
+
const effectiveLayoutMode = viewPreferences?.layoutMode ?? layoutMode;
|
|
479
551
|
// Resolve once per provider mount. Changing fileName/language after mount
|
|
480
552
|
// would require recreating the Monaco model anyway, so treat it as static.
|
|
481
553
|
const { mode: editorMode, language: resolvedLanguage } = useMemo(
|
|
@@ -504,7 +576,7 @@ export function EditorProvider({
|
|
|
504
576
|
);
|
|
505
577
|
const [parseError, setParseError] = useState<string | null>(null);
|
|
506
578
|
const [isParsing, setIsParsing] = useState(false);
|
|
507
|
-
const [
|
|
579
|
+
const [colorScheme, setColorScheme] = useState<EditorColorScheme>(initialColorScheme);
|
|
508
580
|
const [inlinePreviewVisible, setInlinePreviewVisibleRaw] =
|
|
509
581
|
useState<boolean>(effectiveInlinePreview);
|
|
510
582
|
// Sync visibility when the host changes the prop (e.g., toggle from outside).
|
|
@@ -528,6 +600,10 @@ export function EditorProvider({
|
|
|
528
600
|
useEffect(() => {
|
|
529
601
|
setThemeInheritanceRaw(themeInheritance);
|
|
530
602
|
}, [themeInheritance]);
|
|
603
|
+
const [layoutModeState, setLayoutModeRaw] = useState<LayoutMode>(effectiveLayoutMode);
|
|
604
|
+
useEffect(() => {
|
|
605
|
+
setLayoutModeRaw(layoutMode);
|
|
606
|
+
}, [layoutMode]);
|
|
531
607
|
const [imageEditTarget, setImageEditTarget] = useState<string | null>(null);
|
|
532
608
|
const [mediaRevision, setMediaRevision] = useState(0);
|
|
533
609
|
const openImageEdit = useCallback((relativePath: string) => {
|
|
@@ -560,6 +636,9 @@ export function EditorProvider({
|
|
|
560
636
|
if (viewPreferences.themeInheritance !== undefined) {
|
|
561
637
|
setThemeInheritanceRaw(viewPreferences.themeInheritance);
|
|
562
638
|
}
|
|
639
|
+
if (viewPreferences.layoutMode !== undefined) {
|
|
640
|
+
setLayoutModeRaw(viewPreferences.layoutMode);
|
|
641
|
+
}
|
|
563
642
|
}, [viewPreferences]);
|
|
564
643
|
|
|
565
644
|
// Wrap the three setters so user-driven toggles emit a snapshot via
|
|
@@ -579,6 +658,8 @@ export function EditorProvider({
|
|
|
579
658
|
blockTagsRef.current = blockTagsVisible;
|
|
580
659
|
const themeInheritanceRef = useRef(themeInheritanceState);
|
|
581
660
|
themeInheritanceRef.current = themeInheritanceState;
|
|
661
|
+
const layoutModeRef = useRef(layoutModeState);
|
|
662
|
+
layoutModeRef.current = layoutModeState;
|
|
582
663
|
const setInlinePreviewVisible = useCallback((visible: boolean) => {
|
|
583
664
|
setInlinePreviewVisibleRaw(visible);
|
|
584
665
|
onViewPreferencesChangeRef.current?.({
|
|
@@ -587,6 +668,7 @@ export function EditorProvider({
|
|
|
587
668
|
outline: outlineRef.current,
|
|
588
669
|
blockTags: blockTagsRef.current,
|
|
589
670
|
themeInheritance: themeInheritanceRef.current,
|
|
671
|
+
layoutMode: layoutModeRef.current,
|
|
590
672
|
});
|
|
591
673
|
}, []);
|
|
592
674
|
const setStatusBarVisible = useCallback((visible: boolean) => {
|
|
@@ -597,6 +679,7 @@ export function EditorProvider({
|
|
|
597
679
|
outline: outlineRef.current,
|
|
598
680
|
blockTags: blockTagsRef.current,
|
|
599
681
|
themeInheritance: themeInheritanceRef.current,
|
|
682
|
+
layoutMode: layoutModeRef.current,
|
|
600
683
|
});
|
|
601
684
|
}, []);
|
|
602
685
|
const setOutlineVisible = useCallback((visible: boolean) => {
|
|
@@ -607,6 +690,7 @@ export function EditorProvider({
|
|
|
607
690
|
outline: visible,
|
|
608
691
|
blockTags: blockTagsRef.current,
|
|
609
692
|
themeInheritance: themeInheritanceRef.current,
|
|
693
|
+
layoutMode: layoutModeRef.current,
|
|
610
694
|
});
|
|
611
695
|
}, []);
|
|
612
696
|
const setBlockTagsVisible = useCallback((visible: boolean) => {
|
|
@@ -617,6 +701,7 @@ export function EditorProvider({
|
|
|
617
701
|
outline: outlineRef.current,
|
|
618
702
|
blockTags: visible,
|
|
619
703
|
themeInheritance: themeInheritanceRef.current,
|
|
704
|
+
layoutMode: layoutModeRef.current,
|
|
620
705
|
});
|
|
621
706
|
}, []);
|
|
622
707
|
const setThemeInheritance = useCallback((mode: ThemeInheritance) => {
|
|
@@ -627,18 +712,35 @@ export function EditorProvider({
|
|
|
627
712
|
outline: outlineRef.current,
|
|
628
713
|
blockTags: blockTagsRef.current,
|
|
629
714
|
themeInheritance: mode,
|
|
715
|
+
layoutMode: layoutModeRef.current,
|
|
716
|
+
});
|
|
717
|
+
}, []);
|
|
718
|
+
const setLayoutMode = useCallback((mode: LayoutMode) => {
|
|
719
|
+
setLayoutModeRaw(mode);
|
|
720
|
+
onViewPreferencesChangeRef.current?.({
|
|
721
|
+
inlinePreview: inlinePreviewRef.current,
|
|
722
|
+
showStatusBar: statusBarRef.current,
|
|
723
|
+
outline: outlineRef.current,
|
|
724
|
+
blockTags: blockTagsRef.current,
|
|
725
|
+
themeInheritance: themeInheritanceRef.current,
|
|
726
|
+
layoutMode: mode,
|
|
630
727
|
});
|
|
631
728
|
}, []);
|
|
632
729
|
const [tiptapEditor, setTiptapEditor] = useState<TiptapEditor | null>(null);
|
|
633
730
|
const [monacoEditor, setMonacoEditor] = useState<MonacoEditor | null>(null);
|
|
731
|
+
// Mirror the focused canvas textbox editor (published by the inline
|
|
732
|
+
// SceneTextOverlay through a module channel, since the canvas renders in
|
|
733
|
+
// a detached React root outside this provider) so the toolbar can target it.
|
|
734
|
+
const [activeSceneText, setActiveSceneText] = useState<SceneTextHandle | null>(null);
|
|
735
|
+
useEffect(() => sceneTextChannel.subscribe(setActiveSceneText), []);
|
|
634
736
|
|
|
635
737
|
const articleIdRef = useRef(articleId);
|
|
636
738
|
articleIdRef.current = articleId;
|
|
637
739
|
|
|
638
|
-
// Sync
|
|
740
|
+
// Sync color scheme when prop changes
|
|
639
741
|
useEffect(() => {
|
|
640
|
-
|
|
641
|
-
}, [
|
|
742
|
+
setColorScheme(initialColorScheme);
|
|
743
|
+
}, [initialColorScheme]);
|
|
642
744
|
|
|
643
745
|
// Debounced parse on markdown source change
|
|
644
746
|
const parseTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
@@ -702,6 +804,27 @@ export function EditorProvider({
|
|
|
702
804
|
setMarkdownSourceRaw(source);
|
|
703
805
|
}, []);
|
|
704
806
|
|
|
807
|
+
// Block-at-a-time navigation. In 'document' mode the channel passes
|
|
808
|
+
// through to the full source; in 'block' mode `editorSource` is the active
|
|
809
|
+
// block's slice and `setEditorSource` splices edits back in. Gated to
|
|
810
|
+
// markdown mode — code/image surfaces always edit the whole file.
|
|
811
|
+
const blockNav = useBlockNavigator(markdownSource, setMarkdownSource, {
|
|
812
|
+
enabled:
|
|
813
|
+
(layoutModeState === 'block' || layoutModeState === 'timeline') && editorMode === 'markdown',
|
|
814
|
+
});
|
|
815
|
+
const {
|
|
816
|
+
editorSource,
|
|
817
|
+
setEditorSource,
|
|
818
|
+
blockCount,
|
|
819
|
+
activeBlockKey,
|
|
820
|
+
activeBlockStartLine,
|
|
821
|
+
goToBlock,
|
|
822
|
+
goToBlockByLine,
|
|
823
|
+
prevBlock,
|
|
824
|
+
nextBlock,
|
|
825
|
+
addBlock,
|
|
826
|
+
} = blockNav;
|
|
827
|
+
|
|
705
828
|
const insertAtCursor = useCallback(
|
|
706
829
|
(text: string) => {
|
|
707
830
|
if (activeView === 'wysiwyg' && tiptapEditor) {
|
|
@@ -854,7 +977,7 @@ export function EditorProvider({
|
|
|
854
977
|
activeView,
|
|
855
978
|
parseError,
|
|
856
979
|
isParsing,
|
|
857
|
-
|
|
980
|
+
colorScheme,
|
|
858
981
|
editorMode,
|
|
859
982
|
language: resolvedLanguage,
|
|
860
983
|
inlinePreviewVisible,
|
|
@@ -862,11 +985,17 @@ export function EditorProvider({
|
|
|
862
985
|
outlineVisible,
|
|
863
986
|
blockTagsVisible,
|
|
864
987
|
themeInheritance: themeInheritanceState,
|
|
988
|
+
layoutMode: layoutModeState,
|
|
989
|
+
editorSource,
|
|
990
|
+
blockCount,
|
|
991
|
+
activeBlockKey,
|
|
992
|
+
activeBlockStartLine,
|
|
865
993
|
imageEditTarget,
|
|
866
994
|
mediaRevision,
|
|
867
995
|
allowRecording,
|
|
868
996
|
tiptapEditor,
|
|
869
997
|
monacoEditor,
|
|
998
|
+
activeSceneText,
|
|
870
999
|
workspaceContainer,
|
|
871
1000
|
versioning,
|
|
872
1001
|
saveVersion,
|
|
@@ -875,11 +1004,18 @@ export function EditorProvider({
|
|
|
875
1004
|
mentionProvider,
|
|
876
1005
|
documentLinkProvider,
|
|
877
1006
|
setMarkdownSource,
|
|
1007
|
+
setEditorSource,
|
|
1008
|
+
setLayoutMode,
|
|
1009
|
+
goToBlock,
|
|
1010
|
+
goToBlockByLine,
|
|
1011
|
+
prevBlock,
|
|
1012
|
+
nextBlock,
|
|
1013
|
+
addBlock,
|
|
878
1014
|
setMarkdownDoc,
|
|
879
1015
|
setActiveView,
|
|
880
1016
|
setTiptapEditor,
|
|
881
1017
|
setMonacoEditor,
|
|
882
|
-
|
|
1018
|
+
setColorScheme,
|
|
883
1019
|
setInlinePreviewVisible,
|
|
884
1020
|
setStatusBarVisible,
|
|
885
1021
|
setOutlineVisible,
|
|
@@ -898,7 +1034,7 @@ export function EditorProvider({
|
|
|
898
1034
|
activeView,
|
|
899
1035
|
parseError,
|
|
900
1036
|
isParsing,
|
|
901
|
-
|
|
1037
|
+
colorScheme,
|
|
902
1038
|
editorMode,
|
|
903
1039
|
resolvedLanguage,
|
|
904
1040
|
inlinePreviewVisible,
|
|
@@ -906,8 +1042,14 @@ export function EditorProvider({
|
|
|
906
1042
|
outlineVisible,
|
|
907
1043
|
blockTagsVisible,
|
|
908
1044
|
themeInheritanceState,
|
|
1045
|
+
layoutModeState,
|
|
1046
|
+
editorSource,
|
|
1047
|
+
blockCount,
|
|
1048
|
+
activeBlockKey,
|
|
1049
|
+
activeBlockStartLine,
|
|
909
1050
|
tiptapEditor,
|
|
910
1051
|
monacoEditor,
|
|
1052
|
+
activeSceneText,
|
|
911
1053
|
workspaceContainer,
|
|
912
1054
|
versioning,
|
|
913
1055
|
saveVersion,
|
|
@@ -916,11 +1058,18 @@ export function EditorProvider({
|
|
|
916
1058
|
mentionProvider,
|
|
917
1059
|
documentLinkProvider,
|
|
918
1060
|
setMarkdownSource,
|
|
1061
|
+
setEditorSource,
|
|
1062
|
+
setLayoutMode,
|
|
1063
|
+
goToBlock,
|
|
1064
|
+
goToBlockByLine,
|
|
1065
|
+
prevBlock,
|
|
1066
|
+
nextBlock,
|
|
1067
|
+
addBlock,
|
|
919
1068
|
setMarkdownDoc,
|
|
920
1069
|
setActiveView,
|
|
921
1070
|
setTiptapEditor,
|
|
922
1071
|
setMonacoEditor,
|
|
923
|
-
|
|
1072
|
+
setColorScheme,
|
|
924
1073
|
setInlinePreviewVisible,
|
|
925
1074
|
setStatusBarVisible,
|
|
926
1075
|
setOutlineVisible,
|