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.
- x_ipe/app.py +32 -1
- x_ipe/handlers/terminal_handlers.py +6 -0
- x_ipe/handlers/voice_handlers.py +5 -0
- x_ipe/resources/copilot-instructions.md +19 -6
- x_ipe/resources/skills/lesson-learned/SKILL.md +208 -0
- x_ipe/resources/skills/lesson-learned/references/examples.md +238 -0
- x_ipe/resources/skills/project-quality-board-management/SKILL.md +135 -298
- x_ipe/resources/skills/project-quality-board-management/references/evaluation-principles.md +213 -0
- x_ipe/resources/skills/project-quality-board-management/references/evaluation-procedures.md +214 -0
- x_ipe/resources/skills/project-quality-board-management/templates/quality-report.md +70 -18
- x_ipe/resources/skills/task-execution-guideline/SKILL.md +2 -2
- x_ipe/resources/skills/task-execution-guideline/templates/task-record.yaml +1 -1
- x_ipe/resources/skills/task-type-code-implementation/SKILL.md +72 -270
- x_ipe/resources/skills/task-type-code-implementation/references/implementation-guidelines.md +432 -0
- x_ipe/resources/skills/task-type-code-refactor-v2/SKILL.md +127 -353
- x_ipe/resources/skills/task-type-code-refactor-v2/references/refactoring-techniques.md +373 -0
- x_ipe/resources/skills/task-type-feature-breakdown/SKILL.md +31 -243
- x_ipe/resources/skills/task-type-feature-breakdown/references/breakdown-guidelines.md +330 -0
- x_ipe/resources/skills/task-type-feature-refinement/SKILL.md +27 -180
- x_ipe/resources/skills/task-type-feature-refinement/references/specification-writing-guide.md +267 -0
- x_ipe/resources/skills/task-type-idea-mockup/SKILL.md +38 -276
- x_ipe/resources/skills/task-type-idea-mockup/references/mockup-guidelines.md +299 -0
- x_ipe/resources/skills/task-type-idea-to-architecture/SKILL.md +20 -218
- x_ipe/resources/skills/task-type-idea-to-architecture/references/architecture-patterns.md +342 -0
- x_ipe/resources/skills/task-type-ideation/SKILL.md +10 -266
- x_ipe/resources/skills/task-type-ideation/references/folder-naming-guide.md +55 -0
- x_ipe/resources/skills/task-type-ideation/references/tool-usage-guide.md +236 -0
- x_ipe/resources/skills/task-type-ideation-v2/SKILL.md +488 -0
- x_ipe/resources/skills/task-type-ideation-v2/references/examples.md +377 -0
- x_ipe/resources/skills/task-type-ideation-v2/references/folder-naming-guide.md +74 -0
- x_ipe/resources/skills/task-type-ideation-v2/references/tool-usage-guide.md +145 -0
- x_ipe/resources/skills/task-type-ideation-v2/references/visualization-guide.md +160 -0
- x_ipe/resources/skills/task-type-ideation-v2/templates/idea-summary.md +86 -0
- x_ipe/resources/skills/task-type-refactoring-analysis/SKILL.md +83 -145
- x_ipe/resources/skills/task-type-refactoring-analysis/references/output-schema.md +172 -0
- x_ipe/resources/skills/task-type-technical-design/SKILL.md +28 -214
- x_ipe/resources/skills/task-type-technical-design/references/design-templates.md +422 -0
- x_ipe/resources/skills/task-type-test-generation/SKILL.md +47 -332
- x_ipe/resources/skills/task-type-test-generation/references/test-patterns.md +368 -0
- x_ipe/resources/skills/tool-tracing-creator/SKILL.md +312 -0
- x_ipe/resources/skills/tool-tracing-creator/references/examples.md +324 -0
- x_ipe/resources/skills/tool-tracing-instrumentation/SKILL.md +373 -0
- x_ipe/resources/skills/tool-tracing-instrumentation/references/examples.md +264 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/SKILL.md +486 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/10. example-gate-conditions.md +73 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/11. reference-quality-standards.md +127 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/2. reference-section-order.md +127 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/3. example-step-based-code-review.md +84 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/4. example-step-based-feature-implementation.md +113 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/5. example-function-based-validation.md +73 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/6. example-function-based-analysis.md +94 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/7. example-task-io-code-implementation.md +36 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/8. example-structured-summary.md +43 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/9. example-dor-dod.md +77 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/examples.md +429 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/references/skill-general-guidelines-v2.md +611 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-meta.md +153 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-task-based.md +324 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-task-category.md +109 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/skill-meta-x-ipe-tool.md +205 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-meta.md +334 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-task-based.md +279 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-tool.md +175 -0
- x_ipe/resources/skills/x-ipe-skill-creator-v3/templates/x-ipe-workflow-orchestration.md +329 -0
- x_ipe/resources/skills/x-ipe-task-based-ideation/SKILL.md +487 -0
- x_ipe/resources/skills/x-ipe-task-based-ideation/references/examples.md +377 -0
- x_ipe/resources/skills/x-ipe-task-based-ideation/references/folder-naming-guide.md +74 -0
- x_ipe/resources/skills/x-ipe-task-based-ideation/references/tool-usage-guide.md +145 -0
- x_ipe/resources/skills/x-ipe-task-based-ideation/references/visualization-guide.md +160 -0
- x_ipe/resources/skills/x-ipe-task-based-ideation/templates/idea-summary.md +86 -0
- x_ipe/routes/__init__.py +2 -0
- x_ipe/routes/ideas_routes.py +289 -0
- x_ipe/routes/kb_routes.py +80 -0
- x_ipe/routes/main_routes.py +18 -0
- x_ipe/routes/project_routes.py +7 -0
- x_ipe/routes/proxy_routes.py +10 -2
- x_ipe/routes/quality_evaluation_routes.py +193 -0
- x_ipe/routes/settings_routes.py +6 -0
- x_ipe/routes/tools_routes.py +6 -0
- x_ipe/routes/tracing_routes.py +232 -0
- x_ipe/routes/uiux_feedback_routes.py +50 -0
- x_ipe/services/__init__.py +5 -0
- x_ipe/services/config_service.py +6 -0
- x_ipe/services/file_service.py +20 -0
- x_ipe/services/homepage_service.py +160 -0
- x_ipe/services/ideas_service.py +535 -2
- x_ipe/services/kb_service.py +378 -0
- x_ipe/services/proxy_service.py +37 -7
- x_ipe/services/settings_service.py +13 -0
- x_ipe/services/skills_service.py +4 -0
- x_ipe/services/terminal_service.py +24 -0
- x_ipe/services/themes_service.py +4 -0
- x_ipe/services/tools_config_service.py +4 -0
- x_ipe/services/tracing_service.py +333 -0
- x_ipe/services/uiux_feedback_service.py +148 -1
- x_ipe/services/voice_input_service_v2.py +11 -0
- x_ipe/static/css/base.css +7 -0
- x_ipe/static/css/homepage-infinity.css +330 -0
- x_ipe/static/css/kb-core.css +301 -0
- x_ipe/static/css/quality-evaluation.css +345 -0
- x_ipe/static/css/sidebar.css +14 -4
- x_ipe/static/css/terminal.css +23 -0
- x_ipe/static/css/tracing-dashboard.css +796 -0
- x_ipe/static/css/uiux-feedback.css +7 -1
- x_ipe/static/css/workplace.css +636 -0
- x_ipe/static/img/homepage-infinity-loop.png +0 -0
- x_ipe/static/js/features/confirm-dialog.js +169 -0
- x_ipe/static/js/features/folder-view.js +742 -0
- x_ipe/static/js/features/homepage-infinity.js +314 -0
- x_ipe/static/js/features/kb-core.js +371 -0
- x_ipe/static/js/features/quality-evaluation.js +387 -0
- x_ipe/static/js/features/sidebar.js +255 -12
- x_ipe/static/js/features/tracing-dashboard.js +855 -0
- x_ipe/static/js/features/tracing-graph.js +1031 -0
- x_ipe/static/js/features/tree-drag.js +227 -0
- x_ipe/static/js/features/tree-search.js +228 -0
- x_ipe/static/js/features/workplace.js +661 -33
- x_ipe/static/js/init.js +76 -0
- x_ipe/static/js/terminal-v2.js +45 -14
- x_ipe/static/js/terminal.js +50 -49
- x_ipe/static/js/uiux-feedback.js +75 -16
- x_ipe/templates/base.html +24 -0
- x_ipe/templates/index.html +10 -1
- x_ipe/templates/knowledge-base.html +110 -0
- x_ipe/templates/workplace.html +4 -0
- x_ipe/tracing/__init__.py +37 -0
- x_ipe/tracing/buffer.py +135 -0
- x_ipe/tracing/context.py +125 -0
- x_ipe/tracing/decorator.py +288 -0
- x_ipe/tracing/middleware.py +197 -0
- x_ipe/tracing/parser.py +235 -0
- x_ipe/tracing/redactor.py +111 -0
- x_ipe/tracing/writer.py +122 -0
- {x_ipe-1.0.23.dist-info → x_ipe-1.0.25.dist-info}/METADATA +2 -2
- {x_ipe-1.0.23.dist-info → x_ipe-1.0.25.dist-info}/RECORD +138 -65
- x_ipe/app.py.bak +0 -1333
- x_ipe/resources/skills/x-ipe-skill-creator/SKILL.md +0 -329
- x_ipe/resources/skills/x-ipe-skill-creator/references/output-patterns.md +0 -169
- x_ipe/resources/skills/x-ipe-skill-creator/references/skill-structure.md +0 -162
- x_ipe/resources/skills/x-ipe-skill-creator/references/workflows.md +0 -110
- x_ipe/resources/skills/x-ipe-skill-creator/templates/references/examples.md +0 -113
- x_ipe/resources/skills/x-ipe-skill-creator/templates/skill-category-skill.md +0 -296
- x_ipe/resources/skills/x-ipe-skill-creator/templates/task-type-skill.md +0 -269
- {x_ipe-1.0.23.dist-info → x_ipe-1.0.25.dist-info}/WHEEL +0 -0
- {x_ipe-1.0.23.dist-info → x_ipe-1.0.25.dist-info}/entry_points.txt +0 -0
- {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
|
-
|
|
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
|
-
<
|
|
63
|
-
<
|
|
64
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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
|
}
|