update docs and config

This commit is contained in:
2026-02-15 17:29:27 -08:00
parent 8ebf6f02ec
commit 20f5de1cf7
7 changed files with 62 additions and 9 deletions

View File

@@ -46,12 +46,19 @@ The `subminer` wrapper uses a [Bun](https://bun.sh) shebang, so `bun` must be on
### From Source ### From Source
```bash ```bash
git clone https://github.com/ksyasuda/SubMiner.git git clone --recurse-submodules https://github.com/ksyasuda/SubMiner.git
cd SubMiner cd SubMiner
make build make build
make install make install
``` ```
If you already cloned without submodules:
```bash
cd SubMiner
git submodule update --init --recursive
```
For macOS builds, signing, and platform-specific details, see [docs/installation.md](docs/installation.md). For macOS builds, signing, and platform-specific details, see [docs/installation.md](docs/installation.md).
## Quick Start ## Quick Start

View File

@@ -149,6 +149,7 @@
// Primary and secondary subtitle styling. // Primary and secondary subtitle styling.
// ========================================== // ==========================================
"subtitleStyle": { "subtitleStyle": {
"enableJlpt": false,
"fontFamily": "Noto Sans CJK JP Regular, Noto Sans CJK JP, Arial Unicode MS, Arial, sans-serif", "fontFamily": "Noto Sans CJK JP Regular, Noto Sans CJK JP, Arial Unicode MS, Arial, sans-serif",
"fontSize": 35, "fontSize": 35,
"fontColor": "#cad3f5", "fontColor": "#cad3f5",
@@ -157,6 +158,13 @@
"backgroundColor": "rgba(54, 58, 79, 0.5)", "backgroundColor": "rgba(54, 58, 79, 0.5)",
"nPlusOneColor": "#c6a0f6", "nPlusOneColor": "#c6a0f6",
"knownWordColor": "#a6da95", "knownWordColor": "#a6da95",
"jlptColors": {
"N1": "#ed8796",
"N2": "#f5a97f",
"N3": "#f9e2af",
"N4": "#a6e3a1",
"N5": "#8aadf4"
},
"secondary": { "secondary": {
"fontSize": 24, "fontSize": 24,
"fontColor": "#ffffff", "fontColor": "#ffffff",

View File

@@ -552,12 +552,26 @@ See `config.example.jsonc` for detailed configuration options.
| `fontWeight` | string | CSS font-weight, e.g. `"bold"`, `"normal"`, `"600"` (default: `"normal"`) | | `fontWeight` | string | CSS font-weight, e.g. `"bold"`, `"normal"`, `"600"` (default: `"normal"`) |
| `fontStyle` | string | `"normal"` or `"italic"` (default: `"normal"`) | | `fontStyle` | string | `"normal"` or `"italic"` (default: `"normal"`) |
| `backgroundColor` | string | Any CSS color, including `"transparent"` (default: `"rgba(54, 58, 79, 0.5)"`) | | `backgroundColor` | string | Any CSS color, including `"transparent"` (default: `"rgba(54, 58, 79, 0.5)"`) |
| `enableJlpt` | boolean | Enable JLPT level underline styling (`false` by default) |
| `nPlusOneColor` | string | Existing n+1 highlight color (default: `#c6a0f6`) |
| `knownWordColor` | string | Existing known-word highlight color (default: `#a6da95`) |
| `jlptColors` | object | JLPT level underline colors object (`N1`..`N5`) |
| `secondary` | object | Override any of the above for secondary subtitles (optional) | | `secondary` | object | Override any of the above for secondary subtitles (optional) |
Secondary subtitle defaults: `fontSize: 24`, `fontColor: "#ffffff"`, `backgroundColor: "transparent"`. Any property not set in `secondary` falls back to the CSS defaults. Secondary subtitle defaults: `fontSize: 24`, `fontColor: "#ffffff"`, `backgroundColor: "transparent"`. Any property not set in `secondary` falls back to the CSS defaults.
**See `config.example.jsonc`** for the complete list of subtitle style configuration options. **See `config.example.jsonc`** for the complete list of subtitle style configuration options.
`jlptColors` keys are:
| Key | Default | Description |
| ---- | --------- | ---------------------------------------- |
| `N1` | `#ed8796` | JLPT N1 underline color |
| `N2` | `#f5a97f` | JLPT N2 underline color |
| `N3` | `#f9e2af` | JLPT N3 underline color |
| `N4` | `#a6e3a1` | JLPT N4 underline color |
| `N5` | `#8aadf4` | JLPT N5 underline color |
### Texthooker ### Texthooker
Control whether the browser opens automatically when texthooker starts: Control whether the browser opens automatically when texthooker starts:

View File

@@ -149,6 +149,7 @@
// Primary and secondary subtitle styling. // Primary and secondary subtitle styling.
// ========================================== // ==========================================
"subtitleStyle": { "subtitleStyle": {
"enableJlpt": false,
"fontFamily": "Noto Sans CJK JP Regular, Noto Sans CJK JP, Arial Unicode MS, Arial, sans-serif", "fontFamily": "Noto Sans CJK JP Regular, Noto Sans CJK JP, Arial Unicode MS, Arial, sans-serif",
"fontSize": 35, "fontSize": 35,
"fontColor": "#cad3f5", "fontColor": "#cad3f5",
@@ -157,6 +158,13 @@
"backgroundColor": "rgba(54, 58, 79, 0.5)", "backgroundColor": "rgba(54, 58, 79, 0.5)",
"nPlusOneColor": "#c6a0f6", "nPlusOneColor": "#c6a0f6",
"knownWordColor": "#a6da95", "knownWordColor": "#a6da95",
"jlptColors": {
"N1": "#ed8796",
"N2": "#f5a97f",
"N3": "#f9e2af",
"N4": "#a6e3a1",
"N5": "#8aadf4"
},
"secondary": { "secondary": {
"fontSize": 24, "fontSize": 24,
"fontColor": "#ffffff", "fontColor": "#ffffff",

View File

@@ -183,6 +183,10 @@ function isKanaChar(char: string): boolean {
); );
} }
/**
* Detects repeated-kana speech-like tokens (e.g. 「ああああ」, 「ははは」, 「うーん」 style patterns)
* so they are not JLPT-labeled when they are mostly expressive particles/sfx.
*/
function isRepeatedKanaSfx(text: string): boolean { function isRepeatedKanaSfx(text: string): boolean {
const normalized = text.trim(); const normalized = text.trim();
if (!normalized) { if (!normalized) {

View File

@@ -470,20 +470,23 @@ function loadSubtitlePosition(): SubtitlePosition | null {
function getJlptDictionarySearchPaths(): string[] { function getJlptDictionarySearchPaths(): string[] {
const homeDir = os.homedir(); const homeDir = os.homedir();
const dictionaryRoots = [ const dictionaryRoots = [
// Source checkout paths (development + source tree) // Development/runtime source trees where the repo is checked out.
path.join(__dirname, "..", "..", "vendor", "yomitan-jlpt-vocab"), path.join(__dirname, "..", "..", "vendor", "yomitan-jlpt-vocab"),
path.join(app.getAppPath(), "vendor", "yomitan-jlpt-vocab"), path.join(app.getAppPath(), "vendor", "yomitan-jlpt-vocab"),
// Runtime package bundle paths
// Packaged app resources (Electron build output layout).
path.join(process.resourcesPath, "yomitan-jlpt-vocab"), path.join(process.resourcesPath, "yomitan-jlpt-vocab"),
path.join(process.resourcesPath, "app.asar", "vendor", "yomitan-jlpt-vocab"), path.join(process.resourcesPath, "app.asar", "vendor", "yomitan-jlpt-vocab"),
// User-configurable override locations
// User override/config directories for manually installed dictionaries.
USER_DATA_PATH, USER_DATA_PATH,
app.getPath("userData"), app.getPath("userData"),
path.join(homeDir, ".config", "SubMiner"), path.join(homeDir, ".config", "SubMiner"),
path.join(homeDir, ".config", "subminer"), path.join(homeDir, ".config", "subminer"),
path.join(homeDir, "Library", "Application Support", "SubMiner"), path.join(homeDir, "Library", "Application Support", "SubMiner"),
path.join(homeDir, "Library", "Application Support", "subminer"), path.join(homeDir, "Library", "Application Support", "subminer"),
// CLI invocation path (when launched from project root)
// Last-resort fallback: current working directory (local CLI/test runs).
process.cwd(), process.cwd(),
]; ];

View File

@@ -55,10 +55,19 @@ test("computeWordClass preserves known and n+1 classes while adding JLPT classes
}); });
test("JLPT CSS rules use underline-only styling in renderer stylesheet", () => { test("JLPT CSS rules use underline-only styling in renderer stylesheet", () => {
const cssText = fs.readFileSync( const distCssPath = path.join(process.cwd(), "dist", "renderer", "style.css");
path.join(process.cwd(), "dist", "renderer", "style.css"), const srcCssPath = path.join(process.cwd(), "src", "renderer", "style.css");
"utf-8",
const cssPath = fs.existsSync(distCssPath)
? distCssPath
: srcCssPath;
if (!fs.existsSync(cssPath)) {
assert.fail(
"JLPT CSS file missing. Run `pnpm run build` first, or ensure src/renderer/style.css exists.",
); );
}
const cssText = fs.readFileSync(cssPath, "utf-8");
for (let level = 1; level <= 5; level += 1) { for (let level = 1; level <= 5; level += 1) {
const block = extractClassBlock(cssText, level); const block = extractClassBlock(cssText, level);