akhaliq HF Staff commited on
Commit
dc3d28d
·
1 Parent(s): 9ce0c1e

Redesign playground to match StepFun Studio reference UI

Browse files
Files changed (3) hide show
  1. static/app.js +187 -125
  2. static/index.html +228 -154
  3. static/style.css +753 -673
static/app.js CHANGED
@@ -8,12 +8,12 @@ const STATE = {
8
  maxTokens: 2048,
9
  temperature: 0.7,
10
  uploadedFiles: [], // Current prompt attachments
11
- conversationHistory: [], // Conversation log sent to StepFun
12
  gradioClient: null,
13
  isThinking: false
14
  };
15
 
16
- // DOM Selections
17
  const dom = {
18
  apiKeyInput: document.getElementById("api-key-input"),
19
  toggleKeyVisibility: document.getElementById("toggle-key-visibility"),
@@ -22,18 +22,43 @@ const dom = {
22
  maxTokensVal: document.getElementById("max-tokens-val"),
23
  temperatureSlider: document.getElementById("temperature-slider"),
24
  temperatureVal: document.getElementById("temperature-val"),
25
- dropZone: document.getElementById("drop-zone"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  fileUploader: document.getElementById("file-uploader"),
27
- shelfInventory: document.getElementById("shelf-inventory"),
28
- recipeCards: document.querySelectorAll(".recipe-card"),
 
 
29
  clearChatBtn: document.getElementById("clear-chat-button"),
30
- chatMessages: document.getElementById("chat-messages"),
31
- quickShelfPreview: document.getElementById("quick-shelf-preview"),
32
- quickUploadTrigger: document.getElementById("quick-upload-trigger"),
33
- promptInput: document.getElementById("prompt-input"),
34
- sendBtn: document.getElementById("send-button"),
35
- sendIcon: document.getElementById("send-icon"),
36
- sendSpinner: document.getElementById("send-spinner")
37
  };
38
 
39
  // Markdown configuration
@@ -54,15 +79,22 @@ async function initializeApp() {
54
 
55
  // 2. Connect Gradio Client
56
  try {
57
- // connects to current host (where python app.py is served)
58
  STATE.gradioClient = await Client.connect(window.location.origin);
59
  console.log("Successfully connected to Gradio.Server backend.");
60
  } catch (e) {
61
  console.error("Gradio Client Connection Failed:", e);
62
- appendSystemLog("System connection is restricted. Local commands might not be running.", true);
63
  }
64
 
65
- // 3. Register Settings Listeners
 
 
 
 
 
 
 
 
66
  dom.apiKeyInput.addEventListener("input", (e) => {
67
  STATE.apiKey = e.target.value;
68
  localStorage.setItem("step_api_key", STATE.apiKey);
@@ -89,8 +121,9 @@ async function initializeApp() {
89
  dom.temperatureVal.textContent = STATE.temperature.toFixed(1);
90
  });
91
 
92
- // 4. Register Files Upload Listeners
93
- dom.quickUploadTrigger.addEventListener("click", () => dom.fileUploader.click());
 
94
  dom.dropZone.addEventListener("click", () => dom.fileUploader.click());
95
  dom.fileUploader.addEventListener("change", handleFileSelection);
96
 
@@ -115,29 +148,42 @@ async function initializeApp() {
115
  processFiles(files);
116
  });
117
 
118
- // 5. Chat Operations
119
- dom.sendBtn.addEventListener("click", triggerPromptSubmission);
120
- dom.promptInput.addEventListener("keydown", (e) => {
 
 
 
 
 
 
 
 
 
121
  if (e.key === "Enter" && !e.shiftKey) {
122
  e.preventDefault();
123
- triggerPromptSubmission();
124
  }
125
  });
126
 
 
127
  dom.clearChatBtn.addEventListener("click", resetSandbox);
 
128
 
129
- // 6. Recipe Recipes Console Setup
130
- dom.recipeCards.forEach(card => {
131
  card.addEventListener("click", () => {
132
  const recipeType = card.getAttribute("data-recipe");
133
  loadRecipe(recipeType);
134
  });
135
  });
136
 
137
- // Auto-expand input textbox
138
- dom.promptInput.addEventListener("input", () => {
139
- dom.promptInput.style.height = "auto";
140
- dom.promptInput.style.height = (dom.promptInput.scrollHeight) + "px";
 
 
141
  });
142
  }
143
 
@@ -167,14 +213,14 @@ function processFiles(files) {
167
  });
168
  }
169
 
170
- // Update the Visual Attachments list
171
  function updateShelfUI() {
172
- // Clear inventory container
173
- dom.shelfInventory.innerHTML = "";
174
- dom.quickShelfPreview.innerHTML = "";
175
 
176
  if (STATE.uploadedFiles.length === 0) {
177
- dom.shelfInventory.innerHTML = `<div class="empty-shelf-text">No media loaded. Files are held in local state for your next prompts.</div>`;
178
  return;
179
  }
180
 
@@ -202,7 +248,7 @@ function updateShelfUI() {
202
  </div>
203
  </div>
204
  <button class="media-chip-remove" data-id="${file.id}">
205
- <svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
206
  </button>
207
  `;
208
 
@@ -210,38 +256,45 @@ function updateShelfUI() {
210
  removeFile(file.id);
211
  });
212
 
213
- dom.shelfInventory.appendChild(chip);
214
 
215
- // 2. Chat Quick Preview Item
216
- const previewItem = document.createElement("div");
217
- previewItem.className = "quick-preview-item";
218
- previewItem.title = file.name;
219
-
220
- if (file.type.startsWith("image/")) {
221
- previewItem.innerHTML = `<img src="${file.base64}"><div class="quick-preview-badge"></div>`;
222
- } else {
223
- previewItem.innerHTML = `<div class="media-chip-preview" style="width:100%;height:100%;">🎬</div><div class="quick-preview-badge"></div>`;
224
- }
225
 
226
- dom.quickShelfPreview.appendChild(previewItem);
 
 
227
  });
228
  }
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  function removeFile(id) {
231
  STATE.uploadedFiles = STATE.uploadedFiles.filter(f => f.id !== id);
232
  updateShelfUI();
233
  }
234
 
235
- // Load Play Recipes template prompts
236
  function loadRecipe(recipeType) {
237
- // Standard mock files
238
  const mockImageBase64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=";
239
-
240
- // Reset shelf
241
  STATE.uploadedFiles = [];
242
 
 
 
243
  if (recipeType === "whiteboard") {
244
- dom.promptInput.value = "Here is a snapshot of our whiteboard project plan sketch. Translate this visual sequence of tasks and boxes into a clean, itemized roadmap plan with a structured markdown table.";
245
  STATE.uploadedFiles.push({
246
  id: "recipe-whiteboard",
247
  name: "whiteboard_gantt.png",
@@ -250,7 +303,7 @@ function loadRecipe(recipeType) {
250
  base64: mockImageBase64
251
  });
252
  } else if (recipeType === "code") {
253
- dom.promptInput.value = "Review this dashboard interface design. Generate beautiful, fully responsive HTML structure styling using elegant flex grids to match it.";
254
  STATE.uploadedFiles.push({
255
  id: "recipe-dashboard",
256
  name: "dashboard_layout.png",
@@ -259,7 +312,7 @@ function loadRecipe(recipeType) {
259
  base64: mockImageBase64
260
  });
261
  } else if (recipeType === "receipt") {
262
- dom.promptInput.value = "Tabulate the items inside this receipt into a clean markdown table. Breakdown the tax and total, and perform a step-by-step reasoning calculation to check if the item sums match the total amount listed.";
263
  STATE.uploadedFiles.push({
264
  id: "recipe-receipt",
265
  name: "grocery_bill.jpg",
@@ -268,40 +321,48 @@ function loadRecipe(recipeType) {
268
  base64: mockImageBase64
269
  });
270
  } else if (recipeType === "diagnostic") {
271
- dom.promptInput.value = "This is a recorded visual sequence of steps leading to a runtime exception crash. Provide a detailed reconstruction of the event timeline, summarize the diagnostic signals, and suggest an engineering hotfix.";
272
- // Simulating loading a short recording clip
273
  STATE.uploadedFiles.push({
274
  id: "recipe-diagnostics",
275
  name: "console_bug.mp4",
276
  type: "video/mp4",
277
  size: "1.45 MB",
278
- base64: mockImageBase64 // Represented via base64 for simplicity
279
  });
280
  }
281
 
 
 
 
 
282
  updateShelfUI();
283
- dom.promptInput.dispatchEvent(new Event("input"));
284
- dom.promptInput.focus();
 
 
 
 
 
 
 
285
  }
286
 
287
- // Submitting prompts to Server
288
- async function triggerPromptSubmission() {
289
  if (STATE.isThinking) return;
290
 
291
- const promptText = dom.promptInput.value.trim();
292
  if (!promptText && STATE.uploadedFiles.length === 0) return;
293
 
294
- // Check key
295
- if (!STATE.apiKey && !dom.apiKeyInput.placeholder.includes("env key")) {
296
- appendSystemLog("StepFun API Key is required. Please set it in the credentials input or configure STEP_API_KEY environment variable.", true);
297
  return;
298
  }
299
 
300
- // Set UI loading state
301
  setLoadingState(true);
302
 
303
- // 1. Format user message content
304
- // Content starts with the user's textual input prompt
305
  const contentArray = [];
306
  if (promptText) {
307
  contentArray.push({
@@ -310,7 +371,7 @@ async function triggerPromptSubmission() {
310
  });
311
  }
312
 
313
- // Append attachments
314
  STATE.uploadedFiles.forEach(file => {
315
  if (file.type.startsWith("image/")) {
316
  contentArray.push({
@@ -334,31 +395,37 @@ async function triggerPromptSubmission() {
334
  content: contentArray
335
  };
336
 
337
- // 2. Add message to visible chat list
 
 
 
 
 
 
338
  appendUserBubble(promptText, STATE.uploadedFiles);
339
 
340
- // Add user message to backend conversation history log
341
  STATE.conversationHistory.push(userMessage);
342
 
343
- // Empty active UI text box and attachment shelf
344
- dom.promptInput.value = "";
345
- dom.promptInput.style.height = "auto";
346
- const currentPromptAttachments = [...STATE.uploadedFiles];
 
 
347
  STATE.uploadedFiles = [];
348
  updateShelfUI();
349
 
350
- // 3. Connect to Gradio.Server API
351
  try {
352
  if (!STATE.gradioClient) {
353
  throw new Error("Gradio server is not connected.");
354
  }
355
 
356
- // Generate response bubble placeholder
357
  const responseId = appendAssistantPlaceholderBubble();
358
  const startTime = Date.now();
359
 
360
- // Call the chat_with_step endpoint
361
- // Arguments: [messages_json, api_key, reasoning_effort, max_tokens, temperature]
362
  const result = await STATE.gradioClient.predict("/chat_with_step", [
363
  JSON.stringify(STATE.conversationHistory),
364
  STATE.apiKey,
@@ -368,18 +435,13 @@ async function triggerPromptSubmission() {
368
  ]);
369
 
370
  const duration = ((Date.now() - startTime) / 1000).toFixed(1);
371
-
372
- // Parse results
373
  const data = JSON.parse(result.data[0]);
374
 
375
  if (data.status === "error") {
376
  updateAssistantBubble(responseId, `⚠️ **API Error:** ${data.message}`, "", duration);
377
- STATE.conversationHistory.pop(); // Remove failed prompt from history
378
  } else {
379
- // Update bubble with result markdown and thoughts
380
  updateAssistantBubble(responseId, data.content, data.reasoning_content, duration);
381
-
382
- // Append assistant completion to thread history
383
  STATE.conversationHistory.push({
384
  role: "assistant",
385
  content: data.content
@@ -395,26 +457,24 @@ async function triggerPromptSubmission() {
395
  setLoadingState(false);
396
  }
397
 
398
- // UI State Switcher
399
  function setLoadingState(loading) {
400
  STATE.isThinking = loading;
401
  if (loading) {
402
- dom.sendIcon.style.display = "none";
403
- dom.sendSpinner.style.display = "block";
404
- dom.sendBtn.disabled = true;
 
405
  } else {
406
- dom.sendIcon.style.display = "block";
407
- dom.sendSpinner.style.display = "none";
408
- dom.sendBtn.disabled = false;
 
409
  }
410
  }
411
 
412
- // Append User Chat Message Bubble to Screen
413
  function appendUserBubble(text, files) {
414
- // Hide welcome box if present
415
- const welcome = document.querySelector(".welcome-box");
416
- if (welcome) welcome.style.display = "none";
417
-
418
  const bubble = document.createElement("div");
419
  bubble.className = "message-bubble user";
420
 
@@ -449,11 +509,11 @@ function appendUserBubble(text, files) {
449
  </div>
450
  `;
451
 
452
- dom.chatMessages.appendChild(bubble);
453
  scrollToBottom();
454
  }
455
 
456
- // Append Assistant Loader/Placeholder to Chat Feed
457
  function appendAssistantPlaceholderBubble() {
458
  const id = "assistant-" + Math.random().toString(36).substring(2, 9);
459
  const bubble = document.createElement("div");
@@ -466,7 +526,7 @@ function appendAssistantPlaceholderBubble() {
466
  <div class="thought-container" id="${id}-thought-box">
467
  <div class="thought-header">
468
  <div class="thought-title-group">
469
- <div class="spinner" style="width:12px;height:12px;border-width:1.5px;"></div>
470
  <span>Reasoning...</span>
471
  </div>
472
  <span class="thought-timer" id="${id}-timer">0.0s</span>
@@ -478,10 +538,10 @@ function appendAssistantPlaceholderBubble() {
478
  </div>
479
  `;
480
 
481
- dom.chatMessages.appendChild(bubble);
482
  scrollToBottom();
483
 
484
- // Start UI thought Timer
485
  let seconds = 0.0;
486
  const timerEl = document.getElementById(`${id}-timer`);
487
  const interval = setInterval(() => {
@@ -496,7 +556,7 @@ function appendAssistantPlaceholderBubble() {
496
  return id;
497
  }
498
 
499
- // Update Assistant Placeholder with Final API Results
500
  function updateAssistantBubble(id, content, reasoning, duration) {
501
  const bubble = document.getElementById(id);
502
  if (!bubble) return;
@@ -504,7 +564,6 @@ function updateAssistantBubble(id, content, reasoning, duration) {
504
  const thoughtBox = document.getElementById(`${id}-thought-box`);
505
  const textBox = document.getElementById(`${id}-text-box`);
506
 
507
- // 1. Update Collapsible thoughts
508
  if (reasoning) {
509
  thoughtBox.innerHTML = `
510
  <div class="thought-header" id="${id}-thought-toggle">
@@ -519,20 +578,16 @@ function updateAssistantBubble(id, content, reasoning, duration) {
519
  <div class="thought-content">${escapeHtml(reasoning)}</div>
520
  `;
521
 
522
- // Add toggle expand/collapse behavior
523
  const toggleBtn = document.getElementById(`${id}-thought-toggle`);
524
  toggleBtn.addEventListener("click", () => {
525
  thoughtBox.classList.toggle("collapsed");
526
  });
527
  } else {
528
- // If model skipped reasoning, hide thinking layout entirely
529
  thoughtBox.style.display = "none";
530
  }
531
 
532
- // 2. Render final markdown text
533
  textBox.innerHTML = marked.parse(content);
534
 
535
- // Highlight code blocks
536
  textBox.querySelectorAll("pre code").forEach((el) => {
537
  hljs.highlightElement(el);
538
  });
@@ -540,41 +595,48 @@ function updateAssistantBubble(id, content, reasoning, duration) {
540
  scrollToBottom();
541
  }
542
 
543
- // Reset chat log
544
  function resetSandbox() {
545
  STATE.conversationHistory = [];
546
  STATE.uploadedFiles = [];
547
  updateShelfUI();
548
- dom.chatMessages.innerHTML = `
549
- <div class="welcome-box">
550
- <div class="welcome-icon">⚡</div>
551
- <h2>Sandbox Reset</h2>
552
- <p>Conversation log history has been cleared. Load a recipe below or upload files to begin fresh.</p>
553
- </div>
554
- `;
555
- appendSystemLog("Conversation context cleared successfully.");
 
 
 
 
 
 
556
  }
557
 
558
- // Helper: Append system errors or status notifications
559
  function appendSystemLog(message, isError = false) {
 
 
 
 
 
 
560
  const log = document.createElement("div");
561
  log.className = "message-bubble assistant";
562
  log.innerHTML = `
563
  <div class="message-meta">System</div>
564
- <div class="message-body" style="background-color: ${isError ? 'rgba(244,63,94,0.06)' : 'rgba(20,184,166,0.06)'}; border-color: ${isError ? 'rgba(244,63,94,0.2)' : 'rgba(20,184,166,0.2)'};">
565
- <div class="message-text" style="color: ${isError ? varColor('danger') : varColor('accent-teal')}; font-size:12px; font-weight:500;">
566
  ${isError ? '🛑' : 'ℹ️'} ${message}
567
  </div>
568
  </div>
569
  `;
570
- dom.chatMessages.appendChild(log);
571
  scrollToBottom();
572
  }
573
 
574
- function varColor(name) {
575
- return getComputedStyle(document.documentElement).getPropertyValue(`--${name}`).trim();
576
- }
577
-
578
  function escapeHtml(text) {
579
  if (!text) return "";
580
  return text
@@ -586,8 +648,8 @@ function escapeHtml(text) {
586
  }
587
 
588
  function scrollToBottom() {
589
- dom.chatMessages.scrollTop = dom.chatMessages.scrollHeight;
590
  }
591
 
592
- // Initialise Application when loaded
593
  window.addEventListener("DOMContentLoaded", initializeApp);
 
8
  maxTokens: 2048,
9
  temperature: 0.7,
10
  uploadedFiles: [], // Current prompt attachments
11
+ conversationHistory: [], // Sent to StepFun API
12
  gradioClient: null,
13
  isThinking: false
14
  };
15
 
16
+ // DOM Elements hooks
17
  const dom = {
18
  apiKeyInput: document.getElementById("api-key-input"),
19
  toggleKeyVisibility: document.getElementById("toggle-key-visibility"),
 
22
  maxTokensVal: document.getElementById("max-tokens-val"),
23
  temperatureSlider: document.getElementById("temperature-slider"),
24
  temperatureVal: document.getElementById("temperature-val"),
25
+
26
+ // Sidebar drawers
27
+ sidebarLeft: document.getElementById("sidebar-left"),
28
+ sidebarRight: document.getElementById("sidebar-right"),
29
+ btnToggleLeft: document.getElementById("btn-toggle-left"),
30
+ btnToggleRight: document.getElementById("btn-toggle-right"),
31
+
32
+ // Viewports
33
+ studioDashboard: document.getElementById("studio-dashboard"),
34
+ chatThreadContainer: document.getElementById("chat-thread-container"),
35
+ chatMessagesFeed: document.getElementById("chat-messages-feed"),
36
+
37
+ // Main Console Box Elements (Dashboard view)
38
+ studioPromptInput: document.getElementById("studio-prompt-input"),
39
+ innerShelfPreview: document.getElementById("inner-shelf-preview"),
40
+ studioUploadTrigger: document.getElementById("studio-upload-trigger"),
41
+ studioSendBtn: document.getElementById("studio-send-button"),
42
+ studioSpinner: document.getElementById("studio-spinner"),
43
+
44
+ // Mini Console Box Elements (Chat thread view)
45
+ miniPromptInput: document.getElementById("mini-prompt-input"),
46
+ miniShelfPreview: document.getElementById("mini-shelf-preview"),
47
+ miniUploadTrigger: document.getElementById("mini-upload-trigger"),
48
+ miniSendBtn: document.getElementById("mini-send-button"),
49
+ miniSpinner: document.getElementById("mini-spinner"),
50
+
51
+ // Core file upload console
52
  fileUploader: document.getElementById("file-uploader"),
53
+ shelfList: document.getElementById("shelf-list"),
54
+ dropZone: document.getElementById("drop-zone"),
55
+
56
+ // Resets
57
  clearChatBtn: document.getElementById("clear-chat-button"),
58
+ menuNewChat: document.getElementById("menu-new-chat"),
59
+
60
+ // Showcase cards
61
+ showcaseCards: document.querySelectorAll(".showcase-card")
 
 
 
62
  };
63
 
64
  // Markdown configuration
 
79
 
80
  // 2. Connect Gradio Client
81
  try {
 
82
  STATE.gradioClient = await Client.connect(window.location.origin);
83
  console.log("Successfully connected to Gradio.Server backend.");
84
  } catch (e) {
85
  console.error("Gradio Client Connection Failed:", e);
86
+ appendSystemLog("Gradio Server connection is restricted.", true);
87
  }
88
 
89
+ // 3. Register Sidebar Toggles
90
+ dom.btnToggleLeft.addEventListener("click", () => {
91
+ dom.sidebarLeft.classList.toggle("collapsed");
92
+ });
93
+ dom.btnToggleRight.addEventListener("click", () => {
94
+ dom.sidebarRight.classList.toggle("collapsed");
95
+ });
96
+
97
+ // 4. Register Settings Listeners
98
  dom.apiKeyInput.addEventListener("input", (e) => {
99
  STATE.apiKey = e.target.value;
100
  localStorage.setItem("step_api_key", STATE.apiKey);
 
121
  dom.temperatureVal.textContent = STATE.temperature.toFixed(1);
122
  });
123
 
124
+ // 5. Register File Upload Anchors
125
+ dom.studioUploadTrigger.addEventListener("click", () => dom.fileUploader.click());
126
+ dom.miniUploadTrigger.addEventListener("click", () => dom.fileUploader.click());
127
  dom.dropZone.addEventListener("click", () => dom.fileUploader.click());
128
  dom.fileUploader.addEventListener("change", handleFileSelection);
129
 
 
148
  processFiles(files);
149
  });
150
 
151
+ // 6. Submit Triggers
152
+ dom.studioSendBtn.addEventListener("click", () => triggerPromptSubmission(dom.studioPromptInput));
153
+ dom.miniSendBtn.addEventListener("click", () => triggerPromptSubmission(dom.miniPromptInput));
154
+
155
+ dom.studioPromptInput.addEventListener("keydown", (e) => {
156
+ if (e.key === "Enter" && !e.shiftKey) {
157
+ e.preventDefault();
158
+ triggerPromptSubmission(dom.studioPromptInput);
159
+ }
160
+ });
161
+
162
+ dom.miniPromptInput.addEventListener("keydown", (e) => {
163
  if (e.key === "Enter" && !e.shiftKey) {
164
  e.preventDefault();
165
+ triggerPromptSubmission(dom.miniPromptInput);
166
  }
167
  });
168
 
169
+ // 7. Resets
170
  dom.clearChatBtn.addEventListener("click", resetSandbox);
171
+ dom.menuNewChat.addEventListener("click", resetSandbox);
172
 
173
+ // 8. Recipe Recipes Console Setup
174
+ dom.showcaseCards.forEach(card => {
175
  card.addEventListener("click", () => {
176
  const recipeType = card.getAttribute("data-recipe");
177
  loadRecipe(recipeType);
178
  });
179
  });
180
 
181
+ // Auto-expand input textareas
182
+ [dom.studioPromptInput, dom.miniPromptInput].forEach(textarea => {
183
+ textarea.addEventListener("input", () => {
184
+ textarea.style.height = "auto";
185
+ textarea.style.height = (textarea.scrollHeight) + "px";
186
+ });
187
  });
188
  }
189
 
 
213
  });
214
  }
215
 
216
+ // Update UI Attachment Previews
217
  function updateShelfUI() {
218
+ dom.shelfList.innerHTML = "";
219
+ dom.innerShelfPreview.innerHTML = "";
220
+ dom.miniShelfPreview.innerHTML = "";
221
 
222
  if (STATE.uploadedFiles.length === 0) {
223
+ dom.shelfList.innerHTML = `<div class="empty-shelf-text">No active attachments loaded. Upload images or video clips.</div>`;
224
  return;
225
  }
226
 
 
248
  </div>
249
  </div>
250
  <button class="media-chip-remove" data-id="${file.id}">
251
+ <svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
252
  </button>
253
  `;
254
 
 
256
  removeFile(file.id);
257
  });
258
 
259
+ dom.shelfList.appendChild(chip);
260
 
261
+ // 2. Dashboard Inner Console Preview
262
+ const previewItemDash = createPreviewThumb(file);
263
+ dom.innerShelfPreview.appendChild(previewItemDash);
 
 
 
 
 
 
 
264
 
265
+ // 3. Mini Input Preview
266
+ const previewItemMini = createPreviewThumb(file);
267
+ dom.miniShelfPreview.appendChild(previewItemMini);
268
  });
269
  }
