Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: CI

on:
pull_request:
push:
branches: [main]

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
check:
name: Lint, test, build
runs-on: ubuntu-latest
defaults:
run:
working-directory: avatar
env:
# Nothing here launches Electron: the unit-tested modules deliberately
# avoid require('electron'), so the ~100 MB binary is dead weight.
ELECTRON_SKIP_BINARY_DOWNLOAD: 1
steps:
- name: Checkout
uses: actions/checkout@v5

- name: Set up Node
uses: actions/setup-node@v5
with:
# Vite 7 needs ^20.19.0 || >=22.12.0.
node-version: 22
cache: npm
cache-dependency-path: avatar/package-lock.json

- name: Install
run: npm ci

- name: Lint
run: npm run lint

- name: Test
run: npm test

- name: Build
run: npm run build
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-ffb38a?style=flat-square" alt="License MIT" /></a>
<a href="https://vrm.dev/ja/"><img src="https://img.shields.io/badge/BOOTH-VRM-f87171?style=flat-square" alt="BOOTH VRM" /></a>
<a href="https://hub.vroid.com/en/"><img src="https://img.shields.io/badge/VRoid-Hub-f0d78c?style=flat-square" alt="VRoid Hub" /></a>
<a href="https://github.com/ARPAHLS/avatar/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/ARPAHLS/avatar/ci.yml?branch=main&style=flat-square&label=CI" alt="CI" /></a>
</div>

<p align="center">
Expand Down
38 changes: 37 additions & 1 deletion avatar/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{js,jsx}'],
// Renderer — browser globals, React rules.
files: ['src/**/*.{js,jsx}'],
extends: [
js.configs.recommended,
reactHooks.configs['recommended-latest'],
Expand All @@ -26,4 +27,39 @@ export default defineConfig([
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
},
},
{
// Electron main / preload — CommonJS, Node globals. Previously matched no
// config block at all, so main.cjs and the VRoid Hub modules were parsed
// but had zero rules applied.
files: ['electron/**/*.cjs'],
extends: [js.configs.recommended],
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'commonjs',
globals: globals.node,
},
rules: {
'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }],
},
},
{
// Build tooling — Node globals. The test suites need nothing extra: they
// require('node:test') rather than relying on injected globals.
files: ['*.config.js', 'scripts/**/*.{js,mjs}'],
extends: [js.configs.recommended],
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: globals.node,
},
},
{
files: ['scripts/**/*.cjs'],
extends: [js.configs.recommended],
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'commonjs',
globals: globals.node,
},
},
])
2 changes: 1 addition & 1 deletion avatar/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"icons": "node scripts/make-icons.mjs",
"test": "node --test electron/*.test.cjs",
"dist:win": "npm run icons && cross-env AVATAR_SHIP=1 vite build && cross-env CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --win nsis",
"lint": "eslint .",
"lint": "eslint . --max-warnings=0",
"preview": "vite preview"
},
"dependencies": {
Expand Down
6 changes: 1 addition & 5 deletions avatar/scripts/make-icons.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,7 @@ console.log('wrote build/icon.ico', ico.length, 'bytes');

// NSIS assets: copy public PNGs into build/ and emit the BMPs electron-builder expects.
// Sizes are already NSIS-correct (top 150×57, side 164×314); keep them unversioned.
const installerAssets = [
{ name: 'installer_top', width: 150, height: 57 },
{ name: 'installer_side', width: 164, height: 314 },
];

// The authoritative list is the $assets array inside the PowerShell block below.
const installerPs = `
Add-Type -AssemblyName System.Drawing
$public = '${path.join(root, 'public').replace(/'/g, "''")}'
Expand Down
1 change: 0 additions & 1 deletion avatar/src/components/AvatarStage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,6 @@ export function AvatarStage() {
<VoicePanel
audioSourceId={audioSourceId}
setAudioSourceId={setAudioSourceId}
audioFile={audioFile}
setAudioFile={setAudioFile}
windowSourceId={windowSourceId}
setWindowSourceId={setWindowSourceId}
Expand Down
12 changes: 8 additions & 4 deletions avatar/src/components/avatar/VrmAvatar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ export function VrmAvatar({
return undefined;
}

// Captured once so the cleanup detaches from the same group this run
// attached to, even if the ref has been repointed by the time it fires.
const container = group.current;

loader.load(
modelPath,
(gltf) => {
Expand All @@ -58,7 +62,7 @@ export function VrmAvatar({
VRMUtils.rotateVRM0(vrmData);

loaded = vrmData;
group.current?.add(vrmData.scene);
container?.add(vrmData.scene);
setVrm(vrmData);
onLoaded?.();
},
Expand All @@ -72,9 +76,9 @@ export function VrmAvatar({
return () => {
disposed = true;
setVrm(null);
if (group.current) {
while (group.current.children.length > 0) {
group.current.remove(group.current.children[0]);
if (container) {
while (container.children.length > 0) {
container.remove(container.children[0]);
}
}
// Detaching a scene leaves its geometries, materials and textures on the
Expand Down
1 change: 0 additions & 1 deletion avatar/src/components/panels/VoicePanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { PanelSelect } from '../ui/PanelPrimitives';
export function VoicePanel({
audioSourceId,
setAudioSourceId,
audioFile,
setAudioFile,
windowSourceId,
setWindowSourceId,
Expand Down