Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 8 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,21 @@ name: CI

on:
push:
branches: [ main, feat/simulation-system ]
branches: ["**"]
pull_request:
branches: ["**"]

jobs:
test:
build-and-test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Lint
run: npm run lint
- name: Build
run: npm run build
- name: Docs (TypeDoc)
run: npm run docs:api
- run: npm run lint
- run: npm test -- --run
59 changes: 59 additions & 0 deletions .github/workflows/pr-policy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: PR Policy

on:
pull_request:
types: [opened, edited, synchronize]

jobs:
policy:
runs-on: ubuntu-latest
steps:
- name: Check milestone and tests present
uses: actions/github-script@v7
with:
script: |
const pr = context.payload.pull_request
if (!pr) {
core.setFailed('No PR context')
return
}
// Require milestone
if (!pr.milestone) {
core.setFailed('PR must have a milestone assigned')
return
}
// Require at least one test file changed in the PR (tests-first discipline)
const files = await github.paginate(github.rest.pulls.listFiles, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
per_page: 100,
})
// Escape hatch via label and audit line in body
const labels = (pr.labels || []).map(l => typeof l === 'string' ? l : l.name)
const skip = labels.includes('skip-test-check')
if (skip && /skip-test-check:/i.test(pr.body || '')) {
core.notice('tests-first check skipped via label + audit line')
return
}
// Allowlist infra/docs only changes
const infraOnly = files.every(f => (
f.filename.startsWith('.github/') ||
/\.(ya?ml|md|txt)$/.test(f.filename) ||
/(^|\/)Dockerfile$/.test(f.filename) ||
/(^|\/)package-lock\.json$/.test(f.filename)
))
if (infraOnly) {
core.notice('infra/docs-only change: tests change not required')
return
}
const hasTest = files.some(f => /__tests__|\.test\.[tj]sx?$/.test(f.filename))
if (!hasTest) {
core.setFailed('PR must include changes to tests (tests-first).')
return
}
// Optional: Encourage acceptance criteria section
const body = pr.body || ''
if (!/Acceptance Criteria/i.test(body)) {
core.notice('Consider adding an "Acceptance Criteria" section to the PR description.')
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ dist-ssr
*.njsproj
*.sln
*.sw?
feedback.md
2 changes: 2 additions & 0 deletions BLOG_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# On Document-Based Rendering Engines—Why Web Design Feels So Clunky And How To Fix It

{"date": "2025-11-03", "time": "02:42", "summary": "Add PhysicsRegistry + events + SAT + perf overlay; wire registry into controller; tests-first.", "topics": [{"topic": "PhysicsRegistry", "what": "Implemented DOM discovery of physics-enabled nodes (cloth, rigid-static), typed descriptors, and change events; integrated with controller for activation and static AABBs; emits on resize/scroll.", "why": "Make a single source of truth for activation/reset/inspector and align with upcoming inspector.", "context": "Controller previously queried .cloth-enabled and managed static AABBs ad-hoc; no registry events.", "issue": "Lack of typed descriptors and eventing made inspector and resets brittle.", "resolution": "New PhysicsRegistry with add/update/remove events; controller subscribes and updates meshes + collision system.", "future_work": "Extend for rigid-dynamic and data-attr parsing; consume in inspector UI."}, {"topic": "Collision (OBB SAT)", "what": "Added satObbAabb module for OBB vs AABB with MTV, restitution + friction helper; unit tests.", "why": "Prepare for rigid OBB; correct normals and response.", "context": "CollisionSystem only supported AABB vs cloth; no OBB SAT.", "issue": "Glancing collisions and rotation not handled.", "resolution": "SAT helpers and tests; integration of OBB pending."}, {"topic": "Events", "what": "Defined EngineEvent union + runtime validator; EventBus; events panel (filter + JSON) toggled via Cmd/Ctrl+E.", "why": "Make debugging observable and searchable.", "context": "No unified event typing or panel.", "issue": "Payloads ad-hoc and unvalidated.", "resolution": "Union types + guards + simple non-modal panel."}, {"topic": "Perf", "what": "Perf monitor with rolling averages + budgets and overlay; wrap EngineWorld.addSystem to time fixed/frame updates.", "why": "Track per-subsystem budgets (overlay ≤0.8 ms, sim ≤1.5 ms).", "context": "No visibility into subsystem frame time.", "issue": "Perf regressions hard to spot.", "resolution": "Perf overlay toggle in Debug; budgets flagged in red."}, {"topic": "UI polish", "what": "Drawer 150ms hide transition and toast 'panel clothified'; 'Wireframe preview' explainer.", "why": "Match UX expectations and clarify mesh vs DOM.", "context": "Close animation abrupt; unclear wireframe meaning.", "issue": "Debug palette closed instantly; wireframe ambiguous.", "resolution": "Mantine transitionProps; small top toast; explanatory copy."}, {"topic": "CI policy", "what": "Added CI and PR policy workflows: lint+test and milestone/tests gate.", "why": "Enforce tests-first and milestone linkage.", "context": "Prior marathon PR lacked structure.", "issue": "Hard to review and regressions slipped.", "resolution": "Fail PRs without milestone or tests; surface acceptance-criteria hint."}], "key_decisions": ["Use registry events to drive controller activation + collision seed", "Monkey-patch EngineWorld.addSystem to instrument perf without invasive changes", "Events panel is non-modal drawer substitute, docked ~45% height"], "action_items": [{"task": "Parse data attributes (mass, friction, restitution, shape) into rigid bodies and integrate SAT into CollisionSystem", "owner": "Assistant"}, {"task": "Emit engine events from activation/collision/sleep transitions", "owner": "Assistant"}, {"task": "Convert Events panel to draggable/resizable window (GSAP) per roadmap", "owner": "Assistant"}]}

For decades the web has treated every page like a document. HTML, CSS, and the browser’s layout engine were designed to flow text, paginate articles, and keep markup accessible no matter the device. That legacy is why a plain `<p>` looks right on every screen, why screen readers can navigate effortlessly, and why a broken JavaScript bundle doesn’t kill your content. It’s also why the typical website still feels like a dressed-up report despite our hardware being capable of running entire game engines.

Document-centric rendering gives us universality, but the trade-off is rigidity. When a designer wants kinetic layout, real physics, or truly fluid animation, the DOM’s cascading rules, reflow, and box model become constraints rather than allies. Responsive design solved screen fragmentation with breakpoints, percentages, and media queries—a brute-force approach that keeps piling rules onto an already complex system. Developers juggle three technologies at once (HTML, CSS, JavaScript) just to coax an element into the right spot, while stakeholders wonder why “make it pop” becomes a project in itself.
Expand Down
21 changes: 21 additions & 0 deletions docs/issues/physics-registry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

# PhysicsRegistry — discovery, descriptors, and change events

Labels: architecture, physics, registry, project:newton
Milestone: PROJECT: Newton

## Summary

Design and implement a PhysicsRegistry that discovers DOM nodes, stores typed descriptors (type, tag, attrs, origins), emits change events on layout/resize, and serves as the single source of truth for activation/reset/inspector.

## Acceptance Criteria

- [ ] Discovers `.cloth-enabled`, `.rigid-dynamic`, `.rigid-static` and nodes with `data-phys-*` or `data-cloth-*` attributes
- [ ] Descriptor includes: { id, tag, type, attrs (parsed), origin (rect/world), active state }
- [ ] Emits events: `registry:add`, `registry:update`, `registry:remove` with prior/next descriptors
- [ ] Diffing works: only changed fields emitted; stable ids across runs
- [ ] Inspector/activation can subscribe to the registry

## Tests (write first)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix heading spacing to satisfy markdownlint.

Missing blank line below the "## Tests" heading violates MD022. Past comments addressed identical issues at lines 6, 9, and 16—this one slipped through.

Apply this diff:

 ## Tests (write first)
+
 - [ ] physicsRegistry.spec: discovery from a test DOM, descriptor diffing, events on resize
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## Tests (write first)
## Tests (write first)
- [ ] physicsRegistry.spec: discovery from a test DOM, descriptor diffing, events on resize
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

19-19: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

🤖 Prompt for AI Agents
In docs/issues/physics-registry.md around line 19, the "## Tests" heading is
missing the required blank line below it (MD022). Insert a single blank line
immediately after the "## Tests" heading so the heading is separated from the
following content, matching the fixes already applied at lines 6, 9, and 16.

- [ ] physicsRegistry.spec: discovery from a test DOM, descriptor diffing, events on resize
- [ ] integration: inspector or controller subscribes and receives `registry:update`
36 changes: 24 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading