x-ipe 1.0.23__py3-none-any.whl → 1.0.25__py3-none-any.whl

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 (146) hide show
  1. x_ipe/app.py +32 -1
  2. x_ipe/handlers/terminal_handlers.py +6 -0
  3. x_ipe/handlers/voice_handlers.py +5 -0
  4. x_ipe/resources/copilot-instructions.md +19 -6
  5. x_ipe/resources/skills/lesson-learned/SKILL.md +208 -0
  6. x_ipe/resources/skills/lesson-learned/references/examples.md +238 -0
  7. x_ipe/resources/skills/project-quality-board-management/SKILL.md +135 -298
  8. x_ipe/resources/skills/project-quality-board-management/references/evaluation-principles.md +213 -0
  9. x_ipe/resources/skills/project-quality-board-management/references/evaluation-procedures.md +214 -0
  10. x_ipe/resources/skills/project-quality-board-management/templates/quality-report.md +70 -18
  11. x_ipe/resources/skills/task-execution-guideline/SKILL.md +2 -2
  12. x_ipe/resources/skills/task-execution-guideline/templates/task-record.yaml +1 -1
  13. x_ipe/resources/skills/task-type-code-implementation/SKILL.md +72 -270
  14. x_ipe/resources/skills/task-type-code-implementation/references/implementation-guidelines.md +432 -0
  15. x_ipe/resources/skills/task-type-code-refactor-v2/SKILL.md +127 -353
  16. x_ipe/resources/skills/task-type-code-refactor-v2/references/refactoring-techniques.md +373 -0
  17. x_ipe/resources/skills/task-type-feature-breakdown/SKILL.md +31 -243
  18. x_ipe/resources/skills/task-type-feature-breakdown/references/breakdown-guidelines.md +330 -0
  19. x_ipe/resources/skills/task-type-feature-refinement/SKILL.md +27 -180
  20. x_ipe/resources/skills/task-type-feature-refinement/references/specification-writing-guide.md +267 -0
  21. x_ipe/resources/skills/task-type-idea-mockup/SKILL.md +38 -276
  22. x_ipe/resources/skills/task-type-idea-mockup/references/mockup-guidelines.md +299 -0
  23. x_ipe/resources/skills/task-type-idea-to-architecture/SKILL.md +20 -218
  24. x_ipe/resources/skills/task-type-idea-to-architecture/references/architecture-patterns.md +342 -0
  25. x_ipe/resources/skills/task-type-ideation/SKILL.md +10 -266
  26. x_ipe/resources/skills/task-type-ideation/references/folder-naming-guide.md +55 -0
  27. x_ipe/resources/skills/task-type-ideation/references/tool-usage-guide.md +236 -0
  28. x_ipe/resources/skills/task-type-ideation-v2/SKILL.md +488 -0
  29. x_ipe/resources/skills/task-type-ideation-v2/references/examples.md +377 -0
  30. x_ipe/resources/skills/task-type-ideation-v2/references/folder-naming-guide.md +74 -0
  31. x_ipe/resources/skills/task-type-ideation-v2/references/tool-usage-guide.md +145 -0
  32. x_ipe/resources/skills/task-type-ideation-v2/references/visualization-guide.md +160 -0
  33. x_ipe/resources/skills/task-type-ideation-v2/templates/idea-summary.md +86 -0
  34. x_ipe/resources/skills/task-type-refactoring-analysis/SKILL.md +83 -145
  35. x_ipe/resources/skills/task-type-refactoring-analysis/references/output-schema.md +172 -0
  36. x_ipe/resources/skills/task-type-technical-design/SKILL.md +28 -214
  37. x_ipe/resources/skills/task-type-technical-design/references/design-templates.md +422 -0
  38. x_ipe/resources/skills/task-type-test-generation/SKILL.md +47 -332
  39. x_ipe/resources/skills/task-type-test-generation/references/test-patterns.md +368 -0
  40. x_ipe/resources/skills/tool-tracing-creator/SKILL.md +312 -0
  41. x_ipe/resources/skills/tool-tracing-creator/references/examples.md +324 -0
  42. x_ipe/resources/skills/tool-tracing-instrumentation/SKILL.md +373 -0
  43. x_ipe/resources/skills/tool-tracing-instrumentation/references/examples.md +264 -0
  44. x_ipe/resources/skills/x-ipe-skill-creator-v3/SKILL.md +486 -0
  45. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/10. example-gate-conditions.md +73 -0
  46. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/11. reference-quality-standards.md +127 -0
  47. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/2. reference-section-order.md +127 -0
  48. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/3. example-step-based-code-review.md +84 -0
  49. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/4. example-step-based-feature-implementation.md +113 -0
  50. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/5. example-function-based-validation.md +73 -0
  51. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/6. example-function-based-analysis.md +94 -0
  52. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/7. example-task-io-code-implementation.md +36 -0
  53. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/8. example-structured-summary.md +43 -0
  54. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/9. example-dor-dod.md +77 -0
  55. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/examples.md +429 -0
  56. x_ipe/resources/skills/x-ipe-skill-creator-v3/references/skill-general-guidelines-v2.md +611 -0
  57. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-meta.md +153 -0
  58. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-task-based.md +324 -0
  59. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-task-category.md +109 -0
  60. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-tool.md +205 -0
  61. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-meta.md +334 -0
  62. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-task-based.md +279 -0
  63. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-tool.md +175 -0
  64. x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-workflow-orchestration.md +329 -0
  65. x_ipe/resources/skills/x-ipe-task-based-ideation/SKILL.md +487 -0
  66. x_ipe/resources/skills/x-ipe-task-based-ideation/references/examples.md +377 -0
  67. x_ipe/resources/skills/x-ipe-task-based-ideation/references/folder-naming-guide.md +74 -0
  68. x_ipe/resources/skills/x-ipe-task-based-ideation/references/tool-usage-guide.md +145 -0
  69. x_ipe/resources/skills/x-ipe-task-based-ideation/references/visualization-guide.md +160 -0
  70. x_ipe/resources/skills/x-ipe-task-based-ideation/templates/idea-summary.md +86 -0
  71. x_ipe/routes/__init__.py +2 -0
  72. x_ipe/routes/ideas_routes.py +289 -0
  73. x_ipe/routes/kb_routes.py +80 -0
  74. x_ipe/routes/main_routes.py +18 -0
  75. x_ipe/routes/project_routes.py +7 -0
  76. x_ipe/routes/proxy_routes.py +10 -2
  77. x_ipe/routes/quality_evaluation_routes.py +193 -0
  78. x_ipe/routes/settings_routes.py +6 -0
  79. x_ipe/routes/tools_routes.py +6 -0
  80. x_ipe/routes/tracing_routes.py +232 -0
  81. x_ipe/routes/uiux_feedback_routes.py +50 -0
  82. x_ipe/services/__init__.py +5 -0
  83. x_ipe/services/config_service.py +6 -0
  84. x_ipe/services/file_service.py +20 -0
  85. x_ipe/services/homepage_service.py +160 -0
  86. x_ipe/services/ideas_service.py +535 -2
  87. x_ipe/services/kb_service.py +378 -0
  88. x_ipe/services/proxy_service.py +37 -7
  89. x_ipe/services/settings_service.py +13 -0
  90. x_ipe/services/skills_service.py +4 -0
  91. x_ipe/services/terminal_service.py +24 -0
  92. x_ipe/services/themes_service.py +4 -0
  93. x_ipe/services/tools_config_service.py +4 -0
  94. x_ipe/services/tracing_service.py +333 -0
  95. x_ipe/services/uiux_feedback_service.py +148 -1
  96. x_ipe/services/voice_input_service_v2.py +11 -0
  97. x_ipe/static/css/base.css +7 -0
  98. x_ipe/static/css/homepage-infinity.css +330 -0
  99. x_ipe/static/css/kb-core.css +301 -0
  100. x_ipe/static/css/quality-evaluation.css +345 -0
  101. x_ipe/static/css/sidebar.css +14 -4
  102. x_ipe/static/css/terminal.css +23 -0
  103. x_ipe/static/css/tracing-dashboard.css +796 -0
  104. x_ipe/static/css/uiux-feedback.css +7 -1
  105. x_ipe/static/css/workplace.css +636 -0
  106. x_ipe/static/img/homepage-infinity-loop.png +0 -0
  107. x_ipe/static/js/features/confirm-dialog.js +169 -0
  108. x_ipe/static/js/features/folder-view.js +742 -0
  109. x_ipe/static/js/features/homepage-infinity.js +314 -0
  110. x_ipe/static/js/features/kb-core.js +371 -0
  111. x_ipe/static/js/features/quality-evaluation.js +387 -0
  112. x_ipe/static/js/features/sidebar.js +255 -12
  113. x_ipe/static/js/features/tracing-dashboard.js +855 -0
  114. x_ipe/static/js/features/tracing-graph.js +1031 -0
  115. x_ipe/static/js/features/tree-drag.js +227 -0
  116. x_ipe/static/js/features/tree-search.js +228 -0
  117. x_ipe/static/js/features/workplace.js +661 -33
  118. x_ipe/static/js/init.js +76 -0
  119. x_ipe/static/js/terminal-v2.js +45 -14
  120. x_ipe/static/js/terminal.js +50 -49
  121. x_ipe/static/js/uiux-feedback.js +75 -16
  122. x_ipe/templates/base.html +24 -0
  123. x_ipe/templates/index.html +10 -1
  124. x_ipe/templates/knowledge-base.html +110 -0
  125. x_ipe/templates/workplace.html +4 -0
  126. x_ipe/tracing/__init__.py +37 -0
  127. x_ipe/tracing/buffer.py +135 -0
  128. x_ipe/tracing/context.py +125 -0
  129. x_ipe/tracing/decorator.py +288 -0
  130. x_ipe/tracing/middleware.py +197 -0
  131. x_ipe/tracing/parser.py +235 -0
  132. x_ipe/tracing/redactor.py +111 -0
  133. x_ipe/tracing/writer.py +122 -0
  134. {x_ipe-1.0.23.dist-info → x_ipe-1.0.25.dist-info}/METADATA +2 -2
  135. {x_ipe-1.0.23.dist-info → x_ipe-1.0.25.dist-info}/RECORD +138 -65
  136. x_ipe/app.py.bak +0 -1333
  137. x_ipe/resources/skills/x-ipe-skill-creator/SKILL.md +0 -329
  138. x_ipe/resources/skills/x-ipe-skill-creator/references/output-patterns.md +0 -169
  139. x_ipe/resources/skills/x-ipe-skill-creator/references/skill-structure.md +0 -162
  140. x_ipe/resources/skills/x-ipe-skill-creator/references/workflows.md +0 -110
  141. x_ipe/resources/skills/x-ipe-skill-creator/templates/references/examples.md +0 -113
  142. x_ipe/resources/skills/x-ipe-skill-creator/templates/skill-category-skill.md +0 -296
  143. x_ipe/resources/skills/x-ipe-skill-creator/templates/task-type-skill.md +0 -269
  144. {x_ipe-1.0.23.dist-info → x_ipe-1.0.25.dist-info}/WHEEL +0 -0
  145. {x_ipe-1.0.23.dist-info → x_ipe-1.0.25.dist-info}/entry_points.txt +0 -0
  146. {x_ipe-1.0.23.dist-info → x_ipe-1.0.25.dist-info}/licenses/LICENSE +0 -0
