From c63cada6e710ea5599125beb0da84a069072b63e Mon Sep 17 00:00:00 2001 From: CodedSakura Date: Fri, 17 Jul 2026 18:41:55 +0300 Subject: [PATCH] add error popups --- index.html | 10 ++++++++++ scripts/atlas.js | 4 ++-- scripts/configuration.js | 5 +++-- scripts/dialogs.js | 6 ++++++ scripts/errors.js | 15 +++++++++++++++ scripts/files.js | 7 ++++--- scripts/main.js | 8 ++++---- scripts/ncpf-utils.js | 5 +++-- 8 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 scripts/errors.js diff --git a/index.html b/index.html index 2855b51..f63998b 100644 --- a/index.html +++ b/index.html @@ -183,5 +183,15 @@ + diff --git a/scripts/atlas.js b/scripts/atlas.js index d7e8929..1e4cfa5 100644 --- a/scripts/atlas.js +++ b/scripts/atlas.js @@ -88,7 +88,7 @@ export async function buildAtlas(data) { })); if (textures.length === 0) { - throw new Error("Configuration processing failed - no textured blocks found"); + throw new UserError("Configuration processing failed - no textured blocks found"); } await Promise.all(textures.map(getTextureSize)); @@ -97,7 +97,7 @@ export async function buildAtlas(data) { let offset = 0; for (const t of Object.values(Object.groupBy(textures, t => t.size.toString()))) { if (t[0].size[0] !== t[0].size[1]) { - throw new Error("Configuration processing failed - textures must be square"); + throw new UserError("Configuration processing failed - textures must be square"); } const dedupTextureMap = new Map(); diff --git a/scripts/configuration.js b/scripts/configuration.js index e03378a..998e000 100644 --- a/scripts/configuration.js +++ b/scripts/configuration.js @@ -4,6 +4,7 @@ import { setState, STATE } from "./state.js"; import { registerLoadCallback } from "./main.js"; import { addEventToButton } from "./dom-utils.js"; import { showOpenFileDialog } from "./files.js"; +import { DevError, UserError } from "./errors.js"; export let configuration = {}; @@ -38,11 +39,11 @@ export async function loadConfiguration(event) { reader.readAsText(file); }); } else { - throw new Error("Configuration load failed - unrecognised event"); + throw new DevError("Configuration load failed - unrecognised event"); } if ((configData.designs ?? []).length > 0) { - throw new Error("Configuration load failed - contains designs, likely not full configuration"); + throw new UserError("Configuration load failed - contains designs, not a full configuration!"); } emitEvent("config:load:end"); diff --git a/scripts/dialogs.js b/scripts/dialogs.js index cb5c026..f00865b 100644 --- a/scripts/dialogs.js +++ b/scripts/dialogs.js @@ -9,6 +9,7 @@ registerLoadCallback(() => { listenTo("dialogs:welcome", () => welcomeDialog(true)); listenTo("fileLoad:chooseDesign", designChoiceDialog); listenTo("fileSave:nameDesign", designNameDialog); + listenTo("error:user", errorDialog); }); function initializeDialog(dialogName, endEvent, { emptyValue, onDismiss } = {}) { @@ -88,3 +89,8 @@ function designNameDialog() { dismiss(new FormData(e.detail.target).get("name")); }); } + +function errorDialog(e) { + const { dialog } = initializeDialog("error:user", "error:dialog:close"); + dialog.querySelectorAll('[data-text="error"]').forEach(elem => elem.innerText = e.detail); +} diff --git a/scripts/errors.js b/scripts/errors.js new file mode 100644 index 0000000..20e244d --- /dev/null +++ b/scripts/errors.js @@ -0,0 +1,15 @@ +import { emitEvent } from "./events.js"; + +export class UserError extends Error { + constructor(message) { + super(message); + emitEvent("error:user", message); + } +} + +export class DevError extends Error { + constructor(message) { + super(message); + emitEvent("error:dev", message); + } +} diff --git a/scripts/files.js b/scripts/files.js index 18c2007..efd9f00 100644 --- a/scripts/files.js +++ b/scripts/files.js @@ -10,6 +10,7 @@ import { import { getAtlasIndexFromMetadata, getAtlasMetadata } from "./atlas.js"; import { getSolverMetadata, resetSolver } from "./solver.js"; import { registerLoadCallback } from "./main.js"; +import { DevError, UserError } from "./errors.js"; registerLoadCallback(() => { listenTo("file:load", () => showOpenFileDialog(({ data, filename }) => loadFile(data, filename))); @@ -44,8 +45,8 @@ export async function loadExampleFile(filename) { } export async function loadFile(data, name) { - if (!data.designs) throw new Error("invalid file"); - if (data.designs.length === 0) throw new Error("no designs"); + if (!data.designs) throw new UserError("File must contain a list of designs!"); + if (data.designs.length === 0) throw new UserError("Fils must contain at least one design!"); let design = data.designs[0]; if (data.designs.length > 1) { @@ -55,7 +56,7 @@ export async function loadFile(data, name) { design = data.designs[res.detail]; } - if (!design) throw new Error("no design"); + if (!design) throw new DevError("Must choose a design"); const blocks = data.configuration[design.type].blocks .map(block => getAtlasIndexFromMetadata(block, design.type, "blocks")); diff --git a/scripts/main.js b/scripts/main.js index 0b9404d..c35194f 100644 --- a/scripts/main.js +++ b/scripts/main.js @@ -3,9 +3,10 @@ import "./block-picker.js"; import { loadConfiguration } from "./configuration.js"; import "./dialogs.js"; import "./dom-list.js"; -import { addEventToButton, addEventToInput, debounce, getElements } from "./dom-utils.js"; -import { awaitEvent, emitEvent, listenTo } from "./events.js"; -import "./files.js" +import { debounce, getElements } from "./dom-utils.js"; +import "./errors.js"; +import { emitEvent, listenTo } from "./events.js"; +import { loadExampleFile } from "./files.js" import "./js-utils.js" import "./menu-bar.js"; import "./ncpf-utils.js"; @@ -16,7 +17,6 @@ import "./solver.js"; import { getState, setState, STATE } from "./state.js"; import "./storage.js"; import "./tooltip.js"; -import { loadExampleFile } from "./files.js"; let loadCallbacks = []; export function registerLoadCallback(cb) { diff --git a/scripts/ncpf-utils.js b/scripts/ncpf-utils.js index 5ec5f6f..2e9b42e 100644 --- a/scripts/ncpf-utils.js +++ b/scripts/ncpf-utils.js @@ -1,5 +1,6 @@ import { configuration } from "./configuration.js"; import { objectKeepKeys } from "./js-utils.js"; +import { DevError } from "./errors.js"; export function checkEquivalence(a, b) { if (!a || !b || !a.type || !b.type || a.type !== b.type) return false; @@ -31,7 +32,7 @@ export function checkEquivalence(a, b) { } export function stripEntryToMinimums(entry) { - if (!entry.type) throw new Error("must have a type"); + if (!entry.type) throw new DevError("must have a type"); switch (entry.type) { case "legacy_block": return objectKeepKeys(entry, "type", "name", "metadata", "blockstate"); @@ -48,7 +49,7 @@ export function stripEntryToMinimums(entry) { elements: entry.elements.map(stripEntryToMinimums), }; } - throw new Error(`unknown entry type ${entry.type}`); + throw new DevError(`unknown entry type ${entry.type}`); } export function findFullEntry(type, entry, { configurationHint, all = false } = {}) {