Skip to content

Commit

Permalink
Merge branch 'main' into fix/store-state-typing
Browse files Browse the repository at this point in the history
  • Loading branch information
sevenc-nanashi authored Jan 1, 2025
2 parents c5be115 + 4149b24 commit 4458c77
Show file tree
Hide file tree
Showing 46 changed files with 1,278 additions and 464 deletions.
2 changes: 1 addition & 1 deletion .env.production
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ VITE_DEFAULT_ENGINE_INFOS=`[
}
]`
VITE_OFFICIAL_WEBSITE_URL=https://voicevox.hiroshiba.jp/
VITE_LATEST_UPDATE_INFOS_URL=https://raw.githubusercontent.com/VOICEVOX/voicevox_blog/master/src/data/updateInfos.json
VITE_LATEST_UPDATE_INFOS_URL=https://voicevox.hiroshiba.jp/updateInfos.json
VITE_GTM_CONTAINER_ID=GTM-DUMMY
2 changes: 1 addition & 1 deletion .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ VITE_DEFAULT_ENGINE_INFOS=`[
}
]`
VITE_OFFICIAL_WEBSITE_URL=https://voicevox.hiroshiba.jp/
VITE_LATEST_UPDATE_INFOS_URL=https://raw.githubusercontent.com/VOICEVOX/voicevox_blog/master/src/data/updateInfos.json
VITE_LATEST_UPDATE_INFOS_URL=https://voicevox.hiroshiba.jp/updateInfos.json
VITE_GTM_CONTAINER_ID=GTM-DUMMY
17 changes: 17 additions & 0 deletions .env.test-electron-default-vvpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# VVPPデフォルトエンジンでのテスト用の.envファイル。

VITE_APP_NAME=voicevox
VITE_DEFAULT_ENGINE_INFOS=`[
{
"type": "downloadVvpp",
"name": "VOICEVOX Nemo Engine",
"uuid": "208cf94d-43d2-4cf5-abc0-9783cac36d29",
"executionEnabled": true,
"executionArgs": [],
"host": "http://127.0.0.1:50121",
"latestUrl": "https://voicevox.hiroshiba.jp/nemoLatestDefaultEngineInfos.json"
}
]`
VITE_OFFICIAL_WEBSITE_URL=https://voicevox.hiroshiba.jp/
VITE_LATEST_UPDATE_INFOS_URL=https://voicevox.hiroshiba.jp/updateInfos.json
VITE_GTM_CONTAINER_ID=GTM-DUMMY
8 changes: 0 additions & 8 deletions .github/actions/setup-environment/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,5 @@ runs:
restore-keys: |
${{ env.cache-version }}-${{ runner.os }}--electron-builder-cache-
- name: Cache external dependencies
uses: actions/cache@v4
with:
path: ./build/vendored
key: ${{ env.cache-version }}-${{ runner.os }}--vendored-${{ hashFiles('build/*.js') }}
restore-keys: |
${{ env.cache-version }}-${{ runner.os }}--vendored-
- shell: bash
run: npm ci
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ on:
default: false

env:
VOICEVOX_ENGINE_VERSION: 0.22.1
VOICEVOX_RESOURCE_VERSION: 0.22.1
VOICEVOX_ENGINE_VERSION: 0.22.2
VOICEVOX_RESOURCE_VERSION: 0.22.2
VOICEVOX_EDITOR_VERSION:
|- # releaseタグ名か、workflow_dispatchでのバージョン名か、999.999.999-developが入る
${{ github.event.release.tag_name || github.event.inputs.version || '999.999.999-develop' }}
Expand Down
29 changes: 29 additions & 0 deletions .github/workflows/merge_gatekeeper.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: "Merge Gatekeeper"

# auto mergeとmerge queue用のチェッカー。
# Approve数が足りているか、すべてのテストが通っているかを確認します。
# 詳細: https://github.com/VOICEVOX/merge-gatekeeper

on:
pull_request_target:
types: [auto_merge_enabled]
merge_group:
types: [checks_requested]

