File size: 7,487 Bytes
d957247
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
<!-- templates/index.html -->
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"/>
  <title>ICIS Mini-Hub — Mobile</title>
  <style>
    /* Mobile-first simple UI */
    :root{--bg:#0f172a;--card:#0b1220;--accent:#06b6d4;--muted:#94a3b8;--text:#e6eef8}
    *{box-sizing:border-box}
    html,body{height:100%;margin:0;background:linear-gradient(180deg,var(--bg),#071024);font-family:Inter,system-ui,Segoe UI,Roboto,"Helvetica Neue",Arial}
    .app{max-width:480px;margin:0 auto;height:100vh;display:flex;flex-direction:column;gap:8px;padding:12px}
    header{display:flex;align-items:center;gap:10px}
    .brand{color:var(--text);font-weight:700;font-size:18px}
    .sub{color:var(--muted);font-size:12px}
    .chat-window{flex:1;background:transparent;padding:8px;overflow:auto;display:flex;flex-direction:column;gap:8px}
    .bubble{max-width:86%;padding:10px 12px;border-radius:12px;color:var(--text);line-height:1.3}
    .user{align-self:flex-end;background:linear-gradient(90deg,#0ea5a4,#06b6d4);border-bottom-right-radius:4px}
    .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)}
    .meta{font-size:11px;color:var(--muted);margin-top:6px}
    .composer{display:flex;gap:8px;align-items:center;padding:8px;background:transparent}
    .input{flex:1;display:flex;gap:8px;align-items:center;background:rgba(255,255,255,0.03);padding:8px;border-radius:999px}
    input[type="text"]{flex:1;background:transparent;border:0;color:var(--text);outline:none;padding:6px 4px;font-size:15px}
    .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}
    .icon-btn[disabled]{opacity:0.35;cursor:not-allowed}
    .send{background:var(--accent);color:#002; padding:8px 12px;border-radius:12px;border:0;font-weight:700}
    .small{font-size:12px;color:var(--muted)}
    footer.small{text-align:center;padding:6px 0;color:var(--muted);font-size:12px}
    /* simple responsive tweaks */
    @media(min-width:520px){.app{margin-top:30px;border-radius:12px;box-shadow:0 10px 30px rgba(2,6,23,.6);padding:16px}}
  </style>
</head>
<body>
  <div class="app" role="application">
    <header>
      <div>
        <div class="brand">ICIS Mini-Hub</div>
        <div class="sub">Mobile-first chat • image/pdf single-attachment</div>
      </div>
    </header>

    <main class="chat-window" id="chatWindow" aria-live="polite">
      <!-- messages will appear here -->
      <div class="meta small">Function used will show with chat replies. Image/PDF produce direct concise answers.</div>
    </main>

    <div class="composer" aria-label="Composer">
      <div class="input" id="composer">
        <button class="icon-btn" id="imgBtn" title="Attach image">🖼️</button>
        <button class="icon-btn" id="pdfBtn" title="Attach PDF">📄</button>

        <input type="file" id="imgInput" accept="image/*" style="display:none">
        <input type="file" id="pdfInput" accept="application/pdf" style="display:none">

        <input type="text" id="prompt" placeholder="Type a message or add prompt..." aria-label="Message">
      </div>

      <button class="send" id="sendBtn">Send</button>
    </div>

    <footer class="small">Only one file at a time. Attaching image disables PDF and vice versa.</footer>
  </div>

<script>
  // UI behavior: single attachment rule
  const imgBtn = document.getElementById('imgBtn');
  const pdfBtn = document.getElementById('pdfBtn');
  const imgInput = document.getElementById('imgInput');
  const pdfInput = document.getElementById('pdfInput');
  const sendBtn = document.getElementById('sendBtn');
  const promptIn = document.getElementById('prompt');
  const chatWindow = document.getElementById('chatWindow');

  let attachedFile = null; // {type: 'image'|'pdf', file: File}

  function renderBubble(text, who='bot', meta='') {
    const b = document.createElement('div');
    b.className = 'bubble ' + (who==='user' ? 'user' : 'bot');
    b.innerText = text;
    chatWindow.appendChild(b);
    if(meta){
      const m = document.createElement('div');
      m.className = 'meta small';
      m.innerText = meta;
      chatWindow.appendChild(m);
    }
    chatWindow.scrollTop = chatWindow.scrollHeight;
  }

  imgBtn.addEventListener('click', ()=> imgInput.click());
  pdfBtn.addEventListener('click', ()=> pdfInput.click());

  imgInput.addEventListener('change', (e)=>{
    const f = e.target.files[0];
    if(!f) return;
    attachedFile = {type:'image', file:f};
    // disable pdf btn
    pdfBtn.disabled = true;
    imgBtn.style.opacity = '0.9';
    renderBubble('[Image attached: ' + f.name + ']', 'user');
  });

  pdfInput.addEventListener('change', (e)=>{
    const f = e.target.files[0];
    if(!f) return;
    attachedFile = {type:'pdf', file:f};
    imgBtn.disabled = true;
    pdfBtn.style.opacity = '0.9';
    renderBubble('[PDF attached: ' + f.name + ']', 'user');
  });

  // remove attachment if user clears input or selects new
  function clearAttachment(){
    attachedFile = null;
    imgInput.value = '';
    pdfInput.value = '';
    imgBtn.disabled = false;
    pdfBtn.disabled = false;
    imgBtn.style.opacity = '1';
    pdfBtn.style.opacity = '1';
  }

  // helper to post JSON
  async function postJSON(url, body){
    const resp = await fetch(url, {
      method:'POST',
      headers: {'Content-Type':'application/json'},
      body: JSON.stringify(body)
    });
    return resp.json();
  }

  // helper to post multipart
  async function postForm(url, file, prompt){
    const fd = new FormData();
    fd.append('file', file);
    fd.append('prompt', prompt || '');
    const resp = await fetch(url, {method:'POST', body:fd});
    return resp.json();
  }

  sendBtn.addEventListener('click', async ()=>{
    const text = promptIn.value.trim();
    if(!text && !attachedFile){
      // nothing to do
      return;
    }

    // show user message
    if(text) renderBubble(text, 'user');
    // if file attached show small indicator already added at attachment time

    promptIn.value = '';

    try {
      let result = null;
      if(attachedFile){
        if(attachedFile.type === 'image'){
          result = await postForm('/analyze_image', attachedFile.file, text);
          // show direct answer (plain & concise)
          renderBubble(result.response || result.error || 'No response', 'bot', 'Direct (image)');
        } else if(attachedFile.type === 'pdf'){
          result = await postForm('/summarize_pdf', attachedFile.file, text);
          renderBubble(result.response || result.error || 'No response', 'bot', 'Direct (pdf)');
        }
        // after handling, clear attachment
        clearAttachment();
      } else {
        // normal chat
        const json = await postJSON('/chat', {query: text});
        const func = json.function_used || 'chat';
        const resp = json.response || json.error || 'No response';
        renderBubble(resp, 'bot', 'Function: ' + func);
      }
    } catch (err) {
      renderBubble('Network or server error', 'bot');
      console.error(err);
    }
  });

  // allow Enter to send
  promptIn.addEventListener('keydown', (e)=>{
    if(e.key === 'Enter' && !e.shiftKey){
      e.preventDefault();
      sendBtn.click();
    }
  });
</script>
</body>
</html>