contactsheet 0.1.4 → 0.1.5
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 +13 -0
- package/dist/canvas/app.js +149 -106
- package/dist/canvas/app.js.map +4 -4
- package/dist/canvas/style.css +31 -0
- package/dist/cli.js +61 -0
- package/dist/cli.js.map +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -271,6 +271,19 @@ iframe 的画布永远是不透明的,所以组件画板必然有一层底。
|
|
|
271
271
|
- 画板路由在生产环境不可访问:`next build` 的路由清单里它仍然在,但运行时一律 404(`notFound()` 守卫)。
|
|
272
272
|
想连清单都不进,`next build` 前跑一次 `contactsheet clean`。
|
|
273
273
|
|
|
274
|
+
## dev server 出问题时(画布会替你归因)
|
|
275
|
+
|
|
276
|
+
画布长开时,被代理的 `next dev` 自己可能出问题。外壳每 20 秒探测一次它的 SSR 路径,
|
|
277
|
+
连挂两次就在画布顶部拉**红色横幅**——看到横幅,坏的是 dev server,不是画布。两种实测指纹:
|
|
278
|
+
|
|
279
|
+
1. **所有路由秒回 500、连请求日志都不打**:`.next` 开发缓存被硬杀打坏(next/font 会进入
|
|
280
|
+
失败重试死循环,`.next` 疯狂膨胀)。重启救不回来,`rm -rf .next` 后再起即愈。
|
|
281
|
+
2. **静态资源 200、SSR 报错、API 挂死、worker 数暴涨**:dev server 僵死,重启 `next dev` 即愈。
|
|
282
|
+
|
|
283
|
+
外壳侧的配合:客户端中途放弃的请求(僵死期浏览器的重试洪水就是这样)会**向上游同步中断**——
|
|
284
|
+
否则每次放弃都在 dev server 里留一个永远"渲染中"的挂起请求,把它压得更死(这同时也是外壳自身的
|
|
285
|
+
fd 泄漏源,实测 300 次中断泄 300 个 fd,已修)。
|
|
286
|
+
|
|
274
287
|
## 卸载
|
|
275
288
|
|
|
276
289
|
```bash
|
package/dist/canvas/app.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
return req(`${BASE}/api/state`);
|
|
16
16
|
}
|
|
17
17
|
function fetchRegistry() {
|
|
18
|
-
return req(`${BASE}/registry
|
|
18
|
+
return req(`${BASE}/registry`, { signal: AbortSignal.timeout(15e3) });
|
|
19
19
|
}
|
|
20
20
|
function fetchAnnotations() {
|
|
21
21
|
return req(`${BASE}/api/annotations`);
|
|
@@ -72,24 +72,26 @@
|
|
|
72
72
|
}
|
|
73
73
|
if (ev.type === "registry") on.registry(ev.entries ?? []);
|
|
74
74
|
else if (ev.type === "annotations") on.annotations(ev.annotations ?? []);
|
|
75
|
+
else if (ev.type === "health") on.health(ev.ok, ev.detail);
|
|
75
76
|
};
|
|
76
77
|
es.addEventListener("registry", (e) => dispatch(e.data));
|
|
77
78
|
es.addEventListener("annotations", (e) => dispatch(e.data));
|
|
79
|
+
es.addEventListener("health", (e) => dispatch(e.data));
|
|
78
80
|
es.onmessage = (e) => dispatch(e.data);
|
|
79
81
|
return es;
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
// src/canvas/dom.ts
|
|
83
85
|
function h(tag, cls, text) {
|
|
84
|
-
const
|
|
85
|
-
if (cls)
|
|
86
|
-
if (text !== void 0)
|
|
87
|
-
return
|
|
86
|
+
const el2 = document.createElement(tag);
|
|
87
|
+
if (cls) el2.className = cls;
|
|
88
|
+
if (text !== void 0) el2.textContent = text;
|
|
89
|
+
return el2;
|
|
88
90
|
}
|
|
89
91
|
function qs(sel) {
|
|
90
|
-
const
|
|
91
|
-
if (!
|
|
92
|
-
return
|
|
92
|
+
const el2 = document.querySelector(sel);
|
|
93
|
+
if (!el2) throw new Error(`canvas: \u7F3A\u5C11\u9AA8\u67B6\u5143\u7D20 ${sel}`);
|
|
94
|
+
return el2;
|
|
93
95
|
}
|
|
94
96
|
function clamp(v, lo, hi) {
|
|
95
97
|
return v < lo ? lo : v > hi ? hi : v;
|
|
@@ -312,14 +314,14 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
312
314
|
return host;
|
|
313
315
|
}
|
|
314
316
|
function toast(text, kind = "info") {
|
|
315
|
-
const
|
|
316
|
-
ensureHost().appendChild(
|
|
317
|
-
requestAnimationFrame(() =>
|
|
317
|
+
const el2 = h("div", `cs-toast is-${kind}`, text);
|
|
318
|
+
ensureHost().appendChild(el2);
|
|
319
|
+
requestAnimationFrame(() => el2.classList.add("is-in"));
|
|
318
320
|
const life = kind === "error" ? 6e3 : kind === "warn" ? 4500 : 3e3;
|
|
319
321
|
setTimeout(() => {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
setTimeout(() =>
|
|
322
|
+
el2.classList.remove("is-in");
|
|
323
|
+
el2.addEventListener("transitionend", () => el2.remove(), { once: true });
|
|
324
|
+
setTimeout(() => el2.remove(), 400);
|
|
323
325
|
}, life);
|
|
324
326
|
}
|
|
325
327
|
|
|
@@ -472,10 +474,10 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
472
474
|
return null;
|
|
473
475
|
}
|
|
474
476
|
if (board.entry.kind !== "component") return stack[0] ?? null;
|
|
475
|
-
for (const
|
|
476
|
-
const t =
|
|
477
|
-
if (t === "HTML" || t === "BODY" ||
|
|
478
|
-
if (paintsSomething(
|
|
477
|
+
for (const el2 of stack) {
|
|
478
|
+
const t = el2.tagName;
|
|
479
|
+
if (t === "HTML" || t === "BODY" || el2.hasAttribute("data-cs-artboard")) return null;
|
|
480
|
+
if (paintsSomething(el2, doc)) return el2;
|
|
479
481
|
}
|
|
480
482
|
return null;
|
|
481
483
|
}
|
|
@@ -499,15 +501,15 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
499
501
|
const m = bg.match(/^rgba\([^)]*,\s*([\d.]+)\s*\)$/);
|
|
500
502
|
return m ? parseFloat(m[1]) : 1;
|
|
501
503
|
}
|
|
502
|
-
function paintsSomething(
|
|
503
|
-
if (VISIBLE_TAGS.has(
|
|
504
|
-
if (
|
|
505
|
-
for (const n of
|
|
504
|
+
function paintsSomething(el2, doc) {
|
|
505
|
+
if (VISIBLE_TAGS.has(el2.tagName)) return true;
|
|
506
|
+
if (el2.closest("svg")) return true;
|
|
507
|
+
for (const n of el2.childNodes) {
|
|
506
508
|
if (n.nodeType === 3 && n.textContent && n.textContent.trim()) return true;
|
|
507
509
|
}
|
|
508
510
|
const win = doc.defaultView;
|
|
509
511
|
if (!win) return false;
|
|
510
|
-
const cs = win.getComputedStyle(
|
|
512
|
+
const cs = win.getComputedStyle(el2);
|
|
511
513
|
if (cs.backgroundImage !== "none") return true;
|
|
512
514
|
if (bgAlpha(cs.backgroundColor) > 0) return true;
|
|
513
515
|
if (cs.boxShadow !== "none") return true;
|
|
@@ -515,19 +517,19 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
515
517
|
if (cs.outlineStyle !== "none" && (parseFloat(cs.outlineWidth) || 0) > 0) return true;
|
|
516
518
|
return false;
|
|
517
519
|
}
|
|
518
|
-
function screenRect(board,
|
|
520
|
+
function screenRect(board, el2) {
|
|
519
521
|
if (!board.iframe || !board.el.isConnected) return null;
|
|
520
522
|
const fr = board.iframe.getBoundingClientRect();
|
|
521
523
|
const s = liveScale(board, board.frameEl.getBoundingClientRect());
|
|
522
|
-
const b =
|
|
524
|
+
const b = el2.getBoundingClientRect();
|
|
523
525
|
return new DOMRect(fr.left + b.left * s, fr.top + b.top * s, b.width * s, b.height * s);
|
|
524
526
|
}
|
|
525
527
|
function liveScale(board, frame) {
|
|
526
528
|
if (board.width > 0 && frame.width > 0) return frame.width / board.width;
|
|
527
529
|
return state.scale || 1;
|
|
528
530
|
}
|
|
529
|
-
function relPos(
|
|
530
|
-
const r = screenRect(board,
|
|
531
|
+
function relPos(el2, clientX, clientY, board) {
|
|
532
|
+
const r = screenRect(board, el2);
|
|
531
533
|
if (!r || r.width === 0 || r.height === 0) return { x: 0, y: 0 };
|
|
532
534
|
const round = (v) => Math.round(clamp(v, 0, 1) * 1e3) / 1e3;
|
|
533
535
|
return { x: round((clientX - r.left) / r.width), y: round((clientY - r.top) / r.height) };
|
|
@@ -539,31 +541,31 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
539
541
|
return false;
|
|
540
542
|
}
|
|
541
543
|
}
|
|
542
|
-
function idSelector(
|
|
543
|
-
const id =
|
|
544
|
+
function idSelector(el2) {
|
|
545
|
+
const id = el2.getAttribute("id");
|
|
544
546
|
if (!id) return null;
|
|
545
547
|
return `#${CSS.escape(id)}`;
|
|
546
548
|
}
|
|
547
|
-
function dataSelector(
|
|
548
|
-
for (const attr of Array.from(
|
|
549
|
+
function dataSelector(el2) {
|
|
550
|
+
for (const attr of Array.from(el2.attributes)) {
|
|
549
551
|
if (!attr.name.startsWith("data-") || !attr.value) continue;
|
|
550
552
|
const v = attr.value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
551
553
|
return `[${attr.name}="${v}"]`;
|
|
552
554
|
}
|
|
553
555
|
return null;
|
|
554
556
|
}
|
|
555
|
-
function nthPart(
|
|
556
|
-
const tag =
|
|
557
|
-
const parent =
|
|
557
|
+
function nthPart(el2) {
|
|
558
|
+
const tag = el2.localName;
|
|
559
|
+
const parent = el2.parentElement;
|
|
558
560
|
if (!parent) return tag;
|
|
559
561
|
const sames = Array.from(parent.children).filter((c) => c.localName === tag);
|
|
560
562
|
if (sames.length < 2) return tag;
|
|
561
|
-
return `${tag}:nth-of-type(${sames.indexOf(
|
|
563
|
+
return `${tag}:nth-of-type(${sames.indexOf(el2) + 1})`;
|
|
562
564
|
}
|
|
563
|
-
function buildSelector(
|
|
564
|
-
const doc =
|
|
565
|
+
function buildSelector(el2) {
|
|
566
|
+
const doc = el2.ownerDocument;
|
|
565
567
|
const parts = [];
|
|
566
|
-
let cur =
|
|
568
|
+
let cur = el2;
|
|
567
569
|
while (cur && cur !== doc.documentElement && parts.length < 5) {
|
|
568
570
|
const byId = idSelector(cur);
|
|
569
571
|
if (byId && isUnique(doc, byId)) {
|
|
@@ -580,12 +582,12 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
580
582
|
if (isUnique(doc, joined)) return joined;
|
|
581
583
|
cur = cur.parentElement;
|
|
582
584
|
}
|
|
583
|
-
return parts.join(" > ") ||
|
|
585
|
+
return parts.join(" > ") || el2.localName;
|
|
584
586
|
}
|
|
585
|
-
function describe(
|
|
586
|
-
const probe =
|
|
587
|
-
const cls = typeof
|
|
588
|
-
return `<${
|
|
587
|
+
function describe(el2) {
|
|
588
|
+
const probe = el2.closest("[data-probe]")?.getAttribute("data-probe");
|
|
589
|
+
const cls = typeof el2.className === "string" ? el2.className : "";
|
|
590
|
+
return `<${el2.localName}>` + (probe ? ` probe=${probe}` : "") + (cls ? ` .${cls.trim().split(/\s+/).slice(0, 3).join(".")}` : "");
|
|
589
591
|
}
|
|
590
592
|
var TAG_GAP = 1;
|
|
591
593
|
function placeTag(tag, box, frame, st, label) {
|
|
@@ -606,8 +608,8 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
606
608
|
tag.style.left = `${x - box.left - 1}px`;
|
|
607
609
|
tag.style.top = `${y - box.top - 1}px`;
|
|
608
610
|
}
|
|
609
|
-
function paint(box, board,
|
|
610
|
-
const r = screenRect(board,
|
|
611
|
+
function paint(box, board, el2, label) {
|
|
612
|
+
const r = screenRect(board, el2);
|
|
611
613
|
if (!r || r.width + r.height === 0) {
|
|
612
614
|
const off = box.classList.contains("is-on");
|
|
613
615
|
box.classList.remove("is-on");
|
|
@@ -654,8 +656,8 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
654
656
|
}
|
|
655
657
|
}
|
|
656
658
|
if (selectRef) {
|
|
657
|
-
const
|
|
658
|
-
if (
|
|
659
|
+
const el2 = resolveSelected();
|
|
660
|
+
if (el2) changed = paint(selectBox, selectRef.board, el2, selectRef.selector) || changed;
|
|
659
661
|
else if (selectBox.classList.contains("is-on")) {
|
|
660
662
|
selectBox.classList.remove("is-on");
|
|
661
663
|
changed = true;
|
|
@@ -676,9 +678,9 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
676
678
|
stillFrames = 0;
|
|
677
679
|
if (!followRaf && (hoverRef || selectRef)) followRaf = requestAnimationFrame(follow);
|
|
678
680
|
}
|
|
679
|
-
function showHover(board,
|
|
680
|
-
hoverRef = { board, el };
|
|
681
|
-
paint(hoverBox, board,
|
|
681
|
+
function showHover(board, el2) {
|
|
682
|
+
hoverRef = { board, el: el2 };
|
|
683
|
+
paint(hoverBox, board, el2, el2.localName);
|
|
682
684
|
wake();
|
|
683
685
|
}
|
|
684
686
|
function hideHover() {
|
|
@@ -721,14 +723,14 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
721
723
|
let top = 8;
|
|
722
724
|
let orphan = true;
|
|
723
725
|
if (doc && ann.anchor) {
|
|
724
|
-
let
|
|
726
|
+
let el2 = null;
|
|
725
727
|
try {
|
|
726
|
-
|
|
728
|
+
el2 = doc.querySelector(ann.anchor.selector);
|
|
727
729
|
} catch {
|
|
728
|
-
|
|
730
|
+
el2 = null;
|
|
729
731
|
}
|
|
730
|
-
if (
|
|
731
|
-
const r =
|
|
732
|
+
if (el2) {
|
|
733
|
+
const r = el2.getBoundingClientRect();
|
|
732
734
|
left = r.left + ann.anchor.x * r.width;
|
|
733
735
|
top = r.top + ann.anchor.y * r.height;
|
|
734
736
|
orphan = false;
|
|
@@ -1013,10 +1015,10 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1013
1015
|
if (i >= 0) state.annotations[i] = ann;
|
|
1014
1016
|
else state.annotations.push(ann);
|
|
1015
1017
|
}
|
|
1016
|
-
function openComposer(board,
|
|
1018
|
+
function openComposer(board, el2, clientX, clientY) {
|
|
1017
1019
|
closeComposer();
|
|
1018
|
-
const selector = buildSelector(
|
|
1019
|
-
const rel = relPos(
|
|
1020
|
+
const selector = buildSelector(el2);
|
|
1021
|
+
const rel = relPos(el2, clientX, clientY, board);
|
|
1020
1022
|
const box = h("div", "cs-pin-input");
|
|
1021
1023
|
box.style.left = `${Math.min(clientX + 10, window.innerWidth - 250)}px`;
|
|
1022
1024
|
box.style.top = `${Math.min(clientY + 10, window.innerHeight - 170)}px`;
|
|
@@ -1242,9 +1244,9 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1242
1244
|
applyView();
|
|
1243
1245
|
}
|
|
1244
1246
|
function isBlank(target) {
|
|
1245
|
-
const
|
|
1246
|
-
if (!
|
|
1247
|
-
return !
|
|
1247
|
+
const el2 = target;
|
|
1248
|
+
if (!el2) return false;
|
|
1249
|
+
return !el2.closest(".cs-board");
|
|
1248
1250
|
}
|
|
1249
1251
|
function initView() {
|
|
1250
1252
|
viewport = qs("#cs-viewport");
|
|
@@ -1378,13 +1380,13 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1378
1380
|
setHidden(ids, hide);
|
|
1379
1381
|
}
|
|
1380
1382
|
function focusedKey() {
|
|
1381
|
-
const
|
|
1382
|
-
return
|
|
1383
|
+
const el2 = document.activeElement;
|
|
1384
|
+
return el2 && listEl.contains(el2) ? el2.dataset.fk ?? null : null;
|
|
1383
1385
|
}
|
|
1384
1386
|
function restoreFocus(key) {
|
|
1385
1387
|
if (!key) return;
|
|
1386
|
-
const
|
|
1387
|
-
|
|
1388
|
+
const el2 = listEl.querySelector(`[data-fk="${CSS.escape(key)}"]`);
|
|
1389
|
+
el2?.focus({ preventScroll: true });
|
|
1388
1390
|
}
|
|
1389
1391
|
function renderSidebar() {
|
|
1390
1392
|
if (!listEl) return;
|
|
@@ -1425,8 +1427,8 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1425
1427
|
restoreFocus(keepFocus);
|
|
1426
1428
|
}
|
|
1427
1429
|
function group(key, label, ids, headCls) {
|
|
1428
|
-
const
|
|
1429
|
-
if (headCls === "cs-sb-file")
|
|
1430
|
+
const el2 = h("div", "cs-sb-group");
|
|
1431
|
+
if (headCls === "cs-sb-file") el2.classList.add("is-file");
|
|
1430
1432
|
const headEl = h("div", headCls);
|
|
1431
1433
|
const body = h("div", "cs-sb-items");
|
|
1432
1434
|
body.id = `cs-sb-g${++uid}`;
|
|
@@ -1461,12 +1463,12 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1461
1463
|
renderSidebar();
|
|
1462
1464
|
});
|
|
1463
1465
|
headEl.append(caret, bulk);
|
|
1464
|
-
|
|
1465
|
-
return { el, body };
|
|
1466
|
+
el2.append(headEl, body);
|
|
1467
|
+
return { el: el2, body };
|
|
1466
1468
|
}
|
|
1467
1469
|
function row(id, name, sub, kind) {
|
|
1468
|
-
const
|
|
1469
|
-
|
|
1470
|
+
const el2 = h("div", "cs-sb-row");
|
|
1471
|
+
el2.dataset.id = id;
|
|
1470
1472
|
const go = h("button", "cs-sb-go");
|
|
1471
1473
|
go.type = "button";
|
|
1472
1474
|
go.dataset.fk = `go:${id}`;
|
|
@@ -1491,9 +1493,9 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1491
1493
|
toggleHidden(id);
|
|
1492
1494
|
renderSidebar();
|
|
1493
1495
|
});
|
|
1494
|
-
|
|
1495
|
-
if (isHidden(id))
|
|
1496
|
-
return
|
|
1496
|
+
el2.append(go, eye);
|
|
1497
|
+
if (isHidden(id)) el2.classList.add("is-hidden-board");
|
|
1498
|
+
return el2;
|
|
1497
1499
|
}
|
|
1498
1500
|
function syncActive() {
|
|
1499
1501
|
if (!listEl) return;
|
|
@@ -1599,9 +1601,9 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1599
1601
|
return titleEl;
|
|
1600
1602
|
}
|
|
1601
1603
|
function createBoard(entry) {
|
|
1602
|
-
const
|
|
1603
|
-
|
|
1604
|
-
|
|
1604
|
+
const el2 = h("div", "cs-board");
|
|
1605
|
+
el2.dataset.id = entry.id;
|
|
1606
|
+
el2.dataset.kind = entry.kind;
|
|
1605
1607
|
const titleEl = h("div", "cs-board-title");
|
|
1606
1608
|
const nameEl = h("span", "cs-name", entry.exportName);
|
|
1607
1609
|
const badgeEl = h("span", "cs-badge", entry.kind);
|
|
@@ -1613,11 +1615,11 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1613
1615
|
const overlayEl = h("div", "cs-ovl");
|
|
1614
1616
|
const pinLayerEl = h("div", "cs-pins");
|
|
1615
1617
|
frameEl.append(placeholderEl, overlayEl);
|
|
1616
|
-
|
|
1617
|
-
world2.appendChild(
|
|
1618
|
+
el2.append(titleEl, frameEl, pinLayerEl);
|
|
1619
|
+
world2.appendChild(el2);
|
|
1618
1620
|
const board = {
|
|
1619
1621
|
entry,
|
|
1620
|
-
el,
|
|
1622
|
+
el: el2,
|
|
1621
1623
|
titleEl,
|
|
1622
1624
|
nameEl,
|
|
1623
1625
|
badgeEl,
|
|
@@ -1640,10 +1642,10 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
1640
1642
|
}
|
|
1641
1643
|
attachResizeHandles(board);
|
|
1642
1644
|
attachBoardDrag(board);
|
|
1643
|
-
|
|
1645
|
+
el2.hidden = isHidden(entry.id);
|
|
1644
1646
|
applySize(board);
|
|
1645
1647
|
updateBoard(board, entry);
|
|
1646
|
-
io.observe(
|
|
1648
|
+
io.observe(el2);
|
|
1647
1649
|
return board;
|
|
1648
1650
|
}
|
|
1649
1651
|
var WIDTH_PRESETS = [390, 768, 1024, 1280, 1440];
|
|
@@ -2386,9 +2388,9 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
2386
2388
|
// src/canvas/modes.ts
|
|
2387
2389
|
var viewport2;
|
|
2388
2390
|
function boardOf(target) {
|
|
2389
|
-
const
|
|
2390
|
-
if (!
|
|
2391
|
-
return state.boards.get(
|
|
2391
|
+
const el2 = target?.closest?.(".cs-board");
|
|
2392
|
+
if (!el2?.dataset.id) return null;
|
|
2393
|
+
return state.boards.get(el2.dataset.id) ?? null;
|
|
2392
2394
|
}
|
|
2393
2395
|
function setActive(id) {
|
|
2394
2396
|
if (state.activeId === id) return;
|
|
@@ -2455,11 +2457,11 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
2455
2457
|
setProbe("\u79FB\u5230\u753B\u677F\u4E0A\u53CD\u67E5\u5143\u7D20");
|
|
2456
2458
|
}
|
|
2457
2459
|
function isEditable(target) {
|
|
2458
|
-
const
|
|
2459
|
-
if (!
|
|
2460
|
-
const tag =
|
|
2461
|
-
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" ||
|
|
2462
|
-
return
|
|
2460
|
+
const el2 = target;
|
|
2461
|
+
if (!el2) return false;
|
|
2462
|
+
const tag = el2.tagName;
|
|
2463
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || el2.isContentEditable === true) return true;
|
|
2464
|
+
return el2.closest("#cs-sidebar, #cs-args, .cs-picker, #cs-topbar") !== null;
|
|
2463
2465
|
}
|
|
2464
2466
|
function onKeyDown(e) {
|
|
2465
2467
|
if (e.key === "Escape") {
|
|
@@ -2550,13 +2552,13 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
2550
2552
|
}
|
|
2551
2553
|
if (state.mode !== "browse") return;
|
|
2552
2554
|
setActive(board.entry.id);
|
|
2553
|
-
const
|
|
2554
|
-
if (!
|
|
2555
|
+
const el2 = elementAt(board, e.clientX, e.clientY);
|
|
2556
|
+
if (!el2) {
|
|
2555
2557
|
hideHover();
|
|
2556
2558
|
return;
|
|
2557
2559
|
}
|
|
2558
|
-
showHover(board,
|
|
2559
|
-
setProbe(`${board.entry.exportName} ${describe(
|
|
2560
|
+
showHover(board, el2);
|
|
2561
|
+
setProbe(`${board.entry.exportName} ${describe(el2)}`);
|
|
2560
2562
|
}
|
|
2561
2563
|
function onClick(e) {
|
|
2562
2564
|
const target = e.target;
|
|
@@ -2578,15 +2580,15 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
2578
2580
|
return;
|
|
2579
2581
|
}
|
|
2580
2582
|
setActive(board.entry.id);
|
|
2581
|
-
const
|
|
2582
|
-
if (!
|
|
2583
|
+
const el2 = elementAt(board, e.clientX, e.clientY);
|
|
2584
|
+
if (!el2) return;
|
|
2583
2585
|
if (state.pinPending) {
|
|
2584
|
-
openComposer(board,
|
|
2586
|
+
openComposer(board, el2, e.clientX, e.clientY);
|
|
2585
2587
|
return;
|
|
2586
2588
|
}
|
|
2587
2589
|
if (state.mode !== "browse") return;
|
|
2588
|
-
const selector = buildSelector(
|
|
2589
|
-
const rel = relPos(
|
|
2590
|
+
const selector = buildSelector(el2);
|
|
2591
|
+
const rel = relPos(el2, e.clientX, e.clientY, board);
|
|
2590
2592
|
showSelection(board, selector);
|
|
2591
2593
|
state.selection = { artboardId: board.entry.id, selector, x: rel.x, y: rel.y, ts: Date.now() };
|
|
2592
2594
|
setProbe(`\u5DF2\u9009\u4E2D ${board.entry.exportName} \u2192 ${selector}`);
|
|
@@ -2622,6 +2624,32 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
2622
2624
|
syncHud();
|
|
2623
2625
|
}
|
|
2624
2626
|
|
|
2627
|
+
// src/canvas/health-banner.ts
|
|
2628
|
+
var el = null;
|
|
2629
|
+
var lastOkAt;
|
|
2630
|
+
function setLastOk(ts) {
|
|
2631
|
+
lastOkAt = ts;
|
|
2632
|
+
}
|
|
2633
|
+
function showUnhealthy(detail) {
|
|
2634
|
+
hideUnhealthy();
|
|
2635
|
+
const at = lastOkAt ? new Date(lastOkAt).toLocaleTimeString("zh-CN", { hour12: false }) : null;
|
|
2636
|
+
el = h("div", "cs-health");
|
|
2637
|
+
el.setAttribute("role", "alert");
|
|
2638
|
+
el.appendChild(h("b", void 0, "\u4F60\u7684 dev server \u6CA1\u54CD\u5E94\u4E86"));
|
|
2639
|
+
el.appendChild(
|
|
2640
|
+
h(
|
|
2641
|
+
"span",
|
|
2642
|
+
void 0,
|
|
2643
|
+
`${detail ?? "SSR \u63A2\u6D4B\u5931\u8D25"}${at ? ` \xB7 \u6700\u540E\u6B63\u5E38 ${at}` : ""} \u2014\u2014 \u753B\u5E03\u6CA1\u574F,\u753B\u677F\u7834\u56FE\u662F\u5B83\u6E32\u67D3\u4E0D\u51FA\u6765\u3002\u901A\u5E38\u91CD\u542F next dev \u5373\u6108\u3002`
|
|
2644
|
+
)
|
|
2645
|
+
);
|
|
2646
|
+
document.body.appendChild(el);
|
|
2647
|
+
}
|
|
2648
|
+
function hideUnhealthy() {
|
|
2649
|
+
el?.remove();
|
|
2650
|
+
el = null;
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2625
2653
|
// src/canvas/app.ts
|
|
2626
2654
|
function showBootError(err) {
|
|
2627
2655
|
const boot2 = qs("#cs-boot");
|
|
@@ -2678,9 +2706,9 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
2678
2706
|
}
|
|
2679
2707
|
function showTarget() {
|
|
2680
2708
|
const info = state.info;
|
|
2681
|
-
const
|
|
2682
|
-
|
|
2683
|
-
|
|
2709
|
+
const el2 = qs("#cs-target");
|
|
2710
|
+
el2.textContent = info ? `${info.target} \xB7 ${info.designDir}` : "";
|
|
2711
|
+
el2.title = info ? `v${info.version} \xB7 ${info.projectRoot}` : "";
|
|
2684
2712
|
}
|
|
2685
2713
|
function onRegistryEvent(entries) {
|
|
2686
2714
|
void reloadUntilMatches(entries.map((e) => e.id));
|
|
@@ -2724,16 +2752,31 @@ ${board.entry.kind} \xB7 ${board.entry.id}`);
|
|
|
2724
2752
|
}
|
|
2725
2753
|
showTarget();
|
|
2726
2754
|
restoreView();
|
|
2727
|
-
await tryLoadRegistry();
|
|
2728
2755
|
try {
|
|
2729
|
-
|
|
2730
|
-
|
|
2756
|
+
const hres = await fetch("/__cs/api/health");
|
|
2757
|
+
const hstate = await hres.json();
|
|
2758
|
+
setLastOk(hstate.lastOkAt);
|
|
2759
|
+
if (!hstate.ok) showUnhealthy(hstate.detail);
|
|
2731
2760
|
} catch {
|
|
2732
2761
|
}
|
|
2733
2762
|
connectEvents({
|
|
2734
2763
|
registry: onRegistryEvent,
|
|
2735
|
-
annotations: onAnnotations
|
|
2764
|
+
annotations: onAnnotations,
|
|
2765
|
+
health: (ok, detail) => {
|
|
2766
|
+
if (ok) {
|
|
2767
|
+
setLastOk(Date.now());
|
|
2768
|
+
hideUnhealthy();
|
|
2769
|
+
} else {
|
|
2770
|
+
showUnhealthy(detail);
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2736
2773
|
});
|
|
2774
|
+
await tryLoadRegistry();
|
|
2775
|
+
try {
|
|
2776
|
+
state.annotations = await fetchAnnotations();
|
|
2777
|
+
renderPins();
|
|
2778
|
+
} catch {
|
|
2779
|
+
}
|
|
2737
2780
|
}
|
|
2738
2781
|
void boot();
|
|
2739
2782
|
})();
|