Skip to content

Commit fb09c1b

Browse files
committed
feat(dev-tools): add crisp canvas zoom setting
Nearest-neighbour scaling on the world canvases keeps zoom sharp instead of blurry.
1 parent 59de159 commit fb09c1b

5 files changed

Lines changed: 37 additions & 15 deletions

File tree

docs/dev-tools/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ Settings live on this mod. Open **Options → Mods → dev-tools**.
3131
| **Disable autosave** | `disableAutosave` | off | Sets `session.settings.autosaveInterval` to `0`. Manual saves still work |
3232
| **Watch local mods** | `watchLocalMods` | off | Poll other mods' `main.js` and re-eval the renderer bundle. Does not reload this companion, workers, or patches |
3333
| **Fast dev boot** | `fastBoot` | off | Skip `foliage.generate` on boot. Raster, shadows, and shader compile stay vanilla. Writes `localStorage`. Needs `debugPatches` (dev). Restart once after you turn it on |
34+
| **Crisp canvas zoom** | `crispCanvas` | off | Nearest-neighbour scaling on `#canvas` and `#overlay-canvas` so zoom stays sharp instead of blurry |
3435

35-
Turn on **Watch local mods**, **Fast dev boot**, **Auto-load save**, **F3 debug overlay**, **Disable autosave**, **F12**, or **Open DevTools on load** when you want those helpers.
36+
Turn on **Watch local mods**, **Fast dev boot**, **Auto-load save**, **F3 debug overlay**, **Crisp canvas zoom**, **Disable autosave**, **F12**, or **Open DevTools on load** when you want those helpers.
3637

3738
## Features
3839

@@ -45,6 +46,7 @@ Turn on **Watch local mods**, **Fast dev boot**, **Auto-load save**, **F3 debug
4546
- **Dev Tools** (`mod-inspector/`) — pause **Dev Tools** opens a 1100×720 panel. **Mods** tab: compact loaded-mod cards; **Open** fills the tab with details (description first, then contributes, then load meta); save issues stay collapsed. **Elements**: family sand table. **Chains**: pick an element. **Does** shows where it goes (`Gold → Smelter → Liquid Gold`). **Comes from** shows how it is made. Depth 1 is one recipe hop. The index includes engine builtins (Wet Sand shaker, Residue burn, Burnt Residue press, Water contacts) because those rows are not in `mods.recipes`.
4647
- **Watch local mods** (`reload/`) — poll and re-eval other mods' renderer `main.js`.
4748
- **Fast dev boot** (`patches.ts`, `boot/fast-boot.ts`) — when on, skips `foliage.generate` only. Raster fill, shadow rebuild, and outline compile stay vanilla. Needs `debugPatches` (dev). Restart once after you turn it on.
49+
- **Crisp canvas zoom** (`boot/crisp-canvas.ts`) — injects `image-rendering: pixelated` on the world canvases. Toggle in **Options → Mods → dev-tools**.
4850

4951
## DevTools globals
5052

src/dev-tools/boot/crisp-canvas.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
const STYLE_ID = "dev-tools-crisp-canvas";
2+
3+
/** Nearest-neighbour scaling for the world canvases (sharp when zoomed). */
4+
export function syncCrispCanvas(enabled: boolean): void {
5+
let style = document.getElementById(STYLE_ID) as HTMLStyleElement | null;
6+
if (enabled) {
7+
if (!style) {
8+
style = document.createElement("style");
9+
style.id = STYLE_ID;
10+
style.textContent = "#canvas,#overlay-canvas{image-rendering:pixelated}";
11+
document.head.appendChild(style);
12+
}
13+
return;
14+
}
15+
style?.remove();
16+
}

