[Haik]: ckpt, view builder saving issue fixed via templates, system prompts, and skills. Also added a reset to default for modes.

This commit is contained in:
haikdc
2026-03-19 01:58:20 -07:00
parent 9943ccafd5
commit 6dcfb19b17
12 changed files with 461 additions and 99 deletions
+14 -36
View File
@@ -404,11 +404,6 @@ const AgentCard: React.FC<Props> = ({
}
};
const handleCollapse = (e: React.MouseEvent) => {
e.stopPropagation();
e.preventDefault();
dispatch(collapseSession(session.id));
};
useEffect(() => {
if (session.status === 'running' || session.status === 'waiting_approval') {
@@ -750,37 +745,20 @@ const AgentCard: React.FC<Props> = ({
onPointerDown={(e) => e.stopPropagation()}
sx={{ display: 'flex', alignItems: 'center', gap: 0.5, flexShrink: 0, ml: 0.5 }}
>
{expanded ? (
<Tooltip title="Collapse">
<IconButton
size="small"
onClick={handleCollapse}
onMouseDown={(e) => e.stopPropagation()}
sx={{
color: c.text.ghost,
p: 0.5,
'&:hover': { color: c.text.secondary, bgcolor: c.bg.secondary },
}}
>
<CloseIcon sx={{ fontSize: 16 }} />
</IconButton>
</Tooltip>
) : (
<Tooltip title={isDraft ? 'Remove' : 'Close chat'}>
<IconButton
size="small"
onClick={handleRemove}
onMouseDown={(e) => e.stopPropagation()}
sx={{
color: c.text.ghost,
p: 0.5,
'&:hover': { color: c.status.error, bgcolor: `${c.status.errorBg}` },
}}
>
<CloseIcon sx={{ fontSize: 16 }} />
</IconButton>
</Tooltip>
)}
<Tooltip title={isDraft ? 'Remove' : 'Close chat'}>
<IconButton
size="small"
onClick={handleRemove}
onMouseDown={(e) => e.stopPropagation()}
sx={{
color: c.text.ghost,
p: 0.5,
'&:hover': { color: c.status.error, bgcolor: `${c.status.errorBg}` },
}}
>
<CloseIcon sx={{ fontSize: 16 }} />
</IconButton>
</Tooltip>
</Box>
</Box>
+82 -18
View File
@@ -27,6 +27,7 @@ import DeleteIcon from '@mui/icons-material/Delete';
import TuneIcon from '@mui/icons-material/Tune';
import LockIcon from '@mui/icons-material/Lock';
import ArrowForwardIcon from '@mui/icons-material/ArrowForward';
import RestoreIcon from '@mui/icons-material/Restore';
import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined';
import QuestionAnswerOutlinedIcon from '@mui/icons-material/QuestionAnswerOutlined';
import MapOutlinedIcon from '@mui/icons-material/MapOutlined';
@@ -37,6 +38,7 @@ import {
createMode,
updateMode,
deleteMode,
resetMode,
Mode,
} from '@/shared/state/modesSlice';
import { fetchBuiltinTools, fetchTools } from '@/shared/state/toolsSlice';
@@ -105,7 +107,7 @@ const ALL_BUILTIN_TOOL_NAMES = ['Read', 'Edit', 'Write', 'Bash', 'Glob', 'Grep',
const Modes: React.FC = () => {
const c = useClaudeTokens();
const dispatch = useAppDispatch();
const { items, loading } = useAppSelector((s) => s.modes);
const { items, builtinDefaults, loading } = useAppSelector((s) => s.modes);
const toolItems = useAppSelector((s) => s.tools.items);
const modes = useMemo(() => Object.values(items), [items]);
@@ -174,6 +176,45 @@ const Modes: React.FC = () => {
await dispatch(deleteMode(id));
};
const editingIsBuiltin = editingId ? items[editingId]?.is_builtin ?? false : false;
const hasDiverged = useMemo(() => {
if (!editingId || !editingIsBuiltin) return false;
const defaults = builtinDefaults[editingId];
if (!defaults) return false;
const current = items[editingId];
if (!current) return false;
return (
current.name !== defaults.name ||
current.description !== defaults.description ||
(current.system_prompt ?? '') !== (defaults.system_prompt ?? '') ||
JSON.stringify(current.tools) !== JSON.stringify(defaults.tools) ||
(current.default_next_mode ?? '') !== (defaults.default_next_mode ?? '') ||
current.icon !== defaults.icon ||
current.color !== defaults.color ||
(current.default_folder ?? '') !== (defaults.default_folder ?? '')
);
}, [editingId, editingIsBuiltin, items, builtinDefaults]);
const handleReset = async () => {
if (!editingId) return;
const action = await dispatch(resetMode(editingId));
if (resetMode.fulfilled.match(action)) {
const m = action.payload;
setForm({
name: m.name,
description: m.description,
system_prompt: m.system_prompt ?? '',
tools: m.tools ?? [],
toolsEnabled: m.tools !== null,
default_next_mode: m.default_next_mode ?? '',
icon: m.icon,
color: m.color,
default_folder: m.default_folder ?? '',
});
}
};
const otherModes = modes.filter((m) => m.id !== editingId);
return (
@@ -505,23 +546,46 @@ const Modes: React.FC = () => {
</FormControl>
</Box>
</DialogContent>
<DialogActions sx={{ px: 3, pb: 2 }}>
<Button onClick={() => setDialogOpen(false)} sx={{ color: c.text.tertiary, textTransform: 'none' }}>
Cancel
</Button>
<Button
variant="contained"
onClick={handleSave}
disabled={!form.name}
sx={{
bgcolor: c.accent.primary,
'&:hover': { bgcolor: c.accent.pressed },
textTransform: 'none',
borderRadius: 2,
}}
>
{editingId ? 'Save Changes' : 'Create Mode'}
</Button>
<DialogActions sx={{ px: 3, pb: 2, justifyContent: 'space-between' }}>
<Box>
{editingIsBuiltin && (
<Tooltip title={hasDiverged ? 'Restore this mode to its original built-in defaults' : 'Mode matches built-in defaults'}>
<span>
<Button
startIcon={<RestoreIcon sx={{ fontSize: 16 }} />}
onClick={handleReset}
disabled={!hasDiverged}
sx={{
color: hasDiverged ? c.text.muted : c.text.ghost,
textTransform: 'none',
fontSize: '0.82rem',
'&:hover': hasDiverged ? { color: c.status.error, bgcolor: `${c.status.error}10` } : {},
}}
>
Reset to Default
</Button>
</span>
</Tooltip>
)}
</Box>
<Box sx={{ display: 'flex', gap: 1 }}>
<Button onClick={() => setDialogOpen(false)} sx={{ color: c.text.tertiary, textTransform: 'none' }}>
Cancel
</Button>
<Button
variant="contained"
onClick={handleSave}
disabled={!form.name}
sx={{
bgcolor: c.accent.primary,
'&:hover': { bgcolor: c.accent.pressed },
textTransform: 'none',
borderRadius: 2,
}}
>
{editingId ? 'Save Changes' : 'Create Mode'}
</Button>
</Box>
</DialogActions>
</Dialog>
+3 -2
View File
@@ -381,7 +381,7 @@ interface FileTreeItemProps {
c: ReturnType<typeof useClaudeTokens>;
}
const PROTECTED_FILES = new Set(['index.html', 'schema.json', 'meta.json']);
const PROTECTED_FILES = new Set(['index.html', 'schema.json', 'meta.json', 'SKILL.md']);
const FileTreeItem: React.FC<FileTreeItemProps> = ({ node, depth, activeFile, onSelect, onDelete, c }) => {
const [open, setOpen] = useState(true);
@@ -732,6 +732,7 @@ const ViewEditor: React.FC<Props> = ({ output, onClose }) => {
const outputFiles = { ...files };
delete outputFiles['meta.json'];
delete outputFiles['schema.json'];
delete outputFiles['SKILL.md'];
return {
name: name || 'Untitled App',
@@ -948,7 +949,7 @@ const ViewEditor: React.FC<Props> = ({ output, onClose }) => {
? `${SERVE_BASE}/workspace/${workspaceId}/serve/index.html`
: undefined;
const filePaths = useMemo(() => Object.keys(files).filter(p => p !== 'meta.json').sort(), [files]);
const filePaths = useMemo(() => Object.keys(files).filter(p => p !== 'meta.json' && p !== 'SKILL.md').sort(), [files]);
const fileTree = useMemo(() => buildFileTree(filePaths), [filePaths]);
const updateFile = useCallback((path: string, content: string) => {