chenbhao commited on
Commit
e18c81d
Β·
1 Parent(s): d52e9dd

feat: friend refactor to src

Browse files
This view is limited to 50 files because it contains too many changes. Β  See raw diff
Files changed (50) hide show
  1. .gitignore +5 -5
  2. scripts/build.ts +30 -0
  3. src/commands.ts +2 -0
  4. src/commands/friend/friend.tsx +43 -15
  5. {friend/frontend/src β†’ src/components/friend/frontend}/App.tsx +0 -0
  6. {friend/frontend/src β†’ src/components/friend/frontend}/api.ts +0 -0
  7. {friend/frontend/src β†’ src/components/friend/frontend}/assets/lip-sync-profile.json +0 -0
  8. {friend/frontend/src β†’ src/components/friend/frontend}/components/ChatInput.tsx +0 -0
  9. {friend/frontend/src β†’ src/components/friend/frontend}/components/HistoryPanel.tsx +0 -0
  10. {friend/frontend/src β†’ src/components/friend/frontend}/components/MoodIndicator.tsx +0 -0
  11. {friend/frontend/src β†’ src/components/friend/frontend}/components/ResizeHandles.tsx +0 -0
  12. {friend/frontend/src β†’ src/components/friend/frontend}/components/SettingsPanel.tsx +0 -0
  13. {friend/frontend/src β†’ src/components/friend/frontend}/components/TextBubble.tsx +0 -0
  14. {friend/frontend/src β†’ src/components/friend/frontend}/components/VRMScene.tsx +0 -0
  15. {friend/frontend/src β†’ src/components/friend/frontend}/emote.ts +0 -0
  16. {friend/frontend/src β†’ src/components/friend/frontend}/hooks/usePassThrough.ts +0 -0
  17. {friend β†’ src/components/friend}/frontend/index.html +1 -1
  18. {friend/frontend/src β†’ src/components/friend/frontend}/lip-sync.ts +0 -0
  19. {friend/frontend/src β†’ src/components/friend/frontend}/main.tsx +0 -0
  20. {friend/frontend/src β†’ src/components/friend/frontend}/mixamo-loader.ts +0 -0
  21. {friend/frontend/src β†’ src/components/friend/frontend}/mmd-parser.d.ts +0 -0
  22. {friend/frontend/src β†’ src/components/friend/frontend}/motion-controller.ts +0 -0
  23. {friend β†’ src/components/friend}/frontend/package-lock.json +0 -0
  24. {friend β†’ src/components/friend}/frontend/package.json +0 -0
  25. {friend β†’ src/components/friend}/frontend/public/akimbo.vrma +0 -0
  26. {friend β†’ src/components/friend}/frontend/public/angry.fbx +0 -0
  27. {friend β†’ src/components/friend}/frontend/public/angryPump.fbx +0 -0
  28. {friend β†’ src/components/friend}/frontend/public/excited.fbx +0 -0
  29. {friend β†’ src/components/friend}/frontend/public/greeting.fbx +0 -0
  30. {friend β†’ src/components/friend}/frontend/public/happy.fbx +0 -0
  31. {friend β†’ src/components/friend}/frontend/public/idle_loop.vrma +0 -0
  32. {friend β†’ src/components/friend}/frontend/public/jile.vmd +0 -0
  33. {friend β†’ src/components/friend}/frontend/public/love.vmd +0 -0
  34. {friend β†’ src/components/friend}/frontend/public/playFingers.vrma +0 -0
  35. {friend β†’ src/components/friend}/frontend/public/point.fbx +0 -0
  36. {friend β†’ src/components/friend}/frontend/public/salute.fbx +0 -0
  37. {friend β†’ src/components/friend}/frontend/public/scratchHead.vrma +0 -0
  38. {friend β†’ src/components/friend}/frontend/public/shy.fbx +0 -0
  39. {friend β†’ src/components/friend}/frontend/public/stretch.vrma +0 -0
  40. {friend β†’ src/components/friend}/frontend/src-tauri/Cargo.lock +0 -0
  41. {friend β†’ src/components/friend}/frontend/src-tauri/Cargo.toml +0 -0
  42. {friend β†’ src/components/friend}/frontend/src-tauri/build.rs +0 -0
  43. {friend β†’ src/components/friend}/frontend/src-tauri/capabilities/default.json +0 -0
  44. {friend β†’ src/components/friend}/frontend/src-tauri/icons/128x128.png +0 -0
  45. {friend β†’ src/components/friend}/frontend/src-tauri/icons/256x256.png +0 -0
  46. {friend β†’ src/components/friend}/frontend/src-tauri/icons/32x32.png +0 -0
  47. {friend β†’ src/components/friend}/frontend/src-tauri/src/lib.rs +0 -0
  48. {friend β†’ src/components/friend}/frontend/src-tauri/src/main.rs +0 -0
  49. {friend β†’ src/components/friend}/frontend/src-tauri/tauri.conf.json +0 -0
  50. {friend/frontend/src β†’ src/components/friend/frontend}/three-helpers.ts +0 -0
