triflix commited on
Commit
d957247
·
verified ·
1 Parent(s): 4525bd8

Create templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +191 -0
templates/index.html ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- templates/index.html -->
2
+ <!doctype html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"/>
7
+ <title>ICIS Mini-Hub — Mobile</title>
8
+ <style>
9
+ /* Mobile-first simple UI */
10
+ :root{--bg:#0f172a;--card:#0b1220;--accent:#06b6d4;--muted:#94a3b8;--text:#e6eef8}
11
+ *{box-sizing:border-box}
12
+ html,body{height:100%;margin:0;background:linear-gradient(180deg,var(--bg),#071024);font-family:Inter,system-ui,Segoe UI,Roboto,"Helvetica Neue",Arial}
13
+ .app{max-width:480px;margin:0 auto;height:100vh;display:flex;flex-direction:column;gap:8px;padding:12px}
14
+ header{display:flex;align-items:center;gap:10px}
15
+ .brand{color:var(--text);font-weight:700;font-size:18px}
16
+ .sub{color:var(--muted);font-size:12px}
17
+ .chat-window{flex:1;background:transparent;padding:8px;overflow:auto;display:flex;flex-direction:column;gap:8px}
18
+ .bubble{max-width:86%;padding:10px 12px;border-radius:12px;color:var(--text);line-height:1.3}
19
+ .user{align-self:flex-end;background:linear-gradient(90deg,#0ea5a4,#06b6d4);border-bottom-right-radius:4px}
20
+ .bot{align-self:flex-start;background:var(--card);border-bottom-left-radius:4px;color:var(--text);box-shadow:0 2px 8px rgba(2,6,23,.6)}
21
+ .meta{font-size:11px;color:var(--muted);margin-top:6px}
22
+ .composer{display:flex;gap:8px;align-items:center;padding:8px;background:transparent}
23
+ .input{flex:1;display:flex;gap:8px;align-items:center;background:rgba(255,255,255,0.03);padding:8px;border-radius:999px}
24
+ input[type="text"]{flex:1;background:transparent;border:0;color:var(--text);outline:none;padding:6px 4px;font-size:15px}
25
+ .icon-btn{width:40px;height:40px;border-radius:10px;border:0;background:transparent;color:var(--muted);display:flex;align-items:center;justify-content:center;cursor:pointer}
26
+ .icon-btn[disabled]{opacity:0.35;cursor:not-allowed}
27
+ .send{background:var(--accent);color:#002; padding:8px 12px;border-radius:12px;border:0;font-weight:700}
28
+ .small{font-size:12px;color:var(--muted)}
29
+ footer.small{text-align:center;padding:6px 0;color:var(--muted);font-size:12px}
30
+ /* simple responsive tweaks */
31
+ @media(min-width:520px){.app{margin-top:30px;border-radius:12px;box-shadow:0 10px 30px rgba(2,6,23,.6);padding:16px}}
32
+ </style>
33
+ </head>
34
+ <body>
35
+ <div class="app" role="application">
36
+ <header>
37
+ <div>
38
+ <div class="brand">ICIS Mini-Hub</div>
39
+ <div class="sub">Mobile-first chat • image/pdf single-attachment</div>
40
+ </div>
41
+ </header>
42
+
43
+ <main class="chat-window" id="chatWindow" aria-live="polite">
44
+ <!-- messages will appear here -->
45
+ <div class="meta small">Function used will show with chat replies. Image/PDF produce direct concise answers.</div>
46
+ </main>
47
+
48
+ <div class="composer" aria-label="Composer">
49
+ <div class="input" id="composer">
50
+ <button class="icon-btn" id="imgBtn" title="Attach image">🖼️</button>
51
+ <button class="icon-btn" id="pdfBtn" title="Attach PDF">📄</button>
52
+
53
+ <input type="file" id="imgInput" accept="image/*" style="display:none">
54
+ <input type="file" id="pdfInput" accept="application/pdf" style="display:none">
55
+
56
+ <input type="text" id="prompt" placeholder="Type a message or add prompt..." aria-label="Message">
57
+ </div>
58
+
59
+ <button class="send" id="sendBtn">Send</button>
60
+ </div>
61
+
62
+ <footer class="small">Only one file at a time. Attaching image disables PDF and vice versa.</footer>
63
+ </div>
64
+
65
+ <script>
66
+ // UI behavior: single attachment rule
67
+ const imgBtn = document.getElementById('imgBtn');
68
+ const pdfBtn = document.getElementById('pdfBtn');
69
+ const imgInput = document.getElementById('imgInput');
70
+ const pdfInput = document.getElementById('pdfInput');
71
+ const sendBtn = document.getElementById('sendBtn');
72
+ const promptIn = document.getElementById('prompt');
73
+ const chatWindow = document.getElementById('chatWindow');
74
+
75
+ let attachedFile = null; // {type: 'image'|'pdf', file: File}
76
+
77
+ function renderBubble(text, who='bot', meta='') {
78
+ const b = document.createElement('div');
79
+ b.className = 'bubble ' + (who==='user' ? 'user' : 'bot');
80
+ b.innerText = text;
81
+ chatWindow.appendChild(b);
82
+ if(meta){
83
+ const m = document.createElement('div');
84
+ m.className = 'meta small';
85
+ m.innerText = meta;
86
+ chatWindow.appendChild(m);
87
+ }
88
+ chatWindow.scrollTop = chatWindow.scrollHeight;
89
+ }
90
+
91
+ imgBtn.addEventListener('click', ()=> imgInput.click());
92
+ pdfBtn.addEventListener('click', ()=> pdfInput.click());
93
+
94
+ imgInput.addEventListener('change', (e)=>{
95
+ const f = e.target.files[0];
96
+ if(!f) return;
97
+ attachedFile = {type:'image', file:f};
98
+ // disable pdf btn
99
+ pdfBtn.disabled = true;
100
+ imgBtn.style.opacity = '0.9';
101
+ renderBubble('[Image attached: ' + f.name + ']', 'user');
102
+ });
103
+
104
+ pdfInput.addEventListener('change', (e)=>{
105
+ const f = e.target.files[0];
106
+ if(!f) return;
107
+ attachedFile = {type:'pdf', file:f};
108
+ imgBtn.disabled = true;
109
+ pdfBtn.style.opacity = '0.9';
110
+ renderBubble('[PDF attached: ' + f.name + ']', 'user');
111
+ });
112
+
113
+ // remove attachment if user clears input or selects new
114
+ function clearAttachment(){
115
+ attachedFile = null;
116
+ imgInput.value = '';
117
+ pdfInput.value = '';
118
+ imgBtn.disabled = false;
119
+ pdfBtn.disabled = false;
120
+ imgBtn.style.opacity = '1';
121
+ pdfBtn.style.opacity = '1';
122
+ }
123
+
124
+ // helper to post JSON
125
+ async function postJSON(url, body){
126
+ const resp = await fetch(url, {
127
+ method:'POST',
128
+ headers: {'Content-Type':'application/json'},
129
+ body: JSON.stringify(body)
130
+ });
131
+ return resp.json();
132
+ }
133
+
134
+ // helper to post multipart
135
+ async function postForm(url, file, prompt){
136
+ const fd = new FormData();
137
+ fd.append('file', file);
138
+ fd.append('prompt', prompt || '');
139
+ const resp = await fetch(url, {method:'POST', body:fd});
140
+ return resp.json();
141
+ }
142
+
143
+ sendBtn.addEventListener('click', async ()=>{
144
+ const text = promptIn.value.trim();
145
+ if(!text && !attachedFile){
146
+ // nothing to do
147
+ return;
148
+ }
149
+
150
+ // show user message
151
+ if(text) renderBubble(text, 'user');
152
+ // if file attached show small indicator already added at attachment time
153
+
154
+ promptIn.value = '';
155
+
156
+ try {
157
+ let result = null;
158
+ if(attachedFile){
159
+ if(attachedFile.type === 'image'){
160
+ result = await postForm('/analyze_image', attachedFile.file, text);
161
+ // show direct answer (plain & concise)
162
+ renderBubble(result.response || result.error || 'No response', 'bot', 'Direct (image)');
163
+ } else if(attachedFile.type === 'pdf'){
164
+ result = await postForm('/summarize_pdf', attachedFile.file, text);
165
+ renderBubble(result.response || result.error || 'No response', 'bot', 'Direct (pdf)');
166
+ }
167
+ // after handling, clear attachment
168
+ clearAttachment();
169
+ } else {
170
+ // normal chat
171
+ const json = await postJSON('/chat', {query: text});
172
+ const func = json.function_used || 'chat';
173
+ const resp = json.response || json.error || 'No response';
174
+ renderBubble(resp, 'bot', 'Function: ' + func);
175
+ }
176
+ } catch (err) {
177
+ renderBubble('Network or server error', 'bot');
178
+ console.error(err);
179
+ }
180
+ });
181
+
182
+ // allow Enter to send
183
+ promptIn.addEventListener('keydown', (e)=>{
184
+ if(e.key === 'Enter' && !e.shiftKey){
185
+ e.preventDefault();
186
+ sendBtn.click();
187
+ }
188
+ });
189
+ </script>
190
+ </body>
191
+ </html>