@mingxy/opencode-mascot 0.2.5 → 0.2.6
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/package.json
CHANGED
|
@@ -5,7 +5,6 @@ import type { JSX } from "@opentui/solid";
|
|
|
5
5
|
import type { MascotPack } from "../core/types";
|
|
6
6
|
import { createAnimatedRenderer } from "../core/ascii-renderer";
|
|
7
7
|
import { onCelebrate } from "../core/celebration-bus";
|
|
8
|
-
import { useDraggableMascot } from "./use-draggable-mascot";
|
|
9
8
|
|
|
10
9
|
interface HomeMascotProps {
|
|
11
10
|
mascots: Record<string, MascotPack>;
|
|
@@ -21,6 +20,14 @@ export function HomeMascot(props: HomeMascotProps): JSX.Element {
|
|
|
21
20
|
const initialName = names[Math.floor(Math.random() * names.length)];
|
|
22
21
|
|
|
23
22
|
const [currentName, setCurrentName] = createSignal(initialName);
|
|
23
|
+
const [posX, setPosX] = createSignal(0);
|
|
24
|
+
const [posY, setPosY] = createSignal(0);
|
|
25
|
+
let dragStartX = 0;
|
|
26
|
+
let dragStartY = 0;
|
|
27
|
+
let dragAnchorX = 0;
|
|
28
|
+
let dragAnchorY = 0;
|
|
29
|
+
let lastClickTime = 0;
|
|
30
|
+
let isDragging = false;
|
|
24
31
|
|
|
25
32
|
const renderers: Record<string, ReturnType<typeof createAnimatedRenderer>> = {};
|
|
26
33
|
for (const [name, pack] of Object.entries(props.mascots)) {
|
|
@@ -33,17 +40,6 @@ export function HomeMascot(props: HomeMascotProps): JSX.Element {
|
|
|
33
40
|
setCurrentName(names[(idx + 1) % names.length]);
|
|
34
41
|
};
|
|
35
42
|
|
|
36
|
-
const { posX, posY, mouseProps } = useDraggableMascot({
|
|
37
|
-
initialX: 0,
|
|
38
|
-
initialY: 0,
|
|
39
|
-
mascotWidth: 10,
|
|
40
|
-
mascotHeight: 5,
|
|
41
|
-
onSwitch: switchToNext,
|
|
42
|
-
clearSelection: () => props.api.renderer.clearSelection(),
|
|
43
|
-
setDragging: (v) => renderers[currentName()].setDragging(v),
|
|
44
|
-
enableEdge: false,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
43
|
onCelebrate((newVersion) => {
|
|
48
44
|
renderers[currentName()].celebrateUpdate(newVersion);
|
|
49
45
|
});
|
|
@@ -55,7 +51,48 @@ export function HomeMascot(props: HomeMascotProps): JSX.Element {
|
|
|
55
51
|
alignItems="center"
|
|
56
52
|
zIndex={100}
|
|
57
53
|
flexDirection="column"
|
|
58
|
-
{
|
|
54
|
+
onMouseDown={(e: any) => {
|
|
55
|
+
const now = Date.now();
|
|
56
|
+
if (now - lastClickTime < 300) {
|
|
57
|
+
switchToNext();
|
|
58
|
+
lastClickTime = 0;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
lastClickTime = now;
|
|
62
|
+
|
|
63
|
+
if (e.modifiers?.alt) {
|
|
64
|
+
dragStartX = e.x;
|
|
65
|
+
dragStartY = e.y;
|
|
66
|
+
dragAnchorX = posX();
|
|
67
|
+
dragAnchorY = posY();
|
|
68
|
+
isDragging = true;
|
|
69
|
+
renderers[currentName()].setDragging(true);
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
e.stopPropagation();
|
|
72
|
+
props.api.renderer.clearSelection();
|
|
73
|
+
}
|
|
74
|
+
}}
|
|
75
|
+
onMouseDrag={(e: any) => {
|
|
76
|
+
if (e.modifiers?.alt && isDragging) {
|
|
77
|
+
setPosX(dragAnchorX + (e.x - dragStartX));
|
|
78
|
+
setPosY(dragAnchorY + (e.y - dragStartY));
|
|
79
|
+
e.preventDefault();
|
|
80
|
+
e.stopPropagation();
|
|
81
|
+
props.api.renderer.clearSelection();
|
|
82
|
+
}
|
|
83
|
+
}}
|
|
84
|
+
onMouseUp={() => {
|
|
85
|
+
if (isDragging) {
|
|
86
|
+
isDragging = false;
|
|
87
|
+
renderers[currentName()].setDragging(false);
|
|
88
|
+
}
|
|
89
|
+
}}
|
|
90
|
+
onMouseDragEnd={() => {
|
|
91
|
+
if (isDragging) {
|
|
92
|
+
isDragging = false;
|
|
93
|
+
renderers[currentName()].setDragging(false);
|
|
94
|
+
}
|
|
95
|
+
}}
|
|
59
96
|
>
|
|
60
97
|
{renderers[currentName()]?.element() ?? null}
|
|
61
98
|
</box>
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/** @jsxImportSource @opentui/solid */
|
|
2
2
|
|
|
3
|
-
import { createSignal } from "solid-js";
|
|
3
|
+
import { createSignal, onCleanup } from "solid-js";
|
|
4
4
|
import type { JSX } from "@opentui/solid";
|
|
5
5
|
import type { MascotPack, MascotState } from "../core/types";
|
|
6
6
|
import { createAnimatedRenderer } from "../core/ascii-renderer";
|
|
7
7
|
import { onCelebrate } from "../core/celebration-bus";
|
|
8
|
-
import { useDraggableMascot } from "./use-draggable-mascot";
|
|
9
8
|
|
|
10
9
|
interface SidebarMascotProps {
|
|
11
10
|
mascots: Record<string, MascotPack>;
|
|
@@ -30,6 +29,12 @@ const DEFAULT_STATE_MAP: Partial<Record<MascotState, string>> = {
|
|
|
30
29
|
|
|
31
30
|
const MASCOT_WIDTH = 10;
|
|
32
31
|
const MASCOT_HEIGHT = 5;
|
|
32
|
+
const PEEK = 2;
|
|
33
|
+
const PEEK_INTERVAL = 1200;
|
|
34
|
+
|
|
35
|
+
function termWidth(): number {
|
|
36
|
+
return (typeof process !== "undefined" && process.stdout?.columns) || 80;
|
|
37
|
+
}
|
|
33
38
|
|
|
34
39
|
export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
35
40
|
const names = Object.keys(props.mascots);
|
|
@@ -39,41 +44,101 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
|
39
44
|
: names[Math.floor(Math.random() * names.length)];
|
|
40
45
|
|
|
41
46
|
const [currentName, setCurrentName] = createSignal(initialName);
|
|
47
|
+
const [posX, setPosX] = createSignal(20);
|
|
48
|
+
const [posY, setPosY] = createSignal(2);
|
|
49
|
+
let dragStartX = 0;
|
|
50
|
+
let dragStartY = 0;
|
|
51
|
+
let dragAnchorX = 0;
|
|
52
|
+
let dragAnchorY = 0;
|
|
53
|
+
let lastClickTime = 0;
|
|
54
|
+
let isDragging = false;
|
|
55
|
+
let hideSide: "left" | "right" | null = null;
|
|
56
|
+
let peekTimer: ReturnType<typeof setInterval> | null = null;
|
|
57
|
+
let returnTimer: ReturnType<typeof setInterval> | null = null;
|
|
42
58
|
|
|
43
59
|
const renderers: Record<string, ReturnType<typeof createAnimatedRenderer>> = {};
|
|
44
60
|
for (const [name, pack] of Object.entries(props.mascots)) {
|
|
45
61
|
renderers[name] = createAnimatedRenderer(pack);
|
|
46
62
|
}
|
|
47
63
|
|
|
64
|
+
const stopPeek = () => {
|
|
65
|
+
if (peekTimer) { clearInterval(peekTimer); peekTimer = null; }
|
|
66
|
+
};
|
|
67
|
+
const stopReturn = () => {
|
|
68
|
+
if (returnTimer) { clearInterval(returnTimer); returnTimer = null; }
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
onCleanup(() => { stopPeek(); stopReturn(); });
|
|
72
|
+
|
|
48
73
|
const switchToNext = () => {
|
|
49
74
|
const cur = currentName();
|
|
50
75
|
const idx = names.indexOf(cur);
|
|
51
76
|
setCurrentName(names[(idx + 1) % names.length]);
|
|
52
77
|
};
|
|
53
78
|
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
79
|
+
const startPeek = () => {
|
|
80
|
+
stopPeek();
|
|
81
|
+
let phase = false;
|
|
82
|
+
peekTimer = setInterval(() => {
|
|
83
|
+
phase = !phase;
|
|
84
|
+
const stretch = phase ? PEEK : 0;
|
|
85
|
+
const w = termWidth();
|
|
86
|
+
if (hideSide === "left") {
|
|
87
|
+
setPosX(-(MASCOT_WIDTH - PEEK) + stretch);
|
|
88
|
+
} else if (hideSide === "right") {
|
|
89
|
+
setPosX(w - PEEK - stretch);
|
|
90
|
+
}
|
|
91
|
+
}, PEEK_INTERVAL);
|
|
92
|
+
};
|
|
64
93
|
|
|
65
|
-
const
|
|
66
|
-
if (
|
|
67
|
-
|
|
94
|
+
const returnToView = () => {
|
|
95
|
+
if (!hideSide) return;
|
|
96
|
+
stopPeek();
|
|
97
|
+
const w = termWidth();
|
|
98
|
+
const cur = posX();
|
|
99
|
+
const targetX = hideSide === "left" ? 0 : Math.max(0, w - MASCOT_WIDTH);
|
|
100
|
+
const step = targetX > cur ? 2 : -2;
|
|
101
|
+
returnTimer = setInterval(() => {
|
|
102
|
+
const now = posX();
|
|
103
|
+
if (Math.abs(now - targetX) <= 2) {
|
|
104
|
+
setPosX(targetX);
|
|
105
|
+
stopReturn();
|
|
106
|
+
hideSide = null;
|
|
107
|
+
renderers[currentName()].bounce();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
setPosX(now + step);
|
|
111
|
+
}, 16);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const checkEdge = () => {
|
|
115
|
+
const w = termWidth();
|
|
116
|
+
const x = posX();
|
|
117
|
+
if (x <= -(MASCOT_WIDTH - PEEK) + 1) {
|
|
118
|
+
hideSide = "left";
|
|
119
|
+
startPeek();
|
|
120
|
+
} else if (x >= w - PEEK - 1) {
|
|
121
|
+
hideSide = "right";
|
|
122
|
+
startPeek();
|
|
68
123
|
}
|
|
69
124
|
};
|
|
70
125
|
|
|
126
|
+
const clampX = (rawX: number): number => {
|
|
127
|
+
const w = termWidth();
|
|
128
|
+
const minX = -(MASCOT_WIDTH - PEEK);
|
|
129
|
+
const maxX = w - PEEK;
|
|
130
|
+
return Math.max(minX, Math.min(rawX, maxX));
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const clampY = (rawY: number): number => {
|
|
134
|
+
const h = (typeof process !== "undefined" && process.stdout?.rows) || 24;
|
|
135
|
+
return Math.max(0, Math.min(rawY, h - MASCOT_HEIGHT));
|
|
136
|
+
};
|
|
137
|
+
|
|
71
138
|
const setStateWithSwitch = (s: MascotState) => {
|
|
72
139
|
const cur = currentName();
|
|
73
140
|
renderers[cur].setState(s);
|
|
74
|
-
|
|
75
|
-
const stateMap = DEFAULT_STATE_MAP;
|
|
76
|
-
const target = stateMap[s];
|
|
141
|
+
const target = DEFAULT_STATE_MAP[s];
|
|
77
142
|
if (target && target !== cur && props.mascots[target]) {
|
|
78
143
|
setCurrentName(target);
|
|
79
144
|
renderers[target].setState(s);
|
|
@@ -83,9 +148,8 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
|
83
148
|
props.api.event.on("session.status", (data: unknown) => {
|
|
84
149
|
const payload = data as { type?: string; properties?: { sessionID?: string; status?: { type?: string } } } | null;
|
|
85
150
|
const statusType = payload?.properties?.status?.type;
|
|
86
|
-
|
|
87
151
|
if (statusType === "busy" || statusType === "retry") {
|
|
88
|
-
returnToView();
|
|
152
|
+
if (hideSide) returnToView();
|
|
89
153
|
renderers[currentName()].setState("busy");
|
|
90
154
|
} else {
|
|
91
155
|
setStateWithSwitch("idle");
|
|
@@ -100,12 +164,10 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
|
100
164
|
props.api.event.on("mascot.switch", (data: unknown) => {
|
|
101
165
|
const target = data as { name?: string } | null;
|
|
102
166
|
if (target?.name) {
|
|
103
|
-
|
|
167
|
+
const name = target.name;
|
|
168
|
+
if (props.mascots[name] && name !== currentName()) setCurrentName(name);
|
|
104
169
|
} else {
|
|
105
|
-
|
|
106
|
-
if (others.length > 0) {
|
|
107
|
-
switchTo(others[Math.floor(Math.random() * others.length)]);
|
|
108
|
-
}
|
|
170
|
+
switchToNext();
|
|
109
171
|
}
|
|
110
172
|
});
|
|
111
173
|
|
|
@@ -125,7 +187,52 @@ export function SidebarMascot(props: SidebarMascotProps): JSX.Element {
|
|
|
125
187
|
alignItems="center"
|
|
126
188
|
zIndex={100}
|
|
127
189
|
flexDirection="column"
|
|
128
|
-
{
|
|
190
|
+
onMouseDown={(e: any) => {
|
|
191
|
+
if (hideSide) { returnToView(); return; }
|
|
192
|
+
|
|
193
|
+
const now = Date.now();
|
|
194
|
+
if (now - lastClickTime < 300) {
|
|
195
|
+
switchToNext();
|
|
196
|
+
lastClickTime = 0;
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
lastClickTime = now;
|
|
200
|
+
|
|
201
|
+
if (e.modifiers?.alt) {
|
|
202
|
+
dragStartX = e.x;
|
|
203
|
+
dragStartY = e.y;
|
|
204
|
+
dragAnchorX = posX();
|
|
205
|
+
dragAnchorY = posY();
|
|
206
|
+
isDragging = true;
|
|
207
|
+
renderers[currentName()].setDragging(true);
|
|
208
|
+
e.preventDefault();
|
|
209
|
+
e.stopPropagation();
|
|
210
|
+
props.api.renderer.clearSelection();
|
|
211
|
+
}
|
|
212
|
+
}}
|
|
213
|
+
onMouseDrag={(e: any) => {
|
|
214
|
+
if (e.modifiers?.alt && isDragging) {
|
|
215
|
+
setPosX(clampX(dragAnchorX + (e.x - dragStartX)));
|
|
216
|
+
setPosY(clampY(dragAnchorY + (e.y - dragStartY)));
|
|
217
|
+
e.preventDefault();
|
|
218
|
+
e.stopPropagation();
|
|
219
|
+
props.api.renderer.clearSelection();
|
|
220
|
+
}
|
|
221
|
+
}}
|
|
222
|
+
onMouseUp={() => {
|
|
223
|
+
if (isDragging) {
|
|
224
|
+
isDragging = false;
|
|
225
|
+
renderers[currentName()].setDragging(false);
|
|
226
|
+
checkEdge();
|
|
227
|
+
}
|
|
228
|
+
}}
|
|
229
|
+
onMouseDragEnd={() => {
|
|
230
|
+
if (isDragging) {
|
|
231
|
+
isDragging = false;
|
|
232
|
+
renderers[currentName()].setDragging(false);
|
|
233
|
+
checkEdge();
|
|
234
|
+
}
|
|
235
|
+
}}
|
|
129
236
|
>
|
|
130
237
|
{renderers[currentName()]?.element() ?? null}
|
|
131
238
|
</box>
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
/** @jsxImportSource @opentui/solid */
|
|
2
|
-
|
|
3
|
-
import { createSignal, onCleanup, type Accessor } from "solid-js";
|
|
4
|
-
|
|
5
|
-
type DragState = "normal" | "edge_hidden" | "returning";
|
|
6
|
-
type HideSide = "left" | "right" | null;
|
|
7
|
-
|
|
8
|
-
export interface MouseProps {
|
|
9
|
-
onMouseDown: (e: any) => void;
|
|
10
|
-
onMouseDrag: (e: any) => void;
|
|
11
|
-
onMouseUp: (e: any) => void;
|
|
12
|
-
onMouseDragEnd: (e: any) => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface UseDraggableOpts {
|
|
16
|
-
initialX: number;
|
|
17
|
-
initialY: number;
|
|
18
|
-
mascotWidth: number;
|
|
19
|
-
mascotHeight: number;
|
|
20
|
-
onSwitch: () => void;
|
|
21
|
-
clearSelection: () => void;
|
|
22
|
-
setDragging: (v: boolean) => void;
|
|
23
|
-
onReturnComplete?: () => void;
|
|
24
|
-
peekVisible?: number;
|
|
25
|
-
enableEdge?: boolean;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function getTermSize(): { width: number; height: number } {
|
|
29
|
-
const width = (typeof process !== "undefined" && process.stdout?.columns) || 80;
|
|
30
|
-
const height = (typeof process !== "undefined" && process.stdout?.rows) || 24;
|
|
31
|
-
return { width, height };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function useDraggableMascot(opts: UseDraggableOpts): {
|
|
35
|
-
posX: Accessor<number>;
|
|
36
|
-
posY: Accessor<number>;
|
|
37
|
-
mouseProps: MouseProps;
|
|
38
|
-
returnToView: () => void;
|
|
39
|
-
isHidden: () => boolean;
|
|
40
|
-
} {
|
|
41
|
-
const peek = opts.peekVisible ?? 2;
|
|
42
|
-
const enableEdge = opts.enableEdge ?? true;
|
|
43
|
-
|
|
44
|
-
const [posX, setPosX] = createSignal(opts.initialX);
|
|
45
|
-
const [posY, setPosY] = createSignal(opts.initialY);
|
|
46
|
-
|
|
47
|
-
let dragStartX = 0;
|
|
48
|
-
let dragStartY = 0;
|
|
49
|
-
let dragAnchorX = 0;
|
|
50
|
-
let dragAnchorY = 0;
|
|
51
|
-
let lastClickTime = 0;
|
|
52
|
-
let isDragging = false;
|
|
53
|
-
let state: DragState = "normal";
|
|
54
|
-
let hideSide: HideSide = null;
|
|
55
|
-
let peekTimer: ReturnType<typeof setInterval> | null = null;
|
|
56
|
-
let returnTimer: ReturnType<typeof setInterval> | null = null;
|
|
57
|
-
|
|
58
|
-
const stopPeek = () => {
|
|
59
|
-
if (peekTimer) { clearInterval(peekTimer); peekTimer = null; }
|
|
60
|
-
};
|
|
61
|
-
const stopReturn = () => {
|
|
62
|
-
if (returnTimer) { clearInterval(returnTimer); returnTimer = null; }
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
onCleanup(() => { stopPeek(); stopReturn(); });
|
|
66
|
-
|
|
67
|
-
const clampX = (rawX: number): number => {
|
|
68
|
-
if (!enableEdge) return rawX;
|
|
69
|
-
const { width } = getTermSize();
|
|
70
|
-
const minX = -(opts.mascotWidth - peek);
|
|
71
|
-
const maxX = width - peek;
|
|
72
|
-
return Math.max(minX, Math.min(rawX, maxX));
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const clampY = (rawY: number): number => {
|
|
76
|
-
if (!enableEdge) return rawY;
|
|
77
|
-
const { height } = getTermSize();
|
|
78
|
-
return Math.max(0, Math.min(rawY, height - opts.mascotHeight));
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const startPeek = () => {
|
|
82
|
-
stopPeek();
|
|
83
|
-
state = "edge_hidden";
|
|
84
|
-
let phase = false;
|
|
85
|
-
peekTimer = setInterval(() => {
|
|
86
|
-
phase = !phase;
|
|
87
|
-
const stretch = phase ? 2 : 0;
|
|
88
|
-
const { width } = getTermSize();
|
|
89
|
-
if (hideSide === "left") {
|
|
90
|
-
setPosX(-(opts.mascotWidth - peek) + stretch);
|
|
91
|
-
} else if (hideSide === "right") {
|
|
92
|
-
setPosX(width - peek - stretch);
|
|
93
|
-
}
|
|
94
|
-
}, 1200);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const returnToView = () => {
|
|
98
|
-
if (state !== "edge_hidden") return;
|
|
99
|
-
stopPeek();
|
|
100
|
-
state = "returning";
|
|
101
|
-
const { width } = getTermSize();
|
|
102
|
-
const cur = posX();
|
|
103
|
-
const targetX = hideSide === "left" ? 0 : Math.max(0, width - opts.mascotWidth);
|
|
104
|
-
const step = targetX > cur ? 2 : -2;
|
|
105
|
-
|
|
106
|
-
returnTimer = setInterval(() => {
|
|
107
|
-
const now = posX();
|
|
108
|
-
if (Math.abs(now - targetX) <= 2) {
|
|
109
|
-
setPosX(targetX);
|
|
110
|
-
stopReturn();
|
|
111
|
-
state = "normal";
|
|
112
|
-
hideSide = null;
|
|
113
|
-
opts.onReturnComplete?.();
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
setPosX(now + step);
|
|
117
|
-
}, 16);
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const checkEdgeOnRelease = () => {
|
|
121
|
-
if (!enableEdge) return;
|
|
122
|
-
const { width } = getTermSize();
|
|
123
|
-
const x = posX();
|
|
124
|
-
if (x <= -(opts.mascotWidth - peek) + 1) {
|
|
125
|
-
hideSide = "left";
|
|
126
|
-
startPeek();
|
|
127
|
-
} else if (x >= width - peek - 1) {
|
|
128
|
-
hideSide = "right";
|
|
129
|
-
startPeek();
|
|
130
|
-
}
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const mouseProps: MouseProps = {
|
|
134
|
-
onMouseDown(e: any) {
|
|
135
|
-
if (state === "edge_hidden") {
|
|
136
|
-
returnToView();
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
if (state === "returning") return;
|
|
140
|
-
|
|
141
|
-
const now = Date.now();
|
|
142
|
-
if (now - lastClickTime < 300) {
|
|
143
|
-
opts.onSwitch();
|
|
144
|
-
lastClickTime = 0;
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
lastClickTime = now;
|
|
148
|
-
|
|
149
|
-
if (e.modifiers?.alt) {
|
|
150
|
-
dragStartX = e.x;
|
|
151
|
-
dragStartY = e.y;
|
|
152
|
-
dragAnchorX = posX();
|
|
153
|
-
dragAnchorY = posY();
|
|
154
|
-
isDragging = true;
|
|
155
|
-
opts.setDragging(true);
|
|
156
|
-
e.preventDefault();
|
|
157
|
-
e.stopPropagation();
|
|
158
|
-
opts.clearSelection();
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
onMouseDrag(e: any) {
|
|
162
|
-
if (e.modifiers?.alt && isDragging) {
|
|
163
|
-
setPosX(clampX(dragAnchorX + (e.x - dragStartX)));
|
|
164
|
-
setPosY(clampY(dragAnchorY + (e.y - dragStartY)));
|
|
165
|
-
e.preventDefault();
|
|
166
|
-
e.stopPropagation();
|
|
167
|
-
opts.clearSelection();
|
|
168
|
-
}
|
|
169
|
-
},
|
|
170
|
-
onMouseUp() {
|
|
171
|
-
if (isDragging) {
|
|
172
|
-
isDragging = false;
|
|
173
|
-
opts.setDragging(false);
|
|
174
|
-
checkEdgeOnRelease();
|
|
175
|
-
}
|
|
176
|
-
},
|
|
177
|
-
onMouseDragEnd() {
|
|
178
|
-
if (isDragging) {
|
|
179
|
-
isDragging = false;
|
|
180
|
-
opts.setDragging(false);
|
|
181
|
-
checkEdgeOnRelease();
|
|
182
|
-
}
|
|
183
|
-
},
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
return { posX, posY, mouseProps, returnToView, isHidden: () => state === "edge_hidden" };
|
|
187
|
-
}
|