quanliang commited on
Commit
78a21f5
·
1 Parent(s): b417045

QA / System Chart / HypoGen better UI

Browse files
Files changed (3) hide show
  1. .gitignore +2 -1
  2. index.html +1104 -627
  3. main.py +14 -207
.gitignore CHANGED
@@ -11,4 +11,5 @@ uploaded_files/
11
  .DS_Store
12
  .venv/
13
  chroma_db/
14
- plotextract/outputs/
 
 
11
  .DS_Store
12
  .venv/
13
  chroma_db/
14
+ plotextract/outputs/
15
+ CLAUDE.md
index.html CHANGED
@@ -118,6 +118,12 @@
118
  html[data-mode="QA"] #app,
119
  html[data-mode="System Chart Extraction"] #app,
120
  html[data-mode="Hypotheses Generation"] #app {
 
 
 
 
 
 
121
  display: block;
122
  }
123
 
@@ -222,73 +228,8 @@
222
  background-color: #f8f8f8;
223
  }
224
 
225
- #advancedParameters {
226
- width: 100%;
227
- margin-top: 0.25rem;
228
- border: 1px solid #e0e0e0;
229
- border-radius: 6px;
230
- background-color: #fafafa;
231
- }
232
-
233
- #advancedParameters summary {
234
- cursor: pointer;
235
- padding: 0.5rem 0.75rem;
236
- font-size: 0.85rem;
237
- color: #444;
238
- user-select: none;
239
- }
240
-
241
- #advancedParameters summary:hover {
242
- background-color: #f1f1f1;
243
- }
244
-
245
- .advanced-params-content {
246
- padding: 0.6rem 0.75rem 0.8rem;
247
- border-top: 1px solid #e9e9e9;
248
- }
249
-
250
- .advanced-param-row {
251
- display: flex;
252
- flex-direction: column;
253
- margin-bottom: 0.55rem;
254
- font-size: 0.85rem;
255
- color: #333;
256
- max-width: 360px;
257
- }
258
-
259
- .advanced-param-row:last-child {
260
- margin-bottom: 0;
261
- }
262
-
263
- .advanced-param-meta {
264
- display: flex;
265
- justify-content: space-between;
266
- align-items: center;
267
- margin-bottom: 0.25rem;
268
- gap: 0.75rem;
269
- }
270
-
271
- .advanced-param-value {
272
- color: #666;
273
- font-size: 0.8rem;
274
- min-width: 38px;
275
- text-align: right;
276
- }
277
-
278
- .advanced-param-row input[type="range"] {
279
- width: 100%;
280
- margin: 0;
281
- }
282
-
283
- .advanced-param-row input[type="number"],
284
- .advanced-param-row select {
285
- margin-top: 0;
286
- max-width: 180px;
287
- background-color: #fff;
288
- }
289
-
290
- /* Multi-select dropdown styling */
291
- select[multiple] {
292
  min-height: 80px;
293
  padding: 0.2rem 0.4rem;
294
  background-color: #ffffff;
@@ -300,12 +241,12 @@
300
  transition: border-color 0.2s ease;
301
  }
302
 
303
- select[multiple]:focus {
304
  border-color: #4285F4;
305
  box-shadow: 0 0 0 2px rgba(66, 133, 244, 0.2);
306
  }
307
 
308
- select[multiple] option {
309
  padding: 0.3rem 0.5rem;
310
  margin: 0.1rem 0;
311
  border-radius: 3px;
@@ -313,12 +254,12 @@
313
  color: #333;
314
  }
315
 
316
- select[multiple] option:checked {
317
  background-color: #4285F4;
318
  color: white;
319
  }
320
 
321
- select[multiple] option:hover {
322
  background-color: #f0f0f0;
323
  }
324
 
@@ -1026,21 +967,24 @@
1026
  gap: 0.2rem;
1027
  }
1028
 
1029
- form input[type="text"],
1030
- form select,
1031
- form input[type="file"],
1032
- form input[type="password"],
1033
- form input[type="number"],
1034
- textarea,
1035
- #followUpQuestion,
1036
- #hypothesis-chat-input,
1037
- #hypothesis-text-input,
1038
- #original-paper-content-input {
 
 
 
1039
  margin-top: 0.3rem;
1040
  border: 1px solid #c8d6ec !important;
1041
  border-radius: 10px !important;
1042
  background-color: #ffffff !important;
1043
- color: var(--text);
1044
  box-shadow: inset 0 1px 1px rgba(15, 23, 42, 0.04);
1045
  transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.15s ease;
1046
  }
@@ -1087,20 +1031,10 @@
1087
  border-radius: var(--radius-sm);
1088
  }
1089
 
1090
- #advancedParameters {
1091
- border: 1px solid var(--border);
1092
- border-radius: 10px;
1093
- background: var(--surface-soft);
1094
- }
1095
-
1096
- #advancedParameters summary {
1097
- color: #2a3550;
1098
- font-weight: 500;
1099
- }
1100
-
1101
- #chat-area,
1102
- #hypothesis-chat-messages,
1103
- #conversation-messages {
1104
  border: 1px solid var(--border) !important;
1105
  border-radius: var(--radius-md) !important;
1106
  background: linear-gradient(180deg, #fbfcff 0%, color-mix(in srgb, var(--primary-soft) 35%, #ffffff 65%) 100%) !important;
@@ -1122,9 +1056,9 @@
1122
  border: 1px solid var(--chat-ai-border);
1123
  }
1124
 
1125
- .model-selection-banner,
1126
- #visualizationContainer,
1127
- .progress-container {
1128
  border-radius: var(--radius-md);
1129
  border-color: var(--border);
1130
  background: linear-gradient(180deg, #ffffff 0%, #f9fbff 100%);
@@ -1435,7 +1369,7 @@
1435
  --radius-md: 14px;
1436
  --radius-lg: 18px;
1437
  --font-display: "Electrolize", "Space Grotesk", "Segoe UI", sans-serif;
1438
- --font-body: "Space Grotesk", "Segoe UI", sans-serif;
1439
  --font-mono: "JetBrains Mono", "SFMono-Regular", monospace;
1440
  }
1441
 
@@ -1450,12 +1384,12 @@
1450
  --bg-glow-secondary: rgba(6, 182, 212, 0.16);
1451
  --accent-shadow: rgba(59, 130, 246, 0.32);
1452
  --focus-ring: rgba(59, 130, 246, 0.4);
1453
- --chat-user-top: rgba(16, 185, 129, 0.42);
1454
- --chat-user-bottom: rgba(15, 118, 110, 0.42);
1455
- --chat-user-border: rgba(16, 185, 129, 0.55);
1456
- --chat-ai-top: rgba(59, 130, 246, 0.4);
1457
- --chat-ai-bottom: rgba(37, 99, 235, 0.4);
1458
- --chat-ai-border: rgba(59, 130, 246, 0.55);
1459
  --progress-gradient: linear-gradient(120deg, #3b82f6, #06b6d4);
1460
  --scrollbar-thumb: rgba(59, 130, 246, 0.5);
1461
  --scrollbar-track: rgba(15, 23, 42, 0.6);
@@ -1472,12 +1406,12 @@
1472
  --bg-glow-secondary: rgba(16, 185, 129, 0.16);
1473
  --accent-shadow: rgba(20, 184, 166, 0.32);
1474
  --focus-ring: rgba(20, 184, 166, 0.4);
1475
- --chat-user-top: rgba(16, 185, 129, 0.42);
1476
- --chat-user-bottom: rgba(15, 118, 110, 0.42);
1477
- --chat-user-border: rgba(16, 185, 129, 0.55);
1478
- --chat-ai-top: rgba(20, 184, 166, 0.4);
1479
- --chat-ai-bottom: rgba(15, 118, 110, 0.4);
1480
- --chat-ai-border: rgba(20, 184, 166, 0.55);
1481
  --progress-gradient: linear-gradient(120deg, #14b8a6, #10b981);
1482
  --scrollbar-thumb: rgba(20, 184, 166, 0.5);
1483
  --scrollbar-track: rgba(15, 23, 42, 0.6);
@@ -1494,22 +1428,34 @@
1494
  --bg-glow-secondary: rgba(236, 72, 153, 0.16);
1495
  --accent-shadow: rgba(168, 85, 247, 0.32);
1496
  --focus-ring: rgba(168, 85, 247, 0.4);
1497
- --chat-user-top: rgba(168, 85, 247, 0.42);
1498
- --chat-user-bottom: rgba(124, 58, 237, 0.42);
1499
- --chat-user-border: rgba(168, 85, 247, 0.55);
1500
- --chat-ai-top: rgba(236, 72, 153, 0.4);
1501
- --chat-ai-bottom: rgba(168, 85, 247, 0.4);
1502
- --chat-ai-border: rgba(168, 85, 247, 0.55);
1503
  --progress-gradient: linear-gradient(120deg, #a855f7, #ec4899);
1504
  --scrollbar-thumb: rgba(168, 85, 247, 0.5);
1505
  --scrollbar-track: rgba(15, 23, 42, 0.6);
1506
  }
1507
 
 
 
 
 
 
 
1508
  html[data-mode="QA"] body,
1509
  html[data-mode="System Chart Extraction"] body,
1510
  html[data-mode="Hypotheses Generation"] body {
 
 
 
 
 
 
1511
  background:
1512
- radial-gradient(circle at top right, var(--bg-glow-primary), transparent 42%),
1513
  radial-gradient(circle at 20% 80%, var(--bg-glow-secondary), transparent 45%),
1514
  linear-gradient(160deg, #0b0d12 0%, #0c121f 60%, #0a0f1a 100%);
1515
  color: var(--text);
@@ -1522,29 +1468,120 @@
1522
  font-family: inherit;
1523
  }
1524
 
1525
- /* App shell */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1526
  html[data-mode="QA"] #app,
1527
  html[data-mode="System Chart Extraction"] #app,
1528
  html[data-mode="Hypotheses Generation"] #app {
1529
- background: linear-gradient(145deg, var(--primary-soft), rgba(15, 23, 42, 0.92));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1530
  border: 1px solid var(--border);
1531
  box-shadow: var(--shadow-md);
1532
  backdrop-filter: blur(12px);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1533
  color: var(--text);
 
1534
  }
1535
 
1536
- html[data-mode="QA"] .app-header h1,
1537
- html[data-mode="System Chart Extraction"] .app-header h1,
1538
- html[data-mode="Hypotheses Generation"] .app-header h1 {
1539
  color: var(--text);
1540
  font-family: var(--font-display);
1541
  letter-spacing: 0.02em;
1542
  }
1543
 
1544
- html[data-mode="QA"] .app-header p,
1545
- html[data-mode="System Chart Extraction"] .app-header p,
1546
- html[data-mode="Hypotheses Generation"] .app-header p {
1547
  color: var(--muted);
 
 
 
 
1548
  }
1549
 
1550
  html[data-mode="QA"] .section-title,
@@ -1552,6 +1589,74 @@
1552
  html[data-mode="Hypotheses Generation"] .section-title {
1553
  color: var(--text);
1554
  font-family: var(--font-display);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1555
  }
1556
 
1557
  html[data-mode="QA"] hr,
@@ -1568,26 +1673,215 @@
1568
  color: var(--text);
1569
  }
1570
 
1571
- /* Inputs, selects, textareas */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1572
  html[data-mode="QA"] form input[type="text"],
1573
  html[data-mode="QA"] form input[type="password"],
1574
  html[data-mode="QA"] form input[type="number"],
1575
  html[data-mode="QA"] form select,
1576
  html[data-mode="QA"] form textarea,
 
 
 
 
 
1577
  html[data-mode="System Chart Extraction"] form input[type="text"],
1578
  html[data-mode="System Chart Extraction"] form input[type="password"],
1579
  html[data-mode="System Chart Extraction"] form input[type="number"],
1580
  html[data-mode="System Chart Extraction"] form select,
1581
  html[data-mode="System Chart Extraction"] form textarea,
 
 
 
 
 
1582
  html[data-mode="Hypotheses Generation"] form input[type="text"],
1583
  html[data-mode="Hypotheses Generation"] form input[type="password"],
1584
  html[data-mode="Hypotheses Generation"] form input[type="number"],
1585
  html[data-mode="Hypotheses Generation"] form select,
1586
- html[data-mode="Hypotheses Generation"] form textarea {
1587
- background-color: var(--surface-soft) !important;
 
 
 
 
 
1588
  color: var(--text) !important;
1589
  border: 1px solid var(--border) !important;
1590
- border-radius: var(--radius-sm);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1591
  }
1592
 
1593
  html[data-mode="QA"] form input[type="file"],
@@ -1596,6 +1890,15 @@
1596
  color: var(--muted);
1597
  }
1598
 
 
 
 
 
 
 
 
 
 
1599
  html[data-mode="QA"] form input:focus,
1600
  html[data-mode="QA"] form select:focus,
1601
  html[data-mode="QA"] form textarea:focus,
@@ -1610,36 +1913,163 @@
1610
  box-shadow: 0 0 0 3px var(--focus-ring);
1611
  }
1612
 
1613
- /* Buttons */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1614
  html[data-mode="QA"] form button[type="submit"],
1615
  html[data-mode="QA"] button.primary,
 
 
 
 
1616
  html[data-mode="System Chart Extraction"] form button[type="submit"],
1617
  html[data-mode="System Chart Extraction"] button.primary,
 
 
 
 
1618
  html[data-mode="Hypotheses Generation"] form button[type="submit"],
1619
- html[data-mode="Hypotheses Generation"] button.primary {
 
 
 
1620
  background: linear-gradient(120deg, var(--primary), var(--accent-2)) !important;
1621
  color: #fff !important;
1622
  border: none !important;
1623
- padding: 0.6rem 1.4rem;
1624
- border-radius: var(--radius-sm);
1625
  font-weight: 600;
1626
  cursor: pointer;
1627
- box-shadow: 0 8px 24px var(--accent-shadow);
1628
- transition: transform 0.15s ease, box-shadow 0.15s ease;
1629
- }
1630
-
1631
- html[data-mode="QA"] form button[type="submit"]:hover:not(:disabled),
1632
- html[data-mode="System Chart Extraction"] form button[type="submit"]:hover:not(:disabled),
1633
- html[data-mode="Hypotheses Generation"] form button[type="submit"]:hover:not(:disabled) {
 
 
 
 
 
 
 
 
 
1634
  transform: translateY(-1px);
1635
- box-shadow: 0 12px 30px var(--accent-shadow);
1636
  }
1637
 
1638
- html[data-mode="QA"] form button[type="submit"]:disabled,
1639
- html[data-mode="System Chart Extraction"] form button[type="submit"]:disabled,
1640
- html[data-mode="Hypotheses Generation"] form button[type="submit"]:disabled {
1641
- opacity: 0.55;
1642
  cursor: not-allowed;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1643
  }
1644
 
1645
  /* Provider badge + model status (used in API key form) */
@@ -1686,42 +2116,47 @@
1686
  color: #34d399;
1687
  }
1688
 
1689
- /* Advanced parameter rows */
1690
- html[data-mode="QA"] .advanced-params-content,
1691
- html[data-mode="System Chart Extraction"] .advanced-params-content,
1692
- html[data-mode="Hypotheses Generation"] .advanced-params-content {
1693
- background: var(--surface-soft);
1694
- border: 1px solid var(--border);
1695
- border-radius: var(--radius-md);
1696
- }
1697
-
1698
- html[data-mode="QA"] .advanced-param-row,
1699
- html[data-mode="System Chart Extraction"] .advanced-param-row,
1700
- html[data-mode="Hypotheses Generation"] .advanced-param-row {
1701
- color: var(--text);
1702
- }
1703
-
1704
- html[data-mode="QA"] .advanced-param-value,
1705
- html[data-mode="System Chart Extraction"] .advanced-param-value,
1706
- html[data-mode="Hypotheses Generation"] .advanced-param-value {
1707
- color: var(--muted);
1708
- }
1709
-
1710
  html[data-mode="QA"] details summary,
1711
  html[data-mode="System Chart Extraction"] details summary,
1712
  html[data-mode="Hypotheses Generation"] details summary {
1713
  color: var(--text);
1714
  }
1715
 
1716
- /* Chat / message areas */
1717
  html[data-mode="QA"] #chat-area,
1718
  html[data-mode="System Chart Extraction"] #chat-area,
1719
  html[data-mode="Hypotheses Generation"] #chat-area {
1720
- background: var(--surface-soft);
1721
- border: 1px solid var(--border);
1722
- border-radius: var(--radius-md);
1723
- padding: 1rem;
1724
  color: var(--text);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1725
  }
