contactsheet 0.1.2 → 0.1.4
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/README.md +39 -4
- package/dist/canvas/app.js +437 -381
- package/dist/canvas/app.js.map +4 -4
- package/dist/cli.js +84 -40
- package/dist/cli.js.map +4 -4
- package/package.json +1 -1
package/dist/canvas/app.js
CHANGED
|
@@ -1296,220 +1296,210 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1296
1296
|
return viewport.classList.contains("is-panning");
|
|
1297
1297
|
}
|
|
1298
1298
|
|
|
1299
|
-
// src/canvas/
|
|
1300
|
-
var
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1299
|
+
// src/canvas/sidebar.ts
|
|
1300
|
+
var root;
|
|
1301
|
+
var listEl;
|
|
1302
|
+
var filter = "";
|
|
1303
|
+
var searchCollapsed = null;
|
|
1304
|
+
var uid = 0;
|
|
1305
|
+
function initSidebar() {
|
|
1306
|
+
root = qs("#cs-sidebar");
|
|
1307
|
+
root.textContent = "";
|
|
1308
|
+
const head = h("div", "cs-sb-head");
|
|
1309
|
+
const search = document.createElement("input");
|
|
1310
|
+
search.className = "cs-sb-search";
|
|
1311
|
+
search.type = "search";
|
|
1312
|
+
search.placeholder = "\u641C\u7D22\u753B\u677F\u2026";
|
|
1313
|
+
search.addEventListener("input", () => {
|
|
1314
|
+
filter = search.value.trim().toLowerCase();
|
|
1315
|
+
searchCollapsed = filter ? /* @__PURE__ */ new Set() : null;
|
|
1316
|
+
renderSidebar();
|
|
1317
|
+
});
|
|
1318
|
+
search.addEventListener("keydown", (e) => {
|
|
1319
|
+
e.stopPropagation();
|
|
1320
|
+
if (e.key === "Escape") {
|
|
1321
|
+
search.value = "";
|
|
1322
|
+
filter = "";
|
|
1323
|
+
searchCollapsed = null;
|
|
1324
|
+
renderSidebar();
|
|
1325
|
+
search.blur();
|
|
1326
|
+
}
|
|
1327
|
+
});
|
|
1328
|
+
head.append(h("span", "cs-sb-title", "\u753B\u677F"), search);
|
|
1329
|
+
listEl = h("div", "cs-sb-list");
|
|
1330
|
+
listEl.addEventListener("keydown", (e) => {
|
|
1331
|
+
if ((e.key === "Enter" || e.key === " ") && e.target.closest("button")) {
|
|
1332
|
+
e.stopPropagation();
|
|
1333
|
+
}
|
|
1334
|
+
});
|
|
1335
|
+
root.append(head, listEl);
|
|
1336
|
+
const toggle = qs("#cs-sb-toggle");
|
|
1337
|
+
toggle.addEventListener("click", () => {
|
|
1338
|
+
const closed = document.body.dataset.sidebar === "off";
|
|
1339
|
+
document.body.dataset.sidebar = closed ? "on" : "off";
|
|
1340
|
+
toggle.title = closed ? "\u6536\u8D77\u5217\u8868" : "\u5C55\u5F00\u5217\u8868";
|
|
1341
|
+
});
|
|
1342
|
+
renderSidebar();
|
|
1305
1343
|
}
|
|
1306
|
-
function
|
|
1307
|
-
|
|
1308
|
-
state.activeId = id;
|
|
1309
|
-
for (const b of state.boards.values()) b.el.classList.toggle("is-active", b.entry.id === id);
|
|
1344
|
+
function match(text) {
|
|
1345
|
+
return !filter || text.toLowerCase().includes(filter);
|
|
1310
1346
|
}
|
|
1311
|
-
function
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
if (mode === "review" && from !== "review") {
|
|
1320
|
-
state.modeBeforeReview = from;
|
|
1321
|
-
state.viewBeforeReview = { scale: state.scale, tx: state.tx, ty: state.ty };
|
|
1322
|
-
closeArgsPanel();
|
|
1323
|
-
fitActiveBoard();
|
|
1324
|
-
} else if (mode !== "review" && from === "review" && state.viewBeforeReview) {
|
|
1325
|
-
const v = state.viewBeforeReview;
|
|
1326
|
-
state.viewBeforeReview = null;
|
|
1327
|
-
setView(v.tx, v.ty, v.scale);
|
|
1347
|
+
function collapseKey() {
|
|
1348
|
+
return `cs-collapsed:${state.info?.projectRoot ?? "unknown"}`;
|
|
1349
|
+
}
|
|
1350
|
+
function collapsedSet() {
|
|
1351
|
+
try {
|
|
1352
|
+
return new Set(JSON.parse(localStorage.getItem(collapseKey()) ?? "[]"));
|
|
1353
|
+
} catch {
|
|
1354
|
+
return /* @__PURE__ */ new Set();
|
|
1328
1355
|
}
|
|
1329
|
-
setProbe(
|
|
1330
|
-
mode === "interact" ? "\u4EA4\u4E92\u6A21\u5F0F:\u76F4\u63A5\u64CD\u4F5C\u8FD9\u5757\u753B\u677F,Esc \u56DE\u6D4F\u89C8" : mode === "review" ? "\u8D70\u67E5\u6A21\u5F0F:Esc \u9000\u51FA" : "\u79FB\u5230\u753B\u677F\u4E0A\u53CD\u67E5\u5143\u7D20"
|
|
1331
|
-
);
|
|
1332
1356
|
}
|
|
1333
|
-
function
|
|
1334
|
-
|
|
1335
|
-
if (!board) return;
|
|
1336
|
-
const r = board.frameEl.getBoundingClientRect();
|
|
1337
|
-
const worldX = (r.left - state.tx) / state.scale;
|
|
1338
|
-
const worldY = (r.top - state.ty) / state.scale;
|
|
1339
|
-
const vw = window.innerWidth;
|
|
1340
|
-
const vh = window.innerHeight;
|
|
1341
|
-
const s = clamp(Math.min(vw / board.width, vh / board.height), MIN_SCALE, MAX_SCALE);
|
|
1342
|
-
setView((vw - board.width * s) / 2 - worldX * s, (vh - board.height * s) / 2 - worldY * s, s);
|
|
1357
|
+
function isCollapsed(key) {
|
|
1358
|
+
return searchCollapsed ? searchCollapsed.has(key) : collapsedSet().has(key);
|
|
1343
1359
|
}
|
|
1344
|
-
function
|
|
1345
|
-
if (
|
|
1346
|
-
|
|
1347
|
-
exitPinMode();
|
|
1348
|
-
return;
|
|
1349
|
-
}
|
|
1350
|
-
if (state.pinPending) {
|
|
1351
|
-
exitPinMode();
|
|
1352
|
-
return;
|
|
1353
|
-
}
|
|
1354
|
-
if (selectedPin()) {
|
|
1355
|
-
selectPin(null);
|
|
1356
|
-
return;
|
|
1357
|
-
}
|
|
1358
|
-
if (state.mode === "review") {
|
|
1359
|
-
setMode(state.modeBeforeReview === "review" ? "browse" : state.modeBeforeReview);
|
|
1360
|
+
function toggleCollapsed(key) {
|
|
1361
|
+
if (searchCollapsed) {
|
|
1362
|
+
if (!searchCollapsed.delete(key)) searchCollapsed.add(key);
|
|
1360
1363
|
return;
|
|
1361
1364
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
+
const s = collapsedSet();
|
|
1366
|
+
if (s.has(key)) s.delete(key);
|
|
1367
|
+
else s.add(key);
|
|
1368
|
+
try {
|
|
1369
|
+
localStorage.setItem(collapseKey(), JSON.stringify([...s]));
|
|
1370
|
+
} catch {
|
|
1365
1371
|
}
|
|
1366
|
-
clearSelection();
|
|
1367
|
-
closeArgsPanel();
|
|
1368
|
-
setProbe("\u79FB\u5230\u753B\u677F\u4E0A\u53CD\u67E5\u5143\u7D20");
|
|
1369
1372
|
}
|
|
1370
|
-
function
|
|
1371
|
-
const
|
|
1372
|
-
|
|
1373
|
-
const tag = el.tagName;
|
|
1374
|
-
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || el.isContentEditable === true) return true;
|
|
1375
|
-
return el.closest("#cs-sidebar, #cs-args, .cs-picker, #cs-topbar") !== null;
|
|
1373
|
+
function bulkMode(ids) {
|
|
1374
|
+
const shown = ids.filter((id) => !isHidden(id)).length;
|
|
1375
|
+
return { mode: shown === ids.length ? "all" : shown === 0 ? "none" : "some", shown };
|
|
1376
1376
|
}
|
|
1377
|
-
function
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1377
|
+
function setGroupHidden(ids, hide) {
|
|
1378
|
+
setHidden(ids, hide);
|
|
1379
|
+
}
|
|
1380
|
+
function focusedKey() {
|
|
1381
|
+
const el = document.activeElement;
|
|
1382
|
+
return el && listEl.contains(el) ? el.dataset.fk ?? null : null;
|
|
1383
|
+
}
|
|
1384
|
+
function restoreFocus(key) {
|
|
1385
|
+
if (!key) return;
|
|
1386
|
+
const el = listEl.querySelector(`[data-fk="${CSS.escape(key)}"]`);
|
|
1387
|
+
el?.focus({ preventScroll: true });
|
|
1388
|
+
}
|
|
1389
|
+
function renderSidebar() {
|
|
1390
|
+
if (!listEl) return;
|
|
1391
|
+
const keepFocus = focusedKey();
|
|
1392
|
+
listEl.textContent = "";
|
|
1393
|
+
uid = 0;
|
|
1394
|
+
const screens = state.entries.filter((e) => e.kind === "screen");
|
|
1395
|
+
const comps = state.entries.filter((e) => e.kind !== "screen");
|
|
1396
|
+
const visScreens = screens.filter((e) => match(e.exportName) || match(e.url ?? ""));
|
|
1397
|
+
if (visScreens.length) {
|
|
1398
|
+
const g = group("sec:screen", "\u9875\u9762", visScreens.map((e) => e.id), "cs-sb-section");
|
|
1399
|
+
for (const e of visScreens) g.body.appendChild(row(e.id, e.exportName, e.url ?? "", "screen"));
|
|
1400
|
+
listEl.appendChild(g.el);
|
|
1394
1401
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1402
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
1403
|
+
for (const e of comps) {
|
|
1404
|
+
if (!(match(e.exportName) || match(groupLabel(e.file)))) continue;
|
|
1405
|
+
const l = byFile.get(e.file);
|
|
1406
|
+
if (l) l.push(e);
|
|
1407
|
+
else byFile.set(e.file, [e]);
|
|
1401
1408
|
}
|
|
1402
|
-
if (
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1409
|
+
if (byFile.size) {
|
|
1410
|
+
const files = [...byFile.keys()].sort();
|
|
1411
|
+
const allIds = files.flatMap((f) => (byFile.get(f) ?? []).map((e) => e.id));
|
|
1412
|
+
const g = group("sec:component", "\u7EC4\u4EF6", allIds, "cs-sb-section");
|
|
1413
|
+
for (const file of files) {
|
|
1414
|
+
const list = byFile.get(file) ?? [];
|
|
1415
|
+
const fg = group(`file:${file}`, groupLabel(file), list.map((e) => e.id), "cs-sb-file");
|
|
1416
|
+
for (const e of list) fg.body.appendChild(row(e.id, e.exportName, "", "component"));
|
|
1417
|
+
g.body.appendChild(fg.el);
|
|
1406
1418
|
}
|
|
1407
|
-
|
|
1408
|
-
}
|
|
1409
|
-
if (e.key === "y" || e.key === "Y") {
|
|
1410
|
-
e.preventDefault();
|
|
1411
|
-
void copyContext();
|
|
1412
|
-
return;
|
|
1413
|
-
}
|
|
1414
|
-
if (e.key === "p") {
|
|
1415
|
-
e.preventDefault();
|
|
1416
|
-
void pushToClaudeCode();
|
|
1417
|
-
return;
|
|
1418
|
-
}
|
|
1419
|
-
if (e.key === "1") {
|
|
1420
|
-
e.preventDefault();
|
|
1421
|
-
fitAll();
|
|
1422
|
-
return;
|
|
1423
|
-
}
|
|
1424
|
-
if (e.key === "2") {
|
|
1425
|
-
e.preventDefault();
|
|
1426
|
-
fitBoard();
|
|
1427
|
-
return;
|
|
1428
|
-
}
|
|
1429
|
-
if (e.key === "0") {
|
|
1430
|
-
e.preventDefault();
|
|
1431
|
-
zoomReset();
|
|
1432
|
-
return;
|
|
1433
|
-
}
|
|
1434
|
-
if (e.key === "=" || e.key === "+") {
|
|
1435
|
-
e.preventDefault();
|
|
1436
|
-
zoomStep(1.25);
|
|
1437
|
-
return;
|
|
1419
|
+
listEl.appendChild(g.el);
|
|
1438
1420
|
}
|
|
1439
|
-
if (
|
|
1440
|
-
|
|
1441
|
-
zoomStep(0.8);
|
|
1421
|
+
if (!listEl.childElementCount) {
|
|
1422
|
+
listEl.appendChild(h("div", "cs-sb-empty", filter ? "\u6CA1\u6709\u5339\u914D\u7684\u753B\u677F" : "\u8FD8\u6CA1\u6709\u753B\u677F"));
|
|
1442
1423
|
}
|
|
1424
|
+
syncActive();
|
|
1425
|
+
restoreFocus(keepFocus);
|
|
1443
1426
|
}
|
|
1444
|
-
function
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
const
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
}
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
const
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
if (!el) return;
|
|
1484
|
-
if (state.pinPending) {
|
|
1485
|
-
openComposer(board, el, e.clientX, e.clientY);
|
|
1486
|
-
return;
|
|
1487
|
-
}
|
|
1488
|
-
if (state.mode !== "browse") return;
|
|
1489
|
-
const selector = buildSelector(el);
|
|
1490
|
-
const rel = relPos(el, e.clientX, e.clientY, board);
|
|
1491
|
-
showSelection(board, selector);
|
|
1492
|
-
state.selection = { artboardId: board.entry.id, selector, x: rel.x, y: rel.y, ts: Date.now() };
|
|
1493
|
-
setProbe(`\u5DF2\u9009\u4E2D ${board.entry.exportName} \u2192 ${selector}`);
|
|
1494
|
-
postSelection(state.selection).catch((err) => setProbe(`selection \u53D1\u9001\u5931\u8D25:${String(err)}`));
|
|
1495
|
-
}
|
|
1496
|
-
function onDblClick(e) {
|
|
1497
|
-
const board = boardOf(e.target);
|
|
1498
|
-
if (!board || !e.target.closest(".cs-ovl")) return;
|
|
1499
|
-
setMode("interact", board.entry.id);
|
|
1427
|
+
function group(key, label, ids, headCls) {
|
|
1428
|
+
const el = h("div", "cs-sb-group");
|
|
1429
|
+
if (headCls === "cs-sb-file") el.classList.add("is-file");
|
|
1430
|
+
const headEl = h("div", headCls);
|
|
1431
|
+
const body = h("div", "cs-sb-items");
|
|
1432
|
+
body.id = `cs-sb-g${++uid}`;
|
|
1433
|
+
const collapsed = isCollapsed(key);
|
|
1434
|
+
body.hidden = collapsed;
|
|
1435
|
+
const { mode, shown } = bulkMode(ids);
|
|
1436
|
+
const caret = h("button", "cs-sb-caret");
|
|
1437
|
+
caret.type = "button";
|
|
1438
|
+
caret.dataset.fk = `caret:${key}`;
|
|
1439
|
+
caret.setAttribute("aria-expanded", String(!collapsed));
|
|
1440
|
+
caret.setAttribute("aria-controls", body.id);
|
|
1441
|
+
caret.title = collapsed ? `\u5C55\u5F00\u300C${label}\u300D` : `\u6298\u53E0\u300C${label}\u300D`;
|
|
1442
|
+
const arrow = h("span", "cs-sb-arrow", "\u25B8");
|
|
1443
|
+
arrow.setAttribute("aria-hidden", "true");
|
|
1444
|
+
const count = h("span", "cs-sb-count", mode === "all" ? String(ids.length) : `${shown}/${ids.length}`);
|
|
1445
|
+
caret.append(arrow, h("span", "cs-sb-label", label), count);
|
|
1446
|
+
caret.addEventListener("click", () => {
|
|
1447
|
+
toggleCollapsed(key);
|
|
1448
|
+
renderSidebar();
|
|
1449
|
+
});
|
|
1450
|
+
const bulk = h("button", "cs-sb-bulk", mode === "all" ? "\u25CF" : mode === "none" ? "\u25CC" : "\u25D0");
|
|
1451
|
+
bulk.type = "button";
|
|
1452
|
+
bulk.dataset.fk = `bulk:${key}`;
|
|
1453
|
+
bulk.dataset.state = mode;
|
|
1454
|
+
bulk.setAttribute("aria-pressed", mode === "all" ? "true" : mode === "none" ? "false" : "mixed");
|
|
1455
|
+
const act = mode === "all" ? `\u9690\u85CF\u300C${label}\u300D\u4E0B\u5168\u90E8 ${ids.length} \u5757\u753B\u677F` : `\u663E\u793A\u300C${label}\u300D\u4E0B\u5168\u90E8 ${ids.length} \u5757\u753B\u677F`;
|
|
1456
|
+
bulk.setAttribute("aria-label", act);
|
|
1457
|
+
bulk.title = mode === "some" ? `${act}(\u5F53\u524D ${shown}/${ids.length} \u663E\u793A\u4E2D)` : act;
|
|
1458
|
+
bulk.addEventListener("click", (e) => {
|
|
1459
|
+
e.stopPropagation();
|
|
1460
|
+
setGroupHidden(ids, mode === "all");
|
|
1461
|
+
renderSidebar();
|
|
1462
|
+
});
|
|
1463
|
+
headEl.append(caret, bulk);
|
|
1464
|
+
el.append(headEl, body);
|
|
1465
|
+
return { el, body };
|
|
1500
1466
|
}
|
|
1501
|
-
function
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1467
|
+
function row(id, name, sub, kind) {
|
|
1468
|
+
const el = h("div", "cs-sb-row");
|
|
1469
|
+
el.dataset.id = id;
|
|
1470
|
+
const go = h("button", "cs-sb-go");
|
|
1471
|
+
go.type = "button";
|
|
1472
|
+
go.dataset.fk = `go:${id}`;
|
|
1473
|
+
go.dataset.kind = kind;
|
|
1474
|
+
go.title = sub ? `\u805A\u7126 ${name} (${sub})` : `\u805A\u7126 ${name}`;
|
|
1475
|
+
go.append(h("span", "cs-sb-dot"), h("span", "cs-sb-name", name));
|
|
1476
|
+
if (sub) go.appendChild(h("span", "cs-sb-route", sub));
|
|
1477
|
+
go.addEventListener("click", () => {
|
|
1478
|
+
if (isHidden(id)) toggleHidden(id);
|
|
1479
|
+
fitBoard(id);
|
|
1480
|
+
state.activeId = id;
|
|
1481
|
+
syncActive();
|
|
1511
1482
|
});
|
|
1512
|
-
|
|
1483
|
+
const eye = h("button", "cs-sb-eye", isHidden(id) ? "\u25CC" : "\u25CF");
|
|
1484
|
+
eye.type = "button";
|
|
1485
|
+
eye.dataset.fk = `eye:${id}`;
|
|
1486
|
+
eye.title = isHidden(id) ? "\u663E\u793A\u8FD9\u5757\u753B\u677F" : "\u4ECE\u5899\u4E0A\u9690\u85CF(\u4E0D\u5220,\u968F\u65F6\u70B9\u56DE\u6765)";
|
|
1487
|
+
eye.setAttribute("aria-label", isHidden(id) ? `\u663E\u793A ${name}` : `\u9690\u85CF ${name}`);
|
|
1488
|
+
eye.setAttribute("aria-pressed", String(!isHidden(id)));
|
|
1489
|
+
eye.addEventListener("click", (e) => {
|
|
1490
|
+
e.stopPropagation();
|
|
1491
|
+
toggleHidden(id);
|
|
1492
|
+
renderSidebar();
|
|
1493
|
+
});
|
|
1494
|
+
el.append(go, eye);
|
|
1495
|
+
if (isHidden(id)) el.classList.add("is-hidden-board");
|
|
1496
|
+
return el;
|
|
1497
|
+
}
|
|
1498
|
+
function syncActive() {
|
|
1499
|
+
if (!listEl) return;
|
|
1500
|
+
for (const r of listEl.querySelectorAll(".cs-sb-row")) {
|
|
1501
|
+
r.classList.toggle("is-active", r.dataset.id === state.activeId);
|
|
1502
|
+
}
|
|
1513
1503
|
}
|
|
1514
1504
|
|
|
1515
1505
|
// src/canvas/wall.ts
|
|
@@ -1539,6 +1529,7 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1539
1529
|
if (state.scale < MOUNT_MIN_SCALE) continue;
|
|
1540
1530
|
const id = r.target.dataset.id;
|
|
1541
1531
|
const board = id ? state.boards.get(id) : null;
|
|
1532
|
+
if (board?.el.hidden) continue;
|
|
1542
1533
|
if (board) requestMount(board);
|
|
1543
1534
|
io.unobserve(r.target);
|
|
1544
1535
|
}
|
|
@@ -1568,6 +1559,7 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1568
1559
|
heightWatchers.get(id)?.disconnect();
|
|
1569
1560
|
heightWatchers.delete(id);
|
|
1570
1561
|
}
|
|
1562
|
+
const defaultHid = markSeenAndDefaultHide(entries);
|
|
1571
1563
|
for (const [file, list] of byFile) {
|
|
1572
1564
|
const ids = list.map((e) => e.id);
|
|
1573
1565
|
const kept = (state.order.get(file) ?? []).filter((id) => ids.includes(id));
|
|
@@ -1587,6 +1579,7 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1587
1579
|
applyPositions();
|
|
1588
1580
|
if (n && !firstSync) toast(`\u65B0\u753B\u677F ${n} \u5757 \xB7 \u5DF2\u653E\u5230\u5899\u5E95\u90E8`, "info");
|
|
1589
1581
|
}
|
|
1582
|
+
if (defaultHid) toast("\u9875\u9762\u753B\u677F\u9ED8\u8BA4\u6536\u8D77(\u6574\u9875 iframe \u592A\u91CD),\u5728\u5DE6\u4FA7\u300C\u9875\u9762\u300D\u5206\u533A\u6309\u9700\u6253\u5F00", "info");
|
|
1590
1583
|
if (state.boards.size) firstSync = false;
|
|
1591
1584
|
renderSidebar();
|
|
1592
1585
|
}
|
|
@@ -1647,6 +1640,7 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1647
1640
|
}
|
|
1648
1641
|
attachResizeHandles(board);
|
|
1649
1642
|
attachBoardDrag(board);
|
|
1643
|
+
el.hidden = isHidden(entry.id);
|
|
1650
1644
|
applySize(board);
|
|
1651
1645
|
updateBoard(board, entry);
|
|
1652
1646
|
io.observe(el);
|
|
@@ -1729,6 +1723,34 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1729
1723
|
function hiddenKey() {
|
|
1730
1724
|
return `cs-hidden:${state.info?.projectRoot ?? "unknown"}`;
|
|
1731
1725
|
}
|
|
1726
|
+
function seenKey() {
|
|
1727
|
+
return `cs-seen:${state.info?.projectRoot ?? "unknown"}`;
|
|
1728
|
+
}
|
|
1729
|
+
function markSeenAndDefaultHide(entries) {
|
|
1730
|
+
let seen;
|
|
1731
|
+
try {
|
|
1732
|
+
seen = new Set(JSON.parse(localStorage.getItem(seenKey()) ?? "[]"));
|
|
1733
|
+
} catch {
|
|
1734
|
+
seen = /* @__PURE__ */ new Set();
|
|
1735
|
+
}
|
|
1736
|
+
const fresh = entries.filter((e) => !seen.has(e.id));
|
|
1737
|
+
if (fresh.length === 0) return false;
|
|
1738
|
+
const hid = hiddenSet();
|
|
1739
|
+
let hidSomething = false;
|
|
1740
|
+
for (const e of fresh) {
|
|
1741
|
+
seen.add(e.id);
|
|
1742
|
+
if (e.kind === "screen") {
|
|
1743
|
+
hid.add(e.id);
|
|
1744
|
+
hidSomething = true;
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
try {
|
|
1748
|
+
localStorage.setItem(seenKey(), JSON.stringify([...seen]));
|
|
1749
|
+
if (hidSomething) localStorage.setItem(hiddenKey(), JSON.stringify([...hid]));
|
|
1750
|
+
} catch {
|
|
1751
|
+
}
|
|
1752
|
+
return hidSomething;
|
|
1753
|
+
}
|
|
1732
1754
|
function hiddenSet() {
|
|
1733
1755
|
try {
|
|
1734
1756
|
return new Set(JSON.parse(localStorage.getItem(hiddenKey()) ?? "[]"));
|
|
@@ -1979,6 +2001,7 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1979
2001
|
}
|
|
1980
2002
|
}
|
|
1981
2003
|
function requestMount(board) {
|
|
2004
|
+
if (board.el.hidden) return;
|
|
1982
2005
|
if (board.iframe || mountQueue.includes(board)) return;
|
|
1983
2006
|
if (loadingCount >= MAX_LOADING) {
|
|
1984
2007
|
mountQueue.push(board);
|
|
@@ -2360,210 +2383,243 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
2360
2383
|
settleTimer = null;
|
|
2361
2384
|
}
|
|
2362
2385
|
|
|
2363
|
-
// src/canvas/
|
|
2364
|
-
var
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
function initSidebar() {
|
|
2370
|
-
root = qs("#cs-sidebar");
|
|
2371
|
-
root.textContent = "";
|
|
2372
|
-
const head = h("div", "cs-sb-head");
|
|
2373
|
-
const search = document.createElement("input");
|
|
2374
|
-
search.className = "cs-sb-search";
|
|
2375
|
-
search.type = "search";
|
|
2376
|
-
search.placeholder = "\u641C\u7D22\u753B\u677F\u2026";
|
|
2377
|
-
search.addEventListener("input", () => {
|
|
2378
|
-
filter = search.value.trim().toLowerCase();
|
|
2379
|
-
searchCollapsed = filter ? /* @__PURE__ */ new Set() : null;
|
|
2380
|
-
renderSidebar();
|
|
2381
|
-
});
|
|
2382
|
-
search.addEventListener("keydown", (e) => {
|
|
2383
|
-
e.stopPropagation();
|
|
2384
|
-
if (e.key === "Escape") {
|
|
2385
|
-
search.value = "";
|
|
2386
|
-
filter = "";
|
|
2387
|
-
searchCollapsed = null;
|
|
2388
|
-
renderSidebar();
|
|
2389
|
-
search.blur();
|
|
2390
|
-
}
|
|
2391
|
-
});
|
|
2392
|
-
head.append(h("span", "cs-sb-title", "\u753B\u677F"), search);
|
|
2393
|
-
listEl = h("div", "cs-sb-list");
|
|
2394
|
-
listEl.addEventListener("keydown", (e) => {
|
|
2395
|
-
if ((e.key === "Enter" || e.key === " ") && e.target.closest("button")) {
|
|
2396
|
-
e.stopPropagation();
|
|
2397
|
-
}
|
|
2398
|
-
});
|
|
2399
|
-
root.append(head, listEl);
|
|
2400
|
-
const toggle = qs("#cs-sb-toggle");
|
|
2401
|
-
toggle.addEventListener("click", () => {
|
|
2402
|
-
const closed = document.body.dataset.sidebar === "off";
|
|
2403
|
-
document.body.dataset.sidebar = closed ? "on" : "off";
|
|
2404
|
-
toggle.title = closed ? "\u6536\u8D77\u5217\u8868" : "\u5C55\u5F00\u5217\u8868";
|
|
2405
|
-
});
|
|
2406
|
-
renderSidebar();
|
|
2407
|
-
}
|
|
2408
|
-
function match(text) {
|
|
2409
|
-
return !filter || text.toLowerCase().includes(filter);
|
|
2386
|
+
// src/canvas/modes.ts
|
|
2387
|
+
var viewport2;
|
|
2388
|
+
function boardOf(target) {
|
|
2389
|
+
const el = target?.closest?.(".cs-board");
|
|
2390
|
+
if (!el?.dataset.id) return null;
|
|
2391
|
+
return state.boards.get(el.dataset.id) ?? null;
|
|
2410
2392
|
}
|
|
2411
|
-
function
|
|
2412
|
-
|
|
2393
|
+
function setActive(id) {
|
|
2394
|
+
if (state.activeId === id) return;
|
|
2395
|
+
state.activeId = id;
|
|
2396
|
+
for (const b of state.boards.values()) b.el.classList.toggle("is-active", b.entry.id === id);
|
|
2413
2397
|
}
|
|
2414
|
-
function
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2398
|
+
function setMode(mode, boardId) {
|
|
2399
|
+
if (mode !== "browse" && boardId) setActive(boardId);
|
|
2400
|
+
if (mode === "review" && !state.activeId) return;
|
|
2401
|
+
const from = state.mode;
|
|
2402
|
+
state.mode = mode;
|
|
2403
|
+
hideHover();
|
|
2404
|
+
if (mode !== "browse") clearSelection();
|
|
2405
|
+
syncHud();
|
|
2406
|
+
if (mode === "review" && from !== "review") {
|
|
2407
|
+
state.modeBeforeReview = from;
|
|
2408
|
+
state.viewBeforeReview = { scale: state.scale, tx: state.tx, ty: state.ty };
|
|
2409
|
+
closeArgsPanel();
|
|
2410
|
+
fitActiveBoard();
|
|
2411
|
+
} else if (mode !== "review" && from === "review" && state.viewBeforeReview) {
|
|
2412
|
+
const v = state.viewBeforeReview;
|
|
2413
|
+
state.viewBeforeReview = null;
|
|
2414
|
+
setView(v.tx, v.ty, v.scale);
|
|
2419
2415
|
}
|
|
2416
|
+
setProbe(
|
|
2417
|
+
mode === "interact" ? "\u4EA4\u4E92\u6A21\u5F0F:\u76F4\u63A5\u64CD\u4F5C\u8FD9\u5757\u753B\u677F,Esc \u56DE\u6D4F\u89C8" : mode === "review" ? "\u8D70\u67E5\u6A21\u5F0F:Esc \u9000\u51FA" : "\u79FB\u5230\u753B\u677F\u4E0A\u53CD\u67E5\u5143\u7D20"
|
|
2418
|
+
);
|
|
2420
2419
|
}
|
|
2421
|
-
function
|
|
2422
|
-
|
|
2420
|
+
function fitActiveBoard() {
|
|
2421
|
+
const board = state.activeId ? state.boards.get(state.activeId) : null;
|
|
2422
|
+
if (!board) return;
|
|
2423
|
+
const r = board.frameEl.getBoundingClientRect();
|
|
2424
|
+
const worldX = (r.left - state.tx) / state.scale;
|
|
2425
|
+
const worldY = (r.top - state.ty) / state.scale;
|
|
2426
|
+
const vw = window.innerWidth;
|
|
2427
|
+
const vh = window.innerHeight;
|
|
2428
|
+
const s = clamp(Math.min(vw / board.width, vh / board.height), MIN_SCALE, MAX_SCALE);
|
|
2429
|
+
setView((vw - board.width * s) / 2 - worldX * s, (vh - board.height * s) / 2 - worldY * s, s);
|
|
2423
2430
|
}
|
|
2424
|
-
function
|
|
2425
|
-
if (
|
|
2426
|
-
|
|
2431
|
+
function escape() {
|
|
2432
|
+
if (isComposing()) {
|
|
2433
|
+
closeComposer();
|
|
2434
|
+
exitPinMode();
|
|
2427
2435
|
return;
|
|
2428
2436
|
}
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
try {
|
|
2433
|
-
localStorage.setItem(collapseKey(), JSON.stringify([...s]));
|
|
2434
|
-
} catch {
|
|
2437
|
+
if (state.pinPending) {
|
|
2438
|
+
exitPinMode();
|
|
2439
|
+
return;
|
|
2435
2440
|
}
|
|
2441
|
+
if (selectedPin()) {
|
|
2442
|
+
selectPin(null);
|
|
2443
|
+
return;
|
|
2444
|
+
}
|
|
2445
|
+
if (state.mode === "review") {
|
|
2446
|
+
setMode(state.modeBeforeReview === "review" ? "browse" : state.modeBeforeReview);
|
|
2447
|
+
return;
|
|
2448
|
+
}
|
|
2449
|
+
if (state.mode === "interact") {
|
|
2450
|
+
setMode("browse");
|
|
2451
|
+
return;
|
|
2452
|
+
}
|
|
2453
|
+
clearSelection();
|
|
2454
|
+
closeArgsPanel();
|
|
2455
|
+
setProbe("\u79FB\u5230\u753B\u677F\u4E0A\u53CD\u67E5\u5143\u7D20");
|
|
2436
2456
|
}
|
|
2437
|
-
function
|
|
2438
|
-
const
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
}
|
|
2444
|
-
function focusedKey() {
|
|
2445
|
-
const el = document.activeElement;
|
|
2446
|
-
return el && listEl.contains(el) ? el.dataset.fk ?? null : null;
|
|
2447
|
-
}
|
|
2448
|
-
function restoreFocus(key) {
|
|
2449
|
-
if (!key) return;
|
|
2450
|
-
const el = listEl.querySelector(`[data-fk="${CSS.escape(key)}"]`);
|
|
2451
|
-
el?.focus({ preventScroll: true });
|
|
2457
|
+
function isEditable(target) {
|
|
2458
|
+
const el = target;
|
|
2459
|
+
if (!el) return false;
|
|
2460
|
+
const tag = el.tagName;
|
|
2461
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || el.isContentEditable === true) return true;
|
|
2462
|
+
return el.closest("#cs-sidebar, #cs-args, .cs-picker, #cs-topbar") !== null;
|
|
2452
2463
|
}
|
|
2453
|
-
function
|
|
2454
|
-
if (
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
uid = 0;
|
|
2458
|
-
const screens = state.entries.filter((e) => e.kind === "screen");
|
|
2459
|
-
const comps = state.entries.filter((e) => e.kind !== "screen");
|
|
2460
|
-
const visScreens = screens.filter((e) => match(e.exportName) || match(e.url ?? ""));
|
|
2461
|
-
if (visScreens.length) {
|
|
2462
|
-
const g = group("sec:screen", "\u9875\u9762", visScreens.map((e) => e.id), "cs-sb-section");
|
|
2463
|
-
for (const e of visScreens) g.body.appendChild(row(e.id, e.exportName, e.url ?? "", "screen"));
|
|
2464
|
-
listEl.appendChild(g.el);
|
|
2464
|
+
function onKeyDown(e) {
|
|
2465
|
+
if (e.key === "Escape") {
|
|
2466
|
+
escape();
|
|
2467
|
+
return;
|
|
2465
2468
|
}
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
if (
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2469
|
+
if (isEditable(e.target) || e.metaKey || e.ctrlKey || e.altKey) return;
|
|
2470
|
+
if (e.key === "Enter") {
|
|
2471
|
+
if (selectedPin()) {
|
|
2472
|
+
e.preventDefault();
|
|
2473
|
+
editSelectedPin();
|
|
2474
|
+
return;
|
|
2475
|
+
}
|
|
2476
|
+
if (state.mode !== "review" && state.activeId) {
|
|
2477
|
+
e.preventDefault();
|
|
2478
|
+
setMode("review", state.activeId);
|
|
2479
|
+
}
|
|
2480
|
+
return;
|
|
2472
2481
|
}
|
|
2473
|
-
if (
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
const list = byFile.get(file) ?? [];
|
|
2479
|
-
const fg = group(`file:${file}`, groupLabel(file), list.map((e) => e.id), "cs-sb-file");
|
|
2480
|
-
for (const e of list) fg.body.appendChild(row(e.id, e.exportName, "", "component"));
|
|
2481
|
-
g.body.appendChild(fg.el);
|
|
2482
|
+
if (e.key === "Backspace" || e.key === "Delete") {
|
|
2483
|
+
if (selectedPin()) {
|
|
2484
|
+
e.preventDefault();
|
|
2485
|
+
deleteSelectedPin();
|
|
2486
|
+
return;
|
|
2482
2487
|
}
|
|
2483
|
-
|
|
2488
|
+
if (state.mode === "browse" && state.activeId) {
|
|
2489
|
+
e.preventDefault();
|
|
2490
|
+
const b = state.boards.get(state.activeId);
|
|
2491
|
+
setHidden([state.activeId], true);
|
|
2492
|
+
clearSelection();
|
|
2493
|
+
setActive(null);
|
|
2494
|
+
toast(`\u5DF2\u6536\u8D77\u300C${b?.entry.exportName ?? state.activeId}\u300D\xB7 \u5DE6\u4FA7\u5217\u8868\u968F\u65F6\u70B9\u56DE\u6765`, "info");
|
|
2495
|
+
}
|
|
2496
|
+
return;
|
|
2484
2497
|
}
|
|
2485
|
-
if (
|
|
2486
|
-
|
|
2498
|
+
if (e.key === "c" || e.key === "C") {
|
|
2499
|
+
if (state.mode === "browse" && !state.pinPending) {
|
|
2500
|
+
e.preventDefault();
|
|
2501
|
+
enterPinMode();
|
|
2502
|
+
}
|
|
2503
|
+
return;
|
|
2504
|
+
}
|
|
2505
|
+
if (e.key === "y" || e.key === "Y") {
|
|
2506
|
+
e.preventDefault();
|
|
2507
|
+
void copyContext();
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
if (e.key === "p") {
|
|
2511
|
+
e.preventDefault();
|
|
2512
|
+
void pushToClaudeCode();
|
|
2513
|
+
return;
|
|
2514
|
+
}
|
|
2515
|
+
if (e.key === "1") {
|
|
2516
|
+
e.preventDefault();
|
|
2517
|
+
fitAll();
|
|
2518
|
+
return;
|
|
2519
|
+
}
|
|
2520
|
+
if (e.key === "2") {
|
|
2521
|
+
e.preventDefault();
|
|
2522
|
+
fitBoard();
|
|
2523
|
+
return;
|
|
2524
|
+
}
|
|
2525
|
+
if (e.key === "0") {
|
|
2526
|
+
e.preventDefault();
|
|
2527
|
+
zoomReset();
|
|
2528
|
+
return;
|
|
2529
|
+
}
|
|
2530
|
+
if (e.key === "=" || e.key === "+") {
|
|
2531
|
+
e.preventDefault();
|
|
2532
|
+
zoomStep(1.25);
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
if (e.key === "-" || e.key === "_") {
|
|
2536
|
+
e.preventDefault();
|
|
2537
|
+
zoomStep(0.8);
|
|
2487
2538
|
}
|
|
2488
|
-
syncActive();
|
|
2489
|
-
restoreFocus(keepFocus);
|
|
2490
2539
|
}
|
|
2491
|
-
function
|
|
2492
|
-
|
|
2493
|
-
if (headCls === "cs-sb-file") el.classList.add("is-file");
|
|
2494
|
-
const headEl = h("div", headCls);
|
|
2495
|
-
const body = h("div", "cs-sb-items");
|
|
2496
|
-
body.id = `cs-sb-g${++uid}`;
|
|
2497
|
-
const collapsed = isCollapsed(key);
|
|
2498
|
-
body.hidden = collapsed;
|
|
2499
|
-
const { mode, shown } = bulkMode(ids);
|
|
2500
|
-
const caret = h("button", "cs-sb-caret");
|
|
2501
|
-
caret.type = "button";
|
|
2502
|
-
caret.dataset.fk = `caret:${key}`;
|
|
2503
|
-
caret.setAttribute("aria-expanded", String(!collapsed));
|
|
2504
|
-
caret.setAttribute("aria-controls", body.id);
|
|
2505
|
-
caret.title = collapsed ? `\u5C55\u5F00\u300C${label}\u300D` : `\u6298\u53E0\u300C${label}\u300D`;
|
|
2506
|
-
const arrow = h("span", "cs-sb-arrow", "\u25B8");
|
|
2507
|
-
arrow.setAttribute("aria-hidden", "true");
|
|
2508
|
-
const count = h("span", "cs-sb-count", mode === "all" ? String(ids.length) : `${shown}/${ids.length}`);
|
|
2509
|
-
caret.append(arrow, h("span", "cs-sb-label", label), count);
|
|
2510
|
-
caret.addEventListener("click", () => {
|
|
2511
|
-
toggleCollapsed(key);
|
|
2512
|
-
renderSidebar();
|
|
2513
|
-
});
|
|
2514
|
-
const bulk = h("button", "cs-sb-bulk", mode === "all" ? "\u25CF" : mode === "none" ? "\u25CC" : "\u25D0");
|
|
2515
|
-
bulk.type = "button";
|
|
2516
|
-
bulk.dataset.fk = `bulk:${key}`;
|
|
2517
|
-
bulk.dataset.state = mode;
|
|
2518
|
-
bulk.setAttribute("aria-pressed", mode === "all" ? "true" : mode === "none" ? "false" : "mixed");
|
|
2519
|
-
const act = mode === "all" ? `\u9690\u85CF\u300C${label}\u300D\u4E0B\u5168\u90E8 ${ids.length} \u5757\u753B\u677F` : `\u663E\u793A\u300C${label}\u300D\u4E0B\u5168\u90E8 ${ids.length} \u5757\u753B\u677F`;
|
|
2520
|
-
bulk.setAttribute("aria-label", act);
|
|
2521
|
-
bulk.title = mode === "some" ? `${act}(\u5F53\u524D ${shown}/${ids.length} \u663E\u793A\u4E2D)` : act;
|
|
2522
|
-
bulk.addEventListener("click", (e) => {
|
|
2523
|
-
e.stopPropagation();
|
|
2524
|
-
setGroupHidden(ids, mode === "all");
|
|
2525
|
-
renderSidebar();
|
|
2526
|
-
});
|
|
2527
|
-
headEl.append(caret, bulk);
|
|
2528
|
-
el.append(headEl, body);
|
|
2529
|
-
return { el, body };
|
|
2540
|
+
function attachFrameKeys(doc) {
|
|
2541
|
+
doc.addEventListener("keydown", onKeyDown);
|
|
2530
2542
|
}
|
|
2531
|
-
function
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
const
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
eye.type = "button";
|
|
2549
|
-
eye.dataset.fk = `eye:${id}`;
|
|
2550
|
-
eye.title = isHidden(id) ? "\u663E\u793A\u8FD9\u5757\u753B\u677F" : "\u4ECE\u5899\u4E0A\u9690\u85CF(\u4E0D\u5220,\u968F\u65F6\u70B9\u56DE\u6765)";
|
|
2551
|
-
eye.setAttribute("aria-label", isHidden(id) ? `\u663E\u793A ${name}` : `\u9690\u85CF ${name}`);
|
|
2552
|
-
eye.setAttribute("aria-pressed", String(!isHidden(id)));
|
|
2553
|
-
eye.addEventListener("click", (e) => {
|
|
2554
|
-
e.stopPropagation();
|
|
2555
|
-
toggleHidden(id);
|
|
2556
|
-
renderSidebar();
|
|
2557
|
-
});
|
|
2558
|
-
el.append(go, eye);
|
|
2559
|
-
if (isHidden(id)) el.classList.add("is-hidden-board");
|
|
2560
|
-
return el;
|
|
2543
|
+
function onMouseMove(e) {
|
|
2544
|
+
if (isPanning()) return;
|
|
2545
|
+
const overlay = e.target?.closest?.(".cs-ovl");
|
|
2546
|
+
const board = overlay ? boardOf(e.target) : null;
|
|
2547
|
+
if (!board) {
|
|
2548
|
+
hideHover();
|
|
2549
|
+
return;
|
|
2550
|
+
}
|
|
2551
|
+
if (state.mode !== "browse") return;
|
|
2552
|
+
setActive(board.entry.id);
|
|
2553
|
+
const el = elementAt(board, e.clientX, e.clientY);
|
|
2554
|
+
if (!el) {
|
|
2555
|
+
hideHover();
|
|
2556
|
+
return;
|
|
2557
|
+
}
|
|
2558
|
+
showHover(board, el);
|
|
2559
|
+
setProbe(`${board.entry.exportName} ${describe(el)}`);
|
|
2561
2560
|
}
|
|
2562
|
-
function
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2561
|
+
function onClick(e) {
|
|
2562
|
+
const target = e.target;
|
|
2563
|
+
const title = target?.closest?.(".cs-board-title");
|
|
2564
|
+
if (title) {
|
|
2565
|
+
const board2 = boardOf(target);
|
|
2566
|
+
if (!board2) return;
|
|
2567
|
+
setActive(board2.entry.id);
|
|
2568
|
+
if (isArgsOpen(board2.entry.id)) closeArgsPanel();
|
|
2569
|
+
else openArgsPanel(board2);
|
|
2570
|
+
return;
|
|
2571
|
+
}
|
|
2572
|
+
const overlay = target?.closest?.(".cs-ovl");
|
|
2573
|
+
if (!overlay) return;
|
|
2574
|
+
const board = boardOf(target);
|
|
2575
|
+
if (!board) return;
|
|
2576
|
+
if (state.mode === "interact") {
|
|
2577
|
+
if (board.entry.id !== state.activeId) hintInactive(board);
|
|
2578
|
+
return;
|
|
2566
2579
|
}
|
|
2580
|
+
setActive(board.entry.id);
|
|
2581
|
+
const el = elementAt(board, e.clientX, e.clientY);
|
|
2582
|
+
if (!el) return;
|
|
2583
|
+
if (state.pinPending) {
|
|
2584
|
+
openComposer(board, el, e.clientX, e.clientY);
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
2587
|
+
if (state.mode !== "browse") return;
|
|
2588
|
+
const selector = buildSelector(el);
|
|
2589
|
+
const rel = relPos(el, e.clientX, e.clientY, board);
|
|
2590
|
+
showSelection(board, selector);
|
|
2591
|
+
state.selection = { artboardId: board.entry.id, selector, x: rel.x, y: rel.y, ts: Date.now() };
|
|
2592
|
+
setProbe(`\u5DF2\u9009\u4E2D ${board.entry.exportName} \u2192 ${selector}`);
|
|
2593
|
+
postSelection(state.selection).catch((err) => setProbe(`selection \u53D1\u9001\u5931\u8D25:${String(err)}`));
|
|
2594
|
+
}
|
|
2595
|
+
var lastHint = "";
|
|
2596
|
+
var lastHintAt = 0;
|
|
2597
|
+
function hintInactive(board) {
|
|
2598
|
+
const cur = state.boards.get(state.activeId ?? "")?.entry.exportName ?? "?";
|
|
2599
|
+
setProbe(`\u4EA4\u4E92\u4E2D\u7684\u662F\u300C${cur}\u300D\u2014\u2014 \u8981\u4EA4\u4E92\u8FD9\u5757\u8BF7\u53CC\u51FB\u5B83,Esc \u9000\u51FA\u4EA4\u4E92`);
|
|
2600
|
+
const now = Date.now();
|
|
2601
|
+
if (lastHint === board.entry.id && now - lastHintAt < 4e3) return;
|
|
2602
|
+
lastHint = board.entry.id;
|
|
2603
|
+
lastHintAt = now;
|
|
2604
|
+
toast(`\u300C${board.entry.exportName}\u300D\u672A\u6FC0\u6D3B \xB7 \u53CC\u51FB\u5B83\u5207\u6362\u4EA4\u4E92\u76EE\u6807`, "info");
|
|
2605
|
+
}
|
|
2606
|
+
function onDblClick(e) {
|
|
2607
|
+
const board = boardOf(e.target);
|
|
2608
|
+
if (!board || !e.target.closest(".cs-ovl")) return;
|
|
2609
|
+
setMode("interact", board.entry.id);
|
|
2610
|
+
}
|
|
2611
|
+
function initModes() {
|
|
2612
|
+
viewport2 = qs("#cs-viewport");
|
|
2613
|
+
viewport2.addEventListener("mousemove", onMouseMove);
|
|
2614
|
+
viewport2.addEventListener("mouseleave", hideHover);
|
|
2615
|
+
viewport2.addEventListener("click", onClick);
|
|
2616
|
+
viewport2.addEventListener("dblclick", onDblClick);
|
|
2617
|
+
document.addEventListener("keydown", onKeyDown);
|
|
2618
|
+
window.addEventListener("resize", () => {
|
|
2619
|
+
if (state.mode === "review") fitActiveBoard();
|
|
2620
|
+
else applyView();
|
|
2621
|
+
});
|
|
2622
|
+
syncHud();
|
|
2567
2623
|
}
|
|
2568
2624
|
|
|
2569
2625
|
// src/canvas/app.ts
|