@wcstack/devtools 4.0.0-rc.6 → 4.0.0-rc.8
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.ja.md +16 -8
- package/README.md +17 -8
- package/dist/auto.min.js +1 -1
- package/dist/auto.min.js.map +1 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.esm.js +110 -13
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -341,7 +341,12 @@ interface IWiringEntry {
|
|
|
341
341
|
*/
|
|
342
342
|
readonly stateElementRef: WeakRef<object> | null;
|
|
343
343
|
}
|
|
344
|
-
|
|
344
|
+
/**
|
|
345
|
+
* Core の変化の種類。`values` は「状態の値が変わったかもしれない」の合図で、更新バッチ
|
|
346
|
+
* (`state:update-batch`)ごとに流れる。timeline の一時停止(paused)とは独立 —
|
|
347
|
+
* 一時停止が止めるのは記録であって観測ではないので、State ペインは追従し続ける。
|
|
348
|
+
*/
|
|
349
|
+
type CoreChangeKind = "sources" | "roster" | "wiring" | "timeline" | "coverage" | "values";
|
|
345
350
|
type CoreChangeListener = (kind: CoreChangeKind) => void;
|
|
346
351
|
/**
|
|
347
352
|
* 配線カバレッジ 1 行(static-wiring-dx-design.md §4 — 宣言 × 実測の突合)。
|
|
@@ -512,6 +517,10 @@ declare class WcsDevtools extends HTMLElement {
|
|
|
512
517
|
private _expanded;
|
|
513
518
|
private _hotkeyHandler;
|
|
514
519
|
private _pickHandler;
|
|
520
|
+
/** State ペインで開いているインライン編集の入力欄(無ければ null)。 */
|
|
521
|
+
private _editInput;
|
|
522
|
+
/** State ペインの中でポインタが押されている間 true(離すまで再描画を待つ)。 */
|
|
523
|
+
private _statePointerDown;
|
|
515
524
|
get core(): DevtoolsCore | null;
|
|
516
525
|
connectedCallback(): void;
|
|
517
526
|
disconnectedCallback(): void;
|
|
@@ -530,9 +539,20 @@ declare class WcsDevtools extends HTMLElement {
|
|
|
530
539
|
private _exitPickMode;
|
|
531
540
|
private _markDirty;
|
|
532
541
|
private _renderDirty;
|
|
542
|
+
/**
|
|
543
|
+
* State ペインの描き直しを待つべきか: インライン編集の入力欄にフォーカスがあるか、
|
|
544
|
+
* ペインの中でポインタが押されている。描き直すと入力欄(打ちかけの値)や押した行が消える。
|
|
545
|
+
* フォーカスで見るのは、パネルを閉じる・ノードを外すなど blur の来ない抜け方でも
|
|
546
|
+
* 待ち続けないため。
|
|
547
|
+
*/
|
|
548
|
+
private _stateHeld;
|
|
549
|
+
/** 待たせていた State ペインの描き直しを流す(保留が無ければ何もしない)。 */
|
|
550
|
+
private _releaseState;
|
|
551
|
+
private _holdStateWhilePressed;
|
|
533
552
|
private _rosterKey;
|
|
534
553
|
private _selectedRoster;
|
|
535
554
|
private _renderStatePane;
|
|
555
|
+
private _renderStateBody;
|
|
536
556
|
/**
|
|
537
557
|
* 選択ツリーの鍵付き購読セクション(keyedSubscriptions — protocol v2 追補・要件 D17)。
|
|
538
558
|
* `$eq` / `$eqPath` / `$eqIndex` の購読は依存グラフの動的な変化で、上の状態ツリーにも
|
package/dist/index.esm.js
CHANGED
|
@@ -775,6 +775,9 @@ class DevtoolsCore {
|
|
|
775
775
|
if (total === 0) {
|
|
776
776
|
return;
|
|
777
777
|
}
|
|
778
|
+
// 値の変化の合図(State ペインの丸ごとの再読 — devtools-tag-design.md §10 G-U1)。
|
|
779
|
+
// timeline の行より先に、一時停止中でも流す
|
|
780
|
+
this._notify("values");
|
|
778
781
|
const rest = total > labels.length ? `, …(${total})` : "";
|
|
779
782
|
this._appendTimeline({
|
|
780
783
|
sourceId,
|
|
@@ -1123,7 +1126,29 @@ header {
|
|
|
1123
1126
|
border-bottom: 1px solid #2b4f78;
|
|
1124
1127
|
background: #10263f;
|
|
1125
1128
|
}
|
|
1126
|
-
header
|
|
1129
|
+
/* The header never grows wider than the panel: the title and the middle group shrink (the
|
|
1130
|
+
group scrolls sideways when even that is not enough), so dock and × always stay in view —
|
|
1131
|
+
the right dock is only 420px wide. */
|
|
1132
|
+
header .title {
|
|
1133
|
+
flex: 0 1 auto;
|
|
1134
|
+
min-width: 0;
|
|
1135
|
+
overflow: hidden;
|
|
1136
|
+
text-overflow: ellipsis;
|
|
1137
|
+
white-space: nowrap;
|
|
1138
|
+
font-weight: 700;
|
|
1139
|
+
color: #9fd0ff;
|
|
1140
|
+
margin-right: 4px;
|
|
1141
|
+
}
|
|
1142
|
+
header .tools {
|
|
1143
|
+
flex: 1 1 auto;
|
|
1144
|
+
min-width: 0;
|
|
1145
|
+
display: flex;
|
|
1146
|
+
gap: 6px;
|
|
1147
|
+
align-items: center;
|
|
1148
|
+
overflow-x: auto;
|
|
1149
|
+
}
|
|
1150
|
+
header .tools select { flex: 0 1 auto; min-width: 6em; text-overflow: ellipsis; }
|
|
1151
|
+
header .tools button, header > button { flex: none; white-space: nowrap; }
|
|
1127
1152
|
header select, header button {
|
|
1128
1153
|
font: inherit;
|
|
1129
1154
|
background: #16324f;
|
|
@@ -1134,7 +1159,6 @@ header select, header button {
|
|
|
1134
1159
|
cursor: pointer;
|
|
1135
1160
|
}
|
|
1136
1161
|
header button[aria-pressed="true"] { background: #2b5d8f; }
|
|
1137
|
-
header .spacer { flex: 1; }
|
|
1138
1162
|
.panes { display: flex; flex: 1; min-height: 0; }
|
|
1139
1163
|
.pane { flex: 1; min-width: 0; overflow: auto; padding: 6px 8px; border-right: 1px solid #1d3a5c; }
|
|
1140
1164
|
.pane:last-child { border-right: none; }
|
|
@@ -1238,6 +1262,10 @@ class WcsDevtools extends HTMLElement {
|
|
|
1238
1262
|
_expanded = new Set();
|
|
1239
1263
|
_hotkeyHandler = null;
|
|
1240
1264
|
_pickHandler = null;
|
|
1265
|
+
/** State ペインで開いているインライン編集の入力欄(無ければ null)。 */
|
|
1266
|
+
_editInput = null;
|
|
1267
|
+
/** State ペインの中でポインタが押されている間 true(離すまで再描画を待つ)。 */
|
|
1268
|
+
_statePointerDown = false;
|
|
1241
1269
|
get core() {
|
|
1242
1270
|
return this._core;
|
|
1243
1271
|
}
|
|
@@ -1267,6 +1295,13 @@ class WcsDevtools extends HTMLElement {
|
|
|
1267
1295
|
this._markDirty("wiring");
|
|
1268
1296
|
}
|
|
1269
1297
|
}
|
|
1298
|
+
else if (kind === "values") {
|
|
1299
|
+
// 値が変わった: State ペインを丸ごと読み直す(devtools-tag-design.md §10 G-U1)。
|
|
1300
|
+
// 閉じている間は読まない — 開いたときに _applyOpen が描き直す
|
|
1301
|
+
if (this.open) {
|
|
1302
|
+
this._markDirty("state");
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1270
1305
|
else {
|
|
1271
1306
|
this._markDirty("timeline");
|
|
1272
1307
|
}
|
|
@@ -1327,6 +1362,11 @@ class WcsDevtools extends HTMLElement {
|
|
|
1327
1362
|
title.className = "title";
|
|
1328
1363
|
title.textContent = "wcstack devtools";
|
|
1329
1364
|
header.append(title);
|
|
1365
|
+
// 中間のグループ(ツリー選択・pick・⏸・🗑)。幅が足りないときはここが縮んで横に
|
|
1366
|
+
// スクロールし、dock と × はヘッダーの右端に残る
|
|
1367
|
+
const tools = document.createElement("div");
|
|
1368
|
+
tools.className = "tools";
|
|
1369
|
+
header.append(tools);
|
|
1330
1370
|
const stateSelect = document.createElement("select");
|
|
1331
1371
|
stateSelect.title = "state element";
|
|
1332
1372
|
stateSelect.addEventListener("change", () => {
|
|
@@ -1336,9 +1376,9 @@ class WcsDevtools extends HTMLElement {
|
|
|
1336
1376
|
this._markDirty("state");
|
|
1337
1377
|
this._markDirty("wiring");
|
|
1338
1378
|
});
|
|
1339
|
-
|
|
1379
|
+
tools.append(stateSelect);
|
|
1340
1380
|
this._stateSelect = stateSelect;
|
|
1341
|
-
const pickButton = this._headerButton(
|
|
1381
|
+
const pickButton = this._headerButton(tools, "⌖ pick", "pick a page element");
|
|
1342
1382
|
pickButton.addEventListener("click", () => {
|
|
1343
1383
|
if (this._pickMode) {
|
|
1344
1384
|
this._exitPickMode();
|
|
@@ -1349,7 +1389,7 @@ class WcsDevtools extends HTMLElement {
|
|
|
1349
1389
|
pickButton.setAttribute("aria-pressed", String(this._pickMode));
|
|
1350
1390
|
});
|
|
1351
1391
|
pickButton.dataset["role"] = "pick";
|
|
1352
|
-
const pauseButton = this._headerButton(
|
|
1392
|
+
const pauseButton = this._headerButton(tools, "⏸", "pause timeline");
|
|
1353
1393
|
pauseButton.addEventListener("click", () => {
|
|
1354
1394
|
const core = this._core;
|
|
1355
1395
|
if (core === null) {
|
|
@@ -1359,14 +1399,11 @@ class WcsDevtools extends HTMLElement {
|
|
|
1359
1399
|
pauseButton.setAttribute("aria-pressed", String(core.paused));
|
|
1360
1400
|
});
|
|
1361
1401
|
pauseButton.dataset["role"] = "pause";
|
|
1362
|
-
const clearButton = this._headerButton(
|
|
1402
|
+
const clearButton = this._headerButton(tools, "🗑", "clear timeline");
|
|
1363
1403
|
clearButton.addEventListener("click", () => {
|
|
1364
1404
|
this._core?.clearTimeline();
|
|
1365
1405
|
});
|
|
1366
1406
|
clearButton.dataset["role"] = "clear";
|
|
1367
|
-
const spacer = document.createElement("span");
|
|
1368
|
-
spacer.className = "spacer";
|
|
1369
|
-
header.append(spacer);
|
|
1370
1407
|
const dockButton = this._headerButton(header, "dock", "toggle dock position");
|
|
1371
1408
|
dockButton.addEventListener("click", () => {
|
|
1372
1409
|
const next = (this.getAttribute("dock") ?? "bottom") === "bottom" ? "right" : "bottom";
|
|
@@ -1394,6 +1431,11 @@ class WcsDevtools extends HTMLElement {
|
|
|
1394
1431
|
panes.append(pane);
|
|
1395
1432
|
this._paneElements[name] = body;
|
|
1396
1433
|
}
|
|
1434
|
+
// 押している間に State ペインを描き直すと、押した行が消えてクリックが成立しない
|
|
1435
|
+
// (値が毎フレーム変わるページ)。離すまで待つ
|
|
1436
|
+
this._paneElements["state"].parentElement.addEventListener("pointerdown", () => {
|
|
1437
|
+
this._holdStateWhilePressed();
|
|
1438
|
+
});
|
|
1397
1439
|
panel.append(panes);
|
|
1398
1440
|
shadow.append(panel);
|
|
1399
1441
|
this._panel = panel;
|
|
@@ -1402,12 +1444,12 @@ class WcsDevtools extends HTMLElement {
|
|
|
1402
1444
|
shadow.append(highlightLayer);
|
|
1403
1445
|
this._highlightLayer = highlightLayer;
|
|
1404
1446
|
}
|
|
1405
|
-
_headerButton(
|
|
1447
|
+
_headerButton(container, label, title) {
|
|
1406
1448
|
const button = document.createElement("button");
|
|
1407
1449
|
button.textContent = label;
|
|
1408
1450
|
button.title = title;
|
|
1409
1451
|
button.setAttribute("aria-pressed", "false");
|
|
1410
|
-
|
|
1452
|
+
container.append(button);
|
|
1411
1453
|
return button;
|
|
1412
1454
|
}
|
|
1413
1455
|
// --- open/close/dock/hotkey ---
|
|
@@ -1518,7 +1560,13 @@ class WcsDevtools extends HTMLElement {
|
|
|
1518
1560
|
const dirty = this._dirtyPanes;
|
|
1519
1561
|
this._dirtyPanes = new Set();
|
|
1520
1562
|
if (dirty.has("state")) {
|
|
1521
|
-
this.
|
|
1563
|
+
if (this._stateHeld()) {
|
|
1564
|
+
// 編集中・押下中は今の DOM のまま。印だけ残し、終わったときに描く(_releaseState)
|
|
1565
|
+
this._dirtyPanes.add("state");
|
|
1566
|
+
}
|
|
1567
|
+
else {
|
|
1568
|
+
this._renderStatePane();
|
|
1569
|
+
}
|
|
1522
1570
|
}
|
|
1523
1571
|
if (dirty.has("wiring")) {
|
|
1524
1572
|
this._renderWiringPane();
|
|
@@ -1527,6 +1575,37 @@ class WcsDevtools extends HTMLElement {
|
|
|
1527
1575
|
this._renderTimelinePane();
|
|
1528
1576
|
}
|
|
1529
1577
|
}
|
|
1578
|
+
/**
|
|
1579
|
+
* State ペインの描き直しを待つべきか: インライン編集の入力欄にフォーカスがあるか、
|
|
1580
|
+
* ペインの中でポインタが押されている。描き直すと入力欄(打ちかけの値)や押した行が消える。
|
|
1581
|
+
* フォーカスで見るのは、パネルを閉じる・ノードを外すなど blur の来ない抜け方でも
|
|
1582
|
+
* 待ち続けないため。
|
|
1583
|
+
*/
|
|
1584
|
+
_stateHeld() {
|
|
1585
|
+
if (this._statePointerDown) {
|
|
1586
|
+
return true;
|
|
1587
|
+
}
|
|
1588
|
+
// 外れた(描き直しで消えた)入力欄はフォーカスを持てないので、これだけで足りる
|
|
1589
|
+
return this._editInput !== null && this.shadowRoot.activeElement === this._editInput;
|
|
1590
|
+
}
|
|
1591
|
+
/** 待たせていた State ペインの描き直しを流す(保留が無ければ何もしない)。 */
|
|
1592
|
+
_releaseState() {
|
|
1593
|
+
if (this._dirtyPanes.has("state")) {
|
|
1594
|
+
this._markDirty("state");
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
_holdStateWhilePressed() {
|
|
1598
|
+
this._statePointerDown = true;
|
|
1599
|
+
// 離した場所はペインの外かもしれない(ドラッグ)。window で受けて、受けたら外す
|
|
1600
|
+
const release = () => {
|
|
1601
|
+
window.removeEventListener("pointerup", release, true);
|
|
1602
|
+
window.removeEventListener("pointercancel", release, true);
|
|
1603
|
+
this._statePointerDown = false;
|
|
1604
|
+
this._releaseState();
|
|
1605
|
+
};
|
|
1606
|
+
window.addEventListener("pointerup", release, true);
|
|
1607
|
+
window.addEventListener("pointercancel", release, true);
|
|
1608
|
+
}
|
|
1530
1609
|
_rosterKey(entry) {
|
|
1531
1610
|
return `${entry.sourceId}:${entry.label}`;
|
|
1532
1611
|
}
|
|
@@ -1540,8 +1619,18 @@ class WcsDevtools extends HTMLElement {
|
|
|
1540
1619
|
return found ?? roster[0];
|
|
1541
1620
|
}
|
|
1542
1621
|
_renderStatePane() {
|
|
1543
|
-
|
|
1622
|
+
// 描き直しで今の入力欄は消える(編集中ならここへは来ない — _stateHeld)
|
|
1623
|
+
this._editInput = null;
|
|
1544
1624
|
const body = this._paneElements["state"];
|
|
1625
|
+
// スクロール位置は描き直しの前後で保つ。中身を空にした瞬間にレイアウトが走ると
|
|
1626
|
+
// (値の読み出しがページの getter を通る)スクロールが 0 に詰められうる
|
|
1627
|
+
const scroller = body.parentElement;
|
|
1628
|
+
const scrollTop = scroller.scrollTop;
|
|
1629
|
+
this._renderStateBody(body);
|
|
1630
|
+
scroller.scrollTop = scrollTop;
|
|
1631
|
+
}
|
|
1632
|
+
_renderStateBody(body) {
|
|
1633
|
+
const core = this._core;
|
|
1545
1634
|
const select = this._stateSelect;
|
|
1546
1635
|
const roster = core.getRoster();
|
|
1547
1636
|
const selected = this._selectedRoster();
|
|
@@ -1773,6 +1862,7 @@ class WcsDevtools extends HTMLElement {
|
|
|
1773
1862
|
const input = document.createElement("input");
|
|
1774
1863
|
input.value = typeof current === "string" ? current : String(current);
|
|
1775
1864
|
const commit = () => {
|
|
1865
|
+
this._editInput = null;
|
|
1776
1866
|
// 編集は通常のリアクティブパイプラインを通る(devtools-tag-design.md §3.1)
|
|
1777
1867
|
this._core?.writeValue(entry, ref.path, coerceInput(input.value), ref.indexes);
|
|
1778
1868
|
this._markDirty("state");
|
|
@@ -1782,10 +1872,17 @@ class WcsDevtools extends HTMLElement {
|
|
|
1782
1872
|
commit();
|
|
1783
1873
|
}
|
|
1784
1874
|
else if (event.key === "Escape") {
|
|
1875
|
+
this._editInput = null;
|
|
1785
1876
|
this._markDirty("state");
|
|
1786
1877
|
}
|
|
1787
1878
|
});
|
|
1879
|
+
// フォーカスが外れたら、待たせていた描き直しを流す(入力欄はそれで消える = 取り消し)。
|
|
1880
|
+
// 待たせていなければ入力欄はそのまま残る(従来どおり)
|
|
1881
|
+
input.addEventListener("blur", () => {
|
|
1882
|
+
this._releaseState();
|
|
1883
|
+
});
|
|
1788
1884
|
row.replaceChild(input, valueSpan);
|
|
1885
|
+
this._editInput = input;
|
|
1789
1886
|
input.focus();
|
|
1790
1887
|
}
|
|
1791
1888
|
_renderWiringPane() {
|