@bendyline/squisq-editor-react 1.0.1 → 1.1.1
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/dist/DropZoneOverlay.d.ts +24 -0
- package/dist/DropZoneOverlay.d.ts.map +1 -0
- package/dist/DropZoneOverlay.js +53 -0
- package/dist/DropZoneOverlay.js.map +1 -0
- package/dist/EditorContext.d.ts +10 -2
- package/dist/EditorContext.d.ts.map +1 -1
- package/dist/EditorContext.js +49 -1
- package/dist/EditorContext.js.map +1 -1
- package/dist/EditorShell.d.ts +16 -1
- package/dist/EditorShell.d.ts.map +1 -1
- package/dist/EditorShell.js +55 -8
- package/dist/EditorShell.js.map +1 -1
- package/dist/ImageNodeView.d.ts +15 -0
- package/dist/ImageNodeView.d.ts.map +1 -0
- package/dist/ImageNodeView.js +52 -0
- package/dist/ImageNodeView.js.map +1 -0
- package/dist/MediaBin.d.ts +18 -0
- package/dist/MediaBin.d.ts.map +1 -0
- package/dist/MediaBin.js +141 -0
- package/dist/MediaBin.js.map +1 -0
- package/dist/PreviewControls.d.ts +41 -0
- package/dist/PreviewControls.d.ts.map +1 -0
- package/dist/PreviewControls.js +201 -0
- package/dist/PreviewControls.js.map +1 -0
- package/dist/PreviewPanel.d.ts +7 -7
- package/dist/PreviewPanel.d.ts.map +1 -1
- package/dist/PreviewPanel.js +183 -199
- package/dist/PreviewPanel.js.map +1 -1
- package/dist/Toolbar.d.ts +12 -1
- package/dist/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar.js +4 -12
- package/dist/Toolbar.js.map +1 -1
- package/dist/WysiwygEditor.d.ts.map +1 -1
- package/dist/WysiwygEditor.js +3 -1
- package/dist/WysiwygEditor.js.map +1 -1
- package/dist/hooks/useFileDrop.d.ts +41 -0
- package/dist/hooks/useFileDrop.d.ts.map +1 -0
- package/dist/hooks/useFileDrop.js +167 -0
- package/dist/hooks/useFileDrop.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/tiptapBridge.d.ts.map +1 -1
- package/dist/tiptapBridge.js +4 -5
- package/dist/tiptapBridge.js.map +1 -1
- package/dist/utils/dropUtils.d.ts +36 -0
- package/dist/utils/dropUtils.d.ts.map +1 -0
- package/dist/utils/dropUtils.js +71 -0
- package/dist/utils/dropUtils.js.map +1 -0
- package/package.json +5 -3
- package/src/DropZoneOverlay.tsx +137 -0
- package/src/EditorContext.tsx +64 -1
- package/src/EditorShell.tsx +153 -20
- package/src/ImageNodeView.tsx +70 -0
- package/src/MediaBin.tsx +223 -0
- package/src/PreviewControls.tsx +340 -0
- package/src/PreviewPanel.tsx +216 -287
- package/src/Toolbar.tsx +40 -3
- package/src/WysiwygEditor.tsx +3 -1
- package/src/hooks/useFileDrop.ts +226 -0
- package/src/index.ts +29 -0
- package/src/styles/editor.css +349 -8
- package/src/tiptapBridge.ts +5 -6
- package/src/utils/dropUtils.ts +88 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DropZoneOverlay
|
|
3
|
+
*
|
|
4
|
+
* Full-editor overlay that appears when files are dragged over the editor.
|
|
5
|
+
* Shows contextual drop zones depending on the type of files being dragged:
|
|
6
|
+
* - Media files → single "Media" drop zone
|
|
7
|
+
* - Text files → two zones: "Insert" (at cursor) and "Replace" (all content)
|
|
8
|
+
* - Mixed files → all three zones
|
|
9
|
+
*/
|
|
10
|
+
import type { DragContentType, UseFileDropResult } from './hooks/useFileDrop';
|
|
11
|
+
export interface DropZoneOverlayProps {
|
|
12
|
+
/** What kind of content is being dragged */
|
|
13
|
+
dragContentType: DragContentType;
|
|
14
|
+
/** Factory that creates event props for a specific drop target */
|
|
15
|
+
zoneProps: UseFileDropResult['zoneProps'];
|
|
16
|
+
/** Whether a MediaProvider is available (disables media zone when false) */
|
|
17
|
+
hasMediaProvider: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Full-size overlay with contextual drop targets for file uploads.
|
|
21
|
+
* Rendered conditionally by EditorShell when files are dragged over the editor.
|
|
22
|
+
*/
|
|
23
|
+
export declare function DropZoneOverlay({ dragContentType, zoneProps, hasMediaProvider, }: DropZoneOverlayProps): import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
//# sourceMappingURL=DropZoneOverlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DropZoneOverlay.d.ts","sourceRoot":"","sources":["../src/DropZoneOverlay.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAc,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE1F,MAAM,WAAW,oBAAoB;IACnC,4CAA4C;IAC5C,eAAe,EAAE,eAAe,CAAC;IACjC,kEAAkE;IAClE,SAAS,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC1C,4EAA4E;IAC5E,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAC9B,eAAe,EACf,SAAS,EACT,gBAAgB,GACjB,EAAE,oBAAoB,2CAyCtB"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* DropZoneOverlay
|
|
4
|
+
*
|
|
5
|
+
* Full-editor overlay that appears when files are dragged over the editor.
|
|
6
|
+
* Shows contextual drop zones depending on the type of files being dragged:
|
|
7
|
+
* - Media files → single "Media" drop zone
|
|
8
|
+
* - Text files → two zones: "Insert" (at cursor) and "Replace" (all content)
|
|
9
|
+
* - Mixed files → all three zones
|
|
10
|
+
*/
|
|
11
|
+
import { useState } from 'react';
|
|
12
|
+
/**
|
|
13
|
+
* Full-size overlay with contextual drop targets for file uploads.
|
|
14
|
+
* Rendered conditionally by EditorShell when files are dragged over the editor.
|
|
15
|
+
*/
|
|
16
|
+
export function DropZoneOverlay({ dragContentType, zoneProps, hasMediaProvider, }) {
|
|
17
|
+
const showMedia = dragContentType === 'media' || dragContentType === 'mixed';
|
|
18
|
+
const showText = dragContentType === 'text' || dragContentType === 'mixed';
|
|
19
|
+
return (_jsx("div", { className: "squisq-drop-overlay", children: _jsxs("div", { className: "squisq-drop-overlay-inner", children: [showMedia && (_jsx(DropZone, { target: "media", zoneProps: zoneProps, icon: "\uD83D\uDCF7", label: "Media", description: hasMediaProvider ? 'Add to file bin' : 'No file storage configured', disabled: !hasMediaProvider, variant: "media" })), showText && (_jsxs(_Fragment, { children: [_jsx(DropZone, { target: "insert", zoneProps: zoneProps, icon: "\uD83D\uDCCB", label: "Insert", description: "Insert content at cursor", variant: "insert" }), _jsx(DropZone, { target: "replace", zoneProps: zoneProps, icon: "\uD83D\uDD04", label: "Replace", description: "Replace all editor content", variant: "replace" })] }))] }) }));
|
|
20
|
+
}
|
|
21
|
+
function DropZone({ target, zoneProps, icon, label, description, disabled, variant, }) {
|
|
22
|
+
const [isHovering, setIsHovering] = useState(false);
|
|
23
|
+
const props = zoneProps(target);
|
|
24
|
+
return (_jsxs("div", { className: [
|
|
25
|
+
'squisq-drop-zone',
|
|
26
|
+
`squisq-drop-zone--${variant}`,
|
|
27
|
+
isHovering && !disabled ? 'squisq-drop-zone--active' : '',
|
|
28
|
+
disabled ? 'squisq-drop-zone--disabled' : '',
|
|
29
|
+
]
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.join(' '), onDragOver: (e) => {
|
|
32
|
+
if (disabled) {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
props.onDragOver(e);
|
|
37
|
+
}, onDragEnter: (e) => {
|
|
38
|
+
e.preventDefault();
|
|
39
|
+
if (!disabled)
|
|
40
|
+
setIsHovering(true);
|
|
41
|
+
}, onDragLeave: (e) => {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
setIsHovering(false);
|
|
44
|
+
}, onDrop: (e) => {
|
|
45
|
+
setIsHovering(false);
|
|
46
|
+
if (disabled) {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
props.onDrop(e);
|
|
51
|
+
}, children: [_jsx("span", { className: "squisq-drop-zone-icon", children: icon }), _jsx("span", { className: "squisq-drop-zone-label", children: label }), _jsx("span", { className: "squisq-drop-zone-desc", children: description })] }));
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=DropZoneOverlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DropZoneOverlay.js","sourceRoot":"","sources":["../src/DropZoneOverlay.tsx"],"names":[],"mappings":"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"}
|
package/dist/EditorContext.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* and Doc so all views stay in sync.
|
|
8
8
|
*/
|
|
9
9
|
import { type ReactNode } from 'react';
|
|
10
|
-
import type { Doc } from '@bendyline/squisq/schemas';
|
|
10
|
+
import type { Doc, MediaProvider } from '@bendyline/squisq/schemas';
|
|
11
11
|
import type { MarkdownDocument } from '@bendyline/squisq/markdown';
|
|
12
12
|
import type { Editor as TiptapEditor } from '@tiptap/core';
|
|
13
13
|
import type { editor as MonacoEditorNs } from 'monaco-editor';
|
|
@@ -44,12 +44,18 @@ export interface EditorActions {
|
|
|
44
44
|
setMonacoEditor: (editor: MonacoEditor | null) => void;
|
|
45
45
|
/** Set the color theme */
|
|
46
46
|
setTheme: (theme: EditorTheme) => void;
|
|
47
|
+
/** Insert text at the current cursor position in the active editor */
|
|
48
|
+
insertAtCursor: (text: string) => void;
|
|
49
|
+
/** Replace all editor content with the given text */
|
|
50
|
+
replaceAll: (text: string) => void;
|
|
47
51
|
}
|
|
48
52
|
export interface EditorContextValue extends EditorState, EditorActions {
|
|
49
53
|
/** The live Tiptap editor instance (null when WYSIWYG is not mounted) */
|
|
50
54
|
tiptapEditor: TiptapEditor | null;
|
|
51
55
|
/** The live Monaco editor instance (null when Raw is not mounted) */
|
|
52
56
|
monacoEditor: MonacoEditor | null;
|
|
57
|
+
/** MediaProvider for resolving image URLs in the WYSIWYG editor */
|
|
58
|
+
mediaProvider: MediaProvider | null;
|
|
53
59
|
}
|
|
54
60
|
/**
|
|
55
61
|
* Hook to access the editor context. Must be used within an EditorProvider.
|
|
@@ -64,12 +70,14 @@ export interface EditorProviderProps {
|
|
|
64
70
|
articleId?: string;
|
|
65
71
|
/** Color theme */
|
|
66
72
|
theme?: EditorTheme;
|
|
73
|
+
/** MediaProvider for resolving image URLs */
|
|
74
|
+
mediaProvider?: MediaProvider | null;
|
|
67
75
|
children: ReactNode;
|
|
68
76
|
}
|
|
69
77
|
/**
|
|
70
78
|
* Provides shared editor state to all child components.
|
|
71
79
|
* Automatically parses markdown and generates a Doc whenever the source changes.
|
|
72
80
|
*/
|
|
73
|
-
export declare function EditorProvider({ initialMarkdown, initialView, articleId, theme: initialTheme, children, }: EditorProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
81
|
+
export declare function EditorProvider({ initialMarkdown, initialView, articleId, theme: initialTheme, mediaProvider, children, }: EditorProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
74
82
|
export {};
|
|
75
83
|
//# sourceMappingURL=EditorContext.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorContext.d.ts","sourceRoot":"","sources":["../src/EditorContext.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"EditorContext.d.ts","sourceRoot":"","sources":["../src/EditorContext.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAGnE,OAAO,KAAK,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,IAAI,cAAc,EAAE,MAAM,eAAe,CAAC;AAG9D,kDAAkD;AAClD,KAAK,YAAY,GAAG,cAAc,CAAC,qBAAqB,CAAC;AAIzD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,CAAC;AACvD,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3C,MAAM,WAAW,WAAW;IAC1B,iCAAiC;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,WAAW,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACrC,sCAAsC;IACtC,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IAChB,mCAAmC;IACnC,UAAU,EAAE,UAAU,CAAC;IACvB,0BAA0B;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iCAAiC;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,0BAA0B;IAC1B,KAAK,EAAE,WAAW,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,+CAA+C;IAC/C,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,+DAA+D;IAC/D,cAAc,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAChD,6BAA6B;IAC7B,aAAa,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;IAC1C,iFAAiF;IACjF,eAAe,EAAE,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,KAAK,IAAI,CAAC;IACvD,6EAA6E;IAC7E,eAAe,EAAE,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,KAAK,IAAI,CAAC;IACvD,0BAA0B;IAC1B,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACvC,sEAAsE;IACtE,cAAc,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,qDAAqD;IACrD,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,kBAAmB,SAAQ,WAAW,EAAE,aAAa;IACpE,yEAAyE;IACzE,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,qEAAqE;IACrE,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAClC,mEAAmE;IACnE,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;CACrC;AAMD;;GAEG;AAEH,wBAAgB,gBAAgB,IAAI,kBAAkB,CAMrD;AAID,MAAM,WAAW,mBAAmB;IAClC,+BAA+B;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0BAA0B;IAC1B,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB;IAClB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,6CAA6C;IAC7C,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,eAAoB,EACpB,WAAmB,EACnB,SAAsB,EACtB,KAAK,EAAE,YAAsB,EAC7B,aAAoB,EACpB,QAAQ,GACT,EAAE,mBAAmB,2CA8LrB"}
|
package/dist/EditorContext.js
CHANGED
|
@@ -10,6 +10,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
10
10
|
import { createContext, useContext, useState, useCallback, useMemo, useRef, useEffect, } from 'react';
|
|
11
11
|
import { parseMarkdown, stringifyMarkdown } from '@bendyline/squisq/markdown';
|
|
12
12
|
import { markdownToDoc } from '@bendyline/squisq/doc';
|
|
13
|
+
import { markdownToTiptap } from './tiptapBridge';
|
|
13
14
|
// ─── Context ─────────────────────────────────────────────
|
|
14
15
|
const EditorContext = createContext(null);
|
|
15
16
|
/**
|
|
@@ -27,7 +28,7 @@ export function useEditorContext() {
|
|
|
27
28
|
* Provides shared editor state to all child components.
|
|
28
29
|
* Automatically parses markdown and generates a Doc whenever the source changes.
|
|
29
30
|
*/
|
|
30
|
-
export function EditorProvider({ initialMarkdown = '', initialView = 'raw', articleId = 'untitled', theme: initialTheme = 'light', children, }) {
|
|
31
|
+
export function EditorProvider({ initialMarkdown = '', initialView = 'raw', articleId = 'untitled', theme: initialTheme = 'light', mediaProvider = null, children, }) {
|
|
31
32
|
const [markdownSource, setMarkdownSourceRaw] = useState(initialMarkdown);
|
|
32
33
|
const [markdownDoc, setMarkdownDocState] = useState(null);
|
|
33
34
|
const [doc, setDoc] = useState(null);
|
|
@@ -97,6 +98,47 @@ export function EditorProvider({ initialMarkdown = '', initialView = 'raw', arti
|
|
|
97
98
|
const setMarkdownSource = useCallback((source) => {
|
|
98
99
|
setMarkdownSourceRaw(source);
|
|
99
100
|
}, []);
|
|
101
|
+
const insertAtCursor = useCallback((text) => {
|
|
102
|
+
if (activeView === 'wysiwyg' && tiptapEditor) {
|
|
103
|
+
// Insert as HTML so formatting is preserved
|
|
104
|
+
const html = markdownToTiptap(text);
|
|
105
|
+
tiptapEditor.chain().focus().insertContent(html).run();
|
|
106
|
+
}
|
|
107
|
+
else if (activeView === 'raw' && monacoEditor) {
|
|
108
|
+
const position = monacoEditor.getPosition();
|
|
109
|
+
if (position) {
|
|
110
|
+
const model = monacoEditor.getModel();
|
|
111
|
+
if (model) {
|
|
112
|
+
const range = {
|
|
113
|
+
startLineNumber: position.lineNumber,
|
|
114
|
+
startColumn: position.column,
|
|
115
|
+
endLineNumber: position.lineNumber,
|
|
116
|
+
endColumn: position.column,
|
|
117
|
+
};
|
|
118
|
+
monacoEditor.executeEdits('drop', [{ range, text }]);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// No cursor — append
|
|
123
|
+
setMarkdownSourceRaw((prev) => prev + '\n\n' + text);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// Preview or no editor — append to end
|
|
128
|
+
setMarkdownSourceRaw((prev) => prev + '\n\n' + text);
|
|
129
|
+
}
|
|
130
|
+
}, [activeView, tiptapEditor, monacoEditor]);
|
|
131
|
+
const replaceAll = useCallback((text) => {
|
|
132
|
+
setMarkdownSourceRaw(text);
|
|
133
|
+
// Push to editors if mounted
|
|
134
|
+
if (tiptapEditor) {
|
|
135
|
+
const html = markdownToTiptap(text);
|
|
136
|
+
tiptapEditor.commands.setContent(html);
|
|
137
|
+
}
|
|
138
|
+
if (monacoEditor) {
|
|
139
|
+
monacoEditor.setValue(text);
|
|
140
|
+
}
|
|
141
|
+
}, [tiptapEditor, monacoEditor]);
|
|
100
142
|
const setMarkdownDoc = useCallback((newDoc) => {
|
|
101
143
|
setMarkdownDocState(newDoc);
|
|
102
144
|
// Stringify to update the raw source
|
|
@@ -130,12 +172,15 @@ export function EditorProvider({ initialMarkdown = '', initialView = 'raw', arti
|
|
|
130
172
|
theme,
|
|
131
173
|
tiptapEditor,
|
|
132
174
|
monacoEditor,
|
|
175
|
+
mediaProvider,
|
|
133
176
|
setMarkdownSource,
|
|
134
177
|
setMarkdownDoc,
|
|
135
178
|
setActiveView,
|
|
136
179
|
setTiptapEditor,
|
|
137
180
|
setMonacoEditor,
|
|
138
181
|
setTheme,
|
|
182
|
+
insertAtCursor,
|
|
183
|
+
replaceAll,
|
|
139
184
|
}), [
|
|
140
185
|
markdownSource,
|
|
141
186
|
markdownDoc,
|
|
@@ -146,12 +191,15 @@ export function EditorProvider({ initialMarkdown = '', initialView = 'raw', arti
|
|
|
146
191
|
theme,
|
|
147
192
|
tiptapEditor,
|
|
148
193
|
monacoEditor,
|
|
194
|
+
mediaProvider,
|
|
149
195
|
setMarkdownSource,
|
|
150
196
|
setMarkdownDoc,
|
|
151
197
|
setActiveView,
|
|
152
198
|
setTiptapEditor,
|
|
153
199
|
setMonacoEditor,
|
|
154
200
|
setTheme,
|
|
201
|
+
insertAtCursor,
|
|
202
|
+
replaceAll,
|
|
155
203
|
]);
|
|
156
204
|
return _jsx(EditorContext.Provider, { value: value, children: children });
|
|
157
205
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorContext.js","sourceRoot":"","sources":["../src/EditorContext.tsx"],"names":[],"mappings":";AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,aAAa,EACb,UAAU,EACV,QAAQ,EACR,WAAW,EACX,OAAO,EACP,MAAM,EACN,SAAS,GAEV,MAAM,OAAO,CAAC;AAGf,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"EditorContext.js","sourceRoot":"","sources":["../src/EditorContext.tsx"],"names":[],"mappings":";AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,aAAa,EACb,UAAU,EACV,QAAQ,EACR,WAAW,EACX,OAAO,EACP,MAAM,EACN,SAAS,GAEV,MAAM,OAAO,CAAC;AAGf,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAuDlD,4DAA4D;AAE5D,MAAM,aAAa,GAAG,aAAa,CAA4B,IAAI,CAAC,CAAC;AAErE;;GAEG;AACH,gEAAgE;AAChE,MAAM,UAAU,gBAAgB;IAC9B,MAAM,GAAG,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;IACtC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAkBD;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,EAC7B,eAAe,GAAG,EAAE,EACpB,WAAW,GAAG,KAAK,EACnB,SAAS,GAAG,UAAU,EACtB,KAAK,EAAE,YAAY,GAAG,OAAO,EAC7B,aAAa,GAAG,IAAI,EACpB,QAAQ,GACY;IACpB,MAAM,CAAC,cAAc,EAAE,oBAAoB,CAAC,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;IACzE,MAAM,CAAC,WAAW,EAAE,mBAAmB,CAAC,GAAG,QAAQ,CAA0B,IAAI,CAAC,CAAC;IACnF,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAa,IAAI,CAAC,CAAC;IACjD,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAa,WAAW,CAAC,CAAC;IACtE,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAClE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAc,YAAY,CAAC,CAAC;IAC9D,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAsB,IAAI,CAAC,CAAC;IAC5E,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAsB,IAAI,CAAC,CAAC;IAE5E,MAAM,YAAY,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IACvC,YAAY,CAAC,OAAO,GAAG,SAAS,CAAC;IAEjC,+BAA+B;IAC/B,SAAS,CAAC,GAAG,EAAE;QACb,QAAQ,CAAC,YAAY,CAAC,CAAC;IACzB,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,4CAA4C;IAC5C,MAAM,eAAe,GAAG,MAAM,CAAuC,IAAI,CAAC,CAAC;IAE3E,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,MAAc,EAAE,EAAE;QAC7C,YAAY,CAAC,IAAI,CAAC,CAAC;QACnB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;YACrC,mBAAmB,CAAC,MAAM,CAAC,CAAC;YAC5B,aAAa,CAAC,IAAI,CAAC,CAAC;YAEpB,oCAAoC;YACpC,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,aAAa,CAAC,MAAM,EAAE;oBACzC,SAAS,EAAE,YAAY,CAAC,OAAO;iBAChC,CAAC,CAAC;gBACH,MAAM,CAAC,YAAY,CAAC,CAAC;YACvB,CAAC;YAAC,OAAO,MAAe,EAAE,CAAC;gBACzB,uDAAuD;gBACvD,MAAM,CAAC,IAAI,CAAC,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC5F,CAAC;QACH,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,aAAa,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;YAClE,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAC1B,MAAM,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,wCAAwC;IACxC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,eAAe,CAAC,OAAO,EAAE,CAAC;YAC5B,YAAY,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;QACD,eAAe,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YACxC,OAAO,CAAC,cAAc,CAAC,CAAC;QAC1B,CAAC,EAAE,GAAG,CAAC,CAAC;QACR,OAAO,GAAG,EAAE;YACV,IAAI,eAAe,CAAC,OAAO,EAAE,CAAC;gBAC5B,YAAY,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;YACxC,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC;IAE9B,gBAAgB;IAChB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,eAAe,EAAE,CAAC;YACpB,OAAO,CAAC,eAAe,CAAC,CAAC;QAC3B,CAAC;QACD,uDAAuD;IACzD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,iBAAiB,GAAG,WAAW,CAAC,CAAC,MAAc,EAAE,EAAE;QACvD,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,cAAc,GAAG,WAAW,CAChC,CAAC,IAAY,EAAE,EAAE;QACf,IAAI,UAAU,KAAK,SAAS,IAAI,YAAY,EAAE,CAAC;YAC7C,4CAA4C;YAC5C,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;YACpC,YAAY,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC;QACzD,CAAC;aAAM,IAAI,UAAU,KAAK,KAAK,IAAI,YAAY,EAAE,CAAC;YAChD,MAAM,QAAQ,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC;YAC5C,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,EAAE,CAAC;gBACtC,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,KAAK,GAAG;wBACZ,eAAe,EAAE,QAAQ,CAAC,UAAU;wBACpC,WAAW,EAAE,QAAQ,CAAC,MAAM;wBAC5B,aAAa,EAAE,QAAQ,CAAC,UAAU;wBAClC,SAAS,EAAE,QAAQ,CAAC,MAAM;qBAC3B,CAAC;oBACF,YAAY,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;gBACvD,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,qBAAqB;gBACrB,oBAAoB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;aAAM,CAAC;YACN,uCAAuC;YACvC,oBAAoB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC,CAAC;QACvD,CAAC;IACH,CAAC,EACD,CAAC,UAAU,EAAE,YAAY,EAAE,YAAY,CAAC,CACzC,CAAC;IAEF,MAAM,UAAU,GAAG,WAAW,CAC5B,CAAC,IAAY,EAAE,EAAE;QACf,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAE3B,6BAA6B;QAC7B,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;YACpC,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,YAAY,EAAE,CAAC;YACjB,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC,EACD,CAAC,YAAY,EAAE,YAAY,CAAC,CAC7B,CAAC;IAEF,MAAM,cAAc,GAAG,WAAW,CAAC,CAAC,MAAwB,EAAE,EAAE;QAC9D,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAC5B,qCAAqC;QACrC,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;YAC5C,oBAAoB,CAAC,SAAS,CAAC,CAAC;YAChC,aAAa,CAAC,IAAI,CAAC,CAAC;YAEpB,eAAe;YACf,IAAI,CAAC;gBACH,MAAM,YAAY,GAAG,aAAa,CAAC,MAAM,EAAE;oBACzC,SAAS,EAAE,YAAY,CAAC,OAAO;iBAChC,CAAC,CAAC;gBACH,MAAM,CAAC,YAAY,CAAC,CAAC;YACvB,CAAC;YAAC,OAAO,MAAe,EAAE,CAAC;gBACzB,MAAM,CAAC,IAAI,CAAC,CAAC;gBACb,OAAO,CAAC,IAAI,CAAC,wBAAwB,EAAE,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC5F,CAAC;QACH,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,aAAa,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;QACxE,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC;QACL,cAAc;QACd,WAAW;QACX,GAAG;QACH,UAAU;QACV,UAAU;QACV,SAAS;QACT,KAAK;QACL,YAAY;QACZ,YAAY;QACZ,aAAa;QACb,iBAAiB;QACjB,cAAc;QACd,aAAa;QACb,eAAe;QACf,eAAe;QACf,QAAQ;QACR,cAAc;QACd,UAAU;KACX,CAAC,EACF;QACE,cAAc;QACd,WAAW;QACX,GAAG;QACH,UAAU;QACV,UAAU;QACV,SAAS;QACT,KAAK;QACL,YAAY;QACZ,YAAY;QACZ,aAAa;QACb,iBAAiB;QACjB,cAAc;QACd,aAAa;QACb,eAAe;QACf,eAAe;QACf,QAAQ;QACR,cAAc;QACd,UAAU;KACX,CACF,CAAC;IAEF,OAAO,KAAC,aAAa,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAA0B,CAAC;AACnF,CAAC"}
|
package/dist/EditorShell.d.ts
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* in an EditorProvider for shared state.
|
|
7
7
|
*/
|
|
8
8
|
import { type EditorView } from './EditorContext';
|
|
9
|
+
import type { MediaProvider } from '@bendyline/squisq/schemas';
|
|
10
|
+
import type { ContentContainer } from '@bendyline/squisq/storage';
|
|
11
|
+
import type { ReactNode } from 'react';
|
|
9
12
|
export type { EditorTheme } from './EditorContext';
|
|
10
13
|
export interface EditorShellProps {
|
|
11
14
|
/** Initial markdown content */
|
|
@@ -25,10 +28,22 @@ export interface EditorShellProps {
|
|
|
25
28
|
className?: string;
|
|
26
29
|
/** CSS height for the shell container (default: '100vh') */
|
|
27
30
|
height?: string;
|
|
31
|
+
/** Optional MediaProvider for the Files panel. When set (even to null), a Files toggle appears in the toolbar. */
|
|
32
|
+
mediaProvider?: MediaProvider | null;
|
|
33
|
+
/** Optional ContentContainer for audio mapping (MP3 discovery + timing.json reading). */
|
|
34
|
+
container?: ContentContainer | null;
|
|
35
|
+
/** Show the Files toggle in the toolbar. Defaults to true when mediaProvider is passed. */
|
|
36
|
+
showFilesToggle?: boolean;
|
|
37
|
+
/** Content rendered at the left edge of the toolbar, before the view tabs. */
|
|
38
|
+
toolbarSlotLeft?: ReactNode;
|
|
39
|
+
/** Content rendered after the formatting controls (in the middle area of the toolbar). */
|
|
40
|
+
toolbarSlotAfterActions?: ReactNode;
|
|
41
|
+
/** Content rendered at the rightmost end of the toolbar, after all other elements. */
|
|
42
|
+
toolbarSlotRight?: ReactNode;
|
|
28
43
|
}
|
|
29
44
|
/**
|
|
30
45
|
* Complete markdown editor shell with toolbar, view switcher, and three
|
|
31
46
|
* editing modes: Raw (Monaco), WYSIWYG (Tiptap), and Preview.
|
|
32
47
|
*/
|
|
33
|
-
export declare function EditorShell({ initialMarkdown, initialView, articleId, basePath, onChange, theme, className, height, }: EditorShellProps): import("react/jsx-runtime").JSX.Element;
|
|
48
|
+
export declare function EditorShell({ initialMarkdown, initialView, articleId, basePath, onChange, theme, className, height, mediaProvider, container, showFilesToggle, toolbarSlotLeft, toolbarSlotAfterActions, toolbarSlotRight, }: EditorShellProps): import("react/jsx-runtime").JSX.Element;
|
|
34
49
|
//# sourceMappingURL=EditorShell.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../src/EditorShell.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAoC,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../src/EditorShell.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAoC,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAgBpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,MAAM,WAAW,gBAAgB;IAC/B,+BAA+B;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0BAA0B;IAC1B,+CAA+C;IAC/C,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,wDAAwD;IACxD,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kHAAkH;IAClH,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,yFAAyF;IACzF,SAAS,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,2FAA2F;IAC3F,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,8EAA8E;IAC9E,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B,0FAA0F;IAC1F,uBAAuB,CAAC,EAAE,SAAS,CAAC;IACpC,sFAAsF;IACtF,gBAAgB,CAAC,EAAE,SAAS,CAAC;CAC9B;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAC1B,eAAoB,EACpB,WAAuB,EACvB,SAAsB,EACtB,QAAc,EACd,QAAQ,EACR,KAAe,EACf,SAAS,EACT,MAAgB,EAChB,aAAa,EACb,SAAS,EACT,eAAe,EACf,eAAe,EACf,uBAAuB,EACvB,gBAAgB,GACjB,EAAE,gBAAgB,2CA0BlB"}
|
package/dist/EditorShell.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* EditorShell
|
|
4
4
|
*
|
|
@@ -6,22 +6,69 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
6
6
|
* views, and StatusBar into a complete editing experience. Wraps everything
|
|
7
7
|
* in an EditorProvider for shared state.
|
|
8
8
|
*/
|
|
9
|
-
import { useEffect } from 'react';
|
|
9
|
+
import { useEffect, useState, useCallback } from 'react';
|
|
10
10
|
import { EditorProvider, useEditorContext } from './EditorContext';
|
|
11
11
|
import { Toolbar } from './Toolbar';
|
|
12
12
|
import { StatusBar } from './StatusBar';
|
|
13
13
|
import { RawEditor } from './RawEditor';
|
|
14
14
|
import { WysiwygEditor } from './WysiwygEditor';
|
|
15
15
|
import { PreviewPanel } from './PreviewPanel';
|
|
16
|
+
import { PreviewSettingsProvider, PreviewToolbarControls } from './PreviewControls';
|
|
17
|
+
import { MediaBin } from './MediaBin';
|
|
18
|
+
import { DropZoneOverlay } from './DropZoneOverlay';
|
|
19
|
+
import { useFileDrop } from './hooks/useFileDrop';
|
|
20
|
+
import { partitionFiles, processMediaFiles, processTextFile, processTextFiles, } from './utils/dropUtils';
|
|
16
21
|
/**
|
|
17
22
|
* Complete markdown editor shell with toolbar, view switcher, and three
|
|
18
23
|
* editing modes: Raw (Monaco), WYSIWYG (Tiptap), and Preview.
|
|
19
24
|
*/
|
|
20
|
-
export function EditorShell({ initialMarkdown = '', initialView = 'wysiwyg', articleId = 'untitled', basePath = '/', onChange, theme = 'light', className, height = '100vh', }) {
|
|
21
|
-
|
|
25
|
+
export function EditorShell({ initialMarkdown = '', initialView = 'wysiwyg', articleId = 'untitled', basePath = '/', onChange, theme = 'light', className, height = '100vh', mediaProvider, container, showFilesToggle, toolbarSlotLeft, toolbarSlotAfterActions, toolbarSlotRight, }) {
|
|
26
|
+
// Show the toggle when explicitly opted in, or when mediaProvider prop was passed at all
|
|
27
|
+
const filesToggleEnabled = showFilesToggle ?? mediaProvider !== undefined;
|
|
28
|
+
return (_jsx(EditorProvider, { initialMarkdown: initialMarkdown, initialView: initialView, articleId: articleId, theme: theme, mediaProvider: mediaProvider, children: _jsx(EditorShellInner, { basePath: basePath, onChange: onChange, className: className, height: height, mediaProvider: mediaProvider ?? null, container: container, filesToggleEnabled: filesToggleEnabled, toolbarSlotLeft: toolbarSlotLeft, toolbarSlotAfterActions: toolbarSlotAfterActions, toolbarSlotRight: toolbarSlotRight }) }));
|
|
22
29
|
}
|
|
23
|
-
function EditorShellInner({ basePath, onChange, className, height }) {
|
|
24
|
-
const { activeView, markdownSource, theme } = useEditorContext();
|
|
30
|
+
function EditorShellInner({ basePath, onChange, className, height, mediaProvider, container, filesToggleEnabled, toolbarSlotLeft, toolbarSlotAfterActions, toolbarSlotRight, }) {
|
|
31
|
+
const { activeView, markdownSource, doc, theme, insertAtCursor, replaceAll } = useEditorContext();
|
|
32
|
+
const isPreview = activeView === 'preview';
|
|
33
|
+
const [showFiles, setShowFiles] = useState(false);
|
|
34
|
+
const [mediaRefreshKey, setMediaRefreshKey] = useState(0);
|
|
35
|
+
const isDark = theme === 'dark';
|
|
36
|
+
const handleToggleFiles = useCallback(() => {
|
|
37
|
+
setShowFiles((prev) => !prev);
|
|
38
|
+
}, []);
|
|
39
|
+
// ── Drag-and-drop file handling ──
|
|
40
|
+
const handleFileDrop = useCallback(async (files, target) => {
|
|
41
|
+
try {
|
|
42
|
+
const { media, text } = partitionFiles(files);
|
|
43
|
+
// Process media files
|
|
44
|
+
if (media.length > 0 && mediaProvider) {
|
|
45
|
+
await processMediaFiles(media, mediaProvider);
|
|
46
|
+
setMediaRefreshKey((k) => k + 1);
|
|
47
|
+
// Auto-open the media bin so the user sees the new files
|
|
48
|
+
if (!showFiles)
|
|
49
|
+
setShowFiles(true);
|
|
50
|
+
}
|
|
51
|
+
// Process text files
|
|
52
|
+
if (text.length > 0) {
|
|
53
|
+
if (target === 'replace') {
|
|
54
|
+
// Replace with first text file
|
|
55
|
+
const content = await processTextFile(text[0]);
|
|
56
|
+
replaceAll(content);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
// Insert all text files concatenated
|
|
60
|
+
const content = await processTextFiles(text);
|
|
61
|
+
insertAtCursor(content);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
console.error('Failed to process dropped files:', err instanceof Error ? err.message : err);
|
|
67
|
+
}
|
|
68
|
+
}, [mediaProvider, showFiles, replaceAll, insertAtCursor]);
|
|
69
|
+
const { isDragging, dragContentType, containerProps, zoneProps } = useFileDrop({
|
|
70
|
+
onDrop: handleFileDrop,
|
|
71
|
+
});
|
|
25
72
|
// Notify parent of changes
|
|
26
73
|
useEffect(() => {
|
|
27
74
|
onChange?.(markdownSource);
|
|
@@ -49,11 +96,11 @@ function EditorShellInner({ basePath, onChange, className, height }) {
|
|
|
49
96
|
window.addEventListener('keydown', handler);
|
|
50
97
|
return () => window.removeEventListener('keydown', handler);
|
|
51
98
|
}, []);
|
|
52
|
-
return (
|
|
99
|
+
return (_jsx("div", { className: `squisq-editor-shell ${className || ''}`, "data-theme": theme, style: {
|
|
53
100
|
display: 'flex',
|
|
54
101
|
flexDirection: 'column',
|
|
55
102
|
height,
|
|
56
103
|
overflow: 'hidden',
|
|
57
|
-
}, children: [_jsx("div", { className: "squisq-editor-header", children: _jsx(Toolbar, {}) }), _jsxs("div", { className: "squisq-editor-content", style: { flex: 1, overflow: 'hidden', position: 'relative' }, children: [activeView === 'raw' && _jsx(RawEditor, { theme: theme === 'dark' ? 'vs-dark' : 'vs' }), activeView === 'wysiwyg' && _jsx(WysiwygEditor, {}),
|
|
104
|
+
}, ...containerProps, children: _jsxs(PreviewSettingsProvider, { doc: doc, children: [_jsx("div", { className: "squisq-editor-header", children: _jsx(Toolbar, { showFiles: showFiles, onToggleFiles: filesToggleEnabled ? handleToggleFiles : undefined, slotLeft: toolbarSlotLeft, slotAfterActions: _jsxs(_Fragment, { children: [toolbarSlotAfterActions, isPreview && _jsx(PreviewToolbarControls, {})] }), slotRight: toolbarSlotRight }) }), _jsxs("div", { className: "squisq-editor-content", style: { flex: 1, overflow: 'hidden', position: 'relative', display: 'flex' }, children: [_jsxs("div", { style: { flex: 1, overflow: 'hidden', position: 'relative' }, children: [activeView === 'raw' && _jsx(RawEditor, { theme: theme === 'dark' ? 'vs-dark' : 'vs' }), activeView === 'wysiwyg' && _jsx(WysiwygEditor, {}), isPreview && _jsx(PreviewPanel, { basePath: basePath, container: container })] }), showFiles && (_jsx(MediaBin, { mediaProvider: mediaProvider, isDark: isDark, refreshKey: mediaRefreshKey })), isDragging && (_jsx(DropZoneOverlay, { dragContentType: dragContentType, zoneProps: zoneProps, hasMediaProvider: mediaProvider !== null }))] }), _jsx(StatusBar, {})] }) }));
|
|
58
105
|
}
|
|
59
106
|
//# sourceMappingURL=EditorShell.js.map
|
package/dist/EditorShell.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorShell.js","sourceRoot":"","sources":["../src/EditorShell.tsx"],"names":[],"mappings":";AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"EditorShell.js","sourceRoot":"","sources":["../src/EditorShell.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ImageNodeView — Custom Tiptap NodeView for images.
|
|
3
|
+
*
|
|
4
|
+
* Resolves image `src` attributes through the EditorContext's MediaProvider,
|
|
5
|
+
* converting relative paths (e.g. "images/hero.jpg") to displayable blob URLs.
|
|
6
|
+
*
|
|
7
|
+
* The ProseMirror node retains the original relative path so markdown roundtrip
|
|
8
|
+
* is preserved — only the rendered DOM uses the resolved URL.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Image extension with a custom React NodeView that resolves URLs
|
|
12
|
+
* through the EditorContext's MediaProvider.
|
|
13
|
+
*/
|
|
14
|
+
export declare const ImageWithMediaProvider: import("@tiptap/core").Node<import("@tiptap/extension-image").ImageOptions, any>;
|
|
15
|
+
//# sourceMappingURL=ImageNodeView.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageNodeView.d.ts","sourceRoot":"","sources":["../src/ImageNodeView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAqDH;;;GAGG;AACH,eAAO,MAAM,sBAAsB,kFAIjC,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* ImageNodeView — Custom Tiptap NodeView for images.
|
|
4
|
+
*
|
|
5
|
+
* Resolves image `src` attributes through the EditorContext's MediaProvider,
|
|
6
|
+
* converting relative paths (e.g. "images/hero.jpg") to displayable blob URLs.
|
|
7
|
+
*
|
|
8
|
+
* The ProseMirror node retains the original relative path so markdown roundtrip
|
|
9
|
+
* is preserved — only the rendered DOM uses the resolved URL.
|
|
10
|
+
*/
|
|
11
|
+
import { useEffect, useState } from 'react';
|
|
12
|
+
import { NodeViewWrapper, ReactNodeViewRenderer } from '@tiptap/react';
|
|
13
|
+
import Image from '@tiptap/extension-image';
|
|
14
|
+
import { useEditorContext } from './EditorContext';
|
|
15
|
+
function ImageComponent({ node }) {
|
|
16
|
+
const { src, alt, title } = node.attrs;
|
|
17
|
+
const { mediaProvider } = useEditorContext();
|
|
18
|
+
const [resolvedSrc, setResolvedSrc] = useState(src);
|
|
19
|
+
const isRelative = src &&
|
|
20
|
+
!src.startsWith('blob:') &&
|
|
21
|
+
!src.startsWith('http') &&
|
|
22
|
+
!src.startsWith('data:') &&
|
|
23
|
+
!src.startsWith('/');
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!mediaProvider || !isRelative) {
|
|
26
|
+
setResolvedSrc(src);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
let cancelled = false;
|
|
30
|
+
mediaProvider.resolveUrl(src).then((resolved) => {
|
|
31
|
+
if (!cancelled)
|
|
32
|
+
setResolvedSrc(resolved);
|
|
33
|
+
}, () => {
|
|
34
|
+
if (!cancelled)
|
|
35
|
+
setResolvedSrc(src);
|
|
36
|
+
});
|
|
37
|
+
return () => {
|
|
38
|
+
cancelled = true;
|
|
39
|
+
};
|
|
40
|
+
}, [src, mediaProvider, isRelative]);
|
|
41
|
+
return (_jsx(NodeViewWrapper, { as: "figure", style: { margin: '0.5em 0' }, children: _jsx("img", { src: resolvedSrc, alt: alt || '', title: title || undefined, style: { maxWidth: '100%', height: 'auto', display: 'block' } }) }));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Image extension with a custom React NodeView that resolves URLs
|
|
45
|
+
* through the EditorContext's MediaProvider.
|
|
46
|
+
*/
|
|
47
|
+
export const ImageWithMediaProvider = Image.extend({
|
|
48
|
+
addNodeView() {
|
|
49
|
+
return ReactNodeViewRenderer(ImageComponent);
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
//# sourceMappingURL=ImageNodeView.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ImageNodeView.js","sourceRoot":"","sources":["../src/ImageNodeView.tsx"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEvE,OAAO,KAAK,MAAM,yBAAyB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,SAAS,cAAc,CAAC,EAAE,IAAI,EAAiB;IAC7C,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,KAAoD,CAAC;IACtF,MAAM,EAAE,aAAa,EAAE,GAAG,gBAAgB,EAAE,CAAC;IAC7C,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAEpD,MAAM,UAAU,GACd,GAAG;QACH,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC;QACxB,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC;QACvB,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC;QACxB,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAEvB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,aAAa,IAAI,CAAC,UAAU,EAAE,CAAC;YAClC,cAAc,CAAC,GAAG,CAAC,CAAC;YACpB,OAAO;QACT,CAAC;QAED,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,aAAa,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,CAChC,CAAC,QAAQ,EAAE,EAAE;YACX,IAAI,CAAC,SAAS;gBAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;QAC3C,CAAC,EACD,GAAG,EAAE;YACH,IAAI,CAAC,SAAS;gBAAE,cAAc,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC,CACF,CAAC;QAEF,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,GAAG,EAAE,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC;IAErC,OAAO,CACL,KAAC,eAAe,IAAC,EAAE,EAAC,QAAQ,EAAC,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,YACvD,cACE,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,GAAG,IAAI,EAAE,EACd,KAAK,EAAE,KAAK,IAAI,SAAS,EACzB,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAC7D,GACc,CACnB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,CAAC,MAAM,CAAC;IACjD,WAAW;QACT,OAAO,qBAAqB,CAAC,cAAc,CAAC,CAAC;IAC/C,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MediaBin
|
|
3
|
+
*
|
|
4
|
+
* Toggleable side panel that displays files associated with the current
|
|
5
|
+
* content. Shows image thumbnails, icons for other types, file sizes,
|
|
6
|
+
* and provides an upload button to add new media.
|
|
7
|
+
*/
|
|
8
|
+
import type { MediaProvider } from '@bendyline/squisq/schemas';
|
|
9
|
+
export interface MediaBinProps {
|
|
10
|
+
/** The active MediaProvider (null when no media context is available) */
|
|
11
|
+
mediaProvider: MediaProvider | null;
|
|
12
|
+
/** Whether the editor is in dark mode */
|
|
13
|
+
isDark: boolean;
|
|
14
|
+
/** Incremented externally to signal a re-scan of the media list */
|
|
15
|
+
refreshKey?: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function MediaBin({ mediaProvider, isDark, refreshKey }: MediaBinProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
//# sourceMappingURL=MediaBin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaBin.d.ts","sourceRoot":"","sources":["../src/MediaBin.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,2BAA2B,CAAC;AAM3E,MAAM,WAAW,aAAa;IAC5B,yEAAyE;IACzE,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;IACpC,yCAAyC;IACzC,MAAM,EAAE,OAAO,CAAC;IAChB,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA6BD,wBAAgB,QAAQ,CAAC,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,aAAa,2CA2K5E"}
|