backpack-viewer 0.2.13 → 0.2.15

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.
@@ -0,0 +1,67 @@
1
+ const SHORTCUTS = [
2
+ { key: "/", alt: "Ctrl+K", description: "Focus search" },
3
+ { key: "Ctrl+Z", description: "Undo" },
4
+ { key: "Ctrl+Shift+Z", description: "Redo" },
5
+ { key: "?", description: "Show this help" },
6
+ { key: "F", description: "Focus on selected / exit focus" },
7
+ { key: "Esc", description: "Exit focus / close panel" },
8
+ { key: "Click", description: "Select node" },
9
+ { key: "Ctrl+Click", description: "Multi-select nodes" },
10
+ { key: "Drag", description: "Pan canvas" },
11
+ { key: "Scroll", description: "Zoom in/out" },
12
+ ];
13
+ export function initShortcuts(container) {
14
+ const overlay = document.createElement("div");
15
+ overlay.className = "shortcuts-overlay hidden";
16
+ const modal = document.createElement("div");
17
+ modal.className = "shortcuts-modal";
18
+ const title = document.createElement("h3");
19
+ title.className = "shortcuts-title";
20
+ title.textContent = "Keyboard Shortcuts";
21
+ const list = document.createElement("div");
22
+ list.className = "shortcuts-list";
23
+ for (const s of SHORTCUTS) {
24
+ const row = document.createElement("div");
25
+ row.className = "shortcuts-row";
26
+ const keys = document.createElement("div");
27
+ keys.className = "shortcuts-keys";
28
+ const kbd = document.createElement("kbd");
29
+ kbd.textContent = s.key;
30
+ keys.appendChild(kbd);
31
+ if (s.alt) {
32
+ const or = document.createElement("span");
33
+ or.className = "shortcuts-or";
34
+ or.textContent = "or";
35
+ keys.appendChild(or);
36
+ const kbd2 = document.createElement("kbd");
37
+ kbd2.textContent = s.alt;
38
+ keys.appendChild(kbd2);
39
+ }
40
+ const desc = document.createElement("span");
41
+ desc.className = "shortcuts-desc";
42
+ desc.textContent = s.description;
43
+ row.appendChild(keys);
44
+ row.appendChild(desc);
45
+ list.appendChild(row);
46
+ }
47
+ const closeBtn = document.createElement("button");
48
+ closeBtn.className = "shortcuts-close";
49
+ closeBtn.textContent = "\u00d7";
50
+ modal.appendChild(closeBtn);
51
+ modal.appendChild(title);
52
+ modal.appendChild(list);
53
+ overlay.appendChild(modal);
54
+ container.appendChild(overlay);
55
+ function show() {
56
+ overlay.classList.remove("hidden");
57
+ }
58
+ function hide() {
59
+ overlay.classList.add("hidden");
60
+ }
61
+ closeBtn.addEventListener("click", hide);
62
+ overlay.addEventListener("click", (e) => {
63
+ if (e.target === overlay)
64
+ hide();
65
+ });
66
+ return { show, hide };
67
+ }
package/dist/style.css CHANGED
@@ -246,13 +246,102 @@ body {
246
246
  color: var(--text-dim);
247
247
  }
248
248
 
249
- /* --- Theme Toggle --- */
249
+ /* --- Top Bar --- */
250
250
 
