Skip to content

Commit 160a780

Browse files
committed
Redesign frontend with modern dark-mode UI and improved search interaction
1 parent 19a241a commit 160a780

3 files changed

Lines changed: 516 additions & 49 deletions

File tree

src/index.html

Lines changed: 367 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,375 @@
11
<!DOCTYPE html>
22
<html lang="en">
33
<head>
4-
<meta charset="UTF-8">
5-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<title>DocSense</title>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<meta name="description" content="DocSense — fast local document search using TF-IDF and BM25 ranking." />
7+
<title>DocSense</title>
8+
<link rel="preconnect" href="https://fonts.googleapis.com" />
9+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet" />
11+
<style>
12+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
13+
14+
:root {
15+
--bg: #0c0c10;
16+
--surface: #13131a;
17+
--border: rgba(255,255,255,0.07);
18+
--border-focus: rgba(139,92,246,0.5);
19+
--accent: #8b5cf6;
20+
--accent-dim: rgba(139,92,246,0.15);
21+
--text: #e2e2e9;
22+
--muted: #6b6b7a;
23+
--danger: #f87171;
24+
--radius: 14px;
25+
--font: 'Inter', system-ui, sans-serif;
26+
}
27+
28+
html, body {
29+
height: 100%;
30+
background: var(--bg);
31+
color: var(--text);
32+
font-family: var(--font);
33+
font-size: 15px;
34+
line-height: 1.5;
35+
-webkit-font-smoothing: antialiased;
36+
}
37+
38+
/* ── Layout ── */
39+
.page {
40+
min-height: 100vh;
41+
display: grid;
42+
grid-template-rows: 1fr auto;
43+
padding: 0 24px;
44+
}
45+
46+
.main {
47+
display: flex;
48+
flex-direction: column;
49+
align-items: center;
50+
padding-top: min(18vh, 140px);
51+
width: 100%;
52+
}
53+
54+
/* ── Wordmark ── */
55+
.wordmark {
56+
display: flex;
57+
align-items: center;
58+
gap: 10px;
59+
margin-bottom: 40px;
60+
}
61+
62+
.wordmark-icon {
63+
width: 36px;
64+
height: 36px;
65+
background: linear-gradient(135deg, #8b5cf6, #6366f1);
66+
border-radius: 9px;
67+
display: grid;
68+
place-items: center;
69+
flex-shrink: 0;
70+
}
71+
72+
.wordmark-icon svg { display: block; }
73+
74+
.wordmark-name {
75+
font-size: 22px;
76+
font-weight: 600;
77+
letter-spacing: -0.5px;
78+
color: var(--text);
79+
}
80+
81+
.wordmark-tag {
82+
font-size: 12px;
83+
font-weight: 400;
84+
color: var(--muted);
85+
letter-spacing: 0.02em;
86+
}
87+
88+
/* ── Search box ── */
89+
.search-wrap {
90+
position: relative;
91+
width: 100%;
92+
max-width: 640px;
93+
}
94+
95+
.search-icon {
96+
position: absolute;
97+
left: 18px;
98+
top: 50%;
99+
transform: translateY(-50%);
100+
color: var(--muted);
101+
pointer-events: none;
102+
transition: color 0.2s;
103+
}
104+
105+
#query {
106+
width: 100%;
107+
height: 56px;
108+
padding: 0 52px 0 50px;
109+
background: var(--surface);
110+
border: 1px solid var(--border);
111+
border-radius: var(--radius);
112+
color: var(--text);
113+
font-family: var(--font);
114+
font-size: 15px;
115+
font-weight: 400;
116+
outline: none;
117+
transition: border-color 0.2s, box-shadow 0.2s;
118+
-webkit-appearance: none;
119+
}
120+
121+
#query::placeholder { color: var(--muted); }
122+
123+
#query:focus {
124+
border-color: var(--border-focus);
125+
box-shadow: 0 0 0 3px rgba(139,92,246,0.12), 0 1px 2px rgba(0,0,0,0.4);
126+
}
127+
128+
#query:focus + .search-icon-right,
129+
#query:focus ~ .search-icon { color: var(--accent); }
130+
131+
.search-hint {
132+
position: absolute;
133+
right: 16px;
134+
top: 50%;
135+
transform: translateY(-50%);
136+
display: flex;
137+
align-items: center;
138+
gap: 4px;
139+
pointer-events: none;
140+
opacity: 1;
141+
transition: opacity 0.15s;
142+
}
143+
144+
.search-hint.hidden { opacity: 0; }
145+
146+
.kbd {
147+
display: inline-flex;
148+
align-items: center;
149+
justify-content: center;
150+
height: 22px;
151+
padding: 0 6px;
152+
border: 1px solid var(--border);
153+
border-radius: 5px;
154+
background: rgba(255,255,255,0.04);
155+
color: var(--muted);
156+
font-family: var(--font);
157+
font-size: 11px;
158+
font-weight: 500;
159+
line-height: 1;
160+
}
161+
162+
/* ── Spinner ── */
163+
.spinner {
164+
position: absolute;
165+
right: 18px;
166+
top: 50%;
167+
transform: translateY(-50%);
168+
width: 18px;
169+
height: 18px;
170+
border: 2px solid var(--border);
171+
border-top-color: var(--accent);
172+
border-radius: 50%;
173+
display: none;
174+
animation: spin 0.65s linear infinite;
175+
}
176+
.spinner.active { display: block; }
177+
@keyframes spin { to { transform: translateY(-50%) rotate(360deg); } }
178+
179+
/* ── Results ── */
180+
#results {
181+
width: 100%;
182+
max-width: 640px;
183+
margin-top: 12px;
184+
}
185+
186+
.results-header {
187+
display: flex;
188+
align-items: center;
189+
justify-content: space-between;
190+
padding: 10px 4px 8px;
191+
font-size: 12px;
192+
color: var(--muted);
193+
font-weight: 500;
194+
letter-spacing: 0.03em;
195+
}
196+
197+
.result-list { display: flex; flex-direction: column; gap: 2px; }
198+
199+
.result-item {
200+
display: flex;
201+
align-items: center;
202+
gap: 12px;
203+
padding: 11px 14px;
204+
background: var(--surface);
205+
border: 1px solid var(--border);
206+
border-radius: 10px;
207+
text-decoration: none;
208+
color: var(--text);
209+
transition: background 0.15s, border-color 0.15s, transform 0.1s;
210+
animation: fadeUp 0.18s ease both;
211+
}
212+
213+
.result-item:hover {
214+
background: rgba(139,92,246,0.07);
215+
border-color: rgba(139,92,246,0.25);
216+
transform: translateY(-1px);
217+
}
218+
219+
@keyframes fadeUp {
220+
from { opacity: 0; transform: translateY(6px); }
221+
to { opacity: 1; transform: translateY(0); }
222+
}
223+
224+
.result-item:nth-child(2) { animation-delay: 0.03s; }
225+
.result-item:nth-child(3) { animation-delay: 0.06s; }
226+
.result-item:nth-child(4) { animation-delay: 0.09s; }
227+
.result-item:nth-child(5) { animation-delay: 0.12s; }
228+
.result-item:nth-child(6) { animation-delay: 0.15s; }
229+
.result-item:nth-child(7) { animation-delay: 0.18s; }
230+
.result-item:nth-child(8) { animation-delay: 0.21s; }
231+
.result-item:nth-child(9) { animation-delay: 0.24s; }
232+
.result-item:nth-child(10) { animation-delay: 0.27s; }
233+
234+
.ext-badge {
235+
flex-shrink: 0;
236+
width: 36px;
237+
height: 36px;
238+
border-radius: 8px;
239+
display: grid;
240+
place-items: center;
241+
font-size: 10px;
242+
font-weight: 600;
243+
letter-spacing: 0.04em;
244+
text-transform: uppercase;
245+
}
246+
247+
.ext-xml, .ext-xhtml { background: rgba(251,146,60,0.12); color: #fb923c; }
248+
.ext-pdf { background: rgba(248,113,113,0.12); color: #f87171; }
249+
.ext-txt, .ext-md { background: rgba(139,92,246,0.12); color: #a78bfa; }
250+
.ext-default { background: rgba(107,107,122,0.15); color: var(--muted); }
251+
252+
.result-body { flex: 1; min-width: 0; }
253+
254+
.result-filename {
255+
font-size: 14px;
256+
font-weight: 500;
257+
color: var(--text);
258+
white-space: nowrap;
259+
overflow: hidden;
260+
text-overflow: ellipsis;
261+
}
262+
263+
.result-path {
264+
font-size: 12px;
265+
color: var(--muted);
266+
white-space: nowrap;
267+
overflow: hidden;
268+
text-overflow: ellipsis;
269+
margin-top: 1px;
270+
}
271+
272+
.result-rank {
273+
flex-shrink: 0;
274+
font-size: 12px;
275+
color: var(--muted);
276+
font-variant-numeric: tabular-nums;
277+
font-weight: 500;
278+
}
279+
280+
/* ── Empty / Error states ── */
281+
.state-msg {
282+
display: flex;
283+
flex-direction: column;
284+
align-items: center;
285+
gap: 8px;
286+
padding: 40px 0 20px;
287+
color: var(--muted);
288+
font-size: 14px;
289+
text-align: center;
290+
animation: fadeUp 0.2s ease;
291+
}
292+
293+
.state-msg svg { opacity: 0.4; }
294+
.state-msg.error { color: var(--danger); }
295+
296+
/* ── Stats footer ── */
297+
footer {
298+
padding: 20px 0;
299+
text-align: center;
300+
font-size: 12px;
301+
color: var(--muted);
302+
display: flex;
303+
align-items: center;
304+
justify-content: center;
305+
gap: 16px;
306+
}
307+
308+
.stat-item { display: flex; align-items: center; gap: 5px; }
309+
.stat-dot {
310+
width: 6px; height: 6px;
311+
border-radius: 50%;
312+
background: var(--accent);
313+
opacity: 0.6;
314+
}
315+
</style>
7316
</head>
8317
<body>
9-
<center>
10-
<h1><b>DocSense</b></h1>
11-
<h2>Query: </h2>
12-
<input type="text" id="query"/>
13-
<div id="results"></div>
14-
</center>
318+
<div class="page">
319+
<div class="main">
320+
<div class="wordmark">
321+
<div class="wordmark-icon">
322+
<!-- doc + magnifier icon -->
323+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
324+
<path d="M5 3h7l4 4v10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1Z" stroke="rgba(255,255,255,0.9)" stroke-width="1.4" stroke-linejoin="round"/>
325+
<path d="M12 3v4h4" stroke="rgba(255,255,255,0.9)" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
326+
<circle cx="9.5" cy="11.5" r="2" stroke="rgba(255,255,255,0.9)" stroke-width="1.4"/>
327+
<path d="M11 13l1.5 1.5" stroke="rgba(255,255,255,0.9)" stroke-width="1.4" stroke-linecap="round"/>
328+
</svg>
329+
</div>
330+
<div>
331+
<div class="wordmark-name">DocSense</div>
332+
<div class="wordmark-tag">Local document search</div>
333+
</div>
334+
</div>
335+
336+
<div class="search-wrap">
337+
<!-- search icon -->
338+
<svg class="search-icon" width="18" height="18" viewBox="0 0 18 18" fill="none">
339+
<circle cx="8" cy="8" r="5.5" stroke="currentColor" stroke-width="1.5"/>
340+
<path d="M12.5 12.5L16 16" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
341+
</svg>
342+
343+
<input
344+
id="query"
345+
type="text"
346+
placeholder="Search your documents…"
347+
autocomplete="off"
348+
spellcheck="false"
349+
aria-label="Search query"
350+
/>
351+
352+
<div class="search-hint" id="hint">
353+
<span class="kbd"></span>
354+
</div>
355+
<div class="spinner" id="spinner"></div>
356+
</div>
357+
358+
<div id="results" aria-live="polite" aria-label="Search results"></div>
359+
</div>
360+
361+
<footer id="stats">
362+
<span class="stat-item">
363+
<span class="stat-dot"></span>
364+
<span id="stat-docs"></span> documents indexed
365+
</span>
366+
<span class="stat-item">
367+
<span class="stat-dot"></span>
368+
<span id="stat-terms"></span> unique terms
369+
</span>
370+
</footer>
371+
</div>
15372

373+
<script src="index.js"></script>
16374
</body>
17-
<script src="index.js"></script>
18375
</html>

0 commit comments

Comments
 (0)