270
 
271
+ function createPreviewThumb(file) {
272
+ const previewItem = document.createElement("div");
273
+ previewItem.className = "quick-preview-item";
274
+ previewItem.title = file.name;
275
+
276
+ if (file.type.startsWith("image/")) {
277
+ previewItem.innerHTML = `<img src="${file.base64}"><div class="quick-preview-badge"></div>`;
278
+ } else {
279
+ previewItem.innerHTML = `<div class="media-chip-preview" style="width:100%;height:100%;">🎬</div><div class="quick-preview-badge"></div>`;
280
+ }
281
+ return previewItem;
282
+ }
283
+
284
  function removeFile(id) {
285
  STATE.uploadedFiles = STATE.uploadedFiles.filter(f => f.id !== id);
286
  updateShelfUI();
287
  }
288
 
289
+ // Load Cookbook Showcase Recipes
290
  function loadRecipe(recipeType) {
 
291
  const mockImageBase64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=";
 
 
292
  STATE.uploadedFiles = [];
293
 
294
+ let promptText = "";
295
+
296
  if (recipeType === "whiteboard") {
297
+ promptText = "Here is a snapshot of our whiteboard project plan sketch. Translate this visual sequence of tasks and boxes into a clean, itemized roadmap plan with a structured markdown table.";
298
  STATE.uploadedFiles.push({
299
  id: "recipe-whiteboard",
300
  name: "whiteboard_gantt.png",
 
303
  base64: mockImageBase64
304
  });
305
  } else if (recipeType === "code") {
306
+ promptText = "Review this dashboard interface design. Generate beautiful, fully responsive HTML structure styling using elegant flex grids to match it.";
307
  STATE.uploadedFiles.push({
308
  id: "recipe-dashboard",
309
  name: "dashboard_layout.png",
 
312
  base64: mockImageBase64
313
  });
314
  } else if (recipeType === "receipt") {
315
+ promptText = "Tabulate the items inside this receipt into a clean markdown table. Breakdown the tax and total, and perform a step-by-step reasoning calculation to check if the item sums match the total amount listed.";
316
  STATE.uploadedFiles.push({
317
  id: "recipe-receipt",
318
  name: "grocery_bill.jpg",
 
321
  base64: mockImageBase64
322
  });
323
  } else if (recipeType === "diagnostic") {
324
+ promptText = "This is a recorded visual sequence of steps leading to a runtime exception crash. Provide a detailed reconstruction of the event timeline, summarize the diagnostic signals, and suggest an engineering hotfix.";
 
325
  STATE.uploadedFiles.push({
326
  id: "recipe-diagnostics",
327
  name: "console_bug.mp4",
328
  type: "video/mp4",
329
  size: "1.45 MB",
330
+ base64: mockImageBase64
331
  });
332
  }
333
 
334
+ // Set value in BOTH text areas so that it works regardless of viewport state
335
+ dom.studioPromptInput.value = promptText;
336
+ dom.miniPromptInput.value = promptText;
337
+
338
  updateShelfUI();
339
+ dom.studioPromptInput.dispatchEvent(new Event("input"));
340
+ dom.miniPromptInput.dispatchEvent(new Event("input"));
341
+
342
+ // Focus active textarea
343
+ if (dom.studioDashboard.style.display !== "none") {
344
+ dom.studioPromptInput.focus();
345
+ } else {
346
+ dom.miniPromptInput.focus();
347
+ }
348
  }
349
 
