l'avatar ne parle plus ?


claude.ai Gmail
claude.ai Google Calendar
claude.ai Google Drive
claude.ai Indeed
claude.ai Slack


claude Explore general-purpose Plan statusline-setup

- claude: Catch-all for any task that doesn't fit a more specific agent. FleetView's default when no agent name is typed. (Tools: *)

- Explore: Read-only search agent for broad fan-out searches — when answering means sweeping many files, directories, or naming conventions and you only need the conclusion, not the file dumps. It reads excerpts rather than whole files, so it locates code; it doesn't review or audit it. Specify search breadth: "medium" for moderate exploration, "very thorough" for multiple locations and naming conventions. (Tools: All tools except Agent, ExitPlanMode, Edit, Write, NotebookEdit)

- general-purpose: General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you. (Tools: *)

- Plan: Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs. (Tools: All tools except Agent, ExitPlanMode, Edit, Write, NotebookEdit)

- statusline-setup: Use this agent to configure the user's Claude Code status line setting. (Tools: Read, Edit)


- deep-research: Deep research harness — fan-out web searches, fetch sources, adversarially verify claims, synthesize a cited report. - When the user wants a deep, multi-source, fact-checked research report on any topic. BEFORE invoking, check if the question is specific enough to research directly — if underspecified (e.g., "what car to buy" without budget/use-case/region), ask 2-3 clarifying questions to narrow scope. Then pass the refined question as args, weaving the answers in.
- update-config: Use this skill to configure the Claude Code harness via settings.json. Automated behaviors ("from now on when X", "each time X", "whenever X", "before/after X") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions ("allow X", "add permission", "move permission to"), env vars ("set X=Y"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: "allow npm commands", "add bq permission to global settings", "move permission to user settings", "set DEBUG=true", "when claude stops show X". For simple settings like theme/model, suggest the /config command.
- keybindings-help: Use when the user wants to customize keyboard shortcuts, rebind keys, add chord bindings, or modify ~/.claude/keybindings.json. Examples: "rebind ctrl+s", "add a chord shortcut", "change the submit key", "customize keybindings".
- verify: Verify that a code change actually does what it's supposed to by running the app and observing behavior. Use when asked to verify a PR, confirm a fix works, test a change manually, check that a feature works, or validate local changes before pushing.
- code-review: Review the current diff for correctness bugs and reuse/simplification/efficiency cleanups at the given effort level (low/medium: fewer, high-confidence findings; high→max: broader coverage, may include uncertain findings; ultra: deep multi-agent review in the cloud). Pass --comment to post findings as inline PR comments, or --fix to apply the findings to the working tree after the review.
- simplify: Review the changed code for reuse, simplification, efficiency, and altitude cleanups, then apply the fixes. Quality only — it does not hunt for bugs; use /code-review for that.
- fewer-permission-prompts: Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.
- loop: Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace. - When the user wants to set up a recurring task, poll for status, or run something repeatedly on an interval (e.g. "check the deploy every 5 minutes", "keep running /babysit-prs"). Do NOT invoke for one-off tasks.
- schedule: Create, update, list, or run scheduled cloud agents (routines) that execute on a cron schedule. - When the user wants to schedule a recurring cloud agent, set up automated tasks, create a cron job for Claude Code, or manage their scheduled agents/routines. Also use when the user wants a one-time scheduled run ("run this once at 3pm", "remind me to check X tomorrow").
- claude-api: Reference for the Claude API / Anthropic SDK — model ids, pricing, params, streaming, tool use, MCP, agents, caching, token counting, model migration.
TRIGGER — read BEFORE opening the target file; don't skip because it "looks like a one-liner" — whenever: the prompt names Claude/Anthropic in any form (Claude, Anthropic, Fable, Opus, Sonnet, Haiku, `anthropic`, `@anthropic-ai`, `claude-*`, `us.anthropic.*`, `[1m]`); the user asks about an LLM (pricing/model choice/limits/caching) — never answer from memory; OR the task is LLM-shaped with provider unstated (agent/MCP/tool-definition/multi-agent/RAG/LLM-judge/computer-use; generate/summarize/extract/classify/rewrite/converse over NL; debugging refusals/cutoffs/streaming/tool-calls/tokens).
SKIP only when another provider is being worked on (overrides all triggers): OpenAI/GPT/Gemini/Llama/Mistral/Cohere/Ollama named in the query; OR `grep -rE 'openai|langchain_openai|google.generativeai|genai|mistralai|cohere|ollama'` over the project hits (run this grep FIRST if no provider named — don't Read the file).
- run: Launch and drive this project's app to see a change working. Use when asked to run, start, or screenshot the app, or to confirm a change works in the real app (not just tests). First looks for a project skill that already covers launching the app; otherwise falls back to built-in patterns per project type (CLI, server, TUI, Electron, browser-driven, library).
- init: Initialize a new CLAUDE.md file with codebase documentation
- review: Review a pull request
- security-review: Complete a security review of the pending changes on the current branch

deep-research update-config keybindings-help verify code-review simplify fewer-permission-prompts loop schedule claude-api run init review security-review


Je regarde le projet pour comprendre ce qui concerne l'avatar et la parole.


Bash

ls -la && echo "---" && cat README* 2>/dev/null | head -50

List project files and read README


total 24
drwxrwxr-x+         1 wildlama wildlama 4096 Jun 15 21:45 .
drwxrwxr-x+         1 wildlama wildlama    0 Jun 15 23:25 ..
-rw-------+         1 wildlama wildlama 6462 Jun 15 21:45 PLAN.md
-rw-------+         1 wildlama wildlama 2485 Jun 15 21:45 README.md
drwxrwxr-x+         1 wildlama wildlama 4096 Jun 16 08:10 agent_gui
drwxrwxr-x+         1 wildlama wildlama    0 Jun 15 21:35 configs
drwxrwxr-x+         1 wildlama wildlama    0 Jun 15 23:44 data
drwxrwxr-x+         1 wildlama wildlama 4096 Jun 16 08:05 static
drwxrwxr-x+         1 wildlama wildlama    0 Jun 15 21:24 tests
---
# Agent GUI Local

Web app locale qui sert de surcouche graphique universelle pour agents LLM CLI.

V1 incluse:

- Backend Python sans dépendance externe.
- Frontend HTML/CSS/JS vanilla.
- Chat local.
- Sessions JSONL.
- Adaptateur Echo pour tester sans modèle.
- Adaptateur CLI universel pour brancher Hermes, Claude Code, OpenClaw, etc.
- Bureau pixel art animé avec petits personnages façon arcade.
- Avatar de bureau en mode sprite, avec états: idle, thinking, tool, speaking, error.

## Lancer

```bash
cd /home/wildlama/agent-gui
python3 -m agent_gui.app --host 127.0.0.1 --port 8765
```

Puis ouvrir:

```text
http://127.0.0.1:8765
```

## Tester

```bash
cd /home/wildlama/agent-gui
python3 -m unittest discover -s tests -v
```

Smoke-test API:

```bash
curl http://127.0.0.1:8765/health
curl -X POST http://127.0.0.1:8765/api/chat \
  -H 'Content-Type: application/json' \
  -d '{"agent":"echo","message":"hello"}'
```

## Ajouter un agent

Éditer:

```text
/home/wildlama/agent-gui/configs/agents.json


find static agent_gui -type f | head -50 && echo "---STATIC---" && ls -la static

List static and agent_gui files


static/app.js
static/avatar.js
static/index.html
static/lib_lam_renderer.js
static/styles.css
agent_gui/adapters.py
agent_gui/app.py
agent_gui/avatar.py
agent_gui/avatar_lam.py
agent_gui/config.py
agent_gui/sessions.py
agent_gui/tts.py
agent_gui/__init__.py
static/avatar/barbara.zip
agent_gui/__pycache__/adapters.cpython-313.pyc
agent_gui/__pycache__/adapters.cpython-314.pyc
agent_gui/__pycache__/app.cpython-313.pyc
agent_gui/__pycache__/app.cpython-314.pyc
agent_gui/__pycache__/avatar.cpython-313.pyc
agent_gui/__pycache__/avatar.cpython-314.pyc
agent_gui/__pycache__/avatar_lam.cpython-313.pyc
agent_gui/__pycache__/config.cpython-313.pyc
agent_gui/__pycache__/config.cpython-314.pyc
agent_gui/__pycache__/sessions.cpython-313.pyc
agent_gui/__pycache__/sessions.cpython-314.pyc
agent_gui/__pycache__/tts.cpython-313.pyc
agent_gui/__pycache__/__init__.cpython-313.pyc
agent_gui/__pycache__/__init__.cpython-314.pyc
static/avatar/barbara/skin.glb
---STATIC---
total 4032
drwxrwxr-x+  1 wildlama wildlama    4096 Jun 16 08:05 .
drwxrwxr-x+  1 wildlama wildlama    4096 Jun 15 21:45 ..
-rw-------+  1 wildlama wildlama   28059 Jun 16 08:00 app.js
drwxrwxr-x+  1 wildlama wildlama       0 Jun 16 08:04 avatar
-rw-rw-r--+  1 wildlama wildlama    2596 Jun 16 08:04 avatar.js
-rw-------+  1 wildlama wildlama    3154 Jun 16 08:05 index.html
-rw-rw-r--+  1 wildlama wildlama 4067930 Jun 16 08:02 lib_lam_renderer.js
-rw-------+  1 wildlama wildlama    8558 Jun 16 08:05 styles.css


1	import { GaussianSplatRenderer } from '/lib_lam_renderer.js';
2
3	const AVATAR_ZIP = '/avatar/barbara.zip';
4	const FPS = 30;
5
6	let rendererInstance = null;
7	let activeBsData = null;
8	let playStartTime = 0;
9	let audioCtx = null, audioBuffer = null, audioSource = null;
10
11	function setStatus(text) {
12	  const el = document.getElementById('avatarStatus');
13	  if (el) el.textContent = text;
14	}
15
16	// Called by the renderer every frame. Returns {blendshapeName: weight} or null (resting pose).
17	function getExpressionData() {
18	  if (!activeBsData) return null;
19	  const total = activeBsData.frames.length;
20	  const elapsed = performance.now() / 1000 - playStartTime;
21	  const frameIdx = Math.floor(elapsed * FPS);
22	  if (frameIdx >= total) {
23	    activeBsData = null;
24	    setStatus('Prêt');
25	    return null;
26	  }
27	  const weights = activeBsData.frames[frameIdx].weights;
28	  const out = {};
29	  activeBsData.names.forEach((name, i) => {
30	    out[name] = weights[i];
31	  });
32	  return out;
33	}
34
35	async function init() {
36	  const container = document.getElementById('avatarRoot');
37	  if (!container) return;
38	  try {
39	    setStatus('Chargement avatar...');
40	    rendererInstance = await GaussianSplatRenderer.getInstance(container, AVATAR_ZIP, {
41	      getExpressionData,
42	      backgroundColor: '0x05080f',
43	      loadProgress: (p) => setStatus(`Chargement ${Math.round(p * 100)}%`),
44	      downloadProgress: (p) => setStatus(`Téléchargement ${Math.round(p * 100)}%`),
45	    });
46	    setStatus('Prêt');
47	  } catch (err) {
48	    console.error('Avatar init failed:', err);
49	    setStatus('Erreur chargement avatar');
50	  }
51	}
52
53	async function loadAudio(url) {
54	  if (!audioCtx) audioCtx = new AudioContext();
55	  const res = await fetch(url);
56	  const buf = await res.arrayBuffer();
57	  audioBuffer = await audioCtx.decodeAudioData(buf);
58	}
59
60	function playAudio() {
61	  if (!audioCtx || !audioBuffer) return;
62	  if (audioSource) { try { audioSource.stop(); } catch (_) {} }
63	  audioSource = audioCtx.createBufferSource();
64	  audioSource.buffer = audioBuffer;
65	  audioSource.connect(audioCtx.destination);
66	  audioSource.start(0);
67	}
68
69	// Start blendshape playback (synced with audio start)
70	function startAnimation(bsData) {
71	  activeBsData = bsData;
72	  playStartTime = performance.now() / 1000;
73	  setStatus('Parle...');
74	}
75
76	async function speakWithAnimation(sessionId) {
77	  setStatus('Chargement audio...');
78	  try {
79	    await loadAudio(`/api/avatar/audio?session_id=${encodeURIComponent(sessionId)}`);
80	    playAudio();
81	  } catch (e) {
82	    console.warn('Audio load failed:', e);
83	  }
84	}
85
86	window.avatarController = { init, startAnimation, speakWithAnimation, setStatus };
87	init();
88

import { GaussianSplatRenderer } from '/lib_lam_renderer.js';

const AVATAR_ZIP = '/avatar/barbara.zip';
const FPS = 30;

let rendererInstance = null;
let activeBsData = null;
let playStartTime = 0;
let audioCtx = null, audioBuffer = null, audioSource = null;

function setStatus(text) {
  const el = document.getElementById('avatarStatus');
  if (el) el.textContent = text;
}

// Called by the renderer every frame. Returns {blendshapeName: weight} or null (resting pose).
function getExpressionData() {
  if (!activeBsData) return null;
  const total = activeBsData.frames.length;
  const elapsed = performance.now() / 1000 - playStartTime;
  const frameIdx = Math.floor(elapsed * FPS);
  if (frameIdx >= total) {
    activeBsData = null;
    setStatus('Prêt');
    return null;
  }
  const weights = activeBsData.frames[frameIdx].weights;
  const out = {};
  activeBsData.names.forEach((name, i) => {
    out[name] = weights[i];
  });
  return out;
}

async function init() {
  const container = document.getElementById('avatarRoot');
  if (!container) return;
  try {
    setStatus('Chargement avatar...');
    rendererInstance = await GaussianSplatRenderer.getInstance(container, AVATAR_ZIP, {
      getExpressionData,
      backgroundColor: '0x05080f',
      loadProgress: (p) => setStatus(`Chargement ${Math.round(p * 100)}%`),
      downloadProgress: (p) => setStatus(`Téléchargement ${Math.round(p * 100)}%`),
    });
    setStatus('Prêt');
  } catch (err) {
    console.error('Avatar init failed:', err);
    setStatus('Erreur chargement avatar');
  }
}

async function loadAudio(url) {
  if (!audioCtx) audioCtx = new AudioContext();
  const res = await fetch(url);
  const buf = await res.arrayBuffer();
  audioBuffer = await audioCtx.decodeAudioData(buf);
}

function playAudio() {
  if (!audioCtx || !audioBuffer) return;
  if (audioSource) { try { audioSource.stop(); } catch (_) {} }
  audioSource = audioCtx.createBufferSource();
  audioSource.buffer = audioBuffer;
  audioSource.connect(audioCtx.destination);
  audioSource.start(0);
}

// Start blendshape playback (synced with audio start)
function startAnimation(bsData) {
  activeBsData = bsData;
  playStartTime = performance.now() / 1000;
  setStatus('Parle...');
}

async function speakWithAnimation(sessionId) {
  setStatus('Chargement audio...');
  try {
    await loadAudio(`/api/avatar/audio?session_id=${encodeURIComponent(sessionId)}`);
    playAudio();
  } catch (e) {
    console.warn('Audio load failed:', e);
  }
}

window.avatarController = { init, startAnimation, speakWithAnimation, setStatus };
init();


1	from __future__ import annotations
2
3	import subprocess
4	from pathlib import Path
5
6	_EDGE_TTS = Path.home() / "miniconda3/bin/edge-tts"
7
8	# Voix par langue détectée (code ISO 639-1 -> voix edge-tts)
9	_VOICE_BY_LANG = {
10	    "fr": "fr-FR-DeniseNeural",
11	    "en": "en-US-AriaNeural",
12	    "es": "es-ES-ElviraNeural",
13	    "de": "de-DE-KatjaNeural",
14	    "it": "it-IT-ElsaNeural",
15	    "pt": "pt-BR-FranciscaNeural",
16	    "nl": "nl-NL-ColetteNeural",
17	    "ja": "ja-JP-NanamiNeural",
18	    "zh-cn": "zh-CN-XiaoxiaoNeural",
19	    "ko": "ko-KR-SunHiNeural",
20	    "ru": "ru-RU-SvetlanaNeural",
21	    "ar": "ar-SA-ZariyahNeural",
22	}
23	_DEFAULT_VOICE = "fr-FR-DeniseNeural"
24
25
26	def detect_voice(text: str) -> str:
27	    try:
28	        from langdetect import detect
29
30	        lang = detect(text).lower()
31	    except Exception:
32	        return _DEFAULT_VOICE
33	    if lang in _VOICE_BY_LANG:
34	        return _VOICE_BY_LANG[lang]
35	    # langdetect renvoie parfois "zh-tw" etc. — réduire au préfixe
36	    return _VOICE_BY_LANG.get(lang.split("-")[0], _DEFAULT_VOICE)
37
38
39	def synthesize(text: str, wav_path: Path, voice: str | None = None) -> None:
40	    voice = voice or detect_voice(text)
41	    mp3_path = wav_path.with_suffix(".mp3")
42	    subprocess.run(
43	        [str(_EDGE_TTS), "--text", text, "--voice", voice, "--write-media", str(mp3_path)],
44	        check=True,
45	        timeout=60,
46	        capture_output=True,
47	    )
48	    subprocess.run(
49	        ["ffmpeg", "-y", "-i", str(mp3_path), "-ar", "16000", "-ac", "1", str(wav_path)],
50	        check=True,
51	        capture_output=True,
52	    )
53	    mp3_path.unlink(missing_ok=True)
54

from __future__ import annotations

import subprocess
from pathlib import Path

_EDGE_TTS = Path.home() / "miniconda3/bin/edge-tts"

# Voix par langue détectée (code ISO 639-1 -> voix edge-tts)
_VOICE_BY_LANG = {
    "fr": "fr-FR-DeniseNeural",
    "en": "en-US-AriaNeural",
    "es": "es-ES-ElviraNeural",
    "de": "de-DE-KatjaNeural",
    "it": "it-IT-ElsaNeural",
    "pt": "pt-BR-FranciscaNeural",
    "nl": "nl-NL-ColetteNeural",
    "ja": "ja-JP-NanamiNeural",
    "zh-cn": "zh-CN-XiaoxiaoNeural",
    "ko": "ko-KR-SunHiNeural",
    "ru": "ru-RU-SvetlanaNeural",
    "ar": "ar-SA-ZariyahNeural",
}
_DEFAULT_VOICE = "fr-FR-DeniseNeural"

def detect_voice(text: str) -> str:
    try:
        from langdetect import detect

        lang = detect(text).lower()
    except Exception:
        return _DEFAULT_VOICE
    if lang in _VOICE_BY_LANG:
        return _VOICE_BY_LANG[lang]
    # langdetect renvoie parfois "zh-tw" etc. — réduire au préfixe
    return _VOICE_BY_LANG.get(lang.split("-")[0], _DEFAULT_VOICE)

def synthesize(text: str, wav_path: Path, voice: str | None = None) -> None:
    voice = voice or detect_voice(text)
    mp3_path = wav_path.with_suffix(".mp3")
    subprocess.run(
        [str(_EDGE_TTS), "--text", text, "--voice", voice, "--write-media", str(mp3_path)],
        check=True,
        timeout=60,
        capture_output=True,
    )
    subprocess.run(
        ["ffmpeg", "-y", "-i", str(mp3_path), "-ar", "16000", "-ac", "1", str(wav_path)],
        check=True,
        capture_output=True,
    )
    mp3_path.unlink(missing_ok=True)


grep -n "avatar\|speak\|startAnimation\|tts\|blend\|/api/avatar\|bsData\|expression" static/app.js agent_gui/app.py agent_gui/avatar_lam.py

Find avatar/speak wiring across files


static/app.js:271:    setCrewMood(event.agent, 'speaking', truncateBubble(event.content), 2400);
static/app.js:358:      const data = await api(`/api/avatar/anim?session_id=${encodeURIComponent(sessionId)}`);
static/app.js:362:      if (data.status === 'ready' && window.avatarController) {
static/app.js:363:        await window.avatarController.speakWithAnimation(sessionId);
static/app.js:364:        window.avatarController.startAnimation(data.bsData);
static/app.js:368:        if (window.avatarController) window.avatarController.setStatus('Erreur TTS/A2E');
static/app.js:394:    $('officeCaption').textContent = data.avatar?.state === 'speaking'
static/app.js:397:    setStatus(data.avatar?.state || 'speaking');
static/app.js:398:    if (data.avatar_anim === 'pending') {
static/app.js:399:      if (window.avatarController) window.avatarController.setStatus('Génération avatar...');
static/app.js:663:  if (pose === 'speaking') {
static/app.js:744:    if (member.mood === 'speaking') {
static/app.js:796:      if (member.mood === 'speaking' && now - member.lastTalkAt > 2400) {
agent_gui/app.py:12:from .avatar import available_states, get_avatar
agent_gui/app.py:20:TTS_DIR = PROJECT_ROOT / "data" / "tts"
agent_gui/app.py:28:def _start_avatar_pipeline(session_id: str, text: str) -> None:
agent_gui/app.py:34:            from .tts import synthesize
agent_gui/app.py:35:            from .avatar_lam import audio_to_blendshapes
agent_gui/app.py:39:            bs_data = audio_to_blendshapes(wav_path)
agent_gui/app.py:72:        if parsed.path == "/api/avatar":
agent_gui/app.py:74:            return self.json_response({"avatar": get_avatar(state), "states": available_states()})
agent_gui/app.py:80:        if parsed.path == "/api/avatar/anim":
agent_gui/app.py:88:            return self.json_response({"status": "ready", "bsData": val})
agent_gui/app.py:89:        if parsed.path == "/api/avatar/audio":
agent_gui/app.py:131:                _start_avatar_pipeline(session_id, assistant_text)
agent_gui/app.py:138:                    "avatar": get_avatar(result.avatar_state),
agent_gui/app.py:139:                    "avatar_anim": "pending" if assistant_text else None,
agent_gui/app.py:143:            return self.json_response({"error": str(exc), "avatar": get_avatar("error")}, status=404)
agent_gui/app.py:145:            return self.json_response({"error": str(exc), "avatar": get_avatar("error")}, status=500)
agent_gui/avatar_lam.py:13:def audio_to_blendshapes(audio_path: Path) -> dict:


1	from __future__ import annotations
2
3	import argparse
4	import json
5	import mimetypes
6	import threading
7	from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
8	from pathlib import Path
9	from urllib.parse import parse_qs, urlparse
10
11	from .adapters import adapter_for
12	from .avatar import available_states, get_avatar
13	from .config import find_agent, load_agents
14	from .sessions import SessionStore, new_session_id, utc_now
15
16	PROJECT_ROOT = Path(__file__).resolve().parent.parent
17	STATIC_ROOT = PROJECT_ROOT / "static"
18	DEFAULT_CONFIG = PROJECT_ROOT / "configs" / "agents.json"
19	DEFAULT_SESSIONS = PROJECT_ROOT / "data" / "sessions"
20	TTS_DIR = PROJECT_ROOT / "data" / "tts"
21	TTS_DIR.mkdir(parents=True, exist_ok=True)
22
23	# keyed by session_id: None=pending, dict=ready, str=error message
24	_anim_store: dict[str, None | dict | str] = {}
25	_anim_lock = threading.Lock()
26
27
28	def _start_avatar_pipeline(session_id: str, text: str) -> None:
29	    with _anim_lock:
30	        _anim_store[session_id] = None  # pending
31
32	    def _run() -> None:
33	        try:
34	            from .tts import synthesize
35	            from .avatar_lam import audio_to_blendshapes
36
37	            wav_path = TTS_DIR / f"{session_id}.wav"
38	            synthesize(text, wav_path)
39	            bs_data = audio_to_blendshapes(wav_path)
40	            with _anim_lock:
41	                _anim_store[session_id] = bs_data
42	        except Exception as exc:
43	            with _anim_lock:
44	                _anim_store[session_id] = str(exc)
45
46	    threading.Thread(target=_run, daemon=True).start()
47
48
49	class AgentGuiServer(ThreadingHTTPServer):
50	    def __init__(self, server_address, RequestHandlerClass, config_path: Path, sessions_root: Path):
51	        super().__init__(server_address, RequestHandlerClass)
52	        self.config_path = config_path
53	        self.sessions = SessionStore(sessions_root)
54
55	    @property
56	    def agents(self):
57	        return load_agents(self.config_path)
58
59
60	class Handler(BaseHTTPRequestHandler):
61	    server: AgentGuiServer
62
63	    def log_message(self, format: str, *args):
64	        print("[agent-gui] " + format % args)
65
66	    def do_GET(self):
67	        parsed = urlparse(self.path)
68	        if parsed.path == "/health":
69	            return self.json_response({"ok": True, "service": "agent-gui", "time": utc_now()})
70	        if parsed.path == "/api/agents":
71	            return self.json_response({"agents": self.server.agents})
72	        if parsed.path == "/api/avatar":
73	            state = parse_qs(parsed.query).get("state", ["idle"])[0]
74	            return self.json_response({"avatar": get_avatar(state), "states": available_states()})
75	        if parsed.path == "/api/sessions":
76	            return self.json_response({"sessions": self.server.sessions.list_sessions()})
77	        if parsed.path == "/api/history":
78	            session_id = parse_qs(parsed.query).get("session_id", [""])[0]
79	            return self.json_response({"events": self.server.sessions.read(session_id)})
80	        if parsed.path == "/api/avatar/anim":
81	            session_id = parse_qs(parsed.query).get("session_id", [""])[0]
82	            with _anim_lock:
83	                val = _anim_store.get(session_id)
84	            if val is None:
85	                return self.json_response({"status": "pending"})
86	            if isinstance(val, str):
87	                return self.json_response({"status": "error", "message": val})
88	            return self.json_response({"status": "ready", "bsData": val})
89	        if parsed.path == "/api/avatar/audio":
90	            session_id = parse_qs(parsed.query).get("session_id", [""])[0]
91	            wav = TTS_DIR / f"{session_id}.wav"
92	            if not wav.exists():
93	                return self.json_response({"error": "audio not found"}, status=404)
94	            body = wav.read_bytes()
95	            self.send_response(200)
96	            self.send_header("Content-Type", "audio/wav")
97	            self.send_header("Content-Length", str(len(body)))
98	            self.end_headers()
99	            self.wfile.write(body)
100	            return

from __future__ import annotations

import argparse
import json
import mimetypes
import threading
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
from urllib.parse import parse_qs, urlparse

from .adapters import adapter_for
from .avatar import available_states, get_avatar
from .config import find_agent, load_agents
from .sessions import SessionStore, new_session_id, utc_now

PROJECT_ROOT = Path(__file__).resolve().parent.parent
STATIC_ROOT = PROJECT_ROOT / "static"
DEFAULT_CONFIG = PROJECT_ROOT / "configs" / "agents.json"
DEFAULT_SESSIONS = PROJECT_ROOT / "data" / "sessions"
TTS_DIR = PROJECT_ROOT / "data" / "tts"
TTS_DIR.mkdir(parents=True, exist_ok=True)

# keyed by session_id: None=pending, dict=ready, str=error message
_anim_store: dict[str, None | dict | str] = {}
_anim_lock = threading.Lock()

def _start_avatar_pipeline(session_id: str, text: str) -> None:
    with _anim_lock:
        _anim_store[session_id] = None  # pending

    def _run() -> None:
        try:
            from .tts import synthesize
            from .avatar_lam import audio_to_blendshapes

            wav_path = TTS_DIR / f"{session_id}.wav"
            synthesize(text, wav_path)
            bs_data = audio_to_blendshapes(wav_path)
            with _anim_lock:
                _anim_store[session_id] = bs_data
        except Exception as exc:
            with _anim_lock:
                _anim_store[session_id] = str(exc)

    threading.Thread(target=_run, daemon=True).start()

class AgentGuiServer(ThreadingHTTPServer):
    def __init__(self, server_address, RequestHandlerClass, config_path: Path, sessions_root: Path):
        super().__init__(server_address, RequestHandlerClass)
        self.config_path = config_path
        self.sessions = SessionStore(sessions_root)

    @property
    def agents(self):
        return load_agents(self.config_path)

class Handler(BaseHTTPRequestHandler):
    server: AgentGuiServer

    def log_message(self, format: str, *args):
        print("[agent-gui] " + format % args)

    def do_GET(self):
        parsed = urlparse(self.path)
        if parsed.path == "/health":
            return self.json_response({"ok": True, "service": "agent-gui", "time": utc_now()})
        if parsed.path == "/api/agents":
            return self.json_response({"agents": self.server.agents})
        if parsed.path == "/api/avatar":
            state = parse_qs(parsed.query).get("state", ["idle"])[0]
            return self.json_response({"avatar": get_avatar(state), "states": available_states()})
        if parsed.path == "/api/sessions":
            return self.json_response({"sessions": self.server.sessions.list_sessions()})
        if parsed.path == "/api/history":
            session_id = parse_qs(parsed.query).get("session_id", [""])[0]
            return self.json_response({"events": self.server.sessions.read(session_id)})
        if parsed.path == "/api/avatar/anim":
            session_id = parse_qs(parsed.query).get("session_id", [""])[0]
            with _anim_lock:
                val = _anim_store.get(session_id)
            if val is None:
                return self.json_response({"status": "pending"})
            if isinstance(val, str):
                return self.json_response({"status": "error", "message": val})
            return self.json_response({"status": "ready", "bsData": val})
        if parsed.path == "/api/avatar/audio":
            session_id = parse_qs(parsed.query).get("session_id", [""])[0]
            wav = TTS_DIR / f"{session_id}.wav"
            if not wav.exists():
                return self.json_response({"error": "audio not found"}, status=404)
            body = wav.read_bytes()
            self.send_response(200)
            self.send_header("Content-Type", "audio/wav")
            self.send_header("Content-Length", str(len(body)))
            self.end_headers()
            self.wfile.write(body)
            return


1	from __future__ import annotations
2
3	import json
4	import subprocess
5	import tempfile
6	from pathlib import Path
7
8	_LAM_DIR = Path("/mnt/DATA/WORK/LAM_Audio2Expression")
9	_CONDA = Path.home() / "miniconda3/bin/conda"
10	_SCRIPT = _LAM_DIR / "run_inference.py"
11
12
13	def audio_to_blendshapes(audio_path: Path) -> dict:
14	    with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as fh:
15	        out = Path(fh.name)
16	    try:
17	        subprocess.run(
18	            [
19	                str(_CONDA), "run", "--no-capture-output", "-n", "lam_a2e",
20	                "python", str(_SCRIPT),
21	                "--audio", str(audio_path),
22	                "--output", str(out),
23	            ],
24	            check=True,
25	            cwd=str(_LAM_DIR),
26	            timeout=300,
27	        )
28	        return json.loads(out.read_text())
29	    finally:
30	        out.unlink(missing_ok=True)
31

from __future__ import annotations

import json
import subprocess
import tempfile
from pathlib import Path

_LAM_DIR = Path("/mnt/DATA/WORK/LAM_Audio2Expression")
_CONDA = Path.home() / "miniconda3/bin/conda"
_SCRIPT = _LAM_DIR / "run_inference.py"

def audio_to_blendshapes(audio_path: Path) -> dict:
    with tempfile.NamedTemporaryFile(suffix=".json", delete=False) as fh:
        out = Path(fh.name)
    try:
        subprocess.run(
            [
                str(_CONDA), "run", "--no-capture-output", "-n", "lam_a2e",
                "python", str(_SCRIPT),
                "--audio", str(audio_path),
                "--output", str(out),
            ],
            check=True,
            cwd=str(_LAM_DIR),
            timeout=300,
        )
        return json.loads(out.read_text())
    finally:
        out.unlink(missing_ok=True)


345	  $('sessionLabel').textContent = `Session ${sessionId}`;
346	  $('chatLog').innerHTML = '';
347	  $('timeline').innerHTML = '';
348	  const data = await api(`/api/history?session_id=${encodeURIComponent(sessionId)}`);
349	  data.events.forEach(renderEvent);
350	}
351
352	let _animPollTimer = null;
353
354	function pollAvatarAnim(sessionId) {
355	  if (_animPollTimer) clearInterval(_animPollTimer);
356	  _animPollTimer = setInterval(async () => {
357	    try {
358	      const data = await api(`/api/avatar/anim?session_id=${encodeURIComponent(sessionId)}`);
359	      if (data.status === 'pending') return;
360	      clearInterval(_animPollTimer);
361	      _animPollTimer = null;
362	      if (data.status === 'ready' && window.avatarController) {
363	        await window.avatarController.speakWithAnimation(sessionId);
364	        window.avatarController.startAnimation(data.bsData);
365	      }
366	      if (data.status === 'error') {
367	        console.warn('Avatar anim error:', data.message);
368	        if (window.avatarController) window.avatarController.setStatus('Erreur TTS/A2E');
369	      }
370	    } catch (_) {}
371	  }, 800);
372	}
373
374	async function sendMessage(event) {
375	  event.preventDefault();
376	  const input = $('messageInput');
377	  const message = input.value.trim();
378	  if (!message) return;
379	  const agent = $('agentSelect').value;
380	  const sessionId = await ensureSession();
381
382	  input.value = '';
383	  renderEvent({ type: 'user_message', content: message, agent });
384	  setStatus('thinking');
385	  setOfficeCaption(`${agent} est en train de réfléchir...`);
386	  setCrewMood(agent, 'thinking', '', 0);
387
388	  try {
389	    const data = await api('/api/chat', {
390	      method: 'POST',
391	      body: JSON.stringify({ agent, message, session_id: sessionId }),
392	    });
393	    data.events.forEach(renderEvent);
394	    $('officeCaption').textContent = data.avatar?.state === 'speaking'
395	      ? `${agent} est retourné à son bureau.`
396	      : state.sceneMessage;
397	    setStatus(data.avatar?.state || 'speaking');
398	    if (data.avatar_anim === 'pending') {
399	      if (window.avatarController) window.avatarController.setStatus('Génération avatar...');
400	      pollAvatarAnim(sessionId);
401	    }
402	    await loadSessions();
403	  } catch (error) {
404	    addMessage('error', error.message, 'erreur');

  $('sessionLabel').textContent = `Session ${sessionId}`;
  $('chatLog').innerHTML = '';
  $('timeline').innerHTML = '';
  const data = await api(`/api/history?session_id=${encodeURIComponent(sessionId)}`);
  data.events.forEach(renderEvent);
}

let _animPollTimer = null;

function pollAvatarAnim(sessionId) {
  if (_animPollTimer) clearInterval(_animPollTimer);
  _animPollTimer = setInterval(async () => {
    try {
      const data = await api(`/api/avatar/anim?session_id=${encodeURIComponent(sessionId)}`);
      if (data.status === 'pending') return;
      clearInterval(_animPollTimer);
      _animPollTimer = null;
      if (data.status === 'ready' && window.avatarController) {
        await window.avatarController.speakWithAnimation(sessionId);
        window.avatarController.startAnimation(data.bsData);
      }
      if (data.status === 'error') {
        console.warn('Avatar anim error:', data.message);
        if (window.avatarController) window.avatarController.setStatus('Erreur TTS/A2E');
      }
    } catch (_) {}
  }, 800);
}

async function sendMessage(event) {
  event.preventDefault();
  const input = $('messageInput');
  const message = input.value.trim();
  if (!message) return;
  const agent = $('agentSelect').value;
  const sessionId = await ensureSession();

  input.value = '';
  renderEvent({ type: 'user_message', content: message, agent });
  setStatus('thinking');
  setOfficeCaption(`${agent} est en train de réfléchir...`);
  setCrewMood(agent, 'thinking', '', 0);

  try {
    const data = await api('/api/chat', {
      method: 'POST',
      body: JSON.stringify({ agent, message, session_id: sessionId }),
    });
    data.events.forEach(renderEvent);
    $('officeCaption').textContent = data.avatar?.state === 'speaking'
      ? `${agent} est retourné à son bureau.`
      : state.sceneMessage;
    setStatus(data.avatar?.state || 'speaking');
    if (data.avatar_anim === 'pending') {
      if (window.avatarController) window.avatarController.setStatus('Génération avatar...');
      pollAvatarAnim(sessionId);
    }
    await loadSessions();
  } catch (error) {
    addMessage('error', error.message, 'erreur');


cd /mnt/DATA/WORK/agent-gui
echo "=== mtimes ==="
ls -la --time-style=+%H:%M agent_gui/*.py static/*.js static/*.html
echo "=== data/tts ==="
ls -la data/tts 2>/dev/null || echo "(no tts dir yet)"
ls -la data/sessions 2>/dev/null | tail -5
echo "=== deps ==="
ls -la ~/miniconda3/bin/edge-tts 2>/dev/null || echo "NO edge-tts"
~/miniconda3/bin/conda env list 2>/dev/null | grep -i lam || echo "NO lam_a2e env"
ls /mnt/DATA/WORK/LAM_Audio2Expression/run_inference.py 2>/dev/null || echo "NO LAM script"
echo "=== server running? ==="
curl -s http://127.0.0.1:8765/health || echo "server not running"

Check mtimes, deps, server status


Error: This Bash command contains multiple operations. The following parts require approval: tail -5
echo "=== deps ==="
ls -la ~/miniconda3/bin/edge-tts || echo "NO edge-tts"
~/miniconda3/bin/conda env list, grep -i lam || echo "NO lam_a2e env"
ls /mnt/DATA/WORK/LAM_Audio2Expression/run_inference.py || echo "NO LAM script"
echo "=== server running? ==="
curl -s http://127.0.0.1:8765/health || echo "server not running"


Je découpe en commandes séparées.


ls -la --time-style=+%H:%M agent_gui/*.py static/*.js static/*.html && echo "=== tts dir ===" && ls -la data/tts 2>/dev/null || echo "(no tts dir)"

Check file mtimes and tts dir


-rw-------+            1 wildlama wildlama      81 21:19 agent_gui/__init__.py
-rw-------+            1 wildlama wildlama    4036 21:21 agent_gui/adapters.py
-rw-------+            1 wildlama wildlama    8175 23:45 agent_gui/app.py
-rw-------+            1 wildlama wildlama    1801 21:21 agent_gui/avatar.py
-rw-rw-r--+            1 wildlama wildlama     847 23:44 agent_gui/avatar_lam.py
-rw-------+            1 wildlama wildlama    1949 21:35 agent_gui/config.py
-rw-------+            1 wildlama wildlama    2097 21:21 agent_gui/sessions.py
-rw-rw-r--+            1 wildlama wildlama    1575 08:10 agent_gui/tts.py
-rw-------+            1 wildlama wildlama   28059 08:00 static/app.js
-rw-rw-r--+            1 wildlama wildlama    2596 08:04 static/avatar.js
-rw-------+            1 wildlama wildlama    3154 08:05 static/index.html
-rw-rw-r--+            1 wildlama wildlama 4067930 08:02 static/lib_lam_renderer.js
=== tts dir ===
total 280
drwxrwxr-x+  1 wildlama wildlama      0 Jun 16 08:12 .
drwxrwxr-x+  1 wildlama wildlama      0 Jun 15 23:44 ..
-rw-rw-r--+  1 wildlama wildlama 282702 Jun 16 08:12 c2ba25240ba9.wav


ls -la ~/miniconda3/bin/edge-tts 2>/dev/null && echo "edge-tts OK" || echo "NO edge-tts"

Check edge-tts binary


Error: ls in '/home/wildlama/miniconda3/bin/edge-tts' was blocked. For security, Claude Code may only list files in the allowed working directories for this session: '/mnt/DATA/WORK/agent-gui'.


Error: This Bash command contains multiple operations. The following part requires approval: curl -s --max-time 3 http://127.0.0.1:8765/health


ls /mnt/DATA/WORK/LAM_Audio2Expression/run_inference.py 2>/dev/null && echo "LAM OK" || echo "NO LAM script"; ~/miniconda3/bin/conda env list 2>/dev/null | grep -i lam || echo "NO lam_a2e env"

Check LAM script and conda env


Error: This Bash command contains multiple operations. The following part requires approval: ls /mnt/DATA/WORK/LAM_Audio2Expression/run_inference.py && echo "LAM OK" || echo "NO LAM script"; ~/miniconda3/bin/conda env list


curl -s --max-time 3 http://127.0.0.1:8765/health && echo " <- server up" || echo "server not running"

Check if server is running


Le sandbox bloque les chemins hors du dossier projet. Je relance hors sandbox pour les vérifs externes.


test -x ~/miniconda3/bin/edge-tts && echo "edge-tts OK" || echo "NO edge-tts"
test -f /mnt/DATA/WORK/LAM_Audio2Expression/run_inference.py && echo "LAM script OK" || echo "NO LAM script"
~/miniconda3/bin/conda env list 2>/dev/null | grep -i lam || echo "NO lam_a2e env"
curl -s --max-time 3 http://127.0.0.1:8765/health && echo "  <- server up" || echo "server not running"

Check external deps and server


Error: This Bash command contains multiple operations. The following parts require approval: test -x ~/miniconda3/bin/edge-tts && echo "edge-tts OK" || echo "NO edge-tts"
test -f /mnt/DATA/WORK/LAM_Audio2Expression/run_inference.py && echo "LAM script OK" || echo "NO LAM script"
~/miniconda3/bin/conda env list, grep -i lam || echo "NO lam_a2e env"
curl -s --max-time 3 http://127.0.0.1:8765/health && echo "  <- server up" || echo "server not running"


test -x ~/miniconda3/bin/edge-tts && echo "edge-tts OK" || echo "NO edge-tts"

Check edge-tts


edge-tts OK


test -f /mnt/DATA/WORK/LAM_Audio2Expression/run_inference.py && echo "LAM script OK" || echo "NO LAM script"

Check LAM script


LAM script OK


~/miniconda3/bin/conda env list 2>/dev/null | grep -i lam || echo "NO lam_a2e env"

Check conda env


Error: This Bash command contains multiple operations. The following part requires approval: ~/miniconda3/bin/conda env list


curl -s --max-time 3 http://127.0.0.1:8765/health && echo "  <- server up" || echo "server not running"

Check server health


~/miniconda3/bin/conda env list 2>/dev/null | grep -i lam || echo "NO lam_a2e env"

List conda envs grep lam


curl -s --max-time 3 http://127.0.0.1:8765/health || echo "server not running"

Curl server health


List conda envs


This command requires approval


Curl health


edge-tts et le script LAM existent, et un `.wav` a été généré à 08:12 — donc le TTS produit bien de l'audio. Je teste l'étape suivante (blendshapes LAM) qui est la plus fragile, sur le wav existant.


timeout 280 python3 -c "
from pathlib import Path
from agent_gui.avatar_lam import audio_to_blendshapes
wav = Path('data/tts/c2ba25240ba9.wav')
print('wav exists:', wav.exists(), wav.stat().st_size, 'bytes')
try:
    bs = audio_to_blendshapes(wav)
    print('OK keys:', list(bs.keys()))
    print('names:', bs.get('names'))
    print('num frames:', len(bs.get('frames', [])))
except Exception as e:
    import traceback; traceback.print_exc()
" 2>&1 | tail -40

Test LAM blendshape generation on existing wav


Error: This Bash command contains multiple operations. The following part requires approval: timeout 280 python3 -c "
from pathlib import Path
from agent_gui.avatar_lam import audio_to_blendshapes
wav = Path('data/tts/c2ba25240ba9.wav')
print('wav exists:', wav.exists(), wav.stat().st_size, 'bytes')
try:
    bs = audio_to_blendshapes(wav)
    print('OK keys:', list(bs.keys()))
    print('names:', bs.get('names'))
    print('num frames:', len(bs.get('frames', [])))
except Exception as e:
    import traceback; traceback.print_exc()
" 2>&1


timeout 280 python3 -c "
from pathlib import Path
from agent_gui.avatar_lam import audio_to_blendshapes
wav = Path('data/tts/c2ba25240ba9.wav')
print('wav exists:', wav.exists(), wav.stat().st_size, 'bytes')
try:
    bs = audio_to_blendshapes(wav)
    print('OK keys:', list(bs.keys()))
    print('names count:', len(bs.get('names', [])))
    print('num frames:', len(bs.get('frames', [])))
except Exception as e:
    import traceback; traceback.print_exc()
"

Test LAM blendshapes on existing wav


Les commandes lourdes sont refusées par le mode permission. Je continue par lecture du code, qui est plus révélateur. Je vérifie le chargement du module avatar et l'export du renderer.


1	<!doctype html>
2
3
4	  <meta charset="utf-8" />
5	  <meta name="viewport" content="width=device-width, initial-scale=1" />
6	  <title>Agent GUI Local</title>
7	  <link rel="stylesheet" href="/styles.css" />
8
9
10

11

12
13

14
AG

15

16
Pixel command center

17
Agent GUI

18
Surcouche locale universelle

19

20

21
22	      Agent actif
23	      <select id="agentSelect"></select>
24

25
26

27	        <button id="newSessionBtn">Nouvelle session</button>
28	        <button id="reloadBtn" class="secondary">Reload crew</button>
29

30
31

32
Sessions

33

34

35
36
37
38

39

40

41
Virtual office

42
Bureau animé

43

44

45	            idle
46	            Session
47

48

49	        <canvas id="officeCanvas" width="960" height="540"></canvas>
50
Les agents arrivent au bureau.

51

52
53

54

55

56
Conversation

57
Chat

58

59
Ctrl+Enter pour envoyer

60

61
62

63
64	        <form id="chatForm" class="composer">
65	          <textarea id="messageInput" rows="3" placeholder="Écris à l'agent..."></textarea>
66	          <button type="submit">Envoyer</button>
67	        </form>
68

69
70
71
72

73
Avatar LAM

74
Barbara

75

76
Chargement...

77

78
79

80
Crew

81
Agents présents

82

83

84
85

86
Selected

87
Agent

88
idle

89

90

91
92

93
Timeline

94
Activity

95

96

97
98

99
100
101
102
103
104

<!doctype html>

  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Agent GUI Local</title>
  <link rel="stylesheet" href="/styles.css" />

AG

Pixel command center

Agent GUI

Surcouche locale universelle

      Agent actif
      <select id="agentSelect"></select>

        <button id="newSessionBtn">Nouvelle session</button>
        <button id="reloadBtn" class="secondary">Reload crew</button>

Sessions

Virtual office

Bureau animé

            idle
            Session

        <canvas id="officeCanvas" width="960" height="540"></canvas>

Les agents arrivent au bureau.

Conversation

Chat

Ctrl+Enter pour envoyer

        <form id="chatForm" class="composer">
          <textarea id="messageInput" rows="3" placeholder="Écris à l'agent..."></textarea>
          <button type="submit">Envoyer</button>
        </form>

Avatar LAM

Barbara

Chargement...

Crew

Agents présents

Selected

Agent

idle

Timeline

Activity


grep -n "GaussianSplatRenderer\|export" static/lib_lam_renderer.js | head -20

Find renderer export name


<persisted-output>
Output too large (96.6KB). Full output saved to: /home/wildlama/.claude/projects/-mnt-DATA-WORK-agent-gui/2c06a32a-c9af-4ee9-ba94-7a56c4acf23d/tool-results/b1pwluxuk.txt

Preview (first 2KB):
29622:				// Blender exporter used to export a scalar. See #7459
66072:        wasmInstance.exports.sortIndexes(indexesToSortOffset, centersOffset, precomputedDistancesOffset,
70150:// exports
75579:// Non-PURE exports list, side-effects are required here.
76671://export const textureLevel = ( value, uv, level ) => texture( value, uv ).level( level );
105219://export const mx_noise_vec2 = ( texcoord = uv(), amplitude = 1, pivot = 0 ) => mx_perlin_noise_vec3( texcoord.convert( 'vec2|vec3' ) ).mul( amplitude ).add( pivot );
139867:function __exportStar(m, o) {
140085:    __exportStar: __exportStar,
144021:// Create the default instance to be exported
144063:// This module is intended to unwrap Axios default export as named.
144064:// Keep top-level export same with static properties
144128:var jszip_min$2 = {exports: {}};
144141:var jszip_min$1 = jszip_min$2.exports;
144146:	if (hasRequiredJszip_min) return jszip_min$2.exports;
144148:	(function (module, exports) {
144149:		!function(e){if("object"=='object'&&"undefined"!='object')module.exports=e();else if("function"==typeof undefined&&undefined.amd)undefined([],e);else {("undefined"!=typeof window?window:"undefined"!=typeof commonjsGlobal?commonjsGlobal:"undefined"!=typeof self?self:this).JSZip=e();}}(function(){return function s(a,o,h){function u(r,e){if(!o[r]){if(!a[r]){var t="function"==typeof commonjsRequire&&commonjsRequire;if(!e&&t)return t(r,!0);if(l)return l(r,!0);var n=new Error("Cannot find module '"+r+"'");throw n.code="MODULE_NOT_FOUND",n}var i=o[r]={exports:{}};a[r][0].call(i.exports,function(e){var t=a[r][1][e];return u(t||e)},i,i.exports,s,a,o,h);}return o[r].exports}for(var l="function"==typeof commonjsRequire&&commonjsRequire,e=0;e<h.length;e++)u(h[e]);return u}({1:[function(e,t,r){"use strict";var d=e("./utils"),c=e("./support"),p="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";r.encode=function(e){for(var t,r,n,i,s,a,o,h=[],u=0,l=e.length,f=l,c="string"!==d.getTypeOf(e);u<e.l
...
</persisted-output>

29622:				// Blender exporter used to export a scalar. See #7459
66072:        wasmInstance.exports.sortIndexes(indexesToSortOffset, centersOffset, precomputedDistancesOffset,
70150:// exports
75579:// Non-PURE exports list, side-effects are required here.
76671://export const textureLevel = ( value, uv, level ) => texture( value, uv ).level( level );
105219://export const mx_noise_vec2 = ( texcoord = uv(), amplitude = 1, pivot = 0 ) => mx_perlin_noise_vec3( texcoord.convert( 'vec2|vec3' ) ).mul( amplitude ).add( pivot );
139867:function __exportStar(m, o) {
140085:    __exportStar: __exportStar,
144021:// Create the default instance to be exported
144063:// This module is intended to unwrap Axios default export as named.
144064:// Keep top-level export same with static properties
144128:var jszip_min$2 = {exports: {}};
144141:var jszip_min$1 = jszip_min$2.exports;
144146:	if (hasRequiredJszip_min) return jszip_min$2.exports;
144148:	(function (module, exports) {
144149:		!function(e){if("object"=='object'&&"undefined"!='object')module.exports=e();else if("function"==typeof undefined&&undefined.amd)undefined([],e);else {("undefined"!=typeof window?window:"undefined"!=typeof commonjsGlobal?commonjsGlobal:"undefined"!=typeof self?self:this).JSZip=e();}}(function(){return function s(a,o,h){function u(r,e){if(!o[r]){if(!a[r]){var t="function"==typeof commonjsRequire&&commonjsRequire;if(!e&&t)return t(r,!0);if(l)return l(r,!0);var n=new Error("Cannot find module '"+r+"'");throw n.code="MODULE_NOT_FOUND",n}var i=o[r]={exports:{}};a[r][0].call(i.exports,function(e){var t=a[r][1][e];return u(t||e)},i,i.exports,s,a,o,h);}return o[r].exports}for(var l="function"==typeof commonjsRequire&&commonjsRequire,e=0;e<h.length;e++)u(h[e]);return u}({1:[function(e,t,r){"use strict";var d=e("./utils"),c=e("./support"),p="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";r.encode=function(e){for(var t,r,n,i,s,a,o,h=[],u=0,l=e.length,f=l,c="string"!==d.getTypeOf(e);u<e.length;)f=l-u,n=c?(t=e[u++],r=u<l?e[u++]:0,u<l?e[u++]:0):(t=e.charCodeAt(u++),r=u<l?e.charCodeAt(u++):0,u<l?e.charCodeAt(u++):0),i=t>>2,s=(3&t)<<4|r>>4,a=1<f?(15&r)<<2|n>>6:64,o=2<f?63&n:64,h.push(p.charAt(i)+p.charAt(s)+p.charAt(a)+p.charAt(o));return h.join("")},r.decode=function(e){var t,r,n,i,s,a,o=0,h=0,u="data:";if(e.substr(0,u.length)===u)throw new Error("Invalid base64 input, it looks like a data url.");var l,f=3*(e=e.replace(/[^A-Za-z0-9+/=]/g,"")).length/4;if(e.charAt(e.length-1)===p.charAt(64)&&f--,e.charAt(e.length-2)===p.charAt(64)&&f--,f%1!=0)throw new Error("Invalid base64 input, bad content length.");for(l=c.uint8array?new Uint8Array(0|f):new Array(0|f);o<e.length;)t=p.indexOf(e.charAt(o++))<<2|(i=p.indexOf(e.charAt(o++)))>>4,r=(15&i)<<4|(s=p.indexOf(e.charAt(o++)))>>2,n=(3&s)<<6|(a=p.indexOf(e.charAt(o++))),l[h++]=t,64!==s&&(l[h++]=r),64!==a&&(l[h++]=n);return l};},{"./support":30,"./utils":32}],2:[function(e,t,r){"use strict";var n=e("./external"),i=e("./stream/DataWorker"),s=e("./stream/Crc32Probe"),a=e("./stream/DataLengthProbe");function o(e,t,r,n,i){this.compressedSize=e,this.uncompressedSize=t,this.crc32=r,this.compression=n,this.compressedContent=i;}o.prototype={getContentWorker:function(){var e=new i(n.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new a("data_length")),t=this;return e.on("end",function(){if(this.streamInfo.data_length!==t.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),e},getCompressedWorker:function(){return new i(n.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},o.createWorkerFrom=function(e,t,r){return e.pipe(new s).pipe(new a("uncompressedSize")).pipe(t.compressWorker(r)).pipe(new a("compressedSize")).withStreamInfo("compression",t)},t.exports=o;},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(e,t,r){"use strict";var n=e("./stream/GenericWorker");r.STORE={magic:"\0\0",compressWorker:function(){return new n("STORE compression")},uncompressWorker:function(){return new n("STORE decompression")}},r.DEFLATE=e("./flate");},{"./flate":7,"./stream/GenericWorker":28}],4:[function(e,t,r){"use strict";var n=e("./utils");var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e;}return t}();t.exports=function(e,t){return void 0!==e&&e.length?"string"!==n.getTypeOf(e)?function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a<s;a++)e=e>>>8^i[255&(e^t[a])];return -1^e}(0|t,e,e.length,0):function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a<s;a++)e=e>>>8^i[255&(e^t.charCodeAt(a))];return -1^e}(0|t,e,e.length,0):0};},{"./utils":32}],5:[function(e,t,r){"use strict";r.base64=!1,r.binary=!1,r.dir=!1,r.createFolders=!0,r.date=null,r.compression=null,r.compressionOptions=null,r.comment=null,r.unixPermissions=null,r.dosPermissions=null;},{}],6:[function(e,t,r){"use strict";var n=null;n="undefined"!=typeof Promise?Promise:e("lie"),t.exports={Promise:n};},{lie:37}],7:[function(e,t,r){"use strict";var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,i=e("pako"),s=e("./utils"),a=e("./stream/GenericWorker"),o=n?"uint8array":"array";function h(e,t){a.call(this,"FlateWorker/"+e),this._pako=null,this._pakoAction=e,this._pakoOptions=t,this.meta={};}r.magic="\b\0",s.inherits(h,a),h.prototype.processChunk=function(e){this.meta=e.meta,null===this._pako&&this._createPako(),this._pako.push(s.transformTo(o,e.data),!1);},h.prototype.flush=function(){a.prototype.flush.call(this),null===this._pako&&this._createPako(),this._pako.push([],!0);},h.prototype.cleanUp=function(){a.prototype.cleanUp.call(this),this._pako=null;},h.prototype._createPako=function(){this._pako=new i[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var t=this;this._pako.onData=function(e){t.push({data:e,meta:t.meta});};},r.compressWorker=function(e){return new h("Deflate",e)},r.uncompressWorker=function(){return new h("Inflate",{})};},{"./stream/GenericWorker":28,"./utils":32,pako:38}],8:[function(e,t,r){"use strict";function A(e,t){var r,n="";for(r=0;r<t;r++)n+=String.fromCharCode(255&e),e>>>=8;return n}function n(e,t,r,n,i,s){var a,o,h=e.file,u=e.compression,l=s!==O.utf8encode,f=I.transformTo("string",s(h.name)),c=I.transformTo("string",O.utf8encode(h.name)),d=h.comment,p=I.transformTo("string",s(d)),m=I.transformTo("string",O.utf8encode(d)),_=c.length!==h.name.length,g=m.length!==d.length,b="",v="",y="",w=h.dir,k=h.date,x={crc32:0,compressedSize:0,uncompressedSize:0};t&&!r||(x.crc32=e.crc32,x.compressedSize=e.compressedSize,x.uncompressedSize=e.uncompressedSize);var S=0;t&&(S|=8),l||!_&&!g||(S|=2048);var z=0,C=0;w&&(z|=16),"UNIX"===i?(C=798,z|=function(e,t){var r=e;return e||(r=t?16893:33204),(65535&r)<<16}(h.unixPermissions,w)):(C=20,z|=function(e){return 63&(e||0)}(h.dosPermissions)),a=k.getUTCHours(),a<<=6,a|=k.getUTCMinutes(),a<<=5,a|=k.getUTCSeconds()/2,o=k.getUTCFullYear()-1980,o<<=4,o|=k.getUTCMonth()+1,o<<=5,o|=k.getUTCDate(),_&&(v=A(1,1)+A(B(f),4)+c,b+="up"+A(v.length,2)+v),g&&(y=A(1,1)+A(B(p),4)+m,b+="uc"+A(y.length,2)+y);var E="";return E+="\n\0",E+=A(S,2),E+=u.magic,E+=A(a,2),E+=A(o,2),E+=A(x.crc32,4),E+=A(x.compressedSize,4),E+=A(x.uncompressedSize,4),E+=A(f.length,2),E+=A(b.length,2),{fileRecord:R.LOCAL_FILE_HEADER+E+f+b,dirRecord:R.CENTRAL_FILE_HEADER+A(C,2)+E+A(p.length,2)+"\0\0\0\0"+A(z,4)+A(n,4)+f+b+p}}var I=e("../utils"),i=e("../stream/GenericWorker"),O=e("../utf8"),B=e("../crc32"),R=e("../signature");function s(e,t,r,n){i.call(this,"ZipFileWorker"),this.bytesWritten=0,this.zipComment=t,this.zipPlatform=r,this.encodeFileName=n,this.streamFiles=e,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[];}I.inherits(s,i),s.prototype.push=function(e){var t=e.meta.percent||0,r=this.entriesCount,n=this._sources.length;this.accumulate?this.contentBuffer.push(e):(this.bytesWritten+=e.data.length,i.prototype.push.call(this,{data:e.data,meta:{currentFile:this.currentFile,percent:r?(t+100*(r-n-1))/r:100}}));},s.prototype.openedSource=function(e){this.currentSourceOffset=this.bytesWritten,this.currentFile=e.file.name;var t=this.streamFiles&&!e.file.dir;if(t){var r=n(e,t,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:r.fileRecord,meta:{percent:0}});}else this.accumulate=!0;},s.prototype.closedSource=function(e){this.accumulate=!1;var t=this.streamFiles&&!e.file.dir,r=n(e,t,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(r.dirRecord),t)this.push({data:function(e){return R.DATA_DESCRIPTOR+A(e.crc32,4)+A(e.compressedSize,4)+A(e.uncompressedSize,4)}(e),meta:{percent:100}});else for(this.push({data:r.fileRecord,meta:{percent:0}});this.contentBuffer.length;)this.push(this.contentBuffer.shift());this.currentFile=null;},s.prototype.flush=function(){for(var e=this.bytesWritten,t=0;t<this.dirRecords.length;t++)this.push({data:this.dirRecords[t],meta:{percent:100}});var r=this.bytesWritten-e,n=function(e,t,r,n,i){var s=I.transformTo("string",i(n));return R.CENTRAL_DIRECTORY_END+"\0\0\0\0"+A(e,2)+A(e,2)+A(t,4)+A(r,4)+A(s.length,2)+s}(this.dirRecords.length,r,e,this.zipComment,this.encodeFileName);this.push({data:n,meta:{percent:100}});},s.prototype.prepareNextSource=function(){this.previous=this._sources.shift(),this.openedSource(this.previous.streamInfo),this.isPaused?this.previous.pause():this.previous.resume();},s.prototype.registerPrevious=function(e){this._sources.push(e);var t=this;return e.on("data",function(e){t.processChunk(e);}),e.on("end",function(){t.closedSource(t.previous.streamInfo),t._sources.length?t.prepareNextSource():t.end();}),e.on("error",function(e){t.error(e);}),this},s.prototype.resume=function(){return !!i.prototype.resume.call(this)&&(!this.previous&&this._sources.length?(this.prepareNextSource(),!0):this.previous||this._sources.length||this.generatedError?void 0:(this.end(),!0))},s.prototype.error=function(e){var t=this._sources;if(!i.prototype.error.call(this,e))return !1;for(var r=0;r<t.length;r++)try{t[r].error(e);}catch(e){}return !0},s.prototype.lock=function(){i.prototype.lock.call(this);for(var e=this._sources,t=0;t<e.length;t++)e[t].lock();},t.exports=s;},{"../crc32":4,"../signature":23,"../stream/GenericWorker":28,"../utf8":31,"../utils":32}],9:[function(e,t,r){"use strict";var u=e("../compressions"),n=e("./ZipFileWorker");r.generateWorker=function(e,a,t){var o=new n(a.streamFiles,t,a.platform,a.encodeFileName),h=0;try{e.forEach(function(e,t){h++;var r=function(e,t){var r=e||t,n=u[r];if(!n)throw new Error(r+" is not a valid compression method !");return n}(t.options.compression,a.compression),n=t.options.compressionOptions||a.compressionOptions||{},i=t.dir,s=t.date;t._compressWorker(r,n).withStreamInfo("file",{name:e,dir:i,date:s,comment:t.comment||"",unixPermissions:t.unixPermissions,dosPermissions:t.dosPermissions}).pipe(o);}),o.entriesCount=h;}catch(e){o.error(e);}return o};},{"../compressions":3,"./ZipFileWorker":8}],10:[function(e,t,r){"use strict";function n(){if(!(this instanceof n))return new n;if(arguments.length)throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide.");this.files=Object.create(null),this.comment=null,this.root="",this.clone=function(){var e=new n;for(var t in this)"function"!=typeof this[t]&&(e[t]=this[t]);return e};}(n.prototype=e("./object")).loadAsync=e("./load"),n.support=e("./support"),n.defaults=e("./defaults"),n.version="3.10.1",n.loadAsync=function(e,t){return (new n).loadAsync(e,t)},n.external=e("./external"),t.exports=n;},{"./defaults":5,"./external":6,"./load":11,"./object":15,"./support":30}],11:[function(e,t,r){"use strict";var u=e("./utils"),i=e("./external"),n=e("./utf8"),s=e("./zipEntries"),a=e("./stream/Crc32Probe"),l=e("./nodejsUtils");function f(n){return new i.Promise(function(e,t){var r=n.decompressed.getContentWorker().pipe(new a);r.on("error",function(e){t(e);}).on("end",function(){r.streamInfo.crc32!==n.decompressed.crc32?t(new Error("Corrupted zip : CRC32 mismatch")):e();}).resume();})}t.exports=function(e,o){var h=this;return o=u.extend(o||{},{base64:!1,checkCRC32:!1,optimizedBinaryString:!1,createFolders:!1,decodeFileName:n.utf8decode}),l.isNode&&l.isStream(e)?i.Promise.reject(new Error("JSZip can't accept a stream when loading a zip file.")):u.prepareContent("the loaded zip file",e,!0,o.optimizedBinaryString,o.base64).then(function(e){var t=new s(o);return t.load(e),t}).then(function(e){var t=[i.Promise.resolve(e)],r=e.files;if(o.checkCRC32)for(var n=0;n<r.length;n++)t.push(f(r[n]));return i.Promise.all(t)}).then(function(e){for(var t=e.shift(),r=t.files,n=0;n<r.length;n++){var i=r[n],s=i.fileNameStr,a=u.resolve(i.fileNameStr);h.file(a,i.decompressed,{binary:!0,optimizedBinaryString:!0,date:i.date,dir:i.dir,comment:i.fileCommentStr.length?i.fileCommentStr:null,unixPermissions:i.unixPermissions,dosPermissions:i.dosPermissions,createFolders:o.createFolders}),i.dir||(h.file(a).unsafeOriginalName=s);}return t.zipComment.length&&(h.comment=t.zipComment),h})};},{"./external":6,"./nodejsUtils":14,"./stream/Crc32Probe":25,"./utf8":31,"./utils":32,"./zipEntries":33}],12:[function(e,t,r){"use strict";var n=e("../utils"),i=e("../stream/GenericWorker");function s(e,t){i.call(this,"Nodejs stream input adapter for "+e),this._upstreamEnded=!1,this._bindStream(t);}n.inherits(s,i),s.prototype._bindStream=function(e){var t=this;(this._stream=e).pause(),e.on("data",function(e){t.push({data:e,meta:{percent:0}});}).on("error",function(e){t.isPaused?this.generatedError=e:t.error(e);}).on("end",function(){t.isPaused?t._upstreamEnded=!0:t.end();});},s.prototype.pause=function(){return !!i.prototype.pause.call(this)&&(this._stream.pause(),!0)},s.prototype.resume=function(){return !!i.prototype.resume.call(this)&&(this._upstreamEnded?this.end():this._stream.resume(),!0)},t.exports=s;},{"../stream/GenericWorker":28,"../utils":32}],13:[function(e,t,r){"use strict";var i=e("readable-stream").Readable;function n(e,t,r){i.call(this,t),this._helper=e;var n=this;e.on("data",function(e,t){n.push(e)||n._helper.pause(),r&&r(t);}).on("error",function(e){n.emit("error",e);}).on("end",function(){n.push(null);});}e("../utils").inherits(n,i),n.prototype._read=function(){this._helper.resume();},t.exports=n;},{"../utils":32,"readable-stream":16}],14:[function(e,t,r){"use strict";t.exports={isNode:"undefined"!=typeof Buffer,newBufferFrom:function(e,t){if(Buffer.from&&Buffer.from!==Uint8Array.from)return Buffer.from(e,t);if("number"==typeof e)throw new Error('The "data" argument must not be a number');return new Buffer(e,t)},allocBuffer:function(e){if(Buffer.alloc)return Buffer.alloc(e);var t=new Buffer(e);return t.fill(0),t},isBuffer:function(e){return Buffer.isBuffer(e)},isStream:function(e){return e&&"function"==typeof e.on&&"function"==typeof e.pause&&"function"==typeof e.resume}};},{}],15:[function(e,t,r){"use strict";function s(e,t,r){var n,i=u.getTypeOf(t),s=u.extend(r||{},f);s.date=s.date||new Date,null!==s.compression&&(s.compression=s.compression.toUpperCase()),"string"==typeof s.unixPermissions&&(s.unixPermissions=parseInt(s.unixPermissions,8)),s.unixPermissions&&16384&s.unixPermissions&&(s.dir=!0),s.dosPermissions&&16&s.dosPermissions&&(s.dir=!0),s.dir&&(e=g(e)),s.createFolders&&(n=_(e))&&b.call(this,n,!0);var a="string"===i&&!1===s.binary&&!1===s.base64;r&&void 0!==r.binary||(s.binary=!a),(t instanceof c&&0===t.uncompressedSize||s.dir||!t||0===t.length)&&(s.base64=!1,s.binary=!0,t="",s.compression="STORE",i="string");var o=null;o=t instanceof c||t instanceof l?t:p.isNode&&p.isStream(t)?new m(e,t):u.prepareContent(e,t,s.binary,s.optimizedBinaryString,s.base64);var h=new d(e,o,s);this.files[e]=h;}var i=e("./utf8"),u=e("./utils"),l=e("./stream/GenericWorker"),a=e("./stream/StreamHelper"),f=e("./defaults"),c=e("./compressedObject"),d=e("./zipObject"),o=e("./generate"),p=e("./nodejsUtils"),m=e("./nodejs/NodejsStreamInputAdapter"),_=function(e){"/"===e.slice(-1)&&(e=e.substring(0,e.length-1));var t=e.lastIndexOf("/");return 0<t?e.substring(0,t):""},g=function(e){return "/"!==e.slice(-1)&&(e+="/"),e},b=function(e,t){return t=void 0!==t?t:f.createFolders,e=g(e),this.files[e]||s.call(this,e,null,{dir:!0,createFolders:t}),this.files[e]};function h(e){return "[object RegExp]"===Object.prototype.toString.call(e)}var n={load:function(){throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.")},forEach:function(e){var t,r,n;for(t in this.files)n=this.files[t],(r=t.slice(this.root.length,t.length))&&t.slice(0,this.root.length)===this.root&&e(r,n);},filter:function(r){var n=[];return this.forEach(function(e,t){r(e,t)&&n.push(t);}),n},file:function(e,t,r){if(1!==arguments.length)return e=this.root+e,s.call(this,e,t,r),this;if(h(e)){var n=e;return this.filter(function(e,t){return !t.dir&&n.test(e)})}var i=this.files[this.root+e];return i&&!i.dir?i:null},folder:function(r){if(!r)return this;if(h(r))return this.filter(function(e,t){return t.dir&&r.test(e)});var e=this.root+r,t=b.call(this,e),n=this.clone();return n.root=t.name,n},remove:function(r){r=this.root+r;var e=this.files[r];if(e||("/"!==r.slice(-1)&&(r+="/"),e=this.files[r]),e&&!e.dir)delete this.files[r];else for(var t=this.filter(function(e,t){return t.name.slice(0,r.length)===r}),n=0;n<t.length;n++)delete this.files[t[n].name];return this},generate:function(){throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.")},generateInternalStream:function(e){var t,r={};try{if((r=u.extend(e||{},{streamFiles:!1,compression:"STORE",compressionOptions:null,type:"",platform:"DOS",comment:null,mimeType:"application/zip",encodeFileName:i.utf8encode})).type=r.type.toLowerCase(),r.compression=r.compression.toUpperCase(),"binarystring"===r.type&&(r.type="string"),!r.type)throw new Error("No output type specified.");u.checkSupport(r.type),"darwin"!==r.platform&&"freebsd"!==r.platform&&"linux"!==r.platform&&"sunos"!==r.platform||(r.platform="UNIX"),"win32"===r.platform&&(r.platform="DOS");var n=r.comment||this.comment||"";t=o.generateWorker(this,r,n);}catch(e){(t=new l("error")).error(e);}return new a(t,r.type||"string",r.mimeType)},generateAsync:function(e,t){return this.generateInternalStream(e).accumulate(t)},generateNodeStream:function(e,t){return (e=e||{}).type||(e.type="nodebuffer"),this.generateInternalStream(e).toNodejsStream(t)}};t.exports=n;},{"./compressedObject":2,"./defaults":5,"./generate":9,"./nodejs/NodejsStreamInputAdapter":12,"./nodejsUtils":14,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31,"./utils":32,"./zipObject":35}],16:[function(e,t,r){"use strict";t.exports=e("stream");},{stream:void 0}],17:[function(e,t,r){"use strict";var n=e("./DataReader");function i(e){n.call(this,e);for(var t=0;t<this.data.length;t++)e[t]=255&e[t];}e("../utils").inherits(i,n),i.prototype.byteAt=function(e){return this.data[this.zero+e]},i.prototype.lastIndexOfSignature=function(e){for(var t=e.charCodeAt(0),r=e.charCodeAt(1),n=e.charCodeAt(2),i=e.charCodeAt(3),s=this.length-4;0<=s;--s)if(this.data[s]===t&&this.data[s+1]===r&&this.data[s+2]===n&&this.data[s+3]===i)return s-this.zero;return -1},i.prototype.readAndCheckSignature=function(e){var t=e.charCodeAt(0),r=e.charCodeAt(1),n=e.charCodeAt(2),i=e.charCodeAt(3),s=this.readData(4);return t===s[0]&&r===s[1]&&n===s[2]&&i===s[3]},i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return [];var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i;},{"../utils":32,"./DataReader":18}],18:[function(e,t,r){"use strict";var n=e("../utils");function i(e){this.data=e,this.length=e.length,this.index=0,this.zero=0;}i.prototype={checkOffset:function(e){this.checkIndex(this.index+e);},checkIndex:function(e){if(this.length<this.zero+e||e<0)throw new Error("End of data reached (data length = "+this.length+", asked index = "+e+"). Corrupted zip ?")},setIndex:function(e){this.checkIndex(e),this.index=e;},skip:function(e){this.setIndex(this.index+e);},byteAt:function(){},readInt:function(e){var t,r=0;for(this.checkOffset(e),t=this.index+e-1;t>=this.index;t--)r=(r<<8)+this.byteAt(t);return this.index+=e,r},readString:function(e){return n.transformTo("string",this.readData(e))},readData:function(){},lastIndexOfSignature:function(){},readAndCheckSignature:function(){},readDate:function(){var e=this.readInt(4);return new Date(Date.UTC(1980+(e>>25&127),(e>>21&15)-1,e>>16&31,e>>11&31,e>>5&63,(31&e)<<1))}},t.exports=i;},{"../utils":32}],19:[function(e,t,r){"use strict";var n=e("./Uint8ArrayReader");function i(e){n.call(this,e);}e("../utils").inherits(i,n),i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i;},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(e,t,r){"use strict";var n=e("./DataReader");function i(e){n.call(this,e);}e("../utils").inherits(i,n),i.prototype.byteAt=function(e){return this.data.charCodeAt(this.zero+e)},i.prototype.lastIndexOfSignature=function(e){return this.data.lastIndexOf(e)-this.zero},i.prototype.readAndCheckSignature=function(e){return e===this.readData(4)},i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i;},{"../utils":32,"./DataReader":18}],21:[function(e,t,r){"use strict";var n=e("./ArrayReader");function i(e){n.call(this,e);}e("../utils").inherits(i,n),i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return new Uint8Array(0);var t=this.data.subarray(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i;},{"../utils":32,"./ArrayReader":17}],22:[function(e,t,r){"use strict";var n=e("../utils"),i=e("../support"),s=e("./ArrayReader"),a=e("./StringReader"),o=e("./NodeBufferReader"),h=e("./Uint8ArrayReader");t.exports=function(e){var t=n.getTypeOf(e);return n.checkSupport(t),"string"!==t||i.uint8array?"nodebuffer"===t?new o(e):i.uint8array?new h(n.transformTo("uint8array",e)):new s(n.transformTo("array",e)):new a(e)};},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(e,t,r){"use strict";r.LOCAL_FILE_HEADER="PK",r.CENTRAL_FILE_HEADER="PK",r.CENTRAL_DIRECTORY_END="PK",r.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",r.ZIP64_CENTRAL_DIRECTORY_END="PK",r.DATA_DESCRIPTOR="PK\b";},{}],24:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../utils");function s(e){n.call(this,"ConvertWorker to "+e),this.destType=e;}i.inherits(s,n),s.prototype.processChunk=function(e){this.push({data:i.transformTo(this.destType,e.data),meta:e.meta});},t.exports=s;},{"../utils":32,"./GenericWorker":28}],25:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../crc32");function s(){n.call(this,"Crc32Probe"),this.withStreamInfo("crc32",0);}e("../utils").inherits(s,n),s.prototype.processChunk=function(e){this.streamInfo.crc32=i(e.data,this.streamInfo.crc32||0),this.push(e);},t.exports=s;},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataLengthProbe for "+e),this.propName=e,this.withStreamInfo(e,0);}n.inherits(s,i),s.prototype.processChunk=function(e){if(e){var t=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=t+e.data.length;}i.prototype.processChunk.call(this,e);},t.exports=s;},{"../utils":32,"./GenericWorker":28}],27:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataWorker");var t=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type="",this._tickScheduled=!1,e.then(function(e){t.dataIsReady=!0,t.data=e,t.max=e&&e.length||0,t.type=n.getTypeOf(e),t.isPaused||t._tickAndRepeat();},function(e){t.error(e);});}n.inherits(s,i),s.prototype.cleanUp=function(){i.prototype.cleanUp.call(this),this.data=null;},s.prototype.resume=function(){return !!i.prototype.resume.call(this)&&(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,n.delay(this._tickAndRepeat,[],this)),!0)},s.prototype._tickAndRepeat=function(){this._tickScheduled=!1,this.isPaused||this.isFinished||(this._tick(),this.isFinished||(n.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0));},s.prototype._tick=function(){if(this.isPaused||this.isFinished)return !1;var e=null,t=Math.min(this.max,this.index+16384);if(this.index>=this.max)return this.end();switch(this.type){case"string":e=this.data.substring(this.index,t);break;case"uint8array":e=this.data.subarray(this.index,t);break;case"array":case"nodebuffer":e=this.data.slice(this.index,t);}return this.index=t,this.push({data:e,meta:{percent:this.max?this.index/this.max*100:0}})},t.exports=s;},{"../utils":32,"./GenericWorker":28}],28:[function(e,t,r){"use strict";function n(e){this.name=e||"default",this.streamInfo={},this.generatedError=null,this.extraStreamInfo={},this.isPaused=!0,this.isFinished=!1,this.isLocked=!1,this._listeners={data:[],end:[],error:[]},this.previous=null;}n.prototype={push:function(e){this.emit("data",e);},end:function(){if(this.isFinished)return !1;this.flush();try{this.emit("end"),this.cleanUp(),this.isFinished=!0;}catch(e){this.emit("error",e);}return !0},error:function(e){return !this.isFinished&&(this.isPaused?this.generatedError=e:(this.isFinished=!0,this.emit("error",e),this.previous&&this.previous.error(e),this.cleanUp()),!0)},on:function(e,t){return this._listeners[e].push(t),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[];},emit:function(e,t){if(this._listeners[e])for(var r=0;r<this._listeners[e].length;r++)this._listeners[e][r].call(this,t);},pipe:function(e){return e.registerPrevious(this)},registerPrevious:function(e){if(this.isLocked)throw new Error("The stream '"+this+"' has already been used.");this.streamInfo=e.streamInfo,this.mergeStreamInfo(),this.previous=e;var t=this;return e.on("data",function(e){t.processChunk(e);}),e.on("end",function(){t.end();}),e.on("error",function(e){t.error(e);}),this},pause:function(){return !this.isPaused&&!this.isFinished&&(this.isPaused=!0,this.previous&&this.previous.pause(),!0)},resume:function(){if(!this.isPaused||this.isFinished)return !1;var e=this.isPaused=!1;return this.generatedError&&(this.error(this.generatedError),e=!0),this.previous&&this.previous.resume(),!e},flush:function(){},processChunk:function(e){this.push(e);},withStreamInfo:function(e,t){return this.extraStreamInfo[e]=t,this.mergeStreamInfo(),this},mergeStreamInfo:function(){for(var e in this.extraStreamInfo)Object.prototype.hasOwnProperty.call(this.extraStreamInfo,e)&&(this.streamInfo[e]=this.extraStreamInfo[e]);},lock:function(){if(this.isLocked)throw new Error("The stream '"+this+"' has already been used.");this.isLocked=!0,this.previous&&this.previous.lock();},toString:function(){var e="Worker "+this.name;return this.previous?this.previous+" -> "+e:e}},t.exports=n;},{}],29:[function(e,t,r){"use strict";var h=e("../utils"),i=e("./ConvertWorker"),s=e("./GenericWorker"),u=e("../base64"),n=e("../support"),a=e("../external"),o=null;if(n.nodestream)try{o=e("../nodejs/NodejsStreamOutputAdapter");}catch(e){}function l(e,o){return new a.Promise(function(t,r){var n=[],i=e._internalType,s=e._outputType,a=e._mimeType;e.on("data",function(e,t){n.push(e),o&&o(t);}).on("error",function(e){n=[],r(e);}).on("end",function(){try{var e=function(e,t,r){switch(e){case"blob":return h.newBlob(h.transformTo("arraybuffer",t),r);case"base64":return u.encode(t);default:return h.transformTo(e,t)}}(s,function(e,t){var r,n=0,i=null,s=0;for(r=0;r<t.length;r++)s+=t[r].length;switch(e){case"string":return t.join("");case"array":return Array.prototype.concat.apply([],t);case"uint8array":for(i=new Uint8Array(s),r=0;r<t.length;r++)i.set(t[r],n),n+=t[r].length;return i;case"nodebuffer":return Buffer.concat(t);default:throw new Error("concat : unsupported type '"+e+"'")}}(i,n),a);t(e);}catch(e){r(e);}n=[];}).resume();})}function f(e,t,r){var n=t;switch(t){case"blob":case"arraybuffer":n="uint8array";break;case"base64":n="string";}try{this._internalType=n,this._outputType=t,this._mimeType=r,h.checkSupport(n),this._worker=e.pipe(new i(n)),e.lock();}catch(e){this._worker=new s("error"),this._worker.error(e);}}f.prototype={accumulate:function(e){return l(this,e)},on:function(e,t){var r=this;return "data"===e?this._worker.on(e,function(e){t.call(r,e.data,e.meta);}):this._worker.on(e,function(){h.delay(t,arguments,r);}),this},resume:function(){return h.delay(this._worker.resume,[],this._worker),this},pause:function(){return this._worker.pause(),this},toNodejsStream:function(e){if(h.checkSupport("nodestream"),"nodebuffer"!==this._outputType)throw new Error(this._outputType+" is not supported by this method");return new o(this,{objectMode:"nodebuffer"!==this._outputType},e)}},t.exports=f;},{"../base64":1,"../external":6,"../nodejs/NodejsStreamOutputAdapter":13,"../support":30,"../utils":32,"./ConvertWorker":24,"./GenericWorker":28}],30:[function(e,t,r){"use strict";if(r.base64=!0,r.array=!0,r.string=!0,r.arraybuffer="undefined"!=typeof ArrayBuffer&&"undefined"!=typeof Uint8Array,r.nodebuffer="undefined"!=typeof Buffer,r.uint8array="undefined"!=typeof Uint8Array,"undefined"==typeof ArrayBuffer)r.blob=!1;else {var n=new ArrayBuffer(0);try{r.blob=0===new Blob([n],{type:"application/zip"}).size;}catch(e){try{var i=new(self.BlobBuilder||self.WebKitBlobBuilder||self.MozBlobBuilder||self.MSBlobBuilder);i.append(n),r.blob=0===i.getBlob("application/zip").size;}catch(e){r.blob=!1;}}}try{


grep -n "GaussianSplatRenderer" static/lib_lam_renderer.js

Find GaussianSplatRenderer references


152207:class GaussianSplatRenderer {
152250:                const renderer = new GaussianSplatRenderer(container, zipData);
152278:                    threejsCanvas: GaussianSplatRenderer._canvas,
152329:        GaussianSplatRenderer._canvas.style.visibility = 'visible';
152330:        GaussianSplatRenderer._canvas.width = width;
152331:        GaussianSplatRenderer._canvas.height = height;
152332:        _container.appendChild(GaussianSplatRenderer._canvas);
152337:        GaussianSplatRenderer._canvas.style.visibility = 'hidden';
152640:GaussianSplatRenderer._canvas = document.createElement('canvas');
152642:export { AbortablePromise, DropInViewer, GaussianSplatRenderer, KSplatLoader, Utils as LoaderUtils, LogLevel, OrbitControls, PlayCanvasCompressedPlyParser, PlyLoader, PlyParser, RenderMode, SceneFormat, SceneRevealMode, SplatBuffer, SplatBufferGenerator, SplatLoader, SplatParser, SplatPartitioner, SplatRenderMode, Viewer, WebXRMode };


152207	class GaussianSplatRenderer {
152208	    static getInstance(container, assetPath, options) {
152209	        var _a, _b, _c, _d, _e, _f;
152210	        return __awaiter(this, void 0, void 0, function* () {
152211	            if (this.instance != undefined) {
152212	                return this.instance;
152213	            }
152214	            try {
152215	                const characterPath = assetPath;
152216	                const { pathname } = urlParse(characterPath);
152217	                const matches = pathname.match(/\/([^/]+?)\.zip/);
152218	                const characterName = matches && matches[1];
152219	                if (!characterName) {
152220	                    throw new Error('character model is not found');
152221	                }
152222	                NProgress.start();
152223	                const characterZipResponse = yield axios.get(characterPath, {
152224	                    responseType: 'arraybuffer',
152225	                    timeout: 100000,
152226	                    onDownloadProgress: (progressEvent) => {
152227	                        if (progressEvent.lengthComputable && progressEvent && progressEvent.total) {
152228	                            const percent = (progressEvent.loaded / progressEvent.total);
152229	                            // console.log(`Downloaded ${percent * 100.0}%`);
152230	                            NProgress.set(percent);
152231	                            (options === null || options === void 0 ? void 0 : options.downloadProgress) && (options === null || options === void 0 ? void 0 : options.downloadProgress(percent));
152232	                        }
152233	                    }
152234	                });
152235	                (options === null || options === void 0 ? void 0 : options.loadProgress) && (options === null || options === void 0 ? void 0 : options.loadProgress(0.1));
152236	                NProgress.done();
152237	                console.log('download completed:', characterZipResponse.data);
152238	                const arrayBuffer = characterZipResponse.data;
152239	                const zipData = yield jszip_minExports.loadAsync(arrayBuffer);
152240	                let fileName = '';
152241	                Object.values(zipData.files).forEach(file => {
152242	                    var _a, _b;
152243	                    if (file.dir) {
152244	                        fileName = (_a = file.name) === null || _a === void 0 ? void 0 : _a.slice(0, ((_b = file.name) === null || _b === void 0 ? void 0 : _b.length) - 1); // 去掉末尾的‘/’
152245	                    }
152246	                });
152247	                if (!fileName) {
152248	                    throw new Error('file fold is not found');
152249	                }
152250	                const renderer = new GaussianSplatRenderer(container, zipData);
152251	                const cameraPos = new Vector3();
152252	                cameraPos.x = ((_a = charactorConfig.camPos) === null || _a === void 0 ? void 0 : _a.x) || 0;
152253	                cameraPos.y = ((_b = charactorConfig.camPos) === null || _b === void 0 ? void 0 : _b.y) || 0;
152254	                cameraPos.z = ((_c = charactorConfig.camPos) === null || _c === void 0 ? void 0 : _c.z) || 1;
152255	                const cameraRotation = new Vector3();
152256	                cameraRotation.x = ((_d = charactorConfig.camRot) === null || _d === void 0 ? void 0 : _d.x) || 0;
152257	                cameraRotation.y = ((_e = charactorConfig.camRot) === null || _e === void 0 ? void 0 : _e.y) || 0;
152258	                cameraRotation.z = ((_f = charactorConfig.camRot) === null || _f === void 0 ? void 0 : _f.z) || 0;
152259	                let backgroundColor = 0xffffff;
152260	                if (charactorConfig.backgroundColor) {
152261	                    backgroundColor = parseInt(charactorConfig.backgroundColor, 16);
152262	                }
152263	                if (options && options.backgroundColor && renderer.isHexColorStrict(options.backgroundColor)) {
152264	                    backgroundColor = parseInt(options.backgroundColor, 16);
152265	                }
152266	                let alpha = 0;
152267	                if (options && options.alpha) {
152268	                    alpha = Math.max(0, Math.min(1, options.alpha));
152269	                }
152270	                renderer.getChatState = options === null || options === void 0 ? void 0 : options.getChatState;
152271	                renderer.getExpressionData = options === null || options === void 0 ? void 0 : options.getExpressionData;
152272	                if (charactorConfig.useFlame) {
152273	                    renderer.useFlame = (charactorConfig.useFlame == "false") ? false : true;
152274	                }
152275	                console.log(cameraPos, backgroundColor, alpha);
152276	                renderer.viewer = new Viewer({
152277	                    rootElement: container,
152278	                    threejsCanvas: GaussianSplatRenderer._canvas,
152279	                    cameraUp: [0, 1, 0],
152280	                    initialCameraPosition: [cameraPos.x, cameraPos.y, cameraPos.z],
152281	                    initialCameraRotation: [cameraRotation.x, cameraRotation.y, cameraRotation.z],
152282	                    // initialCameraLookAt: [0, cameraPos.y, 0],
152283	                    sphericalHarmonicsDegree: 0,
152284	                    backgroundColor: backgroundColor,
152285	                    alpha: alpha,
152286	                    gpuAcceleratedSort: true
152287	                });
152288	                renderer.viewer.useFlame = renderer.useFlame;
152289	                if (renderer.viewer.useFlame == true) {
152290	                    yield renderer.loadFlameModel(fileName, motionConfig);
152291	                }
152292	                else {
152293	                    yield renderer.loadModel(fileName, animationConfig, motionConfig);
152294	                }
152295	                (options === null || options === void 0 ? void 0 : options.loadProgress) && (options === null || options === void 0 ? void 0 : options.loadProgress(0.2));
152296	                const offsetFileUrl = yield renderer.unpackFileAsBlob(fileName + '/offset.ply');
152297	                (options === null || options === void 0 ? void 0 : options.loadProgress) && (options === null || options === void 0 ? void 0 : options.loadProgress(0.3));
152298	                renderer.viewer
152299	                    .addSplatScene(offsetFileUrl, {
152300	                    progressiveLoad: true,
152301	                    sharedMemoryForWorkers: false,
152302	                    showLoadingUI: false,
152303	                    format: SceneFormat.Ply
152304	                })
152305	                    .then(() => {
152306	                    renderer.render();
152307	                    (options === null || options === void 0 ? void 0 : options.loadProgress) && (options === null || options === void 0 ? void 0 : options.loadProgress(1));
152308	                });
152309	                return renderer;
152310	            }
152311	            catch (error) {
152312	                console.error(error);
152313	            }
152314	        });
152315	    }
152316	    constructor(_container, zipData) {
152317	        this.zipUrls = {
152318	            urls: new Map()
152319	        };
152320	        this.useFlame = false;
152321	        this.lastTime = 0;
152322	        this.startTime = 0;
152323	        this.expressionData = {};
152324	        this.chatState = TYVoiceChatState.Idle;
152325	        Object.assign(this.zipUrls, {
152326	            zip: zipData
152327	        });
152328	        const { width, height } = _container.getBoundingClientRect();
152329	        GaussianSplatRenderer._canvas.style.visibility = 'visible';
152330	        GaussianSplatRenderer._canvas.width = width;
152331	        GaussianSplatRenderer._canvas.height = height;
152332	        _container.appendChild(GaussianSplatRenderer._canvas);
152333	        this.clock = new Clock();
152334	        this.startTime = performance.now() / 1000.0;
152335	    }
152336	    dispose() {
152337	        GaussianSplatRenderer._canvas.style.visibility = 'hidden';
152338	        this.disposeModel();
152339	        this.zipUrls.urls.forEach((value) => {
152340	            URL.revokeObjectURL(value);
152341	        });
152342	    }
152343	    disposeModel() {
152344	        var _a, _b;
152345	        if (this.mixer) {
152346	            this.mixer.stopAllAction();

class GaussianSplatRenderer {
    static getInstance(container, assetPath, options) {
        var _a, _b, _c, _d, _e, _f;
        return __awaiter(this, void 0, void 0, function* () {
            if (this.instance != undefined) {
                return this.instance;
            }
            try {
                const characterPath = assetPath;
                const { pathname } = urlParse(characterPath);
                const matches = pathname.match(/\/([^/]+?)\.zip/);
                const characterName = matches && matches[1];
                if (!characterName) {
                    throw new Error('character model is not found');
                }
                NProgress.start();
                const characterZipResponse = yield axios.get(characterPath, {
                    responseType: 'arraybuffer',
                    timeout: 100000,
                    onDownloadProgress: (progressEvent) => {
                        if (progressEvent.lengthComputable && progressEvent && progressEvent.total) {
                            const percent = (progressEvent.loaded / progressEvent.total);
                            // console.log(`Downloaded ${percent * 100.0}%`);
                            NProgress.set(percent);
                            (options === null || options === void 0 ? void 0 : options.downloadProgress) && (options === null || options === void 0 ? void 0 : options.downloadProgress(percent));
                        }
                    }
                });
                (options === null || options === void 0 ? void 0 : options.loadProgress) && (options === null || options === void 0 ? void 0 : options.loadProgress(0.1));
                NProgress.done();
                console.log('download completed:', characterZipResponse.data);
                const arrayBuffer = characterZipResponse.data;
                const zipData = yield jszip_minExports.loadAsync(arrayBuffer);
                let fileName = '';
                Object.values(zipData.files).forEach(file => {
                    var _a, _b;
                    if (file.dir) {
                        fileName = (_a = file.name) === null || _a === void 0 ? void 0 : _a.slice(0, ((_b = file.name) === null || _b === void 0 ? void 0 : _b.length) - 1); // 去掉末尾的‘/’
                    }
                });
                if (!fileName) {
                    throw new Error('file fold is not found');
                }
                const renderer = new GaussianSplatRenderer(container, zipData);
                const cameraPos = new Vector3();
                cameraPos.x = ((_a = charactorConfig.camPos) === null || _a === void 0 ? void 0 : _a.x) || 0;
                cameraPos.y = ((_b = charactorConfig.camPos) === null || _b === void 0 ? void 0 : _b.y) || 0;
                cameraPos.z = ((_c = charactorConfig.camPos) === null || _c === void 0 ? void 0 : _c.z) || 1;
                const cameraRotation = new Vector3();
                cameraRotation.x = ((_d = charactorConfig.camRot) === null || _d === void 0 ? void 0 : _d.x) || 0;
                cameraRotation.y = ((_e = charactorConfig.camRot) === null || _e === void 0 ? void 0 : _e.y) || 0;
                cameraRotation.z = ((_f = charactorConfig.camRot) === null || _f === void 0 ? void 0 : _f.z) || 0;
                let backgroundColor = 0xffffff;
                if (charactorConfig.backgroundColor) {
                    backgroundColor = parseInt(charactorConfig.backgroundColor, 16);
                }
                if (options && options.backgroundColor && renderer.isHexColorStrict(options.backgroundColor)) {
                    backgroundColor = parseInt(options.backgroundColor, 16);
                }
                let alpha = 0;
                if (options && options.alpha) {
                    alpha = Math.max(0, Math.min(1, options.alpha));
                }
                renderer.getChatState = options === null || options === void 0 ? void 0 : options.getChatState;
                renderer.getExpressionData = options === null || options === void 0 ? void 0 : options.getExpressionData;
                if (charactorConfig.useFlame) {
                    renderer.useFlame = (charactorConfig.useFlame == "false") ? false : true;
                }
                console.log(cameraPos, backgroundColor, alpha);
                renderer.viewer = new Viewer({
                    rootElement: container,
                    threejsCanvas: GaussianSplatRenderer._canvas,
                    cameraUp: [0, 1, 0],
                    initialCameraPosition: [cameraPos.x, cameraPos.y, cameraPos.z],
                    initialCameraRotation: [cameraRotation.x, cameraRotation.y, cameraRotation.z],
                    // initialCameraLookAt: [0, cameraPos.y, 0],
                    sphericalHarmonicsDegree: 0,
                    backgroundColor: backgroundColor,
                    alpha: alpha,
                    gpuAcceleratedSort: true
                });
                renderer.viewer.useFlame = renderer.useFlame;
                if (renderer.viewer.useFlame == true) {
                    yield renderer.loadFlameModel(fileName, motionConfig);
                }
                else {
                    yield renderer.loadModel(fileName, animationConfig, motionConfig);
                }
                (options === null || options === void 0 ? void 0 : options.loadProgress) && (options === null || options === void 0 ? void 0 : options.loadProgress(0.2));
                const offsetFileUrl = yield renderer.unpackFileAsBlob(fileName + '/offset.ply');
                (options === null || options === void 0 ? void 0 : options.loadProgress) && (options === null || options === void 0 ? void 0 : options.loadProgress(0.3));
                renderer.viewer
                    .addSplatScene(offsetFileUrl, {
                    progressiveLoad: true,
                    sharedMemoryForWorkers: false,
                    showLoadingUI: false,
                    format: SceneFormat.Ply
                })
                    .then(() => {
                    renderer.render();
                    (options === null || options === void 0 ? void 0 : options.loadProgress) && (options === null || options === void 0 ? void 0 : options.loadProgress(1));
                });
                return renderer;
            }
            catch (error) {
                console.error(error);
            }
        });
    }
    constructor(_container, zipData) {
        this.zipUrls = {
            urls: new Map()
        };
        this.useFlame = false;
        this.lastTime = 0;
        this.startTime = 0;
        this.expressionData = {};
        this.chatState = TYVoiceChatState.Idle;
        Object.assign(this.zipUrls, {
            zip: zipData
        });
        const { width, height } = _container.getBoundingClientRect();
        GaussianSplatRenderer._canvas.style.visibility = 'visible';
        GaussianSplatRenderer._canvas.width = width;
        GaussianSplatRenderer._canvas.height = height;
        _container.appendChild(GaussianSplatRenderer._canvas);
        this.clock = new Clock();
        this.startTime = performance.now() / 1000.0;
    }
    dispose() {
        GaussianSplatRenderer._canvas.style.visibility = 'hidden';
        this.disposeModel();
        this.zipUrls.urls.forEach((value) => {
            URL.revokeObjectURL(value);
        });
    }
    disposeModel() {
        var _a, _b;
        if (this.mixer) {
            this.mixer.stopAllAction();


grep -n "getExpressionData\|expressionData\|applyExpression\|morphTarget\|influences" static/lib_lam_renderer.js | head -30

Find expression data consumption


11043:		this.morphTargetsRelative = false;
11368:					if ( this.morphTargetsRelative ) {
11439:					if ( this.morphTargetsRelative ) {
11480:					const morphTargetsRelative = this.morphTargetsRelative;
11486:						if ( morphTargetsRelative ) {
11865:		geometry2.morphTargetsRelative = this.morphTargetsRelative;
11968:			data.data.morphTargetsRelative = this.morphTargetsRelative;
12060:		this.morphTargetsRelative = source.morphTargetsRelative;
12150:		if ( source.morphTargetInfluences !== undefined ) {
12152:			this.morphTargetInfluences = source.morphTargetInfluences.slice();
12156:		if ( source.morphTargetDictionary !== undefined ) {
12158:			this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary );
12182:				this.morphTargetInfluences = [];
12183:				this.morphTargetDictionary = {};
12189:					this.morphTargetInfluences.push( 0 );
12190:					this.morphTargetDictionary[ name ] = m;
12205:		const morphTargetsRelative = geometry.morphTargetsRelative;
12209:		const morphInfluences = this.morphTargetInfluences;
12224:				if ( morphTargetsRelative ) {
15807:		const objectInfluences = object.morphTargetInfluences;
15811:		const len = objectInfluences.length + 1; // All influences + the baseInfluenceSum
15895:		const objectInfluences = object.morphTargetInfluences;
15897:		const len = objectInfluences.length + 1; // morphBaseInfluence + all influences
15915:		const morphBaseInfluence = this.geometry.morphTargetsRelative ? 1 : 1 - morphInfluencesSum;
17893:				this.morphTargetInfluences = [];
17894:				this.morphTargetDictionary = {};
17900:					this.morphTargetInfluences.push( 0 );
17901:					this.morphTargetDictionary[ name ] = m;
18163:				this.morphTargetInfluences = [];
18164:				this.morphTargetDictionary = {};


grep -n "getExpressionData\|this.expressionData\|\.expressionData" static/lib_lam_renderer.js

Find expressionData usage in renderer


152271:                renderer.getExpressionData = options === null || options === void 0 ? void 0 : options.getExpressionData;
152323:        this.expressionData = {};
152436:                if (this.getExpressionData) {
152437:                    this.expressionData = this.updateBS(this.getExpressionData());
152446:                        for (const morphTarget in this.expressionData) {
152450:                                this.expressionData[morphTarget] =
152451:                                    this.expressionData[morphTarget] * scale + offset;
152483:        // const sortedKeys = Object.keys(this.expressionData).sort()
152484:        // const entries = sortedKeys.map((key) => this.expressionData[key])
152489:            this.viewer.splatMesh.bsWeight = this.expressionData;
152500:                            const data = this.expressionData[morphTarget];