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
@@ -1,21 +1,21 @@
1
1
  /**
2
- * ClawMate 렌더러 초기화
2
+ * ClawMate renderer initialization
3
3
  *
4
- * 아키텍처:
5
- * AI () ←→ AI Bridge (WebSocket) ←→ AI Controller (렌더러)
6
- *
4
+ * Architecture:
5
+ * AI (brain) <-> AI Bridge (WebSocket) <-> AI Controller (renderer)
6
+ * |
7
7
  * StateMachine / PetEngine / Speech
8
8
  *
9
- * AI 연결 시: AI 모든 행동/말/감정 결정
10
- * AI 미연결 시: 자율 모드 (FSM 기반) 혼자 놀기
9
+ * When AI connected: AI decides all behaviors/speech/emotions
10
+ * When AI disconnected: Autonomous mode (FSM-based) plays alone
11
11
  */
12
12
  (async function initClawMate() {
13
13
  const petContainer = document.getElementById('pet-container');
14
14
 
15
- // 캐릭터 캔버스 생성
15
+ // Create character canvas
16
16
  Character.createCanvas(petContainer);
17
17
 
18
- // 기본 색상 설정 (Pet 모드)
18
+ // Set default colors (Pet mode)
19
19
  Character.setColorMap({
20
20
  primary: '#ff4f40',
21
21
  secondary: '#ff775f',
@@ -25,7 +25,7 @@
25
25
  claw: '#ff4f40',
26
26
  });
27
27
 
28
- // 상태 변화 콜백
28
+ // State change callback
29
29
  StateMachine.setOnStateChange((prevState, newState) => {
30
30
  if (newState === 'sleeping') {
31
31
  const pet = document.getElementById('pet-container');
@@ -45,10 +45,10 @@
45
45
  Interactions.spawnStarEffect();
46
46
  }
47
47
 
48
- // 모션 히스토리 기록
48
+ // Record motion history
49
49
  Memory.recordMotion(newState);
50
50
 
51
- // 상태 변화를 AI에 리포트
51
+ // Report state changes to AI
52
52
  if (window.clawmate.reportToAI) {
53
53
  window.clawmate.reportToAI('state_change', {
54
54
  from: prevState, to: newState,
@@ -56,48 +56,48 @@
56
56
  }
57
57
  });
58
58
 
59
- // 이동 엔진 초기화
59
+ // Initialize movement engine
60
60
  PetEngine.init(petContainer);
61
61
 
62
- // 모드 매니저 초기화
62
+ // Initialize mode manager
63
63
  await ModeManager.init();
64
64
 
65
- // 메모리 초기화 (진화 상태 포함)
65
+ // Initialize memory (including evolution state)
66
66
  await Memory.init();
67
67
 
68
- // AI 컨트롤러 초기화 (AI 연결 관리)
68
+ // Initialize AI controller (AI connection management)
69
69
  AIController.init();
70
70
 
71
- // 상호작용 초기화
71
+ // Initialize interactions
72
72
  Interactions.init();
73
73
 
74
- // 시간 인식 초기화 (자율 모드에서만 주도적으로 동작)
74
+ // Initialize time awareness (only proactive in autonomous mode)
75
75
  TimeAware.init();
76
76
 
77
- // 메트릭 수집기 초기화 (선택적 없어도 정상 동작)
77
+ // Initialize metrics collector (optional -- app works fine without it)
78
78
  if (typeof Metrics !== 'undefined') {
79
79
  Metrics.init();
80
80
  }
81
81
 
82
- // 브라우저 감시 초기화 (참견쟁이 모드)
82
+ // Initialize browser watcher (nosy mode)
83
83
  if (typeof BrowserWatcher !== 'undefined') {
84
84
  BrowserWatcher.init();
85
85
  }
86
86
 
87
- // 엔진 시작
87
+ // Start engine
88
88
  PetEngine.start();
89
89
 
90
- // 말풍선 위치 업데이트 루프
90
+ // Speech bubble position update loop
91
91
  setInterval(() => {
92
92
  Speech.updatePosition();
93
93
  }, 100);
94
94
 
95
- // AI 연결 상태 표시
95
+ // Display AI connection status
96
96
  const connected = await window.clawmate.isAIConnected();
97
97
  if (connected) {
98
- Speech.show('AI와 연결됨. 지시를 기다리는 중...');
98
+ Speech.show('Connected to AI. Awaiting instructions...');
99
99
  } else {
100
- Speech.show('안녕! 혼자서도 있어!');
100
+ Speech.show('Hi! I can play on my own just fine!');
101
101
  }
102
102
 
103
103
  addDynamicStyles();
@@ -1,22 +1,22 @@
1
1
  /**
2
- * 브라우저 활동 감시 + AI 코멘트 시스템
2
+ * Browser activity watcher + AI comment system
3
3
  *
4
- * 가지 모드:
5
- * AI 연결 시: 윈도우 제목 + 화면 캡처 + 커서 위치를 AI 전송 AI 맥락 있는 코멘트 생성
6
- * AI 미연결 시: 프리셋 메시지로 폴백 (자율 모드)
4
+ * Two modes:
5
+ * When AI connected: Send window title + screen capture + cursor position to AI -> AI generates contextual comments
6
+ * When AI disconnected: Fall back to preset messages (autonomous mode)
7
7
  *
8
- * 동작:
9
- * 1. 15초마다 활성 윈도우 제목 + 커서 위치 조회
10
- * 2. 브라우저/앱 감지 AI 컨텍스트 리포트 (제목 + 화면 캡처)
11
- * 3. AI 제목/캡처를 분석해서 상황 맞는 코멘트 생성
12
- * 4. 자율 모드에서는 프리셋 메시지 폴백
8
+ * Behavior:
9
+ * 1. Check active window title + cursor position every 15 seconds
10
+ * 2. Report context to AI when browser/app detected (title + screen capture)
11
+ * 3. AI analyzes title/capture and generates situation-appropriate comments
12
+ * 4. Fall back to preset messages in autonomous mode
13
13
  */
14
14
  const BrowserWatcher = (() => {
15
- const CHECK_INTERVAL = 15000; // 활성 윈도우 체크 주기 (15초)
16
- const AI_COOLDOWN = 45000; // AI 코멘트 쿨다운 (45초)
17
- const FALLBACK_COOLDOWN = 90000; // 자율 모드 코멘트 쿨다운 (90초)
18
- const COMMENT_CHANCE = 0.4; // 코멘트 확률 (40%)
19
- const SITE_CHANGE_BONUS = 0.3; // 사이트 변경 추가 확률
15
+ const CHECK_INTERVAL = 15000; // Active window check interval (15s)
16
+ const AI_COOLDOWN = 45000; // AI comment cooldown (45s)
17
+ const FALLBACK_COOLDOWN = 90000; // Autonomous mode comment cooldown (90s)
18
+ const COMMENT_CHANCE = 0.4; // Comment probability (40%)
19
+ const SITE_CHANGE_BONUS = 0.3; // Additional probability on site change
20
20
 
21
21
  let intervalId = null;
22
22
  let lastCategory = null;
@@ -26,7 +26,7 @@ const BrowserWatcher = (() => {
26
26
 
27
27
  function init() {
28
28
  intervalId = setInterval(check, CHECK_INTERVAL);
29
- // 체크는 10 ( 시작 직후는 건너뜀)
29
+ // First check after 10 seconds (skip right after app start)
30
30
  setTimeout(check, 10000);
31
31
  }
32
32
 
@@ -34,7 +34,7 @@ const BrowserWatcher = (() => {
34
34
  if (!enabled) return;
35
35
  if (typeof Speech === 'undefined') return;
36
36
 
37
- // sleeping 상태면 참견
37
+ // Don't comment when in sleeping state
38
38
  if (typeof StateMachine !== 'undefined' && StateMachine.getState() === 'sleeping') return;
39
39
 
40
40
  try {
@@ -47,25 +47,25 @@ const BrowserWatcher = (() => {
47
47
 
48
48
  const now = Date.now();
49
49
 
50
- // 카테고리 매칭 (AI/자율 모두 사용)
50
+ // Category matching (used by both AI and autonomous modes)
51
51
  const msgs = window._messages;
52
52
  const match = msgs?.browsing ? findCategory(titleLower, msgs.browsing) : null;
53
53
  const category = match?.category || 'unknown';
54
54
 
55
- // 쿨다운 체크
55
+ // Cooldown check
56
56
  const isAI = typeof AIController !== 'undefined' && AIController.isConnected();
57
57
  const cooldown = isAI ? AI_COOLDOWN : FALLBACK_COOLDOWN;
58
58
  if (now - lastCommentTime < cooldown) return;
59
59
 
60
- // 같은 카테고리 + 제목 미변경 스킵
60
+ // Skip if same category and title unchanged
61
61
  if (category === lastCategory && !titleChanged) return;
62
62
 
63
- // 확률 체크
63
+ // Probability check
64
64
  let chance = COMMENT_CHANCE;
65
65
  if (titleChanged) chance += SITE_CHANGE_BONUS;
66
66
  if (Math.random() > chance) return;
67
67
 
68
- // === AI vs 자율 모드 분기 ===
68
+ // === AI vs autonomous mode branch ===
69
69
  if (isAI) {
70
70
  await reportBrowsingToAI(title, category, titleChanged);
71
71
  } else {
@@ -75,19 +75,19 @@ const BrowserWatcher = (() => {
75
75
  lastCategory = category;
76
76
  lastCommentTime = now;
77
77
  } catch {
78
- // IPC 실패 무시
78
+ // Ignore IPC failure
79
79
  }
80
80
  }
81
81
 
82
82
  /**
83
- * AI에 브라우징 컨텍스트 전송
84
- * 제목 + 커서 위치 + 화면 캡처를 한번에 전송
85
- * AI 분석하고 코멘트 생성
83
+ * Send browsing context to AI
84
+ * Transmit title + cursor position + screen capture together
85
+ * AI analyzes and generates comments
86
86
  */
87
87
  async function reportBrowsingToAI(title, category, titleChanged) {
88
88
  if (!window.clawmate.reportToAI) return;
89
89
 
90
- // 커서 위치 조회
90
+ // Get cursor position
91
91
  let cursorX = 0, cursorY = 0;
92
92
  try {
93
93
  if (window.clawmate.getCursorPosition) {
@@ -97,7 +97,7 @@ const BrowserWatcher = (() => {
97
97
  }
98
98
  } catch {}
99
99
 
100
- // 화면 캡처 (AI 페이지 내용을 시각적으로 분석하기 위해)
100
+ // Screen capture (for AI to visually analyze page content)
101
101
  let screenData = null;
102
102
  try {
103
103
  const capture = await window.clawmate.screen.capture();
@@ -110,7 +110,7 @@ const BrowserWatcher = (() => {
110
110
  }
111
111
  } catch {}
112
112
 
113
- // 통합 브라우징 리포트 전송
113
+ // Send unified browsing report
114
114
  window.clawmate.reportToAI('browsing', {
115
115
  title,
116
116
  category,
@@ -123,7 +123,7 @@ const BrowserWatcher = (() => {
123
123
  }
124
124
 
125
125
  /**
126
- * 자율 모드 폴백: 프리셋 메시지 표시
126
+ * Autonomous mode fallback: display preset messages
127
127
  */
128
128
  function showFallbackComment(match) {
129
129
  if (!match?.data?.comments) return;
@@ -132,7 +132,7 @@ const BrowserWatcher = (() => {
132
132
  const comment = comments[Math.floor(Math.random() * comments.length)];
133
133
  Speech.show(comment);
134
134
 
135
- // 50% 확률로 흥분 애니메이션
135
+ // 50% chance of excitement animation
136
136
  if (typeof StateMachine !== 'undefined') {
137
137
  const state = StateMachine.getState();
138
138
  if ((state === 'idle' || state === 'walking') && Math.random() < 0.5) {
@@ -145,8 +145,8 @@ const BrowserWatcher = (() => {
145
145
  }
146
146
 
147
147
  /**
148
- * 카테고리 매칭 (키워드 기반)
149
- * general 다른 카테고리 매칭 때만 사용
148
+ * Category matching (keyword-based)
149
+ * 'general' is only used when no other category matches
150
150
  */
151
151
  function findCategory(titleLower, browsingMsgs) {
152
152
  let generalMatch = null;
@@ -1,20 +1,20 @@
1
1
  /**
2
- * 픽셀 아트 랍스터(Claw) 캐릭터 렌더러
3
- * 16x16 그리드 CSS div 렌더링 4x 확대 (64x64px)
2
+ * Pixel art lobster (Claw) character renderer
3
+ * 16x16 grid -> rendered as CSS div -> 4x scaled (64x64px)
4
4
  *
5
- * 색상 코드:
6
- * 0 = 투명, 1 = primary(빨강), 2 = secondary(연빨강),
7
- * 3 = dark(갈색), 4 = eye, 5 = pupil, 6 = claw 전용
5
+ * Color codes:
6
+ * 0 = transparent, 1 = primary (red), 2 = secondary (light red),
7
+ * 3 = dark (brown), 4 = eye, 5 = pupil, 6 = claw specific
8
8
  */
9
9
  const Character = (() => {
10
10
  const PIXEL = 4;
11
11
  const GRID = 16;
12
12
  const SIZE = PIXEL * GRID;
13
13
 
14
- // --- 프레임 데이터 (16x16 배열) ---
14
+ // --- Frame data (16x16 arrays) ---
15
15
  const FRAMES = {
16
16
  idle: [
17
- // Frame 0: 기본 자세 집게 열림
17
+ // Frame 0: Default pose -- claws open
18
18
  [
19
19
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
20
20
  [0,0,6,6,0,0,0,0,0,0,0,0,6,6,0,0],
@@ -33,7 +33,7 @@ const Character = (() => {
33
33
  [0,3,3,3,0,0,0,3,3,0,0,0,3,3,3,0],
34
34
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
35
35
  ],
36
- // Frame 1: 집게 닫힘 (물결)
36
+ // Frame 1: Claws closed (wave)
37
37
  [
38
38
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
39
39
  [0,0,6,6,0,0,0,0,0,0,0,0,6,6,0,0],
@@ -55,7 +55,7 @@ const Character = (() => {
55
55
  ],
56
56
 
57
57
  walk: [
58
- // Frame 0: 왼쪽 다리 세트 앞으로 크게 벌림, 집게 열림
58
+ // Frame 0: Left leg set extended forward wide, claws open
59
59
  [
60
60
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
61
61
  [0,0,6,6,0,0,0,0,0,0,0,0,6,6,0,0],
@@ -74,7 +74,7 @@ const Character = (() => {
74
74
  [3,3,0,0,0,0,0,3,3,0,0,0,0,0,3,3],
75
75
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
76
76
  ],
77
- // Frame 1: 양쪽 다리 모임 (접촉 순간), 집게 반 닫힘
77
+ // Frame 1: Both leg sets together (contact moment), claws half-closed
78
78
  [
79
79
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
80
80
  [0,0,6,6,0,0,0,0,0,0,0,0,6,6,0,0],
@@ -93,7 +93,7 @@ const Character = (() => {
93
93
  [0,0,0,3,3,0,0,3,3,0,0,3,3,0,0,0],
94
94
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
95
95
  ],
96
- // Frame 2: 오른쪽 다리 세트 앞으로 크게 벌림, 집게 닫힘
96
+ // Frame 2: Right leg set extended forward wide, claws closed
97
97
  [
98
98
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
99
99
  [0,0,6,6,0,0,0,0,0,0,0,0,6,6,0,0],
@@ -112,7 +112,7 @@ const Character = (() => {
112
112
  [0,3,3,0,0,0,0,3,3,0,0,0,0,3,3,0],
113
113
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
114
114
  ],
115
- // Frame 3: 양쪽 다리 모임 (접촉 순간), 집게 반 열림
115
+ // Frame 3: Both leg sets together (contact moment), claws half-open
116
116
  [
117
117
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
118
118
  [0,0,6,6,0,0,0,0,0,0,0,0,6,6,0,0],
@@ -134,7 +134,7 @@ const Character = (() => {
134
134
  ],
135
135
 
136
136
  climb: [
137
- // Frame 0: 기어오르기 윗다리 세트 뻗음, 집게 열림
137
+ // Frame 0: Climbing -- upper leg set extended, claws open
138
138
  [
139
139
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
140
140
  [0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0],
@@ -153,7 +153,7 @@ const Character = (() => {
153
153
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
154
154
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
155
155
  ],
156
- // Frame 1: 기어오르기 다리 모임 (교차 순간)
156
+ // Frame 1: Climbing -- legs together (crossing moment)
157
157
  [
158
158
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
159
159
  [0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0],
@@ -172,7 +172,7 @@ const Character = (() => {
172
172
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
173
173
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
174
174
  ],
175
- // Frame 2: 기어오르기 아랫다리 세트 뻗음, 집게 닫힘
175
+ // Frame 2: Climbing -- lower leg set extended, claws closed
176
176
  [
177
177
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
178
178
  [0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0],
@@ -191,7 +191,7 @@ const Character = (() => {
191
191
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
192
192
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
193
193
  ],
194
- // Frame 3: 기어오르기 다리 모임 (교차 순간, 반대쪽)
194
+ // Frame 3: Climbing -- legs together (crossing moment, opposite side)
195
195
  [
196
196
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
197
197
  [0,0,0,0,6,6,0,0,0,0,0,0,0,0,0,0],
@@ -234,7 +234,7 @@ const Character = (() => {
234
234
  ],
235
235
 
236
236
  carry: [
237
- // Frame 0: 파일 들고 걷기 1
237
+ // Frame 0: Walking with file 1
238
238
  [
239
239
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
240
240
  [0,0,6,1,1,1,1,0,0,1,1,1,1,6,0,0],
@@ -253,7 +253,7 @@ const Character = (() => {
253
253
  [0,3,3,3,0,0,0,3,3,0,0,0,3,3,3,0],
254
254
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
255
255
  ],
256
- // Frame 1: 파일 들고 걷기 2
256
+ // Frame 1: Walking with file 2
257
257
  [
258
258
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
259
259
  [0,0,6,1,1,1,1,0,0,1,1,1,1,6,0,0],
@@ -275,7 +275,7 @@ const Character = (() => {
275
275
  ],
276
276
 
277
277
  scared: [
278
- // Frame 0: 놀람 크게
278
+ // Frame 0: Startled -- eyes wide
279
279
  [
280
280
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
281
281
  [0,0,6,6,0,0,0,0,0,0,0,0,6,6,0,0],
@@ -294,7 +294,7 @@ const Character = (() => {
294
294
  [0,3,3,3,0,0,0,3,3,0,0,0,3,3,3,0],
295
295
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
296
296
  ],
297
- // Frame 1: 뒤로 움찔
297
+ // Frame 1: Flinch backward
298
298
  [
299
299
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
300
300
  [0,0,0,6,6,0,0,0,0,0,0,6,6,0,0,0],
@@ -316,7 +316,7 @@ const Character = (() => {
316
316
  ],
317
317
 
318
318
  excited: [
319
- // Frame 0: 점프
319
+ // Frame 0: Jump up
320
320
  [
321
321
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
322
322
  [0,6,6,0,0,0,0,0,0,0,0,0,0,6,6,0],
@@ -335,7 +335,7 @@ const Character = (() => {
335
335
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
336
336
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
337
337
  ],
338
- // Frame 1: 착지
338
+ // Frame 1: Landing
339
339
  [
340
340
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
341
341
  [0,0,6,6,0,0,0,0,0,0,0,0,6,6,0,0],
@@ -357,7 +357,7 @@ const Character = (() => {
357
357
  ],
358
358
  };
359
359
 
360
- // 상태 프레임셋 매핑
360
+ // State -> frameset mapping
361
361
  const STATE_FRAMES = {
362
362
  idle: 'idle',
363
363
  walking: 'walk',
@@ -370,15 +370,15 @@ const Character = (() => {
370
370
  interacting: 'excited',
371
371
  scared: 'scared',
372
372
  excited: 'excited',
373
- jumping: 'excited', // 점프: excited 프레임셋 재활용
374
- rappelling: 'climb', // 레펠: climb 프레임셋 재활용
375
- falling: 'scared', // 낙하: scared 프레임셋 재활용
376
- custom: 'walk', // 커스텀 이동: 기본은 walk, 패턴별로 동적 변경 가능
373
+ jumping: 'excited', // Jump: reuse excited frameset
374
+ rappelling: 'climb', // Rappel: reuse climb frameset
375
+ falling: 'scared', // Falling: reuse scared frameset
376
+ custom: 'walk', // Custom movement: default is walk, can be dynamically changed per pattern
377
377
  };
378
378
 
379
379
  let currentCanvas = null;
380
380
  let currentColorMap = null;
381
- let originalFrames = null; // 원본 프레임 백업 (리셋용)
381
+ let originalFrames = null; // Original frame backup (for reset)
382
382
 
383
383
  function createCanvas(container) {
384
384
  const canvas = document.createElement('canvas');
@@ -436,8 +436,8 @@ const Character = (() => {
436
436
  }
437
437
 
438
438
  /**
439
- * 커스텀 프레임 데이터 설정 (AI 생성 캐릭터용)
440
- * 기존 프레임을 백업하고 프레임으로 교체
439
+ * Set custom frame data (for AI-generated characters)
440
+ * Backs up existing frames and replaces with new ones
441
441
  */
442
442
  function setCustomFrames(newFrames) {
443
443
  if (!originalFrames) {
@@ -454,7 +454,7 @@ const Character = (() => {
454
454
  }
455
455
 
456
456
  /**
457
- * 캐릭터 데이터 일괄 설정 (색상 + 프레임)
457
+ * Batch set character data (colors + frames)
458
458
  * @param {object} data - { colorMap?, frames? }
459
459
  */
460
460
  function setCharacterData(data) {
@@ -467,7 +467,7 @@ const Character = (() => {
467
467
  }
468
468
 
469
469
  /**
470
- * 원래 캐릭터로 리셋
470
+ * Reset to original character
471
471
  */
472
472
  function resetCharacter() {
473
473
  if (originalFrames) {
@@ -477,7 +477,7 @@ const Character = (() => {
477
477
  Object.assign(FRAMES, originalFrames);
478
478
  originalFrames = null;
479
479
  }
480
- // 기본 색상으로 리셋
480
+ // Reset to default colors
481
481
  setColorMap({
482
482
  primary: '#ff4f40',
483
483
  secondary: '#ff775f',
@@ -1,8 +1,8 @@
1
1
  /**
2
- * 마우스/클릭/드래그 상호작용 시스템
2
+ * Mouse/click/drag interaction system
3
3
  *
4
- * AI 연결 시: 이벤트를 AI 전달 AI 반응 결정
5
- * AI 미연결 시: 자율 반응 (랜덤 FSM)
4
+ * When AI connected: Events forwarded to AI -> AI decides reactions
5
+ * When AI disconnected: Autonomous reactions (random FSM)
6
6
  */
7
7
  const Interactions = (() => {
8
8
  let isDragging = false;
@@ -76,7 +76,7 @@ const Interactions = (() => {
76
76
 
77
77
  const endPos = PetEngine.getPosition();
78
78
 
79
- // 화면 가장자리까지의 거리 계산
79
+ // Calculate distance to screen edges
80
80
  const screenW = window.innerWidth;
81
81
  const screenH = window.innerHeight;
82
82
  const charSize = PetEngine.CHAR_SIZE;
@@ -86,15 +86,15 @@ const Interactions = (() => {
86
86
  const distRight = screenW - charSize - endPos.x;
87
87
  const minEdgeDist = Math.min(distBottom, distTop, distLeft, distRight);
88
88
 
89
- // 가장자리에서 충분히 떨어져 있으면 (화면 중앙 근처) 낙하
90
- // 가장자리 근처 임계값: 화면 짧은 변의 15%
89
+ // If far enough from edges (near screen center) -> free fall
90
+ // Edge proximity threshold: 15% of the shorter screen dimension
91
91
  const edgeThreshold = Math.min(screenW, screenH) * 0.15;
92
92
 
93
93
  if (minEdgeDist > edgeThreshold) {
94
- // 화면 중앙 근처: 중력 낙하로 바닥까지 떨어짐
94
+ // Near screen center: gravity fall to the floor
95
95
  PetEngine.startFalling();
96
96
  } else {
97
- // 가장자리 근처: 기존대로 가장 가까운 가장자리에 붙음
97
+ // Near edge: snap to the nearest edge as before
98
98
  PetEngine.snapToNearestEdge();
99
99
  StateMachine.forceState('idle');
100
100
  }
@@ -102,7 +102,7 @@ const Interactions = (() => {
102
102
  PetEngine.start();
103
103
  window.clawmate.setClickThrough(true);
104
104
 
105
- // AI에 드래그 이벤트 리포트 + 반응 기록
105
+ // Report drag event to AI + record reaction
106
106
  if (dragStartPos) {
107
107
  const draggedAction = StateMachine.getState();
108
108
  Memory.recordReaction(draggedAction, 'drag');
@@ -114,14 +114,14 @@ const Interactions = (() => {
114
114
  const pos = PetEngine.getPosition();
115
115
  const currentAction = StateMachine.getState();
116
116
 
117
- // AI에 클릭 이벤트 리포트
117
+ // Report click event to AI
118
118
  AIController.reportClick(pos);
119
119
 
120
- // 유저 반응 기록 현재 행동 클릭 = 긍정 반응
120
+ // Record user reaction -- click during current action = positive reaction
121
121
  Memory.recordReaction(currentAction, 'click');
122
122
 
123
- // AI 연결 시: AI 반응 결정 (아무것도 함, AI 응답 대기)
124
- // AI 미연결 시: 자율 반응
123
+ // When AI connected: AI decides reaction (do nothing, wait for AI response)
124
+ // When AI disconnected: autonomous reaction
125
125
  if (AIController.isAutonomous()) {
126
126
  StateMachine.forceState('interacting');
127
127
  Speech.show(Speech.getReactionMessage());
@@ -135,26 +135,26 @@ const Interactions = (() => {
135
135
  const pos = PetEngine.getPosition();
136
136
  const currentAction = StateMachine.getState();
137
137
 
138
- // 유저 반응 기록
138
+ // Record user reaction
139
139
  Memory.recordReaction(currentAction, 'double_click');
140
140
 
141
- // AI에 더블클릭 리포트
141
+ // Report double-click to AI
142
142
  if (window.clawmate.reportToAI) {
143
143
  window.clawmate.reportToAI('double_click', { position: pos });
144
144
  }
145
145
 
146
- // 자율 모드: 더블클릭 = 특별 반응 (점프 + 기분좋음)
146
+ // Autonomous mode: double-click = special reaction (jump + excitement)
147
147
  if (AIController.isAutonomous()) {
148
148
  StateMachine.forceState('excited');
149
149
  PetEngine.jumpTo(
150
150
  pos.x + (Math.random() - 0.5) * 200,
151
151
  Math.max(100, pos.y - 150)
152
152
  );
153
- Speech.show('우와! 더블클릭이다!');
153
+ Speech.show('Wow! A double-click!');
154
154
  }
155
155
 
156
156
  Memory.recordClick();
157
- Memory.recordClick(); // 더블클릭 = 2 클릭
157
+ Memory.recordClick(); // double-click = 2 clicks
158
158
  spawnHeartEffect();
159
159
  spawnStarEffect();
160
160
  }
@@ -178,14 +178,14 @@ const Interactions = (() => {
178
178
  const dist = Math.hypot(e.clientX - (pos.x + 32), e.clientY - (pos.y + 32));
179
179
 
180
180
  if (dist < 100) {
181
- // 유저 반응 기록 커서 접근 = 관심 표현
181
+ // Record user reaction -- cursor approach = showing interest
182
182
  const curAction = StateMachine.getState();
183
183
  Memory.recordReaction(curAction, 'cursor_near');
184
184
 
185
- // AI에 커서 접근 리포트
185
+ // Report cursor proximity to AI
186
186
  AIController.reportCursorNear(dist);
187
187
 
188
- // AI 미연결 시: 자율 반응
188
+ // When AI disconnected: autonomous reaction
189
189
  if (AIController.isAutonomous()) {
190
190
  const state = StateMachine.getState();
191
191
  if (state === 'idle' || state === 'walking') {