@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.
Files changed (70) hide show
  1. package/README.md +10 -2
  2. package/dist/index.d.ts +505 -15
  3. package/dist/index.js +6035 -2283
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles/index.css +870 -40
  6. package/package.json +6 -5
  7. package/src/EditorContext.tsx +27 -0
  8. package/src/EditorShell.tsx +24 -0
  9. package/src/PreviewControls.tsx +20 -5
  10. package/src/PreviewPanel.tsx +5 -1
  11. package/src/RawEditor.tsx +12 -0
  12. package/src/RecorderEntry.tsx +3 -0
  13. package/src/Toolbar.tsx +315 -17
  14. package/src/WysiwygEditor.tsx +25 -11
  15. package/src/__tests__/buildPreviewDocContent.test.ts +17 -0
  16. package/src/__tests__/editorShellProps.test.tsx +65 -0
  17. package/src/__tests__/findMode.test.tsx +104 -0
  18. package/src/__tests__/findModel.test.ts +55 -0
  19. package/src/__tests__/markdownCodeFence.test.ts +45 -0
  20. package/src/__tests__/mediaReferences.test.ts +15 -0
  21. package/src/__tests__/previewControls.test.tsx +31 -0
  22. package/src/__tests__/tiptapBridge.test.ts +9 -0
  23. package/src/__tests__/toolbarSelectionConversion.test.tsx +26 -0
  24. package/src/__tests__/writeCanvasSettings.test.ts +15 -0
  25. package/src/asciiDiagram/__tests__/AsciiDiagramExtension.test.ts +20 -1
  26. package/src/buildPreviewDoc.ts +9 -7
  27. package/src/codeSnippet/CodeSnippetExtension.ts +205 -0
  28. package/src/codeSnippet/CodeSnippetWidget.tsx +109 -0
  29. package/src/codeSnippet/__tests__/CodeSnippetExtension.test.ts +95 -0
  30. package/src/codeSnippet/__tests__/codeSnippetLanguages.test.ts +50 -0
  31. package/src/codeSnippet/codeSnippetCommands.ts +21 -0
  32. package/src/codeSnippet/codeSnippetData.ts +43 -0
  33. package/src/codeSnippet/codeSnippetLanguages.ts +216 -0
  34. package/src/diagram/DiagramCanvas.tsx +1 -0
  35. package/src/find/FindHighlightExtension.ts +81 -0
  36. package/src/find/FindToolbar.tsx +314 -0
  37. package/src/find/findModel.ts +77 -0
  38. package/src/index.ts +89 -0
  39. package/src/markdownCodeFence.ts +72 -0
  40. package/src/mediaReferences.ts +5 -3
  41. package/src/mermaid/MermaidDiagramCanvas.tsx +693 -0
  42. package/src/mermaid/MermaidDiagramExtension.ts +243 -0
  43. package/src/mermaid/MermaidDiagramTypeThumbnail.tsx +184 -0
  44. package/src/mermaid/MermaidDiagramWidget.tsx +653 -0
  45. package/src/mermaid/MermaidShapePalette.tsx +245 -0
  46. package/src/mermaid/__tests__/MermaidDiagramExtension.test.ts +361 -0
  47. package/src/mermaid/__tests__/mermaidDiagramTypes.test.ts +35 -0
  48. package/src/mermaid/__tests__/mermaidRenderer.test.ts +49 -0
  49. package/src/mermaid/__tests__/mermaidSourceOps.test.ts +213 -0
  50. package/src/mermaid/__tests__/mermaidSyntax.test.ts +71 -0
  51. package/src/mermaid/mermaidCommands.ts +34 -0
  52. package/src/mermaid/mermaidData.ts +31 -0
  53. package/src/mermaid/mermaidDiagramTypes.ts +325 -0
  54. package/src/mermaid/mermaidModel.ts +31 -0
  55. package/src/mermaid/mermaidRenderer.ts +181 -0
  56. package/src/mermaid/mermaidShapes.ts +113 -0
  57. package/src/mermaid/mermaidSourceOps.ts +454 -0
  58. package/src/scene/Scene.tsx +46 -15
  59. package/src/scene/SceneBlockToolbar.tsx +29 -14
  60. package/src/scene/SceneViewControls.tsx +43 -0
  61. package/src/scene/__tests__/fitScale.test.ts +19 -0
  62. package/src/scene/__tests__/useScenePanZoom.test.ts +28 -1
  63. package/src/scene/fitScale.ts +17 -0
  64. package/src/scene/hooks/useScenePanZoom.ts +11 -4
  65. package/src/scene/scene.css +85 -3
  66. package/src/styles/code-snippet.css +76 -0
  67. package/src/styles/editor.css +322 -2
  68. package/src/styles/index.css +2 -0
  69. package/src/styles/mermaid-diagram.css +487 -0
  70. package/src/writeCanvasSettings.ts +30 -0
package/src/index.ts CHANGED
@@ -89,6 +89,7 @@ export type { RawEditorProps } from './RawEditor.js';
89
89
 
