voyageai-cli 1.22.0 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -2
- package/src/cli.js +4 -0
- package/src/commands/chat.js +503 -0
- package/src/commands/demo.js +75 -0
- package/src/commands/embed.js +10 -0
- package/src/commands/index.js +1 -1
- package/src/commands/init.js +34 -97
- package/src/commands/mcp-server.js +49 -0
- package/src/commands/ping.js +52 -0
- package/src/commands/pipeline.js +17 -3
- package/src/commands/playground.js +186 -0
- package/src/commands/purge.js +3 -1
- package/src/commands/refresh.js +3 -1
- package/src/commands/rerank.js +10 -0
- package/src/commands/scaffold.js +1 -2
- package/src/lib/chat.js +252 -0
- package/src/lib/codegen.js +5 -4
- package/src/lib/config.js +5 -1
- package/src/lib/cost.js +352 -0
- package/src/lib/explanations.js +260 -0
- package/src/lib/history.js +260 -0
- package/src/lib/llm.js +485 -0
- package/src/lib/preflight.js +281 -0
- package/src/lib/prompt.js +111 -0
- package/src/lib/wizard-cli.js +135 -0
- package/src/lib/wizard-steps-chat.js +171 -0
- package/src/lib/wizard-steps-init.js +174 -0
- package/src/lib/wizard.js +222 -0
- package/src/mcp/schemas/index.js +102 -0
- package/src/mcp/server.js +162 -0
- package/src/mcp/tools/embedding.js +67 -0
- package/src/mcp/tools/ingest.js +89 -0
- package/src/mcp/tools/management.js +132 -0
- package/src/mcp/tools/retrieval.js +209 -0
- package/src/mcp/tools/utility.js +219 -0
- package/src/playground/index.html +1195 -199
|
@@ -2058,6 +2058,330 @@ select:focus { outline: none; border-color: var(--accent); }
|
|
|
2058
2058
|
.settings-api-key-missing { color: var(--warning); }
|
|
2059
2059
|
.settings-row .settings-select, .settings-row .settings-input { text-align: right; }
|
|
2060
2060
|
|
|
2061
|
+
/* ── Settings two-column layout (Claude Desktop style) ── */
|
|
2062
|
+
.settings-layout {
|
|
2063
|
+
display: flex;
|
|
2064
|
+
height: 100%;
|
|
2065
|
+
max-width: 960px;
|
|
2066
|
+
margin: 0 auto;
|
|
2067
|
+
gap: 0;
|
|
2068
|
+
}
|
|
2069
|
+
.settings-nav {
|
|
2070
|
+
width: 200px;
|
|
2071
|
+
min-width: 200px;
|
|
2072
|
+
flex-shrink: 0;
|
|
2073
|
+
padding: 24px 0;
|
|
2074
|
+
border-right: 1px solid var(--border);
|
|
2075
|
+
display: flex;
|
|
2076
|
+
flex-direction: column;
|
|
2077
|
+
gap: 2px;
|
|
2078
|
+
position: sticky;
|
|
2079
|
+
top: 0;
|
|
2080
|
+
align-self: flex-start;
|
|
2081
|
+
}
|
|
2082
|
+
.settings-nav-header {
|
|
2083
|
+
font-size: 18px;
|
|
2084
|
+
font-weight: 700;
|
|
2085
|
+
color: var(--text);
|
|
2086
|
+
padding: 0 16px 20px;
|
|
2087
|
+
letter-spacing: -0.3px;
|
|
2088
|
+
}
|
|
2089
|
+
.settings-nav-item {
|
|
2090
|
+
display: flex;
|
|
2091
|
+
align-items: center;
|
|
2092
|
+
gap: 10px;
|
|
2093
|
+
width: 100%;
|
|
2094
|
+
padding: 10px 16px;
|
|
2095
|
+
background: none;
|
|
2096
|
+
border: none;
|
|
2097
|
+
border-left: 3px solid transparent;
|
|
2098
|
+
border-radius: 0 6px 6px 0;
|
|
2099
|
+
color: var(--text-dim);
|
|
2100
|
+
font-size: 13px;
|
|
2101
|
+
font-weight: 500;
|
|
2102
|
+
font-family: var(--font);
|
|
2103
|
+
cursor: pointer;
|
|
2104
|
+
transition: all 0.15s;
|
|
2105
|
+
text-align: left;
|
|
2106
|
+
}
|
|
2107
|
+
.settings-nav-item:hover {
|
|
2108
|
+
background: rgba(255,255,255,0.04);
|
|
2109
|
+
color: var(--text);
|
|
2110
|
+
}
|
|
2111
|
+
[data-theme="light"] .settings-nav-item:hover {
|
|
2112
|
+
background: rgba(0,30,43,0.04);
|
|
2113
|
+
}
|
|
2114
|
+
.settings-nav-item.active {
|
|
2115
|
+
color: var(--accent);
|
|
2116
|
+
border-left-color: var(--accent);
|
|
2117
|
+
background: var(--accent-glow);
|
|
2118
|
+
}
|
|
2119
|
+
.settings-nav-item svg {
|
|
2120
|
+
width: 16px;
|
|
2121
|
+
height: 16px;
|
|
2122
|
+
flex-shrink: 0;
|
|
2123
|
+
color: inherit;
|
|
2124
|
+
}
|
|
2125
|
+
.settings-content {
|
|
2126
|
+
flex: 1;
|
|
2127
|
+
padding: 24px 32px;
|
|
2128
|
+
overflow-y: auto;
|
|
2129
|
+
max-width: 640px;
|
|
2130
|
+
}
|
|
2131
|
+
.settings-panel {
|
|
2132
|
+
display: none;
|
|
2133
|
+
}
|
|
2134
|
+
.settings-panel.active {
|
|
2135
|
+
display: block;
|
|
2136
|
+
}
|
|
2137
|
+
.settings-panel-header {
|
|
2138
|
+
margin-bottom: 24px;
|
|
2139
|
+
padding-bottom: 16px;
|
|
2140
|
+
border-bottom: 1px solid var(--border);
|
|
2141
|
+
}
|
|
2142
|
+
.settings-panel-title {
|
|
2143
|
+
font-size: 22px;
|
|
2144
|
+
font-weight: 700;
|
|
2145
|
+
color: var(--text);
|
|
2146
|
+
margin: 0 0 4px;
|
|
2147
|
+
letter-spacing: -0.3px;
|
|
2148
|
+
}
|
|
2149
|
+
.settings-panel-subtitle {
|
|
2150
|
+
font-size: 14px;
|
|
2151
|
+
color: var(--text-dim);
|
|
2152
|
+
margin: 0;
|
|
2153
|
+
font-weight: 500;
|
|
2154
|
+
}
|
|
2155
|
+
/* Settings origin/provenance badge */
|
|
2156
|
+
.settings-origin {
|
|
2157
|
+
display: inline-flex;
|
|
2158
|
+
align-items: center;
|
|
2159
|
+
font-size: 10px;
|
|
2160
|
+
font-weight: 600;
|
|
2161
|
+
text-transform: uppercase;
|
|
2162
|
+
letter-spacing: 0.5px;
|
|
2163
|
+
padding: 2px 7px;
|
|
2164
|
+
border-radius: 4px;
|
|
2165
|
+
margin-left: 6px;
|
|
2166
|
+
white-space: nowrap;
|
|
2167
|
+
vertical-align: middle;
|
|
2168
|
+
line-height: 1;
|
|
2169
|
+
}
|
|
2170
|
+
.settings-origin:empty { display: none; }
|
|
2171
|
+
.settings-origin.origin-env {
|
|
2172
|
+
background: rgba(4,152,236,0.12);
|
|
2173
|
+
color: #0497EC;
|
|
2174
|
+
}
|
|
2175
|
+
[data-theme="light"] .settings-origin.origin-env {
|
|
2176
|
+
background: rgba(4,120,190,0.10);
|
|
2177
|
+
color: #016BF8;
|
|
2178
|
+
}
|
|
2179
|
+
.settings-origin.origin-config {
|
|
2180
|
+
background: rgba(180,90,242,0.12);
|
|
2181
|
+
color: #B45AF2;
|
|
2182
|
+
}
|
|
2183
|
+
[data-theme="light"] .settings-origin.origin-config {
|
|
2184
|
+
background: rgba(140,60,200,0.10);
|
|
2185
|
+
color: #7B3DB5;
|
|
2186
|
+
}
|
|
2187
|
+
.settings-origin.origin-project {
|
|
2188
|
+
background: rgba(0,237,100,0.12);
|
|
2189
|
+
color: var(--accent);
|
|
2190
|
+
}
|
|
2191
|
+
[data-theme="light"] .settings-origin.origin-project {
|
|
2192
|
+
background: rgba(0,163,92,0.10);
|
|
2193
|
+
color: #00A35C;
|
|
2194
|
+
}
|
|
2195
|
+
.settings-origin.origin-default {
|
|
2196
|
+
background: rgba(136,147,151,0.12);
|
|
2197
|
+
color: var(--text-muted);
|
|
2198
|
+
}
|
|
2199
|
+
/* Chat not-configured state */
|
|
2200
|
+
.chat-not-configured {
|
|
2201
|
+
display: flex;
|
|
2202
|
+
flex-direction: column;
|
|
2203
|
+
align-items: center;
|
|
2204
|
+
justify-content: center;
|
|
2205
|
+
gap: 12px;
|
|
2206
|
+
padding: 48px 24px;
|
|
2207
|
+
text-align: center;
|
|
2208
|
+
color: var(--text-dim);
|
|
2209
|
+
font-size: 14px;
|
|
2210
|
+
}
|
|
2211
|
+
.chat-not-configured-icon {
|
|
2212
|
+
font-size: 40px;
|
|
2213
|
+
opacity: 0.5;
|
|
2214
|
+
}
|
|
2215
|
+
.chat-not-configured .btn {
|
|
2216
|
+
margin-top: 8px;
|
|
2217
|
+
background: var(--accent);
|
|
2218
|
+
color: var(--bg);
|
|
2219
|
+
border: none;
|
|
2220
|
+
padding: 8px 20px;
|
|
2221
|
+
border-radius: var(--radius);
|
|
2222
|
+
font-weight: 600;
|
|
2223
|
+
font-size: 13px;
|
|
2224
|
+
cursor: pointer;
|
|
2225
|
+
font-family: var(--font);
|
|
2226
|
+
}
|
|
2227
|
+
.chat-not-configured .btn:hover {
|
|
2228
|
+
filter: brightness(1.1);
|
|
2229
|
+
}
|
|
2230
|
+
/* Responsive: collapse settings nav on small screens */
|
|
2231
|
+
@media (max-width: 768px) {
|
|
2232
|
+
.settings-layout {
|
|
2233
|
+
flex-direction: column;
|
|
2234
|
+
}
|
|
2235
|
+
.settings-nav {
|
|
2236
|
+
width: 100%;
|
|
2237
|
+
min-width: 100%;
|
|
2238
|
+
flex-direction: row;
|
|
2239
|
+
overflow-x: auto;
|
|
2240
|
+
border-right: none;
|
|
2241
|
+
border-bottom: 1px solid var(--border);
|
|
2242
|
+
padding: 12px 8px;
|
|
2243
|
+
gap: 4px;
|
|
2244
|
+
position: static;
|
|
2245
|
+
}
|
|
2246
|
+
.settings-nav-header { display: none; }
|
|
2247
|
+
.settings-nav-item {
|
|
2248
|
+
border-left: none;
|
|
2249
|
+
border-bottom: 2px solid transparent;
|
|
2250
|
+
border-radius: 6px;
|
|
2251
|
+
padding: 8px 12px;
|
|
2252
|
+
white-space: nowrap;
|
|
2253
|
+
font-size: 12px;
|
|
2254
|
+
}
|
|
2255
|
+
.settings-nav-item.active {
|
|
2256
|
+
border-left-color: transparent;
|
|
2257
|
+
border-bottom-color: var(--accent);
|
|
2258
|
+
}
|
|
2259
|
+
.settings-content { padding: 16px; }
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
/* ── Chat Tab ── */
|
|
2263
|
+
.chat-container {
|
|
2264
|
+
display: flex;
|
|
2265
|
+
flex-direction: column;
|
|
2266
|
+
height: calc(100vh - 140px);
|
|
2267
|
+
max-width: 900px;
|
|
2268
|
+
margin: 0 auto;
|
|
2269
|
+
}
|
|
2270
|
+
.chat-messages {
|
|
2271
|
+
flex: 1;
|
|
2272
|
+
overflow-y: auto;
|
|
2273
|
+
padding: 16px;
|
|
2274
|
+
display: flex;
|
|
2275
|
+
flex-direction: column;
|
|
2276
|
+
gap: 16px;
|
|
2277
|
+
}
|
|
2278
|
+
.chat-message {
|
|
2279
|
+
max-width: 85%;
|
|
2280
|
+
padding: 12px 16px;
|
|
2281
|
+
border-radius: 12px;
|
|
2282
|
+
line-height: 1.6;
|
|
2283
|
+
font-size: 14px;
|
|
2284
|
+
white-space: pre-wrap;
|
|
2285
|
+
word-break: break-word;
|
|
2286
|
+
}
|
|
2287
|
+
.chat-message.user {
|
|
2288
|
+
align-self: flex-end;
|
|
2289
|
+
background: var(--accent);
|
|
2290
|
+
color: #fff;
|
|
2291
|
+
border-bottom-right-radius: 4px;
|
|
2292
|
+
}
|
|
2293
|
+
.chat-message.assistant {
|
|
2294
|
+
align-self: flex-start;
|
|
2295
|
+
background: var(--bg-card);
|
|
2296
|
+
border: 1px solid var(--border);
|
|
2297
|
+
border-bottom-left-radius: 4px;
|
|
2298
|
+
}
|
|
2299
|
+
.chat-message.system-msg {
|
|
2300
|
+
align-self: center;
|
|
2301
|
+
background: none;
|
|
2302
|
+
color: var(--text-muted);
|
|
2303
|
+
font-size: 12px;
|
|
2304
|
+
padding: 4px 8px;
|
|
2305
|
+
}
|
|
2306
|
+
.chat-sources {
|
|
2307
|
+
margin-top: 8px;
|
|
2308
|
+
padding-top: 8px;
|
|
2309
|
+
border-top: 1px solid var(--border);
|
|
2310
|
+
font-size: 12px;
|
|
2311
|
+
color: var(--text-muted);
|
|
2312
|
+
}
|
|
2313
|
+
.chat-sources summary { cursor: pointer; }
|
|
2314
|
+
.chat-sources ul { margin: 4px 0 0 16px; padding: 0; }
|
|
2315
|
+
.chat-sources li { margin: 2px 0; }
|
|
2316
|
+
.chat-input-area {
|
|
2317
|
+
padding: 12px 16px;
|
|
2318
|
+
border-top: 1px solid var(--border);
|
|
2319
|
+
display: flex;
|
|
2320
|
+
gap: 8px;
|
|
2321
|
+
align-items: flex-end;
|
|
2322
|
+
}
|
|
2323
|
+
.chat-input {
|
|
2324
|
+
flex: 1;
|
|
2325
|
+
padding: 10px 14px;
|
|
2326
|
+
border: 1px solid var(--border);
|
|
2327
|
+
border-radius: 8px;
|
|
2328
|
+
background: var(--bg-input);
|
|
2329
|
+
color: var(--text);
|
|
2330
|
+
font-size: 14px;
|
|
2331
|
+
font-family: inherit;
|
|
2332
|
+
resize: none;
|
|
2333
|
+
min-height: 40px;
|
|
2334
|
+
max-height: 120px;
|
|
2335
|
+
outline: none;
|
|
2336
|
+
}
|
|
2337
|
+
.chat-input:focus { border-color: var(--accent); }
|
|
2338
|
+
.chat-send-btn {
|
|
2339
|
+
padding: 10px 20px;
|
|
2340
|
+
background: var(--accent);
|
|
2341
|
+
color: #fff;
|
|
2342
|
+
border: none;
|
|
2343
|
+
border-radius: 8px;
|
|
2344
|
+
cursor: pointer;
|
|
2345
|
+
font-size: 14px;
|
|
2346
|
+
font-weight: 600;
|
|
2347
|
+
}
|
|
2348
|
+
.chat-send-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
2349
|
+
.chat-header {
|
|
2350
|
+
display: flex;
|
|
2351
|
+
align-items: center;
|
|
2352
|
+
justify-content: space-between;
|
|
2353
|
+
padding: 8px 16px;
|
|
2354
|
+
border-bottom: 1px solid var(--border);
|
|
2355
|
+
font-size: 13px;
|
|
2356
|
+
color: var(--text-muted);
|
|
2357
|
+
}
|
|
2358
|
+
.chat-header-status { display: flex; gap: 8px; align-items: center; }
|
|
2359
|
+
.chat-header-status span { opacity: 0.7; }
|
|
2360
|
+
.chat-config-toggle {
|
|
2361
|
+
background: none; border: none; color: var(--text-muted);
|
|
2362
|
+
cursor: pointer; padding: 4px 8px; border-radius: 4px; font-size: 14px;
|
|
2363
|
+
display: flex; align-items: center; gap: 4px;
|
|
2364
|
+
}
|
|
2365
|
+
.chat-config-toggle:hover { background: rgba(255,255,255,0.06); color: var(--text); }
|
|
2366
|
+
[data-theme="light"] .chat-config-toggle:hover { background: rgba(0,0,0,0.04); }
|
|
2367
|
+
.chat-config-toggle.active { color: var(--accent); }
|
|
2368
|
+
/* chat-config-panel/bar removed — chat settings moved to Settings page */
|
|
2369
|
+
.chat-typing {
|
|
2370
|
+
align-self: flex-start;
|
|
2371
|
+
color: var(--text-muted);
|
|
2372
|
+
font-size: 13px;
|
|
2373
|
+
padding: 8px 16px;
|
|
2374
|
+
}
|
|
2375
|
+
.chat-typing::after {
|
|
2376
|
+
content: '';
|
|
2377
|
+
animation: chatDots 1.4s infinite;
|
|
2378
|
+
}
|
|
2379
|
+
@keyframes chatDots {
|
|
2380
|
+
0%, 20% { content: '.'; }
|
|
2381
|
+
40% { content: '..'; }
|
|
2382
|
+
60%, 100% { content: '...'; }
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2061
2385
|
/* ── Multimodal Tab ── */
|
|
2062
2386
|
.mm-grid {
|
|
2063
2387
|
display: grid;
|
|
@@ -2296,7 +2620,7 @@ select:focus { outline: none; border-color: var(--accent); }
|
|
|
2296
2620
|
.mm-gallery-grid { grid-template-columns: repeat(3, 1fr); }
|
|
2297
2621
|
.compare-grid, .search-results { grid-template-columns: 1fr; }
|
|
2298
2622
|
.sidebar { width: 56px; min-width: 56px; }
|
|
2299
|
-
.sidebar-title, .status-label { display: none; }
|
|
2623
|
+
.sidebar-title, .status-label, .sidebar-bug-label { display: none; }
|
|
2300
2624
|
.tab-btn { justify-content: center; padding: 10px; }
|
|
2301
2625
|
.tab-btn span:not(.tab-btn-icon) { display: none; }
|
|
2302
2626
|
.main { padding: 16px; }
|
|
@@ -2342,6 +2666,18 @@ select:focus { outline: none; border-color: var(--accent); }
|
|
|
2342
2666
|
<symbol id="lg-code" viewBox="0 0 16 16">
|
|
2343
2667
|
<path fill="currentColor" d="M5.854 4.146a.5.5 0 0 1 0 .708L2.707 8l3.147 3.146a.5.5 0 0 1-.708.708l-3.5-3.5a.5.5 0 0 1 0-.708l3.5-3.5a.5.5 0 0 1 .708 0zm4.292 0a.5.5 0 0 0 0 .708L13.293 8l-3.147 3.146a.5.5 0 0 0 .708.708l3.5-3.5a.5.5 0 0 0 0-.708l-3.5-3.5a.5.5 0 0 0-.708 0z"/>
|
|
2344
2668
|
</symbol>
|
|
2669
|
+
<symbol id="lg-palette" viewBox="0 0 16 16">
|
|
2670
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 1C4.13401 1 1 4.13401 1 8C1 11.866 4.13401 15 8 15C8.55228 15 9 14.5523 9 14V12.5C9 11.6716 9.67157 11 10.5 11H12C13.6569 11 15 9.65685 15 8C15 4.13401 11.866 1 8 1ZM4.5 9C5.32843 9 6 8.32843 6 7.5C6 6.67157 5.32843 6 4.5 6C3.67157 6 3 6.67157 3 7.5C3 8.32843 3.67157 9 4.5 9ZM7 5.5C7 6.32843 6.32843 7 5.5 7C4.67157 7 4 6.32843 4 5.5C4 4.67157 4.67157 4 5.5 4C6.32843 4 7 4.67157 7 5.5ZM9.5 6C10.3284 6 11 5.32843 11 4.5C11 3.67157 10.3284 3 9.5 3C8.67157 3 8 3.67157 8 4.5C8 5.32843 8.67157 6 9.5 6ZM13 7.5C13 8.32843 12.3284 9 11.5 9C10.6716 9 10 8.32843 10 7.5C10 6.67157 10.6716 6 11.5 6C12.3284 6 13 6.67157 13 7.5Z" fill="currentColor"/>
|
|
2671
|
+
</symbol>
|
|
2672
|
+
<symbol id="lg-cube" viewBox="0 0 16 16">
|
|
2673
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.35 1.18a.75.75 0 0 0-.7 0l-6 3.25A.75.75 0 0 0 1.25 5v6a.75.75 0 0 0 .4.66l6 3.25a.75.75 0 0 0 .7 0l6-3.25a.75.75 0 0 0 .4-.66V5a.75.75 0 0 0-.4-.66l-6-3.16zM8 3.2 3.47 5.65 8 7.82l4.53-2.17L8 3.2zM2.75 6.8v3.82L7.25 12.8V8.97L2.75 6.8zm5.5 6 4.5-2.18V6.8L8.25 8.97V12.8z" fill="currentColor"/>
|
|
2674
|
+
</symbol>
|
|
2675
|
+
<symbol id="lg-chat" viewBox="0 0 16 16">
|
|
2676
|
+
<path d="M2 2h12v9H5l-3 3V2z" fill="none" stroke="currentColor" stroke-width="1.5"/>
|
|
2677
|
+
</symbol>
|
|
2678
|
+
<symbol id="lg-shield" viewBox="0 0 16 16">
|
|
2679
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.35 1.18a.75.75 0 0 0-.7 0l-5 2.7A.75.75 0 0 0 2.25 4.5V8c0 2.9 2.1 5.5 5.5 6.95a.75.75 0 0 0 .5 0C11.65 13.5 13.75 10.9 13.75 8V4.5a.75.75 0 0 0-.4-.62l-5-2.7zM8 3.2 3.75 5.5V8c0 2.2 1.6 4.2 4.25 5.45C10.65 12.2 12.25 10.2 12.25 8V5.5L8 3.2z" fill="currentColor"/>
|
|
2680
|
+
</symbol>
|
|
2345
2681
|
</svg>
|
|
2346
2682
|
|
|
2347
2683
|
<div class="app-shell">
|
|
@@ -2361,6 +2697,7 @@ select:focus { outline: none; border-color: var(--accent); }
|
|
|
2361
2697
|
<button class="tab-btn" data-tab="search" role="tab" aria-selected="false" aria-controls="tab-search" id="tab-btn-search"><span class="tab-btn-icon" aria-hidden="true"><svg><use href="#lg-search"/></svg></span><span>Search</span></button>
|
|
2362
2698
|
<button class="tab-btn" data-tab="multimodal" role="tab" aria-selected="false" aria-controls="tab-multimodal" id="tab-btn-multimodal"><span class="tab-btn-icon" aria-hidden="true"><svg><use href="#lg-image"/></svg></span><span>Multimodal</span></button>
|
|
2363
2699
|
<button class="tab-btn" data-tab="generate" role="tab" aria-selected="false" aria-controls="tab-generate" id="tab-btn-generate"><span class="tab-btn-icon" aria-hidden="true"><svg><use href="#lg-code"/></svg></span><span>Generate</span></button>
|
|
2700
|
+
<button class="tab-btn" data-tab="chat" role="tab" aria-selected="false" aria-controls="tab-chat" id="tab-btn-chat"><span class="tab-btn-icon" aria-hidden="true"><svg width="16" height="16" viewBox="0 0 16 16"><path d="M2 2h12v9H5l-3 3V2z" fill="none" stroke="currentColor" stroke-width="1.5"/></svg></span><span>Chat</span></button>
|
|
2364
2701
|
</div>
|
|
2365
2702
|
<div class="sidebar-nav-divider"></div>
|
|
2366
2703
|
<div class="sidebar-nav-group" role="tablist" aria-label="Learn">
|
|
@@ -2378,7 +2715,13 @@ select:focus { outline: none; border-color: var(--accent); }
|
|
|
2378
2715
|
</div>
|
|
2379
2716
|
<button class="theme-toggle" id="themeToggle" title="Toggle light/dark mode">🌙</button>
|
|
2380
2717
|
</div>
|
|
2381
|
-
<div
|
|
2718
|
+
<div style="display:flex;align-items:center;justify-content:space-between;">
|
|
2719
|
+
<div id="appVersionLabel" style="font-size:10px;color:var(--text-muted);"></div>
|
|
2720
|
+
<button class="sidebar-bug-link" id="bugButton" title="Report a Bug">
|
|
2721
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="8" cy="9" r="4.5"/><path d="M5.5 5.5L3 3M10.5 5.5L13 3M3 9H1M15 9h-2M5.5 12.5L4 15M10.5 12.5L12 15"/></svg>
|
|
2722
|
+
<span class="sidebar-bug-label">Bug</span>
|
|
2723
|
+
</button>
|
|
2724
|
+
</div>
|
|
2382
2725
|
</div>
|
|
2383
2726
|
</aside>
|
|
2384
2727
|
|
|
@@ -3169,6 +3512,35 @@ Reranking models rescore initial search results to improve relevance ordering.</
|
|
|
3169
3512
|
|
|
3170
3513
|
</div>
|
|
3171
3514
|
|
|
3515
|
+
<!-- ========== CHAT TAB ========== -->
|
|
3516
|
+
<div class="tab-panel" id="tab-chat" role="tabpanel" aria-labelledby="tab-btn-chat" tabindex="0">
|
|
3517
|
+
<div class="chat-container">
|
|
3518
|
+
<div class="chat-header">
|
|
3519
|
+
<div class="chat-header-status" id="chatHeaderStatus">
|
|
3520
|
+
<span id="chatStatusProvider">No provider</span>
|
|
3521
|
+
<span>·</span>
|
|
3522
|
+
<span id="chatStatusDb">No database</span>
|
|
3523
|
+
</div>
|
|
3524
|
+
<button class="chat-config-toggle" id="chatOpenSettings" onclick="openChatSettings()" title="Configure in Settings">
|
|
3525
|
+
<svg width="14" height="14" viewBox="0 0 16 16"><use href="#lg-config"/></svg>
|
|
3526
|
+
Configure
|
|
3527
|
+
</button>
|
|
3528
|
+
</div>
|
|
3529
|
+
<div class="chat-not-configured" id="chatNotConfigured" style="display:none;">
|
|
3530
|
+
<div class="chat-not-configured-icon">💬</div>
|
|
3531
|
+
<p>Chat requires an LLM provider and database to be configured.</p>
|
|
3532
|
+
<button class="btn" onclick="openChatSettings()">Open Chat Settings</button>
|
|
3533
|
+
</div>
|
|
3534
|
+
<div class="chat-messages" id="chatMessages">
|
|
3535
|
+
<div class="chat-message system-msg">Chat with your knowledge base. Click <strong>Configure</strong> above to adjust your LLM provider and database.</div>
|
|
3536
|
+
</div>
|
|
3537
|
+
<div class="chat-input-area">
|
|
3538
|
+
<textarea class="chat-input" id="chatInput" placeholder="Ask a question about your documents..." rows="1" onkeydown="chatInputKeydown(event)"></textarea>
|
|
3539
|
+
<button class="chat-send-btn" id="chatSendBtn" onclick="sendChatMessage()">Send</button>
|
|
3540
|
+
</div>
|
|
3541
|
+
</div>
|
|
3542
|
+
</div>
|
|
3543
|
+
|
|
3172
3544
|
<!-- ========== ABOUT TAB ========== -->
|
|
3173
3545
|
<div class="tab-panel" id="tab-about" role="tabpanel" aria-labelledby="tab-btn-about" tabindex="0">
|
|
3174
3546
|
<div class="about-container">
|
|
@@ -3467,213 +3839,354 @@ Reranking models rescore initial search results to improve relevance ordering.</
|
|
|
3467
3839
|
|
|
3468
3840
|
<!-- ========== SETTINGS TAB ========== -->
|
|
3469
3841
|
<div class="tab-panel" id="tab-settings">
|
|
3470
|
-
<div class="settings-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
<
|
|
3474
|
-
|
|
3842
|
+
<div class="settings-layout">
|
|
3843
|
+
<!-- Settings left nav -->
|
|
3844
|
+
<nav class="settings-nav">
|
|
3845
|
+
<div class="settings-nav-header">Settings</div>
|
|
3846
|
+
<button class="settings-nav-item active" data-settings-section="general">
|
|
3847
|
+
<svg width="16" height="16" viewBox="0 0 16 16"><use href="#lg-config"/></svg>
|
|
3848
|
+
<span>General</span>
|
|
3849
|
+
</button>
|
|
3850
|
+
<button class="settings-nav-item" data-settings-section="appearance">
|
|
3851
|
+
<svg width="16" height="16" viewBox="0 0 16 16"><use href="#lg-palette"/></svg>
|
|
3852
|
+
<span>Appearance</span>
|
|
3853
|
+
</button>
|
|
3854
|
+
<button class="settings-nav-item" data-settings-section="models">
|
|
3855
|
+
<svg width="16" height="16" viewBox="0 0 16 16"><use href="#lg-cube"/></svg>
|
|
3856
|
+
<span>Models</span>
|
|
3857
|
+
</button>
|
|
3858
|
+
<button class="settings-nav-item" data-settings-section="chat">
|
|
3859
|
+
<svg width="16" height="16" viewBox="0 0 16 16"><use href="#lg-chat"/></svg>
|
|
3860
|
+
<span>Chat</span>
|
|
3861
|
+
</button>
|
|
3862
|
+
<button class="settings-nav-item" data-settings-section="benchmark">
|
|
3863
|
+
<svg width="16" height="16" viewBox="0 0 16 16"><use href="#lg-gauge"/></svg>
|
|
3864
|
+
<span>Benchmark</span>
|
|
3865
|
+
</button>
|
|
3866
|
+
<button class="settings-nav-item" data-settings-section="privacy">
|
|
3867
|
+
<svg width="16" height="16" viewBox="0 0 16 16"><use href="#lg-shield"/></svg>
|
|
3868
|
+
<span>Data & Privacy</span>
|
|
3869
|
+
</button>
|
|
3870
|
+
</nav>
|
|
3475
3871
|
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
<div class="settings-row">
|
|
3479
|
-
<div class="settings-label">
|
|
3480
|
-
<span class="settings-label-text">Theme</span>
|
|
3481
|
-
<span class="settings-label-hint">Controls the color scheme of the interface</span>
|
|
3482
|
-
</div>
|
|
3483
|
-
<div class="settings-control">
|
|
3484
|
-
<select class="settings-select" id="settingsTheme">
|
|
3485
|
-
<option value="dark">Dark</option>
|
|
3486
|
-
<option value="light">Light</option>
|
|
3487
|
-
</select>
|
|
3488
|
-
</div>
|
|
3489
|
-
</div>
|
|
3490
|
-
<div class="settings-row">
|
|
3491
|
-
<div class="settings-label">
|
|
3492
|
-
<span class="settings-label-text">Vector Heatmap Colors</span>
|
|
3493
|
-
<span class="settings-label-hint">Color palette for embedding visualizations</span>
|
|
3494
|
-
</div>
|
|
3495
|
-
<div class="settings-control" style="display:flex;align-items:center;gap:8px;">
|
|
3496
|
-
<select class="settings-select" id="settingsHeatmap">
|
|
3497
|
-
<option value="viridis">Viridis</option>
|
|
3498
|
-
<option value="plasma">Plasma</option>
|
|
3499
|
-
<option value="inferno">Inferno</option>
|
|
3500
|
-
<option value="magma">Magma</option>
|
|
3501
|
-
<option value="cividis">Cividis</option>
|
|
3502
|
-
<option value="turbo">Turbo</option>
|
|
3503
|
-
</select>
|
|
3504
|
-
<span class="heatmap-preview" id="heatmapPreview"></span>
|
|
3505
|
-
</div>
|
|
3506
|
-
</div>
|
|
3507
|
-
</div>
|
|
3872
|
+
<!-- Settings content panels -->
|
|
3873
|
+
<div class="settings-content">
|
|
3508
3874
|
|
|
3509
|
-
|
|
3510
|
-
<div class="settings-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
<
|
|
3514
|
-
<span class="settings-label-hint">Pre-selected model for Embed and Compare tabs</span>
|
|
3515
|
-
</div>
|
|
3516
|
-
<div class="settings-control">
|
|
3517
|
-
<select class="settings-select" id="settingsDefaultModel"></select>
|
|
3518
|
-
</div>
|
|
3519
|
-
</div>
|
|
3520
|
-
<div class="settings-row">
|
|
3521
|
-
<div class="settings-label">
|
|
3522
|
-
<span class="settings-label-text">Default Input Type</span>
|
|
3523
|
-
<span class="settings-label-hint">Pre-selected input type for embedding requests</span>
|
|
3875
|
+
<!-- ── General ── -->
|
|
3876
|
+
<div class="settings-panel active" id="settings-general">
|
|
3877
|
+
<div class="settings-panel-header">
|
|
3878
|
+
<h3 class="settings-panel-title">General</h3>
|
|
3879
|
+
<p class="settings-panel-subtitle">API access and connection settings</p>
|
|
3524
3880
|
</div>
|
|
3525
|
-
<div class="settings-
|
|
3526
|
-
<
|
|
3527
|
-
<
|
|
3528
|
-
<
|
|
3529
|
-
</
|
|
3881
|
+
<div class="settings-section">
|
|
3882
|
+
<div class="settings-api-banner" id="settingsApiBanner">
|
|
3883
|
+
<span class="settings-api-banner-icon" id="settingsApiBannerIcon">⚠️</span>
|
|
3884
|
+
<span id="settingsApiKeyMsg">No API key configured — add your key below to get started.</span>
|
|
3885
|
+
</div>
|
|
3886
|
+
<div class="settings-row">
|
|
3887
|
+
<div class="settings-label">
|
|
3888
|
+
<span class="settings-label-text">API Key <span class="settings-origin" data-origin-key="apiKey"></span></span>
|
|
3889
|
+
<span class="settings-label-hint">Encrypted via OS keychain · <a href="https://dash.voyageai.com" target="_blank" class="settings-key-link">🔑 Get a key</a></span>
|
|
3890
|
+
</div>
|
|
3891
|
+
<div class="settings-control" style="min-width:260px;">
|
|
3892
|
+
<div class="settings-api-field">
|
|
3893
|
+
<input type="password" id="settingsApiKey" placeholder="pa-..." autocomplete="off" spellcheck="false">
|
|
3894
|
+
<button type="button" id="settingsApiKeyToggle" title="Show/hide key">👁</button>
|
|
3895
|
+
<button type="button" id="settingsApiKeySave" class="save-btn" title="Save key">Save</button>
|
|
3896
|
+
</div>
|
|
3897
|
+
</div>
|
|
3898
|
+
</div>
|
|
3899
|
+
<div class="settings-row">
|
|
3900
|
+
<div class="settings-label">
|
|
3901
|
+
<span class="settings-label-text">API Base URL <span class="settings-origin" data-origin-key="apiBase"></span></span>
|
|
3902
|
+
<span class="settings-label-hint">Override the default endpoint (leave empty for default)</span>
|
|
3903
|
+
</div>
|
|
3904
|
+
<div class="settings-control">
|
|
3905
|
+
<input type="text" class="settings-input" id="settingsApiBase" placeholder="https://api.voyageai.com/v1">
|
|
3906
|
+
</div>
|
|
3907
|
+
</div>
|
|
3908
|
+
<div class="settings-row">
|
|
3909
|
+
<div class="settings-label">
|
|
3910
|
+
<span class="settings-label-text">Request Timeout</span>
|
|
3911
|
+
<span class="settings-label-hint">Max seconds to wait for API responses</span>
|
|
3912
|
+
</div>
|
|
3913
|
+
<div class="settings-control">
|
|
3914
|
+
<select class="settings-select" id="settingsTimeout">
|
|
3915
|
+
<option value="15">15 seconds</option>
|
|
3916
|
+
<option value="30" selected>30 seconds</option>
|
|
3917
|
+
<option value="60">60 seconds</option>
|
|
3918
|
+
<option value="120">120 seconds</option>
|
|
3919
|
+
</select>
|
|
3920
|
+
</div>
|
|
3921
|
+
</div>
|
|
3530
3922
|
</div>
|
|
3531
3923
|
</div>
|
|
3532
|
-
</div>
|
|
3533
3924
|
|
|
3534
|
-
|
|
3535
|
-
<div class="settings-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
</div>
|
|
3540
|
-
<div class="settings-row">
|
|
3541
|
-
<div class="settings-label">
|
|
3542
|
-
<span class="settings-label-text">API Key</span>
|
|
3543
|
-
<span class="settings-label-hint">Encrypted via OS keychain · <a href="https://dash.voyageai.com" target="_blank" class="settings-key-link">🔑 Get a key</a></span>
|
|
3925
|
+
<!-- ── Appearance ── -->
|
|
3926
|
+
<div class="settings-panel" id="settings-appearance">
|
|
3927
|
+
<div class="settings-panel-header">
|
|
3928
|
+
<h3 class="settings-panel-title">Appearance</h3>
|
|
3929
|
+
<p class="settings-panel-subtitle">Theme and visualization options</p>
|
|
3544
3930
|
</div>
|
|
3545
|
-
<div class="settings-
|
|
3546
|
-
<div class="settings-
|
|
3547
|
-
<
|
|
3548
|
-
|
|
3549
|
-
|
|
3931
|
+
<div class="settings-section">
|
|
3932
|
+
<div class="settings-row">
|
|
3933
|
+
<div class="settings-label">
|
|
3934
|
+
<span class="settings-label-text">Theme</span>
|
|
3935
|
+
<span class="settings-label-hint">Controls the color scheme of the interface</span>
|
|
3936
|
+
</div>
|
|
3937
|
+
<div class="settings-control">
|
|
3938
|
+
<select class="settings-select" id="settingsTheme">
|
|
3939
|
+
<option value="dark">Dark</option>
|
|
3940
|
+
<option value="light">Light</option>
|
|
3941
|
+
</select>
|
|
3942
|
+
</div>
|
|
3943
|
+
</div>
|
|
3944
|
+
<div class="settings-row">
|
|
3945
|
+
<div class="settings-label">
|
|
3946
|
+
<span class="settings-label-text">Vector Heatmap Colors</span>
|
|
3947
|
+
<span class="settings-label-hint">Color palette for embedding visualizations</span>
|
|
3948
|
+
</div>
|
|
3949
|
+
<div class="settings-control" style="display:flex;align-items:center;gap:8px;">
|
|
3950
|
+
<select class="settings-select" id="settingsHeatmap">
|
|
3951
|
+
<option value="viridis">Viridis</option>
|
|
3952
|
+
<option value="plasma">Plasma</option>
|
|
3953
|
+
<option value="inferno">Inferno</option>
|
|
3954
|
+
<option value="magma">Magma</option>
|
|
3955
|
+
<option value="cividis">Cividis</option>
|
|
3956
|
+
<option value="turbo">Turbo</option>
|
|
3957
|
+
</select>
|
|
3958
|
+
<span class="heatmap-preview" id="heatmapPreview"></span>
|
|
3959
|
+
</div>
|
|
3550
3960
|
</div>
|
|
3551
3961
|
</div>
|
|
3552
3962
|
</div>
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
<input type="text" class="settings-input" id="settingsApiBase" placeholder="https://api.voyageai.com/v1">
|
|
3560
|
-
</div>
|
|
3561
|
-
</div>
|
|
3562
|
-
<div class="settings-row">
|
|
3563
|
-
<div class="settings-label">
|
|
3564
|
-
<span class="settings-label-text">Request Timeout</span>
|
|
3565
|
-
<span class="settings-label-hint">Max seconds to wait for API responses</span>
|
|
3963
|
+
|
|
3964
|
+
<!-- ── Models ── -->
|
|
3965
|
+
<div class="settings-panel" id="settings-models">
|
|
3966
|
+
<div class="settings-panel-header">
|
|
3967
|
+
<h3 class="settings-panel-title">Models</h3>
|
|
3968
|
+
<p class="settings-panel-subtitle">Default embedding model preferences</p>
|
|
3566
3969
|
</div>
|
|
3567
|
-
<div class="settings-
|
|
3568
|
-
<
|
|
3569
|
-
<
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3970
|
+
<div class="settings-section">
|
|
3971
|
+
<div class="settings-row">
|
|
3972
|
+
<div class="settings-label">
|
|
3973
|
+
<span class="settings-label-text">Default Embedding Model</span>
|
|
3974
|
+
<span class="settings-label-hint">Pre-selected model for Embed and Compare tabs</span>
|
|
3975
|
+
</div>
|
|
3976
|
+
<div class="settings-control">
|
|
3977
|
+
<select class="settings-select" id="settingsDefaultModel"></select>
|
|
3978
|
+
</div>
|
|
3979
|
+
</div>
|
|
3980
|
+
<div class="settings-row">
|
|
3981
|
+
<div class="settings-label">
|
|
3982
|
+
<span class="settings-label-text">Default Input Type</span>
|
|
3983
|
+
<span class="settings-label-hint">Pre-selected input type for embedding requests</span>
|
|
3984
|
+
</div>
|
|
3985
|
+
<div class="settings-control">
|
|
3986
|
+
<select class="settings-select" id="settingsInputType">
|
|
3987
|
+
<option value="document">document</option>
|
|
3988
|
+
<option value="query">query</option>
|
|
3989
|
+
</select>
|
|
3990
|
+
</div>
|
|
3991
|
+
</div>
|
|
3574
3992
|
</div>
|
|
3575
3993
|
</div>
|
|
3576
|
-
</div>
|
|
3577
3994
|
|
|
3578
|
-
|
|
3579
|
-
<div class="settings-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
<
|
|
3583
|
-
<span class="settings-label-hint">Number of runs when benchmarking latency</span>
|
|
3995
|
+
<!-- ── Chat ── -->
|
|
3996
|
+
<div class="settings-panel" id="settings-chat">
|
|
3997
|
+
<div class="settings-panel-header">
|
|
3998
|
+
<h3 class="settings-panel-title">Chat</h3>
|
|
3999
|
+
<p class="settings-panel-subtitle">LLM provider and knowledge base configuration</p>
|
|
3584
4000
|
</div>
|
|
3585
|
-
<div class="settings-
|
|
3586
|
-
<
|
|
3587
|
-
|
|
3588
|
-
<
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
4001
|
+
<div class="settings-section">
|
|
4002
|
+
<div class="settings-section-title">LLM Provider</div>
|
|
4003
|
+
<div class="settings-row">
|
|
4004
|
+
<div class="settings-label">
|
|
4005
|
+
<span class="settings-label-text">Provider <span class="settings-origin" data-origin-key="provider"></span></span>
|
|
4006
|
+
<span class="settings-label-hint">Which LLM to use for chat responses</span>
|
|
4007
|
+
</div>
|
|
4008
|
+
<div class="settings-control">
|
|
4009
|
+
<select class="settings-select" id="chatProvider" onchange="chatProviderChanged()">
|
|
4010
|
+
<option value="">Not configured</option>
|
|
4011
|
+
<option value="anthropic">Anthropic</option>
|
|
4012
|
+
<option value="openai">OpenAI</option>
|
|
4013
|
+
<option value="ollama">Ollama</option>
|
|
4014
|
+
</select>
|
|
4015
|
+
</div>
|
|
4016
|
+
</div>
|
|
4017
|
+
<div class="settings-row">
|
|
4018
|
+
<div class="settings-label">
|
|
4019
|
+
<span class="settings-label-text">Model <span class="settings-origin" data-origin-key="model"></span></span>
|
|
4020
|
+
<span class="settings-label-hint">LLM model for generating responses</span>
|
|
4021
|
+
</div>
|
|
4022
|
+
<div class="settings-control">
|
|
4023
|
+
<select class="settings-select" id="chatModel" style="min-width:220px">
|
|
4024
|
+
<option value="">Select provider first</option>
|
|
4025
|
+
</select>
|
|
4026
|
+
</div>
|
|
4027
|
+
</div>
|
|
3592
4028
|
</div>
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
4029
|
+
<div class="settings-section">
|
|
4030
|
+
<div class="settings-section-title">Knowledge Base</div>
|
|
4031
|
+
<div class="settings-row">
|
|
4032
|
+
<div class="settings-label">
|
|
4033
|
+
<span class="settings-label-text">Database <span class="settings-origin" data-origin-key="db"></span></span>
|
|
4034
|
+
<span class="settings-label-hint">MongoDB database containing your documents</span>
|
|
4035
|
+
</div>
|
|
4036
|
+
<div class="settings-control">
|
|
4037
|
+
<input type="text" class="settings-input" id="chatDb" placeholder="database">
|
|
4038
|
+
</div>
|
|
4039
|
+
</div>
|
|
4040
|
+
<div class="settings-row">
|
|
4041
|
+
<div class="settings-label">
|
|
4042
|
+
<span class="settings-label-text">Collection <span class="settings-origin" data-origin-key="collection"></span></span>
|
|
4043
|
+
<span class="settings-label-hint">Collection with vector-indexed documents</span>
|
|
4044
|
+
</div>
|
|
4045
|
+
<div class="settings-control">
|
|
4046
|
+
<input type="text" class="settings-input" id="chatCollection" placeholder="collection">
|
|
4047
|
+
</div>
|
|
4048
|
+
</div>
|
|
4049
|
+
<div class="settings-row">
|
|
4050
|
+
<div class="settings-label">
|
|
4051
|
+
<span class="settings-label-text">Max Context Documents</span>
|
|
4052
|
+
<span class="settings-label-hint">How many documents to retrieve per query</span>
|
|
4053
|
+
</div>
|
|
4054
|
+
<div class="settings-control">
|
|
4055
|
+
<input type="number" class="settings-input" id="chatMaxDocs" value="5" min="1" max="20" style="min-width:80px;width:80px;">
|
|
4056
|
+
</div>
|
|
4057
|
+
</div>
|
|
4058
|
+
<div class="settings-row">
|
|
4059
|
+
<div class="settings-label">
|
|
4060
|
+
<span class="settings-label-text">Rerank Results</span>
|
|
4061
|
+
<span class="settings-label-hint">Use Voyage reranker to improve retrieval quality</span>
|
|
4062
|
+
</div>
|
|
4063
|
+
<div class="settings-control">
|
|
4064
|
+
<button class="settings-toggle active" id="chatRerank" type="button"></button>
|
|
4065
|
+
</div>
|
|
4066
|
+
</div>
|
|
3598
4067
|
</div>
|
|
3599
|
-
<div class="settings-
|
|
3600
|
-
<
|
|
4068
|
+
<div class="settings-section">
|
|
4069
|
+
<div class="settings-section-title">Custom Instructions</div>
|
|
4070
|
+
<div class="settings-row" style="flex-direction:column;align-items:stretch;gap:8px;">
|
|
4071
|
+
<div class="settings-label">
|
|
4072
|
+
<span class="settings-label-text">System Prompt</span>
|
|
4073
|
+
<span class="settings-label-hint">Additional instructions appended to the base system prompt</span>
|
|
4074
|
+
</div>
|
|
4075
|
+
<textarea class="settings-input" id="chatSystemPrompt" rows="3" placeholder="e.g. You are a movie expert. Be enthusiastic about recommendations." style="min-width:100%;resize:vertical;font-family:var(--font);text-align:left;"></textarea>
|
|
4076
|
+
</div>
|
|
3601
4077
|
</div>
|
|
3602
4078
|
</div>
|
|
3603
|
-
</div>
|
|
3604
4079
|
|
|
3605
|
-
|
|
3606
|
-
<div class="settings-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
<
|
|
3610
|
-
<span class="settings-label-hint">Replay the onboarding walkthrough</span>
|
|
4080
|
+
<!-- ── Benchmark ── -->
|
|
4081
|
+
<div class="settings-panel" id="settings-benchmark">
|
|
4082
|
+
<div class="settings-panel-header">
|
|
4083
|
+
<h3 class="settings-panel-title">Benchmark</h3>
|
|
4084
|
+
<p class="settings-panel-subtitle">Benchmark iteration and display defaults</p>
|
|
3611
4085
|
</div>
|
|
3612
|
-
<div class="settings-
|
|
3613
|
-
<
|
|
4086
|
+
<div class="settings-section">
|
|
4087
|
+
<div class="settings-row">
|
|
4088
|
+
<div class="settings-label">
|
|
4089
|
+
<span class="settings-label-text">Iterations per Model</span>
|
|
4090
|
+
<span class="settings-label-hint">Number of runs when benchmarking latency</span>
|
|
4091
|
+
</div>
|
|
4092
|
+
<div class="settings-control">
|
|
4093
|
+
<select class="settings-select" id="settingsBenchIter">
|
|
4094
|
+
<option value="3">3 runs</option>
|
|
4095
|
+
<option value="5" selected>5 runs</option>
|
|
4096
|
+
<option value="10">10 runs</option>
|
|
4097
|
+
<option value="20">20 runs</option>
|
|
4098
|
+
</select>
|
|
4099
|
+
</div>
|
|
4100
|
+
</div>
|
|
4101
|
+
<div class="settings-row">
|
|
4102
|
+
<div class="settings-label">
|
|
4103
|
+
<span class="settings-label-text">Show Detailed Timings</span>
|
|
4104
|
+
<span class="settings-label-hint">Display p50/p95/p99 in benchmark results</span>
|
|
4105
|
+
</div>
|
|
4106
|
+
<div class="settings-control">
|
|
4107
|
+
<button class="settings-toggle" id="settingsDetailedTimings" type="button"></button>
|
|
4108
|
+
</div>
|
|
4109
|
+
</div>
|
|
3614
4110
|
</div>
|
|
3615
4111
|
</div>
|
|
3616
|
-
</div>
|
|
3617
4112
|
|
|
3618
|
-
|
|
3619
|
-
<div class="settings-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
<
|
|
3623
|
-
<span class="settings-label-hint">Cache embedding results in browser storage to avoid re-fetching</span>
|
|
4113
|
+
<!-- ── Data & Privacy ── -->
|
|
4114
|
+
<div class="settings-panel" id="settings-privacy">
|
|
4115
|
+
<div class="settings-panel-header">
|
|
4116
|
+
<h3 class="settings-panel-title">Data & Privacy</h3>
|
|
4117
|
+
<p class="settings-panel-subtitle">Caching, analytics, and data management</p>
|
|
3624
4118
|
</div>
|
|
3625
|
-
<div class="settings-
|
|
3626
|
-
<
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
<
|
|
4119
|
+
<div class="settings-section">
|
|
4120
|
+
<div class="settings-row">
|
|
4121
|
+
<div class="settings-label">
|
|
4122
|
+
<span class="settings-label-text">Persist Embeddings Locally</span>
|
|
4123
|
+
<span class="settings-label-hint">Cache embedding results in browser storage to avoid re-fetching</span>
|
|
4124
|
+
</div>
|
|
4125
|
+
<div class="settings-control">
|
|
4126
|
+
<button class="settings-toggle active" id="settingsCacheEmbeddings" type="button"></button>
|
|
4127
|
+
</div>
|
|
4128
|
+
</div>
|
|
4129
|
+
<div class="settings-row">
|
|
4130
|
+
<div class="settings-label">
|
|
4131
|
+
<span class="settings-label-text">Clear Cached Data</span>
|
|
4132
|
+
<span class="settings-label-hint">Remove all locally stored embeddings and preferences</span>
|
|
4133
|
+
</div>
|
|
4134
|
+
<div class="settings-control">
|
|
4135
|
+
<button class="settings-reset-btn" id="settingsClearCache">Clear All Data</button>
|
|
4136
|
+
</div>
|
|
4137
|
+
</div>
|
|
4138
|
+
<div class="settings-row">
|
|
4139
|
+
<div class="settings-label">
|
|
4140
|
+
<span class="settings-label-text">Anonymous Usage Analytics</span>
|
|
4141
|
+
<span class="settings-label-hint">Help improve Vai by sharing anonymous usage data (version, platform, features used). No API keys or personal data.</span>
|
|
4142
|
+
</div>
|
|
4143
|
+
<div class="settings-control">
|
|
4144
|
+
<button class="settings-toggle active" id="settingsTelemetry" type="button"></button>
|
|
4145
|
+
</div>
|
|
4146
|
+
</div>
|
|
3636
4147
|
</div>
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
4148
|
+
<div class="settings-section">
|
|
4149
|
+
<div class="settings-section-title">Help</div>
|
|
4150
|
+
<div class="settings-row">
|
|
4151
|
+
<div class="settings-label">
|
|
4152
|
+
<span class="settings-label-text">Welcome Tour</span>
|
|
4153
|
+
<span class="settings-label-hint">Replay the onboarding walkthrough</span>
|
|
4154
|
+
</div>
|
|
4155
|
+
<div class="settings-control">
|
|
4156
|
+
<button class="settings-reset-btn" id="settingsShowTour" style="background:var(--accent);color:var(--bg);">Show Welcome Tour</button>
|
|
4157
|
+
</div>
|
|
4158
|
+
</div>
|
|
3642
4159
|
</div>
|
|
3643
|
-
|
|
3644
|
-
|
|
4160
|
+
<!-- Version Info (visible in Electron only) -->
|
|
4161
|
+
<div class="settings-section" id="settingsVersionSection" style="display:none;">
|
|
4162
|
+
<div class="settings-section-title">Version</div>
|
|
4163
|
+
<div class="settings-row">
|
|
4164
|
+
<div class="settings-label">
|
|
4165
|
+
<span class="settings-label-text">Vai Desktop</span>
|
|
4166
|
+
<span class="settings-label-hint">Electron desktop application</span>
|
|
4167
|
+
</div>
|
|
4168
|
+
<div class="settings-control">
|
|
4169
|
+
<span class="version-badge" id="settingsAppVersion">—</span>
|
|
4170
|
+
</div>
|
|
4171
|
+
</div>
|
|
4172
|
+
<div class="settings-row">
|
|
4173
|
+
<div class="settings-label">
|
|
4174
|
+
<span class="settings-label-text">Vai CLI Engine</span>
|
|
4175
|
+
<span class="settings-label-hint">Underlying CLI powering embeddings, search & reranking</span>
|
|
4176
|
+
</div>
|
|
4177
|
+
<div class="settings-control">
|
|
4178
|
+
<span class="version-badge" id="settingsCliVersion">—</span>
|
|
4179
|
+
</div>
|
|
4180
|
+
</div>
|
|
3645
4181
|
</div>
|
|
3646
4182
|
</div>
|
|
3647
|
-
</div>
|
|
3648
4183
|
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
<div class="settings-section-title">Version</div>
|
|
3652
|
-
<div class="settings-row">
|
|
3653
|
-
<div class="settings-label">
|
|
3654
|
-
<span class="settings-label-text">Vai Desktop</span>
|
|
3655
|
-
<span class="settings-label-hint">Electron desktop application</span>
|
|
3656
|
-
</div>
|
|
3657
|
-
<div class="settings-control">
|
|
3658
|
-
<span class="version-badge" id="settingsAppVersion">—</span>
|
|
3659
|
-
</div>
|
|
4184
|
+
<div style="text-align:center;padding:8px 0;">
|
|
4185
|
+
<span class="settings-saved" id="settingsSavedMsg">✓ Saved</span>
|
|
3660
4186
|
</div>
|
|
3661
|
-
<div class="settings-row">
|
|
3662
|
-
<div class="settings-label">
|
|
3663
|
-
<span class="settings-label-text">Vai CLI Engine</span>
|
|
3664
|
-
<span class="settings-label-hint">Underlying CLI powering embeddings, search & reranking</span>
|
|
3665
|
-
</div>
|
|
3666
|
-
<div class="settings-control">
|
|
3667
|
-
<span class="version-badge" id="settingsCliVersion">—</span>
|
|
3668
|
-
</div>
|
|
3669
|
-
</div>
|
|
3670
|
-
</div>
|
|
3671
4187
|
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
</div>
|
|
3675
|
-
|
|
3676
|
-
</div>
|
|
4188
|
+
</div><!-- .settings-content -->
|
|
4189
|
+
</div><!-- .settings-layout -->
|
|
3677
4190
|
</div>
|
|
3678
4191
|
|
|
3679
4192
|
</div><!-- .main -->
|
|
@@ -4617,6 +5130,9 @@ const CONCEPT_META = {
|
|
|
4617
5130
|
'code-generation': { icon: '💻', tab: 'explore' },
|
|
4618
5131
|
scaffolding: { icon: '🏗️', tab: 'explore' },
|
|
4619
5132
|
'eval-comparison': { icon: '📊', tab: 'benchmark' },
|
|
5133
|
+
// MongoDB Auto-Embedding
|
|
5134
|
+
'auto-embedding': { icon: '⚡', tab: 'explore' },
|
|
5135
|
+
'vai-vs-auto-embedding': { icon: '🔄', tab: 'explore' },
|
|
4620
5136
|
};
|
|
4621
5137
|
|
|
4622
5138
|
let exploreConcepts = {};
|
|
@@ -5938,6 +6454,41 @@ function initSettings() {
|
|
|
5938
6454
|
const embedType = document.getElementById('embedInputType');
|
|
5939
6455
|
if (embedType) embedType.value = s.inputType;
|
|
5940
6456
|
}
|
|
6457
|
+
|
|
6458
|
+
// ── Chat settings event listeners (fields now live in settings page) ──
|
|
6459
|
+
|
|
6460
|
+
// Rerank toggle (was checkbox, now settings-toggle button)
|
|
6461
|
+
const rerankToggle = document.getElementById('chatRerank');
|
|
6462
|
+
if (rerankToggle) {
|
|
6463
|
+
rerankToggle.addEventListener('click', () => {
|
|
6464
|
+
rerankToggle.classList.toggle('active');
|
|
6465
|
+
saveChatSettings();
|
|
6466
|
+
});
|
|
6467
|
+
}
|
|
6468
|
+
|
|
6469
|
+
// Immediate save on select/toggle changes
|
|
6470
|
+
const chatProvider = document.getElementById('chatProvider');
|
|
6471
|
+
if (chatProvider) {
|
|
6472
|
+
chatProvider.addEventListener('change', () => {
|
|
6473
|
+
// chatProviderChanged() is called via onchange attr — save after a tick
|
|
6474
|
+
setTimeout(saveChatSettings, 100);
|
|
6475
|
+
});
|
|
6476
|
+
}
|
|
6477
|
+
const chatModel = document.getElementById('chatModel');
|
|
6478
|
+
if (chatModel) chatModel.addEventListener('change', saveChatSettings);
|
|
6479
|
+
const chatMaxDocs = document.getElementById('chatMaxDocs');
|
|
6480
|
+
if (chatMaxDocs) chatMaxDocs.addEventListener('change', saveChatSettings);
|
|
6481
|
+
|
|
6482
|
+
// Debounced save on text input changes
|
|
6483
|
+
const chatDb = document.getElementById('chatDb');
|
|
6484
|
+
if (chatDb) chatDb.addEventListener('input', () => { updateChatStatus(); saveChatSettingsDebounced(); });
|
|
6485
|
+
const chatCollection = document.getElementById('chatCollection');
|
|
6486
|
+
if (chatCollection) chatCollection.addEventListener('input', () => { updateChatStatus(); saveChatSettingsDebounced(); });
|
|
6487
|
+
const chatSystemPrompt = document.getElementById('chatSystemPrompt');
|
|
6488
|
+
if (chatSystemPrompt) chatSystemPrompt.addEventListener('input', saveChatSettingsDebounced);
|
|
6489
|
+
|
|
6490
|
+
// Set up settings sub-navigation
|
|
6491
|
+
setupSettingsNav();
|
|
5941
6492
|
}
|
|
5942
6493
|
|
|
5943
6494
|
// ── Update Checker ──
|
|
@@ -6122,6 +6673,13 @@ function initOnboarding() {
|
|
|
6122
6673
|
(isElectron ? ' Create projects directly on disk.' : ' Download as ZIP.'),
|
|
6123
6674
|
arrow: 'left',
|
|
6124
6675
|
},
|
|
6676
|
+
{
|
|
6677
|
+
target: '[data-tab="chat"]',
|
|
6678
|
+
icon: '💬',
|
|
6679
|
+
title: 'RAG Chat',
|
|
6680
|
+
body: '<strong>Chat with your knowledge base</strong> using retrieval-augmented generation. Voyage AI finds relevant documents, then your chosen LLM generates grounded answers with source citations.',
|
|
6681
|
+
arrow: 'left',
|
|
6682
|
+
},
|
|
6125
6683
|
{
|
|
6126
6684
|
target: '[data-tab="benchmark"]',
|
|
6127
6685
|
icon: '⏱️',
|
|
@@ -6707,6 +7265,26 @@ function startVSI(canvas, onExit) {
|
|
|
6707
7265
|
const W = canvas.width = 600;
|
|
6708
7266
|
const H = canvas.height = 500;
|
|
6709
7267
|
|
|
7268
|
+
// Helper function to draw a leaf shape
|
|
7269
|
+
function drawLeaf(ctx, x, y, size, color) {
|
|
7270
|
+
ctx.fillStyle = color;
|
|
7271
|
+
ctx.beginPath();
|
|
7272
|
+
// Leaf shape using bezier curves
|
|
7273
|
+
ctx.moveTo(x, y - size);
|
|
7274
|
+
ctx.quadraticCurveTo(x + size * 0.6, y - size * 0.3, x + size * 0.3, y + size * 0.5);
|
|
7275
|
+
ctx.quadraticCurveTo(x, y + size * 0.3, x, y + size);
|
|
7276
|
+
ctx.quadraticCurveTo(x, y + size * 0.3, x - size * 0.3, y + size * 0.5);
|
|
7277
|
+
ctx.quadraticCurveTo(x - size * 0.6, y - size * 0.3, x, y - size);
|
|
7278
|
+
ctx.fill();
|
|
7279
|
+
// Central vein
|
|
7280
|
+
ctx.strokeStyle = color === '#00ED64' ? '#00A35C' : color;
|
|
7281
|
+
ctx.lineWidth = 1;
|
|
7282
|
+
ctx.beginPath();
|
|
7283
|
+
ctx.moveTo(x, y - size);
|
|
7284
|
+
ctx.lineTo(x, y + size);
|
|
7285
|
+
ctx.stroke();
|
|
7286
|
+
}
|
|
7287
|
+
|
|
6710
7288
|
// Ship
|
|
6711
7289
|
const ship = { x: W / 2, w: 40, h: 16, speed: 5 };
|
|
6712
7290
|
const bullets = [];
|
|
@@ -6779,7 +7357,8 @@ function startVSI(canvas, onExit) {
|
|
|
6779
7357
|
for (let i = 0; i < 8; i++) {
|
|
6780
7358
|
const angle = (Math.PI * 2 / 8) * i + Math.random() * 0.5;
|
|
6781
7359
|
const speed = 1.5 + Math.random() * 2;
|
|
6782
|
-
|
|
7360
|
+
const isLeaf = i % 3 === 0; // Every 3rd particle is a leaf
|
|
7361
|
+
particles.push({ x, y, dx: Math.cos(angle) * speed, dy: Math.sin(angle) * speed, life: 30, color, isLeaf });
|
|
6783
7362
|
}
|
|
6784
7363
|
explosionTexts.push({ x, y, text: sim.toFixed(2), life: 40, color });
|
|
6785
7364
|
}
|
|
@@ -6902,24 +7481,17 @@ function startVSI(canvas, onExit) {
|
|
|
6902
7481
|
ctx.fillRect(sx, sy, 1, 1);
|
|
6903
7482
|
}
|
|
6904
7483
|
|
|
6905
|
-
// Ship (
|
|
6906
|
-
ctx.
|
|
6907
|
-
ctx.beginPath();
|
|
6908
|
-
ctx.moveTo(ship.x, H - 40);
|
|
6909
|
-
ctx.lineTo(ship.x - ship.w / 2, H - 24);
|
|
6910
|
-
ctx.lineTo(ship.x + ship.w / 2, H - 24);
|
|
6911
|
-
ctx.closePath();
|
|
6912
|
-
ctx.fill();
|
|
7484
|
+
// Ship (Voyage AI leaf)
|
|
7485
|
+
drawLeaf(ctx, ship.x, H - 32, 12, '#00ED64');
|
|
6913
7486
|
// Ship label
|
|
6914
7487
|
ctx.fillStyle = '#00ED64';
|
|
6915
7488
|
ctx.font = '9px monospace';
|
|
6916
7489
|
ctx.textAlign = 'center';
|
|
6917
|
-
ctx.fillText('
|
|
7490
|
+
ctx.fillText('voyage', ship.x, H - 14);
|
|
6918
7491
|
|
|
6919
|
-
// Bullets
|
|
6920
|
-
ctx.fillStyle = '#00ED64';
|
|
7492
|
+
// Bullets (small leaf shapes)
|
|
6921
7493
|
for (const b of bullets) {
|
|
6922
|
-
ctx
|
|
7494
|
+
drawLeaf(ctx, b.x, b.y, 3, '#00ED64');
|
|
6923
7495
|
}
|
|
6924
7496
|
|
|
6925
7497
|
// Boss
|
|
@@ -6953,11 +7525,15 @@ function startVSI(canvas, onExit) {
|
|
|
6953
7525
|
ctx.fillText(e.text, e.x, e.y + 3);
|
|
6954
7526
|
}
|
|
6955
7527
|
|
|
6956
|
-
// Particles
|
|
7528
|
+
// Particles (with occasional leaf shapes)
|
|
6957
7529
|
for (const p of particles) {
|
|
6958
7530
|
ctx.globalAlpha = p.life / 30;
|
|
6959
|
-
|
|
6960
|
-
|
|
7531
|
+
if (p.isLeaf) {
|
|
7532
|
+
drawLeaf(ctx, p.x, p.y, 2, p.color);
|
|
7533
|
+
} else {
|
|
7534
|
+
ctx.fillStyle = p.color;
|
|
7535
|
+
ctx.fillRect(p.x - 2, p.y - 2, 4, 4);
|
|
7536
|
+
}
|
|
6961
7537
|
}
|
|
6962
7538
|
ctx.globalAlpha = 1;
|
|
6963
7539
|
|
|
@@ -6977,15 +7553,27 @@ function startVSI(canvas, onExit) {
|
|
|
6977
7553
|
ctx.textAlign = 'left';
|
|
6978
7554
|
ctx.fillText(`SCORE: ${score}`, 12, 20);
|
|
6979
7555
|
ctx.fillText(`WAVE: ${wave}`, 12, 38);
|
|
6980
|
-
ctx.textAlign = 'right';
|
|
6981
|
-
ctx.fillText('❤️'.repeat(lives), W - 12, 20);
|
|
6982
7556
|
|
|
6983
|
-
//
|
|
7557
|
+
// Lives (leaf icons)
|
|
7558
|
+
for (let i = 0; i < lives; i++) {
|
|
7559
|
+
drawLeaf(ctx, W - 20 - (i * 18), 15, 6, '#00ED64');
|
|
7560
|
+
}
|
|
7561
|
+
|
|
7562
|
+
// Title with Voyage AI branding
|
|
6984
7563
|
ctx.fillStyle = '#00ED64';
|
|
6985
7564
|
ctx.font = 'bold 10px monospace';
|
|
6986
7565
|
ctx.textAlign = 'center';
|
|
6987
7566
|
ctx.fillText('VECTOR SPACE INVADERS', W / 2, H - 4);
|
|
6988
7567
|
|
|
7568
|
+
// Voyage AI watermark (top right corner)
|
|
7569
|
+
ctx.globalAlpha = 0.3;
|
|
7570
|
+
drawLeaf(ctx, W - 25, 25, 8, '#00ED64');
|
|
7571
|
+
ctx.fillStyle = '#00ED64';
|
|
7572
|
+
ctx.font = 'bold 9px monospace';
|
|
7573
|
+
ctx.textAlign = 'right';
|
|
7574
|
+
ctx.fillText('VOYAGE AI', W - 38, 28);
|
|
7575
|
+
ctx.globalAlpha = 1;
|
|
7576
|
+
|
|
6989
7577
|
// Game over
|
|
6990
7578
|
if (gameOver) {
|
|
6991
7579
|
ctx.fillStyle = 'rgba(0,30,43,0.8)';
|
|
@@ -7026,6 +7614,400 @@ function startVSI(canvas, onExit) {
|
|
|
7026
7614
|
loop();
|
|
7027
7615
|
}
|
|
7028
7616
|
|
|
7617
|
+
// ── Settings Sub-Navigation ──
|
|
7618
|
+
|
|
7619
|
+
function switchSettingsSection(section) {
|
|
7620
|
+
document.querySelectorAll('.settings-nav-item').forEach(btn => {
|
|
7621
|
+
btn.classList.toggle('active', btn.dataset.settingsSection === section);
|
|
7622
|
+
});
|
|
7623
|
+
document.querySelectorAll('.settings-panel').forEach(p => {
|
|
7624
|
+
p.classList.toggle('active', p.id === 'settings-' + section);
|
|
7625
|
+
});
|
|
7626
|
+
// Scroll content area to top
|
|
7627
|
+
const content = document.querySelector('.settings-content');
|
|
7628
|
+
if (content) content.scrollTop = 0;
|
|
7629
|
+
}
|
|
7630
|
+
|
|
7631
|
+
function openChatSettings() {
|
|
7632
|
+
switchTab('settings');
|
|
7633
|
+
switchSettingsSection('chat');
|
|
7634
|
+
}
|
|
7635
|
+
|
|
7636
|
+
function setupSettingsNav() {
|
|
7637
|
+
document.querySelectorAll('.settings-nav-item').forEach(btn => {
|
|
7638
|
+
btn.addEventListener('click', () => {
|
|
7639
|
+
switchSettingsSection(btn.dataset.settingsSection);
|
|
7640
|
+
});
|
|
7641
|
+
});
|
|
7642
|
+
}
|
|
7643
|
+
|
|
7644
|
+
// ── Settings Origins (provenance badges) ──
|
|
7645
|
+
|
|
7646
|
+
async function loadSettingsOrigins() {
|
|
7647
|
+
try {
|
|
7648
|
+
const res = await fetch('/api/settings/origins');
|
|
7649
|
+
if (!res.ok) return;
|
|
7650
|
+
const origins = await res.json();
|
|
7651
|
+
const labels = { env: 'ENV', config: '~/.vai', project: '.vai.json', default: 'Default' };
|
|
7652
|
+
const tooltips = {
|
|
7653
|
+
env: 'Set via environment variable',
|
|
7654
|
+
config: 'Set in ~/.vai/config.json',
|
|
7655
|
+
project: 'Set in project .vai.json',
|
|
7656
|
+
default: 'Using built-in default',
|
|
7657
|
+
};
|
|
7658
|
+
Object.entries(origins).forEach(([key, origin]) => {
|
|
7659
|
+
const badge = document.querySelector(`[data-origin-key="${key}"]`);
|
|
7660
|
+
if (badge) {
|
|
7661
|
+
badge.className = `settings-origin origin-${origin}`;
|
|
7662
|
+
badge.textContent = labels[origin] || origin;
|
|
7663
|
+
badge.title = tooltips[origin] || '';
|
|
7664
|
+
}
|
|
7665
|
+
});
|
|
7666
|
+
} catch { /* silently ignore */ }
|
|
7667
|
+
}
|
|
7668
|
+
|
|
7669
|
+
// ── Save Chat Settings ──
|
|
7670
|
+
|
|
7671
|
+
let _chatSaveTimer = null;
|
|
7672
|
+
|
|
7673
|
+
function saveChatSettings() {
|
|
7674
|
+
const settings = {
|
|
7675
|
+
provider: document.getElementById('chatProvider').value,
|
|
7676
|
+
model: document.getElementById('chatModel').value,
|
|
7677
|
+
db: document.getElementById('chatDb').value,
|
|
7678
|
+
collection: document.getElementById('chatCollection').value,
|
|
7679
|
+
maxDocs: parseInt(document.getElementById('chatMaxDocs').value) || 5,
|
|
7680
|
+
rerank: document.getElementById('chatRerank').classList.contains('active'),
|
|
7681
|
+
systemPrompt: document.getElementById('chatSystemPrompt').value.trim(),
|
|
7682
|
+
};
|
|
7683
|
+
fetch('/api/chat/config', {
|
|
7684
|
+
method: 'POST',
|
|
7685
|
+
headers: { 'Content-Type': 'application/json' },
|
|
7686
|
+
body: JSON.stringify(settings),
|
|
7687
|
+
}).catch(() => {});
|
|
7688
|
+
updateChatStatus();
|
|
7689
|
+
// Update not-configured banner
|
|
7690
|
+
const notConfigured = document.getElementById('chatNotConfigured');
|
|
7691
|
+
if (notConfigured) {
|
|
7692
|
+
notConfigured.style.display = (!settings.provider || !settings.db) ? 'flex' : 'none';
|
|
7693
|
+
}
|
|
7694
|
+
// Refresh origin badges after save
|
|
7695
|
+
loadSettingsOrigins();
|
|
7696
|
+
flashSaved();
|
|
7697
|
+
}
|
|
7698
|
+
|
|
7699
|
+
function saveChatSettingsDebounced() {
|
|
7700
|
+
clearTimeout(_chatSaveTimer);
|
|
7701
|
+
_chatSaveTimer = setTimeout(saveChatSettings, 500);
|
|
7702
|
+
}
|
|
7703
|
+
|
|
7704
|
+
// flashSaved() is defined earlier (near initSettings) — reused here
|
|
7705
|
+
|
|
7706
|
+
// ── Chat Tab ──
|
|
7707
|
+
let chatSessionId = null;
|
|
7708
|
+
|
|
7709
|
+
async function loadChatConfig() {
|
|
7710
|
+
try {
|
|
7711
|
+
const res = await fetch('/api/chat/config');
|
|
7712
|
+
const data = await res.json();
|
|
7713
|
+
if (data.provider) {
|
|
7714
|
+
document.getElementById('chatProvider').value = data.provider;
|
|
7715
|
+
await chatProviderChanged(); // Populate model dropdown
|
|
7716
|
+
// After models load, select the configured model
|
|
7717
|
+
if (data.model) {
|
|
7718
|
+
const modelSelect = document.getElementById('chatModel');
|
|
7719
|
+
const exists = Array.from(modelSelect.options).some(o => o.value === data.model);
|
|
7720
|
+
if (exists) {
|
|
7721
|
+
modelSelect.value = data.model;
|
|
7722
|
+
}
|
|
7723
|
+
}
|
|
7724
|
+
}
|
|
7725
|
+
if (data.db) document.getElementById('chatDb').value = data.db;
|
|
7726
|
+
if (data.collection) document.getElementById('chatCollection').value = data.collection;
|
|
7727
|
+
if (data.chat?.maxContextDocs) document.getElementById('chatMaxDocs').value = data.chat.maxContextDocs;
|
|
7728
|
+
if (data.chat?.rerank === false) document.getElementById('chatRerank').classList.remove('active');
|
|
7729
|
+
if (data.chat?.systemPrompt) document.getElementById('chatSystemPrompt').value = data.chat.systemPrompt;
|
|
7730
|
+
updateChatStatus();
|
|
7731
|
+
// Show not-configured banner if incomplete
|
|
7732
|
+
const notConfigured = document.getElementById('chatNotConfigured');
|
|
7733
|
+
if (notConfigured) {
|
|
7734
|
+
notConfigured.style.display = (!data.provider || !data.db) ? 'flex' : 'none';
|
|
7735
|
+
}
|
|
7736
|
+
} catch {
|
|
7737
|
+
// No config — show not-configured banner
|
|
7738
|
+
const notConfigured = document.getElementById('chatNotConfigured');
|
|
7739
|
+
if (notConfigured) notConfigured.style.display = 'flex';
|
|
7740
|
+
}
|
|
7741
|
+
}
|
|
7742
|
+
|
|
7743
|
+
function updateChatStatus() {
|
|
7744
|
+
const provider = document.getElementById('chatProvider');
|
|
7745
|
+
const db = document.getElementById('chatDb').value;
|
|
7746
|
+
const collection = document.getElementById('chatCollection').value;
|
|
7747
|
+
const providerLabel = provider.value
|
|
7748
|
+
? provider.options[provider.selectedIndex].text
|
|
7749
|
+
: 'No provider';
|
|
7750
|
+
document.getElementById('chatStatusProvider').textContent = providerLabel;
|
|
7751
|
+
document.getElementById('chatStatusDb').textContent =
|
|
7752
|
+
(db && collection) ? `${db}.${collection}` : 'No database';
|
|
7753
|
+
}
|
|
7754
|
+
|
|
7755
|
+
async function chatProviderChanged() {
|
|
7756
|
+
updateChatStatus();
|
|
7757
|
+
const provider = document.getElementById('chatProvider').value;
|
|
7758
|
+
const modelSelect = document.getElementById('chatModel');
|
|
7759
|
+
|
|
7760
|
+
if (!provider) {
|
|
7761
|
+
modelSelect.innerHTML = '<option value="">Select provider first</option>';
|
|
7762
|
+
return;
|
|
7763
|
+
}
|
|
7764
|
+
|
|
7765
|
+
modelSelect.innerHTML = '<option value="">Loading models...</option>';
|
|
7766
|
+
modelSelect.disabled = true;
|
|
7767
|
+
|
|
7768
|
+
try {
|
|
7769
|
+
const res = await fetch(`/api/chat/models?provider=${provider}`);
|
|
7770
|
+
const data = await res.json();
|
|
7771
|
+
const models = data.models || [];
|
|
7772
|
+
const defaults = { anthropic: 'claude-sonnet-4-5-20250929', openai: 'gpt-4o', ollama: 'llama3.1' };
|
|
7773
|
+
const defaultModel = defaults[provider];
|
|
7774
|
+
|
|
7775
|
+
modelSelect.innerHTML = '';
|
|
7776
|
+
|
|
7777
|
+
if (models.length === 0) {
|
|
7778
|
+
if (provider === 'ollama') {
|
|
7779
|
+
modelSelect.innerHTML = '<option value="">No models found — is Ollama running?</option>';
|
|
7780
|
+
} else {
|
|
7781
|
+
// Fallback to default
|
|
7782
|
+
modelSelect.innerHTML = `<option value="${defaultModel}">${defaultModel}</option>`;
|
|
7783
|
+
}
|
|
7784
|
+
} else {
|
|
7785
|
+
models.forEach(m => {
|
|
7786
|
+
const opt = document.createElement('option');
|
|
7787
|
+
opt.value = m.id;
|
|
7788
|
+
let label = m.name || m.id;
|
|
7789
|
+
if (m.context) label += ` (${m.context})`;
|
|
7790
|
+
if (m.size) label += ` — ${m.size}`;
|
|
7791
|
+
if (m.parameterSize) label += ` [${m.parameterSize}]`;
|
|
7792
|
+
if (m.quantization) label += ` ${m.quantization}`;
|
|
7793
|
+
opt.textContent = label;
|
|
7794
|
+
if (m.id === defaultModel) opt.selected = true;
|
|
7795
|
+
modelSelect.appendChild(opt);
|
|
7796
|
+
});
|
|
7797
|
+
}
|
|
7798
|
+
|
|
7799
|
+
// Always add a custom option
|
|
7800
|
+
const customOpt = document.createElement('option');
|
|
7801
|
+
customOpt.value = '__custom__';
|
|
7802
|
+
customOpt.textContent = '✏️ Custom model...';
|
|
7803
|
+
modelSelect.appendChild(customOpt);
|
|
7804
|
+
|
|
7805
|
+
} catch {
|
|
7806
|
+
const defaults = { anthropic: 'claude-sonnet-4-5-20250929', openai: 'gpt-4o', ollama: 'llama3.1' };
|
|
7807
|
+
modelSelect.innerHTML = `<option value="${defaults[provider] || ''}">${defaults[provider] || 'default'}</option>`;
|
|
7808
|
+
}
|
|
7809
|
+
|
|
7810
|
+
modelSelect.disabled = false;
|
|
7811
|
+
}
|
|
7812
|
+
|
|
7813
|
+
// Handle custom model selection
|
|
7814
|
+
document.getElementById('chatModel')?.addEventListener('change', function() {
|
|
7815
|
+
if (this.value === '__custom__') {
|
|
7816
|
+
const custom = prompt('Enter model name:');
|
|
7817
|
+
if (custom) {
|
|
7818
|
+
const opt = document.createElement('option');
|
|
7819
|
+
opt.value = custom;
|
|
7820
|
+
opt.textContent = custom;
|
|
7821
|
+
// Insert before the custom option
|
|
7822
|
+
this.insertBefore(opt, this.lastChild);
|
|
7823
|
+
this.value = custom;
|
|
7824
|
+
} else {
|
|
7825
|
+
this.selectedIndex = 0;
|
|
7826
|
+
}
|
|
7827
|
+
}
|
|
7828
|
+
});
|
|
7829
|
+
|
|
7830
|
+
function chatInputKeydown(e) {
|
|
7831
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
7832
|
+
e.preventDefault();
|
|
7833
|
+
sendChatMessage();
|
|
7834
|
+
}
|
|
7835
|
+
}
|
|
7836
|
+
|
|
7837
|
+
function addChatMessage(role, content, sources) {
|
|
7838
|
+
const container = document.getElementById('chatMessages');
|
|
7839
|
+
const div = document.createElement('div');
|
|
7840
|
+
div.className = `chat-message ${role}`;
|
|
7841
|
+
const contentSpan = document.createElement('span');
|
|
7842
|
+
contentSpan.className = 'chat-message-content';
|
|
7843
|
+
contentSpan.textContent = content;
|
|
7844
|
+
div.appendChild(contentSpan);
|
|
7845
|
+
|
|
7846
|
+
if (sources && sources.length > 0) {
|
|
7847
|
+
const details = document.createElement('details');
|
|
7848
|
+
details.className = 'chat-sources';
|
|
7849
|
+
const summary = document.createElement('summary');
|
|
7850
|
+
summary.textContent = `${sources.length} source${sources.length > 1 ? 's' : ''}`;
|
|
7851
|
+
details.appendChild(summary);
|
|
7852
|
+
const ul = document.createElement('ul');
|
|
7853
|
+
sources.forEach(s => {
|
|
7854
|
+
const li = document.createElement('li');
|
|
7855
|
+
li.textContent = `${s.source} (${s.score?.toFixed(2) || 'N/A'})`;
|
|
7856
|
+
ul.appendChild(li);
|
|
7857
|
+
});
|
|
7858
|
+
details.appendChild(ul);
|
|
7859
|
+
div.appendChild(details);
|
|
7860
|
+
}
|
|
7861
|
+
|
|
7862
|
+
container.appendChild(div);
|
|
7863
|
+
container.scrollTop = container.scrollHeight;
|
|
7864
|
+
return div;
|
|
7865
|
+
}
|
|
7866
|
+
|
|
7867
|
+
async function sendChatMessage() {
|
|
7868
|
+
const input = document.getElementById('chatInput');
|
|
7869
|
+
const query = input.value.trim();
|
|
7870
|
+
if (!query) return;
|
|
7871
|
+
|
|
7872
|
+
const provider = document.getElementById('chatProvider').value;
|
|
7873
|
+
const modelVal = document.getElementById('chatModel').value;
|
|
7874
|
+
const model = (modelVal && modelVal !== '__custom__') ? modelVal : undefined;
|
|
7875
|
+
const db = document.getElementById('chatDb').value;
|
|
7876
|
+
const collection = document.getElementById('chatCollection').value;
|
|
7877
|
+
const maxDocs = parseInt(document.getElementById('chatMaxDocs').value) || 5;
|
|
7878
|
+
const rerank = document.getElementById('chatRerank').classList.contains('active');
|
|
7879
|
+
const systemPrompt = document.getElementById('chatSystemPrompt').value.trim() || undefined;
|
|
7880
|
+
|
|
7881
|
+
if (!provider) {
|
|
7882
|
+
addChatMessage('system-msg', 'Please select an LLM provider in <a href="#" onclick="openChatSettings();return false;">Chat Settings</a>.');
|
|
7883
|
+
return;
|
|
7884
|
+
}
|
|
7885
|
+
if (!db || !collection) {
|
|
7886
|
+
addChatMessage('system-msg', 'Please configure a database and collection in <a href="#" onclick="openChatSettings();return false;">Chat Settings</a>.');
|
|
7887
|
+
return;
|
|
7888
|
+
}
|
|
7889
|
+
|
|
7890
|
+
// Show user message
|
|
7891
|
+
addChatMessage('user', query);
|
|
7892
|
+
input.value = '';
|
|
7893
|
+
input.style.height = 'auto';
|
|
7894
|
+
|
|
7895
|
+
// Show typing indicator
|
|
7896
|
+
const typing = document.createElement('div');
|
|
7897
|
+
typing.className = 'chat-typing';
|
|
7898
|
+
typing.textContent = 'Thinking';
|
|
7899
|
+
document.getElementById('chatMessages').appendChild(typing);
|
|
7900
|
+
|
|
7901
|
+
// Disable input
|
|
7902
|
+
const sendBtn = document.getElementById('chatSendBtn');
|
|
7903
|
+
sendBtn.disabled = true;
|
|
7904
|
+
input.disabled = true;
|
|
7905
|
+
|
|
7906
|
+
try {
|
|
7907
|
+
const res = await fetch('/api/chat/message', {
|
|
7908
|
+
method: 'POST',
|
|
7909
|
+
headers: { 'Content-Type': 'application/json' },
|
|
7910
|
+
body: JSON.stringify({ query, db, collection, provider, model, maxDocs, rerank, systemPrompt }),
|
|
7911
|
+
});
|
|
7912
|
+
|
|
7913
|
+
if (!res.ok) {
|
|
7914
|
+
const err = await res.json();
|
|
7915
|
+
typing.remove();
|
|
7916
|
+
addChatMessage('system-msg', `Error: ${err.error || 'Request failed'}`);
|
|
7917
|
+
return;
|
|
7918
|
+
}
|
|
7919
|
+
|
|
7920
|
+
// Parse SSE stream
|
|
7921
|
+
const reader = res.body.getReader();
|
|
7922
|
+
const decoder = new TextDecoder();
|
|
7923
|
+
let buffer = '';
|
|
7924
|
+
let assistantDiv = null;
|
|
7925
|
+
let fullText = '';
|
|
7926
|
+
let sources = [];
|
|
7927
|
+
|
|
7928
|
+
while (true) {
|
|
7929
|
+
const { done, value } = await reader.read();
|
|
7930
|
+
if (done) break;
|
|
7931
|
+
|
|
7932
|
+
buffer += decoder.decode(value, { stream: true });
|
|
7933
|
+
const lines = buffer.split('\n');
|
|
7934
|
+
buffer = lines.pop() || '';
|
|
7935
|
+
|
|
7936
|
+
let currentEvent = null;
|
|
7937
|
+
for (const line of lines) {
|
|
7938
|
+
if (line.startsWith('event: ')) {
|
|
7939
|
+
currentEvent = line.slice(7).trim();
|
|
7940
|
+
continue;
|
|
7941
|
+
}
|
|
7942
|
+
if (line.startsWith('data: ')) {
|
|
7943
|
+
const data = JSON.parse(line.slice(6));
|
|
7944
|
+
|
|
7945
|
+
if (currentEvent === 'retrieval') {
|
|
7946
|
+
typing.textContent = `Retrieved ${data.docs?.length || 0} docs (${data.timeMs}ms)`;
|
|
7947
|
+
}
|
|
7948
|
+
|
|
7949
|
+
if (currentEvent === 'chunk') {
|
|
7950
|
+
if (!assistantDiv) {
|
|
7951
|
+
typing.remove();
|
|
7952
|
+
assistantDiv = addChatMessage('assistant', '');
|
|
7953
|
+
}
|
|
7954
|
+
fullText += data.text;
|
|
7955
|
+
assistantDiv.querySelector('.chat-message-content').textContent = fullText;
|
|
7956
|
+
document.getElementById('chatMessages').scrollTop = document.getElementById('chatMessages').scrollHeight;
|
|
7957
|
+
}
|
|
7958
|
+
|
|
7959
|
+
if (currentEvent === 'done') {
|
|
7960
|
+
sources = data.sources || [];
|
|
7961
|
+
if (sources.length > 0 && assistantDiv) {
|
|
7962
|
+
const details = document.createElement('details');
|
|
7963
|
+
details.className = 'chat-sources';
|
|
7964
|
+
const summary = document.createElement('summary');
|
|
7965
|
+
summary.textContent = `${sources.length} source${sources.length > 1 ? 's' : ''}`;
|
|
7966
|
+
details.appendChild(summary);
|
|
7967
|
+
const ul = document.createElement('ul');
|
|
7968
|
+
sources.forEach(s => {
|
|
7969
|
+
const li = document.createElement('li');
|
|
7970
|
+
li.textContent = `${s.source} (${s.score?.toFixed(2) || 'N/A'})`;
|
|
7971
|
+
ul.appendChild(li);
|
|
7972
|
+
});
|
|
7973
|
+
details.appendChild(ul);
|
|
7974
|
+
assistantDiv.appendChild(details);
|
|
7975
|
+
}
|
|
7976
|
+
}
|
|
7977
|
+
|
|
7978
|
+
if (currentEvent === 'error') {
|
|
7979
|
+
typing.remove();
|
|
7980
|
+
addChatMessage('system-msg', `Error: ${data.error}`);
|
|
7981
|
+
}
|
|
7982
|
+
|
|
7983
|
+
currentEvent = null;
|
|
7984
|
+
}
|
|
7985
|
+
}
|
|
7986
|
+
}
|
|
7987
|
+
|
|
7988
|
+
// Remove typing if still present
|
|
7989
|
+
if (typing.parentNode) typing.remove();
|
|
7990
|
+
|
|
7991
|
+
} catch (err) {
|
|
7992
|
+
if (typing.parentNode) typing.remove();
|
|
7993
|
+
addChatMessage('system-msg', `Error: ${err.message}`);
|
|
7994
|
+
} finally {
|
|
7995
|
+
sendBtn.disabled = false;
|
|
7996
|
+
input.disabled = false;
|
|
7997
|
+
input.focus();
|
|
7998
|
+
}
|
|
7999
|
+
}
|
|
8000
|
+
|
|
8001
|
+
// Auto-resize chat input
|
|
8002
|
+
document.getElementById('chatInput')?.addEventListener('input', function() {
|
|
8003
|
+
this.style.height = 'auto';
|
|
8004
|
+
this.style.height = Math.min(this.scrollHeight, 120) + 'px';
|
|
8005
|
+
});
|
|
8006
|
+
|
|
8007
|
+
// Load chat config and settings origins on init
|
|
8008
|
+
loadChatConfig();
|
|
8009
|
+
loadSettingsOrigins();
|
|
8010
|
+
|
|
7029
8011
|
// ── Expose functions to global scope for onclick handlers ──
|
|
7030
8012
|
window.setGenerateMode = setGenerateMode;
|
|
7031
8013
|
window.copyGeneratedCode = copyGeneratedCode;
|
|
@@ -7034,6 +8016,12 @@ window.updateGenComponents = updateGenComponents;
|
|
|
7034
8016
|
window.createScaffold = createScaffold;
|
|
7035
8017
|
window.pickScaffoldDirectory = pickScaffoldDirectory;
|
|
7036
8018
|
window.downloadScaffoldZip = downloadScaffoldZip;
|
|
8019
|
+
window.sendChatMessage = sendChatMessage;
|
|
8020
|
+
window.openChatSettings = openChatSettings;
|
|
8021
|
+
window.switchSettingsSection = switchSettingsSection;
|
|
8022
|
+
window.updateChatStatus = updateChatStatus;
|
|
8023
|
+
window.chatInputKeydown = chatInputKeydown;
|
|
8024
|
+
window.chatProviderChanged = chatProviderChanged;
|
|
7037
8025
|
|
|
7038
8026
|
// ── Start ──
|
|
7039
8027
|
init();
|
|
@@ -7126,8 +8114,16 @@ init();
|
|
|
7126
8114
|
|
|
7127
8115
|
<!-- 🐛 Bug Reporter -->
|
|
7128
8116
|
<style>
|
|
7129
|
-
.bug-
|
|
7130
|
-
|
|
8117
|
+
.sidebar-bug-link{
|
|
8118
|
+
display:flex;align-items:center;gap:6px;
|
|
8119
|
+
background:none;border:none;cursor:pointer;
|
|
8120
|
+
font-size:11px;color:var(--text-muted);
|
|
8121
|
+
padding:4px 0;font-family:inherit;
|
|
8122
|
+
transition:color .15s;
|
|
8123
|
+
}
|
|
8124
|
+
.sidebar-bug-link:hover{color:var(--text);}
|
|
8125
|
+
.sidebar-bug-link svg{width:12px;height:12px;flex-shrink:0;opacity:0.6;}
|
|
8126
|
+
.sidebar-bug-link:hover svg{opacity:1;}
|
|
7131
8127
|
.bug-reporter-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.6);backdrop-filter:blur(4px);display:flex;align-items:center;justify-content:center;z-index:9999}
|
|
7132
8128
|
.bug-reporter-modal{background:var(--bg-surface);border-radius:12px;width:90%;max-width:500px;max-height:90vh;overflow-y:auto;box-shadow:0 20px 60px rgba(0,0,0,0.5)}
|
|
7133
8129
|
.bug-reporter-header{display:flex;align-items:center;gap:12px;padding:16px 20px;border-bottom:1px solid var(--border)}
|
|
@@ -7157,7 +8153,7 @@ init();
|
|
|
7157
8153
|
.bug-success code{background:rgba(255,255,255,0.1);padding:4px 8px;border-radius:4px;font-size:12px;color:var(--accent)}
|
|
7158
8154
|
</style>
|
|
7159
8155
|
|
|
7160
|
-
|
|
8156
|
+
<!-- Bug button moved to sidebar footer -->
|
|
7161
8157
|
|
|
7162
8158
|
<div class="bug-reporter-overlay" id="bugOverlay" style="display:none">
|
|
7163
8159
|
<div class="bug-reporter-modal">
|