Vedika commited on
Commit
6c86393
·
verified ·
1 Parent(s): 195ec86

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +563 -593
index.html CHANGED
@@ -1,645 +1,615 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
- <title>🇮🇳 TIRANGA - Advanced AI</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com">
8
- <link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@300;400;500;600;700&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet">
9
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css">
10
- <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
11
- <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
12
- <style>
13
- /* --- BASE RESET & VARIABLES --- */
14
- *{margin:0;padding:0;box-sizing:border-box;}
15
- :root{
16
- --bg-color: #ffffff; --bg-secondary: #f9f9fb; --text-main: #1d1d1f; --text-muted: #86868b;
17
- --accent: #FF9933; --accent-green: #138808; --accent-hover: #e68a2e;
18
- --border-light: rgba(0, 0, 0, 0.08); --border-focus: rgba(255, 153, 51, 0.3);
19
- --glass-bg: rgba(255, 255, 255, 0.85); --glass-border: rgba(255, 255, 255, 0.4);
20
- --shadow-sm: 0 2px 8px rgba(0,0,0,0.04); --shadow-md: 0 8px 32px rgba(0,0,0,0.08);
21
- --shadow-lg: 0 16px 48px rgba(0,0,0,0.12);
22
- --font-serif: 'Instrument Serif', serif; --font-sans: 'Inter', -apple-system, sans-serif;
23
- --font-mono: 'Geist Mono', monospace;
24
- --radius-lg: 24px; --radius-md: 16px; --radius-sm: 8px;
25
- }
26
- html,body{height:100%;overflow:hidden;background:var(--bg-color);color:var(--text-main);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;}
27
-
28
- /* --- TIRANGA BACKGROUND --- */
29
- .bg{position:fixed;inset:0;z-index:0;overflow:hidden;pointer-events:none;background: linear-gradient(135deg, rgba(255,153,51,0.05) 0%, rgba(255,255,255,1) 50%, rgba(19,136,8,0.05) 100%);}
30
- .orb{position:absolute;border-radius:50%;filter:blur(100px);opacity:0.5;animation:drift 25s ease-in-out infinite alternate;}
31
- .orb1{width:600px;height:600px;background:radial-gradient(circle,rgba(255,153,51,0.2),transparent 70%);top:-150px;left:-100px;}
32
- .orb2{width:500px;height:500px;background:radial-gradient(circle,rgba(19,136,8,0.18),transparent 70%);bottom:-100px;right:-100px;animation-delay:-10s;}
33
- @keyframes drift{0%{transform:translate(0,0) scale(1);}100%{transform:translate(60px,40px) scale(1.05);}}
34
-
35
- /* --- MAIN LAYOUT (CHAT + WORKSPACE) --- */
36
- .shell{position:relative;z-index:1;display:flex;height:100vh;width:100vw;overflow:hidden;}
37
- .sidebar{width:280px;display:flex;flex-direction:column;background:var(--bg-secondary);border-right:1px solid var(--border-light);flex-shrink:0;z-index:30;}
38
- .main-wrapper{flex:1;display:flex;position:relative;overflow:hidden;}
39
- .chat-main{flex:1;display:flex;flex-direction:column;background:transparent;position:relative;min-width:300px;transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);}
40
- .workspace{width:0;background:#fafafa;border-left:1px solid var(--border-light);display:flex;flex-direction:column;transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);overflow:hidden;box-shadow:-10px 0 30px rgba(0,0,0,0.02);z-index:25;}
41
- .workspace.open{width:50%;}
42
-
43
- /* --- SIDEBAR & BRANDING --- */
44
- .logo-area{padding:24px 20px;border-bottom:1px solid var(--border-light);}
45
- .logo-row{display:flex;align-items:center;gap:12px;}
46
- .logo-img{width:44px;height:44px;border-radius:12px;object-fit:cover;box-shadow:var(--shadow-sm);border:1px solid var(--border-light);background:#fff;}
47
- .logo-text{line-height:1.2;}
48
- .logo-name{font-family:var(--font-sans);font-weight:800;font-size:18px;color:var(--text-main);letter-spacing:-0.5px;}
49
- .logo-sub{font-size:10px;font-weight:600;letter-spacing:1px;text-transform:uppercase;color:var(--text-muted);}
50
- .settings{padding:20px;flex:1;overflow-y:auto;display:flex;flex-direction:column;gap: 20px;}
51
- .settings::-webkit-scrollbar{width:4px;}
52
- .settings::-webkit-scrollbar-thumb{background:rgba(0,0,0,0.1);border-radius:10px;}
53
-
54
- /* Custom Toggle Switch for Thinking */
55
- .toggle-wrapper {display: flex; align-items: center; justify-content: space-between; background: #fff; padding: 16px; border-radius: 12px; border: 1px solid var(--border-light); box-shadow: var(--shadow-sm);}
56
- .toggle-label {font-size: 13px; font-weight: 600; color: var(--text-main); display: flex; align-items: center; gap: 8px;}
57
- .toggle-label span {font-size: 11px; color: var(--text-muted); font-weight: 500; display: block; margin-top: 2px;}
58
- .switch {position: relative; display: inline-block; width: 44px; height: 24px;}
59
- .switch input {opacity: 0; width: 0; height: 0;}
60
- .slider {position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #e5e5ea; transition: .4s; border-radius: 24px;}
61
- .slider:before {position: absolute; content: ""; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: .4s; border-radius: 50%; box-shadow: 0 2px 4px rgba(0,0,0,0.2);}
62
- input:checked + .slider {background-color: var(--accent-green);}
63
- input:checked + .slider:before {transform: translateX(20px);}
64
-
65
- .brand-box {background: #ffffff;border: 1px solid var(--border-light);border-radius: var(--radius-md);padding: 24px 16px;text-align: center;box-shadow: var(--shadow-sm);margin-top:auto;}
66
- .brand-box img {width: 70px; height: 70px; border-radius: 16px; object-fit: cover; margin-bottom: 12px; box-shadow: var(--shadow-sm); border: 1px solid var(--border-light);}
67
- .brand-box h3 {font-family: var(--font-sans); font-size: 15px; font-weight: 700; color: var(--text-main); margin-bottom: 6px;}
68
- .brand-box p {font-size: 10px; font-weight: 600; color: var(--accent-green); line-height: 1.4; letter-spacing: 0.5px;}
69
-
70
- .clear-btn{width:100%;padding:12px;border-radius:var(--radius-sm);background:#ffffff;border:1px solid var(--border-light);color:var(--text-main);font-size:13px;font-weight:600;cursor:pointer;transition:all 0.2s;}
71
- .clear-btn:hover{background:#fff0f0;color:#ff3b30;border-color:#ff3b30;}
72
-
73
- /* --- CHAT AREA --- */
74
- .chat-hdr{padding:14px 24px;border-bottom:1px solid var(--border-light);background:var(--glass-bg);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);display:flex;align-items:center;justify-content:space-between;z-index:10;}
75
- .model-badge{font-size:13px;font-weight:600;display:flex;align-items:center;gap:8px;}
76
- .status-dot{width:8px;height:8px;border-radius:50%;background:#34c759;box-shadow:0 0 0 2px rgba(52,199,89,0.2);}
77
- .messages{flex:1;overflow-y:auto;padding:30px 40px;display:flex;flex-direction:column;gap:24px;scroll-behavior:smooth;position:relative;}
78
- .messages::-webkit-scrollbar{width:6px;}
79
- .messages::-webkit-scrollbar-thumb{background:rgba(0,0,0,0.1);border-radius:10px;}
80
-
81
- /* Welcome Screen & Prompts */
82
- .welcome{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:40px 20px;margin:auto;animation:fadeInUp 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;width:100%;max-width:650px;}
83
- @keyframes fadeInUp{from{opacity:0;transform:translateY(20px);}to{opacity:1;transform:none;}}
84
- .welcome-logo{width:80px;height:80px;border-radius:24px;object-fit:cover;box-shadow:var(--shadow-md);margin-bottom:24px;border:1px solid var(--border-light);}
85
- .welcome-title{font-size:32px;font-weight:800;letter-spacing:-1px;margin-bottom:12px;}
86
- .welcome-sub{font-size:15px;color:var(--text-muted);line-height:1.6;margin-bottom:32px;max-width: 500px;}
87
- .ex-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px;width:100%;}
88
- .ex-card{background:var(--glass-bg);backdrop-filter:blur(10px);border:1px solid var(--border-light);border-radius:var(--radius-md);padding:16px;cursor:pointer;text-align:left;transition:all 0.2s;box-shadow:var(--shadow-sm);}
89
- .ex-card:hover{transform:translateY(-3px);box-shadow:var(--shadow-md);border-color:var(--accent);}
90
- .ex-title{font-size:13px;font-weight:600;color:var(--text-main);margin-bottom:4px;display:flex;align-items:center;gap:6px;}
91
- .ex-desc{font-size:12px;color:var(--text-muted);line-height:1.4;}
92
-
93
- /* Messages */
94
- .msg{display:flex;gap:16px;animation:msgIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;max-width:85%;}
95
- @keyframes msgIn{from{opacity:0;transform:translateY(15px) scale(0.96);}to{opacity:1;transform:none;}}
96
- .msg.user{flex-direction:row-reverse;align-self:flex-end;}
97
- .msg.bot{align-self:flex-start;}
98
- .avatar{width:36px;height:36px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:600;overflow:hidden;}
99
- .msg.user .avatar{background:#f0f0f0;color:var(--text-main);border:1px solid var(--border-light);}
100
- .msg.bot .avatar{border:1px solid var(--border-light);background:#fff;padding:2px;}
101
- .msg.bot .avatar img{width:100%;height:100%;border-radius:50%;object-fit:cover;}
102
- .body{display:flex;flex-direction:column;gap:6px;max-width:100%; width: 100%;}
103
- .msg.user .body{align-items:flex-end;}
104
-
105
- .bubble{padding:16px 20px;font-size:15px;line-height:1.6;color:var(--text-main);position:relative;word-break:break-word;overflow-wrap:anywhere;}
106
- .msg.user .bubble{background:var(--text-main);color:#ffffff;border-radius:20px 20px 4px 20px;box-shadow:var(--shadow-sm);}
107
- .msg.bot .bubble{background:#ffffff;border:1px solid var(--border-light);border-radius:4px 20px 20px 20px;box-shadow:var(--shadow-sm);width:100%;}
108
-
109
- /* BOT ACTIONS (Copy Button below text) */
110
- .msg-actions{display:flex;gap:8px;margin-top:4px;padding-left:12px;opacity:0.7;transition:opacity 0.2s;}
111
- .msg.bot:hover .msg-actions{opacity:1;}
112
- .action-btn{background:var(--bg-secondary);border:1px solid var(--border-light);color:var(--text-main);font-size:12px;font-weight:500;cursor:pointer;display:flex;align-items:center;gap:6px;padding:6px 12px;border-radius:8px;transition:all 0.2s;}
113
- .action-btn:hover{background:#e5e5ea;transform:translateY(-1px);}
114
-
115
- /* Reasoning / Thinking Box */
116
- .think-box {background: #fdfdfd;border: 1px solid var(--border-light);border-left: 4px solid var(--accent-green);border-radius: 8px;padding: 14px 16px;margin-bottom: 16px;font-family: var(--font-mono);font-size: 13.5px;color: var(--text-muted);line-height: 1.5;box-shadow: inset 0 2px 4px rgba(0,0,0,0.02);}
117
- .think-title {font-weight: 600; color: var(--text-main); margin-bottom: 8px; display: flex; align-items: center; gap: 6px; font-family: var(--font-sans);}
118
- /* Controlled by body class */
119
- body:not(.show-thinking) .think-box {display: none;}
120
-
121
- /* Markdown inside bubble */
122
- .bubble p{margin-bottom:12px;}
123
- .bubble p:last-child{margin-bottom:0;}
124
- .bubble code{font-family:var(--font-mono);background:rgba(0,0,0,0.05);padding:3px 6px;border-radius:6px;font-size:0.85em;color:#e83e8c;}
125
- .msg.user .bubble code{background:rgba(255,255,255,0.2);color:#fff;}
126
- .bubble img{max-width:100%;border-radius:12px;margin:10px 0;box-shadow:var(--shadow-sm);border:1px solid var(--border-light);}
127
- .bubble pre {display: none;} /* Native pre hidden since we override code blocks */
128
-
129
- /* Workspace Trigger Card in Chat */
130
- .ws-trigger-card {background: var(--bg-secondary);border: 1px solid var(--border-light);border-radius: 12px;padding: 16px;margin: 12px 0;display: flex;align-items: center;justify-content: space-between;box-shadow: inset 0 1px 2px rgba(255,255,255,0.5);}
131
- .ws-trigger-info {display: flex;align-items: center;gap: 12px;}
132
- .ws-trigger-icon {width: 44px;height: 44px;border-radius: 10px;background: #fff;display: flex;align-items: center;justify-content: center;font-size: 22px;box-shadow: var(--shadow-sm);border: 1px solid var(--border-light);}
133
- .ws-trigger-text h4 {font-size: 14px;font-weight: 600;margin-bottom: 2px;}
134
- .ws-trigger-text p {font-size: 12px;color: var(--text-muted);font-family: var(--font-mono);text-transform: uppercase;}
135
- .ws-btn {padding: 8px 16px;background: var(--text-main);color: #fff;border: none;border-radius: 8px;font-size: 13px;font-weight: 600;cursor: pointer;transition: all 0.2s;}
136
- .ws-btn:hover {transform: scale(1.05); background: var(--accent);}
137
-
138
- /* Typing & Cursor */
139
- .blinking-cursor {display: inline-block; width: 8px; height: 16px; background: var(--accent); margin-left: 4px; animation: blink 1s step-end infinite; vertical-align: middle;}
140
- @keyframes blink { 50% { opacity: 0; } }
141
- .typing{display:flex;align-items:center;gap:6px;padding:4px;}
142
- .typing span{width:6px;height:6px;border-radius:50%;background:var(--text-muted);opacity:0.4;animation:bounce 1.4s infinite ease-in-out both;}
143
- .typing span:nth-child(1){animation-delay:-0.32s;}.typing span:nth-child(2){animation-delay:-0.16s;}
144
- @keyframes bounce{0%,80%,100%{transform:scale(0);}40%{transform:scale(1);}}
145
-
146
- /* --- SCROLL TO BOTTOM BUTTON (Centered) --- */
147
- .scroll-bottom-btn {position:absolute;bottom:120px;left:50%;width:44px;height:44px;border-radius:50%;background:var(--glass-bg);backdrop-filter:blur(10px);border:1px solid var(--border-light);box-shadow:var(--shadow-md);display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:18px;color:var(--text-main);opacity:0;pointer-events:none;transform:translateX(-50%) translateY(20px);transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);z-index:20;}
148
- .scroll-bottom-btn.show {opacity:1;pointer-events:auto;transform:translateX(-50%) translateY(0);}
149
- .scroll-bottom-btn:hover {background:#fff;transform:translateX(-50%) translateY(-3px);box-shadow:var(--shadow-lg);}
150
-
151
- /* --- INPUT AREA --- */
152
- .input-container{padding:0 40px 30px;background:linear-gradient(to top, var(--bg-color) 70%, transparent);position:relative;z-index:20;}
153
- .input-wrap{display:flex;flex-direction:column;background:var(--glass-bg);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);border:1px solid var(--border-light);border-radius:24px;box-shadow:var(--shadow-md);transition:box-shadow 0.3s, border-color 0.3s;}
154
- .input-wrap:focus-within{border-color:var(--border-focus);box-shadow:0 12px 40px rgba(0,0,0,0.1), 0 0 0 3px rgba(255,153,51,0.1);}
155
-
156
- .img-prev-inline{display:none;padding:12px 20px 0;align-items:center;gap:12px;}
157
- .img-prev-inline.show{display:flex;animation:fadeIn 0.3s;}
158
- .inline-th{width:40px;height:40px;object-fit:cover;border-radius:8px;border:1px solid var(--border-light);}
159
- .inline-info{flex:1;}
160
- .inline-nm{font-size:12px;font-weight:600;color:var(--text-main);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:150px;}
161
- .inline-rm{background:rgba(0,0,0,0.05);border:none;width:24px;height:24px;border-radius:50%;cursor:pointer;font-size:10px;display:flex;align-items:center;justify-content:center;transition:background 0.2s;}
162
- .inline-rm:hover{background:#ff3b30;color:#fff;}
163
-
164
- .input-row{display:flex;align-items:flex-end;gap:12px;padding:12px 16px 12px 20px;}
165
- .chat-ta{flex:1;border:none;outline:none;background:transparent;font-family:var(--font-sans);font-size:15px;color:var(--text-main);line-height:1.5;resize:none;max-height:200px;min-height:24px;padding:8px 0;}
166
- .chat-ta::placeholder{color:var(--text-muted);}
167
-
168
- /* Modern Attachment & Send */
169
- .attach-btn{width:38px;height:38px;border-radius:12px;background:transparent;border:1px solid transparent;display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--text-muted);transition:all 0.2s;position:relative;overflow:hidden;padding-bottom:4px;}
170
- .attach-btn:hover{background:rgba(0,0,0,0.04);color:var(--text-main);}
171
- .attach-btn input{position:absolute;inset:0;opacity:0;cursor:pointer;}
172
- .attach-btn svg{width:20px;height:20px;}
173
-
174
- .send-btn{width:42px;height:42px;border-radius:14px;border:none;background:linear-gradient(135deg, #FF9933 0%, #ffffff 50%, #138808 100%);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all 0.3s;box-shadow:0 4px 12px rgba(0,0,0,0.1);}
175
- .send-btn svg{width:18px;height:18px;fill:#1d1d1f;transition:transform 0.2s;}
176
- .send-btn:hover{box-shadow:0 6px 16px rgba(255,153,51,0.3);transform:translateY(-1px);}
177
- .send-btn:hover svg{transform:translate(2px, -2px);}
178
- .send-btn:disabled{opacity:0.4;cursor:not-allowed;filter:grayscale(100%);transform:none;}
179
-
180
- .input-hint{text-align:center;font-size:11px;color:var(--text-muted);margin-top:12px;font-weight:500;}
181
-
182
- /* --- WORKSPACE PANEL --- */
183
- .ws-hdr{padding:14px 20px;border-bottom:1px solid var(--border-light);background:#fff;display:flex;align-items:center;justify-content:space-between;flex-shrink:0;}
184
- .ws-title{font-size:14px;font-weight:600;display:flex;align-items:center;gap:8px;}
185
- .ws-actions{display:flex;gap:8px;}
186
- .ws-icon-btn{padding:6px 12px;border-radius:8px;background:var(--bg-secondary);border:1px solid var(--border-light);cursor:pointer;display:flex;align-items:center;gap:6px;color:var(--text-main);transition:all 0.2s;font-size:12px;font-weight:600;}
187
- .ws-icon-btn:hover{background:#e5e5ea;}
188
- .ws-close{padding:6px;background:transparent;border-color:transparent;}
189
- .ws-close:hover{color:#ff3b30;background:#fff0f0;}
190
- .ws-content{flex:1;overflow-y:auto;background:#fafafa;padding:20px;}
191
- .ws-content pre{margin:0;padding:20px;background:#fff;border:1px solid var(--border-light);border-radius:12px;font-family:var(--font-mono);font-size:14px;line-height:1.6;overflow-x:auto;box-shadow:var(--shadow-sm);}
192
-
193
- /* --- RESPONSIVE MOBILE --- */
194
- @media(max-width:850px){
195
- .sidebar{position:absolute;left:-280px;z-index:100;height:100%;transition:left 0.3s;}
196
- .sidebar.show{left:0;box-shadow:var(--shadow-lg);}
197
- .workspace.open{position:absolute;top:0;left:0;width:100%;height:100%;z-index:50;}
198
- .messages{padding:20px;}
199
- .input-container{padding:0 20px 20px;}
200
- .msg{max-width:95%;}
201
- .scroll-bottom-btn{bottom:110px;}
202
- }
203
- </style>
204
  </head>
205
- <!-- Body controls the thinking visibility -->
206
- <body class="show-thinking">
207
 
208
- <div class="bg">
209
- <div class="orb orb1"></div>
210
- <div class="orb orb2"></div>
211
- </div>
212
 
213
- <div class="shell">
214
- <!-- Sidebar -->
215
- <aside class="sidebar" id="sidebar">
216
- <div class="logo-area">
217
- <div class="logo-row">
218
- <img src="https://i.ibb.co/x8m7DbMk/TIRANGA-20260523-101648-0000.png" alt="Tiranga Logo" class="logo-img">
219
- <div class="logo-text">
220
- <div class="logo-name">TIRANGA</div>
221
- <div class="logo-sub">Advanced Edition</div>
222
  </div>
223
- </div>
224
- </div>
225
-
226
- <div class="settings">
227
- <!-- Reasoning Toggle -->
228
- <div class="toggle-wrapper">
229
- <div class="toggle-label">
230
- 🧠 Reasoning Mode
231
- <span>Show internal thinking</span>
 
 
232
  </div>
233
- <label class="switch">
234
- <input type="checkbox" id="thinkingToggle" checked onchange="toggleThinking(this)">
235
- <span class="slider"></span>
236
- </label>
237
- </div>
238
-
239
- <div class="brand-box">
240
- <img src="https://i.ibb.co/x8m7DbMk/TIRANGA-20260523-101648-0000.png" alt="Tiranga Identity">
241
- <h3>TIRANGA</h3>
242
- <p>ALWAYS BE FORWARD NEVER BE BACKWARD!!</p>
243
- </div>
244
- <button class="clear-btn" onclick="clearMemory()">🗑️ Clear Chat Memory</button>
245
- </div>
246
- </aside>
247
-
248
- <!-- Main Chat Area -->
249
- <div class="main-wrapper">
250
- <main class="chat-main" id="chatMain">
251
- <header class="chat-hdr">
252
- <div class="model-badge">
253
- <div class="status-dot"></div>
254
- TIRANGA Intelligence
255
  </div>
256
- </header>
257
-
258
- <div class="messages" id="msgs" onscroll="checkScroll()">
259
- <!-- Welcome Screen -->
260
- <div class="welcome" id="welcome">
261
- <img src="https://i.ibb.co/x8m7DbMk/TIRANGA-20260523-101648-0000.png" alt="Tiranga Logo" class="welcome-logo">
262
- <div class="welcome-title">Welcome, Divy Patel!</div>
263
- <div class="welcome-sub">I am TIRANGA. I am equipped with advanced reasoning, workspace coding, and image generation. How can I assist you today?</div>
264
-
265
- <div class="ex-grid">
266
- <div class="ex-card" onclick="sendEx('Write a python script for a simple HTTP server. Provide the code.')">
267
- <div class="ex-title">💻 Code Generation</div>
268
- <div class="ex-desc">Generate secure, workspace-ready code</div>
269
- </div>
270
- <div class="ex-card" onclick="sendEx('Create an image of a futuristic Indian city with flying cars, highly detailed, 8k resolution.')">
271
- <div class="ex-title">🎨 Generate Images</div>
272
- <div class="ex-desc">Stunning visuals using Pollinations AI</div>
 
273
  </div>
274
- </div>
275
  </div>
276
- </div>
277
-
278
- <!-- Scroll to bottom button (Centered) -->
279
- <button class="scroll-bottom-btn" id="scrollBtn" onclick="forceScrollDown()">
280
- <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M19 12l-7 7-7-7"/></svg>
281
- </button>
282
-
283
- <!-- Input Area -->
284
- <div class="input-container">
285
- <div class="input-wrap">
286
- <div class="img-prev-inline" id="imgPrev">
287
- <img class="inline-th" id="imgThumb" src="">
288
- <div class="inline-info">
289
- <div class="inline-nm" id="imgName">image.png</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  </div>
291
- <button class="inline-rm" onclick="removeImg()">✕</button>
292
- </div>
293
-
294
- <div class="input-row">
295
- <button class="attach-btn" title="Upload Image">
296
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"></path></svg>
297
- <input type="file" accept="image/*" onchange="handleFile(this)" id="fileInput">
298
- </button>
299
- <textarea class="chat-ta" id="chatInput" placeholder="Message TIRANGA..." rows="1" onkeydown="handleKey(event)" oninput="autoGrow(this)"></textarea>
300
- <button class="send-btn" id="sendBtn" onclick="sendMsg()">
301
- <svg viewBox="0 0 24 24"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
302
- </button>
303
- </div>
304
  </div>
305
- <div class="input-hint">TIRANGA is engineered for extreme performance. Verified by Divy Patel.</div>
306
- </div>
307
  </main>
 
308
 
309
- <!-- Workspace Panel -->
310
- <aside class="workspace" id="workspace">
311
- <div class="ws-hdr">
312
- <div class="ws-title">
313
- <span>💻</span>
314
- <span id="wsTitleText">Generated Code</span>
 
 
 
315
  </div>
316
- <div class="ws-actions">
317
- <button class="ws-icon-btn" onclick="copyWorkspaceCode()" id="wsCopyBtn">📋 Copy</button>
318
- <button class="ws-icon-btn ws-close" onclick="closeWorkspace()" title="Close">✕</button>
 
 
 
 
 
 
 
 
 
319
  </div>
320
- </div>
321
- <div class="ws-content">
322
- <pre><code id="wsCodeBlock" class="hljs"></code></pre>
323
- </div>
324
- </aside>
325
- </div>
 
 
 
 
 
 
 
 
 
326
  </div>
327
 
328
  <script>
329
- // --- STATE MANAGEMENT ---
330
- const S = {
331
- history: [],
332
- pending: null,
333
- busy: false,
334
- isUserScrolling: false,
335
- codeBlocks: [], // Securely stores raw code to prevent HTML escaping bugs
336
- currentWorkspaceId: null
337
- };
338
 
339
- function toggleThinking(el) {
340
- if(el.checked) document.body.classList.add('show-thinking');
341
- else document.body.classList.remove('show-thinking');
342
- }
343
 
344
- // Memory initialization
345
  window.onload = () => {
346
- const saved = localStorage.getItem('tiranga_v3_memory');
347
- if(saved) {
348
- try {
349
- S.history = JSON.parse(saved);
350
- if(S.history.length > 0) {
351
- document.getElementById('welcome').style.display = 'none';
352
- S.history.forEach(msg => {
353
- if(msg.role === 'user') appendUserDOM(msg.content, msg.badge);
354
- else appendBotDOM(msg.content);
355
- });
356
- setTimeout(forceScrollDown, 100);
357
- }
358
- } catch(e) {}
359
- }
360
  };
361
 
362
- function saveMemory() {
363
- const memToSave = S.history.slice(-20);
364
- localStorage.setItem('tiranga_v3_memory', JSON.stringify(memToSave));
 
 
365
  }
366
- function clearMemory() {
367
- localStorage.removeItem('tiranga_v3_memory');
368
- location.reload();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  }
370
 
371
- // --- FILE HANDLING ---
372
- function handleFile(input){
373
- const f=input.files[0];if(!f)return;
374
- const r=new FileReader();
375
- r.onload=e=>{
376
- const base64Data = e.target.result.split(',')[1];
377
- S.pending={type: 'image', data: base64Data, name: f.name};
378
- document.getElementById('imgThumb').src = e.target.result;
379
- document.getElementById('imgName').textContent=f.name;
380
- document.getElementById('imgPrev').classList.add('show');
381
- };
382
- r.readAsDataURL(f);
383
  }
384
- function removeImg(){S.pending=null;document.getElementById('imgPrev').classList.remove('show');document.getElementById('fileInput').value='';}
385
-
386
- // --- UI INTERACTIONS ---
387
- function handleKey(e){if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();sendMsg();}}
388
- function autoGrow(el){el.style.height='auto';el.style.height=Math.min(el.scrollHeight,200)+'px';}
389
-
390
- function checkScroll() {
391
- const m = document.getElementById('msgs');
392
- // Show center button only if user scrolled UP more than 80px
393
- const isAtBottom = Math.abs((m.scrollHeight - m.scrollTop) - m.clientHeight) < 80;
394
- S.isUserScrolling = !isAtBottom;
395
-
396
- const btn = document.getElementById('scrollBtn');
397
- if(S.isUserScrolling) btn.classList.add('show');
398
- else btn.classList.remove('show');
399
  }
400
- function forceScrollDown(){
401
- const m=document.getElementById('msgs');
402
- m.scrollTo({top: m.scrollHeight, behavior: 'smooth'});
403
- S.isUserScrolling = false;
404
- document.getElementById('scrollBtn').classList.remove('show');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
  }
406
- function autoScroll() {
407
- if(!S.isUserScrolling) {
408
- const m=document.getElementById('msgs');
409
- m.scrollTop = m.scrollHeight;
410
- }
 
 
 
411
  }
412
 
413
- // --- COPY UTILS ---
414
- function copyText(btn, text) {
415
- navigator.clipboard.writeText(text);
416
- const orig = btn.innerHTML;
417
- btn.innerHTML = "✓ Copied";
418
- setTimeout(() => btn.innerHTML = orig, 2000);
 
 
 
 
 
 
 
 
 
419
  }
420
- function copyWorkspaceCode() {
421
- if(S.currentWorkspaceId !== null) {
422
- navigator.clipboard.writeText(S.codeBlocks[S.currentWorkspaceId].code);
423
- const btn = document.getElementById('wsCopyBtn');
424
- const orig = btn.innerHTML;
425
- btn.innerHTML = "✓ Copied";
426
- setTimeout(() => btn.innerHTML = orig, 2000);
427
- }
428
  }
429
 
430
- // --- WORKSPACE LOGIC ---
431
- function openWorkspace(id) {
432
- S.currentWorkspaceId = id;
433
- const block = S.codeBlocks[id];
434
- document.getElementById('wsTitleText').textContent = block.lang ? `Generated ${block.lang.toUpperCase()}` : "Generated Code";
435
-
436
- const codeBlock = document.getElementById('wsCodeBlock');
437
- codeBlock.className = `hljs language-${block.lang || 'plaintext'}`;
438
- codeBlock.textContent = block.code;
439
- hljs.highlightElement(codeBlock);
440
-
441
- document.getElementById('workspace').classList.add('open');
442
- if(window.innerWidth <= 850) { document.getElementById('sidebar').classList.remove('show'); }
 
 
 
 
443
  }
444
- function closeWorkspace() { document.getElementById('workspace').classList.remove('open'); }
445
-
446
- // --- MARKED.JS CUSTOM RENDERER (Fixes the Workspace Bug Completely) ---
447
- const renderer = new marked.Renderer();
448
- renderer.code = function(token) {
449
- // Marked v12 might pass object or string based on usage. Safely extract.
450
- const codeText = typeof token === 'string' ? token : (token.text || '');
451
- const language = typeof token === 'string' ? arguments[1] : (token.lang || '');
452
-
453
- const id = S.codeBlocks.length;
454
- S.codeBlocks.push({ code: codeText, lang: language });
455
-
456
- // Return the Beautiful Trigger Card
457
- return `
458
- <div class="ws-trigger-card">
459
- <div class="ws-trigger-info">
460
- <div class="ws-trigger-icon">💻</div>
461
- <div class="ws-trigger-text">
462
- <h4>Code Generated</h4>
463
- <p>${language || 'Snippet'}</p>
464
- </div>
465
- </div>
466
- <button class="ws-btn" onclick="openWorkspace(${id})">View in Workspace</button>
467
- </div>
468
- `;
469
- };
470
- marked.use({ renderer });
471
-
472
- // Custom Thinking Parser
473
- function parseThinking(raw) {
474
- const thinkRegex = /<think>([\s\S]*?)(<\/think>|$)/gi;
475
- let text = raw;
476
- let thinkContent = '';
477
- text = text.replace(thinkRegex, (match, p1) => {
478
- thinkContent += p1;
479
- return '';
480
- });
481
- return { mainText: text.trim(), thinkText: thinkContent.trim() };
482
  }
483
 
484
- // --- MAIN CHAT LOGIC ---
485
- async function sendMsg(){
486
- if(S.busy)return;
487
- const inp=document.getElementById('chatInput');
488
- const msg=inp.value.trim();if(!msg && !S.pending)return;
489
- inp.value='';inp.style.height='auto';
490
-
491
- document.getElementById('sendBtn').disabled=true;
492
- S.busy=true;
493
-
494
- const w=document.getElementById('welcome');
495
- if(w)w.style.display='none';
496
-
497
- let attachments = [];
498
- let badge = null;
499
- if(S.pending) {
500
- attachments.push(S.pending);
501
- badge = '📎 '+S.pending.name;
502
- appendUserDOM(msg || '[Image Attached]', badge);
503
- S.history.push({role: 'user', content: msg || '[Image Attached]', badge: badge});
504
- } else {
505
- appendUserDOM(msg);
506
- S.history.push({role: 'user', content: msg});
507
- }
508
- removeImg();
509
- saveMemory();
510
-
511
- const botDiv = document.createElement('div');botDiv.className='msg bot';
512
- botDiv.innerHTML=`<div class="avatar"><img src="https://i.ibb.co/x8m7DbMk/TIRANGA-20260523-101648-0000.png"></div><div class="body"><div class="bubble"><div class="typing"><span></span><span></span><span></span></div></div></div>`;
513
- document.getElementById('msgs').appendChild(botDiv);
514
- forceScrollDown();
515
-
516
- // Integrated Pollinations AI at Model Level (Using System Prompt constraints)
517
- const sysPrompt = `You are TIRANGA, a state-of-the-art AI assistant. Developed by Divy Patel.
518
- Always communicate respectfully.
519
-
520
- CRITICAL IMAGE GENERATION PROTOCOL:
521
- If the user asks to generate, create, or draw an image/photo, you MUST generate the image by responding with a markdown image tag using Pollinations AI.
522
- Format to use: ![TIRANGA is creating your image...](https://image.pollinations.ai/prompt/{URL_ENCODED_PROMPT}?width=1024&height=1024&nologo=true&model={MODEL})
523
- Available models for {MODEL}: flux, flux-realism, flux-anime, flux-3d.
524
- Choose the best model based on the prompt. Example: if user says anime, use flux-anime.
525
- Always include this markdown tag when image generation is requested.`;
526
-
527
- const payload = {
528
- message: msg,
529
- attachments: attachments,
530
- system_prompt: sysPrompt,
531
- max_tokens: 4096,
532
- temperature: 0.7
533
- };
534
-
535
- try{
536
- const res = await fetch('/api/chat', {
537
- method: 'POST',
538
- headers: {'Content-Type': 'application/json'},
539
- body: JSON.stringify(payload)
540
- });
541
-
542
- const reader = res.body.getReader();
543
- const decoder = new TextDecoder();
544
- let accumulatedText = "";
545
-
546
- while (true) {
547
- const { done, value } = await reader.read();
548
- if (done) break;
549
-
550
- const chunk = decoder.decode(value, {stream: true});
551
- const lines = chunk.split('\n');
552
-
553
- for (let line of lines) {
554
- if (line.startsWith('data: ')) {
555
- const dataStr = line.substring(6);
556
- if (dataStr.trim() === '[DONE]') continue;
557
- try {
558
- const dataObj = JSON.parse(dataStr);
559
- if (dataObj.choices && dataObj.choices[0].delta.content) {
560
- accumulatedText += dataObj.choices[0].delta.content;
561
- renderBotText(botDiv, accumulatedText, true);
562
- autoScroll();
563
- }
564
- } catch (e) { }
565
- }
566
- }
567
  }
568
- // Final Render without cursor
569
- renderBotText(botDiv, accumulatedText, false);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
570
 
571
- // Save to history
572
- S.history.push({role: 'bot', content: accumulatedText});
573
- saveMemory();
574
 
575
- }catch(err){
576
- renderBotText(botDiv,`**Error:** Server communication failed.`, false);
577
- }
 
 
578
 
579
- S.busy=false;
580
- document.getElementById('sendBtn').disabled=false;
581
- autoScroll();
582
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
583
 
584
- function sendEx(t){document.getElementById('chatInput').value=t;sendMsg();}
 
 
585
 
586
- // --- DOM INJECTION ---
587
- function appendUserDOM(text, badgeText){
588
- let bHtml = badgeText ? `<div style="font-size:11px;color:var(--accent);margin-bottom:4px;font-weight:600;">${esc(badgeText)}</div>` : '';
589
- const el=document.createElement('div');el.className='msg user';
590
- el.innerHTML=`<div class="avatar">U</div><div class="body">${bHtml}<div class="bubble">${esc(text)}</div></div>`;
591
- document.getElementById('msgs').appendChild(el);
592
- }
593
 
594
- function appendBotDOM(content) {
595
- const el=document.createElement('div');el.className='msg bot';
596
- el.innerHTML=`<div class="avatar"><img src="https://i.ibb.co/x8m7DbMk/TIRANGA-20260523-101648-0000.png"></div><div class="body"><div class="bubble"></div></div>`;
597
- document.getElementById('msgs').appendChild(el);
598
- renderBotText(el, content, false);
599
- }
600
 
601
- // Render Markdown + Thinking Toggle + Actions
602
- function renderBotText(el, raw, isStreaming) {
603
- const b = el.querySelector('.bubble');
604
-
605
- // 1. Separate Thinking and Main Text
606
- const { mainText, thinkText } = parseThinking(raw);
607
-
608
- // 2. Parse Markdown
609
- let html = '';
610
-
611
- // Attach Thinking block if exists
612
- if (thinkText) {
613
- html += `<div class="think-box"><div class="think-title">🧠 Reasoning Process</div>${esc(thinkText)}</div>`;
614
- }
615
-
616
- html += marked.parse(mainText);
617
-
618
- // Add Blinking Cursor if streaming
619
- if(isStreaming) {
620
- html += '<span class="blinking-cursor"></span>';
621
- }
622
-
623
- b.innerHTML = html;
624
-
625
- // 3. Add Copy Action Button below bubble (only once streaming is done or if it's history load)
626
- let actionsDiv = el.querySelector('.msg-actions');
627
- if(!isStreaming && !actionsDiv && mainText.trim().length > 0) {
628
- const cleanRaw = mainText.replace(/"/g, '&quot;').replace(/'/g, "\\'").replace(/\n/g, '\\n');
629
- const actionsHTML = `
630
- <div class="msg-actions">
631
- <button class="action-btn" onclick="copyText(this, \`${cleanRaw}\`)">
632
- <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>
633
- Copy Text
634
- </button>
635
- </div>
636
- `;
637
- el.querySelector('.body').insertAdjacentHTML('beforeend', actionsHTML);
638
- }
 
639
  }
640
 
641
- function esc(t){return t.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}
 
 
 
 
 
 
 
 
642
  </script>
643
  </body>
644
  </html>
645
-
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+ <title>Arjun Compound | Advanced AI</title>
7
+
8
+ <!-- Fonts & Icons -->
9
+ <link rel="preconnect" href="https://fonts.googleapis.com">
10
+ <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
12
+
13
+ <!-- Markdown & Syntax Highlighting -->
14
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
15
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
16
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
17
+
18
+ <style>
19
+ /* --- CORE VARIABLES --- */
20
+ :root {
21
+ --bg-main: #F5F5F7; --bg-panel: #FFFFFF;
22
+ --text-dark: #1D1D1F; --text-muted: #86868B;
23
+ --accent-blue: #007AFF; --accent-hover: #0056b3;
24
+ --danger: #FF3B30; --success: #34C759;
25
+ --border-light: rgba(0,0,0,0.08); --border-focus: rgba(0, 122, 255, 0.3);
26
+ --shadow-sm: 0 4px 12px rgba(0,0,0,0.05); --shadow-md: 0 12px 32px rgba(0,0,0,0.08);
27
+ --font-sans: 'Plus Jakarta Sans', sans-serif; --font-mono: 'Fira Code', monospace;
28
+ }
29
+
30
+ * { margin: 0; padding: 0; box-sizing: border-box; -webkit-font-smoothing: antialiased; }
31
+ body { font-family: var(--font-sans); background: var(--bg-main); color: var(--text-dark); height: 100dvh; width: 100vw; overflow: hidden; display: flex; }
32
+
33
+ /* --- TOAST NOTIFICATIONS --- */
34
+ #toast-container { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); z-index: 10000; display: flex; flex-direction: column; gap: 10px; }
35
+ .toast { background: var(--text-dark); color: #fff; padding: 12px 24px; border-radius: 50px; font-size: 14px; font-weight: 600; box-shadow: var(--shadow-md); animation: slideDown 0.4s ease; display: flex; align-items: center; gap: 10px; }
36
+ @keyframes slideDown { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } }
37
+
38
+ /* --- LAYOUT SHELL --- */
39
+ .app-shell { display: flex; width: 100%; height: 100%; position: relative; }
40
+
41
+ /* --- SIDEBAR --- */
42
+ .sidebar { width: 300px; background: var(--bg-panel); border-right: 1px solid var(--border-light); display: flex; flex-direction: column; flex-shrink: 0; transition: transform 0.3s ease; z-index: 100; }
43
+ .brand-header { padding: 24px; border-bottom: 1px solid var(--border-light); display: flex; justify-content: space-between; align-items: center; }
44
+ .brand-info h1 { font-size: 20px; font-weight: 800; letter-spacing: -0.5px; }
45
+ .brand-info p { font-size: 11px; color: var(--text-muted); font-weight: 600; text-transform: uppercase; letter-spacing: 1px; margin-top: 2px; }
46
+ .close-sidebar-btn { display: none; background: none; border: none; font-size: 20px; color: var(--text-dark); cursor: pointer; }
47
+
48
+ .auth-section { padding: 20px; border-bottom: 1px solid var(--border-light); }
49
+ .btn-primary { width: 100%; padding: 14px; background: var(--text-dark); color: #fff; border: none; border-radius: 14px; font-size: 14px; font-weight: 700; cursor: pointer; transition: 0.2s; display: flex; align-items: center; justify-content: center; gap: 8px; }
50
+ .btn-primary:hover { background: #333; transform: translateY(-1px); box-shadow: var(--shadow-sm); }
51
+
52
+ /* Logged In User Profile */
53
+ .user-profile { display: none; align-items: center; justify-content: space-between; background: var(--bg-main); padding: 12px 16px; border-radius: 14px; border: 1px solid var(--border-light); }
54
+ .user-details { display: flex; align-items: center; gap: 12px; overflow: hidden; }
55
+ .user-avatar { width: 36px; height: 36px; border-radius: 50%; background: var(--accent-blue); color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 700; flex-shrink: 0; }
56
+ .user-email { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 140px;}
57
+ .btn-logout { background: none; border: none; color: var(--danger); cursor: pointer; font-size: 16px; padding: 8px; border-radius: 8px; transition: 0.2s; }
58
+ .btn-logout:hover { background: #FFEBEE; }
59
+
60
+ .sidebar-menu { padding: 20px; flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 10px; }
61
+ .menu-btn { width: 100%; padding: 14px; text-align: left; background: transparent; border: 1px solid transparent; border-radius: 12px; font-size: 14px; font-weight: 600; color: var(--text-dark); cursor: pointer; transition: 0.2s; display: flex; align-items: center; gap: 12px; }
62
+ .menu-btn:hover { background: var(--bg-main); border-color: var(--border-light); }
63
+ .menu-btn.danger { color: var(--danger); margin-top: auto; border: 1px solid var(--border-light); }
64
+ .menu-btn.danger:hover { background: #FFEBEE; border-color: var(--danger); }
65
+
66
+ /* --- MAIN WORKSPACE --- */
67
+ .main-area { flex: 1; display: flex; flex-direction: column; position: relative; min-width: 0; background: var(--bg-main); }
68
+
69
+ .top-nav { height: 60px; padding: 0 24px; display: flex; align-items: center; gap: 16px; background: rgba(245,245,247,0.8); backdrop-filter: blur(10px); border-bottom: 1px solid var(--border-light); z-index: 10; }
70
+ .hamburger { background: none; border: none; font-size: 20px; color: var(--text-dark); cursor: pointer; display: none; padding: 8px; border-radius: 8px; }
71
+ .nav-title { font-size: 15px; font-weight: 700; display: flex; align-items: center; gap: 8px; }
72
+ .nav-status { width: 8px; height: 8px; background: var(--success); border-radius: 50%; box-shadow: 0 0 0 2px rgba(52,199,89,0.2); }
73
+
74
+ /* --- CHAT HISTORY --- */
75
+ .chat-container { flex: 1; overflow-y: auto; padding: 30px 40px; display: flex; flex-direction: column; gap: 24px; scroll-behavior: smooth; }
76
+ .chat-container::-webkit-scrollbar { width: 6px; }
77
+ .chat-container::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.15); border-radius: 10px; }
78
+
79
+ .welcome-screen { margin: auto; text-align: center; max-width: 600px; animation: fadeIn 0.5s ease; }
80
+ .welcome-icon { font-size: 48px; margin-bottom: 20px; background: -webkit-linear-gradient(45deg, var(--text-dark), var(--accent-blue)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
81
+ .welcome-title { font-size: 32px; font-weight: 800; margin-bottom: 10px; letter-spacing: -0.5px; }
82
+ .welcome-desc { color: var(--text-muted); font-size: 15px; line-height: 1.6; margin-bottom: 30px; }
83
+
84
+ .guest-badge { display: inline-flex; align-items: center; gap: 8px; background: #FFF4E5; color: #FF9500; padding: 8px 16px; border-radius: 20px; font-size: 13px; font-weight: 700; border: 1px solid #FFE0B2; margin-bottom: 20px; }
85
+
86
+ .msg-row { display: flex; gap: 16px; max-width: 85%; animation: slideUp 0.3s ease; }
87
+ @keyframes slideUp { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } }
88
+ .msg-row.user { align-self: flex-end; flex-direction: row-reverse; }
89
+ .msg-row.bot { align-self: flex-start; }
90
+
91
+ .avatar { width: 36px; height: 36px; border-radius: 50%; display: flex; justify-content: center; align-items: center; font-size: 14px; font-weight: 700; flex-shrink: 0; box-shadow: var(--shadow-sm); }
92
+ .msg-row.user .avatar { background: var(--text-dark); color: #fff; }
93
+ .msg-row.bot .avatar { background: #fff; color: var(--accent-blue); border: 1px solid var(--border-light); }
94
+
95
+ .msg-bubble { padding: 16px 20px; border-radius: 20px; font-size: 15px; line-height: 1.6; word-wrap: break-word; overflow-wrap: anywhere; box-shadow: var(--shadow-sm); }
96
+ .msg-row.user .msg-bubble { background: var(--text-dark); color: #fff; border-top-right-radius: 4px; }
97
+ .msg-row.bot .msg-bubble { background: var(--bg-panel); color: var(--text-dark); border-top-left-radius: 4px; border: 1px solid var(--border-light); width: 100%; }
98
+
99
+ /* Markdown Styles */
100
+ .msg-bubble p { margin-bottom: 12px; }
101
+ .msg-bubble p:last-child { margin-bottom: 0; }
102
+ .msg-bubble code { font-family: var(--font-mono); background: rgba(0,0,0,0.05); padding: 3px 6px; border-radius: 6px; font-size: 0.9em; color: #E91E63; }
103
+ .msg-row.user .msg-bubble code { background: rgba(255,255,255,0.2); color: #fff; }
104
+ .msg-bubble pre { background: #1E1E1E; padding: 16px; border-radius: 12px; overflow-x: auto; margin: 12px 0; border: 1px solid #333; }
105
+ .msg-bubble pre code { background: transparent; color: #D4D4D4; padding: 0; }
106
+ .msg-bubble img { max-width: 100%; border-radius: 12px; margin: 10px 0; box-shadow: var(--shadow-sm); }
107
+ .copy-btn { background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); color: #fff; padding: 4px 10px; border-radius: 6px; font-size: 11px; cursor: pointer; float: right; margin-bottom: 8px; }
108
+
109
+ /* Attachment Previews in Chat */
110
+ .chat-attachment { display: flex; align-items: center; gap: 10px; background: rgba(255,255,255,0.1); padding: 8px 12px; border-radius: 10px; border: 1px solid rgba(255,255,255,0.2); margin-bottom: 8px; font-size: 12px; }
111
+ .chat-attachment i { font-size: 16px; }
112
+ .msg-row.bot .chat-attachment { background: rgba(0,0,0,0.04); border-color: rgba(0,0,0,0.1); }
113
+
114
+ /* --- INPUT AREA --- */
115
+ .input-zone { padding: 0 40px 30px; background: linear-gradient(to top, var(--bg-main) 70%, transparent); position: relative; z-index: 20; }
116
+ .input-wrapper { background: rgba(255,255,255,0.9); backdrop-filter: blur(20px); border: 1px solid var(--border-light); border-radius: 24px; box-shadow: var(--shadow-md); display: flex; flex-direction: column; transition: 0.3s; }
117
+ .input-wrapper:focus-within { border-color: var(--border-focus); box-shadow: 0 12px 40px rgba(0,122,255,0.15); }
118
+
119
+ /* File Preview Area */
120
+ .file-preview { display: none; padding: 16px 20px 0; align-items: center; gap: 12px; border-bottom: 1px solid rgba(0,0,0,0.05); padding-bottom: 12px; margin: 12px 20px 0; }
121
+ .file-preview.active { display: flex; }
122
+ .file-thumb { width: 40px; height: 40px; border-radius: 8px; object-fit: cover; background: #f0f0f0; display: flex; justify-content: center; align-items: center; font-size: 20px; color: var(--text-muted); }
123
+ .file-info { flex: 1; overflow: hidden; }
124
+ .file-name { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
125
+ .file-meta { font-size: 11px; color: var(--text-muted); }
126
+ .remove-file { background: #FFEBEE; color: var(--danger); border: none; width: 28px; height: 28px; border-radius: 50%; cursor: pointer; display: flex; justify-content: center; align-items: center; transition: 0.2s; }
127
+ .remove-file:hover { background: var(--danger); color: #fff; }
128
+
129
+ .input-row { display: flex; align-items: flex-end; gap: 12px; padding: 12px 16px 12px 20px; }
130
+
131
+ .action-icon-btn { width: 40px; height: 40px; border-radius: 12px; background: transparent; border: none; font-size: 18px; color: var(--text-muted); cursor: pointer; display: flex; justify-content: center; align-items: center; transition: 0.2s; position: relative; overflow: hidden; }
132
+ .action-icon-btn:hover { background: rgba(0,0,0,0.05); color: var(--text-dark); }
133
+ .action-icon-btn input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; }
134
+
135
+ /* Voice Active Animation */
136
+ .action-icon-btn.recording { color: var(--danger); animation: pulseRecord 1.5s infinite; }
137
+ @keyframes pulseRecord { 0% { background: rgba(255,59,48,0.1); } 50% { background: rgba(255,59,48,0.3); } 100% { background: rgba(255,59,48,0.1); } }
138
+
139
+ .chat-input { flex: 1; border: none; outline: none; background: transparent; font-family: var(--font-sans); font-size: 15px; color: var(--text-dark); resize: none; min-height: 24px; max-height: 200px; padding: 8px 0; line-height: 1.5; }
140
+
141
+ .send-btn { width: 44px; height: 44px; border-radius: 14px; border: none; background: var(--text-dark); color: #fff; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: 0.2s; box-shadow: 0 4px 12px rgba(0,0,0,0.1); flex-shrink: 0; }
142
+ .send-btn:hover { background: #333; transform: translateY(-2px); box-shadow: 0 6px 16px rgba(0,0,0,0.2); }
143
+ .send-btn:disabled { background: #ccc; cursor: not-allowed; transform: none; box-shadow: none; }
144
+
145
+ /* --- AUTH MODAL --- */
146
+ .modal-overlay { position: fixed; inset: 0; background: rgba(245,245,247,0.8); backdrop-filter: blur(20px); z-index: 5000; display: none; justify-content: center; align-items: center; animation: fadeIn 0.3s ease; }
147
+ @keyframes fadeIn { from{opacity:0;} to{opacity:1;} }
148
+
149
+ .modal-card { background: #fff; padding: 40px; border-radius: 32px; width: 100%; max-width: 440px; border: 1px solid var(--border-light); box-shadow: var(--shadow-md); position: relative; margin: 20px; }
150
+ .close-modal { position: absolute; top: 20px; right: 20px; background: none; border: none; font-size: 20px; color: var(--text-muted); cursor: pointer; transition: 0.2s; }
151
+ .close-modal:hover { color: var(--danger); transform: rotate(90deg); }
152
+
153
+ .modal-header { text-align: center; margin-bottom: 24px; }
154
+ .modal-header h2 { font-size: 26px; font-weight: 800; color: var(--text-dark); }
155
+
156
+ .auth-tabs { display: flex; background: var(--bg-main); padding: 4px; border-radius: 12px; margin-bottom: 24px; }
157
+ .auth-tab { flex: 1; padding: 10px; text-align: center; border-radius: 8px; font-size: 14px; font-weight: 700; color: var(--text-muted); cursor: pointer; transition: 0.2s; }
158
+ .auth-tab.active { background: #fff; color: var(--text-dark); box-shadow: var(--shadow-sm); }
159
+
160
+ .auth-step { display: none; flex-direction: column; animation: slideIn 0.3s ease; }
161
+ .auth-step.active { display: flex; }
162
+ @keyframes slideIn { from{opacity:0; transform:translateX(20px);} to{opacity:1; transform:translateX(0);} }
163
+
164
+ .input-group { position: relative; margin-bottom: 16px; }
165
+ .input-group i { position: absolute; left: 16px; top: 50%; transform: translateY(-50%); color: var(--text-muted); font-size: 16px; }
166
+ .input-group input { width: 100%; padding: 16px 16px 16px 48px; border: 1px solid var(--border-light); border-radius: 14px; font-size: 15px; background: #FAFAFA; outline: none; transition: 0.3s; font-family: var(--font-sans); }
167
+ .input-group input:focus { border-color: var(--accent-blue); background: #fff; box-shadow: 0 4px 12px rgba(0,122,255,0.1); }
168
+
169
+ .btn-text { background: none; border: none; color: var(--text-muted); font-size: 14px; font-weight: 600; cursor: pointer; margin-top: 10px; padding: 10px; width: 100%; transition: 0.2s; }
170
+ .btn-text:hover { color: var(--text-dark); }
171
+
172
+ /* --- RESPONSIVE MOBILE ADAPTIVE --- */
173
+ @media (max-width: 850px) {
174
+ .sidebar { position: absolute; left: -300px; height: 100%; box-shadow: var(--shadow-md); }
175
+ .sidebar.open { left: 0; transform: translateX(0); }
176
+ .hamburger, .close-sidebar-btn { display: block; }
177
+ .chat-container { padding: 20px 20px; }
178
+ .input-zone { padding: 0 15px 20px; }
179
+ .msg-row { max-width: 95%; }
180
+ .modal-card { padding: 30px 24px; }
181
+ }
182
+ </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  </head>
184
+ <body>
 
185
 
186
+ <div id="toast-container"></div>
 
 
 
187
 
188
+ <div class="app-shell">
189
+ <!-- Sidebar -->
190
+ <aside class="sidebar" id="sidebar">
191
+ <div class="brand-header">
192
+ <div class="brand-info">
193
+ <h1>Arjun Compound</h1>
194
+ <p>By Abhay Kumar</p>
195
+ </div>
196
+ <button class="close-sidebar-btn" onclick="toggleSidebar()"><i class="fa-solid fa-xmark"></i></button>
197
  </div>
198
+
199
+ <!-- Auth / Profile Section -->
200
+ <div class="auth-section">
201
+ <button id="loginBtn" class="btn-primary" onclick="openAuthModal()"><i class="fa-solid fa-shield-halved"></i> Secure Login</button>
202
+ <div id="userProfile" class="user-profile">
203
+ <div class="user-details">
204
+ <div class="user-avatar" id="uAvatar">U</div>
205
+ <div class="user-email" id="uEmail">user@email.com</div>
206
+ </div>
207
+ <button class="btn-logout" onclick="logout()" title="Logout Account"><i class="fa-solid fa-power-off"></i></button>
208
+ </div>
209
  </div>
210
+
211
+ <div class="sidebar-menu">
212
+ <button class="menu-btn" onclick="sendEx('Write a python script for a simple HTTP server.')"><i class="fa-solid fa-code"></i> Code Assistant</button>
213
+ <button class="menu-btn" onclick="sendEx('Create a cinematic image of a futuristic Indian city.')"><i class="fa-solid fa-image"></i> Generate Image</button>
214
+ <button class="menu-btn danger" onclick="clearMemory()"><i class="fa-solid fa-trash-can"></i> Clear History</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  </div>
216
+ </aside>
217
+
218
+ <!-- Main Workspace -->
219
+ <main class="main-area">
220
+ <header class="top-nav">
221
+ <button class="hamburger" onclick="toggleSidebar()"><i class="fa-solid fa-bars"></i></button>
222
+ <div class="nav-title"><div class="nav-status"></div> Arjun AI Connected</div>
223
+ </header>
224
+
225
+ <div class="chat-container" id="chatArea">
226
+ <div class="welcome-screen" id="welcomeScreen">
227
+ <div class="welcome-icon"><i class="fa-solid fa-microchip"></i></div>
228
+ <h2 class="welcome-title">How can I help you today?</h2>
229
+ <p class="welcome-desc">I am Arjun Compound, an advanced AI developed by Abhay Kumar. I can write code, analyze files, understand voice, and generate images.</p>
230
+
231
+ <div id="guestBadge" class="guest-badge" style="display: none;">
232
+ <i class="fa-solid fa-circle-exclamation"></i> Guest Mode: <span id="guestCount">0</span>/3 Queries Used. Login to unlock.
233
+ </div>
234
  </div>
 
235
  </div>
236
+
237
+ <div class="input-zone">
238
+ <div class="input-wrapper">
239
+ <!-- File Preview UI -->
240
+ <div class="file-preview" id="filePreview">
241
+ <div class="file-thumb" id="fileThumb"><i class="fa-solid fa-file-lines"></i></div>
242
+ <div class="file-info">
243
+ <div class="file-name" id="fileName">document.pdf</div>
244
+ <div class="file-meta" id="fileMeta">Processing...</div>
245
+ </div>
246
+ <button class="remove-file" onclick="removeFile()"><i class="fa-solid fa-xmark"></i></button>
247
+ </div>
248
+
249
+ <div class="input-row">
250
+ <!-- Universal File Attach -->
251
+ <button class="action-icon-btn" title="Attach File or Image">
252
+ <i class="fa-solid fa-paperclip"></i>
253
+ <input type="file" id="fileInput" accept="*/*" onchange="handleFile(this)">
254
+ </button>
255
+
256
+ <!-- Voice Input (Google Speech API) -->
257
+ <button class="action-icon-btn" id="voiceBtn" onclick="toggleVoice()" title="Voice Input">
258
+ <i class="fa-solid fa-microphone"></i>
259
+ </button>
260
+
261
+ <textarea class="chat-input" id="chatInput" placeholder="Type a message or use voice..." rows="1" oninput="autoGrow(this)" onkeydown="handleKey(event)"></textarea>
262
+
263
+ <button class="send-btn" id="sendBtn" onclick="sendMsg()">
264
+ <i class="fa-solid fa-paper-plane"></i>
265
+ </button>
266
+ </div>
267
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  </div>
 
 
269
  </main>
270
+ </div>
271
 
272
+ <!-- Authentication Modal (GAS Powered) -->
273
+ <div class="modal-overlay" id="authModal">
274
+ <div class="modal-card">
275
+ <button class="close-modal" onclick="closeAuthModal()"><i class="fa-solid fa-xmark"></i></button>
276
+ <div class="modal-header"><h2>Arjun Network</h2></div>
277
+
278
+ <div class="auth-tabs">
279
+ <div class="auth-tab active" id="tab-login" onclick="switchAuthTab('login')">Login</div>
280
+ <div class="auth-tab" id="tab-register" onclick="switchAuthTab('register')">Create Account</div>
281
  </div>
282
+
283
+ <!-- Login Flow -->
284
+ <div id="flow-login">
285
+ <div class="auth-step active" id="log-step-1">
286
+ <div class="input-group"><i class="fa-solid fa-envelope"></i><input type="email" id="logEmail" placeholder="Registered Email"></div>
287
+ <button class="btn-primary" id="btn-log-otp" onclick="processAuth('login_send_otp')">Send Secure OTP</button>
288
+ </div>
289
+ <div class="auth-step" id="log-step-2">
290
+ <div class="input-group"><i class="fa-solid fa-key"></i><input type="number" id="logOTP" placeholder="Enter 6-Digit OTP"></div>
291
+ <button class="btn-primary" id="btn-log-verify" onclick="processAuth('login_verify')">Verify & Login</button>
292
+ <button class="btn-text" onclick="switchStep('log-step-2', 'log-step-1')">Back</button>
293
+ </div>
294
  </div>
295
+
296
+ <!-- Register Flow -->
297
+ <div id="flow-register" style="display: none;">
298
+ <div class="auth-step active" id="reg-step-1">
299
+ <div class="input-group"><i class="fa-solid fa-user"></i><input type="text" id="regName" placeholder="Full Name"></div>
300
+ <div class="input-group"><i class="fa-solid fa-envelope"></i><input type="email" id="regEmail" placeholder="Email Address"></div>
301
+ <button class="btn-primary" id="btn-reg-otp" onclick="processAuth('register_send_otp')">Generate OTP</button>
302
+ </div>
303
+ <div class="auth-step" id="reg-step-2">
304
+ <div class="input-group"><i class="fa-solid fa-shield-check"></i><input type="number" id="regOTP" placeholder="Enter Verification OTP"></div>
305
+ <button class="btn-primary" id="btn-reg-verify" onclick="processAuth('register_verify')">Create Account</button>
306
+ <button class="btn-text" onclick="switchStep('reg-step-2', 'reg-step-1')">Back</button>
307
+ </div>
308
+ </div>
309
+ </div>
310
  </div>
311
 
312
  <script>
313
+ // --- CORE CONFIGURATION & STATE ---
314
+ const GAS_URL = "https://script.google.com/macros/s/AKfycbzTsXGoVh97JjtFAAG825GF4WUZqf7FDB3Zbxyf0nrLXt_1HjJSSqA829UcdoamRPvg0A/exec";
315
+ const STATE = { history: [], pendingFile: null, isProcessing: false };
 
 
 
 
 
 
316
 
317
+ // CACHE MEMORY (Persistent Login)
318
+ let currentUser = localStorage.getItem('arjun_user');
319
+ let guestCount = parseInt(localStorage.getItem('arjun_guest') || '0');
 
320
 
321
+ // --- INITIALIZATION ---
322
  window.onload = () => {
323
+ updateUIForAuth();
324
+ if (currentUser) { fetchHistoryFromGAS(); }
325
+ else {
326
+ document.getElementById('guestBadge').style.display = 'inline-flex';
327
+ document.getElementById('guestCount').innerText = guestCount;
328
+ }
329
+ setupMarkdown();
 
 
 
 
 
 
 
330
  };
331
 
332
+ function showToast(msg) {
333
+ const t = document.createElement('div'); t.className = 'toast';
334
+ t.innerHTML = `<i class="fa-solid fa-circle-info"></i> ${msg}`;
335
+ document.getElementById('toast-container').appendChild(t);
336
+ setTimeout(() => t.remove(), 3000);
337
  }
338
+
339
+ function toggleSidebar() { document.getElementById('sidebar').classList.toggle('open'); }
340
+ function autoGrow(el) { el.style.height = 'auto'; el.style.height = Math.min(el.scrollHeight, 200) + 'px'; }
341
+ function handleKey(e) { if(e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMsg(); } }
342
+ function scrollChat() { const c = document.getElementById('chatArea'); c.scrollTo({ top: c.scrollHeight, behavior: 'smooth' }); }
343
+
344
+ // --- AUTHENTICATION (Login / Register / Cache) ---
345
+ function updateUIForAuth() {
346
+ if (currentUser) {
347
+ document.getElementById('loginBtn').style.display = 'none';
348
+ document.getElementById('userProfile').style.display = 'flex';
349
+ document.getElementById('uEmail').innerText = currentUser;
350
+ document.getElementById('uAvatar').innerText = currentUser.charAt(0).toUpperCase();
351
+ document.getElementById('guestBadge').style.display = 'none';
352
+ } else {
353
+ document.getElementById('loginBtn').style.display = 'flex';
354
+ document.getElementById('userProfile').style.display = 'none';
355
+ }
356
  }
357
 
358
+ function openAuthModal() { document.getElementById('authModal').style.display = 'flex'; }
359
+ function closeAuthModal() { document.getElementById('authModal').style.display = 'none'; }
360
+
361
+ function switchAuthTab(tab) {
362
+ document.querySelectorAll('.auth-tab').forEach(e => e.classList.remove('active'));
363
+ document.getElementById(`tab-${tab}`).classList.add('active');
364
+ document.getElementById('flow-login').style.display = tab === 'login' ? 'block' : 'none';
365
+ document.getElementById('flow-register').style.display = tab === 'register' ? 'block' : 'none';
 
 
 
 
366
  }
367
+ function switchStep(curr, next) {
368
+ document.getElementById(curr).classList.remove('active');
369
+ document.getElementById(next).classList.add('active');
 
 
 
 
 
 
 
 
 
 
 
 
370
  }
371
+
372
+ async function processAuth(action) {
373
+ let payload = { action }; let btnId = '';
374
+
375
+ if (action === 'register_send_otp') {
376
+ payload.name = document.getElementById('regName').value.trim();
377
+ payload.email = document.getElementById('regEmail').value.trim();
378
+ payload.phone = "0000000000"; payload.organization = "Arjun Compound";
379
+ if(!payload.email || !payload.name) return showToast("Name and Email required.");
380
+ btnId = 'btn-reg-otp';
381
+ } else if (action === 'login_send_otp') {
382
+ payload.email = document.getElementById('logEmail').value.trim();
383
+ if(!payload.email) return showToast("Enter email first.");
384
+ btnId = 'btn-log-otp';
385
+ } else if (action === 'register_verify' || action === 'login_verify') {
386
+ payload.email = document.getElementById(action === 'register_verify' ? 'regEmail' : 'logEmail').value.trim();
387
+ payload.otp = document.getElementById(action === 'register_verify' ? 'regOTP' : 'logOTP').value.trim();
388
+ if(!payload.otp) return showToast("Enter OTP!");
389
+ btnId = action === 'register_verify' ? 'btn-reg-verify' : 'btn-log-verify';
390
+ }
391
+
392
+ const btn = document.getElementById(btnId); const ogText = btn.innerHTML;
393
+ btn.disabled = true; btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Processing...';
394
+
395
+ try {
396
+ const res = await fetch(GAS_URL, { method: 'POST', body: JSON.stringify(payload) });
397
+ const data = await res.json();
398
+
399
+ if (data.status === 'success') {
400
+ showToast(data.message);
401
+ if (action === 'register_send_otp') switchStep('reg-step-1', 'reg-step-2');
402
+ else if (action === 'login_send_otp') switchStep('log-step-1', 'log-step-2');
403
+ else if (action === 'register_verify' || action === 'login_verify') {
404
+ currentUser = payload.email;
405
+ localStorage.setItem('arjun_user', currentUser); // Persistent Cache Update
406
+ closeAuthModal(); updateUIForAuth(); fetchHistoryFromGAS();
407
+ }
408
+ } else showToast(data.message);
409
+ } catch (e) { showToast("Network Error."); }
410
+ btn.disabled = false; btn.innerHTML = ogText;
411
  }
412
+
413
+ function logout() { localStorage.removeItem('arjun_user'); location.reload(); }
414
+ async function clearMemory() {
415
+ if(currentUser) {
416
+ showToast("Clearing Cloud History...");
417
+ await fetch(GAS_URL, { method: 'POST', body: JSON.stringify({ action: "delete_history", email: currentUser }) });
418
+ }
419
+ location.reload();
420
  }
421
 
422
+ async function fetchHistoryFromGAS() {
423
+ document.getElementById('welcomeScreen').style.display = 'none';
424
+ const c = document.getElementById('chatArea');
425
+ c.innerHTML = '<div style="text-align:center; color:var(--text-muted); margin-top:20px;"><i class="fa-solid fa-spinner fa-spin"></i> Syncing Arjun Network...</div>';
426
+
427
+ try {
428
+ const res = await fetch(GAS_URL, { method: 'POST', body: JSON.stringify({ action: "get_history", email: currentUser }) });
429
+ const data = await res.json();
430
+ c.innerHTML = '';
431
+ if (data.status === 'success' && data.historyJSON !== "[]") {
432
+ STATE.history = JSON.parse(data.historyJSON);
433
+ STATE.history.forEach(m => appendMsg(m.content, m.role, true));
434
+ setTimeout(scrollChat, 100);
435
+ } else { document.getElementById('welcomeScreen').style.display = 'block'; }
436
+ } catch(e) { c.innerHTML = ''; document.getElementById('welcomeScreen').style.display = 'block'; }
437
  }
438
+
439
+ function syncHistory() {
440
+ if(currentUser) fetch(GAS_URL, { method: 'POST', body: JSON.stringify({ action: "save_history", email: currentUser, historyJSON: JSON.stringify(STATE.history) }) });
 
 
 
 
 
441
  }
442
 
443
+ // --- VOICE RECOGNITION (Google Speech API) ---
444
+ const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
445
+ let recognition = null;
446
+ let isRecording = false;
447
+
448
+ if (SpeechRecognition) {
449
+ recognition = new SpeechRecognition();
450
+ recognition.continuous = false; recognition.interimResults = true; recognition.lang = 'en-US';
451
+
452
+ recognition.onstart = () => { isRecording = true; document.getElementById('voiceBtn').classList.add('recording'); };
453
+ recognition.onresult = (event) => {
454
+ let transcript = '';
455
+ for (let i = event.resultIndex; i < event.results.length; ++i) { transcript += event.results[i][0].transcript; }
456
+ document.getElementById('chatInput').value = transcript; autoGrow(document.getElementById('chatInput'));
457
+ };
458
+ recognition.onend = () => { isRecording = false; document.getElementById('voiceBtn').classList.remove('recording'); };
459
+ recognition.onerror = () => { showToast("Voice recognition failed. Try again."); };
460
  }
461
+
462
+ function toggleVoice() {
463
+ if(!recognition) return showToast("Voice input not supported in this browser.");
464
+ if(isRecording) recognition.stop(); else recognition.start();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
  }
466
 
467
+ // --- MULTI-FILE ATTACHMENT HANDLING ---
468
+ function handleFile(input) {
469
+ const file = input.files[0]; if(!file) return;
470
+ const reader = new FileReader();
471
+
472
+ document.getElementById('fileName').innerText = file.name;
473
+ document.getElementById('fileThumb').innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i>';
474
+ document.getElementById('filePreview').classList.add('active');
475
+
476
+ // Categorize: Image vs Text vs Binary(PDF/Doc)
477
+ if (file.type.startsWith('image/')) {
478
+ reader.onload = (e) => {
479
+ STATE.pendingFile = { type: 'image', data: e.target.result, name: file.name };
480
+ document.getElementById('fileThumb').innerHTML = `<img src="${e.target.result}" style="width:100%; height:100%; border-radius:8px; object-fit:cover;">`;
481
+ document.getElementById('fileMeta').innerText = 'Image Ready';
482
+ };
483
+ reader.readAsDataURL(file);
484
+ } else if (file.type.match(/(text|json|csv|xml)/) || file.name.match(/\.(txt|md|csv|json)$/i)) {
485
+ reader.onload = (e) => {
486
+ STATE.pendingFile = { type: 'text', data: e.target.result, name: file.name };
487
+ document.getElementById('fileThumb').innerHTML = '<i class="fa-solid fa-file-code" style="color:var(--accent-blue)"></i>';
488
+ document.getElementById('fileMeta').innerText = 'Text Extracted';
489
+ };
490
+ reader.readAsText(file);
491
+ } else {
492
+ // Fallback for PDFs, DOCs (Sends as Base64. Actual parsing depends on AI backend capability)
493
+ reader.onload = (e) => {
494
+ STATE.pendingFile = { type: 'document', data: e.target.result, name: file.name };
495
+ document.getElementById('fileThumb').innerHTML = '<i class="fa-solid fa-file-pdf" style="color:var(--danger)"></i>';
496
+ document.getElementById('fileMeta').innerText = 'Document Ready';
497
+ };
498
+ reader.readAsDataURL(file);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
  }
500
+ }
501
+ function removeFile() { STATE.pendingFile = null; document.getElementById('filePreview').classList.remove('active'); document.getElementById('fileInput').value = ''; }
502
+
503
+ // --- CHAT & MARKDOWN SYSTEM ---
504
+ function setupMarkdown() {
505
+ const renderer = new marked.Renderer();
506
+ renderer.code = function(code, lang) {
507
+ const valid = (lang && hljs.getLanguage(lang)) ? lang : 'plaintext';
508
+ const highlighted = hljs.highlight(code, { language: valid }).value;
509
+ const safeCode = encodeURIComponent(code);
510
+ return `<div style="position:relative;">
511
+ <button class="copy-btn" onclick="navigator.clipboard.writeText(decodeURIComponent('${safeCode}')).then(()=>{this.innerText='Copied!';setTimeout(()=>this.innerText='Copy Code',2000)})">Copy Code</button>
512
+ <pre><code class="hljs ${valid}">${highlighted}</code></pre>
513
+ </div>`;
514
+ };
515
+ marked.use({ renderer, breaks: true });
516
+ }
517
+
518
+ async function sendMsg() {
519
+ if (STATE.isProcessing) return;
520
+ const inp = document.getElementById('chatInput');
521
+ let msg = inp.value.trim();
522
 
523
+ if (!msg && !STATE.pendingFile) return;
 
 
524
 
525
+ if (!currentUser) {
526
+ if (guestCount >= 3) { openAuthModal(); return showToast("Guest limit reached. Please Login."); }
527
+ guestCount++; localStorage.setItem('arjun_guest', guestCount.toString());
528
+ document.getElementById('guestCount').innerText = guestCount;
529
+ }
530
 
531
+ inp.value = ''; inp.style.height = 'auto';
532
+ document.getElementById('welcomeScreen').style.display = 'none';
533
+ STATE.isProcessing = true; document.getElementById('sendBtn').disabled = true;
534
+
535
+ // Handle Attached File Content
536
+ let displayMsg = msg;
537
+ let backendPayloadMsg = msg;
538
+ let attachmentHTML = '';
539
+
540
+ if (STATE.pendingFile) {
541
+ if (STATE.pendingFile.type === 'text') {
542
+ backendPayloadMsg = `[User attached file: ${STATE.pendingFile.name}]\nFile Content:\n${STATE.pendingFile.data}\n\nUser Message: ${msg}`;
543
+ attachmentHTML = `<div class="chat-attachment"><i class="fa-solid fa-file-lines"></i> ${STATE.pendingFile.name} (Text Data)</div>`;
544
+ } else if (STATE.pendingFile.type === 'image') {
545
+ // If backend handles image base64, we send it in attachments array (like previous logic).
546
+ attachmentHTML = `<div class="chat-attachment"><i class="fa-solid fa-image"></i> ${STATE.pendingFile.name}</div><img src="${STATE.pendingFile.data}" style="max-width:200px; border-radius:10px; margin-top:10px;">`;
547
+ } else {
548
+ backendPayloadMsg = `[User attached document: ${STATE.pendingFile.name}]\nUser Message: ${msg}`;
549
+ attachmentHTML = `<div class="chat-attachment"><i class="fa-solid fa-file"></i> ${STATE.pendingFile.name}</div>`;
550
+ }
551
+ removeFile();
552
+ }
553
 
554
+ const finalUserHTML = attachmentHTML + (msg ? `<p>${msg.replace(/</g,'&lt;')}</p>` : '');
555
+ appendMsg(finalUserHTML, 'user');
556
+ STATE.history.push({ role: 'user', content: backendPayloadMsg }); // Save text to history
557
 
558
+ const botNode = appendMsg('<i class="fa-solid fa-circle-notch fa-spin"></i> Processing...', 'bot');
559
+ scrollChat();
 
 
 
 
 
560
 
561
+ const sysPrompt = "You are Arjun Compound, an advanced AI developed by Abhay Kumar. Always respond in English unless requested otherwise. To generate images, reply with ![Arjun AI Image](https://image.pollinations.ai/prompt/{URL_ENCODED_PROMPT}?width=1024&height=1024&nologo=true&model=flux)";
 
 
 
 
 
562
 
563
+ try {
564
+ const res = await fetch('/api/chat', {
565
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
566
+ body: JSON.stringify({ message: backendPayloadMsg, system_prompt: sysPrompt, history: STATE.history.slice(0, -1) })
567
+ });
568
+
569
+ const reader = res.body.getReader();
570
+ const decoder = new TextDecoder();
571
+ let fullText = "";
572
+
573
+ while (true) {
574
+ const { done, value } = await reader.read();
575
+ if (done) break;
576
+ const lines = decoder.decode(value, {stream: true}).split('\n');
577
+
578
+ for (let line of lines) {
579
+ if (line.startsWith('data: ') && !line.includes('[DONE]')) {
580
+ try {
581
+ const dataObj = JSON.parse(line.substring(6));
582
+ if (dataObj.choices && dataObj.choices[0].delta.content) {
583
+ fullText += dataObj.choices[0].delta.content;
584
+ // Regex to clean up think tags if present
585
+ let cleanText = fullText.replace(/<think>[\s\S]*?(<\/think>|$)/gi, '');
586
+ botNode.querySelector('.msg-bubble').innerHTML = marked.parse(cleanText) + '<span style="display:inline-block;width:6px;height:12px;background:var(--accent-blue);animation:blink 1s infinite;"></span>';
587
+ scrollChat();
588
+ }
589
+ } catch (e) { }
590
+ }
591
+ }
592
+ }
593
+
594
+ let finalText = fullText.replace(/<think>[\s\S]*?(<\/think>|$)/gi, '');
595
+ botNode.querySelector('.msg-bubble').innerHTML = marked.parse(finalText);
596
+ STATE.history.push({ role: 'bot', content: fullText });
597
+ syncHistory();
598
+
599
+ } catch(err) { botNode.querySelector('.msg-bubble').innerHTML = "<b>Error:</b> Connection failed."; }
600
+
601
+ STATE.isProcessing = false; document.getElementById('sendBtn').disabled = false; scrollChat();
602
  }
603
 
604
+ function sendEx(t) { document.getElementById('chatInput').value = t; sendMsg(); }
605
+
606
+ function appendMsg(htmlContent, role, isMD = false) {
607
+ const d = document.createElement('div'); d.className = `msg-row ${role}`;
608
+ let displayHtml = isMD ? marked.parse(htmlContent.replace(/<think>[\s\S]*?(<\/think>|$)/gi, '')) : htmlContent;
609
+ d.innerHTML = `<div class="avatar">${role === 'user' ? 'U' : '<i class="fa-solid fa-microchip"></i>'}</div><div class="msg-bubble">${displayHtml}</div>`;
610
+ document.getElementById('chatArea').appendChild(d);
611
+ return d;
612
+ }
613
  </script>
614
  </body>
615
  </html>