@akiojin/gwt 4.1.1 → 4.3.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.
Files changed (135) hide show
  1. package/README.md +28 -3
  2. package/dist/claude.d.ts +4 -0
  3. package/dist/claude.d.ts.map +1 -1
  4. package/dist/claude.js +13 -1
  5. package/dist/claude.js.map +1 -1
  6. package/dist/cli/ui/components/App.d.ts.map +1 -1
  7. package/dist/cli/ui/components/App.js +68 -68
  8. package/dist/cli/ui/components/App.js.map +1 -1
  9. package/dist/cli/ui/components/common/Select.d.ts +3 -1
  10. package/dist/cli/ui/components/common/Select.d.ts.map +1 -1
  11. package/dist/cli/ui/components/common/Select.js +13 -2
  12. package/dist/cli/ui/components/common/Select.js.map +1 -1
  13. package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
  14. package/dist/cli/ui/components/screens/BranchListScreen.js +6 -1
  15. package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
  16. package/dist/client/assets/index-ChHC-Puh.css +1 -0
  17. package/dist/client/assets/index-PqK9jkug.js +78 -0
  18. package/dist/client/index.html +2 -2
  19. package/dist/config/builtin-tools.d.ts.map +1 -1
  20. package/dist/config/builtin-tools.js +3 -0
  21. package/dist/config/builtin-tools.js.map +1 -1
  22. package/dist/config/tools.d.ts.map +1 -1
  23. package/dist/config/tools.js +10 -1
  24. package/dist/config/tools.js.map +1 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +37 -4
  27. package/dist/index.js.map +1 -1
  28. package/dist/launcher.d.ts.map +1 -1
  29. package/dist/launcher.js +15 -0
  30. package/dist/launcher.js.map +1 -1
  31. package/dist/services/aiToolResolver.d.ts.map +1 -1
  32. package/dist/services/aiToolResolver.js +55 -8
  33. package/dist/services/aiToolResolver.js.map +1 -1
  34. package/dist/services/customToolResolver.d.ts.map +1 -1
  35. package/dist/services/customToolResolver.js +22 -17
  36. package/dist/services/customToolResolver.js.map +1 -1
  37. package/dist/utils/prompt.d.ts +12 -0
  38. package/dist/utils/prompt.d.ts.map +1 -1
  39. package/dist/utils/prompt.js +60 -10
  40. package/dist/utils/prompt.js.map +1 -1
  41. package/dist/utils/webui.js +1 -1
  42. package/dist/web/client/src/components/BranchGraph.d.ts +5 -0
  43. package/dist/web/client/src/components/BranchGraph.d.ts.map +1 -1
  44. package/dist/web/client/src/components/BranchGraph.js +35 -108
  45. package/dist/web/client/src/components/BranchGraph.js.map +1 -1
  46. package/dist/web/client/src/components/graph/BranchDetailPanel.d.ts +15 -0
  47. package/dist/web/client/src/components/graph/BranchDetailPanel.d.ts.map +1 -0
  48. package/dist/web/client/src/components/graph/BranchDetailPanel.js +57 -0
  49. package/dist/web/client/src/components/graph/BranchDetailPanel.js.map +1 -0
  50. package/dist/web/client/src/components/graph/BranchNode.d.ts +13 -0
  51. package/dist/web/client/src/components/graph/BranchNode.d.ts.map +1 -0
  52. package/dist/web/client/src/components/graph/BranchNode.js +103 -0
  53. package/dist/web/client/src/components/graph/BranchNode.js.map +1 -0
  54. package/dist/web/client/src/components/graph/ClusterNode.d.ts +13 -0
  55. package/dist/web/client/src/components/graph/ClusterNode.d.ts.map +1 -0
  56. package/dist/web/client/src/components/graph/ClusterNode.js +109 -0
  57. package/dist/web/client/src/components/graph/ClusterNode.js.map +1 -0
  58. package/dist/web/client/src/components/graph/SynapticCanvas.d.ts +17 -0
  59. package/dist/web/client/src/components/graph/SynapticCanvas.d.ts.map +1 -0
  60. package/dist/web/client/src/components/graph/SynapticCanvas.js +94 -0
  61. package/dist/web/client/src/components/graph/SynapticCanvas.js.map +1 -0
  62. package/dist/web/client/src/components/graph/SynapticEdge.d.ts +13 -0
  63. package/dist/web/client/src/components/graph/SynapticEdge.d.ts.map +1 -0
  64. package/dist/web/client/src/components/graph/SynapticEdge.js +113 -0
  65. package/dist/web/client/src/components/graph/SynapticEdge.js.map +1 -0
  66. package/dist/web/client/src/components/graph/graphUtils.d.ts +67 -0
  67. package/dist/web/client/src/components/graph/graphUtils.d.ts.map +1 -0
  68. package/dist/web/client/src/components/graph/graphUtils.js +175 -0
  69. package/dist/web/client/src/components/graph/graphUtils.js.map +1 -0
  70. package/dist/web/client/src/components/graph/index.d.ts +10 -0
  71. package/dist/web/client/src/components/graph/index.d.ts.map +1 -0
  72. package/dist/web/client/src/components/graph/index.js +10 -0
  73. package/dist/web/client/src/components/graph/index.js.map +1 -0
  74. package/dist/web/client/src/lib/websocket.d.ts.map +1 -1
  75. package/dist/web/client/src/lib/websocket.js +2 -1
  76. package/dist/web/client/src/lib/websocket.js.map +1 -1
  77. package/dist/web/client/vite.config.js +1 -1
  78. package/dist/web/server/env/importer.d.ts.map +1 -1
  79. package/dist/web/server/env/importer.js +4 -0
  80. package/dist/web/server/env/importer.js.map +1 -1
  81. package/dist/web/server/index.d.ts.map +1 -1
  82. package/dist/web/server/index.js +9 -0
  83. package/dist/web/server/index.js.map +1 -1
  84. package/dist/web/server/pty/manager.d.ts.map +1 -1
  85. package/dist/web/server/pty/manager.js +24 -1
  86. package/dist/web/server/pty/manager.js.map +1 -1
  87. package/dist/web/server/routes/sessions.d.ts.map +1 -1
  88. package/dist/web/server/routes/sessions.js +7 -0
  89. package/dist/web/server/routes/sessions.js.map +1 -1
  90. package/dist/web/server/tray.d.ts +1 -1
  91. package/dist/web/server/tray.d.ts.map +1 -1
  92. package/dist/web/server/tray.js +52 -34
  93. package/dist/web/server/tray.js.map +1 -1
  94. package/dist/web/server/websocket/handler.d.ts.map +1 -1
  95. package/dist/web/server/websocket/handler.js +4 -0
  96. package/dist/web/server/websocket/handler.js.map +1 -1
  97. package/package.json +6 -3
  98. package/src/claude.ts +15 -1
  99. package/src/cli/ui/__tests__/components/App.protected-branch.test.tsx +2 -1
  100. package/src/cli/ui/__tests__/components/App.shortcuts.test.tsx +142 -8
  101. package/src/cli/ui/__tests__/components/App.test.tsx +4 -3
  102. package/src/cli/ui/__tests__/components/ModelSelectorScreen.initial.test.tsx +1 -0
  103. package/src/cli/ui/__tests__/components/common/Select.test.tsx +45 -0
  104. package/src/cli/ui/components/App.tsx +91 -81
  105. package/src/cli/ui/components/common/Select.tsx +14 -1
  106. package/src/cli/ui/components/screens/BranchListScreen.tsx +6 -1
  107. package/src/cli/ui/types.ts +1 -1
  108. package/src/config/builtin-tools.ts +3 -0
  109. package/src/config/tools.ts +24 -1
  110. package/src/index.ts +50 -3
  111. package/src/launcher.ts +26 -0
  112. package/src/services/aiToolResolver.ts +75 -9
  113. package/src/services/customToolResolver.ts +32 -17
  114. package/src/utils/__tests__/prompt.test.ts +72 -35
  115. package/src/utils/prompt.ts +79 -10
  116. package/src/utils/webui.ts +1 -1
  117. package/src/web/client/src/components/BranchGraph.tsx +51 -208
  118. package/src/web/client/src/components/graph/BranchDetailPanel.tsx +152 -0
  119. package/src/web/client/src/components/graph/BranchNode.tsx +200 -0
  120. package/src/web/client/src/components/graph/ClusterNode.tsx +211 -0
  121. package/src/web/client/src/components/graph/SynapticCanvas.tsx +171 -0
  122. package/src/web/client/src/components/graph/SynapticEdge.tsx +311 -0
  123. package/src/web/client/src/components/graph/graphUtils.ts +265 -0
  124. package/src/web/client/src/components/graph/index.ts +10 -0
  125. package/src/web/client/src/index.css +314 -29
  126. package/src/web/client/src/lib/websocket.ts +2 -1
  127. package/src/web/client/vite.config.ts +1 -1
  128. package/src/web/server/env/importer.ts +5 -0
  129. package/src/web/server/index.ts +10 -0
  130. package/src/web/server/pty/manager.ts +43 -1
  131. package/src/web/server/routes/sessions.ts +15 -0
  132. package/src/web/server/tray.ts +62 -46
  133. package/src/web/server/websocket/handler.ts +13 -0
  134. package/dist/client/assets/index-DsDNCy5f.css +0 -1
  135. package/dist/client/assets/index-v8smkNOL.js +0 -72
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Branch[] → React Flow Node/Edge 変換ユーティリティ
3
+ *
4
+ * D3-forceレイアウトと組み合わせてシナプス風配置を実現
5
+ */
6
+
7
+ import {
8
+ forceSimulation,
9
+ forceLink,
10
+ forceManyBody,
11
+ forceCenter,
12
+ forceCollide,
13
+ type Simulation,
14
+ type SimulationNodeDatum,
15
+ type SimulationLinkDatum,
16
+ } from "d3-force";
17
+ import type { Node, Edge } from "@xyflow/react";
18
+ import type { Branch } from "../../../../../types/api.js";
19
+
20
+ /** グラフノードの拡張型 */
21
+ export interface GraphNode extends Node {
22
+ data: {
23
+ branch: Branch | undefined;
24
+ isCluster: boolean;
25
+ clusterSize: number;
26
+ expanded: boolean;
27
+ };
28
+ }
29
+
30
+ /** グラフエッジの型 */
31
+ export interface GraphEdge extends Edge {
32
+ data?: {
33
+ strength: number;
34
+ };
35
+ }
36
+
37
+ /** クラスタ情報 */
38
+ export interface Cluster {
39
+ id: string;
40
+ baseBranch: string;
41
+ branches: Branch[];
42
+ isExpanded: boolean;
43
+ }
44
+
45
+ /** D3シミュレーション用ノード */
46
+ interface SimNode extends SimulationNodeDatum {
47
+ id: string;
48
+ branch?: Branch;
49
+ isCluster: boolean;
50
+ clusterSize?: number;
51
+ }
52
+
53
+ /** D3シミュレーション用リンク */
54
+ interface SimLink extends SimulationLinkDatum<SimNode> {
55
+ source: string | SimNode;
56
+ target: string | SimNode;
57
+ }
58
+
59
+ /**
60
+ * ブランチをクラスタにグループ化
61
+ */
62
+ export function clusterBranches(branches: Branch[]): Cluster[] {
63
+ const clusterMap = new Map<string, Cluster>();
64
+
65
+ // ベースブランチごとにグループ化
66
+ for (const branch of branches) {
67
+ const baseKey = branch.baseBranch ?? "__root__";
68
+
69
+ if (!clusterMap.has(baseKey)) {
70
+ clusterMap.set(baseKey, {
71
+ id: `cluster-${baseKey}`,
72
+ baseBranch: baseKey,
73
+ branches: [],
74
+ isExpanded: false,
75
+ });
76
+ }
77
+
78
+ clusterMap.get(baseKey)?.branches.push(branch);
79
+ }
80
+
81
+ return Array.from(clusterMap.values()).sort((a, b) => {
82
+ // ルートクラスタを先頭に
83
+ if (a.baseBranch === "__root__") return -1;
84
+ if (b.baseBranch === "__root__") return 1;
85
+ // ブランチ数が多い順
86
+ return b.branches.length - a.branches.length;
87
+ });
88
+ }
89
+
90
+ /**
91
+ * D3-forceシミュレーションを実行してノード位置を計算
92
+ */
93
+ export function calculateLayout(
94
+ nodes: SimNode[],
95
+ links: SimLink[],
96
+ width: number,
97
+ height: number,
98
+ ): Promise<SimNode[]> {
99
+ return new Promise((resolve) => {
100
+ const simulation: Simulation<SimNode, SimLink> = forceSimulation(nodes)
101
+ .force(
102
+ "link",
103
+ forceLink<SimNode, SimLink>(links)
104
+ .id((d) => d.id)
105
+ .distance(120)
106
+ .strength(0.8),
107
+ )
108
+ .force("charge", forceManyBody().strength(-400))
109
+ .force("center", forceCenter(width / 2, height / 2))
110
+ .force("collide", forceCollide().radius(60).strength(0.7));
111
+
112
+ // 100ティック分シミュレーションを進める
113
+ for (let i = 0; i < 100; i++) {
114
+ simulation.tick();
115
+ }
116
+
117
+ simulation.stop();
118
+ resolve(nodes);
119
+ });
120
+ }
121
+
122
+ /**
123
+ * Branch[] → GraphNode[] / GraphEdge[] 変換
124
+ */
125
+ export async function branchesToGraph(
126
+ branches: Branch[],
127
+ expandedClusters: Set<string>,
128
+ canvasWidth: number,
129
+ canvasHeight: number,
130
+ ): Promise<{ nodes: GraphNode[]; edges: GraphEdge[] }> {
131
+ const clusters = clusterBranches(branches);
132
+ const _branchMap = new Map(branches.map((b) => [b.name, b]));
133
+
134
+ const simNodes: SimNode[] = [];
135
+ const simLinks: SimLink[] = [];
136
+
137
+ // ブランチ名からノードIDへのマッピング(クラスタ化されている場合はクラスタIDを返す)
138
+ const branchToNodeId = new Map<string, string>();
139
+
140
+ // 最初にノードIDマッピングを構築
141
+ for (const cluster of clusters) {
142
+ const isExpanded = expandedClusters.has(cluster.id);
143
+
144
+ if (cluster.branches.length === 1 || isExpanded) {
145
+ // 個別ノード: ブランチ名がそのままノードID
146
+ for (const branch of cluster.branches) {
147
+ branchToNodeId.set(branch.name, branch.name);
148
+ }
149
+ } else {
150
+ // クラスタノード: 全ブランチがクラスタIDにマッピング
151
+ for (const branch of cluster.branches) {
152
+ branchToNodeId.set(branch.name, cluster.id);
153
+ }
154
+ }
155
+ }
156
+
157
+ // クラスタとノードを生成
158
+ for (const cluster of clusters) {
159
+ const isExpanded = expandedClusters.has(cluster.id);
160
+
161
+ if (cluster.branches.length === 1 || isExpanded) {
162
+ // 単一ブランチまたは展開済み: 個別ノードとして追加
163
+ for (const branch of cluster.branches) {
164
+ simNodes.push({
165
+ id: branch.name,
166
+ branch,
167
+ isCluster: false,
168
+ });
169
+
170
+ // 親ブランチへのリンク(親がノードとして存在する場合のみ)
171
+ if (branch.baseBranch) {
172
+ const parentNodeId = branchToNodeId.get(branch.baseBranch);
173
+ if (parentNodeId && parentNodeId !== branch.name) {
174
+ simLinks.push({
175
+ source: parentNodeId,
176
+ target: branch.name,
177
+ });
178
+ }
179
+ }
180
+ }
181
+ } else {
182
+ // 折りたたみ状態: クラスタノードとして追加
183
+ simNodes.push({
184
+ id: cluster.id,
185
+ isCluster: true,
186
+ clusterSize: cluster.branches.length,
187
+ });
188
+
189
+ // クラスタから親ブランチへのリンク
190
+ if (cluster.baseBranch !== "__root__") {
191
+ const parentNodeId = branchToNodeId.get(cluster.baseBranch);
192
+ if (parentNodeId && parentNodeId !== cluster.id) {
193
+ simLinks.push({
194
+ source: parentNodeId,
195
+ target: cluster.id,
196
+ });
197
+ }
198
+ }
199
+ }
200
+ }
201
+
202
+ // D3-forceでレイアウト計算
203
+ const layoutNodes = await calculateLayout(
204
+ simNodes,
205
+ simLinks,
206
+ canvasWidth,
207
+ canvasHeight,
208
+ );
209
+
210
+ // React Flow形式に変換
211
+ const graphNodes: GraphNode[] = layoutNodes.map((node) => ({
212
+ id: node.id,
213
+ type: node.isCluster ? "cluster" : "branch",
214
+ position: { x: node.x ?? 0, y: node.y ?? 0 },
215
+ data: {
216
+ branch: node.branch,
217
+ isCluster: node.isCluster,
218
+ clusterSize: node.clusterSize ?? 0,
219
+ expanded: expandedClusters.has(node.id),
220
+ },
221
+ }));
222
+
223
+ const graphEdges: GraphEdge[] = simLinks.map((link, idx) => {
224
+ const sourceId =
225
+ typeof link.source === "string" ? link.source : link.source.id;
226
+ const targetId =
227
+ typeof link.target === "string" ? link.target : link.target.id;
228
+
229
+ return {
230
+ id: `edge-${idx}`,
231
+ source: sourceId,
232
+ target: targetId,
233
+ type: "synaptic",
234
+ animated: true,
235
+ data: { strength: 1 },
236
+ };
237
+ });
238
+
239
+ return { nodes: graphNodes, edges: graphEdges };
240
+ }
241
+
242
+ /**
243
+ * ノードの色を決定
244
+ */
245
+ export function getNodeColor(branch: Branch): string {
246
+ if (branch.worktreePath) {
247
+ return "hsl(var(--success))";
248
+ }
249
+ if (branch.type === "local") {
250
+ return "hsl(var(--local))";
251
+ }
252
+ return "hsl(var(--remote))";
253
+ }
254
+
255
+ /**
256
+ * ノードサイズを決定(divergenceに基づく)
257
+ */
258
+ export function getNodeSize(branch: Branch): number {
259
+ const base = 40;
260
+ if (!branch.divergence) return base;
261
+
262
+ const activity = branch.divergence.ahead + branch.divergence.behind;
263
+ // 活発なブランチは大きく表示
264
+ return Math.min(base + activity * 2, 80);
265
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * グラフコンポーネント エクスポート
3
+ */
4
+
5
+ export { SynapticCanvas } from "./SynapticCanvas";
6
+ export { BranchNode } from "./BranchNode";
7
+ export { ClusterNode } from "./ClusterNode";
8
+ export { SynapticEdge } from "./SynapticEdge";
9
+ export { BranchDetailPanel } from "./BranchDetailPanel";
10
+ export * from "./graphUtils";
@@ -1,49 +1,57 @@
1
1
  @import "tailwindcss";
2
2
 
3
3
  /* ===================================
4
- Vercel/Next.js風 Design Tokens
4
+ Bioluminescent Neural Network Theme
5
+ Deep oceanic void with living light
5
6
  =================================== */
6
7
 
7
8
  @theme {
8
- /* Background colors */
9
- --color-background: hsl(0 0% 4%);
10
- --color-foreground: hsl(0 0% 98%);
9
+ /* Void - the deep abyss */
10
+ --color-background: hsl(220 25% 3%);
11
+ --color-foreground: hsl(180 20% 95%);
11
12
 
12
- /* Card colors */
13
- --color-card: hsl(0 0% 9%);
14
- --color-card-foreground: hsl(0 0% 95%);
13
+ /* Card - membrane surfaces */
14
+ --color-card: hsl(220 30% 7%);
15
+ --color-card-foreground: hsl(180 15% 92%);
15
16
 
16
- /* Muted colors */
17
- --color-muted: hsl(0 0% 15%);
18
- --color-muted-foreground: hsl(0 0% 64%);
17
+ /* Muted - shadow depths */
18
+ --color-muted: hsl(220 25% 12%);
19
+ --color-muted-foreground: hsl(200 10% 55%);
19
20
 
20
- /* Border */
21
- --color-border: hsl(0 0% 18%);
21
+ /* Border - membrane edges */
22
+ --color-border: hsl(200 30% 18%);
22
23
 
23
- /* Primary (white-based for Vercel style) */
24
- --color-primary: hsl(0 0% 100%);
25
- --color-primary-foreground: hsl(0 0% 9%);
24
+ /* Primary - bioluminescent cyan */
25
+ --color-primary: hsl(185 100% 65%);
26
+ --color-primary-foreground: hsl(220 30% 5%);
26
27
 
27
- /* Secondary */
28
- --color-secondary: hsl(0 0% 15%);
29
- --color-secondary-foreground: hsl(0 0% 100%);
28
+ /* Secondary - deep tissue */
29
+ --color-secondary: hsl(220 35% 15%);
30
+ --color-secondary-foreground: hsl(180 20% 90%);
30
31
 
31
- /* Accent */
32
- --color-accent: hsl(0 0% 15%);
32
+ /* Accent - synaptic magenta */
33
+ --color-accent: hsl(310 85% 60%);
33
34
  --color-accent-foreground: hsl(0 0% 100%);
34
35
 
35
36
  /* Destructive */
36
- --color-destructive: hsl(0 84% 60%);
37
+ --color-destructive: hsl(0 75% 55%);
37
38
  --color-destructive-foreground: hsl(0 0% 100%);
38
39
 
39
- /* Status colors */
40
- --color-success: hsl(142 76% 36%);
41
- --color-warning: hsl(38 92% 50%);
42
- --color-error: hsl(0 84% 60%);
40
+ /* Status - neural signals */
41
+ --color-success: hsl(160 90% 45%);
42
+ --color-warning: hsl(45 95% 55%);
43
+ --color-error: hsl(0 80% 55%);
43
44
 
44
- /* Branch colors */
45
- --color-local: hsl(270 91% 65%);
46
- --color-remote: hsl(217 91% 60%);
45
+ /* Branch colors - neuron types */
46
+ --color-local: hsl(280 85% 65%);
47
+ --color-remote: hsl(200 90% 55%);
48
+
49
+ /* Neural-specific colors */
50
+ --color-synapse-glow: hsl(185 100% 70%);
51
+ --color-synapse-pulse: hsl(310 90% 65%);
52
+ --color-dendrite: hsl(190 80% 50%);
53
+ --color-axon: hsl(175 70% 45%);
54
+ --color-vesicle: hsl(300 80% 70%);
47
55
 
48
56
  /* Radius */
49
57
  --radius-sm: 4px;
@@ -53,7 +61,7 @@
53
61
  --radius-full: 9999px;
54
62
  }
55
63
 
56
- /* Base styles for shadcn/ui integration */
64
+ /* Base styles */
57
65
  * {
58
66
  border-color: var(--color-border);
59
67
  }
@@ -61,4 +69,281 @@
61
69
  body {
62
70
  background-color: var(--color-background);
63
71
  color: var(--color-foreground);
72
+ font-feature-settings: "cv02", "cv03", "cv04", "cv11";
73
+ }
74
+
75
+ /* ===================================
76
+ Synaptic Graph - Living Animations
77
+ =================================== */
78
+
79
+ /* Neural membrane breathing */
80
+ @keyframes membrane-breathe {
81
+ 0%, 100% {
82
+ transform: scale(1);
83
+ filter: brightness(1);
84
+ }
85
+ 50% {
86
+ transform: scale(1.02);
87
+ filter: brightness(1.1);
88
+ }
89
+ }
90
+
91
+ /* Soma (cell body) pulse */
92
+ @keyframes soma-pulse {
93
+ 0%, 100% {
94
+ box-shadow:
95
+ 0 0 0 0 hsl(185 100% 65% / 0.4),
96
+ 0 0 20px 0 hsl(185 100% 65% / 0.2),
97
+ inset 0 0 15px hsl(185 100% 65% / 0.1);
98
+ }
99
+ 50% {
100
+ box-shadow:
101
+ 0 0 0 8px hsl(185 100% 65% / 0),
102
+ 0 0 30px 5px hsl(185 100% 65% / 0.3),
103
+ inset 0 0 20px hsl(185 100% 65% / 0.2);
104
+ }
105
+ }
106
+
107
+ /* Worktree active state - stronger signal */
108
+ @keyframes active-synapse {
109
+ 0%, 100% {
110
+ box-shadow:
111
+ 0 0 0 0 hsl(160 90% 45% / 0.6),
112
+ 0 0 25px 0 hsl(160 90% 45% / 0.4),
113
+ 0 0 50px 0 hsl(185 100% 65% / 0.2);
114
+ }
115
+ 50% {
116
+ box-shadow:
117
+ 0 0 0 12px hsl(160 90% 45% / 0),
118
+ 0 0 40px 10px hsl(160 90% 45% / 0.5),
119
+ 0 0 60px 5px hsl(185 100% 65% / 0.3);
120
+ }
121
+ }
122
+
123
+ /* Vesicle release - particle burst */
124
+ @keyframes vesicle-release {
125
+ 0% {
126
+ transform: scale(0.8);
127
+ opacity: 0.3;
128
+ }
129
+ 50% {
130
+ transform: scale(1.3);
131
+ opacity: 1;
132
+ }
133
+ 100% {
134
+ transform: scale(0.8);
135
+ opacity: 0.3;
136
+ }
137
+ }
138
+
139
+ /* Dendrite growth pulse */
140
+ @keyframes dendrite-pulse {
141
+ 0%, 100% {
142
+ stroke-dashoffset: 0;
143
+ filter: drop-shadow(0 0 2px hsl(185 100% 65% / 0.5));
144
+ }
145
+ 50% {
146
+ stroke-dashoffset: -10;
147
+ filter: drop-shadow(0 0 6px hsl(185 100% 65% / 0.8));
148
+ }
149
+ }
150
+
151
+ /* Cluster nucleus orbit */
152
+ @keyframes nucleus-orbit {
153
+ 0% {
154
+ transform: rotate(0deg) translateX(8px) rotate(0deg);
155
+ }
156
+ 100% {
157
+ transform: rotate(360deg) translateX(8px) rotate(-360deg);
158
+ }
159
+ }
160
+
161
+ /* Cluster membrane morph */
162
+ @keyframes membrane-morph {
163
+ 0%, 100% {
164
+ border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%;
165
+ }
166
+ 25% {
167
+ border-radius: 58% 42% 75% 25% / 76% 46% 54% 24%;
168
+ }
169
+ 50% {
170
+ border-radius: 50% 50% 33% 67% / 55% 27% 73% 45%;
171
+ }
172
+ 75% {
173
+ border-radius: 33% 67% 58% 42% / 63% 68% 32% 37%;
174
+ }
175
+ }
176
+
177
+ /* Flowing particle along path */
178
+ @keyframes flow-particle {
179
+ 0% {
180
+ offset-distance: 0%;
181
+ opacity: 0;
182
+ }
183
+ 10% {
184
+ opacity: 1;
185
+ }
186
+ 90% {
187
+ opacity: 1;
188
+ }
189
+ 100% {
190
+ offset-distance: 100%;
191
+ opacity: 0;
192
+ }
193
+ }
194
+
195
+ /* Synapse spark */
196
+ @keyframes synapse-spark {
197
+ 0%, 100% {
198
+ transform: scale(1);
199
+ opacity: 0.6;
200
+ filter: blur(0px);
201
+ }
202
+ 50% {
203
+ transform: scale(1.5);
204
+ opacity: 1;
205
+ filter: blur(1px);
206
+ }
207
+ }
208
+
209
+ /* Utility classes */
210
+ .animate-membrane-breathe {
211
+ animation: membrane-breathe 4s ease-in-out infinite;
212
+ }
213
+
214
+ .animate-soma-pulse {
215
+ animation: soma-pulse 3s ease-in-out infinite;
216
+ }
217
+
218
+ .animate-active-synapse {
219
+ animation: active-synapse 2s ease-in-out infinite;
220
+ }
221
+
222
+ .animate-vesicle-release {
223
+ animation: vesicle-release 2s ease-in-out infinite;
224
+ }
225
+
226
+ .animate-dendrite-pulse {
227
+ animation: dendrite-pulse 3s ease-in-out infinite;
228
+ }
229
+
230
+ .animate-nucleus-orbit {
231
+ animation: nucleus-orbit 8s linear infinite;
232
+ }
233
+
234
+ .animate-membrane-morph {
235
+ animation: membrane-morph 12s ease-in-out infinite;
236
+ }
237
+
238
+ .animate-synapse-spark {
239
+ animation: synapse-spark 2.5s ease-in-out infinite;
240
+ }
241
+
242
+ /* Neural glow effects */
243
+ .neural-glow {
244
+ filter: drop-shadow(0 0 10px hsl(185 100% 65% / 0.5));
245
+ }
246
+
247
+ .neural-glow-strong {
248
+ filter: drop-shadow(0 0 15px hsl(185 100% 65% / 0.7))
249
+ drop-shadow(0 0 30px hsl(185 100% 65% / 0.3));
250
+ }
251
+
252
+ /* Gradient overlays for depth */
253
+ .neural-gradient {
254
+ background: radial-gradient(
255
+ ellipse at center,
256
+ hsl(185 100% 65% / 0.15) 0%,
257
+ transparent 70%
258
+ );
259
+ }
260
+
261
+ /* ===================================
262
+ React Flow Customization
263
+ =================================== */
264
+
265
+ .react-flow__node {
266
+ cursor: pointer;
267
+ }
268
+
269
+ .react-flow__pane {
270
+ background: radial-gradient(
271
+ ellipse 80% 50% at 50% 50%,
272
+ hsl(220 30% 8%) 0%,
273
+ hsl(220 25% 3%) 100%
274
+ );
275
+ }
276
+
277
+ .react-flow__background {
278
+ opacity: 0.3;
279
+ }
280
+
281
+ .react-flow__controls {
282
+ box-shadow: 0 4px 20px hsl(220 30% 0% / 0.5) !important;
283
+ border: 1px solid hsl(200 30% 20%) !important;
284
+ border-radius: 8px !important;
285
+ overflow: hidden;
286
+ }
287
+
288
+ .react-flow__controls button {
289
+ background: hsl(220 30% 10%) !important;
290
+ border-color: hsl(200 30% 18%) !important;
291
+ color: hsl(185 100% 65%) !important;
292
+ transition: all 0.2s ease;
293
+ }
294
+
295
+ .react-flow__controls button:hover {
296
+ background: hsl(220 30% 15%) !important;
297
+ box-shadow: inset 0 0 20px hsl(185 100% 65% / 0.1);
298
+ }
299
+
300
+ .react-flow__controls button svg {
301
+ fill: hsl(185 100% 65%) !important;
302
+ }
303
+
304
+ .react-flow__minimap {
305
+ background: hsl(220 30% 5%) !important;
306
+ border: 1px solid hsl(200 30% 18%) !important;
307
+ border-radius: 8px !important;
308
+ box-shadow: 0 4px 20px hsl(220 30% 0% / 0.5) !important;
309
+ }
310
+
311
+ .react-flow__minimap-mask {
312
+ fill: hsl(220 25% 3% / 0.85) !important;
313
+ }
314
+
315
+ /* ===================================
316
+ Detail Panel Animation
317
+ =================================== */
318
+
319
+ @keyframes slide-in-neural {
320
+ from {
321
+ transform: translateX(100%);
322
+ opacity: 0;
323
+ filter: blur(10px);
324
+ }
325
+ to {
326
+ transform: translateX(0);
327
+ opacity: 1;
328
+ filter: blur(0);
329
+ }
330
+ }
331
+
332
+ .animate-in.slide-in-from-right {
333
+ animation: slide-in-neural 0.4s cubic-bezier(0.16, 1, 0.3, 1);
334
+ }
335
+
336
+ /* Scanline effect for sci-fi feel */
337
+ .scanlines::after {
338
+ content: "";
339
+ position: absolute;
340
+ inset: 0;
341
+ background: repeating-linear-gradient(
342
+ 0deg,
343
+ transparent,
344
+ transparent 2px,
345
+ hsl(185 100% 65% / 0.02) 2px,
346
+ hsl(185 100% 65% / 0.02) 4px
347
+ );
348
+ pointer-events: none;
64
349
  }
@@ -71,7 +71,8 @@ export class PTYWebSocket {
71
71
 
72
72
  this.ws.onerror = (event) => {
73
73
  console.error("WebSocket error:", event);
74
- this.handlers.onError?.("WebSocket connection error");
74
+ // エラーは再接続が全て失敗した後にのみ表示する
75
+ // scheduleReconnect内でMAX_WEBSOCKET_RETRIESを超えた場合にonErrorが呼ばれる
75
76
  this.scheduleReconnect();
76
77
  };
77
78
 
@@ -19,7 +19,7 @@ export default defineConfig({
19
19
  port: 5173,
20
20
  proxy: {
21
21
  "/api": {
22
- target: "http://localhost:3000",
22
+ target: "http://localhost:3001",
23
23
  changeOrigin: true,
24
24
  ws: true,
25
25
  },
@@ -1,6 +1,9 @@
1
1
  import { loadToolsConfig, saveToolsConfig } from "../../../config/tools.js";
2
2
  import { recordEnvHistory } from "../../../config/env-history.js";
3
3
  import type { EnvironmentHistoryEntry } from "../../../types/api.js";
4
+ import { createLogger } from "../../../logging/logger.js";
5
+
6
+ const logger = createLogger({ category: "env" });
4
7
 
5
8
  const IMPORTABLE_KEYS = [
6
9
  "OPENAI_API_KEY",
@@ -29,6 +32,7 @@ export async function importOsEnvIntoSharedConfig(): Promise<string[]> {
29
32
  }
30
33
 
31
34
  if (!importedKeys.length) {
35
+ logger.debug({ reason: "no new keys" }, "Env import skipped");
32
36
  return [];
33
37
  }
34
38
 
@@ -46,6 +50,7 @@ export async function importOsEnvIntoSharedConfig(): Promise<string[]> {
46
50
  }));
47
51
  await recordEnvHistory(historyEntries);
48
52
 
53
+ logger.info({ importedKeys }, "Environment variables imported");
49
54
  return importedKeys;
50
55
  }
51
56