@@ -4,10 +4,12 @@
4
4
  *
5
5
  * Two-column view for managing ideas with upload, tree navigation,
6
6
  * inline editing with auto-save, and folder rename.
7
+ *
8
+ * CR-006: Added folder view panel, drag-drop, and search functionality.
7
9
  */
8
10
  class WorkplaceManager {
9
11
  constructor() {
10
- this.currentView = 'tree'; // tree | upload | editor
12
+ this.currentView = 'tree'; // tree | upload | editor | folderView | tracing | homepage
11
13
  this.currentPath = null;
12
14
  this.saveTimer = null;
13
15
  this.saveDelay = 5000; // 5 seconds auto-save delay
@@ -24,6 +26,19 @@ class WorkplaceManager {
24
26
  this.easyMDE = null; // EasyMDE editor instance for compose view
25
27
  this.copilotPrompts = []; // Loaded from x-ipe-docs/config/copilot-prompt.json
26
28
  this.targetFolderPath = null; // Target folder for compose/upload (null = create new folder)
29
+
30
+ // CR-006: New managers
31
+ this.folderViewManager = null;
32
+ this.treeSearchManager = null;
33
+ this.treeDragManager = null;
34
+ this.confirmDialog = null;
35
+
36
+ // FEATURE-023-B: Tracing Dashboard
37
+ this.tracingDashboard = null;
38
+
39
+ // FEATURE-026: Homepage reference
40
+ this.sidebarRef = null;
41
+
27
42
  this._loadCopilotPrompts();
28
43
  }
29
44
 
@@ -35,7 +50,8 @@ class WorkplaceManager {
35
50
  const response = await fetch('/api/config/copilot-prompt');
36
51
  if (response.ok) {
37
52
  const data = await response.json();
38
- this.copilotPrompts = data.prompts || [];
53
+ // TASK-196: Config structure is data.ideation.prompts, not data.prompts
54
+ this.copilotPrompts = data.ideation?.prompts || [];
39
55
  }
40
56
  } catch (error) {
41
57
  console.warn('Failed to load copilot prompts:', error);
@@ -52,16 +68,27 @@ class WorkplaceManager {
52
68
  <div class="workplace-container">
53
69
  <div class="workplace-sidebar pinned" id="workplace-sidebar">
54
70
  <div class="workplace-sidebar-icons">
55
- <button class="workplace-sidebar-icon" title="Browse Ideas" id="workplace-icon-browse">
71
+ <button class="workplace-sidebar-icon active" title="Browse Ideas" id="workplace-icon-browse">
56
72
  <i class="bi bi-folder2"></i>
57
73
  </button>
74
+ <button class="workplace-sidebar-icon" title="Tracing Dashboard" id="workplace-icon-tracing">
75
+ <i class="bi bi-graph-up"></i>
76
+ </button>
58
77
  </div>
59
78
  <div class="workplace-sidebar-content">
60
79
  <div class="workplace-sidebar-header">
61
80
  <span class="workplace-sidebar-title">Ideas</span>
62
- <button class="workplace-pin-btn" title="Unpin sidebar" id="workplace-pin-btn">
63
- <i class="bi bi-pin-angle-fill"></i>
64
- </button>
81
+ <div class="workplace-sidebar-header-actions">
82
+ <button class="workplace-create-folder-btn" title="Create new folder" id="workplace-create-folder-btn">
83
+ <i class="bi bi-folder-plus"></i>
84
+ </button>
85
+ <button class="workplace-collapse-all-btn" title="Collapse all folders" id="workplace-collapse-all-btn">
86
+ <i class="bi bi-arrows-collapse"></i>
87
+ </button>
88
+ <button class="workplace-pin-btn" title="Unpin sidebar" id="workplace-pin-btn">
89
+ <i class="bi bi-pin-angle-fill"></i>
90
+ </button>
91
+ </div>
65
92
  </div>
66
93
  <div class="workplace-tree" id="workplace-tree">
67
94
  <div class="loading-spinner">
@@ -84,9 +111,12 @@ class WorkplaceManager {
84
111
 
85
112
  // Bind sidebar icon events
86
113
  document.getElementById('workplace-icon-browse').addEventListener('click', () => {
87
- // Toggle expanded state on mobile/touch
88
- const sidebar = document.getElementById('workplace-sidebar');
89
- sidebar.classList.toggle('expanded');
114
+ this._switchToIdeasView();
115
+ });
116
+
117
+ // FEATURE-023-B: Tracing icon event
118
+ document.getElementById('workplace-icon-tracing').addEventListener('click', () => {
119
+ this._switchToTracingView();
90
120
  });
91
121
 
92
122
  // Bind pin button
@@ -108,9 +138,220 @@ class WorkplaceManager {
108
138
  }
109
139
  });
110
140
 
141
+ // Bind create folder button
142
+ document.getElementById('workplace-create-folder-btn').addEventListener('click', () => {
143
+ this._createFolder(); // Create new folder at root level
144
+ });
145
+
146
+ // Bind collapse all button
147
+ document.getElementById('workplace-collapse-all-btn').addEventListener('click', () => {
148
+ this._collapseAllFolders();
149
+ });
150
+
111
151
  // Load tree and start polling
112
152
  await this.loadTree();
113
153
  this._startPolling();
154
+
155
+ // CR-006: Initialize new managers
156
+ this._initCR006Managers();
157
+ }
158
+
159
+ /**
160
+ * CR-006: Initialize folder view, search, and drag-drop managers
161
+ */
162
+ _initCR006Managers() {
163
+ const treeContainer = document.getElementById('workplace-tree');
164
+ const contentContainer = document.getElementById('workplace-content');
165
+
166
+ if (!treeContainer || !contentContainer) return;
167
+
168
+ // Initialize confirm dialog
169
+ if (typeof ConfirmDialog !== 'undefined') {
170
+ this.confirmDialog = new ConfirmDialog();
171
+ }
172
+
173
+ // Initialize folder view manager
174
+ if (typeof FolderViewManager !== 'undefined') {
175
+ this.folderViewManager = new FolderViewManager({
176
+ container: contentContainer,
177
+ onAction: async (action, path, data) => {
178
+ return await this._handleFolderViewAction(action, path, data);
179
+ },
180
+ onNavigate: (path) => {
181
+ this.openFile(path);
182
+ },
183
+ onClose: () => {
184
+ this._closeFolderView();
185
+ }
186
+ });
187
+ this.folderViewManager.init();
188
+ }
189
+
190
+ // Initialize search manager
191
+ if (typeof TreeSearchManager !== 'undefined') {
192
+ this.treeSearchManager = new TreeSearchManager({
193
+ treeContainer: treeContainer
194
+ });
195
+ this.treeSearchManager.init();
196
+ }
197
+
198
+ // Initialize drag manager
199
+ if (typeof TreeDragManager !== 'undefined') {
200
+ this.treeDragManager = new TreeDragManager({
201
+ treeContainer: treeContainer,
202
+ onMove: async (sourcePath, targetPath) => {
203
+ return await this._handleMoveItem(sourcePath, targetPath);
204
+ },
205
+ onMoveComplete: () => {
206
+ this.loadTree();
207
+ }
208
+ });
209
+ this.treeDragManager.init();
210
+ }
211
+ }
212
+
213
+ /**
214
+ * CR-006: Handle folder view actions
215
+ */
216
+ async _handleFolderViewAction(action, path, data) {
217
+ try {
218
+ switch (action) {
219
+ case 'add-file':
220
+ this.showUploadView(path);
221
+ return true;
222
+
223
+ case 'add-folder':
224
+ // Prompt for folder name and create via API
225
+ const folderName = prompt('Enter folder name:');
226
+ if (folderName && folderName.trim()) {
227
+ const response = await fetch('/api/ideas/create-folder', {
228
+ method: 'POST',
229
+ headers: {
230
+ 'Content-Type': 'application/json'
231
+ },
232
+ body: JSON.stringify({
233
+ folder_name: folderName.trim(),
234
+ parent_folder: path
235
+ })
236
+ });
237
+ const result = await response.json();
238
+ if (result.success) {
239
+ this._showToast(`Folder "${result.folder_name}" created`, 'success');
240
+ await this.loadTree();
241
+ } else {
242
+ this._showToast(result.error || 'Failed to create folder', 'error');
243
+ }
244
+ return result.success;
245
+ }
246
+ return false;
247
+
248
+ case 'rename':
249
+ const newName = prompt(`Rename "${data.name || path.split('/').pop()}" to:`, data.name || path.split('/').pop());
250
+ if (newName && newName !== data.name) {
251
+ if (data.type === 'folder') {
252
+ const result = await this._renameFolder(path, newName);
253
+ return result.success;
254
+ } else {
255
+ const result = await this._renameFile(path, newName);
256
+ return result.success;
257
+ }
258
+ }
259
+ return false;
260
+
261
+ case 'delete':
262
+ const deleteResult = await this._deleteItem(path);
263
+ if (deleteResult.success) {
264
+ this.loadTree();
265
+ }
266
+ return deleteResult.success;
267
+
268
+ case 'duplicate':
269
+ const dupResponse = await fetch('/api/ideas/duplicate', {
270
+ method: 'POST',
271
+ headers: { 'Content-Type': 'application/json' },
272
+ body: JSON.stringify({ path: path })
273
+ });
274
+ const dupResult = await dupResponse.json();
275
+ if (dupResult.success) {
276
+ this._showToast('Item duplicated', 'success');
277
+ this.loadTree();
278
+ }
279
+ return dupResult.success;
280
+
281
+ // TASK-241: Handle move action from folder view drag-drop
282
+ case 'move':
283
+ const moveResult = await this._handleMoveItem(path, data.targetPath);
284
+ if (moveResult) {
285
+ this.loadTree();
286
+ }
287
+ return moveResult;
288
+
289
+ default:
290
+ console.warn('Unknown folder view action:', action);
291
+ return false;
292
+ }
293
+ } catch (error) {
294
+ console.error('Folder view action failed:', error);
295
+ this._showToast('Action failed', 'error');
296
+ return false;
297
+ }
298
+ }
299
+
300
+ /**
301
+ * CR-006: Handle move item via drag-drop
302
+ */
303
+ async _handleMoveItem(sourcePath, targetPath) {
304
+ try {
305
+ const response = await fetch('/api/ideas/move', {
306
+ method: 'POST',
307
+ headers: { 'Content-Type': 'application/json' },
308
+ body: JSON.stringify({
309
+ source_path: sourcePath,
310
+ target_folder: targetPath
311
+ })
312
+ });
313
+
314
+ const result = await response.json();
315
+
316
+ if (result.success) {
317
+ this._showToast('Item moved', 'success');
318
+ return true;
319
+ } else {
320
+ this._showToast(result.error || 'Move failed', 'error');
321
+ return false;
322
+ }
323
+ } catch (error) {
324
+ console.error('Move failed:', error);
325
+ this._showToast('Move failed', 'error');
326
+ return false;
327
+ }
328
+ }
329
+
330
+ /**
331
+ * CR-006: Open folder view for a folder
332
+ */
333
+ openFolderView(folderPath) {
334
+ if (this.folderViewManager) {
335
+ this.currentView = 'folderView';
336
+ this.folderViewManager.render(folderPath);
337
+ }
338
+ }
339
+
340
+ /**
341
+ * CR-006: Close folder view and return to placeholder
342
+ */
343
+ _closeFolderView() {
344
+ this.currentView = 'tree';
345
+ const contentContainer = document.getElementById('workplace-content');
346
+ if (contentContainer) {
347
+ contentContainer.innerHTML = `
348
+ <div class="workplace-placeholder">
349
+ <i class="bi bi-lightbulb"></i>
350
+ <h5>Welcome to Workplace</h5>
351
+ <p class="text-muted">Click a file to preview, or click the folder arrow to browse contents</p>
352
+ </div>
353
+ `;
354
+ }
114
355
  }
115
356
 
116
357
  /**
@@ -216,6 +457,18 @@ class WorkplaceManager {
216
457
  return expanded;
217
458
  }
218
459
 
460
+ /**
461
+ * Collapse all expanded folders in the tree
462
+ */
463
+ _collapseAllFolders() {
464
+ const container = document.getElementById('workplace-tree');
465
+ if (!container) return;
466
+ const expandedItems = container.querySelectorAll('.workplace-tree-item.expanded');
467
+ expandedItems.forEach(item => {
468
+ item.classList.remove('expanded');
469
+ });
470
+ }
471
+
219
472
  /**
220
473
  * Restore expanded state to folders after re-rendering
221
474
  */
@@ -257,11 +510,15 @@ class WorkplaceManager {
257
510
 
258
511
  for (const node of nodes) {
259
512
  const li = document.createElement('li');
260
- li.className = 'workplace-tree-item';
513
+ li.className = 'workplace-tree-item tree-item'; // CR-006: Added tree-item for drag-drop
261
514
  li.dataset.path = node.path;
262
515
  li.dataset.type = node.type;
263
516
  li.dataset.name = node.name;
264
517
 
518
+ // CR-006: Enable drag-drop
519
+ li.setAttribute('draggable', 'true');
520
+ li.dataset.draggable = 'true';
521
+
265
522
  const itemContent = document.createElement('div');
266
523
  itemContent.className = 'workplace-tree-item-content';
267
524
  // CR-002: Add idea-folder-node class and data for drag-drop targets
@@ -275,8 +532,9 @@ class WorkplaceManager {
275
532
  const icon = document.createElement('i');
276
533
  icon.className = node.type === 'folder' ? 'bi bi-folder' : 'bi bi-file-earmark';
277
534
 
535
+ // CR-006: Add tree-label class for search matching
278
536
  const nameSpan = document.createElement('span');
279
- nameSpan.className = 'workplace-tree-name';
537
+ nameSpan.className = 'workplace-tree-name tree-label';
280
538
  nameSpan.textContent = node.name;
281
539
 
282
540
  // Action buttons container
@@ -333,16 +591,31 @@ class WorkplaceManager {
333
591
  actionBtns.appendChild(renameBtn);
334
592
  }
335
593
 
336
- // Delete button
337
- const deleteBtn = document.createElement('button');
338
- deleteBtn.className = 'workplace-tree-action-btn workplace-tree-delete-btn';
339
- deleteBtn.innerHTML = '<i class="bi bi-trash"></i>';
340
- deleteBtn.title = `Delete ${node.type}`;
341
- deleteBtn.addEventListener('click', (e) => {
342
- e.stopPropagation();
343
- this.confirmDelete(node.path, node.name, node.type);
344
- });
345
- actionBtns.appendChild(deleteBtn);
594
+ // Delete button (only for files - folders can be deleted in folder view)
595
+ if (node.type === 'file') {
596
+ const deleteBtn = document.createElement('button');
597
+ deleteBtn.className = 'workplace-tree-action-btn workplace-tree-delete-btn';
598
+ deleteBtn.innerHTML = '<i class="bi bi-trash"></i>';
599
+ deleteBtn.title = `Delete ${node.type}`;
600
+ deleteBtn.addEventListener('click', (e) => {
601
+ e.stopPropagation();
602
+ this.confirmDelete(node.path, node.name, node.type);
603
+ });
604
+ actionBtns.appendChild(deleteBtn);
605
+ }
606
+
607
+ // CR-006: Folder view button (">") for folders - positioned at right
608
+ if (node.type === 'folder') {
609
+ const folderViewBtn = document.createElement('button');
610
+ folderViewBtn.className = 'workplace-tree-action-btn tree-folder-view-btn';
611
+ folderViewBtn.innerHTML = '<i class="bi bi-box-arrow-in-right"></i>';
612
+ folderViewBtn.title = 'Enter folder view';
613
+ folderViewBtn.addEventListener('click', (e) => {
614
+ e.stopPropagation();
615
+ this.openFolderView(node.path);
616
+ });
617
+ actionBtns.appendChild(folderViewBtn);
618
+ }
346
619
 
347
620
  itemContent.appendChild(icon);
348
621
  itemContent.appendChild(nameSpan);
@@ -393,11 +666,15 @@ class WorkplaceManager {
393
666
 
394
667
  for (const node of nodes) {
395
668
  const li = document.createElement('li');
396
- li.className = 'workplace-tree-item';
669
+ li.className = 'workplace-tree-item tree-item'; // CR-006: Added tree-item for drag-drop
397
670
  li.dataset.path = node.path;
398
671
  li.dataset.type = node.type;
399
672
  li.dataset.name = node.name;
400
673
 
674
+ // CR-006: Enable drag-drop
675
+ li.setAttribute('draggable', 'true');
676
+ li.dataset.draggable = 'true';
677
+
401
678
  const itemContent = document.createElement('div');
402
679
  itemContent.className = 'workplace-tree-item-content';
403
680
  // CR-002: Add idea-folder-node class and data for drag-drop targets
@@ -411,8 +688,9 @@ class WorkplaceManager {
411
688
  const icon = document.createElement('i');
412
689
  icon.className = node.type === 'folder' ? 'bi bi-folder' : 'bi bi-file-earmark';
413
690
 
691
+ // CR-006: Add tree-label class for search matching
414
692
  const nameSpan = document.createElement('span');
415
- nameSpan.className = 'workplace-tree-name';
693
+ nameSpan.className = 'workplace-tree-name tree-label';
416
694
  nameSpan.textContent = node.name;
417
695
 
418
696
  // Action buttons container
@@ -443,16 +721,31 @@ class WorkplaceManager {
443
721
  actionBtns.appendChild(renameBtn);
444
722
  }
445
723
 
446
- // Delete button
447
- const deleteBtn = document.createElement('button');
448
- deleteBtn.className = 'workplace-tree-action-btn workplace-tree-delete-btn';
449
- deleteBtn.innerHTML = '<i class="bi bi-trash"></i>';
450
- deleteBtn.title = `Delete ${node.type}`;
451
- deleteBtn.addEventListener('click', (e) => {
452
- e.stopPropagation();
453
- this.confirmDelete(node.path, node.name, node.type);
454
- });
455
- actionBtns.appendChild(deleteBtn);
724
+ // Delete button (only for files - folders can be deleted in folder view)
725
+ if (node.type === 'file') {
726
+ const deleteBtn = document.createElement('button');
727
+ deleteBtn.className = 'workplace-tree-action-btn workplace-tree-delete-btn';
728
+ deleteBtn.innerHTML = '<i class="bi bi-trash"></i>';
729
+ deleteBtn.title = `Delete ${node.type}`;
730
+ deleteBtn.addEventListener('click', (e) => {
731
+ e.stopPropagation();
732
+ this.confirmDelete(node.path, node.name, node.type);
733
+ });
734
+ actionBtns.appendChild(deleteBtn);
735
+ }
736
+
737
+ // CR-006: Folder view button for nested folders - positioned at right
738
+ if (node.type === 'folder') {
739
+ const folderViewBtn = document.createElement('button');
740
+ folderViewBtn.className = 'workplace-tree-action-btn tree-folder-view-btn';
741
+ folderViewBtn.innerHTML = '<i class="bi bi-box-arrow-in-right"></i>';
742
+ folderViewBtn.title = 'Enter folder view';
743
+ folderViewBtn.addEventListener('click', (e) => {
744
+ e.stopPropagation();
745
+ this.openFolderView(node.path);
746
+ });
747
+ actionBtns.appendChild(folderViewBtn);
748
+ }
456
749
 
457
750
  itemContent.appendChild(icon);
458
751
  itemContent.appendChild(nameSpan);
@@ -493,6 +786,9 @@ class WorkplaceManager {
493
786
  const nameSpan = li.querySelector('.workplace-tree-name');
494
787
  const originalName = currentName;
495
788
 
789
+ // Disable drag during editing to allow text selection
790
+ li.setAttribute('draggable', 'false');
791
+
496
792
  const input = document.createElement('input');
497
793
  input.type = 'text';
498
794
  input.className = 'workplace-rename-input';
@@ -506,8 +802,12 @@ class WorkplaceManager {
506
802
  if (!this.renamingFolder) return;
507
803
 
508
804
  const newName = input.value.trim();
805
+ const liElement = this.renamingFolder;
509
806
  this.renamingFolder = null;
510
807
 
808
+ // Re-enable drag after editing
809
+ liElement.setAttribute('draggable', 'true');
810
+
511
811
  if (save && newName && newName !== originalName) {
512
812
  try {
513
813
  const response = await fetch('/api/ideas/rename', {
@@ -584,6 +884,9 @@ class WorkplaceManager {
584
884
  const nameSpan = li.querySelector('.workplace-tree-name');
585
885
  const originalName = currentName;
586
886
 
887
+ // Disable drag during editing to allow text selection
888
+ li.setAttribute('draggable', 'false');
889
+
587
890
  const input = document.createElement('input');
588
891
  input.type = 'text';
589
892
  input.className = 'workplace-rename-input';
@@ -597,8 +900,12 @@ class WorkplaceManager {
597
900
  if (!this.renamingFolder) return;
598
901
 
599
902
  const newName = input.value.trim();
903
+ const liElement = this.renamingFolder;
600
904
  this.renamingFolder = null;
601
905
 
906
+ // Re-enable drag after editing
907
+ liElement.setAttribute('draggable', 'true');
908
+
602
909
  if (save && newName && newName !== originalName) {
603
910
  try {
604
911
  const response = await fetch('/api/ideas/rename-file', {
@@ -681,6 +988,9 @@ class WorkplaceManager {
681
988
  this.hasUnsavedChanges = false;
682
989
  this.isEditing = false;
683
990
 
991
+ // Highlight parent folder in tree
992
+ this._highlightParentFolder(path);
993
+
684
994
  // Detect file type
685
995
  const ext = path.split('.').pop().toLowerCase();
686
996
  this.fileExtension = ext;
@@ -1494,6 +1804,9 @@ class WorkplaceManager {
1494
1804
  this.hasUnsavedChanges = false;
1495
1805
  this.targetFolderPath = targetFolder;
1496
1806
 
1807
+ // Clear folder highlight when leaving file view
1808
+ this._highlightParentFolder(null);
1809
+
1497
1810
  // Build target folder indicator
1498
1811
  const folderName = targetFolder ? targetFolder.split('/').pop() : null;
1499
1812
  const targetIndicator = targetFolder ? `
@@ -2136,6 +2449,166 @@ class WorkplaceManager {
2136
2449
  }
2137
2450
  }
2138
2451
 
2452
+ /**
2453
+ * CR-006 Fix: Delete item wrapper for folder view (calls deleteItem API)
2454
+ * @param {string} path - Path to delete
2455
+ * @returns {Object} Result with success boolean
2456
+ */
2457
+ async _deleteItem(path) {
2458
+ try {
2459
+ const response = await fetch('/api/ideas/delete', {
2460
+ method: 'POST',
2461
+ headers: { 'Content-Type': 'application/json' },
2462
+ body: JSON.stringify({ path: path })
2463
+ });
2464
+
2465
+ const result = await response.json();
2466
+
2467
+ if (result.success) {
2468
+ const name = path.split('/').pop();
2469
+ this._showToast(`Deleted: ${name}`, 'success');
2470
+
2471
+ // If currently viewing the deleted file, clear the editor
2472
+ if (this.currentPath && (this.currentPath === path || this.currentPath.startsWith(path + '/'))) {
2473
+ this.currentPath = null;
2474
+ this.hasUnsavedChanges = false;
2475
+ }
2476
+ } else {
2477
+ this._showToast(result.error || 'Failed to delete', 'error');
2478
+ }
2479
+
2480
+ return result;
2481
+ } catch (error) {
2482
+ console.error('Failed to delete:', error);
2483
+ this._showToast('Failed to delete', 'error');
2484
+ return { success: false, error: error.message };
2485
+ }
2486
+ }
2487
+
2488
+ /**
2489
+ * CR-006 Fix: Rename file via API
2490
+ * @param {string} path - File path
2491
+ * @param {string} newName - New file name
2492
+ * @returns {Object} Result with success boolean
2493
+ */
2494
+ async _renameFile(path, newName) {
2495
+ try {
2496
+ const response = await fetch('/api/ideas/rename-file', {
2497
+ method: 'POST',
2498
+ headers: { 'Content-Type': 'application/json' },
2499
+ body: JSON.stringify({
2500
+ path: path,
2501
+ new_name: newName
2502
+ })
2503
+ });
2504
+
2505
+ const result = await response.json();
2506
+
2507
+ if (result.success) {
2508
+ this._showToast('File renamed successfully', 'success');
2509
+
2510
+ // Update currentPath if viewing this file
2511
+ if (this.currentPath === path) {
2512
+ this.currentPath = result.new_path;
2513
+ }
2514
+
2515
+ await this.loadTree();
2516
+ } else {
2517
+ this._showToast(result.error || 'Failed to rename file', 'error');
2518
+ }
2519
+
2520
+ return result;
2521
+ } catch (error) {
2522
+ console.error('Failed to rename file:', error);
2523
+ this._showToast('Failed to rename file', 'error');
2524
+ return { success: false, error: error.message };
2525
+ }
2526
+ }
2527
+
2528
+ /**
2529
+ * CR-006 Fix: Rename folder via API
2530
+ * @param {string} path - Folder path
2531
+ * @param {string} newName - New folder name
2532
+ * @returns {Object} Result with success boolean
2533
+ */
2534
+ async _renameFolder(path, newName) {
2535
+ try {
2536
+ // Extract the old folder name from path
2537
+ const parts = path.split('/');
2538
+ const oldName = parts.pop();
2539
+
2540
+ const response = await fetch('/api/ideas/rename', {
2541
+ method: 'POST',
2542
+ headers: { 'Content-Type': 'application/json' },
2543
+ body: JSON.stringify({
2544
+ old_name: oldName,
2545
+ new_name: newName
2546
+ })
2547
+ });
2548
+
2549
+ const result = await response.json();
2550
+
2551
+ if (result.success) {
2552
+ this._showToast('Folder renamed successfully', 'success');
2553
+
2554
+ // Update currentPath if viewing a file inside the renamed folder
2555
+ if (this.currentPath) {
2556
+ const oldSegment = '/' + oldName + '/';
2557
+ const newSegment = '/' + newName + '/';
2558
+ if (this.currentPath.includes(oldSegment)) {
2559
+ this.currentPath = this.currentPath.replace(oldSegment, newSegment);
2560
+ }
2561
+ }
2562
+
2563
+ await this.loadTree();
2564
+ } else {
2565
+ this._showToast(result.error || 'Failed to rename folder', 'error');
2566
+ }
2567
+
2568
+ return result;
2569
+ } catch (error) {
2570
+ console.error('Failed to rename folder:', error);
2571
+ this._showToast('Failed to rename folder', 'error');
2572
+ return { success: false, error: error.message };
2573
+ }
2574
+ }
2575
+
2576
+ /**
2577
+ * Create a new empty folder at the root level or inside a parent folder
2578
+ * @param {string|null} parentFolder - Optional parent folder path
2579
+ */
2580
+ async _createFolder(parentFolder = null) {
2581
+ const folderName = prompt('Enter folder name:');
2582
+ if (!folderName || !folderName.trim()) {
2583
+ return;
2584
+ }
2585
+
2586
+ try {
2587
+ const response = await fetch('/api/ideas/create-folder', {
2588
+ method: 'POST',
2589
+ headers: {
2590
+ 'Content-Type': 'application/json'
2591
+ },
2592
+ body: JSON.stringify({
2593
+ folder_name: folderName.trim(),
2594
+ parent_folder: parentFolder
2595
+ })
2596
+ });
2597
+
2598
+ const result = await response.json();
2599
+
2600
+ if (result.success) {
2601
+ this._showToast(`Folder "${result.folder_name}" created`, 'success');
2602
+ await this.loadTree();
2603
+ } else {
2604
+ this._showToast(result.error || 'Failed to create folder', 'error');
2605
+ }
2606
+ } catch (error) {
2607
+ console.error('Failed to create folder:', error);
2608
+ this._showToast('Failed to create folder', 'error');
2609
+ }
2610
+ }
2611
+
2139
2612
  /**
2140
2613
  * Download a file from the idea folder
2141
2614
  */
@@ -2186,6 +2659,53 @@ class WorkplaceManager {
2186
2659
  }
2187
2660
  }
2188
2661
 
2662
+ /**
2663
+ * Highlight the parent folder of the currently previewed file
2664
+ * Changes folder icon to yellow to indicate active file location
2665
+ */
2666
+ _highlightParentFolder(filePath) {
2667
+ // Remove previous highlight
2668
+ const previousHighlight = document.querySelectorAll('.workplace-tree-item.folder-highlighted');
2669
+ previousHighlight.forEach(el => {
2670
+ el.classList.remove('folder-highlighted');
2671
+ const icon = el.querySelector('.workplace-tree-item-content > i.bi-folder, .workplace-tree-item-content > i.bi-folder-fill');
2672
+ if (icon) {
2673
+ icon.classList.remove('bi-folder-fill');
2674
+ icon.classList.add('bi-folder');
2675
+ }
2676
+ });
2677
+
2678
+ if (!filePath) return;
2679
+
2680
+ // Get parent folder path
2681
+ const pathParts = filePath.split('/');
2682
+ pathParts.pop(); // Remove file name
2683
+ const parentFolderPath = pathParts.join('/');
2684
+
2685
+ if (!parentFolderPath) return;
2686
+
2687
+ // Find and highlight the parent folder
2688
+ const treeItems = document.querySelectorAll('.workplace-tree-item[data-type="folder"]');
2689
+ for (const item of treeItems) {
2690
+ if (item.dataset.path === parentFolderPath) {
2691
+ item.classList.add('folder-highlighted');
2692
+ const icon = item.querySelector('.workplace-tree-item-content > i.bi-folder');
2693
+ if (icon) {
2694
+ icon.classList.remove('bi-folder');
2695
+ icon.classList.add('bi-folder-fill');
2696
+ }
2697
+
2698
+ // Expand parent folders to make highlighted folder visible
2699
+ let parent = item.parentElement?.closest('.workplace-tree-item.has-children');
2700
+ while (parent) {
2701
+ parent.classList.add('expanded');
2702
+ parent = parent.parentElement?.closest('.workplace-tree-item.has-children');
2703
+ }
2704
+ break;
2705
+ }
2706
+ }
2707
+ }
2708
+
2189
2709
  /**
2190
2710
  * Escape HTML special characters
2191
2711
  */
@@ -2194,4 +2714,112 @@ class WorkplaceManager {
2194
2714
  div.textContent = text || '';
2195
2715
  return div.innerHTML;
2196
2716
  }
2717
+
2718
+ // =========================================================================
2719
+ // FEATURE-023-B: Tracing Dashboard View
2720
+ // =========================================================================
2721
+
2722
+ /**
2723
+ * Switch to Ideas view (tree navigation)
2724
+ */
2725
+ _switchToIdeasView() {
2726
+ // Update active icon
2727
+ document.querySelectorAll('.workplace-sidebar-icon').forEach(icon => {
2728
+ icon.classList.remove('active');
2729
+ });
2730
+ document.getElementById('workplace-icon-browse').classList.add('active');
2731
+
2732
+ // Show sidebar content, hide tracing
2733
+ const sidebarContent = document.querySelector('.workplace-sidebar-content');
2734
+ if (sidebarContent) {
2735
+ sidebarContent.style.display = '';
2736
+ }
2737
+
2738
+ // Destroy tracing dashboard if active
2739
+ if (this.tracingDashboard) {
2740
+ this.tracingDashboard.destroy();
2741
+ this.tracingDashboard = null;
2742
+ }
2743
+
2744
+ // Restore content to placeholder or current view
2745
+ const contentContainer = document.getElementById('workplace-content');
2746
+ if (contentContainer && this.currentView === 'tracing') {
2747
+ contentContainer.innerHTML = `
2748
+ <div class="workplace-placeholder">
2749
+ <i class="bi bi-lightbulb"></i>
2750
+ <h5>Welcome to Workplace</h5>
2751
+ <p class="text-muted">Hover sidebar to browse ideas, or click pin to keep it open</p>
2752
+ </div>
2753
+ `;
2754
+ this.currentView = 'tree';
2755
+ }
2756
+ }
2757
+
2758
+ /**
2759
+ * Switch to Tracing Dashboard view
2760
+ */
2761
+ _switchToTracingView() {
2762
+ // Update active icon
2763
+ document.querySelectorAll('.workplace-sidebar-icon').forEach(icon => {
2764
+ icon.classList.remove('active');
2765
+ });
2766
+ document.getElementById('workplace-icon-tracing').classList.add('active');
2767
+
2768
+ // Hide sidebar content (tree)
2769
+ const sidebarContent = document.querySelector('.workplace-sidebar-content');
2770
+ if (sidebarContent) {
2771
+ sidebarContent.style.display = 'none';
2772
+ }
2773
+
2774
+ // Collapse sidebar to icons only
2775
+ const sidebar = document.getElementById('workplace-sidebar');
2776
+ if (sidebar) {
2777
+ sidebar.classList.remove('pinned', 'expanded');
2778
+ }
2779
+
2780
+ // Initialize tracing dashboard
2781
+ this.currentView = 'tracing';
2782
+ const contentContainer = document.getElementById('workplace-content');
2783
+ if (contentContainer && window.TracingDashboard) {
2784
+ this.tracingDashboard = new window.TracingDashboard(contentContainer);
2785
+ this.tracingDashboard.init();
2786
+ } else if (contentContainer) {
2787
+ contentContainer.innerHTML = `
2788
+ <div class="workplace-placeholder">
2789
+ <i class="bi bi-graph-up"></i>
2790
+ <h5>Tracing Dashboard</h5>
2791
+ <p class="text-muted">Tracing module not loaded</p>
2792
+ </div>
2793
+ `;
2794
+ }
2795
+ }
2796
+
2797
+ /**
2798
+ * FEATURE-026: Show Homepage Infinity Loop
2799
+ * Called when X-IPE logo is clicked or no file selected
2800
+ */
2801
+ showHomepage() {
2802
+ this.currentView = 'homepage';
2803
+ this.currentPath = null;
2804
+
2805
+ // Try workplace-content first (workplace two-column view), then content-body (main app)
2806
+ const contentContainer = document.getElementById('workplace-content') ||
2807
+ document.getElementById('content-body');
2808
+ if (!contentContainer) return;
2809
+
2810
+ // Check if HomepageInfinity is available
2811
+ if (typeof window.HomepageInfinity !== 'undefined') {
2812
+ contentContainer.innerHTML = window.HomepageInfinity.getTemplate();
2813
+ window.HomepageInfinity.init(this.sidebarRef);
2814
+ } else {
2815
+ // Fallback placeholder
2816
+ contentContainer.innerHTML = `
2817
+ <div class="workplace-placeholder">
2818
+ <i class="bi bi-infinity"></i>
2819
+ <h5>X-IPE</h5>
2820
+ <p class="text-muted">AI-Powered Development Lifecycle</p>
2821
+ </div>
2822
+ `;
2823
+ }
2824
+ }
2197
2825
  }