@bendyline/squisq-editor-react 2.0.0 → 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 (110) hide show
  1. package/README.md +10 -2
  2. package/dist/index.d.ts +579 -47
  3. package/dist/index.js +7477 -2943
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles/index.css +1008 -38
  6. package/package.json +6 -5
  7. package/src/DocumentSettingsDialog.tsx +32 -18
  8. package/src/EditorContext.tsx +27 -0
  9. package/src/EditorShell.tsx +25 -1
  10. package/src/PreviewControls.tsx +77 -29
  11. package/src/PreviewPanel.tsx +5 -1
  12. package/src/RawEditor.tsx +12 -0
  13. package/src/RecorderEntry.tsx +5 -0
  14. package/src/Toolbar.tsx +479 -36
  15. package/src/WysiwygEditor.tsx +25 -11
  16. package/src/__tests__/buildPreviewDocContent.test.ts +17 -0
  17. package/src/__tests__/codeContextSectionView.test.tsx +8 -6
  18. package/src/__tests__/documentSettingsDialog.test.tsx +22 -0
  19. package/src/__tests__/editorShellProps.test.tsx +65 -0
  20. package/src/__tests__/findMode.test.tsx +104 -0
  21. package/src/__tests__/findModel.test.ts +55 -0
  22. package/src/__tests__/markdownCodeFence.test.ts +45 -0
  23. package/src/__tests__/mediaAttachmentFlow.test.ts +2 -2
  24. package/src/__tests__/mediaReferences.test.ts +15 -0
  25. package/src/__tests__/previewControls.test.tsx +195 -0
  26. package/src/__tests__/recorderTheme.test.tsx +42 -0
  27. package/src/__tests__/selectionConversions.test.ts +80 -0
  28. package/src/__tests__/tiptapBridge.test.ts +57 -7
  29. package/src/__tests__/tiptapImageRoundTrip.test.ts +1 -1
  30. package/src/__tests__/toolbarSelectionConversion.test.tsx +190 -0
  31. package/src/__tests__/writeCanvasSettings.test.ts +15 -0
  32. package/src/asciiDiagram/AsciiDiagramWidget.tsx +34 -4
  33. package/src/asciiDiagram/__tests__/AsciiDiagramExtension.test.ts +20 -1
  34. package/src/asciiDiagram/__tests__/asciiDiagramCommands.test.ts +58 -1
  35. package/src/asciiDiagram/asciiDiagramCommands.ts +36 -10
  36. package/src/asciiDiagram/asciiDiagramData.ts +33 -0
  37. package/src/asciiDiagram/asciiDiagramOps.ts +19 -0
  38. package/src/buildPreviewDoc.ts +9 -7
  39. package/src/codeContext/types.ts +1 -1
  40. package/src/codeSnippet/CodeSnippetExtension.ts +205 -0
  41. package/src/codeSnippet/CodeSnippetWidget.tsx +109 -0
  42. package/src/codeSnippet/__tests__/CodeSnippetExtension.test.ts +95 -0
  43. package/src/codeSnippet/__tests__/codeSnippetLanguages.test.ts +50 -0
  44. package/src/codeSnippet/codeSnippetCommands.ts +21 -0
  45. package/src/codeSnippet/codeSnippetData.ts +43 -0
  46. package/src/codeSnippet/codeSnippetLanguages.ts +216 -0
  47. package/src/customTemplates/__tests__/useMemoryLayerAdapter.test.ts +13 -0
  48. package/src/customTemplates/useMemoryLayerAdapter.ts +7 -1
  49. package/src/diagram/DiagramCanvas.tsx +17 -2
  50. package/src/find/FindHighlightExtension.ts +81 -0
  51. package/src/find/FindToolbar.tsx +314 -0
  52. package/src/find/findModel.ts +77 -0
  53. package/src/frontmatterSettings.ts +23 -0
  54. package/src/index.ts +96 -1
  55. package/src/markdownCodeFence.ts +72 -0
  56. package/src/mediaReferences.ts +5 -3
  57. package/src/mermaid/MermaidDiagramCanvas.tsx +693 -0
  58. package/src/mermaid/MermaidDiagramExtension.ts +243 -0
  59. package/src/mermaid/MermaidDiagramTypeThumbnail.tsx +184 -0
  60. package/src/mermaid/MermaidDiagramWidget.tsx +653 -0
  61. package/src/mermaid/MermaidShapePalette.tsx +245 -0
  62. package/src/mermaid/__tests__/MermaidDiagramExtension.test.ts +361 -0
  63. package/src/mermaid/__tests__/mermaidDiagramTypes.test.ts +35 -0
  64. package/src/mermaid/__tests__/mermaidRenderer.test.ts +49 -0
  65. package/src/mermaid/__tests__/mermaidSourceOps.test.ts +213 -0
  66. package/src/mermaid/__tests__/mermaidSyntax.test.ts +71 -0
  67. package/src/mermaid/mermaidCommands.ts +34 -0
  68. package/src/mermaid/mermaidData.ts +31 -0
  69. package/src/mermaid/mermaidDiagramTypes.ts +325 -0
  70. package/src/mermaid/mermaidModel.ts +31 -0
  71. package/src/mermaid/mermaidRenderer.ts +181 -0
  72. package/src/mermaid/mermaidShapes.ts +113 -0
  73. package/src/mermaid/mermaidSourceOps.ts +454 -0
  74. package/src/recorder/RecorderButton.tsx +9 -1
  75. package/src/recorder/RecorderModal.tsx +84 -41
  76. package/src/recorder/RecorderPanel.tsx +9 -1
  77. package/src/scene/Scene.tsx +46 -15
  78. package/src/scene/SceneBlockToolbar.tsx +29 -14
  79. package/src/scene/SceneViewControls.tsx +43 -0
  80. package/src/scene/__tests__/fitScale.test.ts +19 -0
  81. package/src/scene/__tests__/sceneIsolation.test.tsx +27 -1
  82. package/src/scene/__tests__/useScenePanZoom.test.ts +28 -1
  83. package/src/scene/adapters/DrawingAdapter.ts +6 -1
  84. package/src/scene/adapters/LayoutAdapter.ts +3 -0
  85. package/src/scene/commands/SceneCommand.ts +13 -2
  86. package/src/scene/fitScale.ts +17 -0
  87. package/src/scene/hooks/useScenePanZoom.ts +11 -4
  88. package/src/scene/scene.css +85 -3
  89. package/src/scene/tools/SelectTool.ts +5 -5
  90. package/src/selectionConversions.ts +155 -0
  91. package/src/styles/ascii-timeline.css +101 -4
  92. package/src/styles/code-snippet.css +76 -0
  93. package/src/styles/editor.css +352 -2
  94. package/src/styles/index.css +2 -0
  95. package/src/styles/mermaid-diagram.css +487 -0
  96. package/src/styles/tree-view.css +51 -1
  97. package/src/timeline/TimelineEditorWidget.tsx +200 -41
  98. package/src/timeline/__tests__/TimelineEditorWidget.test.tsx +61 -2
  99. package/src/timeline/__tests__/timelineCommands.test.ts +32 -0
  100. package/src/timeline/__tests__/timelineOps.test.ts +55 -0
  101. package/src/timeline/timelineCommands.ts +54 -0
  102. package/src/timeline/timelineOps.ts +107 -3
  103. package/src/tiptapBridge.ts +23 -5
  104. package/src/treeview/TreeOutlineWidget.tsx +153 -3
  105. package/src/treeview/__tests__/TreeOutlineWidget.test.tsx +156 -0
  106. package/src/treeview/__tests__/treeOps.test.ts +52 -0
  107. package/src/treeview/__tests__/treeViewCommands.test.ts +16 -0
  108. package/src/treeview/treeOps.ts +59 -0
  109. package/src/treeview/treeViewCommands.ts +5 -0
  110. package/src/writeCanvasSettings.ts +30 -0
