fix: resolve type error in MobileNav and PluginTabContent components

This commit is contained in:
Haileyesus
2026-03-09 12:59:52 +03:00
parent ff45a1cfd7
commit 77fb193598
2 changed files with 49 additions and 16 deletions

View File

@@ -1,13 +1,35 @@
import { useState, useRef, useEffect, Dispatch, SetStateAction } from 'react';
import { MessageSquare, Folder, Terminal, GitBranch, ClipboardCheck, Ellipsis, Puzzle, Box, Database, Globe, Wrench, Zap, BarChart3 } from 'lucide-react';
import { useState, useRef, useEffect, type Dispatch, type SetStateAction } from 'react';
import {
MessageSquare,
Folder,
Terminal,
GitBranch,
ClipboardCheck,
Ellipsis,
Puzzle,
Box,
Database,
Globe,
Wrench,
Zap,
BarChart3,
type LucideIcon,
} from 'lucide-react';
import { useTasksSettings } from '../../contexts/TasksSettingsContext';
import { usePlugins } from '../../contexts/PluginsContext';
import { AppTab } from '../../types/app';
const PLUGIN_ICON_MAP = {
const PLUGIN_ICON_MAP: Record<string, LucideIcon> = {
Puzzle, Box, Database, Globe, Terminal, Wrench, Zap, BarChart3, Folder, MessageSquare, GitBranch,
};
type CoreTabId = Exclude<AppTab, `plugin:${string}` | 'preview'>;
type CoreNavItem = {
id: CoreTabId;
icon: LucideIcon;
label: string;
};
type MobileNavProps = {
activeTab: AppTab;
setActiveTab: Dispatch<SetStateAction<AppTab>>;
@@ -19,7 +41,7 @@ export default function MobileNav({ activeTab, setActiveTab, isInputFocused }: M
const shouldShowTasksTab = Boolean(tasksEnabled && isTaskMasterInstalled);
const { plugins } = usePlugins();
const [moreOpen, setMoreOpen] = useState(false);
const moreRef = useRef(null);
const moreRef = useRef<HTMLDivElement | null>(null);
const enabledPlugins = plugins.filter((p) => p.enabled);
const hasPlugins = enabledPlugins.length > 0;
@@ -28,8 +50,9 @@ export default function MobileNav({ activeTab, setActiveTab, isInputFocused }: M
// Close the menu on outside tap
useEffect(() => {
if (!moreOpen) return;
const handleTap = (e) => {
if (moreRef.current && !moreRef.current.contains(e.target)) {
const handleTap = (e: PointerEvent) => {
const target = e.target;
if (moreRef.current && target instanceof Node && !moreRef.current.contains(target)) {
setMoreOpen(false);
}
};
@@ -38,18 +61,21 @@ export default function MobileNav({ activeTab, setActiveTab, isInputFocused }: M
}, [moreOpen]);
// Close menu when a plugin tab is selected
const selectPlugin = (name) => {
setActiveTab(`plugin:${name}`);
const selectPlugin = (name: string) => {
const pluginTab = `plugin:${name}` as AppTab;
setActiveTab(pluginTab);
setMoreOpen(false);
};
const coreItems = [
const baseCoreItems: CoreNavItem[] = [
{ id: 'chat', icon: MessageSquare, label: 'Chat' },
{ id: 'shell', icon: Terminal, label: 'Shell' },
{ id: 'files', icon: Folder, label: 'Files' },
{ id: 'git', icon: GitBranch, label: 'Git' },
...(shouldShowTasksTab ? [{ id: 'tasks', icon: ClipboardCheck, label: 'Tasks' }] : []),
];
const coreItems: CoreNavItem[] = shouldShowTasksTab
? [...baseCoreItems, { id: 'tasks', icon: ClipboardCheck, label: 'Tasks' }]
: baseCoreItems;
return (
<div

View File

@@ -24,10 +24,16 @@ function buildContext(
return {
theme: isDarkMode ? 'dark' : 'light',
project: selectedProject
? { name: selectedProject.name, path: selectedProject.fullPath || selectedProject.path }
? {
name: selectedProject.name,
path: selectedProject.fullPath || selectedProject.path || '',
}
: null,
session: selectedSession
? { id: selectedSession.id, title: selectedSession.title }
? {
id: selectedSession.id,
title: selectedSession.title || selectedSession.name || selectedSession.id,
}
: null,
};
}
@@ -44,7 +50,7 @@ export default function PluginTabContent({
// Stable refs so effects don't need context values in their dep arrays
const contextRef = useRef<PluginContext>(buildContext(isDarkMode, selectedProject, selectedSession));
const contextCallbacksRef = useRef<Set<(ctx: PluginContext) => void>>(new Set());
const moduleRef = useRef<any>(null);
const plugin = plugins.find(p => p.name === pluginName);
@@ -65,6 +71,7 @@ export default function PluginTabContent({
let active = true;
const container = containerRef.current;
const entryFile = plugin?.entry ?? 'index.js';
const contextCallbacks = contextCallbacksRef.current;
(async () => {
try {
@@ -86,8 +93,8 @@ export default function PluginTabContent({
get context(): PluginContext { return contextRef.current; },
onContextChange(cb: (ctx: PluginContext) => void): () => void {
contextCallbacksRef.current.add(cb);
return () => contextCallbacksRef.current.delete(cb);
contextCallbacks.add(cb);
return () => contextCallbacks.delete(cb);
},
async rpc(method: string, path: string, body?: unknown): Promise<unknown> {
@@ -125,7 +132,7 @@ export default function PluginTabContent({
return () => {
active = false;
try { moduleRef.current?.unmount?.(container); } catch { /* ignore */ }
contextCallbacksRef.current.clear();
contextCallbacks.clear();
moduleRef.current = null;
};
}, [pluginName, plugin?.entry, plugin?.enabled]); // re-mount when plugin or enabled state changes