UNPKG

@shutootaki/gwm

Version:
67 lines 4.54 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { Text, Box, useInput } from 'ink'; import { join } from 'path'; import { loadConfig } from '../config.js'; import { getRepositoryName } from '../utils/git.js'; import { useEditableText } from '../hooks/useEditableText.js'; export const TextInput = ({ title, placeholder, initialValue = '', onSubmit, onCancel, onModeSwitch, validate, preview, }) => { // 共通フックで文字列編集ロジックを扱う const { value, cursorPosition } = useEditableText({ initialValue }); const validationError = validate ? validate(value) : null; const previewText = preview ? preview(value) : null; const canSubmit = value.trim() && !validationError; // 送信 / キャンセル / モード切替など、フック外のキーイベントのみ処理 useInput((input, key) => { if (key.escape) { onCancel(); return; } if (key.return) { if (canSubmit) { onSubmit(value.trim()); } return; } if (key.tab && onModeSwitch) { onModeSwitch(); return; } }); return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "cyan", bold: true, children: title }) }), _jsx(Box, { marginBottom: 1, children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "gray", children: placeholder }), _jsxs(Box, { marginTop: 0, children: [_jsxs(Text, { color: "cyan", bold: true, children: ["\u276F", ' '] }), _jsx(Text, { color: validationError ? 'red' : 'white', children: value.slice(0, cursorPosition) }), _jsx(Text, { color: "cyan", children: "\u2588" }), _jsx(Text, { color: validationError ? 'red' : 'white', children: value.slice(cursorPosition) })] })] }) }), validationError && (_jsx(Box, { marginBottom: 1, children: _jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "red", padding: 1, children: [_jsx(Text, { color: "red", bold: true, children: "Invalid input" }), _jsx(Text, { color: "red", children: validationError })] }) })), previewText && !validationError && value.trim() && (_jsx(Box, { marginBottom: 1, children: _jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "green", padding: 1, children: [_jsx(Text, { color: "green", bold: true, children: "Preview" }), _jsx(Text, { color: "gray", children: "Worktree will be created at:" }), _jsx(Text, { color: "cyan", children: previewText })] }) })), _jsx(Box, { children: _jsxs(Text, { color: "gray", children: [_jsx(Text, { color: "green", children: "Enter" }), " ", canSubmit ? 'create' : 'disabled', " \u2022", ' ', _jsx(Text, { color: "red", children: "Esc" }), " cancel", onModeSwitch && (_jsxs(_Fragment, { children: [' ', "\u2022 ", _jsx(Text, { color: "yellow", children: "Tab" }), " browse remote branches"] })), ' ', "\u2022 ", _jsx(Text, { color: "yellow", children: "Cmd+Del" }), " clear \u2022", ' ', _jsx(Text, { color: "yellow", children: "Ctrl+W/\u2325\u232B" }), " delete-word"] }) })] })); }; // ブランチ名用のバリデーション関数 export function validateBranchName(branchName) { if (!branchName.trim()) { return 'Branch name cannot be empty'; } // Git ブランチ名に使用できない文字をチェック。 // 文字クラス内で '[' ']' '\\' を表現するため、それぞれ \\[ \\] \\\\ とエスケープする。 const invalidChars = /[~^:?*\\[\]]/; if (invalidChars.test(branchName)) { return 'Branch name contains invalid characters (~^:?*[]\\)'; } if (branchName.startsWith('.') || branchName.endsWith('.')) { return 'Branch name cannot start or end with a dot'; } if (branchName.includes('..')) { return 'Branch name cannot contain consecutive dots'; } if (branchName.includes(' ')) { return 'Branch name cannot contain spaces'; } if (branchName.length > 50) { return 'Branch name is too long (max 50 characters)'; } return null; } // ワークツリーパスのプレビュー生成関数 export function generateWorktreePreview(branchName) { if (!branchName.trim()) { return null; } const config = loadConfig(); const repoName = getRepositoryName(); const sanitizedBranch = branchName.replace(/\//g, '-'); return join(config.worktree_base_path, repoName, sanitizedBranch); } //# sourceMappingURL=TextInput.js.map