ParisNeo commited on
Commit
1eb6b53
Β·
unverified Β·
1 Parent(s): 0429dfc

split the html and javascript code

Browse files
lightrag/api/static/index.html CHANGED
@@ -98,358 +98,7 @@
98
  </div>
99
  </div>
100
 
101
- <script>
102
- // State management
103
- const state = {
104
- apiKey: localStorage.getItem('apiKey') || '',
105
- files: [],
106
- indexedFiles: [],
107
- currentPage: 'file-manager'
108
- };
109
 
110
- // Utility functions
111
- const showToast = (message, duration = 3000) => {
112
- const toast = document.getElementById('toast');
113
- toast.querySelector('div').textContent = message;
114
- toast.classList.remove('hidden');
115
- setTimeout(() => toast.classList.add('hidden'), duration);
116
- };
117
-
118
- const fetchWithAuth = async (url, options = {}) => {
119
- const headers = {
120
- ...(options.headers || {}),
121
- ...(state.apiKey ? { 'Authorization': `Bearer ${state.apiKey}` } : {})
122
- };
123
- return fetch(url, { ...options, headers });
124
- };
125
-
126
- // Page renderers
127
- const pages = {
128
- 'file-manager': () => `
129
- <div class="space-y-6">
130
- <h2 class="text-2xl font-bold text-gray-800">File Manager</h2>
131
-
132
- <div class="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center hover:border-gray-400 transition-colors">
133
- <input type="file" id="fileInput" multiple accept=".txt,.md,.doc,.docx,.pdf,.pptx" class="hidden">
134
- <label for="fileInput" class="cursor-pointer">
135
- <svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
136
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>
137
- </svg>
138
- <p class="mt-2 text-gray-600">Drag files here or click to select</p>
139
- <p class="text-sm text-gray-500">Supported formats: TXT, MD, DOC, PDF, PPTX</p>
140
- </label>
141
- </div>
142
-
143
- <div id="fileList" class="space-y-2">
144
- <h3 class="text-lg font-semibold text-gray-700">Selected Files</h3>
145
- <div class="space-y-2"></div>
146
- </div>
147
- <div id="uploadProgress" class="hidden mt-4">
148
- <div class="w-full bg-gray-200 rounded-full h-2.5">
149
- <div class="bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div>
150
- </div>
151
- <p class="text-sm text-gray-600 mt-2"><span id="uploadStatus">0</span> files processed</p>
152
- </div>
153
-
154
- <button id="uploadBtn" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors">
155
- Upload & Index Files
156
- </button>
157
-
158
- <div id="indexedFiles" class="space-y-2">
159
- <h3 class="text-lg font-semibold text-gray-700">Indexed Files</h3>
160
- <div class="space-y-2"></div>
161
- </div>
162
-
163
- </div>
164
- `,
165
-
166
- 'query': () => `
167
- <div class="space-y-6">
168
- <h2 class="text-2xl font-bold text-gray-800">Query Database</h2>
169
-
170
- <div class="space-y-4">
171
- <div>
172
- <label class="block text-sm font-medium text-gray-700">Query Mode</label>
173
- <select id="queryMode" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
174
- <option value="hybrid">Hybrid</option>
175
- <option value="local">Local</option>
176
- <option value="global">Global</option>
177
- <option value="naive">Naive</option>
178
- </select>
179
- </div>
180
-
181
- <div>
182
- <label class="block text-sm font-medium text-gray-700">Query</label>
183
- <textarea id="queryInput" rows="4" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"></textarea>
184
- </div>
185
-
186
- <button id="queryBtn" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors">
187
- Send Query
188
- </button>
189
-
190
- <div id="queryResult" class="mt-4 p-4 bg-white rounded-lg shadow"></div>
191
- </div>
192
- </div>
193
- `,
194
-
195
- 'knowledge-graph': () => `
196
- <div class="flex items-center justify-center h-full">
197
- <div class="text-center">
198
- <svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
199
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/>
200
- </svg>
201
- <h3 class="mt-2 text-sm font-medium text-gray-900">Under Construction</h3>
202
- <p class="mt-1 text-sm text-gray-500">Knowledge graph visualization will be available in a future update.</p>
203
- </div>
204
- </div>
205
- `,
206
-
207
- 'status': () => `
208
- <div class="space-y-6">
209
- <h2 class="text-2xl font-bold text-gray-800">System Status</h2>
210
- <div id="statusContent" class="grid grid-cols-1 md:grid-cols-2 gap-6">
211
- <div class="p-6 bg-white rounded-lg shadow-sm">
212
- <h3 class="text-lg font-semibold mb-4">System Health</h3>
213
- <div id="healthStatus"></div>
214
- </div>
215
- <div class="p-6 bg-white rounded-lg shadow-sm">
216
- <h3 class="text-lg font-semibold mb-4">Configuration</h3>
217
- <div id="configStatus"></div>
218
- </div>
219
- </div>
220
- </div>
221
- `,
222
-
223
- 'settings': () => `
224
- <div class="space-y-6">
225
- <h2 class="text-2xl font-bold text-gray-800">Settings</h2>
226
-
227
- <div class="max-w-xl">
228
- <div class="space-y-4">
229
- <div>
230
- <label class="block text-sm font-medium text-gray-700">API Key</label>
231
- <input type="password" id="apiKeyInput" value="${state.apiKey}"
232
- class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
233
- </div>
234
-
235
- <button id="saveSettings" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors">
236
- Save Settings
237
- </button>
238
- </div>
239
- </div>
240
- </div>
241
- `
242
- };
243
-
244
- // Page handlers
245
- const handlers = {
246
- 'file-manager': () => {
247
- const fileInput = document.getElementById('fileInput');
248
- const dropZone = fileInput.parentElement.parentElement;
249
- const fileList = document.querySelector('#fileList div');
250
- const indexedFiles = document.querySelector('#indexedFiles div');
251
- const uploadBtn = document.getElementById('uploadBtn');
252
-
253
- const updateFileList = () => {
254
- fileList.innerHTML = state.files.map(file => `
255
- <div class="flex items-center justify-between bg-white p-3 rounded-lg shadow-sm">
256
- <span>${file.name}</span>
257
- <button class="text-red-600 hover:text-red-700" onclick="removeFile('${file.name}')">
258
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
259
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
260
- </svg>
261
- </button>
262
- </div>
263
- `).join('');
264
- };
265
-
266
- const updateIndexedFiles = async () => {
267
- const response = await fetchWithAuth('/health');
268
- const data = await response.json();
269
- indexedFiles.innerHTML = data.indexed_files.map(file => `
270
- <div class="flex items-center justify-between bg-white p-3 rounded-lg shadow-sm">
271
- <span>${file}</span>
272
- </div>
273
- `).join('');
274
- };
275
-
276
- dropZone.addEventListener('dragover', (e) => {
277
- e.preventDefault();
278
- dropZone.classList.add('border-blue-500');
279
- });
280
-
281
- dropZone.addEventListener('dragleave', () => {
282
- dropZone.classList.remove('border-blue-500');
283
- });
284
-
285
- dropZone.addEventListener('drop', (e) => {
286
- e.preventDefault();
287
- dropZone.classList.remove('border-blue-500');
288
- const files = Array.from(e.dataTransfer.files);
289
- state.files.push(...files);
290
- updateFileList();
291
- });
292
-
293
- fileInput.addEventListener('change', () => {
294
- state.files.push(...Array.from(fileInput.files));
295
- updateFileList();
296
- });
297
-
298
- uploadBtn.addEventListener('click', async () => {
299
- if (state.files.length === 0) {
300
- showToast('Please select files to upload');
301
- return;
302
- }
303
- let apiKey = localStorage.getItem('apiKey') || '';
304
- const progress = document.getElementById('uploadProgress');
305
- const progressBar = progress.querySelector('div');
306
- const statusText = document.getElementById('uploadStatus');
307
- progress.classList.remove('hidden');
308
-
309
- for (let i = 0; i < state.files.length; i++) {
310
- const formData = new FormData();
311
- formData.append('file', state.files[i]);
312
-
313
- try {
314
- await fetch('/documents/upload', {
315
- method: 'POST',
316
- headers: apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {},
317
- body: formData
318
- });
319
-
320
- const percentage = ((i + 1) / state.files.length) * 100;
321
- progressBar.style.width = `${percentage}%`;
322
- statusText.textContent = i + 1;
323
- } catch (error) {
324
- console.error('Upload error:', error);
325
- }
326
- }
327
- progress.classList.add('hidden');
328
- });
329
-
330
- updateIndexedFiles();
331
- },
332
-
333
- 'query': () => {
334
- const queryBtn = document.getElementById('queryBtn');
335
- const queryInput = document.getElementById('queryInput');
336
- const queryMode = document.getElementById('queryMode');
337
- const queryResult = document.getElementById('queryResult');
338
-
339
- queryBtn.addEventListener('click', async () => {
340
- const query = queryInput.value.trim();
341
- if (!query) {
342
- showToast('Please enter a query');
343
- return;
344
- }
345
-
346
- queryBtn.disabled = true;
347
- queryBtn.innerHTML = `
348
- <svg class="animate-spin h-5 w-5 mr-3" viewBox="0 0 24 24">
349
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"/>
350
- <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
351
- </svg>
352
- Processing...
353
- `;
354
-
355
- try {
356
- const response = await fetchWithAuth('/query', {
357
- method: 'POST',
358
- headers: { 'Content-Type': 'application/json' },
359
- body: JSON.stringify({
360
- query,
361
- mode: queryMode.value,
362
- stream: false,
363
- only_need_context: false
364
- })
365
- });
366
-
367
- const data = await response.json();
368
- queryResult.innerHTML = marked.parse(data.response);
369
- } catch (error) {
370
- showToast('Error processing query');
371
- } finally {
372
- queryBtn.disabled = false;
373
- queryBtn.textContent = 'Send Query';
374
- }
375
- });
376
- },
377
-
378
- 'status': async () => {
379
- const healthStatus = document.getElementById('healthStatus');
380
- const configStatus = document.getElementById('configStatus');
381
-
382
- try {
383
- const response = await fetchWithAuth('/health');
384
- const data = await response.json();
385
-
386
- healthStatus.innerHTML = `
387
- <div class="space-y-2">
388
- <div class="flex items-center">
389
- <div class="w-3 h-3 rounded-full ${data.status === 'healthy' ? 'bg-green-500' : 'bg-red-500'} mr-2"></div>
390
- <span class="font-medium">${data.status}</span>
391
- </div>
392
- <div>
393
- <p class="text-sm text-gray-600">Working Directory: ${data.working_directory}</p>
394
- <p class="text-sm text-gray-600">Input Directory: ${data.input_directory}</p>
395
- <p class="text-sm text-gray-600">Indexed Files: ${data.indexed_files_count}</p>
396
- </div>
397
- </div>
398
- `;
399
-
400
- configStatus.innerHTML = Object.entries(data.configuration)
401
- .map(([key, value]) => `
402
- <div class="mb-2">
403
- <span class="text-sm font-medium text-gray-700">${key}:</span>
404
- <span class="text-sm text-gray-600 ml-2">${value}</span>
405
- </div>
406
- `).join('');
407
- } catch (error) {
408
- showToast('Error fetching status');
409
- }
410
- },
411
-
412
- 'settings': () => {
413
- const saveBtn = document.getElementById('saveSettings');
414
- const apiKeyInput = document.getElementById('apiKeyInput');
415
-
416
- saveBtn.addEventListener('click', () => {
417
- state.apiKey = apiKeyInput.value;
418
- localStorage.setItem('apiKey', state.apiKey);
419
- showToast('Settings saved successfully');
420
- });
421
- }
422
- };
423
-
424
- // Navigation handling
425
- document.querySelectorAll('.nav-item').forEach(item => {
426
- item.addEventListener('click', (e) => {
427
- e.preventDefault();
428
- const page = item.dataset.page;
429
- document.getElementById('content').innerHTML = pages[page]();
430
- if (handlers[page]) handlers[page]();
431
- state.currentPage = page;
432
- });
433
- });
434
-
435
- // Initialize with file manager
436
- document.getElementById('content').innerHTML = pages['file-manager']();
437
- handlers['file-manager']();
438
-
439
- // Global functions
440
- window.removeFile = (fileName) => {
441
- state.files = state.files.filter(file => file.name !== fileName);
442
- document.querySelector('#fileList div').innerHTML = state.files.map(file => `
443
- <div class="flex items-center justify-between bg-white p-3 rounded-lg shadow-sm">
444
- <span>${file.name}</span>
445
- <button class="text-red-600 hover:text-red-700" onclick="removeFile('${file.name}')">
446
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
447
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
448
- </svg>
449
- </button>
450
- </div>
451
- `).join('');
452
- };
453
- </script>
454
  </body>
