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.
Files changed (187) hide show
  1. package/CHANGELOG.md +665 -0
  2. package/LICENSE +191 -0
  3. package/README.md +853 -0
  4. package/hooks/claude-code/post-plan.mjs +391 -0
  5. package/hooks/claude-code/post-remember.mjs +239 -0
  6. package/hooks/claude-code/pre-compact.mjs +241 -0
  7. package/hooks/claude-code/pre-websearch.mjs +90 -0
  8. package/hooks/claude-code/prompt-submit.mjs +861 -0
  9. package/hooks/claude-code/session-start.mjs +494 -0
  10. package/hooks/claude-code/shared.mjs +368 -0
  11. package/hooks/claude-code/stop.mjs +646 -0
  12. package/hooks/claude-code/test-user-learning.mjs +531 -0
  13. package/mcp-server/package.json +38 -0
  14. package/mcp-server/run.mjs +10 -0
  15. package/mcp-server/src/config.ts +70 -0
  16. package/mcp-server/src/http.ts +93 -0
  17. package/mcp-server/src/index.ts +203 -0
  18. package/mcp-server/src/preload-http.ts +25 -0
  19. package/mcp-server/src/preload.ts +29 -0
  20. package/mcp-server/src/scripts/migrate-qdrant-to-sqlite.ts +506 -0
  21. package/mcp-server/src/services/categories.ts +429 -0
  22. package/mcp-server/src/services/discord.ts +702 -0
  23. package/mcp-server/src/services/file-checksums.ts +33 -0
  24. package/mcp-server/src/services/local-embeddings.ts +77 -0
  25. package/mcp-server/src/services/neural-interface.ts +344 -0
  26. package/mcp-server/src/services/sqlite.ts +823 -0
  27. package/mcp-server/src/tools/browser-advanced.ts +123 -0
  28. package/mcp-server/src/tools/browser-interact.ts +255 -0
  29. package/mcp-server/src/tools/browser-navigate.ts +77 -0
  30. package/mcp-server/src/tools/browser-observe.ts +1330 -0
  31. package/mcp-server/src/tools/browser.ts +107 -0
  32. package/mcp-server/src/tools/card-tools.ts +193 -0
  33. package/mcp-server/src/tools/card.ts +22 -0
  34. package/mcp-server/src/tools/category.ts +379 -0
  35. package/mcp-server/src/tools/discord-channel.ts +274 -0
  36. package/mcp-server/src/tools/discord-guild.ts +188 -0
  37. package/mcp-server/src/tools/discord-member.ts +158 -0
  38. package/mcp-server/src/tools/discord-message.ts +231 -0
  39. package/mcp-server/src/tools/discord-onboarding.ts +228 -0
  40. package/mcp-server/src/tools/discord-role.ts +209 -0
  41. package/mcp-server/src/tools/discord-thread.ts +110 -0
  42. package/mcp-server/src/tools/discord-webhook.ts +176 -0
  43. package/mcp-server/src/tools/discord.ts +40 -0
  44. package/mcp-server/src/tools/forget.ts +38 -0
  45. package/mcp-server/src/tools/git-tools.ts +141 -0
  46. package/mcp-server/src/tools/git.ts +13 -0
  47. package/mcp-server/src/tools/image-tools.ts +98 -0
  48. package/mcp-server/src/tools/image.ts +17 -0
  49. package/mcp-server/src/tools/leonardo-browser-tools.ts +281 -0
  50. package/mcp-server/src/tools/leonardo.ts +28 -0
  51. package/mcp-server/src/tools/loop.ts +348 -0
  52. package/mcp-server/src/tools/memories.ts +118 -0
  53. package/mcp-server/src/tools/recall.ts +323 -0
  54. package/mcp-server/src/tools/reflect.ts +145 -0
  55. package/mcp-server/src/tools/remember.ts +115 -0
  56. package/mcp-server/src/tools/response.ts +26 -0
  57. package/mcp-server/src/tools/restore.ts +34 -0
  58. package/mcp-server/src/tools/sync.ts +114 -0
  59. package/mcp-server/src/tools/tictactoe-tools.ts +98 -0
  60. package/mcp-server/src/tools/tictactoe.ts +13 -0
  61. package/mcp-server/src/tools/utils.ts +25 -0
  62. package/mcp-server/src/tools/whiteboard-tools.ts +260 -0
  63. package/mcp-server/src/tools/whiteboard.ts +22 -0
  64. package/mcp-server/src/tui.ts +940 -0
  65. package/mcp-server/src/types.ts +85 -0
  66. package/mcp-server/tsconfig.json +19 -0
  67. package/neural-interface/package.json +27 -0
  68. package/neural-interface/public/apple-touch-icon.png +0 -0
  69. package/neural-interface/public/category-logos/claude.svg +1 -0
  70. package/neural-interface/public/category-logos/gemini-color.svg +1 -0
  71. package/neural-interface/public/category-logos/openai.svg +1 -0
  72. package/neural-interface/public/category-logos/synabun.png +0 -0
  73. package/neural-interface/public/claude-chat.css +878 -0
  74. package/neural-interface/public/claude-chat.html +92 -0
  75. package/neural-interface/public/claude-chat.js +1168 -0
  76. package/neural-interface/public/favicon-16x16.png +0 -0
  77. package/neural-interface/public/favicon-32x32.png +0 -0
  78. package/neural-interface/public/gallery.svg +6 -0
  79. package/neural-interface/public/icon-192.png +0 -0
  80. package/neural-interface/public/icon-512.png +0 -0
  81. package/neural-interface/public/index-legacy.html +14677 -0
  82. package/neural-interface/public/index.html +212 -0
  83. package/neural-interface/public/index2d-legacy.html +8193 -0
  84. package/neural-interface/public/index2d.html +127 -0
  85. package/neural-interface/public/invite.html +351 -0
  86. package/neural-interface/public/leonardoai.svg +1 -0
  87. package/neural-interface/public/logo-synabun.png +0 -0
  88. package/neural-interface/public/logoHD.png +0 -0
  89. package/neural-interface/public/manifest.json +14 -0
  90. package/neural-interface/public/offline.html +299 -0
  91. package/neural-interface/public/onboarding.html +2335 -0
  92. package/neural-interface/public/openclaw-logo-text.png +0 -0
  93. package/neural-interface/public/pin-svgrepo-com.svg +25 -0
  94. package/neural-interface/public/sessionicon.svg +8 -0
  95. package/neural-interface/public/shared/ansi-parser.js +305 -0
  96. package/neural-interface/public/shared/api.js +1043 -0
  97. package/neural-interface/public/shared/colors.js +153 -0
  98. package/neural-interface/public/shared/constants.js +141 -0
  99. package/neural-interface/public/shared/html-shell.js +777 -0
  100. package/neural-interface/public/shared/html-term-renderer.js +1022 -0
  101. package/neural-interface/public/shared/i18n.js +147 -0
  102. package/neural-interface/public/shared/registry.js +82 -0
  103. package/neural-interface/public/shared/state.js +86 -0
  104. package/neural-interface/public/shared/storage.js +150 -0
  105. package/neural-interface/public/shared/styles.css +21212 -0
  106. package/neural-interface/public/shared/ui-automation-studio.js +3573 -0
  107. package/neural-interface/public/shared/ui-bookmarks.js +231 -0
  108. package/neural-interface/public/shared/ui-claude-panel.js +6333 -0
  109. package/neural-interface/public/shared/ui-command-runner.js +534 -0
  110. package/neural-interface/public/shared/ui-cost-widget.js +602 -0
  111. package/neural-interface/public/shared/ui-detail.js +1454 -0
  112. package/neural-interface/public/shared/ui-explorer.js +1356 -0
  113. package/neural-interface/public/shared/ui-file-explorer.js +2750 -0
  114. package/neural-interface/public/shared/ui-games.js +411 -0
  115. package/neural-interface/public/shared/ui-help.js +107 -0
  116. package/neural-interface/public/shared/ui-image-gallery.js +461 -0
  117. package/neural-interface/public/shared/ui-invite.js +519 -0
  118. package/neural-interface/public/shared/ui-keybinds.js +452 -0
  119. package/neural-interface/public/shared/ui-layouts.js +297 -0
  120. package/neural-interface/public/shared/ui-link.js +731 -0
  121. package/neural-interface/public/shared/ui-loading.js +255 -0
  122. package/neural-interface/public/shared/ui-menubar.js +499 -0
  123. package/neural-interface/public/shared/ui-multiselect.js +93 -0
  124. package/neural-interface/public/shared/ui-navbar.js +222 -0
  125. package/neural-interface/public/shared/ui-notifications.js +287 -0
  126. package/neural-interface/public/shared/ui-panels.js +309 -0
  127. package/neural-interface/public/shared/ui-resume.js +653 -0
  128. package/neural-interface/public/shared/ui-search.js +145 -0
  129. package/neural-interface/public/shared/ui-sessions.js +531 -0
  130. package/neural-interface/public/shared/ui-settings.js +5987 -0
  131. package/neural-interface/public/shared/ui-sidebar.js +1905 -0
  132. package/neural-interface/public/shared/ui-skills.js +2805 -0
  133. package/neural-interface/public/shared/ui-stats.js +38 -0
  134. package/neural-interface/public/shared/ui-sync.js +201 -0
  135. package/neural-interface/public/shared/ui-terminal.js +6385 -0
  136. package/neural-interface/public/shared/ui-tooltip.js +123 -0
  137. package/neural-interface/public/shared/ui-trash.js +439 -0
  138. package/neural-interface/public/shared/ui-tutorial-draw.js +875 -0
  139. package/neural-interface/public/shared/ui-tutorial-steps.js +2276 -0
  140. package/neural-interface/public/shared/ui-tutorial.js +1117 -0
  141. package/neural-interface/public/shared/ui-update.js +244 -0
  142. package/neural-interface/public/shared/ui-whiteboard.js +3262 -0
  143. package/neural-interface/public/shared/ui-workspaces.js +722 -0
  144. package/neural-interface/public/shared/utils.js +360 -0
  145. package/neural-interface/public/shared/vterm-buffer.js +1102 -0
  146. package/neural-interface/public/sw.js +52 -0
  147. package/neural-interface/public/synabun.png +0 -0
  148. package/neural-interface/public/toolbar-album.svg +8 -0
  149. package/neural-interface/public/toolbar-question.svg +6 -0
  150. package/neural-interface/public/toolbar-sessions.svg +6 -0
  151. package/neural-interface/public/variant/2d/background.js +113 -0
  152. package/neural-interface/public/variant/2d/context-menu.js +133 -0
  153. package/neural-interface/public/variant/2d/gfx.js +45 -0
  154. package/neural-interface/public/variant/2d/graph.js +1635 -0
  155. package/neural-interface/public/variant/2d/hulls.js +207 -0
  156. package/neural-interface/public/variant/2d/lasso.js +135 -0
  157. package/neural-interface/public/variant/2d/layout.js +594 -0
  158. package/neural-interface/public/variant/2d/main.js +521 -0
  159. package/neural-interface/public/variant/2d/minimap.js +312 -0
  160. package/neural-interface/public/variant/2d/settings-gfx.js +98 -0
  161. package/neural-interface/public/variant/3d/background.js +884 -0
  162. package/neural-interface/public/variant/3d/camera.js +661 -0
  163. package/neural-interface/public/variant/3d/gfx.js +116 -0
  164. package/neural-interface/public/variant/3d/graph.js +2399 -0
  165. package/neural-interface/public/variant/3d/main.js +500 -0
  166. package/neural-interface/public/variant/3d/settings-gfx.js +228 -0
  167. package/neural-interface/scripts/rebuild-pty.js +100 -0
  168. package/neural-interface/server.js +15000 -0
  169. package/neural-interface/templates/CLAUDE-template.md +211 -0
  170. package/package.json +53 -0
  171. package/postinstall.js +135 -0
  172. package/setup.js +260 -0
  173. package/skills/leonardo/SKILL.md +159 -0
  174. package/skills/leonardo/icon.svg +1 -0
  175. package/skills/leonardo/modules/browser-guide.md +378 -0
  176. package/skills/leonardo/modules/image-prompter.md +277 -0
  177. package/skills/leonardo/modules/model-advisor.md +100 -0
  178. package/skills/leonardo/modules/prompt-library.md +110 -0
  179. package/skills/leonardo/modules/style-guide.md +186 -0
  180. package/skills/leonardo/modules/video-prompter.md +311 -0
  181. package/skills/synabun/SKILL.md +185 -0
  182. package/skills/synabun/icon.png +0 -0
  183. package/skills/synabun/modules/audit.md +374 -0
  184. package/skills/synabun/modules/changelog.md +194 -0
  185. package/skills/synabun/modules/idea.md +144 -0
  186. package/skills/synabun/modules/memorize.md +188 -0
  187. 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
+ }