clawmate 1.4.0 → 1.4.2

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 (42) hide show
  1. package/index.js +441 -442
  2. package/main/ai-bridge.js +59 -59
  3. package/main/ai-connector.js +60 -60
  4. package/main/autostart.js +6 -6
  5. package/main/desktop-path.js +4 -4
  6. package/main/file-command-parser.js +46 -46
  7. package/main/file-ops.js +27 -27
  8. package/main/index.js +17 -17
  9. package/main/ipc-handlers.js +24 -24
  10. package/main/manifest.js +2 -2
  11. package/main/platform.js +16 -16
  12. package/main/smart-file-ops.js +64 -64
  13. package/main/store.js +1 -1
  14. package/main/telegram.js +137 -137
  15. package/main/tray.js +61 -61
  16. package/main/updater.js +13 -13
  17. package/openclaw.plugin.json +1 -1
  18. package/package.json +2 -2
  19. package/preload/preload.js +18 -18
  20. package/renderer/css/effects.css +6 -6
  21. package/renderer/css/pet.css +8 -8
  22. package/renderer/css/speech.css +5 -5
  23. package/renderer/first-run.html +14 -14
  24. package/renderer/index.html +4 -4
  25. package/renderer/js/ai-controller.js +91 -91
  26. package/renderer/js/app.js +24 -24
  27. package/renderer/js/browser-watcher.js +32 -32
  28. package/renderer/js/character.js +33 -33
  29. package/renderer/js/interactions.js +21 -21
  30. package/renderer/js/memory.js +60 -60
  31. package/renderer/js/metrics.js +141 -141
  32. package/renderer/js/mode-manager.js +13 -13
  33. package/renderer/js/pet-engine.js +236 -236
  34. package/renderer/js/speech.js +19 -19
  35. package/renderer/js/state-machine.js +23 -23
  36. package/renderer/js/time-aware.js +15 -15
  37. package/renderer/launcher.html +8 -8
  38. package/shared/constants.js +11 -11
  39. package/shared/messages.js +130 -130
  40. package/shared/personalities.js +44 -44
  41. package/skills/launch-pet/index.js +57 -47
  42. package/skills/launch-pet/skill.json +12 -23
package/main/tray.js CHANGED
@@ -9,7 +9,7 @@ let tray = null;
9
9
  let aiBridge = null;
10
10
 
11
11
  /**
12
- * 16x16 Claw 픽셀아트 아이콘 생성
12
+ * Generate 16x16 Claw pixel art icon
13
13
  */
