diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..e8a2566 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module", + "project": "./tsconfig.json" + }, + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "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-explicit-any": "error", + "@typescript-eslint/no-non-null-assertion": "warn", + "no-console": ["warn", { "allow": ["warn", "error"] }] + }, + "env": { + "node": true, + "es6": true + }, + "ignorePatterns": ["dist", "node_modules", "*.js"] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7e71adf --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# Dependencies +node_modules/ +package-lock.json +yarn.lock + +# Build outputs +dist/ +*.tsbuildinfo + +# Test coverage +coverage/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Environment +.env +.env.local +.env.*.local + +# Temporary files +*.tmp +.cache/ + +# Backup files +*.backup diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..7717b0c --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +node_modules +dist +coverage +*.log +.git diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..32a2397 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,10 @@ +{ + "semi": true, + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 100, + "tabWidth": 2, + "useTabs": false, + "arrowParens": "always", + "endOfLine": "lf" +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..c9a7e90 --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# ByteGrid + +> Binary data and C struct memory layout visualization for Obsidian + +## Overview + +**ByteGrid** is an Obsidian plugin that visualizes binary data and C structure memory layouts as interactive SVG diagrams. + +## Features + +- 🎨 Color-coded field visualization +- 📏 Byte-accurate grid layout +- 🔍 Bitfield support +- 📊 Hex dump integration +- 🎯 Interactive tooltips +- 📤 Multiple export formats (SVG, PNG, C code, Markdown) + +## Project Status + +🚧 **In Development** - Currently in Phase 1 (MVP) + +## Quick Start + +```bash +# Install dependencies +npm install + +# Build all packages +npm run build + +# Run tests +npm run test + +# Lint +npm run lint + +# Format code +npm run format +``` + +## Project Structure + +``` +bytegrid/ +├── packages/ +│ ├── core/ # Core rendering logic +│ └── obsidian-plugin/ # Obsidian plugin +├── docs/ # Design documentation +└── examples/ # Example files +``` + +## Documentation + +See [CLAUDE.md](./CLAUDE.md) for detailed project guidelines and architecture. + +## Development + +This project follows **Test-Driven Development (TDD)**: + +1. Write tests first +2. Implement minimal code to pass tests +3. Refactor + +### Key Principles + +- **offset is SSOT** - Field size calculated from offset ranges +- **No magic numbers** - Use constants for all layout values +- **Dynamic height** - SVG dimensions calculated from content +- **Explicit padding** - Use `reserved` or `padding` types + +## License + +MIT License - see [LICENSE](./LICENSE) for details + +## Contributing + +Currently in initial development phase. Contribution guidelines will be added soon. diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..428264c --- /dev/null +++ b/jest.config.js @@ -0,0 +1,22 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + roots: ['/packages'], + testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'], + collectCoverageFrom: [ + 'packages/*/src/**/*.ts', + '!packages/*/src/**/*.d.ts', + '!packages/*/src/index.ts', + ], + coverageThreshold: { + global: { + branches: 80, + functions: 80, + lines: 80, + statements: 80, + }, + }, + moduleNameMapper: { + '^@bytegrid/core$': '/packages/core/src', + }, +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..f0475f7 --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "name": "bytegrid", + "version": "0.1.0", + "description": "Binary data and C struct memory layout visualization for Obsidian", + "private": true, + "workspaces": [ + "packages/*" + ], + "scripts": { + "build": "npm run build --workspaces", + "test": "npm run test --workspaces", + "lint": "eslint . --ext .ts", + "format": "prettier --write \"**/*.{ts,json,md}\"", + "format:check": "prettier --check \"**/*.{ts,json,md}\"", + "clean": "rm -rf packages/*/dist packages/*/node_modules node_modules" + }, + "keywords": [ + "obsidian", + "obsidian-plugin", + "binary", + "visualization", + "memory-layout", + "struct" + ], + "author": "ByteGrid Contributors", + "license": "MIT", + "devDependencies": { + "@types/jest": "^29.5.11", + "@types/node": "^20.10.6", + "@typescript-eslint/eslint-plugin": "^6.17.0", + "@typescript-eslint/parser": "^6.17.0", + "eslint": "^8.56.0", + "jest": "^29.7.0", + "prettier": "^3.1.1", + "ts-jest": "^29.1.1", + "typescript": "^5.3.3" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=9.0.0" + } +} diff --git a/packages/core/jest.config.js b/packages/core/jest.config.js new file mode 100644 index 0000000..e5330c3 --- /dev/null +++ b/packages/core/jest.config.js @@ -0,0 +1,7 @@ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + roots: ['/tests'], + testMatch: ['**/?(*.)+(spec|test).ts'], + collectCoverageFrom: ['src/**/*.ts', '!src/**/*.d.ts', '!src/index.ts'], +}; diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000..c22cc38 --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,26 @@ +{ + "name": "@bytegrid/core", + "version": "0.1.0", + "description": "Core rendering logic for ByteGrid", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "build": "tsc", + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage" + }, + "keywords": [ + "bytegrid", + "binary", + "visualization" + ], + "author": "ByteGrid Contributors", + "license": "MIT", + "dependencies": { + "js-yaml": "^4.1.0" + }, + "devDependencies": { + "@types/js-yaml": "^4.0.9" + } +} diff --git a/packages/core/src/errors.ts b/packages/core/src/errors.ts new file mode 100644 index 0000000..04b1ac5 --- /dev/null +++ b/packages/core/src/errors.ts @@ -0,0 +1,58 @@ +/** + * Custom error classes for ByteGrid + */ + +/** + * Base error class for ByteGrid + */ +export class ByteGridError extends Error { + constructor(message: string) { + super(message); + this.name = 'ByteGridError'; + Object.setPrototypeOf(this, ByteGridError.prototype); + } +} + +/** + * Error thrown during YAML parsing + */ +export class ParseError extends ByteGridError { + constructor(message: string, public readonly fieldIndex?: number) { + super(message); + this.name = 'ParseError'; + Object.setPrototypeOf(this, ParseError.prototype); + } +} + +/** + * Error thrown during validation + */ +export class ValidationError extends ByteGridError { + constructor(message: string, public readonly fieldIndex?: number) { + super(message); + this.name = 'ValidationError'; + Object.setPrototypeOf(this, ValidationError.prototype); + } +} + +/** + * Error thrown during rendering + */ +export class RenderError extends ByteGridError { + constructor(message: string) { + super(message); + this.name = 'RenderError'; + Object.setPrototypeOf(this, RenderError.prototype); + } +} + +/** + * Error thrown during binary parsing + */ +export class BinaryParseError extends ByteGridError { + constructor(message: string, public readonly offset?: number) { + super(message); + this.name = 'BinaryParseError'; + Object.setPrototypeOf(this, BinaryParseError.prototype); + } +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 0000000..6672a89 --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1,12 @@ +/** + * ByteGrid Core - Public API + */ + +export * from './types'; +export * from './errors'; + +// TODO: Export parser, validator, layoutEngine, svgRenderer when implemented +// export { parse } from './parser'; +// export { validate } from './validator'; +// export { createLayout } from './layoutEngine'; +// export { renderSVG } from './svgRenderer'; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts new file mode 100644 index 0000000..9935393 --- /dev/null +++ b/packages/core/src/types.ts @@ -0,0 +1,123 @@ +/** + * Core type definitions for ByteGrid + */ + +/** + * Supported data types in ByteGrid + */ +export type DataType = + | 'char' + | 'int8_t' + | 'uint8_t' + | 'int16_t' + | 'uint16_t' + | 'short' + | 'int32_t' + | 'uint32_t' + | 'int' + | 'int64_t' + | 'uint64_t' + | 'long' + | 'float' + | 'double' + | 'reserved' + | 'padding' + | string; // For array types like 'char[4]' + +/** + * Endianness for multi-byte fields + */ +export type Endianness = 'little' | 'big'; + +/** + * Color names for field visualization + */ +export type ColorName = + | 'blue' + | 'cyan' + | 'yellow' + | 'green' + | 'orange' + | 'purple' + | 'mint' + | 'pink' + | 'gray'; + +/** + * Color scheme variants + */ +export type ColorScheme = 'default' | 'dark' | 'light'; + +/** + * Bitfield definition within a field + */ +export interface Bitfield { + name: string; + bits: string; // e.g., "0-3" or "7" + description?: string; +} + +/** + * Field definition + * Note: size is calculated from offset (SSOT principle) + */ +export interface Field { + offset: string; // e.g., "0-3" or "4" (SSOT for size calculation) + name: string; + type: DataType; + value?: string; + description?: string; + color?: ColorName; + endianness?: Endianness; + bitfields?: Bitfield[]; +} + +/** + * Main configuration for ByteGrid visualization + */ +export interface ByteGridConfig { + name: string; + size: number; // Total size in bytes + layout?: number; // Bytes per row (default: 16) + colorScheme?: ColorScheme; + fields: Field[]; +} + +/** + * Parsed offset information + */ +export interface OffsetRange { + start: number; + end: number; + size: number; +} + +/** + * Layout block for rendering (after layout engine processing) + */ +export interface LayoutBlock { + row: number; + col: number; + span: number; // Number of columns this block spans + fieldName: string; + fieldType: DataType; + color: ColorName; + value?: string; + description?: string; + offsetStart: number; + offsetEnd: number; + isPadding: boolean; + bitfields?: Bitfield[]; +} + +/** + * Rendering options + */ +export interface RenderOptions { + showHexDump?: boolean; + showLegend?: boolean; + showGrid?: boolean; + cellWidth?: number; + cellHeight?: number; + fontSize?: number; +} diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000..2a28f2b --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "tests"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b5673d2 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": ["ES2020"], + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist", + "rootDir": "./", + "composite": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "moduleResolution": "node", + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "exclude": ["node_modules", "dist", "**/*.test.ts"] +}