feat: bundle built-in icons into main.js as data URLs

Icons are now embedded at build time via esbuild dataurl loader, so
they work for all install methods without requiring a separate icons/
folder in the release assets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nyxsys 2026-03-23 16:17:52 -04:00
parent a68fc92233
commit 965dcd2d08
5 changed files with 88 additions and 9 deletions

View file

@ -31,7 +31,7 @@ const context = await esbuild.context({
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: `${outDir}/${outFile}`,
loader: { ".md": "text" },
loader: { ".md": "text", ".png": "dataurl" },
});
if (prod) {

View file

@ -5,6 +5,7 @@ import { RandomTableView } from "./random-tables/RandomTableView";
import { DuckmageSettingTab } from "./DuckmageSettingTab";
import { DEFAULT_PALETTE_NAME, DEFAULT_SETTINGS, VIEW_TYPE_HEX_MAP, VIEW_TYPE_HEX_TABLE, VIEW_TYPE_RANDOM_TABLES } from "./constants";
import { normalizeFolder, makeTableTemplate } from "./utils";
import { BUNDLED_ICONS } from "./bundledIcons";
import { parseWorkflow, buildWorkflowContent } from "./random-tables/workflow";
import type { DuckmagePluginSettings, RegionData, TerrainColor, TerrainPalette } from "./types";
import DEFAULT_HEX_TEMPLATE from "./defaultHexTemplate.md";
@ -233,16 +234,9 @@ export default class DuckmagePlugin extends Plugin {
async loadAvailableIcons() {
this.vaultIconsSet = new Set();
const pluginIcons: string[] = [];
const pluginIcons: string[] = Array.from(BUNDLED_ICONS.keys());
const vaultIcons: string[] = [];
try {
const result = await this.app.vault.adapter.list(`${this.manifest.dir}/icons`);
pluginIcons.push(...result.files
.filter(f => f.toLowerCase().endsWith(".png"))
.map(f => f.split("/").pop() as string));
} catch {}
const iconsFolder = normalizeFolder(this.settings.iconsFolder ?? "");
if (iconsFolder) {
try {

77
src/bundledIcons.ts Normal file
View file

@ -0,0 +1,77 @@
import bwBadlands from "../icons/bw-badlands.png";
import bwBrokenlands from "../icons/bw-brokenlands.png";
import bwCactus from "../icons/bw-cactus.png";
import bwCactusHeavy from "../icons/bw-cactus-heavy.png";
import bwDesert from "../icons/bw-desert.png";
import bwDesertRocky from "../icons/bw-desert-rocky.png";
import bwDunes from "../icons/bw-dunes.png";
import bwEvergreen from "../icons/bw-evergreen.png";
import bwEvergreenHeavy from "../icons/bw-evergreen-heavy.png";
import bwEvergreenHills from "../icons/bw-evergreen-hills.png";
import bwEvergreenMountain from "../icons/bw-evergreen-mountain.png";
import bwEvergreenMountains from "../icons/bw-evergreen-mountains.png";
import bwForest from "../icons/bw-forest.png";
import bwForestedHills from "../icons/bw-forested-hills.png";
import bwForestedMountain from "../icons/bw-forested-mountain.png";
import bwForestedMountains from "../icons/bw-forested-mountains.png";
import bwForestHeavy from "../icons/bw-forest-heavy.png";
import bwForestMixed from "../icons/bw-forest-mixed.png";
import bwForestMixedHeavy from "../icons/bw-forest-mixed-heavy.png";
import bwForestMixedHills from "../icons/bw-forest-mixed-hills.png";
import bwForestMixedMountain from "../icons/bw-forest-mixed-mountain.png";
import bwForestMixedMountains from "../icons/bw-forest-mixed-mountains.png";
import bwGrassland from "../icons/bw-grassland.png";
import bwHills from "../icons/bw-hills.png";
import bwJungle from "../icons/bw-jungle.png";
import bwJungleHeavy from "../icons/bw-jungle-heavy.png";
import bwJungleHills from "../icons/bw-jungle-hills.png";
import bwJungleMountain from "../icons/bw-jungle-mountain.png";
import bwJungleMountains from "../icons/bw-jungle-mountains.png";
import bwMarsh from "../icons/bw-marsh.png";
import bwMountain from "../icons/bw-mountain.png";
import bwMountains from "../icons/bw-mountains.png";
import bwMountainSnow from "../icons/bw-mountain-snow.png";
import bwMountainsSnow from "../icons/bw-mountains-snow.png";
import bwSwamp from "../icons/bw-swamp.png";
import bwVolcano from "../icons/bw-volcano.png";
import bwVolcanoDormant from "../icons/bw-volcano-dormant.png";
export const BUNDLED_ICONS: Map<string, string> = new Map([
["bw-badlands.png", bwBadlands],
["bw-brokenlands.png", bwBrokenlands],
["bw-cactus.png", bwCactus],
["bw-cactus-heavy.png", bwCactusHeavy],
["bw-desert.png", bwDesert],
["bw-desert-rocky.png", bwDesertRocky],
["bw-dunes.png", bwDunes],
["bw-evergreen.png", bwEvergreen],
["bw-evergreen-heavy.png", bwEvergreenHeavy],
["bw-evergreen-hills.png", bwEvergreenHills],
["bw-evergreen-mountain.png", bwEvergreenMountain],
["bw-evergreen-mountains.png", bwEvergreenMountains],
["bw-forest.png", bwForest],
["bw-forested-hills.png", bwForestedHills],
["bw-forested-mountain.png", bwForestedMountain],
["bw-forested-mountains.png", bwForestedMountains],
["bw-forest-heavy.png", bwForestHeavy],
["bw-forest-mixed.png", bwForestMixed],
["bw-forest-mixed-heavy.png", bwForestMixedHeavy],
["bw-forest-mixed-hills.png", bwForestMixedHills],
["bw-forest-mixed-mountain.png", bwForestMixedMountain],
["bw-forest-mixed-mountains.png", bwForestMixedMountains],
["bw-grassland.png", bwGrassland],
["bw-hills.png", bwHills],
["bw-jungle.png", bwJungle],
["bw-jungle-heavy.png", bwJungleHeavy],
["bw-jungle-hills.png", bwJungleHills],
["bw-jungle-mountain.png", bwJungleMountain],
["bw-jungle-mountains.png", bwJungleMountains],
["bw-marsh.png", bwMarsh],
["bw-mountain.png", bwMountain],
["bw-mountains.png", bwMountains],
["bw-mountain-snow.png", bwMountainSnow],
["bw-mountains-snow.png", bwMountainsSnow],
["bw-swamp.png", bwSwamp],
["bw-volcano.png", bwVolcano],
["bw-volcano-dormant.png", bwVolcanoDormant],
]);

5
src/md.d.ts vendored
View file

@ -2,3 +2,8 @@ declare module "*.md" {
const content: string;
export default content;
}
declare module "*.png" {
const dataUrl: string;
export default dataUrl;
}

View file

@ -1,4 +1,5 @@
import type DuckmagePlugin from "./DuckmagePlugin";
import { BUNDLED_ICONS } from "./bundledIcons";
export function normalizeFolder(path: string): string {
return path.replace(/^\/+|\/+$/g, "") || "";
@ -51,5 +52,7 @@ export function getIconUrl(plugin: DuckmagePlugin, iconFilename: string): string
const folder = normalizeFolder(plugin.settings.iconsFolder ?? "");
return plugin.app.vault.adapter.getResourcePath(`${folder}/${iconFilename}`);
}
const bundled = BUNDLED_ICONS.get(iconFilename);
if (bundled) return bundled;
return plugin.app.vault.adapter.getResourcePath(`${plugin.manifest.dir}/icons/${iconFilename}`);
}