[Haik]: refactor: migrate frontend from flat file structure to nested AppShell architecture — moves DynamicIsland under TitleBar with CompactPill/ExpandedCard subgroups, consolidates Settings and Commands pages into AppShell/components with dedicated components/hooks/shared dirs, relocates Layout pieces (Sidebar, TitleBar, UpdateBanner) as AppShell children, and updates Main.tsx import path

This commit is contained in:
haikdc
2026-04-18 19:14:26 -07:00
parent decd61349d
commit 8d19cd64cf
36 changed files with 66 additions and 64 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ import {
setUpdateDownloaded,
setUpdateError,
} from '@/shared/state/updateSlice';
import AppShell from './components/Layout/AppShell';
import AppShell from './components/AppShell/AppShell';
import Dashboard from './pages/Dashboard/Dashboard';
import DashboardSelection from './pages/DashboardSelection/DashboardSelection';
import Skills from './pages/Skills/Skills';
@@ -6,7 +6,7 @@ import Snackbar from '@mui/material/Snackbar';
import Alert from '@mui/material/Alert';
import RestartAltIcon from '@mui/icons-material/RestartAlt';
import SystemUpdateAltIcon from '@mui/icons-material/SystemUpdateAlt';
import Settings from '@/app/pages/Settings/Settings';
import Settings from './components/Settings/Settings';
import { useAppDispatch, useAppSelector } from '@/shared/hooks';
import { LIST_DASHBOARDS } from '@/shared/backend-bridge/apps/dashboards';
import { LIST_APPS } from '@/shared/backend-bridge/apps/app_builder';
@@ -14,9 +14,9 @@ import { useClaudeTokens } from '@/shared/styles/ThemeContext';
import { useUpdateNotification } from './hooks/useUpdateNotification';
import { useSidebarResize } from './hooks/useSidebarResize';
import { useUrlInterception } from './hooks/useUrlInterception';
import TitleBar from './TitleBar';
import Sidebar from './Sidebar';
import UpdateBanner from './UpdateBanner';
import TitleBar from './components/TitleBar/TitleBar';
import Sidebar from './components/Sidebar';
import UpdateBanner from './components/UpdateBanner';
const AppShell: React.FC = () => {
const c = useClaudeTokens();
@@ -13,10 +13,10 @@ import Snackbar from '@mui/material/Snackbar';
import Alert from '@mui/material/Alert';
import SaveIcon from '@mui/icons-material/Save';
import CloseIcon from '@mui/icons-material/Close';
import { CommandsContent } from '@/app/pages/Commands/Commands';
import { CommandsTab } from './components/CommandsTab/CommandsTab';
import { useSettings } from './hooks/useSettings';
import GeneralTab from './GeneralTab';
import ModelsTab from './ModelsTab';
import GeneralTab from './components/GeneralTab/GeneralTab';
import ModelsTab from './components/ModelsTab/ModelsTab';
const Settings: React.FC = () => {
const s = useSettings();
@@ -93,7 +93,7 @@ const Settings: React.FC = () => {
<ModelsTab s={s} />
) : (
<Box sx={{ pt: 2.5, pb: 1, animation: 'fadeIn 0.2s ease', '@keyframes fadeIn': { from: { opacity: 0 }, to: { opacity: 1 } } }}>
<CommandsContent />
<CommandsTab />
</Box>
)}
</DialogContent>
@@ -2,11 +2,11 @@ import React from 'react';
import Box from '@mui/material/Box';
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
import { useCommands } from './hooks/useCommands';
import SlashCommandsSection from './SlashCommandsSection';
import AtCommandsSection from './AtCommandsSection';
import ShortcutsSection from './ShortcutsSection';
import SlashCommandsSection from './components/SlashCommandsSection';
import AtCommandsSection from './components/AtCommandsSection';
import ShortcutsSection from './components/ShortcutsSection';
export const CommandsContent: React.FC = () => {
export const CommandsTab: React.FC = () => {
const c = useClaudeTokens();
const { slashCommands, atCommands, modesMap, navShortcuts, actionShortcuts } = useCommands();
@@ -3,8 +3,8 @@ import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import Chip from '@mui/material/Chip';
import AlternateEmailIcon from '@mui/icons-material/AlternateEmail';
import { SectionHeader } from './CommandsHelpers';
import { AtCommand } from './commandsTypes';
import { SectionHeader } from './shared/CommandsHelpers';
import { AtCommand } from './shared/commandsTypes';
interface AtCommandsSectionProps {
atCommands: AtCommand[];
@@ -2,8 +2,8 @@ import React from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import KeyboardIcon from '@mui/icons-material/Keyboard';
import { KeyBadge, SectionHeader } from './CommandsHelpers';
import { Shortcut, SHORTCUTS } from './commandsTypes';
import { KeyBadge, SectionHeader } from './shared/CommandsHelpers';
import { Shortcut, SHORTCUTS } from './shared/commandsTypes';
interface ShortcutsSectionProps {
navShortcuts: Shortcut[];
@@ -5,8 +5,8 @@ import Chip from '@mui/material/Chip';
import PsychologyIcon from '@mui/icons-material/Psychology';
import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined';
import TerminalIcon from '@mui/icons-material/Terminal';
import { SectionHeader } from './CommandsHelpers';
import { SlashCommand } from './commandsTypes';
import { SectionHeader } from './shared/CommandsHelpers';
import { SlashCommand } from './shared/commandsTypes';
interface SlashCommandsSectionProps {
slashCommands: SlashCommand[];
@@ -9,7 +9,7 @@ import { getToolGroupIcon } from '@/app/pages/_shared/getToolGroupIcon';
import { LIST_APPS } from '@/shared/backend-bridge/apps/app_builder';
import { LIST_SKILLS } from '@/shared/backend-bridge/apps/skills';
import { LIST_MODES } from '@/shared/state/modesSlice';
import { SlashCommand, AtCommand, SHORTCUTS } from '../commandsTypes';
import { SlashCommand, AtCommand, SHORTCUTS } from '../components/shared/commandsTypes';
export function useCommands() {
const dispatch = useAppDispatch();
@@ -12,9 +12,9 @@ import LanguageIcon from '@mui/icons-material/Language';
import RestartAltIcon from '@mui/icons-material/RestartAlt';
import { RESET_SYSTEM_PROMPT } from '@/shared/backend-bridge/apps/settings';
import { DEFAULT_SYSTEM_PROMPT } from '@/shared/state/settingsSlice';
import InterfaceSection from './InterfaceSection';
import AboutSection from './AboutSection';
import type { UseSettingsReturn } from './hooks/useSettings';
import InterfaceSection from './components/InterfaceSection';
import AboutSection from './components/AboutSection';
import type { UseSettingsReturn } from '../../hooks/useSettings';
const GeneralTab: React.FC<{ s: UseSettingsReturn }> = ({ s }) => {
const { form, setForm, c, dispatch, modesList, browseFolder,
@@ -8,7 +8,7 @@ import ToggleButtonGroup from '@mui/material/ToggleButtonGroup';
import LightModeIcon from '@mui/icons-material/LightMode';
import DarkModeIcon from '@mui/icons-material/DarkMode';
import KeyboardIcon from '@mui/icons-material/Keyboard';
import type { UseSettingsReturn } from './hooks/useSettings';
import type { UseSettingsReturn } from '../../../hooks/useSettings';
const InterfaceSection: React.FC<{ s: UseSettingsReturn }> = ({ s }) => {
const { form, setForm, c, recordingShortcut, setRecordingShortcut,
@@ -7,8 +7,8 @@ import InputAdornment from '@mui/material/InputAdornment';
import VisibilityIcon from '@mui/icons-material/Visibility';
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import SubscriptionCards from './SubscriptionCards';
import type { UseSettingsReturn } from './hooks/useSettings';
import SubscriptionCards from './components/SubscriptionCards';
import type { UseSettingsReturn } from '../../hooks/useSettings';
const ModelsTab: React.FC<{ s: UseSettingsReturn }> = ({ s }) => {
const { c, form, setForm, showApiKey, setShowApiKey, fieldSx, labelSx, descSx } = s;
@@ -2,10 +2,10 @@ import React, { useMemo, useRef } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
import { SPRING_LAYOUT, SPRING_BOUNCE } from './islandTypes';
import { IdlePill } from './IdlePill';
import { CompactPill } from './CompactPill';
import { CompactActionablePill } from './CompactActionablePill';
import { ExpandedCard } from './ExpandedCard';
import { IdlePill } from './components/IdlePill';
import { CompactPill } from './components/CompactPill/CompactPill';
import { CompactActionablePill } from './components/CompactActionablePill';
import { ExpandedCard } from './components/ExpandedCard/ExpandedCard';
import { useDynamicIslandData } from './hooks/useDynamicIslandData';
import { useDynamicIslandActions } from './hooks/useDynamicIslandActions';
@@ -10,8 +10,8 @@ import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { motion } from 'framer-motion';
import { parseMcpToolName } from '@/app/pages/AgentChat/toolkit/approvalToolkit/utils';
import { useMcpToolMeta } from '@/app/pages/AgentChat/toolkit/approvalToolkit/utils/useMcpToolMeta';
import { SPRING_BOUNCE } from './islandTypes';
import type { ClaudeTokens } from './islandTypes';
import { SPRING_BOUNCE } from '@/app/components/AppShell/components/TitleBar/DynamicIsland/islandTypes';
import type { ClaudeTokens } from '@/app/components/AppShell/components/TitleBar/DynamicIsland/islandTypes';
import type { ApprovalRequest } from '@/shared/state/agentsSlice';
import { Terminal, FileText, FilePen, Search, MessageCircleQuestion, Wrench } from 'lucide-react';
@@ -0,0 +1,20 @@
import React from 'react';
import Box from '@mui/material/Box';
import type { ClaudeTokens } from '@/app/components/AppShell/components/TitleBar/DynamicIsland/islandTypes';
export const ActivityIndicator: React.FC<{ c: ClaudeTokens }> = ({ c }) => (
<Box
sx={{
width: 6,
height: 6,
borderRadius: '50%',
bgcolor: c.text.tertiary,
flexShrink: 0,
animation: 'subtlePulse 2.2s ease-in-out infinite',
'@keyframes subtlePulse': {
'0%, 100%': { opacity: 0.6, transform: 'scale(1)' },
'50%': { opacity: 1, transform: 'scale(1.15)' },
},
}}
/>
);
@@ -2,9 +2,9 @@ import React from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import { motion } from 'framer-motion';
import { ActivityIndicator } from './StatusDot';
import { SPRING_BOUNCE } from './islandTypes';
import type { ClaudeTokens } from './islandTypes';
import { ActivityIndicator } from './ActivityIndicator';
import { SPRING_BOUNCE } from '@/app/components/AppShell/components/TitleBar/DynamicIsland/islandTypes';
import type { ClaudeTokens } from '@/app/components/AppShell/components/TitleBar/DynamicIsland/islandTypes';
export const CompactPill: React.FC<{
c: ClaudeTokens;
@@ -6,8 +6,8 @@ import Tooltip from '@mui/material/Tooltip';
import StopCircleOutlinedIcon from '@mui/icons-material/StopCircleOutlined';
import CloseIcon from '@mui/icons-material/Close';
import { StatusDot } from './StatusDot';
import { STATUS_CONFIG } from './islandTypes';
import type { ClaudeTokens, TrackedAgent } from './islandTypes';
import { STATUS_CONFIG } from '@/app/components/AppShell/components/TitleBar/DynamicIsland/islandTypes';
import type { ClaudeTokens, TrackedAgent } from '@/app/components/AppShell/components/TitleBar/DynamicIsland/islandTypes';
export const AgentStatusRow: React.FC<{
agent: TrackedAgent;
@@ -6,7 +6,7 @@ import Collapse from '@mui/material/Collapse';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
import { AgentStatusRow } from './AgentStatusRow';
import type { ClaudeTokens, TrackedAgent } from './islandTypes';
import type { ClaudeTokens, TrackedAgent } from '@/app/components/AppShell/components/TitleBar/DynamicIsland/islandTypes';
export const CompletedAgentsList: React.FC<{
c: ClaudeTokens;
@@ -8,7 +8,7 @@ import { ApprovalRouter } from '@/app/pages/AgentChat/toolkit/approvalToolkit/Ap
import { BatchApprovalWrapper } from '@/app/pages/AgentChat/toolkit/approvalToolkit/BatchApprovalWrapper';
import { AgentStatusRow } from './AgentStatusRow';
import { CompletedAgentsList } from './CompletedAgentsList';
import type { ClaudeTokens, SessionApprovalGroup, TrackedAgent } from './islandTypes';
import type { ClaudeTokens, SessionApprovalGroup, TrackedAgent } from '@/app/components/AppShell/components/TitleBar/DynamicIsland/islandTypes';
export const ExpandedCard: React.FC<{
c: ClaudeTokens;
@@ -1,7 +1,7 @@
import React from 'react';
import Box from '@mui/material/Box';
import { STATUS_CONFIG } from './islandTypes';
import type { ClaudeTokens } from './islandTypes';
import { STATUS_CONFIG } from '@/app/components/AppShell/components/TitleBar/DynamicIsland/islandTypes';
import type { ClaudeTokens } from '@/app/components/AppShell/components/TitleBar/DynamicIsland/islandTypes';
export const StatusDot: React.FC<{ status: string; c: ClaudeTokens }> = ({ status, c }) => {
const cfg = STATUS_CONFIG[status];
@@ -26,21 +26,4 @@ export const StatusDot: React.FC<{ status: string; c: ClaudeTokens }> = ({ statu
}}
/>
);
};
export const ActivityIndicator: React.FC<{ c: ClaudeTokens }> = ({ c }) => (
<Box
sx={{
width: 6,
height: 6,
borderRadius: '50%',
bgcolor: c.text.tertiary,
flexShrink: 0,
animation: 'subtlePulse 2.2s ease-in-out infinite',
'@keyframes subtlePulse': {
'0%, 100%': { opacity: 0.6, transform: 'scale(1)' },
'50%': { opacity: 1, transform: 'scale(1.15)' },
},
}}
/>
);
};
@@ -4,7 +4,7 @@ import Typography from '@mui/material/Typography';
import Tooltip from '@mui/material/Tooltip';
import SearchIcon from '@mui/icons-material/Search';
import { motion } from 'framer-motion';
import type { ClaudeTokens } from './islandTypes';
import type { ClaudeTokens } from '@/app/components/AppShell/components/TitleBar/DynamicIsland/islandTypes';
export const IdlePill: React.FC<{ c: ClaudeTokens }> = ({ c }) => (
<motion.div
@@ -8,7 +8,7 @@ import {
} from '@/shared/state/agentsSlice';
import { HANDLE_APPROVAL, STOP_AGENT } from '@/shared/backend-bridge/apps/agents';
import { setPendingFocusAgentId } from '@/shared/state/tempStateSlice';
import type { IslandState, SessionApprovalGroup } from '../islandTypes';
import type { IslandState, SessionApprovalGroup } from '@/app/components/AppShell/components/TitleBar/DynamicIsland/islandTypes';
export function useDynamicIslandActions(
groups: SessionApprovalGroup[],
@@ -1,7 +1,7 @@
import { useMemo, useState } from 'react';
import { useAppSelector } from '@/shared/hooks';
import type { HistorySession } from '@/shared/state/agentsSlice';
import type { IslandState, SessionApprovalGroup, TrackedAgent } from '../islandTypes';
import type { IslandState, SessionApprovalGroup, TrackedAgent } from '@/app/components/AppShell/components/TitleBar/DynamicIsland/islandTypes';
export function useDynamicIslandData() {
const sessions = useAppSelector((state) => state.agents.sessions);
@@ -7,7 +7,7 @@ import Tooltip from '@mui/material/Tooltip';
import ViewSidebarOutlinedIcon from '@mui/icons-material/ViewSidebarOutlined';
import ArrowBackOutlinedIcon from '@mui/icons-material/ArrowBackOutlined';
import ArrowForwardOutlinedIcon from '@mui/icons-material/ArrowForwardOutlined';
import DynamicIsland from '@/app/components/DynamicIsland';
import DynamicIsland from './DynamicIsland/DynamicIsland';
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
interface TitleBarProps {
@@ -1 +0,0 @@
export { default } from './DynamicIsland';