diff --git a/lib/community-sync.ts b/lib/community-sync.ts index bc756a2..47b60ef 100644 --- a/lib/community-sync.ts +++ b/lib/community-sync.ts @@ -128,6 +128,7 @@ export class CommunitySync extends EventTarget { #reconnectDelay = 1000; #offlineStore: OfflineStore | null = null; #saveDebounceTimer: ReturnType | null = null; + #syncedDebounceTimer: ReturnType | null = null; #wsUrl: string | null = null; constructor(communitySlug: string, offlineStore?: OfflineStore) { @@ -757,7 +758,14 @@ export class CommunitySync extends EventTarget { } } - this.dispatchEvent(new CustomEvent("synced", { detail: { shapes } })); + // Debounce the synced event — during initial sync negotiation, #applyDocToDOM() + // is called for every Automerge sync message (100+ round-trips). Debounce to + // fire once after the burst settles. + if (this.#syncedDebounceTimer) clearTimeout(this.#syncedDebounceTimer); + this.#syncedDebounceTimer = setTimeout(() => { + this.#syncedDebounceTimer = null; + this.dispatchEvent(new CustomEvent("synced", { detail: { shapes } })); + }, 300); } /** diff --git a/lib/folk-rapp.ts b/lib/folk-rapp.ts index 42c6fba..6c3c35d 100644 --- a/lib/folk-rapp.ts +++ b/lib/folk-rapp.ts @@ -543,7 +543,12 @@ export class FolkRApp extends FolkShape { if (pathParts.length >= 1) this.#spaceSlug = pathParts[0]; } const space = this.#spaceSlug || "demo"; - const iframeUrl = `/${space}/${this.#moduleId}`; + // On subdomain URLs (jeff.rspace.online), the server prepends /{space}/ automatically, + // so the iframe should load /{moduleId} directly. Using /{space}/{moduleId} would + // double-prefix to /{space}/{space}/{moduleId} → 404. + const hostname = window.location.hostname; + const onSubdomain = hostname.split(".").length >= 3 && hostname.startsWith(space + "."); + const iframeUrl = onSubdomain ? `/${this.#moduleId}` : `/${space}/${this.#moduleId}`; const iframe = document.createElement("iframe"); iframe.className = "rapp-iframe";