Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit 9af7e7bf1ffa687c18dddab1d60e69e7f43ca145
parent 99e42237f2067d2aeaa428f56b661c7f97587b7b
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Wed,  1 Jul 2026 23:41:37 -0400

Phase 3: SearchDataContext provider (data source behind context)

TranscriptSearch now reads its summaries + subs-manifest feeds from a
SearchData provider instead of calling useSummaries()/useSubsManifest()
directly, so the component becomes mode-agnostic. SingleSiteDataProvider is a
thin same-origin wrapper (origin=""), leaving single-site behavior identical;
a MultiSiteDataProvider that merges feeds across origins lands with the
channelKey work in the next phase.

- common/components/SearchDataContext.tsx: useSearchData() + SingleSiteDataProvider
- TranscriptSearch.tsx: consume useSearchData() for summariesState + subsManifest
- export/app/page.tsx: wrap TranscriptSearch in SingleSiteDataProvider

Verified: 29/29 data-path e2e (browse-all, live-chat, modal-open, query-tree,
pwa-search, filter-profile-persistence) green; all packages typecheck.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Diffstat:
Acommon/components/SearchDataContext.tsx | 41+++++++++++++++++++++++++++++++++++++++++
Mcommon/components/TranscriptSearch.tsx | 11++++++-----
Mexport/app/page.tsx | 5++++-
3 files changed, 51 insertions(+), 6 deletions(-)

diff --git a/common/components/SearchDataContext.tsx b/common/components/SearchDataContext.tsx @@ -0,0 +1,41 @@ +"use client"; + +// Data source for TranscriptSearch. In single-site mode this is a thin wrapper +// over the same summaries/subs hooks the component used to call directly, so +// behavior is byte-for-byte unchanged. In hub mode (added in a later phase) a +// MultiSiteDataProvider merges these feeds across many origins and hands the +// component one unified, origin-qualified view — so TranscriptSearch itself +// stays mode-agnostic and reads everything from this context. + +import { createContext, useContext, type ReactNode } from "react"; +import { useSummaries, type SummariesState } from "./summariesCache"; +import { useSubsManifest } from "./subsCache"; +import type { SubsManifest } from "../lib/manifest"; + +export type SearchDataValue = { + // Summaries + cross-channel manifest. Merged across origins in hub mode. + summariesState: SummariesState; + // Cross-channel subs manifest (merged across origins in hub mode), or null. + subsManifest: SubsManifest | null; +}; + +const SearchDataContext = createContext<SearchDataValue | null>(null); + +export function useSearchData(): SearchDataValue { + const value = useContext(SearchDataContext); + if (!value) { + throw new Error("useSearchData must be used within a SearchData provider"); + } + return value; +} + +// Single-site data source: the built-in export site (same-origin, origin=""). +export function SingleSiteDataProvider({ children }: { children: ReactNode }) { + const summariesState = useSummaries(""); + const subsManifest = useSubsManifest("").data ?? null; + return ( + <SearchDataContext.Provider value={{ summariesState, subsManifest }}> + {children} + </SearchDataContext.Provider> + ); +} diff --git a/common/components/TranscriptSearch.tsx b/common/components/TranscriptSearch.tsx @@ -4,8 +4,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useWindowVirtualizer } from "@tanstack/react-virtual"; import { usePlayer } from "./PlayerProvider"; import { VirtualRow } from "./VirtualRow"; -import { useSummaries } from "./summariesCache"; -import { useChannelSubsManifests, useSubsManifest } from "./subsCache"; +import { useChannelSubsManifests } from "./subsCache"; +import { useSearchData } from "./SearchDataContext"; import type { LayerHit } from "./searchPipeline"; import { runQueryTree, @@ -257,15 +257,16 @@ export default function TranscriptSearch() { const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); + // Data feeds come from the SearchData provider (single-site or, in hub mode, + // merged across origins) so this component is mode-agnostic. + const { summariesState, subsManifest } = useSearchData(); const { manifest, summaries, loadedPages, pageCount, summariesReady } = - useSummaries(); + summariesState; const transcripts: Summary[] | null = summariesReady ? summaries : null; // We need subs manifests whenever any leaf has scope=chat OR a chat leaf // could be added (we eagerly load when the tab existed in v1). For v2 we // only load subs manifests when a chat leaf is actually in the tree. - const subsManifestQuery = useSubsManifest(); - const subsManifest = subsManifestQuery.data ?? null; const liveChatTotalCount = subsManifest?.liveChatTotalCount ?? 0; const hasSubs = subsManifest !== null && liveChatTotalCount > 0; const draftHasChatLeaf = useMemo( diff --git a/export/app/page.tsx b/export/app/page.tsx @@ -2,6 +2,7 @@ import { countTranscripts } from "yt-dlp-transcript-common/lib/transcripts"; import { PlayerProvider } from "yt-dlp-transcript-common/components/PlayerProvider"; import TranscriptModal from "yt-dlp-transcript-common/components/TranscriptModal"; import TranscriptSearch from "yt-dlp-transcript-common/components/TranscriptSearch"; +import { SingleSiteDataProvider } from "yt-dlp-transcript-common/components/SearchDataContext"; import { currentSite } from "./lib/site"; import NewSinceLastVisit from "./components/NewSinceLastVisit"; @@ -22,7 +23,9 @@ export default async function Home() { )} </div> <NewSinceLastVisit siteId={settings.siteId} count={count} /> - <TranscriptSearch /> + <SingleSiteDataProvider> + <TranscriptSearch /> + </SingleSiteDataProvider> </div> <TranscriptModal /> </PlayerProvider>