feat: Implement dynamic plugin definition loading from remote/local sources and standardize action IDs using plugin metadata.

This commit is contained in:
2026-02-02 12:05:52 -05:00
parent 54c34b6f7d
commit 7fd0d97a67
23 changed files with 270 additions and 243 deletions

View File

@@ -188,7 +188,7 @@ export function EventTimeline() {
<div className="text-[10px] font-mono opacity-70 mb-1">
{new Date(event.timestamp).toLocaleTimeString()}
</div>
{event.data && (
{!!event.data && (
<div className="bg-muted/50 p-1 rounded font-mono text-[9px] max-w-[200px] break-all">
{JSON.stringify(event.data as object).slice(0, 100)}
</div>

View File

@@ -114,8 +114,8 @@ export function PlaybackPlayer({ src }: PlaybackPlayerProps) {
step={0.1}
onValueChange={([val]) => {
if (videoRef.current) {
videoRef.current.currentTime = val;
setCurrentTime(val);
videoRef.current.currentTime = val ?? 0;
setCurrentTime(val ?? 0);
}
}}
className="cursor-pointer"

View File

@@ -156,7 +156,7 @@ export function TrialAnalysisView({ trial }: TrialAnalysisViewProps) {
{event.eventType.replace(/_/g, " ")}
</span>
</div>
{event.data && (
{!!event.data && (
<div className="text-[10px] text-muted-foreground bg-muted p-1.5 rounded border font-mono whitespace-pre-wrap break-all opacity-80 group-hover:opacity-100">
{JSON.stringify(event.data as object, null, 1).replace(/"/g, '').replace(/[{}]/g, '').trim()}
</div>

View File

@@ -255,10 +255,10 @@ export function RobotActionsPanel({
// Look for ROS2 configuration in the action definition
const actionConfig = (actionDef as any).ros2
? {
topic: (actionDef as any).ros2.topic,
messageType: (actionDef as any).ros2.messageType,
payloadMapping: (actionDef as any).ros2.payloadMapping,
}
topic: (actionDef as any).ros2.topic,
messageType: (actionDef as any).ros2.messageType,
payloadMapping: (actionDef as any).ros2.payloadMapping,
}
: undefined;
await executeRosAction(
@@ -635,7 +635,7 @@ export function RobotActionsPanel({
<CardContent className="space-y-4">
{/* Parameters */}
{selectedAction.parameters &&
selectedAction.parameters.length > 0 ? (
selectedAction.parameters.length > 0 ? (
<div className="space-y-4">
<Label className="text-base">Parameters</Label>
{selectedAction.parameters.map((param, index) =>
@@ -662,9 +662,9 @@ export function RobotActionsPanel({
className="w-full"
>
{selectedPluginData &&
executingActions.has(
`${selectedPluginData.plugin.name}.${selectedAction.id}`,
) ? (
executingActions.has(
`${selectedPluginData.plugin.name}.${selectedAction.id}`,
) ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Executing...
@@ -900,50 +900,50 @@ export function RobotActionsPanel({
{selectedPluginData &&
Object.entries(
groupActionsByCategory(
(selectedPluginData.plugin
(selectedPluginData?.plugin
.actionDefinitions as RobotAction[]) ?? [],
),
).map(([category, actions]) => {
const CategoryIcon = getCategoryIcon(category);
const isExpanded = expandedCategories.has(category);
const CategoryIcon = getCategoryIcon(category);
const isExpanded = expandedCategories.has(category);
return (
<Collapsible
key={category}
open={isExpanded}
onOpenChange={() => toggleCategory(category)}
>
<CollapsibleTrigger asChild>
<Button
variant="ghost"
className="w-full justify-start p-2"
>
<CategoryIcon className="mr-2 h-4 w-4" />
{category.charAt(0).toUpperCase() + category.slice(1)}
<Badge variant="secondary" className="ml-auto">
{actions.length}
</Badge>
</Button>
</CollapsibleTrigger>
<CollapsibleContent className="ml-6 space-y-1">
{actions.map((action) => (
return (
<Collapsible
key={category}
open={isExpanded}
onOpenChange={() => toggleCategory(category)}
>
<CollapsibleTrigger asChild>
<Button
key={action.id}
variant={
selectedAction?.id === action.id
? "default"
: "ghost"
}
className="w-full justify-start text-sm"
onClick={() => setSelectedAction(action)}
variant="ghost"
className="w-full justify-start p-2"
>
{action.name}
<CategoryIcon className="mr-2 h-4 w-4" />
{category.charAt(0).toUpperCase() + category.slice(1)}
<Badge variant="secondary" className="ml-auto">
{actions.length}
</Badge>
</Button>
))}
</CollapsibleContent>
</Collapsible>
);
})}
</CollapsibleTrigger>
<CollapsibleContent className="ml-6 space-y-1">
{actions.map((action) => (
<Button
key={action.id}
variant={
selectedAction?.id === action.id
? "default"
: "ghost"
}
className="w-full justify-start text-sm"
onClick={() => setSelectedAction(action)}
>
{action.name}
</Button>
))}
</CollapsibleContent>
</Collapsible>
);
})}
</div>
</ScrollArea>
</div>
@@ -962,7 +962,7 @@ export function RobotActionsPanel({
<CardContent className="space-y-4">
{/* Parameters */}
{selectedAction?.parameters &&
(selectedAction.parameters?.length ?? 0) > 0 ? (
(selectedAction?.parameters?.length ?? 0) > 0 ? (
<div className="space-y-4">
<Label className="text-base">Parameters</Label>
{selectedAction?.parameters?.map((param, index) =>
@@ -990,10 +990,10 @@ export function RobotActionsPanel({
className="w-full"
>
{selectedPluginData &&
selectedAction &&
executingActions.has(
`${selectedPluginData?.plugin.name}.${selectedAction?.id}`,
) ? (
selectedAction &&
executingActions.has(
`${selectedPluginData?.plugin.name}.${selectedAction?.id}`,
) ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Executing...