Release v1.01 Enhanced: Vi Control, TUI Gen5, Core Stability

This commit is contained in:
Gemini AI
2025-12-20 01:12:45 +04:00
Unverified
parent 2407c42eb9
commit 142aaeee1e
254 changed files with 44888 additions and 31025 deletions

141
bin/ui/components/Toast.mjs Normal file
View File

@@ -0,0 +1,141 @@
/**
* Toast Component - Minimal confirmations
*
* DESIGN:
* - Copy/applied/saved/reverted appear as brief toasts
* - Don't add to transcript (displayed separately)
* - Auto-dismiss after timeout
*/
import React, { useState, useEffect } from 'react';
import { Box, Text } from 'ink';
import { colors } from '../../tui-theme.mjs';
import { icon } from '../../icons.mjs';
import { getCapabilities } from '../../terminal-profile.mjs';
const h = React.createElement;
/**
* Toast - Single toast notification
*/
const Toast = ({
message,
type = 'info', // info, success, warning, error
duration = 3000,
onDismiss = null
}) => {
const caps = getCapabilities();
const [visible, setVisible] = useState(true);
useEffect(() => {
const timer = setTimeout(() => {
setVisible(false);
onDismiss?.();
}, duration);
return () => clearTimeout(timer);
}, [duration, onDismiss]);
if (!visible) return null;
const typeConfig = {
info: { color: colors.accent, icon: caps.unicodeOK ? '' : 'i' },
success: { color: colors.success, icon: caps.unicodeOK ? '✓' : '+' },
warning: { color: colors.warning, icon: caps.unicodeOK ? '⚠' : '!' },
error: { color: colors.error, icon: caps.unicodeOK ? '✗' : 'X' }
};
const config = typeConfig[type] || typeConfig.info;
return h(Box, {
flexDirection: 'row',
justifyContent: 'flex-end',
paddingX: 1
},
h(Text, { color: config.color }, config.icon + ' '),
h(Text, { color: config.color }, message)
);
};
/**
* ToastContainer - Manages multiple toasts
*/
const ToastContainer = ({ toasts = [], onDismiss }) => {
if (toasts.length === 0) return null;
return h(Box, {
flexDirection: 'column',
position: 'absolute',
right: 0,
top: 0
},
...toasts.map((toast, i) =>
h(Toast, {
key: toast.id || i,
message: toast.message,
type: toast.type,
duration: toast.duration,
onDismiss: () => onDismiss?.(toast.id || i)
})
)
);
};
/**
* useToasts - Hook for managing toasts
*/
const createToastManager = () => {
let toasts = [];
let listeners = [];
let nextId = 0;
const subscribe = (listener) => {
listeners.push(listener);
return () => {
listeners = listeners.filter(l => l !== listener);
};
};
const notify = () => {
listeners.forEach(l => l(toasts));
};
const add = (message, type = 'info', duration = 3000) => {
const id = nextId++;
toasts = [...toasts, { id, message, type, duration }];
notify();
setTimeout(() => {
toasts = toasts.filter(t => t.id !== id);
notify();
}, duration);
return id;
};
const dismiss = (id) => {
toasts = toasts.filter(t => t.id !== id);
notify();
};
return { subscribe, add, dismiss, get: () => toasts };
};
// Global toast manager (singleton)
const toastManager = createToastManager();
// Convenience methods
const showToast = (message, type, duration) => toastManager.add(message, type, duration);
const showSuccess = (message) => showToast(message, 'success', 2000);
const showError = (message) => showToast(message, 'error', 4000);
const showInfo = (message) => showToast(message, 'info', 3000);
export default Toast;
export {
Toast,
ToastContainer,
toastManager,
showToast,
showSuccess,
showError,
showInfo
};