gopeak 2.1.0 → 2.2.1
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 +224 -75
- package/build/addon/godot_mcp_editor/mcp_client.gd +178 -0
- package/build/addon/godot_mcp_editor/plugin.cfg +6 -0
- package/build/addon/godot_mcp_editor/plugin.gd +84 -0
- package/build/addon/godot_mcp_editor/tool_executor.gd +114 -0
- package/build/addon/godot_mcp_editor/tools/animation_tools.gd +502 -0
- package/build/addon/godot_mcp_editor/tools/resource_tools.gd +425 -0
- package/build/addon/godot_mcp_editor/tools/scene_tools.gd +710 -0
- package/build/cli/check.js +77 -0
- package/build/cli/notify.js +88 -0
- package/build/cli/setup.js +115 -0
- package/build/cli/star.js +51 -0
- package/build/cli/uninstall.js +26 -0
- package/build/cli/utils.js +149 -0
- package/build/cli.js +91 -0
- package/build/gdscript_parser.js +828 -0
- package/build/godot-bridge.js +556 -0
- package/build/index.js +2761 -2064
- package/build/prompts.js +163 -0
- package/build/visualizer/canvas.js +832 -0
- package/build/visualizer/events.js +814 -0
- package/build/visualizer/layout.js +304 -0
- package/build/visualizer/main.js +245 -0
- package/build/visualizer/modals.js +239 -0
- package/build/visualizer/panel.js +1091 -0
- package/build/visualizer/state.js +210 -0
- package/build/visualizer/syntax.js +106 -0
- package/build/visualizer/usages.js +352 -0
- package/build/visualizer/websocket.js +85 -0
- package/build/visualizer-server.js +375 -0
- package/build/visualizer.html +6395 -0
- package/package.json +15 -6
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context menu, new script modal, and view switching
|
|
3
|
+
*/
|
|
4
|
+
import { nodes, edges, setCurrentView, setSceneData, getFolderColor, setExpandedScene, setExpandedSceneHierarchy, setSelectedSceneNode, setHoveredSceneNode, expandedScene, gitChangeSummary, categoryColorMap } from './state.js';
|
|
5
|
+
import { sendCommand } from './websocket.js';
|
|
6
|
+
import { draw, getCanvas, roundRect, getContext, clearPositions, fitToView } from './canvas.js';
|
|
7
|
+
import { initLayout } from './layout.js';
|
|
8
|
+
import { closePanel, closeSceneNodePanel } from './panel.js';
|
|
9
|
+
import { updateStats, buildChangesPanel } from './events.js';
|
|
10
|
+
let contextMenu;
|
|
11
|
+
export function initModals() {
|
|
12
|
+
contextMenu = document.getElementById('context-menu');
|
|
13
|
+
initContextMenu();
|
|
14
|
+
}
|
|
15
|
+
// ---- Context Menu ----
|
|
16
|
+
function initContextMenu() {
|
|
17
|
+
const canvas = getCanvas();
|
|
18
|
+
canvas.addEventListener('contextmenu', (e) => {
|
|
19
|
+
e.preventDefault();
|
|
20
|
+
// Position menu at mouse
|
|
21
|
+
contextMenu.style.left = e.clientX + 'px';
|
|
22
|
+
contextMenu.style.top = e.clientY + 'px';
|
|
23
|
+
contextMenu.classList.add('visible');
|
|
24
|
+
});
|
|
25
|
+
// Hide context menu on click elsewhere
|
|
26
|
+
document.addEventListener('click', (e) => {
|
|
27
|
+
if (!contextMenu.contains(e.target)) {
|
|
28
|
+
contextMenu.classList.remove('visible');
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
document.addEventListener('keydown', (e) => {
|
|
32
|
+
if (e.key === 'Escape') {
|
|
33
|
+
contextMenu.classList.remove('visible');
|
|
34
|
+
closeNewScriptModal();
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
// ---- New Script Creation ----
|
|
39
|
+
window.createNewScript = function () {
|
|
40
|
+
contextMenu.classList.remove('visible');
|
|
41
|
+
document.getElementById('new-script-modal').style.display = 'flex';
|
|
42
|
+
document.getElementById('new-script-path').focus();
|
|
43
|
+
};
|
|
44
|
+
window.closeNewScriptModal = function () {
|
|
45
|
+
document.getElementById('new-script-modal').style.display = 'none';
|
|
46
|
+
};
|
|
47
|
+
function closeNewScriptModal() {
|
|
48
|
+
document.getElementById('new-script-modal').style.display = 'none';
|
|
49
|
+
}
|
|
50
|
+
window.submitNewScript = async function () {
|
|
51
|
+
const path = document.getElementById('new-script-path').value.trim();
|
|
52
|
+
const extendsType = document.getElementById('new-script-extends').value;
|
|
53
|
+
const className = document.getElementById('new-script-classname').value.trim();
|
|
54
|
+
if (!path) {
|
|
55
|
+
alert('Please enter a script path');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (!path.startsWith('res://') || !path.endsWith('.gd')) {
|
|
59
|
+
alert('Path must start with res:// and end with .gd');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
// Use existing create_script tool via invokeTool (not internal)
|
|
64
|
+
const result = await sendCommand('create_script_file', {
|
|
65
|
+
path: path,
|
|
66
|
+
extends: extendsType,
|
|
67
|
+
class_name: className || ''
|
|
68
|
+
});
|
|
69
|
+
if (result.ok) {
|
|
70
|
+
closeNewScriptModal();
|
|
71
|
+
// Refresh the project map
|
|
72
|
+
refreshProject();
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
alert('Failed to create script: ' + (result.error || 'Unknown error'));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
alert('Failed to create script: ' + err.message);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
window.refreshProject = async function () {
|
|
83
|
+
contextMenu.classList.remove('visible');
|
|
84
|
+
const refreshBtn = document.getElementById('refresh-btn');
|
|
85
|
+
if (!refreshBtn)
|
|
86
|
+
return;
|
|
87
|
+
// Prevent double-clicks
|
|
88
|
+
if (refreshBtn.classList.contains('spinning'))
|
|
89
|
+
return;
|
|
90
|
+
refreshBtn.classList.add('spinning');
|
|
91
|
+
const spinStart = Date.now();
|
|
92
|
+
let success = false;
|
|
93
|
+
try {
|
|
94
|
+
const result = await sendCommand('refresh_map', {});
|
|
95
|
+
if (result.ok && result.project_map) {
|
|
96
|
+
// Build old-position lookup by path for stable repositioning
|
|
97
|
+
const oldPositions = {};
|
|
98
|
+
nodes.forEach(n => { oldPositions[n.path] = { x: n.x, y: n.y }; });
|
|
99
|
+
// Map new nodes, preserving positions for existing paths
|
|
100
|
+
const newNodes = result.project_map.nodes.map((n) => {
|
|
101
|
+
const old = oldPositions[n.path];
|
|
102
|
+
return {
|
|
103
|
+
...n,
|
|
104
|
+
x: old ? old.x : 0,
|
|
105
|
+
y: old ? old.y : 0,
|
|
106
|
+
color: categoryColorMap[n.category] || getFolderColor(n.folder),
|
|
107
|
+
highlighted: true,
|
|
108
|
+
visible: true,
|
|
109
|
+
categoryVisible: true
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
nodes.length = 0;
|
|
113
|
+
nodes.push(...newNodes);
|
|
114
|
+
edges.length = 0;
|
|
115
|
+
edges.push(...result.project_map.edges);
|
|
116
|
+
// Recalculate git change summary
|
|
117
|
+
gitChangeSummary.modified = 0;
|
|
118
|
+
gitChangeSummary.added = 0;
|
|
119
|
+
gitChangeSummary.untracked = 0;
|
|
120
|
+
nodes.forEach(n => {
|
|
121
|
+
if (n.gitStatus === 'modified')
|
|
122
|
+
gitChangeSummary.modified++;
|
|
123
|
+
else if (n.gitStatus === 'added')
|
|
124
|
+
gitChangeSummary.added++;
|
|
125
|
+
else if (n.gitStatus === 'untracked')
|
|
126
|
+
gitChangeSummary.untracked++;
|
|
127
|
+
});
|
|
128
|
+
// Rebuild changes panel UI
|
|
129
|
+
buildChangesPanel();
|
|
130
|
+
// Show/hide changes panel based on whether changes exist
|
|
131
|
+
const totalChanges = gitChangeSummary.modified + gitChangeSummary.added + gitChangeSummary.untracked;
|
|
132
|
+
const changesPanel = document.getElementById('changes-panel');
|
|
133
|
+
if (changesPanel) {
|
|
134
|
+
changesPanel.style.display = totalChanges > 0 ? '' : 'none';
|
|
135
|
+
}
|
|
136
|
+
// Only run full layout for brand-new nodes (no prior position)
|
|
137
|
+
const hasNewNodes = newNodes.some(n => n.x === 0 && n.y === 0 && !oldPositions[n.path]);
|
|
138
|
+
if (hasNewNodes) {
|
|
139
|
+
initLayout();
|
|
140
|
+
}
|
|
141
|
+
updateStats();
|
|
142
|
+
draw();
|
|
143
|
+
success = true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
console.error('Failed to refresh:', err);
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
// Ensure spinner is visible for at least 600ms so user sees feedback
|
|
151
|
+
const elapsed = Date.now() - spinStart;
|
|
152
|
+
const remaining = Math.max(0, 600 - elapsed);
|
|
153
|
+
setTimeout(() => {
|
|
154
|
+
refreshBtn.classList.remove('spinning');
|
|
155
|
+
// Flash success (green) or error (red) for 1.5s
|
|
156
|
+
const feedbackClass = success ? 'refresh-done' : 'refresh-error';
|
|
157
|
+
refreshBtn.classList.add(feedbackClass);
|
|
158
|
+
setTimeout(() => refreshBtn.classList.remove(feedbackClass), 1500);
|
|
159
|
+
}, remaining);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
function refreshProject() {
|
|
163
|
+
window.refreshProject();
|
|
164
|
+
}
|
|
165
|
+
// ---- Reset Layout ----
|
|
166
|
+
window.resetLayout = function () {
|
|
167
|
+
contextMenu.classList.remove('visible');
|
|
168
|
+
// Clear saved positions
|
|
169
|
+
clearPositions();
|
|
170
|
+
// Re-run force-directed layout
|
|
171
|
+
initLayout();
|
|
172
|
+
// Fit view to show all nodes
|
|
173
|
+
fitToView(nodes);
|
|
174
|
+
// Redraw
|
|
175
|
+
draw();
|
|
176
|
+
};
|
|
177
|
+
// ---- View Switching (Scripts/Scenes) ----
|
|
178
|
+
window.switchView = function (view) {
|
|
179
|
+
const currentViewTab = document.querySelector('#view-tabs button.active')?.textContent.toLowerCase();
|
|
180
|
+
if (view === currentViewTab)
|
|
181
|
+
return;
|
|
182
|
+
// Close any open panels
|
|
183
|
+
if (view === 'scripts') {
|
|
184
|
+
closeSceneNodePanel();
|
|
185
|
+
// Clear scene state
|
|
186
|
+
setExpandedScene(null);
|
|
187
|
+
setExpandedSceneHierarchy(null);
|
|
188
|
+
setSelectedSceneNode(null);
|
|
189
|
+
setHoveredSceneNode(null);
|
|
190
|
+
// Hide scene back button
|
|
191
|
+
const backBtn = document.getElementById('scene-back-btn');
|
|
192
|
+
if (backBtn)
|
|
193
|
+
backBtn.style.display = 'none';
|
|
194
|
+
// Show legend for scripts view
|
|
195
|
+
const legend = document.getElementById('legend');
|
|
196
|
+
if (legend)
|
|
197
|
+
legend.classList.remove('hidden');
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
closePanel();
|
|
201
|
+
}
|
|
202
|
+
setCurrentView(view);
|
|
203
|
+
// Update tab buttons
|
|
204
|
+
document.querySelectorAll('#view-tabs button').forEach(btn => {
|
|
205
|
+
btn.classList.toggle('active', btn.textContent.toLowerCase() === view);
|
|
206
|
+
});
|
|
207
|
+
// Update search placeholder
|
|
208
|
+
const searchInput = document.getElementById('search');
|
|
209
|
+
if (searchInput) {
|
|
210
|
+
searchInput.placeholder = view === 'scripts' ? 'Search scripts...' : 'Search scenes...';
|
|
211
|
+
}
|
|
212
|
+
if (view === 'scenes') {
|
|
213
|
+
loadSceneView();
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
updateStats();
|
|
217
|
+
draw();
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
async function loadSceneView() {
|
|
221
|
+
// Request scene data from Godot
|
|
222
|
+
try {
|
|
223
|
+
const result = await sendCommand('map_scenes', { root: 'res://' });
|
|
224
|
+
if (result.ok) {
|
|
225
|
+
setSceneData(result.scene_map);
|
|
226
|
+
updateStats();
|
|
227
|
+
draw();
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
console.error('Failed to load scenes:', result.error);
|
|
231
|
+
alert('Failed to load scenes: ' + (result.error || 'Unknown error'));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
console.error('Failed to load scenes:', err);
|
|
236
|
+
// Show placeholder for now
|
|
237
|
+
draw();
|
|
238
|
+
}
|
|
239
|
+
}
|