.gitignore CHANGED
@@ -8,11 +8,11 @@ VersperClaw
8
  # Prebuilt native binaries (tracked in claude-code/ reference)
9
  vendor/
10
 
11
- # Friend VRM desktop (Tauri) build artifacts
12
- friend/frontend/node_modules/
13
- friend/frontend/dist/
14
- friend/frontend/src-tauri/target/
15
- friend/frontend/src-tauri/gen/
16
 
17
  # Python cache
18
  __pycache__/
 
8
  # Prebuilt native binaries (tracked in claude-code/ reference)
9
  vendor/
10
 
11
+ # Friend VRM frontend (Vite + Tauri) build artifacts
12
+ src/components/friend/frontend/node_modules/
13
+ src/components/friend/frontend/dist/
14
+ src/components/friend/frontend/src-tauri/target/
15
+ src/components/friend/frontend/src-tauri/gen/
16
 
17
  # Python cache
18
  __pycache__/
scripts/build.ts CHANGED
@@ -118,6 +118,36 @@ const outfile = compile
118
  : dev
119
  ? './VersperClaw'
120
  : './cli'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  const buildTime = new Date().toISOString()
122
  const version = dev ? getDevVersion(pkg.version) : pkg.version
123
 
 
118
  : dev
119
  ? './VersperClaw'
120
  : './cli'
121
+
122
+ // ── Pre-step: build Friend VRM frontend ───────────────────────────────────────
123
+ function buildFriendFrontend(): boolean {
124
+ const frontendDir = join(process.cwd(), 'src', 'components', 'friend', 'frontend')
125
+ const distIndex = join(frontendDir, 'dist', 'index.html')
126
+ if (existsSync(distIndex)) {
127
+ console.log('Friend frontend already built, skipping.')
128
+ return true
129
+ }
130
+ console.log('Building Friend VRM frontend...')
131
+ const proc = Bun.spawnSync({
132
+ cmd: ['npm', 'run', 'build'],
133
+ cwd: frontendDir,
134
+ stdout: 'inherit',
135
+ stderr: 'inherit',
136
+ })
137
+ if (proc.exitCode !== 0) {
138
+ console.error('Friend frontend build failed.')
139
+ return false
140
+ }
141
+ console.log('Friend frontend built.')
142
+ return true
143
+ }
144
+
145
+ if (!buildFriendFrontend()) {
146
+ process.exit(1)
147
+ }
148
+
149
+ // ─────────────────────────────────────────────────────────────────────────────
150
+
151
  const buildTime = new Date().toISOString()
152
  const version = dev ? getDevVersion(pkg.version) : pkg.version
153
 
src/commands.ts CHANGED
@@ -44,6 +44,7 @@ import skills from './commands/skills/index.js'
44
  import status from './commands/status/index.js'
45
  import tasks from './commands/tasks/index.js'
46
  import feishu from './commands/feishu/index.js'
 
47
  import goals from './commands/goal/index.js'
48
  import telegram from './commands/telegram/index.js'
49
  import teleport from './commands/teleport/index.js'
