@bendyline/squisq-editor-react 1.6.2 → 2.0.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 +12 -3
- package/dist/index.d.ts +1229 -179
- package/dist/index.js +18017 -11231
- package/dist/index.js.map +1 -1
- package/dist/monaco.d.ts +1 -0
- package/dist/monaco.js +4 -0
- package/dist/monaco.js.map +1 -0
- package/dist/styles/index.css +1471 -245
- package/package.json +9 -5
- package/src/BlockPropertiesPopover.tsx +30 -15
- package/src/DocumentSettingsDialog.tsx +13 -21
- package/src/EditorContext.tsx +199 -42
- package/src/EditorShell.tsx +348 -260
- package/src/ImageEditor.tsx +22 -1
- package/src/InlinePreviewGutter.tsx +16 -8
- package/src/MediaBin.tsx +107 -14
- package/src/OutlinePanel.tsx +202 -7
- package/src/PlainHtmlPreview.tsx +40 -1
- package/src/PreviewControls.tsx +309 -69
- package/src/PreviewPanel.tsx +312 -78
- package/src/RawEditor.tsx +13 -2
- package/src/StatusBar.tsx +5 -2
- package/src/TemplateAnnotation.ts +12 -3
- package/src/TemplatePicker.tsx +75 -21
- package/src/ThemeCustomizerPanel.tsx +8 -0
- package/src/ThemePicker.tsx +10 -5
- package/src/TimelineBlockPreview.tsx +1 -1
- package/src/Toolbar.tsx +202 -41
- package/src/TransitionPicker.tsx +36 -4
- package/src/ViewMenuPanel.tsx +12 -14
- package/src/WysiwygEditor.tsx +124 -91
- package/src/__tests__/blockPropertiesPopoverTheme.test.tsx +33 -0
- package/src/__tests__/blockTagActivity.test.ts +183 -0
- package/src/__tests__/buildDocumentPreviewMarkdown.test.ts +75 -0
- package/src/__tests__/buildPreviewDocContent.test.ts +48 -0
- package/src/__tests__/buildPreviewDocTransition.test.ts +21 -1
- package/src/__tests__/canvasSurfaceTextEditing.test.tsx +60 -0
- package/src/__tests__/customLayoutManagerTheme.test.tsx +34 -0
- package/src/__tests__/documentSettingsDialog.test.tsx +29 -1
- package/src/__tests__/editorScrollHandoff.test.tsx +102 -0
- package/src/__tests__/editorScrollSync.test.ts +65 -0
- package/src/__tests__/editorShellProps.test.tsx +123 -1
- package/src/__tests__/emojiPicker.test.tsx +2 -42
- package/src/__tests__/headingTransition.test.ts +0 -12
- package/src/__tests__/imageEditAffordance.test.tsx +5 -1
- package/src/__tests__/imageEditModalContract.test.tsx +119 -0
- package/src/__tests__/imageEditorShell.test.tsx +15 -1
- package/src/__tests__/jsonEditor.test.tsx +56 -1
- package/src/__tests__/layersPanel.test.tsx +75 -5
- package/src/__tests__/majorApiTypes.test.ts +26 -0
- package/src/__tests__/mediaBinDrop.test.tsx +90 -0
- package/src/__tests__/narrationSave.test.ts +148 -0
- package/src/__tests__/outlinePanel.test.tsx +93 -3
- package/src/__tests__/outlineSource.test.ts +217 -0
- package/src/__tests__/plainHtmlPreview.test.tsx +17 -1
- package/src/__tests__/presentationMode.test.tsx +290 -0
- package/src/__tests__/previewControls.test.tsx +218 -5
- package/src/__tests__/previewPanelAudioFailure.test.tsx +46 -0
- package/src/__tests__/previewPanelPresentation.test.tsx +126 -0
- package/src/__tests__/previewPanelTransforms.test.tsx +117 -0
- package/src/__tests__/rawEditorModelIsolation.test.ts +19 -0
- package/src/__tests__/resolveBlockVisual.test.ts +43 -0
- package/src/__tests__/statusBar.test.tsx +27 -0
- package/src/__tests__/teleprompter.test.tsx +190 -0
- package/src/__tests__/templateAnnotationRoundTrip.test.ts +26 -0
- package/src/__tests__/templatePickerPortal.test.tsx +62 -0
- package/src/__tests__/tiptapBridge.test.ts +23 -0
- package/src/__tests__/tiptapCodeBlockRoundTrip.test.ts +109 -0
- package/src/__tests__/transformStyleId.test.ts +13 -0
- package/src/__tests__/useImageEditor.test.tsx +67 -0
- package/src/__tests__/useMediaRecorder.test.ts +75 -0
- package/src/__tests__/viewMenuPanel.test.tsx +130 -0
- package/src/__tests__/wysiwygImageUpload.test.ts +69 -0
- package/src/__tests__/wysiwygTemplateBadgeFocus.test.tsx +56 -0
- package/src/asciiDiagram/AsciiDiagramExtension.ts +343 -0
- package/src/asciiDiagram/AsciiDiagramWidget.tsx +224 -0
- package/src/asciiDiagram/RepairableDiagramExtension.ts +200 -0
- package/src/asciiDiagram/__tests__/AsciiDiagramExtension.test.ts +145 -0
- package/src/asciiDiagram/__tests__/RepairableDiagramExtension.test.ts +159 -0
- package/src/asciiDiagram/__tests__/asciiDiagramCommands.test.ts +227 -0
- package/src/asciiDiagram/__tests__/asciiDiagramOps.test.ts +114 -0
- package/src/asciiDiagram/__tests__/asciiPaste.test.ts +43 -0
- package/src/asciiDiagram/asciiDiagramCommands.ts +148 -0
- package/src/asciiDiagram/asciiDiagramData.ts +116 -0
- package/src/asciiDiagram/asciiDiagramOps.ts +219 -0
- package/src/asciiDiagram/asciiPaste.ts +19 -0
- package/src/blockTagActivity.ts +233 -0
- package/src/buildDocumentPreviewMarkdown.ts +168 -0
- package/src/buildPreviewDoc.ts +19 -12
- package/src/customTemplates/CustomLayoutManager.tsx +3 -2
- package/src/customTemplates/CustomTemplateContext.tsx +6 -0
- package/src/customTemplates/TemplateDesigner.tsx +7 -4
- package/src/customTemplates/__tests__/library.test.ts +8 -0
- package/src/customTemplates/designer.css +158 -99
- package/src/customTemplates/library.ts +16 -3
- package/src/customThemes/CustomThemeContext.tsx +6 -0
- package/src/customThemes/CustomThemeDialog.tsx +35 -5
- package/src/customThemes/ImportThemeSection.tsx +178 -0
- package/src/customThemes/__tests__/customThemeLibrary.test.ts +9 -0
- package/src/customThemes/__tests__/importThemeSection.test.tsx +192 -0
- package/src/customThemes/customThemeLibrary.ts +7 -4
- package/src/customThemes/index.ts +7 -1
- package/src/customThemes/themeDraft.ts +23 -7
- package/src/diagram/DiagramCanvas.tsx +9 -4
- package/src/diagram/types.ts +35 -0
- package/src/editorScrollSync.ts +73 -0
- package/src/emojiData.ts +3 -23
- package/src/headingTransition.ts +0 -15
- package/src/imageEditor/CanvasSurface.tsx +29 -23
- package/src/imageEditor/LayersPanel.tsx +78 -3
- package/src/imageEditor/Toolbar.tsx +5 -1
- package/src/imageEditor/icons.tsx +4 -0
- package/src/imageEditor/image-editor.css +75 -0
- package/src/imageEditor/layers/SelectionHandles.tsx +1 -1
- package/src/imageEditor/useImageEditor.ts +71 -14
- package/src/index.ts +191 -19
- package/src/jsonEditor/JsonEditorContext.tsx +12 -6
- package/src/jsonEditor/RenderNode.tsx +24 -3
- package/src/jsonEditor/editors.tsx +86 -19
- package/src/mediaEntries.ts +12 -0
- package/src/monaco.ts +35 -0
- package/src/monacoWorkers.ts +89 -0
- package/src/outlineSource.ts +171 -0
- package/src/presentation/PresentationMode.tsx +596 -0
- package/src/rawEditorIsolation.ts +18 -0
- package/src/recorder/hooks/useMediaRecorder.ts +97 -53
- package/src/resolveBlockVisual.ts +10 -4
- package/src/scene/Scene.tsx +48 -18
- package/src/scene/SceneBlockExtension.ts +17 -10
- package/src/scene/SceneBlockWidget.tsx +5 -1
- package/src/scene/SceneViewport.tsx +7 -3
- package/src/scene/ShapePalette.tsx +17 -2
- package/src/scene/__tests__/DiagramAdapter.test.ts +86 -0
- package/src/scene/__tests__/SceneBlockExtension.test.ts +33 -0
- package/src/scene/__tests__/sceneIsolation.test.tsx +93 -0
- package/src/scene/adapters/DiagramAdapter.ts +12 -101
- package/src/scene/commands/SceneCommand.ts +4 -1
- package/src/scene/index.ts +1 -6
- package/src/scene/layers/DiagramEdges.tsx +21 -7
- package/src/scene/layers/edgeGeometry.ts +9 -1
- package/src/scene/layers/nodeCard.tsx +27 -8
- package/src/scene/scene.css +5 -2
- package/src/scene/text/SceneTextOverlay.tsx +5 -5
- package/src/scene/text/sceneTextChannel.ts +26 -20
- package/src/scene/text/sceneTextConfig.ts +4 -0
- package/src/scene/tools/ConnectTool.ts +13 -4
- package/src/scene/tools/SceneTool.ts +8 -0
- package/src/scene/tools/SelectTool.ts +34 -18
- package/src/scene/tools/ShapeTool.ts +2 -3
- package/src/styles/ascii-diagram.css +79 -0
- package/src/styles/ascii-timeline.css +499 -0
- package/src/styles/editor.css +706 -120
- package/src/styles/index.css +3 -0
- package/src/styles/tree-view.css +139 -0
- package/src/teleprompter/TeleprompterControls.tsx +218 -0
- package/src/teleprompter/TeleprompterSelfView.tsx +22 -0
- package/src/teleprompter/TeleprompterSurface.tsx +267 -0
- package/src/teleprompter/TeleprompterView.tsx +338 -0
- package/src/teleprompter/canvasRenderer.ts +161 -0
- package/src/teleprompter/floatingWindow.ts +352 -0
- package/src/teleprompter/index.ts +61 -0
- package/src/teleprompter/pcmWorklet.ts +62 -0
- package/src/teleprompter/recording/insertPreamble.ts +80 -0
- package/src/teleprompter/recording/narrationSave.ts +134 -0
- package/src/teleprompter/recording/useNarrationRecorder.ts +367 -0
- package/src/teleprompter/scrollModel.ts +85 -0
- package/src/teleprompter/teleprompterTheme.ts +303 -0
- package/src/teleprompter/types.ts +38 -0
- package/src/teleprompter/useFloatingWindow.ts +74 -0
- package/src/teleprompter/useMicAnalysis.ts +211 -0
- package/src/teleprompter/useTeleprompter.ts +421 -0
- package/src/templateContentPreviewResolver.ts +12 -5
- package/src/timeline/TimelineEditorWidget.tsx +702 -0
- package/src/timeline/TimelineViewExtension.ts +252 -0
- package/src/timeline/__tests__/TimelineEditorWidget.test.tsx +355 -0
- package/src/timeline/__tests__/TimelineViewExtension.test.ts +163 -0
- package/src/timeline/__tests__/timelineCommands.test.ts +327 -0
- package/src/timeline/__tests__/timelineOps.test.ts +222 -0
- package/src/timeline/__tests__/timelinePaste.test.ts +34 -0
- package/src/timeline/timelineCommands.ts +367 -0
- package/src/timeline/timelineData.ts +54 -0
- package/src/timeline/timelineOps.ts +277 -0
- package/src/timeline/timelinePaste.ts +8 -0
- package/src/tiptapBridge.ts +24 -16
- package/src/transformStyleId.ts +17 -0
- package/src/treeview/TreeOutlineWidget.tsx +240 -0
- package/src/treeview/TreeViewExtension.ts +250 -0
- package/src/treeview/__tests__/TreeViewExtension.test.ts +122 -0
- package/src/treeview/__tests__/treeOps.test.ts +125 -0
- package/src/treeview/__tests__/treePaste.test.ts +40 -0
- package/src/treeview/__tests__/treeViewCommands.test.ts +163 -0
- package/src/treeview/treeOps.ts +172 -0
- package/src/treeview/treePaste.ts +13 -0
- package/src/treeview/treeViewCommands.ts +94 -0
- package/src/treeview/treeViewData.ts +41 -0
- package/src/useMonacoLoader.ts +11 -34
- package/src/wysiwygImageUpload.ts +113 -0
- package/src/diagram/DiagramExtension.ts +0 -209
- package/src/diagram/DiagramWidget.tsx +0 -272
- package/src/diagram/useDiagramData.ts +0 -126
package/src/Toolbar.tsx
CHANGED
|
@@ -29,7 +29,7 @@ import { ViewMenuPanel } from './ViewMenuPanel';
|
|
|
29
29
|
import {
|
|
30
30
|
TemplatePicker,
|
|
31
31
|
TEMPLATE_NAMES,
|
|
32
|
-
|
|
32
|
+
TEMPLATE_GALLERY_PORTAL_SELECTOR,
|
|
33
33
|
templateLabel,
|
|
34
34
|
} from './TemplatePicker';
|
|
35
35
|
import { TransitionPicker, TRANSITION_FLYOUT_PORTAL_ID } from './TransitionPicker';
|
|
@@ -50,7 +50,8 @@ import { CustomLayoutManager } from './customTemplates/CustomLayoutManager';
|
|
|
50
50
|
import { Icon } from './Icon';
|
|
51
51
|
import type { PickerEntry } from './emojiData';
|
|
52
52
|
import { createPortal } from 'react-dom';
|
|
53
|
-
import { usePreviewSettingsOptional } from './PreviewControls';
|
|
53
|
+
import { PreviewModeMenu, displayModeLabel, usePreviewSettingsOptional } from './PreviewControls';
|
|
54
|
+
import { filterVisibleMediaEntries } from './mediaEntries';
|
|
54
55
|
|
|
55
56
|
const VIEWS: { id: EditorView; label: string; shortLabel?: string; shortcut: string }[] = [
|
|
56
57
|
{ id: 'wysiwyg', label: 'Write', shortcut: '⌘1' },
|
|
@@ -220,6 +221,22 @@ const BUTTONS: ToolbarButton[] = [
|
|
|
220
221
|
group: 'media',
|
|
221
222
|
faIcon: 'fa-solid fa-diagram-project',
|
|
222
223
|
},
|
|
224
|
+
{
|
|
225
|
+
id: 'tree',
|
|
226
|
+
label: 'tree',
|
|
227
|
+
icon: '',
|
|
228
|
+
title: 'Insert tree',
|
|
229
|
+
group: 'media',
|
|
230
|
+
faIcon: 'fa-solid fa-folder-tree',
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
id: 'timeline',
|
|
234
|
+
label: 'timeline',
|
|
235
|
+
icon: '',
|
|
236
|
+
title: 'Insert timeline',
|
|
237
|
+
group: 'media',
|
|
238
|
+
faIcon: 'fa-solid fa-timeline',
|
|
239
|
+
},
|
|
223
240
|
{
|
|
224
241
|
id: 'drawing',
|
|
225
242
|
label: 'drawing',
|
|
@@ -355,6 +372,79 @@ const LAYOUT_STARTER_TEXT_PARAMS =
|
|
|
355
372
|
/** Multi-line markdown for a new layout (raw / code views). */
|
|
356
373
|
const LAYOUT_STARTER_MARKDOWN = `\n## Layout {[layout]}\n\n### {#text-1} {[text ${LAYOUT_STARTER_TEXT_PARAMS}]}\n\nLayout\n`;
|
|
357
374
|
|
|
375
|
+
/**
|
|
376
|
+
* Starter art for a new diagram — diagrams are authored as ASCII-art code
|
|
377
|
+
* fences (the fence is the source of truth; `AsciiDiagramExtension` mounts
|
|
378
|
+
* the interactive canvas over it). This exact art is the codec's own
|
|
379
|
+
* canonical rendering of a two-node flow, so it re-parses and re-renders
|
|
380
|
+
* byte-stably.
|
|
381
|
+
*/
|
|
382
|
+
const DIAGRAM_STARTER_ART = [
|
|
383
|
+
'┌─────────┐',
|
|
384
|
+
'│ Start │',
|
|
385
|
+
'└────┬────┘',
|
|
386
|
+
' │',
|
|
387
|
+
' ▼',
|
|
388
|
+
'┌────┴────┐',
|
|
389
|
+
'│ Next │',
|
|
390
|
+
'└─────────┘',
|
|
391
|
+
].join('\n');
|
|
392
|
+
/**
|
|
393
|
+
* Fenced form for raw / code views — tagged with the explicit `diagram`
|
|
394
|
+
* language so the block's identity survives markdown ↔ Tiptap round-trips
|
|
395
|
+
* (the language class round-trips; fence meta does not).
|
|
396
|
+
*/
|
|
397
|
+
const DIAGRAM_STARTER_MARKDOWN = '\n```diagram\n' + DIAGRAM_STARTER_ART + '\n```\n';
|
|
398
|
+
|
|
399
|
+
/** Insert a starter ASCII-diagram code fence after the current top-level block. */
|
|
400
|
+
function insertAsciiDiagramBlock(editor: TiptapEditor): void {
|
|
401
|
+
insertFenceBlock(editor, DIAGRAM_STARTER_ART, 'diagram');
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Starter art for a new file tree — an ASCII tree fence (the fence is the
|
|
406
|
+
* source of truth; `TreeViewExtension` mounts the interactive outline).
|
|
407
|
+
*/
|
|
408
|
+
const TREE_STARTER_ART = ['src/', '├── index.ts', '└── utils/', ' └── helpers.ts'].join('\n');
|
|
409
|
+
const TREE_STARTER_MARKDOWN = '\n```tree\n' + TREE_STARTER_ART + '\n```\n';
|
|
410
|
+
|
|
411
|
+
/** Insert a starter ASCII tree code fence after the current top-level block. */
|
|
412
|
+
function insertTreeBlock(editor: TiptapEditor): void {
|
|
413
|
+
insertFenceBlock(editor, TREE_STARTER_ART, 'tree');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/** Starter multi-event rail for the authored timeline view. */
|
|
417
|
+
const TIMELINE_STARTER_ART =
|
|
418
|
+
'Milestones: ● Start {#start} ─────● Review {#review} ─────● Ship {#ship} ───►';
|
|
419
|
+
const TIMELINE_STARTER_MARKDOWN = '\n```timeline\n' + TIMELINE_STARTER_ART + '\n```\n';
|
|
420
|
+
|
|
421
|
+
/** Insert a starter ASCII timeline code fence after the current top-level block. */
|
|
422
|
+
function insertTimelineBlock(editor: TiptapEditor): void {
|
|
423
|
+
insertFenceBlock(editor, TIMELINE_STARTER_ART, 'timeline');
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Insert a code fence after the current top-level block. `lang` tags the
|
|
428
|
+
* fence's `language` attribute — pass the explicit authored-view tag so its
|
|
429
|
+
* identity round-trips through markdown and Tiptap.
|
|
430
|
+
*/
|
|
431
|
+
function insertFenceBlock(editor: TiptapEditor, art: string, lang?: string): void {
|
|
432
|
+
editor
|
|
433
|
+
.chain()
|
|
434
|
+
.focus()
|
|
435
|
+
.command(({ tr, state, dispatch }) => {
|
|
436
|
+
const codeBlockType = state.schema.nodes.codeBlock;
|
|
437
|
+
if (!codeBlockType) return false;
|
|
438
|
+
const attrs = lang ? { language: lang } : null;
|
|
439
|
+
const block = codeBlockType.create(attrs, state.schema.text(art));
|
|
440
|
+
const { $from } = state.selection;
|
|
441
|
+
const insertPos = $from.depth > 0 ? $from.after(1) : state.doc.content.size;
|
|
442
|
+
if (dispatch) tr.insert(insertPos, block);
|
|
443
|
+
return true;
|
|
444
|
+
})
|
|
445
|
+
.run();
|
|
446
|
+
}
|
|
447
|
+
|
|
358
448
|
/**
|
|
359
449
|
* Insert a block-level heading carrying a Scene template (`diagram` /
|
|
360
450
|
* `drawing` / `layout`) at the top level, after the block the caret sits
|
|
@@ -506,6 +596,7 @@ export function Toolbar({
|
|
|
506
596
|
bumpMediaRevision,
|
|
507
597
|
} = useEditorContext();
|
|
508
598
|
const previewSettings = usePreviewSettingsOptional();
|
|
599
|
+
const [useModeMenuRequest, requestUseModeMenu] = useReducer((count: number) => count + 1, 0);
|
|
509
600
|
// When a canvas textbox is being edited, its Tiptap instance takes over
|
|
510
601
|
// the formatting buttons; otherwise they drive the document editor. The
|
|
511
602
|
// `level` gates which buttons apply (inline labels vs. rich textboxes).
|
|
@@ -533,7 +624,7 @@ export function Toolbar({
|
|
|
533
624
|
let cancelled = false;
|
|
534
625
|
mediaProvider.listMedia().then(
|
|
535
626
|
(entries) => {
|
|
536
|
-
if (!cancelled) setScannedFileCount(entries.length);
|
|
627
|
+
if (!cancelled) setScannedFileCount(filterVisibleMediaEntries(entries).length);
|
|
537
628
|
},
|
|
538
629
|
() => {
|
|
539
630
|
if (!cancelled) setScannedFileCount(0);
|
|
@@ -682,23 +773,25 @@ export function Toolbar({
|
|
|
682
773
|
// with the cursor context and change width with their value, all without
|
|
683
774
|
// resizing the flex:1 container itself — a container-only observer would
|
|
684
775
|
// go stale.
|
|
685
|
-
const ro = new ResizeObserver(measure);
|
|
776
|
+
const ro = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(measure);
|
|
686
777
|
const observeAll = () => {
|
|
687
|
-
ro
|
|
688
|
-
ro
|
|
778
|
+
ro?.disconnect();
|
|
779
|
+
ro?.observe(container);
|
|
689
780
|
container
|
|
690
781
|
.querySelectorAll<HTMLElement>(':scope > .squisq-toolbar-group')
|
|
691
|
-
.forEach((group) => ro
|
|
782
|
+
.forEach((group) => ro?.observe(group));
|
|
692
783
|
};
|
|
693
784
|
const mo = new MutationObserver(() => {
|
|
694
785
|
observeAll();
|
|
695
786
|
measure();
|
|
696
787
|
});
|
|
697
788
|
mo.observe(container, { childList: true });
|
|
789
|
+
if (!ro) window.addEventListener('resize', measure);
|
|
698
790
|
observeAll();
|
|
699
791
|
measure();
|
|
700
792
|
return () => {
|
|
701
|
-
ro
|
|
793
|
+
ro?.disconnect();
|
|
794
|
+
if (!ro) window.removeEventListener('resize', measure);
|
|
702
795
|
mo.disconnect();
|
|
703
796
|
};
|
|
704
797
|
}, [activeView]);
|
|
@@ -714,7 +807,7 @@ export function Toolbar({
|
|
|
714
807
|
if (overflowRef.current?.contains(target)) return;
|
|
715
808
|
if (
|
|
716
809
|
target instanceof Element &&
|
|
717
|
-
target.closest(
|
|
810
|
+
target.closest(`${TEMPLATE_GALLERY_PORTAL_SELECTOR}, #${TRANSITION_FLYOUT_PORTAL_ID}`)
|
|
718
811
|
) {
|
|
719
812
|
return;
|
|
720
813
|
}
|
|
@@ -856,9 +949,19 @@ export function Toolbar({
|
|
|
856
949
|
insertTaskList(tiptapEditor);
|
|
857
950
|
break;
|
|
858
951
|
case 'diagram':
|
|
859
|
-
//
|
|
860
|
-
//
|
|
861
|
-
|
|
952
|
+
// Diagrams are ASCII-art code fences; the AsciiDiagramExtension
|
|
953
|
+
// mounts the interactive canvas over the inserted starter.
|
|
954
|
+
insertAsciiDiagramBlock(tiptapEditor);
|
|
955
|
+
break;
|
|
956
|
+
case 'tree':
|
|
957
|
+
// File trees are ASCII tree fences; TreeViewExtension mounts the
|
|
958
|
+
// interactive outline over the inserted starter.
|
|
959
|
+
insertTreeBlock(tiptapEditor);
|
|
960
|
+
break;
|
|
961
|
+
case 'timeline':
|
|
962
|
+
// The authored fence is immediately replaced by the WYSIWYG rail
|
|
963
|
+
// canvas; Preview uses the same events as vector layers.
|
|
964
|
+
insertTimelineBlock(tiptapEditor);
|
|
862
965
|
break;
|
|
863
966
|
case 'drawing':
|
|
864
967
|
// Empty drawing is usable via the canvas's "Shapes ▾" palette;
|
|
@@ -1015,9 +1118,19 @@ export function Toolbar({
|
|
|
1015
1118
|
break;
|
|
1016
1119
|
}
|
|
1017
1120
|
case 'diagram': {
|
|
1018
|
-
// A diagram is
|
|
1019
|
-
//
|
|
1020
|
-
replacement =
|
|
1121
|
+
// A diagram is an ASCII-art code fence; the WYSIWYG view
|
|
1122
|
+
// renders its editable canvas, preview renders it as a slide.
|
|
1123
|
+
replacement = DIAGRAM_STARTER_MARKDOWN;
|
|
1124
|
+
newCursorOffset = replacement.length;
|
|
1125
|
+
break;
|
|
1126
|
+
}
|
|
1127
|
+
case 'tree': {
|
|
1128
|
+
replacement = TREE_STARTER_MARKDOWN;
|
|
1129
|
+
newCursorOffset = replacement.length;
|
|
1130
|
+
break;
|
|
1131
|
+
}
|
|
1132
|
+
case 'timeline': {
|
|
1133
|
+
replacement = TIMELINE_STARTER_MARKDOWN;
|
|
1021
1134
|
newCursorOffset = replacement.length;
|
|
1022
1135
|
break;
|
|
1023
1136
|
}
|
|
@@ -1099,7 +1212,13 @@ export function Toolbar({
|
|
|
1099
1212
|
insertion = `\n${TASK_LIST_MARKDOWN}\n`;
|
|
1100
1213
|
break;
|
|
1101
1214
|
case 'diagram':
|
|
1102
|
-
insertion =
|
|
1215
|
+
insertion = DIAGRAM_STARTER_MARKDOWN;
|
|
1216
|
+
break;
|
|
1217
|
+
case 'tree':
|
|
1218
|
+
insertion = TREE_STARTER_MARKDOWN;
|
|
1219
|
+
break;
|
|
1220
|
+
case 'timeline':
|
|
1221
|
+
insertion = TIMELINE_STARTER_MARKDOWN;
|
|
1103
1222
|
break;
|
|
1104
1223
|
case 'drawing':
|
|
1105
1224
|
insertion = '\n## Drawing {[drawing]}\n';
|
|
@@ -1540,10 +1659,12 @@ export function Toolbar({
|
|
|
1540
1659
|
viewport: previewSettings?.activeViewport ?? VIEWPORT_PRESETS.landscape,
|
|
1541
1660
|
basePath: '/',
|
|
1542
1661
|
mediaProvider,
|
|
1662
|
+
customTemplates: doc?.customTemplates,
|
|
1543
1663
|
}
|
|
1544
1664
|
: undefined,
|
|
1545
1665
|
[
|
|
1546
1666
|
mediaProvider,
|
|
1667
|
+
doc?.customTemplates,
|
|
1547
1668
|
previewSettings?.activeTheme,
|
|
1548
1669
|
previewSettings?.activeViewport,
|
|
1549
1670
|
templatePreviewBlock,
|
|
@@ -1692,35 +1813,65 @@ export function Toolbar({
|
|
|
1692
1813
|
{/* View tabs — hidden when only one view is available (e.g. code mode). */}
|
|
1693
1814
|
{showViewTabs && (
|
|
1694
1815
|
<div className="squisq-toolbar-view-tabs" role="tablist" aria-label="Editor view">
|
|
1695
|
-
{visibleViews.map((view) =>
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1816
|
+
{visibleViews.map((view) => {
|
|
1817
|
+
const viewLabel =
|
|
1818
|
+
view.id === 'preview' && previewSettings
|
|
1819
|
+
? displayModeLabel(previewSettings.activeDisplayMode)
|
|
1820
|
+
: view.label;
|
|
1821
|
+
const tab = (
|
|
1822
|
+
<button
|
|
1823
|
+
role="tab"
|
|
1824
|
+
data-view={view.id}
|
|
1825
|
+
aria-selected={activeView === view.id}
|
|
1826
|
+
className={`squisq-toolbar-view-tab${activeView === view.id ? ' squisq-toolbar-view-tab--active' : ''}`}
|
|
1827
|
+
onClick={() => {
|
|
1828
|
+
if (view.id === 'preview' && activeView === 'preview' && previewSettings) {
|
|
1829
|
+
requestUseModeMenu();
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
setActiveView(view.id);
|
|
1833
|
+
}}
|
|
1834
|
+
data-tooltip={`${viewLabel} (${view.shortcut})`}
|
|
1708
1835
|
>
|
|
1709
|
-
{view.label}
|
|
1710
|
-
</span>
|
|
1711
|
-
{view.shortLabel && view.shortLabel !== view.label && (
|
|
1712
1836
|
<span
|
|
1713
|
-
className="squisq-toolbar-view-tab-label squisq-toolbar-view-tab-label--
|
|
1714
|
-
data-label={
|
|
1837
|
+
className="squisq-toolbar-view-tab-label squisq-toolbar-view-tab-label--long"
|
|
1838
|
+
data-label={viewLabel}
|
|
1715
1839
|
>
|
|
1716
|
-
{
|
|
1840
|
+
{viewLabel}
|
|
1717
1841
|
</span>
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1842
|
+
{view.shortLabel && view.shortLabel !== view.label && (
|
|
1843
|
+
<span
|
|
1844
|
+
className="squisq-toolbar-view-tab-label squisq-toolbar-view-tab-label--short"
|
|
1845
|
+
data-label={view.shortLabel}
|
|
1846
|
+
>
|
|
1847
|
+
{view.shortLabel}
|
|
1848
|
+
</span>
|
|
1849
|
+
)}
|
|
1850
|
+
</button>
|
|
1851
|
+
);
|
|
1852
|
+
|
|
1853
|
+
if (view.id !== 'preview' || !previewSettings) {
|
|
1854
|
+
return (
|
|
1855
|
+
<div key={view.id} className="squisq-toolbar-view-tab-wrap" role="presentation">
|
|
1856
|
+
{tab}
|
|
1857
|
+
</div>
|
|
1858
|
+
);
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
return (
|
|
1862
|
+
<div
|
|
1863
|
+
key={view.id}
|
|
1864
|
+
className={`squisq-toolbar-view-tab-wrap squisq-toolbar-use-tab${activeView === 'preview' ? ' squisq-toolbar-use-tab--active' : ''}`}
|
|
1865
|
+
role="presentation"
|
|
1866
|
+
>
|
|
1867
|
+
{tab}
|
|
1868
|
+
<PreviewModeMenu openRequest={useModeMenuRequest} />
|
|
1869
|
+
</div>
|
|
1870
|
+
);
|
|
1871
|
+
})}
|
|
1721
1872
|
</div>
|
|
1722
1873
|
)}
|
|
1723
|
-
{/* After-tabs slot — left side, before formatting
|
|
1874
|
+
{/* After-tabs slot — left side, before formatting or preview controls. */}
|
|
1724
1875
|
{slotAfterTabs}
|
|
1725
1876
|
{/* Formatting buttons — hidden in preview mode and code mode */}
|
|
1726
1877
|
{!isPreview && !isCodeMode && (
|
|
@@ -1803,7 +1954,12 @@ export function Toolbar({
|
|
|
1803
1954
|
data-contextual="transition"
|
|
1804
1955
|
>
|
|
1805
1956
|
<div className="squisq-toolbar-separator" />
|
|
1806
|
-
<TransitionPicker
|
|
1957
|
+
<TransitionPicker
|
|
1958
|
+
value={currentTransition}
|
|
1959
|
+
onChange={handleTransitionPick}
|
|
1960
|
+
colorScheme={colorScheme}
|
|
1961
|
+
accentColor={previewSettings?.activeTheme.colors.primary}
|
|
1962
|
+
/>
|
|
1807
1963
|
</div>
|
|
1808
1964
|
)}
|
|
1809
1965
|
|
|
@@ -2074,7 +2230,12 @@ export function Toolbar({
|
|
|
2074
2230
|
the flyout carries follow-up controls (direction/duration). */}
|
|
2075
2231
|
{showTransitionInOverflow && (
|
|
2076
2232
|
<div className="squisq-toolbar-overflow-item squisq-toolbar-overflow-template">
|
|
2077
|
-
<TransitionPicker
|
|
2233
|
+
<TransitionPicker
|
|
2234
|
+
value={currentTransition!}
|
|
2235
|
+
onChange={handleTransitionPick}
|
|
2236
|
+
colorScheme={colorScheme}
|
|
2237
|
+
accentColor={previewSettings?.activeTheme.colors.primary}
|
|
2238
|
+
/>
|
|
2078
2239
|
</div>
|
|
2079
2240
|
)}
|
|
2080
2241
|
|
package/src/TransitionPicker.tsx
CHANGED
|
@@ -30,6 +30,10 @@ import {
|
|
|
30
30
|
export interface TransitionPickerProps {
|
|
31
31
|
value: TransitionFields;
|
|
32
32
|
onChange: (next: TransitionFields) => void;
|
|
33
|
+
/** Surface scheme copied onto the portaled flyout so it can style independently of the shell. */
|
|
34
|
+
colorScheme?: 'light' | 'dark';
|
|
35
|
+
/** Active document-theme accent used for open and selected states. */
|
|
36
|
+
accentColor?: string;
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
/**
|
|
@@ -41,7 +45,12 @@ export const TRANSITION_FLYOUT_PORTAL_ID = 'squisq-transition-flyout-portal';
|
|
|
41
45
|
|
|
42
46
|
const FLYOUT_ID = TRANSITION_FLYOUT_PORTAL_ID;
|
|
43
47
|
|
|
44
|
-
export function TransitionPicker({
|
|
48
|
+
export function TransitionPicker({
|
|
49
|
+
value,
|
|
50
|
+
onChange,
|
|
51
|
+
colorScheme = 'light',
|
|
52
|
+
accentColor,
|
|
53
|
+
}: TransitionPickerProps) {
|
|
45
54
|
const [open, setOpen] = useState(false);
|
|
46
55
|
const [style, setStyle] = useState<React.CSSProperties>({});
|
|
47
56
|
const triggerRef = useRef<HTMLButtonElement>(null);
|
|
@@ -119,13 +128,22 @@ export function TransitionPicker({ value, onChange }: TransitionPickerProps) {
|
|
|
119
128
|
|
|
120
129
|
const flyout = open
|
|
121
130
|
? createPortal(
|
|
122
|
-
<TransitionFlyout
|
|
131
|
+
<TransitionFlyout
|
|
132
|
+
style={{ ...style, ...blockAccentStyle(accentColor) }}
|
|
133
|
+
value={value}
|
|
134
|
+
onChange={onChange}
|
|
135
|
+
colorScheme={colorScheme}
|
|
136
|
+
/>,
|
|
123
137
|
document.body,
|
|
124
138
|
)
|
|
125
139
|
: null;
|
|
126
140
|
|
|
127
141
|
return (
|
|
128
|
-
<div
|
|
142
|
+
<div
|
|
143
|
+
className="squisq-transition-picker"
|
|
144
|
+
data-theme={colorScheme}
|
|
145
|
+
style={blockAccentStyle(accentColor)}
|
|
146
|
+
>
|
|
129
147
|
<button
|
|
130
148
|
ref={triggerRef}
|
|
131
149
|
type="button"
|
|
@@ -158,10 +176,12 @@ function TransitionFlyout({
|
|
|
158
176
|
style,
|
|
159
177
|
value,
|
|
160
178
|
onChange,
|
|
179
|
+
colorScheme,
|
|
161
180
|
}: {
|
|
162
181
|
style: React.CSSProperties;
|
|
163
182
|
value: TransitionFields;
|
|
164
183
|
onChange: (next: TransitionFields) => void;
|
|
184
|
+
colorScheme: 'light' | 'dark';
|
|
165
185
|
}) {
|
|
166
186
|
const [query, setQuery] = useState('');
|
|
167
187
|
const searchRef = useRef<HTMLInputElement>(null);
|
|
@@ -211,7 +231,13 @@ function TransitionFlyout({
|
|
|
211
231
|
};
|
|
212
232
|
|
|
213
233
|
return (
|
|
214
|
-
<div
|
|
234
|
+
<div
|
|
235
|
+
id={FLYOUT_ID}
|
|
236
|
+
className="squisq-transition-flyout"
|
|
237
|
+
data-theme={colorScheme}
|
|
238
|
+
role="menu"
|
|
239
|
+
style={style}
|
|
240
|
+
>
|
|
215
241
|
<div className="squisq-transition-flyout-search">
|
|
216
242
|
<input
|
|
217
243
|
ref={searchRef}
|
|
@@ -313,6 +339,12 @@ function TransitionFlyout({
|
|
|
313
339
|
);
|
|
314
340
|
}
|
|
315
341
|
|
|
342
|
+
function blockAccentStyle(accentColor: string | undefined): React.CSSProperties {
|
|
343
|
+
return accentColor
|
|
344
|
+
? ({ ['--squisq-block-props-accent' as string]: accentColor } as React.CSSProperties)
|
|
345
|
+
: {};
|
|
346
|
+
}
|
|
347
|
+
|
|
316
348
|
function TransitionOption({
|
|
317
349
|
entry,
|
|
318
350
|
selected,
|
package/src/ViewMenuPanel.tsx
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* EditorShell `inlinePreview` prop.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
13
|
+
import { useCallback, useEffect, useId, useRef, useState } from 'react';
|
|
14
14
|
import { useEditorContext, type ThemeInheritance, type LayoutMode } from './EditorContext';
|
|
15
15
|
import { Icon } from './Icon';
|
|
16
16
|
|
|
@@ -22,8 +22,8 @@ export function ViewMenuPanel() {
|
|
|
22
22
|
setStatusBarVisible,
|
|
23
23
|
outlineVisible,
|
|
24
24
|
setOutlineVisible,
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
blockTagVisibility,
|
|
26
|
+
setBlockTagVisibility,
|
|
27
27
|
themeInheritance,
|
|
28
28
|
setThemeInheritance,
|
|
29
29
|
layoutMode,
|
|
@@ -56,10 +56,6 @@ export function ViewMenuPanel() {
|
|
|
56
56
|
setOutlineVisible(!outlineVisible);
|
|
57
57
|
}, [outlineVisible, setOutlineVisible]);
|
|
58
58
|
|
|
59
|
-
const toggleBlockTags = useCallback(() => {
|
|
60
|
-
setBlockTagsVisible(!blockTagsVisible);
|
|
61
|
-
}, [blockTagsVisible, setBlockTagsVisible]);
|
|
62
|
-
|
|
63
59
|
return (
|
|
64
60
|
<div className="squisq-view-menu" ref={containerRef}>
|
|
65
61
|
<button
|
|
@@ -79,7 +75,6 @@ export function ViewMenuPanel() {
|
|
|
79
75
|
<div className="squisq-view-menu-popover" role="menu" aria-label="View options">
|
|
80
76
|
<MenuRadioGroup
|
|
81
77
|
label="View"
|
|
82
|
-
name="layout-mode"
|
|
83
78
|
value={layoutMode}
|
|
84
79
|
onChange={(v) => setLayoutMode(v as LayoutMode)}
|
|
85
80
|
options={[
|
|
@@ -95,10 +90,15 @@ export function ViewMenuPanel() {
|
|
|
95
90
|
checked={inlinePreviewVisible}
|
|
96
91
|
onChange={toggleInlinePreview}
|
|
97
92
|
/>
|
|
98
|
-
<
|
|
93
|
+
<MenuRadioGroup
|
|
99
94
|
label="Show block tags"
|
|
100
|
-
|
|
101
|
-
onChange={
|
|
95
|
+
value={blockTagVisibility}
|
|
96
|
+
onChange={(v) => setBlockTagVisibility(v as typeof blockTagVisibility)}
|
|
97
|
+
options={[
|
|
98
|
+
{ value: 'none', label: 'No inline block tags' },
|
|
99
|
+
{ value: 'active', label: 'Selected/hovered block' },
|
|
100
|
+
{ value: 'always', label: 'Always show' },
|
|
101
|
+
]}
|
|
102
102
|
/>
|
|
103
103
|
<MenuToggle
|
|
104
104
|
label="Show status bar"
|
|
@@ -108,7 +108,6 @@ export function ViewMenuPanel() {
|
|
|
108
108
|
<div className="squisq-view-menu-separator" role="separator" />
|
|
109
109
|
<MenuRadioGroup
|
|
110
110
|
label="Editor styling from theme"
|
|
111
|
-
name="theme-inheritance"
|
|
112
111
|
value={themeInheritance}
|
|
113
112
|
onChange={(v) => setThemeInheritance(v as ThemeInheritance)}
|
|
114
113
|
options={[
|
|
@@ -147,17 +146,16 @@ function MenuToggle({
|
|
|
147
146
|
|
|
148
147
|
function MenuRadioGroup({
|
|
149
148
|
label,
|
|
150
|
-
name,
|
|
151
149
|
value,
|
|
152
150
|
onChange,
|
|
153
151
|
options,
|
|
154
152
|
}: {
|
|
155
153
|
label: string;
|
|
156
|
-
name: string;
|
|
157
154
|
value: string;
|
|
158
155
|
onChange: (next: string) => void;
|
|
159
156
|
options: readonly { value: string; label: string }[];
|
|
160
157
|
}) {
|
|
158
|
+
const name = useId();
|
|
161
159
|
return (
|
|
162
160
|
<div
|
|
163
161
|
className="squisq-view-menu-row squisq-view-menu-row--radios"
|