@@ -359,6 +359,119 @@
359
359
  min-width: 0;
360
360
  }
361
361
 
362
+ /* Host-triggered Find mode. It occupies the toolbar's flexible middle lane,
363
+ leaving the view tabs on its left and the normal right-side host/settings
364
+ controls on its right. */
365
+ .squisq-find-toolbar {
366
+ display: flex;
367
+ align-items: center;
368
+ flex: 1 1 auto;
369
+ gap: 3px;
370
+ min-width: 180px;
371
+ padding: 4px 6px;
372
+ }
373
+
374
+ .squisq-find-field {
375
+ display: flex;
376
+ align-items: center;
377
+ flex: 0 1 420px;
378
+ min-width: 130px;
379
+ height: 28px;
380
+ border: 1px solid #9ca3af;
381
+ border-radius: 5px;
382
+ background: #ffffff;
383
+ color: #6b7280;
384
+ box-sizing: border-box;
385
+ }
386
+
387
+ .squisq-find-field:focus-within {
388
+ border-color: #2563eb;
389
+ box-shadow: 0 0 0 1px #2563eb;
390
+ }
391
+
392
+ .squisq-find-field > i {
393
+ margin-left: 8px;
394
+ color: #6b7280;
395
+ font-size: 11px;
396
+ }
397
+
398
+ .squisq-find-input {
399
+ flex: 1 1 auto;
400
+ min-width: 32px;
401
+ height: 100%;
402
+ padding: 0 7px;
403
+ border: 0;
404
+ outline: 0;
405
+ background: transparent;
406
+ color: #111827;
407
+ font: inherit;
408
+ font-size: 12.5px;
409
+ }
410
+
411
+ .squisq-find-input::-webkit-search-cancel-button {
412
+ display: none;
413
+ }
414
+
415
+ .squisq-find-count {
416
+ flex: 0 0 auto;
417
+ padding-right: 7px;
418
+ color: #6b7280;
419
+ font-size: 11px;
420
+ white-space: nowrap;
421
+ }
422
+
423
+ .squisq-find-button {
424
+ display: inline-flex;
425
+ align-items: center;
426
+ justify-content: center;
427
+ width: 28px;
428
+ height: 28px;
429
+ padding: 0;
430
+ border: 0;
431
+ border-radius: 4px;
432
+ background: transparent;
433
+ color: var(--squisq-toolbar-icon);
434
+ cursor: pointer;
435
+ }
436
+
437
+ .squisq-find-button:hover:not(:disabled) {
438
+ background: rgba(0, 0, 0, 0.08);
439
+ color: #111827;
440
+ }
441
+
442
+ .squisq-find-button:disabled {
443
+ opacity: 0.35;
444
+ cursor: default;
445
+ }
446
+
447
+ .squisq-find-close {
448
+ margin-left: 2px;
449
+ }
450
+
451
+ /* Shared by Tiptap decorations, Monaco inline decorations, and the fallback
452
+ used when a preview webview lacks the CSS Custom Highlight API. */
453
+ .squisq-find-match {
454
+ border-radius: 2px;
455
+ background: #fde68a !important;
456
+ color: #111827 !important;
457
+ }
458
+
459
+ .squisq-find-match--selected {
460
+ background: #f59e0b !important;
461
+ box-shadow: 0 0 0 1px #b45309;
462
+ }
463
+
464
+ @container squisq-toolbar (max-width: 680px) {
465
+ .squisq-find-toolbar {
466
+ min-width: 120px;
467
+ padding-right: 2px;
468
+ }
469
+
470
+ .squisq-find-count {
471
+ display: none;
472
+ }
473
+ }
474
+
362
475
  /* Contextual groups (template/transition pickers, table controls) sit at the
363
476
  end of the actions row. When measurement marks one as clipped it moves into
364
477
  the ··· overflow menu; hide the cropped inline original rather than painting
@@ -645,6 +758,19 @@
645
758
  z-index: 100;
646
759
  min-width: 200px;
647
760
  padding: 4px 0;
761
+ /* This menu is portaled to <body>, outside the editor shell that normally
762
+ supplies the UX font. Keep its unbuttoned content (notably the header)
763
+ on the same sans-serif UI stack instead of inheriting the host page font. */
764
+ font-family: var(
765
+ --squisq-ux-font,
766
+ -apple-system,
767
+ BlinkMacSystemFont,
768
+ 'Segoe UI',
769
+ 'Noto Sans',
770
+ Helvetica,
771
+ Arial,
772
+ sans-serif
773
+ );
648
774
  background: #fff;