1726
 
1727
  html[data-mode="QA"] .user-bubble,
@@ -1730,6 +2165,7 @@
1730
  background: linear-gradient(160deg, var(--chat-user-top), var(--chat-user-bottom));
1731
  border: 1px solid var(--chat-user-border);
1732
  color: var(--text);
 
1733
  }
1734
 
1735
  html[data-mode="QA"] .ai-bubble,
@@ -1738,6 +2174,7 @@
1738
  background: linear-gradient(160deg, var(--chat-ai-top), var(--chat-ai-bottom));
1739
  border: 1px solid var(--chat-ai-border);
1740
  color: var(--text);
 
1741
  }
1742
 
1743
  html[data-mode="QA"] .error-bubble,
@@ -1751,16 +2188,266 @@
1751
  html[data-mode="QA"] .references-section,
1752
  html[data-mode="System Chart Extraction"] .references-section,
1753
  html[data-mode="Hypotheses Generation"] .references-section {
1754
- background: var(--surface-soft);
1755
- border: 1px solid var(--border);
1756
- box-shadow: var(--shadow-sm);
1757
  color: var(--text);
1758
  }
 
1759
  html[data-mode="QA"] .references-section h3,
 
1760
  html[data-mode="System Chart Extraction"] .references-section h3,
 
1761
  html[data-mode="Hypotheses Generation"] .references-section h3 {
1762
  color: var(--text);
1763
- border-bottom-color: var(--primary);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1764
  }
1765
  html[data-mode="QA"] .reference-item,
1766
  html[data-mode="System Chart Extraction"] .reference-item,
@@ -1820,49 +2507,27 @@
1820
  color: var(--muted);
1821
  }
1822
 
1823
- /* Buttons embedded inline (Start hypothesis chat, Send, Load conversation, Submit follow-up) */
1824
- html[data-mode="QA"] #start-hypothesis-conversation-btn,
1825
- html[data-mode="QA"] #send-hypothesis-message-btn,
1826
- html[data-mode="QA"] #loadConversationBtn,
1827
- html[data-mode="System Chart Extraction"] #start-hypothesis-conversation-btn,
1828
- html[data-mode="System Chart Extraction"] #send-hypothesis-message-btn,
1829
- html[data-mode="System Chart Extraction"] #loadConversationBtn,
1830
- html[data-mode="Hypotheses Generation"] #start-hypothesis-conversation-btn,
1831
- html[data-mode="Hypotheses Generation"] #send-hypothesis-message-btn,
1832
- html[data-mode="Hypotheses Generation"] #loadConversationBtn {
1833
- background: linear-gradient(120deg, var(--primary), var(--accent-2)) !important;
1834
- color: #fff !important;
1835
- border: none !important;
1836
- box-shadow: 0 8px 24px var(--accent-shadow);
1837
- }
1838
-
1839
- /* Section panels with hardcoded f8f9fa backgrounds */
1840
- html[data-mode="QA"] #hypothesesContainer > div,
1841
- html[data-mode="QA"] #conversation-section > div,
1842
- html[data-mode="QA"] #hypotheses-conversation-section > div,
1843
- html[data-mode="System Chart Extraction"] #hypothesesContainer > div,
1844
- html[data-mode="System Chart Extraction"] #conversation-section > div,
1845
- html[data-mode="System Chart Extraction"] #hypotheses-conversation-section > div,
1846
- html[data-mode="Hypotheses Generation"] #hypothesesContainer > div,
1847
- html[data-mode="Hypotheses Generation"] #conversation-section > div,
1848
- html[data-mode="Hypotheses Generation"] #hypotheses-conversation-section > div {
1849
- background: var(--surface) !important;
1850
- border: 1px solid var(--border) !important;
1851
- box-shadow: var(--shadow-sm);
1852
- }
1853
-
1854
  html[data-mode="QA"] #visualizationContainer,
1855
  html[data-mode="System Chart Extraction"] #visualizationContainer,
1856
  html[data-mode="Hypotheses Generation"] #visualizationContainer {
1857
- background: var(--surface) !important;
1858
- border: 1px solid var(--border) !important;
1859
- box-shadow: var(--shadow-sm);
 
 
 
 
 
 
 
1860
  }
1861
 
1862
  html[data-mode="QA"] #visualizationContainer label,
1863
  html[data-mode="System Chart Extraction"] #visualizationContainer label,
1864
  html[data-mode="Hypotheses Generation"] #visualizationContainer label {
1865
  color: var(--text) !important;
 
 
1866
  }
1867
 
1868
  /* h4 inside the hypothesis/conversation panes */
@@ -2066,6 +2731,20 @@
2066
  html[data-mode="Hypotheses Generation"] .step-number-default { display: none; }
2067
  html[data-mode="Hypotheses Generation"] .step-number-hypotheses { display: inline; }
2068
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2069
  </style>
2070
  </head>
2071
  <body>
@@ -2091,265 +2770,207 @@
2091
  </button>
2092
  </div>
2093
  </div>
 
2094
  <div id="app">
2095
- <!-- Header Section -->
2096
- <div class="app-header">
2097
- <h1 id="appTitle">Chatbot</h1>
2098
- <p>Upload PDF or text files, select a model, and ask questions!</p>
2099
- </div>
2100
-
2101
- <div class="model-selection-banner" id="openaiOnlyBanner">
2102
- <h2>Proprietary Models</h2>
2103
- <div class="info-box">
2104
- <strong>Note:</strong> This deployment supports proprietary models with your API key.
2105
  </div>
2106
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
2107
 
2108
- <!-- API Key Form -->
2109
- <div class="section-title">1) Set API Key</div>
2110
- <form id="apiKeyForm">
2111
- <div class="form-inputs">
2112
- <label id="apiKeyLabel">
2113
- API Key:
2114
- <input type="password" id="apiKeyInput" name="api_key_input" placeholder="sk-... (OpenAI), sk-ant-... (Anthropic), or AIza... (Gemini)" autocomplete="off">
2115
- </label>
2116
- <div id="providerBadge" class="provider-badge" data-provider="">Provider: —</div>
2117
- <label>
2118
- Model:
2119
  <select name="model" id="modelSelect" disabled>
2120
  <option value="">Enter API key to load models</option>
2121
  </select>
2122
  <span id="modelStatus" class="model-status"></span>
2123
- </label>
2124
- <details id="advancedParameters">
2125
- <summary>Advanced parameter selection</summary>
2126
- <div class="advanced-params-content" id="advancedParamsContent">
2127
- <div class="advanced-param-row" data-param="temperature">
2128
- <div class="advanced-param-meta">
2129
- <span>Temperature</span>
2130
- <span class="advanced-param-value" id="temperatureValue">1.00</span>
2131
- </div>
2132
- <input type="range" name="temperature" id="temperatureInput" min="0" max="2" step="0.01" value="1">
2133
- </div>
2134
 
2135
- <div class="advanced-param-row" data-param="top_p">
2136
- <div class="advanced-param-meta">
2137
- <span>Top P</span>
2138
- <span class="advanced-param-value" id="topPValue">1.00</span>
2139
- </div>
2140
- <input type="range" name="top_p" id="topPInput" min="0" max="1" step="0.01" value="1">
2141
- </div>
2142
 
2143
- <label class="advanced-param-row" data-param="verbosity">
2144
- <div class="advanced-param-meta">
2145
- <span>Verbosity</span>
 
 
 
 
 
 
 
 
 
 
 
2146
  </div>
2147
- <select name="verbosity" id="verbosityInput">
2148
- <option value="low">low</option>
2149
- <option value="medium" selected>medium</option>
2150
- <option value="high">high</option>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2151
  </select>
 
2152
  </label>
 
 
2153
 
2154
- <label class="advanced-param-row" data-param="reasoning_effort">
2155
- <div class="advanced-param-meta">
2156
- <span>Reasoning Effort</span>
2157
- </div>
2158
- <select name="reasoning_effort" id="reasoningEffortInput">
2159
- <option value="">Default</option>
2160
- <option value="minimal">minimal</option>
2161
- <option value="low">low</option>
2162
- <option value="medium">medium</option>
2163
- <option value="high">high</option>
2164
- </select>
2165
  </label>
 
2166
 
2167
- <label class="advanced-param-row" data-param="thinking_level">
2168
- <div class="advanced-param-meta">
2169
- <span>Thinking Level</span>
2170
- </div>
2171
- <select name="thinking_level" id="thinkingLevelInput">
2172
- <option value="high">high</option>
2173
- </select>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2174
  </label>
 
2175
  </div>
2176
- </details>
2177
- </div>
2178
- <button type="submit" id="modelSubmitButton">Submit</button>
2179
- </form>
2180
 
2181
- <hr id="uploadSectionDivider">
 
2182
 
2183
- <!-- File Upload Form -->
2184
- <div id="uploadSection">
2185
- <div class="section-title">2) Upload Files</div>
2186
- <p style="font-size: 0.9rem; color: #666; margin-bottom: 0.5rem;">If the number of files is greater than 1000, please upload a zip file instead.</p>
2187
- <form id="uploadForm">
2188
- <label>
2189
- Database:
2190
- <input type="text" name="category" value="my-database">
2191
- </label>
2192
 
2193
- <label>
2194
- Select File(s) (.pdf, .txt, .zip, etc.):
2195
- <input type="file" name="files" multiple accept=".pdf,.txt,.text,.png,.jpg,.jpeg,.gif,.bmp,.tiff,.webp,.zip,application/zip">
2196
- </label>
2197
- <button type="submit">Upload</button>
2198
- </form>
 
2199
 
2200
- <hr>
2201
- </div>
 
 
2202
 
2203
- <!-- Ask Question Form -->
2204
- <div class="section-title"><span class="step-number-default">3)</span><span class="step-number-hypotheses">2)</span> Ask Questions</div>
2205
- <form id="askForm">
2206
- <div class="form-inputs">
2207
- <div id="databaseContainer">
2208
- <label>
2209
- Databases:
2210
- <select name="categories" id="databaseSelect" multiple style="min-height: 80px; width: 200px;">
2211
- <option value="my-database">my-database</option>
2212
- </select>
2213
- <small style="color: #666; font-size: 0.8rem;">Hold Ctrl/Cmd to select multiple databases</small>
2214
- </label>
2215
- </div>
2216
- <input type="hidden" name="goal" id="goalInput" value="">
2217
-
2218
- <!-- Add the visualization options container -->
2219
- <div id="visualizationContainer" style="display: none;">
2220
- <label style="display: flex; align-items: center; font-weight: 500; margin-bottom: 0;">
2221
- <input type="checkbox" name="enable_visualization" id="enableVisualization">
2222
- Enable visualization of extracted system charts
2223
- </label>
2224
- </div>
2225
-
2226
- <!-- Add the hypotheses generation options container -->
2227
- <div id="hypothesesContainer" style="display: none;">
2228
- <div style="background-color: #f8f9fa; border: 1px solid #e9ecef; border-radius: 6px; padding: 12px 15px; margin: 5px 0; min-width: 280px; flex-shrink: 0;">
2229
- <label style="display: block; margin-bottom: 8px;">
2230
- System Charts Files:
2231
- <input type="file" id="systemChartsFile" accept=".txt" multiple style="margin-top: 4px; width: 100%;">
2232
- <small style="color: #666; font-size: 0.8rem;">Select multiple .txt files containing system charts</small>
2233
- </label>
2234
- <label style="display: block; margin-bottom: 8px;">
2235
- Mechanism 1:
2236
- <input type="text" id="materialGoal1" placeholder="e.g., property #1 for xxx material" style="margin-top: 4px; width: 100%; padding: 6px;">
2237
- </label>
2238
- <label style="display: block; margin-bottom: 8px;">
2239
- Mechanism 2:
2240
- <input type="text" id="materialGoal2" placeholder="e.g., property #2 for xxx material" style="margin-top: 4px; width: 100%; padding: 6px;">
2241
- </label>
2242
- <label style="display: block; margin-bottom: 0;">
2243
- Number of Iterations:
2244
- <input type="number" id="iterationsCount" min="1" max="100" value="1" placeholder="1" style="margin-top: 4px; width: 100%; padding: 6px;">
2245
- <small style="color: #666; font-size: 0.8rem;">Generate multiple hypothesis iterations (1-100)</small>
2246
- </label>
2247
  </div>
2248
- </div>
2249
-
2250
- <div id="questionContainer">
2251
- <label id="questionLabel">
2252
- Question:
2253
- <input type="text" name="question" placeholder="Type your question here...">
2254
- </label>
2255
- </div>
2256
- </div>
2257
-
2258
- <button type="submit">Ask</button>
2259
- </form>
2260
-
2261
- <!-- Chat Area BELOW the question input bar -->
2262
- <div id="chat-area"></div>
2263
-
2264
- <!-- Hypotheses Continuous Conversation Section -->
2265
- <div id="hypotheses-conversation-section" style="display: none;">
2266
- <hr style="margin: 2rem 0;">
2267
- <div class="section-title">Continue Hypothesis Analysis</div>
2268
- <div style="background-color: #f8f9fa; border: 1px solid #e9ecef; border-radius: 8px; padding: 1rem; margin-bottom: 1rem;">
2269
- <p style="margin: 0 0 0.5rem 0; color: #666; font-size: 0.9rem;">
2270
- Paste any generated hypothesis below to start a continuous conversation with the original papers and system charts.
2271
- </p>
2272
-
2273
- <div style="margin-bottom: 15px;">
2274
- <label style="display: block; margin-bottom: 5px; font-weight: bold;">Hypothesis Text:</label>
2275
- <textarea id="hypothesis-text-input" placeholder="Paste your hypothesis here..." style="width: 100%; height: 120px; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-family: Arial, sans-serif; font-size: 14px; line-height: 1.4; resize: vertical;"></textarea>
2276
- </div>
2277
-
2278
- <div style="margin-bottom: 15px;">
2279
- <label style="display: block; margin-bottom: 5px; font-weight: bold;">Original Paper Content:</label>
2280
- <textarea id="original-paper-content-input" placeholder="Paste the original paper content you want to reference here..." style="width: 100%; height: 200px; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-family: Arial, sans-serif; font-size: 14px; line-height: 1.4; resize: vertical;"></textarea>
2281
- <small style="color: #666; font-size: 0.85rem; display: block; margin-top: 5px;">
2282
- Instead of automatically retrieving papers from the database, manually paste the content of the original papers you want to reference in your hypothesis analysis.
2283
- </small>
2284
- </div>
2285
-
2286
- <button id="start-hypothesis-conversation-btn" style="background-color: #4285F4; color: white; padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer;">
2287
- Start Hypothesis Conversation
2288
- </button>
2289
-
2290
- <div id="hypothesis-conversation-status" style="display: none; margin-top: 10px; padding: 10px; border-radius: 4px;">
2291
- <!-- Status messages will be populated here -->
2292
- </div>
2293
- </div>
2294
-
2295
- <div id="hypothesis-conversation-chat" style="display: none; margin-top: 15px;">
2296
- <h4 style="margin: 0 0 10px 0; color: #333;">Hypothesis Analysis Chat:</h4>
2297
- <div id="hypothesis-chat-messages" style="background-color: #fafafa; border: 1px solid #eee; border-radius: 4px; padding: 1rem; max-height: 400px; overflow-y: auto; margin-bottom: 10px;">
2298
- <!-- Messages will be populated here -->
2299
- </div>
2300
- <div style="display: flex; gap: 10px;">
2301
- <input type="text" id="hypothesis-chat-input" placeholder="Ask about this hypothesis..." style="flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px;">
2302
- <button id="send-hypothesis-message-btn" style="background-color: #4285F4; color: white; padding: 8px 16px; border: none; border-radius: 4px; cursor: pointer;">
2303
- Send
2304
  </button>
2305
- </div>
2306
- </div>
2307
- </div>
2308
 
