@bendyline/squisq-editor-react 1.6.2 → 2.0.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/README.md +12 -3
- package/dist/index.d.ts +1275 -183
- package/dist/index.js +20345 -12777
- 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 +1607 -241
- package/package.json +9 -5
- package/src/BlockPropertiesPopover.tsx +30 -15
- package/src/DocumentSettingsDialog.tsx +39 -33
- package/src/EditorContext.tsx +199 -42
- package/src/EditorShell.tsx +349 -261
- 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 +362 -89
- package/src/PreviewPanel.tsx +312 -78
- package/src/RawEditor.tsx +13 -2
- package/src/RecorderEntry.tsx +2 -0
- 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 +357 -51
- 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__/codeContextSectionView.test.tsx +8 -6
- package/src/__tests__/customLayoutManagerTheme.test.tsx +34 -0
- package/src/__tests__/documentSettingsDialog.test.tsx +51 -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__/mediaAttachmentFlow.test.ts +2 -2
- 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 +382 -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__/recorderTheme.test.tsx +42 -0
- package/src/__tests__/resolveBlockVisual.test.ts +43 -0
- package/src/__tests__/selectionConversions.test.ts +80 -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 +71 -7
- package/src/__tests__/tiptapCodeBlockRoundTrip.test.ts +109 -0
- package/src/__tests__/tiptapImageRoundTrip.test.ts +1 -1
- package/src/__tests__/toolbarSelectionConversion.test.tsx +164 -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 +254 -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 +284 -0
- package/src/asciiDiagram/__tests__/asciiDiagramOps.test.ts +114 -0
- package/src/asciiDiagram/__tests__/asciiPaste.test.ts +43 -0
- package/src/asciiDiagram/asciiDiagramCommands.ts +174 -0
- package/src/asciiDiagram/asciiDiagramData.ts +149 -0
- package/src/asciiDiagram/asciiDiagramOps.ts +238 -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/codeContext/types.ts +1 -1
- 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/__tests__/useMemoryLayerAdapter.test.ts +13 -0
- package/src/customTemplates/designer.css +158 -99
- package/src/customTemplates/library.ts +16 -3
- package/src/customTemplates/useMemoryLayerAdapter.ts +7 -1
- 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 +25 -6
- package/src/diagram/types.ts +35 -0
- package/src/editorScrollSync.ts +73 -0
- package/src/emojiData.ts +3 -23
- package/src/frontmatterSettings.ts +23 -0
- 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 +198 -20
- 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/RecorderButton.tsx +9 -1
- package/src/recorder/RecorderModal.tsx +84 -41
- package/src/recorder/RecorderPanel.tsx +9 -1
- 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 +119 -0
- package/src/scene/adapters/DiagramAdapter.ts +12 -101
- package/src/scene/adapters/DrawingAdapter.ts +6 -1
- package/src/scene/adapters/LayoutAdapter.ts +3 -0
- package/src/scene/commands/SceneCommand.ts +17 -3
- 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 +39 -23
- package/src/scene/tools/ShapeTool.ts +2 -3
- package/src/selectionConversions.ts +155 -0
- package/src/styles/ascii-diagram.css +79 -0
- package/src/styles/ascii-timeline.css +596 -0
- package/src/styles/editor.css +736 -120
- package/src/styles/index.css +3 -0
- package/src/styles/tree-view.css +189 -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 +861 -0
- package/src/timeline/TimelineViewExtension.ts +252 -0
- package/src/timeline/__tests__/TimelineEditorWidget.test.tsx +414 -0
- package/src/timeline/__tests__/TimelineViewExtension.test.ts +163 -0
- package/src/timeline/__tests__/timelineCommands.test.ts +359 -0
- package/src/timeline/__tests__/timelineOps.test.ts +277 -0
- package/src/timeline/__tests__/timelinePaste.test.ts +34 -0
- package/src/timeline/timelineCommands.ts +421 -0
- package/src/timeline/timelineData.ts +54 -0
- package/src/timeline/timelineOps.ts +381 -0
- package/src/timeline/timelinePaste.ts +8 -0
- package/src/tiptapBridge.ts +47 -21
- package/src/transformStyleId.ts +17 -0
- package/src/treeview/TreeOutlineWidget.tsx +390 -0
- package/src/treeview/TreeViewExtension.ts +250 -0
- package/src/treeview/__tests__/TreeOutlineWidget.test.tsx +156 -0
- package/src/treeview/__tests__/TreeViewExtension.test.ts +122 -0
- package/src/treeview/__tests__/treeOps.test.ts +177 -0
- package/src/treeview/__tests__/treePaste.test.ts +40 -0
- package/src/treeview/__tests__/treeViewCommands.test.ts +179 -0
- package/src/treeview/treeOps.ts +231 -0
- package/src/treeview/treePaste.ts +13 -0
- package/src/treeview/treeViewCommands.ts +99 -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
|
@@ -110,6 +110,7 @@ export function useImageEditor(options: UseImageEditorOptions): UseImageEditorRe
|
|
|
110
110
|
const seeded = await seedFromSource(container, initialSrc);
|
|
111
111
|
if (cancelled) return;
|
|
112
112
|
await writeImageEditDoc(container, seeded, stateFilename);
|
|
113
|
+
if (cancelled) return;
|
|
113
114
|
dispatch({ type: 'load', doc: seeded });
|
|
114
115
|
setReady(true);
|
|
115
116
|
// Capture an initial snapshot of the freshly-seeded state so the
|
|
@@ -131,27 +132,53 @@ export function useImageEditor(options: UseImageEditorOptions): UseImageEditorRe
|
|
|
131
132
|
// ── Debounced persistence of state.json ────────────────────────────────
|
|
132
133
|
const persistTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
133
134
|
const docRef = useRef<ImageEditDoc | null>(null);
|
|
134
|
-
|
|
135
|
+
const dirtyRef = useRef(false);
|
|
136
|
+
const revisionRef = useRef(0);
|
|
137
|
+
const previousDocRef = useRef<ImageEditDoc | null>(null);
|
|
138
|
+
const nextDoc = state?.doc ?? null;
|
|
139
|
+
if (nextDoc !== previousDocRef.current) {
|
|
140
|
+
previousDocRef.current = nextDoc;
|
|
141
|
+
revisionRef.current += 1;
|
|
142
|
+
}
|
|
143
|
+
docRef.current = nextDoc;
|
|
144
|
+
dirtyRef.current = state?.dirty ?? false;
|
|
145
|
+
const writeQueueRef = useRef<Promise<void>>(Promise.resolve());
|
|
146
|
+
const persistTargetRef = useRef({ container, stateFilename });
|
|
147
|
+
persistTargetRef.current = { container, stateFilename };
|
|
148
|
+
|
|
149
|
+
const enqueueWrite = useCallback(
|
|
150
|
+
(doc: ImageEditDoc, revision: number, markClean: boolean): Promise<void> => {
|
|
151
|
+
const write = writeQueueRef.current
|
|
152
|
+
.catch(() => undefined)
|
|
153
|
+
.then(() => writeImageEditDoc(container, doc, stateFilename));
|
|
154
|
+
writeQueueRef.current = write.catch(() => undefined);
|
|
155
|
+
return write.then(() => {
|
|
156
|
+
if (markClean && revision === revisionRef.current && docRef.current === doc) {
|
|
157
|
+
dispatch({ type: 'mark-clean' });
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
},
|
|
161
|
+
[container, stateFilename],
|
|
162
|
+
);
|
|
135
163
|
|
|
136
164
|
useEffect(() => {
|
|
137
165
|
if (!state?.dirty) return;
|
|
138
166
|
if (persistTimerRef.current) clearTimeout(persistTimerRef.current);
|
|
139
167
|
persistTimerRef.current = setTimeout(() => {
|
|
140
|
-
const doc =
|
|
168
|
+
const doc = state.doc;
|
|
141
169
|
if (!doc) return;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
});
|
|
170
|
+
const revision = revisionRef.current;
|
|
171
|
+
enqueueWrite(doc, revision, true).catch((err: unknown) => {
|
|
172
|
+
console.warn(
|
|
173
|
+
'[squisq-editor] image-edit state persist failed:',
|
|
174
|
+
err instanceof Error ? err.message : err,
|
|
175
|
+
);
|
|
176
|
+
});
|
|
150
177
|
}, persistDebounceMs);
|
|
151
178
|
return () => {
|
|
152
179
|
if (persistTimerRef.current) clearTimeout(persistTimerRef.current);
|
|
153
180
|
};
|
|
154
|
-
}, [state?.dirty, state?.doc,
|
|
181
|
+
}, [state?.dirty, state?.doc, persistDebounceMs, enqueueWrite]);
|
|
155
182
|
|
|
156
183
|
const flush = useCallback(async () => {
|
|
157
184
|
const doc = docRef.current;
|
|
@@ -160,9 +187,29 @@ export function useImageEditor(options: UseImageEditorOptions): UseImageEditorRe
|
|
|
160
187
|
clearTimeout(persistTimerRef.current);
|
|
161
188
|
persistTimerRef.current = null;
|
|
162
189
|
}
|
|
163
|
-
await
|
|
164
|
-
|
|
165
|
-
|
|
190
|
+
await enqueueWrite(doc, revisionRef.current, true);
|
|
191
|
+
}, [enqueueWrite]);
|
|
192
|
+
|
|
193
|
+
// A quick modal close must not discard the final edit that is still inside
|
|
194
|
+
// the debounce window. Queue it behind any in-flight write on unmount.
|
|
195
|
+
useEffect(
|
|
196
|
+
() => () => {
|
|
197
|
+
if (persistTimerRef.current) clearTimeout(persistTimerRef.current);
|
|
198
|
+
const doc = docRef.current;
|
|
199
|
+
if (!dirtyRef.current || !doc) return;
|
|
200
|
+
const target = persistTargetRef.current;
|
|
201
|
+
const write = writeQueueRef.current
|
|
202
|
+
.catch(() => undefined)
|
|
203
|
+
.then(() => writeImageEditDoc(target.container, doc, target.stateFilename));
|
|
204
|
+
writeQueueRef.current = write.catch((err: unknown) => {
|
|
205
|
+
console.warn(
|
|
206
|
+
'[squisq-editor] image-edit final persist failed:',
|
|
207
|
+
err instanceof Error ? err.message : err,
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
},
|
|
211
|
+
[],
|
|
212
|
+
);
|
|
166
213
|
|
|
167
214
|
// ── Versioning ─────────────────────────────────────────────────────────
|
|
168
215
|
const versioning = useMemo(
|
|
@@ -204,17 +251,26 @@ export function useImageEditor(options: UseImageEditorOptions): UseImageEditorRe
|
|
|
204
251
|
|
|
205
252
|
// ── Asset URL cache ────────────────────────────────────────────────────
|
|
206
253
|
const urlCacheRef = useRef<Map<string, string>>(new Map());
|
|
254
|
+
const urlCacheGenerationRef = useRef(0);
|
|
207
255
|
|
|
208
256
|
const resolveAssetUrl = useCallback(
|
|
209
257
|
async (path: string): Promise<string> => {
|
|
210
258
|
const cache = urlCacheRef.current;
|
|
259
|
+
const generation = urlCacheGenerationRef.current;
|
|
211
260
|
const cached = cache.get(path);
|
|
212
261
|
if (cached) return cached;
|
|
213
262
|
const data = await container.readFile(path);
|
|
263
|
+
if (generation !== urlCacheGenerationRef.current) {
|
|
264
|
+
throw new Error('useImageEditor: asset resolution cancelled');
|
|
265
|
+
}
|
|
214
266
|
if (!data) throw new Error(`useImageEditor: missing asset "${path}"`);
|
|
215
267
|
const list = await container.listFiles(path);
|
|
216
268
|
const mime = list.find((e) => e.path === path)?.mimeType ?? 'application/octet-stream';
|
|
217
269
|
const url = URL.createObjectURL(new Blob([data], { type: mime }));
|
|
270
|
+
if (generation !== urlCacheGenerationRef.current) {
|
|
271
|
+
URL.revokeObjectURL(url);
|
|
272
|
+
throw new Error('useImageEditor: asset resolution cancelled');
|
|
273
|
+
}
|
|
218
274
|
cache.set(path, url);
|
|
219
275
|
return url;
|
|
220
276
|
},
|
|
@@ -225,6 +281,7 @@ export function useImageEditor(options: UseImageEditorOptions): UseImageEditorRe
|
|
|
225
281
|
useEffect(() => {
|
|
226
282
|
const cache = urlCacheRef.current;
|
|
227
283
|
return () => {
|
|
284
|
+
urlCacheGenerationRef.current += 1;
|
|
228
285
|
for (const url of cache.values()) URL.revokeObjectURL(url);
|
|
229
286
|
cache.clear();
|
|
230
287
|
};
|
package/src/index.ts
CHANGED
|
@@ -45,6 +45,7 @@ export type {
|
|
|
45
45
|
DocumentLinkProvider,
|
|
46
46
|
ViewPreferences,
|
|
47
47
|
ThemeInheritance,
|
|
48
|
+
BlockTagVisibility,
|
|
48
49
|
LayoutMode,
|
|
49
50
|
} from './EditorContext.js';
|
|
50
51
|
|
|
@@ -99,11 +100,12 @@ export { DocumentSettingsDialog } from './DocumentSettingsDialog.js';
|
|
|
99
100
|
export type { DocumentSettingsDialogProps } from './DocumentSettingsDialog.js';
|
|
100
101
|
export { ThemePicker } from './ThemePicker.js';
|
|
101
102
|
export type { ThemePickerProps } from './ThemePicker.js';
|
|
102
|
-
export {
|
|
103
|
-
export type {
|
|
103
|
+
export { PICKER_CATEGORIES, ALL_PICKER_ENTRIES, searchPickerEntries } from './emojiData.js';
|
|
104
|
+
export type { PickerEntry, PickerCategory } from './emojiData.js';
|
|
104
105
|
export {
|
|
105
106
|
PreviewSettingsProvider,
|
|
106
107
|
PreviewToolbarControls,
|
|
108
|
+
PreviewModeMenu,
|
|
107
109
|
PreviewModeSwitch,
|
|
108
110
|
PreviewFormatSwitch,
|
|
109
111
|
usePreviewSettings,
|
|
@@ -142,7 +144,6 @@ export {
|
|
|
142
144
|
setHeadingLineTransition,
|
|
143
145
|
readBlockAttrsTransition,
|
|
144
146
|
setHeadingAttrsTransition,
|
|
145
|
-
setBlockAttrsTransition,
|
|
146
147
|
EMPTY_TRANSITION,
|
|
147
148
|
} from './headingTransition.js';
|
|
148
149
|
export type { HeadingTransitionAttrs, TransitionFields } from './headingTransition.js';
|
|
@@ -190,12 +191,26 @@ export {
|
|
|
190
191
|
export { useMonacoLoader } from './useMonacoLoader.js';
|
|
191
192
|
export type { UseMonacoLoaderResult } from './useMonacoLoader.js';
|
|
192
193
|
|
|
194
|
+
// Monaco language-service worker wiring — the host supplies the `?worker`
|
|
195
|
+
// constructors, this owns the label→worker mapping. See monacoWorkers.ts.
|
|
196
|
+
export { configureMonacoWorkers } from './monacoWorkers.js';
|
|
197
|
+
export type { MonacoWorkerConstructor, MonacoWorkerConstructors } from './monacoWorkers.js';
|
|
198
|
+
|
|
193
199
|
// Custom themes — provider stack (doc frontmatter + browser-local library)
|
|
194
|
-
export {
|
|
200
|
+
export {
|
|
201
|
+
CustomThemeProvider,
|
|
202
|
+
useCustomThemes,
|
|
203
|
+
useDocCustomThemes,
|
|
204
|
+
ImportThemeSection,
|
|
205
|
+
draftPatchFromImportedTheme,
|
|
206
|
+
} from './customThemes/index.js';
|
|
195
207
|
export type {
|
|
196
208
|
CustomThemeContextValue,
|
|
197
209
|
CustomThemeProviderProps,
|
|
198
210
|
DocCustomThemes,
|
|
211
|
+
ImportThemeSectionProps,
|
|
212
|
+
ImportedThemeResult,
|
|
213
|
+
ThemeSaveExtras,
|
|
199
214
|
} from './customThemes/index.js';
|
|
200
215
|
|
|
201
216
|
// Custom templates — provider stack (doc frontmatter + browser-local library)
|
|
@@ -219,24 +234,130 @@ export { buildPreviewDoc } from './buildPreviewDoc.js';
|
|
|
219
234
|
// Tiptap extension: Heading with template annotation support
|
|
220
235
|
export { HeadingWithTemplate } from './TemplateAnnotation.js';
|
|
221
236
|
|
|
222
|
-
// Diagram editor —
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
|
|
237
|
+
// Diagram editor — ASCII fences are the authored diagram format. The
|
|
238
|
+
// AsciiDiagramExtension turns code fences containing box-and-line art
|
|
239
|
+
// into interactive canvases; edits re-render the art (the fence stays the
|
|
240
|
+
// source of truth). Legacy `{[diagram]}` heading sections still RENDER in
|
|
241
|
+
// preview/player via core, but heading-based canvas editing was removed
|
|
242
|
+
// (BREAKING: DiagramExtension / DiagramWidget / useDiagramData / the
|
|
243
|
+
// heading command helpers are gone).
|
|
226
244
|
export { DiagramCanvas } from './diagram/DiagramCanvas.js';
|
|
227
245
|
export type { DiagramCommand } from './diagram/DiagramCanvas.js';
|
|
228
|
-
export {
|
|
229
|
-
export {
|
|
230
|
-
|
|
246
|
+
export type { DiagramData, DiagramNode, DiagramEdge } from './diagram/types.js';
|
|
247
|
+
export {
|
|
248
|
+
AsciiDiagramExtension,
|
|
249
|
+
findAsciiDiagramBlockPos,
|
|
250
|
+
isAsciiSourceVisible,
|
|
251
|
+
toggleAsciiSource,
|
|
252
|
+
} from './asciiDiagram/AsciiDiagramExtension.js';
|
|
253
|
+
export type {
|
|
254
|
+
AsciiDiagramBlockEntry,
|
|
255
|
+
AsciiDiagramExtensionOptions,
|
|
256
|
+
AsciiDiagramPluginState,
|
|
257
|
+
} from './asciiDiagram/AsciiDiagramExtension.js';
|
|
258
|
+
export { AsciiDiagramWidget } from './asciiDiagram/AsciiDiagramWidget.js';
|
|
259
|
+
export { useAsciiDiagramData, asciiDiagramToCanvas } from './asciiDiagram/asciiDiagramData.js';
|
|
260
|
+
export type { AsciiDiagramView } from './asciiDiagram/asciiDiagramData.js';
|
|
261
|
+
export {
|
|
262
|
+
applyAsciiDiagramCommand,
|
|
263
|
+
applyRepairCommand,
|
|
264
|
+
replaceAsciiFenceText,
|
|
265
|
+
} from './asciiDiagram/asciiDiagramCommands.js';
|
|
266
|
+
export type { ApplyAsciiDiagramCommandOptions } from './asciiDiagram/asciiDiagramCommands.js';
|
|
267
|
+
// RepairableDiagramExtension mounts an inline "Repair as diagram" button on
|
|
268
|
+
// code fences holding BROKEN box art — art too misaligned for clean detection
|
|
269
|
+
// (so it renders as a faithful code block). One click reconstructs it into
|
|
270
|
+
// clean `diagram`-tagged art that the AsciiDiagramExtension turns into a
|
|
271
|
+
// canvas. Peer to the diagram/tree editors; claims the fences neither does.
|
|
272
|
+
export {
|
|
273
|
+
RepairableDiagramExtension,
|
|
274
|
+
findRepairableBlockPos,
|
|
275
|
+
isRepairableFence,
|
|
276
|
+
REPAIRABLE_KEY,
|
|
277
|
+
} from './asciiDiagram/RepairableDiagramExtension.js';
|
|
278
|
+
export type {
|
|
279
|
+
RepairableBlockEntry,
|
|
280
|
+
RepairableDiagramExtensionOptions,
|
|
281
|
+
RepairablePluginState,
|
|
282
|
+
} from './asciiDiagram/RepairableDiagramExtension.js';
|
|
283
|
+
export {
|
|
284
|
+
addEdgeOp,
|
|
285
|
+
addNodeOp,
|
|
286
|
+
moveNodeOp,
|
|
287
|
+
removeEdgeOp,
|
|
288
|
+
removeNodeOp,
|
|
289
|
+
renameNodeOp,
|
|
290
|
+
resizeNodeOp,
|
|
291
|
+
sanitizeAsciiLabel,
|
|
292
|
+
translateDiagramOp,
|
|
293
|
+
} from './asciiDiagram/asciiDiagramOps.js';
|
|
294
|
+
export { shouldPasteAsAsciiFence } from './asciiDiagram/asciiPaste.js';
|
|
295
|
+
|
|
296
|
+
// Treeview editor — ASCII file-tree / outline fences get an interactive
|
|
297
|
+
// outline widget; edits re-render the tree art (the fence stays the source
|
|
298
|
+
// of truth). Peer to the ASCII diagram editor; mutually exclusive with it
|
|
299
|
+
// (a fence with boxes is a diagram, not a tree).
|
|
300
|
+
export { TreeViewExtension, findTreeBlockPos } from './treeview/TreeViewExtension.js';
|
|
301
|
+
export type {
|
|
302
|
+
TreeBlockEntry,
|
|
303
|
+
TreeViewExtensionOptions,
|
|
304
|
+
TreeViewPluginState,
|
|
305
|
+
} from './treeview/TreeViewExtension.js';
|
|
306
|
+
export { TreeOutlineWidget } from './treeview/TreeOutlineWidget.js';
|
|
307
|
+
export { useTreeViewData } from './treeview/treeViewData.js';
|
|
308
|
+
export type { TreeViewData } from './treeview/treeViewData.js';
|
|
309
|
+
export { applyTreeCommand, replaceTreeFenceText } from './treeview/treeViewCommands.js';
|
|
310
|
+
export type { TreeCommand } from './treeview/treeViewCommands.js';
|
|
231
311
|
export {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
312
|
+
addItemOp,
|
|
313
|
+
indentItemOp,
|
|
314
|
+
moveItemDownOp,
|
|
315
|
+
moveItemUpOp,
|
|
316
|
+
outdentItemOp,
|
|
317
|
+
removeItemOp,
|
|
318
|
+
renameItemOp,
|
|
319
|
+
sanitizeTreeLabel,
|
|
320
|
+
toggleDirOp,
|
|
321
|
+
} from './treeview/treeOps.js';
|
|
322
|
+
export { shouldPasteAsTreeFence } from './treeview/treePaste.js';
|
|
323
|
+
|
|
324
|
+
// Timeline editor — authored marker-rail fences become an accessible canvas
|
|
325
|
+
// with add-point affordances and a selected-point inspector. Semantic edits
|
|
326
|
+
// re-render the canonical `timeline` fence in one undoable transaction.
|
|
327
|
+
export {
|
|
328
|
+
TimelineViewExtension,
|
|
329
|
+
TIMELINE_VIEW_KEY,
|
|
330
|
+
findTimelineBlockPos,
|
|
331
|
+
getTimelineForNode,
|
|
332
|
+
parseTimelineForNode,
|
|
333
|
+
} from './timeline/TimelineViewExtension.js';
|
|
334
|
+
export type {
|
|
335
|
+
TimelineBlockEntry,
|
|
336
|
+
TimelineViewExtensionOptions,
|
|
337
|
+
TimelineViewPluginState,
|
|
338
|
+
} from './timeline/TimelineViewExtension.js';
|
|
339
|
+
export { TimelineEditorWidget } from './timeline/TimelineEditorWidget.js';
|
|
340
|
+
export type { TimelineEditorWidgetProps } from './timeline/TimelineEditorWidget.js';
|
|
341
|
+
export { useTimelineData } from './timeline/timelineData.js';
|
|
342
|
+
export type { TimelineViewData } from './timeline/timelineData.js';
|
|
343
|
+
export {
|
|
344
|
+
applyTimelineCommand,
|
|
345
|
+
isTimelineSourceSafeForSemanticEdit,
|
|
346
|
+
} from './timeline/timelineCommands.js';
|
|
347
|
+
export type { TimelineCommand, TimelineCommandResult } from './timeline/timelineCommands.js';
|
|
348
|
+
export {
|
|
349
|
+
addTimelineEventOp,
|
|
350
|
+
nextTimelineEventId,
|
|
351
|
+
removeTimelineEventOp,
|
|
352
|
+
sanitizeTimelineText,
|
|
353
|
+
updateTimelineEventOp,
|
|
354
|
+
} from './timeline/timelineOps.js';
|
|
355
|
+
export type {
|
|
356
|
+
AddTimelineEventOptions,
|
|
357
|
+
AddTimelineEventResult,
|
|
358
|
+
TimelineEventPatch,
|
|
359
|
+
} from './timeline/timelineOps.js';
|
|
360
|
+
export { shouldPasteAsTimelineFence } from './timeline/timelinePaste.js';
|
|
240
361
|
|
|
241
362
|
// JSON Form — editable component
|
|
242
363
|
export { JsonEditor } from './jsonEditor/index.js';
|
|
@@ -248,7 +369,11 @@ export type { JsonEditorProps } from './jsonEditor/index.js';
|
|
|
248
369
|
// `MediaProvider`. Previously published as `@bendyline/squisq-recorder-react`;
|
|
249
370
|
// folded into editor-react so it ships with the editor it's wired into.
|
|
250
371
|
export { RecorderModal } from './recorder/RecorderModal.js';
|
|
251
|
-
export type {
|
|
372
|
+
export type {
|
|
373
|
+
RecorderColorScheme,
|
|
374
|
+
RecorderModalProps,
|
|
375
|
+
RecorderSaveResult,
|
|
376
|
+
} from './recorder/RecorderModal.js';
|
|
252
377
|
export { RecorderButton } from './recorder/RecorderButton.js';
|
|
253
378
|
export type { RecorderButtonProps } from './recorder/RecorderButton.js';
|
|
254
379
|
export { RecorderPanel } from './recorder/RecorderPanel.js';
|
|
@@ -277,6 +402,59 @@ export type { CaptureKind, ResolvedFormat } from './recorder/formats.js';
|
|
|
277
402
|
export { buildTimingJson, encodeTimingJson, timingPathFor } from './recorder/timingJson.js';
|
|
278
403
|
export type { TimingJson, RecordedBookmark } from './recorder/timingJson.js';
|
|
279
404
|
|
|
405
|
+
// Teleprompter — the Narrate display mode under the Use tab: voice-paced
|
|
406
|
+
// prompter surface, floating-window tiers, and the mic analysis pipeline.
|
|
407
|
+
export {
|
|
408
|
+
TeleprompterView,
|
|
409
|
+
TeleprompterSurface,
|
|
410
|
+
TeleprompterControls,
|
|
411
|
+
useTeleprompter,
|
|
412
|
+
useNarrationRecorder,
|
|
413
|
+
buildNarrationSavePlan,
|
|
414
|
+
executeNarrationSave,
|
|
415
|
+
insertNarrationPreamble,
|
|
416
|
+
narrationAnnotationLine,
|
|
417
|
+
cameraVideoLine,
|
|
418
|
+
vadConfigForSensitivity,
|
|
419
|
+
useMicAnalysis,
|
|
420
|
+
PCM_WORKLET_SOURCE,
|
|
421
|
+
PCM_WORKLET_NAME,
|
|
422
|
+
registerPcmWorklet,
|
|
423
|
+
createFloatingWindowManager,
|
|
424
|
+
detectFloatTiers,
|
|
425
|
+
useFloatingWindow,
|
|
426
|
+
measureTokenLines,
|
|
427
|
+
targetOffsetFor,
|
|
428
|
+
stepScroll,
|
|
429
|
+
EYE_LINE_FRACTION,
|
|
430
|
+
TELEPROMPTER_CSS,
|
|
431
|
+
ensureTeleprompterStyles,
|
|
432
|
+
prompterVarsFromTheme,
|
|
433
|
+
DEFAULT_TELEPROMPTER_PREFS,
|
|
434
|
+
} from './teleprompter/index.js';
|
|
435
|
+
export type {
|
|
436
|
+
TeleprompterViewProps,
|
|
437
|
+
TeleprompterSurfaceProps,
|
|
438
|
+
TeleprompterControlsProps,
|
|
439
|
+
TeleprompterController,
|
|
440
|
+
TeleprompterRecordingDeps,
|
|
441
|
+
NarrationRecorderController,
|
|
442
|
+
NarrationRecorderState,
|
|
443
|
+
NarrationTake,
|
|
444
|
+
NarrationSavePlan,
|
|
445
|
+
NarrationSaveResult,
|
|
446
|
+
MicAnalysisHandle,
|
|
447
|
+
MicAnalysisStatus,
|
|
448
|
+
FloatingWindowManager,
|
|
449
|
+
FloatOpenOptions,
|
|
450
|
+
CanvasSink,
|
|
451
|
+
FloatingWindowHandle,
|
|
452
|
+
TokenLineMap,
|
|
453
|
+
TeleprompterPrefs,
|
|
454
|
+
PrompterTransport,
|
|
455
|
+
FloatTier,
|
|
456
|
+
} from './teleprompter/index.js';
|
|
457
|
+
|
|
280
458
|
// Image editor — layered, sidecar-persisted raster authoring surface.
|
|
281
459
|
// Pairs with `ImageViewer` and the `<basename>_files/` sidecar convention.
|
|
282
460
|
export { ImageEditor } from './ImageEditor.js';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* to the host's `onChange`.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { createContext, useContext, useMemo, type ReactNode } from 'react';
|
|
8
|
+
import { createContext, useContext, useEffect, useMemo, type ReactNode } from 'react';
|
|
9
9
|
import type {
|
|
10
10
|
JsonFormValidationError,
|
|
11
11
|
JsonFormValidator,
|
|
@@ -38,18 +38,24 @@ export interface JsonEditorProviderProps {
|
|
|
38
38
|
export function JsonEditorProvider(props: JsonEditorProviderProps) {
|
|
39
39
|
const { rootSchema, rootData, onRootChange, density, validate, onValidate, children } = props;
|
|
40
40
|
|
|
41
|
+
const validationErrors = useMemo(
|
|
42
|
+
() => (validate ? validate(rootData, rootSchema) : []),
|
|
43
|
+
[validate, rootData, rootSchema],
|
|
44
|
+
);
|
|
41
45
|
const errors = useMemo(() => {
|
|
42
|
-
if (!validate) return new Map<string, JsonFormValidationError[]>();
|
|
43
|
-
const list = validate(rootData, rootSchema);
|
|
44
|
-
if (onValidate) onValidate(list);
|
|
45
46
|
const grouped = new Map<string, JsonFormValidationError[]>();
|
|
46
|
-
for (const e of
|
|
47
|
+
for (const e of validationErrors) {
|
|
47
48
|
const arr = grouped.get(e.path) ?? [];
|
|
48
49
|
arr.push(e);
|
|
49
50
|
grouped.set(e.path, arr);
|
|
50
51
|
}
|
|
51
52
|
return grouped;
|
|
52
|
-
}, [
|
|
53
|
+
}, [validationErrors]);
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (!onValidate) return;
|
|
57
|
+
onValidate(validationErrors);
|
|
58
|
+
}, [validationErrors, onValidate]);
|
|
53
59
|
|
|
54
60
|
const setAtPath = useMemo(
|
|
55
61
|
() => (pointer: string, value: unknown) => {
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
resolveRef,
|
|
12
12
|
type SquisqAnnotatedSchema,
|
|
13
13
|
} from '@bendyline/squisq/jsonForm';
|
|
14
|
+
import { useId } from 'react';
|
|
14
15
|
import { useJsonEditor } from './JsonEditorContext';
|
|
15
16
|
import { EDITORS, isCompositeControl } from './editors';
|
|
16
17
|
|
|
@@ -23,6 +24,7 @@ export interface RenderNodeProps {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export function RenderNode(props: RenderNodeProps): React.ReactElement | null {
|
|
27
|
+
const generatedId = useId().replace(/:/g, '');
|
|
26
28
|
const { rootSchema, rootData, errors } = useJsonEditor();
|
|
27
29
|
const resolved = resolveRef(props.schema, rootSchema) ?? props.schema;
|
|
28
30
|
|
|
@@ -35,6 +37,11 @@ export function RenderNode(props: RenderNodeProps): React.ReactElement | null {
|
|
|
35
37
|
const label = resolved.squisq?.label ?? resolved.title;
|
|
36
38
|
const help = resolved.squisq?.help ?? resolved.description;
|
|
37
39
|
const fieldErrors = errors.get(props.pointer) ?? [];
|
|
40
|
+
const controlId = `squisq-json-control-${generatedId}`;
|
|
41
|
+
const labelId = label ? `${controlId}-label` : undefined;
|
|
42
|
+
const helpId = help ? `${controlId}-help` : undefined;
|
|
43
|
+
const errorId = fieldErrors.length > 0 ? `${controlId}-error` : undefined;
|
|
44
|
+
const describedBy = [helpId, errorId].filter(Boolean).join(' ') || undefined;
|
|
38
45
|
|
|
39
46
|
const editorEl = (
|
|
40
47
|
<Editor
|
|
@@ -42,6 +49,10 @@ export function RenderNode(props: RenderNodeProps): React.ReactElement | null {
|
|
|
42
49
|
schema={resolved}
|
|
43
50
|
pointer={props.pointer}
|
|
44
51
|
disabled={disabled}
|
|
52
|
+
controlId={controlId}
|
|
53
|
+
labelledBy={labelId}
|
|
54
|
+
describedBy={describedBy}
|
|
55
|
+
invalid={fieldErrors.length > 0}
|
|
45
56
|
// GroupEditor honors this — other editors ignore the extra prop.
|
|
46
57
|
// @ts-expect-error optional renderer-specific prop
|
|
47
58
|
suppressTitle={props.suppressTopGroupTitle}
|
|
@@ -55,11 +66,21 @@ export function RenderNode(props: RenderNodeProps): React.ReactElement | null {
|
|
|
55
66
|
|
|
56
67
|
return (
|
|
57
68
|
<div className={`squisq-jf-field${disabled ? ' squisq-jf-field--disabled' : ''}`}>
|
|
58
|
-
{label ?
|
|
69
|
+
{label ? (
|
|
70
|
+
<label id={labelId} htmlFor={controlId} className="squisq-jf-field__label">
|
|
71
|
+
{label}
|
|
72
|
+
</label>
|
|
73
|
+
) : null}
|
|
59
74
|
{editorEl}
|
|
60
|
-
{help ?
|
|
75
|
+
{help ? (
|
|
76
|
+
<p id={helpId} className="squisq-jf-field__help">
|
|
77
|
+
{help}
|
|
78
|
+
</p>
|
|
79
|
+
) : null}
|
|
61
80
|
{fieldErrors.length > 0 ? (
|
|
62
|
-
<p className="squisq-jf-field__error">
|
|
81
|
+
<p id={errorId} className="squisq-jf-field__error" role="alert">
|
|
82
|
+
{fieldErrors[0].message}
|
|
83
|
+
</p>
|
|
63
84
|
) : null}
|
|
64
85
|
</div>
|
|
65
86
|
);
|