649
775
  border: 1px solid #e5e7eb;
650
776
  border-radius: 6px;
@@ -661,6 +787,12 @@
661
787
  user-select: none;
662
788
  }
663
789
 
790
+ .squisq-insert-menu-header--separated {
791
+ margin-top: 4px;
792
+ padding-top: 8px;
793
+ border-top: 1px solid #e5e7eb;
794
+ }
795
+
664
796
  /* Portaled outside squisq-editor-shell, so dark mode is applied via data-theme. */
665
797
  .squisq-insert-menu[data-theme='dark'] {
666
798
  background: #1f2937;
@@ -680,8 +812,182 @@
680
812
  color: #6b7280;
681
813
  }
682
814
 
815
+ .squisq-insert-menu[data-theme='dark'] .squisq-insert-menu-header--separated {
816
+ border-top-color: #374151;
817
+ }
818
+
819
+ .squisq-code-snippet-menu {
820
+ width: 220px;
821
+ max-height: min(440px, calc(100vh - 16px));
822
+ overflow-y: auto;
823
+ }
824
+
825
+ .squisq-code-snippet-language {
826
+ justify-content: space-between;
827
+ gap: 16px;
828
+ }
829
+
830
+ .squisq-code-snippet-language code {
831
+ overflow: hidden;
832
+ color: #9ca3af;
833
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
834
+ font-size: 11px;
835
+ text-overflow: ellipsis;
836
+ white-space: nowrap;
837
+ }
838
+
683
839
  /* ─── Template Picker (toolbar) ──────────────────────── */
684
840
 
841
+ .squisq-mermaid-type-menu {
842
+ width: min(520px, calc(100vw - 16px));
843
+ max-height: min(600px, calc(100vh - 16px));
844
+ padding: 0 8px 10px;
845
+ overflow-y: auto;
846
+ }
847
+
848
+ .squisq-mermaid-type-menu-heading {
849
+ position: sticky;
850
+ z-index: 1;
851
+ top: 0;
852
+ display: flex;
853
+ flex-direction: column;
854
+ gap: 2px;
855
+ margin: 0 -8px 4px;
856
+ padding: 12px 14px 10px;
857
+ color: #111827;
858
+ background: #fff;
859
+ border-bottom: 1px solid #e5e7eb;
860
+ }
861
+
862
+ .squisq-mermaid-type-menu-heading strong {
863
+ font-size: 14px;
864
+ }
865
+
866
+ .squisq-mermaid-type-menu-heading span {
867
+ color: #6b7280;
868
+ font-size: 11px;
869
+ line-height: 1.35;
870
+ }
871
+
872
+ .squisq-mermaid-type-section + .squisq-mermaid-type-section {
873
+ margin-top: 4px;
874
+ }
875
+
876
+ .squisq-mermaid-type-grid {
877
+ display: grid;
878
+ grid-template-columns: repeat(2, minmax(0, 1fr));
879
+ gap: 6px;
880
+ }
881
+
882
+ .squisq-mermaid-type-card {
883
+ display: grid;
884
+ grid-template-columns: 82px minmax(0, 1fr);
885
+ align-items: center;
886
+ min-width: 0;
887
+ padding: 8px;
888
+ color: #1f2937;
889
+ font: inherit;
890
+ text-align: left;
891
+ background: #f9fafb;
892
+ border: 1px solid #e5e7eb;
893
+ border-radius: 7px;
894
+ cursor: pointer;
895
+ }
896
+
897
+ .squisq-mermaid-type-card:hover,
898
+ .squisq-mermaid-type-card:focus-visible {
899
+ color: #1d4ed8;
900
+ background: #eff6ff;
901
+ border-color: #93c5fd;
902
+ outline: none;
903
+ }
904
+
905
+ .squisq-mermaid-type-thumbnail {
906
+ display: flex;
907
+ height: 48px;
908
+ align-items: center;
909
+ justify-content: center;
910
+ padding: 3px;
911
+ color: currentcolor;
912
+ }
913
+
914
+ .squisq-mermaid-type-thumbnail svg {
915
+ display: block;
916
+ width: 100%;
917
+ height: 100%;
918
+ }
919
+
920
+ .squisq-mermaid-type-copy,
921
+ .squisq-mermaid-type-title {
922
+ min-width: 0;
923
+ }
924
+
925
+ .squisq-mermaid-type-copy {
926
+ display: flex;
927
+ flex-direction: column;
928
+ gap: 3px;
929
+ }
930
+
931
+ .squisq-mermaid-type-title {
932
+ display: flex;
933
+ align-items: center;
934
+ gap: 5px;
935
+ font-size: 12px;
936
+ font-weight: 650;
937
+ }
938
+
939
+ .squisq-mermaid-type-description {
940
+ display: -webkit-box;
941
+ overflow: hidden;
942
+ color: #6b7280;
943
+ font-size: 10px;
944
+ line-height: 1.3;
945
+ -webkit-box-orient: vertical;
946
+ -webkit-line-clamp: 2;
947
+ }
948
+
949
+ .squisq-mermaid-type-badge {
950
+ flex: none;
951
+ padding: 1px 4px;
952
+ color: #92400e;
953
+ font-size: 8px;
954
+ font-weight: 700;
955
+ letter-spacing: 0.03em;
956
+ text-transform: uppercase;
957
+ background: #fef3c7;
958
+ border-radius: 999px;
959
+ }
960
+
961
+ .squisq-mermaid-type-menu[data-theme='dark'] .squisq-mermaid-type-menu-heading {
962
+ color: #f9fafb;
963
+ background: #1f2937;
964
+ border-bottom-color: #374151;
965
+ }
966
+
967
+ .squisq-mermaid-type-menu[data-theme='dark'] .squisq-mermaid-type-menu-heading span,
968
+ .squisq-mermaid-type-menu[data-theme='dark'] .squisq-mermaid-type-description {
969
+ color: #9ca3af;
970
+ }
971
+
972
+ .squisq-mermaid-type-menu[data-theme='dark'] .squisq-mermaid-type-card {
973
+ color: #e5e7eb;
974
+ background: #111827;
975
+ border-color: #374151;
976
+ }
977
+
978
+ .squisq-mermaid-type-menu[data-theme='dark'] .squisq-mermaid-type-card:hover,
979
+ .squisq-mermaid-type-menu[data-theme='dark'] .squisq-mermaid-type-card:focus-visible {
980
+ color: #bfdbfe;
981
+ background: #1e3a5f;
982
+ border-color: #3b82f6;
983
+ }
984
+
985
+ @media (max-width: 620px) {
986
+ .squisq-mermaid-type-grid {
987
+ grid-template-columns: 1fr;
988
+ }
989
+ }
990
+
685
991
  .squisq-template-picker {
686
992
  display: flex;
687
993
  align-items: center;
@@ -1967,6 +2273,7 @@
1967
2273
  /* Body font follows the active squisq theme when one is selected; the
1968
2274
  fallback inherits from the host page when it isn't. */
1969
2275
  font-family: var(--squisq-theme-body-font, inherit);
2276
+ font-size: var(--squisq-write-text-size, inherit);
1970
2277
  /* Explicit caret color so the blinking insertion point stays visible
1971
2278
  even when an ancestor (e.g. a DocBlocks shell with the OS in dark
1972
2279
  mode) inherits a near-white color into the editor. Without this
@@ -2046,6 +2353,15 @@
2046
2353
  margin: 1.6em 0 0.3em;
2047
2354
  }
2048
2355
 
2356
+ /* ProseMirror gives an empty text block a trailing <br> so its caret remains
2357
+ visible. Heading controls are siblings of the editable content span, so an
2358
+ uncontained break would temporarily push those controls onto a second line
2359
+ after a Markdown heading shortcut. Keep the break's caret line inside a
2360
+ zero-width inline box until the first heading character replaces it. */
2361
+ .squisq-heading-content:has(> br.ProseMirror-trailingBreak:only-child) {
2362
+ display: inline-block;
2363
+ }
2364
+
2049
2365
  /* The first block in the document has nothing above it — don't push the top of
2050
2366
  the editor down with the section-break margin. (The editable ProseMirror node
2051
2367
  itself carries the `squisq-wysiwyg-editor` class, so its blocks are direct
@@ -2055,8 +2371,10 @@
2055
2371
  }
2056
2372
 
2057
2373
  .squisq-wysiwyg-editor p {
2058
- margin: 0.5em 0;
2059
- line-height: 1.7;
2374
+ /* Slightly roomier paragraph separators make authored blank lines easier
2375
+ to scan without turning each paragraph into a separate card. */
2376
+ margin: 0.625em 0;
2377
+ line-height: var(--squisq-write-line-spacing, 1.7);
2060
2378
  }