2309
- <!-- Conversation Continuation Section -->
2310
- <div id="conversation-section" style="display: none;">
2311
- <hr style="margin: 2rem 0;">
2312
- <div class="section-title">Continue System Chart Extraction Conversations</div>
2313
- <div style="background-color: #f8f9fa; border: 1px solid #e9ecef; border-radius: 8px; padding: 1rem; margin-bottom: 1rem;">
2314
- <p style="margin: 0 0 0.5rem 0; color: #666; font-size: 0.9rem;">
2315
- After system chart extraction, you can continue conversations with the AI about individual papers.
2316
- </p>
2317
- <form id="conversationForm">
2318
- <div style="display: flex; gap: 1rem; align-items: flex-end; margin-bottom: 1rem;">
2319
- <label style="flex: 1;">
2320
- Select Paper Conversation:
2321
- <select id="conversationSelect" style="width: 100%; padding: 0.5rem; margin-top: 0.3rem; border: 1px solid #ddd; border-radius: 4px;">
2322
- <option value="">Select a paper to continue conversation...</option>
2323
- </select>
2324
- </label>
2325
- <button type="button" id="loadConversationBtn" style="padding: 0.5rem 1rem; background-color: #4285F4; color: white; border: none; border-radius: 4px; cursor: pointer;">
2326
- Load Conversation
2327
- </button>
2328
  </div>
2329
- <div id="followUpContainer" style="display: none;">
2330
- <label>
2331
- Follow-up Question:
2332
- <input type="text" id="followUpQuestion" placeholder="Ask a follow-up question about this paper..." style="width: 100%; padding: 0.5rem; margin-top: 0.3rem; border: 1px solid #ddd; border-radius: 4px;">
2333
- </label>
2334
- <button type="submit" style="margin-top: 0.5rem; padding: 0.5rem 1rem; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer;">
2335
- Continue Conversation
 
 
2336
  </button>
2337
  </div>
2338
- </form>
2339
  </div>
2340
-
2341
- <!-- Conversation History Display -->
2342
- <div id="conversation-history" style="display: none;">
2343
- <h4 style="margin: 1rem 0 0.5rem 0; color: #333;">Conversation History:</h4>
2344
- <div id="conversation-messages" style="background-color: #fafafa; border: 1px solid #eee; border-radius: 4px; padding: 1rem; max-height: 400px; overflow-y: auto;">
2345
- <!-- Messages will be populated here -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2346
  </div>
2347
  </div>
2348
- </div>
2349
 
2350
- <!-- References Section -->
2351
- <div class="references-section" id="referencesSection">
2352
- <h3>References</h3>
 
2353
 
2354
  <div class="reference-item" id="systemChartReference">
2355
  <span class="category-label">System Chart Extraction:</span> Liu, Q., Polak, M. P., Shuvo, M. A. A., Deodhar, H. S., Han, J., Morgan, D., & Oh, H. (2025, October). Automated extraction of materials system charts using a large language model framework. <em>Scripta Materialia</em>. Elsevier BV. http://doi.org/10.1016/j.scriptamat.2025.116815
@@ -2357,7 +2978,33 @@
2357
  <div class="reference-item" id="hypothesesReference">
2358
  <span class="category-label">Hypotheses Generation:</span> Liu, Q., Polak, M. P., Kim, S. Y., Shuvo, M. A. A., Deodhar, H. S., Han, J., … Oh, H. (2025, September). Beyond designer's knowledge: Generating materials design hypotheses via a large language model. <em>Acta Materialia</em>. Elsevier BV. http://doi.org/10.1016/j.actamat.2025.121307
2359
  </div>
2360
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2361
 
2362
  </div>
2363
 
@@ -2399,17 +3046,16 @@
2399
  if (appTitle) {
2400
  appTitle.textContent = window.selectedMode;
2401
  }
2402
- updateQuestionVisibility();
2403
- const modelSelect = document.getElementById('modelSelect');
2404
- if (modelSelect) {
2405
- configureAdvancedControlsForModel(modelSelect.value);
2406
- if (window.selectedMode === 'QA') {
2407
- const advancedParameters = document.getElementById('advancedParameters');
2408
- if (advancedParameters) {
2409
- advancedParameters.style.display = 'block';
2410
- }
2411
- }
2412
  }
 
2413
  }
2414
 
2415
  function setupModeSelection() {
@@ -2437,180 +3083,7 @@
2437
 
2438
  document.addEventListener('DOMContentLoaded', setupModeSelection);
2439
 
2440
- // ========================
2441
- // Handle model selection panel
2442
- // ========================
2443
- const MODEL_ADVANCED_CONFIG = {
2444
- "gpt-5-nano": {
2445
- visible: ["reasoning_effort", "verbosity"],
2446
- defaults: { reasoning_effort: "medium", verbosity: "medium" },
2447
- options: {
2448
- reasoning_effort: ["minimal", "low", "medium", "high"],
2449
- verbosity: ["low", "medium", "high"]
2450
- }
2451
- },
2452
- "gpt-5-mini": {
2453
- visible: ["reasoning_effort", "verbosity"],
2454
- defaults: { reasoning_effort: "medium", verbosity: "medium" },
2455
- options: {
2456
- reasoning_effort: ["minimal", "low", "medium", "high"],
2457
- verbosity: ["low", "medium", "high"]
2458
- }
2459
- },
2460
- "gpt-5.2": {
2461
- visible: ["reasoning_effort", "verbosity"],
2462
- defaults: { reasoning_effort: "medium", verbosity: "medium" },
2463
- options: {
2464
- reasoning_effort: ["none", "low", "medium", "high", "xhigh"],
2465
- verbosity: ["low", "medium", "high"]
2466
- }
2467
- },
2468
- "gemini-2.5-flash-lite": {
2469
- visible: ["temperature", "top_p"],
2470
- defaults: { temperature: 1.0, top_p: 0.95 },
2471
- options: {}
2472
- },
2473
- "gemini-3-flash-preview": {
2474
- visible: ["temperature", "top_p", "thinking_level"],
2475
- defaults: { temperature: 1.0, top_p: 0.95, thinking_level: "high" },
2476
- options: {
2477
- thinking_level: ["minimal", "low", "medium", "high"]
2478
- }
2479
- },
2480
- "gemini-3-pro-preview": {
2481
- visible: ["temperature", "top_p", "thinking_level"],
2482
- defaults: { temperature: 1.0, top_p: 0.95, thinking_level: "high" },
2483
- options: {
2484
- thinking_level: ["low", "high"]
2485
- }
2486
- }
2487
- };
2488
-
2489
- function getModelAdvancedConfig(modelName) {
2490
- if (MODEL_ADVANCED_CONFIG[modelName]) return MODEL_ADVANCED_CONFIG[modelName];
2491
- const m = (modelName || '').toLowerCase();
2492
- // Pattern fallback so dynamically-fetched models still surface a sensible advanced-params UI.
2493
- if (m.includes('gpt-5')) {
2494
- return {
2495
- visible: ["reasoning_effort", "verbosity"],
2496
- defaults: { reasoning_effort: "medium", verbosity: "medium" },
2497
- options: { reasoning_effort: ["minimal", "low", "medium", "high"], verbosity: ["low", "medium", "high"] }
2498
- };
2499
- }
2500
- if (m.includes('gpt')) {
2501
- return { visible: ["temperature", "top_p"], defaults: { temperature: 1.0, top_p: 1.0 }, options: {} };
2502
- }
2503
- if (m.includes('gemini') && m.includes('pro')) {
2504
- return {
2505
- visible: ["temperature", "top_p", "thinking_level"],
2506
- defaults: { temperature: 1.0, top_p: 0.95, thinking_level: "high" },
2507
- options: { thinking_level: ["low", "high"] }
2508
- };
2509
- }
2510
- if (m.includes('gemini')) {
2511
- return { visible: ["temperature", "top_p"], defaults: { temperature: 1.0, top_p: 0.95 }, options: {} };
2512
- }
2513
- if (m.includes('claude')) {
2514
- return { visible: ["temperature", "top_p"], defaults: { temperature: 1.0, top_p: 1.0 }, options: {} };
2515
- }
2516
- return { visible: [], defaults: {}, options: {} };
2517
- }
2518
-
2519
- function setSelectOptions(selectElement, options, defaultValue) {
2520
- if (!selectElement) return;
2521
- selectElement.innerHTML = '';
2522
- options.forEach((optionValue) => {
2523
- const option = document.createElement('option');
2524
- option.value = optionValue;
2525
- option.textContent = optionValue;
2526
- if (optionValue === defaultValue) {
2527
- option.selected = true;
2528
- }
2529
- selectElement.appendChild(option);
2530
- });
2531
- if (defaultValue && options.includes(defaultValue)) {
2532
- selectElement.value = defaultValue;
2533
- }
2534
- }
2535
-
2536
- function applyAdvancedDefaultsForModel(modelName) {
2537
- const config = getModelAdvancedConfig(modelName);
2538
- const defaults = config.defaults || {};
2539
- const options = config.options || {};
2540
-
2541
- const temperatureInput = document.getElementById('temperatureInput');
2542
- const topPInput = document.getElementById('topPInput');
2543
- const temperatureValue = document.getElementById('temperatureValue');
2544
- const topPValue = document.getElementById('topPValue');
2545
- const reasoningEffortInput = document.getElementById('reasoningEffortInput');
2546
- const verbosityInput = document.getElementById('verbosityInput');
2547
- const thinkingLevelInput = document.getElementById('thinkingLevelInput');
2548
-
2549
- if (options.reasoning_effort) {
2550
- setSelectOptions(reasoningEffortInput, options.reasoning_effort, defaults.reasoning_effort);
2551
- }
2552
- if (options.verbosity) {
2553
- setSelectOptions(verbosityInput, options.verbosity, defaults.verbosity);
2554
- }
2555
- if (options.thinking_level) {
2556
- setSelectOptions(thinkingLevelInput, options.thinking_level, defaults.thinking_level);
2557
- }
2558
-
2559
- if (typeof defaults.temperature === 'number') {
2560
- temperatureInput.value = defaults.temperature.toFixed(2);
2561
- }
2562
- if (typeof defaults.top_p === 'number') {
2563
- topPInput.value = defaults.top_p.toFixed(2);
2564
- }
2565
-
2566
- temperatureValue.textContent = Number(temperatureInput.value).toFixed(2);
2567
- topPValue.textContent = Number(topPInput.value).toFixed(2);
2568
- }
2569
-
2570
- function setAdvancedParamVisibilityForModel(modelName) {
2571
- const advancedParameters = document.getElementById('advancedParameters');
2572
- const visibleParams = getModelAdvancedConfig(modelName).visible || [];
2573
- const rows = advancedParameters.querySelectorAll('[data-param]');
2574
- advancedParameters.style.display = 'block';
2575
-
2576
- rows.forEach((row) => {
2577
- const paramName = row.getAttribute('data-param');
2578
- const isVisible = visibleParams.includes(paramName);
2579
- row.style.display = isVisible ? 'flex' : 'none';
2580
- row.querySelectorAll('input, select').forEach((el) => {
2581
- el.disabled = !isVisible;
2582
- });
2583
- });
2584
- }
2585
-
2586
- function configureAdvancedControlsForModel(modelName) {
2587
- applyAdvancedDefaultsForModel(modelName);
2588
- setAdvancedParamVisibilityForModel(modelName);
2589
- }
2590
-
2591
- function initializeAdvancedParameterControls() {
2592
- const temperatureInput = document.getElementById('temperatureInput');
2593
- const topPInput = document.getElementById('topPInput');
2594
- const temperatureValue = document.getElementById('temperatureValue');
2595
- const topPValue = document.getElementById('topPValue');
2596
-
2597
- temperatureInput.addEventListener('input', () => {
2598
- temperatureValue.textContent = Number(temperatureInput.value).toFixed(2);
2599
- });
2600
-
2601
- topPInput.addEventListener('input', () => {
2602
- topPValue.textContent = Number(topPInput.value).toFixed(2);
2603
- });
2604
-
2605
- temperatureValue.textContent = Number(temperatureInput.value).toFixed(2);
2606
- topPValue.textContent = Number(topPInput.value).toFixed(2);
2607
-
2608
- const selectedModel = document.getElementById('modelSelect').value;
2609
- configureAdvancedControlsForModel(selectedModel);
2610
- }
2611
-
2612
  document.addEventListener('DOMContentLoaded', () => {
2613
- initializeAdvancedParameterControls();
2614
  setupApiKeyAutoLoad();
2615
  });
2616
 
@@ -2662,12 +3135,9 @@
2662
  function setupApiKeyAutoLoad() {
2663
  const apiKeyInput = document.getElementById('apiKeyInput');
2664
  const modelSelect = document.getElementById('modelSelect');
 
2665
  if (!apiKeyInput || !modelSelect) return;
2666
 
2667
- modelSelect.addEventListener('change', () => {
2668
- configureAdvancedControlsForModel(modelSelect.value);
2669
- });
2670
-
2671
  apiKeyInput.addEventListener('input', () => {
2672
  const provider = detectProviderFromKey(apiKeyInput.value);
2673
  if (provider) {
@@ -2685,6 +3155,14 @@
2685
  apiKeyInput.addEventListener('blur', () => {
2686
  loadModelsForKey();
2687
  });
 
 
 
 
 
 
 
 
2688
  }
2689
 
2690
  async function loadModelsForKey({ silent = false } = {}) {
@@ -2737,7 +3215,6 @@
2737
  }
2738
  modelSelect.disabled = false;
2739
  modelSelect.value = data.default_model || models[0].id;
2740
- configureAdvancedControlsForModel(modelSelect.value);
2741
 
2742
  _modelsLoadedForKey = key;
2743
  setProviderBadge(data.provider, data.provider_label);
 
118
  html[data-mode="QA"] #app,
119
  html[data-mode="System Chart Extraction"] #app,
120
  html[data-mode="Hypotheses Generation"] #app {
121
+ display: flex;
122
+ }
123
+
124
+ html[data-mode="QA"] .app-glow,
125
+ html[data-mode="System Chart Extraction"] .app-glow,
126
+ html[data-mode="Hypotheses Generation"] .app-glow {
127
  display: block;
128
  }
129
 
 
228
  background-color: #f8f8f8;
229
  }
230
 
231
+ /* Multi-select dropdown styling — light defaults; dark modes override below */
232
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) select[multiple] {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
  min-height: 80px;
234
  padding: 0.2rem 0.4rem;
235
  background-color: #ffffff;
 
241
  transition: border-color 0.2s ease;
242
  }
243
 
244
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) select[multiple]:focus {
245
  border-color: #4285F4;
246
  box-shadow: 0 0 0 2px rgba(66, 133, 244, 0.2);
247
  }
248
 
249
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) select[multiple] option {
250
  padding: 0.3rem 0.5rem;
251
  margin: 0.1rem 0;
252
  border-radius: 3px;
 
254
  color: #333;
255
  }
256
 
257
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) select[multiple] option:checked {
258
  background-color: #4285F4;
259
  color: white;
260
  }
261
 
262
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) select[multiple] option:hover {
263
  background-color: #f0f0f0;
264
  }
265
 
 
967
  gap: 0.2rem;
968
  }
969
 
970
+ /* Light form chrome — only when NOT in a dark app mode. Scoped so the
971
+ forced white background cannot fight dark-mode select/input overrides
972
+ (which previously left white ink on a white field). */
973
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) form input[type="text"],
974
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) form select,
975
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) form input[type="file"],
976
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) form input[type="password"],
977
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) form input[type="number"],
978
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) textarea,
979
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) #followUpQuestion,
980
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) #hypothesis-chat-input,
981
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) #hypothesis-text-input,
982
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) #original-paper-content-input {
983
  margin-top: 0.3rem;
984
  border: 1px solid #c8d6ec !important;
985
  border-radius: 10px !important;
986
  background-color: #ffffff !important;
987
+ color: #1f2937;
988
  box-shadow: inset 0 1px 1px rgba(15, 23, 42, 0.04);
989
  transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.15s ease;
990
  }
 
1031
  border-radius: var(--radius-sm);
1032
  }
1033
 
1034
+ /* Light chat panes — scoped out of dark app modes (dark overlay owns these) */
1035
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) #chat-area,
1036
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) #hypothesis-chat-messages,
1037
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) #conversation-messages {
 
 
 
 
 
 
 
 
 
 
1038
  border: 1px solid var(--border) !important;
1039
  border-radius: var(--radius-md) !important;
1040
  background: linear-gradient(180deg, #fbfcff 0%, color-mix(in srgb, var(--primary-soft) 35%, #ffffff 65%) 100%) !important;
 
1056
  border: 1px solid var(--chat-ai-border);
1057
  }
1058
 
