draftX

DraftX — CAD Widget Scaffold (v0.1)

A clean, kernel-first TypeScript scaffold for a two-canvas CAD/diagram widget. DraftX separates world-space (content/grid) from device-space (rulers/overlays), centralizes viewport math, and enforces canvas sizing invariants before every frame. It’s small, strict, and designed to grow without drifting into spaghetti.


✨ What’s in this scaffold


🚀 Quick start

# Install
npm i

# Run Dev UI at /dev/
npm run dev

# Type check & lint
npm run typecheck
npm run lint

Open http://localhost:5173/dev/ — click New to reset the view and verify rulers/grid.

Note: Aliases (@app, @core, @kernel, @render, @util) are configured in tsconfig and vite.config.ts.


🧱 Public API

import { createWidget } from 'draftx'; // in dev we import from src/index

const widget = createWidget({ gutterCSS: 32 });

widget.mount(hostElement);
widget.newDocument(); // resets zoom=1 and positions world(0,0) at (gutter,gutter) in CSS space

// More coming soon:
// widget.fit();
// widget.importSvg(svgText);
// widget.setGridVisible(true/false);
// widget.on('view:changed', payload => ...);

Options


🗂️ Project layout

src/
  app/
    runtime.ts           # composition root & public widget surface
  core/
    bus.ts               # typed event bus
    state.ts             # App/View/UI state types & initial values
    commands/
      view.ts            # thin view commands that call kernel + applyView
  kernel/
    viewport.ts          # pure viewport math & world transform helper
    commit.ts            # applyView (atomic state change + events)
    scheduler.ts         # frame loop with canvas size invariants
  render/
    pipeline.ts          # frame orchestration: base→grid→content, then overlay
    layers/
      grid.ts            # world-space grid
      rulers.ts          # device-space rulers
  io/
    // svg.ts (planned)
  util/
    // dom & helpers (planned)

dev/
  index.html             # simple side panel + host
  app.ts                 # mounts the widget

test/
  smoke/
    smoke_runner.js      # placeholder (will add rulers/grid/fit checks)

🧭 Architecture (at a glance)

flowchart LR
  A[Dev UI] -->|calls| B[Widget API];
  B --> C[Runtime];
  C --> D[FrameScheduler];
  D --> E[Render Pipeline];
  E --> F[Base Canvas - world];
  E --> G[Overlay Canvas - device];
  C --> H[Event Bus];
  C --> I[Commands];
  I --> J[Kernel applyView];
  J --> H;

Event ordering

sequenceDiagram
  participant CMD as Command
  participant K as applyView
  participant BUS as EventBus
  participant S as Scheduler
  participant R as Renderer

  CMD->>K: nextCenter, nextZoom
  K->>BUS: view:changed
  K->>S: requestFrame
  K->>BUS: postframe:view
  S->>R: renderFrame(base → overlay)

Render pipeline

flowchart LR
  RS[Frame Start] --> SZ[Ensure canvas sizes];
  SZ -->|resize| B0[Draw base only and requeue];
  SZ -->|ok| B1[Clear base];
  B1 --> WT[Apply world transform];
  WT --> G[Draw grid];
  G --> C[Draw content - future];
  C --> O0[Clear overlay];
  O0 --> OR[Draw rulers];
  OR --> OS[Draw selection - future];

🧠 Design principles


🧩 Key modules

Kernel

Commands

Render layers


🧪 Testing roadmap


🧰 Scripts

{
  "dev": "vite",
  "build": "tsc -p tsconfig.json",
  "preview": "vite preview",
  "lint": "eslint .",
  "typecheck": "tsc -p tsconfig.json --noEmit",
  "test:smoke": "node test/smoke/smoke_runner.js"
}

🐛 Common gotchas


📝 Roadmap (near term)

  1. Dev UI cards (View/Grid/Rulers) wired to public API
  2. Smoke suites for rulers+grid+fit
  3. Selection overlay + pointer tools (pan/zoom, ruler-pan)
  4. SVG import ({ nodes, bounds }) + auto fit(bounds, size, gutter)
  5. Content layer primitives

📄 License

MIT (add your preferred license file)