2061
2379
 
2062
2380
  .squisq-wysiwyg-editor blockquote {
@@ -2066,6 +2384,13 @@
2066
2384
  color: var(--squisq-theme-text-muted, #6b7280);
2067
2385
  }
2068
2386
 
2387
+ /* Consecutive Markdown quote lines share one blockquote. Keep their Tiptap
2388
+ paragraphs visually line-adjacent instead of applying the normal prose
2389
+ paragraph gap between every authored `>` line. */
2390
+ .squisq-wysiwyg-editor blockquote > p {
2391
+ margin: 0;
2392
+ }
2393
+
2069
2394
  /* Inline `code` and `<pre>` blocks: the foreground / background draw from
2070
2395
  the active theme when the WYSIWYG container inherits theme colors
2071
2396
  (View menu → "Editor styling from theme: Fonts & colors"). The
@@ -2715,6 +3040,31 @@
2715
3040
  --squisq-toolbar-icon: #9ca3af;
2716
3041
  }
2717
3042
 
3043
+ .squisq-editor-shell[data-theme='dark'] .squisq-find-field {
3044
+ border-color: #4b5563;
3045
+ background: #111827;
3046
+ color: #9ca3af;
3047
+ }
3048
+
3049
+ .squisq-editor-shell[data-theme='dark'] .squisq-find-field:focus-within {
3050
+ border-color: #60a5fa;
3051
+ box-shadow: 0 0 0 1px #60a5fa;
3052
+ }
3053
+
3054
+ .squisq-editor-shell[data-theme='dark'] .squisq-find-input {
3055
+ color: #f9fafb;
3056
+ }
3057
+
3058
+ .squisq-editor-shell[data-theme='dark'] .squisq-find-input::placeholder,
3059
+ .squisq-editor-shell[data-theme='dark'] .squisq-find-count {
3060
+ color: #9ca3af;
3061
+ }
3062
+
3063
+ .squisq-editor-shell[data-theme='dark'] .squisq-find-button:hover:not(:disabled) {
3064
+ background: rgba(255, 255, 255, 0.1);
3065
+ color: #f9fafb;
3066
+ }
3067
+
2718
3068
  .squisq-editor-shell[data-theme='dark'] .squisq-toolbar-view-tabs {
2719
3069
  background: #111827;
2720
3070
  border-right-color: rgba(255, 255, 255, 0.15);
@@ -12,6 +12,8 @@
12
12
  @import './image-edit-affordance.css';
13
13
  @import './diagram.css';
14
14
  @import './ascii-diagram.css';
15
+ @import './mermaid-diagram.css';
16
+ @import './code-snippet.css';
15
17
  @import './tree-view.css';
16
18
  @import './ascii-timeline.css';
17
19
  @import '../scene/scene.css';