350
+ // Submit prompt values to Gradio.Server API
351
+ async function triggerPromptSubmission(inputElement) {
352
  if (STATE.isThinking) return;
353
 
354
+ const promptText = inputElement.value.trim();
355
  if (!promptText && STATE.uploadedFiles.length === 0) return;
356
 
357
+ // Check credentials
358
+ if (!STATE.apiKey && !dom.apiKeyInput.placeholder.includes("server env")) {
359
+ appendSystemLog("StepFun API Key is required. Please set it in the Credentials settings card.", true);
360
  return;
361
  }
362
 
 
363
  setLoadingState(true);
364
 
365
+ // 1. Format user message contents
 
366
  const contentArray = [];
367
  if (promptText) {
368
  contentArray.push({
 
371
  });
372
  }
373
 
374
+ // Attachments
375
  STATE.uploadedFiles.forEach(file => {
376
  if (file.type.startsWith("image/")) {
377
  contentArray.push({
 
395
  content: contentArray
396
  };
397
 
398
+ // 2. Transition dashboard to Chat Thread view
399
+ if (dom.studioDashboard.style.display !== "none") {
400
+ dom.studioDashboard.style.display = "none";
401
+ dom.chatThreadContainer.style.display = "flex";
402
+ }
403
+
404
+ // Append to UI thread list
405
  appendUserBubble(promptText, STATE.uploadedFiles);
406
 
407
+ // Append to backend log history
408
  STATE.conversationHistory.push(userMessage);
409
 
410
+ // Clear active UI containers
411
+ dom.studioPromptInput.value = "";
412
+ dom.miniPromptInput.value = "";
413
+ dom.studioPromptInput.style.height = "auto";
414
+ dom.miniPromptInput.style.height = "auto";
415
+
416
  STATE.uploadedFiles = [];
417
  updateShelfUI();
418
 
419
+ // 3. Connect API Call
420
  try {
421
  if (!STATE.gradioClient) {
422
  throw new Error("Gradio server is not connected.");
423
  }
424
 
 
425
  const responseId = appendAssistantPlaceholderBubble();
426
  const startTime = Date.now();
427
 
428
+ // Call our gradio.Server api endpoint
 
429
  const result = await STATE.gradioClient.predict("/chat_with_step", [
430
  JSON.stringify(STATE.conversationHistory),
431
  STATE.apiKey,
 
435
  ]);
436
 
437
  const duration = ((Date.now() - startTime) / 1000).toFixed(1);
 
 
438
  const data = JSON.parse(result.data[0]);
439
 
440
  if (data.status === "error") {
441
  updateAssistantBubble(responseId, `⚠️ **API Error:** ${data.message}`, "", duration);
442
+ STATE.conversationHistory.pop(); // Remove failed prompt
443
  } else {
 
444
  updateAssistantBubble(responseId, data.content, data.reasoning_content, duration);
 
 
445
  STATE.conversationHistory.push({
446
  role: "assistant",
447
  content: data.content
 
457
  setLoadingState(false);
458
  }
459
 
460
+ // UI spinner state toggles
461
  function setLoadingState(loading) {
462
  STATE.isThinking = loading;
463
  if (loading) {
464
+ dom.studioSpinner.style.display = "block";
465
+ dom.miniSpinner.style.display = "block";
466
+ dom.studioSendBtn.disabled = true;
467
+ dom.miniSendBtn.disabled = true;
468
  } else {
469
+ dom.studioSpinner.style.display = "none";
470
+ dom.miniSpinner.style.display = "none";
471
+ dom.studioSendBtn.disabled = false;
472
+ dom.miniSendBtn.disabled = false;
473
  }
474
  }
475
 
476
+ // Render User Bubble
477
  function appendUserBubble(text, files) {
 
 
 
 
478
  const bubble = document.createElement("div");
479
  bubble.className = "message-bubble user";
480
 
 
509
  </div>
510
  `;
511
 
512
+ dom.chatMessagesFeed.appendChild(bubble);
513
  scrollToBottom();
514
  }
515
 
516
+ // Render Assistant Placeholder
517
  function appendAssistantPlaceholderBubble() {
518
  const id = "assistant-" + Math.random().toString(36).substring(2, 9);
519
  const bubble = document.createElement("div");
 
526
  <div class="thought-container" id="${id}-thought-box">
527
  <div class="thought-header">
528
  <div class="thought-title-group">
529
+ <div class="spinner-light" style="width:12px;height:12px;border-width:1.5px; border-top-color:#111827; border-left-color:#e5e7eb;"></div>
530
  <span>Reasoning...</span>
531
  </div>
532
  <span class="thought-timer" id="${id}-timer">0.0s</span>
 
538
  </div>
539
  `;
540
 
541
+ dom.chatMessagesFeed.appendChild(bubble);
542
  scrollToBottom();
543
 
544
+ // Start Thought Timer
545
  let seconds = 0.0;
546
  const timerEl = document.getElementById(`${id}-timer`);
547
  const interval = setInterval(() => {
 
556
  return id;
557
  }
558
 
559
+ // Complete Assistant Bubble
560
  function updateAssistantBubble(id, content, reasoning, duration) {
561
  const bubble = document.getElementById(id);
562
  if (!bubble) return;
 
564
  const thoughtBox = document.getElementById(`${id}-thought-box`);
565
  const textBox = document.getElementById(`${id}-text-box`);
566
 
 
567
  if (reasoning) {
568
  thoughtBox.innerHTML = `
569
  <div class="thought-header" id="${id}-thought-toggle">
 
578
  <div class="thought-content">${escapeHtml(reasoning)}</div>
579
  `;
580
 
 
581
  const toggleBtn = document.getElementById(`${id}-thought-toggle`);
582
  toggleBtn.addEventListener("click", () => {
583
  thoughtBox.classList.toggle("collapsed");
584
  });
585
  } else {
 
586
  thoughtBox.style.display = "none";
587
  }
588
 
 
589
  textBox.innerHTML = marked.parse(content);
590
 
 
591
  textBox.querySelectorAll("pre code").forEach((el) => {
592
  hljs.highlightElement(el);
593
  });
 
595
  scrollToBottom();
596
  }
597
 
598
+ // Reset Sandbox Chat Context Logs
599
  function resetSandbox() {
600
  STATE.conversationHistory = [];
601
  STATE.uploadedFiles = [];
602
  updateShelfUI();
603
+
604
+ // Clear feed
605
+ dom.chatMessagesFeed.innerHTML = "";
606
+
607
+ // Show dashboard
608
+ dom.chatThreadContainer.style.display = "none";
609
+ dom.studioDashboard.style.display = "flex";
610
+
611
+ dom.studioPromptInput.value = "";
612
+ dom.miniPromptInput.value = "";
613
+ dom.studioPromptInput.style.height = "auto";
614
+ dom.miniPromptInput.style.height = "auto";
615
+
616
+ appendSystemLog("Workspace sandbox reset successful.");
617
  }
618
 
 
619
  function appendSystemLog(message, isError = false) {
620
+ // If chat container is hidden, we display a standard javascript alert or log it
621
+ if (dom.chatThreadContainer.style.display === "none") {
622
+ console.warn(`System Log: ${message}`);
623
+ return;
624
+ }
625
+
626
  const log = document.createElement("div");
627
  log.className = "message-bubble assistant";
628
  log.innerHTML = `
629
  <div class="message-meta">System</div>
630
+ <div class="message-body" style="background-color: ${isError ? '#fef2f2' : '#f0fdf4'}; border-color: ${isError ? '#fee2e2' : '#bbf7d0'};">
631
+ <div class="message-text" style="color: ${isError ? '#ef4444' : '#16a34a'}; font-size: 11.5px; font-weight: 500;">
632
  ${isError ? '🛑' : 'ℹ️'} ${message}
633
  </div>
634
  </div>
635
  `;
636
+ dom.chatMessagesFeed.appendChild(log);
637
  scrollToBottom();
638
  }
639
 
 
 
 
 
640
  function escapeHtml(text) {
641
  if (!text) return "";
642
  return text
 
648
  }
649
 
650
  function scrollToBottom() {
651
+ dom.chatMessagesFeed.scrollTop = dom.chatMessagesFeed.scrollHeight;
652
  }
653
 
654
+ // Initialise application when DOM is fully set up
655
  window.addEventListener("DOMContentLoaded", initializeApp);
static/index.html CHANGED
@@ -3,13 +3,13 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Step 3.7 Flash - Multimodal Reasoning Playground</title>
7
  <!-- Google Fonts -->
8
  <link rel="preconnect" href="https://fonts.googleapis.com">
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
- <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&family=Inter:wght@300;400;500;600&family=Outfit:wght@400;500;600;700;800&display=swap" rel="stylesheet">
11
  <!-- Highlight.js for Code Highlighting -->
12
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css">
13
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
14
  <!-- Marked.js for Markdown Parsing -->
15
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
@@ -18,188 +18,262 @@
18
  </head>
19
  <body>
20
  <div class="app-container">
21
- <!-- Sidebar Navigation & Parameter Controls -->
22
- <aside class="sidebar">
23
- <div class="sidebar-header">
24
- <div class="logo-container">
25
- <span class="logo-icon">⚡</span>
26
- <div class="logo-text">
27
- <h1>Step 3.7 Flash</h1>
28
- <span class="badge">NATIVE MULTIMODAL</span>
29
- </div>
 
 
30
  </div>
31
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- <div class="sidebar-content">
34
- <!-- API Credentials -->
35
- <section class="config-section">
36
- <h2 class="section-title">Credentials</h2>
37
- <div class="form-group">
38
- <label for="api-key-input">StepFun API Key</label>
39
- <div class="password-wrapper">
40
- <input type="password" id="api-key-input" placeholder="Enter your key or use env key" autocomplete="off">
41
- <button type="button" id="toggle-key-visibility" class="icon-button" title="Show/Hide Key">
42
- <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>
43
- </button>
44
- </div>
45
- <span class="input-tip">Visit StepFun Console to obtain an API Key.</span>
46
- </div>
47
- </section>
48
 
49
- <!-- Model Parameters -->
50
- <section class="config-section">
51
- <h2 class="section-title">Parameters</h2>
 
 
 
52
 
53
- <div class="form-group">
54
- <label>Reasoning Effort</label>
55
- <div class="effort-picker">
56
- <label class="effort-pill">
57
- <input type="radio" name="reasoning-effort" value="low">
58
- <span>Low</span>
59
- </label>
60
- <label class="effort-pill">
61
- <input type="radio" name="reasoning-effort" value="medium" checked>
62
- <span>Medium</span>
63
- </label>
64
- <label class="effort-pill">
65
- <input type="radio" name="reasoning-effort" value="high">
66
- <span>High</span>
67
- </label>
68
- </div>
69
- <span class="input-tip">Low: fast summary/Q&amp;A. High: complex math/code.</span>
70
- </div>
71
 
72
- <div class="form-group">
73
- <div class="label-row">
74
- <label for="max-tokens-slider">Max Output Tokens</label>
75
- <span class="value-display" id="max-tokens-val">2048</span>
 
 
 
 
 
76
  </div>
77
- <input type="range" id="max-tokens-slider" min="256" max="8192" step="256" value="2048">
78
  </div>
79
 
80
- <div class="form-group">
81
- <div class="label-row">
82
- <label for="temperature-slider">Temperature</label>
83
- <span class="value-display" id="temperature-val">0.7</span>
 
84
  </div>
85
- <input type="range" id="temperature-slider" min="0.0" max="1.5" step="0.1" value="0.7">
86
- </div>
87
- </section>
88
-
89
- <!-- Media Upload Desk -->
90
- <section class="config-section">
91
- <h2 class="section-title">Multimodal Shelf</h2>
92
- <div class="upload-zone" id="drop-zone">
93
- <input type="file" id="file-uploader" accept="image/*,video/*" multiple style="display: none;">
94
- <svg class="upload-icon" viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>
95
- <p>Drag files here or <span>browse</span></p>
96
- <span class="upload-limit">Images (PNG, JPG, WebP) | Videos (&lt;128MB, MP4)</span>
97
- </div>
98
- <div class="shelf-inventory" id="shelf-inventory">
99
- <!-- Uploaded files will populate here dynamically -->
100
- <div class="empty-shelf-text">No media loaded. Files are held in local state for your next prompts.</div>
101
- </div>
102
- </section>
103
-
104
- <!-- Cookbooks & Quick Prompts -->
105
- <section class="config-section">
106
- <h2 class="section-title">Playground Recipes</h2>
107
- <div class="recipes-grid">
108
- <div class="recipe-card" data-recipe="whiteboard">
109
- <span class="recipe-icon">✏️</span>
110
- <div class="recipe-details">
111
- <h3>Whiteboard Plan</h3>
112
- <p>Turn a sketch into a project roadmap table.</p>
113
  </div>
114
- </div>
115
- <div class="recipe-card" data-recipe="code">
116
- <span class="recipe-icon">💻</span>
117
- <div class="recipe-details">
118
- <h3>Screenshot to Code</h3>
119
- <p>Extract design elements to code.</p>
 
 
120
  </div>
121
- </div>
122
- <div class="recipe-card" data-recipe="receipt">
123
- <span class="recipe-icon">📊</span>
124
- <div class="recipe-details">
125
- <h3>Receipt Auditor</h3>
126
- <p>Tabulate receipts with reasoning check.</p>
 
 
 
 
 
 
 
 
 
 
 
127
  </div>
128
  </div>
129
- <div class="recipe-card" data-recipe="diagnostic">
130
- <span class="recipe-icon">🎥</span>
131
- <div class="recipe-details">
132
- <h3>Video Diagnostics</h3>
133
- <p>Reconstruct timeline events from video clip.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  </div>
135
  </div>
136
  </div>
137
- </section>
 
138
  </div>
 
 
 
 
 
139
 
140
- <div class="sidebar-footer">
141
- <div class="connection-status">
142
- <span class="status-indicator online"></span>
143
- <span>Gradio Server Queued</span>
144
- </div>
 
145
  </div>
146
- </aside>
147
 
148
- <!-- Main Chat Area -->
149
- <main class="chat-container">
150
- <header class="chat-header">
151
- <div class="header-main">
152
- <h2>Workspace Playground</h2>
153
- <span class="model-badge">step-3.7-flash</span>
154
- </div>
155
- <div class="header-actions">
156
- <button id="clear-chat-button" class="btn btn-secondary flex-center" title="Reset Sandbox">
157
- <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>
158
- <span>Clear Chat</span>
159
- </button>
 
 
160
  </div>
161
- </header>
162
 
163
- <!-- Chat Message Feed -->
164
- <div class="chat-messages" id="chat-messages">
165
- <!-- Welcome greeting message -->
166
- <div class="welcome-box">
167
- <div class="welcome-icon"></div>
168
- <h2>Welcome to Step 3.7 Flash Playground</h2>
169
- <p>Experience ultra-fast, state-of-the-art multimodal reasoning. This client communicates natively with your backend via <strong>Gradio.Server</strong>, using robust FastAPI queuing and threading.</p>
170
- <div class="feature-tags">
171
- <span class="tag">💡 High-Fidelity Thoughts</span>
172
- <span class="tag">🖼️ Multi-Image Compares</span>
173
- <span class="tag">🎬 Native Video Support</span>
174
- <span class="tag">⚡ Low Latency Queuing</span>
175
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  </div>
177
- </div>
178
 
179
- <!-- Input Controls and Chat Bar -->
180
- <footer class="chat-footer">
181
- <!-- Quick Shelf Preview above input bar -->
182
- <div class="quick-shelf-preview" id="quick-shelf-preview"></div>
183
-
184
- <div class="input-panel">
185
- <button class="btn btn-icon flex-center" id="quick-upload-trigger" title="Attach Media File">
186
- <svg viewBox="0 0 24 24" width="20" height="20" stroke="currentColor" stroke-width="2" fill="none" 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>
187
- </button>
188
-
189
- <div class="textarea-wrapper">
190
- <textarea id="prompt-input" rows="1" placeholder="Ask anything, or drop an image/video here..." autofocus></textarea>
191
  </div>
 
192
 
193
- <button class="btn btn-primary btn-send flex-center" id="send-button" title="Send message to model">
194
- <svg id="send-icon" viewBox="0 0 24 24" width="18" height="18" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
195
- <div id="send-spinner" class="spinner" style="display: none;"></div>
196
- </button>
 
197
  </div>
198
- </footer>
199
- </main>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
200
  </div>
201
 
202
- <!-- Gradio Client JS Library Connection -->
203
  <script type="module" src="/static/app.js"></script>
204
  </body>
205
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>StepFun Studio - Multimodal Playground</title>
7
  <!-- Google Fonts -->
8
  <link rel="preconnect" href="https://fonts.googleapis.com">
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;500;600;700;800&display=swap" rel="stylesheet">
11
  <!-- Highlight.js for Code Highlighting -->
12
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css">
13
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
14
  <!-- Marked.js for Markdown Parsing -->
15
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
 
18
  </head>
19
  <body>
20
  <div class="app-container">
21
+
22
+ <!-- Left Sidebar: Menu Navigation -->
23
+ <aside class="sidebar-left" id="sidebar-left">
24
+ <div class="sidebar-brand">
25
+ <div class="brand-logo">
26
+ <svg viewBox="0 0 100 100" width="28" height="28" fill="currentColor">
27
+ <circle cx="50" cy="50" r="46" fill="#111827"/>
28
+ <path d="M35 30 C35 30, 48 40, 48 55 C48 70, 35 70, 35 70" stroke="#ffffff" stroke-width="8" stroke-linecap="round" fill="none"/>
29
+ <circle cx="62" cy="50" r="7" fill="#ffffff"/>
30
+ </svg>
31
+ <span class="brand-name">StepFun</span>
32
  </div>
33
  </div>
34
+
35
+ <nav class="sidebar-menu">
36
+ <a href="#" class="menu-item active" id="menu-new-chat">
37
+ <svg viewBox="0 0 24 24" width="18" height="18" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 1 1 3 3L12 15l-4 1 1-4z"></path></svg>
38
+ <span>New Chat</span>
39
+ </a>
40
+ <a href="#" class="menu-item">
41
+ <svg viewBox="0 0 24 24" width="18" height="18" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
42
+ <span>search</span>
43
+ </a>
44
+ <a href="#" class="menu-item">
45
+ <svg viewBox="0 0 24 24" width="18" height="18" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9"></rect><rect x="14" y="3" width="7" height="5"></rect><rect x="14" y="12" width="7" height="9"></rect><rect x="3" y="16" width="7" height="5"></rect></svg>
46
+ <span>Showcase Library</span>
47
+ </a>
48
+ <a href="#" class="menu-item">
49
+ <svg viewBox="0 0 24 24" width="18" height="18" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
50
+ <span>My Assets</span>
51
+ </a>
52
+ <a href="#" class="menu-item">
53
+ <svg viewBox="0 0 24 24" width="18" height="18" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line></svg>
54
+ <span>Playground</span>
55
+ </a>
56
+ <div class="menu-separator"></div>
57
+ <a href="#" class="menu-item">
58
+ <svg viewBox="0 0 24 24" width="18" height="18" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="1"></circle><circle cx="19" cy="12" r="1"></circle><circle cx="5" cy="12" r="1"></circle></svg>
59
+ <span>More</span>
60
+ </a>
61
+ </nav>
62
+ </aside>
63
 
64
+ <!-- Main Content Area -->
65
+ <main class="main-workspace">
66
+ <!-- Top Navigation Bar -->
67
+ <header class="workspace-header">
68
+ <button class="icon-toggle-btn" id="btn-toggle-left" title="Toggle Navigation Sidebar">
69
+ <svg viewBox="0 0 24 24" width="20" height="20" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="9" y1="3" x2="9" y2="21"></line></svg>
70
+ </button>
71
+ <div class="header-model-title">
72
+ <span class="studio-badge">Studio</span>
73
+ <span class="model-name">step-3.7-flash</span>
74
+ </div>
75
+ <button class="icon-toggle-btn" id="btn-toggle-right" title="Toggle Settings Sidebar">
76
+ <svg viewBox="0 0 24 24" width="20" height="20" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="15" y1="3" x2="15" y2="21"></line></svg>
77
+ </button>
78
+ </header>
79
 
80
+ <!-- Interactive Workspace Content -->
81
+ <div class="workspace-viewport">
82
+
83
+ <!-- 1. EMPTY CHAT / STUDIO DASHBOARD (Matches Screenshot) -->
84
+ <div class="studio-dashboard" id="studio-dashboard">
85
+ <h2 class="welcome-heading">Hello, welcome to Studio</h2>
86
 
87
+ <!-- Central Search/Input Console -->
88
+ <div class="console-box">
89
+ <textarea id="studio-prompt-input" placeholder="Describe the task you want to verify, or start with the showcase below..."></textarea>
90
+
91
+ <!-- Upload preview shelf internally -->
92
+ <div class="inner-shelf-preview" id="inner-shelf-preview"></div>
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
+ <div class="console-action-row">
95
+ <button class="circle-btn-action" id="studio-upload-trigger" title="Attach Media File">
96
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
97
+ </button>
98
+
99
+ <button class="circle-btn-send" id="studio-send-button" title="Send Task">
100
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="#ffffff" stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="19" x2="12" y2="5"></line><polyline points="5 12 12 5 19 12"></polyline></svg>
101
+ <div class="spinner-light" id="studio-spinner" style="display: none;"></div>
102
+ </button>
103
  </div>
 
104
  </div>
105
 
106
+ <!-- Showcase Section -->
107
+ <div class="showcase-section">
108
+ <div class="showcase-header">
109
+ <h3>Showcase</h3>
110
+ <a href="#" class="view-all-link">View all <span>&rsaquo;</span></a>
111
  </div>
112
+
113
+ <div class="showcase-cards-grid">
114
+ <div class="showcase-card" data-recipe="whiteboard">
115
+ <div class="card-icon-container">
116
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="#3b82f6" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
117
+ </div>
118
+ <h4 class="card-title">Global AI Funding Quarterly Report - Series A Radar</h4>
119
+ <p class="card-desc">Create a single-page HTML real-time dashboard for "Global AI Funding Series A".</p>
120
+ <button class="card-action-btn">Try it</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  </div>
122
+
123
+ <div class="showcase-card" data-recipe="code">
124
+ <div class="card-icon-container">
125
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="#3b82f6" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
126
+ </div>
127
+ <h4 class="card-title">China's New Energy Vehicle Q1 Sales Report</h4>
128
+ <p class="card-desc">Create a one-page HTML report titled "In-depth Observation of China's New Energy..."</p>
129
+ <button class="card-action-btn">Try it</button>
130
  </div>
131
+
132
+ <div class="showcase-card" data-recipe="receipt">
133
+ <div class="card-icon-container">
134
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="#3b82f6" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
135
+ </div>
136
+ <h4 class="card-title">ALS Family Handbook: Living with Time</h4>
137
+ <p class="card-desc">Create a fictional patient education platform called "Mingyan Doctors and Patients..."</p>
138
+ <button class="card-action-btn">Try it</button>
139
+ </div>
140
+
141
+ <div class="showcase-card" data-recipe="diagnostic">
142
+ <div class="card-icon-container">
143
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="#3b82f6" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg>
144
+ </div>
145
+ <h4 class="card-title">In-depth analysis of NVIDIA's Q1 financial report</h4>
146
+ <p class="card-desc">In-depth analysis of NVIDIA's Q1 FY2026 financial report: core financial metrics table...</p>
147
+ <button class="card-action-btn">Try it</button>
148
  </div>
149
  </div>
150
+ </div>
151
+ </div>
152
+
153
+ <!-- 2. ACTIVE CHAT THREAD (Hidden initially) -->
154
+ <div class="chat-thread-container" id="chat-thread-container" style="display: none;">
155
+ <div class="chat-messages-feed" id="chat-messages-feed">
156
+ <!-- Message bubbles populate dynamically -->
157
+ </div>
158
+
159
+ <!-- Bottom fixed mini-input panel -->
160
+ <div class="chat-mini-footer">
161
+ <div class="console-box mini">
162
+ <textarea id="mini-prompt-input" rows="1" placeholder="Reply or ask anything..."></textarea>
163
+ <div class="mini-shelf-preview" id="mini-shelf-preview"></div>
164
+ <div class="console-action-row">
165
+ <button class="circle-btn-action" id="mini-upload-trigger" title="Attach Media File">
166
+ <svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
167
+ </button>
168
+ <button class="circle-btn-send" id="mini-send-button" title="Send Task">
169
+ <svg viewBox="0 0 24 24" width="14" height="14" stroke="#ffffff" stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="19" x2="12" y2="5"></line><polyline points="5 12 12 5 19 12"></polyline></svg>
170
+ <div class="spinner-light" id="mini-spinner" style="display: none;"></div>
171
+ </button>
172
  </div>
173
  </div>
174
  </div>
175
+ </div>
176
+
177
  </div>
178
+ </main>
179
+
180
+ <!-- Right Sidebar: Configuration Cards & Multimodal Shelf (Matches right blocks) -->
181
+ <aside class="sidebar-right" id="sidebar-right">
182
+ <input type="file" id="file-uploader" accept="image/*,video/*" multiple style="display: none;">
183
 
184
+ <div class="right-sidebar-header">
185
+ <h3>Workspace Settings</h3>
186
+ <button id="clear-chat-button" class="btn-clear-chat" title="Clear Context Logs">
187
+ <svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
188
+ <span>Reset Chat</span>
189
+ </button>
190
  </div>
 
191
 
192
+ <div class="sidebar-right-content">
193
+
194
+ <!-- Credentials Card -->
195
+ <div class="right-block-card">
196
+ <div class="card-block-header">
197
+ <span class="block-title">Credentials</span>
198
+ <svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" class="block-icon"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg>
199
+ </div>
200
+ <div class="password-wrapper">
201
+ <input type="password" id="api-key-input" placeholder="Enter key or uses server env..." autocomplete="off">
202
+ <button type="button" id="toggle-key-visibility" class="visibility-toggle" title="Show/Hide">
203
+ <svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx="12" cy="12" r="3"></circle></svg>
204
+ </button>
205
+ </div>
206
  </div>
 
207
 
208
+ <!-- Parameters Card -->
209
+ <div class="right-block-card">
210
+ <div class="card-block-header">
211
+ <span class="block-title">Reasoning Mode</span>
212
+ <div class="recommend-tag">recommend</div>
 
 
 
 
 
 
 
213
  </div>
214
+ <div class="effort-picker">
215
+ <label class="effort-pill">
216
+ <input type="radio" name="reasoning-effort" value="low">
217
+ <span>Low</span>
218
+ </label>
219
+ <label class="effort-pill">
220
+ <input type="radio" name="reasoning-effort" value="medium" checked>
221
+ <span>Medium</span>
222
+ </label>
223
+ <label class="effort-pill">
224
+ <input type="radio" name="reasoning-effort" value="high">
225
+ <span>High</span>
226
+ </label>
227
+ </div>
228
+ <span class="block-tip">Controls the computational budget for Step 3.7 Flash's thought processes.</span>
229
  </div>
 
230
 
231
+ <!-- Generation Limits Card -->
232
+ <div class="right-block-card">
233
+ <div class="card-block-header">
234
+ <span class="block-title">Generation Limits</span>
235
+ <svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" class="block-icon"><polygon points="12 2 2 7 12 12 22 7 12 2"></polygon><polyline points="2 17 12 22 22 17"></polyline><polyline points="2 12 12 17 22 12"></polyline></svg>
236
+ </div>
237
+ <div class="slider-row">
238
+ <label for="max-tokens-slider">Max Output Tokens</label>
239
+ <span class="slider-val" id="max-tokens-val">2048</span>
 
 
 
240
  </div>
241
+ <input type="range" id="max-tokens-slider" min="256" max="8192" step="256" value="2048">
242
 
243
+ <div class="slider-row" style="margin-top: 14px;">
244
+ <label for="temperature-slider">Temperature</label>
245
+ <span class="slider-val" id="temperature-val">0.7</span>
246
+ </div>
247
+ <input type="range" id="temperature-slider" min="0.0" max="1.5" step="0.1" value="0.7">
248
  </div>
249
+
250
+ <!-- Active Shelf uploads Card -->
251
+ <div class="right-block-card flex-1-card">
252
+ <div class="card-block-header">
253
+ <span class="block-title">Multimodal Shelf</span>
254
+ <svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" class="block-icon"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>
255
+ </div>
256
+ <div class="drag-upload-zone" id="drop-zone">
257
+ <svg viewBox="0 0 24 24" width="20" height="20" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>
258
+ <p>Drag files or <span>browse</span></p>
259
+ </div>
260
+ <div class="shelf-list" id="shelf-list">
261
+ <div class="empty-shelf-text">No active attachments loaded. Upload images or video clips.</div>
262
+ </div>
263
+ </div>
264
+ </div>
265
+
266
+ <div class="right-sidebar-footer">
267
+ <div class="status-indicator-group">
268
+ <span class="status-dot online"></span>
269
+ <span>Server Queued</span>
270
+ </div>
271
+ </div>
272
+ </aside>
273
+
274
  </div>
275
 
276
+ <!-- Gradio Client JS library connection -->
277
  <script type="module" src="/static/app.js"></script>
278
  </body>
279
  </html>
static/style.css CHANGED
@@ -1,38 +1,44 @@
1
- /* CSS Variables for Premium Dark Theme Design System */
2
  :root {
3
- --bg-primary: #080c14;
4
- --bg-secondary: #0e1626;
5
- --bg-sidebar: #0a0e1a;
6
- --bg-glass: rgba(15, 23, 42, 0.65);
7
- --border-glass: rgba(99, 102, 241, 0.12);
8
- --border-focus: rgba(99, 102, 241, 0.4);
9
 
10
- --accent-indigo: #6366f1;
11
- --accent-teal: #14b8a6;
12
- --accent-indigo-gradient: linear-gradient(135deg, #6366f1 0%, #4f46e5 100%);
13
- --accent-teal-gradient: linear-gradient(135deg, #14b8a6 0%, #0d9488 100%);
14
- --accent-fusion-gradient: linear-gradient(135deg, #6366f1 0%, #14b8a6 100%);
15
 
16
- --text-primary: #f8fafc;
17
- --text-secondary: #94a3b8;
18
- --text-muted: #64748b;
 
 
 
19
  --danger: #f43f5e;
20
 
21
- --shadow-sm: 0 2px 8px -2px rgba(0, 0, 0, 0.5);
22
- --shadow-md: 0 4px 20px -4px rgba(0, 0, 0, 0.7);
23
- --shadow-lg: 0 10px 30px -5px rgba(0, 0, 0, 0.8), 0 0 20px -2px rgba(99, 102, 241, 0.1);
 
24
 
25
- --font-heading: 'Outfit', sans-serif;
26
- --font-body: 'Inter', sans-serif;
27
  --font-mono: 'Fira Code', monospace;
28
 
29
- --transition-smooth: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
30
  --border-radius-sm: 8px;
31
  --border-radius-md: 12px;
32
  --border-radius-lg: 16px;
 
 
 
 
 
33
  }
34
 
35
- /* Reset & Base Styling */
36
  * {
37
  margin: 0;
38
  padding: 0;
@@ -43,17 +49,18 @@ body {
43
  background-color: var(--bg-primary);
44
  color: var(--text-primary);
45
  font-family: var(--font-body);
46
- font-size: 15px;
47
- line-height: 1.6;
48
  overflow: hidden;
49
  height: 100vh;
50
  width: 100vw;
 
51
  }
52
 
53
- /* Scrollbar styling */
54
  ::-webkit-scrollbar {
55
- width: 6px;
56
- height: 6px;
57
  }
58
 
59
  ::-webkit-scrollbar-track {
@@ -61,659 +68,419 @@ body {
61
  }
62
 
63
  ::-webkit-scrollbar-thumb {
64
- background: rgba(255, 255, 255, 0.1);
65
  border-radius: 10px;
66
  }
67
 
68
  ::-webkit-scrollbar-thumb:hover {
69
- background: rgba(99, 102, 241, 0.3);
70
  }
71
 
72
- /* Container Structure */
73
  .app-container {
74
  display: flex;
75
  height: 100vh;
76
  width: 100vw;
 
77
  position: relative;
78
- background-image:
79
- radial-gradient(at 10% 20%, rgba(99, 102, 241, 0.05) 0px, transparent 50%),
80
- radial-gradient(at 90% 80%, rgba(20, 184, 166, 0.05) 0px, transparent 50%);
81
  }
82
 
83
- /* Sidebar Layout */
84
- .sidebar {
85
- width: 380px;
86
  background-color: var(--bg-sidebar);
87
- border-right: 1px solid var(--border-glass);
88
  display: flex;
89
  flex-direction: column;
90
  height: 100%;
91
  flex-shrink: 0;
92
- backdrop-filter: blur(10px);
 
93
  }
94
 
95
- .sidebar-header {
96
- padding: 24px;
97
- border-bottom: 1px solid var(--border-glass);
 
98
  }
99
 
100
- .logo-container {
101
- display: flex;
102
- align-items: center;
103
- gap: 12px;
104
  }
105
 
106
- .logo-icon {
107
- font-size: 28px;
108
- background: var(--accent-fusion-gradient);
109
- -webkit-background-clip: text;
110
- -webkit-text-fill-color: transparent;
111
- filter: drop-shadow(0 0 8px rgba(99, 102, 241, 0.4));
112
- animation: pulse 3s infinite ease-in-out;
113
  }
114
 
115
- .logo-text h1 {
116
  font-family: var(--font-heading);
117
- font-size: 20px;
118
  font-weight: 700;
119
- letter-spacing: -0.5px;
120
  color: var(--text-primary);
121
  }
122
 
123
- .badge {
124
- font-size: 10px;
125
- font-weight: 600;
126
- text-transform: uppercase;
127
- letter-spacing: 1px;
128
- background: rgba(99, 102, 241, 0.15);
129
- color: var(--accent-indigo);
130
- padding: 2px 8px;
131
- border-radius: 100px;
132
- border: 1px solid rgba(99, 102, 241, 0.25);
133
- display: inline-block;
134
- margin-top: 2px;
135
- }
136
-
137
- .sidebar-content {
138
  flex: 1;
139
- overflow-y: auto;
140
- padding: 24px;
141
  display: flex;
142
  flex-direction: column;
143
- gap: 28px;
 
144
  }
145
 
146
- /* Section styling inside sidebar */
147
- .config-section {
148
  display: flex;
149
- flex-direction: column;
150
- gap: 16px;
 
 
 
 
 
 
 
151
  }
152
 
153
- .section-title {
154
- font-family: var(--font-heading);
155
- font-size: 13px;
 
 
 
 
 
156
  font-weight: 600;
157
- text-transform: uppercase;
158
- letter-spacing: 1.5px;
159
- color: var(--text-secondary);
160
- border-bottom: 1px solid rgba(255, 255, 255, 0.05);
161
- padding-bottom: 8px;
162
  }
163
 
164
- /* Form inputs & Groups */
165
- .form-group {
 
 
 
 
 
 
 
166
  display: flex;
167
  flex-direction: column;
168
- gap: 8px;
 
 
 
169
  }
170
 
171
- .form-group label {
172
- font-family: var(--font-heading);
173
- font-size: 13px;
174
- font-weight: 500;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  color: var(--text-primary);
176
  }
177
 
178
- .label-row {
179
  display: flex;
180
- justify-content: space-between;
181
  align-items: center;
 
182
  }
183
 
184
- .value-display {
185
- font-family: var(--font-mono);
 
186
  font-size: 11px;
187
- background: rgba(255, 255, 255, 0.05);
188
  padding: 1px 6px;
189
  border-radius: 4px;
190
- color: var(--accent-teal);
191
  }
192
 
193
- .input-tip {
194
- font-size: 11px;
195
- color: var(--text-muted);
 
196
  }
197
 
198
- /* Credentials visibility toggler input */
199
- .password-wrapper {
200
- position: relative;
 
201
  display: flex;
202
- align-items: center;
203
  }
204
 
205
- input[type="text"],
206
- input[type="password"] {
 
207
  width: 100%;
208
- background-color: rgba(15, 23, 42, 0.4);
209
- border: 1px solid var(--border-glass);
210
- border-radius: var(--border-radius-sm);
 
 
 
 
 
 
 
 
211
  color: var(--text-primary);
212
- padding: 10px 14px;
213
- font-size: 13px;
214
- font-family: var(--font-body);
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  transition: var(--transition-smooth);
 
216
  }
217
 
218
- input[type="password"] {
219
- padding-right: 40px;
220
  }
221
 
222
- input[type="text"]:focus,
223
- input[type="password"]:focus {
 
224
  outline: none;
225
- border-color: var(--accent-indigo);
226
- box-shadow: 0 0 10px rgba(99, 102, 241, 0.15);
227
- background-color: rgba(15, 23, 42, 0.8);
 
 
 
 
228
  }
229
 
230
- .password-wrapper .icon-button {
231
- position: absolute;
232
- right: 8px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  background: transparent;
234
- border: none;
235
  color: var(--text-secondary);
236
  cursor: pointer;
237
- padding: 6px;
238
- border-radius: 4px;
 
239
  transition: var(--transition-smooth);
240
  }
241
 
242
- .password-wrapper .icon-button:hover {
 
243
  color: var(--text-primary);
244
- background-color: rgba(255, 255, 255, 0.05);
245
- }
246
-
247
- /* Range input customizing */
248
- input[type="range"] {
249
- -webkit-appearance: none;
250
- width: 100%;
251
- height: 4px;
252
- background: rgba(255, 255, 255, 0.1);
253
- border-radius: 2px;
254
- outline: none;
255
  }
256
 
257
- input[type="range"]::-webkit-slider-thumb {
258
- -webkit-appearance: none;
259
- appearance: none;
260
- width: 14px;
261
- height: 14px;
262
  border-radius: 50%;
263
- background: var(--accent-indigo);
 
 
264
  cursor: pointer;
 
 
 
265
  transition: var(--transition-smooth);
266
- box-shadow: 0 0 8px rgba(99, 102, 241, 0.5);
267
  }
268
 
269
- input[type="range"]::-webkit-slider-thumb:hover {
270
- background: var(--accent-teal);
271
- box-shadow: 0 0 10px rgba(20, 184, 166, 0.6);
272
- transform: scale(1.1);
273
  }
274
 
275
- /* Effort Radio Pills Picker */
276
- .effort-picker {
277
- display: flex;
278
- background-color: rgba(15, 23, 42, 0.4);
279
- border: 1px solid var(--border-glass);
280
- padding: 3px;
281
- border-radius: var(--border-radius-sm);
282
- gap: 2px;
283
  }
284
 
285
- .effort-pill {
286
- flex: 1;
287
- cursor: pointer;
288
- text-align: center;
289
- position: relative;
290
  }
291
 
292
- .effort-pill input {
293
- position: absolute;
294
- opacity: 0;
295
- width: 0;
296
- height: 0;
297
  }
298
 
299
- .effort-pill span {
300
- display: block;
301
- padding: 8px 0;
 
 
 
 
 
302
  font-size: 12px;
303
- font-weight: 500;
304
- border-radius: 6px;
305
  color: var(--text-secondary);
306
- transition: var(--transition-smooth);
307
- font-family: var(--font-heading);
308
  }
309
 
310
- .effort-pill input:checked + span {
311
- background: var(--accent-indigo-gradient);
312
  color: var(--text-primary);
313
- box-shadow: var(--shadow-sm);
314
  }
315
 
316
- .effort-pill:hover span:not(input:checked + span) {
317
- color: var(--text-primary);
318
- background-color: rgba(255, 255, 255, 0.03);
 
 
319
  }
320
 
321
- /* Upload zone interface */
322
- .upload-zone {
323
- border: 1.5px dashed var(--border-glass);
 
324
  border-radius: var(--border-radius-md);
325
  padding: 20px;
326
- text-align: center;
327
- cursor: pointer;
328
- background: rgba(15, 23, 42, 0.2);
329
- transition: var(--transition-smooth);
330
  display: flex;
331
  flex-direction: column;
332
- align-items: center;
333
- gap: 8px;
 
334
  }
335
 
336
- .upload-zone:hover {
337
- border-color: var(--accent-indigo);
338
- background: rgba(99, 102, 241, 0.03);
 
 
 
 
 
 
 
 
 
 
 
339
  }
340
 
341
- .upload-zone.drag-active {
342
- border-color: var(--accent-teal);
343
- background: rgba(20, 184, 166, 0.06);
344
- transform: scale(0.98);
 
 
 
345
  }
346
 
347
- .upload-icon {
 
348
  color: var(--text-secondary);
349
- transition: var(--transition-smooth);
 
 
350
  }
351
 
352
- .upload-zone:hover .upload-icon {
353
- color: var(--accent-indigo);
354
- transform: translateY(-2px);
 
 
 
 
 
 
 
 
 
355
  }
356
 
357
- .upload-zone p {
358
- font-size: 13px;
359
- font-weight: 500;
360
  }
361
 
362
- .upload-zone p span {
363
- color: var(--accent-indigo);
364
- font-weight: 600;
 
 
 
 
365
  }
366
 
367
- .upload-limit {
368
- font-size: 10px;
369
- color: var(--text-muted);
 
 
 
 
 
 
 
370
  }
371
 
372
- /* Shelf Inventory */
373
- .shelf-inventory {
374
  display: flex;
375
  flex-direction: column;
376
- gap: 8px;
377
- max-height: 200px;
378
- overflow-y: auto;
379
- padding-right: 4px;
380
- }
381
-
382
- .empty-shelf-text {
383
- font-size: 11px;
384
- color: var(--text-muted);
385
- text-align: center;
386
- padding: 10px 0;
387
- font-style: italic;
388
- }
389
-
390
- .media-chip {
391
- display: flex;
392
- align-items: center;
393
- background-color: rgba(255, 255, 255, 0.03);
394
- border: 1px solid var(--border-glass);
395
- border-radius: var(--border-radius-sm);
396
- padding: 6px 10px;
397
- gap: 10px;
398
- transition: var(--transition-smooth);
399
- position: relative;
400
- overflow: hidden;
401
- }
402
-
403
- .media-chip:hover {
404
- border-color: rgba(99, 102, 241, 0.3);
405
- background-color: rgba(255, 255, 255, 0.05);
406
- }
407
-
408
- .media-chip-preview {
409
- width: 32px;
410
- height: 32px;
411
- border-radius: 4px;
412
- object-fit: cover;
413
- background-color: #000;
414
- flex-shrink: 0;
415
- display: flex;
416
- align-items: center;
417
- justify-content: center;
418
- font-size: 14px;
419
- border: 1px solid rgba(255, 255, 255, 0.1);
420
- }
421
-
422
- .media-chip-preview img,
423
- .media-chip-preview video {
424
- width: 100%;
425
- height: 100%;
426
- object-fit: cover;
427
- }
428
-
429
- .media-chip-details {
430
- flex: 1;
431
- min-width: 0;
432
- }
433
-
434
- .media-chip-name {
435
- font-size: 12px;
436
- font-weight: 500;
437
- white-space: nowrap;
438
- overflow: hidden;
439
- text-overflow: ellipsis;
440
- }
441
-
442
- .media-chip-meta {
443
- font-size: 10px;
444
- color: var(--text-muted);
445
- display: flex;
446
- gap: 8px;
447
- }
448
-
449
- .media-chip-remove {
450
- background: transparent;
451
- border: none;
452
- color: var(--text-muted);
453
- cursor: pointer;
454
- padding: 4px;
455
- border-radius: 4px;
456
- display: flex;
457
- align-items: center;
458
- justify-content: center;
459
- transition: var(--transition-smooth);
460
- }
461
-
462
- .media-chip-remove:hover {
463
- color: var(--danger);
464
- background-color: rgba(244, 63, 94, 0.1);
465
- }
466
-
467
- /* Recipes Grids */
468
- .recipes-grid {
469
- display: grid;
470
- grid-template-columns: 1fr 1fr;
471
- gap: 8px;
472
- }
473
-
474
- .recipe-card {
475
- background-color: rgba(255, 255, 255, 0.02);
476
- border: 1px solid var(--border-glass);
477
- border-radius: var(--border-radius-sm);
478
- padding: 10px;
479
- cursor: pointer;
480
- transition: var(--transition-smooth);
481
- display: flex;
482
- gap: 8px;
483
- align-items: flex-start;
484
- }
485
-
486
- .recipe-card:hover {
487
- background-color: rgba(99, 102, 241, 0.05);
488
- border-color: rgba(99, 102, 241, 0.25);
489
- transform: translateY(-2px);
490
- }
491
-
492
- .recipe-icon {
493
- font-size: 16px;
494
- background: rgba(255, 255, 255, 0.03);
495
- padding: 6px;
496
- border-radius: 6px;
497
- }
498
-
499
- .recipe-details h3 {
500
- font-family: var(--font-heading);
501
- font-size: 11px;
502
- font-weight: 600;
503
- color: var(--text-primary);
504
- }
505
-
506
- .recipe-details p {
507
- font-size: 9px;
508
- color: var(--text-muted);
509
- line-height: 1.3;
510
- margin-top: 2px;
511
- }
512
-
513
- /* Sidebar Footer area */
514
- .sidebar-footer {
515
- padding: 16px 24px;
516
- border-top: 1px solid var(--border-glass);
517
- }
518
-
519
- .connection-status {
520
- display: flex;
521
- align-items: center;
522
- gap: 8px;
523
- font-size: 11px;
524
- color: var(--text-secondary);
525
- }
526
-
527
- .status-indicator {
528
- width: 6px;
529
- height: 6px;
530
- border-radius: 50%;
531
- position: relative;
532
- }
533
-
534
- .status-indicator.online {
535
- background-color: var(--accent-teal);
536
- box-shadow: 0 0 6px var(--accent-teal);
537
- }
538
-
539
- /* Main Chat Container Area */
540
- .chat-container {
541
- flex: 1;
542
- display: flex;
543
- flex-direction: column;
544
- height: 100%;
545
- background-color: rgba(11, 15, 25, 0.5);
546
- }
547
-
548
- /* Chat Header */
549
- .chat-header {
550
- padding: 18px 30px;
551
- border-bottom: 1px solid var(--border-glass);
552
- display: flex;
553
- justify-content: space-between;
554
- align-items: center;
555
- backdrop-filter: blur(10px);
556
- z-index: 10;
557
- }
558
-
559
- .header-main h2 {
560
- font-family: var(--font-heading);
561
- font-size: 16px;
562
- font-weight: 600;
563
- color: var(--text-primary);
564
- }
565
-
566
- .model-badge {
567
- font-family: var(--font-mono);
568
- font-size: 11px;
569
- color: var(--accent-teal);
570
- background-color: rgba(20, 184, 166, 0.08);
571
- border: 1px solid rgba(20, 184, 166, 0.2);
572
- padding: 1px 6px;
573
- border-radius: 4px;
574
- display: inline-block;
575
- margin-top: 2px;
576
- }
577
-
578
- .flex-center {
579
- display: flex;
580
- align-items: center;
581
- justify-content: center;
582
- }
583
-
584
- /* Buttons */
585
- .btn {
586
- border-radius: var(--border-radius-sm);
587
- font-family: var(--font-heading);
588
- font-weight: 500;
589
- font-size: 12px;
590
- cursor: pointer;
591
- transition: var(--transition-smooth);
592
- border: none;
593
- outline: none;
594
- gap: 6px;
595
- }
596
-
597
- .btn-secondary {
598
- background-color: rgba(255, 255, 255, 0.04);
599
- border: 1px solid var(--border-glass);
600
- color: var(--text-primary);
601
- padding: 8px 12px;
602
- }
603
-
604
- .btn-secondary:hover {
605
- background-color: rgba(255, 255, 255, 0.08);
606
- border-color: rgba(255, 255, 255, 0.15);
607
- }
608
-
609
- .btn-icon {
610
- background: transparent;
611
- color: var(--text-secondary);
612
- border: 1px solid var(--border-glass);
613
- border-radius: var(--border-radius-sm);
614
- width: 38px;
615
- height: 38px;
616
- }
617
-
618
- .btn-icon:hover {
619
- color: var(--text-primary);
620
- background-color: rgba(255, 255, 255, 0.05);
621
- border-color: rgba(255, 255, 255, 0.1);
622
- }
623
-
624
- .btn-primary {
625
- background: var(--accent-indigo-gradient);
626
- color: var(--text-primary);
627
- padding: 8px 16px;
628
- font-weight: 600;
629
- box-shadow: 0 4px 12px rgba(99, 102, 241, 0.25);
630
- }
631
-
632
- .btn-primary:hover {
633
- transform: translateY(-1px);
634
- box-shadow: 0 6px 16px rgba(99, 102, 241, 0.35);
635
- }
636
-
637
- .btn-primary:active {
638
- transform: translateY(0);
639
- }
640
-
641
- /* Messages area */
642
- .chat-messages {
643
- flex: 1;
644
- overflow-y: auto;
645
- padding: 30px;
646
- display: flex;
647
- flex-direction: column;
648
- gap: 24px;
649
- }
650
-
651
- /* Welcome Box styling */
652
- .welcome-box {
653
- max-width: 600px;
654
- margin: 40px auto;
655
- background-color: var(--bg-glass);
656
- border: 1px solid var(--border-glass);
657
- padding: 40px;
658
- border-radius: var(--border-radius-lg);
659
- text-align: center;
660
- box-shadow: var(--shadow-lg);
661
- backdrop-filter: blur(16px);
662
- animation: fadeIn 0.6s cubic-bezier(0.16, 1, 0.3, 1);
663
- }
664
-
665
- .welcome-icon {
666
- font-size: 40px;
667
- width: 70px;
668
- height: 70px;
669
- background: rgba(99, 102, 241, 0.1);
670
- border: 1px solid rgba(99, 102, 241, 0.2);
671
- border-radius: 50%;
672
- margin: 0 auto 20px;
673
- display: flex;
674
- align-items: center;
675
- justify-content: center;
676
- box-shadow: 0 0 20px rgba(99, 102, 241, 0.15);
677
- }
678
-
679
- .welcome-box h2 {
680
- font-family: var(--font-heading);
681
- font-size: 22px;
682
- font-weight: 700;
683
- margin-bottom: 12px;
684
- }
685
-
686
- .welcome-box p {
687
- color: var(--text-secondary);
688
- font-size: 13.5px;
689
- margin-bottom: 24px;
690
- line-height: 1.5;
691
- }
692
-
693
- .feature-tags {
694
- display: flex;
695
- flex-wrap: wrap;
696
- gap: 8px;
697
- justify-content: center;
698
- }
699
-
700
- .tag {
701
- font-size: 11px;
702
- font-weight: 500;
703
- background-color: rgba(255, 255, 255, 0.03);
704
- border: 1px solid rgba(255, 255, 255, 0.06);
705
- padding: 4px 10px;
706
- border-radius: 100px;
707
- color: var(--text-secondary);
708
- }
709
-
710
- /* Chat bubble entries */
711
- .message-bubble {
712
- display: flex;
713
- flex-direction: column;
714
- gap: 8px;
715
- max-width: 80%;
716
- animation: slideUp 0.4s cubic-bezier(0.16, 1, 0.3, 1);
717
  }
718
 
719
  .message-bubble.user {
@@ -722,12 +489,13 @@ input[type="range"]::-webkit-slider-thumb:hover {
722
 
723
  .message-bubble.assistant {
724
  align-self: flex-start;
 
725
  }
726
 
727
  .message-meta {
728
  font-size: 11px;
729
  color: var(--text-muted);
730
- font-weight: 500;
731
  margin: 0 4px;
732
  }
733
 
@@ -739,27 +507,25 @@ input[type="range"]::-webkit-slider-thumb:hover {
739
  padding: 14px 18px;
740
  border-radius: var(--border-radius-md);
741
  font-size: 14.5px;
742
- line-height: 1.65;
743
- position: relative;
744
  box-shadow: var(--shadow-sm);
745
  }
746
 
747
  .message-bubble.user .message-body {
748
- background-color: rgba(99, 102, 241, 0.15);
749
- border: 1px solid rgba(99, 102, 241, 0.25);
750
- border-bottom-right-radius: 4px;
751
  color: var(--text-primary);
 
752
  }
753
 
754
  .message-bubble.assistant .message-body {
755
- background-color: var(--bg-glass);
756
- border: 1px solid var(--border-glass);
757
- border-bottom-left-radius: 4px;
758
  color: var(--text-primary);
759
- backdrop-filter: blur(10px);
760
  }
761
 
762
- /* Rendered Multimodal media inside Chat Bubbles */
763
  .bubble-attachments {
764
  display: grid;
765
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
@@ -771,8 +537,8 @@ input[type="range"]::-webkit-slider-thumb:hover {
771
  .bubble-attachment-card {
772
  border-radius: var(--border-radius-sm);
773
  overflow: hidden;
774
- border: 1px solid rgba(255, 255, 255, 0.1);
775
- background-color: #000;
776
  position: relative;
777
  aspect-ratio: 16/10;
778
  }
@@ -789,7 +555,7 @@ input[type="range"]::-webkit-slider-thumb:hover {
789
  bottom: 0;
790
  left: 0;
791
  right: 0;
792
- background: rgba(0, 0, 0, 0.7);
793
  color: #fff;
794
  font-size: 8px;
795
  text-align: center;
@@ -797,13 +563,13 @@ input[type="range"]::-webkit-slider-thumb:hover {
797
  font-family: var(--font-mono);
798
  }
799
 
800
- /* Collapsible Thought Process Box for reasoning models */
801
  .thought-container {
802
- margin-bottom: 12px;
803
- border: 1px solid rgba(99, 102, 241, 0.15);
804
  border-radius: var(--border-radius-sm);
805
  overflow: hidden;
806
- background-color: rgba(99, 102, 241, 0.02);
807
  }
808
 
809
  .thought-header {
@@ -811,14 +577,14 @@ input[type="range"]::-webkit-slider-thumb:hover {
811
  justify-content: space-between;
812
  align-items: center;
813
  padding: 8px 12px;
814
- background-color: rgba(99, 102, 241, 0.05);
815
  cursor: pointer;
816
  user-select: none;
817
  transition: var(--transition-smooth);
818
  }
819
 
820
  .thought-header:hover {
821
- background-color: rgba(99, 102, 241, 0.08);
822
  }
823
 
824
  .thought-title-group {
@@ -826,15 +592,15 @@ input[type="range"]::-webkit-slider-thumb:hover {
826
  align-items: center;
827
  gap: 6px;
828
  font-family: var(--font-heading);
829
- font-size: 11px;
830
  font-weight: 600;
831
- color: var(--accent-indigo);
832
  }
833
 
834
  .thought-timer {
835
  font-family: var(--font-mono);
836
  font-size: 10px;
837
- color: var(--text-secondary);
838
  }
839
 
840
  .thought-toggle-icon {
@@ -852,10 +618,10 @@ input[type="range"]::-webkit-slider-thumb:hover {
852
  font-family: var(--font-mono);
853
  font-size: 12px;
854
  color: var(--text-secondary);
855
- background-color: rgba(8, 12, 20, 0.4);
856
- border-top: 1px solid rgba(99, 102, 241, 0.1);
857
  white-space: pre-wrap;
858
- max-height: 250px;
859
  overflow-y: auto;
860
  }
861
 
@@ -863,22 +629,22 @@ input[type="range"]::-webkit-slider-thumb:hover {
863
  display: none;
864
  }
865
 
866
- /* Markdown typography rules within message bodies */
867
  .markdown-body h1,
868
  .markdown-body h2,
869
  .markdown-body h3 {
870
  margin-top: 14px;
871
- margin-bottom: 8px;
872
  font-family: var(--font-heading);
873
  font-weight: 600;
874
  }
875
 
876
- .markdown-body h1 { font-size: 18px; border-bottom: 1px solid rgba(255,255,255,0.08); padding-bottom: 4px; }
877
- .markdown-body h2 { font-size: 15px; }
878
- .markdown-body h3 { font-size: 13.5px; }
879
 
880
  .markdown-body p {
881
- margin-bottom: 12px;
882
  }
883
 
884
  .markdown-body p:last-child {
@@ -887,171 +653,485 @@ input[type="range"]::-webkit-slider-thumb:hover {
887
 
888
  .markdown-body code {
889
  font-family: var(--font-mono);
890
- font-size: 12px;
891
- background-color: rgba(255, 255, 255, 0.08);
892
- padding: 2px 6px;
893
  border-radius: 4px;
894
- color: var(--accent-teal);
895
  }
896
 
897
  .markdown-body pre {
898
- margin: 14px 0;
899
  border-radius: var(--border-radius-sm);
900
  overflow: hidden;
901
- border: 1px solid rgba(255,255,255,0.08);
902
  }
903
 
904
  .markdown-body pre code {
905
  display: block;
906
- padding: 12px 14px;
907
  overflow-x: auto;
908
- background-color: #0c101b !important;
909
  color: #e2e8f0;
910
  }
911
 
912
  .markdown-body table {
913
  width: 100%;
914
  border-collapse: collapse;
915
- margin: 14px 0;
916
- font-size: 13px;
917
  }
918
 
919
  .markdown-body th,
920
  .markdown-body td {
921
- border: 1px solid rgba(255,255,255,0.08);
922
- padding: 8px 12px;
923
  text-align: left;
924
  }
925
 
926
  .markdown-body th {
927
- background-color: rgba(255,255,255,0.03);
928
  font-weight: 600;
929
  }
930
 
931
- .markdown-body ul,
932
- .markdown-body ol {
933
- margin-left: 20px;
934
- margin-bottom: 12px;
 
935
  }
936
 
937
- .markdown-body li {
938
- margin-bottom: 4px;
 
 
 
 
939
  }
940
 
941
- /* Chat Input Console Footer */
942
- .chat-footer {
943
- padding: 20px 30px;
944
- border-top: 1px solid var(--border-glass);
945
  background-color: var(--bg-sidebar);
 
 
 
 
 
 
 
946
  }
947
 
948
- /* Quick upload preview shelf */
949
- .quick-shelf-preview {
 
 
 
 
 
 
 
950
  display: flex;
951
- gap: 8px;
952
- margin-bottom: 12px;
953
- overflow-x: auto;
954
- padding-bottom: 4px;
955
  }
956
 
957
- .quick-preview-item {
958
- width: 48px;
959
- height: 48px;
960
- border-radius: 6px;
961
- border: 1.5px solid var(--accent-indigo);
962
- overflow: hidden;
963
- position: relative;
964
- flex-shrink: 0;
965
- box-shadow: 0 0 8px rgba(99, 102, 241, 0.3);
 
 
 
 
 
 
 
 
 
 
 
966
  }
967
 
968
- .quick-preview-item img,
969
- .quick-preview-item video {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
970
  width: 100%;
971
- height: 100%;
972
- object-fit: cover;
 
 
 
 
 
 
973
  }
974
 
975
- .quick-preview-badge {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
976
  position: absolute;
977
- top: 2px;
978
- right: 2px;
979
- background-color: var(--accent-teal);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
980
  width: 12px;
981
  height: 12px;
982
  border-radius: 50%;
983
- border: 1px solid #fff;
 
 
984
  }
985
 
986
- /* Actual input bar layout */
987
- .input-panel {
 
 
 
 
 
 
 
 
 
 
 
988
  display: flex;
 
989
  align-items: center;
990
- background-color: rgba(15, 23, 42, 0.4);
991
- border: 1px solid var(--border-glass);
992
- border-radius: var(--border-radius-md);
993
- padding: 6px 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
994
  gap: 8px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
995
  transition: var(--transition-smooth);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
996
  }
997
 
998
- .input-panel:focus-within {
999
- border-color: var(--accent-indigo);
1000
- box-shadow: 0 0 14px rgba(99, 102, 241, 0.1);
1001
- background-color: rgba(15, 23, 42, 0.8);
1002
  }
1003
 
1004
- .textarea-wrapper {
1005
  flex: 1;
 
1006
  }
1007
 
1008
- .input-panel textarea {
1009
- width: 100%;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1010
  background: transparent;
1011
  border: none;
1012
- outline: none;
1013
- color: var(--text-primary);
1014
- font-family: var(--font-body);
1015
- font-size: 14px;
1016
- padding: 10px 4px;
1017
- resize: none;
1018
- max-height: 120px;
 
 
 
1019
  }
1020
 
1021
- .btn-send {
1022
- width: 38px;
1023
- height: 38px;
 
1024
  border-radius: var(--border-radius-sm);
1025
- padding: 0;
 
 
1026
  flex-shrink: 0;
1027
  }
1028
 
1029
- /* Loading animations spinner */
1030
- .spinner {
1031
- width: 16px;
1032
- height: 16px;
1033
- border: 2px solid rgba(255, 255, 255, 0.3);
1034
- border-top: 2px solid #fff;
 
 
 
 
 
 
 
1035
  border-radius: 50%;
1036
- animation: spin 0.8s linear infinite;
1037
  }
1038
 
1039
- @keyframes spin {
1040
- 0% { transform: rotate(0deg); }
1041
- 100% { transform: rotate(360deg); }
 
1042
  }
1043
 
1044
- @keyframes pulse {
1045
- 0%, 100% { transform: scale(1); opacity: 1; }
1046
- 50% { transform: scale(1.05); opacity: 0.85; }
 
 
 
1047
  }
1048
 
1049
- @keyframes fadeIn {
1050
- from { opacity: 0; transform: translateY(10px); }
1051
- to { opacity: 1; transform: translateY(0); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1052
  }
1053
 
1054
  @keyframes slideUp {
1055
- from { opacity: 0; transform: translateY(20px); }
1056
  to { opacity: 1; transform: translateY(0); }
1057
  }
 
1
+ /* StepFun Studio Premium Light Theme Design System */
2
  :root {
3
+ --bg-primary: #f8f9fa;
4
+ --bg-workspace: #f8f9fa;
5
+ --bg-card: #ffffff;
6
+ --bg-sidebar: #ffffff;
7
+ --bg-input: #ffffff;
 
8
 
9
+ --border-color: #e5e7eb;
10
+ --border-light: #f1f2f4;
11
+ --border-focus: #111827;
 
 
12
 
13
+ --accent-blue: #3b82f6;
14
+ --accent-blue-bg: #eff6ff;
15
+
16
+ --text-primary: #111827;
17
+ --text-secondary: #4b5563;
18
+ --text-muted: #9ca3af;
19
  --danger: #f43f5e;
20
 
21
+ --btn-dark-bg: #111827;
22
+ --btn-dark-hover: #1f2937;
23
+ --btn-light-bg: #f3f4f6;
24
+ --btn-light-hover: #e5e7eb;
25
 
26
+ --font-heading: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
27
+ --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
28
  --font-mono: 'Fira Code', monospace;
29
 
30
+ --transition-smooth: all 0.2s ease-in-out;
31
  --border-radius-sm: 8px;
32
  --border-radius-md: 12px;
33
  --border-radius-lg: 16px;
34
+ --border-radius-pill: 100px;
35
+
36
+ --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
37
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.04), 0 2px 4px -1px rgba(0, 0, 0, 0.02);
38
+ --shadow-lg: 0 10px 30px -10px rgba(0, 0, 0, 0.06), 0 1px 3px rgba(0, 0, 0, 0.02);
39
  }
40
 
41
+ /* Reset & Global Styling */
42
  * {
43
  margin: 0;
44
  padding: 0;
 
49
  background-color: var(--bg-primary);
50
  color: var(--text-primary);
51
  font-family: var(--font-body);
52
+ font-size: 14px;
53
+ line-height: 1.5;
54
  overflow: hidden;
55
  height: 100vh;
56
  width: 100vw;
57
+ -webkit-font-smoothing: antialiased;
58
  }
59
 
60
+ /* Scrollbar customization */
61
  ::-webkit-scrollbar {
62
+ width: 5px;
63
+ height: 5px;
64
  }
65
 
66
  ::-webkit-scrollbar-track {
 
68
  }
69
 
70
  ::-webkit-scrollbar-thumb {
71
+ background: #e5e7eb;
72
  border-radius: 10px;
73
  }
74
 
75
  ::-webkit-scrollbar-thumb:hover {
76
+ background: #cbd5e1;
77
  }
78
 
79
+ /* Flex Container Layout */
80
  .app-container {
81
  display: flex;
82
  height: 100vh;
83
  width: 100vw;
84
+ overflow: hidden;
85
  position: relative;
 
 
 
86
  }
87
 
88
+ /* Left Sidebar Menu (Matches screenshot left list) */
89
+ .sidebar-left {
90
+ width: 240px;
91
  background-color: var(--bg-sidebar);
92
+ border-right: 1px solid var(--border-color);
93
  display: flex;
94
  flex-direction: column;
95
  height: 100%;
96
  flex-shrink: 0;
97
+ transition: var(--transition-smooth);
98
+ z-index: 100;
99
  }
100
 
101
+ .sidebar-left.collapsed {
102
+ width: 0;
103
+ overflow: hidden;
104
+ border-right: none;
105
  }
106
 
107
+ .sidebar-brand {
108
+ padding: 24px 20px;
 
 
109
  }
110
 
111
+ .brand-logo {
112
+ display: flex;
113
+ align-items: center;
114
+ gap: 10px;
 
 
 
115
  }
116
 
117
+ .brand-name {
118
  font-family: var(--font-heading);
119
+ font-size: 18px;
120
  font-weight: 700;
121
+ letter-spacing: -0.3px;
122
  color: var(--text-primary);
123
  }
124
 
125
+ .sidebar-menu {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  flex: 1;
 
 
127
  display: flex;
128
  flex-direction: column;
129
+ padding: 10px 12px;
130
+ gap: 4px;
131
  }
132
 
133
+ .menu-item {
 
134
  display: flex;
135
+ align-items: center;
136
+ gap: 12px;
137
+ padding: 10px 14px;
138
+ border-radius: var(--border-radius-sm);
139
+ color: var(--text-secondary);
140
+ text-decoration: none;
141
+ font-weight: 500;
142
+ font-size: 13.5px;
143
+ transition: var(--transition-smooth);
144
  }
145
 
146
+ .menu-item:hover {
147
+ background-color: var(--btn-light-bg);
148
+ color: var(--text-primary);
149
+ }
150
+
151
+ .menu-item.active {
152
+ background-color: var(--btn-light-bg);
153
+ color: var(--text-primary);
154
  font-weight: 600;
 
 
 
 
 
155
  }
156
 
157
+ .menu-separator {
158
+ height: 1px;
159
+ background-color: var(--border-light);
160
+ margin: 8px 14px;
161
+ }
162
+
163
+ /* Main content workspace */
164
+ .main-workspace {
165
+ flex: 1;
166
  display: flex;
167
  flex-direction: column;
168
+ height: 100%;
169
+ background-color: var(--bg-workspace);
170
+ overflow: hidden;
171
+ position: relative;
172
  }
173
 
174
+ /* Header bar */
175
+ .workspace-header {
176
+ height: 56px;
177
+ border-bottom: 1px solid var(--border-color);
178
+ background-color: var(--bg-sidebar);
179
+ display: flex;
180
+ align-items: center;
181
+ justify-content: space-between;
182
+ padding: 0 16px;
183
+ z-index: 10;
184
+ }
185
+
186
+ .icon-toggle-btn {
187
+ background: transparent;
188
+ border: none;
189
+ color: var(--text-secondary);
190
+ cursor: pointer;
191
+ padding: 6px;
192
+ border-radius: var(--border-radius-sm);
193
+ display: flex;
194
+ align-items: center;
195
+ justify-content: center;
196
+ transition: var(--transition-smooth);
197
+ }
198
+
199
+ .icon-toggle-btn:hover {
200
+ background-color: var(--btn-light-bg);
201
  color: var(--text-primary);
202
  }
203
 
204
+ .header-model-title {
205
  display: flex;
 
206
  align-items: center;
207
+ gap: 8px;
208
  }
209
 
210
+ .studio-badge {
211
+ background-color: #f3f4f6;
212
+ color: #4b5563;
213
  font-size: 11px;
214
+ font-weight: 600;
215
  padding: 1px 6px;
216
  border-radius: 4px;
217
+ font-family: var(--font-heading);
218
  }
219
 
220
+ .model-name {
221
+ font-size: 13px;
222
+ font-family: var(--font-mono);
223
+ color: var(--text-secondary);
224
  }
225
 
226
+ /* Viewport structure */
227
+ .workspace-viewport {
228
+ flex: 1;
229
+ overflow-y: auto;
230
  display: flex;
231
+ flex-direction: column;
232
  }
233
 
234
+ /* 1. Studio Dashboard Styling (Matches Screenshot perfectly) */
235
+ .studio-dashboard {
236
+ max-width: 800px;
237
  width: 100%;
238
+ margin: 0 auto;
239
+ padding: 60px 24px;
240
+ display: flex;
241
+ flex-direction: column;
242
+ align-items: center;
243
+ }
244
+
245
+ .welcome-heading {
246
+ font-family: var(--font-heading);
247
+ font-size: 32px;
248
+ font-weight: 600;
249
  color: var(--text-primary);
250
+ margin-bottom: 30px;
251
+ letter-spacing: -0.5px;
252
+ text-align: center;
253
+ }
254
+
255
+ /* Central Search/Prompt Console Box */
256
+ .console-box {
257
+ width: 100%;
258
+ background-color: var(--bg-card);
259
+ border: 1px solid var(--border-color);
260
+ border-radius: var(--border-radius-lg);
261
+ box-shadow: var(--shadow-lg);
262
+ padding: 16px 20px;
263
+ display: flex;
264
+ flex-direction: column;
265
+ gap: 12px;
266
  transition: var(--transition-smooth);
267
+ margin-bottom: 48px;
268
  }
269
 
270
+ .console-box:focus-within {
271
+ border-color: var(--border-focus);
272
  }
273
 
274
+ .console-box textarea {
275
+ width: 100%;
276
+ border: none;
277
  outline: none;
278
+ resize: none;
279
+ font-family: var(--font-body);
280
+ font-size: 15px;
281
+ color: var(--text-primary);
282
+ background: transparent;
283
+ line-height: 1.5;
284
+ min-height: 50px;
285
  }
286
 
287
+ .console-box textarea::placeholder {
288
+ color: var(--text-muted);
289
+ }
290
+
291
+ /* Inside console drawer for attachments */
292
+ .inner-shelf-preview,
293
+ .mini-shelf-preview {
294
+ display: flex;
295
+ flex-wrap: wrap;
296
+ gap: 8px;
297
+ padding-bottom: 4px;
298
+ }
299
+
300
+ /* Console Footer Row containing circle buttons */
301
+ .console-action-row {
302
+ display: flex;
303
+ justify-content: space-between;
304
+ align-items: center;
305
+ border-top: 1px solid var(--border-light);
306
+ padding-top: 12px;
307
+ }
308
+
309
+ /* Action button (e.g. + Circle) */
310
+ .circle-btn-action {
311
+ width: 32px;
312
+ height: 32px;
313
+ border-radius: 50%;
314
+ border: 1px solid var(--border-color);
315
  background: transparent;
 
316
  color: var(--text-secondary);
317
  cursor: pointer;
318
+ display: flex;
319
+ align-items: center;
320
+ justify-content: center;
321
  transition: var(--transition-smooth);
322
  }
323
 
324
+ .circle-btn-action:hover {
325
+ background-color: var(--btn-light-bg);
326
  color: var(--text-primary);
327
+ border-color: var(--btn-light-hover);
 
 
 
 
 
 
 
 
 
 
328
  }
329
 
330
+ /* Send button (e.g. Up-Arrow inside dark circle) */
331
+ .circle-btn-send {
332
+ width: 32px;
333
+ height: 32px;
 
334
  border-radius: 50%;
335
+ border: none;
336
+ background-color: #1f2937;
337
+ color: #ffffff;
338
  cursor: pointer;
339
+ display: flex;
340
+ align-items: center;
341
+ justify-content: center;
342
  transition: var(--transition-smooth);
 
343
  }
344
 
345
+ .circle-btn-send:hover {
346
+ background-color: #111827;
347
+ transform: scale(1.05);
 
348
  }
349
 
350
+ .circle-btn-send:active {
351
+ transform: scale(0.95);
 
 
 
 
 
 
352
  }
353
 
354
+ /* Showcase Grid Layout (Matches Screenshot grid) */
355
+ .showcase-section {
356
+ width: 100%;
 
 
357
  }
358
 
359
+ .showcase-header {
360
+ display: flex;
361
+ justify-content: space-between;
362
+ align-items: center;
363
+ margin-bottom: 16px;
364
  }
365
 
366
+ .showcase-header h3 {
367
+ font-family: var(--font-heading);
368
+ font-size: 15px;
369
+ font-weight: 600;
370
+ color: var(--text-primary);
371
+ }
372
+
373
+ .view-all-link {
374
  font-size: 12px;
 
 
375
  color: var(--text-secondary);
376
+ text-decoration: none;
377
+ font-weight: 500;
378
  }
379
 
380
+ .view-all-link:hover {
 
381
  color: var(--text-primary);
 
382
  }
383
 
384
+ .showcase-cards-grid {
385
+ display: grid;
386
+ grid-template-columns: 1fr 1fr;
387
+ gap: 16px;
388
+ width: 100%;
389
  }
390
 
391
+ /* Showcase Card item (StepFun mockup style) */
392
+ .showcase-card {
393
+ background-color: var(--bg-card);
394
+ border: 1px solid var(--border-color);
395
  border-radius: var(--border-radius-md);
396
  padding: 20px;
 
 
 
 
397
  display: flex;
398
  flex-direction: column;
399
+ transition: var(--transition-smooth);
400
+ position: relative;
401
+ cursor: pointer;
402
  }
403
 
404
+ .showcase-card:hover {
405
+ box-shadow: var(--shadow-lg);
406
+ border-color: #d1d5db;
407
+ }
408
+
409
+ .card-icon-container {
410
+ width: 32px;
411
+ height: 32px;
412
+ border-radius: var(--border-radius-sm);
413
+ background-color: var(--accent-blue-bg);
414
+ display: flex;
415
+ align-items: center;
416
+ justify-content: center;
417
+ margin-bottom: 14px;
418
  }
419
 
420
+ .card-title {
421
+ font-family: var(--font-heading);
422
+ font-size: 13px;
423
+ font-weight: 700;
424
+ line-height: 1.4;
425
+ color: var(--text-primary);
426
+ margin-bottom: 6px;
427
  }
428
 
429
+ .card-desc {
430
+ font-size: 11.5px;
431
  color: var(--text-secondary);
432
+ line-height: 1.4;
433
+ margin-bottom: 20px;
434
+ flex: 1;
435
  }
436
 
437
+ /* Black pill button on bottom left */
438
+ .card-action-btn {
439
+ align-self: flex-start;
440
+ background-color: var(--btn-dark-bg);
441
+ color: #ffffff;
442
+ border: none;
443
+ padding: 6px 16px;
444
+ border-radius: var(--border-radius-pill);
445
+ font-size: 11px;
446
+ font-weight: 600;
447
+ cursor: pointer;
448
+ transition: var(--transition-smooth);
449
  }
450
 
451
+ .card-action-btn:hover {
452
+ background-color: var(--btn-dark-hover);
453
+ transform: translateY(-1px);
454
  }
455
 
456
+ /* 2. Active Chat thread view styling */
457
+ .chat-thread-container {
458
+ flex: 1;
459
+ display: flex;
460
+ flex-direction: column;
461
+ height: 100%;
462
+ overflow: hidden;
463
  }
464
 
465
+ .chat-messages-feed {
466
+ flex: 1;
467
+ overflow-y: auto;
468
+ padding: 30px;
469
+ display: flex;
470
+ flex-direction: column;
471
+ gap: 28px;
472
+ max-width: 800px;
473
+ width: 100%;
474
+ margin: 0 auto;
475
  }
476
 
477
+ /* Chat bubble aesthetics */
478
+ .message-bubble {
479
  display: flex;
480
  flex-direction: column;
481
+ gap: 6px;
482
+ max-width: 85%;
483
+ animation: slideUp 0.3s ease-out;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
484
  }
485
 
486
  .message-bubble.user {
 
489
 
490
  .message-bubble.assistant {
491
  align-self: flex-start;
492
+ width: 100%;
493
  }
494
 
495
  .message-meta {
496
  font-size: 11px;
497
  color: var(--text-muted);
498
+ font-weight: 600;
499
  margin: 0 4px;
500
  }
501
 
 
507
  padding: 14px 18px;
508
  border-radius: var(--border-radius-md);
509
  font-size: 14.5px;
510
+ line-height: 1.6;
 
511
  box-shadow: var(--shadow-sm);
512
  }
513
 
514
  .message-bubble.user .message-body {
515
+ background-color: var(--btn-light-bg);
516
+ border: 1px solid var(--border-color);
 
517
  color: var(--text-primary);
518
+ border-bottom-right-radius: 4px;
519
  }
520
 
521
  .message-bubble.assistant .message-body {
522
+ background-color: var(--bg-card);
523
+ border: 1px solid var(--border-color);
 
524
  color: var(--text-primary);
525
+ border-bottom-left-radius: 4px;
526
  }
527
 
528
+ /* Thumbnail Attachments inside bubbles */
529
  .bubble-attachments {
530
  display: grid;
531
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
 
537
  .bubble-attachment-card {
538
  border-radius: var(--border-radius-sm);
539
  overflow: hidden;
540
+ border: 1px solid var(--border-color);
541
+ background-color: #fafafa;
542
  position: relative;
543
  aspect-ratio: 16/10;
544
  }
 
555
  bottom: 0;
556
  left: 0;
557
  right: 0;
558
+ background: rgba(0, 0, 0, 0.6);
559
  color: #fff;
560
  font-size: 8px;
561
  text-align: center;
 
563
  font-family: var(--font-mono);
564
  }
565
 
566
+ /* Collagen Thoughts Box for reasoning steps */
567
  .thought-container {
568
+ margin-bottom: 14px;
569
+ border: 1px solid var(--border-color);
570
  border-radius: var(--border-radius-sm);
571
  overflow: hidden;
572
+ background-color: #fafbfc;
573
  }
574
 
575
  .thought-header {
 
577
  justify-content: space-between;
578
  align-items: center;
579
  padding: 8px 12px;
580
+ background-color: #f3f4f6;
581
  cursor: pointer;
582
  user-select: none;
583
  transition: var(--transition-smooth);
584
  }
585
 
586
  .thought-header:hover {
587
+ background-color: #e5e7eb;
588
  }
589
 
590
  .thought-title-group {
 
592
  align-items: center;
593
  gap: 6px;
594
  font-family: var(--font-heading);
595
+ font-size: 11.5px;
596
  font-weight: 600;
597
+ color: var(--text-secondary);
598
  }
599
 
600
  .thought-timer {
601
  font-family: var(--font-mono);
602
  font-size: 10px;
603
+ color: var(--text-muted);
604
  }
605
 
606
  .thought-toggle-icon {
 
618
  font-family: var(--font-mono);
619
  font-size: 12px;
620
  color: var(--text-secondary);
621
+ background-color: #ffffff;
622
+ border-top: 1px solid var(--border-color);
623
  white-space: pre-wrap;
624
+ max-height: 200px;
625
  overflow-y: auto;
626
  }
627
 
 
629
  display: none;
630
  }
631
 
632
+ /* Markdown tags within chat response body */
633
  .markdown-body h1,
634
  .markdown-body h2,
635
  .markdown-body h3 {
636
  margin-top: 14px;
637
+ margin-bottom: 6px;
638
  font-family: var(--font-heading);
639
  font-weight: 600;
640
  }
641
 
642
+ .markdown-body h1 { font-size: 16px; border-bottom: 1px solid var(--border-light); padding-bottom: 2px; }
643
+ .markdown-body h2 { font-size: 14.5px; }
644
+ .markdown-body h3 { font-size: 13px; }
645
 
646
  .markdown-body p {
647
+ margin-bottom: 8px;
648
  }
649
 
650
  .markdown-body p:last-child {
 
653
 
654
  .markdown-body code {
655
  font-family: var(--font-mono);
656
+ font-size: 11.5px;
657
+ background-color: var(--btn-light-bg);
658
+ padding: 2px 5px;
659
  border-radius: 4px;
660
+ color: var(--accent-blue);
661
  }
662
 
663
  .markdown-body pre {
664
+ margin: 12px 0;
665
  border-radius: var(--border-radius-sm);
666
  overflow: hidden;
667
+ border: 1px solid var(--border-color);
668
  }
669
 
670
  .markdown-body pre code {
671
  display: block;
672
+ padding: 12px;
673
  overflow-x: auto;
674
+ background-color: #0f172a !important;
675
  color: #e2e8f0;
676
  }
677
 
678
  .markdown-body table {
679
  width: 100%;
680
  border-collapse: collapse;
681
+ margin: 12px 0;
682
+ font-size: 12.5px;
683
  }
684
 
685
  .markdown-body th,
686
  .markdown-body td {
687
+ border: 1px solid var(--border-color);
688
+ padding: 6px 10px;
689
  text-align: left;
690
  }
691
 
692
  .markdown-body th {
693
+ background-color: var(--bg-primary);
694
  font-weight: 600;
695
  }
696
 
697
+ /* Chat Bottom Mini Input bar */
698
+ .chat-mini-footer {
699
+ padding: 16px 24px;
700
+ background-color: var(--bg-workspace);
701
+ border-top: 1px solid var(--border-color);
702
  }
703
 
704
+ .chat-mini-footer .console-box {
705
+ max-width: 800px;
706
+ margin: 0 auto;
707
+ box-shadow: var(--shadow-md);
708
+ padding: 10px 16px;
709
+ border-radius: var(--border-radius-md);
710
  }
711
 
712
+ /* Right Sidebar: Settings (Matches Right Mockup blocks) */
713
+ .sidebar-right {
714
+ width: 320px;
 
715
  background-color: var(--bg-sidebar);
716
+ border-left: 1px solid var(--border-color);
717
+ display: flex;
718
+ flex-direction: column;
719
+ height: 100%;
720
+ flex-shrink: 0;
721
+ transition: var(--transition-smooth);
722
+ z-index: 100;
723
  }
724
 
725
+ .sidebar-right.collapsed {
726
+ width: 0;
727
+ overflow: hidden;
728
+ border-left: none;
729
+ }
730
+
731
+ .right-sidebar-header {
732
+ padding: 16px 20px;
733
+ border-bottom: 1px solid var(--border-color);
734
  display: flex;
735
+ justify-content: space-between;
736
+ align-items: center;
 
 
737
  }
738
 
739
+ .right-sidebar-header h3 {
740
+ font-family: var(--font-heading);
741
+ font-size: 14px;
742
+ font-weight: 600;
743
+ color: var(--text-primary);
744
+ }
745
+
746
+ .btn-clear-chat {
747
+ background: transparent;
748
+ border: 1px solid var(--border-color);
749
+ color: var(--text-secondary);
750
+ padding: 5px 10px;
751
+ border-radius: var(--border-radius-sm);
752
+ font-size: 11px;
753
+ font-weight: 500;
754
+ cursor: pointer;
755
+ display: flex;
756
+ align-items: center;
757
+ gap: 4px;
758
+ transition: var(--transition-smooth);
759
  }
760
 
761
+ .btn-clear-chat:hover {
762
+ background-color: var(--btn-light-bg);
763
+ color: var(--text-primary);
764
+ }
765
+
766
+ .sidebar-right-content {
767
+ flex: 1;
768
+ overflow-y: auto;
769
+ padding: 20px;
770
+ display: flex;
771
+ flex-direction: column;
772
+ gap: 16px;
773
+ }
774
+
775
+ /* Card Block Design (Right cards in screenshot) */
776
+ .right-block-card {
777
+ background-color: var(--bg-card);
778
+ border: 1px solid var(--border-color);
779
+ border-radius: var(--border-radius-md);
780
+ padding: 16px;
781
+ display: flex;
782
+ flex-direction: column;
783
+ gap: 12px;
784
+ }
785
+
786
+ .flex-1-card {
787
+ flex: 1;
788
+ min-height: 200px;
789
+ }
790
+
791
+ .card-block-header {
792
+ display: flex;
793
+ justify-content: space-between;
794
+ align-items: center;
795
+ }
796
+
797
+ .block-title {
798
+ font-family: var(--font-heading);
799
+ font-size: 12.5px;
800
+ font-weight: 700;
801
+ color: var(--text-primary);
802
+ }
803
+
804
+ .block-icon {
805
+ color: var(--text-muted);
806
+ }
807
+
808
+ .block-tip {
809
+ font-size: 10.5px;
810
+ color: var(--text-secondary);
811
+ line-height: 1.3;
812
+ }
813
+
814
+ /* Small recommend Pill tag */
815
+ .recommend-tag {
816
+ font-size: 9px;
817
+ font-weight: 700;
818
+ text-transform: uppercase;
819
+ background-color: #f3f4f6;
820
+ color: #4b5563;
821
+ padding: 1px 6px;
822
+ border-radius: 4px;
823
+ border: 1px solid #e5e7eb;
824
+ }
825
+
826
+ /* Settings Inputs inside cards */
827
+ input[type="password"] {
828
  width: 100%;
829
+ background-color: #fafafa;
830
+ border: 1px solid var(--border-color);
831
+ border-radius: var(--border-radius-sm);
832
+ color: var(--text-primary);
833
+ padding: 8px 12px;
834
+ font-size: 12.5px;
835
+ font-family: var(--font-body);
836
+ transition: var(--transition-smooth);
837
  }
838
 
839
+ input[type="password"]:focus {
840
+ outline: none;
841
+ border-color: var(--border-focus);
842
+ background-color: #ffffff;
843
+ }
844
+
845
+ .password-wrapper {
846
+ position: relative;
847
+ display: flex;
848
+ align-items: center;
849
+ }
850
+
851
+ .password-wrapper input {
852
+ padding-right: 36px;
853
+ }
854
+
855
+ .visibility-toggle {
856
  position: absolute;
857
+ right: 8px;
858
+ background: transparent;
859
+ border: none;
860
+ color: var(--text-muted);
861
+ cursor: pointer;
862
+ padding: 4px;
863
+ border-radius: 4px;
864
+ display: flex;
865
+ align-items: center;
866
+ }
867
+
868
+ .visibility-toggle:hover {
869
+ color: var(--text-primary);
870
+ }
871
+
872
+ /* Choice Picker Pills */
873
+ .effort-picker {
874
+ display: flex;
875
+ background-color: #fafafa;
876
+ border: 1px solid var(--border-color);
877
+ padding: 3px;
878
+ border-radius: var(--border-radius-sm);
879
+ gap: 2px;
880
+ }
881
+
882
+ .effort-pill {
883
+ flex: 1;
884
+ cursor: pointer;
885
+ text-align: center;
886
+ position: relative;
887
+ }
888
+
889
+ .effort-pill input {
890
+ position: absolute;
891
+ opacity: 0;
892
+ width: 0;
893
+ height: 0;
894
+ }
895
+
896
+ .effort-pill span {
897
+ display: block;
898
+ padding: 6px 0;
899
+ font-size: 11.5px;
900
+ font-weight: 500;
901
+ border-radius: 4px;
902
+ color: var(--text-secondary);
903
+ transition: var(--transition-smooth);
904
+ font-family: var(--font-heading);
905
+ }
906
+
907
+ .effort-pill input:checked + span {
908
+ background-color: #111827;
909
+ color: #ffffff;
910
+ }
911
+
912
+ .effort-pill:hover span:not(input:checked + span) {
913
+ color: var(--text-primary);
914
+ background-color: rgba(0,0,0,0.03);
915
+ }
916
+
917
+ /* Slider Controls */
918
+ .slider-row {
919
+ display: flex;
920
+ justify-content: space-between;
921
+ align-items: center;
922
+ font-size: 11.5px;
923
+ font-weight: 500;
924
+ }
925
+
926
+ .slider-val {
927
+ font-family: var(--font-mono);
928
+ color: var(--accent-blue);
929
+ font-size: 11px;
930
+ }
931
+
932
+ input[type="range"] {
933
+ -webkit-appearance: none;
934
+ width: 100%;
935
+ height: 3px;
936
+ background: #e5e7eb;
937
+ border-radius: 2px;
938
+ outline: none;
939
+ }
940
+
941
+ input[type="range"]::-webkit-slider-thumb {
942
+ -webkit-appearance: none;
943
+ appearance: none;
944
  width: 12px;
945
  height: 12px;
946
  border-radius: 50%;
947
+ background: #111827;
948
+ cursor: pointer;
949
+ transition: var(--transition-smooth);
950
  }
951
 
952
+ input[type="range"]::-webkit-slider-thumb:hover {
953
+ transform: scale(1.15);
954
+ }
955
+
956
+ /* Multimodal Shelf Uploader & chip logs */
957
+ .drag-upload-zone {
958
+ border: 1.5px dashed var(--border-color);
959
+ border-radius: var(--border-radius-sm);
960
+ padding: 16px;
961
+ text-align: center;
962
+ cursor: pointer;
963
+ background: #fafafa;
964
+ transition: var(--transition-smooth);
965
  display: flex;
966
+ flex-direction: column;
967
  align-items: center;
968
+ gap: 6px;
969
+ color: var(--text-secondary);
970
+ }
971
+
972
+ .drag-upload-zone:hover {
973
+ border-color: var(--border-focus);
974
+ background: #fbfbfb;
975
+ }
976
+
977
+ .drag-upload-zone p {
978
+ font-size: 12px;
979
+ }
980
+
981
+ .drag-upload-zone p span {
982
+ font-weight: 600;
983
+ text-decoration: underline;
984
+ }
985
+
986
+ .shelf-list {
987
+ display: flex;
988
+ flex-direction: column;
989
  gap: 8px;
990
+ overflow-y: auto;
991
+ }
992
+
993
+ .empty-shelf-text {
994
+ font-size: 11px;
995
+ color: var(--text-muted);
996
+ text-align: center;
997
+ padding: 10px 0;
998
+ font-style: italic;
999
+ }
1000
+
1001
+ .media-chip {
1002
+ display: flex;
1003
+ align-items: center;
1004
+ background-color: #fafafa;
1005
+ border: 1px solid var(--border-color);
1006
+ border-radius: var(--border-radius-sm);
1007
+ padding: 6px 10px;
1008
+ gap: 10px;
1009
  transition: var(--transition-smooth);
1010
+ position: relative;
1011
+ }
1012
+
1013
+ .media-chip-preview {
1014
+ width: 28px;
1015
+ height: 28px;
1016
+ border-radius: 4px;
1017
+ object-fit: cover;
1018
+ background-color: #eaeaea;
1019
+ flex-shrink: 0;
1020
+ display: flex;
1021
+ align-items: center;
1022
+ justify-content: center;
1023
+ font-size: 12px;
1024
  }
1025
 
1026
+ .media-chip-preview img {
1027
+ width: 100%;
1028
+ height: 100%;
1029
+ object-fit: cover;
1030
  }
1031
 
1032
+ .media-chip-details {
1033
  flex: 1;
1034
+ min-width: 0;
1035
  }
1036
 
1037
+ .media-chip-name {
1038
+ font-size: 11px;
1039
+ font-weight: 500;
1040
+ white-space: nowrap;
1041
+ overflow: hidden;
1042
+ text-overflow: ellipsis;
1043
+ color: var(--text-primary);
1044
+ }
1045
+
1046
+ .media-chip-meta {
1047
+ font-size: 9.5px;
1048
+ color: var(--text-muted);
1049
+ display: flex;
1050
+ gap: 6px;
1051
+ }
1052
+
1053
+ .media-chip-remove {
1054
  background: transparent;
1055
  border: none;
1056
+ color: var(--text-muted);
1057
+ cursor: pointer;
1058
+ padding: 4px;
1059
+ border-radius: 4px;
1060
+ display: flex;
1061
+ }
1062
+
1063
+ .media-chip-remove:hover {
1064
+ color: var(--danger);
1065
+ background-color: #fee2e2;
1066
  }
1067
 
1068
+ /* Quick Shelf Attachment Thumbnail inside textarea */
1069
+ .quick-preview-item {
1070
+ width: 36px;
1071
+ height: 36px;
1072
  border-radius: var(--border-radius-sm);
1073
+ border: 1px solid var(--border-color);
1074
+ overflow: hidden;
1075
+ position: relative;
1076
  flex-shrink: 0;
1077
  }
1078
 
1079
+ .quick-preview-item img {
1080
+ width: 100%;
1081
+ height: 100%;
1082
+ object-fit: cover;
1083
+ }
1084
+
1085
+ .quick-preview-badge {
1086
+ position: absolute;
1087
+ top: 2px;
1088
+ right: 2px;
1089
+ background-color: var(--accent-blue);
1090
+ width: 8px;
1091
+ height: 8px;
1092
  border-radius: 50%;
 
1093
  }
1094
 
1095
+ /* Right Sidebar Footer indicator */
1096
+ .right-sidebar-footer {
1097
+ padding: 12px 20px;
1098
+ border-top: 1px solid var(--border-color);
1099
  }
1100
 
1101
+ .status-indicator-group {
1102
+ display: flex;
1103
+ align-items: center;
1104
+ gap: 8px;
1105
+ font-size: 11px;
1106
+ color: var(--text-secondary);
1107
  }
1108
 
1109
+ .status-dot {
1110
+ width: 6px;
1111
+ height: 6px;
1112
+ border-radius: 50%;
1113
+ }
1114
+
1115
+ .status-dot.online {
1116
+ background-color: #10b981;
1117
+ }
1118
+
1119
+ /* Loading animations */
1120
+ .spinner-light {
1121
+ width: 14px;
1122
+ height: 14px;
1123
+ border: 2px solid rgba(255, 255, 255, 0.2);
1124
+ border-top: 2px solid #ffffff;
1125
+ border-radius: 50%;
1126
+ animation: spin 0.8s linear infinite;
1127
+ }
1128
+
1129
+ @keyframes spin {
1130
+ 0% { transform: rotate(0deg); }
1131
+ 100% { transform: rotate(360deg); }
1132
  }
1133
 
1134
  @keyframes slideUp {
1135
+ from { opacity: 0; transform: translateY(10px); }
1136
  to { opacity: 1; transform: translateY(0); }
1137
  }