90
90
  export { WysiwygEditor } from './WysiwygEditor.js';
91
91
  export type { WysiwygEditorProps } from './WysiwygEditor.js';
92
+ export type { WriteCanvasSettings } from './writeCanvasSettings.js';
92
93
 
93
94
  export { PreviewPanel } from './PreviewPanel.js';
94
95
  export type { PreviewPanelProps } from './PreviewPanel.js';
@@ -293,6 +294,94 @@ export {
293
294
  } from './asciiDiagram/asciiDiagramOps.js';
294
295
  export { shouldPasteAsAsciiFence } from './asciiDiagram/asciiPaste.js';
295
296
 
297
+ // Complex diagram editor — every explicit `mermaid` fence is rendered through
298
+ // Mermaid's public renderer inside the shared diagram chrome. Flowcharts add
299
+ // source-backed node/edge gestures and the full Mermaid shape catalog; every
300
+ // other diagram family remains lossless render + source editing.
301
+ export {
302
+ MermaidDiagramExtension,
303
+ MERMAID_DIAGRAM_KEY,
304
+ findMermaidDiagramBlockPos,
305
+ isMermaidDiagramNode,
306
+ isMermaidSourceVisible,
307
+ toggleMermaidSource,
308
+ } from './mermaid/MermaidDiagramExtension.js';
309
+ export type {
310
+ MermaidDiagramBlockEntry,
311
+ MermaidDiagramExtensionOptions,
312
+ MermaidDiagramPluginState,
313
+ } from './mermaid/MermaidDiagramExtension.js';
314
+ export { MermaidDiagramWidget } from './mermaid/MermaidDiagramWidget.js';
315
+ export type { MermaidDiagramWidgetProps } from './mermaid/MermaidDiagramWidget.js';
316
+ export { MermaidDiagramCanvas } from './mermaid/MermaidDiagramCanvas.js';
317
+ export type {
318
+ MermaidDiagramCanvasProps,
319
+ MermaidNodeCanvasAction,
320
+ } from './mermaid/MermaidDiagramCanvas.js';
321
+ export { useMermaidDiagramData } from './mermaid/mermaidData.js';
322
+ export type { MermaidDiagramData } from './mermaid/mermaidData.js';
323
+ export {
324
+ renderMermaidDiagram,
325
+ inspectMermaidSource,
326
+ mermaidErrorMessage,
327
+ } from './mermaid/mermaidRenderer.js';
328
+ export type { MermaidRenderResult } from './mermaid/mermaidRenderer.js';
329
+ export { MermaidShapePalette } from './mermaid/MermaidShapePalette.js';
330
+ export type { MermaidShapePaletteProps } from './mermaid/MermaidShapePalette.js';
331
+ export {
332
+ MERMAID_FLOWCHART_SHAPES,
333
+ isMermaidFlowchartShapeId,
334
+ normalizeMermaidFlowchartShape,
335
+ } from './mermaid/mermaidShapes.js';
336
+ export type { MermaidFlowchartShape, MermaidFlowchartShapeId } from './mermaid/mermaidShapes.js';
337
+ export type {
338
+ MermaidEditableEdge,
339
+ MermaidEditableModel,
340
+ MermaidEditableNode,
341
+ MermaidFlowchartDirection,
342
+ MermaidFlowchartModel,
343
+ } from './mermaid/mermaidModel.js';
344
+ export {
345
+ DEFAULT_MERMAID_DIAGRAM_TYPE,
346
+ MERMAID_DIAGRAM_TYPES,
347
+ mermaidDiagramMarkdown,
348
+ } from './mermaid/mermaidDiagramTypes.js';
349
+ export type {
350
+ MermaidDiagramCategory,
351
+ MermaidDiagramPreview,
352
+ MermaidDiagramType,
353
+ } from './mermaid/mermaidDiagramTypes.js';
354
+ export { MermaidDiagramTypeThumbnail } from './mermaid/MermaidDiagramTypeThumbnail.js';
355
+
356
+ // Code snippets — every ordinary explicit-language fence is replaced in the
357
+ // WYSIWYG surface by a Monaco inset. The fence node remains authoritative, so
358
+ // edits preserve the exact language tag and source when serialized to Markdown.
359
+ export {
360
+ CodeSnippetExtension,
361
+ CODE_SNIPPET_KEY,
362
+ findCodeSnippetBlockPos,
363
+ isCodeSnippetNode,
364
+ } from './codeSnippet/CodeSnippetExtension.js';
365
+ export type {
366
+ CodeSnippetBlockEntry,
367
+ CodeSnippetExtensionOptions,
368
+ CodeSnippetPluginState,
369
+ } from './codeSnippet/CodeSnippetExtension.js';
370
+ export { CodeSnippetWidget } from './codeSnippet/CodeSnippetWidget.js';
371
+ export type { CodeSnippetWidgetProps } from './codeSnippet/CodeSnippetWidget.js';
372
+ export { replaceCodeSnippetText } from './codeSnippet/codeSnippetCommands.js';
373
+ export { useCodeSnippetData } from './codeSnippet/codeSnippetData.js';
374
+ export type { CodeSnippetData } from './codeSnippet/codeSnippetData.js';
375
+ export {
376
+ CODE_SNIPPET_LANGUAGES,
377
+ codeSnippetFenceLanguageToken,
378
+ codeSnippetLanguageLabel,
379
+ codeSnippetMarkdown,
380
+ isCodeSnippetFenceLanguage,
381
+ monacoLanguageForFence,
382
+ } from './codeSnippet/codeSnippetLanguages.js';
383
+ export type { CodeSnippetLanguage } from './codeSnippet/codeSnippetLanguages.js';
384
+
296
385
  // Treeview editor — ASCII file-tree / outline fences get an interactive
