Files
maestro/ui/src/components/detail/tabs/console/MobileKeyboardBar.tsx
T
cladeandswallow 7049a874f3 feat: initial public release (MAESTRO v0.1.0)
Open-source release of MAESTRO, an agent orchestration platform that runs
LLM-driven tasks through sandboxed tools, with a web UI. Apache-2.0.
See README.md and docs/ (getting-started, configuration, architecture).
2026-06-03 04:01:14 +00:00

61 lines
1.9 KiB
TypeScript

import type { ConsoleSessionApi } from '../../../../hooks/useConsoleSession';
import { KEY_BYTES, type KeyId } from './keys';
interface Props {
session: ConsoleSessionApi;
}
const BUTTONS: Array<{ id: KeyId; label: string; ariaLabel: string }> = [
{ id: 'esc', label: 'Esc', ariaLabel: 'Esc' },
{ id: 'tab', label: 'Tab', ariaLabel: 'Tab' },
{ id: 'arrow-left', label: '←', ariaLabel: '左' },
{ id: 'arrow-down', label: '↓', ariaLabel: '下' },
{ id: 'arrow-up', label: '↑', ariaLabel: '上' },
{ id: 'arrow-right', label: '→', ariaLabel: '右' },
{ id: 'ctrl-c', label: '^C', ariaLabel: 'Ctrl+C' },
];
export function MobileKeyboardBar({ session }: Props) {
const handleKey = (id: KeyId) => {
session.send(KEY_BYTES[id]);
};
const handlePaste = async () => {
try {
const text = await navigator.clipboard.readText();
if (text) session.send(text);
} catch {
/* clipboard API blocked or empty; silent no-op */
}
};
return (
<div
role="toolbar"
aria-label="ターミナルキーボード補助"
className="flex gap-px bg-slate-900 border-t border-slate-700 px-1 flex-shrink-0"
style={{ paddingBottom: 'env(safe-area-inset-bottom, 0px)' }}
>
{BUTTONS.map((btn) => (
<button
key={btn.id}
type="button"
aria-label={btn.ariaLabel}
onClick={() => handleKey(btn.id)}
className="h-11 flex-1 flex items-center justify-center text-sm font-mono text-slate-200 bg-slate-800 active:bg-slate-700 transition-colors rounded-sm"
>
{btn.label}
</button>
))}
<button
type="button"
aria-label="ペースト"
onClick={handlePaste}
className="h-11 flex-1 flex items-center justify-center text-base text-slate-200 bg-slate-800 active:bg-slate-700 transition-colors rounded-sm"
>
📋
</button>
</div>
);
}