synabun 1.3.0
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/CHANGELOG.md +665 -0
- package/LICENSE +191 -0
- package/README.md +853 -0
- package/hooks/claude-code/post-plan.mjs +391 -0
- package/hooks/claude-code/post-remember.mjs +239 -0
- package/hooks/claude-code/pre-compact.mjs +241 -0
- package/hooks/claude-code/pre-websearch.mjs +90 -0
- package/hooks/claude-code/prompt-submit.mjs +861 -0
- package/hooks/claude-code/session-start.mjs +494 -0
- package/hooks/claude-code/shared.mjs +368 -0
- package/hooks/claude-code/stop.mjs +646 -0
- package/hooks/claude-code/test-user-learning.mjs +531 -0
- package/mcp-server/package.json +38 -0
- package/mcp-server/run.mjs +10 -0
- package/mcp-server/src/config.ts +70 -0
- package/mcp-server/src/http.ts +93 -0
- package/mcp-server/src/index.ts +203 -0
- package/mcp-server/src/preload-http.ts +25 -0
- package/mcp-server/src/preload.ts +29 -0
- package/mcp-server/src/scripts/migrate-qdrant-to-sqlite.ts +506 -0
- package/mcp-server/src/services/categories.ts +429 -0
- package/mcp-server/src/services/discord.ts +702 -0
- package/mcp-server/src/services/file-checksums.ts +33 -0
- package/mcp-server/src/services/local-embeddings.ts +77 -0
- package/mcp-server/src/services/neural-interface.ts +344 -0
- package/mcp-server/src/services/sqlite.ts +823 -0
- package/mcp-server/src/tools/browser-advanced.ts +123 -0
- package/mcp-server/src/tools/browser-interact.ts +255 -0
- package/mcp-server/src/tools/browser-navigate.ts +77 -0
- package/mcp-server/src/tools/browser-observe.ts +1330 -0
- package/mcp-server/src/tools/browser.ts +107 -0
- package/mcp-server/src/tools/card-tools.ts +193 -0
- package/mcp-server/src/tools/card.ts +22 -0
- package/mcp-server/src/tools/category.ts +379 -0
- package/mcp-server/src/tools/discord-channel.ts +274 -0
- package/mcp-server/src/tools/discord-guild.ts +188 -0
- package/mcp-server/src/tools/discord-member.ts +158 -0
- package/mcp-server/src/tools/discord-message.ts +231 -0
- package/mcp-server/src/tools/discord-onboarding.ts +228 -0
- package/mcp-server/src/tools/discord-role.ts +209 -0
- package/mcp-server/src/tools/discord-thread.ts +110 -0
- package/mcp-server/src/tools/discord-webhook.ts +176 -0
- package/mcp-server/src/tools/discord.ts +40 -0
- package/mcp-server/src/tools/forget.ts +38 -0
- package/mcp-server/src/tools/git-tools.ts +141 -0
- package/mcp-server/src/tools/git.ts +13 -0
- package/mcp-server/src/tools/image-tools.ts +98 -0
- package/mcp-server/src/tools/image.ts +17 -0
- package/mcp-server/src/tools/leonardo-browser-tools.ts +281 -0
- package/mcp-server/src/tools/leonardo.ts +28 -0
- package/mcp-server/src/tools/loop.ts +348 -0
- package/mcp-server/src/tools/memories.ts +118 -0
- package/mcp-server/src/tools/recall.ts +323 -0
- package/mcp-server/src/tools/reflect.ts +145 -0
- package/mcp-server/src/tools/remember.ts +115 -0
- package/mcp-server/src/tools/response.ts +26 -0
- package/mcp-server/src/tools/restore.ts +34 -0
- package/mcp-server/src/tools/sync.ts +114 -0
- package/mcp-server/src/tools/tictactoe-tools.ts +98 -0
- package/mcp-server/src/tools/tictactoe.ts +13 -0
- package/mcp-server/src/tools/utils.ts +25 -0
- package/mcp-server/src/tools/whiteboard-tools.ts +260 -0
- package/mcp-server/src/tools/whiteboard.ts +22 -0
- package/mcp-server/src/tui.ts +940 -0
- package/mcp-server/src/types.ts +85 -0
- package/mcp-server/tsconfig.json +19 -0
- package/neural-interface/package.json +27 -0
- package/neural-interface/public/apple-touch-icon.png +0 -0
- package/neural-interface/public/category-logos/claude.svg +1 -0
- package/neural-interface/public/category-logos/gemini-color.svg +1 -0
- package/neural-interface/public/category-logos/openai.svg +1 -0
- package/neural-interface/public/category-logos/synabun.png +0 -0
- package/neural-interface/public/claude-chat.css +878 -0
- package/neural-interface/public/claude-chat.html +92 -0
- package/neural-interface/public/claude-chat.js +1168 -0
- package/neural-interface/public/favicon-16x16.png +0 -0
- package/neural-interface/public/favicon-32x32.png +0 -0
- package/neural-interface/public/gallery.svg +6 -0
- package/neural-interface/public/icon-192.png +0 -0
- package/neural-interface/public/icon-512.png +0 -0
- package/neural-interface/public/index-legacy.html +14677 -0
- package/neural-interface/public/index.html +212 -0
- package/neural-interface/public/index2d-legacy.html +8193 -0
- package/neural-interface/public/index2d.html +127 -0
- package/neural-interface/public/invite.html +351 -0
- package/neural-interface/public/leonardoai.svg +1 -0
- package/neural-interface/public/logo-synabun.png +0 -0
- package/neural-interface/public/logoHD.png +0 -0
- package/neural-interface/public/manifest.json +14 -0
- package/neural-interface/public/offline.html +299 -0
- package/neural-interface/public/onboarding.html +2335 -0
- package/neural-interface/public/openclaw-logo-text.png +0 -0
- package/neural-interface/public/pin-svgrepo-com.svg +25 -0
- package/neural-interface/public/sessionicon.svg +8 -0
- package/neural-interface/public/shared/ansi-parser.js +305 -0
- package/neural-interface/public/shared/api.js +1043 -0
- package/neural-interface/public/shared/colors.js +153 -0
- package/neural-interface/public/shared/constants.js +141 -0
- package/neural-interface/public/shared/html-shell.js +777 -0
- package/neural-interface/public/shared/html-term-renderer.js +1022 -0
- package/neural-interface/public/shared/i18n.js +147 -0
- package/neural-interface/public/shared/registry.js +82 -0
- package/neural-interface/public/shared/state.js +86 -0
- package/neural-interface/public/shared/storage.js +150 -0
- package/neural-interface/public/shared/styles.css +21212 -0
- package/neural-interface/public/shared/ui-automation-studio.js +3573 -0
- package/neural-interface/public/shared/ui-bookmarks.js +231 -0
- package/neural-interface/public/shared/ui-claude-panel.js +6333 -0
- package/neural-interface/public/shared/ui-command-runner.js +534 -0
- package/neural-interface/public/shared/ui-cost-widget.js +602 -0
- package/neural-interface/public/shared/ui-detail.js +1454 -0
- package/neural-interface/public/shared/ui-explorer.js +1356 -0
- package/neural-interface/public/shared/ui-file-explorer.js +2750 -0
- package/neural-interface/public/shared/ui-games.js +411 -0
- package/neural-interface/public/shared/ui-help.js +107 -0
- package/neural-interface/public/shared/ui-image-gallery.js +461 -0
- package/neural-interface/public/shared/ui-invite.js +519 -0
- package/neural-interface/public/shared/ui-keybinds.js +452 -0
- package/neural-interface/public/shared/ui-layouts.js +297 -0
- package/neural-interface/public/shared/ui-link.js +731 -0
- package/neural-interface/public/shared/ui-loading.js +255 -0
- package/neural-interface/public/shared/ui-menubar.js +499 -0
- package/neural-interface/public/shared/ui-multiselect.js +93 -0
- package/neural-interface/public/shared/ui-navbar.js +222 -0
- package/neural-interface/public/shared/ui-notifications.js +287 -0
- package/neural-interface/public/shared/ui-panels.js +309 -0
- package/neural-interface/public/shared/ui-resume.js +653 -0
- package/neural-interface/public/shared/ui-search.js +145 -0
- package/neural-interface/public/shared/ui-sessions.js +531 -0
- package/neural-interface/public/shared/ui-settings.js +5987 -0
- package/neural-interface/public/shared/ui-sidebar.js +1905 -0
- package/neural-interface/public/shared/ui-skills.js +2805 -0
- package/neural-interface/public/shared/ui-stats.js +38 -0
- package/neural-interface/public/shared/ui-sync.js +201 -0
- package/neural-interface/public/shared/ui-terminal.js +6385 -0
- package/neural-interface/public/shared/ui-tooltip.js +123 -0
- package/neural-interface/public/shared/ui-trash.js +439 -0
- package/neural-interface/public/shared/ui-tutorial-draw.js +875 -0
- package/neural-interface/public/shared/ui-tutorial-steps.js +2276 -0
- package/neural-interface/public/shared/ui-tutorial.js +1117 -0
- package/neural-interface/public/shared/ui-update.js +244 -0
- package/neural-interface/public/shared/ui-whiteboard.js +3262 -0
- package/neural-interface/public/shared/ui-workspaces.js +722 -0
- package/neural-interface/public/shared/utils.js +360 -0
- package/neural-interface/public/shared/vterm-buffer.js +1102 -0
- package/neural-interface/public/sw.js +52 -0
- package/neural-interface/public/synabun.png +0 -0
- package/neural-interface/public/toolbar-album.svg +8 -0
- package/neural-interface/public/toolbar-question.svg +6 -0
- package/neural-interface/public/toolbar-sessions.svg +6 -0
- package/neural-interface/public/variant/2d/background.js +113 -0
- package/neural-interface/public/variant/2d/context-menu.js +133 -0
- package/neural-interface/public/variant/2d/gfx.js +45 -0
- package/neural-interface/public/variant/2d/graph.js +1635 -0
- package/neural-interface/public/variant/2d/hulls.js +207 -0
- package/neural-interface/public/variant/2d/lasso.js +135 -0
- package/neural-interface/public/variant/2d/layout.js +594 -0
- package/neural-interface/public/variant/2d/main.js +521 -0
- package/neural-interface/public/variant/2d/minimap.js +312 -0
- package/neural-interface/public/variant/2d/settings-gfx.js +98 -0
- package/neural-interface/public/variant/3d/background.js +884 -0
- package/neural-interface/public/variant/3d/camera.js +661 -0
- package/neural-interface/public/variant/3d/gfx.js +116 -0
- package/neural-interface/public/variant/3d/graph.js +2399 -0
- package/neural-interface/public/variant/3d/main.js +500 -0
- package/neural-interface/public/variant/3d/settings-gfx.js +228 -0
- package/neural-interface/scripts/rebuild-pty.js +100 -0
- package/neural-interface/server.js +15000 -0
- package/neural-interface/templates/CLAUDE-template.md +211 -0
- package/package.json +53 -0
- package/postinstall.js +135 -0
- package/setup.js +260 -0
- package/skills/leonardo/SKILL.md +159 -0
- package/skills/leonardo/icon.svg +1 -0
- package/skills/leonardo/modules/browser-guide.md +378 -0
- package/skills/leonardo/modules/image-prompter.md +277 -0
- package/skills/leonardo/modules/model-advisor.md +100 -0
- package/skills/leonardo/modules/prompt-library.md +110 -0
- package/skills/leonardo/modules/style-guide.md +186 -0
- package/skills/leonardo/modules/video-prompter.md +311 -0
- package/skills/synabun/SKILL.md +185 -0
- package/skills/synabun/icon.png +0 -0
- package/skills/synabun/modules/audit.md +374 -0
- package/skills/synabun/modules/changelog.md +194 -0
- package/skills/synabun/modules/idea.md +144 -0
- package/skills/synabun/modules/memorize.md +188 -0
- package/skills/synabun/synabunicon.png +0 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — 2D Category Hulls
|
|
3
|
+
// Convex hull overlays around category clusters
|
|
4
|
+
// Canvas: #hull-canvas
|
|
5
|
+
// ═══════════════════════════════════════════
|
|
6
|
+
|
|
7
|
+
import { gfx } from './gfx.js';
|
|
8
|
+
import { state, emit, on } from '../../shared/state.js';
|
|
9
|
+
import { catColor, hexAlpha } from '../../shared/colors.js';
|
|
10
|
+
|
|
11
|
+
const $ = (id) => document.getElementById(id);
|
|
12
|
+
|
|
13
|
+
// ── Private state ──
|
|
14
|
+
let _hullsVisible = true;
|
|
15
|
+
let _hullThrottle = 0;
|
|
16
|
+
let _hullCanvas = null;
|
|
17
|
+
let _hullCtx = null;
|
|
18
|
+
let _graph = null;
|
|
19
|
+
|
|
20
|
+
// ═══════════════════════════════════════════
|
|
21
|
+
// INIT
|
|
22
|
+
// ═══════════════════════════════════════════
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Initialize hull overlay canvas and toggle button.
|
|
26
|
+
* @param {object} graphInstance The force-graph instance
|
|
27
|
+
*/
|
|
28
|
+
export function initHulls(graphInstance) {
|
|
29
|
+
_graph = graphInstance;
|
|
30
|
+
_hullCanvas = $('hull-canvas');
|
|
31
|
+
if (!_hullCanvas) return;
|
|
32
|
+
_hullCtx = _hullCanvas.getContext('2d');
|
|
33
|
+
|
|
34
|
+
_hullCanvas.width = window.innerWidth;
|
|
35
|
+
_hullCanvas.height = window.innerHeight;
|
|
36
|
+
|
|
37
|
+
// Toggle button
|
|
38
|
+
const toggleBtn = $('hull-toggle-btn');
|
|
39
|
+
if (toggleBtn) {
|
|
40
|
+
toggleBtn.addEventListener('click', () => {
|
|
41
|
+
toggleHulls();
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Handle window resize
|
|
46
|
+
window.addEventListener('resize', _onResize);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ═══════════════════════════════════════════
|
|
50
|
+
// DRAW
|
|
51
|
+
// ═══════════════════════════════════════════
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Draw convex hulls around category clusters.
|
|
55
|
+
* Throttled to max ~2fps (500ms interval).
|
|
56
|
+
* Called from graph's onRenderFramePost callback.
|
|
57
|
+
*/
|
|
58
|
+
export function drawHulls() {
|
|
59
|
+
if (!_hullsVisible || !_graph) {
|
|
60
|
+
if (_hullCanvas && _hullCtx) {
|
|
61
|
+
_hullCtx.clearRect(0, 0, _hullCanvas.width, _hullCanvas.height);
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const now = performance.now();
|
|
66
|
+
if (now - _hullThrottle < 500) return;
|
|
67
|
+
_hullThrottle = now;
|
|
68
|
+
|
|
69
|
+
if (!_hullCanvas || !_hullCtx) return;
|
|
70
|
+
|
|
71
|
+
_hullCanvas.width = window.innerWidth;
|
|
72
|
+
_hullCanvas.height = window.innerHeight;
|
|
73
|
+
const ctx = _hullCtx;
|
|
74
|
+
ctx.clearRect(0, 0, _hullCanvas.width, _hullCanvas.height);
|
|
75
|
+
|
|
76
|
+
const gd = _graph.graphData();
|
|
77
|
+
const zoom = _graph.zoom();
|
|
78
|
+
const center = _graph.centerAt();
|
|
79
|
+
|
|
80
|
+
// Group nodes by category (skip anchors and tags)
|
|
81
|
+
const catGroups = {};
|
|
82
|
+
gd.nodes.forEach(n => {
|
|
83
|
+
if (n.payload._isAnchor || n.payload._isTag) return;
|
|
84
|
+
const cat = n.payload.category;
|
|
85
|
+
if (!catGroups[cat]) catGroups[cat] = [];
|
|
86
|
+
catGroups[cat].push(n);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Convert graph coords to screen coords
|
|
90
|
+
function toScreen(gx, gy) {
|
|
91
|
+
return {
|
|
92
|
+
x: (gx - center.x) * zoom + window.innerWidth / 2,
|
|
93
|
+
y: (gy - center.y) * zoom + window.innerHeight / 2,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const [cat, nodes] of Object.entries(catGroups)) {
|
|
98
|
+
if (nodes.length < 3) continue;
|
|
99
|
+
const color = catColor(cat);
|
|
100
|
+
|
|
101
|
+
// Convert to screen points
|
|
102
|
+
const points = nodes.map(n => toScreen(n.x, n.y));
|
|
103
|
+
|
|
104
|
+
// Compute convex hull
|
|
105
|
+
const hull = convexHull(points);
|
|
106
|
+
if (hull.length < 3) continue;
|
|
107
|
+
|
|
108
|
+
// Draw hull with padding
|
|
109
|
+
ctx.beginPath();
|
|
110
|
+
const pad = 20 * zoom;
|
|
111
|
+
const cx = hull.reduce((s, p) => s + p.x, 0) / hull.length;
|
|
112
|
+
const cy = hull.reduce((s, p) => s + p.y, 0) / hull.length;
|
|
113
|
+
|
|
114
|
+
hull.forEach((p, i) => {
|
|
115
|
+
const dx = p.x - cx, dy = p.y - cy;
|
|
116
|
+
const dist = Math.sqrt(dx * dx + dy * dy) || 1;
|
|
117
|
+
const px = p.x + (dx / dist) * pad;
|
|
118
|
+
const py = p.y + (dy / dist) * pad;
|
|
119
|
+
if (i === 0) ctx.moveTo(px, py);
|
|
120
|
+
else ctx.lineTo(px, py);
|
|
121
|
+
});
|
|
122
|
+
ctx.closePath();
|
|
123
|
+
ctx.fillStyle = hexAlpha(color, gfx.hullOpacity);
|
|
124
|
+
ctx.strokeStyle = hexAlpha(color, gfx.hullOpacity * 2.5);
|
|
125
|
+
ctx.lineWidth = 1;
|
|
126
|
+
ctx.fill();
|
|
127
|
+
ctx.stroke();
|
|
128
|
+
|
|
129
|
+
// Category label at centroid
|
|
130
|
+
ctx.font = 'bold 11px sans-serif';
|
|
131
|
+
ctx.fillStyle = hexAlpha(color, 0.5);
|
|
132
|
+
ctx.textAlign = 'center';
|
|
133
|
+
ctx.fillText(cat, cx, cy);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ═══════════════════════════════════════════
|
|
138
|
+
// CONVEX HULL (Graham scan)
|
|
139
|
+
// ═══════════════════════════════════════════
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Compute the convex hull of a set of 2D points using Graham scan.
|
|
143
|
+
* @param {Array<{x:number, y:number}>} points
|
|
144
|
+
* @returns {Array<{x:number, y:number}>} Hull vertices in order
|
|
145
|
+
*/
|
|
146
|
+
function convexHull(points) {
|
|
147
|
+
if (points.length < 3) return points;
|
|
148
|
+
const pts = [...points].sort((a, b) => a.x - b.x || a.y - b.y);
|
|
149
|
+
const cross = (O, A, B) => (A.x - O.x) * (B.y - O.y) - (A.y - O.y) * (B.x - O.x);
|
|
150
|
+
const lower = [];
|
|
151
|
+
for (const p of pts) { while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 0) lower.pop(); lower.push(p); }
|
|
152
|
+
const upper = [];
|
|
153
|
+
for (let i = pts.length - 1; i >= 0; i--) { const p = pts[i]; while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], p) <= 0) upper.pop(); upper.push(p); }
|
|
154
|
+
upper.pop(); lower.pop();
|
|
155
|
+
return lower.concat(upper);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ═══════════════════════════════════════════
|
|
159
|
+
// TOGGLE
|
|
160
|
+
// ═══════════════════════════════════════════
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Toggle hull visibility on/off.
|
|
164
|
+
* Updates the toggle button active state and clears the canvas when hiding.
|
|
165
|
+
*/
|
|
166
|
+
export function toggleHulls() {
|
|
167
|
+
_hullsVisible = !_hullsVisible;
|
|
168
|
+
const btn = $('hull-toggle-btn');
|
|
169
|
+
if (btn) btn.classList.toggle('active', _hullsVisible);
|
|
170
|
+
if (!_hullsVisible && _hullCanvas && _hullCtx) {
|
|
171
|
+
_hullCtx.clearRect(0, 0, _hullCanvas.width, _hullCanvas.height);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Get whether hulls are currently visible.
|
|
177
|
+
* @returns {boolean}
|
|
178
|
+
*/
|
|
179
|
+
export function isHullsVisible() {
|
|
180
|
+
return _hullsVisible;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ═══════════════════════════════════════════
|
|
184
|
+
// RESIZE
|
|
185
|
+
// ═══════════════════════════════════════════
|
|
186
|
+
|
|
187
|
+
function _onResize() {
|
|
188
|
+
if (!_hullCanvas) return;
|
|
189
|
+
_hullCanvas.width = window.innerWidth;
|
|
190
|
+
_hullCanvas.height = window.innerHeight;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Update the graph instance reference (e.g. after graph rebuild).
|
|
195
|
+
* @param {object} graphInstance
|
|
196
|
+
*/
|
|
197
|
+
export function setGraph(graphInstance) {
|
|
198
|
+
_graph = graphInstance;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Get the hull canvas element (for external resize coordination).
|
|
203
|
+
* @returns {HTMLCanvasElement|null}
|
|
204
|
+
*/
|
|
205
|
+
export function getHullCanvas() {
|
|
206
|
+
return _hullCanvas;
|
|
207
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — 2D Lasso Selection
|
|
3
|
+
// Shift+drag rectangle selection on the canvas
|
|
4
|
+
// Adds matched cards to state.multiSelected
|
|
5
|
+
// Canvas: #lasso-canvas
|
|
6
|
+
// ═══════════════════════════════════════════
|
|
7
|
+
|
|
8
|
+
import { state, emit } from '../../shared/state.js';
|
|
9
|
+
import { updateMultiSelectBar } from '../../shared/ui-multiselect.js';
|
|
10
|
+
import { screenToWorld, getAllCards } from './graph.js';
|
|
11
|
+
import { CARD_W, CARD_H } from './layout.js';
|
|
12
|
+
|
|
13
|
+
const $ = (id) => document.getElementById(id);
|
|
14
|
+
|
|
15
|
+
// ── Private state ──
|
|
16
|
+
let _lassoActive = false;
|
|
17
|
+
let _lassoStart = null;
|
|
18
|
+
let _lassoEnd = null;
|
|
19
|
+
let _lassoCanvas = null;
|
|
20
|
+
let _lassoCtx = null;
|
|
21
|
+
|
|
22
|
+
// ═══════════════════════════════════════════
|
|
23
|
+
// INIT
|
|
24
|
+
// ═══════════════════════════════════════════
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Initialize lasso selection system.
|
|
28
|
+
* @param {object} _graphInstance Unused — kept for API compatibility
|
|
29
|
+
*/
|
|
30
|
+
export function initLasso(_graphInstance) {
|
|
31
|
+
_lassoCanvas = $('lasso-canvas');
|
|
32
|
+
if (!_lassoCanvas) return;
|
|
33
|
+
_lassoCtx = _lassoCanvas.getContext('2d');
|
|
34
|
+
|
|
35
|
+
_lassoCanvas.width = window.innerWidth;
|
|
36
|
+
_lassoCanvas.height = window.innerHeight;
|
|
37
|
+
|
|
38
|
+
document.addEventListener('pointerdown', _onPointerDown);
|
|
39
|
+
document.addEventListener('pointermove', _onPointerMove);
|
|
40
|
+
document.addEventListener('pointerup', _onPointerUp);
|
|
41
|
+
window.addEventListener('resize', _onResize);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ═══════════════════════════════════════════
|
|
45
|
+
// EVENT HANDLERS
|
|
46
|
+
// ═══════════════════════════════════════════
|
|
47
|
+
|
|
48
|
+
function _onPointerDown(e) {
|
|
49
|
+
if (!e.shiftKey || e.button !== 0) return;
|
|
50
|
+
if (e.target.closest('#title-bar, #category-sidebar, .detail-card, #minimap, .glass')) return;
|
|
51
|
+
_lassoActive = true;
|
|
52
|
+
_lassoStart = { x: e.clientX, y: e.clientY };
|
|
53
|
+
_lassoEnd = { x: e.clientX, y: e.clientY };
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function _onPointerMove(e) {
|
|
58
|
+
if (!_lassoActive) return;
|
|
59
|
+
_lassoEnd = { x: e.clientX, y: e.clientY };
|
|
60
|
+
|
|
61
|
+
// Draw selection rect
|
|
62
|
+
_lassoCtx.clearRect(0, 0, _lassoCanvas.width, _lassoCanvas.height);
|
|
63
|
+
const x = Math.min(_lassoStart.x, _lassoEnd.x);
|
|
64
|
+
const y = Math.min(_lassoStart.y, _lassoEnd.y);
|
|
65
|
+
const w = Math.abs(_lassoEnd.x - _lassoStart.x);
|
|
66
|
+
const h = Math.abs(_lassoEnd.y - _lassoStart.y);
|
|
67
|
+
_lassoCtx.strokeStyle = 'rgba(100, 180, 255, 0.6)';
|
|
68
|
+
_lassoCtx.fillStyle = 'rgba(100, 180, 255, 0.08)';
|
|
69
|
+
_lassoCtx.lineWidth = 1;
|
|
70
|
+
_lassoCtx.fillRect(x, y, w, h);
|
|
71
|
+
_lassoCtx.strokeRect(x, y, w, h);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function _onPointerUp() {
|
|
75
|
+
if (!_lassoActive) return;
|
|
76
|
+
_lassoActive = false;
|
|
77
|
+
_lassoCtx.clearRect(0, 0, _lassoCanvas.width, _lassoCanvas.height);
|
|
78
|
+
|
|
79
|
+
const x1 = Math.min(_lassoStart.x, _lassoEnd.x);
|
|
80
|
+
const y1 = Math.min(_lassoStart.y, _lassoEnd.y);
|
|
81
|
+
const x2 = Math.max(_lassoStart.x, _lassoEnd.x);
|
|
82
|
+
const y2 = Math.max(_lassoStart.y, _lassoEnd.y);
|
|
83
|
+
|
|
84
|
+
// Only select if drag was meaningful (> 10px in either direction)
|
|
85
|
+
if (x2 - x1 < 10 && y2 - y1 < 10) return;
|
|
86
|
+
|
|
87
|
+
// Convert screen rectangle corners to world coordinates
|
|
88
|
+
const topLeft = screenToWorld(x1, y1);
|
|
89
|
+
const bottomRight = screenToWorld(x2, y2);
|
|
90
|
+
|
|
91
|
+
state.multiSelected.clear();
|
|
92
|
+
|
|
93
|
+
const cards = getAllCards();
|
|
94
|
+
for (const card of cards) {
|
|
95
|
+
if (card.x >= topLeft.x && card.x <= bottomRight.x &&
|
|
96
|
+
card.y >= topLeft.y && card.y <= bottomRight.y) {
|
|
97
|
+
state.multiSelected.add(card.node.id);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
updateMultiSelectBar();
|
|
102
|
+
emit('lasso:completed', { count: state.multiSelected.size });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ═══════════════════════════════════════════
|
|
106
|
+
// CLEAR
|
|
107
|
+
// ═══════════════════════════════════════════
|
|
108
|
+
|
|
109
|
+
export function clearLasso() {
|
|
110
|
+
_lassoActive = false;
|
|
111
|
+
if (_lassoCtx && _lassoCanvas) {
|
|
112
|
+
_lassoCtx.clearRect(0, 0, _lassoCanvas.width, _lassoCanvas.height);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ═══════════════════════════════════════════
|
|
117
|
+
// RESIZE
|
|
118
|
+
// ═══════════════════════════════════════════
|
|
119
|
+
|
|
120
|
+
function _onResize() {
|
|
121
|
+
if (!_lassoCanvas) return;
|
|
122
|
+
_lassoCanvas.width = window.innerWidth;
|
|
123
|
+
_lassoCanvas.height = window.innerHeight;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Update the graph instance reference (compatibility — no-op).
|
|
128
|
+
*/
|
|
129
|
+
export function setGraph(_graphInstance) {
|
|
130
|
+
// No-op
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function getLassoCanvas() {
|
|
134
|
+
return _lassoCanvas;
|
|
135
|
+
}
|