Spaces:
Sleeping
Sleeping
Commit Β·
5f07df7
1
Parent(s): 4364da3
server and ui fixed
Browse files- ui/index.html +12 -2
- ui/static/app.js +62 -26
- ui/static/style.css +25 -0
ui/index.html
CHANGED
|
@@ -27,14 +27,24 @@
|
|
| 27 |
autocomplete="off"
|
| 28 |
spellcheck="false"
|
| 29 |
/>
|
| 30 |
-
<label class="btn btn--secondary" for="zipInput" id="zipLabel">Upload ZIP</label>
|
| 31 |
-
<input id="zipInput" type="file" accept=".zip" style="display:none" />
|
| 32 |
<button id="indexBtn" class="btn btn--primary">
|
| 33 |
<span id="indexBtnText">Index</span>
|
| 34 |
<span id="indexSpinner" class="spinner hidden"></span>
|
| 35 |
</button>
|
| 36 |
</div>
|
| 37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
<div id="statusBadge" class="status-badge status-badge--idle">
|
| 39 |
<span class="status-badge__dot"></span>
|
| 40 |
<span id="statusText">Not indexed</span>
|
|
|
|
| 27 |
autocomplete="off"
|
| 28 |
spellcheck="false"
|
| 29 |
/>
|
|
|
|
|
|
|
| 30 |
<button id="indexBtn" class="btn btn--primary">
|
| 31 |
<span id="indexBtnText">Index</span>
|
| 32 |
<span id="indexSpinner" class="spinner hidden"></span>
|
| 33 |
</button>
|
| 34 |
</div>
|
| 35 |
|
| 36 |
+
<div class="topbar__zip">
|
| 37 |
+
<label class="btn btn--zip" for="zipInput" id="zipLabel">
|
| 38 |
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
|
| 39 |
+
<span id="zipLabelText">Upload ZIP</span>
|
| 40 |
+
</label>
|
| 41 |
+
<input id="zipInput" type="file" accept=".zip" style="display:none" />
|
| 42 |
+
<button id="zipIndexBtn" class="btn btn--primary">
|
| 43 |
+
<span id="zipIndexBtnText">Index ZIP</span>
|
| 44 |
+
<span id="zipIndexSpinner" class="spinner hidden"></span>
|
| 45 |
+
</button>
|
| 46 |
+
</div>
|
| 47 |
+
|
| 48 |
<div id="statusBadge" class="status-badge status-badge--idle">
|
| 49 |
<span class="status-badge__dot"></span>
|
| 50 |
<span id="statusText">Not indexed</span>
|
ui/static/app.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
/* app.js β Codebase Oracle frontend logic */
|
| 2 |
|
| 3 |
-
const API =
|
| 4 |
|
| 5 |
// ββ State βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 6 |
const state = {
|
|
@@ -16,8 +16,11 @@ const $ = id => document.getElementById(id);
|
|
| 16 |
|
| 17 |
const dom = {
|
| 18 |
pathInput: $('pathInput'),
|
|
|
|
| 19 |
zipInput: $('zipInput'),
|
| 20 |
-
|
|
|
|
|
|
|
| 21 |
indexBtn: $('indexBtn'),
|
| 22 |
indexBtnText: $('indexBtnText'),
|
| 23 |
indexSpinner: $('indexSpinner'),
|
|
@@ -63,16 +66,15 @@ function setStatus(type, text) {
|
|
| 63 |
dom.zipInput.addEventListener('change', () => {
|
| 64 |
if (dom.zipInput.files.length > 0) {
|
| 65 |
dom.zipLabel.textContent = dom.zipInput.files[0].name;
|
|
|
|
| 66 |
dom.pathInput.value = '';
|
| 67 |
}
|
| 68 |
});
|
| 69 |
|
| 70 |
dom.indexBtn.addEventListener('click', async () => {
|
| 71 |
-
const path
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
if (!path && !zipFile) {
|
| 75 |
-
appendMessage('error', null, 'β Provide an absolute path or upload a ZIP file.');
|
| 76 |
return;
|
| 77 |
}
|
| 78 |
|
|
@@ -82,32 +84,21 @@ dom.indexBtn.addEventListener('click', async () => {
|
|
| 82 |
setStatus('indexing', 'Indexingβ¦');
|
| 83 |
|
| 84 |
try {
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
data = await res.json();
|
| 92 |
-
} else {
|
| 93 |
-
res = await fetch(`${API}/index`, {
|
| 94 |
-
method: 'POST',
|
| 95 |
-
headers: { 'Content-Type': 'application/json' },
|
| 96 |
-
body: JSON.stringify({ root_path: path }),
|
| 97 |
-
});
|
| 98 |
-
data = await res.json();
|
| 99 |
-
}
|
| 100 |
-
|
| 101 |
if (!res.ok) throw new Error(data.detail || 'Indexing failed');
|
| 102 |
|
| 103 |
-
const label = zipFile ? zipFile.name : path;
|
| 104 |
state.indexed = true;
|
| 105 |
setStatus('ready', `Ready Β· ${data.total_chunks} chunks`);
|
| 106 |
dom.chunkCount.textContent = `${data.total_chunks} chunks`;
|
| 107 |
dom.sendBtn.disabled = false;
|
| 108 |
|
| 109 |
appendMessage('system', null,
|
| 110 |
-
`β Indexed **${
|
| 111 |
`| Metric | Value |\n|--------|-------|\n` +
|
| 112 |
`| Class chunks | ${data.class_chunks} |\n` +
|
| 113 |
`| Function chunks | ${data.function_chunks} |\n` +
|
|
@@ -115,7 +106,6 @@ dom.indexBtn.addEventListener('click', async () => {
|
|
| 115 |
`| Graph nodes | ${data.graph_nodes} |\n` +
|
| 116 |
`| Graph edges | ${data.graph_edges} |`
|
| 117 |
);
|
| 118 |
-
|
| 119 |
loadTree();
|
| 120 |
|
| 121 |
} catch (err) {
|
|
@@ -125,7 +115,53 @@ dom.indexBtn.addEventListener('click', async () => {
|
|
| 125 |
dom.indexBtn.disabled = false;
|
| 126 |
dom.indexBtnText.classList.remove('hidden');
|
| 127 |
dom.indexSpinner.classList.add('hidden');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
dom.zipLabel.textContent = 'Upload ZIP';
|
|
|
|
| 129 |
dom.zipInput.value = '';
|
| 130 |
}
|
| 131 |
});
|
|
|
|
| 1 |
/* app.js β Codebase Oracle frontend logic */
|
| 2 |
|
| 3 |
+
const API = window.location.origin;
|
| 4 |
|
| 5 |
// ββ State βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 6 |
const state = {
|
|
|
|
| 16 |
|
| 17 |
const dom = {
|
| 18 |
pathInput: $('pathInput'),
|
| 19 |
+
zipLabel: $('zipLabelText'),
|
| 20 |
zipInput: $('zipInput'),
|
| 21 |
+
zipIndexBtn: $('zipIndexBtn'),
|
| 22 |
+
zipIndexBtnText: $('zipIndexBtnText'),
|
| 23 |
+
zipIndexSpinner: $('zipIndexSpinner'),
|
| 24 |
indexBtn: $('indexBtn'),
|
| 25 |
indexBtnText: $('indexBtnText'),
|
| 26 |
indexSpinner: $('indexSpinner'),
|
|
|
|
| 66 |
dom.zipInput.addEventListener('change', () => {
|
| 67 |
if (dom.zipInput.files.length > 0) {
|
| 68 |
dom.zipLabel.textContent = dom.zipInput.files[0].name;
|
| 69 |
+
document.getElementById('zipLabel').classList.add('has-file');
|
| 70 |
dom.pathInput.value = '';
|
| 71 |
}
|
| 72 |
});
|
| 73 |
|
| 74 |
dom.indexBtn.addEventListener('click', async () => {
|
| 75 |
+
const path = dom.pathInput.value.trim();
|
| 76 |
+
if (!path) {
|
| 77 |
+
appendMessage('error', null, 'β Provide an absolute path to index.');
|
|
|
|
|
|
|
| 78 |
return;
|
| 79 |
}
|
| 80 |
|
|
|
|
| 84 |
setStatus('indexing', 'Indexingβ¦');
|
| 85 |
|
| 86 |
try {
|
| 87 |
+
const res = await fetch(`${API}/index`, {
|
| 88 |
+
method: 'POST',
|
| 89 |
+
headers: { 'Content-Type': 'application/json' },
|
| 90 |
+
body: JSON.stringify({ root_path: path }),
|
| 91 |
+
});
|
| 92 |
+
const data = await res.json();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
if (!res.ok) throw new Error(data.detail || 'Indexing failed');
|
| 94 |
|
|
|
|
| 95 |
state.indexed = true;
|
| 96 |
setStatus('ready', `Ready Β· ${data.total_chunks} chunks`);
|
| 97 |
dom.chunkCount.textContent = `${data.total_chunks} chunks`;
|
| 98 |
dom.sendBtn.disabled = false;
|
| 99 |
|
| 100 |
appendMessage('system', null,
|
| 101 |
+
`β Indexed **${path}**\n\n` +
|
| 102 |
`| Metric | Value |\n|--------|-------|\n` +
|
| 103 |
`| Class chunks | ${data.class_chunks} |\n` +
|
| 104 |
`| Function chunks | ${data.function_chunks} |\n` +
|
|
|
|
| 106 |
`| Graph nodes | ${data.graph_nodes} |\n` +
|
| 107 |
`| Graph edges | ${data.graph_edges} |`
|
| 108 |
);
|
|
|
|
| 109 |
loadTree();
|
| 110 |
|
| 111 |
} catch (err) {
|
|
|
|
| 115 |
dom.indexBtn.disabled = false;
|
| 116 |
dom.indexBtnText.classList.remove('hidden');
|
| 117 |
dom.indexSpinner.classList.add('hidden');
|
| 118 |
+
}
|
| 119 |
+
});
|
| 120 |
+
|
| 121 |
+
dom.zipIndexBtn.addEventListener('click', async () => {
|
| 122 |
+
const zipFile = dom.zipInput.files[0];
|
| 123 |
+
if (!zipFile) {
|
| 124 |
+
appendMessage('error', null, 'β Please upload a ZIP file first.');
|
| 125 |
+
return;
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
dom.zipIndexBtn.disabled = true;
|
| 129 |
+
dom.zipIndexBtnText.classList.add('hidden');
|
| 130 |
+
dom.zipIndexSpinner.classList.remove('hidden');
|
| 131 |
+
setStatus('indexing', 'Indexingβ¦');
|
| 132 |
+
|
| 133 |
+
try {
|
| 134 |
+
const form = new FormData();
|
| 135 |
+
form.append('file', zipFile);
|
| 136 |
+
const res = await fetch(`${API}/upload-index`, { method: 'POST', body: form });
|
| 137 |
+
const data = await res.json();
|
| 138 |
+
if (!res.ok) throw new Error(data.detail || 'Indexing failed');
|
| 139 |
+
|
| 140 |
+
state.indexed = true;
|
| 141 |
+
setStatus('ready', `Ready Β· ${data.total_chunks} chunks`);
|
| 142 |
+
dom.chunkCount.textContent = `${data.total_chunks} chunks`;
|
| 143 |
+
dom.sendBtn.disabled = false;
|
| 144 |
+
|
| 145 |
+
appendMessage('system', null,
|
| 146 |
+
`β Indexed **${zipFile.name}**\n\n` +
|
| 147 |
+
`| Metric | Value |\n|--------|-------|\n` +
|
| 148 |
+
`| Class chunks | ${data.class_chunks} |\n` +
|
| 149 |
+
`| Function chunks | ${data.function_chunks} |\n` +
|
| 150 |
+
`| Total chunks | ${data.total_chunks} |\n` +
|
| 151 |
+
`| Graph nodes | ${data.graph_nodes} |\n` +
|
| 152 |
+
`| Graph edges | ${data.graph_edges} |`
|
| 153 |
+
);
|
| 154 |
+
loadTree();
|
| 155 |
+
|
| 156 |
+
} catch (err) {
|
| 157 |
+
setStatus('error', 'Error');
|
| 158 |
+
appendMessage('error', null, `β ${err.message}`);
|
| 159 |
+
} finally {
|
| 160 |
+
dom.zipIndexBtn.disabled = false;
|
| 161 |
+
dom.zipIndexBtnText.classList.remove('hidden');
|
| 162 |
+
dom.zipIndexSpinner.classList.add('hidden');
|
| 163 |
dom.zipLabel.textContent = 'Upload ZIP';
|
| 164 |
+
document.getElementById('zipLabel').classList.remove('has-file');
|
| 165 |
dom.zipInput.value = '';
|
| 166 |
}
|
| 167 |
});
|
ui/static/style.css
CHANGED
|
@@ -111,6 +111,31 @@ html, body {
|
|
| 111 |
|
| 112 |
.path-input::placeholder { color: var(--text-muted); }
|
| 113 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
/* ββ Buttons βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 115 |
.btn {
|
| 116 |
display: inline-flex;
|
|
|
|
| 111 |
|
| 112 |
.path-input::placeholder { color: var(--text-muted); }
|
| 113 |
|
| 114 |
+
.topbar__zip {
|
| 115 |
+
display: flex;
|
| 116 |
+
align-items: center;
|
| 117 |
+
gap: 8px;
|
| 118 |
+
padding-left: 16px;
|
| 119 |
+
border-left: 1px solid var(--border);
|
| 120 |
+
flex-shrink: 0;
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
.btn--zip {
|
| 124 |
+
height: 34px;
|
| 125 |
+
padding: 0 14px;
|
| 126 |
+
background: var(--bg-elevated);
|
| 127 |
+
border: 1px solid var(--border);
|
| 128 |
+
color: var(--text-secondary);
|
| 129 |
+
font-family: var(--font-ui);
|
| 130 |
+
font-weight: 600;
|
| 131 |
+
font-size: 13px;
|
| 132 |
+
gap: 6px;
|
| 133 |
+
cursor: pointer;
|
| 134 |
+
transition: all var(--transition);
|
| 135 |
+
}
|
| 136 |
+
.btn--zip:hover { border-color: var(--accent); color: var(--text-primary); }
|
| 137 |
+
.btn--zip.has-file { border-color: var(--accent); color: var(--accent); background: var(--accent-dim); }
|
| 138 |
+
|
| 139 |
/* ββ Buttons βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
| 140 |
.btn {
|
| 141 |
display: inline-flex;
|