@akiojin/gwt 2.13.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +41 -0
- package/README.md +38 -0
- package/dist/claude.d.ts.map +1 -1
- package/dist/claude.js +17 -11
- package/dist/claude.js.map +1 -1
- package/dist/cli/ui/components/App.d.ts +0 -6
- package/dist/cli/ui/components/App.d.ts.map +1 -1
- package/dist/cli/ui/components/App.js +27 -88
- package/dist/cli/ui/components/App.js.map +1 -1
- package/dist/cli/ui/components/common/Select.js +2 -2
- package/dist/cli/ui/components/common/Select.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.d.ts.map +1 -1
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.js +3 -3
- package/dist/cli/ui/components/screens/BranchQuickStartScreen.js.map +1 -1
- package/dist/cli/ui/utils/continueSession.d.ts.map +1 -1
- package/dist/cli/ui/utils/continueSession.js +1 -1
- package/dist/cli/ui/utils/continueSession.js.map +1 -1
- package/dist/client/assets/index-DsDNCy5f.css +1 -0
- package/dist/client/assets/index-f5D2XwDh.js +72 -0
- package/dist/client/index.html +2 -2
- package/dist/codex.d.ts.map +1 -1
- package/dist/codex.js +21 -11
- package/dist/codex.js.map +1 -1
- package/dist/config/builtin-tools.d.ts.map +1 -1
- package/dist/config/builtin-tools.js +3 -2
- package/dist/config/builtin-tools.js.map +1 -1
- package/dist/config/shared-env.d.ts +41 -0
- package/dist/config/shared-env.d.ts.map +1 -0
- package/dist/config/shared-env.js +114 -0
- package/dist/config/shared-env.js.map +1 -0
- package/dist/gemini.d.ts.map +1 -1
- package/dist/gemini.js +20 -17
- package/dist/gemini.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -8
- package/dist/index.js.map +1 -1
- package/dist/logging/logger.d.ts.map +1 -1
- package/dist/logging/logger.js +4 -1
- package/dist/logging/logger.js.map +1 -1
- package/dist/qwen.d.ts.map +1 -1
- package/dist/qwen.js +15 -11
- package/dist/qwen.js.map +1 -1
- package/dist/services/aiToolResolver.d.ts +41 -0
- package/dist/services/aiToolResolver.d.ts.map +1 -0
- package/dist/services/aiToolResolver.js +194 -0
- package/dist/services/aiToolResolver.js.map +1 -0
- package/dist/services/customToolResolver.d.ts +10 -0
- package/dist/services/customToolResolver.d.ts.map +1 -0
- package/dist/services/customToolResolver.js +71 -0
- package/dist/services/customToolResolver.js.map +1 -0
- package/dist/shared/aiToolConstants.d.ts +9 -0
- package/dist/shared/aiToolConstants.d.ts.map +1 -0
- package/dist/shared/aiToolConstants.js +29 -0
- package/dist/shared/aiToolConstants.js.map +1 -0
- package/dist/types/tools.d.ts +12 -0
- package/dist/types/tools.d.ts.map +1 -1
- package/dist/utils/prompt.d.ts.map +1 -1
- package/dist/utils/prompt.js.map +1 -1
- package/dist/utils/session.d.ts.map +1 -1
- package/dist/utils/session.js +15 -6
- package/dist/utils/session.js.map +1 -1
- package/dist/utils/terminal.d.ts +12 -3
- package/dist/utils/terminal.d.ts.map +1 -1
- package/dist/utils/terminal.js +5 -34
- package/dist/utils/terminal.js.map +1 -1
- package/dist/utils/webui.d.ts +8 -0
- package/dist/utils/webui.d.ts.map +1 -0
- package/dist/utils/webui.js +35 -0
- package/dist/utils/webui.js.map +1 -0
- package/dist/web/client/src/components/AIToolLaunchModal.d.ts +9 -0
- package/dist/web/client/src/components/AIToolLaunchModal.d.ts.map +1 -0
- package/dist/web/client/src/components/AIToolLaunchModal.js +363 -0
- package/dist/web/client/src/components/AIToolLaunchModal.js.map +1 -0
- package/dist/web/client/src/components/BranchGraph.d.ts.map +1 -1
- package/dist/web/client/src/components/BranchGraph.js +46 -49
- package/dist/web/client/src/components/BranchGraph.js.map +1 -1
- package/dist/web/client/src/components/CustomToolForm.d.ts +23 -0
- package/dist/web/client/src/components/CustomToolForm.d.ts.map +1 -0
- package/dist/web/client/src/components/CustomToolForm.js +209 -0
- package/dist/web/client/src/components/CustomToolForm.js.map +1 -0
- package/dist/web/client/src/components/CustomToolList.d.ts +10 -0
- package/dist/web/client/src/components/CustomToolList.d.ts.map +1 -0
- package/dist/web/client/src/components/CustomToolList.js +57 -0
- package/dist/web/client/src/components/CustomToolList.js.map +1 -0
- package/dist/web/client/src/components/EnvEditor.d.ts.map +1 -1
- package/dist/web/client/src/components/EnvEditor.js +33 -26
- package/dist/web/client/src/components/EnvEditor.js.map +1 -1
- package/dist/web/client/src/components/EnvironmentEditor.d.ts +17 -0
- package/dist/web/client/src/components/EnvironmentEditor.d.ts.map +1 -0
- package/dist/web/client/src/components/EnvironmentEditor.js +22 -0
- package/dist/web/client/src/components/EnvironmentEditor.js.map +1 -0
- package/dist/web/client/src/components/Terminal.d.ts.map +1 -1
- package/dist/web/client/src/components/Terminal.js +10 -3
- package/dist/web/client/src/components/Terminal.js.map +1 -1
- package/dist/web/client/src/components/branch-detail/BranchInfoCards.d.ts +10 -0
- package/dist/web/client/src/components/branch-detail/BranchInfoCards.d.ts.map +1 -0
- package/dist/web/client/src/components/branch-detail/BranchInfoCards.js +104 -0
- package/dist/web/client/src/components/branch-detail/BranchInfoCards.js.map +1 -0
- package/dist/web/client/src/components/branch-detail/SessionHistoryTable.d.ts +22 -0
- package/dist/web/client/src/components/branch-detail/SessionHistoryTable.d.ts.map +1 -0
- package/dist/web/client/src/components/branch-detail/SessionHistoryTable.js +79 -0
- package/dist/web/client/src/components/branch-detail/SessionHistoryTable.js.map +1 -0
- package/dist/web/client/src/components/branch-detail/TerminalPanel.d.ts +11 -0
- package/dist/web/client/src/components/branch-detail/TerminalPanel.d.ts.map +1 -0
- package/dist/web/client/src/components/branch-detail/TerminalPanel.js +32 -0
- package/dist/web/client/src/components/branch-detail/TerminalPanel.js.map +1 -0
- package/dist/web/client/src/components/branch-detail/ToolLauncher.d.ts +40 -0
- package/dist/web/client/src/components/branch-detail/ToolLauncher.d.ts.map +1 -0
- package/dist/web/client/src/components/branch-detail/ToolLauncher.js +147 -0
- package/dist/web/client/src/components/branch-detail/ToolLauncher.js.map +1 -0
- package/dist/web/client/src/components/branch-detail/index.d.ts +5 -0
- package/dist/web/client/src/components/branch-detail/index.d.ts.map +1 -0
- package/dist/web/client/src/components/branch-detail/index.js +5 -0
- package/dist/web/client/src/components/branch-detail/index.js.map +1 -0
- package/dist/web/client/src/components/common/BranchCard.d.ts +17 -0
- package/dist/web/client/src/components/common/BranchCard.d.ts.map +1 -0
- package/dist/web/client/src/components/common/BranchCard.js +36 -0
- package/dist/web/client/src/components/common/BranchCard.js.map +1 -0
- package/dist/web/client/src/components/common/MetricCard.d.ts +10 -0
- package/dist/web/client/src/components/common/MetricCard.d.ts.map +1 -0
- package/dist/web/client/src/components/common/MetricCard.js +10 -0
- package/dist/web/client/src/components/common/MetricCard.js.map +1 -0
- package/dist/web/client/src/components/common/PageHeader.d.ts +12 -0
- package/dist/web/client/src/components/common/PageHeader.d.ts.map +1 -0
- package/dist/web/client/src/components/common/PageHeader.js +14 -0
- package/dist/web/client/src/components/common/PageHeader.js.map +1 -0
- package/dist/web/client/src/components/common/SearchInput.d.ts +14 -0
- package/dist/web/client/src/components/common/SearchInput.d.ts.map +1 -0
- package/dist/web/client/src/components/common/SearchInput.js +15 -0
- package/dist/web/client/src/components/common/SearchInput.js.map +1 -0
- package/dist/web/client/src/components/common/StatusBadge.d.ts +10 -0
- package/dist/web/client/src/components/common/StatusBadge.d.ts.map +1 -0
- package/dist/web/client/src/components/common/StatusBadge.js +15 -0
- package/dist/web/client/src/components/common/StatusBadge.js.map +1 -0
- package/dist/web/client/src/components/common/index.d.ts +6 -0
- package/dist/web/client/src/components/common/index.d.ts.map +1 -0
- package/dist/web/client/src/components/common/index.js +6 -0
- package/dist/web/client/src/components/common/index.js.map +1 -0
- package/dist/web/client/src/components/ui/alert.d.ts +9 -0
- package/dist/web/client/src/components/ui/alert.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/alert.js +25 -0
- package/dist/web/client/src/components/ui/alert.js.map +1 -0
- package/dist/web/client/src/components/ui/badge.d.ts +10 -0
- package/dist/web/client/src/components/ui/badge.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/badge.js +25 -0
- package/dist/web/client/src/components/ui/badge.js.map +1 -0
- package/dist/web/client/src/components/ui/button.d.ts +12 -0
- package/dist/web/client/src/components/ui/button.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/button.js +33 -0
- package/dist/web/client/src/components/ui/button.js.map +1 -0
- package/dist/web/client/src/components/ui/card.d.ts +9 -0
- package/dist/web/client/src/components/ui/card.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/card.js +16 -0
- package/dist/web/client/src/components/ui/card.js.map +1 -0
- package/dist/web/client/src/components/ui/index.d.ts +8 -0
- package/dist/web/client/src/components/ui/index.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/index.js +8 -0
- package/dist/web/client/src/components/ui/index.js.map +1 -0
- package/dist/web/client/src/components/ui/input.d.ts +4 -0
- package/dist/web/client/src/components/ui/input.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/input.js +8 -0
- package/dist/web/client/src/components/ui/input.js.map +1 -0
- package/dist/web/client/src/components/ui/select.d.ts +14 -0
- package/dist/web/client/src/components/ui/select.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/select.js +39 -0
- package/dist/web/client/src/components/ui/select.js.map +1 -0
- package/dist/web/client/src/components/ui/table.d.ts +11 -0
- package/dist/web/client/src/components/ui/table.d.ts.map +1 -0
- package/dist/web/client/src/components/ui/table.js +21 -0
- package/dist/web/client/src/components/ui/table.js.map +1 -0
- package/dist/web/client/src/hooks/useSessions.d.ts.map +1 -1
- package/dist/web/client/src/hooks/useSessions.js +6 -1
- package/dist/web/client/src/hooks/useSessions.js.map +1 -1
- package/dist/web/client/src/lib/utils.d.ts +7 -0
- package/dist/web/client/src/lib/utils.d.ts.map +1 -0
- package/dist/web/client/src/lib/utils.js +10 -0
- package/dist/web/client/src/lib/utils.js.map +1 -0
- package/dist/web/client/src/lib/websocket.d.ts +7 -0
- package/dist/web/client/src/lib/websocket.d.ts.map +1 -1
- package/dist/web/client/src/lib/websocket.js +44 -0
- package/dist/web/client/src/lib/websocket.js.map +1 -1
- package/dist/web/client/src/pages/BranchDetailPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/BranchDetailPage.js +125 -376
- package/dist/web/client/src/pages/BranchDetailPage.js.map +1 -1
- package/dist/web/client/src/pages/BranchListPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/BranchListPage.js +89 -127
- package/dist/web/client/src/pages/BranchListPage.js.map +1 -1
- package/dist/web/client/src/pages/ConfigManagementPage.d.ts.map +1 -1
- package/dist/web/client/src/pages/ConfigManagementPage.js +46 -41
- package/dist/web/client/src/pages/ConfigManagementPage.js.map +1 -1
- package/dist/web/client/src/pages/ConfigPage.d.ts +3 -0
- package/dist/web/client/src/pages/ConfigPage.d.ts.map +1 -0
- package/dist/web/client/src/pages/ConfigPage.js +216 -0
- package/dist/web/client/src/pages/ConfigPage.js.map +1 -0
- package/dist/web/client/vite.config.d.ts.map +1 -1
- package/dist/web/client/vite.config.js +8 -1
- package/dist/web/client/vite.config.js.map +1 -1
- package/dist/web/server/index.d.ts +24 -2
- package/dist/web/server/index.d.ts.map +1 -1
- package/dist/web/server/index.js +46 -15
- package/dist/web/server/index.js.map +1 -1
- package/dist/web/server/pty/manager.d.ts +12 -10
- package/dist/web/server/pty/manager.d.ts.map +1 -1
- package/dist/web/server/pty/manager.js +76 -43
- package/dist/web/server/pty/manager.js.map +1 -1
- package/dist/web/server/routes/sessions.d.ts.map +1 -1
- package/dist/web/server/routes/sessions.js +35 -2
- package/dist/web/server/routes/sessions.js.map +1 -1
- package/dist/web/server/routes/worktrees.js +2 -2
- package/dist/web/server/routes/worktrees.js.map +1 -1
- package/dist/web/server/services/worktrees.d.ts.map +1 -1
- package/dist/web/server/services/worktrees.js +7 -1
- package/dist/web/server/services/worktrees.js.map +1 -1
- package/dist/web/server/tray.d.ts +25 -0
- package/dist/web/server/tray.d.ts.map +1 -0
- package/dist/web/server/tray.js +98 -0
- package/dist/web/server/tray.js.map +1 -0
- package/dist/web/server/websocket/handler.d.ts +18 -2
- package/dist/web/server/websocket/handler.d.ts.map +1 -1
- package/dist/web/server/websocket/handler.js +82 -9
- package/dist/web/server/websocket/handler.js.map +1 -1
- package/package.json +15 -2
- package/src/claude.ts +26 -15
- package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +1 -1
- package/src/cli/ui/__tests__/components/common/Select.test.tsx +11 -0
- package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +3 -1
- package/src/cli/ui/__tests__/components/screens/BranchQuickStartScreen.test.tsx +4 -4
- package/src/cli/ui/components/App.tsx +33 -133
- package/src/cli/ui/components/common/Select.tsx +2 -2
- package/src/cli/ui/components/screens/BranchListScreen.tsx +28 -21
- package/src/cli/ui/components/screens/BranchQuickStartScreen.tsx +41 -46
- package/src/cli/ui/utils/continueSession.ts +1 -7
- package/src/codex.ts +31 -22
- package/src/config/builtin-tools.ts +6 -2
- package/src/config/shared-env.ts +139 -0
- package/src/gemini.ts +35 -22
- package/src/index.ts +13 -8
- package/src/logging/logger.ts +5 -2
- package/src/qwen.ts +28 -19
- package/src/services/aiToolResolver.ts +276 -0
- package/src/services/customToolResolver.ts +98 -0
- package/src/shared/aiToolConstants.ts +30 -0
- package/src/trayicon.d.ts +30 -0
- package/src/types/tools.ts +15 -0
- package/src/utils/prompt.ts +15 -9
- package/src/utils/session.ts +80 -26
- package/src/utils/terminal.ts +11 -41
- package/src/utils/webui.ts +43 -0
- package/src/web/client/components.json +21 -0
- package/src/web/client/src/components/AIToolLaunchModal.tsx +575 -0
- package/src/web/client/src/components/BranchGraph.tsx +95 -75
- package/src/web/client/src/components/CustomToolForm.tsx +386 -0
- package/src/web/client/src/components/CustomToolList.tsx +119 -0
- package/src/web/client/src/components/EnvEditor.tsx +91 -81
- package/src/web/client/src/components/EnvironmentEditor.tsx +97 -0
- package/src/web/client/src/components/Terminal.tsx +11 -3
- package/src/web/client/src/components/branch-detail/BranchInfoCards.tsx +179 -0
- package/src/web/client/src/components/branch-detail/SessionHistoryTable.tsx +181 -0
- package/src/web/client/src/components/branch-detail/TerminalPanel.tsx +92 -0
- package/src/web/client/src/components/branch-detail/ToolLauncher.tsx +327 -0
- package/src/web/client/src/components/branch-detail/index.ts +4 -0
- package/src/web/client/src/components/common/BranchCard.tsx +117 -0
- package/src/web/client/src/components/common/MetricCard.tsx +22 -0
- package/src/web/client/src/components/common/PageHeader.tsx +44 -0
- package/src/web/client/src/components/common/SearchInput.tsx +40 -0
- package/src/web/client/src/components/common/StatusBadge.tsx +37 -0
- package/src/web/client/src/components/common/index.ts +5 -0
- package/src/web/client/src/components/ui/alert.tsx +63 -0
- package/src/web/client/src/components/ui/badge.tsx +44 -0
- package/src/web/client/src/components/ui/button.tsx +57 -0
- package/src/web/client/src/components/ui/card.tsx +82 -0
- package/src/web/client/src/components/ui/index.ts +32 -0
- package/src/web/client/src/components/ui/input.tsx +21 -0
- package/src/web/client/src/components/ui/select.tsx +156 -0
- package/src/web/client/src/components/ui/table.tsx +119 -0
- package/src/web/client/src/hooks/useSessions.ts +10 -1
- package/src/web/client/src/index.css +46 -816
- package/src/web/client/src/lib/utils.ts +10 -0
- package/src/web/client/src/lib/websocket.ts +48 -1
- package/src/web/client/src/pages/BranchDetailPage.tsx +247 -723
- package/src/web/client/src/pages/BranchListPage.tsx +190 -236
- package/src/web/client/src/pages/ConfigManagementPage.tsx +94 -76
- package/src/web/client/src/pages/ConfigPage.tsx +362 -0
- package/src/web/client/vite.config.ts +8 -1
- package/src/web/server/index.ts +72 -15
- package/src/web/server/pty/manager.ts +128 -55
- package/src/web/server/routes/sessions.ts +59 -7
- package/src/web/server/routes/worktrees.ts +3 -3
- package/src/web/server/services/worktrees.ts +12 -4
- package/src/web/server/tray.ts +120 -0
- package/src/web/server/websocket/handler.ts +119 -13
- package/dist/client/assets/index-DeNwPosA.css +0 -1
- package/dist/client/assets/index-Dl798X5w.js +0 -32
|
@@ -9,7 +9,17 @@ import {
|
|
|
9
9
|
} from "../hooks/useSessions";
|
|
10
10
|
import { useConfig } from "../hooks/useConfig";
|
|
11
11
|
import { ApiError } from "../lib/api";
|
|
12
|
-
import {
|
|
12
|
+
import { PageHeader } from "@/components/common/PageHeader";
|
|
13
|
+
import { Button } from "@/components/ui/button";
|
|
14
|
+
import { Badge } from "@/components/ui/badge";
|
|
15
|
+
import { Alert, AlertDescription } from "@/components/ui/alert";
|
|
16
|
+
import {
|
|
17
|
+
SessionHistoryTable,
|
|
18
|
+
ToolLauncher,
|
|
19
|
+
BranchInfoCards,
|
|
20
|
+
TerminalPanel,
|
|
21
|
+
type SelectableTool,
|
|
22
|
+
} from "@/components/branch-detail";
|
|
13
23
|
import type {
|
|
14
24
|
Branch,
|
|
15
25
|
CustomAITool,
|
|
@@ -19,44 +29,6 @@ import type {
|
|
|
19
29
|
type ToolType = "claude-code" | "codex-cli" | "custom";
|
|
20
30
|
type ToolMode = "normal" | "continue" | "resume";
|
|
21
31
|
|
|
22
|
-
type SelectableTool =
|
|
23
|
-
| { id: "claude-code"; label: string; target: "claude" }
|
|
24
|
-
| { id: "codex-cli"; label: string; target: "codex" }
|
|
25
|
-
| { id: string; label: string; target: "custom"; definition: CustomAITool };
|
|
26
|
-
|
|
27
|
-
interface ToolSummary {
|
|
28
|
-
command: string;
|
|
29
|
-
defaultArgs?: string[] | null;
|
|
30
|
-
modeArgs?: {
|
|
31
|
-
normal?: string[];
|
|
32
|
-
continue?: string[];
|
|
33
|
-
resume?: string[];
|
|
34
|
-
};
|
|
35
|
-
permissionSkipArgs?: string[] | null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const BUILTIN_TOOL_SUMMARIES: Record<string, ToolSummary> = {
|
|
39
|
-
"claude-code": {
|
|
40
|
-
command: "claude",
|
|
41
|
-
defaultArgs: [],
|
|
42
|
-
modeArgs: {
|
|
43
|
-
normal: [],
|
|
44
|
-
continue: ["-c"],
|
|
45
|
-
resume: ["-r"],
|
|
46
|
-
},
|
|
47
|
-
permissionSkipArgs: ["--dangerously-skip-permissions"],
|
|
48
|
-
},
|
|
49
|
-
"codex-cli": {
|
|
50
|
-
command: "codex",
|
|
51
|
-
defaultArgs: ["--auto-approve", "--verbose"],
|
|
52
|
-
modeArgs: {
|
|
53
|
-
normal: [],
|
|
54
|
-
continue: ["resume", "--last"],
|
|
55
|
-
resume: ["resume"],
|
|
56
|
-
},
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
|
|
60
32
|
interface BannerState {
|
|
61
33
|
type: "success" | "error" | "info";
|
|
62
34
|
message: string;
|
|
@@ -73,15 +45,6 @@ const MERGE_STATUS_LABEL: Record<Branch["mergeStatus"], string> = {
|
|
|
73
45
|
unknown: "状態不明",
|
|
74
46
|
};
|
|
75
47
|
|
|
76
|
-
const MERGE_STATUS_TONE: Record<
|
|
77
|
-
Branch["mergeStatus"],
|
|
78
|
-
"success" | "warning" | "muted"
|
|
79
|
-
> = {
|
|
80
|
-
merged: "success",
|
|
81
|
-
unmerged: "warning",
|
|
82
|
-
unknown: "muted",
|
|
83
|
-
};
|
|
84
|
-
|
|
85
48
|
export function BranchDetailPage() {
|
|
86
49
|
const { branchName } = useParams<{ branchName: string }>();
|
|
87
50
|
const decodedBranchName = branchName ? decodeURIComponent(branchName) : "";
|
|
@@ -115,58 +78,136 @@ export function BranchDetailPage() {
|
|
|
115
78
|
[branch?.commitDate],
|
|
116
79
|
);
|
|
117
80
|
|
|
81
|
+
// Handle fullscreen body overflow
|
|
118
82
|
useEffect(() => {
|
|
119
|
-
if (!isTerminalFullscreen)
|
|
120
|
-
return undefined;
|
|
121
|
-
}
|
|
122
|
-
|
|
83
|
+
if (!isTerminalFullscreen) return undefined;
|
|
123
84
|
const previousOverflow = document.body.style.overflow;
|
|
124
85
|
document.body.style.overflow = "hidden";
|
|
125
|
-
|
|
126
86
|
return () => {
|
|
127
87
|
document.body.style.overflow = previousOverflow;
|
|
128
88
|
};
|
|
129
89
|
}, [isTerminalFullscreen]);
|
|
130
90
|
|
|
91
|
+
// Available tools - must be before conditional returns
|
|
92
|
+
const customTools: CustomAITool[] = config?.tools ?? [];
|
|
93
|
+
const availableTools: SelectableTool[] = useMemo(
|
|
94
|
+
() => [
|
|
95
|
+
{ id: "claude-code", label: "Claude Code", target: "claude" },
|
|
96
|
+
{ id: "codex-cli", label: "Codex CLI", target: "codex" },
|
|
97
|
+
...customTools.map(
|
|
98
|
+
(tool): SelectableTool => ({
|
|
99
|
+
id: tool.id,
|
|
100
|
+
label: tool.displayName,
|
|
101
|
+
target: "custom" as const,
|
|
102
|
+
definition: tool,
|
|
103
|
+
}),
|
|
104
|
+
),
|
|
105
|
+
],
|
|
106
|
+
[customTools],
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Ensure selected tool is valid - must be before conditional returns
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (!availableTools.length) {
|
|
112
|
+
setSelectedToolId("claude-code");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (!availableTools.find((tool) => tool.id === selectedToolId)) {
|
|
116
|
+
const first = availableTools[0];
|
|
117
|
+
if (first) setSelectedToolId(first.id);
|
|
118
|
+
}
|
|
119
|
+
}, [availableTools, selectedToolId]);
|
|
120
|
+
|
|
121
|
+
// Branch sessions - must be before conditional returns
|
|
122
|
+
const branchSessions = useMemo(() => {
|
|
123
|
+
return (sessionsData ?? [])
|
|
124
|
+
.filter((session) => session.worktreePath === branch?.worktreePath)
|
|
125
|
+
.sort((a, b) => (b.startedAt ?? "").localeCompare(a.startedAt ?? ""));
|
|
126
|
+
}, [sessionsData, branch?.worktreePath]);
|
|
127
|
+
|
|
128
|
+
// Latest tool usage - must be before conditional returns
|
|
129
|
+
const latestToolUsage: LastToolUsage | null = useMemo(() => {
|
|
130
|
+
if (!branch) return null;
|
|
131
|
+
if (branch.lastToolUsage) return branch.lastToolUsage;
|
|
132
|
+
const first = branchSessions[0];
|
|
133
|
+
if (!first) return null;
|
|
134
|
+
return {
|
|
135
|
+
branch: branch.name,
|
|
136
|
+
worktreePath: branch.worktreePath ?? null,
|
|
137
|
+
toolId:
|
|
138
|
+
first.toolType === "custom"
|
|
139
|
+
? (first.toolName ?? "custom")
|
|
140
|
+
: (first.toolType as LastToolUsage["toolId"]),
|
|
141
|
+
toolLabel:
|
|
142
|
+
first.toolType === "custom"
|
|
143
|
+
? (first.toolName ?? "Custom")
|
|
144
|
+
: toolLabel(first.toolType),
|
|
145
|
+
mode: first.mode ?? "normal",
|
|
146
|
+
model: null,
|
|
147
|
+
timestamp: first.startedAt ? Date.parse(first.startedAt) : Date.now(),
|
|
148
|
+
};
|
|
149
|
+
}, [branch, branchSessions]);
|
|
150
|
+
|
|
151
|
+
// Loading state
|
|
131
152
|
if (isLoading) {
|
|
132
153
|
return (
|
|
133
|
-
<div className="
|
|
134
|
-
<
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
154
|
+
<div className="min-h-screen bg-background">
|
|
155
|
+
<PageHeader
|
|
156
|
+
eyebrow="BRANCH DETAIL"
|
|
157
|
+
title="読み込み中..."
|
|
158
|
+
subtitle="ブランチ情報を取得しています"
|
|
159
|
+
/>
|
|
160
|
+
<main className="mx-auto max-w-7xl px-6 py-8">
|
|
161
|
+
<div className="flex items-center justify-center py-20">
|
|
162
|
+
<div className="text-center">
|
|
163
|
+
<div className="mb-4 text-4xl">⏳</div>
|
|
164
|
+
<p className="text-muted-foreground">Loading branch...</p>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
</main>
|
|
138
168
|
</div>
|
|
139
169
|
);
|
|
140
170
|
}
|
|
141
171
|
|
|
172
|
+
// Error state
|
|
142
173
|
if (error) {
|
|
143
174
|
return (
|
|
144
|
-
<div className="
|
|
145
|
-
<
|
|
146
|
-
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
175
|
+
<div className="min-h-screen bg-background">
|
|
176
|
+
<PageHeader eyebrow="BRANCH DETAIL" title="エラー" />
|
|
177
|
+
<main className="mx-auto max-w-7xl px-6 py-8">
|
|
178
|
+
<Alert variant="destructive">
|
|
179
|
+
<AlertDescription>
|
|
180
|
+
{error instanceof Error ? error.message : "未知のエラーです"}
|
|
181
|
+
</AlertDescription>
|
|
182
|
+
</Alert>
|
|
183
|
+
<div className="mt-4">
|
|
184
|
+
<Button variant="ghost" asChild>
|
|
185
|
+
<Link to="/">← ブランチ一覧に戻る</Link>
|
|
186
|
+
</Button>
|
|
187
|
+
</div>
|
|
188
|
+
</main>
|
|
152
189
|
</div>
|
|
153
190
|
);
|
|
154
191
|
}
|
|
155
192
|
|
|
193
|
+
// Not found state
|
|
156
194
|
if (!branch) {
|
|
157
195
|
return (
|
|
158
|
-
<div className="
|
|
159
|
-
<
|
|
160
|
-
|
|
161
|
-
<p
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
196
|
+
<div className="min-h-screen bg-background">
|
|
197
|
+
<PageHeader eyebrow="BRANCH DETAIL" title="Branch not found" />
|
|
198
|
+
<main className="mx-auto max-w-7xl px-6 py-8">
|
|
199
|
+
<p className="mb-4 text-muted-foreground">
|
|
200
|
+
指定されたブランチは存在しません。
|
|
201
|
+
</p>
|
|
202
|
+
<Button variant="ghost" asChild>
|
|
203
|
+
<Link to="/">← ブランチ一覧に戻る</Link>
|
|
204
|
+
</Button>
|
|
205
|
+
</main>
|
|
166
206
|
</div>
|
|
167
207
|
);
|
|
168
208
|
}
|
|
169
209
|
|
|
210
|
+
// Computed values
|
|
170
211
|
const canStartSession = Boolean(branch.worktreePath);
|
|
171
212
|
const divergenceInfo = branch.divergence ?? null;
|
|
172
213
|
const hasBlockingDivergence = Boolean(
|
|
@@ -174,84 +215,18 @@ export function BranchDetailPage() {
|
|
|
174
215
|
);
|
|
175
216
|
const needsRemoteSync = Boolean(
|
|
176
217
|
branch.worktreePath &&
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
218
|
+
divergenceInfo &&
|
|
219
|
+
divergenceInfo.behind > 0 &&
|
|
220
|
+
divergenceInfo.ahead === 0 &&
|
|
221
|
+
!hasBlockingDivergence,
|
|
181
222
|
);
|
|
182
223
|
const isSyncingBranch = syncBranch.isPending;
|
|
183
224
|
|
|
184
|
-
const customTools: CustomAITool[] = config?.tools ?? [];
|
|
185
|
-
const availableTools: SelectableTool[] = useMemo(
|
|
186
|
-
() => [
|
|
187
|
-
{ id: "claude-code", label: "Claude Code", target: "claude" },
|
|
188
|
-
{ id: "codex-cli", label: "Codex CLI", target: "codex" },
|
|
189
|
-
...customTools.map(
|
|
190
|
-
(tool): SelectableTool => ({
|
|
191
|
-
id: tool.id,
|
|
192
|
-
label: tool.displayName,
|
|
193
|
-
target: "custom" as const,
|
|
194
|
-
definition: tool,
|
|
195
|
-
}),
|
|
196
|
-
),
|
|
197
|
-
],
|
|
198
|
-
[customTools],
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
useEffect(() => {
|
|
202
|
-
if (!availableTools.length) {
|
|
203
|
-
setSelectedToolId("claude-code");
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
if (!availableTools.find((tool) => tool.id === selectedToolId)) {
|
|
207
|
-
const first = availableTools[0];
|
|
208
|
-
if (first) {
|
|
209
|
-
setSelectedToolId(first.id);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}, [availableTools, selectedToolId]);
|
|
213
|
-
|
|
214
225
|
const selectedTool = availableTools.find(
|
|
215
226
|
(tool) => tool.id === selectedToolId,
|
|
216
227
|
);
|
|
217
228
|
|
|
218
|
-
|
|
219
|
-
if (!selectedTool) {
|
|
220
|
-
return null;
|
|
221
|
-
}
|
|
222
|
-
if (selectedTool.target === "custom") {
|
|
223
|
-
return {
|
|
224
|
-
command: selectedTool.definition.command,
|
|
225
|
-
defaultArgs: selectedTool.definition.defaultArgs ?? null,
|
|
226
|
-
modeArgs: selectedTool.definition.modeArgs,
|
|
227
|
-
permissionSkipArgs: selectedTool.definition.permissionSkipArgs ?? null,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
return BUILTIN_TOOL_SUMMARIES[selectedTool.id] ?? null;
|
|
231
|
-
}, [selectedTool]);
|
|
232
|
-
|
|
233
|
-
const argsPreview = useMemo(() => {
|
|
234
|
-
if (!selectedToolSummary) {
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
237
|
-
const args: string[] = [];
|
|
238
|
-
if (selectedToolSummary.defaultArgs?.length) {
|
|
239
|
-
args.push(...selectedToolSummary.defaultArgs);
|
|
240
|
-
}
|
|
241
|
-
const mode = selectedToolSummary.modeArgs?.[selectedMode];
|
|
242
|
-
if (mode?.length) {
|
|
243
|
-
args.push(...mode);
|
|
244
|
-
}
|
|
245
|
-
if (skipPermissions && selectedToolSummary.permissionSkipArgs?.length) {
|
|
246
|
-
args.push(...selectedToolSummary.permissionSkipArgs);
|
|
247
|
-
}
|
|
248
|
-
const extraArgs = parseExtraArgs(extraArgsText);
|
|
249
|
-
if (extraArgs.length) {
|
|
250
|
-
args.push(...extraArgs);
|
|
251
|
-
}
|
|
252
|
-
return { command: selectedToolSummary.command, args };
|
|
253
|
-
}, [selectedToolSummary, selectedMode, skipPermissions, extraArgsText]);
|
|
254
|
-
|
|
229
|
+
// Handlers
|
|
255
230
|
const handleCreateWorktree = async () => {
|
|
256
231
|
try {
|
|
257
232
|
await createWorktree.mutateAsync({
|
|
@@ -290,8 +265,7 @@ export function BranchDetailPage() {
|
|
|
290
265
|
if (needsRemoteSync) {
|
|
291
266
|
setBanner({
|
|
292
267
|
type: "error",
|
|
293
|
-
message:
|
|
294
|
-
"リモートの更新を取り込むまでAIツールは起動できません。『最新の変更を同期』を実行してください。",
|
|
268
|
+
message: "リモートの更新を取り込むまでAIツールは起動できません。",
|
|
295
269
|
});
|
|
296
270
|
return;
|
|
297
271
|
}
|
|
@@ -299,17 +273,14 @@ export function BranchDetailPage() {
|
|
|
299
273
|
if (hasBlockingDivergence) {
|
|
300
274
|
setBanner({
|
|
301
275
|
type: "error",
|
|
302
|
-
message:
|
|
303
|
-
"リモートとローカルの双方で進捗が発生しているため、CLIと同様にAIツールの起動をブロックしました。先に rebase/merge 等で差分を解消してください。",
|
|
276
|
+
message: "差分を解消してから起動してください。",
|
|
304
277
|
});
|
|
305
278
|
return;
|
|
306
279
|
}
|
|
307
280
|
|
|
308
281
|
if (
|
|
309
282
|
skipPermissions &&
|
|
310
|
-
!window.confirm(
|
|
311
|
-
"権限チェックをスキップして起動します。自己責任で実行してください。続行しますか?",
|
|
312
|
-
)
|
|
283
|
+
!window.confirm("権限チェックをスキップして起動します。続行しますか?")
|
|
313
284
|
) {
|
|
314
285
|
return;
|
|
315
286
|
}
|
|
@@ -322,7 +293,10 @@ export function BranchDetailPage() {
|
|
|
322
293
|
: selectedTool.target === "custom"
|
|
323
294
|
? "custom"
|
|
324
295
|
: "claude-code";
|
|
325
|
-
const extraArgs =
|
|
296
|
+
const extraArgs = extraArgsText
|
|
297
|
+
.split(/\s+/)
|
|
298
|
+
.map((c) => c.trim())
|
|
299
|
+
.filter(Boolean);
|
|
326
300
|
const sessionRequest = {
|
|
327
301
|
toolType,
|
|
328
302
|
toolName: selectedTool.target === "custom" ? selectedTool.id : null,
|
|
@@ -360,9 +334,7 @@ export function BranchDetailPage() {
|
|
|
360
334
|
try {
|
|
361
335
|
await deleteSession.mutateAsync(sessionId);
|
|
362
336
|
setBanner({ type: "success", message: "セッションを終了しました" });
|
|
363
|
-
if (activeSessionId === sessionId)
|
|
364
|
-
setActiveSessionId(null);
|
|
365
|
-
}
|
|
337
|
+
if (activeSessionId === sessionId) setActiveSessionId(null);
|
|
366
338
|
} catch (err) {
|
|
367
339
|
setBanner({
|
|
368
340
|
type: "error",
|
|
@@ -408,40 +380,6 @@ export function BranchDetailPage() {
|
|
|
408
380
|
}
|
|
409
381
|
};
|
|
410
382
|
|
|
411
|
-
const branchSessions = useMemo(() => {
|
|
412
|
-
return (sessionsData ?? [])
|
|
413
|
-
.filter((session) => session.worktreePath === branch?.worktreePath)
|
|
414
|
-
.sort((a, b) => (b.startedAt ?? "").localeCompare(a.startedAt ?? ""));
|
|
415
|
-
}, [sessionsData, branch?.worktreePath]);
|
|
416
|
-
|
|
417
|
-
const latestToolUsage: LastToolUsage | null = useMemo(() => {
|
|
418
|
-
if (branch?.lastToolUsage) {
|
|
419
|
-
return branch.lastToolUsage;
|
|
420
|
-
}
|
|
421
|
-
const first = branchSessions[0];
|
|
422
|
-
if (!first) return null;
|
|
423
|
-
return {
|
|
424
|
-
branch: branch.name,
|
|
425
|
-
worktreePath: branch.worktreePath ?? null,
|
|
426
|
-
toolId:
|
|
427
|
-
first.toolType === "custom"
|
|
428
|
-
? (first.toolName ?? "custom")
|
|
429
|
-
: (first.toolType as LastToolUsage["toolId"]),
|
|
430
|
-
toolLabel:
|
|
431
|
-
first.toolType === "custom"
|
|
432
|
-
? (first.toolName ?? "Custom")
|
|
433
|
-
: toolLabel(first.toolType),
|
|
434
|
-
mode: first.mode ?? "normal",
|
|
435
|
-
model: null,
|
|
436
|
-
timestamp: first.startedAt ? Date.parse(first.startedAt) : Date.now(),
|
|
437
|
-
};
|
|
438
|
-
}, [
|
|
439
|
-
branch?.lastToolUsage,
|
|
440
|
-
branch?.name,
|
|
441
|
-
branch?.worktreePath,
|
|
442
|
-
branchSessions,
|
|
443
|
-
]);
|
|
444
|
-
|
|
445
383
|
const handleSessionExit = (code: number) => {
|
|
446
384
|
setActiveSessionId(null);
|
|
447
385
|
setIsTerminalFullscreen(false);
|
|
@@ -452,488 +390,132 @@ export function BranchDetailPage() {
|
|
|
452
390
|
};
|
|
453
391
|
|
|
454
392
|
return (
|
|
455
|
-
<div className="
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
393
|
+
<div className="min-h-screen bg-background">
|
|
394
|
+
{/* Fullscreen backdrop */}
|
|
395
|
+
{isTerminalFullscreen && (
|
|
396
|
+
<div
|
|
397
|
+
className="fixed inset-0 z-40 bg-black/80"
|
|
398
|
+
aria-hidden="true"
|
|
399
|
+
onClick={() => setIsTerminalFullscreen(false)}
|
|
400
|
+
/>
|
|
401
|
+
)}
|
|
402
|
+
|
|
403
|
+
{/* Header */}
|
|
404
|
+
<PageHeader
|
|
405
|
+
eyebrow="BRANCH DETAIL"
|
|
406
|
+
title={branch.name}
|
|
407
|
+
subtitle={`最新コミット ${branch.commitHash.slice(0, 7)} · ${formattedCommitDate}`}
|
|
408
|
+
>
|
|
409
|
+
<div className="mt-4 flex flex-wrap gap-2">
|
|
410
|
+
<Badge variant={branch.type === "local" ? "local" : "remote"}>
|
|
467
411
|
{BRANCH_TYPE_LABEL[branch.type]}
|
|
468
|
-
</
|
|
469
|
-
<
|
|
470
|
-
|
|
412
|
+
</Badge>
|
|
413
|
+
<Badge
|
|
414
|
+
variant={
|
|
415
|
+
branch.mergeStatus === "merged"
|
|
416
|
+
? "success"
|
|
417
|
+
: branch.mergeStatus === "unmerged"
|
|
418
|
+
? "warning"
|
|
419
|
+
: "outline"
|
|
420
|
+
}
|
|
471
421
|
>
|
|
472
422
|
{MERGE_STATUS_LABEL[branch.mergeStatus]}
|
|
473
|
-
</
|
|
474
|
-
<
|
|
475
|
-
className={`status-badge ${
|
|
476
|
-
branch.worktreePath
|
|
477
|
-
? "status-badge--success"
|
|
478
|
-
: "status-badge--muted"
|
|
479
|
-
}`}
|
|
480
|
-
>
|
|
423
|
+
</Badge>
|
|
424
|
+
<Badge variant={branch.worktreePath ? "success" : "outline"}>
|
|
481
425
|
{branch.worktreePath ? "Worktreeあり" : "Worktree未作成"}
|
|
482
|
-
</
|
|
426
|
+
</Badge>
|
|
483
427
|
</div>
|
|
484
|
-
<div className="
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
{renderToolUsage(latestToolUsage)}
|
|
489
|
-
</span>
|
|
490
|
-
<span className="status-badge status-badge--muted">
|
|
491
|
-
{formatUsageTimestamp(latestToolUsage.timestamp)} ・ worktree:{" "}
|
|
492
|
-
{latestToolUsage.worktreePath ?? branch.worktreePath ?? "N/A"}
|
|
493
|
-
</span>
|
|
494
|
-
</>
|
|
495
|
-
) : (
|
|
496
|
-
<span className="status-badge status-badge--muted">Unknown</span>
|
|
497
|
-
)}
|
|
498
|
-
</div>
|
|
499
|
-
<div className="page-hero__actions">
|
|
428
|
+
<div className="mt-4 flex flex-wrap gap-2">
|
|
429
|
+
<Button variant="ghost" size="sm" asChild>
|
|
430
|
+
<Link to="/">← ブランチ一覧</Link>
|
|
431
|
+
</Button>
|
|
500
432
|
{!canStartSession ? (
|
|
501
|
-
<
|
|
502
|
-
type="button"
|
|
503
|
-
className="button button--primary"
|
|
433
|
+
<Button
|
|
504
434
|
onClick={handleCreateWorktree}
|
|
505
435
|
disabled={createWorktree.isPending}
|
|
506
436
|
>
|
|
507
437
|
{createWorktree.isPending ? "作成中..." : "Worktreeを作成"}
|
|
508
|
-
</
|
|
438
|
+
</Button>
|
|
509
439
|
) : (
|
|
510
|
-
<
|
|
511
|
-
|
|
512
|
-
</
|
|
440
|
+
<Button variant="secondary" asChild>
|
|
441
|
+
<Link to="/config">カスタムツール設定</Link>
|
|
442
|
+
</Button>
|
|
513
443
|
)}
|
|
514
444
|
</div>
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
445
|
+
</PageHeader>
|
|
446
|
+
|
|
447
|
+
{/* Banner */}
|
|
448
|
+
{banner && (
|
|
449
|
+
<div className="mx-auto max-w-7xl px-6 pt-4">
|
|
450
|
+
<Alert
|
|
451
|
+
variant={
|
|
452
|
+
banner.type === "error"
|
|
453
|
+
? "destructive"
|
|
454
|
+
: banner.type === "success"
|
|
455
|
+
? "success"
|
|
456
|
+
: "info"
|
|
457
|
+
}
|
|
458
|
+
>
|
|
459
|
+
<AlertDescription>{banner.message}</AlertDescription>
|
|
460
|
+
</Alert>
|
|
461
|
+
</div>
|
|
529
462
|
)}
|
|
530
|
-
<main className="page-content page-content--wide">
|
|
531
|
-
<div className="page-layout page-layout--split">
|
|
532
|
-
<div className="info-stack">
|
|
533
|
-
<section className="section-card">
|
|
534
|
-
<header className="terminal-section__header">
|
|
535
|
-
<div>
|
|
536
|
-
<h2>AIツール起動</h2>
|
|
537
|
-
<p className="section-card__body">
|
|
538
|
-
Web UI
|
|
539
|
-
から直接AIツールを起動できます。設定したカスタムツールも一覧に表示されます。
|
|
540
|
-
</p>
|
|
541
|
-
</div>
|
|
542
|
-
{configError && (
|
|
543
|
-
<span className="pill pill--warning">
|
|
544
|
-
設定の取得に失敗しました
|
|
545
|
-
</span>
|
|
546
|
-
)}
|
|
547
|
-
</header>
|
|
548
|
-
|
|
549
|
-
{!canStartSession ? (
|
|
550
|
-
<p className="section-card__body">
|
|
551
|
-
Worktreeが未作成のため、先にWorktreeを作成してください。
|
|
552
|
-
</p>
|
|
553
|
-
) : (
|
|
554
|
-
<div className="tool-form">
|
|
555
|
-
<div className="form-grid">
|
|
556
|
-
<label className="form-field">
|
|
557
|
-
<span>AIツール</span>
|
|
558
|
-
<select
|
|
559
|
-
value={selectedToolId}
|
|
560
|
-
onChange={(event) =>
|
|
561
|
-
setSelectedToolId(event.target.value)
|
|
562
|
-
}
|
|
563
|
-
disabled={isConfigLoading}
|
|
564
|
-
>
|
|
565
|
-
{availableTools.map((tool) => (
|
|
566
|
-
<option key={tool.id} value={tool.id}>
|
|
567
|
-
{tool.label}
|
|
568
|
-
</option>
|
|
569
|
-
))}
|
|
570
|
-
</select>
|
|
571
|
-
</label>
|
|
572
|
-
|
|
573
|
-
<label className="form-field">
|
|
574
|
-
<span>起動モード</span>
|
|
575
|
-
<select
|
|
576
|
-
value={selectedMode}
|
|
577
|
-
onChange={(event) =>
|
|
578
|
-
setSelectedMode(event.target.value as ToolMode)
|
|
579
|
-
}
|
|
580
|
-
>
|
|
581
|
-
<option value="normal">normal</option>
|
|
582
|
-
<option value="continue">continue</option>
|
|
583
|
-
<option value="resume">resume</option>
|
|
584
|
-
</select>
|
|
585
|
-
</label>
|
|
586
|
-
|
|
587
|
-
<label className="form-field">
|
|
588
|
-
<span>追加引数 (スペース区切り)</span>
|
|
589
|
-
<input
|
|
590
|
-
type="text"
|
|
591
|
-
value={extraArgsText}
|
|
592
|
-
onChange={(event) =>
|
|
593
|
-
setExtraArgsText(event.target.value)
|
|
594
|
-
}
|
|
595
|
-
placeholder="--flag value"
|
|
596
|
-
/>
|
|
597
|
-
</label>
|
|
598
|
-
</div>
|
|
599
|
-
|
|
600
|
-
<label className="form-field">
|
|
601
|
-
<span>
|
|
602
|
-
<input
|
|
603
|
-
type="checkbox"
|
|
604
|
-
checked={skipPermissions}
|
|
605
|
-
onChange={(event) =>
|
|
606
|
-
setSkipPermissions(event.target.checked)
|
|
607
|
-
}
|
|
608
|
-
/>
|
|
609
|
-
<span style={{ marginLeft: "0.5rem" }}>
|
|
610
|
-
権限チェックをスキップ (自己責任)
|
|
611
|
-
</span>
|
|
612
|
-
</span>
|
|
613
|
-
</label>
|
|
614
|
-
{skipPermissions && (
|
|
615
|
-
<div className="inline-banner inline-banner--warning">
|
|
616
|
-
<p>
|
|
617
|
-
権限チェックをスキップすることで、CLI での
|
|
618
|
-
`--dangerously-skip-permissions`
|
|
619
|
-
指定と同様のリスクを負います。
|
|
620
|
-
</p>
|
|
621
|
-
</div>
|
|
622
|
-
)}
|
|
623
|
-
{needsRemoteSync && (
|
|
624
|
-
<div
|
|
625
|
-
className="inline-banner inline-banner--info"
|
|
626
|
-
data-testid="sync-required"
|
|
627
|
-
>
|
|
628
|
-
<p>
|
|
629
|
-
リモートに未取得の更新 ({branch.divergence?.behind ?? 0}{" "}
|
|
630
|
-
commits)
|
|
631
|
-
があるため、AIツールを起動する前に同期してください。
|
|
632
|
-
</p>
|
|
633
|
-
<p className="section-card__body">
|
|
634
|
-
CLI の `git fetch --all` と `git pull --ff-only`
|
|
635
|
-
と同じ処理を Web UI から実行できます。
|
|
636
|
-
</p>
|
|
637
|
-
</div>
|
|
638
|
-
)}
|
|
639
|
-
{hasBlockingDivergence && (
|
|
640
|
-
<div
|
|
641
|
-
className="inline-banner inline-banner--warning"
|
|
642
|
-
data-testid="divergence-warning"
|
|
643
|
-
>
|
|
644
|
-
<p>
|
|
645
|
-
リモートとローカルの両方に未解決の差分があるため、Web UI
|
|
646
|
-
でも CLI と同様に起動をブロックしています。
|
|
647
|
-
</p>
|
|
648
|
-
<ul className="list-muted">
|
|
649
|
-
<li>
|
|
650
|
-
git fetch && git pull --ff-only origin {branch.name}
|
|
651
|
-
</li>
|
|
652
|
-
<li>
|
|
653
|
-
必要に応じて git push origin {branch.name}{" "}
|
|
654
|
-
でローカル進捗を共有
|
|
655
|
-
</li>
|
|
656
|
-
</ul>
|
|
657
|
-
<p className="section-card__body">
|
|
658
|
-
rebase / merge
|
|
659
|
-
などで差分を解消した後にページを更新してください。
|
|
660
|
-
</p>
|
|
661
|
-
</div>
|
|
662
|
-
)}
|
|
663
463
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
<div>
|
|
705
|
-
<dt>permissionSkipArgs</dt>
|
|
706
|
-
<dd>
|
|
707
|
-
{renderArgs(selectedToolSummary.permissionSkipArgs)}
|
|
708
|
-
</dd>
|
|
709
|
-
</div>
|
|
710
|
-
{argsPreview && (
|
|
711
|
-
<div className="metadata-grid__full">
|
|
712
|
-
<dt>最終的に実行されるコマンド</dt>
|
|
713
|
-
<dd className="tool-card__command">
|
|
714
|
-
{argsPreview.command} {argsPreview.args.join(" ")}
|
|
715
|
-
</dd>
|
|
716
|
-
</div>
|
|
717
|
-
)}
|
|
718
|
-
</dl>
|
|
719
|
-
)}
|
|
720
|
-
</div>
|
|
721
|
-
)}
|
|
722
|
-
</section>
|
|
723
|
-
<section className="section-card">
|
|
724
|
-
<header className="terminal-section__header">
|
|
725
|
-
<div>
|
|
726
|
-
<h2>セッション履歴</h2>
|
|
727
|
-
<p className="section-card__body">
|
|
728
|
-
この Worktree に紐づいた最新の AI
|
|
729
|
-
セッションが表示されます。CLI からの起動分も共有されます。
|
|
730
|
-
</p>
|
|
731
|
-
</div>
|
|
732
|
-
{isSessionsLoading && (
|
|
733
|
-
<span className="pill">読み込み中...</span>
|
|
734
|
-
)}
|
|
735
|
-
</header>
|
|
736
|
-
{branchSessions.length === 0 ? (
|
|
737
|
-
<p className="section-card__body">
|
|
738
|
-
セッション履歴はまだありません。
|
|
739
|
-
</p>
|
|
740
|
-
) : (
|
|
741
|
-
<div className="session-table-wrapper">
|
|
742
|
-
<table className="session-table">
|
|
743
|
-
<thead>
|
|
744
|
-
<tr>
|
|
745
|
-
<th>状態</th>
|
|
746
|
-
<th>ツール</th>
|
|
747
|
-
<th>モード</th>
|
|
748
|
-
<th>開始時刻</th>
|
|
749
|
-
<th>終了時刻</th>
|
|
750
|
-
<th>操作</th>
|
|
751
|
-
</tr>
|
|
752
|
-
</thead>
|
|
753
|
-
<tbody>
|
|
754
|
-
{branchSessions.slice(0, 5).map((session) => (
|
|
755
|
-
<tr key={session.sessionId}>
|
|
756
|
-
<td>
|
|
757
|
-
<span
|
|
758
|
-
className={`status-pill status-pill--${session.status}`}
|
|
759
|
-
>
|
|
760
|
-
{SESSION_STATUS_LABEL[session.status]}
|
|
761
|
-
</span>
|
|
762
|
-
</td>
|
|
763
|
-
<td>
|
|
764
|
-
{session.toolType === "custom"
|
|
765
|
-
? (session.toolName ?? "custom")
|
|
766
|
-
: toolLabel(session.toolType)}
|
|
767
|
-
</td>
|
|
768
|
-
<td>{session.mode}</td>
|
|
769
|
-
<td>{formatDate(session.startedAt)}</td>
|
|
770
|
-
<td>
|
|
771
|
-
{session.endedAt
|
|
772
|
-
? formatDate(session.endedAt)
|
|
773
|
-
: "--"}
|
|
774
|
-
</td>
|
|
775
|
-
<td>
|
|
776
|
-
{session.status === "running" ? (
|
|
777
|
-
<button
|
|
778
|
-
type="button"
|
|
779
|
-
className="button button--ghost"
|
|
780
|
-
onClick={() =>
|
|
781
|
-
handleTerminateSession(session.sessionId)
|
|
782
|
-
}
|
|
783
|
-
disabled={
|
|
784
|
-
terminatingSessionId === session.sessionId ||
|
|
785
|
-
deleteSession.isPending
|
|
786
|
-
}
|
|
787
|
-
>
|
|
788
|
-
{terminatingSessionId === session.sessionId
|
|
789
|
-
? "終了中..."
|
|
790
|
-
: "終了"}
|
|
791
|
-
</button>
|
|
792
|
-
) : (
|
|
793
|
-
<span className="session-table__muted">--</span>
|
|
794
|
-
)}
|
|
795
|
-
</td>
|
|
796
|
-
</tr>
|
|
797
|
-
))}
|
|
798
|
-
</tbody>
|
|
799
|
-
</table>
|
|
800
|
-
</div>
|
|
801
|
-
)}
|
|
802
|
-
</section>
|
|
803
|
-
<section className="section-card">
|
|
804
|
-
<header>
|
|
805
|
-
<h2>ブランチインサイト</h2>
|
|
806
|
-
</header>
|
|
807
|
-
<dl className="metadata-grid">
|
|
808
|
-
<div>
|
|
809
|
-
<dt>コミット</dt>
|
|
810
|
-
<dd>{branch.commitHash}</dd>
|
|
811
|
-
</div>
|
|
812
|
-
<div>
|
|
813
|
-
<dt>Author</dt>
|
|
814
|
-
<dd>{branch.author ?? "N/A"}</dd>
|
|
815
|
-
</div>
|
|
816
|
-
<div>
|
|
817
|
-
<dt>更新日</dt>
|
|
818
|
-
<dd>{formattedCommitDate}</dd>
|
|
819
|
-
</div>
|
|
820
|
-
<div>
|
|
821
|
-
<dt>Worktree</dt>
|
|
822
|
-
<dd>{branch.worktreePath ?? "未作成"}</dd>
|
|
823
|
-
</div>
|
|
824
|
-
</dl>
|
|
825
|
-
</section>
|
|
826
|
-
|
|
827
|
-
<section className="section-card">
|
|
828
|
-
<header>
|
|
829
|
-
<h2>コミット情報</h2>
|
|
830
|
-
</header>
|
|
831
|
-
<p className="section-card__body">
|
|
832
|
-
{branch.commitMessage ?? "コミットメッセージがありません。"}
|
|
833
|
-
</p>
|
|
834
|
-
</section>
|
|
835
|
-
|
|
836
|
-
{branch.divergence && (
|
|
837
|
-
<section className="section-card">
|
|
838
|
-
<header>
|
|
839
|
-
<h2>差分状況</h2>
|
|
840
|
-
</header>
|
|
841
|
-
<div className="pill-group">
|
|
842
|
-
<span className="pill">Ahead {branch.divergence.ahead}</span>
|
|
843
|
-
<span className="pill">
|
|
844
|
-
Behind {branch.divergence.behind}
|
|
845
|
-
</span>
|
|
846
|
-
<span
|
|
847
|
-
className={`pill ${
|
|
848
|
-
branch.divergence.upToDate
|
|
849
|
-
? "pill--success"
|
|
850
|
-
: "pill--warning"
|
|
851
|
-
}`}
|
|
852
|
-
>
|
|
853
|
-
{branch.divergence.upToDate ? "最新" : "更新あり"}
|
|
854
|
-
</span>
|
|
855
|
-
</div>
|
|
856
|
-
</section>
|
|
857
|
-
)}
|
|
858
|
-
|
|
859
|
-
<section className="section-card">
|
|
860
|
-
<header>
|
|
861
|
-
<h2>Worktree情報</h2>
|
|
862
|
-
</header>
|
|
863
|
-
<ul className="list-muted">
|
|
864
|
-
<li>
|
|
865
|
-
パス: <strong>{branch.worktreePath ?? "未作成"}</strong>
|
|
866
|
-
</li>
|
|
867
|
-
<li>
|
|
868
|
-
AIツールの起動にはクリーンなワークツリーであることを推奨します。
|
|
869
|
-
</li>
|
|
870
|
-
<li>
|
|
871
|
-
Worktreeを再作成すると既存のローカル変更が失われる可能性があります。
|
|
872
|
-
</li>
|
|
873
|
-
</ul>
|
|
874
|
-
</section>
|
|
464
|
+
{/* Main Content */}
|
|
465
|
+
<main className="mx-auto max-w-7xl space-y-6 px-6 py-8">
|
|
466
|
+
<div className="grid gap-6 lg:grid-cols-[1fr_400px]">
|
|
467
|
+
{/* Left Column - Tool Launcher & Session History */}
|
|
468
|
+
<div className="space-y-6">
|
|
469
|
+
<ToolLauncher
|
|
470
|
+
branch={branch}
|
|
471
|
+
availableTools={availableTools}
|
|
472
|
+
selectedToolId={selectedToolId}
|
|
473
|
+
selectedMode={selectedMode}
|
|
474
|
+
skipPermissions={skipPermissions}
|
|
475
|
+
extraArgsText={extraArgsText}
|
|
476
|
+
isConfigLoading={isConfigLoading}
|
|
477
|
+
configError={configError ?? null}
|
|
478
|
+
isStartingSession={isStartingSession}
|
|
479
|
+
isSyncingBranch={isSyncingBranch}
|
|
480
|
+
needsRemoteSync={needsRemoteSync}
|
|
481
|
+
hasBlockingDivergence={hasBlockingDivergence}
|
|
482
|
+
onToolChange={setSelectedToolId}
|
|
483
|
+
onModeChange={setSelectedMode}
|
|
484
|
+
onSkipPermissionsChange={setSkipPermissions}
|
|
485
|
+
onExtraArgsChange={setExtraArgsText}
|
|
486
|
+
onStartSession={handleStartSession}
|
|
487
|
+
onSyncBranch={handleSyncBranch}
|
|
488
|
+
/>
|
|
489
|
+
|
|
490
|
+
<SessionHistoryTable
|
|
491
|
+
sessions={branchSessions}
|
|
492
|
+
isLoading={isSessionsLoading}
|
|
493
|
+
terminatingSessionId={terminatingSessionId}
|
|
494
|
+
isDeleting={deleteSession.isPending}
|
|
495
|
+
onTerminate={handleTerminateSession}
|
|
496
|
+
onSelectSession={setActiveSessionId}
|
|
497
|
+
/>
|
|
498
|
+
|
|
499
|
+
<BranchInfoCards
|
|
500
|
+
branch={branch}
|
|
501
|
+
formattedCommitDate={formattedCommitDate}
|
|
502
|
+
latestToolUsage={latestToolUsage}
|
|
503
|
+
/>
|
|
875
504
|
</div>
|
|
876
505
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
</p>
|
|
891
|
-
</div>
|
|
892
|
-
<div className="terminal-section__controls">
|
|
893
|
-
<button
|
|
894
|
-
type="button"
|
|
895
|
-
className="button button--ghost"
|
|
896
|
-
onClick={() => setIsTerminalFullscreen((prev) => !prev)}
|
|
897
|
-
>
|
|
898
|
-
{isTerminalFullscreen
|
|
899
|
-
? "通常表示に戻す"
|
|
900
|
-
: "ターミナルを最大化"}
|
|
901
|
-
</button>
|
|
902
|
-
</div>
|
|
903
|
-
</div>
|
|
904
|
-
<div className="terminal-surface">
|
|
905
|
-
<Terminal
|
|
906
|
-
sessionId={activeSessionId}
|
|
907
|
-
onExit={handleSessionExit}
|
|
908
|
-
onError={(message) =>
|
|
909
|
-
setBanner({
|
|
910
|
-
type: "error",
|
|
911
|
-
message: message ?? "不明なエラー",
|
|
912
|
-
})
|
|
913
|
-
}
|
|
914
|
-
/>
|
|
915
|
-
</div>
|
|
916
|
-
{isTerminalFullscreen && (
|
|
917
|
-
<button
|
|
918
|
-
type="button"
|
|
919
|
-
className="terminal-section__close"
|
|
920
|
-
aria-label="ターミナルを閉じる"
|
|
921
|
-
onClick={() => setIsTerminalFullscreen(false)}
|
|
922
|
-
>
|
|
923
|
-
×
|
|
924
|
-
</button>
|
|
925
|
-
)}
|
|
926
|
-
</section>
|
|
927
|
-
) : (
|
|
928
|
-
<section className="section-card session-hint">
|
|
929
|
-
<header>
|
|
930
|
-
<h2>セッションは未起動</h2>
|
|
931
|
-
</header>
|
|
932
|
-
<p className="section-card__body">
|
|
933
|
-
上部のアクションからAIツールを起動すると、このエリアにターミナルが表示されます。
|
|
934
|
-
</p>
|
|
935
|
-
</section>
|
|
936
|
-
)}
|
|
506
|
+
{/* Right Column - Terminal */}
|
|
507
|
+
<div className="lg:sticky lg:top-6 lg:self-start">
|
|
508
|
+
<TerminalPanel
|
|
509
|
+
sessionId={activeSessionId}
|
|
510
|
+
isFullscreen={isTerminalFullscreen}
|
|
511
|
+
onToggleFullscreen={() =>
|
|
512
|
+
setIsTerminalFullscreen((prev) => !prev)
|
|
513
|
+
}
|
|
514
|
+
onExit={handleSessionExit}
|
|
515
|
+
onError={(message) =>
|
|
516
|
+
setBanner({ type: "error", message: message ?? "不明なエラー" })
|
|
517
|
+
}
|
|
518
|
+
/>
|
|
937
519
|
</div>
|
|
938
520
|
</div>
|
|
939
521
|
</main>
|
|
@@ -941,91 +523,33 @@ export function BranchDetailPage() {
|
|
|
941
523
|
);
|
|
942
524
|
}
|
|
943
525
|
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
}
|
|
948
|
-
|
|
526
|
+
// Helper functions
|
|
527
|
+
function formatDate(value?: string | null): string {
|
|
528
|
+
if (!value) return "日時不明";
|
|
949
529
|
try {
|
|
950
|
-
const date = new Date(value);
|
|
951
530
|
return new Intl.DateTimeFormat("ja-JP", {
|
|
952
531
|
year: "numeric",
|
|
953
532
|
month: "short",
|
|
954
533
|
day: "numeric",
|
|
955
534
|
hour: "2-digit",
|
|
956
535
|
minute: "2-digit",
|
|
957
|
-
}).format(
|
|
958
|
-
} catch
|
|
536
|
+
}).format(new Date(value));
|
|
537
|
+
} catch {
|
|
959
538
|
return value;
|
|
960
539
|
}
|
|
961
540
|
}
|
|
962
541
|
|
|
963
|
-
function formatError(error: unknown, fallback: string) {
|
|
542
|
+
function formatError(error: unknown, fallback: string): string {
|
|
964
543
|
if (error instanceof ApiError) {
|
|
965
544
|
return `${error.message}${error.details ? `\n${error.details}` : ""}`;
|
|
966
545
|
}
|
|
967
|
-
if (error instanceof Error)
|
|
968
|
-
return error.message;
|
|
969
|
-
}
|
|
546
|
+
if (error instanceof Error) return error.message;
|
|
970
547
|
return fallback;
|
|
971
548
|
}
|
|
972
549
|
|
|
973
|
-
function toolLabel(tool:
|
|
974
|
-
if (tool === "custom" && selectedTool?.target === "custom")
|
|
550
|
+
function toolLabel(tool: string, selectedTool?: SelectableTool): string {
|
|
551
|
+
if (tool === "custom" && selectedTool?.target === "custom")
|
|
975
552
|
return selectedTool.label;
|
|
976
|
-
|
|
977
|
-
if (tool === "codex-cli") {
|
|
978
|
-
return "Codex CLI";
|
|
979
|
-
}
|
|
553
|
+
if (tool === "codex-cli") return "Codex CLI";
|
|
980
554
|
return "Claude Code";
|
|
981
555
|
}
|
|
982
|
-
|
|
983
|
-
function renderArgs(args?: string[] | null) {
|
|
984
|
-
if (!args || args.length === 0) {
|
|
985
|
-
return <span className="tool-card__muted">未設定</span>;
|
|
986
|
-
}
|
|
987
|
-
return args.join(" ");
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
const SESSION_STATUS_LABEL: Record<
|
|
991
|
-
"pending" | "running" | "completed" | "failed",
|
|
992
|
-
string
|
|
993
|
-
> = {
|
|
994
|
-
pending: "pending",
|
|
995
|
-
running: "running",
|
|
996
|
-
completed: "completed",
|
|
997
|
-
failed: "failed",
|
|
998
|
-
};
|
|
999
|
-
|
|
1000
|
-
function renderToolUsage(usage: LastToolUsage): string {
|
|
1001
|
-
const modeLabel =
|
|
1002
|
-
usage.mode === "normal"
|
|
1003
|
-
? "New"
|
|
1004
|
-
: usage.mode === "continue"
|
|
1005
|
-
? "Continue"
|
|
1006
|
-
: usage.mode === "resume"
|
|
1007
|
-
? "Resume"
|
|
1008
|
-
: null;
|
|
1009
|
-
const toolText = mapToolLabel(usage.toolId, usage.toolLabel);
|
|
1010
|
-
return [toolText, modeLabel, usage.model].filter(Boolean).join(" | ");
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
function formatUsageTimestamp(value: number): string {
|
|
1014
|
-
return formatDate(new Date(value).toISOString());
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
function mapToolLabel(toolId: string, toolLabel?: string | null): string {
|
|
1018
|
-
if (toolId === "claude-code") return "Claude";
|
|
1019
|
-
if (toolId === "codex-cli") return "Codex";
|
|
1020
|
-
if (toolId === "gemini-cli") return "Gemini";
|
|
1021
|
-
if (toolId === "qwen-cli") return "Qwen";
|
|
1022
|
-
if (toolLabel) return toolLabel;
|
|
1023
|
-
return "Custom";
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
function parseExtraArgs(value: string): string[] {
|
|
1027
|
-
return value
|
|
1028
|
-
.split(/\s+/)
|
|
1029
|
-
.map((chunk) => chunk.trim())
|
|
1030
|
-
.filter(Boolean);
|
|
1031
|
-
}
|