@@ -309,6 +310,7 @@ const COMMANDS = memoize((): Command[] => [
309
  theme,
310
  feedback,
311
  feishu,
 
312
  goals,
313
  review,
314
  ultrareview,
 
44
  import status from './commands/status/index.js'
45
  import tasks from './commands/tasks/index.js'
46
  import feishu from './commands/feishu/index.js'
47
+ import friend from './commands/friend/index.js'
48
  import goals from './commands/goal/index.js'
49
  import telegram from './commands/telegram/index.js'
50
  import teleport from './commands/teleport/index.js'
 
310
  theme,
311
  feedback,
312
  feishu,
313
+ friend,
314
  goals,
315
  review,
316
  ultrareview,
src/commands/friend/friend.tsx CHANGED
@@ -4,13 +4,26 @@
4
  * Ink-based terminal UI that shows status and controls for the
5
  * Friend desktop pet feature.
6
  */
7
- import React, { useState, useEffect } from 'react'
8
  import { Box, Text, useInput } from '../../ink.js'
9
  import { getPrefs, updatePrefs } from '../../friend/prefs.js'
10
- import type { LocalJSXCommandOnDone, CommandResultDisplay } from '../../commands.js'
 
 
 
 
 
11
 
 
12
  const FRIEND_URL = 'http://127.0.0.1:3456/friend/'
13
 
 
 
 
 
 
 
 
14
  type Page = 'status' | 'help'
15
 
16
  export async function call(
@@ -20,22 +33,31 @@ export async function call(
20
  ): Promise<React.ReactNode> {
21
  const trimmed = args?.trim().toLowerCase()
22
 
23
- // Non-interactive modes
24
  if (trimmed === 'start') {
25
  updatePrefs({ enabled: true })
 
 
 
 
 
 
 
 
 
 
26
  return (
27
  <Box flexDirection="column">
28
- <Text>Friend companion enabled.</Text>
29
- <Text>Start the server with:</Text>
30
- <Text> bun run src/server/index.ts --port 3456</Text>
31
- <Text>Then open {FRIEND_URL} in a browser (or Tauri will launch automatically).</Text>
32
  </Box>
33
  )
34
  }
35
 
36
  if (trimmed === 'stop') {
37
  updatePrefs({ enabled: false })
38
- return <Text>Friend companion disabled.</Text>
 
39
  }
40
 
41
  if (trimmed === 'status') {
@@ -43,7 +65,6 @@ export async function call(
43
  return <FriendStatus prefs={prefs} compact />
44
  }
45
 
46
- // Interactive mode (no args or unrecognized)
47
  return <FriendManager onDone={onDone} />
48
  }
49
 
@@ -60,17 +81,17 @@ function FriendManager({ onDone }: { onDone: LocalJSXCommandOnDone }) {
60
  <Box flexDirection="column" padding={1}>
61
  <Box marginBottom={1}>
62
  <Text bold underline>
63
- {page === 'status' ? 'πŸ„΅ Friend VRM Desktop Pet' : 'Help'}
64
  </Text>
65
  </Box>
66
 
67
- {page === 'status' && <FriendStatus prefs={getPrefs()} compact={false} />}
68
 
69
  <Box marginTop={1} flexDirection="column">
70
  <Text dimColor>Usage:</Text>
71
- <Text dimColor> /friend Interactive status &amp; controls</Text>
72
- <Text dimColor> /friend start Enable &amp; launch friend</Text>
73
- <Text dimColor> /friend stop Disable friend</Text>
74
  <Text dimColor> /friend status Quick status overview</Text>
75
  </Box>
76
 
@@ -90,6 +111,7 @@ function FriendStatus({
90
  }) {
91
  const statusDot = prefs.enabled ? '●' : 'β—‹'
92
  const statusColor = prefs.enabled ? 'green' : 'gray'
 
93
 
94
  return (
95
  <Box flexDirection="column">
@@ -101,6 +123,12 @@ function FriendStatus({
101
  </Box>
102
  {!compact && (
103
  <>
 
 
 
 
 
 
104
  <Box>
105
  <Text> URL: </Text>
106
  <Text dimColor>{FRIEND_URL}</Text>
@@ -121,4 +149,4 @@ function FriendStatus({
121
  )}
122
  </Box>
123
  )
124
- }
 
4
  * Ink-based terminal UI that shows status and controls for the
5
  * Friend desktop pet feature.
6
  */
7
+ import React, { useState } from 'react'
8
  import { Box, Text, useInput } from '../../ink.js'
9
  import { getPrefs, updatePrefs } from '../../friend/prefs.js'
10
+ import {
11
+ launchTauri,
12
+ stopTauri,
13
+ getTauriProcess,
14
+ } from '../../friend/tauri-launcher.js'
15
+ import type { LocalJSXCommandOnDone, CommandResultDisplay } from '../../types/command.js'
16
 
17
+ const FRIEND_FRONTEND_DIR = '../../components/friend/frontend'
18
  const FRIEND_URL = 'http://127.0.0.1:3456/friend/'
19
 
20
+ function logger() {
21
+ return {
22
+ info: (msg: string) => console.log(`[Friend] ${msg}`),
23
+ warn: (msg: string) => console.warn(`[Friend] ${msg}`),
24
+ }
25
+ }
26
+
27
  type Page = 'status' | 'help'
28
 
29
  export async function call(
 
33
  ): Promise<React.ReactNode> {
34
  const trimmed = args?.trim().toLowerCase()
35
 
 
36
  if (trimmed === 'start') {
37
  updatePrefs({ enabled: true })
38
+ const alreadyRunning = !!getTauriProcess()
39
+ if (alreadyRunning) {
40
+ return (
41
+ <Box flexDirection="column">
42
+ <Text>Friend is already running.</Text>
43
+ <Text dimColor>Open {FRIEND_URL} in your browser.</Text>
44
+ </Box>
45
+ )
46
+ }
47
+ launchTauri(FRIEND_FRONTEND_DIR, logger())
48
  return (
49
  <Box flexDirection="column">
50
+ <Text>Starting Friend VRM companion...</Text>
51
+ <Text dimColor>A Tauri window will open.</Text>
52
+ <Text dimColor>Also available at: {FRIEND_URL}</Text>
 
53
  </Box>
54
  )
55
  }
56
 
57
  if (trimmed === 'stop') {
58
  updatePrefs({ enabled: false })
59
+ stopTauri(logger())
60
+ return <Text>Friend companion stopped.</Text>
61
  }
62
 
63
  if (trimmed === 'status') {
 
65
  return <FriendStatus prefs={prefs} compact />
66
  }
67
 
 
68
  return <FriendManager onDone={onDone} />
69
  }
70
 
 
81
  <Box flexDirection="column" padding={1}>
82
  <Box marginBottom={1}>
83
  <Text bold underline>
84
+ Friend VRM Desktop Pet
85
  </Text>
86
  </Box>
87
 
88
+ <FriendStatus prefs={getPrefs()} compact={false} />
89
 
90
  <Box marginTop={1} flexDirection="column">
91
  <Text dimColor>Usage:</Text>
92
+ <Text dimColor> /friend Interactive status & controls</Text>
93
+ <Text dimColor> /friend start Launch friend window</Text>
94
+ <Text dimColor> /friend stop Stop friend window</Text>
95
  <Text dimColor> /friend status Quick status overview</Text>
96
  </Box>
97
 
 
111
  }) {
112
  const statusDot = prefs.enabled ? '●' : 'β—‹'
113
  const statusColor = prefs.enabled ? 'green' : 'gray'
114
+ const running = !!getTauriProcess()
115
 
116
  return (
117
  <Box flexDirection="column">
 
123
  </Box>
124
  {!compact && (
125
  <>
126
+ {running && (
127
+ <Box>
128
+ <Text> Window: </Text>
129
+ <Text color="green">Running</Text>
130
+ </Box>
131
+ )}
132
  <Box>
133
  <Text> URL: </Text>
134
  <Text dimColor>{FRIEND_URL}</Text>
 
149
  )}
150
  </Box>
151
  )
152
+ }
{friend/frontend/src β†’ src/components/friend/frontend}/App.tsx RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/api.ts RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/assets/lip-sync-profile.json RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/components/ChatInput.tsx RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/components/HistoryPanel.tsx RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/components/MoodIndicator.tsx RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/components/ResizeHandles.tsx RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/components/SettingsPanel.tsx RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/components/TextBubble.tsx RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/components/VRMScene.tsx RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/emote.ts RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/hooks/usePassThrough.ts RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/index.html RENAMED
@@ -96,7 +96,7 @@
96
 
97
  <body>
98
  <div id="root"></div>
99
- <script type="module" src="/src/main.tsx"></script>
100
  </body>
101
 
102
  </html>
 
96
 
97
  <body>
98
  <div id="root"></div>
99
+ <script type="module" src="./main.tsx"></script>
100
  </body>
101
 
102
  </html>
{friend/frontend/src β†’ src/components/friend/frontend}/lip-sync.ts RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/main.tsx RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/mixamo-loader.ts RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/mmd-parser.d.ts RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/motion-controller.ts RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/package-lock.json RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/package.json RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/akimbo.vrma RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/angry.fbx RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/angryPump.fbx RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/excited.fbx RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/greeting.fbx RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/happy.fbx RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/idle_loop.vrma RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/jile.vmd RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/love.vmd RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/playFingers.vrma RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/point.fbx RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/salute.fbx RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/scratchHead.vrma RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/shy.fbx RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/public/stretch.vrma RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/src-tauri/Cargo.lock RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/src-tauri/Cargo.toml RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/src-tauri/build.rs RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/src-tauri/capabilities/default.json RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/src-tauri/icons/128x128.png RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/src-tauri/icons/256x256.png RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/src-tauri/icons/32x32.png RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/src-tauri/src/lib.rs RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/src-tauri/src/main.rs RENAMED
File without changes
{friend β†’ src/components/friend}/frontend/src-tauri/tauri.conf.json RENAMED
File without changes
{friend/frontend/src β†’ src/components/friend/frontend}/three-helpers.ts RENAMED
File without changes