MCPcopy Create free account
hub / github.com/agegr/pi-web / AppShell

Function AppShell

components/AppShell.tsx:31–1290  ·  view source on GitHub ↗
()

Source from the content-addressed store, hash-verified

29 | { kind: "error"; message: string };
30
31export function AppShell() {
32 const router = useRouter();
33 const searchParams = useSearchParams();
34 const [initialNavigation] = useState(() => getInitialNavigation(searchParams));
35 const { isDark, toggleTheme } = useTheme();
36 const isMobile = useIsMobile();
37 const [selectedSession, setSelectedSession] = useState<SessionInfo | null>(null);
38 // When user clicks +, we only store the cwd — no fake session id
39 const [newSessionCwd, setNewSessionCwd] = useState<string | null>(null);
40 const [initialCwdStatus, setInitialCwdStatus] = useState<"idle" | "validating" | "ready" | "error">(
41 () => initialNavigation.requestedCwd ? "validating" : "idle",
42 );
43 const [initialCwdError, setInitialCwdError] = useState<string | null>(null);
44 const [refreshKey, setRefreshKey] = useState(0);
45 const [sessionKey, setSessionKey] = useState(0);
46 const [explorerRefreshKey, setExplorerRefreshKey] = useState(0);
47 const [modelsConfigOpen, setModelsConfigOpen] = useState(false);
48 const [modelsRefreshKey, setModelsRefreshKey] = useState(0);
49 const [skillsConfigOpen, setSkillsConfigOpen] = useState(false);
50 const [pluginsConfigOpen, setPluginsConfigOpen] = useState(false);
51 const [sidebarOpen, setSidebarOpen] = useState(true);
52 const [mobileSidebarReady, setMobileSidebarReady] = useState(false);
53 // On mobile the sidebar is an overlay drawer; hide it by default so the chat
54 // is visible on load. Runs once the breakpoint resolves after hydration.
55 useEffect(() => {
56 if (isMobile) setSidebarOpen(false);
57 }, [isMobile]);
58 useEffect(() => {
59 setMobileSidebarReady(true);
60 }, []);
61 const chatInputRef = useRef<ChatInputHandle | null>(null);
62 const topBarRef = useRef<HTMLDivElement>(null);
63
64 // Branch navigator state — populated by ChatWindow via onBranchDataChange
65 const [branchTree, setBranchTree] = useState<SessionTreeNode[]>([]);
66 const [branchActiveLeafId, setBranchActiveLeafId] = useState<string | null>(null);
67 const branchLeafChangeFnRef = useRef<((leafId: string | null) => void) | null>(null);
68
69 const handleBranchDataChange = useCallback((tree: SessionTreeNode[], activeLeafId: string | null, onLeafChange: (leafId: string | null) => void) => {
70 setBranchTree(tree);
71 setBranchActiveLeafId(activeLeafId);
72 branchLeafChangeFnRef.current = onLeafChange;
73 }, []);
74
75 const handleBranchLeafChange = useCallback((leafId: string | null) => {
76 branchLeafChangeFnRef.current?.(leafId);
77 }, []);
78
79 const [systemPrompt, setSystemPrompt] = useState<string | null>(null);
80 const systemBtnRef = useRef<HTMLButtonElement>(null);
81
82 const handleSystemPromptChange = useCallback((prompt: string | null) => {
83 setSystemPrompt(prompt);
84 }, []);
85
86 // Session stats (tokens + cost) — populated by ChatWindow, displayed in top bar
87 const [sessionStats, setSessionStats] = useState<SessionStatsInfo | null>(null);
88 const [autoNameStatus, setAutoNameStatus] = useState<AutoNameStatus>({ kind: "idle" });

Callers

nothing calls this directly

Calls 15

getInitialNavigationFunction · 0.90
useThemeFunction · 0.90
useIsMobileFunction · 0.90
copyTextFunction · 0.90
buildAtMentionTextFunction · 0.90
buildFileAtMentionsTextFunction · 0.90
getFileNameFunction · 0.90
syncWindowTitleFunction · 0.85
fmtFunction · 0.85
formatCompactFunction · 0.85
copyButtonFunction · 0.85

Tested by

no test coverage detected