/** * @file gitChanged.ts * @description One window event, `agenthub:git-changed` `{laneId, action}`, * fired after EVERY git write the dashboard sends for a workspace * (`api.lanes.gitAction` fires it, success or failure — a failed shelve or * a conflicted restore still moved files). Every view that shows that * workspace's git state — the Changes panel of each repo, the header's facts * (`useLaneGitFacts`, the rail counts included), the workspace home's branch — * listens and reads again, so a switch made in one of them is never left * stale in another for a poll interval. The views do not share a callback; * this is the bus between them. * @author Nguyễn Ngọc Trí Vĩ */ import { useEffect, useRef } from "react"; export const GIT_CHANGED_EVENT = "agenthub:git-changed"; export interface GitChangedDetail { laneId: number; /** The git action that ran (`switch`, `commit`, `shelve`, …). */ action: string; } /** Tell every view of `laneId` that its git state may have moved. */ export function emitGitChanged(laneId: number, action: string): void { if (typeof window === "undefined") return; window.dispatchEvent( new CustomEvent(GIT_CHANGED_EVENT, { detail: { laneId, action } }) ); } /** * Call `fn` after every git write on workspace `laneId`. The latest `fn` is * used, so a caller may pass an inline closure without resubscribing. */ export function useGitChanged(laneId: number, fn: (action: string) => void): void { const latest = useRef(fn); latest.current = fn; useEffect(() => { const on = (e: Event) => { const d = (e as CustomEvent).detail; if (d && d.laneId === laneId) latest.current(d.action); }; window.addEventListener(GIT_CHANGED_EVENT, on); return () => window.removeEventListener(GIT_CHANGED_EVENT, on); }, [laneId]); }