-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
118 lines (101 loc) · 4.15 KB
/
Copy pathindex.html
File metadata and controls
118 lines (101 loc) · 4.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, viewport-fit=cover"
/>
<title>Private Doc Chat — On-Device RAG (MVP)</title>
<link rel="stylesheet" href="./styles.css" />
</head>
<body>
<header class="app-header">
<h1>Private Doc Chat — On-Device RAG</h1>
<div class="badge">100% Client-Side</div>
</header>
<main class="container" id="app-root" aria-busy="false">
<!-- Controls -->
<section class="panel controls" aria-label="Controls">
<div class="control-row">
<label class="file-button" for="pdf-input" tabindex="0" aria-label="Upload PDF">
📄 Upload PDF
</label>
<input id="pdf-input" type="file" accept="application/pdf" />
<button id="clear-data" class="danger" aria-label="Clear local data">🧹 Clear Data</button>
</div>
<div class="control-row wrap">
<label for="topk">Top-k</label>
<select id="topk" aria-label="Top-k retrieved chunks">
<option>3</option>
<option>4</option>
<option>5</option>
<option selected>6</option>
<option>7</option>
<option>8</option>
</select>
<label for="maxTokens">Max answer length</label>
<input id="maxTokens" type="range" min="64" max="512" step="32" value="256" />
<span id="maxTokensValue" aria-live="polite">256</span>
<!-- NEW: Strict mode -->
<label for="strictMode" style="display:inline-flex;align-items:center;gap:6px">
<input id="strictMode" type="checkbox" />
Strict (quote-only)
</label>
<label for="question" class="sr-only">Ask a question</label>
<input id="question" class="ask" type="text" placeholder="Ask a question about your PDF..." />
<button id="ask" class="primary" disabled aria-disabled="true">Ask</button>
</div>
</section>
<!-- Status / model loading -->
<section class="panel status" aria-live="polite" aria-atomic="true">
<div><strong>WebGPU:</strong> <span id="webgpu-status">Checking…</span></div>
<div><strong>Transformers.js:</strong> <span id="xenova-status">Loading…</span></div>
<div><strong>PDF.js:</strong> <span id="pdfjs-status">Loading…</span></div>
<div><strong>localForage:</strong> <span id="lf-status">Loading…</span></div>
<div class="model-load">
<button id="load-model" class="secondary">⬇️ Load Tiny Model</button>
<div id="model-progress-wrap" class="progress-wrap hidden" aria-hidden="true">
<div id="model-progress" class="progress-bar" style="width:0%"></div>
</div>
<div id="model-status" class="muted">Model not loaded</div>
</div>
</section>
<!-- Answer -->
<section class="panel" aria-label="Answer">
<h2>Answer</h2>
<div id="answer" class="answer"></div>
</section>
<!-- Sources -->
<section class="panel" aria-label="Sources">
<h2>Sources</h2>
<div id="sources" class="sources"></div>
</section>
<!-- Ingest log -->
<section class="panel" aria-label="Ingest Log">
<h2>Ingest</h2>
<div id="ingest-log" class="log"></div>
</section>
</main>
<!-- Busy overlay (shown during long tasks) -->
<div id="busy-overlay" class="busy hidden" aria-hidden="true" aria-label="Working">
<div class="spinner" role="status" aria-live="assertive" aria-label="Loading"></div>
<div class="busy-text">Working…</div>
</div>
<footer class="app-footer">
<span>Privacy: All compute stays in your browser. Use “Clear Data” any time.</span>
</footer>
<!-- localForage (IndexedDB helper) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/localforage/1.10.0/localforage.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- Our app (ES module) -->
<script type="module" src="./app.js"></script>
<!-- Register offline Service Worker -->
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('./sw.js').catch(() => {});
});
}
</script>
</body>
</html>