Skip to content

Commit efae3a6

Browse files
committed
Improve SigilExplorer auto sync
1 parent 4adeb60 commit efae3a6

3 files changed

Lines changed: 36 additions & 4 deletions

File tree

src/App.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
} from "./utils/kai_pulse";
3535
import { fmt2, formatPulse, modPos, readNum } from "./utils/kaiTimeDisplay";
3636
import { usePerfMode } from "./hooks/usePerfMode";
37+
import { SIGIL_EXPLORER_OPEN_EVENT } from "./constants/sigilExplorer";
3738

3839
import SovereignDeclarations from "./components/SovereignDeclarations";
3940
import { DEFAULT_APP_VERSION, SW_VERSION_EVENT } from "./version";
@@ -606,6 +607,11 @@ function ExplorerPopover({
606607
return () => document.removeEventListener("keydown", onKey);
607608
}, [open, onClose, isClient]);
608609

610+
useEffect(() => {
611+
if (!open || !isClient) return;
612+
window.dispatchEvent(new CustomEvent(SIGIL_EXPLORER_OPEN_EVENT));
613+
}, [open, isClient]);
614+
609615
const closeBtnRef = useRef<HTMLButtonElement | null>(null);
610616
useEffect(() => {
611617
if (!open) return;

src/components/SigilExplorer.tsx

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import {
5151
type UsernameClaimRegistry,
5252
} from "../utils/usernameClaimRegistry";
5353
import { USERNAME_CLAIM_KIND, type UsernameClaimPayload } from "../types/usernameClaim";
54+
import { SIGIL_EXPLORER_OPEN_EVENT } from "../constants/sigilExplorer";
5455
import "./SigilExplorer.css";
5556

5657
/* ─────────────────────────────────────────────────────────────────────
@@ -119,6 +120,8 @@ type ApiInhaleResponse = {
119120
urls?: string[] | null;
120121
};
121122

123+
type SyncReason = "open" | "pulse" | "visible" | "focus" | "online" | "import";
124+
122125
/* ─────────────────────────────────────────────────────────────────────
123126
* Chakra tint system (per node)
124127
* ───────────────────────────────────────────────────────────────────── */
@@ -2286,6 +2289,7 @@ const SigilExplorer: React.FC = () => {
22862289
ownerHint?: string | null;
22872290
}>
22882291
>([]);
2292+
const syncNowRef = useRef<((reason: SyncReason) => Promise<void>) | null>(null);
22892293

22902294
const markInteracting = useCallback((ms: number) => {
22912295
const until = nowMs() + ms;
@@ -2785,7 +2789,7 @@ const SigilExplorer: React.FC = () => {
27852789
// ── BREATH LOOP: inhale (push) ⇄ exhale (pull)
27862790
const ac = new AbortController();
27872791

2788-
const syncOnce = async (reason: "open" | "pulse" | "visible" | "focus" | "online") => {
2792+
const syncOnce = async (reason: SyncReason) => {
27892793
if (unmounted.current) return;
27902794
if (!isOnline()) return;
27912795
if (syncInFlightRef.current) return;
@@ -2794,7 +2798,7 @@ const SigilExplorer: React.FC = () => {
27942798
if (scrollingRef.current) return;
27952799

27962800
// ✅ mobile stability: avoid heavy remote import while in interaction window
2797-
if (nowMs() < interactUntilRef.current && reason === "pulse") return;
2801+
if (nowMs() < interactUntilRef.current && (reason === "pulse" || reason === "import")) return;
27982802

27992803
syncInFlightRef.current = true;
28002804

@@ -2846,7 +2850,7 @@ const SigilExplorer: React.FC = () => {
28462850
const sealNow = remoteSealRef.current;
28472851
const shouldFullSeed =
28482852
reason === "open" ||
2849-
((reason === "visible" || reason === "focus" || reason === "online") &&
2853+
((reason === "visible" || reason === "focus" || reason === "online" || reason === "import") &&
28502854
sealNow !== lastFullSeedSealRef.current);
28512855

28522856
if (shouldFullSeed) {
@@ -2859,6 +2863,8 @@ const SigilExplorer: React.FC = () => {
28592863
}
28602864
};
28612865

2866+
syncNowRef.current = syncOnce;
2867+
28622868
// OPEN: do a full inhale seed immediately (guarantees repopulation power)
28632869
seedInhaleFromRegistry();
28642870
void syncOnce("open");
@@ -2890,11 +2896,28 @@ const SigilExplorer: React.FC = () => {
28902896
flushTimerRef.current = null;
28912897
window.clearInterval(intervalId);
28922898
ac.abort();
2899+
syncNowRef.current = null;
28932900
unmounted.current = true;
28942901
};
28952902
// eslint-disable-next-line react-hooks/exhaustive-deps
28962903
}, [bump, markInteracting, scheduleUiFlush, setLastAddedSafe]);
28972904

2905+
const requestImmediateSync = useCallback(
2906+
(reason: SyncReason) => {
2907+
const fn = syncNowRef.current;
2908+
if (fn) void fn(reason);
2909+
},
2910+
[],
2911+
);
2912+
2913+
useEffect(() => {
2914+
if (!hasWindow) return;
2915+
2916+
const onOpen = () => requestImmediateSync("visible");
2917+
window.addEventListener(SIGIL_EXPLORER_OPEN_EVENT, onOpen);
2918+
return () => window.removeEventListener(SIGIL_EXPLORER_OPEN_EVENT, onOpen);
2919+
}, [requestImmediateSync]);
2920+
28982921
const forest = useMemo(() => buildForest(memoryRegistry), [registryRev]);
28992922

29002923
const phiTotalsByPulse = useMemo((): ReadonlyMap<number, number> => {
@@ -3102,11 +3125,13 @@ const SigilExplorer: React.FC = () => {
31023125
} else if (urls.length > 0) {
31033126
forceInhaleUrls(urls);
31043127
}
3128+
3129+
requestImmediateSync("import");
31053130
} catch {
31063131
// ignore
31073132
}
31083133
},
3109-
[bump, markInteracting, setLastAddedSafe],
3134+
[bump, markInteracting, requestImmediateSync, setLastAddedSafe],
31103135
);
31113136

31123137
const handleExport = useCallback(() => {

src/constants/sigilExplorer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const SIGIL_EXPLORER_OPEN_EVENT = "sigil:explorer:open";

0 commit comments

Comments
 (0)