mirror of
https://github.com/sbuffkin/hexmaker.git
synced 2026-07-22 14:30:24 +00:00
fix issues from pr, begin optimizing again
This commit is contained in:
parent
700bb9de3b
commit
ca49d08331
21 changed files with 444 additions and 252 deletions
189
hexcrawl pr.md
Normal file
189
hexcrawl pr.md
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "hexmaker",
|
||||
"name": "Hexmap World Creator",
|
||||
"version": "1.0.11",
|
||||
"version": "1.0.12",
|
||||
"minAppVersion": "1.12.0",
|
||||
"description": "Create a hex map and a guidebook level wiki for your setting with minimal fuss. Hex crawl worldbuilding toolbox. System agnostic, ready for any TRPG you care to run in it.",
|
||||
"author": "morkdev",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "hexmaker-plugin",
|
||||
"version": "1.0.11",
|
||||
"version": "1.0.12",
|
||||
"description": "A plugin to power hex crawl worldbuilding and running.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -12,11 +12,13 @@ export class HexmakerModal extends Modal {
|
|||
if (modalEl.dataset.draggable) return;
|
||||
modalEl.dataset.draggable = "1";
|
||||
modalEl.addClass("duckmage-editor-modal-drag");
|
||||
modalEl.style.position = "absolute";
|
||||
modalEl.style.left = "50%";
|
||||
modalEl.style.top = "50%";
|
||||
modalEl.style.transform = "translate(-50%, -50%)";
|
||||
modalEl.style.margin = "0";
|
||||
modalEl.setCssProps({
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
margin: '0',
|
||||
});
|
||||
|
||||
modalEl.addEventListener("mousedown", (e: MouseEvent) => {
|
||||
const modalContent = modalEl.querySelector<HTMLElement>(".modal-content");
|
||||
|
|
@ -25,14 +27,11 @@ export class HexmakerModal extends Modal {
|
|||
|
||||
e.preventDefault();
|
||||
const r = modalEl.getBoundingClientRect();
|
||||
modalEl.style.transform = "none";
|
||||
modalEl.style.left = `${r.left}px`;
|
||||
modalEl.style.top = `${r.top}px`;
|
||||
modalEl.setCssProps({ transform: 'none', left: `${r.left}px`, top: `${r.top}px` });
|
||||
const sx = e.clientX, sy = e.clientY;
|
||||
const ox = r.left, oy = r.top;
|
||||
const onMove = (ev: MouseEvent) => {
|
||||
modalEl.style.left = `${ox + ev.clientX - sx}px`;
|
||||
modalEl.style.top = `${oy + ev.clientY - sy}px`;
|
||||
modalEl.setCssProps({ left: `${ox + ev.clientX - sx}px`, top: `${oy + ev.clientY - sy}px` });
|
||||
};
|
||||
const onUp = () => {
|
||||
document.removeEventListener("mousemove", onMove);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export default class HexmakerPlugin extends Plugin {
|
|||
this.registerView(VIEW_TYPE_HEX_MAP, (leaf) => new HexMapView(leaf, this));
|
||||
this.registerView(VIEW_TYPE_HEX_TABLE, (leaf) => new HexTableView(leaf, this));
|
||||
this.registerView(VIEW_TYPE_RANDOM_TABLES, (leaf) => new RandomTableView(leaf, this));
|
||||
this.addRibbonIcon("map", "Hexmaker: Open hex map", () => this.openHexMap());
|
||||
this.addRibbonIcon("map", "Hexmaker: open hex map", () => this.openHexMap());
|
||||
this.addCommand({
|
||||
id: "open-hex-map",
|
||||
name: "Open hex map",
|
||||
|
|
@ -34,22 +34,23 @@ export default class HexmakerPlugin extends Plugin {
|
|||
this.addCommand({
|
||||
id: "open-hex-table",
|
||||
name: "Open hex table",
|
||||
callback: () => this.app.workspace.getLeaf().setViewState({ type: VIEW_TYPE_HEX_TABLE }),
|
||||
callback: () => void this.app.workspace.getLeaf().setViewState({ type: VIEW_TYPE_HEX_TABLE }),
|
||||
});
|
||||
this.addCommand({
|
||||
id: "open-random-tables",
|
||||
name: "Open random tables",
|
||||
callback: () => this.app.workspace.getLeaf().setViewState({ type: VIEW_TYPE_RANDOM_TABLES }),
|
||||
callback: () => void this.app.workspace.getLeaf().setViewState({ type: VIEW_TYPE_RANDOM_TABLES }),
|
||||
});
|
||||
this.addSettingTab(new HexmakerSettingTab(this.app, this));
|
||||
|
||||
interface WithOpenTable { openTable?(path: string): void; }
|
||||
this.registerObsidianProtocolHandler("duckmage-roll", (params) => {
|
||||
const filePath = params["file"];
|
||||
if (!filePath) return;
|
||||
const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_RANDOM_TABLES);
|
||||
if (leaves.length > 0) {
|
||||
this.app.workspace.revealLeaf(leaves[0]);
|
||||
(leaves[0].view as any).openTable?.(filePath);
|
||||
void this.app.workspace.revealLeaf(leaves[0]);
|
||||
(leaves[0].view as unknown as WithOpenTable).openTable?.(filePath);
|
||||
} else {
|
||||
void this.app.workspace.getLeaf("tab").setViewState({
|
||||
type: VIEW_TYPE_RANDOM_TABLES,
|
||||
|
|
@ -58,13 +59,14 @@ export default class HexmakerPlugin extends Plugin {
|
|||
}
|
||||
});
|
||||
|
||||
interface WithOpenWorkflow { openWorkflow?(path: string): void; }
|
||||
this.registerObsidianProtocolHandler("duckmage-workflow", (params) => {
|
||||
const filePath = params["file"];
|
||||
if (!filePath) return;
|
||||
const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_RANDOM_TABLES);
|
||||
if (leaves.length > 0) {
|
||||
this.app.workspace.revealLeaf(leaves[0]);
|
||||
(leaves[0].view as any).openWorkflow?.(filePath);
|
||||
void this.app.workspace.revealLeaf(leaves[0]);
|
||||
(leaves[0].view as unknown as WithOpenWorkflow).openWorkflow?.(filePath);
|
||||
} else {
|
||||
void this.app.workspace.getLeaf("tab").setViewState({
|
||||
type: VIEW_TYPE_RANDOM_TABLES,
|
||||
|
|
@ -133,7 +135,7 @@ export default class HexmakerPlugin extends Plugin {
|
|||
onunload() {}
|
||||
|
||||
private openHexMap(): void {
|
||||
this.app.workspace.getLeaf().setViewState({ type: VIEW_TYPE_HEX_MAP });
|
||||
void this.app.workspace.getLeaf().setViewState({ type: VIEW_TYPE_HEX_MAP });
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
|
|
|
|||
|
|
@ -255,7 +255,7 @@ export class HexmakerSettingTab extends PluginSettingTab {
|
|||
const lbl = setting.controlEl.createEl("label", {
|
||||
cls: "duckmage-collapse-cb-label",
|
||||
});
|
||||
const cb = lbl.createEl("input") as HTMLInputElement;
|
||||
const cb = lbl.createEl("input");
|
||||
cb.type = "checkbox";
|
||||
cb.checked = get();
|
||||
cb.addEventListener("change", async () => {
|
||||
|
|
@ -438,7 +438,7 @@ export class HexmakerSettingTab extends PluginSettingTab {
|
|||
const nameInput = rowEl.createEl("input", {
|
||||
type: "text",
|
||||
value: pal.name,
|
||||
}) as HTMLInputElement;
|
||||
});
|
||||
nameInput.addClass("duckmage-palette-mgmt-name");
|
||||
nameInput.addEventListener("blur", async () => {
|
||||
const trimmed = nameInput.value.trim();
|
||||
|
|
@ -485,7 +485,7 @@ export class HexmakerSettingTab extends PluginSettingTab {
|
|||
new Setting(listEl).addButton((btn) =>
|
||||
btn.setButtonText("Add palette").onClick(async () => {
|
||||
palettes.push({
|
||||
name: "New Palette",
|
||||
name: "New palette",
|
||||
terrains:
|
||||
this.plugin.settings.terrainPalettes[0]?.terrains.map((t) => ({
|
||||
...t,
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ export class HexEditorModal extends HexmakerModal {
|
|||
|
||||
async loadData(): Promise<void> {
|
||||
// Reset all fields so stale data from a previous hex never bleeds through
|
||||
this.hexExists = false;
|
||||
this.allText = new Map();
|
||||
this.allLinks = new Map();
|
||||
this.directTerrain = null;
|
||||
|
|
@ -58,11 +59,11 @@ export class HexEditorModal extends HexmakerModal {
|
|||
|
||||
const path = this.plugin.hexPath(this.x, this.y, this.regionName);
|
||||
const file = this.app.vault.getAbstractFileByPath(path);
|
||||
this.hexExists = file instanceof TFile;
|
||||
if (!this.hexExists) return;
|
||||
if (!(file instanceof TFile)) return;
|
||||
this.hexExists = true;
|
||||
|
||||
// Single read — reused for both frontmatter and section parsing
|
||||
const rawContent = await this.app.vault.read(file as TFile);
|
||||
const rawContent = await this.app.vault.read(file);
|
||||
|
||||
const fmMatch = rawContent.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
||||
if (fmMatch) {
|
||||
|
|
@ -97,8 +98,9 @@ export class HexEditorModal extends HexmakerModal {
|
|||
});
|
||||
centerBtn.addEventListener("click", () => {
|
||||
const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_HEX_MAP);
|
||||
interface WithCenterOnHex { centerOnHex?(x: number, y: number): void; }
|
||||
if (leaves.length > 0)
|
||||
(leaves[0].view as any).centerOnHex?.(this.x, this.y);
|
||||
(leaves[0].view as unknown as WithCenterOnHex).centerOnHex?.(this.x, this.y);
|
||||
});
|
||||
|
||||
// "Open note" can be determined synchronously from the vault index
|
||||
|
|
@ -109,7 +111,7 @@ export class HexEditorModal extends HexmakerModal {
|
|||
cls: "duckmage-editor-open-link",
|
||||
});
|
||||
openLink.addEventListener("click", () => {
|
||||
this.app.workspace.getLeaf("tab").openFile(fileNow);
|
||||
void this.app.workspace.getLeaf("tab").openFile(fileNow);
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
|
|
@ -149,7 +151,7 @@ export class HexEditorModal extends HexmakerModal {
|
|||
const swatch = preview.createSpan({
|
||||
cls: "duckmage-terrain-header-swatch",
|
||||
});
|
||||
if (paletteEntry) swatch.style.backgroundColor = paletteEntry.color;
|
||||
if (paletteEntry) swatch.setCssProps({ 'background-color': paletteEntry.color });
|
||||
if (iconToShow) {
|
||||
const img = swatch.createEl("img");
|
||||
img.src = getIconUrl(this.plugin, iconToShow);
|
||||
|
|
@ -288,20 +290,19 @@ export class HexEditorModal extends HexmakerModal {
|
|||
const tile = widget.createDiv({
|
||||
cls: `duckmage-neighbor-tile${onMap ? "" : " duckmage-neighbor-tile-offmap"}`,
|
||||
});
|
||||
tile.style.left = `${l}px`;
|
||||
tile.style.top = `${t}px`;
|
||||
tile.setCssProps({ left: `${l}px`, top: `${t}px` });
|
||||
|
||||
if (onMap) {
|
||||
tile.title = `Hex ${nx}, ${ny}`;
|
||||
const nPath = this.plugin.hexPath(nx, ny, this.regionName);
|
||||
const terrain = getTerrainFromFile(this.app, nPath);
|
||||
const entry = terrain ? paletteMap.get(terrain) : undefined;
|
||||
if (entry) tile.style.backgroundColor = entry.color;
|
||||
if (entry) tile.setCssProps({ 'background-color': entry.color });
|
||||
tile.addEventListener("click", () => {
|
||||
this.x = nx;
|
||||
this.y = ny;
|
||||
this.onNavigate?.(nx, ny);
|
||||
this.loadData().then(() => this.onOpen());
|
||||
void this.loadData().then(() => this.onOpen());
|
||||
});
|
||||
} else {
|
||||
tile.title = "Off map";
|
||||
|
|
@ -327,10 +328,10 @@ export class HexEditorModal extends HexmakerModal {
|
|||
cls: "duckmage-editor-collapsible-title",
|
||||
});
|
||||
const body = wrapper.createDiv({ cls: "duckmage-editor-collapsible-body" });
|
||||
if (startCollapsed) body.style.display = "none";
|
||||
if (startCollapsed) body.hide();
|
||||
header.addEventListener("click", () => {
|
||||
const collapsed = body.style.display === "none";
|
||||
body.style.display = collapsed ? "" : "none";
|
||||
const collapsed = !body.isShown();
|
||||
collapsed ? body.show() : body.hide();
|
||||
arrow.textContent = collapsed ? "▼" : "▶";
|
||||
});
|
||||
return { body, header };
|
||||
|
|
@ -374,7 +375,7 @@ export class HexEditorModal extends HexmakerModal {
|
|||
});
|
||||
|
||||
const preview = btn.createDiv({ cls: "duckmage-terrain-preview" });
|
||||
preview.style.backgroundColor = entry.color;
|
||||
preview.setCssProps({ 'background-color': entry.color });
|
||||
|
||||
if (entry.icon) {
|
||||
createIconEl(
|
||||
|
|
@ -437,17 +438,17 @@ export class HexEditorModal extends HexmakerModal {
|
|||
cls: "duckmage-clear-btn",
|
||||
title: "Remove icon override",
|
||||
});
|
||||
clearIconBtn.style.visibility = currentIcon ? "visible" : "hidden";
|
||||
clearIconBtn.setCssProps({ visibility: currentIcon ? "visible" : "hidden" });
|
||||
clearIconBtn.addEventListener("click", async () => {
|
||||
await this.ensureHexNote();
|
||||
await setIconOverrideInFile(this.app, path, null);
|
||||
this.onChanged(terrainOverrides, new Map([[path, null]]));
|
||||
iconSelect.value = "";
|
||||
clearIconBtn.style.visibility = "hidden";
|
||||
clearIconBtn.setCssProps({ visibility: "hidden" });
|
||||
});
|
||||
// Show/hide clear button as icon selection changes
|
||||
iconSelect.addEventListener("change", () => {
|
||||
clearIconBtn.style.visibility = iconSelect.value ? "visible" : "hidden";
|
||||
clearIconBtn.setCssProps({ visibility: iconSelect.value ? "visible" : "hidden" });
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -503,7 +504,7 @@ export class HexEditorModal extends HexmakerModal {
|
|||
const dropdown = comboWrap.createDiv({
|
||||
cls: "duckmage-link-combo-dropdown",
|
||||
});
|
||||
dropdown.style.display = "none";
|
||||
dropdown.hide();
|
||||
|
||||
// ── Link list ──────────────────────────────────────────────────────────
|
||||
const linksEl = sectionEl.createDiv({ cls: "duckmage-link-list" });
|
||||
|
|
@ -517,16 +518,17 @@ export class HexEditorModal extends HexmakerModal {
|
|||
const onItemClick =
|
||||
section === "Encounters Table"
|
||||
? async (_link: string, file: TFile) => {
|
||||
interface WithOpenTable { openTable?(path: string): void; }
|
||||
const leaves = this.app.workspace.getLeavesOfType(
|
||||
VIEW_TYPE_RANDOM_TABLES,
|
||||
);
|
||||
if (leaves.length > 0) {
|
||||
this.app.workspace.revealLeaf(leaves[0]);
|
||||
(leaves[0].view as any).openTable?.(file.path);
|
||||
(leaves[0].view as unknown as WithOpenTable).openTable?.(file.path);
|
||||
} else {
|
||||
const leaf = this.app.workspace.getLeaf("tab");
|
||||
await leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES });
|
||||
(leaf.view as any).openTable?.(file.path);
|
||||
(leaf.view as unknown as WithOpenTable).openTable?.(file.path);
|
||||
}
|
||||
this.close();
|
||||
}
|
||||
|
|
@ -613,12 +615,12 @@ export class HexEditorModal extends HexmakerModal {
|
|||
const openDropdown = () => {
|
||||
isOpen = true;
|
||||
populateDropdown(input.value);
|
||||
dropdown.style.display = "";
|
||||
dropdown.show();
|
||||
};
|
||||
|
||||
const closeDropdown = () => {
|
||||
isOpen = false;
|
||||
dropdown.style.display = "none";
|
||||
dropdown.hide();
|
||||
};
|
||||
|
||||
const selectFile = async (file: TFile) => {
|
||||
|
|
@ -659,19 +661,17 @@ export class HexEditorModal extends HexmakerModal {
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (!(file instanceof TFile)) return;
|
||||
const hexFile = await this.ensureHexNote();
|
||||
if (!hexFile) {
|
||||
new Notice("Could not create hex note.");
|
||||
return;
|
||||
}
|
||||
const linkPath = this.app.metadataCache.fileToLinktext(
|
||||
file as TFile,
|
||||
path,
|
||||
);
|
||||
const linkPath = this.app.metadataCache.fileToLinktext(file, path);
|
||||
currentLinks = [...currentLinks, linkPath];
|
||||
refresh();
|
||||
void addLinkToSection(this.app, path, section, `[[${linkPath}]]`);
|
||||
void addBacklinkToFile(this.app, (file as TFile).path, path);
|
||||
void addBacklinkToFile(this.app, file.path, path);
|
||||
this.onChanged();
|
||||
};
|
||||
|
||||
|
|
@ -780,7 +780,7 @@ export class HexEditorModal extends HexmakerModal {
|
|||
if (onItemClick) {
|
||||
void onItemClick(link, file);
|
||||
} else {
|
||||
this.app.workspace.getLeaf("tab").openFile(file);
|
||||
void this.app.workspace.getLeaf("tab").openFile(file);
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
App,
|
||||
Component,
|
||||
ItemView,
|
||||
MarkdownRenderer,
|
||||
Menu,
|
||||
|
|
@ -345,7 +346,7 @@ export class HexMapView extends ItemView {
|
|||
if (existing.length > 0) {
|
||||
this.app.workspace.revealLeaf(existing[0]);
|
||||
} else {
|
||||
this.app.workspace.getLeaf().setViewState({ type: VIEW_TYPE_HEX_TABLE });
|
||||
void this.app.workspace.getLeaf().setViewState({ type: VIEW_TYPE_HEX_TABLE });
|
||||
}
|
||||
});
|
||||
tableBtn.addEventListener("auxclick", (e: MouseEvent) => {
|
||||
|
|
@ -365,7 +366,7 @@ export class HexMapView extends ItemView {
|
|||
if (existing.length > 0) {
|
||||
this.app.workspace.revealLeaf(existing[0]);
|
||||
} else {
|
||||
this.app.workspace.getLeaf().setViewState({ type: VIEW_TYPE_RANDOM_TABLES });
|
||||
void this.app.workspace.getLeaf().setViewState({ type: VIEW_TYPE_RANDOM_TABLES });
|
||||
}
|
||||
});
|
||||
rtBtn.addEventListener("auxclick", (e: MouseEvent) => {
|
||||
|
|
@ -388,7 +389,8 @@ export class HexMapView extends ItemView {
|
|||
this.redoStack = [];
|
||||
this.updateUndoButton();
|
||||
this.updateRegionBtnLabel();
|
||||
(this.leaf as any).updateHeader();
|
||||
interface WithUpdateHeader { updateHeader?(): void; }
|
||||
(this.leaf as unknown as WithUpdateHeader).updateHeader?.();
|
||||
this.renderGrid();
|
||||
}).open(),
|
||||
);
|
||||
|
|
@ -761,13 +763,13 @@ export class HexMapView extends ItemView {
|
|||
const hasB = fileB instanceof TFile;
|
||||
if (!hasA && !hasB) return;
|
||||
|
||||
if (hasA && !hasB) {
|
||||
if (fileA instanceof TFile && !hasB) {
|
||||
await this.app.vault.rename(fileA, pathB);
|
||||
} else if (!hasA && hasB) {
|
||||
await this.app.vault.rename(fileB as TFile, pathA);
|
||||
} else {
|
||||
await this.app.vault.rename(fileA as TFile, tempPath);
|
||||
await this.app.vault.rename(fileB as TFile, pathA);
|
||||
} else if (!hasA && fileB instanceof TFile) {
|
||||
await this.app.vault.rename(fileB, pathA);
|
||||
} else if (fileA instanceof TFile && fileB instanceof TFile) {
|
||||
await this.app.vault.rename(fileA, tempPath);
|
||||
await this.app.vault.rename(fileB, pathA);
|
||||
const tmp = this.app.vault.getAbstractFileByPath(tempPath);
|
||||
if (!(tmp instanceof TFile)) throw new Error("temp file missing");
|
||||
await this.app.vault.rename(tmp, pathB);
|
||||
|
|
@ -855,10 +857,10 @@ export class HexMapView extends ItemView {
|
|||
? this.plugin.settings.pathTypes.find(p => p.name === this.activePathTypeName)
|
||||
: this.plugin.settings.pathTypes[0];
|
||||
if (activeType) {
|
||||
this.pathBtnSwatch.style.backgroundColor = activeType.color;
|
||||
this.pathBtnSwatch.style.display = "inline-block";
|
||||
this.pathBtnSwatch.setCssProps({ 'background-color': activeType.color });
|
||||
this.pathBtnSwatch.show();
|
||||
} else {
|
||||
this.pathBtnSwatch.style.display = "none";
|
||||
this.pathBtnSwatch.hide();
|
||||
}
|
||||
}
|
||||
this.terrainToolbarBtn?.toggleClass(
|
||||
|
|
@ -888,22 +890,23 @@ export class HexMapView extends ItemView {
|
|||
if (this.drawingMode === "icon" && this.paintIconName) {
|
||||
if (this.iconBtnPreview) {
|
||||
this.iconBtnPreview.src = getIconUrl(this.plugin, this.paintIconName);
|
||||
this.iconBtnPreview.style.display = "inline-block";
|
||||
this.iconBtnPreview.show();
|
||||
}
|
||||
} else {
|
||||
if (this.iconBtnPreview) this.iconBtnPreview.style.display = "none";
|
||||
if (this.iconBtnPreview) this.iconBtnPreview.hide();
|
||||
}
|
||||
if (this.drawingMode === "terrain") {
|
||||
if (this.terrainPickMode) {
|
||||
// Eyedropper waiting for a click — show ⌖ as the preview
|
||||
if (this.terrainToolbarBtn) {
|
||||
this.terrainToolbarBtn.style.borderColor =
|
||||
"var(--interactive-accent)";
|
||||
this.terrainToolbarBtn.style.color = "var(--interactive-accent)";
|
||||
this.terrainToolbarBtn.setCssProps({
|
||||
'border-color': "var(--interactive-accent)",
|
||||
color: "var(--interactive-accent)",
|
||||
});
|
||||
}
|
||||
if (this.terrainBtnPreview) {
|
||||
this.terrainBtnPreview.style.backgroundColor = "";
|
||||
this.terrainBtnPreview.style.display = "inline-block";
|
||||
this.terrainBtnPreview.setCssProps({ 'background-color': "" });
|
||||
this.terrainBtnPreview.show();
|
||||
this.terrainBtnPreview.textContent = "⌖";
|
||||
}
|
||||
} else {
|
||||
|
|
@ -915,29 +918,28 @@ export class HexMapView extends ItemView {
|
|||
: undefined;
|
||||
if (entry) {
|
||||
if (this.terrainToolbarBtn) {
|
||||
this.terrainToolbarBtn.style.borderColor = entry.color;
|
||||
this.terrainToolbarBtn.setCssProps({ 'border-color': entry.color });
|
||||
}
|
||||
if (this.terrainBtnPreview) {
|
||||
this.terrainBtnPreview.style.backgroundColor = entry.color;
|
||||
this.terrainBtnPreview.style.display = "inline-block";
|
||||
this.terrainBtnPreview.setCssProps({ 'background-color': entry.color });
|
||||
this.terrainBtnPreview.show();
|
||||
}
|
||||
} else {
|
||||
// Clear mode — show active state without a color
|
||||
if (this.terrainToolbarBtn) {
|
||||
this.terrainToolbarBtn.style.borderColor = "";
|
||||
this.terrainToolbarBtn.setCssProps({ 'border-color': "" });
|
||||
}
|
||||
if (this.terrainBtnPreview) {
|
||||
this.terrainBtnPreview.style.display = "none";
|
||||
this.terrainBtnPreview.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.terrainToolbarBtn) {
|
||||
this.terrainToolbarBtn.style.borderColor = "";
|
||||
this.terrainToolbarBtn.style.color = "";
|
||||
this.terrainToolbarBtn.setCssProps({ 'border-color': "", color: "" });
|
||||
}
|
||||
if (this.terrainBtnPreview) {
|
||||
this.terrainBtnPreview.style.display = "none";
|
||||
this.terrainBtnPreview.hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1135,7 +1137,7 @@ export class HexMapView extends ItemView {
|
|||
}
|
||||
},
|
||||
);
|
||||
modal.loadData().then(() => modal.open());
|
||||
void modal.loadData().then(() => modal.open());
|
||||
}
|
||||
|
||||
private onHexContextMenu(evt: MouseEvent, x: number, y: number): void {
|
||||
|
|
@ -1818,7 +1820,7 @@ export class HexMapView extends ItemView {
|
|||
gridContainer
|
||||
.querySelectorAll<HTMLElement>(".duckmage-hex-icon[data-svg-elevated]")
|
||||
.forEach((img) => {
|
||||
img.style.display = "";
|
||||
img.show();
|
||||
img.removeAttribute("data-svg-elevated");
|
||||
});
|
||||
|
||||
|
|
@ -1855,8 +1857,6 @@ export class HexMapView extends ItemView {
|
|||
const h = gridContainer.offsetTop + gridContainer.offsetHeight + 20;
|
||||
svg.setAttribute("width", String(w));
|
||||
svg.setAttribute("height", String(h));
|
||||
svg.style.cssText =
|
||||
"position:absolute;top:0;left:0;pointer-events:none;z-index:5;";
|
||||
|
||||
// Build a smooth path through an ordered list of points using quadratic
|
||||
// bezier curves — corners are rounded by curving through midpoints.
|
||||
|
|
@ -2097,7 +2097,7 @@ export class HexMapView extends ItemView {
|
|||
if (!pos) return;
|
||||
const origImg = hexEl.querySelector<HTMLElement>(".duckmage-hex-icon");
|
||||
if (origImg) {
|
||||
origImg.style.display = "none";
|
||||
origImg.hide();
|
||||
origImg.setAttribute("data-svg-elevated", "1");
|
||||
}
|
||||
const imgEl = document.createElementNS(svgNS, "image");
|
||||
|
|
@ -2250,8 +2250,7 @@ class TablePickerModal extends Modal {
|
|||
}
|
||||
|
||||
onOpen(): void {
|
||||
this.titleEl.style.cssText =
|
||||
"display:flex;align-items:center;justify-content:space-between;";
|
||||
this.titleEl.setCssProps({ display: 'flex', 'align-items': 'center', 'justify-content': 'space-between' });
|
||||
this.titleEl.createSpan({ text: "Select table" });
|
||||
const openViewBtn = this.titleEl.createEl("button", {
|
||||
cls: "duckmage-rt-icon-btn",
|
||||
|
|
@ -2259,7 +2258,7 @@ class TablePickerModal extends Modal {
|
|||
title: "Open random tables view",
|
||||
});
|
||||
openViewBtn.addEventListener("click", () => {
|
||||
this.app.workspace
|
||||
void this.app.workspace
|
||||
.getLeaf("tab")
|
||||
.setViewState({ type: VIEW_TYPE_RANDOM_TABLES });
|
||||
});
|
||||
|
|
@ -2369,17 +2368,17 @@ class TablePickerModal extends Modal {
|
|||
const childrenEl = folderEl.createDiv({
|
||||
cls: "duckmage-rt-folder-children",
|
||||
});
|
||||
if (isCollapsed) childrenEl.style.display = "none";
|
||||
if (isCollapsed) childrenEl.hide();
|
||||
this.renderNodes(childrenEl, node.children, forceExpanded);
|
||||
header.addEventListener("click", () => {
|
||||
const nowCollapsed = !this.collapsedFolders.has(node.path);
|
||||
if (nowCollapsed) {
|
||||
this.collapsedFolders.add(node.path);
|
||||
childrenEl.style.display = "none";
|
||||
childrenEl.hide();
|
||||
arrow.textContent = "▶";
|
||||
} else {
|
||||
this.collapsedFolders.delete(node.path);
|
||||
childrenEl.style.display = "";
|
||||
childrenEl.show();
|
||||
arrow.textContent = "▼";
|
||||
}
|
||||
});
|
||||
|
|
@ -2522,17 +2521,17 @@ class FactionPickerModal extends Modal {
|
|||
const childrenEl = folderEl.createDiv({
|
||||
cls: "duckmage-rt-folder-children",
|
||||
});
|
||||
if (isCollapsed) childrenEl.style.display = "none";
|
||||
if (isCollapsed) childrenEl.hide();
|
||||
this.renderNodes(childrenEl, node.children, forceExpanded);
|
||||
header.addEventListener("click", () => {
|
||||
const nowCollapsed = !this.collapsedFolders.has(node.path);
|
||||
if (nowCollapsed) {
|
||||
this.collapsedFolders.add(node.path);
|
||||
childrenEl.style.display = "none";
|
||||
childrenEl.hide();
|
||||
arrow.textContent = "▶";
|
||||
} else {
|
||||
this.collapsedFolders.delete(node.path);
|
||||
childrenEl.style.display = "";
|
||||
childrenEl.show();
|
||||
arrow.textContent = "▼";
|
||||
}
|
||||
});
|
||||
|
|
@ -2553,13 +2552,12 @@ class HexHelpModal extends Modal {
|
|||
onOpen(): void {
|
||||
this.titleEl.setText("Hex map — controls & tools");
|
||||
this.contentEl.addClass("duckmage-help-modal");
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
void MarkdownRenderer.render(
|
||||
this.app,
|
||||
HELP_CONTENT,
|
||||
this.contentEl,
|
||||
"",
|
||||
this as any,
|
||||
this as unknown as Component,
|
||||
);
|
||||
}
|
||||
onClose(): void {
|
||||
|
|
|
|||
|
|
@ -54,9 +54,9 @@ export class RegionModal extends HexmakerModal {
|
|||
const createRow = contentEl.createDiv({ cls: "duckmage-region-row" });
|
||||
const nameInput = createRow.createEl("input", { type: "text", placeholder: "region-name" }) as HTMLInputElement;
|
||||
const colsInput = createRow.createEl("input", { type: "number", value: "20" }) as HTMLInputElement;
|
||||
colsInput.style.width = "55px";
|
||||
colsInput.setCssProps({ width: "55px" });
|
||||
const rowsInput = createRow.createEl("input", { type: "number", value: "16" }) as HTMLInputElement;
|
||||
rowsInput.style.width = "55px";
|
||||
rowsInput.setCssProps({ width: "55px" });
|
||||
|
||||
const paletteSelect = createRow.createEl("select") as HTMLSelectElement;
|
||||
for (const pal of this.plugin.settings.terrainPalettes) {
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ export class TerrainPickerModal extends HexmakerModal {
|
|||
for (const entry of this.palette) {
|
||||
const btn = grid.createDiv({ cls: "duckmage-terrain-option" });
|
||||
const preview = btn.createDiv({ cls: "duckmage-terrain-preview" });
|
||||
preview.style.backgroundColor = entry.color;
|
||||
preview.setCssProps({ 'background-color': entry.color });
|
||||
if (entry.icon) {
|
||||
createIconEl(preview, getIconUrl(this.plugin, entry.icon), entry.name, entry.iconColor, "duckmage-terrain-preview-icon");
|
||||
}
|
||||
|
|
@ -175,7 +175,7 @@ export class TerrainPickerModal extends HexmakerModal {
|
|||
|
||||
// Standard colored preview + icon (same as pick mode)
|
||||
const preview = tile.createDiv({ cls: "duckmage-terrain-preview" });
|
||||
preview.style.backgroundColor = entry.color;
|
||||
preview.setCssProps({ 'background-color': entry.color });
|
||||
if (entry.icon) {
|
||||
createIconEl(preview, getIconUrl(this.plugin, entry.icon), entry.name, entry.iconColor, "duckmage-terrain-preview-icon");
|
||||
}
|
||||
|
|
@ -320,7 +320,7 @@ class TerrainTemplatePickerModal extends HexmakerModal {
|
|||
for (const t of terrains) {
|
||||
const row = list.createDiv({ cls: "duckmage-terrain-template-row" });
|
||||
const swatch = row.createSpan({ cls: "duckmage-terrain-template-swatch" });
|
||||
swatch.style.background = t.color;
|
||||
swatch.setCssProps({ background: t.color });
|
||||
row.createSpan({ text: t.name });
|
||||
row.addEventListener("click", () => { this.onPick(t); this.close(); });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,13 @@
|
|||
import { App, ItemView, Notice, TFile, WorkspaceLeaf } from "obsidian";
|
||||
import { ItemView, TFile, WorkspaceLeaf } from "obsidian";
|
||||
import type HexmakerPlugin from "../HexmakerPlugin";
|
||||
import {
|
||||
VIEW_TYPE_HEX_MAP,
|
||||
VIEW_TYPE_HEX_TABLE,
|
||||
VIEW_TYPE_RANDOM_TABLES,
|
||||
} from "../constants";
|
||||
import type { HexMapView } from "../hex-map/HexMapView";
|
||||
import type { RandomTableView } from "../random-tables/RandomTableView";
|
||||
import {
|
||||
getAllSectionData,
|
||||
setSectionContent,
|
||||
addLinkToSection,
|
||||
addBacklinkToFile,
|
||||
} from "../sections";
|
||||
import { getTerrainFromFile, setTerrainInFile } from "../frontmatter";
|
||||
import { getIconUrl, normalizeFolder, makeTableTemplate, createIconEl } from "../utils";
|
||||
import type { TerrainColor, LinkSection } from "../types";
|
||||
import { getAllSectionData } from "../sections";
|
||||
import { getTerrainFromFile } from "../frontmatter";
|
||||
import { normalizeFolder, makeTableTemplate } from "../utils";
|
||||
import { TerrainFilterModal } from "./TerrainFilterModal";
|
||||
import { HexCellModal } from "./HexCellModal";
|
||||
import { MultiLinkNavModal } from "./MultiLinkNavModal";
|
||||
|
|
@ -97,7 +89,7 @@ export class HexTableView extends ItemView {
|
|||
: "Hex table";
|
||||
}
|
||||
|
||||
async onOpen(): Promise<void> {
|
||||
onOpen(): Promise<void> {
|
||||
const { contentEl } = this;
|
||||
contentEl.addClass("duckmage-hex-table-container");
|
||||
|
||||
|
|
@ -116,7 +108,7 @@ export class HexTableView extends ItemView {
|
|||
toolbar.createSpan({ text: "X:", cls: "duckmage-filter-label" });
|
||||
this.filterXMinInput = toolbar.createEl("input", {
|
||||
cls: "duckmage-filter-range-input",
|
||||
}) as HTMLInputElement;
|
||||
});
|
||||
this.filterXMinInput.type = "number";
|
||||
this.filterXMinInput.placeholder = "min";
|
||||
this.filterXMinInput.addEventListener("input", () => {
|
||||
|
|
@ -127,7 +119,7 @@ export class HexTableView extends ItemView {
|
|||
toolbar.createSpan({ text: "–", cls: "duckmage-filter-label" });
|
||||
this.filterXMaxInput = toolbar.createEl("input", {
|
||||
cls: "duckmage-filter-range-input",
|
||||
}) as HTMLInputElement;
|
||||
});
|
||||
this.filterXMaxInput.type = "number";
|
||||
this.filterXMaxInput.placeholder = "max";
|
||||
this.filterXMaxInput.addEventListener("input", () => {
|
||||
|
|
@ -142,7 +134,7 @@ export class HexTableView extends ItemView {
|
|||
toolbar.createSpan({ text: "Y:", cls: "duckmage-filter-label" });
|
||||
this.filterYMinInput = toolbar.createEl("input", {
|
||||
cls: "duckmage-filter-range-input",
|
||||
}) as HTMLInputElement;
|
||||
});
|
||||
this.filterYMinInput.type = "number";
|
||||
this.filterYMinInput.placeholder = "min";
|
||||
this.filterYMinInput.addEventListener("input", () => {
|
||||
|
|
@ -153,7 +145,7 @@ export class HexTableView extends ItemView {
|
|||
toolbar.createSpan({ text: "–", cls: "duckmage-filter-label" });
|
||||
this.filterYMaxInput = toolbar.createEl("input", {
|
||||
cls: "duckmage-filter-range-input",
|
||||
}) as HTMLInputElement;
|
||||
});
|
||||
this.filterYMaxInput.type = "number";
|
||||
this.filterYMaxInput.placeholder = "max";
|
||||
this.filterYMaxInput.addEventListener("input", () => {
|
||||
|
|
@ -194,7 +186,7 @@ export class HexTableView extends ItemView {
|
|||
const townLabel = toolbar.createEl("label", {
|
||||
cls: "duckmage-filter-check-label",
|
||||
});
|
||||
this.townCb = townLabel.createEl("input") as HTMLInputElement;
|
||||
this.townCb = townLabel.createEl("input");
|
||||
this.townCb.type = "checkbox";
|
||||
townLabel.appendText("Town");
|
||||
this.townCb.addEventListener("change", () => {
|
||||
|
|
@ -206,7 +198,7 @@ export class HexTableView extends ItemView {
|
|||
const dungeonLabel = toolbar.createEl("label", {
|
||||
cls: "duckmage-filter-check-label",
|
||||
});
|
||||
this.dungeonCb = dungeonLabel.createEl("input") as HTMLInputElement;
|
||||
this.dungeonCb = dungeonLabel.createEl("input");
|
||||
this.dungeonCb.type = "checkbox";
|
||||
dungeonLabel.appendText("Dungeon");
|
||||
this.dungeonCb.addEventListener("change", () => {
|
||||
|
|
@ -218,7 +210,7 @@ export class HexTableView extends ItemView {
|
|||
const featureLabel = toolbar.createEl("label", {
|
||||
cls: "duckmage-filter-check-label",
|
||||
});
|
||||
this.featureCb = featureLabel.createEl("input") as HTMLInputElement;
|
||||
this.featureCb = featureLabel.createEl("input");
|
||||
this.featureCb.type = "checkbox";
|
||||
featureLabel.appendText("Feature");
|
||||
this.featureCb.addEventListener("change", () => {
|
||||
|
|
@ -230,7 +222,7 @@ export class HexTableView extends ItemView {
|
|||
const questLabel = toolbar.createEl("label", {
|
||||
cls: "duckmage-filter-check-label",
|
||||
});
|
||||
this.questCb = questLabel.createEl("input") as HTMLInputElement;
|
||||
this.questCb = questLabel.createEl("input");
|
||||
this.questCb.type = "checkbox";
|
||||
questLabel.appendText("Quest");
|
||||
this.questCb.addEventListener("change", () => {
|
||||
|
|
@ -242,7 +234,7 @@ export class HexTableView extends ItemView {
|
|||
const factionLabel = toolbar.createEl("label", {
|
||||
cls: "duckmage-filter-check-label",
|
||||
});
|
||||
this.factionCb = factionLabel.createEl("input") as HTMLInputElement;
|
||||
this.factionCb = factionLabel.createEl("input");
|
||||
this.factionCb.type = "checkbox";
|
||||
factionLabel.appendText("Faction");
|
||||
this.factionCb.addEventListener("change", () => {
|
||||
|
|
@ -255,22 +247,24 @@ export class HexTableView extends ItemView {
|
|||
// Region filter
|
||||
const regionSelect = toolbar.createEl("select", {
|
||||
cls: "duckmage-hex-table-region-select",
|
||||
}) as HTMLSelectElement;
|
||||
});
|
||||
this.regionSelectEl = regionSelect;
|
||||
regionSelect.createEl("option", { value: "all", text: "All regions" });
|
||||
for (const r of this.plugin.settings.regions) {
|
||||
regionSelect.createEl("option", { value: r.name, text: r.name });
|
||||
}
|
||||
// Default to active map view's region
|
||||
interface WithActiveRegionName { activeRegionName: string; }
|
||||
const mapLeaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_HEX_MAP);
|
||||
if (mapLeaves.length > 0) {
|
||||
const mapView = mapLeaves[0].view as HexMapView;
|
||||
const mapView = mapLeaves[0].view as unknown as WithActiveRegionName;
|
||||
this.regionFilter = mapView.activeRegionName;
|
||||
}
|
||||
regionSelect.value = this.regionFilter;
|
||||
regionSelect.addEventListener("change", () => {
|
||||
this.regionFilter = regionSelect.value;
|
||||
(this.leaf as any).updateHeader();
|
||||
interface WithUpdateHeader { updateHeader?(): void; }
|
||||
(this.leaf as unknown as WithUpdateHeader).updateHeader?.();
|
||||
void this.loadTable();
|
||||
});
|
||||
|
||||
|
|
@ -349,12 +343,14 @@ export class HexTableView extends ItemView {
|
|||
);
|
||||
|
||||
void this.loadTable();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async onClose(): Promise<void> {
|
||||
onClose(): Promise<void> {
|
||||
for (const timer of this.updateTimers.values()) clearTimeout(timer);
|
||||
this.updateTimers.clear();
|
||||
this.contentEl.empty();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
async loadTable(): Promise<void> {
|
||||
|
|
@ -402,11 +398,9 @@ export class HexTableView extends ItemView {
|
|||
}
|
||||
|
||||
files.sort((a, b) => {
|
||||
const [p, s] = this.sortPrimary === "x" ? ["x", "y"] : ["y", "x"];
|
||||
const diff =
|
||||
(a as any)[p] !== (b as any)[p]
|
||||
? (a as any)[p] - (b as any)[p]
|
||||
: (a as any)[s] - (b as any)[s];
|
||||
const p = this.sortPrimary === "x" ? "x" : "y";
|
||||
const s = this.sortPrimary === "x" ? "y" : "x";
|
||||
const diff = a[p] !== b[p] ? a[p] - b[p] : a[s] - b[s];
|
||||
return this.sortAsc ? diff : -diff;
|
||||
});
|
||||
|
||||
|
|
@ -601,7 +595,6 @@ export class HexTableView extends ItemView {
|
|||
const palette = this.plugin.getRegionPalette(region);
|
||||
const paletteMap = new Map(palette.map((p) => [p.name, p]));
|
||||
const terrainName = getTerrainFromFile(this.app, path);
|
||||
const terrainEntry = terrainName ? paletteMap.get(terrainName) : undefined;
|
||||
|
||||
const hasTown = (links.get("towns") ?? []).length > 0;
|
||||
const hasDungeon = (links.get("dungeons") ?? []).length > 0;
|
||||
|
|
@ -640,16 +633,17 @@ export class HexTableView extends ItemView {
|
|||
jumpBtn.title = "Center map on this hex";
|
||||
jumpBtn.addEventListener("click", async (e) => {
|
||||
e.stopPropagation();
|
||||
interface WithCenterOnHex { centerOnHex(x: number, y: number): void; }
|
||||
const existingLeaves =
|
||||
this.app.workspace.getLeavesOfType(VIEW_TYPE_HEX_MAP);
|
||||
if (existingLeaves.length > 0) {
|
||||
this.app.workspace.revealLeaf(existingLeaves[0]);
|
||||
(existingLeaves[0].view as HexMapView).centerOnHex(x, y);
|
||||
void this.app.workspace.revealLeaf(existingLeaves[0]);
|
||||
(existingLeaves[0].view as unknown as WithCenterOnHex).centerOnHex(x, y);
|
||||
} else {
|
||||
const leaf = this.app.workspace.getLeaf("tab");
|
||||
await leaf.setViewState({ type: VIEW_TYPE_HEX_MAP });
|
||||
// Wait one frame for the view to render before centering
|
||||
setTimeout(() => (leaf.view as HexMapView).centerOnHex(x, y), 100);
|
||||
setTimeout(() => (leaf.view as unknown as WithCenterOnHex).centerOnHex(x, y), 100);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -719,10 +713,11 @@ export class HexTableView extends ItemView {
|
|||
e.preventDefault();
|
||||
const file = this.app.metadataCache.getFirstLinkpathDest(linkList[0], path);
|
||||
if (!(file instanceof TFile)) return;
|
||||
interface WithOpenTable { openTable(path: string): Promise<void>; }
|
||||
const leaf = this.app.workspace.getLeaf("tab");
|
||||
await leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES, active: true });
|
||||
this.app.workspace.revealLeaf(leaf);
|
||||
await (leaf.view as RandomTableView).openTable(file.path);
|
||||
void this.app.workspace.revealLeaf(leaf);
|
||||
await (leaf.view as unknown as WithOpenTable).openTable(file.path);
|
||||
});
|
||||
td.addEventListener("click", async () => {
|
||||
if (linkList.length === 0) {
|
||||
|
|
@ -744,11 +739,12 @@ export class HexTableView extends ItemView {
|
|||
path,
|
||||
);
|
||||
if (file instanceof TFile) {
|
||||
interface WithOpenTable { openTable(path: string): Promise<void>; }
|
||||
const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_RANDOM_TABLES);
|
||||
const leaf = leaves.length > 0 ? leaves[0] : this.app.workspace.getLeaf("tab");
|
||||
await leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES, active: true });
|
||||
this.app.workspace.revealLeaf(leaf);
|
||||
await (leaf.view as RandomTableView).openTable(file.path);
|
||||
void this.app.workspace.revealLeaf(leaf);
|
||||
await (leaf.view as unknown as WithOpenTable).openTable(file.path);
|
||||
}
|
||||
} else {
|
||||
const file = this.app.metadataCache.getFirstLinkpathDest(
|
||||
|
|
@ -756,7 +752,7 @@ export class HexTableView extends ItemView {
|
|||
path,
|
||||
);
|
||||
if (file instanceof TFile)
|
||||
this.app.workspace.getLeaf().openFile(file);
|
||||
void this.app.workspace.getLeaf().openFile(file);
|
||||
}
|
||||
} else {
|
||||
// Multiple: show a nav list
|
||||
|
|
@ -846,7 +842,7 @@ export class HexTableView extends ItemView {
|
|||
let totalWidth = 0;
|
||||
for (let i = 0; i < ths.length; i++) {
|
||||
const w = defaultWidths[i] ?? 160;
|
||||
const col = document.createElement("col") as HTMLTableColElement;
|
||||
const col = document.createElement("col");
|
||||
col.style.width = `${w}px`;
|
||||
colgroup.appendChild(col);
|
||||
cols.push(col);
|
||||
|
|
@ -869,7 +865,6 @@ export class HexTableView extends ItemView {
|
|||
|
||||
const onMove = (me: MouseEvent) => {
|
||||
const newW = Math.max(20, startW + me.clientX - startX);
|
||||
const delta = newW - parseInt(col.style.width, 10);
|
||||
col.style.width = `${newW}px`;
|
||||
table.style.width = `${startTW + (newW - startW)}px`;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -63,10 +63,10 @@ export class LinkPickerModal extends HexmakerModal {
|
|||
cls: "mod-cta",
|
||||
});
|
||||
createBtn.addEventListener("click", () =>
|
||||
this.createAndLink(input.value.trim()),
|
||||
void this.createAndLink(input.value.trim()),
|
||||
);
|
||||
input.addEventListener("keydown", (e: KeyboardEvent) => {
|
||||
if (e.key === "Enter") this.createAndLink(input.value.trim());
|
||||
if (e.key === "Enter") void this.createAndLink(input.value.trim());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +95,8 @@ export class LinkPickerModal extends HexmakerModal {
|
|||
return;
|
||||
}
|
||||
}
|
||||
await this.addLink(file as TFile);
|
||||
if (!(file instanceof TFile)) return;
|
||||
await this.addLink(file);
|
||||
}
|
||||
|
||||
private async ensureHexNote(): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export class MultiLinkNavModal extends HexmakerModal {
|
|||
this.sourcePath,
|
||||
);
|
||||
if (file instanceof TFile) {
|
||||
this.app.workspace.getLeaf().openFile(file);
|
||||
void this.app.workspace.getLeaf().openFile(file);
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export class TerrainFilterModal extends HexmakerModal {
|
|||
|
||||
onOpen(): void {
|
||||
this.makeDraggable();
|
||||
this.titleEl.setText("Filter by Terrain");
|
||||
this.titleEl.setText("Filter by terrain");
|
||||
const { contentEl } = this;
|
||||
contentEl.addClass("duckmage-terrain-filter-modal");
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ export class TerrainFilterModal extends HexmakerModal {
|
|||
const lbl = list.createEl("label", {
|
||||
cls: "duckmage-terrain-filter-row" + (indented ? " duckmage-terrain-filter-row-indented" : ""),
|
||||
});
|
||||
const cb = lbl.createEl("input") as HTMLInputElement;
|
||||
const cb = lbl.createEl("input");
|
||||
cb.type = "checkbox";
|
||||
applyRowState(lbl, cb, name);
|
||||
|
||||
|
|
|
|||
|
|
@ -83,12 +83,12 @@ export class RandomTableEditorModal extends HexmakerModal {
|
|||
await this.app.fileManager.renameFile(this.file, newPath);
|
||||
this.titleEl.setText(`Edit: ${this.file.basename}`);
|
||||
this.onSaved?.();
|
||||
} catch (e) {
|
||||
} catch {
|
||||
nameInput.value = this.file.basename; // revert on error
|
||||
}
|
||||
};
|
||||
|
||||
nameInput.addEventListener("blur", doRename);
|
||||
nameInput.addEventListener("blur", () => void doRename());
|
||||
nameInput.addEventListener("keydown", (e: KeyboardEvent) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
|
|
@ -139,8 +139,8 @@ export class RandomTableEditorModal extends HexmakerModal {
|
|||
let dragSrcIndex = -1;
|
||||
|
||||
const autoResize = (el: HTMLTextAreaElement) => {
|
||||
el.style.height = "auto";
|
||||
el.style.height = `${el.scrollHeight}px`;
|
||||
el.setCssProps({ height: "auto" });
|
||||
el.setCssProps({ height: `${el.scrollHeight}px` });
|
||||
};
|
||||
|
||||
const renderRows = () => {
|
||||
|
|
@ -281,7 +281,7 @@ export class RandomTableEditorModal extends HexmakerModal {
|
|||
const errorEl = entriesSection.createDiv({
|
||||
cls: "duckmage-table-editor-add-error",
|
||||
});
|
||||
errorEl.style.display = "none";
|
||||
errorEl.hide();
|
||||
|
||||
// ── Description ───────────────────────────────────────────────────
|
||||
const descRow = contentEl.createDiv({
|
||||
|
|
@ -382,16 +382,16 @@ export class RandomTableEditorModal extends HexmakerModal {
|
|||
);
|
||||
if (!(found instanceof TFile)) {
|
||||
errorEl.setText(`No note found: "${normalizedPath}"`);
|
||||
errorEl.style.display = "";
|
||||
errorEl.show();
|
||||
return;
|
||||
}
|
||||
errorEl.style.display = "none";
|
||||
errorEl.hide();
|
||||
// Store the vault-relative path without extension (canonical link form)
|
||||
const resolvedPath = found.path.replace(/\.md$/i, "");
|
||||
const weight = Math.max(1, parseInt(newWeight.value, 10) || 1);
|
||||
entries.push({ result: resolvedPath, weight, isLink: true });
|
||||
} else {
|
||||
errorEl.style.display = "none";
|
||||
errorEl.hide();
|
||||
const weight = Math.max(1, parseInt(newWeight.value, 10) || 1);
|
||||
entries.push({ result: raw, weight });
|
||||
}
|
||||
|
|
@ -493,7 +493,6 @@ export class RandomTableEditorModal extends HexmakerModal {
|
|||
.filter(
|
||||
(f) => f.parent?.path === folderPath && !f.basename.startsWith("_"),
|
||||
);
|
||||
const existingNames = new Set(existing.map((f) => f.basename));
|
||||
|
||||
// For each entry: create note if missing
|
||||
for (const entry of entries) {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export class RandomTableModal extends HexmakerModal {
|
|||
this.makeDraggable();
|
||||
|
||||
if (this.initialFilePath) {
|
||||
this.loadTable(contentEl, this.initialFilePath);
|
||||
void this.loadTable(contentEl, this.initialFilePath);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ export class RandomTableModal extends HexmakerModal {
|
|||
|
||||
// "Open in roller" link — hidden until a table is selected
|
||||
const openLink = contentEl.createEl("a", { text: "Open in roller view", cls: "duckmage-roll-modal-open-link" });
|
||||
openLink.style.display = "none";
|
||||
openLink.hide();
|
||||
|
||||
const tableContainer = contentEl.createDiv({ cls: "duckmage-roll-modal-table-wrap" });
|
||||
const resultBox = this.buildResultBox(contentEl);
|
||||
|
|
@ -65,14 +65,14 @@ export class RandomTableModal extends HexmakerModal {
|
|||
|
||||
select.addEventListener("change", async () => {
|
||||
tableContainer.empty();
|
||||
resultBox.el.style.display = "none";
|
||||
resultBox.el.hide();
|
||||
rollBtn.disabled = true;
|
||||
const path = select.value;
|
||||
if (!path) { openLink.style.display = "none"; return; }
|
||||
if (!path) { openLink.hide(); return; }
|
||||
const file = this.app.vault.getAbstractFileByPath(path);
|
||||
if (!(file instanceof TFile)) return;
|
||||
|
||||
openLink.style.display = "";
|
||||
openLink.show();
|
||||
openLink.onclick = () => { this.openInRoller(file.path); };
|
||||
|
||||
await this.renderOddsTable(tableContainer, resultBox, rollBtn, file);
|
||||
|
|
@ -97,7 +97,7 @@ export class RandomTableModal extends HexmakerModal {
|
|||
new RandomTableEditorModal(this.app, this.plugin, file, async () => {
|
||||
// Reload the table in place after saving
|
||||
tableContainer.empty();
|
||||
resultBox.el.style.display = "none";
|
||||
resultBox.el.hide();
|
||||
rollBtn.disabled = true;
|
||||
await this.renderOddsTable(tableContainer, resultBox, rollBtn, file);
|
||||
}, content).open();
|
||||
|
|
@ -118,7 +118,7 @@ export class RandomTableModal extends HexmakerModal {
|
|||
const leaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_RANDOM_TABLES);
|
||||
if (leaves.length > 0) {
|
||||
this.app.workspace.revealLeaf(leaves[0]);
|
||||
(leaves[0].view as any).openTable?.(filePath);
|
||||
(leaves[0].view as unknown as { openTable?: (path: string) => void }).openTable?.(filePath);
|
||||
} else {
|
||||
void this.app.workspace.getLeaf("tab").setViewState({
|
||||
type: VIEW_TYPE_RANDOM_TABLES,
|
||||
|
|
@ -130,7 +130,7 @@ export class RandomTableModal extends HexmakerModal {
|
|||
|
||||
private buildResultBox(contentEl: HTMLElement): { el: HTMLElement; textarea: HTMLTextAreaElement } {
|
||||
const resultBox = contentEl.createDiv({ cls: "duckmage-roll-result" });
|
||||
resultBox.style.display = "none";
|
||||
resultBox.hide();
|
||||
const resultTextarea = resultBox.createEl("textarea", { cls: "duckmage-roll-result-textarea" });
|
||||
const resultBtns = resultBox.createDiv({ cls: "duckmage-roll-result-btns" });
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ export class RandomTableModal extends HexmakerModal {
|
|||
} else {
|
||||
const copyBtn = resultBtns.createEl("button", { text: "Copy", cls: "mod-cta" });
|
||||
copyBtn.addEventListener("click", () => {
|
||||
navigator.clipboard.writeText(resultTextarea.value);
|
||||
void navigator.clipboard.writeText(resultTextarea.value);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -187,7 +187,7 @@ export class RandomTableModal extends HexmakerModal {
|
|||
copyBtn.title = "Copy entry";
|
||||
copyBtn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
navigator.clipboard.writeText(entry.result);
|
||||
void navigator.clipboard.writeText(entry.result);
|
||||
copyBtn.setText("✓");
|
||||
setTimeout(() => copyBtn.setText("⎘"), 1200);
|
||||
});
|
||||
|
|
@ -202,7 +202,7 @@ export class RandomTableModal extends HexmakerModal {
|
|||
tr.textContent?.includes(rolled.result) || false,
|
||||
);
|
||||
});
|
||||
resultBox.el.style.display = "";
|
||||
resultBox.el.show();
|
||||
resultBox.textarea.value = rolled.result;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,9 @@ import {
|
|||
parseRandomTable,
|
||||
rollOnTable,
|
||||
getDieRanges,
|
||||
getOddsLabel,
|
||||
setDiceInFrontmatter,
|
||||
extractPostTableContent,
|
||||
type RandomTable,
|
||||
type RandomTableEntry,
|
||||
} from "./randomTable";
|
||||
import { parseWorkflow, generateDefaultTemplate } from "./workflow";
|
||||
|
||||
|
|
@ -89,13 +87,14 @@ export class RandomTableView extends ItemView {
|
|||
return "dice";
|
||||
}
|
||||
|
||||
async setState(state: any, result: ViewStateResult): Promise<void> {
|
||||
async setState(state: unknown, result: ViewStateResult): Promise<void> {
|
||||
await super.setState(state, result);
|
||||
if (state?.viewMode && (state.viewMode === "tables" || state.viewMode === "workflows")) {
|
||||
if (state.viewMode !== this.viewMode) this.setViewMode(state.viewMode);
|
||||
const s = state as { viewMode?: string; filePath?: string } | null;
|
||||
if (s?.viewMode && (s.viewMode === "tables" || s.viewMode === "workflows")) {
|
||||
if (s.viewMode !== this.viewMode) this.setViewMode(s.viewMode as "tables" | "workflows");
|
||||
}
|
||||
if (state?.filePath) {
|
||||
const file = this.app.vault.getAbstractFileByPath(state.filePath);
|
||||
if (s?.filePath) {
|
||||
const file = this.app.vault.getAbstractFileByPath(s.filePath);
|
||||
if (file instanceof TFile) {
|
||||
if (this.isInWorkflowsFolder(file.path)) {
|
||||
this.viewMode = "workflows";
|
||||
|
|
@ -105,7 +104,7 @@ export class RandomTableView extends ItemView {
|
|||
await this.loadWorkflow(file);
|
||||
} else {
|
||||
await this.loadList();
|
||||
this.loadTable(file);
|
||||
void this.loadTable(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -129,7 +128,7 @@ export class RandomTableView extends ItemView {
|
|||
cls: "duckmage-rt-icon-btn",
|
||||
});
|
||||
refreshBtn.title = "Refresh list";
|
||||
refreshBtn.addEventListener("click", () => this.loadList());
|
||||
refreshBtn.addEventListener("click", () => void this.loadList());
|
||||
|
||||
// ── Mode toggle tabs ─────────────────────────────────────────────────
|
||||
const modeTabs = leftCol.createDiv({ cls: "duckmage-rt-mode-tabs" });
|
||||
|
|
@ -149,14 +148,14 @@ export class RandomTableView extends ItemView {
|
|||
if (e.button !== 1) return;
|
||||
e.preventDefault();
|
||||
const leaf = this.app.workspace.getLeaf("tab");
|
||||
leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES, active: true, state: { viewMode: "tables" } });
|
||||
void leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES, active: true, state: { viewMode: "tables" } });
|
||||
this.app.workspace.revealLeaf(leaf);
|
||||
});
|
||||
this.workflowsBtn.addEventListener("auxclick", (e: MouseEvent) => {
|
||||
if (e.button !== 1) return;
|
||||
e.preventDefault();
|
||||
const leaf = this.app.workspace.getLeaf("tab");
|
||||
leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES, active: true, state: { viewMode: "workflows" } });
|
||||
void leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES, active: true, state: { viewMode: "workflows" } });
|
||||
this.app.workspace.revealLeaf(leaf);
|
||||
});
|
||||
|
||||
|
|
@ -167,7 +166,7 @@ export class RandomTableView extends ItemView {
|
|||
searchInput.placeholder = "Filter tables…";
|
||||
searchInput.addEventListener("input", () => {
|
||||
this.filterQuery = searchInput.value.toLowerCase().trim();
|
||||
this.loadList();
|
||||
void this.loadList();
|
||||
});
|
||||
|
||||
this.listEl = leftCol.createDiv({ cls: "duckmage-rt-list" });
|
||||
|
|
@ -198,12 +197,12 @@ export class RandomTableView extends ItemView {
|
|||
|
||||
// ── Workflow footer (shown in workflows mode) ──────────────────────────
|
||||
this.workflowFooterEl = listFooter.createDiv();
|
||||
this.workflowFooterEl.style.display = "none";
|
||||
this.workflowFooterEl.hide();
|
||||
const newWfFooterBtn = this.workflowFooterEl.createEl("button", {
|
||||
text: "+ New workflow",
|
||||
cls: "duckmage-rt-new-btn",
|
||||
});
|
||||
newWfFooterBtn.addEventListener("click", () => this.createWorkflow());
|
||||
newWfFooterBtn.addEventListener("click", () => void this.createWorkflow());
|
||||
|
||||
// ── Table footer (shown in tables mode) ───────────────────────────────
|
||||
this.tableFooterEl = listFooter.createDiv();
|
||||
|
|
@ -236,7 +235,7 @@ export class RandomTableView extends ItemView {
|
|||
cls: "duckmage-rt-from-folder-input",
|
||||
attr: { placeholder: "Generate from folder link (optional)…" },
|
||||
});
|
||||
fromFolderInput.style.marginTop = "6px";
|
||||
fromFolderInput.setCssProps({ "margin-top": "6px" });
|
||||
|
||||
const createTable = async () => {
|
||||
const name = newInput.value.trim();
|
||||
|
|
@ -281,14 +280,14 @@ export class RandomTableView extends ItemView {
|
|||
newInput.title = "";
|
||||
fromFolderInput.value = "";
|
||||
await this.loadList();
|
||||
await this.loadTable(file as TFile);
|
||||
if (file instanceof TFile) await this.loadTable(file);
|
||||
// Re-render after vault I/O settles to ensure linked-folder link styling is applied
|
||||
if (srcFolder) await this.renderDetail();
|
||||
};
|
||||
|
||||
newBtn.addEventListener("click", createTable);
|
||||
newInput.addEventListener("keydown", (e: KeyboardEvent) => {
|
||||
if (e.key === "Enter") createTable();
|
||||
if (e.key === "Enter") void createTable();
|
||||
});
|
||||
|
||||
// ── Right column: table detail ───────────────────────────────────────
|
||||
|
|
@ -401,8 +400,9 @@ export class RandomTableView extends ItemView {
|
|||
);
|
||||
}
|
||||
|
||||
async onClose(): Promise<void> {
|
||||
onClose(): Promise<void> {
|
||||
this.contentEl.empty();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// ── Public: open a specific table (called from hex editor / protocol handler) ──
|
||||
|
|
@ -418,7 +418,7 @@ export class RandomTableView extends ItemView {
|
|||
parent = parent.parent;
|
||||
}
|
||||
await this.loadList();
|
||||
this.loadTable(file);
|
||||
void this.loadTable(file);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -450,8 +450,8 @@ export class RandomTableView extends ItemView {
|
|||
this.viewMode = mode;
|
||||
this.tablesBtn?.toggleClass("is-active", mode === "tables");
|
||||
this.workflowsBtn?.toggleClass("is-active", mode === "workflows");
|
||||
if (this.tableFooterEl) this.tableFooterEl.style.display = mode === "tables" ? "" : "none";
|
||||
if (this.workflowFooterEl) this.workflowFooterEl.style.display = mode === "workflows" ? "" : "none";
|
||||
if (this.tableFooterEl) { if (mode === "tables") this.tableFooterEl.show(); else this.tableFooterEl.hide(); }
|
||||
if (this.workflowFooterEl) { if (mode === "workflows") this.workflowFooterEl.show(); else this.workflowFooterEl.hide(); }
|
||||
this.filterQuery = "";
|
||||
this.activeFile = null;
|
||||
this.app.workspace.trigger("layout-change");
|
||||
|
|
@ -540,7 +540,7 @@ export class RandomTableView extends ItemView {
|
|||
this.listEl.empty();
|
||||
|
||||
if (this.viewMode === "workflows") {
|
||||
await this.loadWorkflowList();
|
||||
this.loadWorkflowList();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -622,7 +622,7 @@ export class RandomTableView extends ItemView {
|
|||
}
|
||||
}
|
||||
|
||||
private async loadWorkflowList(): Promise<void> {
|
||||
private loadWorkflowList(): void {
|
||||
if (!this.listEl) return;
|
||||
const wfFolder = normalizeFolder(this.plugin.settings.workflowsFolder);
|
||||
if (!wfFolder) {
|
||||
|
|
@ -674,7 +674,7 @@ export class RandomTableView extends ItemView {
|
|||
folderHeader.createSpan({ cls: "duckmage-rt-folder-name", text: node.name });
|
||||
|
||||
const childrenEl = folderEl.createDiv({ cls: "duckmage-rt-folder-children" });
|
||||
if (isCollapsed) childrenEl.style.display = "none";
|
||||
if (isCollapsed) childrenEl.hide();
|
||||
this.renderWorkflowTreeNodes(childrenEl, node.children);
|
||||
|
||||
folderHeader.addEventListener("click", () => {
|
||||
|
|
@ -682,11 +682,11 @@ export class RandomTableView extends ItemView {
|
|||
const nowCollapsed = !this.collapsedFolders.has(key);
|
||||
if (nowCollapsed) {
|
||||
this.collapsedFolders.add(key);
|
||||
childrenEl.style.display = "none";
|
||||
childrenEl.hide();
|
||||
arrow.textContent = "▶";
|
||||
} else {
|
||||
this.collapsedFolders.delete(key);
|
||||
childrenEl.style.display = "";
|
||||
childrenEl.show();
|
||||
arrow.textContent = "▼";
|
||||
}
|
||||
});
|
||||
|
|
@ -695,12 +695,12 @@ export class RandomTableView extends ItemView {
|
|||
if (node.file === this.activeFile) row.addClass("is-active");
|
||||
row.setText(node.file.basename);
|
||||
row.title = node.file.path;
|
||||
row.addEventListener("click", () => this.loadWorkflow(node.file));
|
||||
row.addEventListener("click", () => void this.loadWorkflow(node.file));
|
||||
row.addEventListener("auxclick", (e: MouseEvent) => {
|
||||
if (e.button !== 1) return;
|
||||
e.preventDefault();
|
||||
const leaf = this.app.workspace.getLeaf("tab");
|
||||
leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES, active: true, state: { filePath: node.file.path } });
|
||||
void leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES, active: true, state: { filePath: node.file.path } });
|
||||
this.app.workspace.revealLeaf(leaf);
|
||||
});
|
||||
row.addEventListener("contextmenu", (e: MouseEvent) => {
|
||||
|
|
@ -711,7 +711,7 @@ export class RandomTableView extends ItemView {
|
|||
item.setIcon("external-link");
|
||||
item.onClick(() => {
|
||||
const leaf = this.app.workspace.getLeaf("tab");
|
||||
leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES, active: true, state: { filePath: node.file.path } });
|
||||
void leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES, active: true, state: { filePath: node.file.path } });
|
||||
this.app.workspace.revealLeaf(leaf);
|
||||
});
|
||||
});
|
||||
|
|
@ -820,19 +820,19 @@ export class RandomTableView extends ItemView {
|
|||
runBtn.addEventListener("click", () => {
|
||||
new WorkflowWizardModal(this.app, this.plugin, file).open();
|
||||
});
|
||||
runBtn.style.marginRight = "8px";
|
||||
runBtn.setCssProps({ "margin-right": "8px" });
|
||||
|
||||
const copyWfLinkBtn = this.detailEl.createEl("button", {
|
||||
text: "🔗 Copy link",
|
||||
cls: "duckmage-rt-copy-link-btn",
|
||||
});
|
||||
copyWfLinkBtn.title = "Copy a markdown link to open this workflow";
|
||||
copyWfLinkBtn.style.marginBottom = "12px";
|
||||
copyWfLinkBtn.setCssProps({ "margin-bottom": "12px" });
|
||||
copyWfLinkBtn.addEventListener("click", () => {
|
||||
const vault = encodeURIComponent(this.app.vault.getName());
|
||||
const path = encodeURIComponent(file.path);
|
||||
const link = `[🔗 ${file.basename}](obsidian://duckmage-workflow?vault=${vault}&file=${path})`;
|
||||
navigator.clipboard.writeText(link).then(() => {
|
||||
void navigator.clipboard.writeText(link).then(() => {
|
||||
copyWfLinkBtn.setText("Copied!");
|
||||
setTimeout(() => copyWfLinkBtn.setText("🔗 Copy link"), 1500);
|
||||
});
|
||||
|
|
@ -853,8 +853,7 @@ export class RandomTableView extends ItemView {
|
|||
const stepsEl = this.detailEl.createDiv({ cls: "duckmage-wf-detail-steps" });
|
||||
stepsEl.createEl("p", { text: "Steps", cls: "duckmage-rt-history-label" });
|
||||
const list = stepsEl.createEl("ul");
|
||||
list.style.margin = "0";
|
||||
list.style.paddingLeft = "18px";
|
||||
list.setCssProps({ margin: "0", "padding-left": "18px" });
|
||||
for (const step of workflow.steps) {
|
||||
const li = list.createEl("li");
|
||||
let primaryName: string;
|
||||
|
|
@ -886,7 +885,7 @@ export class RandomTableView extends ItemView {
|
|||
tmplSection.createEl("p", { text: "Template", cls: "duckmage-rt-history-label" });
|
||||
const tmplLink = tmplSection.createEl("a", { text: tmplFile.basename, cls: "duckmage-rt-entry-link" });
|
||||
tmplLink.addEventListener("click", () => {
|
||||
this.app.workspace.getLeaf(false).openFile(tmplFile);
|
||||
void this.app.workspace.getLeaf(false).openFile(tmplFile);
|
||||
});
|
||||
const tmplContent = await this.app.vault.read(tmplFile);
|
||||
const escapedContent = tmplContent.replace(/\$/g, "\\$");
|
||||
|
|
@ -950,7 +949,7 @@ export class RandomTableView extends ItemView {
|
|||
const childrenEl = folderEl.createDiv({
|
||||
cls: "duckmage-rt-folder-children",
|
||||
});
|
||||
if (isCollapsed) childrenEl.style.display = "none";
|
||||
if (isCollapsed) childrenEl.hide();
|
||||
|
||||
this.renderTreeNodes(childrenEl, node.children, forceExpanded);
|
||||
|
||||
|
|
@ -989,11 +988,11 @@ export class RandomTableView extends ItemView {
|
|||
const nowCollapsed = !this.collapsedFolders.has(node.path);
|
||||
if (nowCollapsed) {
|
||||
this.collapsedFolders.add(node.path);
|
||||
childrenEl.style.display = "none";
|
||||
childrenEl.hide();
|
||||
arrow.textContent = "▶";
|
||||
} else {
|
||||
this.collapsedFolders.delete(node.path);
|
||||
childrenEl.style.display = "";
|
||||
childrenEl.show();
|
||||
arrow.textContent = "▼";
|
||||
}
|
||||
});
|
||||
|
|
@ -1002,12 +1001,12 @@ export class RandomTableView extends ItemView {
|
|||
if (node.file === this.activeFile) row.addClass("is-active");
|
||||
row.setText(node.file.basename);
|
||||
row.title = node.file.path;
|
||||
row.addEventListener("click", () => this.loadTable(node.file));
|
||||
row.addEventListener("click", () => void this.loadTable(node.file));
|
||||
row.addEventListener("auxclick", (e: MouseEvent) => {
|
||||
if (e.button !== 1) return;
|
||||
e.preventDefault();
|
||||
const leaf = this.app.workspace.getLeaf("tab");
|
||||
leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES, active: true, state: { filePath: node.file.path } });
|
||||
void leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES, active: true, state: { filePath: node.file.path } });
|
||||
this.app.workspace.revealLeaf(leaf);
|
||||
});
|
||||
row.addEventListener("contextmenu", (e: MouseEvent) => {
|
||||
|
|
@ -1093,7 +1092,7 @@ export class RandomTableView extends ItemView {
|
|||
item.setIcon("external-link");
|
||||
item.onClick(() => {
|
||||
const leaf = this.app.workspace.getLeaf("tab");
|
||||
leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES, active: true, state: { filePath: file.path } });
|
||||
void leaf.setViewState({ type: VIEW_TYPE_RANDOM_TABLES, active: true, state: { filePath: file.path } });
|
||||
this.app.workspace.revealLeaf(leaf);
|
||||
});
|
||||
});
|
||||
|
|
@ -1160,7 +1159,7 @@ export class RandomTableView extends ItemView {
|
|||
await this.loadList();
|
||||
if (wasActive) {
|
||||
const newFile = this.app.vault.getAbstractFileByPath(newPath);
|
||||
if (newFile instanceof TFile) this.loadTable(newFile);
|
||||
if (newFile instanceof TFile) void this.loadTable(newFile);
|
||||
}
|
||||
} catch (err) {
|
||||
new Notice(`Could not move "${srcFile.basename}": ${err}`);
|
||||
|
|
@ -1250,7 +1249,7 @@ export class RandomTableView extends ItemView {
|
|||
editLink.addEventListener("click", async () => {
|
||||
const content = await this.app.vault.read(file);
|
||||
new RandomTableEditorModal(this.app, this.plugin, file, () =>
|
||||
this.renderDetail(), content,
|
||||
void this.renderDetail(), content,
|
||||
).open();
|
||||
});
|
||||
|
||||
|
|
@ -1259,7 +1258,7 @@ export class RandomTableView extends ItemView {
|
|||
cls: "duckmage-rt-edit-link",
|
||||
});
|
||||
openNoteLink.addEventListener("click", () => {
|
||||
this.app.workspace.getLeaf().openFile(file);
|
||||
void this.app.workspace.getLeaf().openFile(file);
|
||||
});
|
||||
|
||||
const dieSelect = header.createEl("select", {
|
||||
|
|
@ -1289,10 +1288,10 @@ export class RandomTableView extends ItemView {
|
|||
descCollapseBtn.title = "Collapse description";
|
||||
descHeader.createSpan({ text: "Description", cls: "duckmage-rt-section-label" });
|
||||
const descBody = descSection.createDiv({ cls: "duckmage-rt-desc-body" });
|
||||
MarkdownRenderer.render(this.app, table.description, descBody, file.path, this);
|
||||
void MarkdownRenderer.render(this.app, table.description, descBody, file.path, this);
|
||||
descCollapseBtn.addEventListener("click", () => {
|
||||
const collapsed = descBody.style.display === "none";
|
||||
descBody.style.display = collapsed ? "" : "none";
|
||||
if (collapsed) descBody.show(); else descBody.hide();
|
||||
descCollapseBtn.setText(collapsed ? "▼" : "▶");
|
||||
descCollapseBtn.title = collapsed ? "Collapse description" : "Expand description";
|
||||
});
|
||||
|
|
@ -1314,7 +1313,7 @@ export class RandomTableView extends ItemView {
|
|||
|
||||
collapseBtn.addEventListener("click", () => {
|
||||
const collapsed = tableBody.style.display === "none";
|
||||
tableBody.style.display = collapsed ? "" : "none";
|
||||
if (collapsed) tableBody.show(); else tableBody.hide();
|
||||
collapseBtn.setText(collapsed ? "▼" : "▶");
|
||||
collapseBtn.title = collapsed ? "Collapse table" : "Expand table";
|
||||
});
|
||||
|
|
@ -1355,7 +1354,7 @@ export class RandomTableView extends ItemView {
|
|||
`${table.linkedFolder}/${entry.result}.md`,
|
||||
);
|
||||
if (noteFile instanceof TFile)
|
||||
this.app.workspace.getLeaf().openFile(noteFile);
|
||||
void this.app.workspace.getLeaf().openFile(noteFile);
|
||||
});
|
||||
} else if (entry.isLink) {
|
||||
const label = entry.result.split("/").pop() ?? entry.result;
|
||||
|
|
@ -1368,7 +1367,7 @@ export class RandomTableView extends ItemView {
|
|||
const noteFile = this.app.vault.getAbstractFileByPath(entry.result + ".md")
|
||||
?? this.app.metadataCache.getFirstLinkpathDest(entry.result, "");
|
||||
if (noteFile instanceof TFile)
|
||||
this.app.workspace.getLeaf().openFile(noteFile);
|
||||
void this.app.workspace.getLeaf().openFile(noteFile);
|
||||
});
|
||||
} else {
|
||||
resultTd.setText(entry.result);
|
||||
|
|
@ -1386,7 +1385,7 @@ export class RandomTableView extends ItemView {
|
|||
copyBtn.title = "Copy entry";
|
||||
copyBtn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
navigator.clipboard.writeText(entry.result);
|
||||
void navigator.clipboard.writeText(entry.result);
|
||||
copyBtn.setText("✓");
|
||||
setTimeout(() => copyBtn.setText("⎘"), 1200);
|
||||
});
|
||||
|
|
@ -1406,7 +1405,7 @@ export class RandomTableView extends ItemView {
|
|||
const vault = encodeURIComponent(this.app.vault.getName());
|
||||
const path = encodeURIComponent(file.path);
|
||||
const link = `[🎲 ${file.basename}](obsidian://duckmage-roll?vault=${vault}&file=${path})`;
|
||||
navigator.clipboard.writeText(link).then(() => {
|
||||
void navigator.clipboard.writeText(link).then(() => {
|
||||
copyLinkBtn.setText("Copied!");
|
||||
setTimeout(() => copyLinkBtn.setText("🎲 Copy link"), 1500);
|
||||
});
|
||||
|
|
@ -1418,7 +1417,7 @@ export class RandomTableView extends ItemView {
|
|||
});
|
||||
|
||||
const resultBox = this.detailEl.createDiv({ cls: "duckmage-roll-result" });
|
||||
resultBox.style.display = "none";
|
||||
resultBox.hide();
|
||||
const resultTextarea = resultBox.createEl("textarea", {
|
||||
cls: "duckmage-roll-result-textarea",
|
||||
});
|
||||
|
|
@ -1430,7 +1429,7 @@ export class RandomTableView extends ItemView {
|
|||
cls: "duckmage-roll-copy-btn",
|
||||
});
|
||||
copyBtn.addEventListener("click", () => {
|
||||
navigator.clipboard.writeText(resultTextarea.value);
|
||||
void navigator.clipboard.writeText(resultTextarea.value);
|
||||
copyBtn.setText("Copied!");
|
||||
setTimeout(() => copyBtn.setText("Copy"), 1500);
|
||||
});
|
||||
|
|
@ -1439,7 +1438,7 @@ export class RandomTableView extends ItemView {
|
|||
text: "Open note",
|
||||
cls: "duckmage-rt-open-note-btn",
|
||||
});
|
||||
openNoteBtn.style.display = "none";
|
||||
openNoteBtn.hide();
|
||||
|
||||
const historyEl = this.detailEl.createDiv({ cls: "duckmage-rt-history" });
|
||||
this.renderHistory(historyEl);
|
||||
|
|
@ -1476,7 +1475,7 @@ export class RandomTableView extends ItemView {
|
|||
text: "+ New workflow with this table",
|
||||
cls: "duckmage-rt-entry-link",
|
||||
});
|
||||
newWfLink.style.fontStyle = "italic";
|
||||
newWfLink.setCssProps({ "font-style": "italic" });
|
||||
newWfLink.addEventListener("click", async () => {
|
||||
this.setViewMode("workflows");
|
||||
await this.createWorkflow(tableKey);
|
||||
|
|
@ -1508,31 +1507,31 @@ export class RandomTableView extends ItemView {
|
|||
);
|
||||
});
|
||||
|
||||
resultBox.style.display = "";
|
||||
resultBox.show();
|
||||
resultTextarea.value = displayLabel;
|
||||
resultTextarea.focus();
|
||||
|
||||
// Update "Open note" button
|
||||
if (openNoteBtn) {
|
||||
if (table.linkedFolder) {
|
||||
openNoteBtn.style.display = "";
|
||||
openNoteBtn.show();
|
||||
openNoteBtn.onclick = () => {
|
||||
const noteFile = this.app.vault.getAbstractFileByPath(
|
||||
`${table.linkedFolder}/${entry.result}.md`,
|
||||
);
|
||||
if (noteFile instanceof TFile)
|
||||
this.app.workspace.getLeaf().openFile(noteFile);
|
||||
void this.app.workspace.getLeaf().openFile(noteFile);
|
||||
};
|
||||
} else if (entry.isLink) {
|
||||
openNoteBtn.style.display = "";
|
||||
openNoteBtn.show();
|
||||
openNoteBtn.onclick = () => {
|
||||
const noteFile = this.app.vault.getAbstractFileByPath(entry.result + ".md")
|
||||
?? this.app.metadataCache.getFirstLinkpathDest(entry.result, "");
|
||||
if (noteFile instanceof TFile)
|
||||
this.app.workspace.getLeaf().openFile(noteFile);
|
||||
void this.app.workspace.getLeaf().openFile(noteFile);
|
||||
};
|
||||
} else {
|
||||
openNoteBtn.style.display = "none";
|
||||
openNoteBtn.hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1558,7 +1557,7 @@ export class RandomTableView extends ItemView {
|
|||
});
|
||||
copyIcon.title = "Copy";
|
||||
copyIcon.addEventListener("click", () =>
|
||||
navigator.clipboard.writeText(result),
|
||||
void navigator.clipboard.writeText(result),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ export class WorkflowEditorModal extends HexmakerModal {
|
|||
nameInput.value = this.file.basename;
|
||||
}
|
||||
};
|
||||
nameInput.addEventListener("blur", doRename);
|
||||
nameInput.addEventListener("blur", () => void doRename());
|
||||
nameInput.addEventListener("keydown", (e: KeyboardEvent) => {
|
||||
if (e.key === "Enter") { e.preventDefault(); nameInput.blur(); }
|
||||
if (e.key === "Escape") { nameInput.value = this.file.basename; nameInput.blur(); }
|
||||
|
|
@ -112,8 +112,7 @@ export class WorkflowEditorModal extends HexmakerModal {
|
|||
templateArea.addEventListener("input", () => { templateContent = templateArea.value; updateValidation(); });
|
||||
|
||||
const validationEl = templateSectionWrap.createDiv();
|
||||
validationEl.style.marginTop = "4px";
|
||||
validationEl.style.fontSize = "0.85em";
|
||||
validationEl.setCssProps({ "margin-top": "4px", "font-size": "0.85em" });
|
||||
|
||||
// ── Description ───────────────────────────────────────────────────
|
||||
const descRow = contentEl.createDiv({ cls: "duckmage-table-editor-desc-row" });
|
||||
|
|
@ -126,7 +125,7 @@ export class WorkflowEditorModal extends HexmakerModal {
|
|||
|
||||
// ── Results folder row ────────────────────────────────────────────
|
||||
const rfRow = contentEl.createDiv({ cls: "duckmage-table-editor-name-row" });
|
||||
rfRow.style.marginTop = "8px";
|
||||
rfRow.setCssProps({ "margin-top": "8px" });
|
||||
rfRow.createEl("label", { text: "Results folder", cls: "duckmage-table-editor-name-label" });
|
||||
|
||||
// Build a datalist of all folders under the world folder
|
||||
|
|
@ -320,7 +319,7 @@ export class WorkflowEditorModal extends HexmakerModal {
|
|||
const rollsInput = row.createEl("input", { type: "number" });
|
||||
rollsInput.min = "1";
|
||||
rollsInput.value = String(step.rolls);
|
||||
rollsInput.style.width = "52px";
|
||||
rollsInput.setCssProps({ width: "52px" });
|
||||
rollsInput.title = "Number of rolls";
|
||||
rollsInput.addEventListener("input", () => {
|
||||
const oldRolls = steps[i].rolls;
|
||||
|
|
@ -334,7 +333,7 @@ export class WorkflowEditorModal extends HexmakerModal {
|
|||
labelInput = row.createEl("input", { type: "text" });
|
||||
labelInput.placeholder = "Label…";
|
||||
labelInput.value = step.label ?? "";
|
||||
labelInput.style.flex = "1";
|
||||
labelInput.setCssProps({ flex: "1" });
|
||||
labelInput.addEventListener("input", () => {
|
||||
const fallback = step.kind === "dice"
|
||||
? (step.diceFormula ? `(${step.diceFormula})` : "")
|
||||
|
|
@ -447,12 +446,12 @@ export class WorkflowEditorModal extends HexmakerModal {
|
|||
addRollsInput = addRow.createEl("input", { type: "number" });
|
||||
addRollsInput.min = "1";
|
||||
addRollsInput.value = "1";
|
||||
addRollsInput.style.width = "52px";
|
||||
addRollsInput.setCssProps({ width: "52px" });
|
||||
addRollsInput.title = "Number of rolls";
|
||||
|
||||
addLabelInput = addRow.createEl("input", { type: "text" });
|
||||
addLabelInput.placeholder = "Label (auto)…";
|
||||
addLabelInput.style.flex = "1";
|
||||
addLabelInput.setCssProps({ flex: "1" });
|
||||
|
||||
const addBtnRow = addArea.createDiv({ cls: "duckmage-wf-editor-add-btn-row" });
|
||||
const addBtn = addBtnRow.createEl("button", { text: "Add step", cls: "mod-cta" });
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ export class WorkflowWizardModal extends HexmakerModal {
|
|||
|
||||
const copyResultBtn = header.createEl("button", { text: "Copy result" });
|
||||
copyResultBtn.addEventListener("click", () => {
|
||||
navigator.clipboard.writeText(this.resultTextarea?.value ?? "").then(() => {
|
||||
void navigator.clipboard.writeText(this.resultTextarea?.value ?? "").then(() => {
|
||||
copyResultBtn.setText("Copied!");
|
||||
setTimeout(() => copyResultBtn.setText("Copy result"), 1500);
|
||||
});
|
||||
|
|
@ -105,7 +105,7 @@ export class WorkflowWizardModal extends HexmakerModal {
|
|||
// ── Result textarea ───────────────────────────────────────────────
|
||||
contentEl.createEl("p", { text: "Result", cls: "duckmage-table-editor-heading" });
|
||||
this.resultTextarea = contentEl.createEl("textarea", { cls: "duckmage-wf-template-area" });
|
||||
this.resultTextarea.style.minHeight = "120px";
|
||||
this.resultTextarea.setCssProps({ "min-height": "120px" });
|
||||
this.resultTextarea.readOnly = true;
|
||||
this.resultTextarea.value = this.assembleResult();
|
||||
|
||||
|
|
@ -344,7 +344,7 @@ export class WorkflowWizardModal extends HexmakerModal {
|
|||
private async saveAsNote(): Promise<void> {
|
||||
const noteName = this.saveNoteNameInput.value.trim();
|
||||
if (!noteName) {
|
||||
this.saveStatusEl.style.color = "var(--color-red)";
|
||||
this.saveStatusEl.setCssProps({ color: "var(--color-red)" });
|
||||
this.saveStatusEl.setText("Note name is required.");
|
||||
return;
|
||||
}
|
||||
|
|
@ -370,7 +370,7 @@ export class WorkflowWizardModal extends HexmakerModal {
|
|||
this.close();
|
||||
await this.app.workspace.getLeaf("tab").openFile(savedFile);
|
||||
} catch (err) {
|
||||
this.saveStatusEl.style.color = "var(--color-red)";
|
||||
this.saveStatusEl.setCssProps({ color: "var(--color-red)" });
|
||||
this.saveStatusEl.setText(`Error: ${err}`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
24
src/utils.ts
24
src/utils.ts
|
|
@ -31,20 +31,22 @@ export function createIconEl(
|
|||
): HTMLElement {
|
||||
if (iconColor) {
|
||||
const div = parent.createEl("div", { cls, title: alt });
|
||||
div.style.maskImage = `url('${src}')`;
|
||||
div.style.setProperty("-webkit-mask-image", `url('${src}')`);
|
||||
div.style.maskSize = "contain";
|
||||
div.style.setProperty("-webkit-mask-size", "contain");
|
||||
div.style.maskRepeat = "no-repeat";
|
||||
div.style.setProperty("-webkit-mask-repeat", "no-repeat");
|
||||
div.style.maskPosition = "center";
|
||||
div.style.setProperty("-webkit-mask-position", "center");
|
||||
div.style.backgroundColor = iconColor;
|
||||
div.setCssProps({
|
||||
'mask-image': `url("${src}")`,
|
||||
'-webkit-mask-image': `url("${src}")`,
|
||||
'mask-size': 'contain',
|
||||
'-webkit-mask-size': 'contain',
|
||||
'mask-repeat': 'no-repeat',
|
||||
'-webkit-mask-repeat': 'no-repeat',
|
||||
'mask-position': 'center',
|
||||
'-webkit-mask-position': 'center',
|
||||
'background-color': iconColor,
|
||||
});
|
||||
return div;
|
||||
}
|
||||
const img = parent.createEl("img", { cls });
|
||||
(img as HTMLImageElement).src = src;
|
||||
(img as HTMLImageElement).alt = alt;
|
||||
img.src = src;
|
||||
img.alt = alt;
|
||||
return img;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,15 @@
|
|||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* SVG overlay for paths, coordinate labels and elevated icons */
|
||||
.duckmage-path-svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
/* ── Pointy-top row layout ────────────────────────────────────────────────── */
|
||||
|
||||
.duckmage-hex-row {
|
||||
|
|
|
|||
Loading…
Reference in a new issue