diff --git a/.eslintrc.json b/.eslintrc.json index e8a2566..f8a76ac 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,7 +4,7 @@ "parserOptions": { "ecmaVersion": 2020, "sourceType": "module", - "project": "./tsconfig.json" + "project": "./tsconfig.eslint.json" }, "plugins": ["@typescript-eslint"], "extends": [ @@ -13,19 +13,15 @@ "plugin:@typescript-eslint/recommended-requiring-type-checking" ], "rules": { - "@typescript-eslint/no-unused-vars": [ - "error", - { "argsIgnorePattern": "^_" } - ], - "@typescript-eslint/explicit-function-return-type": [ - "warn", - { "allowExpressions": true } - ], + "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], + "@typescript-eslint/explicit-function-return-type": ["warn", { "allowExpressions": true }], "@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/no-non-null-assertion": "warn", "no-console": ["warn", { "allow": ["warn", "error"] }] }, "env": { + "browser": true, + "jest": true, "node": true, "es6": true }, diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..95ef18e --- /dev/null +++ b/TODO.md @@ -0,0 +1,104 @@ +# TODO + +## P0 - Repository Health + +- [x] Fix root `npm test`. + - Current state: core tests pass, but root workspace test fails because `@bytegrid/obsidian-plugin` has no `test` script. + - Options: + - Add a minimal `test` script to `packages/obsidian-plugin/package.json`. + - Or change the root test script to run only workspaces that actually have tests. + +- [x] Split plugin build from Obsidian vault deployment. + - Current state: `npm run build` creates `packages/obsidian-plugin/main.js`, then tries to copy files to `OBSIDIAN_PLUGIN_DIR` from `.env`. + - Problem: normal builds are tied to a personal local vault path and can fail in sandbox/CI. + - Suggested shape: + - `build`: bundle only. + - `deploy:obsidian`: copy `main.js`, `manifest.json`, and `styles.css` to the configured vault. + +- [x] Fix `npm run lint`. + - Current state: ESLint checks test files, but root `tsconfig.json` excludes `**/*.test.ts`, causing parser project errors. + - Also fix unsafe type errors in `packages/obsidian-plugin/src/main.ts`. + - Options: + - Add `tsconfig.eslint.json` including `src` and `tests`. + - Or configure ESLint overrides for test files and Obsidian DOM helper types. + +- [x] Run Prettier and commit formatting intentionally. + - Current state: `npm run format:check` reports formatting issues in core source, tests, README, roadmap, AGENTS, and screenshot guide. + - Do this as a separate formatting-only change to keep diffs readable. + +## P1 - Correctness + +- [ ] Make numeric parsing strict. + - Current state: `parseInt` accepts partial values such as `12abc`. + - Affected areas: + - `packages/core/src/parser.ts` + - `packages/core/src/validator.ts` + - Use explicit regex validation for size, layout, offsets, and bit ranges before converting to numbers. + +- [ ] Validate config enum values at parse or validation time. + - Check invalid `layoutUnit`, `colorScheme`, `legendPosition`, `endianness`, and field `color`. + - Current parser mostly casts strings without rejecting unsupported values. + +- [ ] Add bitfield overlap validation. + - Current validator checks bitfield range bounds but does not appear to reject overlapping bitfield definitions inside the same field. + +- [ ] Review array type size consistency. + - Current validator accepts array types like `uint32_t[4]`, but field size still comes only from `offset`. + - Decide whether to warn/reject mismatches between declared type size and offset-derived size. + +## P2 - Feature Roadmap + +- [ ] Implement `showHexDump`. + - Already exposed in render options, but not rendered yet. + +- [ ] Add `bitOrder: msb | lsb`. + - Current bitfield rendering assumes MSB-first display. + +- [ ] Add field tooltips/highlight behavior. + - Roadmap lists interactive tooltips and highlighting, but current SVG is static. + +- [ ] Add value display formatting. + - Support hex, decimal, binary, and maybe ASCII display where appropriate. + +- [ ] Add endianness indicators. + - `endianness` is accepted in config but not visibly represented in the SVG. + +## P3 - Missing Planned Modules + +- [ ] Add `binaryParser.ts`. + - Read binary data, extract field values, handle endianness, and generate hex dump data. + +- [ ] Add `templates.ts`. + - Provide predefined structures such as WAV, ELF, TCP, and PNG headers. + +- [ ] Add `comparison.ts`. + - Support side-by-side, overlay, and diff views. + +- [ ] Add `exporter.ts`. + - Export SVG, PNG, C code, and Markdown. + +- [ ] Add `cache.ts`. + - Hash-based render cache with TTL and future persistence path. + +## Documentation + +- [ ] Reconcile README, roadmap, and AGENTS instructions with actual repository state. + - Current docs mention modules and docs files that do not exist yet. + +- [ ] Add a user-facing `docs/` structure when the feature set stabilizes. + - Suggested first files: + - `docs/getting-started.md` + - `docs/syntax-reference.md` + - `docs/type-reference.md` + - `docs/troubleshooting.md` + +## Current Baseline + +- Core tests: passing, 141 tests. +- Core coverage command: + - `npm run test --workspace=@bytegrid/core -- --coverage --runInBand` +- Passing repo-level commands: + - `npm test` + - `npm run build` + - `npm run lint` + - `npm run format:check` diff --git a/manifest.json b/manifest.json index 1129c25..f74431d 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "bytegrid", "name": "ByteGrid", - "version": "1.0.0", + "version": "1.0.1", "minAppVersion": "1.0.0", "description": "Visualize binary data and C struct memory layouts.", "author": "waaraawa", diff --git a/package.json b/package.json index 58a1f2b..a409885 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bytegrid", - "version": "1.0.0", + "version": "1.0.1", "description": "Binary data and C struct memory layout visualization for Obsidian", "private": true, "workspaces": [ @@ -8,7 +8,7 @@ ], "scripts": { "build": "npm run build --workspaces", - "test": "npm run test --workspaces", + "test": "npm run test --workspaces --if-present", "lint": "eslint . --ext .ts", "format": "prettier --write \"**/*.{ts,json,md}\"", "format:check": "prettier --check \"**/*.{ts,json,md}\"", diff --git a/packages/core/package.json b/packages/core/package.json index 65e73dc..3f1a7cb 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@bytegrid/core", - "version": "1.0.0", + "version": "1.0.1", "description": "Core rendering logic for ByteGrid", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/obsidian-plugin/esbuild.config.mjs b/packages/obsidian-plugin/esbuild.config.mjs index d5cef13..09948ff 100644 --- a/packages/obsidian-plugin/esbuild.config.mjs +++ b/packages/obsidian-plugin/esbuild.config.mjs @@ -2,6 +2,7 @@ import esbuild from 'esbuild'; import process from 'process'; import builtins from 'builtin-modules'; import fs from 'fs'; +import os from 'os'; import path from 'path'; const banner = `/* @@ -11,7 +12,20 @@ if you want to view the source, please visit the github repository of this plugi `; const prod = process.argv[2] === 'production'; -const watch = process.argv[2] === '--watch'; +const watch = process.argv.includes('--watch'); +const deploy = process.argv.includes('--deploy'); + +function expandHomePath(value) { + if (value === '~') { + return os.homedir(); + } + + if (value.startsWith('~/')) { + return path.join(os.homedir(), value.slice(2)); + } + + return value; +} // Obsidian vault plugin directory let OBSIDIAN_PLUGIN_DIR = process.env.OBSIDIAN_PLUGIN_DIR; @@ -25,6 +39,10 @@ try { // Ignore if .env doesn't exist } +if (OBSIDIAN_PLUGIN_DIR) { + OBSIDIAN_PLUGIN_DIR = expandHomePath(OBSIDIAN_PLUGIN_DIR); +} + // Plugin to copy files to Obsidian after build const copyPlugin = { name: 'copy-to-obsidian', @@ -92,7 +110,7 @@ const context = await esbuild.context({ treeShaking: true, outfile: 'main.js', minify: prod, - plugins: [copyPlugin], + plugins: deploy ? [copyPlugin] : [], }); if (watch) { diff --git a/packages/obsidian-plugin/package.json b/packages/obsidian-plugin/package.json index ebf1e72..06411c1 100644 --- a/packages/obsidian-plugin/package.json +++ b/packages/obsidian-plugin/package.json @@ -1,11 +1,12 @@ { "name": "@bytegrid/obsidian-plugin", - "version": "1.0.0", + "version": "1.0.1", "description": "ByteGrid Obsidian plugin", "main": "main.js", "scripts": { "build": "node esbuild.config.mjs", - "dev": "node esbuild.config.mjs --watch" + "dev": "node esbuild.config.mjs --watch --deploy", + "deploy:obsidian": "node esbuild.config.mjs --deploy" }, "keywords": [ "obsidian", @@ -14,7 +15,7 @@ "author": "ByteGrid Contributors", "license": "MIT", "dependencies": { - "@bytegrid/core": "^1.0.0" + "@bytegrid/core": "^1.0.1" }, "devDependencies": { "@types/node": "^20.10.6", diff --git a/packages/obsidian-plugin/src/main.ts b/packages/obsidian-plugin/src/main.ts index 67014cc..9794b22 100644 --- a/packages/obsidian-plugin/src/main.ts +++ b/packages/obsidian-plugin/src/main.ts @@ -3,10 +3,10 @@ import { parse, validate, createLayout, renderSVG } from '@bytegrid/core'; export default class ByteGridPlugin extends Plugin { onload(): void { - this.registerMarkdownCodeBlockProcessor('bytegrid', (source, el, ctx) => { + this.registerMarkdownCodeBlockProcessor('bytegrid', (source, el, _ctx) => { try { // Parse YAML input - const parsed = parseYaml(source); + const parsed = parseYaml(source) as unknown; const config = parse(parsed); // Validate configuration @@ -25,7 +25,7 @@ export default class ByteGridPlugin extends Plugin { border: 'var(--background-modifier-border)', gridLine: 'var(--background-modifier-border)', gridLineSubtle: 'var(--background-modifier-border-hover)', - // For block cell text, using var(--text-normal) might lack contrast against colored blocks. + // For block cell text, using var(--text-normal) might lack contrast against colored blocks. // We use a high-contrast fallback variable or default to white/black via CSS if available. // Obsidian's callouts use --callout-title-color which might be good, but for now we rely on standard text-muted. // Let's omit cellText overrides to fallback to textNormal/textMuted, @@ -39,13 +39,13 @@ export default class ByteGridPlugin extends Plugin { mint: 'var(--color-green)', // No mint in standard Obsidian, fallback to green pink: 'var(--color-pink)', gray: 'var(--color-base-40)', - } - } + }, + }, }); // Display SVG safely using DOMParser - const parser = new DOMParser(); - const svgDoc = parser.parseFromString(svg, 'image/svg+xml'); + const domParser = new DOMParser(); + const svgDoc = domParser.parseFromString(svg, 'image/svg+xml'); const svgElement = svgDoc.documentElement; if (svgElement.tagName === 'svg') { @@ -55,10 +55,20 @@ export default class ByteGridPlugin extends Plugin { } } catch (error) { // Display error - const container = el.createDiv({ cls: 'bytegrid-error' }); - container.createEl('h4', { text: 'Bytegrid error' }); - container.createEl('p', { text: error instanceof Error ? error.message : String(error) }); - container.createEl('pre', { text: source }); + const container = document.createElement('div'); + container.className = 'bytegrid-error'; + + const title = document.createElement('h4'); + title.textContent = 'Bytegrid error'; + + const message = document.createElement('p'); + message.textContent = error instanceof Error ? error.message : String(error); + + const input = document.createElement('pre'); + input.textContent = source; + + container.append(title, message, input); + el.appendChild(container); } }); } diff --git a/packages/obsidian-plugin/versions.json b/packages/obsidian-plugin/versions.json index 608a97a..85df038 100644 --- a/packages/obsidian-plugin/versions.json +++ b/packages/obsidian-plugin/versions.json @@ -1,3 +1,4 @@ { - "1.0.0": "1.0.0" + "1.0.0": "1.0.0", + "1.0.1": "1.0.0" } diff --git a/tsconfig.eslint.json b/tsconfig.eslint.json new file mode 100644 index 0000000..912bc6e --- /dev/null +++ b/tsconfig.eslint.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "lib": ["ES2020", "DOM"], + "noEmit": true, + "types": ["jest", "node"] + }, + "include": ["packages/**/*.ts"], + "exclude": ["node_modules", "dist"] +}