diff --git a/.claude/commands/lint.md b/.claude/commands/lint.md index 8a64925..5a0fdcd 100644 --- a/.claude/commands/lint.md +++ b/.claude/commands/lint.md @@ -35,6 +35,15 @@ this.registerEvent( ``` This removes `any` entirely; `unknown as SomeInterface` is fully type-safe. +### `@typescript-eslint/no-unnecessary-type-assertion` +A cast (`as T`) where TypeScript already knows the value has type `T`. + +**Common cause — `createEl` with a specific tag**: Obsidian's `createEl(tag: K, ...)` returns `HTMLElementTagNameMap[K]`, so `createEl("input", ...)` already returns `HTMLInputElement`. Any subsequent `as HTMLInputElement` cast is redundant. + +**Common cause — ambient types not imported**: Using `as DomElementInfo` inline causes ESLint's `no-undef` to fire because the ambient type isn't an explicit import. Drop the cast entirely — `{ type: "checkbox" }` is assignable to `DomElementInfo` without a cast. + +**Fix**: Delete the redundant cast. If the type isn't narrow enough, use `as unknown as T` (two-step) rather than a direct `as T` — but prefer removing the cast entirely. + ### `obsidianmd/ui/sentence-case` UI text must use sentence case: only the first word and proper nouns are capitalised. @@ -43,6 +52,7 @@ UI text must use sentence case: only the first word and proper nouns are capital - `"← Pick icons"` → `"← pick icons"` - `"↩ Map mode"` → `"↩ map mode"` - Parenthetical range notation: `"(A → Z)"` → `"(a → z)"` +- Mid-sentence acronyms (e.g. "Remove GM icon"): the rule flags consecutive capitals that are not the first word. Reword to move the acronym to the front, avoid it, or use the full term in lowercase: `"Remove icon"` or `"GM icon: remove"`. **Fix B — eslint-disable** for intentional special-case UI text that would look wrong if sentence-cased: - Short abbreviation labels: `"wt ↑"`, `"a→z"`, `"z→a"` — these are symbols/abbreviations, not sentences diff --git a/.claude/commands/rebuild.md b/.claude/commands/rebuild.md index 4ac748b..3dd3d03 100644 --- a/.claude/commands/rebuild.md +++ b/.claude/commands/rebuild.md @@ -1,5 +1,5 @@ --- -description: Build the duckmage plugin (type-check + bundle) +description: Build the duckmage plugin (type-check + bundle + lint + tests) allowed-tools: Bash(npm:*) --- @@ -15,7 +15,17 @@ Report the build result clearly: - If it succeeded, confirm that `main.js` was updated. - If it failed, show the full error output and identify the likely cause (TypeScript type error, missing import, syntax error, etc.). -Then, regardless of build result, run the test suite: +Then run the linter: + +``` +cd /mnt/c/Users/markr/Documents/KB/journal/.obsidian/plugins/duckmage-plugin && npm run lint 2>&1 +``` + +Report the lint result clearly: +- If there are no issues, confirm it is lint-clean. +- If there are errors or warnings, list each one with file, line, rule name, and a brief description. Errors from the Obsidian reviewer are **required** fixes; address them before marking the task done. + +Then, regardless of build/lint result, run the test suite: ``` cd /mnt/c/Users/markr/Documents/KB/journal/.obsidian/plugins/duckmage-plugin && npm test diff --git a/manifest.json b/manifest.json index 5162ab2..7bdbee4 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "hexmaker", "name": "Hexmap World Creator", - "version": "1.0.27", + "version": "1.0.28", "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", diff --git a/package-lock.json b/package-lock.json index a9898ce..48ffd88 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "hexmaker-plugin", - "version": "1.0.27", + "version": "1.0.28", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hexmaker-plugin", - "version": "1.0.27", + "version": "1.0.28", "license": "MIT", "dependencies": { "minisearch": "^7.2.0" diff --git a/package.json b/package.json index 6bcd913..2087797 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hexmaker-plugin", - "version": "1.0.27", + "version": "1.0.28", "description": "A plugin to power hex crawl worldbuilding and running.", "main": "main.js", "scripts": { diff --git a/src/HexmakerPlugin.ts b/src/HexmakerPlugin.ts index e060971..ea01920 100644 --- a/src/HexmakerPlugin.ts +++ b/src/HexmakerPlugin.ts @@ -736,7 +736,7 @@ export default class HexmakerPlugin extends Plugin { let linked = 0; for (const file of hexFiles) { const cache = this.app.metadataCache.getFileCache(file); - const region = cache?.frontmatter?.["region"]; + const region: unknown = cache?.frontmatter?.["region"]; if (typeof region !== "string" || !region) continue; await this.syncHexRegionTableLink(file.path, region); linked++; diff --git a/src/hex-map/HexEditorModal.ts b/src/hex-map/HexEditorModal.ts index 11ffc6a..367bec7 100644 --- a/src/hex-map/HexEditorModal.ts +++ b/src/hex-map/HexEditorModal.ts @@ -512,7 +512,7 @@ export class HexEditorModal extends HexmakerModal { const clearGmBtn = gmRow.createEl("button", { text: "Clear", cls: "duckmage-clear-btn", - title: "Remove GM icon", + title: "Remove icon", }); clearGmBtn.setCssProps({ visibility: currentGmIcon ? "visible" : "hidden", diff --git a/src/hex-map/HexMapView.ts b/src/hex-map/HexMapView.ts index de12f21..3f1a332 100644 --- a/src/hex-map/HexMapView.ts +++ b/src/hex-map/HexMapView.ts @@ -2777,7 +2777,7 @@ export class HexMapView extends ItemView { const file = this.app.vault.getAbstractFileByPath(hexFilePath); if (!(file instanceof TFile)) return null; const cache = this.app.metadataCache.getFileCache(file); - const region = cache?.frontmatter?.["region"]; + const region: unknown = cache?.frontmatter?.["region"]; return typeof region === "string" ? region : null; } diff --git a/src/hex-map/IconPickerModal.ts b/src/hex-map/IconPickerModal.ts index f1a7c50..c5754ab 100644 --- a/src/hex-map/IconPickerModal.ts +++ b/src/hex-map/IconPickerModal.ts @@ -39,15 +39,15 @@ export class IconPickerModal extends HexmakerModal { // GM layer only toggle const gmRow = headerRight.createDiv({ cls: "duckmage-icon-gm-toggle-row" }); - const gmCb = gmRow.createEl("input", { type: "checkbox" } as DomElementInfo); - (gmCb as HTMLInputElement).checked = this.gmOnly; + const gmCb = gmRow.createEl("input", { type: "checkbox" }); + gmCb.checked = this.gmOnly; const gmLabel = gmRow.createSpan({ text: "GM layer only", cls: "duckmage-icon-gm-toggle-label" }); const applyGm = () => { - this.gmOnly = (gmCb as HTMLInputElement).checked; + this.gmOnly = gmCb.checked; }; gmCb.addEventListener("change", applyGm); gmLabel.addEventListener("click", () => { - (gmCb as HTMLInputElement).checked = !(gmCb as HTMLInputElement).checked; + gmCb.checked = !gmCb.checked; applyGm(); }); diff --git a/versions.json b/versions.json index 3120d0c..94e34df 100644 --- a/versions.json +++ b/versions.json @@ -16,5 +16,6 @@ "1.0.24": "1.12.0", "1.0.25": "1.12.0", "1.0.26": "1.12.0", - "1.0.27": "1.12.0" + "1.0.27": "1.12.0", + "1.0.28": "1.12.0" } \ No newline at end of file