@bendyline/squisq-editor-react 2.0.1 → 2.2.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/LICENSE +21 -0
- package/NOTICE.md +40 -0
- package/README.md +10 -2
- package/dist/index.d.ts +683 -60
- package/dist/index.js +9648 -3750
- package/dist/monaco.js +0 -1
- package/dist/styles/index.css +1160 -241
- package/package.json +10 -6
- package/dist/index.js.map +0 -1
- package/dist/monaco.js.map +0 -1
- package/src/BlockCardView.tsx +0 -121
- package/src/BlockPreviewPanel.tsx +0 -69
- package/src/BlockPropertiesPopover.tsx +0 -222
- package/src/DocumentSettingsDialog.tsx +0 -272
- package/src/DropZoneOverlay.tsx +0 -137
- package/src/EditorContext.tsx +0 -1247
- package/src/EditorShell.tsx +0 -1334
- package/src/EmojiPicker.tsx +0 -332
- package/src/FolderView.tsx +0 -131
- package/src/Icon.tsx +0 -26
- package/src/ImageEditor.tsx +0 -395
- package/src/ImageNodeView.tsx +0 -284
- package/src/ImageViewer.tsx +0 -221
- package/src/InlineIcon.ts +0 -84
- package/src/InlinePreviewGutter.tsx +0 -590
- package/src/LinkDialog.tsx +0 -276
- package/src/MediaBin.tsx +0 -500
- package/src/MentionExtension.tsx +0 -261
- package/src/OutlinePanel.tsx +0 -547
- package/src/PlainHtmlPreview.tsx +0 -277
- package/src/PreviewControls.tsx +0 -1250
- package/src/PreviewPanel.tsx +0 -422
- package/src/RawEditor.tsx +0 -731
- package/src/RecorderEntry.tsx +0 -159
- package/src/StatusBar.tsx +0 -51
- package/src/TemplateAnnotation.ts +0 -167
- package/src/TemplateContentPreview.tsx +0 -56
- package/src/TemplatePicker.tsx +0 -1314
- package/src/ThemeCustomizerPanel.tsx +0 -304
- package/src/ThemePicker.tsx +0 -433
- package/src/TimelineBlockPreview.tsx +0 -37
- package/src/TimelineTrack.tsx +0 -671
- package/src/Toolbar.tsx +0 -2587
- package/src/Tooltip.tsx +0 -142
- package/src/TransitionPicker.tsx +0 -390
- package/src/VersionHistoryPanel.tsx +0 -359
- package/src/ViewMenuPanel.tsx +0 -189
- package/src/ViewSwitcher.tsx +0 -56
- package/src/WysiwygEditor.tsx +0 -792
- package/src/__tests__/blockProperties.test.ts +0 -92
- package/src/__tests__/blockPropertiesPopoverTheme.test.tsx +0 -33
- package/src/__tests__/blockRange.test.ts +0 -105
- package/src/__tests__/blockTagActivity.test.ts +0 -183
- package/src/__tests__/buildDocumentPreviewMarkdown.test.ts +0 -75
- package/src/__tests__/buildPreviewDocContent.test.ts +0 -48
- package/src/__tests__/buildPreviewDocTransition.test.ts +0 -92
- package/src/__tests__/canvasSurfaceTextEditing.test.tsx +0 -60
- package/src/__tests__/codeContextSectionView.test.tsx +0 -97
- package/src/__tests__/codeContextZoneManager.test.ts +0 -127
- package/src/__tests__/createShapeLayer.test.ts +0 -46
- package/src/__tests__/customLayoutManagerTheme.test.tsx +0 -34
- package/src/__tests__/detectMarkdown.test.ts +0 -73
- package/src/__tests__/diffContextSections.test.ts +0 -39
- package/src/__tests__/documentSettingsDialog.test.tsx +0 -197
- package/src/__tests__/drawingShapeRoundTrip.test.ts +0 -49
- package/src/__tests__/editorScrollHandoff.test.tsx +0 -102
- package/src/__tests__/editorScrollSync.test.ts +0 -65
- package/src/__tests__/editorShellCodeContext.test.tsx +0 -86
- package/src/__tests__/editorShellProps.test.tsx +0 -485
- package/src/__tests__/embeddedMedia.test.ts +0 -48
- package/src/__tests__/emojiPicker.test.tsx +0 -93
- package/src/__tests__/fileKind.test.ts +0 -112
- package/src/__tests__/headingTransition.test.ts +0 -176
- package/src/__tests__/imageEditAffordance.test.tsx +0 -272
- package/src/__tests__/imageEditModalContract.test.tsx +0 -119
- package/src/__tests__/imageEditorShell.test.tsx +0 -94
- package/src/__tests__/imageEditorState.test.ts +0 -171
- package/src/__tests__/inlinePreviewGutter.test.tsx +0 -62
- package/src/__tests__/inlinePreviewGutterAllBlocks.test.tsx +0 -103
- package/src/__tests__/jsonEditor.test.tsx +0 -223
- package/src/__tests__/layersPanel.test.tsx +0 -167
- package/src/__tests__/layoutChildRoundTrip.test.ts +0 -71
- package/src/__tests__/linkDialogDocPicker.test.tsx +0 -137
- package/src/__tests__/majorApiTypes.test.ts +0 -26
- package/src/__tests__/mediaAttachmentFlow.test.ts +0 -110
- package/src/__tests__/mediaBinDrop.test.tsx +0 -90
- package/src/__tests__/mediaReferences.test.ts +0 -82
- package/src/__tests__/narrationSave.test.ts +0 -148
- package/src/__tests__/outlinePanel.test.tsx +0 -169
- package/src/__tests__/outlineSource.test.ts +0 -217
- package/src/__tests__/plainHtmlPreview.test.tsx +0 -125
- package/src/__tests__/presentationMode.test.tsx +0 -290
- package/src/__tests__/previewControls.test.tsx +0 -540
- package/src/__tests__/previewPanelAudioFailure.test.tsx +0 -46
- package/src/__tests__/previewPanelPresentation.test.tsx +0 -126
- package/src/__tests__/previewPanelTransforms.test.tsx +0 -117
- package/src/__tests__/propertiesPanel.test.tsx +0 -69
- package/src/__tests__/rawEditorModelIsolation.test.ts +0 -19
- package/src/__tests__/recorderFormats.test.ts +0 -146
- package/src/__tests__/recorderMediaInsert.test.ts +0 -86
- package/src/__tests__/recorderTheme.test.tsx +0 -42
- package/src/__tests__/recorderTimingJson.test.ts +0 -41
- package/src/__tests__/resolveBlockVisual.test.ts +0 -43
- package/src/__tests__/selectionConversions.test.ts +0 -80
- package/src/__tests__/statusBar.test.tsx +0 -27
- package/src/__tests__/teleprompter.test.tsx +0 -190
- package/src/__tests__/templateAnnotationRoundTrip.test.ts +0 -99
- package/src/__tests__/templateContentPreview.test.ts +0 -101
- package/src/__tests__/templatePickerMetadata.test.ts +0 -32
- package/src/__tests__/templatePickerPortal.test.tsx +0 -62
- package/src/__tests__/timelineSource.test.ts +0 -134
- package/src/__tests__/tiptapBridge.test.ts +0 -566
- package/src/__tests__/tiptapBridgeConformance.test.ts +0 -47
- package/src/__tests__/tiptapCodeBlockRoundTrip.test.ts +0 -109
- package/src/__tests__/tiptapImageRoundTrip.test.ts +0 -73
- package/src/__tests__/toolbarSelectionConversion.test.tsx +0 -164
- package/src/__tests__/tooltip.test.tsx +0 -72
- package/src/__tests__/transformStyleId.test.ts +0 -13
- package/src/__tests__/transitionCatalog.test.ts +0 -64
- package/src/__tests__/useBlockNavigator.test.tsx +0 -67
- package/src/__tests__/useImageEditor.test.tsx +0 -226
- package/src/__tests__/useJsonEditorTokens.test.ts +0 -59
- package/src/__tests__/useMediaRecorder.test.ts +0 -302
- package/src/__tests__/useTimelineClock.test.ts +0 -21
- package/src/__tests__/versionHistory.test.tsx +0 -197
- package/src/__tests__/viewMenuPanel.test.tsx +0 -130
- package/src/__tests__/wysiwygImageUpload.test.ts +0 -69
- package/src/__tests__/wysiwygTemplateBadgeFocus.test.tsx +0 -56
- package/src/asciiDiagram/AsciiDiagramExtension.ts +0 -343
- package/src/asciiDiagram/AsciiDiagramWidget.tsx +0 -254
- package/src/asciiDiagram/RepairableDiagramExtension.ts +0 -200
- package/src/asciiDiagram/__tests__/AsciiDiagramExtension.test.ts +0 -145
- package/src/asciiDiagram/__tests__/RepairableDiagramExtension.test.ts +0 -159
- package/src/asciiDiagram/__tests__/asciiDiagramCommands.test.ts +0 -284
- package/src/asciiDiagram/__tests__/asciiDiagramOps.test.ts +0 -114
- package/src/asciiDiagram/__tests__/asciiPaste.test.ts +0 -43
- package/src/asciiDiagram/asciiDiagramCommands.ts +0 -174
- package/src/asciiDiagram/asciiDiagramData.ts +0 -149
- package/src/asciiDiagram/asciiDiagramOps.ts +0 -238
- package/src/asciiDiagram/asciiPaste.ts +0 -19
- package/src/blockProperties.ts +0 -88
- package/src/blockRange.ts +0 -132
- package/src/blockSlice.ts +0 -75
- package/src/blockTagActivity.ts +0 -233
- package/src/buildDocumentPreviewMarkdown.ts +0 -168
- package/src/buildPreviewDoc.ts +0 -405
- package/src/codeContext/CodeContextSectionView.tsx +0 -124
- package/src/codeContext/CodeContextZoneManager.ts +0 -149
- package/src/codeContext/CodeContextZones.tsx +0 -121
- package/src/codeContext/diffContextSections.ts +0 -38
- package/src/codeContext/types.ts +0 -75
- package/src/customTemplates/AddBin.tsx +0 -126
- package/src/customTemplates/CustomLayoutManager.tsx +0 -234
- package/src/customTemplates/CustomTemplateContext.tsx +0 -188
- package/src/customTemplates/LayerToolbar.tsx +0 -580
- package/src/customTemplates/ShapeGlyph.tsx +0 -47
- package/src/customTemplates/TemplateDesigner.tsx +0 -433
- package/src/customTemplates/__tests__/library.test.ts +0 -96
- package/src/customTemplates/__tests__/normalizePositions.test.ts +0 -109
- package/src/customTemplates/__tests__/shapeDefs.test.ts +0 -49
- package/src/customTemplates/__tests__/useMemoryLayerAdapter.test.ts +0 -108
- package/src/customTemplates/designer.css +0 -732
- package/src/customTemplates/index.ts +0 -31
- package/src/customTemplates/library.ts +0 -110
- package/src/customTemplates/normalizePositions.ts +0 -75
- package/src/customTemplates/shapeDefs.ts +0 -131
- package/src/customTemplates/thumbnail.tsx +0 -63
- package/src/customTemplates/tokenDefs.ts +0 -60
- package/src/customTemplates/useDocCustomTemplates.ts +0 -52
- package/src/customTemplates/useMemoryLayerAdapter.ts +0 -129
- package/src/customThemes/CustomThemeContext.tsx +0 -185
- package/src/customThemes/CustomThemeDialog.tsx +0 -316
- package/src/customThemes/ImportThemeSection.tsx +0 -178
- package/src/customThemes/__tests__/CustomThemeContext.test.tsx +0 -64
- package/src/customThemes/__tests__/CustomThemeDialog.test.tsx +0 -47
- package/src/customThemes/__tests__/customThemeLibrary.test.ts +0 -60
- package/src/customThemes/__tests__/importThemeSection.test.tsx +0 -192
- package/src/customThemes/customThemeLibrary.ts +0 -100
- package/src/customThemes/index.ts +0 -37
- package/src/customThemes/themeControls.tsx +0 -229
- package/src/customThemes/themeDraft.ts +0 -288
- package/src/customThemes/useDocCustomThemes.ts +0 -49
- package/src/detectMarkdown.ts +0 -62
- package/src/diagram/DiagramCanvas.tsx +0 -259
- package/src/diagram/DiagramMaximizedOverlay.tsx +0 -46
- package/src/diagram/diagramCommands.ts +0 -604
- package/src/diagram/diagramConstants.ts +0 -17
- package/src/diagram/types.ts +0 -35
- package/src/editorScrollSync.ts +0 -73
- package/src/embeddedMedia.ts +0 -78
- package/src/emojiData.ts +0 -1317
- package/src/fileKind.ts +0 -158
- package/src/frontmatter.ts +0 -29
- package/src/frontmatterSettings.ts +0 -23
- package/src/headingTransition.ts +0 -291
- package/src/hooks/useFileDrop.ts +0 -266
- package/src/imageEditor/CanvasSurface.tsx +0 -703
- package/src/imageEditor/ImageVersionHistoryDropdown.tsx +0 -396
- package/src/imageEditor/LayersPanel.tsx +0 -218
- package/src/imageEditor/PropertiesPanel.tsx +0 -474
- package/src/imageEditor/Toolbar.tsx +0 -459
- package/src/imageEditor/createShapeLayer.ts +0 -280
- package/src/imageEditor/icons.tsx +0 -68
- package/src/imageEditor/image-editor.css +0 -574
- package/src/imageEditor/layers/EditorImageLayer.tsx +0 -45
- package/src/imageEditor/layers/EditorShapeLayer.tsx +0 -62
- package/src/imageEditor/layers/EditorTextLayer.tsx +0 -45
- package/src/imageEditor/layers/SelectionHandles.tsx +0 -86
- package/src/imageEditor/state.ts +0 -173
- package/src/imageEditor/useImageEditor.ts +0 -385
- package/src/imageEditor/useImageEditorTokens.ts +0 -70
- package/src/index.ts +0 -470
- package/src/jsonEditor/EmbeddedRichTextField.tsx +0 -81
- package/src/jsonEditor/JsonEditor.tsx +0 -81
- package/src/jsonEditor/JsonEditorContext.tsx +0 -81
- package/src/jsonEditor/RenderNode.tsx +0 -87
- package/src/jsonEditor/editors.tsx +0 -745
- package/src/jsonEditor/index.ts +0 -2
- package/src/jsonEditor/json-editor.css +0 -463
- package/src/jsonEditor/useJsonEditorTokens.ts +0 -33
- package/src/mediaDragMime.ts +0 -32
- package/src/mediaEntries.ts +0 -12
- package/src/mediaReferences.ts +0 -299
- package/src/monaco.ts +0 -35
- package/src/monacoWorkers.ts +0 -89
- package/src/outlineSource.ts +0 -171
- package/src/presentation/PresentationMode.tsx +0 -596
- package/src/rawEditorIsolation.ts +0 -18
- package/src/recorder/RecorderButton.tsx +0 -80
- package/src/recorder/RecorderModal.tsx +0 -706
- package/src/recorder/RecorderPanel.tsx +0 -77
- package/src/recorder/formats.ts +0 -159
- package/src/recorder/hooks/useMediaRecorder.ts +0 -436
- package/src/recorder/hooks/useStreamPreview.ts +0 -47
- package/src/recorder/insertMediaBlock.ts +0 -30
- package/src/recorder/sources/cameraStream.ts +0 -32
- package/src/recorder/sources/micStream.ts +0 -25
- package/src/recorder/sources/screenStream.ts +0 -162
- package/src/recorder/timingJson.ts +0 -66
- package/src/resolveBlockVisual.ts +0 -39
- package/src/scene/Scene.tsx +0 -616
- package/src/scene/SceneBlockExtension.ts +0 -205
- package/src/scene/SceneBlockToolbar.tsx +0 -201
- package/src/scene/SceneBlockWidget.tsx +0 -441
- package/src/scene/ScenePropsBar.tsx +0 -85
- package/src/scene/SceneSelection.tsx +0 -111
- package/src/scene/SceneSideToolbar.tsx +0 -89
- package/src/scene/SceneViewport.tsx +0 -106
- package/src/scene/ShapePalette.tsx +0 -196
- package/src/scene/__tests__/DiagramAdapter.test.ts +0 -142
- package/src/scene/__tests__/SceneBlockExtension.test.ts +0 -33
- package/src/scene/__tests__/bezierEdit.test.ts +0 -85
- package/src/scene/__tests__/blockLayers.test.ts +0 -57
- package/src/scene/__tests__/sceneIsolation.test.tsx +0 -119
- package/src/scene/__tests__/shapeLayers.test.ts +0 -106
- package/src/scene/__tests__/useSceneHitTest.test.ts +0 -90
- package/src/scene/__tests__/useScenePanZoom.test.ts +0 -103
- package/src/scene/adapters/DiagramAdapter.ts +0 -79
- package/src/scene/adapters/DrawingAdapter.ts +0 -420
- package/src/scene/adapters/LayoutAdapter.ts +0 -313
- package/src/scene/adapters/blockLayers.ts +0 -159
- package/src/scene/commands/SceneCommand.ts +0 -84
- package/src/scene/commands/drawingCommands.ts +0 -318
- package/src/scene/commands/layoutCommands.ts +0 -301
- package/src/scene/hooks/useSceneHitTest.ts +0 -105
- package/src/scene/hooks/useScenePanZoom.ts +0 -147
- package/src/scene/hooks/useSceneSelection.ts +0 -62
- package/src/scene/index.ts +0 -90
- package/src/scene/layers/DiagramEdges.tsx +0 -142
- package/src/scene/layers/edgeGeometry.ts +0 -104
- package/src/scene/layers/nodeCard.tsx +0 -164
- package/src/scene/layers/renderLayer.tsx +0 -70
- package/src/scene/layers/shapeLayers.ts +0 -201
- package/src/scene/paths/bezierEdit.ts +0 -208
- package/src/scene/scene.css +0 -792
- package/src/scene/text/SceneTextOverlay.tsx +0 -161
- package/src/scene/text/sceneTextChannel.ts +0 -46
- package/src/scene/text/sceneTextConfig.ts +0 -31
- package/src/scene/text/sceneTiptap.ts +0 -36
- package/src/scene/text/useSceneTextEditing.ts +0 -39
- package/src/scene/tools/ConnectTool.ts +0 -209
- package/src/scene/tools/DrawingConnectTool.ts +0 -231
- package/src/scene/tools/PathTool.ts +0 -158
- package/src/scene/tools/PlaceTool.ts +0 -47
- package/src/scene/tools/SceneTool.ts +0 -85
- package/src/scene/tools/SelectTool.ts +0 -300
- package/src/scene/tools/ShapeTool.ts +0 -143
- package/src/scene/tools/TextTool.ts +0 -72
- package/src/scene/tools/TokenTool.ts +0 -95
- package/src/scene/tools/createDrawShapeTool.ts +0 -82
- package/src/selectionConversions.ts +0 -155
- package/src/styles/ascii-diagram.css +0 -79
- package/src/styles/ascii-timeline.css +0 -596
- package/src/styles/code-context.css +0 -155
- package/src/styles/diagram.css +0 -183
- package/src/styles/editor.css +0 -6811
- package/src/styles/folder-view.css +0 -210
- package/src/styles/image-edit-affordance.css +0 -201
- package/src/styles/index.css +0 -18
- package/src/styles/tree-view.css +0 -189
- package/src/teleprompter/TeleprompterControls.tsx +0 -218
- package/src/teleprompter/TeleprompterSelfView.tsx +0 -22
- package/src/teleprompter/TeleprompterSurface.tsx +0 -267
- package/src/teleprompter/TeleprompterView.tsx +0 -338
- package/src/teleprompter/canvasRenderer.ts +0 -161
- package/src/teleprompter/floatingWindow.ts +0 -352
- package/src/teleprompter/index.ts +0 -61
- package/src/teleprompter/pcmWorklet.ts +0 -62
- package/src/teleprompter/recording/insertPreamble.ts +0 -80
- package/src/teleprompter/recording/narrationSave.ts +0 -134
- package/src/teleprompter/recording/useNarrationRecorder.ts +0 -367
- package/src/teleprompter/scrollModel.ts +0 -85
- package/src/teleprompter/teleprompterTheme.ts +0 -303
- package/src/teleprompter/types.ts +0 -38
- package/src/teleprompter/useFloatingWindow.ts +0 -74
- package/src/teleprompter/useMicAnalysis.ts +0 -211
- package/src/teleprompter/useTeleprompter.ts +0 -421
- package/src/templateContentPreviewResolver.ts +0 -360
- package/src/timeline/TimelineEditorWidget.tsx +0 -861
- package/src/timeline/TimelineViewExtension.ts +0 -252
- package/src/timeline/__tests__/TimelineEditorWidget.test.tsx +0 -414
- package/src/timeline/__tests__/TimelineViewExtension.test.ts +0 -163
- package/src/timeline/__tests__/timelineCommands.test.ts +0 -359
- package/src/timeline/__tests__/timelineOps.test.ts +0 -277
- package/src/timeline/__tests__/timelinePaste.test.ts +0 -34
- package/src/timeline/timelineCommands.ts +0 -421
- package/src/timeline/timelineData.ts +0 -54
- package/src/timeline/timelineOps.ts +0 -381
- package/src/timeline/timelinePaste.ts +0 -8
- package/src/timelineSource.ts +0 -244
- package/src/tiptap/TiptapAudio.tsx +0 -86
- package/src/tiptap/TiptapVideo.tsx +0 -119
- package/src/tiptap/useResolvedMediaSrc.ts +0 -47
- package/src/tiptapBridge.ts +0 -986
- package/src/tooltipPlacement.ts +0 -13
- package/src/transformStyleId.ts +0 -17
- package/src/transitionCatalog.ts +0 -159
- package/src/treeview/TreeOutlineWidget.tsx +0 -390
- package/src/treeview/TreeViewExtension.ts +0 -250
- package/src/treeview/__tests__/TreeOutlineWidget.test.tsx +0 -156
- package/src/treeview/__tests__/TreeViewExtension.test.ts +0 -122
- package/src/treeview/__tests__/treeOps.test.ts +0 -177
- package/src/treeview/__tests__/treePaste.test.ts +0 -40
- package/src/treeview/__tests__/treeViewCommands.test.ts +0 -179
- package/src/treeview/treeOps.ts +0 -231
- package/src/treeview/treePaste.ts +0 -13
- package/src/treeview/treeViewCommands.ts +0 -99
- package/src/treeview/treeViewData.ts +0 -41
- package/src/types/monaco-shims.d.ts +0 -10
- package/src/useBlockNavigator.ts +0 -153
- package/src/useHeadingLayout.ts +0 -294
- package/src/useMonacoLoader.ts +0 -82
- package/src/useTimelineClock.ts +0 -76
- package/src/utils/collectInlineFontAwesomeCss.ts +0 -69
- package/src/utils/dropUtils.ts +0 -136
- package/src/utils/normalizeMalformedAssetUrl.ts +0 -22
- package/src/wysiwygImageUpload.ts +0 -113
package/src/useTimelineClock.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useTimelineClock
|
|
3
|
-
*
|
|
4
|
-
* A minimal real-time playback clock for the timeline view — a
|
|
5
|
-
* `requestAnimationFrame` loop that advances `currentTime` at wall-clock speed
|
|
6
|
-
* between `play()` and `pause()`, clamped to `[0, total]`. No audio element;
|
|
7
|
-
* media playback is driven separately (the timeline feeds `currentTime` to
|
|
8
|
-
* `MediaClipLayer`). Mirrors the fallback timer in `useAudioSync`.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
12
|
-
|
|
13
|
-
export interface TimelineClock {
|
|
14
|
-
/** Seconds from the start of the timeline. */
|
|
15
|
-
currentTime: number;
|
|
16
|
-
isPlaying: boolean;
|
|
17
|
-
/** Start playing; restarts from 0 when already at the end. */
|
|
18
|
-
play: () => void;
|
|
19
|
-
pause: () => void;
|
|
20
|
-
toggle: () => void;
|
|
21
|
-
/** Jump to a time (clamped to `[0, total]`). */
|
|
22
|
-
seek: (t: number) => void;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** Advance `prev` by `dt` seconds, clamped to `[0, total]`. Pure. */
|
|
26
|
-
export function advanceTime(prev: number, dt: number, total: number): number {
|
|
27
|
-
if (total <= 0) return 0;
|
|
28
|
-
return Math.min(total, Math.max(0, prev + dt));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function useTimelineClock(total: number): TimelineClock {
|
|
32
|
-
const [currentTime, setCurrentTime] = useState(0);
|
|
33
|
-
const [isPlaying, setIsPlaying] = useState(false);
|
|
34
|
-
const rafRef = useRef<number | null>(null);
|
|
35
|
-
const lastRef = useRef(0);
|
|
36
|
-
|
|
37
|
-
// Keep currentTime within the (possibly shrinking) timeline.
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
setCurrentTime((t) => Math.min(t, Math.max(0, total)));
|
|
40
|
-
}, [total]);
|
|
41
|
-
|
|
42
|
-
useEffect(() => {
|
|
43
|
-
if (!isPlaying) return;
|
|
44
|
-
lastRef.current = performance.now();
|
|
45
|
-
const tick = (now: number) => {
|
|
46
|
-
const dt = (now - lastRef.current) / 1000;
|
|
47
|
-
lastRef.current = now;
|
|
48
|
-
setCurrentTime((prev) => {
|
|
49
|
-
const next = advanceTime(prev, dt, total);
|
|
50
|
-
if (next >= total) {
|
|
51
|
-
setIsPlaying(false);
|
|
52
|
-
return total;
|
|
53
|
-
}
|
|
54
|
-
return next;
|
|
55
|
-
});
|
|
56
|
-
rafRef.current = requestAnimationFrame(tick);
|
|
57
|
-
};
|
|
58
|
-
rafRef.current = requestAnimationFrame(tick);
|
|
59
|
-
return () => {
|
|
60
|
-
if (rafRef.current != null) cancelAnimationFrame(rafRef.current);
|
|
61
|
-
};
|
|
62
|
-
}, [isPlaying, total]);
|
|
63
|
-
|
|
64
|
-
const play = useCallback(() => {
|
|
65
|
-
setCurrentTime((t) => (total > 0 && t >= total ? 0 : t));
|
|
66
|
-
setIsPlaying(true);
|
|
67
|
-
}, [total]);
|
|
68
|
-
const pause = useCallback(() => setIsPlaying(false), []);
|
|
69
|
-
const toggle = useCallback(() => setIsPlaying((p) => !p), []);
|
|
70
|
-
const seek = useCallback(
|
|
71
|
-
(t: number) => setCurrentTime(Math.min(Math.max(0, t), Math.max(0, total))),
|
|
72
|
-
[total],
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
return { currentTime, isPlaying, play, pause, toggle, seek };
|
|
76
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Collect FontAwesome `@font-face` + utility CSS rules from the host
|
|
3
|
-
* document's loaded stylesheets so we can inline them into a sandboxed
|
|
4
|
-
* iframe preview.
|
|
5
|
-
*
|
|
6
|
-
* Why: an iframe rendered via `srcDoc` cannot reliably load
|
|
7
|
-
* FontAwesome from a cross-origin CDN. Tracking prevention, stricter
|
|
8
|
-
* origin policies, and the iframe's about:srcdoc origin all conspire
|
|
9
|
-
* to silently drop font fetches. The icons end up invisible even
|
|
10
|
-
* though the `<i class="fa-…">` tags are present in the markup. The
|
|
11
|
-
* host page (editor-react ships a bundled `@import` of
|
|
12
|
-
* `@fortawesome/fontawesome-free`) already has the font loaded and its
|
|
13
|
-
* woff2 URLs resolve to same-origin assets, so we extract those rules
|
|
14
|
-
* once and pass them into the renderer as inline CSS.
|
|
15
|
-
*
|
|
16
|
-
* Returns `undefined` when no FA rules are found — typical for SSR or
|
|
17
|
-
* for hosts that don't bundle FA — in which case the renderer falls
|
|
18
|
-
* back to its cdnjs `<link>` (still works in plain standalone HTML).
|
|
19
|
-
*/
|
|
20
|
-
export function collectInlineFontAwesomeCss(): string | undefined {
|
|
21
|
-
if (typeof document === 'undefined') return undefined;
|
|
22
|
-
|
|
23
|
-
const collected: string[] = [];
|
|
24
|
-
|
|
25
|
-
for (const sheet of Array.from(document.styleSheets)) {
|
|
26
|
-
let rules: CSSRuleList | null = null;
|
|
27
|
-
try {
|
|
28
|
-
rules = sheet.cssRules;
|
|
29
|
-
} catch {
|
|
30
|
-
// Cross-origin stylesheets throw on `.cssRules` access. We can't
|
|
31
|
-
// read them, but they're also not where editor-react's bundled
|
|
32
|
-
// FA lives, so it's safe to skip them.
|
|
33
|
-
continue;
|
|
34
|
-
}
|
|
35
|
-
if (!rules) continue;
|
|
36
|
-
|
|
37
|
-
for (const rule of Array.from(rules)) {
|
|
38
|
-
if (isFontAwesomeRule(rule)) {
|
|
39
|
-
collected.push(rule.cssText);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return collected.length > 0 ? collected.join('\n') : undefined;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Match `@font-face` declarations whose family starts with
|
|
49
|
-
* "Font Awesome", plus the FA utility classes (the `.fa-*` rules that
|
|
50
|
-
* set `content: "\fXXX"` and font-family). The former gets the woff2
|
|
51
|
-
* loaded into the iframe, the latter wires the `<i>` markup to it.
|
|
52
|
-
*/
|
|
53
|
-
function isFontAwesomeRule(rule: CSSRule): boolean {
|
|
54
|
-
const text = rule.cssText;
|
|
55
|
-
// `@font-face { font-family: "Font Awesome 6 Brands"; … }`
|
|
56
|
-
if (rule.type === CSSRule.FONT_FACE_RULE) {
|
|
57
|
-
return /font-family:\s*['"]?Font Awesome/i.test(text);
|
|
58
|
-
}
|
|
59
|
-
// `.fa-github::before { content: "\f09b"; }` and the base
|
|
60
|
-
// `.fa-brands { font-family: "Font Awesome 6 Brands"; … }` rules.
|
|
61
|
-
// FA's stylesheet prefixes selectors with `.fa`, `.fas`, `.far`,
|
|
62
|
-
// `.fab`, `.fa-solid`, `.fa-regular`, `.fa-brands`. Match the
|
|
63
|
-
// family name in the declaration block to avoid false positives
|
|
64
|
-
// (e.g. someone else's `.fa-…` class that doesn't use the font).
|
|
65
|
-
if (rule.type === CSSRule.STYLE_RULE) {
|
|
66
|
-
return /Font Awesome/i.test(text) || /content:\s*["']\\[ef]/.test(text);
|
|
67
|
-
}
|
|
68
|
-
return false;
|
|
69
|
-
}
|
package/src/utils/dropUtils.ts
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Drop Utilities
|
|
3
|
-
*
|
|
4
|
-
* File processing pipeline for dropped files. Classifies files by type,
|
|
5
|
-
* processes media files into a MediaProvider, and converts text files
|
|
6
|
-
* (.md, .txt, .docx) to markdown strings.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { MediaProvider } from '@bendyline/squisq/schemas';
|
|
10
|
-
import { stringifyMarkdown } from '@bendyline/squisq/markdown';
|
|
11
|
-
import { classifyFile, type FileCategory } from '../hooks/useFileDrop';
|
|
12
|
-
|
|
13
|
-
export type { FileCategory };
|
|
14
|
-
export { classifyFile };
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Partition an array of files into media and text categories.
|
|
18
|
-
* Files with unknown type are skipped.
|
|
19
|
-
*/
|
|
20
|
-
export function partitionFiles(files: File[]): { media: File[]; text: File[] } {
|
|
21
|
-
const media: File[] = [];
|
|
22
|
-
const text: File[] = [];
|
|
23
|
-
|
|
24
|
-
for (const file of files) {
|
|
25
|
-
const cat = classifyFile(file);
|
|
26
|
-
if (cat === 'media') media.push(file);
|
|
27
|
-
else if (cat === 'text') text.push(file);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return { media, text };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Add media files to a MediaProvider. Returns the relative paths
|
|
35
|
-
* assigned by the provider, with `null` slots where a file could not
|
|
36
|
-
* be processed — keeping the result aligned with the input array so
|
|
37
|
-
* callers can correlate indices.
|
|
38
|
-
*
|
|
39
|
-
* Two failure modes are handled defensively:
|
|
40
|
-
*
|
|
41
|
-
* 1. `file.arrayBuffer()` throws (`InvalidStateError` — "An operation
|
|
42
|
-
* that depends on state cached in an interface object was made but
|
|
43
|
-
* the state had changed since it was read from disk"). This happens
|
|
44
|
-
* with virtual drag sources whose File reference goes stale before
|
|
45
|
-
* the async read completes — Phone Link / iOS continuity / certain
|
|
46
|
-
* screenshot tools / etc.
|
|
47
|
-
*
|
|
48
|
-
* 2. `file.arrayBuffer()` returns a 0-byte buffer. Some virtual
|
|
49
|
-
* sources resolve the read successfully but with no payload,
|
|
50
|
-
* leaving an empty file in the media bin. We skip those so the
|
|
51
|
-
* bin doesn't accumulate placeholders.
|
|
52
|
-
*
|
|
53
|
-
* In both cases we warn via console rather than throwing, so a single
|
|
54
|
-
* problematic file doesn't abort a multi-file drop.
|
|
55
|
-
*/
|
|
56
|
-
export async function processMediaFiles(
|
|
57
|
-
files: File[],
|
|
58
|
-
mediaProvider: MediaProvider,
|
|
59
|
-
): Promise<(string | null)[]> {
|
|
60
|
-
const paths: (string | null)[] = [];
|
|
61
|
-
|
|
62
|
-
for (const file of files) {
|
|
63
|
-
let buffer: ArrayBuffer;
|
|
64
|
-
try {
|
|
65
|
-
buffer = await file.arrayBuffer();
|
|
66
|
-
} catch (err: unknown) {
|
|
67
|
-
console.warn(
|
|
68
|
-
`[squisq-editor] Skipped dropped file "${file.name}" — could not read its contents.`,
|
|
69
|
-
'This is typical for drags from virtual sources (Phone Link, screenshot tools, cross-tab drags) whose File reference goes stale before the async read completes.',
|
|
70
|
-
err instanceof Error ? err.message : err,
|
|
71
|
-
);
|
|
72
|
-
paths.push(null);
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
if (buffer.byteLength === 0) {
|
|
77
|
-
console.warn(
|
|
78
|
-
`[squisq-editor] Skipped dropped file "${file.name}" — its contents read as 0 bytes. ` +
|
|
79
|
-
'The drag source likely never materialized the file (try saving it to disk first, then dragging from there).',
|
|
80
|
-
);
|
|
81
|
-
paths.push(null);
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const mimeType = file.type || 'application/octet-stream';
|
|
86
|
-
try {
|
|
87
|
-
const path = await mediaProvider.addMedia(file.name, buffer, mimeType);
|
|
88
|
-
paths.push(path);
|
|
89
|
-
} catch (err: unknown) {
|
|
90
|
-
console.warn(
|
|
91
|
-
`[squisq-editor] Failed to save "${file.name}" via mediaProvider:`,
|
|
92
|
-
err instanceof Error ? err.message : err,
|
|
93
|
-
);
|
|
94
|
-
paths.push(null);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return paths;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Read a text-content file and return its content as a markdown string.
|
|
103
|
-
*
|
|
104
|
-
* - `.md` and `.txt` files are read as UTF-8 text directly
|
|
105
|
-
* - `.docx` files are converted to markdown via `@bendyline/squisq-formats/docx`
|
|
106
|
-
*/
|
|
107
|
-
export async function processTextFile(file: File): Promise<string> {
|
|
108
|
-
const ext = file.name.split('.').pop()?.toLowerCase() ?? '';
|
|
109
|
-
|
|
110
|
-
if (ext === 'md' || ext === 'txt') {
|
|
111
|
-
return await file.text();
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (ext === 'docx') {
|
|
115
|
-
const buffer = await file.arrayBuffer();
|
|
116
|
-
const { docxToMarkdownDoc } = await import('@bendyline/squisq-formats/docx');
|
|
117
|
-
const markdownDoc = await docxToMarkdownDoc(buffer);
|
|
118
|
-
return stringifyMarkdown(markdownDoc);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return await file.text();
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Process multiple text files and concatenate their content.
|
|
126
|
-
*/
|
|
127
|
-
export async function processTextFiles(files: File[]): Promise<string> {
|
|
128
|
-
const results: string[] = [];
|
|
129
|
-
|
|
130
|
-
for (const file of files) {
|
|
131
|
-
const content = await processTextFile(file);
|
|
132
|
-
results.push(content);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return results.join('\n\n');
|
|
136
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Detect image URLs that were malformed by an MS "Save Page As Web Page" /
|
|
3
|
-
* pandoc import: a relative path like `mikehome_files/foo.png` gets
|
|
4
|
-
* `http://` prepended somewhere upstream, so the URL parser interprets
|
|
5
|
-
* `mikehome_files` as a hostname. The browser then either fails to load
|
|
6
|
-
* the image or has it blocked by CSP.
|
|
7
|
-
*
|
|
8
|
-
* Heuristic: hostname has no dots and no port, and ends in `_files` —
|
|
9
|
-
* that's the Word-style asset-folder naming convention, not a real host.
|
|
10
|
-
*
|
|
11
|
-
* Returns the recovered relative path (e.g. `mikehome_files/foo.png`)
|
|
12
|
-
* when the shape matches, or `null` otherwise.
|
|
13
|
-
*/
|
|
14
|
-
export function normalizeMalformedAssetUrl(src: string | null | undefined): string | null {
|
|
15
|
-
if (!src) return null;
|
|
16
|
-
const match = src.match(/^https?:\/\/([^/?#]+)\/(.+)$/i);
|
|
17
|
-
if (!match) return null;
|
|
18
|
-
const [, host, rest] = match;
|
|
19
|
-
if (host.includes('.') || host.includes(':')) return null;
|
|
20
|
-
if (!host.toLowerCase().endsWith('_files')) return null;
|
|
21
|
-
return `${host}/${rest}`;
|
|
22
|
-
}
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import type { MediaProvider } from '@bendyline/squisq/schemas';
|
|
2
|
-
import type { Node as ProseMirrorNode } from '@tiptap/pm/model';
|
|
3
|
-
import type { EditorView as ProseMirrorView } from '@tiptap/pm/view';
|
|
4
|
-
|
|
5
|
-
type ImageMutationView = Pick<ProseMirrorView, 'state' | 'dispatch'>;
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Upload images while stable placeholder nodes retain the original event
|
|
9
|
-
* position as the surrounding ProseMirror document continues to change.
|
|
10
|
-
*/
|
|
11
|
-
export async function uploadAndInsertImages(
|
|
12
|
-
view: ImageMutationView,
|
|
13
|
-
files: File[],
|
|
14
|
-
mediaProvider: MediaProvider,
|
|
15
|
-
onMediaUploaded?: () => void,
|
|
16
|
-
): Promise<void> {
|
|
17
|
-
const placeholders = files.map(() => `squisq-upload:${uniquePasteToken()}`);
|
|
18
|
-
const imageType = view.state.schema.nodes.image;
|
|
19
|
-
if (!imageType) return;
|
|
20
|
-
|
|
21
|
-
let placeholderTr = view.state.tr;
|
|
22
|
-
for (const token of placeholders) {
|
|
23
|
-
placeholderTr = placeholderTr.replaceSelectionWith(
|
|
24
|
-
imageType.create({
|
|
25
|
-
src: 'data:image/gif;base64,R0lGODlhAQABAAAAACw=',
|
|
26
|
-
alt: 'Uploading image',
|
|
27
|
-
title: token,
|
|
28
|
-
}),
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
view.dispatch(placeholderTr);
|
|
32
|
-
|
|
33
|
-
for (let index = 0; index < files.length; index++) {
|
|
34
|
-
const file = files[index];
|
|
35
|
-
const token = placeholders[index];
|
|
36
|
-
try {
|
|
37
|
-
const buffer = await file.arrayBuffer();
|
|
38
|
-
const mimeType = file.type || 'image/png';
|
|
39
|
-
const name =
|
|
40
|
-
file.name && file.name !== 'image.png'
|
|
41
|
-
? file.name
|
|
42
|
-
: `pasted-${uniquePasteToken()}.${extFromMime(mimeType)}`;
|
|
43
|
-
const relativePath = await mediaProvider.addMedia(name, buffer, mimeType);
|
|
44
|
-
const altText = name.replace(/\.[^.]+$/, '').replace(/[-_]/g, ' ');
|
|
45
|
-
replaceUploadPlaceholder(view, token, relativePath, altText);
|
|
46
|
-
onMediaUploaded?.();
|
|
47
|
-
} catch (err) {
|
|
48
|
-
removeUploadPlaceholder(view, token);
|
|
49
|
-
console.error('Failed to upload dropped image:', err);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function findUploadPlaceholder(
|
|
55
|
-
view: ImageMutationView,
|
|
56
|
-
token: string,
|
|
57
|
-
): { pos: number; node: ProseMirrorNode } | null {
|
|
58
|
-
let found: { pos: number; node: ProseMirrorNode } | null = null;
|
|
59
|
-
view.state.doc.descendants((node, pos) => {
|
|
60
|
-
if (node.type === view.state.schema.nodes.image && node.attrs.title === token) {
|
|
61
|
-
found = { pos, node };
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
return undefined;
|
|
65
|
-
});
|
|
66
|
-
return found;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function replaceUploadPlaceholder(
|
|
70
|
-
view: ImageMutationView,
|
|
71
|
-
token: string,
|
|
72
|
-
src: string,
|
|
73
|
-
alt: string,
|
|
74
|
-
): void {
|
|
75
|
-
const found = findUploadPlaceholder(view, token);
|
|
76
|
-
if (!found) return;
|
|
77
|
-
view.dispatch(
|
|
78
|
-
view.state.tr.setNodeMarkup(found.pos, undefined, {
|
|
79
|
-
...found.node.attrs,
|
|
80
|
-
src,
|
|
81
|
-
alt,
|
|
82
|
-
title: null,
|
|
83
|
-
}),
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function removeUploadPlaceholder(view: ImageMutationView, token: string): void {
|
|
88
|
-
const found = findUploadPlaceholder(view, token);
|
|
89
|
-
if (!found) return;
|
|
90
|
-
view.dispatch(view.state.tr.delete(found.pos, found.pos + found.node.nodeSize));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
let pasteCounter = 0;
|
|
94
|
-
function uniquePasteToken(): string {
|
|
95
|
-
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
96
|
-
return crypto.randomUUID();
|
|
97
|
-
}
|
|
98
|
-
pasteCounter = (pasteCounter + 1) % 1_000_000;
|
|
99
|
-
return `${Date.now()}-${pasteCounter.toString(36)}`;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function extFromMime(mime: string): string {
|
|
103
|
-
const map: Record<string, string> = {
|
|
104
|
-
'image/png': 'png',
|
|
105
|
-
'image/jpeg': 'jpg',
|
|
106
|
-
'image/jpg': 'jpg',
|
|
107
|
-
'image/gif': 'gif',
|
|
108
|
-
'image/webp': 'webp',
|
|
109
|
-
'image/svg+xml': 'svg',
|
|
110
|
-
'image/avif': 'avif',
|
|
111
|
-
};
|
|
112
|
-
return map[mime.toLowerCase()] ?? 'png';
|
|
113
|
-
}
|