astro-tractstack 2.2.6 → 2.2.7
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/index.js +19 -7
- package/package.json +1 -1
- package/templates/src/components/compositor/ToolDragLayer.tsx +38 -0
- package/templates/src/components/compositor/tools/NodeOverlay.tsx +42 -13
- package/templates/src/components/edit/ToolBar.tsx +12 -3
- package/templates/src/components/edit/ToolMode.tsx +6 -3
- package/templates/src/pages/[...slug]/edit.astro +2 -0
- package/templates/src/stores/toolDrag.ts +57 -0
- package/templates/src/types/compositorTypes.ts +1 -1
- package/templates/src/utils/compositor/toolDragManager.ts +69 -0
- package/utils/inject-files.ts +12 -0
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { fileURLToPath as d } from "node:url";
|
|
2
2
|
import { dirname as i, resolve as l } from "node:path";
|
|
3
|
-
import { existsSync as
|
|
3
|
+
import { existsSync as n, mkdirSync as x, copyFileSync as k, writeFileSync as u } from "node:fs";
|
|
4
4
|
import { resolve as a } from "path";
|
|
5
5
|
function b(t) {
|
|
6
6
|
const e = i(d(t));
|
|
@@ -94,6 +94,10 @@ async function w(t, e, c) {
|
|
|
94
94
|
src: t("../templates/src/components/compositor/Node.tsx"),
|
|
95
95
|
dest: "src/components/compositor/Node.tsx"
|
|
96
96
|
},
|
|
97
|
+
{
|
|
98
|
+
src: t("../templates/src/components/compositor/ToolDragLayer.tsx"),
|
|
99
|
+
dest: "src/components/compositor/ToolDragLayer.tsx"
|
|
100
|
+
},
|
|
97
101
|
{
|
|
98
102
|
src: t(
|
|
99
103
|
"../templates/src/components/compositor/tools/NodeOverlay.tsx"
|
|
@@ -574,6 +578,10 @@ async function w(t, e, c) {
|
|
|
574
578
|
src: t("../templates/src/stores/backend.ts"),
|
|
575
579
|
dest: "src/stores/backend.ts"
|
|
576
580
|
},
|
|
581
|
+
{
|
|
582
|
+
src: t("../templates/src/stores/toolDrag.ts"),
|
|
583
|
+
dest: "src/stores/toolDrag.ts"
|
|
584
|
+
},
|
|
577
585
|
{
|
|
578
586
|
src: t("../templates/src/stores/resources.ts"),
|
|
579
587
|
dest: "src/stores/resources.ts"
|
|
@@ -617,6 +625,10 @@ async function w(t, e, c) {
|
|
|
617
625
|
src: t("../templates/src/utils/compositor/savePipeline.ts"),
|
|
618
626
|
dest: "src/utils/compositor/savePipeline.ts"
|
|
619
627
|
},
|
|
628
|
+
{
|
|
629
|
+
src: t("../templates/src/utils/compositor/toolDragManager.ts"),
|
|
630
|
+
dest: "src/utils/compositor/toolDragManager.ts"
|
|
631
|
+
},
|
|
620
632
|
{
|
|
621
633
|
src: t("../templates/src/utils/compositor/aiPaneParser.ts"),
|
|
622
634
|
dest: "src/utils/compositor/aiPaneParser.ts"
|
|
@@ -2195,10 +2207,10 @@ async function w(t, e, c) {
|
|
|
2195
2207
|
for (const s of r)
|
|
2196
2208
|
try {
|
|
2197
2209
|
const p = i(s.dest);
|
|
2198
|
-
|
|
2199
|
-
const
|
|
2200
|
-
if (!
|
|
2201
|
-
if (
|
|
2210
|
+
n(p) || x(p, { recursive: !0 });
|
|
2211
|
+
const o = !s.protected && (s.dest === "tailwind.config.cjs" || s.dest.startsWith("src/components/codehooks/") || s.dest.startsWith("src/components/widgets/") || s.dest.startsWith("src/") || s.dest.startsWith("public/client/") || s.dest === ".gitignore");
|
|
2212
|
+
if (!n(s.dest) || o)
|
|
2213
|
+
if (n(s.src))
|
|
2202
2214
|
k(s.src, s.dest), e.info(`Updated ${s.dest}`);
|
|
2203
2215
|
else {
|
|
2204
2216
|
const m = y(s.dest);
|
|
@@ -2206,8 +2218,8 @@ async function w(t, e, c) {
|
|
|
2206
2218
|
}
|
|
2207
2219
|
else s.protected ? e.info(`Protected: ${s.dest} (skipped overwrite)`) : e.info(`Skipped existing ${s.dest}`);
|
|
2208
2220
|
} catch (p) {
|
|
2209
|
-
const
|
|
2210
|
-
e.error(`Failed to create ${s.dest}: ${
|
|
2221
|
+
const o = p instanceof Error ? p.message : String(p);
|
|
2222
|
+
e.error(`Failed to create ${s.dest}: ${o}`);
|
|
2211
2223
|
}
|
|
2212
2224
|
}
|
|
2213
2225
|
function y(t) {
|
package/package.json
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useStore } from '@nanostores/react';
|
|
2
|
+
import { toolDragStore } from '@/stores/toolDrag';
|
|
3
|
+
import { toolAddModeTitles } from '@/constants';
|
|
4
|
+
import type { ToolAddMode } from '@/types/compositorTypes';
|
|
5
|
+
import ArrowsUpDownIcon from '@heroicons/react/24/outline/ArrowsUpDownIcon';
|
|
6
|
+
|
|
7
|
+
const ToolDragLayer = () => {
|
|
8
|
+
const { isDragging, pointer, payload, dragType } = useStore(toolDragStore);
|
|
9
|
+
|
|
10
|
+
if (!isDragging || !payload) return null;
|
|
11
|
+
|
|
12
|
+
const style = {
|
|
13
|
+
transform: `translate(${pointer.x}px, ${pointer.y}px)`,
|
|
14
|
+
position: 'fixed' as const,
|
|
15
|
+
left: 0,
|
|
16
|
+
top: 0,
|
|
17
|
+
zIndex: 9999,
|
|
18
|
+
pointerEvents: 'none' as const,
|
|
19
|
+
marginTop: '-20px',
|
|
20
|
+
marginLeft: '-20px',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div style={style} className="pointer-events-none flex items-center gap-2">
|
|
25
|
+
{dragType === 'insert' ? (
|
|
26
|
+
<div className="rounded-lg border-2 border-myblue bg-white px-4 py-2 font-bold text-myblue opacity-90 shadow-xl">
|
|
27
|
+
{toolAddModeTitles[payload as ToolAddMode] || payload}
|
|
28
|
+
</div>
|
|
29
|
+
) : (
|
|
30
|
+
<div className="flex h-10 w-10 items-center justify-center rounded-full border-2 border-myblue bg-white text-myblue opacity-90 shadow-xl">
|
|
31
|
+
<ArrowsUpDownIcon className="h-6 w-6" />
|
|
32
|
+
</div>
|
|
33
|
+
)}
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default ToolDragLayer;
|
|
@@ -11,6 +11,8 @@ import TrashIcon from '@heroicons/react/24/outline/TrashIcon';
|
|
|
11
11
|
import PlusIcon from '@heroicons/react/24/outline/PlusIcon';
|
|
12
12
|
import { getCtx } from '@/stores/nodes';
|
|
13
13
|
import { settingsPanelStore } from '@/stores/storykeep';
|
|
14
|
+
import { toolDragStore, startToolDrag } from '@/stores/toolDrag';
|
|
15
|
+
import { initToolDragListeners } from '@/utils/compositor/toolDragManager';
|
|
14
16
|
import { handleClickEventDefault } from '@/utils/compositor/handleClickEvent';
|
|
15
17
|
import { getTemplateNode } from '@/utils/compositor/nodesHelper';
|
|
16
18
|
import { classNames } from '@/utils/helpers';
|
|
@@ -44,9 +46,9 @@ export const NodeOverlay = ({
|
|
|
44
46
|
const toolMode = useStore(ctx.toolModeValStore).value;
|
|
45
47
|
const toolAddMode = useStore(ctx.toolAddModeStore).value;
|
|
46
48
|
const settingsPanel = useStore(settingsPanelStore);
|
|
49
|
+
const dragStore = useStore(toolDragStore);
|
|
47
50
|
const [hoverZone, setHoverZone] = useState<'before' | 'after' | null>(null);
|
|
48
51
|
|
|
49
|
-
// put a contentEditable={false} component inside a tree that inherits contentEditable={true}.
|
|
50
52
|
const chromeRef = useRef<HTMLDivElement>(null);
|
|
51
53
|
|
|
52
54
|
useEffect(() => {
|
|
@@ -92,11 +94,30 @@ export const NodeOverlay = ({
|
|
|
92
94
|
}
|
|
93
95
|
};
|
|
94
96
|
|
|
97
|
+
const handleReorderStart = (e: MouseEvent) => {
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
e.stopPropagation();
|
|
100
|
+
startToolDrag('reorder', nodeId, e.clientX, e.clientY);
|
|
101
|
+
initToolDragListeners();
|
|
102
|
+
};
|
|
103
|
+
|
|
95
104
|
const canInsert =
|
|
96
105
|
toolMode === 'insert'
|
|
97
106
|
? ctx.allowInsert(nodeId, toolAddMode || 'p')
|
|
98
107
|
: { allowInsertBefore: false, allowInsertAfter: false };
|
|
99
108
|
|
|
109
|
+
const isReorderMode = toolMode === 'reorder';
|
|
110
|
+
const isDragging = dragStore.isDragging;
|
|
111
|
+
|
|
112
|
+
const showZones =
|
|
113
|
+
(toolMode === 'insert' && toolAddMode !== `span`) ||
|
|
114
|
+
(isReorderMode && isDragging);
|
|
115
|
+
|
|
116
|
+
const isDragTarget = dragStore.activeDropZone?.nodeId === nodeId;
|
|
117
|
+
const activeLocation = isDragTarget
|
|
118
|
+
? dragStore.activeDropZone?.location
|
|
119
|
+
: null;
|
|
120
|
+
|
|
100
121
|
const iconSrc = getIconForTag(node.tagName);
|
|
101
122
|
|
|
102
123
|
return (
|
|
@@ -104,14 +125,19 @@ export const NodeOverlay = ({
|
|
|
104
125
|
className={classNames(
|
|
105
126
|
'compositor-wrapper group relative transition-all duration-200',
|
|
106
127
|
zIndexClass,
|
|
107
|
-
toolMode === 'text' ? outlineClass : ''
|
|
128
|
+
toolMode === 'text' ? outlineClass : '',
|
|
129
|
+
isReorderMode && !isDragging
|
|
130
|
+
? 'cursor-grab hover:outline hover:outline-dotted hover:outline-2 hover:outline-offset-2 hover:outline-cyan-500'
|
|
131
|
+
: ''
|
|
108
132
|
)}
|
|
109
133
|
style={isInline ? { display: 'inline-block' } : {}}
|
|
110
134
|
data-node-overlay={nodeId}
|
|
135
|
+
onMouseDown={
|
|
136
|
+
isReorderMode && !isDragging ? handleReorderStart : undefined
|
|
137
|
+
}
|
|
111
138
|
>
|
|
112
139
|
{children}
|
|
113
140
|
|
|
114
|
-
{/* Text Mode: Tool Cart */}
|
|
115
141
|
{toolMode === 'text' && (
|
|
116
142
|
<div
|
|
117
143
|
ref={chromeRef}
|
|
@@ -156,18 +182,19 @@ export const NodeOverlay = ({
|
|
|
156
182
|
</div>
|
|
157
183
|
)}
|
|
158
184
|
|
|
159
|
-
{
|
|
160
|
-
{toolMode === 'insert' && toolAddMode !== `span` && (
|
|
185
|
+
{showZones && (
|
|
161
186
|
<div
|
|
162
187
|
className="compositor-chrome absolute inset-0 z-50 flex flex-col"
|
|
163
188
|
data-attr="exclude"
|
|
164
189
|
>
|
|
165
|
-
{/* Top / Before Zone */}
|
|
166
190
|
<div
|
|
167
191
|
className={classNames(
|
|
168
192
|
'flex-1 transition-colors duration-200',
|
|
169
|
-
|
|
170
|
-
? '
|
|
193
|
+
activeLocation === 'before'
|
|
194
|
+
? 'bg-blue-500 bg-opacity-10'
|
|
195
|
+
: 'hover:bg-blue-500 hover:bg-opacity-10',
|
|
196
|
+
canInsert.allowInsertBefore || isReorderMode
|
|
197
|
+
? 'cursor-pointer'
|
|
171
198
|
: 'cursor-not-allowed opacity-0'
|
|
172
199
|
)}
|
|
173
200
|
onMouseEnter={() => setHoverZone('before')}
|
|
@@ -176,7 +203,7 @@ export const NodeOverlay = ({
|
|
|
176
203
|
canInsert.allowInsertBefore && handleInsert('before', e)
|
|
177
204
|
}
|
|
178
205
|
>
|
|
179
|
-
{canInsert.allowInsertBefore && (
|
|
206
|
+
{toolMode === 'insert' && canInsert.allowInsertBefore && (
|
|
180
207
|
<div
|
|
181
208
|
className={classNames(
|
|
182
209
|
'absolute left-1/2 top-0 -translate-x-1/2 -translate-y-1/2 transform transition-opacity duration-200',
|
|
@@ -190,12 +217,14 @@ export const NodeOverlay = ({
|
|
|
190
217
|
)}
|
|
191
218
|
</div>
|
|
192
219
|
|
|
193
|
-
{/* Bottom / After Zone */}
|
|
194
220
|
<div
|
|
195
221
|
className={classNames(
|
|
196
222
|
'flex-1 transition-colors duration-200',
|
|
197
|
-
|
|
198
|
-
? '
|
|
223
|
+
activeLocation === 'after'
|
|
224
|
+
? 'bg-blue-500 bg-opacity-10'
|
|
225
|
+
: 'hover:bg-blue-500 hover:bg-opacity-10',
|
|
226
|
+
canInsert.allowInsertAfter || isReorderMode
|
|
227
|
+
? 'cursor-pointer'
|
|
199
228
|
: 'cursor-not-allowed opacity-0'
|
|
200
229
|
)}
|
|
201
230
|
onMouseEnter={() => setHoverZone('after')}
|
|
@@ -204,7 +233,7 @@ export const NodeOverlay = ({
|
|
|
204
233
|
canInsert.allowInsertAfter && handleInsert('after', e)
|
|
205
234
|
}
|
|
206
235
|
>
|
|
207
|
-
{canInsert.allowInsertAfter && (
|
|
236
|
+
{toolMode === 'insert' && canInsert.allowInsertAfter && (
|
|
208
237
|
<div
|
|
209
238
|
className={classNames(
|
|
210
239
|
'absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-1/2 transform transition-opacity duration-200',
|
|
@@ -3,6 +3,8 @@ import XMarkIcon from '@heroicons/react/24/outline/XMarkIcon';
|
|
|
3
3
|
import { getCtx } from '@/stores/nodes';
|
|
4
4
|
import { toggleSettingsPanel } from '@/stores/storykeep';
|
|
5
5
|
import { toolAddModeTitles, toolAddModes } from '@/constants';
|
|
6
|
+
import { startToolDrag } from '@/stores/toolDrag';
|
|
7
|
+
import { initToolDragListeners } from '@/utils/compositor/toolDragManager';
|
|
6
8
|
|
|
7
9
|
import type { ToolAddMode } from '@/types/compositorTypes';
|
|
8
10
|
|
|
@@ -18,11 +20,18 @@ const AddElementsPanel = ({
|
|
|
18
20
|
ctx.notifyNode('root');
|
|
19
21
|
};
|
|
20
22
|
|
|
23
|
+
const handleMouseDown = (e: React.MouseEvent, mode: ToolAddMode) => {
|
|
24
|
+
e.preventDefault();
|
|
25
|
+
startToolDrag('insert', mode, e.clientX, e.clientY);
|
|
26
|
+
initToolDragListeners();
|
|
27
|
+
};
|
|
28
|
+
|
|
21
29
|
return (
|
|
22
30
|
<>
|
|
23
31
|
{toolAddModes.map((mode) => (
|
|
24
32
|
<button
|
|
25
33
|
key={mode}
|
|
34
|
+
onMouseDown={(e) => handleMouseDown(e, mode)}
|
|
26
35
|
onClick={() => handleElementClick(mode)}
|
|
27
36
|
className={`rounded px-3 py-1.5 text-sm font-bold transition-colors ${
|
|
28
37
|
currentToolAddMode === mode
|
|
@@ -39,12 +48,9 @@ const AddElementsPanel = ({
|
|
|
39
48
|
|
|
40
49
|
const StoryKeepToolBar = () => {
|
|
41
50
|
const ctx = getCtx();
|
|
42
|
-
|
|
43
|
-
// Connect to stores
|
|
44
51
|
const { value: toolModeVal } = useStore(ctx.toolModeValStore);
|
|
45
52
|
const { value: toolAddModeVal } = useStore(ctx.toolAddModeStore);
|
|
46
53
|
|
|
47
|
-
// Only show when in insert mode
|
|
48
54
|
if (toolModeVal !== 'insert') {
|
|
49
55
|
return null;
|
|
50
56
|
}
|
|
@@ -67,6 +73,9 @@ const StoryKeepToolBar = () => {
|
|
|
67
73
|
<div className="flex flex-wrap gap-x-2 gap-y-1">
|
|
68
74
|
<AddElementsPanel currentToolAddMode={toolAddModeVal} />
|
|
69
75
|
</div>
|
|
76
|
+
<p className="px-2 pt-4 text-xs">
|
|
77
|
+
Drag and drop, or select element and click the + to insert into a pane.
|
|
78
|
+
</p>
|
|
70
79
|
</div>
|
|
71
80
|
);
|
|
72
81
|
};
|
|
@@ -2,11 +2,8 @@ import { useRef, useEffect } from 'react';
|
|
|
2
2
|
import { useStore } from '@nanostores/react';
|
|
3
3
|
import PencilSquareIcon from '@heroicons/react/24/outline/PencilSquareIcon';
|
|
4
4
|
import PaintBrushIcon from '@heroicons/react/24/outline/PaintBrushIcon';
|
|
5
|
-
import TrashIcon from '@heroicons/react/24/outline/TrashIcon';
|
|
6
|
-
import SparklesIcon from '@heroicons/react/24/outline/SparklesIcon';
|
|
7
5
|
import ArrowsUpDownIcon from '@heroicons/react/24/outline/ArrowsUpDownIcon';
|
|
8
6
|
import PlusIcon from '@heroicons/react/24/outline/PlusIcon';
|
|
9
|
-
import BugAntIcon from '@heroicons/react/24/outline/BugAntIcon';
|
|
10
7
|
import LinkIcon from '@heroicons/react/24/solid/LinkIcon';
|
|
11
8
|
import ChatBubbleBottomCenterTextIcon from '@heroicons/react/24/outline/ChatBubbleBottomCenterTextIcon';
|
|
12
9
|
import XMarkIcon from '@heroicons/react/24/solid/XMarkIcon';
|
|
@@ -29,6 +26,12 @@ const storykeepToolModes = [
|
|
|
29
26
|
title: 'Add',
|
|
30
27
|
description: 'Add new element, e.g. paragraph or image',
|
|
31
28
|
},
|
|
29
|
+
{
|
|
30
|
+
key: 'reorder',
|
|
31
|
+
Icon: ArrowsUpDownIcon,
|
|
32
|
+
title: 'Reorder',
|
|
33
|
+
description: 'Drag and drop to reorder elements',
|
|
34
|
+
},
|
|
32
35
|
] as const;
|
|
33
36
|
|
|
34
37
|
interface StoryKeepToolModeProps {
|
|
@@ -12,6 +12,7 @@ import StoryKeepToolBar from '@/components/edit/ToolBar';
|
|
|
12
12
|
import StoryKeepToolMode from '@/components/edit/ToolMode';
|
|
13
13
|
import SettingsPanel from '@/components/edit/SettingsPanel';
|
|
14
14
|
import { Compositor } from '@/components/compositor/Compositor';
|
|
15
|
+
import ToolDragLayer from '@/components/compositor/ToolDragLayer';
|
|
15
16
|
import { requireAdminOrEditor } from '@/utils/auth';
|
|
16
17
|
import { preHealthCheck } from '@/utils/backend';
|
|
17
18
|
|
|
@@ -217,6 +218,7 @@ for (const [key, value] of Astro.url.searchParams) {
|
|
|
217
218
|
/>
|
|
218
219
|
</div>
|
|
219
220
|
</aside>
|
|
221
|
+
<ToolDragLayer client:only="react" />
|
|
220
222
|
</Layout>
|
|
221
223
|
|
|
222
224
|
<script>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { map } from 'nanostores';
|
|
2
|
+
|
|
3
|
+
export type DragType = 'insert' | 'reorder';
|
|
4
|
+
|
|
5
|
+
export interface ToolDragState {
|
|
6
|
+
isDragging: boolean;
|
|
7
|
+
dragType: DragType | null;
|
|
8
|
+
payload: string | null;
|
|
9
|
+
pointer: { x: number; y: number };
|
|
10
|
+
activeDropZone: {
|
|
11
|
+
nodeId: string;
|
|
12
|
+
location: 'before' | 'after';
|
|
13
|
+
} | null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const toolDragStore = map<ToolDragState>({
|
|
17
|
+
isDragging: false,
|
|
18
|
+
dragType: null,
|
|
19
|
+
payload: null,
|
|
20
|
+
pointer: { x: 0, y: 0 },
|
|
21
|
+
activeDropZone: null,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const startToolDrag = (
|
|
25
|
+
type: DragType,
|
|
26
|
+
payload: string,
|
|
27
|
+
startX: number,
|
|
28
|
+
startY: number
|
|
29
|
+
) => {
|
|
30
|
+
toolDragStore.set({
|
|
31
|
+
isDragging: true,
|
|
32
|
+
dragType: type,
|
|
33
|
+
payload,
|
|
34
|
+
pointer: { x: startX, y: startY },
|
|
35
|
+
activeDropZone: null,
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const updateToolDragPosition = (x: number, y: number) => {
|
|
40
|
+
toolDragStore.setKey('pointer', { x, y });
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const updateActiveDropZone = (
|
|
44
|
+
zone: { nodeId: string; location: 'before' | 'after' } | null
|
|
45
|
+
) => {
|
|
46
|
+
toolDragStore.setKey('activeDropZone', zone);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const endToolDrag = () => {
|
|
50
|
+
toolDragStore.set({
|
|
51
|
+
isDragging: false,
|
|
52
|
+
dragType: null,
|
|
53
|
+
payload: null,
|
|
54
|
+
pointer: { x: 0, y: 0 },
|
|
55
|
+
activeDropZone: null,
|
|
56
|
+
});
|
|
57
|
+
};
|
|
@@ -4,7 +4,7 @@ export type LispToken = string | number | LispToken[];
|
|
|
4
4
|
|
|
5
5
|
export type ViewportKey = 'mobile' | 'tablet' | 'desktop' | 'auto';
|
|
6
6
|
export type ViewportAuto = 'mobile' | 'tablet' | 'desktop';
|
|
7
|
-
export type ToolModeVal = 'text' | 'insert';
|
|
7
|
+
export type ToolModeVal = 'text' | 'insert' | 'reorder';
|
|
8
8
|
|
|
9
9
|
export const toolAddModes = [
|
|
10
10
|
'p',
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {
|
|
2
|
+
toolDragStore,
|
|
3
|
+
updateToolDragPosition,
|
|
4
|
+
updateActiveDropZone,
|
|
5
|
+
endToolDrag,
|
|
6
|
+
} from '@/stores/toolDrag';
|
|
7
|
+
import { getCtx } from '@/stores/nodes';
|
|
8
|
+
import { getTemplateNode } from '@/utils/compositor/nodesHelper';
|
|
9
|
+
import type { ToolAddMode } from '@/types/compositorTypes';
|
|
10
|
+
|
|
11
|
+
export const initToolDragListeners = () => {
|
|
12
|
+
const handleMouseMove = (e: MouseEvent) => {
|
|
13
|
+
const state = toolDragStore.get();
|
|
14
|
+
if (!state.isDragging) return;
|
|
15
|
+
|
|
16
|
+
e.preventDefault();
|
|
17
|
+
updateToolDragPosition(e.clientX, e.clientY);
|
|
18
|
+
|
|
19
|
+
const elements = document.elementsFromPoint(e.clientX, e.clientY);
|
|
20
|
+
const targetOverlay = elements.find((el) =>
|
|
21
|
+
el.hasAttribute('data-node-overlay')
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
if (!targetOverlay) {
|
|
25
|
+
updateActiveDropZone(null);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const nodeId = targetOverlay.getAttribute('data-node-overlay');
|
|
30
|
+
if (!nodeId) return;
|
|
31
|
+
|
|
32
|
+
if (state.dragType === 'reorder' && state.payload === nodeId) {
|
|
33
|
+
updateActiveDropZone(null);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const rect = targetOverlay.getBoundingClientRect();
|
|
38
|
+
const midPoint = rect.top + rect.height / 2;
|
|
39
|
+
const location = e.clientY < midPoint ? 'before' : 'after';
|
|
40
|
+
|
|
41
|
+
updateActiveDropZone({ nodeId, location });
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handleMouseUp = (_: MouseEvent) => {
|
|
45
|
+
const state = toolDragStore.get();
|
|
46
|
+
if (!state.isDragging) return;
|
|
47
|
+
|
|
48
|
+
if (state.activeDropZone) {
|
|
49
|
+
const ctx = getCtx();
|
|
50
|
+
const { nodeId, location } = state.activeDropZone;
|
|
51
|
+
|
|
52
|
+
if (state.dragType === 'reorder' && state.payload) {
|
|
53
|
+
ctx.moveNodeTo(state.payload, nodeId, location);
|
|
54
|
+
} else if (state.dragType === 'insert' && state.payload) {
|
|
55
|
+
const template = getTemplateNode(state.payload as ToolAddMode);
|
|
56
|
+
if (template) {
|
|
57
|
+
ctx.addTemplateNode(nodeId, template, nodeId, location);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
endToolDrag();
|
|
63
|
+
window.removeEventListener('mousemove', handleMouseMove);
|
|
64
|
+
window.removeEventListener('mouseup', handleMouseUp);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
window.addEventListener('mousemove', handleMouseMove);
|
|
68
|
+
window.addEventListener('mouseup', handleMouseUp);
|
|
69
|
+
};
|
package/utils/inject-files.ts
CHANGED
|
@@ -95,6 +95,10 @@ export async function injectTemplateFiles(
|
|
|
95
95
|
src: resolve('../templates/src/components/compositor/Node.tsx'),
|
|
96
96
|
dest: 'src/components/compositor/Node.tsx',
|
|
97
97
|
},
|
|
98
|
+
{
|
|
99
|
+
src: resolve('../templates/src/components/compositor/ToolDragLayer.tsx'),
|
|
100
|
+
dest: 'src/components/compositor/ToolDragLayer.tsx',
|
|
101
|
+
},
|
|
98
102
|
{
|
|
99
103
|
src: resolve(
|
|
100
104
|
'../templates/src/components/compositor/tools/NodeOverlay.tsx'
|
|
@@ -576,6 +580,10 @@ export async function injectTemplateFiles(
|
|
|
576
580
|
src: resolve('../templates/src/stores/backend.ts'),
|
|
577
581
|
dest: 'src/stores/backend.ts',
|
|
578
582
|
},
|
|
583
|
+
{
|
|
584
|
+
src: resolve('../templates/src/stores/toolDrag.ts'),
|
|
585
|
+
dest: 'src/stores/toolDrag.ts',
|
|
586
|
+
},
|
|
579
587
|
{
|
|
580
588
|
src: resolve('../templates/src/stores/resources.ts'),
|
|
581
589
|
dest: 'src/stores/resources.ts',
|
|
@@ -621,6 +629,10 @@ export async function injectTemplateFiles(
|
|
|
621
629
|
src: resolve('../templates/src/utils/compositor/savePipeline.ts'),
|
|
622
630
|
dest: 'src/utils/compositor/savePipeline.ts',
|
|
623
631
|
},
|
|
632
|
+
{
|
|
633
|
+
src: resolve('../templates/src/utils/compositor/toolDragManager.ts'),
|
|
634
|
+
dest: 'src/utils/compositor/toolDragManager.ts',
|
|
635
|
+
},
|
|
624
636
|
{
|
|
625
637
|
src: resolve('../templates/src/utils/compositor/aiPaneParser.ts'),
|
|
626
638
|
dest: 'src/utils/compositor/aiPaneParser.ts',
|