1059
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) .model-selection-banner,
1060
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) #visualizationContainer,
1061
+ html:not([data-mode="QA"]):not([data-mode="System Chart Extraction"]):not([data-mode="Hypotheses Generation"]) .progress-container {
1062
  border-radius: var(--radius-md);
1063
  border-color: var(--border);
1064
  background: linear-gradient(180deg, #ffffff 0%, #f9fbff 100%);
 
1369
  --radius-md: 14px;
1370
  --radius-lg: 18px;
1371
  --font-display: "Electrolize", "Space Grotesk", "Segoe UI", sans-serif;
1372
+ --font-body: "Electrolize", "Segoe UI", sans-serif;
1373
  --font-mono: "JetBrains Mono", "SFMono-Regular", monospace;
1374
  }
1375
 
 
1384
  --bg-glow-secondary: rgba(6, 182, 212, 0.16);
1385
  --accent-shadow: rgba(59, 130, 246, 0.32);
1386
  --focus-ring: rgba(59, 130, 246, 0.4);
1387
+ --chat-user-top: #15201c;
1388
+ --chat-user-bottom: #121a18;
1389
+ --chat-user-border: rgba(16, 185, 129, 0.32);
1390
+ --chat-ai-top: #151c2a;
1391
+ --chat-ai-bottom: #121826;
1392
+ --chat-ai-border: rgba(59, 130, 246, 0.32);
1393
  --progress-gradient: linear-gradient(120deg, #3b82f6, #06b6d4);
1394
  --scrollbar-thumb: rgba(59, 130, 246, 0.5);
1395
  --scrollbar-track: rgba(15, 23, 42, 0.6);
 
1406
  --bg-glow-secondary: rgba(16, 185, 129, 0.16);
1407
  --accent-shadow: rgba(20, 184, 166, 0.32);
1408
  --focus-ring: rgba(20, 184, 166, 0.4);
1409
+ --chat-user-top: #15201c;
1410
+ --chat-user-bottom: #121a18;
1411
+ --chat-user-border: rgba(16, 185, 129, 0.32);
1412
+ --chat-ai-top: #14201e;
1413
+ --chat-ai-bottom: #121826;
1414
+ --chat-ai-border: rgba(20, 184, 166, 0.32);
1415
  --progress-gradient: linear-gradient(120deg, #14b8a6, #10b981);
1416
  --scrollbar-thumb: rgba(20, 184, 166, 0.5);
1417
  --scrollbar-track: rgba(15, 23, 42, 0.6);
 
1428
  --bg-glow-secondary: rgba(236, 72, 153, 0.16);
1429
  --accent-shadow: rgba(168, 85, 247, 0.32);
1430
  --focus-ring: rgba(168, 85, 247, 0.4);
1431
+ --chat-user-top: #1a1624;
1432
+ --chat-user-bottom: #15121e;
1433
+ --chat-user-border: rgba(168, 85, 247, 0.32);
1434
+ --chat-ai-top: #1a1524;
1435
+ --chat-ai-bottom: #121826;
1436
+ --chat-ai-border: rgba(168, 85, 247, 0.32);
1437
  --progress-gradient: linear-gradient(120deg, #a855f7, #ec4899);
1438
  --scrollbar-thumb: rgba(168, 85, 247, 0.5);
1439
  --scrollbar-track: rgba(15, 23, 42, 0.6);
1440
  }
1441
 
1442
+ html[data-mode="QA"],
1443
+ html[data-mode="System Chart Extraction"],
1444
+ html[data-mode="Hypotheses Generation"] {
1445
+ color-scheme: dark;
1446
+ }
1447
+
1448
  html[data-mode="QA"] body,
1449
  html[data-mode="System Chart Extraction"] body,
1450
  html[data-mode="Hypotheses Generation"] body {
1451
+ display: block;
1452
+ justify-content: initial;
1453
+ align-items: initial;
1454
+ padding: 0;
1455
+ min-height: 100vh;
1456
+ color-scheme: dark;
1457
  background:
1458
+ radial-gradient(circle at top left, var(--bg-glow-primary), transparent 50%),
1459
  radial-gradient(circle at 20% 80%, var(--bg-glow-secondary), transparent 45%),
1460
  linear-gradient(160deg, #0b0d12 0%, #0c121f 60%, #0a0f1a 100%);
1461
  color: var(--text);
 
1468
  font-family: inherit;
1469
  }
1470
 
1471
+ /* Glow orb (PlotExtract .glow) */
1472
+ .app-glow {
1473
+ display: none;
1474
+ position: fixed;
1475
+ inset: -20% 10% auto auto;
1476
+ width: 420px;
1477
+ height: 420px;
1478
+ border-radius: 50%;
1479
+ background: radial-gradient(circle, var(--bg-glow-primary), transparent 70%);
1480
+ filter: blur(5px);
1481
+ z-index: -1;
1482
+ pointer-events: none;
1483
+ }
1484
+
1485
+ /* App shell — match PlotExtract .shell (transparent, stacked panels) */
1486
  html[data-mode="QA"] #app,
1487
  html[data-mode="System Chart Extraction"] #app,
1488
  html[data-mode="Hypotheses Generation"] #app {
1489
+ max-width: 1150px;
1490
+ width: 100%;
1491
+ margin: 0 auto;
1492
+ padding: 48px 24px 64px;
1493
+ flex-direction: column;
1494
+ gap: 32px;
1495
+ background: transparent;
1496
+ border: none;
1497
+ box-shadow: none;
1498
+ backdrop-filter: none;
1499
+ border-radius: 0;
1500
+ color: var(--text);
1501
+ }
1502
+
1503
+ /* Floating panels (PlotExtract .panel) */
1504
+ html[data-mode="QA"] .panel,
1505
+ html[data-mode="System Chart Extraction"] .panel,
1506
+ html[data-mode="Hypotheses Generation"] .panel {
1507
+ background: var(--surface);
1508
+ border-radius: var(--radius-lg);
1509
+ padding: 28px;
1510
  border: 1px solid var(--border);
1511
  box-shadow: var(--shadow-md);
1512
  backdrop-filter: blur(12px);
1513
+ }
1514
+
1515
+ html[data-mode="QA"] .panel-head,
1516
+ html[data-mode="System Chart Extraction"] .panel-head,
1517
+ html[data-mode="Hypotheses Generation"] .panel-head {
1518
+ margin-bottom: 8px;
1519
+ }
1520
+
1521
+ html[data-mode="QA"] .panel-head h2,
1522
+ html[data-mode="System Chart Extraction"] .panel-head h2,
1523
+ html[data-mode="Hypotheses Generation"] .panel-head h2 {
1524
+ margin: 0 0 8px;
1525
+ color: var(--text);
1526
+ font-family: var(--font-display);
1527
+ font-size: 1.35rem;
1528
+ font-weight: 600;
1529
+ }
1530
+
1531
+ html[data-mode="QA"] .panel-head p,
1532
+ html[data-mode="System Chart Extraction"] .panel-head p,
1533
+ html[data-mode="Hypotheses Generation"] .panel-head p {
1534
+ margin: 0;
1535
+ color: var(--muted);
1536
+ line-height: 1.55;
1537
+ }
1538
+
1539
+ html[data-mode="QA"] .panel-subtitle,
1540
+ html[data-mode="System Chart Extraction"] .panel-subtitle,
1541
+ html[data-mode="Hypotheses Generation"] .panel-subtitle {
1542
+ margin: 0 0 10px;
1543
+ color: var(--text);
1544
+ font-family: var(--font-display);
1545
+ font-size: 1.05rem;
1546
+ }
1547
+
1548
+ /* Hero / brand (PlotExtract .brand + .subhead) */
1549
+ html[data-mode="QA"] .app-header,
1550
+ html[data-mode="System Chart Extraction"] .app-header,
1551
+ html[data-mode="Hypotheses Generation"] .app-header {
1552
+ text-align: left;
1553
+ margin-bottom: 0;
1554
+ }
1555
+
1556
+ html[data-mode="QA"] .app-header .brand,
1557
+ html[data-mode="System Chart Extraction"] .app-header .brand,
1558
+ html[data-mode="Hypotheses Generation"] .app-header .brand {
1559
+ display: inline-flex;
1560
+ align-items: center;
1561
+ gap: 12px;
1562
+ font-size: 1.7rem;
1563
+ font-weight: 600;
1564
+ margin-bottom: 12px;
1565
  color: var(--text);
1566
+ font-family: var(--font-display);
1567
  }
1568
 
1569
+ html[data-mode="QA"] .app-header .brand #appTitle,
1570
+ html[data-mode="System Chart Extraction"] .app-header .brand #appTitle,
1571
+ html[data-mode="Hypotheses Generation"] .app-header .brand #appTitle {
1572
  color: var(--text);
1573
  font-family: var(--font-display);
1574
  letter-spacing: 0.02em;
1575
  }
1576
 
1577
+ html[data-mode="QA"] .app-header .subhead,
1578
+ html[data-mode="System Chart Extraction"] .app-header .subhead,
1579
+ html[data-mode="Hypotheses Generation"] .app-header .subhead {
1580
  color: var(--muted);
1581
+ line-height: 1.6;
1582
+ margin: 0;
1583
+ font-size: 1rem;
1584
+ max-width: 720px;
1585
  }
1586
 
1587
  html[data-mode="QA"] .section-title,
 
1589
  html[data-mode="Hypotheses Generation"] .section-title {
1590
  color: var(--text);
1591
  font-family: var(--font-display);
1592
+ margin-top: 1.4rem;
1593
+ margin-bottom: 0.75rem;
1594
+ }
1595
+
1596
+ html[data-mode="QA"] .panel > .section-title:first-child,
1597
+ html[data-mode="System Chart Extraction"] .panel > .section-title:first-child,
1598
+ html[data-mode="Hypotheses Generation"] .panel > .section-title:first-child {
1599
+ margin-top: 0.4rem;
1600
+ }
1601
+
1602
+ html[data-mode="QA"] .hint,
1603
+ html[data-mode="System Chart Extraction"] .hint,
1604
+ html[data-mode="Hypotheses Generation"] .hint {
1605
+ margin: 0 0 0.65rem;
1606
+ color: var(--muted);
1607
+ font-size: 0.85rem;
1608
+ line-height: 1.5;
1609
+ }
1610
+
1611
+ html[data-mode="QA"] .hint.left,
1612
+ html[data-mode="System Chart Extraction"] .hint.left,
1613
+ html[data-mode="Hypotheses Generation"] .hint.left {
1614
+ text-align: left;
1615
+ display: block;
1616
+ }
1617
+
1618
+ html[data-mode="QA"] .label,
1619
+ html[data-mode="System Chart Extraction"] .label,
1620
+ html[data-mode="Hypotheses Generation"] .label {
1621
+ display: block;
1622
+ font-weight: 600;
1623
+ color: var(--text);
1624
+ margin-bottom: 6px;
1625
+ }
1626
+
1627
+ html[data-mode="QA"] .input-row,
1628
+ html[data-mode="System Chart Extraction"] .input-row,
1629
+ html[data-mode="Hypotheses Generation"] .input-row {
1630
+ display: flex;
1631
+ gap: 12px;
1632
+ align-items: flex-end;
1633
+ flex-wrap: wrap;
1634
+ }
1635
+
1636
+ html[data-mode="QA"] .input-row input[type="text"],
1637
+ html[data-mode="System Chart Extraction"] .input-row input[type="text"],
1638
+ html[data-mode="Hypotheses Generation"] .input-row input[type="text"] {
1639
+ flex: 1;
1640
+ min-width: 180px;
1641
+ }
1642
+
1643
+ html[data-mode="QA"] .result-card,
1644
+ html[data-mode="System Chart Extraction"] .result-card,
1645
+ html[data-mode="Hypotheses Generation"] .result-card {
1646
+ background: var(--surface-strong);
1647
+ border-radius: 16px;
1648
+ padding: 18px;
1649
+ border: 1px solid rgba(148, 163, 184, 0.2);
1650
+ display: grid;
1651
+ gap: 12px;
1652
+ margin: 5px 0;
1653
+ }
1654
+
1655
+ html[data-mode="QA"] .result-card.hypotheses-options,
1656
+ html[data-mode="System Chart Extraction"] .result-card.hypotheses-options,
1657
+ html[data-mode="Hypotheses Generation"] .result-card.hypotheses-options {
1658
+ min-width: 280px;
1659
+ flex-shrink: 0;
1660
  }
1661
 
1662
  html[data-mode="QA"] hr,
 
1673
  color: var(--text);
1674
  }
1675
 
1676
+ /* Flatten nested form cards — panels already provide the PlotExtract surface */
1677
+ html[data-mode="QA"] #apiKeyForm,
1678
+ html[data-mode="QA"] #uploadForm,
1679
+ html[data-mode="QA"] #askForm,
1680
+ html[data-mode="QA"] #conversationForm,
1681
+ html[data-mode="QA"] #hypotheses-conversation-section,
1682
+ html[data-mode="QA"] #conversation-section,
1683
+ html[data-mode="System Chart Extraction"] #apiKeyForm,
1684
+ html[data-mode="System Chart Extraction"] #uploadForm,
1685
+ html[data-mode="System Chart Extraction"] #askForm,
1686
+ html[data-mode="System Chart Extraction"] #conversationForm,
1687
+ html[data-mode="System Chart Extraction"] #hypotheses-conversation-section,
1688
+ html[data-mode="System Chart Extraction"] #conversation-section,
1689
+ html[data-mode="Hypotheses Generation"] #apiKeyForm,
1690
+ html[data-mode="Hypotheses Generation"] #uploadForm,
1691
+ html[data-mode="Hypotheses Generation"] #askForm,
1692
+ html[data-mode="Hypotheses Generation"] #conversationForm,
1693
+ html[data-mode="Hypotheses Generation"] #hypotheses-conversation-section,
1694
+ html[data-mode="Hypotheses Generation"] #conversation-section {
1695
+ border: none !important;
1696
+ background: transparent !important;
1697
+ box-shadow: none !important;
1698
+ padding: 0 !important;
1699
+ margin-top: 0;
1700
+ }
1701
+
1702
+ /* Inputs, selects, textareas — PlotExtract field chrome */
1703
  html[data-mode="QA"] form input[type="text"],
1704
  html[data-mode="QA"] form input[type="password"],
1705
  html[data-mode="QA"] form input[type="number"],
1706
  html[data-mode="QA"] form select,
1707
  html[data-mode="QA"] form textarea,
1708
+ html[data-mode="QA"] #hypothesis-text-input,
1709
+ html[data-mode="QA"] #original-paper-content-input,
1710
+ html[data-mode="QA"] #hypothesis-chat-input,
1711
+ html[data-mode="QA"] #followUpQuestion,
1712
+ html[data-mode="QA"] #conversationSelect,
1713
  html[data-mode="System Chart Extraction"] form input[type="text"],
1714
  html[data-mode="System Chart Extraction"] form input[type="password"],
1715
  html[data-mode="System Chart Extraction"] form input[type="number"],
1716
  html[data-mode="System Chart Extraction"] form select,
1717
  html[data-mode="System Chart Extraction"] form textarea,
1718
+ html[data-mode="System Chart Extraction"] #hypothesis-text-input,
1719
+ html[data-mode="System Chart Extraction"] #original-paper-content-input,
1720
+ html[data-mode="System Chart Extraction"] #hypothesis-chat-input,
1721
+ html[data-mode="System Chart Extraction"] #followUpQuestion,
1722
+ html[data-mode="System Chart Extraction"] #conversationSelect,
1723
  html[data-mode="Hypotheses Generation"] form input[type="text"],
1724
  html[data-mode="Hypotheses Generation"] form input[type="password"],
1725
  html[data-mode="Hypotheses Generation"] form input[type="number"],
1726
  html[data-mode="Hypotheses Generation"] form select,
1727
+ html[data-mode="Hypotheses Generation"] form textarea,
1728
+ html[data-mode="Hypotheses Generation"] #hypothesis-text-input,
1729
+ html[data-mode="Hypotheses Generation"] #original-paper-content-input,
1730
+ html[data-mode="Hypotheses Generation"] #hypothesis-chat-input,
1731
+ html[data-mode="Hypotheses Generation"] #followUpQuestion,
1732
+ html[data-mode="Hypotheses Generation"] #conversationSelect {
1733
+ background-color: rgba(15, 23, 42, 0.7) !important;
1734
  color: var(--text) !important;
1735
  border: 1px solid var(--border) !important;
