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.
- package/index.js +441 -442
- package/main/ai-bridge.js +59 -59
- package/main/ai-connector.js +60 -60
- package/main/autostart.js +6 -6
- package/main/desktop-path.js +4 -4
- package/main/file-command-parser.js +46 -46
- package/main/file-ops.js +27 -27
- package/main/index.js +17 -17
- package/main/ipc-handlers.js +24 -24
- package/main/manifest.js +2 -2
- package/main/platform.js +16 -16
- package/main/smart-file-ops.js +64 -64
- package/main/store.js +1 -1
- package/main/telegram.js +137 -137
- package/main/tray.js +61 -61
- package/main/updater.js +13 -13
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -2
- package/preload/preload.js +18 -18
- package/renderer/css/effects.css +6 -6
- package/renderer/css/pet.css +8 -8
- package/renderer/css/speech.css +5 -5
- package/renderer/first-run.html +14 -14
- package/renderer/index.html +4 -4
- package/renderer/js/ai-controller.js +91 -91
- package/renderer/js/app.js +24 -24
- package/renderer/js/browser-watcher.js +32 -32
- package/renderer/js/character.js +33 -33
- package/renderer/js/interactions.js +21 -21
- package/renderer/js/memory.js +60 -60
- package/renderer/js/metrics.js +141 -141
- package/renderer/js/mode-manager.js +13 -13
- package/renderer/js/pet-engine.js +236 -236
- package/renderer/js/speech.js +19 -19
- package/renderer/js/state-machine.js +23 -23
- package/renderer/js/time-aware.js +15 -15
- package/renderer/launcher.html +8 -8
- package/shared/constants.js +11 -11
- package/shared/messages.js +130 -130
- package/shared/personalities.js +44 -44
- package/skills/launch-pet/index.js +57 -47
- 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
|
-
*
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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
|
-
//
|
|
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('[
|
|
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:
|
|
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('[
|
|
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
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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('[
|
|
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(`[
|
|
31
|
+
console.log(`[Update] Download progress: ${Math.round(progress.percent)}%`);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
autoUpdater.on('update-downloaded', (info) => {
|
|
35
|
-
console.log('[
|
|
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('[
|
|
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);
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawmate",
|
|
3
|
-
"version": "1.4.
|
|
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"
|
package/preload/preload.js
CHANGED
|
@@ -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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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),
|
package/renderer/css/effects.css
CHANGED
|
@@ -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
|
-
/*
|
|
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;
|
package/renderer/css/pet.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*
|
|
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
|
-
/*
|
|
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
|
}
|
package/renderer/css/speech.css
CHANGED
|
@@ -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
|
}
|