14
14
  const CLAW_ICON = [
15
15
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
@@ -41,48 +41,48 @@ const COLOR_MAP = {
41
41
  };
42
42
 
43
43
  /**
44
- * 캐릭터 프리셋 목록
45
- * 트레이에서 선택하면 set_character 명령으로 렌더러에 전달
44
+ * Character preset list
45
+ * When selected from tray, sent to renderer via set_character command
46
46
  */
47
47
  const CHARACTER_PRESETS = {
48
48
  default: {
49
- name: '기본 Claw (빨강)',
49
+ name: 'Default Claw (Red)',
50
50
  colorMap: { primary: '#ff4f40', secondary: '#ff775f', dark: '#8B4513', eye: '#ffffff', pupil: '#111111', claw: '#ff4f40' },
51
51
  },
52
52
  blue: {
53
- name: '파란 Claw',
53
+ name: 'Blue Claw',
54
54
  colorMap: { primary: '#4488ff', secondary: '#6699ff', dark: '#223388', eye: '#ffffff', pupil: '#111111', claw: '#4488ff' },
55
55
  },
56
56
  green: {
57
- name: '초록 Claw',
57
+ name: 'Green Claw',
58
58
  colorMap: { primary: '#44cc44', secondary: '#66dd66', dark: '#226622', eye: '#ffffff', pupil: '#111111', claw: '#44cc44' },
59
59
  },
60
60
  purple: {
61
- name: '보라 Claw',
61
+ name: 'Purple Claw',
62
62
  colorMap: { primary: '#8844cc', secondary: '#aa66dd', dark: '#442266', eye: '#ffffff', pupil: '#111111', claw: '#8844cc' },
63
63
  },
64
64
  gold: {
65
- name: '골드 Claw',
65
+ name: 'Gold Claw',
66
66
  colorMap: { primary: '#ffcc00', secondary: '#ffdd44', dark: '#886600', eye: '#ffffff', pupil: '#111111', claw: '#ffcc00' },
67
67
  },
68
68
  pink: {
69
- name: '핑크 Claw',
69
+ name: 'Pink Claw',
70
70
  colorMap: { primary: '#ff69b4', secondary: '#ff8cc4', dark: '#8B3060', eye: '#ffffff', pupil: '#111111', claw: '#ff69b4' },
71
71
  },
72
72
  cat: {
73
- name: '고양이',
73
+ name: 'Cat',
74
74
  colorMap: { primary: '#ff9944', secondary: '#ffbb66', dark: '#663300', eye: '#88ff88', pupil: '#111111', claw: '#ff9944' },
75
75
  },
76
76
  robot: {
77
- name: '로봇',
77
+ name: 'Robot',
78
78
  colorMap: { primary: '#888888', secondary: '#aaaaaa', dark: '#444444', eye: '#66aaff', pupil: '#0044aa', claw: '#66aaff' },
79
79
  },
80
80
  ghost: {
81
- name: '유령',
81
+ name: 'Ghost',
82
82
  colorMap: { primary: '#ccccff', secondary: '#eeeeff', dark: '#6666aa', eye: '#ff6666', pupil: '#cc0000', claw: '#ccccff' },
83
83
  },
84
84
  dragon: {
85
- name: '드래곤',
85
+ name: 'Dragon',
86
86
  colorMap: { primary: '#cc2222', secondary: '#ff4444', dark: '#661111', eye: '#ffaa00', pupil: '#111111', claw: '#ffaa00' },
87
87
  },
88
88
  };
@@ -114,7 +114,7 @@ function setupTray(mainWindow, bridge) {
114
114
 
115
115
  const icon = createClawIcon();
116
116
  tray = new Tray(icon);
117
- tray.setToolTip('ClawMate - 데스크톱 ');
117
+ tray.setToolTip('ClawMate - AI Cyber Body');
118
118
 
119
119
  function buildMenu() {
120
120
  const mode = store.get('mode') || 'pet';
@@ -124,7 +124,7 @@ function setupTray(mainWindow, bridge) {
124
124
  const currentChar = store.get('character') || 'default';
125
125
  const hasTelegramToken = !!(store.get('telegramToken'));
126
126
 
127
- // 캐릭터 서브메뉴
127
+ // Character submenu
128
128
  const characterSubmenu = Object.entries(CHARACTER_PRESETS).map(([key, preset]) => ({
129
129
  label: preset.name,
130
130
  type: 'radio',
@@ -140,7 +140,7 @@ function setupTray(mainWindow, bridge) {
140
140
  mainWindow.webContents.send('ai-command', {
141
141
  type: 'set_character', payload: {
142
142
  colorMap: preset.colorMap,
143
- speech: `${preset.name}(으)로 변신!`,
143
+ speech: `Transforming into ${preset.name}!`,
144
144
  },
145
145
  });
146
146
  }
@@ -155,18 +155,18 @@ function setupTray(mainWindow, bridge) {
155
155
  enabled: false,
156
156
  },
157
157
  {
158
- label: aiConnected ? 'AI: 연결됨' : 'AI: 자율 모드',
158
+ label: aiConnected ? 'AI: Connected' : 'AI: Autonomous Mode',
159
159
  enabled: false,
160
160
  },
161
161
  { type: 'separator' },
162
162
 
163
- // === 모드 선택 ===
163
+ // === Mode Selection ===
164
164
  {
165
- label: '모드',
165
+ label: 'Mode',
166
166
  submenu: [
167
167
  {
168
- label: 'Pet 모드 (Clawby)',
169
- sublabel: '귀여운 펫을 키우기',
168
+ label: 'Pet Mode (Clawby)',
169
+ sublabel: 'Raise a cute pet',
170
170
  type: 'radio',
171
171
  checked: mode === 'pet',
172
172
  click: () => {
@@ -176,8 +176,8 @@ function setupTray(mainWindow, bridge) {
176
176
  },
177
177
  },
178
178
  {
179
- label: 'Incarnation 모드 (Claw)',
180
- sublabel: '봇이 육체를 얻음',
179
+ label: 'Incarnation Mode (Claw)',
180
+ sublabel: 'AI gains a cyber body',
181
181
  type: 'radio',
182
182
  checked: mode === 'incarnation',
183
183
  click: () => {
@@ -187,8 +187,8 @@ function setupTray(mainWindow, bridge) {
187
187
  },
188
188
  },
189
189
  {
190
- label: ' (Pet + Incarnation)',
191
- sublabel: '펫도 키우고, 인격도 반영',
190
+ label: 'Both (Pet + Incarnation)',
191
+ sublabel: 'Raise pet and reflect AI persona',
192
192
  type: 'radio',
193
193
  checked: mode === 'both',
194
194
  click: () => {
@@ -200,17 +200,17 @@ function setupTray(mainWindow, bridge) {
200
200
  ],
201
201
  },
202
202
 
203
- // === 캐릭터 선택 ===
203
+ // === Character Selection ===
204
204
  {
205
- label: '캐릭터',
205
+ label: 'Character',
206
206
  submenu: characterSubmenu,
207
207
  },
208
208
 
209
209
  { type: 'separator' },
210
210
 
211
- // === 설정 ===
211
+ // === Settings ===
212
212
  {
213
- label: '파일 상호작용',
213
+ label: 'File Interaction',
214
214
  type: 'checkbox',
215
215
  checked: fileInteraction,
216
216
  click: (item) => {
@@ -219,7 +219,7 @@ function setupTray(mainWindow, bridge) {
219
219
  },
220
220
  },
221
221
  {
222
- label: '컴퓨터 시작 시 자동 실행',
222
+ label: 'Launch at Startup',
223
223
  type: 'checkbox',
224
224
  checked: autoStart,
225
225
  click: () => {
@@ -230,37 +230,37 @@ function setupTray(mainWindow, bridge) {
230
230
 
231
231
  { type: 'separator' },
232
232
 
233
- // === 텔레그램 ===
233
+ // === Telegram Bot ===
234
234
  {
235
- label: '텔레그램 ',
235
+ label: 'Telegram Bot',
236
236
  submenu: [
237
237
  {
238
- label: hasTelegramToken ? ' 토큰: 설정됨' : ' 토큰: 미설정',
238
+ label: hasTelegramToken ? 'Bot Token: Configured' : 'Bot Token: Not Set',
239
239
  enabled: false,
240
240
  },
241
241
  {
242
- label: ' 토큰 설정...',
242
+ label: 'Set Bot Token...',
243
243
  click: async () => {
244
244
  const result = await dialog.showMessageBox({
245
245
  type: 'question',
246
- buttons: ['클립보드에서 붙여넣기', '직접 입력', '취소'],
247
- title: 'ClawMate 텔레그램 ',
248
- message: '텔레그램 토큰을 설정합니다.',
249
- detail: '@BotFather에서 받은 토큰을 입력하세요.\n현재 클립보드 내용을 붙여넣으려면 "클립보드에서 붙여넣기" 선택하세요.',
246
+ buttons: ['Paste from Clipboard', 'Manual Entry', 'Cancel'],
247
+ title: 'ClawMate Telegram Bot',
248
+ message: 'Set up Telegram bot token.',
249
+ detail: 'Enter the bot token received from @BotFather.\nSelect "Paste from Clipboard" to paste current clipboard content.',
250
250
  });
251
251
 
252
252
  let token = null;
253
253
  if (result.response === 0) {
254
- // 클립보드에서 붙여넣기
254
+ // Paste from clipboard
255
255
  token = clipboard.readText().trim();
256
256
  } else if (result.response === 1) {
257
- // prompt 없으므로 클립보드 안내
257
+ // No prompt available, guide to use clipboard
258
258
  const promptResult = await dialog.showMessageBox({
259
259
  type: 'info',
260
- buttons: ['확인'],
261
- title: '텔레그램 토큰',
262
- message: ' 토큰을 클립보드에 복사한 다시 " 토큰 설정" 선택하세요.',
263
- detail: '텔레그램에서 @BotFather /newbot 토큰 복사',
260
+ buttons: ['OK'],
261
+ title: 'Telegram Bot Token',
262
+ message: 'Copy the bot token to clipboard, then select "Set Bot Token" again.',
263
+ detail: 'In Telegram: @BotFather -> /newbot -> Copy token',
264
264
  });
265
265
  return;
266
266
  } else {
@@ -272,26 +272,26 @@ function setupTray(mainWindow, bridge) {
272
272
  process.env.CLAWMATE_TELEGRAM_TOKEN = token;
273
273
  buildAndSet();
274
274
 
275
- // 알림
275
+ // Pet notification
276
276
  if (mainWindow && !mainWindow.isDestroyed()) {
277
277
  mainWindow.webContents.send('ai-command', {
278
278
  type: 'speak',
279
- payload: { text: '텔레그램 토큰 설정 완료!' },
279
+ payload: { text: 'Telegram bot token configured!' },
280
280
  });
281
281
  }
282
282
  } else {
283
283
  await dialog.showMessageBox({
284
284
  type: 'error',
285
- buttons: ['확인'],
286
- title: '잘못된 토큰',
287
- message: '유효한 텔레그램 토큰이 아닙니다.',
288
- detail: '올바른 형식: 123456789:ABCdefGHIjklMNOpqrsTUVwxyz',
285
+ buttons: ['OK'],
286
+ title: 'Invalid Token',
287
+ message: 'Not a valid Telegram bot token.',
288
+ detail: 'Correct format: 123456789:ABCdefGHIjklMNOpqrsTUVwxyz',
289
289
  });
290
290
  }
291
291
  },
292
292
  },
293
293
  {
294
- label: ' 토큰 제거',
294
+ label: 'Remove Bot Token',
295
295
  enabled: hasTelegramToken,
296
296
  click: () => {
297
297
  store.set('telegramToken', '');
@@ -305,13 +305,13 @@ function setupTray(mainWindow, bridge) {
305
305
  { type: 'separator' },
306
306
 
307
307
  {
308
- label: '업데이트 확인',
308
+ label: 'Check for Updates',
309
309
  click: async () => {
310
310
  await checkForUpdateManual(mainWindow);
311
311
  },
312
312
  },
313
313
  {
314
- label: '파일 이동 되돌리기',
314
+ label: 'Undo File Moves',
315
315
  click: async () => {
316
316
  const manifest = await getFileManifest();
317
317
  const pending = manifest.filter(m => !m.restored);
@@ -321,7 +321,7 @@ function setupTray(mainWindow, bridge) {
321
321
  },
322
322
  { type: 'separator' },
323
323
  {
324
- label: '종료',
324
+ label: 'Quit',
325
325
  click: () => {
326
326
  app.quit();
327
327
  },
@@ -333,7 +333,7 @@ function setupTray(mainWindow, bridge) {
333
333
  tray.setContextMenu(buildMenu());
334
334
  }
335
335
 
336
- // AI 연결 상태 변경 메뉴 업데이트
336
+ // Update menu when AI connection state changes
337
337
  if (aiBridge) {
338
338
  aiBridge.on('connected', () => buildAndSet());
339
339
  aiBridge.on('disconnected', () => buildAndSet());
@@ -344,7 +344,7 @@ function setupTray(mainWindow, bridge) {
344
344
  }
345
345
 
346
346
  /**
347
- * 수동 업데이트 확인
347
+ * Manual update check
348
348
  */
349
349
  async function checkForUpdateManual(mainWindow) {
350
350
  if (app.isPackaged) {
@@ -352,7 +352,7 @@ async function checkForUpdateManual(mainWindow) {
352
352
  const { autoUpdater } = require('electron-updater');
353
353
  autoUpdater.checkForUpdatesAndNotify();
354
354
  } catch (err) {
355
- console.error('[업데이트] electron-updater 실패:', err.message);
355
+ console.error('[Update] electron-updater failed:', err.message);
356
356
  }
357
357
  } else {
358
358
  try {
@@ -366,7 +366,7 @@ async function checkForUpdateManual(mainWindow) {
366
366
  if (mainWindow && !mainWindow.isDestroyed()) {
367
367
  mainWindow.webContents.send('ai-command', {
368
368
  type: 'speak',
369
- payload: { text: `새 버전 v${latest} 사용 가능! (현재: v${current})` },
369
+ payload: { text: `New version v${latest} available! (current: v${current})` },
370
370
  });
371
371
  }
372
372
  shell.openExternal('https://www.npmjs.com/package/clawmate');
@@ -374,16 +374,16 @@ async function checkForUpdateManual(mainWindow) {
374
374
  if (mainWindow && !mainWindow.isDestroyed()) {
375
375
  mainWindow.webContents.send('ai-command', {
376
376
  type: 'speak',
377
- payload: { text: `v${current} — 이미 최신 버전이야!` },
377
+ payload: { text: `v${current} — already up to date!` },
378
378
  });
379
379
  }
380
380
  }
381
381
  } catch (err) {
382
- console.error('[업데이트] npm 버전 확인 실패:', err.message);
382
+ console.error('[Update] npm version check failed:', err.message);
383
383
  if (mainWindow && !mainWindow.isDestroyed()) {
384
384
  mainWindow.webContents.send('ai-command', {
385
385
  type: 'speak',
386
- payload: { text: '업데이트 확인 실패...' },
386
+ payload: { text: 'Update check failed...' },
387
387
  });
388
388
  }
389
389
  }
package/main/updater.js CHANGED
@@ -1,48 +1,48 @@
1
1
  /**
2
- * ClawMate 자동 업데이트 모듈
2
+ * ClawMate Auto-Update Module
3
3
  *
4
- * electron-updater 사용하여 GitHub Releases에서
5
- * 버전이 있으면 자동으로 다운로드하고, 종료 시 설치한다.
6
- * 개발 모드(app.isPackaged === false)에서는 동작하지 않는다.
4
+ * Uses electron-updater to automatically download new versions
5
+ * from GitHub Releases and install on app quit.
6
+ * Does not run in development mode (app.isPackaged === false).
7
7
  */
8
8
  const { app } = require('electron');
9
9
  const { autoUpdater } = require('electron-updater');
10
10
 
11
11
  function checkForUpdates() {
12
- // 빌드된 앱에서만 동작 (개발 모드 제외)
12
+ // Only runs in packaged app (excludes dev mode)
13
13
  if (!app.isPackaged) return;
14
14
 
15
15
  autoUpdater.autoDownload = true;
16
16
  autoUpdater.autoInstallOnAppQuit = true;
17
17
 
18
18
  autoUpdater.on('checking-for-update', () => {
19
- console.log('[업데이트] 버전 확인 중...');
19
+ console.log('[Update] Checking for new version...');
20
20
  });
21
21
 
22
22
  autoUpdater.on('update-available', (info) => {
23
- console.log('[업데이트] 버전 발견:', info.version);
23
+ console.log('[Update] New version found:', info.version);
24
24
  });
25
25
 
26
26
  autoUpdater.on('update-not-available', () => {
27
- console.log('[업데이트] 현재 최신 버전입니다.');
27
+ console.log('[Update] Already up to date.');
28
28
  });
29
29
 
30
30
  autoUpdater.on('download-progress', (progress) => {
31
- console.log(`[업데이트] 다운로드 진행: ${Math.round(progress.percent)}%`);
31
+ console.log(`[Update] Download progress: ${Math.round(progress.percent)}%`);
32
32
  });
33
33
 
34
34
  autoUpdater.on('update-downloaded', (info) => {
35
- console.log('[업데이트] 다운로드 완료, 재시작 설치됨:', info.version);
35
+ console.log('[Update] Download complete, will install on restart:', info.version);
36
36
  });
37
37
 
38
38
  autoUpdater.on('error', (err) => {
39
- console.error('[업데이트] 오류:', err.message);
39
+ console.error('[Update] Error:', err.message);
40
40
  });
41
41
 
42
- // 최초 업데이트 확인
42
+ // Initial update check
43
43
  autoUpdater.checkForUpdatesAndNotify();
44
44
 
45
- // 6시간마다 업데이트 확인
45
+ // Check for updates every 6 hours
46
46
  setInterval(() => {
47
47
  autoUpdater.checkForUpdatesAndNotify();
48
48
  }, 6 * 60 * 60 * 1000);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "clawmate",
3
3
  "name": "ClawMate",
4
- "description": "ClawMate - 화면 위의 살아있는 데스크톱 ",
4
+ "description": "ClawMate - Give your AI a living body on screen",
5
5
  "version": "1.0.0",
6
6
  "skills": ["skills/launch-pet"],
7
7
  "configSchema": {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "clawmate",
3
- "version": "1.4.0",
4
- "description": "ClawMate - AI 조종하는 화면 위의 살아있는 데스크톱 펫",
3
+ "version": "1.4.2",
4
+ "description": "ClawMate - Give your AI a living body on screen",
5
5
  "main": "main/index.js",
6
6
  "bin": {
7
7
  "clawmate": "./skills/launch-pet/index.js"
@@ -1,40 +1,40 @@
1
1
  const { contextBridge, ipcRenderer } = require('electron');
2
2
 
3
3
  contextBridge.exposeInMainWorld('clawmate', {
4
- // 클릭 통과 제어
4
+ // Click-through control
5
5
  setClickThrough: (ignore) => ipcRenderer.send('set-click-through', ignore),
6
6
 
7
- // 파일 작업
7
+ // File operations
8
8
  getDesktopFiles: () => ipcRenderer.invoke('get-desktop-files'),
9
9
  moveFile: (fileName, newPosition) => ipcRenderer.invoke('move-file', fileName, newPosition),
10
10
  undoFileMove: (moveId) => ipcRenderer.invoke('undo-file-move', moveId),
11
11
  undoAllMoves: () => ipcRenderer.invoke('undo-all-moves'),
12
12
  getFileManifest: () => ipcRenderer.invoke('get-file-manifest'),
13
13
 
14
- // 모드 전환
14
+ // Mode switching
15
15
  getMode: () => ipcRenderer.invoke('get-mode'),
16
16
  setMode: (mode) => ipcRenderer.invoke('set-mode', mode),
17
17
 
18
- // 설정
18
+ // Settings
19
19
  getConfig: () => ipcRenderer.invoke('get-config'),
20
20
  setConfig: (key, value) => ipcRenderer.invoke('set-config', key, value),
21
21
 
22
- // 메모리 (사용자 상호작용 기억)
22
+ // Memory (user interaction history)
23
23
  getMemory: () => ipcRenderer.invoke('get-memory'),
24
24
  saveMemory: (data) => ipcRenderer.invoke('save-memory', data),
25
25
 
26
- // 정보
26
+ // Window info
27
27
  getScreenSize: () => ipcRenderer.invoke('get-screen-size'),
28
28
 
29
- // 열린 윈도우 위치/크기 조회
29
+ // Open window position/size query
30
30
  getWindowPositions: () => ipcRenderer.invoke('get-window-positions'),
31
31
 
32
- // 화면 캡처
32
+ // Screen capture
33
33
  screen: {
34
34
  capture: () => ipcRenderer.invoke('capture-screen'),
35
35
  },
36
36
 
37
- // 이벤트 수신
37
+ // Event listeners
38
38
  onModeChanged: (callback) => {
39
39
  ipcRenderer.on('mode-changed', (_, mode) => callback(mode));
40
40
  },
@@ -42,14 +42,14 @@ contextBridge.exposeInMainWorld('clawmate', {
42
42
  ipcRenderer.on('config-changed', (_, config) => callback(config));
43
43
  },
44
44
 
45
- // === AI 통신 ===
45
+ // === AI Communication ===
46
46
 
47
- // AI 명령 수신 (AI )
47
+ // Receive AI commands (AI -> Pet)
48
48
  onAICommand: (callback) => {
49
49
  ipcRenderer.on('ai-command', (_, command) => callback(command));
50
50
  },
51
51
 
52
- // AI 연결/해제 이벤트
52
+ // AI connect/disconnect events
53
53
  onAIConnected: (callback) => {
54
54
  ipcRenderer.on('ai-connected', () => callback());
55
55
  },
@@ -57,22 +57,22 @@ contextBridge.exposeInMainWorld('clawmate', {
57
57
  ipcRenderer.on('ai-disconnected', () => callback());
58
58
  },
59
59
 
60
- // 사용자 이벤트를 AI 전달 ( AI)
60
+ // Forward user events to AI (Pet -> AI)
61
61
  reportToAI: (event, data) => ipcRenderer.send('report-to-ai', event, data),
62
62
 
63
- // AI 연결 상태 확인
63
+ // Check AI connection status
64
64
  isAIConnected: () => ipcRenderer.invoke('is-ai-connected'),
65
65
 
66
- // 메트릭 보고 (렌더러 main AI)
66
+ // Metrics reporting (renderer -> main -> AI)
67
67
  reportMetrics: (summary) => ipcRenderer.send('report-metrics', summary),
68
68
 
69
- // 활성 윈도우 제목 조회 (브라우저 감시)
69
+ // Get active window title (browser watching)
70
70
  getActiveWindowTitle: () => ipcRenderer.invoke('get-active-window-title'),
71
71
 
72
- // 커서 위치 조회 (화면 좌표)
72
+ // Get cursor position (screen coordinates)
73
73
  getCursorPosition: () => ipcRenderer.invoke('get-cursor-position'),
74
74
 
75
- // === 스마트 파일 조작 ===
75
+ // === Smart file operations ===
76
76
  parseFileCommand: (text) => ipcRenderer.invoke('parse-file-command', text),
77
77
  listFilteredFiles: (sourceDir, filter) => ipcRenderer.invoke('list-filtered-files', sourceDir, filter),
78
78
  smartFileOp: (command) => ipcRenderer.invoke('smart-file-op', command),
@@ -1,6 +1,6 @@
1
- /* 파티클 전환 효과 */
1
+ /* Particle and transition effects */
2
2
 
3
- /* 모드 전환 파티클 */
3
+ /* Mode transition particle */
4
4
  .mode-transition-particle {
5
5
  position: absolute;
6
6
  width: 4px;
@@ -21,7 +21,7 @@
21
21
  }
22
22
  }
23
23
 
24
- /* 진화 글로우 */
24
+ /* Evolution glow ring */
25
25
  .evolve-ring {
26
26
  position: absolute;
27
27
  border-radius: 50%;
@@ -41,7 +41,7 @@
41
41
  }
42
42
  }
43
43
 
44
- /* 파일 픽업 이펙트 */
44
+ /* File pickup effect */
45
45
  .file-pickup-effect {
46
46
  position: absolute;
47
47
  font-size: 12px;
@@ -57,7 +57,7 @@
57
57
  100% { opacity: 0; transform: translateY(-30px); }
58
58
  }
59
59
 
60
- /* 꼬리 흔들기 (idle 상태 추가 효과) */
60
+ /* Tail wag (additional idle state effect) */
61
61
  .tail-wag {
62
62
  animation: wag 0.5s ease-in-out infinite alternate;
63
63
  }
@@ -67,7 +67,7 @@
67
67
  100% { transform: rotate(5deg); }
68
68
  }
69
69
 
70
- /* 진화 단계 전환 밝은 플래시 */
70
+ /* Evolution stage transition -- bright flash */
71
71
  .evolve-flash {
72
72
  position: fixed;
73
73
  top: 0;
@@ -1,4 +1,4 @@
1
- /* 레펠 실(thread) SVG 레이어 */
1
+ /* Rappel thread SVG layer */
2
2
  #thread-svg {
3
3
  position: absolute;
4
4
  top: 0;
@@ -9,7 +9,7 @@
9
9
  z-index: 999;
10
10
  }
11
11
 
12
- /* 컨테이너 */
12
+ /* Pet container */
13
13
  #pet-container {
14
14
  position: absolute;
15
15
  width: 64px;
@@ -25,7 +25,7 @@
25
25
  image-rendering: crisp-edges;
26
26
  }
27
27
 
28
- /* 수면 Z 이펙트 */
28
+ /* Sleep Z effect */
29
29
  .sleep-z {
30
30
  position: absolute;
31
31
  top: -20px;
@@ -57,7 +57,7 @@
57
57
  100% { opacity: 0; transform: translateY(-20px) translateX(10px); }
58
58
  }
59
59
 
60
- /* 흥분 이펙트 */
60
+ /* Excitement star effect */
61
61
  .star-effect {
62
62
  position: absolute;
63
63
  width: 8px;
@@ -74,7 +74,7 @@
74
74
  100% { opacity: 0; transform: scale(0.5) translateY(-20px); }
75
75
  }
76
76
 
77
- /* 하트 이펙트 (상호작용) */
77
+ /* Heart effect (interaction) */
78
78
  .heart-effect {
79
79
  position: absolute;
80
80
  font-size: 16px;
@@ -88,7 +88,7 @@
88
88
  100% { opacity: 0; transform: translateY(-40px) scale(1.2); }
89
89
  }
90
90
 
91
- /* 진화 반짝임 이펙트 */
91
+ /* Evolution sparkle effect */
92
92
  .evolve-sparkle {
93
93
  position: absolute;
94
94
  width: 6px;
@@ -104,13 +104,13 @@
104
104
  100% { opacity: 0; transform: scale(0) rotate(360deg); }
105
105
  }
106
106
 
107
- /* 드래그 상태 */
107
+ /* Dragging state */
108
108
  #pet-container.dragging {
109
109
  cursor: grabbing;
110
110
  filter: brightness(1.1);
111
111
  }
112
112
 
113
- /* 모드별 글로우 효과 */
113
+ /* Mode-specific glow effect */
114
114
  #pet-container.mode-incarnation canvas {
115
115
  filter: drop-shadow(0 0 4px rgba(0, 191, 165, 0.6));
116
116
  }
@@ -1,4 +1,4 @@
1
- /* 말풍선 공통 */
1
+ /* Speech bubble common styles */
2
2
  .speech-bubble {
3
3
  position: absolute;
4
4
  padding: 8px 14px;
@@ -25,7 +25,7 @@
25
25
  border-right: 8px solid transparent;
26
26
  }
27
27
 
28
- /* Pet 모드 말풍선 둥글고 빨간 테두리 */
28
+ /* Pet mode speech bubble -- rounded with red border */
29
29
  .speech-pet {
30
30
  background: #fff5f5;
31
31
  border: 2px solid #ff4f40;
@@ -38,7 +38,7 @@
38
38
  border-top: 8px solid #ff4f40;
39
39
  }
40
40
 
41
- /* Incarnation 모드 말풍선 각지고 발광 */
41
+ /* Incarnation mode speech bubble -- angular with teal glow */
42
42
  .speech-incarnation {
43
43
  background: #f0fffd;
44
44
  border: 2px solid #00BFA5;
@@ -51,12 +51,12 @@
51
51
  border-top: 8px solid #00BFA5;
52
52
  }
53
53
 
54
- /* 타자기 커서 효과 */
54
+ /* Typewriter cursor effect */
55
55
  .speech-text {
56
56
  display: inline;
57
57
  }
58
58
 
59
- /* 페이드 아웃 */
59
+ /* Fade out */
60
60
  .speech-fade {
61
61
  animation: speech-disappear 0.5s ease-in forwards;
62
62
  }