@akiojin/gwt 2.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 +323 -0
- package/README.md +347 -0
- package/bin/gwt.js +5 -0
- package/package.json +125 -0
- package/src/claude-history.ts +717 -0
- package/src/claude.ts +292 -0
- package/src/cli/ui/__tests__/SKIPPED_TESTS.md +119 -0
- package/src/cli/ui/__tests__/acceptance/branchList.acceptance.test.tsx.skip +239 -0
- package/src/cli/ui/__tests__/acceptance/navigation.acceptance.test.tsx +214 -0
- package/src/cli/ui/__tests__/acceptance/realtimeUpdate.acceptance.test.tsx.skip +219 -0
- package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +183 -0
- package/src/cli/ui/__tests__/components/App.shortcuts.test.tsx +313 -0
- package/src/cli/ui/__tests__/components/App.test.tsx +270 -0
- package/src/cli/ui/__tests__/components/common/Confirm.test.tsx +66 -0
- package/src/cli/ui/__tests__/components/common/ErrorBoundary.test.tsx +103 -0
- package/src/cli/ui/__tests__/components/common/Input.test.tsx +92 -0
- package/src/cli/ui/__tests__/components/common/LoadingIndicator.test.tsx +127 -0
- package/src/cli/ui/__tests__/components/common/Select.memo.test.tsx +264 -0
- package/src/cli/ui/__tests__/components/common/Select.test.tsx +246 -0
- package/src/cli/ui/__tests__/components/parts/Footer.test.tsx +62 -0
- package/src/cli/ui/__tests__/components/parts/Header.test.tsx +54 -0
- package/src/cli/ui/__tests__/components/parts/ScrollableList.test.tsx +68 -0
- package/src/cli/ui/__tests__/components/parts/Stats.test.tsx +135 -0
- package/src/cli/ui/__tests__/components/screens/AIToolSelectorScreen.test.tsx +153 -0
- package/src/cli/ui/__tests__/components/screens/BranchCreatorScreen.test.tsx +215 -0
- package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +293 -0
- package/src/cli/ui/__tests__/components/screens/ExecutionModeSelectorScreen.test.tsx +161 -0
- package/src/cli/ui/__tests__/components/screens/PRCleanupScreen.test.tsx +215 -0
- package/src/cli/ui/__tests__/components/screens/SessionSelectorScreen.test.tsx +99 -0
- package/src/cli/ui/__tests__/components/screens/WorktreeManagerScreen.test.tsx +127 -0
- package/src/cli/ui/__tests__/hooks/useGitData.test.ts.skip +228 -0
- package/src/cli/ui/__tests__/hooks/useScreenState.test.ts +146 -0
- package/src/cli/ui/__tests__/hooks/useTerminalSize.test.ts +98 -0
- package/src/cli/ui/__tests__/integration/branchList.test.tsx.skip +253 -0
- package/src/cli/ui/__tests__/integration/edgeCases.test.tsx +306 -0
- package/src/cli/ui/__tests__/integration/navigation.test.tsx +405 -0
- package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx +505 -0
- package/src/cli/ui/__tests__/integration/realtimeUpdate.test.tsx.skip +216 -0
- package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +180 -0
- package/src/cli/ui/__tests__/performance/useMemoOptimization.test.tsx +237 -0
- package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +775 -0
- package/src/cli/ui/__tests__/utils/statisticsCalculator.test.ts +243 -0
- package/src/cli/ui/components/App.tsx +793 -0
- package/src/cli/ui/components/common/Confirm.tsx +40 -0
- package/src/cli/ui/components/common/ErrorBoundary.tsx +57 -0
- package/src/cli/ui/components/common/Input.tsx +36 -0
- package/src/cli/ui/components/common/LoadingIndicator.tsx +95 -0
- package/src/cli/ui/components/common/Select.tsx +216 -0
- package/src/cli/ui/components/parts/Footer.tsx +41 -0
- package/src/cli/ui/components/parts/Header.test.tsx +85 -0
- package/src/cli/ui/components/parts/Header.tsx +63 -0
- package/src/cli/ui/components/parts/MergeStatusList.tsx +75 -0
- package/src/cli/ui/components/parts/ProgressBar.tsx +73 -0
- package/src/cli/ui/components/parts/ScrollableList.tsx +24 -0
- package/src/cli/ui/components/parts/Stats.tsx +67 -0
- package/src/cli/ui/components/screens/AIToolSelectorScreen.tsx +116 -0
- package/src/cli/ui/components/screens/BatchMergeProgressScreen.tsx +70 -0
- package/src/cli/ui/components/screens/BatchMergeResultScreen.tsx +104 -0
- package/src/cli/ui/components/screens/BranchCreatorScreen.tsx +213 -0
- package/src/cli/ui/components/screens/BranchListScreen.tsx +299 -0
- package/src/cli/ui/components/screens/ExecutionModeSelectorScreen.tsx +149 -0
- package/src/cli/ui/components/screens/PRCleanupScreen.tsx +167 -0
- package/src/cli/ui/components/screens/SessionSelectorScreen.tsx +100 -0
- package/src/cli/ui/components/screens/WorktreeManagerScreen.tsx +117 -0
- package/src/cli/ui/hooks/useBatchMerge.ts +96 -0
- package/src/cli/ui/hooks/useGitData.ts +157 -0
- package/src/cli/ui/hooks/useScreenState.ts +44 -0
- package/src/cli/ui/hooks/useTerminalSize.ts +33 -0
- package/src/cli/ui/screens/BranchActionSelectorScreen.tsx +102 -0
- package/src/cli/ui/screens/__tests__/BranchActionSelectorScreen.test.tsx +151 -0
- package/src/cli/ui/types.ts +295 -0
- package/src/cli/ui/utils/baseBranch.ts +34 -0
- package/src/cli/ui/utils/branchFormatter.ts +222 -0
- package/src/cli/ui/utils/statisticsCalculator.ts +44 -0
- package/src/codex.ts +139 -0
- package/src/config/builtin-tools.ts +44 -0
- package/src/config/constants.ts +100 -0
- package/src/config/env-history.ts +45 -0
- package/src/config/index.ts +204 -0
- package/src/config/tools.ts +293 -0
- package/src/git.ts +1102 -0
- package/src/github.ts +158 -0
- package/src/index.test.ts +87 -0
- package/src/index.ts +684 -0
- package/src/index.ts.backup +1543 -0
- package/src/launcher.ts +142 -0
- package/src/repositories/git.repository.ts +129 -0
- package/src/repositories/github.repository.ts +83 -0
- package/src/repositories/worktree.repository.ts +69 -0
- package/src/services/BatchMergeService.ts +251 -0
- package/src/services/WorktreeOrchestrator.ts +115 -0
- package/src/services/__tests__/BatchMergeService.test.ts +518 -0
- package/src/services/__tests__/WorktreeOrchestrator.test.ts +258 -0
- package/src/services/dependency-installer.ts +199 -0
- package/src/services/git.service.ts +113 -0
- package/src/services/github.service.ts +61 -0
- package/src/services/worktree.service.ts +66 -0
- package/src/types/api.ts +241 -0
- package/src/types/tools.ts +235 -0
- package/src/utils/spinner.ts +54 -0
- package/src/utils/terminal.ts +272 -0
- package/src/utils.test.ts +43 -0
- package/src/utils.ts +60 -0
- package/src/web/client/index.html +12 -0
- package/src/web/client/src/components/BranchGraph.tsx +231 -0
- package/src/web/client/src/components/EnvEditor.tsx +145 -0
- package/src/web/client/src/components/Terminal.tsx +137 -0
- package/src/web/client/src/hooks/useBranches.ts +41 -0
- package/src/web/client/src/hooks/useConfig.ts +31 -0
- package/src/web/client/src/hooks/useSessions.ts +59 -0
- package/src/web/client/src/hooks/useWorktrees.ts +47 -0
- package/src/web/client/src/index.css +834 -0
- package/src/web/client/src/lib/api.ts +184 -0
- package/src/web/client/src/lib/websocket.ts +174 -0
- package/src/web/client/src/main.tsx +29 -0
- package/src/web/client/src/pages/BranchDetailPage.tsx +847 -0
- package/src/web/client/src/pages/BranchListPage.tsx +264 -0
- package/src/web/client/src/pages/ConfigManagementPage.tsx +203 -0
- package/src/web/client/src/router.tsx +27 -0
- package/src/web/client/vite.config.ts +21 -0
- package/src/web/server/env/importer.ts +54 -0
- package/src/web/server/index.ts +74 -0
- package/src/web/server/pty/manager.ts +189 -0
- package/src/web/server/routes/branches.ts +126 -0
- package/src/web/server/routes/config.ts +220 -0
- package/src/web/server/routes/index.ts +37 -0
- package/src/web/server/routes/sessions.ts +130 -0
- package/src/web/server/routes/worktrees.ts +108 -0
- package/src/web/server/services/branches.ts +368 -0
- package/src/web/server/services/worktrees.ts +85 -0
- package/src/web/server/websocket/handler.ts +180 -0
- package/src/worktree.ts +703 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REST API Client
|
|
3
|
+
*
|
|
4
|
+
* バックエンドのREST APIと通信するクライアント。
|
|
5
|
+
* 仕様: specs/SPEC-d5e56259/contracts/rest-api.yaml
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Branch,
|
|
10
|
+
Worktree,
|
|
11
|
+
AIToolSession,
|
|
12
|
+
HealthResponse,
|
|
13
|
+
CreateWorktreeRequest,
|
|
14
|
+
StartSessionRequest,
|
|
15
|
+
BranchSyncRequest,
|
|
16
|
+
BranchSyncResult,
|
|
17
|
+
ConfigPayload,
|
|
18
|
+
} from "../../../../types/api.js";
|
|
19
|
+
|
|
20
|
+
const API_BASE = "/api";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* APIエラー
|
|
24
|
+
*/
|
|
25
|
+
export class ApiError extends Error {
|
|
26
|
+
constructor(
|
|
27
|
+
message: string,
|
|
28
|
+
public statusCode: number,
|
|
29
|
+
public details?: string | null,
|
|
30
|
+
) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.name = "ApiError";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* fetchラッパー - エラーハンドリングとJSON解析
|
|
38
|
+
*/
|
|
39
|
+
async function apiFetch<T>(url: string, options?: RequestInit): Promise<T> {
|
|
40
|
+
const response = await fetch(url, {
|
|
41
|
+
...options,
|
|
42
|
+
headers: {
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
...options?.headers,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const data = await response.json();
|
|
49
|
+
|
|
50
|
+
if (!response.ok || !data.success) {
|
|
51
|
+
throw new ApiError(
|
|
52
|
+
data.error || "API request failed",
|
|
53
|
+
response.status,
|
|
54
|
+
data.details,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return data.data;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* ヘルスチェック
|
|
63
|
+
*/
|
|
64
|
+
export async function checkHealth(): Promise<HealthResponse> {
|
|
65
|
+
const response = await fetch(`${API_BASE}/health`);
|
|
66
|
+
return response.json();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* ブランチAPI
|
|
71
|
+
*/
|
|
72
|
+
export const branchApi = {
|
|
73
|
+
/**
|
|
74
|
+
* すべてのブランチ一覧を取得
|
|
75
|
+
*/
|
|
76
|
+
list: async (): Promise<Branch[]> => {
|
|
77
|
+
return apiFetch<Branch[]>(`${API_BASE}/branches`);
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 特定のブランチ情報を取得
|
|
82
|
+
*/
|
|
83
|
+
get: async (branchName: string): Promise<Branch> => {
|
|
84
|
+
const encoded = encodeURIComponent(branchName);
|
|
85
|
+
return apiFetch<Branch>(`${API_BASE}/branches/${encoded}`);
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
sync: async (
|
|
89
|
+
branchName: string,
|
|
90
|
+
payload: BranchSyncRequest,
|
|
91
|
+
): Promise<BranchSyncResult> => {
|
|
92
|
+
const encoded = encodeURIComponent(branchName);
|
|
93
|
+
return apiFetch<BranchSyncResult>(`${API_BASE}/branches/${encoded}/sync`, {
|
|
94
|
+
method: "POST",
|
|
95
|
+
body: JSON.stringify(payload),
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* WorktreeAPI
|
|
102
|
+
*/
|
|
103
|
+
export const worktreeApi = {
|
|
104
|
+
/**
|
|
105
|
+
* すべてのWorktree一覧を取得
|
|
106
|
+
*/
|
|
107
|
+
list: async (): Promise<Worktree[]> => {
|
|
108
|
+
return apiFetch<Worktree[]>(`${API_BASE}/worktrees`);
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* 新しいWorktreeを作成
|
|
113
|
+
*/
|
|
114
|
+
create: async (request: CreateWorktreeRequest): Promise<Worktree> => {
|
|
115
|
+
return apiFetch<Worktree>(`${API_BASE}/worktrees`, {
|
|
116
|
+
method: "POST",
|
|
117
|
+
body: JSON.stringify(request),
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Worktreeを削除
|
|
123
|
+
*/
|
|
124
|
+
delete: async (path: string): Promise<void> => {
|
|
125
|
+
const url = new URL(`${API_BASE}/worktrees/delete`, window.location.origin);
|
|
126
|
+
url.searchParams.set("path", path);
|
|
127
|
+
await apiFetch<void>(url.toString(), {
|
|
128
|
+
method: "DELETE",
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* セッションAPI
|
|
135
|
+
*/
|
|
136
|
+
export const sessionApi = {
|
|
137
|
+
/**
|
|
138
|
+
* すべてのセッション一覧を取得
|
|
139
|
+
*/
|
|
140
|
+
list: async (): Promise<AIToolSession[]> => {
|
|
141
|
+
return apiFetch<AIToolSession[]>(`${API_BASE}/sessions`);
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 新しいセッションを開始
|
|
146
|
+
*/
|
|
147
|
+
start: async (request: StartSessionRequest): Promise<AIToolSession> => {
|
|
148
|
+
return apiFetch<AIToolSession>(`${API_BASE}/sessions`, {
|
|
149
|
+
method: "POST",
|
|
150
|
+
body: JSON.stringify(request),
|
|
151
|
+
});
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 特定のセッション情報を取得
|
|
156
|
+
*/
|
|
157
|
+
get: async (sessionId: string): Promise<AIToolSession> => {
|
|
158
|
+
return apiFetch<AIToolSession>(`${API_BASE}/sessions/${sessionId}`);
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* セッションを終了
|
|
163
|
+
*/
|
|
164
|
+
delete: async (sessionId: string): Promise<void> => {
|
|
165
|
+
await apiFetch<void>(`${API_BASE}/sessions/${sessionId}`, {
|
|
166
|
+
method: "DELETE",
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* 設定API
|
|
173
|
+
*/
|
|
174
|
+
export const configApi = {
|
|
175
|
+
/**
|
|
176
|
+
* カスタムAI Tool設定を取得
|
|
177
|
+
*/
|
|
178
|
+
get: async (): Promise<ConfigPayload> => apiFetch(`${API_BASE}/config`),
|
|
179
|
+
update: async (request: ConfigPayload): Promise<ConfigPayload> =>
|
|
180
|
+
apiFetch(`${API_BASE}/config`, {
|
|
181
|
+
method: "PUT",
|
|
182
|
+
body: JSON.stringify(request),
|
|
183
|
+
}),
|
|
184
|
+
};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket Client
|
|
3
|
+
*
|
|
4
|
+
* PTYセッションとの双方向通信を管理するクライアント。
|
|
5
|
+
* 仕様: specs/SPEC-d5e56259/contracts/websocket.md
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
ServerMessage,
|
|
10
|
+
InputMessage,
|
|
11
|
+
ResizeMessage,
|
|
12
|
+
PingMessage,
|
|
13
|
+
OutputMessage,
|
|
14
|
+
ExitMessage,
|
|
15
|
+
ErrorMessage,
|
|
16
|
+
} from "../../../../types/api.js";
|
|
17
|
+
|
|
18
|
+
export type WebSocketEventHandler = {
|
|
19
|
+
onOutput?: (data: string) => void;
|
|
20
|
+
onExit?: (code: number, signal?: string) => void;
|
|
21
|
+
onError?: (message: string) => void;
|
|
22
|
+
onPong?: () => void;
|
|
23
|
+
onOpen?: () => void;
|
|
24
|
+
onClose?: () => void;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* PTY WebSocketクライアント
|
|
29
|
+
*/
|
|
30
|
+
export class PTYWebSocket {
|
|
31
|
+
private ws: WebSocket | null = null;
|
|
32
|
+
private handlers: WebSocketEventHandler;
|
|
33
|
+
private sessionId: string;
|
|
34
|
+
|
|
35
|
+
constructor(sessionId: string, handlers: WebSocketEventHandler) {
|
|
36
|
+
this.sessionId = sessionId;
|
|
37
|
+
this.handlers = handlers;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* WebSocket接続を確立
|
|
42
|
+
*/
|
|
43
|
+
public connect(): void {
|
|
44
|
+
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
45
|
+
const host = window.location.host;
|
|
46
|
+
const url = `${protocol}//${host}/api/sessions/${this.sessionId}/terminal?sessionId=${this.sessionId}`;
|
|
47
|
+
|
|
48
|
+
this.ws = new WebSocket(url);
|
|
49
|
+
|
|
50
|
+
this.ws.onopen = () => {
|
|
51
|
+
this.handlers.onOpen?.();
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
this.ws.onmessage = (event) => {
|
|
55
|
+
try {
|
|
56
|
+
const message: ServerMessage = JSON.parse(event.data);
|
|
57
|
+
this.handleServerMessage(message);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error("Failed to parse WebSocket message:", error);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
this.ws.onerror = (event) => {
|
|
64
|
+
console.error("WebSocket error:", event);
|
|
65
|
+
this.handlers.onError?.("WebSocket connection error");
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
this.ws.onclose = () => {
|
|
69
|
+
this.handlers.onClose?.();
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* サーバーメッセージを処理
|
|
75
|
+
*/
|
|
76
|
+
private handleServerMessage(message: ServerMessage): void {
|
|
77
|
+
switch (message.type) {
|
|
78
|
+
case "output": {
|
|
79
|
+
const outputMsg = message as OutputMessage;
|
|
80
|
+
this.handlers.onOutput?.(outputMsg.data);
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case "exit": {
|
|
84
|
+
const exitMsg = message as ExitMessage;
|
|
85
|
+
this.handlers.onExit?.(exitMsg.data.code, exitMsg.data.signal);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
case "error": {
|
|
89
|
+
const errorMsg = message as ErrorMessage;
|
|
90
|
+
this.handlers.onError?.(errorMsg.data.message);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
case "pong": {
|
|
94
|
+
this.handlers.onPong?.();
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
default: {
|
|
98
|
+
const unknownMsg = message as { type: string };
|
|
99
|
+
console.warn("Unknown server message type:", unknownMsg.type);
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 入力データを送信
|
|
107
|
+
*/
|
|
108
|
+
public sendInput(data: string): void {
|
|
109
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
110
|
+
console.error("WebSocket is not connected");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const message: InputMessage = {
|
|
115
|
+
type: "input",
|
|
116
|
+
data,
|
|
117
|
+
timestamp: new Date().toISOString(),
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
this.ws.send(JSON.stringify(message));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* ターミナルサイズ変更を送信
|
|
125
|
+
*/
|
|
126
|
+
public sendResize(cols: number, rows: number): void {
|
|
127
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
128
|
+
console.error("WebSocket is not connected");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const message: ResizeMessage = {
|
|
133
|
+
type: "resize",
|
|
134
|
+
data: { cols, rows },
|
|
135
|
+
timestamp: new Date().toISOString(),
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
this.ws.send(JSON.stringify(message));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Pingを送信
|
|
143
|
+
*/
|
|
144
|
+
public sendPing(): void {
|
|
145
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
146
|
+
console.error("WebSocket is not connected");
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const message: PingMessage = {
|
|
151
|
+
type: "ping",
|
|
152
|
+
timestamp: new Date().toISOString(),
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
this.ws.send(JSON.stringify(message));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* WebSocket接続を切断
|
|
160
|
+
*/
|
|
161
|
+
public disconnect(): void {
|
|
162
|
+
if (this.ws) {
|
|
163
|
+
this.ws.close();
|
|
164
|
+
this.ws = null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* 接続状態を取得
|
|
170
|
+
*/
|
|
171
|
+
public isConnected(): boolean {
|
|
172
|
+
return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import ReactDOM from "react-dom/client";
|
|
3
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
4
|
+
import { RouterProvider } from "react-router-dom";
|
|
5
|
+
import { router } from "./router";
|
|
6
|
+
import "./index.css";
|
|
7
|
+
|
|
8
|
+
const queryClient = new QueryClient({
|
|
9
|
+
defaultOptions: {
|
|
10
|
+
queries: {
|
|
11
|
+
refetchOnWindowFocus: false,
|
|
12
|
+
retry: 1,
|
|
13
|
+
staleTime: 30000, // 30秒
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const rootElement = document.getElementById("root");
|
|
19
|
+
if (!rootElement) {
|
|
20
|
+
throw new Error("Root element not found");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
ReactDOM.createRoot(rootElement).render(
|
|
24
|
+
<React.StrictMode>
|
|
25
|
+
<QueryClientProvider client={queryClient}>
|
|
26
|
+
<RouterProvider router={router} />
|
|
27
|
+
</QueryClientProvider>
|
|
28
|
+
</React.StrictMode>,
|
|
29
|
+
);
|