297
386
  // outline widget; edits re-render the tree art (the fence stays the source
298
387
  // of truth). Peer to the ASCII diagram editor; mutually exclusive with it
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Source-level Markdown fenced-code detection for editor features that cannot
3
+ * operate on the parsed AST. The returned mask includes opening and closing
4
+ * fence lines, and an unclosed fence remains active through EOF.
5
+ */
6
+
7
+ interface FenceState {
8
+ marker: '`' | '~';
9
+ length: number;
10
+ }
11
+
12
+ const FENCE_RE = /^ {0,3}(`{3,}|~{3,})(.*)$/;
13
+ const CLOSING_FENCE_RE = /^ {0,3}(`{3,}|~{3,})[ \t]*$/;
14
+
15
+ function openingFence(line: string): FenceState | null {
16
+ const match = FENCE_RE.exec(line);
17
+ if (!match) return null;
18
+ const run = match[1];
19
+ const marker = run[0] as '`' | '~';
20
+ // CommonMark forbids backticks in a backtick fence's info string.
21
+ if (marker === '`' && match[2].includes('`')) return null;
22
+ return { marker, length: run.length };
23
+ }
24
+
25
+ function isClosingFence(line: string, fence: FenceState): boolean {
26
+ const match = CLOSING_FENCE_RE.exec(line);
27
+ return !!match && match[1][0] === fence.marker && match[1].length >= fence.length;
28
+ }
29
+
30
+ /** One boolean per physical source line; true means the whole line is fenced code. */
31
+ export function markdownFencedCodeLineMask(source: string): boolean[] {
32
+ const lines = source.split(/\r\n|\r|\n/);
33
+ const mask: boolean[] = [];
34
+ let fence: FenceState | null = null;
35
+
36
+ for (const line of lines) {
37
+ if (fence) {
38
+ mask.push(true);
39
+ if (isClosingFence(line, fence)) fence = null;
40
+ continue;
41
+ }
42
+
43
+ const opening = openingFence(line);
44
+ mask.push(opening != null);
45
+ fence = opening;
46
+ }
47
+
48
+ return mask;
49
+ }
50
+
51
+ /** Whether a 1-based physical source line is an opening, body, or closing fence line. */
52
+ export function isMarkdownFencedCodeLine(source: string, lineNumber: number): boolean {
53
+ if (!Number.isInteger(lineNumber) || lineNumber < 1) return false;
54
+ return markdownFencedCodeLineMask(source)[lineNumber - 1] ?? false;
55
+ }
56
+
57
+ /**
58
+ * Replace fenced-code line contents with spaces while retaining every line
59
+ * ending and character offset. Raw-source scanners can safely inspect the
60
+ * result and use any match offsets against the original source.
61
+ */
62
+ export function maskMarkdownFencedCode(source: string): string {
63
+ const mask = markdownFencedCodeLineMask(source);
64
+ let lineIndex = 0;
65
+ return source
66
+ .split(/(\r\n|\r|\n)/)
67
+ .map((part, index) => {
68
+ if (index % 2 === 1) return part;
69
+ return mask[lineIndex++] ? ' '.repeat(part.length) : part;
70
+ })
71
+ .join('');
72
+ }
@@ -1,4 +1,5 @@
1
1
  import { splitKeyValueToken, tokenizeAttrTokens } from '@bendyline/squisq/markdown';
2
+ import { maskMarkdownFencedCode } from './markdownCodeFence';
2
3
 
3
4
  interface MediaReferenceRange {
4
5
  start: number;
@@ -32,9 +33,10 @@ const HTML_MEDIA_ATTRS = ['src', 'href', 'poster'] as const;
32
33
  */
33
34
  export function collectMediaReferencesFromMarkdown(source: string): ReadonlySet<string> {
34
35
  const refs = new Set<string>();
35
- collectMarkdownInlineReferences(source, refs);
36
- collectHtmlAttributeReferences(source, refs);
37
- collectAnnotationReferences(source, refs);
36
+ const searchableSource = maskMarkdownFencedCode(source);
37
+ collectMarkdownInlineReferences(searchableSource, refs);
38
+ collectHtmlAttributeReferences(searchableSource, refs);
39
+ collectAnnotationReferences(searchableSource, refs);
38
40
  return refs;
39
41
  }
40
42