File size: 1,527 Bytes
e1bb50d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import type { VRMSceneHandle } from './components/VRMScene'

/** VersperClaw server base URL */
export const SERVER_URL = 'http://127.0.0.1:3456'

/** Base path for the Friend VRM plugin API */
export const FRIEND_API = `${SERVER_URL}/plugins/friend`

let sceneHandle: VRMSceneHandle | null = null

export function bindScene(handle: VRMSceneHandle | null) {
  sceneHandle = handle
}

/**
 * POST to a server API. If the request body contains an `emotion` field,
 * the VRM expression will automatically change to match.
 *
 * Body example:
 *   { emotion: "happy", message: "hello", ... }
 *
 * Supported emotions: happy, sad, angry, surprised, think, neutral
 *
 * Optional fields:
 *   emotionDuration — ms, auto-reset to neutral after this time
 *   emotionIntensity — 0~1, defaults to 1
 */
export async function postApi<T = any>(url: string, body: Record<string, any>): Promise<T> {
  // Trigger emotion from the request body
  if (body.emotion && sceneHandle) {
    const duration = body.emotionDuration as number | undefined
    const intensity = body.emotionIntensity as number | undefined
    if (duration) {
      sceneHandle.setEmotionWithReset(body.emotion, duration, intensity)
    } else {
      sceneHandle.setEmotion(body.emotion, intensity)
    }
  }

  const res = await fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  })

  if (!res.ok) {
    throw new Error(`API error: ${res.status} ${res.statusText}`)
  }

  return res.json()
}