lance-context 1.36.2 → 1.37.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.
@@ -220,26 +220,17 @@ export function getDashboardHTML() {
220
220
 
221
221
  .grid {
222
222
  display: grid;
223
- grid-template-columns: repeat(3, 1fr);
223
+ grid-template-columns: repeat(2, 1fr);
224
224
  gap: 16px;
225
225
  }
226
226
 
227
- /* Tablet: 2 cards per row */
228
- @media (max-width: 1024px) {
229
- .grid {
230
- grid-template-columns: repeat(2, 1fr);
231
- }
232
- .card.double-width {
233
- grid-column: span 2;
234
- }
235
- }
236
-
237
227
  /* Mobile: 1 card per row */
238
228
  @media (max-width: 768px) {
239
229
  .grid {
240
230
  grid-template-columns: 1fr;
241
231
  }
242
- .card.double-width {
232
+ .card.double-width,
233
+ .card.half-width {
243
234
  grid-column: span 1;
244
235
  }
245
236
  }
@@ -563,6 +554,29 @@ export function getDashboardHTML() {
563
554
  grid-column: span 2;
564
555
  }
565
556
 
557
+ /* Half-width: takes 1 column (50% on 2-col grid) */
558
+ .card.half-width {
559
+ grid-column: span 1;
560
+ }
561
+
562
+ /* Compact card style */
563
+ .card.compact {
564
+ padding: 16px;
565
+ }
566
+
567
+ .card.compact .stat {
568
+ margin-bottom: 8px;
569
+ }
570
+
571
+ .card.compact .stat-value {
572
+ font-size: 20px;
573
+ }
574
+
575
+ .card.compact .reindex-actions {
576
+ margin-top: 8px;
577
+ padding-top: 8px;
578
+ }
579
+
566
580
  .empty-state {
567
581
  text-align: center;
568
582
  padding: 40px 20px;
@@ -783,16 +797,28 @@ export function getDashboardHTML() {
783
797
 
784
798
  /* Beads Section Styles */
785
799
  .beads-section {
786
- margin-top: 32px;
787
- padding-top: 24px;
788
- border-top: 1px solid var(--border-color);
800
+ /* Now a standalone tab, no border needed */
789
801
  }
790
802
 
791
803
  .beads-header {
792
804
  display: flex;
793
805
  align-items: center;
806
+ justify-content: space-between;
794
807
  gap: 12px;
795
808
  margin-bottom: 16px;
809
+ flex-wrap: wrap;
810
+ }
811
+
812
+ .beads-header-left {
813
+ display: flex;
814
+ align-items: center;
815
+ gap: 12px;
816
+ }
817
+
818
+ .beads-header-right {
819
+ display: flex;
820
+ align-items: center;
821
+ gap: 24px;
796
822
  }
797
823
 
798
824
  .beads-logo {
@@ -967,6 +993,194 @@ export function getDashboardHTML() {
967
993
  font-style: italic;
968
994
  color: var(--text-muted);
969
995
  }
996
+
997
+ /* Tab navigation */
998
+ .tab-container {
999
+ margin-bottom: 24px;
1000
+ }
1001
+
1002
+ .tab-nav {
1003
+ display: flex;
1004
+ gap: 4px;
1005
+ border-bottom: 1px solid var(--border-color);
1006
+ padding-bottom: 0;
1007
+ }
1008
+
1009
+ .tab-btn {
1010
+ padding: 10px 20px;
1011
+ font-size: 14px;
1012
+ font-weight: 500;
1013
+ color: var(--text-secondary);
1014
+ background: transparent;
1015
+ border: none;
1016
+ border-bottom: 2px solid transparent;
1017
+ cursor: pointer;
1018
+ transition: all 0.2s ease;
1019
+ margin-bottom: -1px;
1020
+ }
1021
+
1022
+ .tab-btn:hover {
1023
+ color: var(--text-primary);
1024
+ background-color: var(--bg-tertiary);
1025
+ }
1026
+
1027
+ .tab-btn.active {
1028
+ color: var(--accent-blue);
1029
+ border-bottom-color: var(--accent-blue);
1030
+ }
1031
+
1032
+ .tab-content {
1033
+ display: none;
1034
+ }
1035
+
1036
+ .tab-content.active {
1037
+ display: block;
1038
+ }
1039
+
1040
+ /* Slider styles */
1041
+ .form-slider {
1042
+ width: 100%;
1043
+ height: 8px;
1044
+ border-radius: 4px;
1045
+ background: var(--bg-tertiary);
1046
+ outline: none;
1047
+ -webkit-appearance: none;
1048
+ appearance: none;
1049
+ cursor: pointer;
1050
+ }
1051
+
1052
+ .form-slider::-webkit-slider-thumb {
1053
+ -webkit-appearance: none;
1054
+ appearance: none;
1055
+ width: 20px;
1056
+ height: 20px;
1057
+ border-radius: 50%;
1058
+ background: var(--accent-blue);
1059
+ cursor: pointer;
1060
+ border: 2px solid var(--bg-primary);
1061
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
1062
+ }
1063
+
1064
+ .form-slider::-moz-range-thumb {
1065
+ width: 20px;
1066
+ height: 20px;
1067
+ border-radius: 50%;
1068
+ background: var(--accent-blue);
1069
+ cursor: pointer;
1070
+ border: 2px solid var(--bg-primary);
1071
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
1072
+ }
1073
+
1074
+ .slider-labels {
1075
+ display: flex;
1076
+ justify-content: space-between;
1077
+ margin-top: 8px;
1078
+ font-size: 12px;
1079
+ color: var(--text-muted);
1080
+ }
1081
+
1082
+ .slider-value {
1083
+ text-align: center;
1084
+ font-size: 14px;
1085
+ font-weight: 500;
1086
+ color: var(--text-primary);
1087
+ margin-top: 8px;
1088
+ }
1089
+
1090
+ /* Chip styles */
1091
+ .chips-container {
1092
+ display: flex;
1093
+ flex-wrap: wrap;
1094
+ gap: 8px;
1095
+ margin-bottom: 12px;
1096
+ }
1097
+
1098
+ .chip {
1099
+ display: inline-flex;
1100
+ align-items: center;
1101
+ gap: 6px;
1102
+ padding: 6px 10px;
1103
+ font-size: 13px;
1104
+ font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
1105
+ background-color: var(--bg-tertiary);
1106
+ border: 1px solid var(--border-color);
1107
+ border-radius: 16px;
1108
+ color: var(--text-secondary);
1109
+ }
1110
+
1111
+ .chip.exclude {
1112
+ color: var(--accent-red);
1113
+ border-color: rgba(248, 81, 73, 0.3);
1114
+ background-color: rgba(248, 81, 73, 0.1);
1115
+ }
1116
+
1117
+ .chip-remove {
1118
+ display: flex;
1119
+ align-items: center;
1120
+ justify-content: center;
1121
+ width: 16px;
1122
+ height: 16px;
1123
+ border-radius: 50%;
1124
+ background: transparent;
1125
+ border: none;
1126
+ color: var(--text-muted);
1127
+ cursor: pointer;
1128
+ padding: 0;
1129
+ font-size: 14px;
1130
+ line-height: 1;
1131
+ transition: all 0.2s ease;
1132
+ }
1133
+
1134
+ .chip-remove:hover {
1135
+ background-color: var(--accent-red);
1136
+ color: white;
1137
+ }
1138
+
1139
+ .chip-input-group {
1140
+ display: flex;
1141
+ gap: 8px;
1142
+ margin-top: 8px;
1143
+ }
1144
+
1145
+ .chip-input-group .form-input {
1146
+ flex: 1;
1147
+ }
1148
+
1149
+ .chip-input-group .btn {
1150
+ flex-shrink: 0;
1151
+ }
1152
+
1153
+ .chips-empty {
1154
+ color: var(--text-muted);
1155
+ font-size: 13px;
1156
+ font-style: italic;
1157
+ }
1158
+
1159
+ /* Patterns section layout */
1160
+ .patterns-section {
1161
+ display: flex;
1162
+ flex-direction: column;
1163
+ gap: 24px;
1164
+ }
1165
+
1166
+ .patterns-group {
1167
+ display: flex;
1168
+ flex-direction: column;
1169
+ gap: 8px;
1170
+ }
1171
+
1172
+ .patterns-group-header {
1173
+ display: flex;
1174
+ align-items: center;
1175
+ gap: 8px;
1176
+ margin-bottom: 4px;
1177
+ }
1178
+
1179
+ .patterns-group-title {
1180
+ font-size: 14px;
1181
+ font-weight: 600;
1182
+ color: var(--text-primary);
1183
+ }
970
1184
  </style>
971
1185
  </head>
972
1186
  <body>
@@ -997,6 +1211,18 @@ export function getDashboardHTML() {
997
1211
  </div>
998
1212
  </header>
999
1213
 
1214
+ <!-- Tab Navigation -->
1215
+ <div class="tab-container">
1216
+ <nav class="tab-nav">
1217
+ <button class="tab-btn active" data-tab="status">Status</button>
1218
+ <button class="tab-btn" data-tab="beads">Beads</button>
1219
+ <button class="tab-btn" data-tab="settings">Settings</button>
1220
+ </nav>
1221
+ </div>
1222
+
1223
+ <!-- Status Tab -->
1224
+ <div id="tab-status" class="tab-content active">
1225
+
1000
1226
  <!-- Backend Fallback Warning Banner -->
1001
1227
  <div class="warning-banner" id="fallbackBanner">
1002
1228
  <div class="warning-banner-header">
@@ -1014,8 +1240,29 @@ export function getDashboardHTML() {
1014
1240
  </div>
1015
1241
 
1016
1242
  <div class="grid">
1243
+ <!-- Command Usage Card -->
1244
+ <div class="card full-width">
1245
+ <div class="card-header">
1246
+ <span class="card-title">Command Usage</span>
1247
+ <span class="badge" id="sessionBadge">This Session</span>
1248
+ </div>
1249
+ <div id="usageChartContainer">
1250
+ <div class="usage-empty" id="usageEmpty">No commands executed yet</div>
1251
+ <div id="chartWrapper">
1252
+ <table class="charts-css column show-primary-axis data-spacing-5" id="usage-chart" style="display: none;">
1253
+ <tbody id="usageChartBody"></tbody>
1254
+ </table>
1255
+ </div>
1256
+ <ul class="charts-css legend legend-inline legend-square" id="chartLegend" style="display: none;"></ul>
1257
+ <div class="usage-total" id="usageTotal" style="display: none;">
1258
+ <span class="usage-total-label">Total Commands</span>
1259
+ <span class="usage-total-count" id="totalCount">0</span>
1260
+ </div>
1261
+ </div>
1262
+ </div>
1263
+
1017
1264
  <!-- Index Status Card -->
1018
- <div class="card">
1265
+ <div class="card half-width compact">
1019
1266
  <div class="card-header">
1020
1267
  <span class="card-title">Index Status</span>
1021
1268
  <span class="badge" id="indexBadge">Loading...</span>
@@ -1045,132 +1292,8 @@ export function getDashboardHTML() {
1045
1292
  </div>
1046
1293
  </div>
1047
1294
 
1048
- <!-- Embedding Backend Card -->
1049
- <div class="card">
1050
- <div class="card-header">
1051
- <span class="card-title">Embedding Backend</span>
1052
- <span class="badge" id="embeddingStatus">-</span>
1053
- </div>
1054
- <div class="stat">
1055
- <div class="stat-label">Current Backend</div>
1056
- <div class="stat-value small" id="embeddingBackend">-</div>
1057
- </div>
1058
- <div class="stat">
1059
- <div class="stat-label">Index Path</div>
1060
- <div class="stat-value small" id="indexPath">-</div>
1061
- </div>
1062
- <div class="settings-form" id="embeddingSettingsForm">
1063
- <div class="form-group">
1064
- <label for="backendSelect">Select Backend</label>
1065
- <select id="backendSelect" class="form-select">
1066
- <option value="ollama">Ollama (local)</option>
1067
- <option value="gemini" selected>Google Gemini (free - requires API key)</option>
1068
- </select>
1069
- </div>
1070
- <div class="form-group" id="ollamaSettingsGroup">
1071
- <label for="concurrencySelect">Ollama Concurrency</label>
1072
- <select id="concurrencySelect" class="form-select">
1073
- <option value="1" selected>1 (default)</option>
1074
- <option value="2">2</option>
1075
- <option value="3">3</option>
1076
- <option value="5">5</option>
1077
- <option value="10">10</option>
1078
- <option value="25">25</option>
1079
- <option value="50">50</option>
1080
- <option value="100">100</option>
1081
- <option value="250">250</option>
1082
- <option value="500">500</option>
1083
- <option value="1000">1000</option>
1084
- </select>
1085
- </div>
1086
- <div class="form-group" id="batchSizeGroup">
1087
- <label for="batchSizeSelect">Batch Size</label>
1088
- <select id="batchSizeSelect" class="form-select">
1089
- <option value="32">32</option>
1090
- <option value="64">64</option>
1091
- <option value="128">128</option>
1092
- <option value="256" selected>256 (default)</option>
1093
- <option value="512">512</option>
1094
- <option value="1024">1024</option>
1095
- <option value="2048">2048</option>
1096
- <option value="4096">4096</option>
1097
- <option value="8192">8192</option>
1098
- <option value="16384">16384</option>
1099
- </select>
1100
- </div>
1101
- <div class="form-group" id="apiKeyGroup" style="display: none;">
1102
- <label for="apiKeyInput" id="apiKeyLabel">API Key</label>
1103
- <input type="password" id="apiKeyInput" class="form-input" placeholder="" />
1104
- <div class="form-hint" id="apiKeyHint"></div>
1105
- </div>
1106
- <div class="form-actions">
1107
- <button type="button" id="saveEmbeddingBtn" class="btn btn-primary">Save Settings</button>
1108
- <span id="saveStatus" class="save-status"></span>
1109
- </div>
1110
- </div>
1111
- </div>
1112
-
1113
- <!-- Dashboard Settings Card -->
1114
- <div class="card">
1115
- <div class="card-header">
1116
- <span class="card-title">Dashboard Settings</span>
1117
- <span class="badge" id="dashboardBadge">Enabled</span>
1118
- </div>
1119
- <div class="stat">
1120
- <div class="stat-label">Auto-Start on MCP Launch</div>
1121
- <div class="stat-value small" id="dashboardEnabled">-</div>
1122
- </div>
1123
- <div class="settings-form" id="dashboardSettingsForm">
1124
- <div class="form-group">
1125
- <label for="dashboardEnabledSelect">Dashboard Auto-Start</label>
1126
- <select id="dashboardEnabledSelect" class="form-select">
1127
- <option value="true">Enabled (auto-start with MCP server)</option>
1128
- <option value="false">Disabled (manual start only)</option>
1129
- </select>
1130
- </div>
1131
- <div class="form-hint">
1132
- When disabled, use the <code>open_dashboard</code> MCP tool to start manually.
1133
- </div>
1134
- <div class="form-actions">
1135
- <button type="button" id="saveDashboardBtn" class="btn btn-primary" style="display: none;">Save Settings</button>
1136
- <span id="saveDashboardStatus" class="save-status"></span>
1137
- </div>
1138
- </div>
1139
- </div>
1140
-
1141
- <!-- Configuration Card -->
1142
- <div class="card">
1143
- <div class="card-header">
1144
- <span class="card-title">Configuration</span>
1145
- </div>
1146
- <div class="stat">
1147
- <div class="stat-label">Project Path</div>
1148
- <div class="stat-value small" id="projectPath">-</div>
1149
- </div>
1150
- <div class="stat">
1151
- <div class="stat-label">Chunk Size</div>
1152
- <div class="stat-value small" id="chunkSize">-</div>
1153
- </div>
1154
- <div class="stat">
1155
- <div class="stat-label">Search Weights</div>
1156
- <div class="stat-value small" id="searchWeights">-</div>
1157
- </div>
1158
- <div class="stat">
1159
- <div class="stat-label">Include Patterns</div>
1160
- <div class="patterns-list" id="includePatterns">
1161
- <span class="pattern-tag">Loading...</span>
1162
- </div>
1163
- </div>
1164
- <div class="stat">
1165
- <div class="stat-label">Exclude Patterns</div>
1166
- <div class="patterns-list" id="excludePatterns">
1167
- <span class="pattern-tag exclude">Loading...</span>
1168
- </div>
1169
- </div>
1170
- </div>
1171
-
1172
1295
  <!-- Token Savings Card -->
1173
- <div class="card">
1296
+ <div class="card half-width compact">
1174
1297
  <div class="card-header">
1175
1298
  <span class="card-title">Token Savings</span>
1176
1299
  <span class="badge" id="savingsBadge">This Session</span>
@@ -1191,103 +1314,244 @@ export function getDashboardHTML() {
1191
1314
  <div class="stat-label">Operations Tracked</div>
1192
1315
  <div class="stat-value small" id="operationCount">0</div>
1193
1316
  </div>
1194
- <div class="form-hint" style="margin-top: 10px;">
1195
- Semantic search returns only relevant code chunks instead of entire files, saving context tokens.
1196
- </div>
1197
1317
  </div>
1318
+ </div>
1198
1319
 
1199
- <!-- Command Usage Card -->
1200
- <div class="card double-width">
1201
- <div class="card-header">
1202
- <span class="card-title">Command Usage</span>
1203
- <span class="badge" id="sessionBadge">This Session</span>
1320
+ <!-- Server Log Section -->
1321
+ <div class="log-section" id="logSection">
1322
+ <div class="log-header" id="logHeader">
1323
+ <svg class="log-toggle" id="logToggle" width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
1324
+ <path d="M6 4l4 4-4 4"/>
1325
+ </svg>
1326
+ <span class="log-title">Server Logs</span>
1327
+ <span class="badge" id="logCount">0</span>
1328
+ <div class="log-actions">
1329
+ <button type="button" id="clearLogsBtn">Clear</button>
1204
1330
  </div>
1205
- <div id="usageChartContainer">
1206
- <div class="usage-empty" id="usageEmpty">No commands executed yet</div>
1207
- <div id="chartWrapper">
1208
- <table class="charts-css column show-primary-axis data-spacing-5" id="usage-chart" style="display: none;">
1209
- <tbody id="usageChartBody"></tbody>
1210
- </table>
1331
+ </div>
1332
+ <div class="log-container" id="logContainer">
1333
+ <div class="log-empty" id="logEmpty">No logs yet. Logs will appear when indexing or other server operations occur.</div>
1334
+ </div>
1335
+ </div>
1336
+
1337
+ </div><!-- End Status Tab -->
1338
+
1339
+ <!-- Beads Tab -->
1340
+ <div id="tab-beads" class="tab-content">
1341
+ <div class="beads-section" id="beadsSection">
1342
+ <div class="beads-header">
1343
+ <div class="beads-header-left">
1344
+ <svg class="beads-logo" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1345
+ <circle cx="12" cy="5" r="3"/>
1346
+ <circle cx="12" cy="12" r="3"/>
1347
+ <circle cx="12" cy="19" r="3"/>
1348
+ <line x1="12" y1="8" x2="12" y2="9"/>
1349
+ <line x1="12" y1="15" x2="12" y2="16"/>
1350
+ </svg>
1351
+ <span class="beads-title">Beads Issue Tracker</span>
1352
+ <span class="badge success" id="beadsBadge" style="display: none;">Active</span>
1211
1353
  </div>
1212
- <ul class="charts-css legend legend-inline legend-square" id="chartLegend" style="display: none;"></ul>
1213
- <div class="usage-total" id="usageTotal" style="display: none;">
1214
- <span class="usage-total-label">Total Commands</span>
1215
- <span class="usage-total-count" id="totalCount">0</span>
1354
+ <div class="beads-header-right" id="beadsHeaderStats" style="display: none;">
1355
+ <div class="beads-stats">
1356
+ <div class="beads-stat">
1357
+ <span class="beads-stat-value" id="beadsReadyCount">0</span>
1358
+ <span class="beads-stat-label">Ready</span>
1359
+ </div>
1360
+ <div class="beads-stat">
1361
+ <span class="beads-stat-value" id="beadsOpenCount">0</span>
1362
+ <span class="beads-stat-label">Open</span>
1363
+ </div>
1364
+ <div class="beads-stat">
1365
+ <span class="beads-stat-value" id="beadsTotalCount">0</span>
1366
+ <span class="beads-stat-label">Total</span>
1367
+ </div>
1368
+ </div>
1369
+ <div class="beads-daemon-status" id="beadsDaemonStatus">
1370
+ <div class="status-dot" id="beadsDaemonDot"></div>
1371
+ <span id="beadsDaemonText">Daemon status unknown</span>
1372
+ </div>
1373
+ </div>
1374
+ </div>
1375
+ <div id="beadsUnavailable" class="beads-unavailable">
1376
+ <p>Beads is not configured for this project.</p>
1377
+ <p style="margin-top: 8px; font-size: 13px;">Visit <a href="https://github.com/nicholaspsmith/beads" target="_blank" style="color: var(--accent-blue);">github.com/nicholaspsmith/beads</a> to learn more.</p>
1378
+ </div>
1379
+ <div id="beadsContent" style="display: none;">
1380
+ <div class="card">
1381
+ <div class="card-header">
1382
+ <span class="card-title">Ready Tasks</span>
1383
+ <span class="badge" id="readyTasksBadge">0 tasks</span>
1384
+ </div>
1385
+ <div class="beads-issues" id="beadsIssuesList">
1386
+ <div class="beads-empty">No ready tasks</div>
1387
+ </div>
1216
1388
  </div>
1217
1389
  </div>
1218
1390
  </div>
1219
- </div>
1391
+ </div><!-- End Beads Tab -->
1220
1392
 
1221
- <!-- Beads Section -->
1222
- <div class="beads-section" id="beadsSection" style="display: none;">
1223
- <div class="beads-header">
1224
- <svg class="beads-logo" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
1225
- <circle cx="12" cy="5" r="3"/>
1226
- <circle cx="12" cy="12" r="3"/>
1227
- <circle cx="12" cy="19" r="3"/>
1228
- <line x1="12" y1="8" x2="12" y2="9"/>
1229
- <line x1="12" y1="15" x2="12" y2="16"/>
1230
- </svg>
1231
- <span class="beads-title">Beads Issue Tracker</span>
1232
- </div>
1393
+ <!-- Settings Tab -->
1394
+ <div id="tab-settings" class="tab-content">
1233
1395
  <div class="grid">
1396
+ <!-- Embedding Backend Card -->
1234
1397
  <div class="card">
1235
1398
  <div class="card-header">
1236
- <span class="card-title">Status</span>
1237
- <span class="badge success" id="beadsBadge">Active</span>
1399
+ <span class="card-title">Embedding Backend</span>
1400
+ <span class="badge" id="embeddingStatus">-</span>
1401
+ </div>
1402
+ <div class="stat">
1403
+ <div class="stat-label">Current Backend</div>
1404
+ <div class="stat-value small" id="embeddingBackend">-</div>
1405
+ </div>
1406
+ <div class="stat">
1407
+ <div class="stat-label">Index Path</div>
1408
+ <div class="stat-value small" id="indexPath">-</div>
1238
1409
  </div>
1239
- <div class="beads-stats">
1240
- <div class="beads-stat">
1241
- <span class="beads-stat-value" id="beadsReadyCount">0</span>
1242
- <span class="beads-stat-label">Ready</span>
1410
+ <div class="settings-form" id="embeddingSettingsForm">
1411
+ <div class="form-group">
1412
+ <label for="backendSelect">Select Backend</label>
1413
+ <select id="backendSelect" class="form-select">
1414
+ <option value="ollama">Ollama (local)</option>
1415
+ <option value="gemini" selected>Google Gemini (free - requires API key)</option>
1416
+ </select>
1243
1417
  </div>
1244
- <div class="beads-stat">
1245
- <span class="beads-stat-value" id="beadsOpenCount">0</span>
1246
- <span class="beads-stat-label">Open</span>
1418
+ <div class="form-group" id="ollamaSettingsGroup">
1419
+ <label for="concurrencySelect">Ollama Concurrency</label>
1420
+ <select id="concurrencySelect" class="form-select">
1421
+ <option value="1" selected>1 (default)</option>
1422
+ <option value="2">2</option>
1423
+ <option value="3">3</option>
1424
+ <option value="5">5</option>
1425
+ <option value="10">10</option>
1426
+ <option value="25">25</option>
1427
+ <option value="50">50</option>
1428
+ <option value="100">100</option>
1429
+ <option value="250">250</option>
1430
+ <option value="500">500</option>
1431
+ <option value="1000">1000</option>
1432
+ </select>
1247
1433
  </div>
1248
- <div class="beads-stat">
1249
- <span class="beads-stat-value" id="beadsTotalCount">0</span>
1250
- <span class="beads-stat-label">Total</span>
1434
+ <div class="form-group" id="batchSizeGroup">
1435
+ <label for="batchSizeSelect">Batch Size</label>
1436
+ <select id="batchSizeSelect" class="form-select">
1437
+ <option value="32">32</option>
1438
+ <option value="64">64</option>
1439
+ <option value="128">128</option>
1440
+ <option value="256" selected>256 (default)</option>
1441
+ <option value="512">512</option>
1442
+ <option value="1024">1024</option>
1443
+ <option value="2048">2048</option>
1444
+ <option value="4096">4096</option>
1445
+ <option value="8192">8192</option>
1446
+ <option value="16384">16384</option>
1447
+ </select>
1448
+ </div>
1449
+ <div class="form-group" id="apiKeyGroup" style="display: none;">
1450
+ <label for="apiKeyInput" id="apiKeyLabel">API Key</label>
1451
+ <input type="password" id="apiKeyInput" class="form-input" placeholder="" />
1452
+ <div class="form-hint" id="apiKeyHint"></div>
1453
+ </div>
1454
+ <div class="form-actions">
1455
+ <button type="button" id="saveEmbeddingBtn" class="btn btn-primary">Save Settings</button>
1456
+ <span id="saveStatus" class="save-status"></span>
1251
1457
  </div>
1252
1458
  </div>
1253
- <div class="beads-daemon-status" id="beadsDaemonStatus">
1254
- <div class="status-dot" id="beadsDaemonDot"></div>
1255
- <span id="beadsDaemonText">Daemon status unknown</span>
1459
+ </div>
1460
+
1461
+ <!-- Dashboard Settings Card -->
1462
+ <div class="card">
1463
+ <div class="card-header">
1464
+ <span class="card-title">Dashboard Settings</span>
1465
+ <span class="badge" id="dashboardBadge">Enabled</span>
1466
+ </div>
1467
+ <div class="stat">
1468
+ <div class="stat-label">Auto-Start on MCP Launch</div>
1469
+ <div class="stat-value small" id="dashboardEnabled">-</div>
1256
1470
  </div>
1257
- <div class="stat" style="margin-top: 12px;" id="beadsSyncBranchStat">
1258
- <div class="stat-label">Sync Branch</div>
1259
- <div class="stat-value small" id="beadsSyncBranch">-</div>
1471
+ <div class="settings-form" id="dashboardSettingsForm">
1472
+ <div class="form-group">
1473
+ <label for="dashboardEnabledSelect">Dashboard Auto-Start</label>
1474
+ <select id="dashboardEnabledSelect" class="form-select">
1475
+ <option value="true">Enabled (auto-start with MCP server)</option>
1476
+ <option value="false">Disabled (manual start only)</option>
1477
+ </select>
1478
+ </div>
1479
+ <div class="form-hint">
1480
+ When disabled, use the <code>open_dashboard</code> MCP tool to start manually.
1481
+ </div>
1482
+ <div class="form-actions">
1483
+ <button type="button" id="saveDashboardBtn" class="btn btn-primary" style="display: none;">Save Settings</button>
1484
+ <span id="saveDashboardStatus" class="save-status"></span>
1485
+ </div>
1260
1486
  </div>
1261
1487
  </div>
1262
1488
 
1263
- <div class="card" style="grid-column: span 2;">
1489
+ <!-- Search Weights Card -->
1490
+ <div class="card">
1264
1491
  <div class="card-header">
1265
- <span class="card-title">Ready Tasks</span>
1266
- <span class="badge" id="readyTasksBadge">0 tasks</span>
1492
+ <span class="card-title">Search Weights</span>
1493
+ </div>
1494
+ <div class="stat">
1495
+ <div class="stat-label">Semantic vs Keyword Balance</div>
1496
+ <div class="slider-value" id="weightsDisplay">Semantic: 70%, Keyword: 30%</div>
1497
+ </div>
1498
+ <div class="form-group" style="margin-top: 16px;">
1499
+ <input type="range" id="weightsSlider" class="form-slider" min="0" max="100" value="70">
1500
+ <div class="slider-labels">
1501
+ <span>100% Keyword</span>
1502
+ <span>100% Semantic</span>
1503
+ </div>
1504
+ </div>
1505
+ <div class="form-hint" style="margin-top: 12px;">
1506
+ Semantic search finds conceptually similar code. Keyword search matches exact terms.
1267
1507
  </div>
1268
- <div class="beads-issues" id="beadsIssuesList">
1269
- <div class="beads-empty">No ready tasks</div>
1508
+ <div class="form-actions">
1509
+ <button type="button" id="saveWeightsBtn" class="btn btn-primary" style="display: none;">Save Weights</button>
1510
+ <span id="saveWeightsStatus" class="save-status"></span>
1270
1511
  </div>
1271
1512
  </div>
1272
- </div>
1273
- </div>
1274
1513
 
1275
- <!-- Server Log Section -->
1276
- <div class="log-section" id="logSection">
1277
- <div class="log-header" id="logHeader">
1278
- <svg class="log-toggle" id="logToggle" width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
1279
- <path d="M6 4l4 4-4 4"/>
1280
- </svg>
1281
- <span class="log-title">Server Logs</span>
1282
- <span class="badge" id="logCount">0</span>
1283
- <div class="log-actions">
1284
- <button type="button" id="clearLogsBtn">Clear</button>
1514
+ <!-- File Patterns Card -->
1515
+ <div class="card">
1516
+ <div class="card-header">
1517
+ <span class="card-title">File Patterns</span>
1518
+ </div>
1519
+ <div class="patterns-section">
1520
+ <div class="patterns-group">
1521
+ <div class="patterns-group-header">
1522
+ <span class="patterns-group-title">Include</span>
1523
+ <span class="badge" id="includePatternsCount">0</span>
1524
+ </div>
1525
+ <div class="stat-label">Files matching these patterns will be indexed</div>
1526
+ <div class="chips-container" id="includePatternsChips">
1527
+ <span class="chips-empty">Loading...</span>
1528
+ </div>
1529
+ <div class="chip-input-group">
1530
+ <input type="text" id="includePatternInput" class="form-input" placeholder="e.g., **/*.ts">
1531
+ <button type="button" id="addIncludePatternBtn" class="btn btn-secondary">Add</button>
1532
+ </div>
1533
+ <span id="includePatternStatus" class="save-status"></span>
1534
+ </div>
1535
+ <div class="patterns-group">
1536
+ <div class="patterns-group-header">
1537
+ <span class="patterns-group-title">Exclude</span>
1538
+ <span class="badge" id="excludePatternsCount">0</span>
1539
+ </div>
1540
+ <div class="stat-label">Files matching these patterns will be skipped</div>
1541
+ <div class="chips-container" id="excludePatternsChips">
1542
+ <span class="chips-empty">Loading...</span>
1543
+ </div>
1544
+ <div class="chip-input-group">
1545
+ <input type="text" id="excludePatternInput" class="form-input" placeholder="e.g., **/node_modules/**">
1546
+ <button type="button" id="addExcludePatternBtn" class="btn btn-secondary">Add</button>
1547
+ </div>
1548
+ <span id="excludePatternStatus" class="save-status"></span>
1549
+ </div>
1550
+ </div>
1285
1551
  </div>
1286
1552
  </div>
1287
- <div class="log-container" id="logContainer">
1288
- <div class="log-empty" id="logEmpty">No logs yet. Logs will appear when indexing or other server operations occur.</div>
1289
- </div>
1290
- </div>
1553
+ </div><!-- End Settings Tab -->
1554
+
1291
1555
  </div>
1292
1556
 
1293
1557
  <script>
@@ -1313,6 +1577,33 @@ export function getDashboardHTML() {
1313
1577
  // Theme toggle button
1314
1578
  document.getElementById('themeToggle').addEventListener('click', toggleTheme);
1315
1579
 
1580
+ // Tab management
1581
+ function getStoredTab() {
1582
+ return localStorage.getItem('lance-context-tab') || 'status';
1583
+ }
1584
+
1585
+ function setActiveTab(tabId) {
1586
+ // Update buttons
1587
+ document.querySelectorAll('.tab-btn').forEach(btn => {
1588
+ btn.classList.toggle('active', btn.getAttribute('data-tab') === tabId);
1589
+ });
1590
+ // Update content
1591
+ document.querySelectorAll('.tab-content').forEach(content => {
1592
+ content.classList.toggle('active', content.id === 'tab-' + tabId);
1593
+ });
1594
+ localStorage.setItem('lance-context-tab', tabId);
1595
+ }
1596
+
1597
+ // Initialize tabs
1598
+ setActiveTab(getStoredTab());
1599
+
1600
+ // Tab click handlers
1601
+ document.querySelectorAll('.tab-btn').forEach(btn => {
1602
+ btn.addEventListener('click', () => {
1603
+ setActiveTab(btn.getAttribute('data-tab'));
1604
+ });
1605
+ });
1606
+
1316
1607
  // State
1317
1608
  let isConnected = false;
1318
1609
  let eventSource = null;
@@ -1331,11 +1622,6 @@ export function getDashboardHTML() {
1331
1622
  const indexPath = document.getElementById('indexPath');
1332
1623
  const fallbackBanner = document.getElementById('fallbackBanner');
1333
1624
  const fallbackContent = document.getElementById('fallbackContent');
1334
- const projectPath = document.getElementById('projectPath');
1335
- const chunkSize = document.getElementById('chunkSize');
1336
- const searchWeights = document.getElementById('searchWeights');
1337
- const includePatterns = document.getElementById('includePatterns');
1338
- const excludePatterns = document.getElementById('excludePatterns');
1339
1625
  const progressContainer = document.getElementById('progressContainer');
1340
1626
  const progressFill = document.getElementById('progressFill');
1341
1627
  const progressText = document.getElementById('progressText');
@@ -1685,6 +1971,228 @@ export function getDashboardHTML() {
1685
1971
  // Load dashboard settings on page load
1686
1972
  loadDashboardSettings();
1687
1973
 
1974
+ // ===== Search Weights Settings =====
1975
+ const weightsSlider = document.getElementById('weightsSlider');
1976
+ const weightsDisplay = document.getElementById('weightsDisplay');
1977
+ const saveWeightsBtn = document.getElementById('saveWeightsBtn');
1978
+ const saveWeightsStatus = document.getElementById('saveWeightsStatus');
1979
+ let savedSemanticWeight = 70;
1980
+
1981
+ function updateWeightsDisplay() {
1982
+ const semantic = parseInt(weightsSlider.value, 10);
1983
+ const keyword = 100 - semantic;
1984
+ weightsDisplay.textContent = 'Semantic: ' + semantic + '%, Keyword: ' + keyword + '%';
1985
+ }
1986
+
1987
+ function hasWeightsChanged() {
1988
+ return parseInt(weightsSlider.value, 10) !== savedSemanticWeight;
1989
+ }
1990
+
1991
+ function updateWeightsSaveButtonVisibility() {
1992
+ saveWeightsBtn.style.display = hasWeightsChanged() ? 'inline-block' : 'none';
1993
+ saveWeightsStatus.textContent = '';
1994
+ }
1995
+
1996
+ weightsSlider.addEventListener('input', function() {
1997
+ updateWeightsDisplay();
1998
+ updateWeightsSaveButtonVisibility();
1999
+ });
2000
+
2001
+ async function loadSearchWeights() {
2002
+ try {
2003
+ const response = await fetch('/api/search-weights');
2004
+ if (response.ok) {
2005
+ const weights = await response.json();
2006
+ const semantic = Math.round(weights.semanticWeight * 100);
2007
+ savedSemanticWeight = semantic;
2008
+ weightsSlider.value = semantic;
2009
+ updateWeightsDisplay();
2010
+ updateWeightsSaveButtonVisibility();
2011
+ }
2012
+ } catch (error) {
2013
+ console.error('Failed to load search weights:', error);
2014
+ }
2015
+ }
2016
+
2017
+ saveWeightsBtn.addEventListener('click', async function() {
2018
+ const semantic = parseInt(weightsSlider.value, 10) / 100;
2019
+ const keyword = 1 - semantic;
2020
+
2021
+ saveWeightsBtn.disabled = true;
2022
+ saveWeightsStatus.textContent = 'Saving...';
2023
+ saveWeightsStatus.className = 'save-status';
2024
+
2025
+ try {
2026
+ const response = await fetch('/api/search-weights', {
2027
+ method: 'PUT',
2028
+ headers: { 'Content-Type': 'application/json' },
2029
+ body: JSON.stringify({ semanticWeight: semantic, keywordWeight: keyword })
2030
+ });
2031
+
2032
+ const result = await response.json();
2033
+
2034
+ if (response.ok) {
2035
+ savedSemanticWeight = parseInt(weightsSlider.value, 10);
2036
+ saveWeightsStatus.textContent = 'Saved!';
2037
+ saveWeightsStatus.className = 'save-status success';
2038
+ updateWeightsSaveButtonVisibility();
2039
+ fetchData(); // Refresh config display
2040
+ } else {
2041
+ saveWeightsStatus.textContent = result.error || 'Failed to save';
2042
+ saveWeightsStatus.className = 'save-status error';
2043
+ }
2044
+ } catch (error) {
2045
+ saveWeightsStatus.textContent = 'Network error';
2046
+ saveWeightsStatus.className = 'save-status error';
2047
+ } finally {
2048
+ saveWeightsBtn.disabled = false;
2049
+ }
2050
+ });
2051
+
2052
+ loadSearchWeights();
2053
+
2054
+ // ===== Include/Exclude Patterns =====
2055
+ const includePatternsChips = document.getElementById('includePatternsChips');
2056
+ const excludePatternsChips = document.getElementById('excludePatternsChips');
2057
+ const includePatternsCount = document.getElementById('includePatternsCount');
2058
+ const excludePatternsCount = document.getElementById('excludePatternsCount');
2059
+ const includePatternInput = document.getElementById('includePatternInput');
2060
+ const excludePatternInput = document.getElementById('excludePatternInput');
2061
+ const addIncludePatternBtn = document.getElementById('addIncludePatternBtn');
2062
+ const addExcludePatternBtn = document.getElementById('addExcludePatternBtn');
2063
+ const includePatternStatus = document.getElementById('includePatternStatus');
2064
+ const excludePatternStatus = document.getElementById('excludePatternStatus');
2065
+
2066
+ function renderPatternChips(container, patterns, type, countBadge) {
2067
+ // Clear container safely
2068
+ while (container.firstChild) {
2069
+ container.removeChild(container.firstChild);
2070
+ }
2071
+
2072
+ if (!patterns || patterns.length === 0) {
2073
+ const empty = document.createElement('span');
2074
+ empty.className = 'chips-empty';
2075
+ empty.textContent = 'No patterns configured';
2076
+ container.appendChild(empty);
2077
+ countBadge.textContent = '0';
2078
+ return;
2079
+ }
2080
+
2081
+ countBadge.textContent = String(patterns.length);
2082
+
2083
+ patterns.forEach(function(pattern) {
2084
+ const chip = document.createElement('span');
2085
+ chip.className = type === 'exclude' ? 'chip exclude' : 'chip';
2086
+
2087
+ const patternText = document.createTextNode(pattern);
2088
+ chip.appendChild(patternText);
2089
+
2090
+ const removeBtn = document.createElement('button');
2091
+ removeBtn.type = 'button';
2092
+ removeBtn.className = 'chip-remove';
2093
+ removeBtn.title = 'Remove pattern';
2094
+ removeBtn.textContent = '\\u00d7';
2095
+
2096
+ removeBtn.addEventListener('click', async function(e) {
2097
+ e.stopPropagation();
2098
+ await removePatternFromConfig(pattern, type);
2099
+ });
2100
+
2101
+ chip.appendChild(removeBtn);
2102
+ container.appendChild(chip);
2103
+ });
2104
+ }
2105
+
2106
+ async function removePatternFromConfig(pattern, type) {
2107
+ const statusEl = type === 'include' ? includePatternStatus : excludePatternStatus;
2108
+ statusEl.textContent = 'Removing...';
2109
+ statusEl.className = 'save-status';
2110
+
2111
+ try {
2112
+ const response = await fetch('/api/patterns', {
2113
+ method: 'DELETE',
2114
+ headers: { 'Content-Type': 'application/json' },
2115
+ body: JSON.stringify({ pattern, type })
2116
+ });
2117
+
2118
+ const result = await response.json();
2119
+
2120
+ if (response.ok) {
2121
+ statusEl.textContent = 'Removed!';
2122
+ statusEl.className = 'save-status success';
2123
+ setTimeout(function() { statusEl.textContent = ''; }, 2000);
2124
+ fetchData(); // Refresh patterns
2125
+ } else {
2126
+ statusEl.textContent = result.error || 'Failed to remove';
2127
+ statusEl.className = 'save-status error';
2128
+ }
2129
+ } catch (error) {
2130
+ statusEl.textContent = 'Network error';
2131
+ statusEl.className = 'save-status error';
2132
+ }
2133
+ }
2134
+
2135
+ async function addPatternToConfig(pattern, type) {
2136
+ const statusEl = type === 'include' ? includePatternStatus : excludePatternStatus;
2137
+ const inputEl = type === 'include' ? includePatternInput : excludePatternInput;
2138
+
2139
+ if (!pattern.trim()) {
2140
+ statusEl.textContent = 'Pattern is required';
2141
+ statusEl.className = 'save-status error';
2142
+ return;
2143
+ }
2144
+
2145
+ statusEl.textContent = 'Adding...';
2146
+ statusEl.className = 'save-status';
2147
+
2148
+ try {
2149
+ const response = await fetch('/api/patterns', {
2150
+ method: 'POST',
2151
+ headers: { 'Content-Type': 'application/json' },
2152
+ body: JSON.stringify({ pattern: pattern.trim(), type })
2153
+ });
2154
+
2155
+ const result = await response.json();
2156
+
2157
+ if (response.ok) {
2158
+ inputEl.value = '';
2159
+ statusEl.textContent = 'Added!';
2160
+ statusEl.className = 'save-status success';
2161
+ setTimeout(function() { statusEl.textContent = ''; }, 2000);
2162
+ fetchData(); // Refresh patterns
2163
+ } else {
2164
+ statusEl.textContent = result.error || 'Failed to add';
2165
+ statusEl.className = 'save-status error';
2166
+ }
2167
+ } catch (error) {
2168
+ statusEl.textContent = 'Network error';
2169
+ statusEl.className = 'save-status error';
2170
+ }
2171
+ }
2172
+
2173
+ addIncludePatternBtn.addEventListener('click', function() {
2174
+ addPatternToConfig(includePatternInput.value, 'include');
2175
+ });
2176
+
2177
+ addExcludePatternBtn.addEventListener('click', function() {
2178
+ addPatternToConfig(excludePatternInput.value, 'exclude');
2179
+ });
2180
+
2181
+ // Handle Enter key for pattern inputs
2182
+ includePatternInput.addEventListener('keydown', function(e) {
2183
+ if (e.key === 'Enter') {
2184
+ e.preventDefault();
2185
+ addPatternToConfig(includePatternInput.value, 'include');
2186
+ }
2187
+ });
2188
+
2189
+ excludePatternInput.addEventListener('keydown', function(e) {
2190
+ if (e.key === 'Enter') {
2191
+ e.preventDefault();
2192
+ addPatternToConfig(excludePatternInput.value, 'exclude');
2193
+ }
2194
+ });
2195
+
1688
2196
  // Format date
1689
2197
  function formatDate(isoString) {
1690
2198
  if (!isoString) return 'Never';
@@ -1750,8 +2258,6 @@ export function getDashboardHTML() {
1750
2258
 
1751
2259
  // Update config display
1752
2260
  function updateConfig(config) {
1753
- projectPath.textContent = config.projectPath || '-';
1754
-
1755
2261
  // Update project name in header
1756
2262
  if (config.projectName) {
1757
2263
  projectNameHeader.textContent = config.projectName;
@@ -1760,33 +2266,12 @@ export function getDashboardHTML() {
1760
2266
  projectNameHeader.textContent = config.projectPath.split('/').pop() || config.projectPath;
1761
2267
  }
1762
2268
 
1763
- if (config.chunking) {
1764
- chunkSize.textContent = config.chunking.maxLines + ' lines (overlap: ' + config.chunking.overlap + ')';
1765
- }
1766
-
1767
- if (config.search) {
1768
- searchWeights.textContent = 'Semantic: ' + (config.search.semanticWeight * 100) + '%, Keyword: ' + (config.search.keywordWeight * 100) + '%';
1769
- }
1770
-
1771
- // Update patterns
2269
+ // Update Settings tab pattern chips
1772
2270
  if (config.patterns) {
1773
- includePatterns.innerHTML = config.patterns
1774
- .slice(0, 10)
1775
- .map(p => '<span class="pattern-tag">' + escapeHtml(p) + '</span>')
1776
- .join('');
1777
- if (config.patterns.length > 10) {
1778
- includePatterns.innerHTML += '<span class="pattern-tag">+' + (config.patterns.length - 10) + ' more</span>';
1779
- }
2271
+ renderPatternChips(includePatternsChips, config.patterns, 'include', includePatternsCount);
1780
2272
  }
1781
-
1782
2273
  if (config.excludePatterns) {
1783
- excludePatterns.innerHTML = config.excludePatterns
1784
- .slice(0, 6)
1785
- .map(p => '<span class="pattern-tag exclude">' + escapeHtml(p) + '</span>')
1786
- .join('');
1787
- if (config.excludePatterns.length > 6) {
1788
- excludePatterns.innerHTML += '<span class="pattern-tag exclude">+' + (config.excludePatterns.length - 6) + ' more</span>';
1789
- }
2274
+ renderPatternChips(excludePatternsChips, config.excludePatterns, 'exclude', excludePatternsCount);
1790
2275
  }
1791
2276
  }
1792
2277
 
@@ -1945,17 +2430,27 @@ export function getDashboardHTML() {
1945
2430
  const beadsTotalCount = document.getElementById('beadsTotalCount');
1946
2431
  const beadsDaemonDot = document.getElementById('beadsDaemonDot');
1947
2432
  const beadsDaemonText = document.getElementById('beadsDaemonText');
1948
- const beadsSyncBranch = document.getElementById('beadsSyncBranch');
1949
2433
  const beadsIssuesList = document.getElementById('beadsIssuesList');
1950
2434
  const readyTasksBadge = document.getElementById('readyTasksBadge');
1951
2435
 
1952
2436
  function updateBeads(data) {
2437
+ const beadsUnavailable = document.getElementById('beadsUnavailable');
2438
+ const beadsContent = document.getElementById('beadsContent');
2439
+ const beadsHeaderStats = document.getElementById('beadsHeaderStats');
2440
+ const beadsBadgeEl = document.getElementById('beadsBadge');
2441
+
1953
2442
  if (!data.available) {
1954
- beadsSection.style.display = 'none';
2443
+ beadsUnavailable.style.display = 'block';
2444
+ beadsContent.style.display = 'none';
2445
+ beadsHeaderStats.style.display = 'none';
2446
+ beadsBadgeEl.style.display = 'none';
1955
2447
  return;
1956
2448
  }
1957
2449
 
1958
- beadsSection.style.display = 'block';
2450
+ beadsUnavailable.style.display = 'none';
2451
+ beadsContent.style.display = 'block';
2452
+ beadsHeaderStats.style.display = 'flex';
2453
+ beadsBadgeEl.style.display = 'inline-flex';
1959
2454
  beadsReadyCount.textContent = data.readyCount;
1960
2455
  beadsOpenCount.textContent = data.openCount;
1961
2456
  beadsTotalCount.textContent = data.issueCount;
@@ -1970,9 +2465,6 @@ export function getDashboardHTML() {
1970
2465
  beadsDaemonText.textContent = 'Daemon not running';
1971
2466
  }
1972
2467
 
1973
- // Sync branch
1974
- beadsSyncBranch.textContent = data.syncBranch || 'Not configured';
1975
-
1976
2468
  // Issues list
1977
2469
  if (data.issues && data.issues.length > 0) {
1978
2470
  let html = '';