// ====== Skill editor (with file tree) ====== function TreeNode({ node, depth, active, expanded, onToggleExpand, onSelect, onDelete }) { const indentCls = depth > 0 ? `indent-${Math.min(depth, 2)}` : ""; if (node.isDir) { const open = expanded.has(node.path); return ( <>
onToggleExpand(node.path)} title={node.path} > 📁 {node.name}/
{open && node.children.map((c) => ( ))} ); } const isReq = node.file.required; return (
onSelect(node.path)} title={node.path} > · {node.name} {isReq && REQ} {!isReq && ( { e.stopPropagation(); onDelete(node.path); }} title="Delete"> )}
); } function TextEditor({ file, disabled, onChange, changedRange }) { const textareaRef = React.useRef(null); const gutterRef = React.useRef(null); const backdropRef = React.useRef(null); const lines = (file.content || "").split("\n"); const highlightLine = (lineNumber) => changedRange && lineNumber >= changedRange.start && lineNumber <= changedRange.end; const syncScroll = (event) => { const { scrollTop, scrollLeft } = event.currentTarget; if (gutterRef.current) gutterRef.current.scrollTop = scrollTop; if (backdropRef.current) { backdropRef.current.scrollTop = scrollTop; backdropRef.current.scrollLeft = scrollLeft; } }; return