mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 12:17:45 +02:00
[Haik]: refactor: restructure Dashboard page from flat file layout into nested component directories — move DashboardCanvas, DashboardHeader, DashboardHeaderParts, DashboardToolbar, ToolbarButtons, HistoryPanel, ViewPickerPanel, CanvasControls, and DashboardViewCard into DashboardCanvas/components/ subtree with dedicated folders for DashboardToolbar and DashboardHeader; consolidate shared types (CardType, CanvasActions, TetherInfo) from useDashboardSelection, useCanvasControls, and useTetherPaths into types/types.ts; extract duplicated formatRelativeTime into utils/formatRelativeTime.ts; update all import paths across hooks, shared modules, and DashboardCard
This commit is contained in:
@@ -15,7 +15,7 @@ import { useDashboardInit } from './hooks/useDashboardInit';
|
||||
import { useSubAgentAutoReveal } from './hooks/useSubAgentAutoReveal';
|
||||
import { useTetherPaths } from './hooks/useTetherPaths';
|
||||
import { useToolbarActions } from './hooks/useToolbarActions';
|
||||
import DashboardCanvas from './DashboardCanvas';
|
||||
import DashboardCanvas from './DashboardCanvas/DashboardCanvas';
|
||||
|
||||
const SELECT_ATTR = 'data-select-type';
|
||||
|
||||
|
||||
+9
-9
@@ -2,17 +2,17 @@ import React from 'react';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import Box from '@mui/material/Box';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import AgentCard from './AgentCard/AgentCard';
|
||||
import DashboardViewCard from './DashboardViewCard';
|
||||
import BrowserCard from './BrowserCard/BrowserCard';
|
||||
import CanvasControls from './CanvasControls';
|
||||
import DashboardToolbar from './DashboardToolbar';
|
||||
import DashboardHeader from './DashboardHeader';
|
||||
import AgentCard from '@/app/pages/Dashboard/AgentCard/AgentCard';
|
||||
import DashboardViewCard from './components/DashboardViewCard';
|
||||
import BrowserCard from '@/app/pages/Dashboard/BrowserCard/BrowserCard';
|
||||
import CanvasControls from './components/CanvasControls';
|
||||
import DashboardToolbar from './components/DashboardToolbar/DashboardToolbar';
|
||||
import DashboardHeader from './components/DashboardHeader/DashboardHeader';
|
||||
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
|
||||
import { EXPANDED_CARD_MIN_H, DEFAULT_CARD_W, GRID_GAP } from '@/shared/state/dashboardLayoutSlice';
|
||||
import type { TetherInfo } from './hooks/useTetherPaths';
|
||||
import type { CardType } from './useDashboardSelection';
|
||||
import type { CanvasActions } from './useCanvasControls';
|
||||
import type { TetherInfo } from '@/app/pages/Dashboard/types/types';
|
||||
import type { CardType } from '@/app/pages/Dashboard/types/types';
|
||||
import type { CanvasActions } from '@/app/pages/Dashboard/types/types';
|
||||
|
||||
const TETHER_FADE_MS = 2500;
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import AddIcon from '@mui/icons-material/Add';
|
||||
import FitScreenIcon from '@mui/icons-material/FitScreen';
|
||||
import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
|
||||
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
|
||||
import type { CanvasActions } from './useCanvasControls';
|
||||
import type { CanvasActions } from '@/app/pages/Dashboard/types/types';
|
||||
|
||||
interface Props {
|
||||
zoom: number;
|
||||
+1
-1
@@ -10,7 +10,7 @@ import { useClaudeTokens } from '@/shared/styles/ThemeContext';
|
||||
import type { AgentSession } from '@/shared/state/agentsSlice';
|
||||
import type { CardPosition, ViewCardPosition, BrowserCardPosition } from '@/shared/state/dashboardLayoutSlice';
|
||||
import type { App } from '@/shared/backend-bridge/apps/app_builder';
|
||||
import type { CanvasActions } from './useCanvasControls';
|
||||
import type { CanvasActions } from '@/app/pages/Dashboard/types/types';
|
||||
import { STATUS_DOT, cleanUrl, CategoryGroup, ItemRow } from './DashboardHeaderParts';
|
||||
|
||||
interface DashboardHeaderProps {
|
||||
+8
-9
@@ -1,13 +1,12 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import ChatInput from '@/app/pages/AgentChat/ChatInput/ChatInput';
|
||||
import type { Props } from './toolbarShared';
|
||||
import { MotionBox, TOOLBAR_OWNER_ID } from './toolbarShared';
|
||||
import { useDashboardToolbar } from './useDashboardToolbar';
|
||||
import HistoryPanel from './HistoryPanel';
|
||||
import ViewPickerPanel from './ViewPickerPanel';
|
||||
import ToolbarButtons from './ToolbarButtons';
|
||||
import { useDashboardToolbar, TOOLBAR_OWNER_ID, ToolbarProps } from './useDashboardToolbar';
|
||||
import HistoryPanel from './components/HistoryPanel';
|
||||
import ViewPickerPanel from './components/ViewPickerPanel';
|
||||
import ToolbarButtons from './components/ToolbarButtons/ToolbarButtons';
|
||||
|
||||
const DashboardToolbar = React.forwardRef<HTMLDivElement, Props>(
|
||||
const DashboardToolbar = React.forwardRef<HTMLDivElement, ToolbarProps>(
|
||||
({ inputOpen, onNewAgent, onCancel, onSend, onAddView, onHistoryResume, onAddBrowser, dashboardId }, ref) => {
|
||||
const {
|
||||
c, containerRef, searchInputRef, historyInputRef, historyListRef,
|
||||
@@ -26,7 +25,7 @@ const DashboardToolbar = React.forwardRef<HTMLDivElement, Props>(
|
||||
React.useImperativeHandle(ref, () => containerRef.current!, []);
|
||||
|
||||
return (
|
||||
<MotionBox
|
||||
<motion.div
|
||||
ref={containerRef}
|
||||
layout
|
||||
transition={{ layout: { duration: 0.15, ease: [0.25, 0.1, 0.25, 1] } }}
|
||||
@@ -87,7 +86,7 @@ const DashboardToolbar = React.forwardRef<HTMLDivElement, Props>(
|
||||
c={c}
|
||||
/>
|
||||
)}
|
||||
</MotionBox>
|
||||
</motion.div>
|
||||
);
|
||||
},
|
||||
);
|
||||
+1
-1
@@ -5,7 +5,7 @@ import InputBase from '@mui/material/InputBase';
|
||||
import CircularProgress from '@mui/material/CircularProgress';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import type { ClaudeTokens } from '@/shared/styles/claudeTokens';
|
||||
import { formatRelativeTime } from './toolbarShared';
|
||||
import { formatRelativeTime } from '@/app/pages/Dashboard/utils/formatRelativeTime';
|
||||
|
||||
interface HistoryPanelProps {
|
||||
historyInputRef: React.RefObject<HTMLInputElement>;
|
||||
+13
-11
@@ -6,7 +6,9 @@ import StickyNote2OutlinedIcon from '@mui/icons-material/StickyNote2Outlined';
|
||||
import HistoryRoundedIcon from '@mui/icons-material/HistoryRounded';
|
||||
import LanguageIcon from '@mui/icons-material/Language';
|
||||
import type { ClaudeTokens } from '@/shared/styles/claudeTokens';
|
||||
import { WarmTooltip, BTN } from './toolbarShared';
|
||||
import { WarmTooltip } from './WarmTooltip';
|
||||
|
||||
const BUTTON_SIZE = 40;
|
||||
|
||||
interface ToolbarButtonsProps {
|
||||
onNewAgent: () => void;
|
||||
@@ -36,8 +38,8 @@ export default function ToolbarButtons({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: BTN,
|
||||
height: BTN,
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
borderRadius: `${c.radius.lg}px`,
|
||||
bgcolor: c.accent.primary,
|
||||
color: '#fff',
|
||||
@@ -71,8 +73,8 @@ export default function ToolbarButtons({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: BTN,
|
||||
height: BTN,
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
borderRadius: `${c.radius.md}px`,
|
||||
color: c.text.tertiary,
|
||||
cursor: 'pointer',
|
||||
@@ -104,8 +106,8 @@ export default function ToolbarButtons({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: BTN,
|
||||
height: BTN,
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
borderRadius: `${c.radius.md}px`,
|
||||
color: c.text.tertiary,
|
||||
cursor: 'pointer',
|
||||
@@ -137,8 +139,8 @@ export default function ToolbarButtons({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: BTN,
|
||||
height: BTN,
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
borderRadius: `${c.radius.md}px`,
|
||||
color: c.text.tertiary,
|
||||
cursor: 'pointer',
|
||||
@@ -169,8 +171,8 @@ export default function ToolbarButtons({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: BTN,
|
||||
height: BTN,
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
borderRadius: `${c.radius.md}px`,
|
||||
color: c.text.tertiary,
|
||||
opacity: 0.45,
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import React from 'react';
|
||||
import Tooltip, { tooltipClasses } from '@mui/material/Tooltip';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import type { ClaudeTokens } from '@/shared/styles/claudeTokens';
|
||||
|
||||
export const WarmTooltip = styled(
|
||||
({ className, ...props }: React.ComponentProps<typeof Tooltip> & { className?: string }) => (
|
||||
<Tooltip {...props} classes={{ popper: className }} />
|
||||
)
|
||||
)<{ tokens: ClaudeTokens }>(({ tokens: c }) => ({
|
||||
[`& .${tooltipClasses.tooltip}`]: {
|
||||
backgroundColor: c.bg.inverse,
|
||||
color: c.text.inverse,
|
||||
fontFamily: c.font.sans,
|
||||
fontSize: '0.78rem',
|
||||
fontWeight: 500,
|
||||
padding: '6px 12px',
|
||||
borderRadius: c.radius.md,
|
||||
boxShadow: c.shadow.md,
|
||||
letterSpacing: '0.01em',
|
||||
},
|
||||
[`& .${tooltipClasses.arrow}`]: {
|
||||
color: c.bg.inverse,
|
||||
},
|
||||
}));
|
||||
+24
-3
@@ -6,12 +6,33 @@ import { useAppDispatch, useAppSelector } from '@/shared/hooks';
|
||||
import { clearHistorySearch } from '@/shared/state/agentsSlice';
|
||||
import { GET_HISTORY } from '@/shared/backend-bridge/apps/agents';
|
||||
import type { App } from '@/shared/backend-bridge/apps/app_builder';
|
||||
import type { Props } from './toolbarShared';
|
||||
import { TOOLBAR_OWNER_ID, HISTORY_PAGE_SIZE } from './toolbarShared';
|
||||
|
||||
export interface ToolbarProps {
|
||||
inputOpen: boolean;
|
||||
onNewAgent: () => void;
|
||||
onCancel: () => void;
|
||||
onSend: (
|
||||
prompt: string,
|
||||
mode: string,
|
||||
model: string,
|
||||
images?: Array<{ data: string; media_type: string }>,
|
||||
contextPaths?: ContextPath[],
|
||||
forcedTools?: string[],
|
||||
attachedSkills?: Array<{ id: string; name: string; content: string }>,
|
||||
selectedBrowserIds?: string[],
|
||||
) => void;
|
||||
onAddView: (outputId: string) => void;
|
||||
onHistoryResume: (sessionId: string) => void;
|
||||
onAddBrowser: () => void;
|
||||
dashboardId?: string;
|
||||
}
|
||||
|
||||
export const TOOLBAR_OWNER_ID = '__toolbar__';
|
||||
const HISTORY_PAGE_SIZE = 20;
|
||||
|
||||
export function useDashboardToolbar({
|
||||
inputOpen, onCancel, onSend, onAddView, onHistoryResume, onAddBrowser, dashboardId,
|
||||
}: Omit<Props, 'onNewAgent'>) {
|
||||
}: Omit<ToolbarProps, 'onNewAgent'>) {
|
||||
const c = useClaudeTokens();
|
||||
const dispatch = useAppDispatch();
|
||||
const elementSelection = useElementSelection();
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState, useCallback, useRef } from 'react';
|
||||
import { useAppDispatch } from '@/shared/hooks';
|
||||
import { moveCards, bringToFront } from '@/shared/state/dashboardLayoutSlice';
|
||||
import type { CardType } from '../useDashboardSelection';
|
||||
import type { CardType } from '@/app/pages/Dashboard/types/types';
|
||||
|
||||
interface DragSelection {
|
||||
isSelected: (id: string) => boolean;
|
||||
|
||||
@@ -14,7 +14,7 @@ import { LIST_APPS } from '@/shared/backend-bridge/apps/app_builder';
|
||||
import { agentsWs } from '@/shared/ws/WebSocketManager';
|
||||
import { initBrowserCommandHandler } from '@/shared/browserCommandHandler';
|
||||
import { clearPendingBrowserUrl, clearPendingFocusAgentId } from '@/shared/state/tempStateSlice';
|
||||
import type { CanvasActions } from '../useCanvasControls';
|
||||
import type { CanvasActions } from '@/app/pages/Dashboard/types/types';
|
||||
|
||||
interface InitDeps {
|
||||
dashboardId: string | undefined;
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
placeCard,
|
||||
} from '@/shared/state/dashboardLayoutSlice';
|
||||
import { setClipboardCards, getClipboardCards, type ClipboardCard } from '@/shared/dashboardClipboard';
|
||||
import type { CardType } from '../useDashboardSelection';
|
||||
import type { CardType } from '@/app/pages/Dashboard/types/types';
|
||||
|
||||
interface KeyboardDeps {
|
||||
newAgentShortcut: string;
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
import { useMemo } from 'react';
|
||||
import type { RefObject } from 'react';
|
||||
import { EXPANDED_CARD_MIN_H } from '@/shared/state/dashboardLayoutSlice';
|
||||
|
||||
export interface TetherInfo {
|
||||
key: string;
|
||||
path: string;
|
||||
labelX: number;
|
||||
labelY: number;
|
||||
label: string;
|
||||
fading: boolean;
|
||||
}
|
||||
import type { TetherInfo } from '@/app/pages/Dashboard/types/types';
|
||||
|
||||
interface TetherDeps {
|
||||
glowingAgentCards: Record<string, { sourceId: string; fading: boolean; sourceYRatio?: number; label?: string }>;
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
} from '@/shared/state/dashboardLayoutSlice';
|
||||
import { GENERATE_DASHBOARD_NAME } from '@/shared/backend-bridge/apps/dashboards';
|
||||
import type { ContextPath } from '@/shared/state/agentsTypes';
|
||||
import type { CanvasActions } from '../useCanvasControls';
|
||||
import type { CanvasActions } from '@/app/pages/Dashboard/types/types';
|
||||
|
||||
interface ToolbarDeps {
|
||||
cards: Record<string, CardPosition>;
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import React from 'react';
|
||||
import Tooltip, { tooltipClasses } from '@mui/material/Tooltip';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import { motion } from 'framer-motion';
|
||||
import type { ClaudeTokens } from '@/shared/styles/claudeTokens';
|
||||
import type { ContextPath } from '@/shared/state/agentsTypes';
|
||||
|
||||
export interface Props {
|
||||
inputOpen: boolean;
|
||||
onNewAgent: () => void;
|
||||
onCancel: () => void;
|
||||
onSend: (
|
||||
prompt: string,
|
||||
mode: string,
|
||||
model: string,
|
||||
images?: Array<{ data: string; media_type: string }>,
|
||||
contextPaths?: ContextPath[],
|
||||
forcedTools?: string[],
|
||||
attachedSkills?: Array<{ id: string; name: string; content: string }>,
|
||||
selectedBrowserIds?: string[],
|
||||
) => void;
|
||||
onAddView: (outputId: string) => void;
|
||||
onHistoryResume: (sessionId: string) => void;
|
||||
onAddBrowser: () => void;
|
||||
dashboardId?: string;
|
||||
}
|
||||
|
||||
export const TOOLBAR_OWNER_ID = '__toolbar__';
|
||||
export const BTN = 40;
|
||||
export const HISTORY_PAGE_SIZE = 20;
|
||||
|
||||
export const WarmTooltip = styled(
|
||||
({ className, ...props }: React.ComponentProps<typeof Tooltip> & { className?: string }) => (
|
||||
<Tooltip {...props} classes={{ popper: className }} />
|
||||
)
|
||||
)<{ tokens: ClaudeTokens }>(({ tokens: c }) => ({
|
||||
[`& .${tooltipClasses.tooltip}`]: {
|
||||
backgroundColor: c.bg.inverse,
|
||||
color: c.text.inverse,
|
||||
fontFamily: c.font.sans,
|
||||
fontSize: '0.78rem',
|
||||
fontWeight: 500,
|
||||
padding: '6px 12px',
|
||||
borderRadius: c.radius.md,
|
||||
boxShadow: c.shadow.md,
|
||||
letterSpacing: '0.01em',
|
||||
},
|
||||
[`& .${tooltipClasses.arrow}`]: {
|
||||
color: c.bg.inverse,
|
||||
},
|
||||
}));
|
||||
|
||||
export const MotionBox = motion.div;
|
||||
|
||||
export function formatRelativeTime(dateStr: string | null): string {
|
||||
if (!dateStr) return '';
|
||||
const seconds = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1000);
|
||||
if (seconds < 60) return 'just now';
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
if (minutes < 60) return `${minutes}m ago`;
|
||||
const hours = Math.floor(minutes / 60);
|
||||
if (hours < 24) return `${hours}h ago`;
|
||||
const days = Math.floor(hours / 24);
|
||||
return `${days}d ago`;
|
||||
}
|
||||
@@ -1,6 +1,28 @@
|
||||
export interface TabLocalState {
|
||||
loading: boolean;
|
||||
canGoBack: boolean;
|
||||
canGoForward: boolean;
|
||||
}
|
||||
|
||||
loading: boolean;
|
||||
canGoBack: boolean;
|
||||
canGoForward: boolean;
|
||||
}
|
||||
|
||||
export interface TetherInfo {
|
||||
key: string;
|
||||
path: string;
|
||||
labelX: number;
|
||||
labelY: number;
|
||||
label: string;
|
||||
fading: boolean;
|
||||
}
|
||||
|
||||
export type CardType = 'agent' | 'view' | 'browser';
|
||||
|
||||
export interface CanvasActions {
|
||||
zoomIn: () => void;
|
||||
zoomOut: () => void;
|
||||
resetZoom: () => void;
|
||||
fitToView: () => void;
|
||||
fitToCards: (
|
||||
cardRects: Array<{ x: number; y: number; width: number; height: number }>,
|
||||
maxZoom?: number,
|
||||
animate?: boolean,
|
||||
) => void;
|
||||
}
|
||||
@@ -170,5 +170,3 @@ export function useCanvasControls(zoomSensitivity: number = 50) {
|
||||
actions,
|
||||
} as const;
|
||||
}
|
||||
|
||||
export type CanvasActions = ReturnType<typeof useCanvasControls>['actions'];
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useState, useCallback, useRef, useEffect, RefObject } from 'react';
|
||||
import type { CardPosition, ViewCardPosition, BrowserCardPosition } from '@/shared/state/dashboardLayoutSlice';
|
||||
|
||||
export type CardType = 'agent' | 'view' | 'browser';
|
||||
import type { CardType } from '@/app/pages/Dashboard/types/types';
|
||||
|
||||
interface SelectedCard {
|
||||
id: string;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
export function formatRelativeTime(dateStr: string | null): string {
|
||||
if (!dateStr) return '';
|
||||
const seconds = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1000);
|
||||
if (seconds < 60) return 'just now';
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
if (minutes < 60) return `${minutes}m ago`;
|
||||
const hours = Math.floor(minutes / 60);
|
||||
if (hours < 24) return `${hours}h ago`;
|
||||
const days = Math.floor(hours / 24);
|
||||
return `${days}d ago`;
|
||||
}
|
||||
|
||||
@@ -8,18 +8,7 @@ import DashboardIcon from '@mui/icons-material/Dashboard';
|
||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||
import { Dashboard } from '@/shared/state/dashboardsSlice';
|
||||
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
|
||||
|
||||
function formatRelativeTime(dateStr: string | null): string {
|
||||
if (!dateStr) return '';
|
||||
const seconds = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1000);
|
||||
if (seconds < 60) return 'just now';
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
if (minutes < 60) return `${minutes}m ago`;
|
||||
const hours = Math.floor(minutes / 60);
|
||||
if (hours < 24) return `${hours}h ago`;
|
||||
const days = Math.floor(hours / 24);
|
||||
return `${days}d ago`;
|
||||
}
|
||||
import { formatRelativeTime } from '@/app/pages/Dashboard/utils/formatRelativeTime';
|
||||
|
||||
interface DashboardCardProps {
|
||||
dashboard: Dashboard;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CardType } from '@/app/pages/Dashboard/useDashboardSelection';
|
||||
import type { CardType } from '@/app/pages/Dashboard/types/types';
|
||||
|
||||
export interface ClipboardCard {
|
||||
type: CardType;
|
||||
|
||||
Reference in New Issue
Block a user