251
- .theme-toggle {
251
+ .canvas-top-bar {
252
252
  position: absolute;
253
253
  top: 16px;
254
+ left: 16px;
254
255
  right: 16px;
255
256
  z-index: 30;
257
+ display: flex;
258
+ justify-content: space-between;
259
+ align-items: flex-start;
260
+ pointer-events: none;
261
+ }
262
+
263
+ .canvas-top-left,
264
+ .canvas-top-center,
265
+ .canvas-top-right {
266
+ pointer-events: auto;
267
+ display: flex;
268
+ align-items: center;
269
+ gap: 4px;
270
+ }
271
+
272
+ .canvas-top-center {
273
+ flex: 1;
274
+ justify-content: center;
275
+ }
276
+
277
+ /* --- Focus Indicator --- */
278
+
279
+ .focus-indicator {
280
+ display: flex;
281
+ align-items: center;
282
+ gap: 2px;
283
+ background: var(--bg-surface);
284
+ border: 1px solid rgba(212, 162, 127, 0.4);
285
+ border-radius: 8px;
286
+ padding: 4px 6px 4px 10px;
287
+ box-shadow: 0 2px 8px var(--shadow);
288
+ }
289
+
290
+ .focus-indicator-label {
291
+ font-size: 11px;
292
+ color: var(--accent);
293
+ font-weight: 500;
294
+ white-space: nowrap;
295
+ margin-right: 4px;
296
+ }
297
+
298
+ .focus-indicator-hops {
299
+ font-size: 11px;
300
+ color: var(--text-muted);
301
+ font-family: monospace;
302
+ min-width: 12px;
303
+ text-align: center;
304
+ }
305
+
306
+ .focus-indicator-btn {
307
+ background: none;
308
+ border: none;
309
+ color: var(--text-muted);
310
+ font-size: 14px;
311
+ cursor: pointer;
312
+ padding: 2px 4px;
313
+ line-height: 1;
314
+ border-radius: 4px;
315
+ transition: color 0.15s, background 0.15s;
316
+ }
317
+
318
+ .focus-indicator-btn:hover:not(:disabled) {
319
+ color: var(--text);
320
+ background: var(--bg-hover);
321
+ }
322
+
323
+ .focus-indicator-btn:disabled {
324
+ color: var(--text-dim);
325
+ cursor: default;
326
+ opacity: 0.3;
327
+ }
328
+
329
+ .focus-indicator-exit {
330
+ font-size: 16px;
331
+ margin-left: 2px;
332
+ }
333
+
334
+ .focus-indicator-exit:hover {
335
+ color: #ef4444 !important;
336
+ }
337
+
338
+ .info-focus-btn {
339
+ font-size: 14px;
340
+ }
341
+
342
+ /* --- Theme Toggle --- */
343
+
344
+ .theme-toggle {
256
345
  background: var(--bg-surface);
257
346
  border: 1px solid var(--border);
258
347
  border-radius: 8px;
@@ -274,18 +363,8 @@ body {
274
363
  /* --- Zoom Controls --- */
275
364
 
276
365
  .zoom-controls {
277
- position: absolute;
278
- top: 16px;
279
- right: 16px;
280
- z-index: 30;
281
366
  display: flex;
282
367
  gap: 4px;
283
- transition: right 0.2s ease;
284
- }
285
-
286
- /* Shift left when theme toggle is present (standalone viewer) */
287
- .theme-toggle ~ .zoom-controls {
288
- right: 58px;
289
368
  }
290
369
 
291
370
  .zoom-btn {
@@ -333,11 +412,7 @@ body {
333
412
  /* --- Search Overlay --- */
334
413
 
335
414
  .search-overlay {
336
- position: absolute;
337
- top: 16px;
338
- left: 50%;
339
- transform: translateX(-50%);
340
- z-index: 20;
415
+ position: relative;
341
416
  display: flex;
342
417
  flex-direction: column;
343
418
  align-items: center;
@@ -842,9 +917,14 @@ body {
842
917
  border-radius: 4px;
843
918
  padding: 3px 6px;
844
919
  font-size: 12px;
920
+ font-family: inherit;
845
921
  color: var(--text);
846
922
  flex: 1;
847
923
  min-width: 0;
924
+ resize: vertical;
925
+ overflow: hidden;
926
+ line-height: 1.4;
927
+ max-height: 300px;
848
928
  }
849
929
 
850
930
  .info-edit-input:focus {
@@ -953,9 +1033,451 @@ body {
953
1033
  background: rgba(239, 68, 68, 0.1);
954
1034
  }
955
1035
 
1036
+ /* --- Tools Pane (left side) --- */
1037
+
1038
+ .tools-pane-toggle {
1039
+ background: var(--bg-surface);
1040
+ border: 1px solid var(--border);
1041
+ border-radius: 8px;
1042
+ color: var(--text-muted);
1043
+ font-size: 18px;
1044
+ cursor: pointer;
1045
+ padding: 6px 10px;
1046
+ line-height: 1;
1047
+ transition: color 0.15s, border-color 0.15s, background 0.15s;
1048
+ box-shadow: 0 2px 8px var(--shadow);
1049
+ }
1050
+
1051
+ .tools-pane-toggle.hidden {
1052
+ display: none;
1053
+ }
1054
+
1055
+ .tools-pane-toggle:hover {
1056
+ color: var(--text);
1057
+ border-color: var(--text-muted);
1058
+ background: var(--bg-hover);
1059
+ }
1060
+
1061
+ .tools-pane-toggle.active {
1062
+ color: var(--accent);
1063
+ border-color: rgba(212, 162, 127, 0.4);
1064
+ }
1065
+
1066
+ .tools-pane-content {
1067
+ position: absolute;
1068
+ top: 56px;
1069
+ left: 16px;
1070
+ bottom: 16px;
1071
+ z-index: 20;
1072
+ width: 200px;
1073
+ overflow-y: auto;
1074
+ background: var(--bg-surface);
1075
+ border: 1px solid var(--border);
1076
+ border-radius: 10px;
1077
+ padding: 12px;
1078
+ box-shadow: 0 8px 32px var(--shadow);
1079
+ }
1080
+
1081
+ .tools-pane-content.hidden {
1082
+ display: none;
1083
+ }
1084
+
1085
+ .tools-pane-section {
1086
+ margin-bottom: 12px;
1087
+ }
1088
+
1089
+ .tools-pane-section:last-child {
1090
+ margin-bottom: 0;
1091
+ }
1092
+
1093
+ .tools-pane-heading {
1094
+ font-size: 10px;
1095
+ font-weight: 600;
1096
+ text-transform: uppercase;
1097
+ letter-spacing: 0.05em;
1098
+ color: var(--text-dim);
1099
+ margin-bottom: 6px;
1100
+ }
1101
+
1102
+ .tools-pane-row {
1103
+ display: flex;
1104
+ align-items: center;
1105
+ gap: 6px;
1106
+ padding: 3px 0;
1107
+ font-size: 12px;
1108
+ }
1109
+
1110
+ .tools-pane-dot {
1111
+ width: 6px;
1112
+ height: 6px;
1113
+ border-radius: 50%;
1114
+ flex-shrink: 0;
1115
+ }
1116
+
1117
+ .tools-pane-name {
1118
+ flex: 1;
1119
+ color: var(--text);
1120
+ overflow: hidden;
1121
+ text-overflow: ellipsis;
1122
+ white-space: nowrap;
1123
+ }
1124
+
1125
+ .tools-pane-count {
1126
+ color: var(--text-dim);
1127
+ font-size: 11px;
1128
+ flex-shrink: 0;
1129
+ }
1130
+
1131
+ .tools-pane-summary {
1132
+ display: flex;
1133
+ align-items: center;
1134
+ gap: 4px;
1135
+ font-size: 11px;
1136
+ color: var(--text-muted);
1137
+ padding-bottom: 10px;
1138
+ margin-bottom: 10px;
1139
+ border-bottom: 1px solid var(--border);
1140
+ }
1141
+
1142
+ .tools-pane-sep {
1143
+ color: var(--text-dim);
1144
+ }
1145
+
1146
+ .tools-pane-clickable {
1147
+ cursor: pointer;
1148
+ border-radius: 4px;
1149
+ padding: 3px 4px;
1150
+ margin: 0 -4px;
1151
+ transition: background 0.1s;
1152
+ }
1153
+
1154
+ .tools-pane-clickable:hover {
1155
+ background: var(--bg-hover);
1156
+ }
1157
+
1158
+ .tools-pane-clickable.active {
1159
+ background: var(--bg-hover);
1160
+ outline: 1px solid var(--border);
1161
+ }
1162
+
1163
+ .tools-pane-badge {
1164
+ font-size: 9px;
1165
+ color: var(--accent);
1166
+ flex-shrink: 0;
1167
+ opacity: 0.8;
1168
+ }
1169
+
1170
+ .tools-pane-issue .tools-pane-name {
1171
+ color: var(--text-muted);
1172
+ }
1173
+
1174
+ .tools-pane-more {
1175
+ font-size: 10px;
1176
+ color: var(--text-dim);
1177
+ padding: 4px 0 0;
1178
+ }
1179
+
1180
+ .tools-pane-edit {
1181
+ background: none;
1182
+ border: none;
1183
+ color: var(--text-dim);
1184
+ font-size: 11px;
1185
+ cursor: pointer;
1186
+ padding: 0 2px;
1187
+ opacity: 0;
1188
+ transition: opacity 0.1s, color 0.1s;
1189
+ flex-shrink: 0;
1190
+ }
1191
+
1192
+ .tools-pane-row:hover .tools-pane-edit {
1193
+ opacity: 1;
1194
+ }
1195
+
1196
+ .tools-pane-edit:hover {
1197
+ color: var(--accent);
1198
+ }
1199
+
1200
+ .tools-pane-focus-toggle {
1201
+ opacity: 0.4;
1202
+ font-size: 11px;
1203
+ }
1204
+
1205
+ .tools-pane-focus-active {
1206
+ opacity: 1 !important;
1207
+ color: var(--accent) !important;
1208
+ }
1209
+
1210
+ .tools-pane-focus-clear {
1211
+ margin-top: 4px;
1212
+ border-top: 1px solid var(--border);
1213
+ padding-top: 6px;
1214
+ }
1215
+
1216
+ .tools-pane-editing {
1217
+ background: none !important;
1218
+ }
1219
+
1220
+ .tools-pane-inline-input {
1221
+ width: 100%;
1222
+ background: var(--bg);
1223
+ border: 1px solid var(--accent);
1224
+ border-radius: 4px;
1225
+ color: var(--text);
1226
+ font-size: 12px;
1227
+ padding: 2px 6px;
1228
+ outline: none;
1229
+ }
1230
+
1231
+ .tools-pane-slider-row {
1232
+ display: flex;
1233
+ align-items: center;
1234
+ gap: 6px;
1235
+ padding: 4px 0;
1236
+ }
1237
+
1238
+ .tools-pane-slider-label {
1239
+ font-size: 11px;
1240
+ color: var(--text-muted);
1241
+ white-space: nowrap;
1242
+ min-width: 56px;
1243
+ }
1244
+
1245
+ .tools-pane-slider {
1246
+ flex: 1;
1247
+ min-width: 0;
1248
+ height: 4px;
1249
+ accent-color: var(--accent);
1250
+ cursor: pointer;
1251
+ }
1252
+
1253
+ .tools-pane-slider-value {
1254
+ font-size: 10px;
1255
+ color: var(--text-dim);
1256
+ min-width: 28px;
1257
+ text-align: right;
1258
+ font-family: monospace;
1259
+ }
1260
+
1261
+ .tools-pane-checkbox {
1262
+ width: 14px;
1263
+ height: 14px;
1264
+ accent-color: var(--accent);
1265
+ cursor: pointer;
1266
+ flex-shrink: 0;
1267
+ }
1268
+
1269
+ .tools-pane-export-row {
1270
+ display: flex;
1271
+ gap: 4px;
1272
+ margin-top: 6px;
1273
+ }
1274
+
1275
+ .tools-pane-export-btn {
1276
+ flex: 1;
1277
+ padding: 4px 8px;
1278
+ font-size: 11px;
1279
+ background: var(--bg);
1280
+ border: 1px solid var(--border);
1281
+ border-radius: 6px;
1282
+ color: var(--text-muted);
1283
+ cursor: pointer;
1284
+ transition: color 0.15s, border-color 0.15s;
1285
+ }
1286
+
1287
+ .tools-pane-export-btn:hover {
1288
+ color: var(--text);
1289
+ border-color: var(--text-muted);
1290
+ }
1291
+
1292
+ .tools-pane-empty {
1293
+ font-size: 11px;
1294
+ color: var(--text-dim);
1295
+ text-align: center;
1296
+ padding: 8px 0;
1297
+ }
1298
+
1299
+ /* --- Empty State --- */
1300
+
1301
+ .empty-state {
1302
+ position: absolute;
1303
+ inset: 0;
1304
+ display: flex;
1305
+ align-items: center;
1306
+ justify-content: center;
1307
+ z-index: 5;
1308
+ pointer-events: none;
1309
+ }
1310
+
1311
+ .empty-state.hidden {
1312
+ display: none;
1313
+ }
1314
+
1315
+ .empty-state-content {
1316
+ text-align: center;
1317
+ max-width: 420px;
1318
+ padding: 40px 24px;
1319
+ }
1320
+
1321
+ .empty-state-icon {
1322
+ color: var(--text-dim);
1323
+ margin-bottom: 16px;
1324
+ }
1325
+
1326
+ .empty-state-title {
1327
+ font-size: 18px;
1328
+ font-weight: 600;
1329
+ color: var(--text);
1330
+ margin-bottom: 8px;
1331
+ }
1332
+
1333
+ .empty-state-desc {
1334
+ font-size: 13px;
1335
+ color: var(--text-muted);
1336
+ line-height: 1.5;
1337
+ margin-bottom: 20px;
1338
+ }
1339
+
1340
+ .empty-state-setup {
1341
+ background: var(--bg-surface);
1342
+ border: 1px solid var(--border);
1343
+ border-radius: 8px;
1344
+ padding: 12px 16px;
1345
+ margin-bottom: 16px;
1346
+ }
1347
+
1348
+ .empty-state-label {
1349
+ font-size: 11px;
1350
+ color: var(--text-dim);
1351
+ margin-bottom: 8px;
1352
+ }
1353
+
1354
+ .empty-state-code {
1355
+ display: block;
1356
+ font-size: 12px;
1357
+ color: var(--accent);
1358
+ font-family: monospace;
1359
+ word-break: break-all;
1360
+ }
1361
+
1362
+ .empty-state-hint {
1363
+ font-size: 11px;
1364
+ color: var(--text-dim);
1365
+ }
1366
+
1367
+ .empty-state-hint kbd {
1368
+ padding: 1px 5px;
1369
+ border: 1px solid var(--border);
1370
+ border-radius: 3px;
1371
+ background: var(--bg-surface);
1372
+ font-family: monospace;
1373
+ font-size: 11px;
1374
+ }
1375
+
1376
+ /* --- Keyboard Shortcuts Modal --- */
1377
+
1378
+ .shortcuts-overlay {
1379
+ position: fixed;
1380
+ inset: 0;
1381
+ background: rgba(0, 0, 0, 0.5);
1382
+ display: flex;
1383
+ align-items: center;
1384
+ justify-content: center;
1385
+ z-index: 100;
1386
+ }
1387
+
1388
+ .shortcuts-overlay.hidden {
1389
+ display: none;
1390
+ }
1391
+
1392
+ .shortcuts-modal {
1393
+ background: var(--bg-surface);
1394
+ border: 1px solid var(--border);
1395
+ border-radius: 12px;
1396
+ padding: 24px;
1397
+ min-width: 300px;
1398
+ max-width: 400px;
1399
+ box-shadow: 0 16px 48px var(--shadow);
1400
+ position: relative;
1401
+ }
1402
+
1403
+ .shortcuts-close {
1404
+ position: absolute;
1405
+ top: 12px;
1406
+ right: 14px;
1407
+ background: none;
1408
+ border: none;
1409
+ color: var(--text-dim);
1410
+ font-size: 20px;
1411
+ cursor: pointer;
1412
+ padding: 0 4px;
1413
+ line-height: 1;
1414
+ }
1415
+
1416
+ .shortcuts-close:hover {
1417
+ color: var(--text);
1418
+ }
1419
+
1420
+ .shortcuts-title {
1421
+ font-size: 15px;
1422
+ font-weight: 600;
1423
+ color: var(--text);
1424
+ margin-bottom: 16px;
1425
+ }
1426
+
1427
+ .shortcuts-list {
1428
+ display: flex;
1429
+ flex-direction: column;
1430
+ gap: 8px;
1431
+ }
1432
+
1433
+ .shortcuts-row {
1434
+ display: flex;
1435
+ align-items: center;
1436
+ justify-content: space-between;
1437
+ gap: 12px;
1438
+ }
1439
+
1440
+ .shortcuts-keys {
1441
+ display: flex;
1442
+ align-items: center;
1443
+ gap: 4px;
1444
+ }
1445
+
1446
+ .shortcuts-keys kbd {
1447
+ padding: 2px 7px;
1448
+ border: 1px solid var(--border);
1449
+ border-radius: 4px;
1450
+ background: var(--bg);
1451
+ color: var(--text);
1452
+ font-size: 11px;
1453
+ font-family: monospace;
1454
+ }
1455
+
1456
+ .shortcuts-or {
1457
+ font-size: 10px;
1458
+ color: var(--text-dim);
1459
+ }
1460
+
1461
+ .shortcuts-desc {
1462
+ font-size: 12px;
1463
+ color: var(--text-muted);
1464
+ }
1465
+
956
1466
  /* --- Mobile --- */
957
1467
 
958
1468
  @media (max-width: 768px) {
1469
+ #app {
1470
+ flex-direction: column;
1471
+ }
1472
+
1473
+ #sidebar {
1474
+ width: 100%;
1475
+ min-width: 0;
1476
+ max-height: 35vh;
1477
+ border-right: none;
1478
+ border-bottom: 1px solid var(--border);
1479
+ }
1480
+
959
1481
  .info-panel {
960
1482
  top: auto;
961
1483
  bottom: 72px;
@@ -971,4 +1493,22 @@ body {
971
1493
  left: 0;
972
1494
  right: 0;
973
1495
  }
1496
+
1497
+ .canvas-top-bar {
1498
+ top: 8px;
1499
+ left: 8px;
1500
+ right: 8px;
1501
+ }
1502
+
1503
+ .tools-pane-content {
1504
+ top: 48px;
1505
+ left: 8px;
1506
+ bottom: 80px;
1507
+ width: 160px;
1508
+ max-width: calc(100vw - 24px);
1509
+ }
1510
+
1511
+ .tools-pane-edit {
1512
+ opacity: 0.6;
1513
+ }
974
1514
  }
@@ -0,0 +1,21 @@
1
+ import type { LearningGraphData } from "backpack-ontology";
2
+ interface ToolsPaneCallbacks {
3
+ onFilterByType: (type: string | null) => void;
4
+ onNavigateToNode: (nodeId: string) => void;
5
+ onFocusChange: (seedNodeIds: string[] | null) => void;
6
+ onRenameNodeType: (oldType: string, newType: string) => void;
7
+ onRenameEdgeType: (oldType: string, newType: string) => void;
8
+ onToggleEdgeLabels: (visible: boolean) => void;
9
+ onToggleTypeHulls: (visible: boolean) => void;
10
+ onToggleMinimap: (visible: boolean) => void;
11
+ onLayoutChange: (param: string, value: number) => void;
12
+ onExport: (format: "png" | "svg") => void;
13
+ onOpen?: () => void;
14
+ }
15
+ export declare function initToolsPane(container: HTMLElement, callbacks: ToolsPaneCallbacks): {
16
+ collapse(): void;
17
+ addToFocusSet(nodeIds: string[]): void;
18
+ clearFocusSet(): void;
19
+ setData(newData: LearningGraphData | null): void;
20
+ };
21
+ export {};