@cluesmith/codev 2.0.2 → 2.0.3

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 (209) hide show
  1. package/dashboard/dist/assets/{index-b38SaXk5.js → index-UsH9ixz1.js} +20 -20
  2. package/dashboard/dist/assets/index-UsH9ixz1.js.map +1 -0
  3. package/dashboard/dist/index.html +1 -1
  4. package/dist/agent-farm/cli.js +1 -1
  5. package/dist/agent-farm/cli.js.map +1 -1
  6. package/dist/agent-farm/commands/architect.d.ts +1 -1
  7. package/dist/agent-farm/commands/architect.js +3 -3
  8. package/dist/agent-farm/commands/architect.js.map +1 -1
  9. package/dist/agent-farm/commands/attach.js +1 -1
  10. package/dist/agent-farm/commands/attach.js.map +1 -1
  11. package/dist/agent-farm/commands/cleanup.js +6 -6
  12. package/dist/agent-farm/commands/cleanup.js.map +1 -1
  13. package/dist/agent-farm/commands/open.js +5 -5
  14. package/dist/agent-farm/commands/open.js.map +1 -1
  15. package/dist/agent-farm/commands/send.js +5 -5
  16. package/dist/agent-farm/commands/send.js.map +1 -1
  17. package/dist/agent-farm/commands/shell.js +5 -5
  18. package/dist/agent-farm/commands/shell.js.map +1 -1
  19. package/dist/agent-farm/commands/spawn-worktree.d.ts +1 -1
  20. package/dist/agent-farm/commands/spawn-worktree.d.ts.map +1 -1
  21. package/dist/agent-farm/commands/spawn-worktree.js +8 -8
  22. package/dist/agent-farm/commands/spawn-worktree.js.map +1 -1
  23. package/dist/agent-farm/commands/spawn.js +3 -3
  24. package/dist/agent-farm/commands/spawn.js.map +1 -1
  25. package/dist/agent-farm/commands/start.d.ts +4 -4
  26. package/dist/agent-farm/commands/start.js +16 -16
  27. package/dist/agent-farm/commands/start.js.map +1 -1
  28. package/dist/agent-farm/commands/status.d.ts +1 -1
  29. package/dist/agent-farm/commands/status.js +16 -16
  30. package/dist/agent-farm/commands/status.js.map +1 -1
  31. package/dist/agent-farm/commands/stop.d.ts +4 -4
  32. package/dist/agent-farm/commands/stop.js +9 -9
  33. package/dist/agent-farm/commands/stop.js.map +1 -1
  34. package/dist/agent-farm/db/index.d.ts.map +1 -1
  35. package/dist/agent-farm/db/index.js +82 -7
  36. package/dist/agent-farm/db/index.js.map +1 -1
  37. package/dist/agent-farm/db/schema.d.ts +2 -2
  38. package/dist/agent-farm/db/schema.d.ts.map +1 -1
  39. package/dist/agent-farm/db/schema.js +21 -4
  40. package/dist/agent-farm/db/schema.js.map +1 -1
  41. package/dist/agent-farm/lib/tower-client.d.ts +20 -20
  42. package/dist/agent-farm/lib/tower-client.d.ts.map +1 -1
  43. package/dist/agent-farm/lib/tower-client.js +25 -25
  44. package/dist/agent-farm/lib/tower-client.js.map +1 -1
  45. package/dist/agent-farm/lib/tunnel-client.d.ts +12 -2
  46. package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -1
  47. package/dist/agent-farm/lib/tunnel-client.js +59 -1
  48. package/dist/agent-farm/lib/tunnel-client.js.map +1 -1
  49. package/dist/agent-farm/servers/tower-instances.d.ts +18 -18
  50. package/dist/agent-farm/servers/tower-instances.d.ts.map +1 -1
  51. package/dist/agent-farm/servers/tower-instances.js +89 -89
  52. package/dist/agent-farm/servers/tower-instances.js.map +1 -1
  53. package/dist/agent-farm/servers/tower-routes.d.ts +1 -1
  54. package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -1
  55. package/dist/agent-farm/servers/tower-routes.js +184 -162
  56. package/dist/agent-farm/servers/tower-routes.js.map +1 -1
  57. package/dist/agent-farm/servers/tower-server.js +23 -19
  58. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  59. package/dist/agent-farm/servers/tower-terminals.d.ts +27 -29
  60. package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -1
  61. package/dist/agent-farm/servers/tower-terminals.js +95 -116
  62. package/dist/agent-farm/servers/tower-terminals.js.map +1 -1
  63. package/dist/agent-farm/servers/tower-tunnel.d.ts +2 -2
  64. package/dist/agent-farm/servers/tower-tunnel.d.ts.map +1 -1
  65. package/dist/agent-farm/servers/tower-tunnel.js +12 -12
  66. package/dist/agent-farm/servers/tower-tunnel.js.map +1 -1
  67. package/dist/agent-farm/servers/tower-types.d.ts +8 -10
  68. package/dist/agent-farm/servers/tower-types.d.ts.map +1 -1
  69. package/dist/agent-farm/servers/tower-utils.d.ts +9 -9
  70. package/dist/agent-farm/servers/tower-utils.d.ts.map +1 -1
  71. package/dist/agent-farm/servers/tower-utils.js +18 -18
  72. package/dist/agent-farm/servers/tower-utils.js.map +1 -1
  73. package/dist/agent-farm/servers/tower-websocket.d.ts +2 -2
  74. package/dist/agent-farm/servers/tower-websocket.js +14 -14
  75. package/dist/agent-farm/servers/tower-websocket.js.map +1 -1
  76. package/dist/agent-farm/types.d.ts +2 -2
  77. package/dist/agent-farm/types.d.ts.map +1 -1
  78. package/dist/agent-farm/utils/config.d.ts +1 -1
  79. package/dist/agent-farm/utils/config.d.ts.map +1 -1
  80. package/dist/agent-farm/utils/config.js +16 -16
  81. package/dist/agent-farm/utils/config.js.map +1 -1
  82. package/dist/agent-farm/utils/file-tabs.d.ts +3 -3
  83. package/dist/agent-farm/utils/file-tabs.d.ts.map +1 -1
  84. package/dist/agent-farm/utils/file-tabs.js +9 -9
  85. package/dist/agent-farm/utils/file-tabs.js.map +1 -1
  86. package/dist/agent-farm/utils/gate-status.d.ts +2 -2
  87. package/dist/agent-farm/utils/gate-status.d.ts.map +1 -1
  88. package/dist/agent-farm/utils/gate-status.js +3 -3
  89. package/dist/agent-farm/utils/gate-status.js.map +1 -1
  90. package/dist/agent-farm/utils/index.d.ts +0 -1
  91. package/dist/agent-farm/utils/index.d.ts.map +1 -1
  92. package/dist/agent-farm/utils/index.js +0 -1
  93. package/dist/agent-farm/utils/index.js.map +1 -1
  94. package/dist/agent-farm/utils/notifications.d.ts +4 -4
  95. package/dist/agent-farm/utils/notifications.d.ts.map +1 -1
  96. package/dist/agent-farm/utils/notifications.js +18 -18
  97. package/dist/agent-farm/utils/notifications.js.map +1 -1
  98. package/dist/commands/adopt.d.ts +2 -2
  99. package/dist/commands/adopt.d.ts.map +1 -1
  100. package/dist/commands/adopt.js +13 -3
  101. package/dist/commands/adopt.js.map +1 -1
  102. package/dist/commands/consult/index.d.ts +1 -1
  103. package/dist/commands/consult/index.d.ts.map +1 -1
  104. package/dist/commands/consult/index.js +52 -51
  105. package/dist/commands/consult/index.js.map +1 -1
  106. package/dist/commands/doctor.js +6 -6
  107. package/dist/commands/doctor.js.map +1 -1
  108. package/dist/commands/import.js +4 -4
  109. package/dist/commands/import.js.map +1 -1
  110. package/dist/commands/init.d.ts +2 -2
  111. package/dist/commands/init.d.ts.map +1 -1
  112. package/dist/commands/init.js +13 -3
  113. package/dist/commands/init.js.map +1 -1
  114. package/dist/commands/porch/index.d.ts +6 -6
  115. package/dist/commands/porch/index.d.ts.map +1 -1
  116. package/dist/commands/porch/index.js +37 -37
  117. package/dist/commands/porch/index.js.map +1 -1
  118. package/dist/commands/porch/next.d.ts +1 -1
  119. package/dist/commands/porch/next.d.ts.map +1 -1
  120. package/dist/commands/porch/next.js +43 -40
  121. package/dist/commands/porch/next.js.map +1 -1
  122. package/dist/commands/porch/notify.d.ts +11 -0
  123. package/dist/commands/porch/notify.d.ts.map +1 -0
  124. package/dist/commands/porch/notify.js +30 -0
  125. package/dist/commands/porch/notify.js.map +1 -0
  126. package/dist/commands/porch/plan.d.ts +1 -1
  127. package/dist/commands/porch/plan.d.ts.map +1 -1
  128. package/dist/commands/porch/plan.js +3 -3
  129. package/dist/commands/porch/plan.js.map +1 -1
  130. package/dist/commands/porch/prompts.d.ts +1 -1
  131. package/dist/commands/porch/prompts.d.ts.map +1 -1
  132. package/dist/commands/porch/prompts.js +13 -13
  133. package/dist/commands/porch/prompts.js.map +1 -1
  134. package/dist/commands/porch/protocol.d.ts +1 -1
  135. package/dist/commands/porch/protocol.d.ts.map +1 -1
  136. package/dist/commands/porch/protocol.js +6 -6
  137. package/dist/commands/porch/protocol.js.map +1 -1
  138. package/dist/commands/porch/state.d.ts +6 -6
  139. package/dist/commands/porch/state.d.ts.map +1 -1
  140. package/dist/commands/porch/state.js +11 -11
  141. package/dist/commands/porch/state.js.map +1 -1
  142. package/dist/commands/update.d.ts.map +1 -1
  143. package/dist/commands/update.js +10 -1
  144. package/dist/commands/update.js.map +1 -1
  145. package/dist/lib/scaffold.d.ts +13 -0
  146. package/dist/lib/scaffold.d.ts.map +1 -1
  147. package/dist/lib/scaffold.js +34 -0
  148. package/dist/lib/scaffold.js.map +1 -1
  149. package/dist/lib/skeleton.d.ts +7 -7
  150. package/dist/lib/skeleton.d.ts.map +1 -1
  151. package/dist/lib/skeleton.js +10 -10
  152. package/dist/lib/skeleton.js.map +1 -1
  153. package/dist/terminal/pty-manager.d.ts +1 -1
  154. package/dist/terminal/pty-manager.d.ts.map +1 -1
  155. package/dist/terminal/pty-manager.js +3 -3
  156. package/dist/terminal/pty-manager.js.map +1 -1
  157. package/package.json +1 -1
  158. package/templates/open.html +13 -13
  159. package/templates/tower.html +54 -54
  160. package/templates/vendor/marked.min.js +6 -0
  161. package/templates/vendor/prism-bash.min.js +1 -0
  162. package/templates/vendor/prism-css.min.js +1 -0
  163. package/templates/vendor/prism-javascript.min.js +1 -0
  164. package/templates/vendor/prism-json.min.js +1 -0
  165. package/templates/vendor/prism-markdown.min.js +1 -0
  166. package/templates/vendor/prism-markup.min.js +1 -0
  167. package/templates/vendor/prism-python.min.js +1 -0
  168. package/templates/vendor/prism-tomorrow.min.css +1 -0
  169. package/templates/vendor/prism-typescript.min.js +1 -0
  170. package/templates/vendor/prism-yaml.min.js +1 -0
  171. package/templates/vendor/prism.min.js +1 -0
  172. package/templates/vendor/purify.min.js +3 -0
  173. package/dashboard/dist/assets/index-b38SaXk5.js.map +0 -1
  174. package/dist/agent-farm/hq-connector.d.ts +0 -19
  175. package/dist/agent-farm/hq-connector.d.ts.map +0 -1
  176. package/dist/agent-farm/hq-connector.js +0 -351
  177. package/dist/agent-farm/hq-connector.js.map +0 -1
  178. package/dist/agent-farm/utils/deps.d.ts +0 -51
  179. package/dist/agent-farm/utils/deps.d.ts.map +0 -1
  180. package/dist/agent-farm/utils/deps.js +0 -162
  181. package/dist/agent-farm/utils/deps.js.map +0 -1
  182. package/dist/agent-farm/utils/gate-watcher.d.ts +0 -38
  183. package/dist/agent-farm/utils/gate-watcher.d.ts.map +0 -1
  184. package/dist/agent-farm/utils/gate-watcher.js +0 -122
  185. package/dist/agent-farm/utils/gate-watcher.js.map +0 -1
  186. package/dist/agent-farm/utils/session.d.ts +0 -32
  187. package/dist/agent-farm/utils/session.d.ts.map +0 -1
  188. package/dist/agent-farm/utils/session.js +0 -57
  189. package/dist/agent-farm/utils/session.js.map +0 -1
  190. package/dist/lib/projectlist-parser.d.ts +0 -70
  191. package/dist/lib/projectlist-parser.d.ts.map +0 -1
  192. package/dist/lib/projectlist-parser.js +0 -200
  193. package/dist/lib/projectlist-parser.js.map +0 -1
  194. package/templates/dashboard/css/dialogs.css +0 -149
  195. package/templates/dashboard/css/files.css +0 -558
  196. package/templates/dashboard/css/layout.css +0 -133
  197. package/templates/dashboard/css/projects.css +0 -501
  198. package/templates/dashboard/css/statusbar.css +0 -23
  199. package/templates/dashboard/css/tabs.css +0 -314
  200. package/templates/dashboard/css/utilities.css +0 -50
  201. package/templates/dashboard/css/variables.css +0 -45
  202. package/templates/dashboard/index.html +0 -149
  203. package/templates/dashboard/js/dialogs.js +0 -368
  204. package/templates/dashboard/js/files.js +0 -448
  205. package/templates/dashboard/js/main.js +0 -476
  206. package/templates/dashboard/js/projects.js +0 -544
  207. package/templates/dashboard/js/state.js +0 -91
  208. package/templates/dashboard/js/tabs.js +0 -518
  209. package/templates/dashboard/js/utils.js +0 -191
