@bendyline/squisq-editor-react 2.0.1 → 2.1.0
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 +10 -2
- package/dist/index.d.ts +505 -15
- package/dist/index.js +6035 -2283
- package/dist/index.js.map +1 -1
- package/dist/styles/index.css +870 -40
- package/package.json +6 -5
- package/src/EditorContext.tsx +27 -0
- package/src/EditorShell.tsx +24 -0
- package/src/PreviewControls.tsx +20 -5
- package/src/PreviewPanel.tsx +5 -1
- package/src/RawEditor.tsx +12 -0
- package/src/RecorderEntry.tsx +3 -0
- package/src/Toolbar.tsx +315 -17
- package/src/WysiwygEditor.tsx +25 -11
- package/src/__tests__/buildPreviewDocContent.test.ts +17 -0
- package/src/__tests__/editorShellProps.test.tsx +65 -0
- package/src/__tests__/findMode.test.tsx +104 -0
- package/src/__tests__/findModel.test.ts +55 -0
- package/src/__tests__/markdownCodeFence.test.ts +45 -0
- package/src/__tests__/mediaReferences.test.ts +15 -0
- package/src/__tests__/previewControls.test.tsx +31 -0
- package/src/__tests__/tiptapBridge.test.ts +9 -0
- package/src/__tests__/toolbarSelectionConversion.test.tsx +26 -0
- package/src/__tests__/writeCanvasSettings.test.ts +15 -0
- package/src/asciiDiagram/__tests__/AsciiDiagramExtension.test.ts +20 -1
- package/src/buildPreviewDoc.ts +9 -7
- package/src/codeSnippet/CodeSnippetExtension.ts +205 -0
- package/src/codeSnippet/CodeSnippetWidget.tsx +109 -0
- package/src/codeSnippet/__tests__/CodeSnippetExtension.test.ts +95 -0
- package/src/codeSnippet/__tests__/codeSnippetLanguages.test.ts +50 -0
- package/src/codeSnippet/codeSnippetCommands.ts +21 -0
- package/src/codeSnippet/codeSnippetData.ts +43 -0
- package/src/codeSnippet/codeSnippetLanguages.ts +216 -0
- package/src/diagram/DiagramCanvas.tsx +1 -0
- package/src/find/FindHighlightExtension.ts +81 -0
- package/src/find/FindToolbar.tsx +314 -0
- package/src/find/findModel.ts +77 -0
- package/src/index.ts +89 -0
- package/src/markdownCodeFence.ts +72 -0
- package/src/mediaReferences.ts +5 -3
- package/src/mermaid/MermaidDiagramCanvas.tsx +693 -0
- package/src/mermaid/MermaidDiagramExtension.ts +243 -0
- package/src/mermaid/MermaidDiagramTypeThumbnail.tsx +184 -0
- package/src/mermaid/MermaidDiagramWidget.tsx +653 -0
- package/src/mermaid/MermaidShapePalette.tsx +245 -0
- package/src/mermaid/__tests__/MermaidDiagramExtension.test.ts +361 -0
- package/src/mermaid/__tests__/mermaidDiagramTypes.test.ts +35 -0
- package/src/mermaid/__tests__/mermaidRenderer.test.ts +49 -0
- package/src/mermaid/__tests__/mermaidSourceOps.test.ts +213 -0
- package/src/mermaid/__tests__/mermaidSyntax.test.ts +71 -0
- package/src/mermaid/mermaidCommands.ts +34 -0
- package/src/mermaid/mermaidData.ts +31 -0
- package/src/mermaid/mermaidDiagramTypes.ts +325 -0
- package/src/mermaid/mermaidModel.ts +31 -0
- package/src/mermaid/mermaidRenderer.ts +181 -0
- package/src/mermaid/mermaidShapes.ts +113 -0
- package/src/mermaid/mermaidSourceOps.ts +454 -0
- package/src/scene/Scene.tsx +46 -15
- package/src/scene/SceneBlockToolbar.tsx +29 -14
- package/src/scene/SceneViewControls.tsx +43 -0
- package/src/scene/__tests__/fitScale.test.ts +19 -0
- package/src/scene/__tests__/useScenePanZoom.test.ts +28 -1
- package/src/scene/fitScale.ts +17 -0
- package/src/scene/hooks/useScenePanZoom.ts +11 -4
- package/src/scene/scene.css +85 -3
- package/src/styles/code-snippet.css +76 -0
- package/src/styles/editor.css +322 -2
- package/src/styles/index.css +2 -0
- package/src/styles/mermaid-diagram.css +487 -0
- package/src/writeCanvasSettings.ts +30 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bendyline/squisq-editor-react",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "React editor shell with raw/WYSIWYG/preview modes for Squisq documents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bendyline",
|
|
@@ -56,9 +56,9 @@
|
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@bendyline/squisq": "2.0
|
|
60
|
-
"@bendyline/squisq-formats": "2.0
|
|
61
|
-
"@bendyline/squisq-react": "2.0
|
|
59
|
+
"@bendyline/squisq": "2.1.0",
|
|
60
|
+
"@bendyline/squisq-formats": "2.1.0",
|
|
61
|
+
"@bendyline/squisq-react": "2.1.0",
|
|
62
62
|
"@fortawesome/fontawesome-free": "7.2.0",
|
|
63
63
|
"@monaco-editor/react": "4.7.0",
|
|
64
64
|
"@tiptap/extension-image": "2.27.2",
|
|
@@ -74,7 +74,8 @@
|
|
|
74
74
|
"@tiptap/pm": "2.27.2",
|
|
75
75
|
"@tiptap/react": "2.27.2",
|
|
76
76
|
"@tiptap/starter-kit": "2.27.2",
|
|
77
|
-
"@tiptap/suggestion": "2.27.2"
|
|
77
|
+
"@tiptap/suggestion": "2.27.2",
|
|
78
|
+
"mermaid": "11.16.0"
|
|
78
79
|
},
|
|
79
80
|
"devDependencies": {
|
|
80
81
|
"@types/react": "18.3.28",
|
package/src/EditorContext.tsx
CHANGED
|
@@ -153,6 +153,8 @@ export interface EditorState {
|
|
|
153
153
|
colorScheme: EditorColorScheme;
|
|
154
154
|
/** Operating mode — 'markdown' for the full shell, 'code' for Monaco-only. */
|
|
155
155
|
editorMode: EditorMode;
|
|
156
|
+
/** Whether the host-triggered Find toolbar is active. */
|
|
157
|
+
findMode: boolean;
|
|
156
158
|
/** Monaco language ID for the Raw editor. */
|
|
157
159
|
language: string;
|
|
158
160
|
/**
|
|
@@ -263,6 +265,8 @@ export interface EditorActions {
|
|
|
263
265
|
setMarkdownDoc: (doc: MarkdownDocument) => void;
|
|
264
266
|
/** Switch the active view */
|
|
265
267
|
setActiveView: (view: EditorView) => void;
|
|
268
|
+
/** Enter or leave the host-triggered Find toolbar mode. */
|
|
269
|
+
setFindMode: (active: boolean) => void;
|
|
266
270
|
/** Register / unregister the Tiptap editor instance (called by WysiwygEditor) */
|
|
267
271
|
setTiptapEditor: (editor: TiptapEditor | null) => void;
|
|
268
272
|
/** Register / unregister the Monaco editor instance (called by RawEditor) */
|
|
@@ -462,6 +466,13 @@ export interface EditorProviderProps {
|
|
|
462
466
|
fileName?: string;
|
|
463
467
|
/** Explicit Monaco language ID — wins over the fileName-derived one. */
|
|
464
468
|
language?: string;
|
|
469
|
+
/**
|
|
470
|
+
* Controlled Find-mode state. No trigger button is rendered by default;
|
|
471
|
+
* hosts can set this prop or call `setFindMode` from editor context.
|
|
472
|
+
*/
|
|
473
|
+
findMode?: boolean;
|
|
474
|
+
/** Notified when Find mode requests a state change (for example, its X button). */
|
|
475
|
+
onFindModeChange?: (active: boolean) => void;
|
|
465
476
|
/**
|
|
466
477
|
* Initial visibility of the inline preview gutter. Defaults to false.
|
|
467
478
|
* The toolbar's View menu can toggle it at runtime.
|
|
@@ -577,6 +588,8 @@ export function EditorProvider({
|
|
|
577
588
|
allowNarrate = true,
|
|
578
589
|
fileName,
|
|
579
590
|
language,
|
|
591
|
+
findMode: controlledFindMode,
|
|
592
|
+
onFindModeChange,
|
|
580
593
|
inlinePreview = false,
|
|
581
594
|
showStatusBar = true,
|
|
582
595
|
outline = false,
|
|
@@ -618,6 +631,16 @@ export function EditorProvider({
|
|
|
618
631
|
const [activeView, setActiveViewRaw] = useState<EditorView>(
|
|
619
632
|
editorMode === 'markdown' ? initialView : 'raw',
|
|
620
633
|
);
|
|
634
|
+
const [uncontrolledFindMode, setUncontrolledFindMode] = useState(false);
|
|
635
|
+
const findMode = editorMode !== 'image' && (controlledFindMode ?? uncontrolledFindMode);
|
|
636
|
+
const setFindMode = useCallback(
|
|
637
|
+
(active: boolean) => {
|
|
638
|
+
if (editorMode === 'image') return;
|
|
639
|
+
if (controlledFindMode === undefined) setUncontrolledFindMode(active);
|
|
640
|
+
onFindModeChange?.(active);
|
|
641
|
+
},
|
|
642
|
+
[controlledFindMode, editorMode, onFindModeChange],
|
|
643
|
+
);
|
|
621
644
|
const activeViewRef = useRef(activeView);
|
|
622
645
|
activeViewRef.current = activeView;
|
|
623
646
|
const tiptapEditorRef = useRef<TiptapEditor | null>(null);
|
|
@@ -1128,6 +1151,7 @@ export function EditorProvider({
|
|
|
1128
1151
|
isParsing,
|
|
1129
1152
|
colorScheme,
|
|
1130
1153
|
editorMode,
|
|
1154
|
+
findMode,
|
|
1131
1155
|
language: resolvedLanguage,
|
|
1132
1156
|
inlinePreviewVisible,
|
|
1133
1157
|
statusBarVisible,
|
|
@@ -1165,6 +1189,7 @@ export function EditorProvider({
|
|
|
1165
1189
|
addBlock,
|
|
1166
1190
|
setMarkdownDoc,
|
|
1167
1191
|
setActiveView,
|
|
1192
|
+
setFindMode,
|
|
1168
1193
|
setTiptapEditor,
|
|
1169
1194
|
setMonacoEditor,
|
|
1170
1195
|
setColorScheme,
|
|
@@ -1189,6 +1214,7 @@ export function EditorProvider({
|
|
|
1189
1214
|
isParsing,
|
|
1190
1215
|
colorScheme,
|
|
1191
1216
|
editorMode,
|
|
1217
|
+
findMode,
|
|
1192
1218
|
resolvedLanguage,
|
|
1193
1219
|
inlinePreviewVisible,
|
|
1194
1220
|
statusBarVisible,
|
|
@@ -1222,6 +1248,7 @@ export function EditorProvider({
|
|
|
1222
1248
|
addBlock,
|
|
1223
1249
|
setMarkdownDoc,
|
|
1224
1250
|
setActiveView,
|
|
1251
|
+
setFindMode,
|
|
1225
1252
|
setTiptapEditor,
|
|
1226
1253
|
setMonacoEditor,
|
|
1227
1254
|
setColorScheme,
|
package/src/EditorShell.tsx
CHANGED
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
import type { PrunePolicy, SaveVersionResult } from '@bendyline/squisq/versions';
|
|
66
66
|
import type { CSSProperties, KeyboardEvent as ReactKeyboardEvent, ReactNode } from 'react';
|
|
67
67
|
import { MediaContext } from '@bendyline/squisq-react';
|
|
68
|
+
import { writeCanvasSettingsStyle, type WriteCanvasSettings } from './writeCanvasSettings';
|
|
68
69
|
|
|
69
70
|
export type { EditorColorScheme } from './EditorContext';
|
|
70
71
|
|
|
@@ -210,6 +211,12 @@ export interface EditorShellProps {
|
|
|
210
211
|
* toolbar, tabs, and status bar keep their normal sizing.
|
|
211
212
|
*/
|
|
212
213
|
thinMargins?: boolean;
|
|
214
|
+
/**
|
|
215
|
+
* Host-controlled typography for the markdown Write canvas. `textSize` is
|
|
216
|
+
* measured in CSS pixels and `lineSpacing` is a unitless line-height
|
|
217
|
+
* multiplier. This is presentation-only and does not modify the markdown.
|
|
218
|
+
*/
|
|
219
|
+
writeCanvasSettings?: WriteCanvasSettings;
|
|
213
220
|
/**
|
|
214
221
|
* Render the bottom status bar (word / character / line / block counts
|
|
215
222
|
* and parse-state indicator). Defaults to `true`. Set to `false` in
|
|
@@ -243,6 +250,14 @@ export interface EditorShellProps {
|
|
|
243
250
|
* switches the shell into code mode.
|
|
244
251
|
*/
|
|
245
252
|
language?: string;
|
|
253
|
+
/**
|
|
254
|
+
* Controlled Find-mode state. Find has no built-in trigger button; hosts
|
|
255
|
+
* opt in by setting this prop (or by calling `setFindMode` from a toolbar
|
|
256
|
+
* slot rendered inside the editor context).
|
|
257
|
+
*/
|
|
258
|
+
findMode?: boolean;
|
|
259
|
+
/** Called when Find mode requests a state change, including its X button. */
|
|
260
|
+
onFindModeChange?: (active: boolean) => void;
|
|
246
261
|
/**
|
|
247
262
|
* Optional async provider for `@`-mention suggestions. When supplied,
|
|
248
263
|
* typing `@` inside the editor opens a popover of candidates; selecting
|
|
@@ -431,10 +446,13 @@ export function EditorShell({
|
|
|
431
446
|
fullWidth = false,
|
|
432
447
|
uxFont,
|
|
433
448
|
thinMargins = false,
|
|
449
|
+
writeCanvasSettings,
|
|
434
450
|
showStatusBar = true,
|
|
435
451
|
imageDisplayMode = 'inline',
|
|
436
452
|
fileName,
|
|
437
453
|
language,
|
|
454
|
+
findMode,
|
|
455
|
+
onFindModeChange,
|
|
438
456
|
mentionProvider,
|
|
439
457
|
documentLinkProvider,
|
|
440
458
|
allowRecording = true,
|
|
@@ -498,6 +516,8 @@ export function EditorShell({
|
|
|
498
516
|
allowNarrate={allowNarrate}
|
|
499
517
|
fileName={fileName}
|
|
500
518
|
language={language}
|
|
519
|
+
findMode={findMode}
|
|
520
|
+
onFindModeChange={onFindModeChange}
|
|
501
521
|
inlinePreview={inlinePreview}
|
|
502
522
|
showStatusBar={showStatusBar}
|
|
503
523
|
outline={outline}
|
|
@@ -527,6 +547,7 @@ export function EditorShell({
|
|
|
527
547
|
fullWidth={fullWidth}
|
|
528
548
|
uxFont={uxFont}
|
|
529
549
|
thinMargins={thinMargins}
|
|
550
|
+
writeCanvasSettings={writeCanvasSettings}
|
|
530
551
|
readOnly={readOnly}
|
|
531
552
|
imageSrc={imageSrc}
|
|
532
553
|
imageAlt={imageAlt}
|
|
@@ -564,6 +585,7 @@ interface EditorShellInnerProps {
|
|
|
564
585
|
fullWidth: boolean;
|
|
565
586
|
uxFont?: string;
|
|
566
587
|
thinMargins: boolean;
|
|
588
|
+
writeCanvasSettings?: WriteCanvasSettings;
|
|
567
589
|
readOnly: boolean;
|
|
568
590
|
imageSrc?: string;
|
|
569
591
|
imageAlt?: string;
|
|
@@ -597,6 +619,7 @@ function EditorShellInner({
|
|
|
597
619
|
fullWidth,
|
|
598
620
|
uxFont,
|
|
599
621
|
thinMargins,
|
|
622
|
+
writeCanvasSettings,
|
|
600
623
|
readOnly,
|
|
601
624
|
imageSrc,
|
|
602
625
|
imageAlt,
|
|
@@ -862,6 +885,7 @@ function EditorShellInner({
|
|
|
862
885
|
// tabs, status bar) consume `--squisq-ux-font` as their
|
|
863
886
|
// `font-family`, falling back to the system stack when unset.
|
|
864
887
|
...(uxFont ? ({ '--squisq-ux-font': uxFont } as CSSProperties) : {}),
|
|
888
|
+
...writeCanvasSettingsStyle(writeCanvasSettings),
|
|
865
889
|
// Exposed so the toolbar's view-tabs section AND the outline pane read
|
|
866
890
|
// one shared width, lining up the view-tabs' right-edge separator with
|
|
867
891
|
// the outline's right edge. A fixed `outlineWidth` pins it to px;
|
package/src/PreviewControls.tsx
CHANGED
|
@@ -616,6 +616,20 @@ const SUMMARIZE_TOOLTIP =
|
|
|
616
616
|
type ControlKey = 'format' | 'theme' | 'transform' | 'captions' | 'cover';
|
|
617
617
|
const CONTROL_KEYS: ControlKey[] = ['format', 'theme', 'transform', 'captions', 'cover'];
|
|
618
618
|
|
|
619
|
+
/**
|
|
620
|
+
* Controls that apply to the active display mode. Page (`linear`) is a
|
|
621
|
+
* variable-height HTML rendition: the aspect-ratio format switch only
|
|
622
|
+
* affects embedded canvas sections (which default sensibly) and captions
|
|
623
|
+
* never applied, so both are hidden there. Cover, Theme, and Summarize
|
|
624
|
+
* remain live in every mode.
|
|
625
|
+
*/
|
|
626
|
+
function controlKeysForMode(displayMode: string): ControlKey[] {
|
|
627
|
+
if (displayMode === 'linear') {
|
|
628
|
+
return CONTROL_KEYS.filter((key) => key !== 'format' && key !== 'captions');
|
|
629
|
+
}
|
|
630
|
+
return CONTROL_KEYS;
|
|
631
|
+
}
|
|
632
|
+
|
|
619
633
|
const PREVIEW_POPOVER_GAP = 4;
|
|
620
634
|
const PREVIEW_POPOVER_MARGIN = 8;
|
|
621
635
|
const PREVIEW_POPOVER_FALLBACK_WIDTH = 220;
|
|
@@ -666,6 +680,7 @@ const selectStyle: React.CSSProperties = {
|
|
|
666
680
|
*/
|
|
667
681
|
export function PreviewToolbarControls() {
|
|
668
682
|
const s = usePreviewSettings();
|
|
683
|
+
const controlKeys = controlKeysForMode(s.activeDisplayMode);
|
|
669
684
|
const [visibleCount, setVisibleCount] = useState(CONTROL_KEYS.length);
|
|
670
685
|
const [popoverOpen, setPopoverOpen] = useState(false);
|
|
671
686
|
// `rootRef` (flex:1) always spans the toolbar's leftover width, so its
|
|
@@ -732,7 +747,7 @@ export function PreviewToolbarControls() {
|
|
|
732
747
|
ro.observe(probe);
|
|
733
748
|
measure();
|
|
734
749
|
return () => ro.disconnect();
|
|
735
|
-
}, []);
|
|
750
|
+
}, [controlKeys.length]);
|
|
736
751
|
|
|
737
752
|
// Close popover on outside click
|
|
738
753
|
useEffect(() => {
|
|
@@ -900,9 +915,9 @@ export function PreviewToolbarControls() {
|
|
|
900
915
|
}
|
|
901
916
|
};
|
|
902
917
|
|
|
903
|
-
const hasOverflow = visibleCount <
|
|
904
|
-
const visibleKeys =
|
|
905
|
-
const overflowKeys =
|
|
918
|
+
const hasOverflow = visibleCount < controlKeys.length;
|
|
919
|
+
const visibleKeys = controlKeys.slice(0, visibleCount);
|
|
920
|
+
const overflowKeys = controlKeys.slice(visibleCount);
|
|
906
921
|
|
|
907
922
|
useEffect(() => {
|
|
908
923
|
if (!hasOverflow && popoverOpen) closePopover();
|
|
@@ -920,7 +935,7 @@ export function PreviewToolbarControls() {
|
|
|
920
935
|
inline/overflow split. Absolutely positioned so it never affects
|
|
921
936
|
layout. */}
|
|
922
937
|
<div className="squisq-preview-controls-probe" ref={probeRef} aria-hidden="true">
|
|
923
|
-
{
|
|
938
|
+
{controlKeys.map((key) => renderControl(key, false))}
|
|
924
939
|
</div>
|
|
925
940
|
|
|
926
941
|
{visibleKeys.length > 0 && (
|
package/src/PreviewPanel.tsx
CHANGED
|
@@ -14,7 +14,7 @@ import { createPortal } from 'react-dom';
|
|
|
14
14
|
import { DocPlayer, LinearDocView, useMediaProvider } from '@bendyline/squisq-react';
|
|
15
15
|
import type { AudioController, PlaybackState } from '@bendyline/squisq-react';
|
|
16
16
|
import type { Doc } from '@bendyline/squisq/schemas';
|
|
17
|
-
import { applyTransform } from '@bendyline/squisq/transform';
|
|
17
|
+
import { applyTransform, resolveTransformStyle } from '@bendyline/squisq/transform';
|
|
18
18
|
import { resolveAudioMapping } from '@bendyline/squisq/doc';
|
|
19
19
|
import type { ContentContainer } from '@bendyline/squisq/storage';
|
|
20
20
|
import { useEditorContext } from './EditorContext';
|
|
@@ -333,6 +333,10 @@ export function PreviewPanel({ basePath = '/', className, workspaceContainer }:
|
|
|
333
333
|
viewport={activeViewport}
|
|
334
334
|
theme={activeTheme}
|
|
335
335
|
globalKeyboardShortcuts={!audience}
|
|
336
|
+
showCover={activeCoverSlide}
|
|
337
|
+
transformPage={
|
|
338
|
+
activeTransformStyle ? resolveTransformStyle(activeTransformStyle).page : undefined
|
|
339
|
+
}
|
|
336
340
|
/>
|
|
337
341
|
);
|
|
338
342
|
}
|
package/src/RawEditor.tsx
CHANGED
|
@@ -16,6 +16,7 @@ import { BLOCK_META_KEY_DESCRIPTORS, tokenizeAttrTokens } from '@bendyline/squis
|
|
|
16
16
|
import { SQUISQ_MEDIA_MIME, parseSquisqMediaPayload } from './mediaDragMime';
|
|
17
17
|
import { canHandleSquisqMediaDrop, ownsMonacoModel } from './rawEditorIsolation';
|
|
18
18
|
import { useMonacoLoader } from './useMonacoLoader';
|
|
19
|
+
import { isMarkdownFencedCodeLine, markdownFencedCodeLineMask } from './markdownCodeFence';
|
|
19
20
|
|
|
20
21
|
// Monaco is loaded lazily through `useMonacoLoader` (see the hook for the
|
|
21
22
|
// rationale). The type-only `import type * as monaco from 'monaco-editor'`
|
|
@@ -200,6 +201,9 @@ export function RawEditor({
|
|
|
200
201
|
triggerCharacters: ['['],
|
|
201
202
|
provideCompletionItems(model: monaco.editor.ITextModel, position: monaco.Position) {
|
|
202
203
|
if (!ownsMonacoModel(editor, model)) return { suggestions: [] };
|
|
204
|
+
if (isMarkdownFencedCodeLine(model.getValue(), position.lineNumber)) {
|
|
205
|
+
return { suggestions: [] };
|
|
206
|
+
}
|
|
203
207
|
const lineContent = model.getLineContent(position.lineNumber);
|
|
204
208
|
|
|
205
209
|
// Only trigger inside a heading line that has {[ before the cursor
|
|
@@ -314,6 +318,9 @@ export function RawEditor({
|
|
|
314
318
|
triggerCharacters: ['['],
|
|
315
319
|
provideCompletionItems(model, position) {
|
|
316
320
|
if (!ownsMonacoModel(editor, model)) return { suggestions: [] };
|
|
321
|
+
if (isMarkdownFencedCodeLine(model.getValue(), position.lineNumber)) {
|
|
322
|
+
return { suggestions: [] };
|
|
323
|
+
}
|
|
317
324
|
const lineContent = model.getLineContent(position.lineNumber);
|
|
318
325
|
const textBeforeCursor = lineContent.substring(0, position.column - 1);
|
|
319
326
|
const textAfterCursor = lineContent.substring(position.column - 1);
|
|
@@ -397,6 +404,9 @@ export function RawEditor({
|
|
|
397
404
|
triggerCharacters: ['=', ' '],
|
|
398
405
|
provideCompletionItems(model, position) {
|
|
399
406
|
if (!ownsMonacoModel(editor, model)) return { suggestions: [] };
|
|
407
|
+
if (isMarkdownFencedCodeLine(model.getValue(), position.lineNumber)) {
|
|
408
|
+
return { suggestions: [] };
|
|
409
|
+
}
|
|
400
410
|
const lineContent = model.getLineContent(position.lineNumber);
|
|
401
411
|
// Block-meta attributes only mean something on a heading's
|
|
402
412
|
// template annotation — same gate as the template provider.
|
|
@@ -624,8 +634,10 @@ export function RawEditor({
|
|
|
624
634
|
|
|
625
635
|
const decorations: monaco.editor.IModelDeltaDecoration[] = [];
|
|
626
636
|
const lines = model.getLineCount();
|
|
637
|
+
const fencedLines = markdownFencedCodeLineMask(model.getValue());
|
|
627
638
|
const re = /\{\[([a-zA-Z0-9_:-]+)\]\}/g;
|
|
628
639
|
for (let line = 1; line <= lines; line++) {
|
|
640
|
+
if (fencedLines[line - 1]) continue;
|
|
629
641
|
const text = model.getLineContent(line);
|
|
630
642
|
re.lastIndex = 0;
|
|
631
643
|
let match: RegExpExecArray | null;
|
package/src/RecorderEntry.tsx
CHANGED
|
@@ -26,6 +26,7 @@ import { RecorderPanel } from './recorder/RecorderPanel.js';
|
|
|
26
26
|
import type { RecorderSaveResult } from './recorder/RecorderModal.js';
|
|
27
27
|
import { insertMediaBlock } from './recorder/insertMediaBlock.js';
|
|
28
28
|
import { useEditorContext } from './EditorContext';
|
|
29
|
+
import { markdownFencedCodeLineMask } from './markdownCodeFence';
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Insert a narration annotation `{[audio=filename]}` onto the heading
|
|
@@ -42,8 +43,10 @@ function annotateMonacoHeading(
|
|
|
42
43
|
if (!model) return false;
|
|
43
44
|
const position = editor.getPosition();
|
|
44
45
|
if (!position) return false;
|
|
46
|
+
const fencedLines = markdownFencedCodeLineMask(model.getValue());
|
|
45
47
|
// Walk upward from the cursor to find the nearest heading line.
|
|
46
48
|
for (let line = position.lineNumber; line >= 1; line--) {
|
|
49
|
+
if (fencedLines[line - 1]) continue;
|
|
47
50
|
const text = model.getLineContent(line);
|
|
48
51
|
if (!/^#{1,6}\s/.test(text)) continue;
|
|
49
52
|
// Skip if the heading already has an audio annotation — don't
|