mcpField
I
inkjs-design

inkjs-design

github.com/akiojin開発759 回インストール34 週間前に更新
リポジトリ

inkjs-design について

Ink.js でターミナル UI を構築する開発者向けの設計ガイドです。画面、再利用可能なパーツ、入力コンポーネントに分け、React ベースの CLI パターンや実例、トラブルシューティング資料をまとめています。

主な機能

  1. 01Screen、Part、Common のコンポーネントパターン
  2. 02キーボード入力とフォーカスの指針
  3. 03複数画面のナビゲーションパターン
  4. 04ターミナルのレスポンシブレイアウト指針
  5. 05パフォーマンスとアニメーションの資料
  6. 06テストとトラブルシューティングの資料

活用シーン

  1. 01新しい Ink.js CLI のコンポーネント設計
  2. 02ターミナルのレイアウトや入力競合の解決
  3. 03CLI の画面間ナビゲーションの追加

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/akiojin/skills --skill inkjs-design

インストール後、Claude Code で/inkjs-designを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

examples/
component-examples.md
references/
animation-patterns.md
component-patterns.md
hooks-guide.md
ink-gotchas.md
input-handling.md
multi-screen-navigation.md
performance-optimization.md
responsive-layout.md
state-management.md
testing-patterns.md

詳細

Ink.js の設計

Ink.js(CLI 向け React)でターミナル UI を構築するための包括的なガイドです。

クイックスタート

新しいコンポーネントの作成
  1. コンポーネントの種類を決定します:Screen/Part/Common
  2. 類似するパターンについてはcomponent-patterns.mdを参照します
  3. 型定義を追加します
  4. コンポーネントを実装します
  5. テストを作成します
よくある問題と解決策
問題参照先
絵文字の幅がずれるink-gotchas.md
Ctrl+C が 2 回呼び出されるink-gotchas.md
useInput の競合ink-gotchas.md
レイアウトが崩れるresponsive-layout.md
画面遷移multi-screen-navigation.md

ディレクトリの規約

src/cli/ui/
├── components/
│   ├── App.tsx              # Root component with screen management
│   ├── common/              # Common input components (Select, Input)
│   ├── parts/               # Reusable UI parts (Header, Footer)
│   └── screens/             # Full-screen components
├── hooks/                   # Custom hooks
├── utils/                   # Utility functions
└── types.ts                 # Type definitions

コンポーネントの分類

Screen(全画面ビュー)
  • 完全な画面/ページを表します
  • useInput を介してキーボード入力を処理します
  • Header/Content/Footer のレイアウトを実装します
  • 画面レベルの状態を管理します
Part(再利用可能な要素)
  • 再利用可能な UI の構成要素です
  • React.memo で最適化します
  • ステートレス/純粋なコンポーネントを推奨します
  • props で設定を受け取ります
Common(入力コンポーネント)
  • 基本的な入力コンポーネントです
  • 制御モードと非制御モードの両方をサポートします
  • フォーカス管理を処理します
  • 一貫した UX を提供します

重要なパターン

1. アイコンの幅の上書き

string-width v8 の絵文字幅計算に関する問題を修正します:

const WIDTH_OVERRIDES: Record<string, number> = {
  "⚡": 1, "✨": 1, "🐛": 1, "🔥": 1, "🚀": 1,
  "🟢": 1, "🟠": 1, "✅": 1, "⚠️": 1,
};

const getIconWidth = (icon: string): number => {
  const baseWidth = stringWidth(icon);
  const override = WIDTH_OVERRIDES[icon];
  return override !== undefined ? Math.max(baseWidth, override) : baseWidth;
};
2. useInput の競合回避

複数の useInput フックがすべて実行されるため、早期リターンまたは isActive を使用します:

useInput((input, key) => {
  if (disabled) return;  // Early return when inactive
  // Handle input...
}, { isActive: isFocused });
3. Ctrl+C の処理
render(<App />, { exitOnCtrlC: false });

// In component
const { exit } = useApp();
useInput((input, key) => {
  if (key.ctrl && input === "c") {
    cleanup();
    exit();
  }
});
4. 動的な高さの計算
const { rows } = useTerminalSize();
const HEADER_LINES = 3;
const FOOTER_LINES = 2;
const contentHeight = rows - HEADER_LINES - FOOTER_LINES;
const visibleItems = Math.max(5, contentHeight);
5. カスタム比較関数を使った React.memo
function arePropsEqual<T>(prev: Props<T>, next: Props<T>): boolean {
  if (prev.items.length !== next.items.length) return false;
  for (let i = 0; i < prev.items.length; i++) {
    if (prev.items[i].value !== next.items[i].value) return false;
  }
  return prev.selectedIndex === next.selectedIndex;
}

export const Select = React.memo(SelectComponent, arePropsEqual);
6. 複数画面のナビゲーション
type ScreenType = "main" | "detail" | "settings";

const [screenStack, setScreenStack] = useState<ScreenType[]>(["main"]);
const currentScreen = screenStack[screenStack.length - 1];

const navigateTo = (screen: ScreenType) => {
  setScreenStack(prev => [...prev, screen]);
};

const goBack = () => {
  if (screenStack.length > 1) {
    setScreenStack(prev => prev.slice(0, -1));
  }
};

詳細なリファレンス

コアパターン
発展的なトピック
トラブルシューティング

例

実装例についてはexamples/を参照してください。

コメント