455
  </html>
 
98
  </div>
99
  </div>
100
 
101
+ <script src="/js/lightrag_api.js"></script>
 
 
 
 
 
 
 
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  </body>
104
  </html>
lightrag/api/{webui/static β†’ static}/js/graph.js RENAMED
File without changes
lightrag/api/static/js/lightrag_api.js ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // State management
2
+ const state = {
3
+ apiKey: localStorage.getItem('apiKey') || '',
4
+ files: [],
5
+ indexedFiles: [],
6
+ currentPage: 'file-manager'
7
+ };
8
+
9
+ // Utility functions
10
+ const showToast = (message, duration = 3000) => {
11
+ const toast = document.getElementById('toast');
12
+ toast.querySelector('div').textContent = message;
13
+ toast.classList.remove('hidden');
14
+ setTimeout(() => toast.classList.add('hidden'), duration);
15
+ };
16
+
17
+ const fetchWithAuth = async (url, options = {}) => {
18
+ const headers = {
19
+ ...(options.headers || {}),
20
+ ...(state.apiKey ? { 'Authorization': `Bearer ${state.apiKey}` } : {})
21
+ };
22
+ return fetch(url, { ...options, headers });
23
+ };
24
+
25
+ // Page renderers
26
+ const pages = {
27
+ 'file-manager': () => `
28
+ <div class="space-y-6">
29
+ <h2 class="text-2xl font-bold text-gray-800">File Manager</h2>
30
+
31
+ <div class="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center hover:border-gray-400 transition-colors">
32
+ <input type="file" id="fileInput" multiple accept=".txt,.md,.doc,.docx,.pdf,.pptx" class="hidden">
33
+ <label for="fileInput" class="cursor-pointer">
34
+ <svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
35
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>
36
+ </svg>
37
+ <p class="mt-2 text-gray-600">Drag files here or click to select</p>
38
+ <p class="text-sm text-gray-500">Supported formats: TXT, MD, DOC, PDF, PPTX</p>
39
+ </label>
40
+ </div>
41
+
42
+ <div id="fileList" class="space-y-2">
43
+ <h3 class="text-lg font-semibold text-gray-700">Selected Files</h3>
44
+ <div class="space-y-2"></div>
45
+ </div>
46
+ <div id="uploadProgress" class="hidden mt-4">
47
+ <div class="w-full bg-gray-200 rounded-full h-2.5">
48
+ <div class="bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div>
49
+ </div>
50
+ <p class="text-sm text-gray-600 mt-2"><span id="uploadStatus">0</span> files processed</p>
51
+ </div>
52
+
53
+ <button id="uploadBtn" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors">
54
+ Upload & Index Files
55
+ </button>
56
+
57
+ <div id="indexedFiles" class="space-y-2">
58
+ <h3 class="text-lg font-semibold text-gray-700">Indexed Files</h3>
59
+ <div class="space-y-2"></div>
60
+ </div>
61
+
62
+ </div>
63
+ `,
64
+
65
+ 'query': () => `
66
+ <div class="space-y-6">
67
+ <h2 class="text-2xl font-bold text-gray-800">Query Database</h2>
68
+
69
+ <div class="space-y-4">
70
+ <div>
71
+ <label class="block text-sm font-medium text-gray-700">Query Mode</label>
72
+ <select id="queryMode" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
73
+ <option value="hybrid">Hybrid</option>
74
+ <option value="local">Local</option>
75
+ <option value="global">Global</option>
76
+ <option value="naive">Naive</option>
77
+ </select>
78
+ </div>
79
+
80
+ <div>
81
+ <label class="block text-sm font-medium text-gray-700">Query</label>
82
+ <textarea id="queryInput" rows="4" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"></textarea>
83
+ </div>
84
+
85
+ <button id="queryBtn" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors">
86
+ Send Query
87
+ </button>
88
+
89
+ <div id="queryResult" class="mt-4 p-4 bg-white rounded-lg shadow"></div>
90
+ </div>
91
+ </div>
92
+ `,
93
+
94
+ 'knowledge-graph': () => `
95
+ <div class="flex items-center justify-center h-full">
96
+ <div class="text-center">
97
+ <svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
98
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"/>
99
+ </svg>
100
+ <h3 class="mt-2 text-sm font-medium text-gray-900">Under Construction</h3>
101
+ <p class="mt-1 text-sm text-gray-500">Knowledge graph visualization will be available in a future update.</p>
102
+ </div>
103
+ </div>
104
+ `,
105
+
106
+ 'status': () => `
107
+ <div class="space-y-6">
108
+ <h2 class="text-2xl font-bold text-gray-800">System Status</h2>
109
+ <div id="statusContent" class="grid grid-cols-1 md:grid-cols-2 gap-6">
110
+ <div class="p-6 bg-white rounded-lg shadow-sm">
111
+ <h3 class="text-lg font-semibold mb-4">System Health</h3>
112
+ <div id="healthStatus"></div>
113
+ </div>
114
+ <div class="p-6 bg-white rounded-lg shadow-sm">
115
+ <h3 class="text-lg font-semibold mb-4">Configuration</h3>
116
+ <div id="configStatus"></div>
117
+ </div>
118
+ </div>
119
+ </div>
120
+ `,
121
+
122
+ 'settings': () => `
123
+ <div class="space-y-6">
124
+ <h2 class="text-2xl font-bold text-gray-800">Settings</h2>
125
+
126
+ <div class="max-w-xl">
127
+ <div class="space-y-4">
128
+ <div>
129
+ <label class="block text-sm font-medium text-gray-700">API Key</label>
130
+ <input type="password" id="apiKeyInput" value="${state.apiKey}"
131
+ class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500">
132
+ </div>
133
+
134
+ <button id="saveSettings" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700 transition-colors">
135
+ Save Settings
136
+ </button>
137
+ </div>
138
+ </div>
139
+ </div>
140
+ `
141
+ };
142
+
143
+ // Page handlers
144
+ const handlers = {
145
+ 'file-manager': () => {
146
+ const fileInput = document.getElementById('fileInput');
147
+ const dropZone = fileInput.parentElement.parentElement;
148
+ const fileList = document.querySelector('#fileList div');
149
+ const indexedFiles = document.querySelector('#indexedFiles div');
150
+ const uploadBtn = document.getElementById('uploadBtn');
151
+
152
+ const updateFileList = () => {
153
+ fileList.innerHTML = state.files.map(file => `
154
+ <div class="flex items-center justify-between bg-white p-3 rounded-lg shadow-sm">
155
+ <span>${file.name}</span>
156
+ <button class="text-red-600 hover:text-red-700" onclick="removeFile('${file.name}')">
157
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
158
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
159
+ </svg>
160
+ </button>
161
+ </div>
162
+ `).join('');
163
+ };
164
+
165
+ const updateIndexedFiles = async () => {
166
+ const response = await fetchWithAuth('/health');
167
+ const data = await response.json();
168
+ indexedFiles.innerHTML = data.indexed_files.map(file => `
169
+ <div class="flex items-center justify-between bg-white p-3 rounded-lg shadow-sm">
170
+ <span>${file}</span>
171
+ </div>
172
+ `).join('');
173
+ };
174
+
175
+ dropZone.addEventListener('dragover', (e) => {
176
+ e.preventDefault();
177
+ dropZone.classList.add('border-blue-500');
178
+ });
179
+
180
+ dropZone.addEventListener('dragleave', () => {
181
+ dropZone.classList.remove('border-blue-500');
182
+ });
183
+
184
+ dropZone.addEventListener('drop', (e) => {
185
+ e.preventDefault();
186
+ dropZone.classList.remove('border-blue-500');
187
+ const files = Array.from(e.dataTransfer.files);
188
+ state.files.push(...files);
189
+ updateFileList();
190
+ });
191
+
192
+ fileInput.addEventListener('change', () => {
193
+ state.files.push(...Array.from(fileInput.files));
194
+ updateFileList();
195
+ });
196
+
197
+ uploadBtn.addEventListener('click', async () => {
198
+ if (state.files.length === 0) {
199
+ showToast('Please select files to upload');
200
+ return;
201
+ }
202
+ let apiKey = localStorage.getItem('apiKey') || '';
203
+ const progress = document.getElementById('uploadProgress');
204
+ const progressBar = progress.querySelector('div');
205
+ const statusText = document.getElementById('uploadStatus');
206
+ progress.classList.remove('hidden');
207
+
208
+ for (let i = 0; i < state.files.length; i++) {
209
+ const formData = new FormData();
210
+ formData.append('file', state.files[i]);
211
+
212
+ try {
213
+ await fetch('/documents/upload', {
214
+ method: 'POST',
215
+ headers: apiKey ? { 'Authorization': `Bearer ${apiKey}` } : {},
216
+ body: formData
217
+ });
218
+
219
+ const percentage = ((i + 1) / state.files.length) * 100;
220
+ progressBar.style.width = `${percentage}%`;
221
+ statusText.textContent = i + 1;
222
+ } catch (error) {
223
+ console.error('Upload error:', error);
224
+ }
225
+ }
226
+ progress.classList.add('hidden');
227
+ });
228
+
229
+ updateIndexedFiles();
230
+ },
231
+
232
+ 'query': () => {
233
+ const queryBtn = document.getElementById('queryBtn');
234
+ const queryInput = document.getElementById('queryInput');
235
+ const queryMode = document.getElementById('queryMode');
236
+ const queryResult = document.getElementById('queryResult');
237
+
238
+ let apiKey = localStorage.getItem('apiKey') || '';
239
+
240
+ queryBtn.addEventListener('click', async () => {
241
+ const query = queryInput.value.trim();
242
+ if (!query) {
243
+ showToast('Please enter a query');
244
+ return;
245
+ }
246
+
247
+ queryBtn.disabled = true;
248
+ queryBtn.innerHTML = `
249
+ <svg class="animate-spin h-5 w-5 mr-3" viewBox="0 0 24 24">
250
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"/>
251
+ <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"/>
252
+ </svg>
253
+ Processing...
254
+ `;
255
+
256
+ try {
257
+ const response = await fetchWithAuth('/query', {
258
+ method: 'POST',
259
+ headers: { 'Content-Type': 'application/json' },
260
+ body: JSON.stringify({
261
+ query,
262
+ mode: queryMode.value,
263
+ stream: false,
264
+ only_need_context: false
265
+ })
266
+ });
267
+
268
+ const data = await response.json();
269
+ queryResult.innerHTML = marked.parse(data.response);
270
+ } catch (error) {
271
+ showToast('Error processing query');
272
+ } finally {
273
+ queryBtn.disabled = false;
274
+ queryBtn.textContent = 'Send Query';
275
+ }
276
+ });
277
+ },
278
+
279
+ 'status': async () => {
280
+ const healthStatus = document.getElementById('healthStatus');
281
+ const configStatus = document.getElementById('configStatus');
282
+
283
+ try {
284
+ const response = await fetchWithAuth('/health');
285
+ const data = await response.json();
286
+
287
+ healthStatus.innerHTML = `
288
+ <div class="space-y-2">
289
+ <div class="flex items-center">
290
+ <div class="w-3 h-3 rounded-full ${data.status === 'healthy' ? 'bg-green-500' : 'bg-red-500'} mr-2"></div>
291
+ <span class="font-medium">${data.status}</span>
292
+ </div>
293
+ <div>
294
+ <p class="text-sm text-gray-600">Working Directory: ${data.working_directory}</p>
295
+ <p class="text-sm text-gray-600">Input Directory: ${data.input_directory}</p>
296
+ <p class="text-sm text-gray-600">Indexed Files: ${data.indexed_files_count}</p>
297
+ </div>
298
+ </div>
299
+ `;
300
+
301
+ configStatus.innerHTML = Object.entries(data.configuration)
302
+ .map(([key, value]) => `
303
+ <div class="mb-2">
304
+ <span class="text-sm font-medium text-gray-700">${key}:</span>
305
+ <span class="text-sm text-gray-600 ml-2">${value}</span>
306
+ </div>
307
+ `).join('');
308
+ } catch (error) {
309
+ showToast('Error fetching status');
310
+ }
311
+ },
312
+
313
+ 'settings': () => {
314
+ const saveBtn = document.getElementById('saveSettings');
315
+ const apiKeyInput = document.getElementById('apiKeyInput');
316
+
317
+ saveBtn.addEventListener('click', () => {
318
+ state.apiKey = apiKeyInput.value;
319
+ localStorage.setItem('apiKey', state.apiKey);
320
+ showToast('Settings saved successfully');
321
+ });
322
+ }
323
+ };
324
+
325
+ // Navigation handling
326
+ document.querySelectorAll('.nav-item').forEach(item => {
327
+ item.addEventListener('click', (e) => {
328
+ e.preventDefault();
329
+ const page = item.dataset.page;
330
+ document.getElementById('content').innerHTML = pages[page]();
331
+ if (handlers[page]) handlers[page]();
332
+ state.currentPage = page;
333
+ });
334
+ });
335
+
336
+ // Initialize with file manager
337
+ document.getElementById('content').innerHTML = pages['file-manager']();
338
+ handlers['file-manager']();
339
+
340
+ // Global functions
341
+ window.removeFile = (fileName) => {
342
+ state.files = state.files.filter(file => file.name !== fileName);
343
+ document.querySelector('#fileList div').innerHTML = state.files.map(file => `
344
+ <div class="flex items-center justify-between bg-white p-3 rounded-lg shadow-sm">
345
+ <span>${file.name}</span>
346
+ <button class="text-red-600 hover:text-red-700" onclick="removeFile('${file.name}')">
347
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
348
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
349
+ </svg>
350
+ </button>
351
+ </div>
352
+ `).join('');
353
+ };
lightrag/api/{webui β†’ webui_depricated}/static/__init__.py RENAMED
File without changes
lightrag/api/{webui β†’ webui_depricated}/static/css/__init__.py RENAMED
File without changes
lightrag/api/{webui β†’ webui_depricated}/static/css/graph.css RENAMED
File without changes
lightrag/api/{webui β†’ webui_depricated}/static/css/lightrag.css RENAMED
File without changes
lightrag/api/{webui β†’ webui_depricated}/static/index.html RENAMED
File without changes
lightrag/api/{webui β†’ webui_depricated}/static/js/__init__.py RENAMED
File without changes
lightrag/api/{webui β†’ webui_depricated}/static/js/lightrag.js RENAMED
File without changes