Code editor and widget

This commit is contained in:
2026-02-17 23:48:46 +00:00
parent d3f94e45ea
commit 030cf9fbd3
18 changed files with 871 additions and 9 deletions
@@ -0,0 +1,63 @@
import { useMemo } from 'react';
import { X } from 'lucide-react';
import { getIcon } from 'material-file-icons';
import type { OpenFile } from './useEditorState';
type EditorTabsProps = {
files: OpenFile[];
activePath: string | null;
onSelect: (path: string) => void;
onClose: (path: string) => void;
theme?: string;
};
const FileIcon = ({ name }: { name: string }) => {
const svg = useMemo(() => getIcon(name).svg, [name]);
return <span className="inline-flex h-4 w-4 shrink-0" dangerouslySetInnerHTML={{ __html: svg }} />;
};
export const EditorTabs = ({ files, activePath, onSelect, onClose, theme }: EditorTabsProps) => {
if (files.length === 0) return null;
const isDark = theme !== 'vs';
const borderColor = isDark ? '#333' : '#e0e0e0';
const activeBg = isDark ? '#1e1e1e' : '#ffffff';
const inactiveBg = isDark ? '#181818' : '#f3f3f3';
const activeColor = isDark ? '#ccc' : '#333';
const inactiveColor = isDark ? '#888' : '#666';
return (
<div className="flex items-center overflow-x-auto shrink-0 code-editor-scrollable" style={{ borderBottom: `1px solid ${borderColor}` }}>
{files.map((file) => {
const isActive = file.path === activePath;
return (
<button
key={file.path}
onClick={() => onSelect(file.path)}
className="flex items-center gap-1.5 px-3 py-1.5 text-sm cursor-pointer whitespace-nowrap transition-colors"
style={{
background: isActive ? activeBg : inactiveBg,
color: isActive ? activeColor : inactiveColor,
borderRight: `1px solid ${borderColor}`,
}}
>
<FileIcon name={file.name} />
<span>{file.name}</span>
{file.isDirty && <span className="w-2 h-2 rounded-full bg-blue-400 shrink-0" />}
<span
role="button"
className="ml-1 p-0.5 rounded transition-colors"
style={{ color: inactiveColor }}
onClick={(ev) => {
ev.stopPropagation();
onClose(file.path);
}}
>
<X className="h-3 w-3" />
</span>
</button>
);
})}
</div>
);
};