@@ -1,476 +0,0 @@
1
- // Dashboard Main - Initialization, Polling, Keyboard Shortcuts
2
-
3
- // Initialize the dashboard
4
- function init() {
5
- buildTabsFromState();
6
- renderArchitect();
7
- renderTabs();
8
- renderTabContent();
9
- updateStatusBar();
10
- startPolling();
11
- setupBroadcastChannel();
12
- setupOverflowDetection();
13
- setupKeyboardShortcuts();
14
- setupActivityModalListeners();
15
- }
16
-
17
- // Set up BroadcastChannel for cross-tab communication
18
- function setupBroadcastChannel() {
19
- const channel = new BroadcastChannel('agent-farm');
20
- channel.onmessage = async (event) => {
21
- const { type, path, line } = event.data;
22
- if (type === 'openFile' && path) {
23
- await openFileFromMessage(path, line);
24
- }
25
- };
26
- }
27
-
28
- // Open a file from a BroadcastChannel message
29
- // Uses shared openFileTab from utils.js (Maintenance Run 0004)
30
- async function openFileFromMessage(filePath, lineNumber) {
31
- await openFileTab(filePath, { lineNumber });
32
- }
33
-
34
- // Refresh state from API
35
- async function refresh() {
36
- try {
37
- const response = await fetch(apiUrl('api/state'));
38
- if (!response.ok) throw new Error('Failed to fetch state');
39
-
40
- const newState = await response.json();
41
- Object.assign(state, newState);
42
-
43
- buildTabsFromState();
44
- renderArchitect();
45
- renderTabs();
46
- renderTabContent();
47
- updateStatusBar();
48
- } catch (err) {
49
- console.error('Refresh error:', err);
50
- }
51
- }
52
-
53
- // Polling for state updates
54
- function startPolling() {
55
- pollInterval = setInterval(refresh, 1000);
56
- }
57
-
58
- function stopPolling() {
59
- if (pollInterval) {
60
- clearInterval(pollInterval);
61
- pollInterval = null;
62
- }
63
- }
64
-
65
- // Poll for projectlist.md creation when in starter mode
66
- async function pollForProjectlistCreation() {
67
- try {
68
- const response = await fetch(apiUrl('api/projectlist-exists'));
69
- if (!response.ok) return;
70
-
71
- const { exists } = await response.json();
72
- if (exists) {
73
- if (starterModePollingInterval) {
74
- clearInterval(starterModePollingInterval);
75
- starterModePollingInterval = null;
76
- }
77
- window.location.reload();
78
- }
79
- } catch (err) {
80
- // Silently ignore polling errors
81
- }
82
- }
83
-
84
- // Check if we should start starter mode polling
85
- function checkStarterMode() {
86
- const isStarterMode = projectsData.length === 0 && !projectlistError && projectlistHash === null;
87
-
88
- if (isStarterMode && !starterModePollingInterval) {
89
- starterModePollingInterval = setInterval(pollForProjectlistCreation, 15000);
90
- } else if (!isStarterMode && starterModePollingInterval) {
91
- clearInterval(starterModePollingInterval);
92
- starterModePollingInterval = null;
93
- }
94
- }
95
-
96
- // Render the info header
97
- function renderInfoHeader() {
98
- return `
99
- <div class="projects-info">
100
- <h1 style="font-size: 20px; margin-bottom: 12px; color: var(--text-primary);">Agent Farm Dashboard</h1>
101
- <p>Coordinate AI builders working on your codebase. The left panel shows the Architect terminal – tell it what you want to build. <strong>Tabs</strong> shows open terminals (Architect, Builders, utility shells). <strong>Files</strong> lets you browse and open project files. <strong>Projects</strong> tracks work as it moves from conception to integration.</p>
102
- <p>Docs: <a href="#" onclick="openProjectFile('codev/resources/cheatsheet.md'); return false;">Cheatsheet</a> · <a href="#" onclick="openProjectFile('codev/resources/lifecycle.md'); return false;">Lifecycle</a> · <a href="#" onclick="openProjectFile('codev/resources/commands/overview.md'); return false;">CLI Reference</a> · <a href="#" onclick="openProjectFile('codev/protocols/spir/protocol.md'); return false;">SPIR Protocol</a> · <a href="https://github.com/cluesmith/codev#readme" target="_blank">README</a> · <a href="https://discord.gg/mJ92DhDa6n" target="_blank">Discord</a></p>
103
- </div>
104
- `;
105
- }
106
-
107
- // Render the dashboard tab content
108
- function renderDashboardTabContent() {
109
- const content = document.getElementById('tab-content');
110
-
111
- content.innerHTML = `
112
- <div class="dashboard-container">
113
- ${renderInfoHeader()}
114
- <div class="dashboard-header">
115
- <!-- Tabs Section -->
116
- <div class="dashboard-section section-tabs ${sectionState.tabs ? '' : 'collapsed'}">
117
- <div class="dashboard-section-header" onclick="toggleSection('tabs')">
118
- <h3><span class="collapse-icon">▼</span> Tabs</h3>
119
- </div>
120
- <div class="dashboard-section-content">
121
- <div class="dashboard-tabs-list" id="dashboard-tabs-list">
122
- ${renderDashboardTabsList()}
123
- </div>
124
- </div>
125
- </div>
126
- <!-- Files Section -->
127
- <div class="dashboard-section section-files ${sectionState.files ? '' : 'collapsed'}">
128
- <div class="dashboard-section-header" onclick="toggleSection('files')">
129
- <h3><span class="collapse-icon">▼</span> Files</h3>
130
- <div class="header-actions" onclick="event.stopPropagation()">
131
- <button class="btn-create" onclick="showCreateFileDialog()" title="Create New File">+</button>
132
- <button onclick="refreshFilesTree()" title="Refresh">↻</button>
133
- <button onclick="collapseAllFolders()" title="Collapse All">⊟</button>
134
- <button onclick="expandAllFolders()" title="Expand All">⊞</button>
135
- </div>
136
- </div>
137
- <div class="dashboard-section-content">
138
- <div class="files-search-container" onclick="event.stopPropagation()">
139
- <input type="text"
140
- id="files-search-input"
141
- class="files-search-input"
142
- placeholder="Search files by name..."
143
- oninput="onFilesSearchInput(this.value)"
144
- onkeydown="onFilesSearchKeydown(event)"
145
- value="${escapeHtml(filesSearchQuery)}" />
146
- <button class="files-search-clear ${filesSearchQuery ? '' : 'hidden'}"
147
- onclick="clearFilesSearch()"
148
- title="Clear search">×</button>
149
- </div>
150
- <div id="dashboard-files-content">
151
- ${filesSearchQuery ? renderFilesSearchResults() : renderDashboardFilesBrowserWithWrapper()}
152
- </div>
153
- </div>
154
- </div>
155
- </div>
156
- <!-- Projects Section -->
157
- <div class="dashboard-section section-projects ${sectionState.projects ? '' : 'collapsed'}">
158
- <div class="dashboard-section-header" onclick="toggleSection('projects')">
159
- <h3><span class="collapse-icon">▼</span> Projects</h3>
160
- </div>
161
- <div class="dashboard-section-content" id="dashboard-projects">
162
- ${renderDashboardProjectsSection()}
163
- </div>
164
- </div>
165
- </div>
166
- `;
167
- }
168
-
169
- // Render the tabs list for dashboard
170
- function renderDashboardTabsList() {
171
- const terminalTabs = tabs.filter(t => t.type !== 'dashboard' && t.type !== 'files');
172
-
173
- // Action items at the top of the list
174
- const actionItems = `
175
- <div class="dashboard-tab-item dashboard-tab-action" onclick="spawnShell()">
176
- <span class="tab-icon">+</span>
177
- <span class="tab-name">Create new shell</span>
178
- </div>
179
- <div class="dashboard-tab-item dashboard-tab-action" onclick="spawnBuilder()">
180
- <span class="tab-icon">+</span>
181
- <span class="tab-name">Create new worktree + shell</span>
182
- </div>
183
- `;
184
-
185
- if (terminalTabs.length === 0) {
186
- return actionItems;
187
- }
188
-
189
- const tabItems = terminalTabs.map(tab => {
190
- const isActive = tab.id === activeTabId;
191
- const icon = getTabIcon(tab.type);
192
- const statusIndicator = getDashboardStatusIndicator(tab);
193
-
194
- return `
195
- <div class="dashboard-tab-item ${isActive ? 'active' : ''}" onclick="selectTab('${tab.id}')">
196
- ${statusIndicator}
197
- <span class="tab-icon">${icon}</span>
198
- <span class="tab-name">${escapeHtml(tab.name)}</span>
199
- </div>
200
- `;
201
- }).join('');
202
-
203
- return actionItems + tabItems;
204
- }
205
-
206
- // Get status indicator for dashboard tab list
207
- function getDashboardStatusIndicator(tab) {
208
- if (tab.type !== 'builder') return '';
209
-
210
- const builderState = (state.builders || []).find(b => `builder-${b.id}` === tab.id);
211
- if (!builderState) return '';
212
-
213
- const status = builderState.status;
214
- if (['spawning', 'implementing'].includes(status)) {
215
- return '<span class="dashboard-status-indicator dashboard-status-working" title="Working"></span>';
216
- }
217
- if (status === 'blocked') {
218
- return '<span class="dashboard-status-indicator dashboard-status-blocked" title="Blocked"></span>';
219
- }
220
- if (['pr-ready', 'complete'].includes(status)) {
221
- return '<span class="dashboard-status-indicator dashboard-status-idle" title="Idle"></span>';
222
- }
223
- return '';
224
- }
225
-
226
- // Render the dashboard tab (entry point)
227
- async function renderDashboardTab() {
228
- const content = document.getElementById('tab-content');
229
- content.innerHTML = '<div class="dashboard-container"><p style="color: var(--text-muted); padding: 16px;">Loading dashboard...</p></div>';
230
-
231
- await Promise.all([
232
- loadProjectlist(),
233
- loadFilesTreeIfNeeded()
234
- ]);
235
-
236
- // Start auto-polling for file changes
237
- startFilesPolling();
238
-
239
- renderDashboardTabContent();
240
- checkStarterMode();
241
- }
242
-
243
- // Set up keyboard shortcuts
244
- function setupKeyboardShortcuts() {
245
- document.addEventListener('keydown', (e) => {
246
- // Escape to close dialogs and menus
247
- if (e.key === 'Escape') {
248
- hideFileDialog();
249
- hideCloseDialog();
250
- hideCreateFileDialog();
251
- hideContextMenu();
252
- hideOverflowMenu();
253
- const activityModal = document.getElementById('activity-modal');
254
- if (activityModal && !activityModal.classList.contains('hidden')) {
255
- closeActivityModal();
256
- }
257
- if (paletteOpen) {
258
- closePalette();
259
- }
260
- }
261
-
262
- // Enter in dialogs
263
- if (e.key === 'Enter') {
264
- if (!document.getElementById('file-dialog').classList.contains('hidden')) {
265
- openFile();
266
- }
267
- if (!document.getElementById('create-file-dialog').classList.contains('hidden')) {
268
- createFile();
269
- }
270
- }
271
-
272
- // Ctrl+Tab / Ctrl+Shift+Tab to switch tabs
273
- if (e.ctrlKey && e.key === 'Tab') {
274
- e.preventDefault();
275
- if (tabs.length < 2) return;
276
-
277
- const currentIndex = tabs.findIndex(t => t.id === activeTabId);
278
- let newIndex;
279
-
280
- if (e.shiftKey) {
281
- newIndex = currentIndex <= 0 ? tabs.length - 1 : currentIndex - 1;
282
- } else {
283
- newIndex = currentIndex >= tabs.length - 1 ? 0 : currentIndex + 1;
284
- }
285
-
286
- selectTab(tabs[newIndex].id);
287
- }
288
-
289
- // Ctrl+W to close current tab
290
- if (e.ctrlKey && e.key === 'w') {
291
- e.preventDefault();
292
- if (activeTabId) {
293
- closeTab(activeTabId, e);
294
- }
295
- }
296
-
297
- // Cmd+P (macOS) or Ctrl+P (Windows/Linux) for file search palette
298
- if ((e.metaKey || e.ctrlKey) && e.key === 'p') {
299
- const active = document.activeElement;
300
- const isOurInput = active?.id === 'palette-input' || active?.id === 'files-search-input';
301
- const isEditable = active?.tagName === 'INPUT' || active?.tagName === 'TEXTAREA' || active?.isContentEditable;
302
-
303
- if (!isOurInput && isEditable) return;
304
-
305
- e.preventDefault();
306
- if (paletteOpen) {
307
- closePalette();
308
- } else {
309
- openPalette();
310
- }
311
- }
312
- });
313
- }
314
-
315
- // Set up activity modal event listeners
316
- function setupActivityModalListeners() {
317
- const activityModal = document.getElementById('activity-modal');
318
- if (activityModal) {
319
- activityModal.addEventListener('click', (e) => {
320
- if (e.target.id === 'activity-modal') {
321
- closeActivityModal();
322
- }
323
- });
324
- }
325
- }
326
-
327
- // Start projectlist polling (separate from main state polling)
328
- setInterval(pollProjectlist, 5000);
329
-
330
- // ========================================
331
- // Hot Reload Functions (Spec 0060)
332
- // ========================================
333
-
334
- // Hot reload CSS by replacing stylesheet link with cache-busted version
335
- function hotReloadCSS(filename) {
336
- const links = document.querySelectorAll(`link[href^="/dashboard/css/${filename}"]`);
337
- links.forEach(link => {
338
- const newHref = `/dashboard/css/${filename}?t=${Date.now()}`;
339
- link.href = newHref;
340
- });
341
- console.log(`[Hot Reload] CSS updated: ${filename}`);
342
- }
343
-
344
- // Hot reload JS by saving state and reloading page
345
- function hotReloadJS(filename) {
346
- // Save current UI state to sessionStorage for restoration after reload
347
- try {
348
- const uiState = {
349
- activeTabId,
350
- sectionState,
351
- filesTreeExpanded: Array.from(filesTreeExpanded),
352
- expandedProjectId,
353
- filesSearchQuery,
354
- paletteOpen
355
- };
356
- sessionStorage.setItem('codev-hot-reload-state', JSON.stringify(uiState));
357
- } catch (e) {
358
- console.warn('[Hot Reload] Could not save state:', e);
359
- }
360
-
361
- console.log(`[Hot Reload] JS changed: ${filename} - reloading page...`);
362
- showToast(`Reloading for ${filename} changes...`, 'info');
363
-
364
- // Small delay to show toast before reload
365
- setTimeout(() => {
366
- window.location.reload();
367
- }, 300);
368
- }
369
-
370
- // Restore UI state after hot reload
371
- function restoreHotReloadState() {
372
- try {
373
- const saved = sessionStorage.getItem('codev-hot-reload-state');
374
- if (!saved) return;
375
-
376
- const uiState = JSON.parse(saved);
377
- sessionStorage.removeItem('codev-hot-reload-state');
378
-
379
- // Restore section state
380
- if (uiState.sectionState) {
381
- sectionState = uiState.sectionState;
382
- saveSectionState();
383
- }
384
-
385
- // Restore files tree expansion
386
- if (uiState.filesTreeExpanded) {
387
- filesTreeExpanded = new Set(uiState.filesTreeExpanded);
388
- }
389
-
390
- // Restore expanded project
391
- if (uiState.expandedProjectId) {
392
- expandedProjectId = uiState.expandedProjectId;
393
- }
394
-
395
- // Restore active tab (will be applied after tabs are built)
396
- if (uiState.activeTabId) {
397
- // Store for later application
398
- window._hotReloadActiveTabId = uiState.activeTabId;
399
- }
400
-
401
- console.log('[Hot Reload] State restored from previous session');
402
- } catch (e) {
403
- console.warn('[Hot Reload] Could not restore state:', e);
404
- }
405
- }
406
-
407
- // Apply restored active tab after tabs are built
408
- function applyRestoredActiveTab() {
409
- if (window._hotReloadActiveTabId) {
410
- const restoredTab = tabs.find(t => t.id === window._hotReloadActiveTabId);
411
- if (restoredTab) {
412
- activeTabId = window._hotReloadActiveTabId;
413
- }
414
- delete window._hotReloadActiveTabId;
415
- }
416
- }
417
-
418
- // Poll for file changes
419
- async function pollHotReload() {
420
- if (!hotReloadEnabled) return;
421
-
422
- try {
423
- const response = await fetch(apiUrl('api/hot-reload'));
424
- if (!response.ok) return;
425
-
426
- const data = await response.json();
427
- const newMtimes = data.mtimes || {};
428
-
429
- // Check for changes
430
- for (const [file, mtime] of Object.entries(newMtimes)) {
431
- const oldMtime = hotReloadMtimes[file];
432
-
433
- if (oldMtime !== undefined && oldMtime !== mtime) {
434
- // File changed!
435
- const filename = file.split('/').pop();
436
-
437
- if (file.startsWith('css/')) {
438
- hotReloadCSS(filename);
439
- } else if (file.startsWith('js/')) {
440
- hotReloadJS(filename);
441
- return; // Stop polling, page will reload
442
- }
443
- }
444
- }
445
-
446
- // Update tracked mtimes
447
- hotReloadMtimes = newMtimes;
448
- } catch (err) {
449
- // Silently ignore polling errors
450
- }
451
- }
452
-
453
- // Start hot reload polling
454
- function startHotReload() {
455
- if (hotReloadInterval) return;
456
-
457
- // Initial fetch to populate mtimes
458
- pollHotReload();
459
-
460
- // Poll every 2 seconds
461
- hotReloadInterval = setInterval(pollHotReload, 2000);
462
- }
463
-
464
- // Stop hot reload polling
465
- function stopHotReload() {
466
- if (hotReloadInterval) {
467
- clearInterval(hotReloadInterval);
468
- hotReloadInterval = null;
469
- }
470
- }
471
-
472
- // Initialize on load
473
- restoreHotReloadState();
474
- init();
475
- applyRestoredActiveTab();
476
- startHotReload();