feat: initial public release (MAESTRO)

This commit is contained in:
oss-sync
2026-06-03 05:08:00 +00:00
commit f5c7666f6b
823 changed files with 184150 additions and 0 deletions
@@ -0,0 +1,112 @@
import { useState } from 'react';
import { MovementForm } from './MovementForm';
export interface MovementAccordionProps {
movements: any[];
onChange: (index: number, field: string, value: any) => void;
onAdd: () => void;
onRemove: (index: number) => void;
onMove: (index: number, direction: 'up' | 'down') => void;
}
export function MovementAccordion({ movements, onChange, onAdd, onRemove, onMove }: MovementAccordionProps) {
const [expandedIndex, setExpandedIndex] = useState<number | null>(null);
const movementNames = movements.map((m) => m.name ?? '');
const toggle = (i: number) => {
setExpandedIndex((prev) => (prev === i ? null : i));
};
return (
<div>
<h3 className="text-sm font-semibold text-slate-800 mb-2">Movements</h3>
<div className="space-y-2">
{movements.map((movement, i) => {
const isExpanded = expandedIndex === i;
const toolCount = (movement.allowed_tools ?? []).length;
const ruleCount = (movement.rules ?? []).length;
return (
<div key={i} className="bg-white border border-slate-200 rounded-lg">
{/* Collapsed header */}
<div
className="flex items-center gap-2 p-3 cursor-pointer select-none"
onClick={() => toggle(i)}
>
<span className="text-xs text-slate-400 mr-1">{isExpanded ? '\u25BC' : '\u25B6'}</span>
<span className="text-sm font-medium text-slate-800">{movement.name || '(unnamed)'}</span>
{movement.persona && (
<span className="bg-blue-100 text-blue-700 text-xs px-2 py-0.5 rounded">
{movement.persona}
</span>
)}
<span className={`text-xs px-2 py-0.5 rounded ${movement.edit ? 'bg-green-100 text-green-700' : 'bg-purple-100 text-purple-700'}`}>
edit: {movement.edit ? 'on' : 'off'}
</span>
<span className="text-xs text-slate-400">{toolCount} tools</span>
<span className="text-xs text-slate-400">{ruleCount} rules</span>
{/* Spacer */}
<div className="flex-1" />
{/* Controls */}
<div className="flex items-center gap-1" onClick={(e) => e.stopPropagation()}>
<button
type="button"
onClick={() => onMove(i, 'up')}
disabled={i === 0}
className="text-slate-400 hover:text-slate-600 disabled:opacity-30 text-sm px-1"
title="Move up"
>
&#9650;
</button>
<button
type="button"
onClick={() => onMove(i, 'down')}
disabled={i === movements.length - 1}
className="text-slate-400 hover:text-slate-600 disabled:opacity-30 text-sm px-1"
title="Move down"
>
&#9660;
</button>
<button
type="button"
onClick={() => {
if (confirm(`Movement "${movement.name}" を削除しますか?`)) {
onRemove(i);
if (expandedIndex === i) setExpandedIndex(null);
}
}}
className="text-slate-400 hover:text-red-500 text-sm px-1"
title="Delete"
>
&times;
</button>
</div>
</div>
{/* Expanded form */}
{isExpanded && (
<div className="px-3 pb-3 border-t border-slate-100 pt-3">
<MovementForm
movement={movement}
movementNames={movementNames}
onChange={(field, value) => onChange(i, field, value)}
/>
</div>
)}
</div>
);
})}
</div>
<button
type="button"
onClick={onAdd}
className="mt-3 text-sm text-blue-600 hover:text-blue-700"
>
+ Add Movement
</button>
</div>
);
}