x-ipe 1.0.24__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 (139) hide show
  1. x_ipe/app.py +25 -3
  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 +17 -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 +2 -0
  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 +30 -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 +19 -0
  87. x_ipe/services/kb_service.py +378 -0
  88. x_ipe/services/proxy_service.py +4 -0
  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 +32 -0
  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 +1 -0
  103. x_ipe/static/css/tracing-dashboard.css +796 -0
  104. x_ipe/static/css/workplace.css +20 -0
  105. x_ipe/static/img/homepage-infinity-loop.png +0 -0
  106. x_ipe/static/js/features/homepage-infinity.js +314 -0
  107. x_ipe/static/js/features/kb-core.js +371 -0
  108. x_ipe/static/js/features/quality-evaluation.js +387 -0
  109. x_ipe/static/js/features/sidebar.js +255 -12
  110. x_ipe/static/js/features/tracing-dashboard.js +855 -0
  111. x_ipe/static/js/features/tracing-graph.js +1031 -0
  112. x_ipe/static/js/features/tree-search.js +6 -2
  113. x_ipe/static/js/features/workplace.js +200 -6
  114. x_ipe/static/js/init.js +76 -0
  115. x_ipe/static/js/uiux-feedback.js +18 -2
  116. x_ipe/templates/base.html +19 -0
  117. x_ipe/templates/index.html +7 -1
  118. x_ipe/templates/knowledge-base.html +110 -0
  119. x_ipe/templates/workplace.html +4 -0
  120. x_ipe/tracing/__init__.py +37 -0
  121. x_ipe/tracing/buffer.py +135 -0
  122. x_ipe/tracing/context.py +125 -0
  123. x_ipe/tracing/decorator.py +288 -0
  124. x_ipe/tracing/middleware.py +197 -0
  125. x_ipe/tracing/parser.py +235 -0
  126. x_ipe/tracing/redactor.py +111 -0
  127. x_ipe/tracing/writer.py +122 -0
  128. {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/METADATA +2 -2
  129. {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/RECORD +132 -62
  130. x_ipe/resources/skills/x-ipe-skill-creator/SKILL.md +0 -329
  131. x_ipe/resources/skills/x-ipe-skill-creator/references/output-patterns.md +0 -169
  132. x_ipe/resources/skills/x-ipe-skill-creator/references/skill-structure.md +0 -162
  133. x_ipe/resources/skills/x-ipe-skill-creator/references/workflows.md +0 -110
  134. x_ipe/resources/skills/x-ipe-skill-creator/templates/references/examples.md +0 -113
  135. x_ipe/resources/skills/x-ipe-skill-creator/templates/skill-category-skill.md +0 -296
  136. x_ipe/resources/skills/x-ipe-skill-creator/templates/task-type-skill.md +0 -269
  137. {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/WHEEL +0 -0
  138. {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/entry_points.txt +0 -0
  139. {x_ipe-1.0.24.dist-info → x_ipe-1.0.25.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,371 @@
1
+ /**
2
+ * FEATURE-025-A: KB Core Infrastructure
3
+ *
4
+ * Frontend JavaScript for Knowledge Base page.
5
+ * Uses two-column layout (like Ideation view) with tree on left of content area.
6
+ */
7
+
8
+ const kbCore = {
9
+ index: null,
10
+ topics: [],
11
+ searchTerm: '',
12
+ container: null,
13
+
14
+ /**
15
+ * Render the Knowledge Base view in a container (like WorkplaceManager)
16
+ */
17
+ render(container) {
18
+ this.container = container;
19
+ container.innerHTML = `
20
+ <div class="kb-container">
21
+ <div class="kb-sidebar pinned" id="kb-sidebar">
22
+ <div class="kb-sidebar-header">
23
+ <span class="kb-sidebar-title">Knowledge Base</span>
24
+ <button class="kb-refresh-btn" id="btn-refresh-index" title="Refresh index">
25
+ <i class="bi bi-arrow-clockwise"></i>
26
+ </button>
27
+ </div>
28
+ <div class="kb-search-box">
29
+ <input type="text" class="kb-search-input" id="kb-search" placeholder="Search files...">
30
+ </div>
31
+ <div class="kb-tree" id="kb-tree">
32
+ <div class="kb-loading"><i class="bi bi-hourglass-split"></i> Loading...</div>
33
+ </div>
34
+ </div>
35
+ <div class="kb-content" id="kb-content">
36
+ <div class="kb-placeholder">
37
+ <i class="bi bi-archive"></i>
38
+ <h5>Knowledge Base</h5>
39
+ <p class="text-muted">Select a file from the tree to view</p>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ `;
44
+ this.init();
45
+ },
46
+
47
+ /**
48
+ * Initialize the Knowledge Base view.
49
+ * Fetches index and renders tree + content.
50
+ */
51
+ async init() {
52
+ console.log('[KB] Initializing Knowledge Base view');
53
+
54
+ // Bind event handlers
55
+ this.bindEvents();
56
+
57
+ // Load initial data
58
+ await this.loadIndex();
59
+
60
+ // Render UI
61
+ this.renderTree();
62
+ this.renderWelcome();
63
+ },
64
+
65
+ /**
66
+ * Bind DOM event handlers.
67
+ */
68
+ bindEvents() {
69
+ // Refresh button
70
+ const refreshBtn = document.getElementById('btn-refresh-index');
71
+ if (refreshBtn) {
72
+ refreshBtn.addEventListener('click', () => this.refreshIndex());
73
+ }
74
+
75
+ // Search input
76
+ const searchInput = document.getElementById('kb-search');
77
+ if (searchInput) {
78
+ searchInput.addEventListener('input', (e) => {
79
+ this.searchTerm = e.target.value.toLowerCase();
80
+ this.renderTree();
81
+ });
82
+ }
83
+ },
84
+
85
+ /**
86
+ * Load file index from API.
87
+ */
88
+ async loadIndex() {
89
+ try {
90
+ const response = await fetch('/api/kb/index');
91
+ if (response.ok) {
92
+ this.index = await response.json();
93
+ console.log('[KB] Index loaded:', this.index.files?.length || 0, 'files');
94
+ } else {
95
+ console.error('[KB] Failed to load index:', response.status);
96
+ this.index = { version: '1.0', last_updated: null, files: [] };
97
+ }
98
+ } catch (error) {
99
+ console.error('[KB] Error loading index:', error);
100
+ this.index = { version: '1.0', last_updated: null, files: [] };
101
+ }
102
+
103
+ // Also load topics
104
+ try {
105
+ const response = await fetch('/api/kb/topics');
106
+ if (response.ok) {
107
+ const data = await response.json();
108
+ this.topics = data.topics || [];
109
+ }
110
+ } catch (error) {
111
+ console.error('[KB] Error loading topics:', error);
112
+ this.topics = [];
113
+ }
114
+ },
115
+
116
+ /**
117
+ * Refresh index from file system.
118
+ */
119
+ async refreshIndex() {
120
+ const refreshBtn = document.getElementById('btn-refresh-index');
121
+ if (refreshBtn) {
122
+ refreshBtn.disabled = true;
123
+ refreshBtn.innerHTML = '<i class="bi bi-hourglass-split"></i>';
124
+ }
125
+
126
+ try {
127
+ const response = await fetch('/api/kb/index/refresh', { method: 'POST' });
128
+ if (response.ok) {
129
+ this.index = await response.json();
130
+ console.log('[KB] Index refreshed:', this.index.files?.length || 0, 'files');
131
+
132
+ // Reload topics
133
+ await this.loadIndex();
134
+
135
+ // Re-render
136
+ this.renderTree();
137
+ }
138
+ } catch (error) {
139
+ console.error('[KB] Error refreshing index:', error);
140
+ } finally {
141
+ if (refreshBtn) {
142
+ refreshBtn.disabled = false;
143
+ refreshBtn.innerHTML = '<i class="bi bi-arrow-clockwise"></i>';
144
+ }
145
+ }
146
+ },
147
+
148
+ /**
149
+ * Render the tree in kb-sidebar (within content area).
150
+ */
151
+ renderTree() {
152
+ const container = document.getElementById('kb-tree');
153
+ if (!container) return;
154
+
155
+ const files = this.index?.files || [];
156
+
157
+ // Filter by search term
158
+ const filteredFiles = this.searchTerm
159
+ ? files.filter(f =>
160
+ f.name.toLowerCase().includes(this.searchTerm) ||
161
+ f.topic?.toLowerCase().includes(this.searchTerm) ||
162
+ f.keywords?.some(k => k.includes(this.searchTerm))
163
+ )
164
+ : files;
165
+
166
+ // Group by folder structure
167
+ const landing = filteredFiles.filter(f => f.path.startsWith('landing/'));
168
+ const byTopic = {};
169
+
170
+ filteredFiles.forEach(f => {
171
+ if (f.topic && !f.path.startsWith('landing/')) {
172
+ if (!byTopic[f.topic]) byTopic[f.topic] = [];
173
+ byTopic[f.topic].push(f);
174
+ }
175
+ });
176
+
177
+ // Build HTML
178
+ let html = '';
179
+
180
+ // Landing folder
181
+ html += `
182
+ <div class="kb-folder">
183
+ <div class="kb-folder-header" data-folder="landing">
184
+ <i class="bi bi-chevron-down"></i>
185
+ <i class="bi bi-inbox text-warning"></i>
186
+ <span>Landing (${landing.length})</span>
187
+ </div>
188
+ <div class="kb-folder-files">
189
+ ${landing.map(f => this.renderFileItem(f)).join('')}
190
+ ${landing.length === 0 ? '<div class="kb-empty">No files</div>' : ''}
191
+ </div>
192
+ </div>
193
+ `;
194
+
195
+ // Topic folders
196
+ const sortedTopics = Object.keys(byTopic).sort();
197
+ sortedTopics.forEach(topic => {
198
+ const topicFiles = byTopic[topic];
199
+ html += `
200
+ <div class="kb-folder">
201
+ <div class="kb-folder-header" data-folder="${topic}">
202
+ <i class="bi bi-chevron-down"></i>
203
+ <i class="bi bi-folder text-info"></i>
204
+ <span>${topic} (${topicFiles.length})</span>
205
+ </div>
206
+ <div class="kb-folder-files">
207
+ ${topicFiles.map(f => this.renderFileItem(f)).join('')}
208
+ </div>
209
+ </div>
210
+ `;
211
+ });
212
+
213
+ // Empty topics placeholder
214
+ if (sortedTopics.length === 0 && landing.length === 0) {
215
+ html += '<div class="kb-empty-state"><i class="bi bi-archive"></i><p>No files in Knowledge Base</p></div>';
216
+ }
217
+
218
+ container.innerHTML = html;
219
+
220
+ // Bind folder toggle events
221
+ container.querySelectorAll('.kb-folder-header').forEach(header => {
222
+ header.addEventListener('click', (e) => {
223
+ const folder = e.currentTarget.closest('.kb-folder');
224
+ folder.classList.toggle('collapsed');
225
+ const icon = e.currentTarget.querySelector('i:first-child');
226
+ icon.classList.toggle('bi-chevron-down');
227
+ icon.classList.toggle('bi-chevron-right');
228
+ });
229
+ });
230
+
231
+ // Bind file click events
232
+ container.querySelectorAll('.kb-file-item').forEach(item => {
233
+ item.addEventListener('click', () => {
234
+ // Remove active from others
235
+ container.querySelectorAll('.kb-file-item').forEach(i => i.classList.remove('active'));
236
+ item.classList.add('active');
237
+
238
+ const path = item.dataset.path;
239
+ this.showFilePreview(path);
240
+ });
241
+ });
242
+ },
243
+
244
+ /**
245
+ * Render a single file item in the sidebar.
246
+ */
247
+ renderFileItem(file) {
248
+ const icon = this.getFileIcon(file.type);
249
+ return `
250
+ <div class="kb-file-item" data-path="${file.path}" title="${file.name}">
251
+ <i class="bi ${icon}"></i>
252
+ <span class="kb-file-name">${file.name}</span>
253
+ </div>
254
+ `;
255
+ },
256
+
257
+ /**
258
+ * Get Bootstrap icon class for file type.
259
+ */
260
+ getFileIcon(type) {
261
+ const icons = {
262
+ 'pdf': 'bi-file-earmark-pdf text-danger',
263
+ 'markdown': 'bi-markdown text-info',
264
+ 'text': 'bi-file-text',
265
+ 'docx': 'bi-file-earmark-word text-primary',
266
+ 'xlsx': 'bi-file-earmark-excel text-success',
267
+ 'python': 'bi-filetype-py text-warning',
268
+ 'javascript': 'bi-filetype-js text-warning',
269
+ 'typescript': 'bi-filetype-tsx text-primary',
270
+ 'java': 'bi-filetype-java text-danger',
271
+ 'json': 'bi-filetype-json text-warning',
272
+ 'html': 'bi-filetype-html text-danger',
273
+ 'css': 'bi-filetype-css text-info',
274
+ 'image': 'bi-file-image text-success',
275
+ 'unknown': 'bi-file-earmark'
276
+ };
277
+ return icons[type] || icons['unknown'];
278
+ },
279
+
280
+ /**
281
+ * Render welcome/placeholder in content area.
282
+ */
283
+ renderWelcome() {
284
+ const content = document.getElementById('kb-content');
285
+ if (!content) return;
286
+
287
+ const files = this.index?.files || [];
288
+ const landing = files.filter(f => f.path.startsWith('landing/'));
289
+
290
+ content.innerHTML = `
291
+ <div class="kb-welcome">
292
+ <div class="kb-stats mb-4">
293
+ <span class="badge bg-secondary me-2">${files.length} files</span>
294
+ <span class="badge bg-info">${this.topics.length} topics</span>
295
+ </div>
296
+ <div class="kb-section">
297
+ <h6><i class="bi bi-inbox text-warning me-2"></i>Landing (Unprocessed)</h6>
298
+ <div class="kb-landing-list">
299
+ ${landing.length > 0 ? landing.map(f => `
300
+ <div class="kb-landing-file">
301
+ <i class="bi ${this.getFileIcon(f.type)}"></i>
302
+ <span>${f.name}</span>
303
+ <span class="text-muted small ms-auto">${this.formatSize(f.size)}</span>
304
+ </div>
305
+ `).join('') : '<p class="text-muted small">No files in landing folder</p>'}
306
+ </div>
307
+ </div>
308
+ <div class="kb-section mt-4">
309
+ <h6><i class="bi bi-folder text-info me-2"></i>Topics</h6>
310
+ <div class="kb-topics-grid">
311
+ ${this.topics.length > 0 ? this.topics.map(topic => {
312
+ const topicFiles = files.filter(f => f.topic === topic);
313
+ return `
314
+ <div class="kb-topic-card">
315
+ <i class="bi bi-folder me-2"></i>${topic}
316
+ <span class="badge bg-secondary ms-auto">${topicFiles.length}</span>
317
+ </div>
318
+ `;
319
+ }).join('') : '<p class="text-muted small">No topics created yet</p>'}
320
+ </div>
321
+ </div>
322
+ </div>
323
+ `;
324
+ },
325
+
326
+ /**
327
+ * Show file preview in content area.
328
+ */
329
+ async showFilePreview(path) {
330
+ const content = document.getElementById('kb-content');
331
+ if (!content) return;
332
+
333
+ const file = this.index?.files?.find(f => f.path === path);
334
+ if (!file) {
335
+ content.innerHTML = '<div class="kb-error">File not found</div>';
336
+ return;
337
+ }
338
+
339
+ content.innerHTML = `
340
+ <div class="kb-file-preview">
341
+ <div class="kb-file-header">
342
+ <i class="bi ${this.getFileIcon(file.type)} fs-4"></i>
343
+ <div class="kb-file-info">
344
+ <h6 class="mb-0">${file.name}</h6>
345
+ <small class="text-muted">${file.path} · ${this.formatSize(file.size)}</small>
346
+ </div>
347
+ </div>
348
+ <div class="kb-file-meta mt-3">
349
+ <span class="badge bg-secondary me-1">${file.type}</span>
350
+ ${file.topic ? `<span class="badge bg-info me-1">${file.topic}</span>` : ''}
351
+ ${(file.keywords || []).map(k => `<span class="badge bg-outline-secondary me-1">${k}</span>`).join('')}
352
+ </div>
353
+ <div class="kb-file-content mt-3">
354
+ <p class="text-muted small">File preview coming in a future release.</p>
355
+ </div>
356
+ </div>
357
+ `;
358
+ },
359
+
360
+ /**
361
+ * Format file size for display.
362
+ */
363
+ formatSize(bytes) {
364
+ if (bytes < 1024) return bytes + ' B';
365
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
366
+ return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
367
+ }
368
+ };
369
+
370
+ // Export for use in template
371
+ window.kbCore = kbCore;