1736
+ border-radius: 12px !important;
1737
+ padding: 12px 14px !important;
1738
+ font-size: 0.95rem;
1739
+ box-shadow: none !important;
1740
+ }
1741
+
1742
+ html[data-mode="QA"] #hypothesis-text-input,
1743
+ html[data-mode="QA"] #original-paper-content-input,
1744
+ html[data-mode="System Chart Extraction"] #hypothesis-text-input,
1745
+ html[data-mode="System Chart Extraction"] #original-paper-content-input,
1746
+ html[data-mode="Hypotheses Generation"] #hypothesis-text-input,
1747
+ html[data-mode="Hypotheses Generation"] #original-paper-content-input {
1748
+ width: 100%;
1749
+ min-height: 120px;
1750
+ resize: vertical;
1751
+ font-family: var(--font-mono);
1752
+ font-size: 0.85rem;
1753
+ line-height: 1.45;
1754
+ background: rgba(2, 6, 23, 0.7) !important;
1755
+ color: #e2e8f0 !important;
1756
+ }
1757
+
1758
+ /* Ask Questions — vertical stack, equal-width fields (match API key step) */
1759
+ html[data-mode="QA"] #askForm,
1760
+ html[data-mode="System Chart Extraction"] #askForm {
1761
+ display: flex !important;
1762
+ flex-direction: column;
1763
+ align-items: stretch;
1764
+ gap: 16px;
1765
+ max-width: 520px;
1766
+ }
1767
+
1768
+ html[data-mode="QA"] #askForm .form-inputs,
1769
+ html[data-mode="System Chart Extraction"] #askForm .form-inputs {
1770
+ display: flex;
1771
+ flex-direction: column;
1772
+ align-items: stretch;
1773
+ gap: 16px;
1774
+ width: 100%;
1775
+ flex-grow: 0;
1776
+ }
1777
+
1778
+ html[data-mode="QA"] #databaseContainer,
1779
+ html[data-mode="System Chart Extraction"] #databaseContainer,
1780
+ html[data-mode="QA"] #visualizationContainer,
1781
+ html[data-mode="System Chart Extraction"] #visualizationContainer,
1782
+ html[data-mode="QA"] #questionContainer,
1783
+ html[data-mode="System Chart Extraction"] #questionContainer {
1784
+ width: 100%;
1785
+ max-width: 100%;
1786
+ }
1787
+
1788
+ html[data-mode="QA"] #databaseContainer label,
1789
+ html[data-mode="System Chart Extraction"] #databaseContainer label {
1790
+ display: flex;
1791
+ flex-direction: column;
1792
+ gap: 0.45rem;
1793
+ width: 100%;
1794
+ font-weight: 600;
1795
+ }
1796
+
1797
+ html[data-mode="QA"] #databaseSelect,
1798
+ html[data-mode="System Chart Extraction"] #databaseSelect {
1799
+ width: 100% !important;
1800
+ min-height: 110px !important;
1801
+ box-sizing: border-box;
1802
+ }
1803
+
1804
+ /* Multi-select listboxes (Databases) — light-theme rules force white option
1805
+ backgrounds; without these overrides, white ink on white options is
1806
+ invisible (esp. Safari / native listboxes). */
1807
+ html[data-mode="QA"] select[multiple],
1808
+ html[data-mode="System Chart Extraction"] select[multiple],
1809
+ html[data-mode="Hypotheses Generation"] select[multiple],
1810
+ html[data-mode="QA"] #databaseSelect,
1811
+ html[data-mode="System Chart Extraction"] #databaseSelect {
1812
+ background-color: rgba(15, 23, 42, 0.85) !important;
1813
+ color: #f8fafc !important;
1814
+ border: 1px solid var(--border-strong) !important;
1815
+ color-scheme: dark;
1816
+ }
1817
+
1818
+ html[data-mode="QA"] select[multiple] option,
1819
+ html[data-mode="System Chart Extraction"] select[multiple] option,
1820
+ html[data-mode="Hypotheses Generation"] select[multiple] option,
1821
+ html[data-mode="QA"] select option,
1822
+ html[data-mode="System Chart Extraction"] select option,
1823
+ html[data-mode="Hypotheses Generation"] select option {
1824
+ background-color: #121826 !important;
1825
+ color: #f8fafc !important;
1826
+ }
1827
+
1828
+ html[data-mode="QA"] select[multiple] option:checked,
1829
+ html[data-mode="System Chart Extraction"] select[multiple] option:checked,
1830
+ html[data-mode="Hypotheses Generation"] select[multiple] option:checked {
1831
+ background-color: var(--primary) !important;
1832
+ background: linear-gradient(0deg, var(--primary) 0%, var(--primary) 100%) !important;
1833
+ color: #ffffff !important;
1834
+ }
1835
+
1836
+ html[data-mode="QA"] select[multiple] option:hover,
1837
+ html[data-mode="System Chart Extraction"] select[multiple] option:hover,
1838
+ html[data-mode="Hypotheses Generation"] select[multiple] option:hover {
1839
+ background-color: rgba(148, 163, 184, 0.18) !important;
1840
+ }
1841
+
1842
+ /* QA question field — full-width, roomy textarea */
1843
+ html[data-mode="QA"] #questionContainer {
1844
+ width: 100%;
1845
+ margin-top: 0;
1846
+ }
1847
+
1848
+ html[data-mode="QA"] #questionLabel {
1849
+ display: block;
1850
+ margin-bottom: 0.45rem;
1851
+ font-weight: 600;
1852
+ color: var(--text);
1853
+ }
1854
+
1855
+ html[data-mode="QA"] #questionInput {
1856
+ display: block;
1857
+ width: 100%;
1858
+ min-height: 96px;
1859
+ resize: vertical;
1860
+ padding: 14px 16px !important;
1861
+ font-size: 1rem !important;
1862
+ line-height: 1.5;
1863
+ border-radius: 14px !important;
1864
+ background-color: rgba(15, 23, 42, 0.85) !important;
1865
+ border: 1px solid var(--border-strong) !important;
1866
+ color: var(--text) !important;
1867
+ box-sizing: border-box;
1868
+ }
1869
+
1870
+ html[data-mode="QA"] #questionInput::placeholder {
1871
+ color: var(--muted);
1872
+ opacity: 0.85;
1873
+ }
1874
+
1875
+ html[data-mode="QA"] #questionInput:focus {
1876
+ border-color: var(--primary) !important;
1877
+ box-shadow: 0 0 0 3px var(--focus-ring) !important;
1878
+ outline: none;
1879
+ }
1880
+
1881
+ html[data-mode="QA"] #original-paper-content-input,
1882
+ html[data-mode="System Chart Extraction"] #original-paper-content-input,
1883
+ html[data-mode="Hypotheses Generation"] #original-paper-content-input {
1884
+ min-height: 200px;
1885
  }
1886
 
1887
  html[data-mode="QA"] form input[type="file"],
 
1890
  color: var(--muted);
1891
  }
1892
 
1893
+ /* Single-select dropdown chrome (model / conversation pickers) */
1894
+ html[data-mode="QA"] select:not([multiple]),
1895
+ html[data-mode="System Chart Extraction"] select:not([multiple]),
1896
+ html[data-mode="Hypotheses Generation"] select:not([multiple]) {
1897
+ background-color: rgba(15, 23, 42, 0.7) !important;
1898
+ color: #f8fafc !important;
1899
+ color-scheme: dark;
1900
+ }
1901
+
1902
  html[data-mode="QA"] form input:focus,
1903
  html[data-mode="QA"] form select:focus,
1904
  html[data-mode="QA"] form textarea:focus,
 
1913
  box-shadow: 0 0 0 3px var(--focus-ring);
1914
  }
1915
 
1916
+ /* Submit / Upload / Ask — same size, left-aligned across all modes */
1917
+ html[data-mode="QA"] #modelSubmitButton,
1918
+ html[data-mode="QA"] #uploadForm button[type="submit"],
1919
+ html[data-mode="QA"] #askForm > button[type="submit"],
1920
+ html[data-mode="System Chart Extraction"] #modelSubmitButton,
1921
+ html[data-mode="System Chart Extraction"] #uploadForm button[type="submit"],
1922
+ html[data-mode="System Chart Extraction"] #askForm > button[type="submit"],
1923
+ html[data-mode="Hypotheses Generation"] #modelSubmitButton,
1924
+ html[data-mode="Hypotheses Generation"] #uploadForm button[type="submit"],
1925
+ html[data-mode="Hypotheses Generation"] #askForm > button[type="submit"] {
1926
+ width: 140px !important;
1927
+ min-width: 140px !important;
1928
+ max-width: 140px !important;
1929
+ height: 44px !important;
1930
+ padding: 0 20px !important;
1931
+ margin: 0 !important;
1932
+ margin-left: 0 !important;
1933
+ margin-top: 4px !important;
1934
+ align-self: flex-start !important;
1935
+ box-sizing: border-box;
1936
+ display: inline-flex !important;
1937
+ align-items: center;
1938
+ justify-content: center;
1939
+ }
1940
+
1941
+ html[data-mode="QA"] .api-key-actions,
1942
+ html[data-mode="System Chart Extraction"] .api-key-actions,
1943
+ html[data-mode="Hypotheses Generation"] .api-key-actions,
1944
+ html[data-mode="QA"] .upload-panel-grid .upload-actions,
1945
+ html[data-mode="System Chart Extraction"] .upload-panel-grid .upload-actions,
1946
+ html[data-mode="Hypotheses Generation"] .upload-panel-grid .upload-actions {
1947
+ display: flex;
1948
+ justify-content: flex-start;
1949
+ align-items: center;
1950
+ margin-top: 20px;
1951
+ }
1952
+
1953
+ html[data-mode="QA"] #askForm > button[type="submit"],
1954
+ html[data-mode="System Chart Extraction"] #askForm > button[type="submit"],
1955
+ html[data-mode="Hypotheses Generation"] #askForm > button[type="submit"] {
1956
+ align-self: flex-start !important;
1957
+ margin-top: 4px !important;
1958
+ }
1959
+
1960
+ /* Buttons — PlotExtract .primary, keeping each mode's accent pair */
1961
+ html[data-mode="QA"] form button:not(.ghost),
1962
  html[data-mode="QA"] form button[type="submit"],
1963
  html[data-mode="QA"] button.primary,
1964
+ html[data-mode="QA"] #start-hypothesis-conversation-btn,
1965
+ html[data-mode="QA"] #send-hypothesis-message-btn,
1966
+ html[data-mode="QA"] #loadConversationBtn,
1967
+ html[data-mode="System Chart Extraction"] form button:not(.ghost),
1968
  html[data-mode="System Chart Extraction"] form button[type="submit"],
1969
  html[data-mode="System Chart Extraction"] button.primary,
1970
+ html[data-mode="System Chart Extraction"] #start-hypothesis-conversation-btn,
1971
+ html[data-mode="System Chart Extraction"] #send-hypothesis-message-btn,
1972
+ html[data-mode="System Chart Extraction"] #loadConversationBtn,
1973
+ html[data-mode="Hypotheses Generation"] form button:not(.ghost),
1974
  html[data-mode="Hypotheses Generation"] form button[type="submit"],
1975
+ html[data-mode="Hypotheses Generation"] button.primary,
1976
+ html[data-mode="Hypotheses Generation"] #start-hypothesis-conversation-btn,
1977
+ html[data-mode="Hypotheses Generation"] #send-hypothesis-message-btn,
1978
+ html[data-mode="Hypotheses Generation"] #loadConversationBtn {
1979
  background: linear-gradient(120deg, var(--primary), var(--accent-2)) !important;
1980
  color: #fff !important;
1981
  border: none !important;
1982
+ padding: 12px 26px !important;
1983
+ border-radius: 12px !important;
1984
  font-weight: 600;
1985
  cursor: pointer;
1986
+ box-shadow: 0 10px 30px var(--accent-shadow);
1987
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
1988
+ }
1989
+
1990
+ html[data-mode="QA"] form button:not(.ghost):hover:not(:disabled),
1991
+ html[data-mode="QA"] #start-hypothesis-conversation-btn:hover:not(:disabled),
1992
+ html[data-mode="QA"] #send-hypothesis-message-btn:hover:not(:disabled),
1993
+ html[data-mode="QA"] #loadConversationBtn:hover:not(:disabled),
1994
+ html[data-mode="System Chart Extraction"] form button:not(.ghost):hover:not(:disabled),
1995
+ html[data-mode="System Chart Extraction"] #start-hypothesis-conversation-btn:hover:not(:disabled),
1996
+ html[data-mode="System Chart Extraction"] #send-hypothesis-message-btn:hover:not(:disabled),
1997
+ html[data-mode="System Chart Extraction"] #loadConversationBtn:hover:not(:disabled),
1998
+ html[data-mode="Hypotheses Generation"] form button:not(.ghost):hover:not(:disabled),
1999
+ html[data-mode="Hypotheses Generation"] #start-hypothesis-conversation-btn:hover:not(:disabled),
2000
+ html[data-mode="Hypotheses Generation"] #send-hypothesis-message-btn:hover:not(:disabled),
2001
+ html[data-mode="Hypotheses Generation"] #loadConversationBtn:hover:not(:disabled) {
2002
  transform: translateY(-1px);
2003
+ box-shadow: 0 12px 34px var(--accent-shadow);
2004
  }
2005
 
2006
+ html[data-mode="QA"] form button:not(.ghost):disabled,
2007
+ html[data-mode="System Chart Extraction"] form button:not(.ghost):disabled,
2008
+ html[data-mode="Hypotheses Generation"] form button:not(.ghost):disabled {
2009
+ opacity: 0.6;
2010
  cursor: not-allowed;
2011
+ transform: none;
2012
+ }
2013
+
2014
+ /* PlotExtract .ghost (Show / Hide key) */
2015
+ html[data-mode="QA"] .ghost,
2016
+ html[data-mode="System Chart Extraction"] .ghost,
2017
+ html[data-mode="Hypotheses Generation"] .ghost {
2018
+ background: transparent !important;
2019
+ border: 1px solid var(--border) !important;
2020
+ color: var(--text) !important;
2021
+ padding: 10px 14px !important;
2022
+ border-radius: 12px !important;
2023
+ cursor: pointer;
2024
+ font-weight: 500;
2025
+ box-shadow: none !important;
2026
+ transition: border 0.2s ease, color 0.2s ease;
2027
+ white-space: nowrap;
2028
+ }
2029
+
2030
+ html[data-mode="QA"] .ghost:hover,
2031
+ html[data-mode="System Chart Extraction"] .ghost:hover,
2032
+ html[data-mode="Hypotheses Generation"] .ghost:hover {
2033
+ border-color: var(--accent-2) !important;
2034
+ color: var(--accent-2) !important;
2035
+ transform: none;
2036
+ box-shadow: none !important;
2037
+ }
2038
+
2039
+ html[data-mode="QA"] #apiKeyForm,
2040
+ html[data-mode="System Chart Extraction"] #apiKeyForm,
2041
+ html[data-mode="Hypotheses Generation"] #apiKeyForm {
2042
+ display: block !important;
2043
+ flex-wrap: nowrap;
2044
+ }
2045
+
2046
+ html[data-mode="QA"] .api-key-stack,
2047
+ html[data-mode="System Chart Extraction"] .api-key-stack,
2048
+ html[data-mode="Hypotheses Generation"] .api-key-stack {
2049
+ max-width: 520px;
2050
+ }
2051
+
2052
+ html[data-mode="QA"] .api-key-stack .input-row,
2053
+ html[data-mode="System Chart Extraction"] .api-key-stack .input-row,
2054
+ html[data-mode="Hypotheses Generation"] .api-key-stack .input-row {
2055
+ align-items: stretch;
2056
+ flex-wrap: nowrap;
2057
+ }
2058
+
2059
+ html[data-mode="QA"] .api-key-stack .input-row input,
2060
+ html[data-mode="System Chart Extraction"] .api-key-stack .input-row input,
2061
+ html[data-mode="Hypotheses Generation"] .api-key-stack .input-row input {
2062
+ flex: 1;
2063
+ min-width: 0;
2064
+ }
2065
+
2066
+ html[data-mode="QA"] #modelSelect,
2067
+ html[data-mode="System Chart Extraction"] #modelSelect,
2068
+ html[data-mode="Hypotheses Generation"] #modelSelect {
2069
+ appearance: none;
2070
+ -webkit-appearance: none;
2071
+ -moz-appearance: none;
2072
+ background-image: none !important;
2073
  }
2074
 
2075
  /* Provider badge + model status (used in API key form) */
 
2116
  color: #34d399;
2117
  }
2118
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2119
  html[data-mode="QA"] details summary,
2120
  html[data-mode="System Chart Extraction"] details summary,
2121
  html[data-mode="Hypotheses Generation"] details summary {
2122
  color: var(--text);
2123
  }
2124
 
2125
+ /* Chat / message areas — PlotExtract .preview surface */
2126
  html[data-mode="QA"] #chat-area,
