forked from CopilotKit/CopilotKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathframework-provider.tsx
More file actions
144 lines (128 loc) · 4.37 KB
/
Copy pathframework-provider.tsx
File metadata and controls
144 lines (128 loc) · 4.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"use client";
// FrameworkProvider — tracks the currently "active" agentic backend.
//
// IMPORTANT: `framework` is STRICTLY URL-derived. It's non-null only when
// the user is actually on a framework-scoped route (`/<framework>/...`).
// On `/docs/...`, `/`, and other non-scoped routes, `framework` is null
// and the page renders the "no agentic backend selected" state.
//
// `storedFramework` is a separate, advisory signal: the user's last
// remembered choice from localStorage. Consumers use it to mark "this
// was your last pick" (e.g. highlight that card in the framework picker)
// WITHOUT treating it as the active framework. Visiting `/docs/` after
// previously picking LangChain must still show the unselected state —
// only explicit navigation to `/langgraph-python/...` (or clicking the
// card) makes LangChain active.
//
// Whenever the URL asserts a framework, we persist it as the new
// storedFramework so the preference carries.
import React, {
createContext,
useContext,
useEffect,
useMemo,
useState,
} from "react";
import { usePathname } from "next/navigation";
export interface FrameworkContextValue {
/**
* Currently ACTIVE framework — strictly URL-derived. Non-null only on
* `/<framework>/...` routes. Consumers that render "is this a
* framework-scoped view?" chrome (selectors, banners, snippets) should
* branch on this field.
*/
framework: string | null;
/**
* Last REMEMBERED framework from localStorage — advisory, does NOT
* auto-activate. Use to mark the user's last pick in a picker UI
* without implying the current view is scoped to it.
*/
storedFramework: string | null;
/** All known framework slugs derived from the registry. */
knownFrameworks: string[];
/** Persist a new framework preference (does not navigate). */
setStoredFramework: (slug: string | null) => void;
}
const FrameworkContext = createContext<FrameworkContextValue | null>(null);
const STORAGE_KEY = "selectedFramework";
function readStoredFramework(): string | null {
if (typeof window === "undefined") return null;
try {
return window.localStorage.getItem(STORAGE_KEY);
} catch {
return null;
}
}
function writeStoredFramework(slug: string | null) {
if (typeof window === "undefined") return;
try {
if (slug === null) {
window.localStorage.removeItem(STORAGE_KEY);
} else {
window.localStorage.setItem(STORAGE_KEY, slug);
}
} catch {
// localStorage may be unavailable (SSR, private mode, etc.) — silent no-op
}
}
export function FrameworkProvider({
children,
knownFrameworks,
}: {
children: React.ReactNode;
knownFrameworks: string[];
}) {
const pathname = usePathname() ?? "";
const urlFramework = useMemo(() => {
const first = pathname.split("/").filter(Boolean)[0];
if (first && knownFrameworks.includes(first)) return first;
return null;
}, [pathname, knownFrameworks]);
const [stored, setStored] = useState<string | null>(null);
// Hydrate stored framework on client mount
useEffect(() => {
setStored(readStoredFramework());
}, []);
// Whenever the URL asserts a framework, persist it so the preference
// follows the user when they navigate back to /docs/*
useEffect(() => {
if (urlFramework && urlFramework !== stored) {
writeStoredFramework(urlFramework);
setStored(urlFramework);
}
}, [urlFramework, stored]);
// ACTIVE framework is strictly URL-derived. localStorage NEVER promotes
// itself into `framework` — it lives in `storedFramework` where it can
// be shown as "your last pick" without implying the current view is
// scoped to it.
const framework = urlFramework;
const setStoredFramework = (slug: string | null) => {
writeStoredFramework(slug);
setStored(slug);
};
const value: FrameworkContextValue = {
framework,
storedFramework: stored,
knownFrameworks,
setStoredFramework,
};
return (
<FrameworkContext.Provider value={value}>
{children}
</FrameworkContext.Provider>
);
}
export function useFramework(): FrameworkContextValue {
const ctx = useContext(FrameworkContext);
if (!ctx) {
// Graceful fallback for trees that forgot to wrap in the provider —
// return a neutral, read-only value rather than throwing.
return {
framework: null,
storedFramework: null,
knownFrameworks: [],
setStoredFramework: () => {},
};
}
return ctx;
}