@tmustier/pi-nes 0.2.10 → 0.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/extensions/nes/config.ts +13 -2
- package/extensions/nes/index.ts +14 -26
- package/package.json +1 -1
- package/spec.md +4 -2
package/README.md
CHANGED
|
@@ -54,14 +54,15 @@ On first run, you'll be prompted to set your ROM directory and display quality.
|
|
|
54
54
|
|
|
55
55
|
## Configuration
|
|
56
56
|
|
|
57
|
-
Config is stored at `~/.pi/nes/config.json`. Use `/nes config` for
|
|
57
|
+
Config is stored at `~/.pi/nes/config.json`. Use `/nes config` for quick setup.
|
|
58
58
|
|
|
59
59
|
```json
|
|
60
60
|
{
|
|
61
61
|
"romDir": "/roms/nes",
|
|
62
62
|
"saveDir": "/roms/nes/saves",
|
|
63
63
|
"renderer": "image",
|
|
64
|
-
"
|
|
64
|
+
"imageQuality": "balanced",
|
|
65
|
+
"pixelScale": 1.0,
|
|
65
66
|
"keybindings": {
|
|
66
67
|
"up": ["up", "w"],
|
|
67
68
|
"down": ["down", "s"],
|
|
@@ -82,7 +83,8 @@ Config is stored at `~/.pi/nes/config.json`. Use `/nes config` for guided setup.
|
|
|
82
83
|
| `romDir` | `/roms/nes` | Where to look for ROM files |
|
|
83
84
|
| `saveDir` | `/roms/nes/saves` | Where to store battery saves (defaults to `<romDir>/saves`) |
|
|
84
85
|
| `renderer` | `"image"` | `"image"` (Kitty graphics) or `"text"` (ANSI) |
|
|
85
|
-
| `
|
|
86
|
+
| `imageQuality` | `"balanced"` | `"balanced"` (30 fps) or `"high"` (60 fps) |
|
|
87
|
+
| `pixelScale` | `1.0` | Display scale (0.5–4.0) |
|
|
86
88
|
|
|
87
89
|
## Terminal Support
|
|
88
90
|
|
package/extensions/nes/config.ts
CHANGED
|
@@ -5,12 +5,14 @@ import { DEFAULT_INPUT_MAPPING, type InputMapping } from "./input-map.js";
|
|
|
5
5
|
import { normalizePath } from "./paths.js";
|
|
6
6
|
|
|
7
7
|
export type RendererMode = "image" | "text";
|
|
8
|
+
export type ImageQuality = "balanced" | "high";
|
|
8
9
|
|
|
9
10
|
export interface NesConfig {
|
|
10
11
|
romDir: string;
|
|
11
12
|
saveDir: string;
|
|
12
13
|
enableAudio: boolean;
|
|
13
14
|
renderer: RendererMode;
|
|
15
|
+
imageQuality: ImageQuality;
|
|
14
16
|
pixelScale: number;
|
|
15
17
|
keybindings: InputMapping;
|
|
16
18
|
}
|
|
@@ -26,7 +28,8 @@ export const DEFAULT_CONFIG: NesConfig = {
|
|
|
26
28
|
saveDir: getDefaultSaveDir(DEFAULT_ROM_DIR),
|
|
27
29
|
enableAudio: false,
|
|
28
30
|
renderer: "image",
|
|
29
|
-
|
|
31
|
+
imageQuality: "balanced",
|
|
32
|
+
pixelScale: 1.0,
|
|
30
33
|
keybindings: cloneMapping(DEFAULT_INPUT_MAPPING),
|
|
31
34
|
};
|
|
32
35
|
|
|
@@ -35,6 +38,7 @@ interface RawConfig {
|
|
|
35
38
|
saveDir?: unknown;
|
|
36
39
|
enableAudio?: unknown;
|
|
37
40
|
renderer?: unknown;
|
|
41
|
+
imageQuality?: unknown;
|
|
38
42
|
pixelScale?: unknown;
|
|
39
43
|
keybindings?: unknown;
|
|
40
44
|
}
|
|
@@ -54,12 +58,15 @@ export function normalizeConfig(raw: unknown): NesConfig {
|
|
|
54
58
|
typeof parsed.saveDir === "string" && parsed.saveDir.length > 0
|
|
55
59
|
? normalizePath(parsed.saveDir, saveDirFallback)
|
|
56
60
|
: saveDirFallback;
|
|
61
|
+
const imageQuality = normalizeImageQuality(parsed.imageQuality);
|
|
62
|
+
const pixelScale = normalizePixelScale(parsed.pixelScale);
|
|
57
63
|
return {
|
|
58
64
|
romDir,
|
|
59
65
|
saveDir,
|
|
60
66
|
enableAudio: typeof parsed.enableAudio === "boolean" ? parsed.enableAudio : DEFAULT_CONFIG.enableAudio,
|
|
61
67
|
renderer: parsed.renderer === "text" ? "text" : DEFAULT_CONFIG.renderer,
|
|
62
|
-
|
|
68
|
+
imageQuality,
|
|
69
|
+
pixelScale,
|
|
63
70
|
keybindings: normalizeKeybindings(parsed.keybindings),
|
|
64
71
|
};
|
|
65
72
|
}
|
|
@@ -111,6 +118,10 @@ function normalizePixelScale(raw: unknown): number {
|
|
|
111
118
|
return Math.min(4, Math.max(0.5, raw));
|
|
112
119
|
}
|
|
113
120
|
|
|
121
|
+
function normalizeImageQuality(raw: unknown): ImageQuality {
|
|
122
|
+
return raw === "high" ? "high" : "balanced";
|
|
123
|
+
}
|
|
124
|
+
|
|
114
125
|
function normalizeKeybindings(raw: unknown): InputMapping {
|
|
115
126
|
const mapping = cloneMapping(DEFAULT_INPUT_MAPPING);
|
|
116
127
|
if (!raw || typeof raw !== "object") {
|
package/extensions/nes/index.ts
CHANGED
|
@@ -18,7 +18,8 @@ import { NesSession } from "./nes-session.js";
|
|
|
18
18
|
import { listRoms } from "./roms.js";
|
|
19
19
|
import { loadSram } from "./saves.js";
|
|
20
20
|
|
|
21
|
-
const
|
|
21
|
+
const IMAGE_RENDER_INTERVAL_BALANCED_MS = 1000 / 30;
|
|
22
|
+
const IMAGE_RENDER_INTERVAL_HIGH_MS = 1000 / 60;
|
|
22
23
|
const TEXT_RENDER_INTERVAL_MS = 1000 / 60;
|
|
23
24
|
|
|
24
25
|
let activeSession: NesSession | null = null;
|
|
@@ -174,35 +175,17 @@ async function configureWithWizard(
|
|
|
174
175
|
}
|
|
175
176
|
}
|
|
176
177
|
|
|
177
|
-
const qualityChoice = await ctx.ui.select("Quality
|
|
178
|
-
"Balanced (
|
|
179
|
-
"
|
|
180
|
-
"High (1.5) - sharper",
|
|
181
|
-
"Custom...",
|
|
178
|
+
const qualityChoice = await ctx.ui.select("Quality", [
|
|
179
|
+
"Balanced (recommended) — 30 fps",
|
|
180
|
+
"High — 60 fps",
|
|
182
181
|
]);
|
|
183
182
|
if (!qualityChoice) {
|
|
184
183
|
return false;
|
|
185
184
|
}
|
|
186
185
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
} else if (qualityChoice.startsWith("Balanced")) {
|
|
191
|
-
pixelScale = 1.2;
|
|
192
|
-
} else if (qualityChoice.startsWith("High")) {
|
|
193
|
-
pixelScale = 1.5;
|
|
194
|
-
} else {
|
|
195
|
-
const pixelScaleInput = await ctx.ui.input("Pixel scale (0.5 - 4)", config.pixelScale.toString());
|
|
196
|
-
if (pixelScaleInput === undefined) {
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
const parsed = pixelScaleInput.trim() === "" ? config.pixelScale : Number(pixelScaleInput);
|
|
200
|
-
if (Number.isNaN(parsed)) {
|
|
201
|
-
ctx.ui.notify("Pixel scale must be a number.", "error");
|
|
202
|
-
return false;
|
|
203
|
-
}
|
|
204
|
-
pixelScale = parsed;
|
|
205
|
-
}
|
|
186
|
+
const isHighQuality = qualityChoice.startsWith("High");
|
|
187
|
+
const imageQuality = isHighQuality ? "high" : "balanced";
|
|
188
|
+
const pixelScale = config.pixelScale;
|
|
206
189
|
|
|
207
190
|
const defaultSaveDir = getDefaultSaveDir(config.romDir);
|
|
208
191
|
const shouldSyncSaveDir = config.saveDir === defaultSaveDir;
|
|
@@ -211,6 +194,7 @@ async function configureWithWizard(
|
|
|
211
194
|
...config,
|
|
212
195
|
romDir,
|
|
213
196
|
saveDir,
|
|
197
|
+
imageQuality,
|
|
214
198
|
pixelScale,
|
|
215
199
|
});
|
|
216
200
|
await saveConfig(normalized);
|
|
@@ -318,7 +302,11 @@ async function attachSession(
|
|
|
318
302
|
}
|
|
319
303
|
: undefined;
|
|
320
304
|
|
|
321
|
-
const renderIntervalMs = config.renderer === "image"
|
|
305
|
+
const renderIntervalMs = config.renderer === "image"
|
|
306
|
+
? config.imageQuality === "high"
|
|
307
|
+
? IMAGE_RENDER_INTERVAL_HIGH_MS
|
|
308
|
+
: IMAGE_RENDER_INTERVAL_BALANCED_MS
|
|
309
|
+
: TEXT_RENDER_INTERVAL_MS;
|
|
322
310
|
session.setRenderIntervalMs(renderIntervalMs);
|
|
323
311
|
|
|
324
312
|
await ctx.ui.custom(
|
package/package.json
CHANGED
package/spec.md
CHANGED
|
@@ -70,7 +70,8 @@ pi-nes/
|
|
|
70
70
|
- `saveDir`
|
|
71
71
|
- `enableAudio`
|
|
72
72
|
- `renderer` ("image" or "text")
|
|
73
|
-
- `
|
|
73
|
+
- `imageQuality` ("balanced" or "high", controls render fps)
|
|
74
|
+
- `pixelScale` (float, e.g. 1.0)
|
|
74
75
|
- `keybindings` (button-to-keys map, e.g. `{ "a": ["z"] }`)
|
|
75
76
|
|
|
76
77
|
Note: audio output is currently disabled; setting `enableAudio` will show a warning.
|
|
@@ -88,5 +89,6 @@ Note: audio output is currently disabled; setting `enableAudio` will show a warn
|
|
|
88
89
|
- Audio: disabled (no safe dependency selected).
|
|
89
90
|
- Default ROM dir: `/roms/nes` (configurable).
|
|
90
91
|
- Default core: `native`.
|
|
91
|
-
- Default
|
|
92
|
+
- Default image quality: `balanced` (30 fps).
|
|
93
|
+
- Default pixel scale: `1.0`.
|
|
92
94
|
- Default save dir: `/roms/nes/saves` (configurable).
|