let nextScopeId = 0 let nextLayerOrder = 0 export interface CreateOverlayScopeOptions { name?: string } export interface OverlayLayer { dismiss: () => void dismissible: boolean id: string order: number } /** UI-agnostic storage shared by independently imported overlay features. */ export class OverlayScope { readonly id: string readonly name?: string private readonly channels = new Map() private readonly layers = new Map() constructor({ name }: CreateOverlayScopeOptions = {}) { this.id = `overlay-scope-${nextScopeId++}` this.name = name } /** @internal Used by atomic overlay feature modules. */ getChannel(key: symbol, create: () => Channel): Channel { const existing = this.channels.get(key) if (existing) return existing as Channel const channel = create() this.channels.set(key, channel) return channel } /** @internal Registers a visible layer for navigation interception. */ registerLayer(layer: Omit) { const registered = { ...layer, order: nextLayerOrder++ } this.layers.set(registered.id, registered) return registered.order } /** @internal Removes a layer after it has settled. */ unregisterLayer(id: string) { this.layers.delete(id) } getTopmostLayer() { let topmost: OverlayLayer | undefined for (const layer of this.layers.values()) { if (!topmost || layer.order > topmost.order) topmost = layer } return topmost } /** Cancels navigation while a layer is open and dismisses it when allowed. */ interceptNavigation() { const layer = this.getTopmostLayer() if (!layer) return false if (layer.dismissible) layer.dismiss() return true } } export function createOverlayScope(options?: CreateOverlayScopeOptions) { return new OverlayScope(options) } export const globalOverlayScope = createOverlayScope({ name: "global" })