Merge remote-tracking branches 'origin/sub_laptop_3' and 'origin/sub_laptop_7' into sub_laptop_5

This commit is contained in:
2026-10-06 12:40:50 +09:00
18 changed files with 308 additions and 240 deletions
@@ -0,0 +1,83 @@
/* 54-22 — 종단 점 ▲ 누름 멈춤(긴 작업) 재기: B06 은 120ms 디바운스 · 종단도 한 번 · B05 는 실시간 그대로
* 시작 화면: B06(#/b06-section) 또는 B05(#/b05-profile) · 검증 프로젝트 · 로그인
* 부르는 법: 단계마다 한 번씩 — 앞에 `window.__m5422 = "단계";` · 긴 단계는 `void` 로 띄우고
* `sessionStorage.getItem("__5422_out_{단계}")` 로 읽음
* snap → 처음 초안(세션 원문)을 `__5422_orig` 에 남김(이미 있으면 그대로)
* one → 가운데 측점 ▲ 한 번 — 긴 작업 최장 · 합 · 횡단 재계산까지 걸린 때
* hold → 가운데 측점 ▲ 2.5초 길게 — 긴 작업 최장 · 합 · 재계산 횟수
* restore → 처음 초안 원문으로 되돌림 → 그 뒤 orca reload
* 판정: B06 one 최장 ≈200ms 안팎 · hold 합 ≈0.5초 안팎(고치기 전 587 / 2,684ms) ·
* B05 one 재계산 때 = 16ms 디바운스 근처(실시간)
* 쓰는 것: sessionStorage(계획선 초안) — 정본(서버)은 안 씀
*/
(async () => {
const MODE = window.__m5422 || "one";
const w = (ms) => new Promise((r) => setTimeout(r, ms));
const DRAFTS = /^aislo:draft:/;
const out = { mode: MODE, page: location.hash };
const save = () => sessionStorage.setItem(`__5422_out_${MODE}`, JSON.stringify(out));
if (MODE === "snap") {
if (!sessionStorage.getItem("__5422_orig")) {
const orig = Object.fromEntries(
Object.keys(sessionStorage)
.filter((k) => DRAFTS.test(k))
.map((k) => [k, sessionStorage.getItem(k)]),
);
sessionStorage.setItem("__5422_orig", JSON.stringify(orig));
}
out.keys = Object.keys(JSON.parse(sessionStorage.getItem("__5422_orig")));
save();
return JSON.stringify(out);
}
if (MODE === "restore") {
const orig = JSON.parse(sessionStorage.getItem("__5422_orig") || "null");
if (!orig) return "no orig";
for (const k of Object.keys(sessionStorage)) if (DRAFTS.test(k)) sessionStorage.removeItem(k);
for (const [k, v] of Object.entries(orig)) sessionStorage.setItem(k, v);
for (const k of Object.keys(sessionStorage))
if (k.startsWith("__5422")) sessionStorage.removeItem(k);
return JSON.stringify({ restored: Object.keys(orig) });
}
const ups = [...document.querySelectorAll(".b05-profile-edit__btn.is-up")];
const button = ups[Math.floor(ups.length / 2)];
if (!button) {
out.error = "▲ 없음";
save();
return JSON.stringify(out);
}
out.button = button.title.split("\n")[0];
const tasks = [];
const observer = new PerformanceObserver((list) =>
list.getEntries().forEach((e) => tasks.push(Math.round(e.duration))),
);
observer.observe({ type: "longtask" });
// 횡단 재계산이 카드 · 캐시에 닿은 때 — 계획선 초안 말고 다른 캐시 쓰기
const writes = [];
const t0 = performance.now();
const origSet = Storage.prototype.setItem;
Storage.prototype.setItem = function (k, v) {
if (this === sessionStorage && !k.startsWith("__5422"))
writes.push([Math.round(performance.now() - t0), k.replace(/:[0-9a-f-]{36}.*/, "")]);
return origSet.call(this, k, v);
};
// 화면이 바뀐 때(ms) — 누른 뒤 재계산 반영이 닿는 때를 봄(B05 ≈16ms 디바운스 · B06 ≈120ms)
const domAt = new Set();
const mo = new MutationObserver(() => domAt.add(Math.round(performance.now() - t0)));
mo.observe(document.body, { subtree: true, childList: true, characterData: true });
button.dispatchEvent(new PointerEvent("pointerdown", { bubbles: true, pointerId: 1 }));
await w(MODE === "hold" ? 2500 : 60);
window.dispatchEvent(new PointerEvent("pointerup", { bubbles: true, pointerId: 1 }));
await w(2500);
Storage.prototype.setItem = origSet;
observer.disconnect();
mo.disconnect();
out.domAt = [...domAt].slice(0, 12);
out.longTasks = tasks;
out.max = tasks.length ? Math.max(...tasks) : 0;
out.sum = tasks.reduce((a, b) => a + b, 0);
out.writes = writes;
save();
return JSON.stringify(out);
})();