2127
  html[data-mode="System Chart Extraction"] #chat-area,
2128
  html[data-mode="Hypotheses Generation"] #chat-area {
2129
+ background: rgba(11, 13, 18, 0.72) !important;
2130
+ border: 1px solid var(--border) !important;
2131
+ border-radius: 14px;
2132
+ padding: 20px;
2133
  color: var(--text);
2134
+ margin-top: 1.25rem;
2135
+ min-height: 120px;
2136
+ box-shadow: none;
2137
+ }
2138
+
2139
+ /* Hide empty message panes until a conversation starts */
2140
+ #chat-area:empty,
2141
+ #hypothesis-chat-messages:empty,
2142
+ #conversation-messages:empty {
2143
+ display: none !important;
2144
+ }
2145
+
2146
+ html[data-mode="QA"] #hypothesis-chat-messages,
2147
+ html[data-mode="QA"] #conversation-messages,
2148
+ html[data-mode="System Chart Extraction"] #hypothesis-chat-messages,
2149
+ html[data-mode="System Chart Extraction"] #conversation-messages,
2150
+ html[data-mode="Hypotheses Generation"] #hypothesis-chat-messages,
2151
+ html[data-mode="Hypotheses Generation"] #conversation-messages {
2152
+ background: rgba(11, 13, 18, 0.55) !important;
2153
+ border: 1px solid var(--border) !important;
2154
+ border-radius: 14px !important;
2155
+ padding: 1rem !important;
2156
+ max-height: 400px;
2157
+ overflow-y: auto;
2158
+ margin-bottom: 10px;
2159
+ color: var(--text) !important;
2160
  }
2161
 
2162
  html[data-mode="QA"] .user-bubble,
 
2165
  background: linear-gradient(160deg, var(--chat-user-top), var(--chat-user-bottom));
2166
  border: 1px solid var(--chat-user-border);
2167
  color: var(--text);
2168
+ box-shadow: none;
2169
  }
2170
 
2171
  html[data-mode="QA"] .ai-bubble,
 
2174
  background: linear-gradient(160deg, var(--chat-ai-top), var(--chat-ai-bottom));
2175
  border: 1px solid var(--chat-ai-border);
2176
  color: var(--text);
2177
+ box-shadow: none;
2178
  }
2179
 
2180
  html[data-mode="QA"] .error-bubble,
 
2188
  html[data-mode="QA"] .references-section,
2189
  html[data-mode="System Chart Extraction"] .references-section,
2190
  html[data-mode="Hypotheses Generation"] .references-section {
 
 
 
2191
  color: var(--text);
2192
  }
2193
+ html[data-mode="QA"] .references-section h2,
2194
  html[data-mode="QA"] .references-section h3,
2195
+ html[data-mode="System Chart Extraction"] .references-section h2,
2196
  html[data-mode="System Chart Extraction"] .references-section h3,
2197
+ html[data-mode="Hypotheses Generation"] .references-section h2,
2198
  html[data-mode="Hypotheses Generation"] .references-section h3 {
2199
  color: var(--text);
2200
+ border-bottom: none;
2201
+ margin-bottom: 0.75rem;
2202
+ }
2203
+
2204
+ html[data-mode="QA"] .mode-footer,
2205
+ html[data-mode="System Chart Extraction"] .mode-footer,
2206
+ html[data-mode="Hypotheses Generation"] .mode-footer {
2207
+ margin-top: 0;
2208
+ text-align: center;
2209
+ color: var(--muted);
2210
+ font-size: 0.9rem;
2211
+ line-height: 1.6;
2212
+ max-width: 920px;
2213
+ margin-left: auto;
2214
+ margin-right: auto;
2215
+ }
2216
+
2217
+ html[data-mode="QA"] .info-box,
2218
+ html[data-mode="QA"] #openaiOnlyBanner .info-box,
2219
+ html[data-mode="System Chart Extraction"] .info-box,
2220
+ html[data-mode="System Chart Extraction"] #openaiOnlyBanner .info-box,
2221
+ html[data-mode="Hypotheses Generation"] .info-box,
2222
+ html[data-mode="Hypotheses Generation"] #openaiOnlyBanner .info-box {
2223
+ background: var(--primary-soft) !important;
2224
+ border-left: 4px solid var(--primary) !important;
2225
+ border-radius: 12px;
2226
+ color: var(--text) !important;
2227
+ }
2228
+
2229
+ html[data-mode="QA"] .model-selection-banner h2,
2230
+ html[data-mode="QA"] #openaiOnlyBanner h2,
2231
+ html[data-mode="System Chart Extraction"] .model-selection-banner h2,
2232
+ html[data-mode="System Chart Extraction"] #openaiOnlyBanner h2,
2233
+ html[data-mode="Hypotheses Generation"] .model-selection-banner h2,
2234
+ html[data-mode="Hypotheses Generation"] #openaiOnlyBanner h2 {
2235
+ color: var(--text) !important;
2236
+ font-family: var(--font-display);
2237
+ font-size: 1.25rem;
2238
+ }
2239
+
2240
+ html[data-mode="QA"] .model-selection-banner,
2241
+ html[data-mode="QA"] #openaiOnlyBanner,
2242
+ html[data-mode="System Chart Extraction"] .model-selection-banner,
2243
+ html[data-mode="System Chart Extraction"] #openaiOnlyBanner,
2244
+ html[data-mode="Hypotheses Generation"] .model-selection-banner,
2245
+ html[data-mode="Hypotheses Generation"] #openaiOnlyBanner {
2246
+ background: var(--surface-strong) !important;
2247
+ border: 1px solid rgba(148, 163, 184, 0.2) !important;
2248
+ border-radius: 16px;
2249
+ color: var(--text);
2250
+ text-align: left;
2251
+ padding: 18px;
2252
+ margin-bottom: 0.5rem;
2253
+ }
2254
+
2255
+ html[data-mode="QA"] .qa-result-card,
2256
+ html[data-mode="System Chart Extraction"] .qa-result-card,
2257
+ html[data-mode="Hypotheses Generation"] .qa-result-card {
2258
+ background: var(--surface-strong);
2259
+ border: 1px solid rgba(148, 163, 184, 0.2);
2260
+ border-left: 4px solid var(--primary);
2261
+ color: var(--text);
2262
+ }
2263
+
2264
+ html[data-mode="QA"] .qa-response,
2265
+ html[data-mode="QA"] .qa-response .qa-p,
2266
+ html[data-mode="QA"] .qa-response .qa-h,
2267
+ html[data-mode="System Chart Extraction"] .qa-response,
2268
+ html[data-mode="System Chart Extraction"] .qa-response .qa-p,
2269
+ html[data-mode="System Chart Extraction"] .qa-response .qa-h,
2270
+ html[data-mode="Hypotheses Generation"] .qa-response,
2271
+ html[data-mode="Hypotheses Generation"] .qa-response .qa-p,
2272
+ html[data-mode="Hypotheses Generation"] .qa-response .qa-h {
2273
+ color: var(--text);
2274
+ }
2275
+
2276
+ html[data-mode="QA"] .qa-response .qa-blockquote,
2277
+ html[data-mode="System Chart Extraction"] .qa-response .qa-blockquote,
2278
+ html[data-mode="Hypotheses Generation"] .qa-response .qa-blockquote {
2279
+ background: var(--primary-soft);
2280
+ color: var(--muted);
2281
+ border-left-color: var(--primary);
2282
+ }
2283
+
2284
+ html[data-mode="QA"] .qa-response .qa-code-inline,
2285
+ html[data-mode="System Chart Extraction"] .qa-response .qa-code-inline,
2286
+ html[data-mode="Hypotheses Generation"] .qa-response .qa-code-inline {
2287
+ background: rgba(15, 23, 42, 0.85);
2288
+ color: #e2e8f0;
2289
+ border-color: var(--border);
2290
+ }
2291
+
2292
+ html[data-mode="QA"] .qa-response .qa-table-wrap,
2293
+ html[data-mode="System Chart Extraction"] .qa-response .qa-table-wrap,
2294
+ html[data-mode="Hypotheses Generation"] .qa-response .qa-table-wrap {
2295
+ background: var(--surface-strong);
2296
+ border-color: var(--border);
2297
+ }
2298
+
2299
+ html[data-mode="QA"] .qa-response .qa-table th,
2300
+ html[data-mode="System Chart Extraction"] .qa-response .qa-table th,
2301
+ html[data-mode="Hypotheses Generation"] .qa-response .qa-table th {
2302
+ background: var(--primary-soft);
2303
+ color: var(--text);
2304
+ border-bottom-color: var(--border);
2305
+ }
2306
+
2307
+ html[data-mode="QA"] .qa-response .qa-table td,
2308
+ html[data-mode="System Chart Extraction"] .qa-response .qa-table td,
2309
+ html[data-mode="Hypotheses Generation"] .qa-response .qa-table td {
2310
+ border-bottom-color: var(--border);
2311
+ color: var(--muted);
2312
+ }
2313
+
2314
+ html[data-mode="QA"] .qa-sources-body,
2315
+ html[data-mode="System Chart Extraction"] .qa-sources-body,
2316
+ html[data-mode="Hypotheses Generation"] .qa-sources-body {
2317
+ background: var(--surface-soft);
2318
+ border-color: var(--border);
2319
+ color: var(--muted);
2320
+ }
2321
+
2322
+ /* Dropzone + stacks (PlotExtract upload chrome; accent border uses mode primary) */
2323
+ html[data-mode="QA"] .stack,
2324
+ html[data-mode="System Chart Extraction"] .stack,
2325
+ html[data-mode="Hypotheses Generation"] .stack {
2326
+ display: grid;
2327
+ gap: 12px;
2328
+ }
2329
+
2330
+ html[data-mode="QA"] .stack.compact .hint,
2331
+ html[data-mode="System Chart Extraction"] .stack.compact .hint,
2332
+ html[data-mode="Hypotheses Generation"] .stack.compact .hint {
2333
+ margin-bottom: 6px;
2334
+ }
2335
+
2336
+ html[data-mode="QA"] .upload-panel-grid,
2337
+ html[data-mode="System Chart Extraction"] .upload-panel-grid {
2338
+ display: grid !important;
2339
+ grid-template-columns: 1fr;
2340
+ gap: 16px;
2341
+ align-items: stretch;
2342
+ margin-top: 12px;
2343
+ max-width: 520px;
2344
+ }
2345
+
2346
+ html[data-mode="QA"] .upload-panel-grid .stack,
2347
+ html[data-mode="System Chart Extraction"] .upload-panel-grid .stack,
2348
+ html[data-mode="QA"] .upload-panel-grid input[type="text"],
2349
+ html[data-mode="System Chart Extraction"] .upload-panel-grid input[type="text"],
2350
+ html[data-mode="QA"] .upload-panel-grid .dropzone,
2351
+ html[data-mode="System Chart Extraction"] .upload-panel-grid .dropzone {
2352
+ width: 100%;
2353
+ box-sizing: border-box;
2354
+ }
2355
+
2356
+ html[data-mode="Hypotheses Generation"] .upload-panel-grid {
2357
+ display: grid !important;
2358
+ grid-template-columns: repeat(2, minmax(240px, 1fr));
2359
+ gap: 24px;
2360
+ align-items: start;
2361
+ margin-top: 12px;
2362
+ }
2363
+
2364
+ html[data-mode="QA"] .upload-panel-grid .upload-actions,
2365
+ html[data-mode="System Chart Extraction"] .upload-panel-grid .upload-actions,
2366
+ html[data-mode="Hypotheses Generation"] .upload-panel-grid .upload-actions {
2367
+ grid-column: 1 / -1;
2368
+ display: flex;
2369
+ align-items: center;
2370
+ justify-content: flex-start;
2371
+ margin-top: 20px;
2372
+ }
2373
+
2374
+ html[data-mode="QA"] .dropzone,
2375
+ html[data-mode="System Chart Extraction"] .dropzone,
2376
+ html[data-mode="Hypotheses Generation"] .dropzone {
2377
+ position: relative;
2378
+ border: 1px dashed color-mix(in srgb, var(--primary) 70%, transparent);
2379
+ border-radius: 16px;
2380
+ padding: 18px;
2381
+ min-height: 140px;
2382
+ display: flex;
2383
+ align-items: center;
2384
+ justify-content: center;
2385
+ background: rgba(15, 23, 42, 0.4);
2386
+ transition: border 0.2s ease, transform 0.2s ease;
2387
+ }
2388
+
2389
+ html[data-mode="QA"] .dropzone.tall,
2390
+ html[data-mode="System Chart Extraction"] .dropzone.tall,
2391
+ html[data-mode="Hypotheses Generation"] .dropzone.tall {
2392
+ min-height: 200px;
2393
+ }
2394
+
2395
+ html[data-mode="QA"] .dropzone input[type="file"],
2396
+ html[data-mode="System Chart Extraction"] .dropzone input[type="file"],
2397
+ html[data-mode="Hypotheses Generation"] .dropzone input[type="file"] {
2398
+ position: absolute;
2399
+ inset: 0;
2400
+ opacity: 0;
2401
+ cursor: pointer;
2402
+ width: 100%;
2403
+ height: 100%;
2404
+ padding: 0 !important;
2405
+ border: none !important;
2406
+ background: transparent !important;
2407
+ }
2408
+
2409
+ html[data-mode="QA"] .dropzone-content,
2410
+ html[data-mode="System Chart Extraction"] .dropzone-content,
2411
+ html[data-mode="Hypotheses Generation"] .dropzone-content {
2412
+ text-align: center;
2413
+ pointer-events: none;
2414
+ }
2415
+
2416
+ html[data-mode="QA"] .dropzone-content p,
2417
+ html[data-mode="System Chart Extraction"] .dropzone-content p,
2418
+ html[data-mode="Hypotheses Generation"] .dropzone-content p {
2419
+ margin: 0 0 8px;
2420
+ color: var(--text);
2421
+ }
2422
+
2423
+ html[data-mode="QA"] .small,
2424
+ html[data-mode="System Chart Extraction"] .small,
2425
+ html[data-mode="Hypotheses Generation"] .small {
2426
+ font-size: 0.85rem;
2427
+ color: var(--muted);
2428
+ }
2429
+
2430
+ html[data-mode="QA"] #adminFab,
2431
+ html[data-mode="System Chart Extraction"] #adminFab,
2432
+ html[data-mode="Hypotheses Generation"] #adminFab {
2433
+ background: var(--surface);
2434
+ color: var(--text);
2435
+ border: 1px solid var(--border);
2436
+ box-shadow: var(--shadow-sm);
2437
+ }
2438
+
2439
+ @media (max-width: 720px) {
2440
+ html[data-mode="QA"] .upload-panel-grid,
2441
+ html[data-mode="System Chart Extraction"] .upload-panel-grid,
2442
+ html[data-mode="Hypotheses Generation"] .upload-panel-grid {
2443
+ grid-template-columns: 1fr;
2444
+ }
2445
+
2446
+ html[data-mode="QA"] #app,
2447
+ html[data-mode="System Chart Extraction"] #app,
2448
+ html[data-mode="Hypotheses Generation"] #app {
2449
+ padding: 28px 16px 48px;
2450
+ }
2451
  }
2452
  html[data-mode="QA"] .reference-item,
2453
  html[data-mode="System Chart Extraction"] .reference-item,
 
2507
  color: var(--muted);
2508
  }
2509
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2510
  html[data-mode="QA"] #visualizationContainer,
2511
  html[data-mode="System Chart Extraction"] #visualizationContainer,
2512
  html[data-mode="Hypotheses Generation"] #visualizationContainer {
2513
+ background: var(--surface-strong) !important;
2514
+ border: 1px solid rgba(148, 163, 184, 0.2) !important;
2515
+ border-radius: 16px;
2516
+ padding: 14px 16px;
2517
+ box-shadow: none;
2518
+ min-width: 0;
2519
+ width: 100%;
2520
+ box-sizing: border-box;
2521
+ flex-shrink: 1;
2522
+ margin: 0;
2523
  }
2524
 
2525
  html[data-mode="QA"] #visualizationContainer label,
2526
  html[data-mode="System Chart Extraction"] #visualizationContainer label,
2527
  html[data-mode="Hypotheses Generation"] #visualizationContainer label {
2528
  color: var(--text) !important;
2529
+ white-space: normal;
2530
+ align-items: flex-start;
2531
  }
2532
 
2533
  /* h4 inside the hypothesis/conversation panes */
 
2731
  html[data-mode="Hypotheses Generation"] .step-number-default { display: none; }
