@bendyline/squisq-editor-react 1.6.0 → 1.6.2
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 +57 -10
- package/dist/index.d.ts +476 -105
- package/dist/index.js +8703 -6741
- package/dist/index.js.map +1 -1
- package/dist/styles/fa-brands-400-AHOAZHCU.woff2 +0 -0
- package/dist/styles/fa-regular-400-VRZYIBIZ.woff2 +0 -0
- package/dist/styles/fa-solid-900-MDEYK55F.woff2 +0 -0
- package/dist/styles/fa-v4compatibility-ETEVP6IB.woff2 +0 -0
- package/dist/styles/index.css +14617 -0
- package/package.json +15 -7
- package/src/BlockPropertiesPopover.tsx +23 -7
- package/src/EditorContext.tsx +22 -16
- package/src/EditorShell.tsx +121 -35
- package/src/MediaBin.tsx +171 -28
- package/src/OutlinePanel.tsx +26 -4
- package/src/PreviewControls.tsx +475 -145
- package/src/PreviewPanel.tsx +17 -9
- package/src/RawEditor.tsx +10 -4
- package/src/TemplateAnnotation.ts +22 -7
- package/src/TemplateContentPreview.tsx +56 -0
- package/src/TemplatePicker.tsx +295 -128
- package/src/ThemeCustomizerPanel.tsx +22 -15
- package/src/Toolbar.tsx +547 -217
- package/src/TransitionPicker.tsx +8 -1
- package/src/VersionHistoryPanel.tsx +2 -2
- package/src/ViewSwitcher.tsx +4 -4
- package/src/WysiwygEditor.tsx +45 -3
- package/src/__tests__/buildPreviewDocTransition.test.ts +1 -2
- package/src/__tests__/codeContextSectionView.test.tsx +95 -0
- package/src/__tests__/codeContextZoneManager.test.ts +127 -0
- package/src/__tests__/diffContextSections.test.ts +39 -0
- package/src/__tests__/editorShellCodeContext.test.tsx +86 -0
- package/src/__tests__/editorShellProps.test.tsx +363 -0
- package/src/__tests__/headingTransition.test.ts +59 -9
- package/src/__tests__/imageEditorShell.test.tsx +23 -0
- package/src/__tests__/mediaReferences.test.ts +82 -0
- package/src/__tests__/previewControls.test.tsx +163 -0
- package/src/__tests__/templateAnnotationRoundTrip.test.ts +23 -2
- package/src/__tests__/templateContentPreview.test.ts +101 -0
- package/src/__tests__/tiptapBridge.test.ts +47 -0
- package/src/__tests__/useJsonEditorTokens.test.ts +59 -0
- package/src/__tests__/useMediaRecorder.test.ts +17 -0
- package/src/codeContext/CodeContextSectionView.tsx +124 -0
- package/src/codeContext/CodeContextZoneManager.ts +149 -0
- package/src/codeContext/CodeContextZones.tsx +121 -0
- package/src/codeContext/diffContextSections.ts +38 -0
- package/src/codeContext/types.ts +75 -0
- package/src/diagram/DiagramWidget.tsx +6 -4
- package/src/headingTransition.ts +96 -21
- package/src/index.ts +34 -2
- package/src/jsonEditor/useJsonEditorTokens.ts +13 -43
- package/src/mediaReferences.ts +299 -0
- package/src/recorder/hooks/useMediaRecorder.ts +9 -10
- package/src/scene/Scene.tsx +53 -7
- package/src/scene/SceneBlockWidget.tsx +6 -3
- package/src/scene/SceneSelection.tsx +19 -15
- package/src/scene/SceneSideToolbar.tsx +89 -0
- package/src/scene/layers/DiagramEdges.tsx +4 -3
- package/src/scene/layers/edgeGeometry.ts +23 -4
- package/src/scene/scene.css +142 -2
- package/src/scene/tools/ConnectTool.ts +113 -24
- package/src/scene/tools/DrawingConnectTool.ts +86 -16
- package/src/scene/tools/SceneTool.ts +2 -0
- package/src/styles/code-context.css +155 -0
- package/src/styles/editor.css +991 -84
- package/src/styles/index.css +1 -0
- package/src/templateContentPreviewResolver.ts +353 -0
- package/src/tiptapBridge.ts +60 -33
|
@@ -16,6 +16,12 @@
|
|
|
16
16
|
import { createElement, type JSX } from 'react';
|
|
17
17
|
import type { SceneTool, SceneToolContext } from './SceneTool';
|
|
18
18
|
import { shapeIdFromLayerId, isPrimaryShapeLayer } from '../layers/shapeLayers';
|
|
19
|
+
import {
|
|
20
|
+
edgeEndpoints,
|
|
21
|
+
snapPointToward,
|
|
22
|
+
straightPath,
|
|
23
|
+
type EdgeNodeBox,
|
|
24
|
+
} from '../layers/edgeGeometry';
|
|
19
25
|
|
|
20
26
|
/** A grab on the selected connector's endpoint handle. */
|
|
21
27
|
export interface EndpointHit {
|
|
@@ -37,8 +43,9 @@ type DragState =
|
|
|
37
43
|
| {
|
|
38
44
|
mode: 'new';
|
|
39
45
|
sourceId: string;
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
sourceBox: EdgeNodeBox;
|
|
47
|
+
start: { x: number; y: number };
|
|
48
|
+
end: { x: number; y: number };
|
|
42
49
|
hovered: string | null;
|
|
43
50
|
}
|
|
44
51
|
| {
|
|
@@ -46,8 +53,9 @@ type DragState =
|
|
|
46
53
|
connectorId: string;
|
|
47
54
|
end: 'from' | 'to';
|
|
48
55
|
fixedShapeId: string;
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
fixedBox: EdgeNodeBox;
|
|
57
|
+
start: { x: number; y: number };
|
|
58
|
+
endPoint: { x: number; y: number };
|
|
51
59
|
hovered: string | null;
|
|
52
60
|
};
|
|
53
61
|
|
|
@@ -59,10 +67,16 @@ export function createDrawingConnectTool(options: DrawingConnectOptions = {}): S
|
|
|
59
67
|
return { sx: e.clientX - rect.left, sy: e.clientY - rect.top };
|
|
60
68
|
}
|
|
61
69
|
|
|
62
|
-
function
|
|
70
|
+
function shapeBox(ctx: SceneToolContext, shapeId: string): EdgeNodeBox | null {
|
|
63
71
|
const it = ctx.hitItems.find((h) => h.id === `dshape-${shapeId}`);
|
|
64
72
|
if (!it) return null;
|
|
65
|
-
return {
|
|
73
|
+
return {
|
|
74
|
+
id: shapeId,
|
|
75
|
+
x: it.bounds.x,
|
|
76
|
+
y: it.bounds.y,
|
|
77
|
+
width: it.bounds.width,
|
|
78
|
+
height: it.bounds.height,
|
|
79
|
+
};
|
|
66
80
|
}
|
|
67
81
|
|
|
68
82
|
function shapeAt(ctx: SceneToolContext, v: { x: number; y: number }): string | null {
|
|
@@ -71,6 +85,45 @@ export function createDrawingConnectTool(options: DrawingConnectOptions = {}): S
|
|
|
71
85
|
return shapeIdFromLayerId(hitId);
|
|
72
86
|
}
|
|
73
87
|
|
|
88
|
+
function previewNew(
|
|
89
|
+
sourceBox: EdgeNodeBox,
|
|
90
|
+
current: { x: number; y: number },
|
|
91
|
+
targetBox: EdgeNodeBox | null,
|
|
92
|
+
): { start: { x: number; y: number }; end: { x: number; y: number } } {
|
|
93
|
+
if (targetBox) {
|
|
94
|
+
return (
|
|
95
|
+
edgeEndpoints([sourceBox, targetBox], sourceBox.id, targetBox.id) ?? {
|
|
96
|
+
start: current,
|
|
97
|
+
end: current,
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
start: snapPointToward([sourceBox], sourceBox.id, current) ?? current,
|
|
103
|
+
end: current,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function previewRetarget(
|
|
108
|
+
fixedBox: EdgeNodeBox,
|
|
109
|
+
movedEnd: 'from' | 'to',
|
|
110
|
+
current: { x: number; y: number },
|
|
111
|
+
targetBox: EdgeNodeBox | null,
|
|
112
|
+
): { start: { x: number; y: number }; end: { x: number; y: number } } {
|
|
113
|
+
if (targetBox) {
|
|
114
|
+
const source = movedEnd === 'from' ? targetBox : fixedBox;
|
|
115
|
+
const target = movedEnd === 'from' ? fixedBox : targetBox;
|
|
116
|
+
return (
|
|
117
|
+
edgeEndpoints([source, target], source.id, target.id) ?? {
|
|
118
|
+
start: current,
|
|
119
|
+
end: current,
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
const fixed = snapPointToward([fixedBox], fixedBox.id, current) ?? current;
|
|
124
|
+
return movedEnd === 'from' ? { start: current, end: fixed } : { start: fixed, end: current };
|
|
125
|
+
}
|
|
126
|
+
|
|
74
127
|
return {
|
|
75
128
|
id: 'connect',
|
|
76
129
|
label: 'Connect',
|
|
@@ -85,14 +138,17 @@ export function createDrawingConnectTool(options: DrawingConnectOptions = {}): S
|
|
|
85
138
|
// Endpoint handle of the selected connector → re-target gesture.
|
|
86
139
|
const ep = options.endpointAt?.(v);
|
|
87
140
|
if (ep) {
|
|
88
|
-
const
|
|
141
|
+
const fixedBox = shapeBox(ctx, ep.fixedShapeId);
|
|
142
|
+
if (!fixedBox) return;
|
|
143
|
+
const preview = previewRetarget(fixedBox, ep.end, v, null);
|
|
89
144
|
drag = {
|
|
90
145
|
mode: 'retarget',
|
|
91
146
|
connectorId: ep.connectorId,
|
|
92
147
|
end: ep.end,
|
|
93
148
|
fixedShapeId: ep.fixedShapeId,
|
|
94
|
-
|
|
95
|
-
|
|
149
|
+
fixedBox,
|
|
150
|
+
start: preview.start,
|
|
151
|
+
endPoint: preview.end,
|
|
96
152
|
hovered: null,
|
|
97
153
|
};
|
|
98
154
|
(e.currentTarget as Element).setPointerCapture?.(e.pointerId);
|
|
@@ -103,11 +159,15 @@ export function createDrawingConnectTool(options: DrawingConnectOptions = {}): S
|
|
|
103
159
|
// Otherwise start a new connection from the shape under the pointer.
|
|
104
160
|
const src = shapeAt(ctx, v);
|
|
105
161
|
if (!src) return;
|
|
162
|
+
const sourceBox = shapeBox(ctx, src);
|
|
163
|
+
if (!sourceBox) return;
|
|
164
|
+
const preview = previewNew(sourceBox, v, null);
|
|
106
165
|
drag = {
|
|
107
166
|
mode: 'new',
|
|
108
167
|
sourceId: src,
|
|
109
|
-
|
|
110
|
-
|
|
168
|
+
sourceBox,
|
|
169
|
+
start: preview.start,
|
|
170
|
+
end: preview.end,
|
|
111
171
|
hovered: null,
|
|
112
172
|
};
|
|
113
173
|
(e.currentTarget as Element).setPointerCapture?.(e.pointerId);
|
|
@@ -118,10 +178,20 @@ export function createDrawingConnectTool(options: DrawingConnectOptions = {}): S
|
|
|
118
178
|
if (!drag) return;
|
|
119
179
|
const { sx, sy } = pointer(e);
|
|
120
180
|
const v = ctx.screenToViewport(sx, sy);
|
|
121
|
-
drag.current = v;
|
|
122
181
|
const tgt = shapeAt(ctx, v);
|
|
123
182
|
const exclude = drag.mode === 'new' ? drag.sourceId : drag.fixedShapeId;
|
|
124
|
-
|
|
183
|
+
const targetId = tgt && tgt !== exclude ? tgt : null;
|
|
184
|
+
const targetBox = targetId ? shapeBox(ctx, targetId) : null;
|
|
185
|
+
if (drag.mode === 'new') {
|
|
186
|
+
const preview = previewNew(drag.sourceBox, v, targetBox);
|
|
187
|
+
drag.start = preview.start;
|
|
188
|
+
drag.end = preview.end;
|
|
189
|
+
} else {
|
|
190
|
+
const preview = previewRetarget(drag.fixedBox, drag.end, v, targetBox);
|
|
191
|
+
drag.start = preview.start;
|
|
192
|
+
drag.endPoint = preview.end;
|
|
193
|
+
}
|
|
194
|
+
drag.hovered = targetBox?.id ?? null;
|
|
125
195
|
},
|
|
126
196
|
|
|
127
197
|
onPointerUp(e, ctx) {
|
|
@@ -139,13 +209,13 @@ export function createDrawingConnectTool(options: DrawingConnectOptions = {}): S
|
|
|
139
209
|
|
|
140
210
|
renderOverlay(): JSX.Element | null {
|
|
141
211
|
if (!drag) return null;
|
|
142
|
-
const a = drag.
|
|
143
|
-
const b = drag.
|
|
212
|
+
const a = drag.start;
|
|
213
|
+
const b = drag.mode === 'new' ? drag.end : drag.endPoint;
|
|
144
214
|
return createElement(
|
|
145
215
|
'g',
|
|
146
216
|
{ key: 'drawing-connect-preview' },
|
|
147
217
|
createElement('path', {
|
|
148
|
-
d:
|
|
218
|
+
d: straightPath(a, b),
|
|
149
219
|
className: 'squisq-scene-connect-preview',
|
|
150
220
|
fill: 'none',
|
|
151
221
|
}),
|
|
@@ -55,6 +55,8 @@ export interface SceneTool {
|
|
|
55
55
|
cursor: string;
|
|
56
56
|
/** Optional keyboard accelerator (single-character, lowercase). */
|
|
57
57
|
shortcut?: string;
|
|
58
|
+
/** Hide resize handles on selected layers while this tool is active. */
|
|
59
|
+
hideSelectionHandles?: boolean;
|
|
58
60
|
/**
|
|
59
61
|
* Optional icon — JSX rendered inside a fixed-size toolbar button. Keep
|
|
60
62
|
* it light (a single SVG path is ideal); the toolbar wrapper handles
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code-context sections — host-supplied markdown blurbs rendered inside
|
|
3
|
+
* Monaco view zones above anchor lines (see src/codeContext/).
|
|
4
|
+
*
|
|
5
|
+
* The zone dom node lives in Monaco's `.view-zones` layer, which sits below
|
|
6
|
+
* `.view-lines` in hit-test order. Raising the zone's z-index inside that
|
|
7
|
+
* shared stacking context is what makes section content clickable; Monaco's
|
|
8
|
+
* own `suppressMouseDown` keeps the cursor from jumping.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
.squisq-ccx-zone {
|
|
12
|
+
z-index: 10;
|
|
13
|
+
pointer-events: auto;
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.squisq-ccx-section {
|
|
18
|
+
--ccx-fg: #57606a;
|
|
19
|
+
--ccx-fg-strong: #24292f;
|
|
20
|
+
--ccx-bg: rgba(175, 184, 193, 0.12);
|
|
21
|
+
--ccx-bg-hover: rgba(175, 184, 193, 0.22);
|
|
22
|
+
--ccx-border: rgba(140, 149, 159, 0.35);
|
|
23
|
+
--ccx-accent: #0969da;
|
|
24
|
+
font-family: var(--squisq-ux-font, system-ui, -apple-system, sans-serif);
|
|
25
|
+
font-size: 12px;
|
|
26
|
+
line-height: 1.45;
|
|
27
|
+
color: var(--ccx-fg);
|
|
28
|
+
border-left: 2px solid var(--ccx-border);
|
|
29
|
+
background: var(--ccx-bg);
|
|
30
|
+
margin: 2px 8px 2px 4px;
|
|
31
|
+
border-radius: 4px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.squisq-editor-shell[data-theme='dark'] .squisq-ccx-section {
|
|
35
|
+
--ccx-fg: #9198a1;
|
|
36
|
+
--ccx-fg-strong: #e6edf3;
|
|
37
|
+
--ccx-bg: rgba(110, 118, 129, 0.14);
|
|
38
|
+
--ccx-bg-hover: rgba(110, 118, 129, 0.26);
|
|
39
|
+
--ccx-border: rgba(110, 118, 129, 0.45);
|
|
40
|
+
--ccx-accent: #4493f8;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* ── Collapsed strip ─────────────────────────────────────────────── */
|
|
44
|
+
|
|
45
|
+
.squisq-ccx-strip {
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
gap: 6px;
|
|
49
|
+
width: 100%;
|
|
50
|
+
padding: 3px 10px;
|
|
51
|
+
border: none;
|
|
52
|
+
background: transparent;
|
|
53
|
+
color: inherit;
|
|
54
|
+
font: inherit;
|
|
55
|
+
text-align: left;
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
border-radius: 4px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.squisq-ccx-strip:hover {
|
|
61
|
+
background: var(--ccx-bg-hover);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.squisq-ccx-chevron {
|
|
65
|
+
flex: 0 0 auto;
|
|
66
|
+
font-size: 9px;
|
|
67
|
+
opacity: 0.7;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.squisq-ccx-strip-text {
|
|
71
|
+
flex: 1 1 auto;
|
|
72
|
+
min-width: 0;
|
|
73
|
+
white-space: nowrap;
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
text-overflow: ellipsis;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Flatten the strip's markdown to a single inline line. */
|
|
79
|
+
.squisq-ccx-strip-text .squisq-md,
|
|
80
|
+
.squisq-ccx-strip-text .squisq-md-p {
|
|
81
|
+
display: inline;
|
|
82
|
+
margin: 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.squisq-ccx-strip-text .squisq-md-strong {
|
|
86
|
+
color: var(--ccx-fg-strong);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* ── Expanded body ───────────────────────────────────────────────── */
|
|
90
|
+
|
|
91
|
+
.squisq-ccx-body {
|
|
92
|
+
padding: 2px 12px 8px 24px;
|
|
93
|
+
font-size: 12px;
|
|
94
|
+
user-select: text;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.squisq-ccx-body--loading {
|
|
98
|
+
opacity: 0.6;
|
|
99
|
+
font-style: italic;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.squisq-ccx-body .squisq-md-p {
|
|
103
|
+
margin: 4px 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.squisq-ccx-body .squisq-md-strong {
|
|
107
|
+
color: var(--ccx-fg-strong);
|
|
108
|
+
font-size: 11px;
|
|
109
|
+
text-transform: none;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.squisq-ccx-body .squisq-md-list {
|
|
113
|
+
margin: 2px 0 6px;
|
|
114
|
+
padding-left: 18px;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.squisq-ccx-body .squisq-md-li {
|
|
118
|
+
margin: 1px 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.squisq-ccx-body .squisq-md-link {
|
|
122
|
+
color: var(--ccx-accent);
|
|
123
|
+
text-decoration: none;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.squisq-ccx-body .squisq-md-link:hover {
|
|
127
|
+
text-decoration: underline;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.squisq-ccx-body .squisq-md-inline-code,
|
|
131
|
+
.squisq-ccx-body .squisq-md-link .squisq-md-inline-code {
|
|
132
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
133
|
+
font-size: 11px;
|
|
134
|
+
padding: 0 3px;
|
|
135
|
+
border-radius: 3px;
|
|
136
|
+
background: rgba(140, 149, 159, 0.18);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.squisq-ccx-body .squisq-md-link .squisq-md-inline-code {
|
|
140
|
+
color: var(--ccx-accent);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.squisq-ccx-body .squisq-md-code-block {
|
|
144
|
+
margin: 4px 0;
|
|
145
|
+
padding: 6px 8px;
|
|
146
|
+
border-radius: 4px;
|
|
147
|
+
background: rgba(140, 149, 159, 0.14);
|
|
148
|
+
overflow-x: auto;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.squisq-ccx-body .squisq-md-code-block code {
|
|
152
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
153
|
+
font-size: 11px;
|
|
154
|
+
white-space: pre;
|
|
155
|
+
}
|