src/dev-tools/boot/setting-defaults.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* Defaults when `api.settings.get` has no boolean yet.
3-
* Keep in sync with `configSchema` in `../modinfo.ts`.
3+
* Keep in sync with `configSchema` in `../modinfo.json`.
44
*/
55
export const SETTING_DEFAULTS: Record<string, boolean> = {
66
openDevTools: false,
@@ -10,4 +10,5 @@ export const SETTING_DEFAULTS: Record<string, boolean> = {
1010
disableAutosave: false,
1111
watchLocalMods: false,
1212
fastBoot: false,
13+
crispCanvas: false,
1314
};

src/dev-tools/boot/settings.test.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,15 @@ import { fileURLToPath } from "node:url";
55
import { dirname, join } from "node:path";
66
import { SETTING_DEFAULTS } from "./setting-defaults.ts";
77

8-
const BOOT_DIR = dirname(fileURLToPath(import.meta.url));
8+
const DEV_TOOLS_DIR = dirname(dirname(fileURLToPath(import.meta.url)));
99

10-
function booleanSchemaDefault(source: string, key: string): boolean {
11-
const match = source.match(
12-
new RegExp(`${key}:\\s*\\{[\\s\\S]*?type:\\s*"boolean",[\\s\\S]*?default:\\s*(true|false)`),
13-
);
14-
assert.ok(match, `boolean schema default for ${key}`);
15-
return match[1] === "true";
16-
}
17-
18-
test("SETTING_DEFAULTS matches boolean configSchema defaults in modinfo.ts", () => {
19-
const source = readFileSync(join(BOOT_DIR, "..", "modinfo.ts"), "utf8");
10+
test("SETTING_DEFAULTS matches boolean configSchema defaults in modinfo.json", () => {
11+
const manifest = JSON.parse(readFileSync(join(DEV_TOOLS_DIR, "modinfo.json"), "utf8")) as {
12+
configSchema?: Record<string, { type?: string; default?: unknown }>;
13+
};
2014
for (const [key, value] of Object.entries(SETTING_DEFAULTS)) {
21-
assert.equal(booleanSchemaDefault(source, key), value, key);
15+
const field = manifest.configSchema?.[key];
16+
assert.equal(field?.type, "boolean", `${key} must be a boolean schema field`);
17+
assert.equal(field?.default, value, `${key} default`);
2218
}
2319
});

src/dev-tools/main.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { disableSessionAutosave } from "./boot/autosave";
22
import { registerDevToolsShortcut, scheduleMainMenuBoot } from "./boot/boot-menu";
3+
import { syncCrispCanvas } from "./boot/crisp-canvas";
34
import { syncFastBootPrefs } from "./boot/fast-boot";
45
import { settingOn } from "./boot/settings";
56
import { installDebugCompanion } from "./f3/install";
67
import { installModInspector } from "./mod-inspector/install";
7-
import { modinfo } from "./modinfo";
8+
import modinfo from "./modinfo.json";
89
import { installFirstLoadApiWrap } from "./reload/first-load-wrap.ts";
910
import { installLocalModReload } from "./reload/install.ts";
1011
import { isEnabled } from "modkit/utils";
@@ -29,6 +30,10 @@ function syncBootPatches(): void {
2930
syncFastBootPrefs(api);
3031
}
3132

33+
function syncCrispCanvasSetting(): void {
34+
syncCrispCanvas(settingOn(api, "crispCanvas"));
35+
}
36+
3237
function main() {
3338
if (!isEnabled(api)) return;
3439

@@ -39,6 +44,7 @@ function main() {
3944
Object.assign(globalThis, { sandkit, api, enums, react });
4045

4146
syncBootPatches();
47+
syncCrispCanvasSetting();
4248
if (settingOn(api, "f12DevTools")) registerDevToolsShortcut();
4349
scheduleMainMenuBoot(api);
4450
if (settingOn(api, "disableAutosave")) disableSessionAutosave();
@@ -47,6 +53,7 @@ function main() {
4753
syncLocalModReload();
4854
api.settings.onChange(() => {
4955
syncBootPatches();
56+
syncCrispCanvasSetting();
5057
syncLocalModReload();
5158
});
5259

0 commit comments

Comments
 (0)