2732
  html[data-mode="Hypotheses Generation"] .step-number-hypotheses { display: inline; }
2733
 
2734
+ /* Per-mode footer disclaimers (PlotExtract-style) */
2735
+ .mode-footer {
2736
+ display: none;
2737
+ margin-top: 2rem;
2738
+ text-align: center;
2739
+ color: var(--muted);
2740
+ font-size: 0.9rem;
2741
+ }
2742
+ html[data-mode="QA"] #qaFooter,
2743
+ html[data-mode="System Chart Extraction"] #systemChartFooter,
2744
+ html[data-mode="Hypotheses Generation"] #hypothesesFooter {
2745
+ display: block;
2746
+ }
2747
+
2748
  </style>
2749
  </head>
2750
  <body>
 
2770
  </button>
2771
  </div>
2772
  </div>
2773
+ <div class="app-glow" aria-hidden="true"></div>
2774
  <div id="app">
2775
+ <!-- Header — PlotExtract-style brand/hero -->
2776
+ <header class="app-header">
2777
+ <div class="brand">
2778
+ <span id="appTitle">Chatbot</span>
 
 
 
 
 
 
2779
  </div>
2780
+ <p class="subhead">Upload PDF or text files, select a model, and ask questions!</p>
2781
+ </header>
2782
+
2783
+ <!-- Setup panel: API key + upload -->
2784
+ <section class="panel" id="setupPanel">
2785
+ <div class="section-title">1) Set API Key</div>
2786
+ <form id="apiKeyForm">
2787
+ <div class="stack compact api-key-stack">
2788
+ <label class="label" id="apiKeyLabel" for="apiKeyInput">API Key</label>
2789
+ <div class="input-row">
2790
+ <input type="password" id="apiKeyInput" name="api_key_input" placeholder="sk-ant-..., sk-proj-..., or Gemini key" autocomplete="off">
2791
+ <button class="ghost" id="toggleKey" type="button">Show</button>
2792
+ </div>
2793
+ <p class="hint left">We never store your key. It is used only for Anthropic, OpenAI, or Google Gemini API requests.</p>
2794
 
2795
+ <label class="label" for="modelSelect">Model</label>
 
 
 
 
 
 
 
 
 
 
2796
  <select name="model" id="modelSelect" disabled>
2797
  <option value="">Enter API key to load models</option>
2798
  </select>
2799
  <span id="modelStatus" class="model-status"></span>
2800
+ </div>
2801
+ <div class="actions api-key-actions">
2802
+ <button type="submit" id="modelSubmitButton">Submit</button>
2803
+ </div>
2804
+ </form>
 
 
 
 
 
 
2805
 
2806
+ <hr id="uploadSectionDivider">
 
 
 
 
 
 
2807
 
2808
+ <div id="uploadSection">
2809
+ <div class="section-title">2) Upload Files</div>
2810
+ <p class="hint left">If the number of files is greater than 1000, please upload a zip file instead.</p>
2811
+ <form id="uploadForm" class="upload-panel-grid">
2812
+ <div class="stack compact">
2813
+ <label class="label" for="categoryInput">Database</label>
2814
+ <input type="text" name="category" id="categoryInput" value="my-database">
2815
+ </div>
2816
+ <div class="stack">
2817
+ <div class="dropzone tall" id="uploadDropzone">
2818
+ <input type="file" name="files" id="filesInput" multiple accept=".pdf,.txt,.text,.png,.jpg,.jpeg,.gif,.bmp,.tiff,.webp,.zip,application/zip">
2819
+ <div class="dropzone-content">
2820
+ <p>Drag & drop or click to upload files.</p>
2821
+ <span class="small">PDF, TXT, images, or ZIP. For &gt;1000 files, use a ZIP.</span>
2822
  </div>
2823
+ </div>
2824
+ </div>
2825
+ <div class="actions upload-actions">
2826
+ <button type="submit">Upload</button>
2827
+ </div>
2828
+ </form>
2829
+ </div>
2830
+ </section>
2831
+
2832
+ <!-- Workspace panel: ask + chat + continuations -->
2833
+ <section class="panel" id="workspacePanel">
2834
+ <div class="section-title"><span class="step-number-default">3)</span><span class="step-number-hypotheses">2)</span> Ask Questions</div>
2835
+ <form id="askForm">
2836
+ <div class="form-inputs">
2837
+ <div id="databaseContainer">
2838
+ <label for="databaseSelect">
2839
+ Databases:
2840
+ <select name="categories" id="databaseSelect" multiple>
2841
+ <option value="my-database">my-database</option>
2842
  </select>
2843
+ <small class="hint left">Hold Ctrl/Cmd to select multiple databases</small>
2844
  </label>
2845
+ </div>
2846
+ <input type="hidden" name="goal" id="goalInput" value="">
2847
 
2848
+ <div id="visualizationContainer" style="display: none;">
2849
+ <label style="display: flex; align-items: center; font-weight: 500; margin-bottom: 0;">
2850
+ <input type="checkbox" name="enable_visualization" id="enableVisualization">
2851
+ Enable visualization of extracted system charts
 
 
 
 
 
 
 
2852
  </label>
2853
+ </div>
2854
 
2855
+ <div id="hypothesesContainer" style="display: none;">
2856
+ <div class="result-card hypotheses-options">
2857
+ <label style="display: block; margin-bottom: 8px;">
2858
+ System Charts Files:
2859
+ <input type="file" id="systemChartsFile" accept=".txt" multiple style="margin-top: 4px; width: 100%;">
2860
+ <small class="hint left">Select multiple .txt files containing system charts, generated by the System Chart Extraction tool.</small>
2861
+ </label>
2862
+ <label style="display: block; margin-bottom: 8px;">
2863
+ Mechanism 1:
2864
+ <input type="text" id="materialGoal1" placeholder="e.g., property #1 for xxx material" style="margin-top: 4px; width: 100%; padding: 6px;">
2865
+ </label>
2866
+ <label style="display: block; margin-bottom: 8px;">
2867
+ Mechanism 2:
2868
+ <input type="text" id="materialGoal2" placeholder="e.g., property #2 for xxx material" style="margin-top: 4px; width: 100%; padding: 6px;">
2869
+ </label>
2870
+ <label style="display: block; margin-bottom: 0;">
2871
+ Number of Iterations:
2872
+ <input type="number" id="iterationsCount" min="1" max="100" value="1" placeholder="1" style="margin-top: 4px; width: 100%; padding: 6px;">
2873
+ <small class="hint left">Generate multiple hypothesis iterations (1-100)</small>
2874
+ </label>
2875
+ </div>
2876
+ </div>
2877
+
2878
+ <div id="questionContainer">
2879
+ <label id="questionLabel" for="questionInput">
2880
+ Question:
2881
  </label>
2882
+ <textarea id="questionInput" name="question" rows="3" placeholder="Type your question here..."></textarea>
2883
  </div>
2884
+ </div>
 
 
 
2885
 
2886
+ <button type="submit">Ask</button>
2887
+ </form>
2888
 
2889
+ <div id="chat-area"></div>
 
 
 
 
 
 
 
 
2890
 
2891
+ <div id="hypotheses-conversation-section" style="display: none;">
2892
+ <hr>
2893
+ <div class="section-title">Continue Hypothesis Analysis</div>
2894
+ <div class="result-card">
2895
+ <p class="hint left">
2896
+ Paste any generated hypothesis below to start a continuous conversation with the original papers and system charts.
2897
+ </p>
2898
 
2899
+ <div style="margin-bottom: 15px;">
2900
+ <label class="label" for="hypothesis-text-input">Hypothesis Text:</label>
2901
+ <textarea id="hypothesis-text-input" placeholder="Paste your hypothesis here..."></textarea>
2902
+ </div>
2903
 
2904
+ <div style="margin-bottom: 15px;">
2905
+ <label class="label" for="original-paper-content-input">Original Paper Content:</label>
2906
+ <textarea id="original-paper-content-input" placeholder="Paste the original paper content you want to reference here..."></textarea>
2907
+ <small class="hint left">
2908
+ Instead of automatically retrieving papers from the database, manually paste the content of the original papers you want to reference in your hypothesis analysis.
2909
+ </small>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2910
  </div>
2911
+
2912
+ <button id="start-hypothesis-conversation-btn" type="button">
2913
+ Start Hypothesis Conversation
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2914
  </button>
 
 
 
2915
 
2916
+ <div id="hypothesis-conversation-status" style="display: none; margin-top: 10px; padding: 10px; border-radius: 4px;">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2917
  </div>
2918
+ </div>
2919
+
2920
+ <div id="hypothesis-conversation-chat" style="display: none; margin-top: 15px;">
2921
+ <h4 class="panel-subtitle">Hypothesis Analysis Chat:</h4>
2922
+ <div id="hypothesis-chat-messages"></div>
2923
+ <div class="input-row">
2924
+ <input type="text" id="hypothesis-chat-input" placeholder="Ask about this hypothesis...">
2925
+ <button id="send-hypothesis-message-btn" type="button">
2926
+ Send
2927
  </button>
2928
  </div>
2929
+ </div>
2930
  </div>
2931
+
2932
+ <div id="conversation-section" style="display: none;">
2933
+ <hr>
2934
+ <div class="section-title">Continue System Chart Extraction Conversations</div>
2935
+ <div class="result-card">
2936
+ <p class="hint left">
2937
+ After system chart extraction, you can continue conversations with the AI about individual papers.
2938
+ </p>
2939
+ <form id="conversationForm">
2940
+ <div class="input-row conversation-load-row">
2941
+ <label style="flex: 1;">
2942
+ Select Paper Conversation:
2943
+ <select id="conversationSelect">
2944
+ <option value="">Select a paper to continue conversation...</option>
2945
+ </select>
2946
+ </label>
2947
+ <button type="button" id="loadConversationBtn">
2948
+ Load Conversation
2949
+ </button>
2950
+ </div>
2951
+ <div id="followUpContainer" style="display: none;">
2952
+ <label>
2953
+ Follow-up Question:
2954
+ <input type="text" id="followUpQuestion" placeholder="Ask a follow-up question about this paper...">
2955
+ </label>
2956
+ <button type="submit">
2957
+ Continue Conversation
2958
+ </button>
2959
+ </div>
2960
+ </form>
2961
+ </div>
2962
+
2963
+ <div id="conversation-history" style="display: none;">
2964
+ <h4 class="panel-subtitle">Conversation History:</h4>
2965
+ <div id="conversation-messages"></div>
2966
  </div>
2967
  </div>
2968
+ </section>
2969
 
2970
+ <section class="panel references-section" id="referencesSection">
2971
+ <div class="panel-head">
2972
+ <h2>References</h2>
2973
+ </div>
2974
 
2975
  <div class="reference-item" id="systemChartReference">
2976
  <span class="category-label">System Chart Extraction:</span> Liu, Q., Polak, M. P., Shuvo, M. A. A., Deodhar, H. S., Han, J., Morgan, D., & Oh, H. (2025, October). Automated extraction of materials system charts using a large language model framework. <em>Scripta Materialia</em>. Elsevier BV. http://doi.org/10.1016/j.scriptamat.2025.116815
 
2978
  <div class="reference-item" id="hypothesesReference">
2979
  <span class="category-label">Hypotheses Generation:</span> Liu, Q., Polak, M. P., Kim, S. Y., Shuvo, M. A. A., Deodhar, H. S., Han, J., … Oh, H. (2025, September). Beyond designer's knowledge: Generating materials design hypotheses via a large language model. <em>Acta Materialia</em>. Elsevier BV. http://doi.org/10.1016/j.actamat.2025.121307
2980
  </div>
2981
+ </section>
2982
+
2983
+ <footer class="mode-footer footer" id="qaFooter">
2984
+ <p>
2985
+ This tool lets you upload research papers and ask questions about them; answers are grounded
2986
+ in the content of your uploaded documents. It works best with clear, specific questions about
2987
+ machine-readable PDFs. Because answers are generated by LLMs, they can contain mistakes or
2988
+ omissions and are not guaranteed to be correct. Always verify important information against
2989
+ the original papers before using it for analysis, reporting, or decision-making.
2990
+ </p>
2991
+ </footer>
2992
+ <footer class="mode-footer footer" id="systemChartFooter">
2993
+ <p>
2994
+ This tool automatically extracts materials system charts, the relationships linking
2995
+ processing, structure, and properties, from uploaded research papers. Because extraction relies on LLMs, results can contain mistakes, omissions, or formatting issues and are not guaranteed to be correct. Always review and verify the extracted
2996
+ charts against the original papers before using them for analysis or reporting.
2997
+ </p>
2998
+ </footer>
2999
+ <footer class="mode-footer footer" id="hypothesesFooter">
3000
+ <p>
3001
+ This tool generates materials design hypotheses by synergistically combining materials system charts, helping you explore directions beyond your existing
3002
+ knowledge. Generated hypotheses are produced by LLMs and are not guaranteed to be
3003
+ scientifically valid or novel, so please take the
3004
+ results with caution. Always apply domain expertise and verify against the literature before acting
3005
+ on any hypothesis.
3006
+ </p>
3007
+ </footer>
3008
 
3009
  </div>
3010
 
 
3046
  if (appTitle) {
3047
  appTitle.textContent = window.selectedMode;
3048
  }
3049
+ const subhead = document.querySelector('#app .app-header .subhead');
3050
+ if (subhead) {
3051
+ const subheads = {
3052
+ 'QA': 'Upload research papers, select a model, and ask questions across your documents.',
3053
+ 'System Chart Extraction': 'Upload papers, select a model, and extract materials system charts linking processing, structure, and properties.',
3054
+ 'Hypotheses Generation': 'Provide system charts and mechanisms, select a model, and generate materials design hypotheses.'
3055
+ };
3056
+ subhead.textContent = subheads[window.selectedMode] || subhead.textContent;
 
 
3057
  }
3058
+ updateQuestionVisibility();
3059
  }
3060
 