jobs:
merge_gatekeeper:
runs-on: ubuntu-latest
steps:
- uses: voicevox/merge-gatekeeper@main
with:
token: ${{ secrets.GATEKEEPER_TOKEN }}
required_score: 2
score_rules: |
#maintainer: 2
#reviewer: 1
- uses: upsidr/merge-gatekeeper@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
self: merge_gatekeeper
# https://github.com/upsidr/merge-gatekeeper/issues/71#issuecomment-1660607977
ref: ${{ github.event.pull_request && github.event.pull_request.head.ref || github.ref }}
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ on:
pull_request:
branches:
- "**"
merge_group:
types: [checks_requested]
workflow_dispatch:

env:
Expand Down
5 changes: 5 additions & 0 deletions public/updateInfos.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
[
{
"version": "0.22.3",
"descriptions": ["キャラクター「ぞん子」を追加"],
"contributors": []
},
{
"version": "0.22.2",
"descriptions": ["キャラクター「中部つるぎ」を追加"],
Expand Down
3 changes: 2 additions & 1 deletion src/backend/browser/browserConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { BaseConfigManager, Metadata } from "@/backend/common/ConfigManager";
import { ConfigType, EngineId, engineSettingSchema } from "@/type/preload";
import { ensureNotNullish } from "@/helpers/errorHelper";
import { UnreachableError } from "@/type/utility";
import { isMac } from "@/helpers/platform";

const dbName = `${import.meta.env.VITE_APP_NAME}-web`;
const settingStoreKey = "config";
Expand All @@ -20,7 +21,7 @@ const defaultEngineId = EngineId(defaultEngine.uuid);
export async function getConfigManager() {
await configManagerLock.acquire("configManager", async () => {
if (!configManager) {
configManager = new BrowserConfigManager();
configManager = new BrowserConfigManager(isMac);
await configManager.initialize();
}
});
Expand Down
16 changes: 16 additions & 0 deletions src/backend/browser/fakePath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { z } from "zod";
import { uuid4 } from "@/helpers/random";

const fakePathSchema = z
.string()
.regex(/^<browser-dummy-[0-9a-f]+>-.+$/)
.brand("FakePath");
export type FakePath = z.infer<typeof fakePathSchema>;

export const isFakePath = (path: string): path is FakePath => {
return fakePathSchema.safeParse(path).success;
};

export const createFakePath = (name: string): FakePath => {
return fakePathSchema.parse(`<browser-dummy-${uuid4()}>-${name}`);
};
118 changes: 92 additions & 26 deletions src/backend/browser/fileImpl.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { directoryHandleStoreKey } from "./contract";
import { openDB } from "./browserConfig";
import { createFakePath, FakePath, isFakePath } from "./fakePath";
import { SandboxKey } from "@/type/preload";
import { failure, success } from "@/type/result";
import { createLogger } from "@/domain/frontend/log";
import { uuid4 } from "@/helpers/random";
import { normalizeError } from "@/helpers/normalizeError";
import path from "@/helpers/path";
import { ExhaustiveError } from "@/type/utility";

const log = createLogger("fileImpl");

Expand Down Expand Up @@ -113,45 +114,81 @@ const getDirectoryHandleFromDirectoryPath = async (
}
};

export type WritableFilePath =
| {
// ファイル名のみ。ダウンロードとして扱われます。
type: "nameOnly";
path: string;
}
| {
// ディレクトリ内への書き込み。
type: "child";
path: string;
}
| {
// 疑似パス。
type: "fake";
path: FakePath;
};

// NOTE: fixedExportEnabled が有効になっている GENERATE_AND_SAVE_AUDIO action では、ファイル名に加えディレクトリ名も指定された状態でfilePathが渡ってくる
// また GENERATE_AND_SAVE_ALL_AUDIO action では fixedExportEnabled の有効の有無に関わらず、ディレクトリ名も指定された状態でfilePathが渡ってくる
export const writeFileImpl: (typeof window)[typeof SandboxKey]["writeFile"] =
async (obj: { filePath: string; buffer: ArrayBuffer }) => {
const filePath = obj.filePath;
// showExportFilePicker での疑似パスが渡ってくる可能性もある。
export const writeFileImpl = async (obj: {
filePath: WritableFilePath;
buffer: ArrayBuffer;
}) => {
const filePath = obj.filePath;

if (!filePath.includes(path.SEPARATOR)) {
switch (filePath.type) {
case "fake": {
const fileHandle = fileHandleMap.get(filePath.path);
if (fileHandle == undefined) {
return failure(new Error(`ファイルが見つかりません: ${filePath.path}`));
}
const writable = await fileHandle.createWritable();
await writable.write(obj.buffer);
return writable.close().then(() => success(undefined));
}

case "nameOnly": {
const aTag = document.createElement("a");
const blob = URL.createObjectURL(new Blob([obj.buffer]));
aTag.href = blob;
aTag.download = filePath;
aTag.download = filePath.path;
document.body.appendChild(aTag);
aTag.click();
document.body.removeChild(aTag);
URL.revokeObjectURL(blob);
return success(undefined);
}

const fileName = resolveFileName(filePath);
const maybeDirectoryHandleName = resolveDirectoryName(filePath);
case "child": {
const fileName = resolveFileName(filePath.path);
const maybeDirectoryHandleName = resolveDirectoryName(filePath.path);

const directoryHandle = await getDirectoryHandleFromDirectoryPath(
maybeDirectoryHandleName,
);
const directoryHandle = await getDirectoryHandleFromDirectoryPath(
maybeDirectoryHandleName,
);

directoryHandleMap.set(maybeDirectoryHandleName, directoryHandle);
directoryHandleMap.set(maybeDirectoryHandleName, directoryHandle);

return directoryHandle
.getFileHandle(fileName, { create: true })
.then(async (fileHandle) => {
const writable = await fileHandle.createWritable();
await writable.write(obj.buffer);
return writable.close();
})
.then(() => success(undefined))
.catch((e) => {
return failure(normalizeError(e));
});
};
return directoryHandle
.getFileHandle(fileName, { create: true })
.then(async (fileHandle) => {
const writable = await fileHandle.createWritable();
await writable.write(obj.buffer);
return writable.close();
})
.then(() => success(undefined))
.catch((e) => {
return failure(normalizeError(e));
});
}
default:
throw new ExhaustiveError(filePath);
}
};

export const checkFileExistsImpl: (typeof window)[typeof SandboxKey]["checkFileExists"] =
async (filePath) => {
Expand Down Expand Up @@ -182,7 +219,7 @@ export const checkFileExistsImpl: (typeof window)[typeof SandboxKey]["checkFileE
};

// FileSystemFileHandleを保持するMap。キーは生成した疑似パス。
const fileHandleMap: Map<string, FileSystemFileHandle> = new Map();
const fileHandleMap: Map<FakePath, FileSystemFileHandle> = new Map();

// ファイル選択ダイアログを開く
// 返り値はファイルパスではなく、疑似パスを返す
Expand All @@ -201,7 +238,7 @@ export const showOpenFilePickerImpl = async (options: {
});
const paths = [];
for (const handle of handles) {
const fakePath = `<browser-dummy-${uuid4()}>-${handle.name}`;
const fakePath = createFakePath(handle.name);
fileHandleMap.set(fakePath, handle);
paths.push(fakePath);
}
Expand All @@ -214,6 +251,9 @@ export const showOpenFilePickerImpl = async (options: {

// 指定した疑似パスのファイルを読み込む
export const readFileImpl = async (filePath: string) => {
if (!isFakePath(filePath)) {
return failure(new Error(`疑似パスではありません: ${filePath}`));
}
const fileHandle = fileHandleMap.get(filePath);
if (fileHandle == undefined) {
return failure(new Error(`ファイルが見つかりません: ${filePath}`));
Expand All @@ -222,3 +262,29 @@ export const readFileImpl = async (filePath: string) => {
const buffer = await file.arrayBuffer();
return success(buffer);
};

// ファイル選択ダイアログを開く
// 返り値はファイルパスではなく、疑似パスを返す
export const showExportFilePickerImpl: (typeof window)[typeof SandboxKey]["showExportFileDialog"] =
async (obj: {
defaultPath?: string;
extensionName: string;
extensions: string[];
title: string;
}) => {
const handle = await showSaveFilePicker({
suggestedName: obj.defaultPath,
types: [
{
description: obj.extensions.join("、"),
accept: {
"application/octet-stream": obj.extensions.map((ext) => `.${ext}`),
},
},
],
});
const fakePath = createFakePath(handle.name);
fileHandleMap.set(fakePath, handle);

return fakePath;
};
Loading

0 comments on commit 4458c77

Please sign in to comment.