@tmustier/pi-nes 0.2.11 → 0.2.13
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 +2 -2
- package/extensions/nes/config.ts +6 -7
- package/extensions/nes/index.ts +14 -15
- package/extensions/nes/nes-component.ts +4 -0
- package/extensions/nes/renderer.ts +13 -6
- package/package.json +1 -1
- package/spec.md +2 -2
package/README.md
CHANGED
|
@@ -83,8 +83,8 @@ Config is stored at `~/.pi/nes/config.json`. Use `/nes config` for quick setup.
|
|
|
83
83
|
| `romDir` | `/roms/nes` | Where to look for ROM files |
|
|
84
84
|
| `saveDir` | `/roms/nes/saves` | Where to store battery saves (defaults to `<romDir>/saves`) |
|
|
85
85
|
| `renderer` | `"image"` | `"image"` (Kitty graphics) or `"text"` (ANSI) |
|
|
86
|
-
| `imageQuality` | `"balanced"` | `"balanced"` (30 fps
|
|
87
|
-
| `pixelScale` | `1.0` | Display scale (0.5–
|
|
86
|
+
| `imageQuality` | `"balanced"` | `"balanced"` (30 fps) or `"high"` (60 fps) |
|
|
87
|
+
| `pixelScale` | `1.0` | Display scale (0.5–4.0) |
|
|
88
88
|
|
|
89
89
|
## Terminal Support
|
|
90
90
|
|
package/extensions/nes/config.ts
CHANGED
|
@@ -59,11 +59,7 @@ export function normalizeConfig(raw: unknown): NesConfig {
|
|
|
59
59
|
? normalizePath(parsed.saveDir, saveDirFallback)
|
|
60
60
|
: saveDirFallback;
|
|
61
61
|
const imageQuality = normalizeImageQuality(parsed.imageQuality);
|
|
62
|
-
const
|
|
63
|
-
const pixelScale =
|
|
64
|
-
typeof parsed.pixelScale === "number" && !Number.isNaN(parsed.pixelScale)
|
|
65
|
-
? normalizePixelScale(parsed.pixelScale, maxPixelScale)
|
|
66
|
-
: maxPixelScale;
|
|
62
|
+
const pixelScale = normalizePixelScale(parsed.pixelScale);
|
|
67
63
|
return {
|
|
68
64
|
romDir,
|
|
69
65
|
saveDir,
|
|
@@ -115,8 +111,11 @@ async function ensureDirectory(dirPath: string): Promise<void> {
|
|
|
115
111
|
}
|
|
116
112
|
}
|
|
117
113
|
|
|
118
|
-
function normalizePixelScale(raw:
|
|
119
|
-
|
|
114
|
+
function normalizePixelScale(raw: unknown): number {
|
|
115
|
+
if (typeof raw !== "number" || Number.isNaN(raw)) {
|
|
116
|
+
return DEFAULT_CONFIG.pixelScale;
|
|
117
|
+
}
|
|
118
|
+
return Math.min(4, Math.max(0.5, raw));
|
|
120
119
|
}
|
|
121
120
|
|
|
122
121
|
function normalizeImageQuality(raw: unknown): ImageQuality {
|
package/extensions/nes/index.ts
CHANGED
|
@@ -176,8 +176,8 @@ async function configureWithWizard(
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
const qualityChoice = await ctx.ui.select("Quality", [
|
|
179
|
-
"Balanced (recommended) — 30 fps
|
|
180
|
-
"High — 60 fps
|
|
179
|
+
"Balanced (recommended) — 30 fps",
|
|
180
|
+
"High — 60 fps",
|
|
181
181
|
]);
|
|
182
182
|
if (!qualityChoice) {
|
|
183
183
|
return false;
|
|
@@ -185,7 +185,7 @@ async function configureWithWizard(
|
|
|
185
185
|
|
|
186
186
|
const isHighQuality = qualityChoice.startsWith("High");
|
|
187
187
|
const imageQuality = isHighQuality ? "high" : "balanced";
|
|
188
|
-
const pixelScale =
|
|
188
|
+
const pixelScale = config.pixelScale;
|
|
189
189
|
|
|
190
190
|
const defaultSaveDir = getDefaultSaveDir(config.romDir);
|
|
191
191
|
const shouldSyncSaveDir = config.saveDir === defaultSaveDir;
|
|
@@ -289,18 +289,16 @@ async function attachSession(
|
|
|
289
289
|
): Promise<boolean> {
|
|
290
290
|
let shouldStop = false;
|
|
291
291
|
try {
|
|
292
|
-
const
|
|
293
|
-
const overlayOptions =
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
: undefined;
|
|
292
|
+
const isImageRenderer = config.renderer === "image";
|
|
293
|
+
const overlayOptions = {
|
|
294
|
+
overlay: true,
|
|
295
|
+
overlayOptions: {
|
|
296
|
+
width: isImageRenderer ? "90%" : "85%",
|
|
297
|
+
maxHeight: "90%",
|
|
298
|
+
anchor: "center",
|
|
299
|
+
margin: { top: 1 },
|
|
300
|
+
},
|
|
301
|
+
};
|
|
304
302
|
|
|
305
303
|
const renderIntervalMs = config.renderer === "image"
|
|
306
304
|
? config.imageQuality === "high"
|
|
@@ -323,6 +321,7 @@ async function attachSession(
|
|
|
323
321
|
config.keybindings,
|
|
324
322
|
config.renderer,
|
|
325
323
|
config.pixelScale,
|
|
324
|
+
isImageRenderer,
|
|
326
325
|
debug,
|
|
327
326
|
() => session.getStats(),
|
|
328
327
|
"native",
|
|
@@ -84,6 +84,7 @@ export class NesOverlayComponent implements Component {
|
|
|
84
84
|
private readonly imageRenderer = new NesImageRenderer();
|
|
85
85
|
private readonly rendererMode: RendererMode;
|
|
86
86
|
private readonly pixelScale: number;
|
|
87
|
+
private readonly windowed: boolean;
|
|
87
88
|
private readonly debug: boolean;
|
|
88
89
|
private readonly statsProvider?: () => NesSessionStats;
|
|
89
90
|
private readonly debugLabel?: string;
|
|
@@ -97,6 +98,7 @@ export class NesOverlayComponent implements Component {
|
|
|
97
98
|
inputMapping: InputMapping = DEFAULT_INPUT_MAPPING,
|
|
98
99
|
rendererMode: RendererMode = "image",
|
|
99
100
|
pixelScale = 1,
|
|
101
|
+
windowed = false,
|
|
100
102
|
debug = false,
|
|
101
103
|
statsProvider?: () => NesSessionStats,
|
|
102
104
|
debugLabel?: string,
|
|
@@ -104,6 +106,7 @@ export class NesOverlayComponent implements Component {
|
|
|
104
106
|
this.inputMapping = inputMapping;
|
|
105
107
|
this.rendererMode = rendererMode;
|
|
106
108
|
this.pixelScale = pixelScale;
|
|
109
|
+
this.windowed = windowed;
|
|
107
110
|
this.debug = debug;
|
|
108
111
|
this.statsProvider = statsProvider;
|
|
109
112
|
this.debugLabel = debugLabel;
|
|
@@ -154,6 +157,7 @@ export class NesOverlayComponent implements Component {
|
|
|
154
157
|
width,
|
|
155
158
|
footerRows,
|
|
156
159
|
this.pixelScale,
|
|
160
|
+
!this.windowed,
|
|
157
161
|
);
|
|
158
162
|
if (debugLine) {
|
|
159
163
|
lines.push(truncateToWidth(debugLine, width));
|
|
@@ -68,17 +68,18 @@ export class NesImageRenderer {
|
|
|
68
68
|
widthCells: number,
|
|
69
69
|
footerRows = 1,
|
|
70
70
|
pixelScale = 1,
|
|
71
|
+
padToHeight = true,
|
|
71
72
|
): string[] {
|
|
72
73
|
const caps = getCapabilities();
|
|
73
74
|
if (caps.images === "kitty") {
|
|
74
|
-
const shared = this.renderKittySharedMemory(frameBuffer, tui, widthCells, footerRows, pixelScale);
|
|
75
|
+
const shared = this.renderKittySharedMemory(frameBuffer, tui, widthCells, footerRows, pixelScale, padToHeight);
|
|
75
76
|
if (shared) {
|
|
76
77
|
return shared;
|
|
77
78
|
}
|
|
78
|
-
return this.renderKittyRaw(frameBuffer, tui, widthCells, footerRows, pixelScale);
|
|
79
|
+
return this.renderKittyRaw(frameBuffer, tui, widthCells, footerRows, pixelScale, padToHeight);
|
|
79
80
|
}
|
|
80
81
|
|
|
81
|
-
return this.renderPng(frameBuffer, tui, widthCells, footerRows, pixelScale);
|
|
82
|
+
return this.renderPng(frameBuffer, tui, widthCells, footerRows, pixelScale, padToHeight);
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
dispose(tui: TUI): void {
|
|
@@ -121,6 +122,7 @@ export class NesImageRenderer {
|
|
|
121
122
|
widthCells: number,
|
|
122
123
|
footerRows: number,
|
|
123
124
|
pixelScale: number,
|
|
125
|
+
padToHeight: boolean,
|
|
124
126
|
): string[] | null {
|
|
125
127
|
const module = this.getSharedMemoryModule();
|
|
126
128
|
if (!module) {
|
|
@@ -155,7 +157,8 @@ export class NesImageRenderer {
|
|
|
155
157
|
const marker = `\x1b_pi:nes:${this.rawVersion}\x07`;
|
|
156
158
|
lines.push(`${moveUp}${sequence}${marker}`);
|
|
157
159
|
|
|
158
|
-
|
|
160
|
+
const padded = applyHorizontalPadding(lines, padLeft);
|
|
161
|
+
return padToHeight ? centerLines(padded, availableRows) : padded;
|
|
159
162
|
}
|
|
160
163
|
|
|
161
164
|
private renderKittyRaw(
|
|
@@ -164,6 +167,7 @@ export class NesImageRenderer {
|
|
|
164
167
|
widthCells: number,
|
|
165
168
|
footerRows: number,
|
|
166
169
|
pixelScale: number,
|
|
170
|
+
padToHeight: boolean,
|
|
167
171
|
): string[] {
|
|
168
172
|
const layout = computeKittyLayout(tui, widthCells, footerRows, pixelScale);
|
|
169
173
|
const { availableRows, columns, rows, padLeft } = layout;
|
|
@@ -199,7 +203,8 @@ export class NesImageRenderer {
|
|
|
199
203
|
const marker = `\x1b_pi:nes:${this.rawVersion}\x07`;
|
|
200
204
|
lines.push(`${moveUp}${cached.sequence}${marker}`);
|
|
201
205
|
|
|
202
|
-
|
|
206
|
+
const padded = applyHorizontalPadding(lines, padLeft);
|
|
207
|
+
return padToHeight ? centerLines(padded, availableRows) : padded;
|
|
203
208
|
}
|
|
204
209
|
|
|
205
210
|
private renderPng(
|
|
@@ -208,6 +213,7 @@ export class NesImageRenderer {
|
|
|
208
213
|
widthCells: number,
|
|
209
214
|
footerRows: number,
|
|
210
215
|
pixelScale: number,
|
|
216
|
+
padToHeight: boolean,
|
|
211
217
|
): string[] {
|
|
212
218
|
const layout = computePngLayout(tui, widthCells, footerRows, pixelScale);
|
|
213
219
|
const { availableRows, maxWidthCells, padLeft, targetHeight, targetWidth } = layout;
|
|
@@ -244,7 +250,8 @@ export class NesImageRenderer {
|
|
|
244
250
|
{ widthPx: this.cachedImage.width, heightPx: this.cachedImage.height },
|
|
245
251
|
);
|
|
246
252
|
|
|
247
|
-
|
|
253
|
+
const padded = applyHorizontalPadding(image.render(widthCells), padLeft);
|
|
254
|
+
return padToHeight ? centerLines(padded, availableRows) : padded;
|
|
248
255
|
}
|
|
249
256
|
|
|
250
257
|
private fillRawBuffer(frameBuffer: FrameBuffer): void {
|
package/package.json
CHANGED
package/spec.md
CHANGED
|
@@ -70,7 +70,7 @@ pi-nes/
|
|
|
70
70
|
- `saveDir`
|
|
71
71
|
- `enableAudio`
|
|
72
72
|
- `renderer` ("image" or "text")
|
|
73
|
-
- `imageQuality` ("balanced" or "high")
|
|
73
|
+
- `imageQuality` ("balanced" or "high", controls render fps)
|
|
74
74
|
- `pixelScale` (float, e.g. 1.0)
|
|
75
75
|
- `keybindings` (button-to-keys map, e.g. `{ "a": ["z"] }`)
|
|
76
76
|
|
|
@@ -89,6 +89,6 @@ Note: audio output is currently disabled; setting `enableAudio` will show a warn
|
|
|
89
89
|
- Audio: disabled (no safe dependency selected).
|
|
90
90
|
- Default ROM dir: `/roms/nes` (configurable).
|
|
91
91
|
- Default core: `native`.
|
|
92
|
-
- Default image quality: `balanced
|
|
92
|
+
- Default image quality: `balanced` (30 fps).
|
|
93
93
|
- Default pixel scale: `1.0`.
|
|
94
94
|
- Default save dir: `/roms/nes/saves` (configurable).
|