3061
  function setupModeSelection() {
 
3083
 
3084
  document.addEventListener('DOMContentLoaded', setupModeSelection);
3085
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3086
  document.addEventListener('DOMContentLoaded', () => {
 
3087
  setupApiKeyAutoLoad();
3088
  });
3089
 
 
3135
  function setupApiKeyAutoLoad() {
3136
  const apiKeyInput = document.getElementById('apiKeyInput');
3137
  const modelSelect = document.getElementById('modelSelect');
3138
+ const toggleKeyBtn = document.getElementById('toggleKey');
3139
  if (!apiKeyInput || !modelSelect) return;
3140
 
 
 
 
 
3141
  apiKeyInput.addEventListener('input', () => {
3142
  const provider = detectProviderFromKey(apiKeyInput.value);
3143
  if (provider) {
 
3155
  apiKeyInput.addEventListener('blur', () => {
3156
  loadModelsForKey();
3157
  });
3158
+
3159
+ if (toggleKeyBtn) {
3160
+ toggleKeyBtn.addEventListener('click', () => {
3161
+ const isPassword = apiKeyInput.type === 'password';
3162
+ apiKeyInput.type = isPassword ? 'text' : 'password';
3163
+ toggleKeyBtn.textContent = isPassword ? 'Hide' : 'Show';
3164
+ });
3165
+ }
3166
  }
3167
 
3168
  async function loadModelsForKey({ silent = false } = {}) {
 
3215
  }
3216
  modelSelect.disabled = false;
3217
  modelSelect.value = data.default_model || models[0].id;
 
3218
 
3219
  _modelsLoadedForKey = key;
3220
  setProviderBadge(data.provider, data.provider_label);
main.py CHANGED
@@ -675,6 +675,10 @@ AVAILABLE_MODELS = OPENAI_MODELS + GEMINI_MODELS
675
  DEFAULT_MODEL = "gpt-5.2"
676
  DEFAULT_MODEL_QA = DEFAULT_MODEL
677
  DEFAULT_MODEL_SYSTEM_CHART_EXTRACTION = DEFAULT_MODEL
 
 
 
 
678
 
679
  # -------------- Global placeholders (ensure they're declared) -------------- #
680
  api_key = None
@@ -854,83 +858,6 @@ def choose_default_model(models: List[Dict[str, str]], provider: str) -> str:
854
  return max(pros, key=priority)["id"]
855
  return max(models, key=priority)["id"]
856
 
857
- MODEL_ADVANCED_PARAMETERS: Dict[str, Dict[str, Any]] = {
858
- "gpt-5-nano": {
859
- "reasoning_effort": {"choices": ["minimal", "low", "medium", "high"]},
860
- "verbosity": {"choices": ["low", "medium", "high"]},
861
- },
862
- "gpt-5-mini": {
863
- "reasoning_effort": {"choices": ["minimal", "low", "medium", "high"]},
864
- "verbosity": {"choices": ["low", "medium", "high"]},
865
- },
866
- "gpt-5.2": {
867
- "reasoning_effort": {"choices": ["none", "low", "medium", "high", "xhigh"]},
868
- "verbosity": {"choices": ["low", "medium", "high"]},
869
- },
870
- "gemini-2.5-flash-lite": {
871
- "temperature": {"min": 0.0, "max": 2.0},
872
- "top_p": {"min": 0.0, "max": 1.0},
873
- },
874
- "gemini-3-flash-preview": {
875
- "temperature": {"min": 0.0, "max": 2.0},
876
- "top_p": {"min": 0.0, "max": 1.0},
877
- "thinking_level": {"choices": ["minimal", "low", "medium", "high"]},
878
- },
879
- "gemini-3-pro-preview": {
880
- "temperature": {"min": 0.0, "max": 2.0},
881
- "top_p": {"min": 0.0, "max": 1.0},
882
- "thinking_level": {"choices": ["low", "high"]},
883
- },
884
- }
885
-
886
-
887
- def get_model_advanced_parameter_rules(model: str) -> Dict[str, Any]:
888
- if model in MODEL_ADVANCED_PARAMETERS:
889
- return MODEL_ADVANCED_PARAMETERS[model]
890
- # Pattern fallback so models fetched live from a provider's API still
891
- # surface a sensible advanced-params UI.
892
- m = (model or "").lower()
893
- if "gpt-5" in m:
894
- return {
895
- "reasoning_effort": {"choices": ["minimal", "low", "medium", "high"]},
896
- "verbosity": {"choices": ["low", "medium", "high"]},
897
- }
898
- if "gpt" in m:
899
- return {
900
- "temperature": {"min": 0.0, "max": 2.0},
901
- "top_p": {"min": 0.0, "max": 1.0},
902
- }
903
- if "gemini" in m and "pro" in m:
904
- return {
905
- "temperature": {"min": 0.0, "max": 2.0},
906
- "top_p": {"min": 0.0, "max": 1.0},
907
- "thinking_level": {"choices": ["low", "high"]},
908
- }
909
- if "gemini" in m:
910
- return {
911
- "temperature": {"min": 0.0, "max": 2.0},
912
- "top_p": {"min": 0.0, "max": 1.0},
913
- }
914
- if "claude" in m:
915
- return {
916
- "temperature": {"min": 0.0, "max": 1.0},
917
- "top_p": {"min": 0.0, "max": 1.0},
918
- }
919
- return {}
920
-
921
-
922
- def parse_optional_float(value: Optional[str], field_name: str) -> Tuple[Optional[float], Optional[str]]:
923
- if value is None:
924
- return None, None
925
- value_str = value.strip()
926
- if not value_str:
927
- return None, None
928
- try:
929
- return float(value_str), None
930
- except ValueError:
931
- return None, f"Invalid {field_name}: '{value}'."
932
-
933
-
934
  class AsyncEmbeddingAdapter:
935
  def __init__(self, embedder):
936
  self._embedder = embedder
@@ -977,11 +904,6 @@ def llm_content_to_text(content: Any) -> str:
977
  async def set_api_key(
978
  api_key_input: Optional[str] = Form(None),
979
  model: str = Form(DEFAULT_MODEL),
980
- temperature: Optional[str] = Form(None),
981
- top_p: Optional[str] = Form(None),
982
- reasoning_effort: Optional[str] = Form(None),
983
- verbosity: Optional[str] = Form(None),
984
- thinking_level: Optional[str] = Form(None),
985
  ):
986
  """
987
  Sets up global LLM and embeddings for proprietary models.
@@ -1008,7 +930,6 @@ async def set_api_key(
1008
  content={"message": "Please select a model."},
1009
  status_code=400
1010
  )
1011
- model_advanced_rules = get_model_advanced_parameter_rules(model)
1012
  DEFAULT_MODEL_QA = model
1013
  DEFAULT_MODEL_SYSTEM_CHART_EXTRACTION = model
1014
 
@@ -1019,120 +940,21 @@ async def set_api_key(
1019
  status_code=400
1020
  )
1021
 
1022
- llm_config: Dict[str, Any] = {}
1023
- allowed_advanced_params = set(model_advanced_rules.keys())
1024
-
1025
- parsed_temperature, temp_error = parse_optional_float(temperature, "temperature")
1026
- if temp_error:
1027
- return JSONResponse(content={"message": temp_error}, status_code=400)
1028
- if parsed_temperature is not None:
1029
- temperature_rules = model_advanced_rules.get("temperature", {})
1030
- min_temp = float(temperature_rules.get("min", 0.0))
1031
- max_temp = float(temperature_rules.get("max", 2.0))
1032
- if not min_temp <= parsed_temperature <= max_temp:
1033
- return JSONResponse(
1034
- content={"message": f"temperature must be between {min_temp} and {max_temp}."},
1035
- status_code=400
1036
- )
1037
- if "temperature" not in allowed_advanced_params:
1038
- return JSONResponse(
1039
- content={"message": f"Model '{model}' does not support temperature."},
1040
- status_code=400
1041
- )
1042
- llm_config["temperature"] = parsed_temperature
1043
-
1044
- parsed_top_p, top_p_error = parse_optional_float(top_p, "top_p")
1045
- if top_p_error:
1046
- return JSONResponse(content={"message": top_p_error}, status_code=400)
1047
- if parsed_top_p is not None:
1048
- top_p_rules = model_advanced_rules.get("top_p", {})
1049
- min_top_p = float(top_p_rules.get("min", 0.0))
1050
- max_top_p = float(top_p_rules.get("max", 1.0))
1051
- if not min_top_p <= parsed_top_p <= max_top_p:
1052
- return JSONResponse(
1053
- content={"message": f"top_p must be between {min_top_p} and {max_top_p}."},
1054
- status_code=400
1055
- )
1056
- if "top_p" not in allowed_advanced_params:
1057
- return JSONResponse(
1058
- content={"message": f"Model '{model}' does not support top_p."},
1059
- status_code=400
1060
- )
1061
- llm_config["top_p"] = parsed_top_p
1062
-
1063
- if reasoning_effort:
1064
- normalized_reasoning_effort = reasoning_effort.strip().lower()
1065
- if normalized_reasoning_effort:
1066
- allowed_reasoning_efforts = model_advanced_rules.get("reasoning_effort", {}).get("choices", [])
1067
- if normalized_reasoning_effort not in allowed_reasoning_efforts:
1068
- return JSONResponse(
1069
- content={"message": f"Invalid reasoning_effort: '{reasoning_effort}'."},
1070
- status_code=400
1071
- )
1072
- if "reasoning_effort" not in allowed_advanced_params:
1073
- return JSONResponse(
1074
- content={"message": f"Model '{model}' does not support reasoning_effort."},
1075
- status_code=400
1076
- )
1077
- llm_config["reasoning_effort"] = normalized_reasoning_effort
1078
-
1079
- if verbosity:
1080
- normalized_verbosity = verbosity.strip().lower()
1081
- if normalized_verbosity:
1082
- allowed_verbosity = model_advanced_rules.get("verbosity", {}).get("choices", [])
1083
- if normalized_verbosity not in allowed_verbosity:
1084
- return JSONResponse(
1085
- content={"message": f"Invalid verbosity: '{verbosity}'."},
1086
- status_code=400
1087
- )
1088
- if "verbosity" not in allowed_advanced_params:
1089
- return JSONResponse(
1090
- content={"message": f"Model '{model}' does not support verbosity."},
1091
- status_code=400
1092
- )
1093
- llm_config["verbosity"] = normalized_verbosity
1094
-
1095
- if thinking_level:
1096
- normalized_thinking_level = thinking_level.strip().lower()
1097
- if normalized_thinking_level:
1098
- allowed_thinking_levels = model_advanced_rules.get("thinking_level", {}).get("choices", [])
1099
- if normalized_thinking_level not in allowed_thinking_levels:
1100
- return JSONResponse(
1101
- content={"message": f"Invalid thinking_level: '{thinking_level}'."},
1102
- status_code=400
1103
- )
1104
- if "thinking_level" not in allowed_advanced_params:
1105
- return JSONResponse(
1106
- content={"message": f"Model '{model}' does not support thinking_level."},
1107
- status_code=400
1108
- )
1109
- llm_config["thinking_level"] = normalized_thinking_level
1110
-
1111
  if model_provider == "openai":
1112
  embedding_model = OpenAIEmbeddings(openai_api_key=api_key)
1113
  embedding_model = ensure_async_embeddings(embedding_model)
1114
- openai_model_kwargs: Dict[str, Any] = {}
1115
- if "reasoning_effort" in llm_config:
1116
- openai_model_kwargs["reasoning"] = {
1117
- "effort": llm_config["reasoning_effort"]
1118
- }
1119
- if "verbosity" in llm_config:
1120
- openai_model_kwargs["text"] = {
1121
- "verbosity": llm_config["verbosity"]
1122
- }
1123
  openai_common_kwargs: Dict[str, Any] = {
1124
  "openai_api_key": api_key,
1125
  "model": DEFAULT_MODEL_QA,
1126
- "use_responses_api": True
 
1127
  }
1128
  openai_chart_kwargs: Dict[str, Any] = {
1129
  "openai_api_key": api_key,
1130
  "model": DEFAULT_MODEL_SYSTEM_CHART_EXTRACTION,
1131
- "use_responses_api": True
 
1132
  }
1133
- if openai_model_kwargs:
1134
- openai_common_kwargs["model_kwargs"] = openai_model_kwargs
1135
- openai_chart_kwargs["model_kwargs"] = openai_model_kwargs
1136
  llm_QA = ChatOpenAI(
1137
  **openai_common_kwargs
1138
  )
@@ -1155,25 +977,14 @@ async def set_api_key(
1155
  embedding_model = ensure_async_embeddings(embedding_model)
1156
  gemini_common_kwargs: Dict[str, Any] = {
1157
  "google_api_key": api_key,
1158
- "model": DEFAULT_MODEL_QA
 
1159
  }
1160
  gemini_chart_kwargs: Dict[str, Any] = {
1161
  "google_api_key": api_key,
1162
- "model": DEFAULT_MODEL_SYSTEM_CHART_EXTRACTION
 
1163
  }
1164
- if "temperature" in llm_config:
1165
- gemini_common_kwargs["temperature"] = llm_config["temperature"]
1166
- gemini_chart_kwargs["temperature"] = llm_config["temperature"]
1167
- if "top_p" in llm_config:
1168
- gemini_common_kwargs["top_p"] = llm_config["top_p"]
1169
- gemini_chart_kwargs["top_p"] = llm_config["top_p"]
1170
-
1171
- gemini_model_kwargs: Dict[str, Any] = {}
1172
- if "thinking_level" in llm_config:
1173
- gemini_model_kwargs["thinking_level"] = llm_config["thinking_level"]
1174
- if gemini_model_kwargs:
1175
- gemini_common_kwargs["model_kwargs"] = gemini_model_kwargs
1176
- gemini_chart_kwargs["model_kwargs"] = gemini_model_kwargs
1177
  llm_QA = ChatGoogleGenerativeAI(
1178
  **gemini_common_kwargs
1179
  )
@@ -1192,17 +1003,13 @@ async def set_api_key(
1192
  anthropic_common_kwargs: Dict[str, Any] = {
1193
  "anthropic_api_key": api_key,
1194
  "model": DEFAULT_MODEL_QA,
 
1195
  }
1196
  anthropic_chart_kwargs: Dict[str, Any] = {
1197
  "anthropic_api_key": api_key,
1198
  "model": DEFAULT_MODEL_SYSTEM_CHART_EXTRACTION,
 
1199
  }
1200
- if "temperature" in llm_config:
1201
- anthropic_common_kwargs["temperature"] = llm_config["temperature"]
1202
- anthropic_chart_kwargs["temperature"] = llm_config["temperature"]
1203
- if "top_p" in llm_config:
1204
- anthropic_common_kwargs["top_p"] = llm_config["top_p"]
1205
- anthropic_chart_kwargs["top_p"] = llm_config["top_p"]
1206
  llm_QA = ChatAnthropic(**anthropic_common_kwargs)
1207
  llm_system_chart_extraction = ChatAnthropic(**anthropic_chart_kwargs)
1208
  provider_label = "Anthropic"
 
675
  DEFAULT_MODEL = "gpt-5.2"
676
  DEFAULT_MODEL_QA = DEFAULT_MODEL
677
  DEFAULT_MODEL_SYSTEM_CHART_EXTRACTION = DEFAULT_MODEL
678
+ # Shared output-length cap for QA, system chart extraction, and hypotheses.
679
+ # LangChain's max_tokens maps to provider-specific names:
680
+ # Anthropic max_tokens, OpenAI max_output_tokens / max_completion_tokens, Gemini maxOutputTokens.
681
+ DEFAULT_MAX_OUTPUT_TOKENS = 8192
682
 
683
  # -------------- Global placeholders (ensure they're declared) -------------- #
684
  api_key = None
 
858
  return max(pros, key=priority)["id"]
859
  return max(models, key=priority)["id"]
860
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
861
  class AsyncEmbeddingAdapter:
862
  def __init__(self, embedder):
863
  self._embedder = embedder
 
904
  async def set_api_key(
905
  api_key_input: Optional[str] = Form(None),
906
  model: str = Form(DEFAULT_MODEL),
 
 
 
 
 
907
  ):
908
  """
909
  Sets up global LLM and embeddings for proprietary models.
 
930
  content={"message": "Please select a model."},
931
  status_code=400
932
  )
 
933
  DEFAULT_MODEL_QA = model
934
  DEFAULT_MODEL_SYSTEM_CHART_EXTRACTION = model
935
 
 
940
  status_code=400
941
  )
942
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
943
  if model_provider == "openai":
944
  embedding_model = OpenAIEmbeddings(openai_api_key=api_key)
945
  embedding_model = ensure_async_embeddings(embedding_model)
 
 
 
 
 
 
 
 
 
946
  openai_common_kwargs: Dict[str, Any] = {
947
  "openai_api_key": api_key,
948
  "model": DEFAULT_MODEL_QA,
949
+ "use_responses_api": True,
950
+ "max_tokens": DEFAULT_MAX_OUTPUT_TOKENS,
951
  }
952
  openai_chart_kwargs: Dict[str, Any] = {
953
  "openai_api_key": api_key,
954
  "model": DEFAULT_MODEL_SYSTEM_CHART_EXTRACTION,
955
+ "use_responses_api": True,
956
+ "max_tokens": DEFAULT_MAX_OUTPUT_TOKENS,
957
  }
 
 
 
958
  llm_QA = ChatOpenAI(
959
  **openai_common_kwargs
960
  )
 
977
  embedding_model = ensure_async_embeddings(embedding_model)
978
  gemini_common_kwargs: Dict[str, Any] = {
979
  "google_api_key": api_key,
980
+ "model": DEFAULT_MODEL_QA,
981
+ "max_tokens": DEFAULT_MAX_OUTPUT_TOKENS,
982
  }
983
  gemini_chart_kwargs: Dict[str, Any] = {
984
  "google_api_key": api_key,
985
+ "model": DEFAULT_MODEL_SYSTEM_CHART_EXTRACTION,
986
+ "max_tokens": DEFAULT_MAX_OUTPUT_TOKENS,
987
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
988
  llm_QA = ChatGoogleGenerativeAI(
989
  **gemini_common_kwargs
990
  )
 
1003
  anthropic_common_kwargs: Dict[str, Any] = {
1004
  "anthropic_api_key": api_key,
1005
  "model": DEFAULT_MODEL_QA,
1006
+ "max_tokens": DEFAULT_MAX_OUTPUT_TOKENS,
1007
  }
1008
  anthropic_chart_kwargs: Dict[str, Any] = {
1009
  "anthropic_api_key": api_key,
1010
  "model": DEFAULT_MODEL_SYSTEM_CHART_EXTRACTION,
1011
+ "max_tokens": DEFAULT_MAX_OUTPUT_TOKENS,
1012
  }
 
 
 
 
 
 
1013
  llm_QA = ChatAnthropic(**anthropic_common_kwargs)
1014
  llm_system_chart_extraction = ChatAnthropic(**anthropic_chart_kwargs)
1015
  provider_label = "Anthropic"