@tmustier/pi-nes 0.2.34 → 0.2.35
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 +9 -3
- package/extensions/nes/index.ts +81 -1
- package/extensions/nes/native/nes-core/Cargo.lock +814 -1
- package/extensions/nes/native/nes-core/Cargo.toml +5 -0
- package/extensions/nes/native/nes-core/index.d.ts +1 -0
- package/extensions/nes/native/nes-core/index.node +0 -0
- package/extensions/nes/native/nes-core/native.d.ts +1 -0
- package/extensions/nes/native/nes-core/package.json +3 -1
- package/extensions/nes/native/nes-core/src/audio_cpal.rs +217 -0
- package/extensions/nes/native/nes-core/src/lib.rs +47 -1
- package/extensions/nes/nes-core.ts +6 -3
- package/package.json +1 -1
- package/spec.md +1 -1
package/README.md
CHANGED
|
@@ -52,11 +52,12 @@ On first run, you'll be prompted to set your ROM directory and display quality.
|
|
|
52
52
|
| `/nes` | Pick a ROM or reattach to running session |
|
|
53
53
|
| `/nes <path>` | Load a specific ROM file |
|
|
54
54
|
| `/nes config` | Configure ROM directory and quality |
|
|
55
|
+
| `/nes-config` | Toggle audio + access advanced config options |
|
|
55
56
|
| `/nes debug` | Show FPS and memory stats |
|
|
56
57
|
|
|
57
58
|
## Configuration
|
|
58
59
|
|
|
59
|
-
Config is stored at `~/.pi/nes/config.json`. Use `/nes config` for quick setup.
|
|
60
|
+
Config is stored at `~/.pi/nes/config.json`. Use `/nes config` for quick setup, or `/nes-config` to toggle audio inline and access advanced options.
|
|
60
61
|
|
|
61
62
|
```json
|
|
62
63
|
{
|
|
@@ -65,6 +66,7 @@ Config is stored at `~/.pi/nes/config.json`. Use `/nes config` for quick setup.
|
|
|
65
66
|
"renderer": "image",
|
|
66
67
|
"imageQuality": "balanced",
|
|
67
68
|
"videoFilter": "ntsc-composite",
|
|
69
|
+
"enableAudio": false,
|
|
68
70
|
"pixelScale": 1.0,
|
|
69
71
|
"keybindings": {
|
|
70
72
|
"up": ["up", "w"],
|
|
@@ -88,6 +90,7 @@ Config is stored at `~/.pi/nes/config.json`. Use `/nes config` for quick setup.
|
|
|
88
90
|
| `renderer` | `"image"` | `"image"` (Kitty graphics) or `"text"` (ANSI) |
|
|
89
91
|
| `imageQuality` | `"balanced"` | `"balanced"` (30 fps) or `"high"` (60 fps) |
|
|
90
92
|
| `videoFilter` | `"ntsc-composite"` | `"off"`, `"ntsc-composite"`, `"ntsc-svideo"`, `"ntsc-rgb"` |
|
|
93
|
+
| `enableAudio` | `false` | Enable audio output (requires native core built with `audio-cpal`) |
|
|
91
94
|
| `pixelScale` | `1.0` | Display scale (0.5–4.0) |
|
|
92
95
|
|
|
93
96
|
`videoFilter` applies a lightweight CRT/NTSC-inspired pass (horizontal bleed + scanlines). It runs in the native core and is optional.
|
|
@@ -109,8 +112,8 @@ Set `"renderer": "text"` if you prefer the ANSI renderer or have display issues.
|
|
|
109
112
|
|
|
110
113
|
## Limitations
|
|
111
114
|
|
|
112
|
-
- **
|
|
113
|
-
- **No save
|
|
115
|
+
- **Audio is opt-in** — Requires building the native core with `audio-cpal` and setting `enableAudio: true`
|
|
116
|
+
- **No auto-save** — Save manually just like you would with the original NES (battery-backed SRAM)
|
|
114
117
|
|
|
115
118
|
## Vendored Dependencies
|
|
116
119
|
|
|
@@ -133,6 +136,9 @@ npm install
|
|
|
133
136
|
cd extensions/nes/native/nes-core
|
|
134
137
|
npm install && npm run build
|
|
135
138
|
|
|
139
|
+
# Build the NES core with audio (optional)
|
|
140
|
+
npm run build:audio
|
|
141
|
+
|
|
136
142
|
# Build shared memory renderer (optional, faster on Kitty)
|
|
137
143
|
cd ../kitty-shm
|
|
138
144
|
npm install && npm run build
|
package/extensions/nes/index.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import type { ExtensionAPI, ExtensionCommandContext } from "@mariozechner/pi-coding-agent";
|
|
3
|
+
import type { ExtensionAPI, ExtensionCommandContext, Theme } from "@mariozechner/pi-coding-agent";
|
|
4
|
+
import { DynamicBorder, getSettingsListTheme } from "@mariozechner/pi-coding-agent";
|
|
5
|
+
import { Container, SettingsList, Spacer, Text, type SettingItem } from "@mariozechner/pi-tui";
|
|
4
6
|
import { NesOverlayComponent } from "./nes-component.js";
|
|
5
7
|
import { createNesCore } from "./nes-core.js";
|
|
6
8
|
import {
|
|
@@ -12,6 +14,7 @@ import {
|
|
|
12
14
|
loadConfig,
|
|
13
15
|
normalizeConfig,
|
|
14
16
|
saveConfig,
|
|
17
|
+
type NesConfig,
|
|
15
18
|
type VideoFilter,
|
|
16
19
|
} from "./config.js";
|
|
17
20
|
import { displayPath, resolvePathInput } from "./paths.js";
|
|
@@ -203,12 +206,89 @@ async function configureWithWizard(
|
|
|
203
206
|
return true;
|
|
204
207
|
}
|
|
205
208
|
|
|
209
|
+
type ConfigMenuResult = "close" | "more";
|
|
210
|
+
|
|
211
|
+
class NesConfigMenu extends Container {
|
|
212
|
+
private readonly settingsList: SettingsList;
|
|
213
|
+
|
|
214
|
+
constructor(
|
|
215
|
+
config: NesConfig,
|
|
216
|
+
theme: Theme,
|
|
217
|
+
onAudioChange: (enabled: boolean) => void,
|
|
218
|
+
onDone: (result: ConfigMenuResult) => void,
|
|
219
|
+
) {
|
|
220
|
+
super();
|
|
221
|
+
const audioValue = config.enableAudio ? "on" : "off (default)";
|
|
222
|
+
const items: SettingItem[] = [
|
|
223
|
+
{
|
|
224
|
+
id: "audio",
|
|
225
|
+
label: "Audio",
|
|
226
|
+
description: "Enable audio output (requires native core built with audio-cpal)",
|
|
227
|
+
currentValue: audioValue,
|
|
228
|
+
values: ["off (default)", "on"],
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
id: "more",
|
|
232
|
+
label: "More settings",
|
|
233
|
+
description: "Quick setup, advanced JSON, or reset defaults",
|
|
234
|
+
currentValue: "Open",
|
|
235
|
+
values: ["Open"],
|
|
236
|
+
},
|
|
237
|
+
];
|
|
238
|
+
|
|
239
|
+
this.settingsList = new SettingsList(
|
|
240
|
+
items,
|
|
241
|
+
6,
|
|
242
|
+
getSettingsListTheme(),
|
|
243
|
+
(id, value) => {
|
|
244
|
+
if (id === "audio") {
|
|
245
|
+
onAudioChange(value === "on");
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (id === "more") {
|
|
249
|
+
onDone("more");
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
() => onDone("close"),
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
this.addChild(new DynamicBorder());
|
|
256
|
+
this.addChild(new Text(theme.bold(theme.fg("accent", "NES config")), 1, 0));
|
|
257
|
+
this.addChild(new Spacer(1));
|
|
258
|
+
this.addChild(this.settingsList);
|
|
259
|
+
this.addChild(new Spacer(1));
|
|
260
|
+
this.addChild(new DynamicBorder());
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
handleInput(data: string): void {
|
|
264
|
+
this.settingsList.handleInput(data);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
206
268
|
async function editConfig(ctx: ExtensionCommandContext): Promise<void> {
|
|
207
269
|
if (!ctx.hasUI) {
|
|
208
270
|
ctx.ui.notify("NES config requires interactive mode", "error");
|
|
209
271
|
return;
|
|
210
272
|
}
|
|
211
273
|
const config = await loadConfig();
|
|
274
|
+
const action = await ctx.ui.custom<ConfigMenuResult>((_tui, theme, _keybindings, done) =>
|
|
275
|
+
new NesConfigMenu(
|
|
276
|
+
config,
|
|
277
|
+
theme,
|
|
278
|
+
(enabled) => {
|
|
279
|
+
void (async () => {
|
|
280
|
+
const normalized = normalizeConfig({ ...config, enableAudio: enabled });
|
|
281
|
+
await saveConfig(normalized);
|
|
282
|
+
config.enableAudio = normalized.enableAudio;
|
|
283
|
+
ctx.ui.notify(`Saved config to ${getConfigPath()}`, "info");
|
|
284
|
+
})();
|
|
285
|
+
},
|
|
286
|
+
done,
|
|
287
|
+
),
|
|
288
|
+
);
|
|
289
|
+
if (action !== "more") {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
212
292
|
const choice = await ctx.ui.select("NES configuration", [
|
|
213
293
|
"Quick setup",
|
|
214
294
|
"Advanced (edit config JSON)",
|