diff --git a/.changeset/add-interactive-resize.md b/.changeset/add-interactive-resize.md index 690fecb..01b03fc 100644 --- a/.changeset/add-interactive-resize.md +++ b/.changeset/add-interactive-resize.md @@ -1,11 +1,11 @@ --- -"@floormap-tools/core": minor -"@floormap-tools/svg": minor -"@floormap-tools/react": minor +"@spatialkit/core": minor +"@spatialkit/svg": minor +"@spatialkit/react": minor --- Add interactive entity resizing via drag handles. -- `@floormap-tools/core` exports a new `ResizeDirection` type (`'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw'`), `hitTestResizeHandles` to detect which handle is under the cursor in screen space, and `applyResizeDelta` to compute new bounds from a world-space drag delta (with optional snap-to-grid on the edge position) -- `@floormap-tools/svg` adds `paintResizeHandles` painter that draws 8 handles around each selected entity at a consistent pixel size regardless of zoom, and wires resize drag into `attachHandlers` with a priority-over-entity-drag rule -- `@floormap-tools/react` adds a `ResizeHandles` internal component that renders the same 8 handles declaratively, and wires resize drag into `attachHandlers` with the same state machine (`idle → pending → resize → idle`) +- `@spatialkit/core` exports a new `ResizeDirection` type (`'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w' | 'nw'`), `hitTestResizeHandles` to detect which handle is under the cursor in screen space, and `applyResizeDelta` to compute new bounds from a world-space drag delta (with optional snap-to-grid on the edge position) +- `@spatialkit/svg` adds `paintResizeHandles` painter that draws 8 handles around each selected entity at a consistent pixel size regardless of zoom, and wires resize drag into `attachHandlers` with a priority-over-entity-drag rule +- `@spatialkit/react` adds a `ResizeHandles` internal component that renders the same 8 handles declaratively, and wires resize drag into `attachHandlers` with the same state machine (`idle → pending → resize → idle`) diff --git a/.changeset/add-snap-to-grid.md b/.changeset/add-snap-to-grid.md index af41fe4..e0ca254 100644 --- a/.changeset/add-snap-to-grid.md +++ b/.changeset/add-snap-to-grid.md @@ -1,11 +1,11 @@ --- -"@floormap-tools/core": minor -"@floormap-tools/svg": minor -"@floormap-tools/react": minor +"@spatialkit/core": minor +"@spatialkit/svg": minor +"@spatialkit/react": minor --- Add snap-to-grid support for entity drag. -- `@floormap-tools/core` exports a new `snapToGrid(value, gridSize)` utility that rounds a world-space coordinate to the nearest grid multiple -- `@floormap-tools/svg` accepts a `snapToGrid?: number` option on `mountSvgRenderer`; when set, entity positions are snapped on every drag frame -- `@floormap-tools/react` accepts a `snapToGrid?: number` prop on `FloormapCanvas` with the same behavior +- `@spatialkit/core` exports a new `snapToGrid(value, gridSize)` utility that rounds a world-space coordinate to the nearest grid multiple +- `@spatialkit/svg` accepts a `snapToGrid?: number` option on `mountSvgRenderer`; when set, entity positions are snapped on every drag frame +- `@spatialkit/react` accepts a `snapToGrid?: number` prop on `SpatialCanvas` with the same behavior diff --git a/.claude/skills/issue.md b/.claude/skills/issue.md index 38011f8..f86123b 100644 --- a/.claude/skills/issue.md +++ b/.claude/skills/issue.md @@ -1,17 +1,17 @@ --- name: issue -description: Interactive creator and editor of GitHub issues for the floormap-tools/floormap project. Asks structured questions to build a complete, consistent issue body matching the project's template. +description: Interactive creator and editor of GitHub issues for the spatialkit/spatial project. Asks structured questions to build a complete, consistent issue body matching the project's template. --- -You are a GitHub issue creator and editor for the **floormap-tools/floormap** repository. Your job is to collect structured information and produce a well-formatted issue that matches the project's template. +You are a GitHub issue creator and editor for the **spatialkit/spatial** repository. Your job is to collect structured information and produce a well-formatted issue that matches the project's template. --- ## Project reference -**Repo:** `floormap-tools/floormap` -**Owner:** `floormap-tools` -**Packages:** `@floormap-tools/core` · `@floormap-tools/svg` · `@floormap-tools/react` · cross-cutting +**Repo:** `spatialkit/spatial` +**Owner:** `spatialkit` +**Packages:** `@spatialkit/core` · `@spatialkit/svg` · `@spatialkit/react` · cross-cutting **Milestones:** | # | Title | Scope | |---|-------|-------| @@ -53,7 +53,7 @@ Questions to ask in round 1: 2. "What type of issue is this?" (header: "Type") Options: Feature | Bug | Chore 3. "Which package does this affect?" (header: "Package") - Options: @floormap-tools/core | @floormap-tools/svg | @floormap-tools/react | cross-cutting + Options: @spatialkit/core | @spatialkit/svg | @spatialkit/react | cross-cutting 4. "Which milestone does this belong to?" (header: "Milestone") Options: M1 — Functional editor | M2 — Undo/Redo & Persistence | M3 — Extend & Optimize | M4 — Ecosystem | None ``` @@ -124,8 +124,8 @@ Use the answers to generate a complete body in this template: **Create:** ``` mcp__github__create_issue( - owner: "floormap-tools", - repo: "floormap", + owner: "spatialkit", + repo: "spatial", title: , body: <generated body>, labels: [<label matching type>], @@ -136,8 +136,8 @@ mcp__github__create_issue( **Edit:** ``` mcp__github__update_issue( - owner: "floormap-tools", - repo: "floormap", + owner: "spatialkit", + repo: "spatial", issue_number: <number>, title: <updated title if changed>, body: <new body> @@ -165,5 +165,5 @@ These fields can't be set via the API — open the issue in the project board to - Never skip the question rounds — always ask before writing the body. - Never invent code or file paths not derivable from the project context. -- Never add production dependencies in issues related to `@floormap-tools/core` or `@floormap-tools/svg`. +- Never add production dependencies in issues related to `@spatialkit/core` or `@spatialkit/svg`. - If the user provides a partial description in their invocation args, use it to pre-fill context but still confirm via questions. diff --git a/.claude/skills/floormap-expert.md b/.claude/skills/spatial-expert.md similarity index 83% rename from .claude/skills/floormap-expert.md rename to .claude/skills/spatial-expert.md index e8e6ef4..ef4d53e 100644 --- a/.claude/skills/floormap-expert.md +++ b/.claude/skills/spatial-expert.md @@ -1,15 +1,15 @@ --- -name: floormap-expert -description: Expert skill for the Floormap project. Activates deep context about architecture, conventions, module responsibilities, and development workflow. Use when working on any code in this repository. +name: spatial-expert +description: Expert skill for the Spatial project. Activates deep context about architecture, conventions, module responsibilities, and development workflow. Use when working on any code in this repository. --- -You are an expert on the **Floormap** codebase. Apply this context whenever working on any code in this repository. +You are an expert on the **Spatial** codebase. Apply this context whenever working on any code in this repository. --- -## What Floormap is +## What Spatial is -A zero-dependency TypeScript toolkit for building interactive 2D editors (floor plans, seat maps, office layouts). It provides scene management, pan/zoom/selection/picking primitives, and pluggable renderers. It is a monorepo with two publishable packages: `@floormap-tools/core` and `@floormap-tools/svg`. +A zero-dependency TypeScript toolkit for building interactive 2D editors (floor plans, seat maps, office layouts). It provides scene management, pan/zoom/selection/picking primitives, and pluggable renderers. It is a monorepo with two publishable packages: `@spatialkit/core` and `@spatialkit/svg`. --- @@ -33,7 +33,7 @@ A zero-dependency TypeScript toolkit for building interactive 2D editors (floor ## Architecture rules to enforce -1. **No production deps** — `@floormap-tools/core` and `@floormap-tools/svg` must stay dependency-free. +1. **No production deps** — `@spatialkit/core` and `@spatialkit/svg` must stay dependency-free. 2. **Core is framework-agnostic** — no DOM, no React, no browser APIs in `packages/core/src/`. 3. **Branded types everywhere** — `EntityId` and `LayerId` are branded strings; never substitute with `string`. 4. **Immutable selection** — `applySelection` returns a new `Set`, never mutates the input. @@ -55,7 +55,7 @@ Both are in `viewport.ts`. Use them — don't reimplement. ``` matrix(zoom 0 0 zoom -pan.x*zoom -pan.y*zoom) ``` -Applied on `<g data-fm="viewport">`. Changing this breaks all rendering. +Applied on `<g data-spatial="viewport">`. Changing this breaks all rendering. ### Core events ``` @@ -106,5 +106,5 @@ Test environment: **Node** for core, **jsdom** for svg. Tests live in `src/__tes - Forgetting `selectable: true` on entities → `hitTestPoint` returns null - Using `string` instead of `EntityId` / `LayerId` → type errors downstream - Mutating the `Set` from `applySelection` → breaks immutability contract -- Adding DOM code to `@floormap-tools/core` → breaks the agnostic renderer model +- Adding DOM code to `@spatialkit/core` → breaks the agnostic renderer model - Not updating `index.ts` after adding exports → new symbols invisible to consumers diff --git a/.github/workflows/deploy-examples.yml b/.github/workflows/deploy-examples.yml index 5836c7c..75664cd 100644 --- a/.github/workflows/deploy-examples.yml +++ b/.github/workflows/deploy-examples.yml @@ -38,7 +38,7 @@ jobs: <html lang="en"> <head> <meta charset="UTF-8" /> - <title>Floormap — Examples + Spatial — Examples -

Floormap

+

Spatial

Interactive 2D editor toolkit — live examples

Vanilla JS - @floormap-tools/core + @floormap-tools/svg + @spatialkit/core + @spatialkit/svg React - @floormap-tools/core + @floormap-tools/react + @spatialkit/core + @spatialkit/react diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 10e41cb..d7ecc75 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -24,7 +24,7 @@ jobs: node-version: 22 cache: pnpm registry-url: https://npm.pkg.github.com - scope: '@floormap-tools' + scope: '@spatialkit' - run: pnpm install --frozen-lockfile diff --git a/.npmrc b/.npmrc index 31496ae..3e4c91a 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1 @@ -@floormap-tools:registry=https://npm.pkg.github.com +@spatialkit:registry=https://npm.pkg.github.com diff --git a/CLAUDE.md b/CLAUDE.md index 6e97bde..d75828a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,10 +1,10 @@ -# Floormap — Claude Code Guide +# Spatial — Claude Code Guide ## Project overview -Floormap is a zero-dependency TypeScript toolkit for building interactive 2D editors (floor plans, seat maps, office layouts, warehouse maps). It exposes low-level primitives for pan/zoom/selection/picking and ships pluggable renderers. +Spatial is a zero-dependency TypeScript toolkit for building interactive 2D editors (floor plans, seat maps, office layouts, warehouse maps). It exposes low-level primitives for pan/zoom/selection/picking and ships pluggable renderers. -**Repo:** `github.com/floormap-tools/floormap` +**Repo:** `github.com/spatialkit/spatial` **Status:** v0.0.0 — core stable, SVG renderer complete, React adapter in progress > Detailed per-package guides: [`packages/core/CLAUDE.md`](packages/core/CLAUDE.md) · [`packages/svg/CLAUDE.md`](packages/svg/CLAUDE.md) @@ -15,10 +15,10 @@ Floormap is a zero-dependency TypeScript toolkit for building interactive 2D edi ``` packages/ - core/ @floormap-tools/core — scene engine, viewport, selection, events, picking - svg/ @floormap-tools/svg — SVG renderer + DOM event handlers + core/ @spatialkit/core — scene engine, viewport, selection, events, picking + svg/ @spatialkit/svg — SVG renderer + DOM event handlers adapters/ - react/ @floormap-tools/react — React adapter (hooks + FloormapCanvas component) + react/ @spatialkit/react — React adapter (hooks + SpatialCanvas component) examples/ js-vanilla/ vanilla JS example react/ React example @@ -47,7 +47,7 @@ Run from repo root. Per-package commands work inside each `packages/*` directory ## Architecture -### @floormap-tools/core +### @spatialkit/core | Module | Responsibility | |--------|---------------| @@ -71,7 +71,7 @@ Run from repo root. Per-package commands work inside each `packages/*` directory - `zoomAt` delta is multiplicative: `0.1` = +10%, `−0.1` = −10% - `panBy` takes screen-space delta; positive `x` shifts content left (pan moves right in world) -### @floormap-tools/svg +### @spatialkit/svg | Module | Responsibility | |--------|---------------| @@ -84,13 +84,13 @@ Run from repo root. Per-package commands work inside each `packages/*` directory **SVG group hierarchy:** ``` - ← transform matrix applied here - - - ← one per layer, in scene.layers order - - - ← only if defs callback is provided + ← transform matrix applied here + + + ← one per layer, in scene.layers order + + + ← only if defs callback is provided ``` **Transform matrix:** `matrix(zoom 0 0 zoom -pan.x*zoom -pan.y*zoom)` @@ -142,8 +142,8 @@ Run from repo root. Per-package commands work inside each `packages/*` directory ## What NOT to do -- Do not add production dependencies to `@floormap-tools/core` or `@floormap-tools/svg`. +- Do not add production dependencies to `@spatialkit/core` or `@spatialkit/svg`. - Do not bypass branded types with plain strings. - Do not mutate `Set` objects returned by `applySelection`. -- Do not add framework-specific code to `@floormap-tools/core` (keep it agnostic). +- Do not add framework-specific code to `@spatialkit/core` (keep it agnostic). - Do not skip typecheck — the build uses project references. diff --git a/LICENSE b/LICENSE index cecf728..0bd224f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 Floor Map +Copyright (c) 2025 spatialkit Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 2bd64e1..fcf0db8 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@

- - Floor Map + + Spatial

- Floormap + Spatial
Open-source TypeScript toolkit for interactive 2D editors and floor plans.

@@ -13,15 +13,15 @@ A zero-dependency toolkit for building interactive 2D editors — floor plans, seat maps, office layouts, warehouse maps, and more. -Floormap provides the low-level engine for pan/zoom, selection, hit testing, and event management, plus pluggable renderers. No framework lock-in, no production dependencies. +Spatial provides the low-level engine for pan/zoom, selection, hit testing, and event management, plus pluggable renderers. No framework lock-in, no production dependencies. ## Packages | Package | Version | Description | |---------|---------|-------------| -| [`@floormap-tools/core`](packages/core) | 0.1.0 | Scene engine, viewport, event bus, picking, selection | -| [`@floormap-tools/svg`](packages/svg) | 0.1.0 | SVG renderer + DOM event handlers | -| [`@floormap-tools/react`](packages/adapters/react) | 0.1.0 | React adapter — hooks and `FloormapCanvas` component | +| [`@spatialkit/core`](packages/core) | 0.1.0 | Scene engine, viewport, event bus, picking, selection | +| [`@spatialkit/svg`](packages/svg) | 0.1.0 | SVG renderer + DOM event handlers | +| [`@spatialkit/react`](packages/adapters/react) | 0.1.0 | React adapter — hooks and `SpatialCanvas` component | --- @@ -30,13 +30,13 @@ Floormap provides the low-level engine for pan/zoom, selection, hit testing, and **Vanilla JS / framework-agnostic:** ```bash -npm install @floormap-tools/core @floormap-tools/svg +npm install @spatialkit/core @spatialkit/svg ``` **React:** ```bash -npm install @floormap-tools/core @floormap-tools/react +npm install @spatialkit/core @spatialkit/react ``` All packages ship dual **ESM + CJS** builds with full TypeScript declarations. @@ -47,14 +47,14 @@ All packages ship dual **ESM + CJS** builds with full TypeScript declarations. ```tsx import { useCallback, useEffect } from 'react'; -import { createEmptyScene, addEntity } from '@floormap-tools/core'; -import type { Entity, EntityId, LayerId } from '@floormap-tools/core'; +import { createEmptyScene, addEntity } from '@spatialkit/core'; +import type { Entity, EntityId, LayerId } from '@spatialkit/core'; import { - FloormapCanvas, - useFloormapCore, + SpatialCanvas, + useSpatialCore, useSelection, useViewport, -} from '@floormap-tools/react'; +} from '@spatialkit/react'; const LAYER = 'main' as LayerId; @@ -73,7 +73,7 @@ const initialScene = (() => { export function FloorPlan() { // Create the engine once per component lifetime - const core = useFloormapCore(() => ({ + const core = useSpatialCore(() => ({ scene: initialScene, viewport: { zoom: 1, pan: { x: 0, y: 0 }, screenSize: { width: 800, height: 600 } }, })); @@ -99,7 +99,7 @@ export function FloorPlan() {

Zoom: {Math.round(viewport.zoom * 100)}%

Selected: {[...selection].join(', ') || 'none'}

- ` +### `` The main component. Renders an `` element and wires all interactions. ```tsx - } @@ -156,18 +156,18 @@ The main component. Renders an `` element and wires all interactions. ### Hooks -#### `useFloormapCore(init)` +#### `useSpatialCore(init)` -Creates a `FloormapCore` instance that persists for the lifetime of the component. +Creates a `SpatialCore` instance that persists for the lifetime of the component. ```ts -const core = useFloormapCore(() => ({ +const core = useSpatialCore(() => ({ scene: myScene, viewport: { zoom: 1, pan: { x: 0, y: 0 }, screenSize: { width: 800, height: 600 } }, })); ``` -Accepts an initializer function (lazy) or a plain `FloormapCoreOptions` object. The core is created exactly once and never re-created on re-renders. +Accepts an initializer function (lazy) or a plain `SpatialCoreOptions` object. The core is created exactly once and never re-created on re-renders. --- @@ -208,9 +208,9 @@ useCoreEvent(core, 'entities:changed', ({ type, ids }) => { ## Quick start — Vanilla JS ```ts -import { createCore, createEmptyScene } from '@floormap-tools/core'; -import { mountSvgRenderer } from '@floormap-tools/svg'; -import type { EntityId, LayerId } from '@floormap-tools/core'; +import { createCore, createEmptyScene } from '@spatialkit/core'; +import { mountSvgRenderer } from '@spatialkit/svg'; +import type { EntityId, LayerId } from '@spatialkit/core'; // 1. Define layers (bottom → top z-order) const LAYERS = { @@ -290,7 +290,7 @@ renderer.destroy(); - `fitToBounds`, `fitToScene`, `centerOn` for programmatic camera control - Precise coordinate transforms: `worldToScreen` / `screenToWorld` -**Interaction** (via `@floormap-tools/svg` or `@floormap-tools/react`) +**Interaction** (via `@spatialkit/svg` or `@spatialkit/react`) - Mouse wheel zoom (configurable sensitivity) - Pointer drag-to-pan - Pinch-to-zoom on touch and pen devices @@ -298,15 +298,15 @@ renderer.destroy(); - Multi-select: Shift (add), Ctrl/Meta (toggle) - Drag-to-move selected entities -**Rendering** (via `@floormap-tools/svg` or `@floormap-tools/react`) +**Rendering** (via `@spatialkit/svg` or `@spatialkit/react`) - Bring-your-own-drawEntity callback — full SVG freedom - World-aligned background grid that stays crisp at any zoom - Built-in selection highlight overlay with customisable style - Automatic repaints on viewport, entity, and selection changes -**React adapter** (`@floormap-tools/react`) -- `FloormapCanvas` component — drop in an SVG canvas with zero boilerplate -- `useFloormapCore` — lazy engine initialisation, stable across re-renders +**React adapter** (`@spatialkit/react`) +- `SpatialCanvas` component — drop in an SVG canvas with zero boilerplate +- `useSpatialCore` — lazy engine initialisation, stable across re-renders - `useSelection` / `useViewport` — fine-grained reactive subscriptions via `useSyncExternalStore` - `useCoreEvent` — subscribe to any core event with a stable handler ref @@ -325,25 +325,25 @@ renderer.destroy(); │ Your application │ │ (add/update entities, read selection) │ └────────────────┬────────────────────────┘ - │ FloormapCore API + │ SpatialCore API ┌───────────▼────────────┐ - │ @floormap-tools/core │ + │ @spatialkit/core │ │ scene · viewport │ │ events · picking │ │ selection · store │ └─────────┬──────────────┘ │ subscribes to events ┌─────────┴──────────────┬──────────────────────┐ - │ @floormap-tools/svg │ @floormap-tools/react │ - │ DOM setup · renderer │ FloormapCanvas │ - │ handlers · grid │ useFloormapCore │ - │ selection overlay │ useSelection │ - └────────────────────────┘ useViewport │ - useCoreEvent │ - └────────────────────┘ + │ @spatialkit/svg │ @spatialkit/react │ + │ DOM setup · renderer │ SpatialCanvas │ + │ handlers · grid │ useSpatialCore │ + │ selection overlay │ useSelection │ + └────────────────────────┤ useViewport │ + │ useCoreEvent │ + └──────────────────────┘ ``` -`@floormap-tools/core` is fully framework-agnostic — no DOM, no browser APIs. `@floormap-tools/svg` and `@floormap-tools/react` are independent renderer/adapter layers that connect core to the browser. Use whichever fits your stack. +`@spatialkit/core` is fully framework-agnostic — no DOM, no browser APIs. `@spatialkit/svg` and `@spatialkit/react` are independent renderer/adapter layers that connect core to the browser. Use whichever fits your stack. --- @@ -372,8 +372,8 @@ All entity `bounds` are in world coordinates. Mouse positions from DOM events ar **Prerequisites:** Node 22, pnpm 10 ```bash -git clone https://github.com/floormap-tools/floormap -cd floormap +git clone https://github.com/spatialkit/spatial +cd spatial pnpm install ``` @@ -397,7 +397,7 @@ If you find a bug or want to suggest an improvement, please open an **issue**. For pull requests: - Keep the PR focused on a single change or feature - Run `pnpm typecheck && pnpm test && pnpm lint` before submitting -- Do not add production dependencies to `@floormap-tools/core`, `@floormap-tools/svg`, or `@floormap-tools/react` +- Do not add production dependencies to `@spatialkit/core`, `@spatialkit/svg`, or `@spatialkit/react` --- diff --git a/package.json b/package.json index ed79ca6..88cc1ca 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "floormap", + "name": "spatial", "private": true, "packageManager": "pnpm@10.17.0", "workspaces": [ diff --git a/packages/adapters/react/CHANGELOG.md b/packages/adapters/react/CHANGELOG.md index 9c4074b..b8e7280 100644 --- a/packages/adapters/react/CHANGELOG.md +++ b/packages/adapters/react/CHANGELOG.md @@ -1,12 +1,12 @@ -# @floormap-tools/react +# @spatialkit/react ## 0.1.0 ### Minor Changes -- Initial release of @floormap-tools/core, @floormap-tools/svg, and @floormap-tools/react. +- Initial release of @spatialkit/core, @spatialkit/svg, and @spatialkit/react. ### Patch Changes - Updated dependencies - - @floormap-tools/core@0.1.0 + - @spatialkit/core@0.1.0 diff --git a/packages/adapters/react/README.md b/packages/adapters/react/README.md index a650742..e47f5b7 100644 --- a/packages/adapters/react/README.md +++ b/packages/adapters/react/README.md @@ -1,13 +1,13 @@ -# @floormap-tools/react +# @spatialkit/react -React adapter for [`@floormap-tools/core`](../../core). Provides a drop-in `` component and hooks for building interactive 2D editors — floor plans, seat maps, office layouts — with zero boilerplate. +React adapter for [`@spatialkit/core`](../../core). Provides a drop-in `` component and hooks for building interactive 2D editors — floor plans, seat maps, office layouts — with zero boilerplate. ## Installation ```bash -npm install @floormap-tools/core @floormap-tools/react +npm install @spatialkit/core @spatialkit/react # or -pnpm add @floormap-tools/core @floormap-tools/react +pnpm add @spatialkit/core @spatialkit/react ``` React 18 is required as a peer dependency. @@ -16,21 +16,21 @@ React 18 is required as a peer dependency. ```tsx import { useCallback, useEffect } from 'react'; -import { createEmptyScene } from '@floormap-tools/core'; -import type { Entity, EntityId, LayerId } from '@floormap-tools/core'; -import { FloormapCanvas, useFloormapCore, useSelection, useViewport } from '@floormap-tools/react'; +import { createEmptyScene } from '@spatialkit/core'; +import type { Entity, EntityId, LayerId } from '@spatialkit/core'; +import { SpatialCanvas, useSpatialCore, useSelection, useViewport } from '@spatialkit/react'; const LAYER = 'main' as LayerId; const initialScene = (() => { const scene = createEmptyScene({ width: 1200, height: 800 }, [LAYER]); - // addEntity is a store helper — import from @floormap-tools/core if needed, + // addEntity is a store helper — import from @spatialkit/core if needed, // or use core.add() after the core is created. return scene; })(); export function FloorPlan() { - const core = useFloormapCore(() => ({ + const core = useSpatialCore(() => ({ scene: initialScene, viewport: { zoom: 1, pan: { x: 0, y: 0 }, screenSize: { width: 800, height: 600 } }, })); @@ -61,7 +61,7 @@ export function FloorPlan() {

Zoom: {Math.round(viewport.zoom * 100)}%

Selected: {[...selection].join(', ') || 'none'}

- ` +### `` The main component. Renders an `` element, manages the viewport transform, wires all pointer/wheel interactions, and calls `drawEntity` for every entity in the scene. ```tsx - } grid={{ size: 40 }} @@ -95,7 +95,7 @@ The main component. Renders an `` element, manages the viewport transform, | Prop | Type | Description | |------|------|-------------| -| `core` | `FloormapCore` | The engine instance created with `useFloormapCore` | +| `core` | `SpatialCore` | The engine instance created with `useSpatialCore` | | `drawEntity` | `(entity, ctx) => ReactNode` | Called for every entity — return React SVG elements | #### Rendering @@ -172,12 +172,12 @@ interface SelectionOverlayStyle { --- -### `useFloormapCore(init)` +### `useSpatialCore(init)` -Creates a `FloormapCore` instance that persists for the lifetime of the component. The initializer is called exactly once. +Creates a `SpatialCore` instance that persists for the lifetime of the component. The initializer is called exactly once. ```ts -const core = useFloormapCore(() => ({ +const core = useSpatialCore(() => ({ scene: myScene, viewport: { zoom: 1, pan: { x: 0, y: 0 }, screenSize: { width: 800, height: 600 } }, })); @@ -229,7 +229,7 @@ useCoreEvent(core, 'selection:change', ({ selection }) => { ### Updating `screenSize` on resize -`FloormapCanvas` automatically keeps `core.viewport.screenSize` in sync with the `` element via a `ResizeObserver` — no manual wiring needed. +`SpatialCanvas` automatically keeps `core.viewport.screenSize` in sync with the `` element via a `ResizeObserver` — no manual wiring needed. ### Fitting the scene on mount diff --git a/packages/adapters/react/package.json b/packages/adapters/react/package.json index faf1b80..6c39904 100644 --- a/packages/adapters/react/package.json +++ b/packages/adapters/react/package.json @@ -1,5 +1,5 @@ { - "name": "@floormap-tools/react", + "name": "@spatialkit/react", "version": "0.1.0", "private": false, "type": "module", @@ -18,7 +18,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/floormap-tools/floormap.git" + "url": "https://github.com/spatialkit/spatial.git" }, "scripts": { "build": "tsup src/index.ts --dts --format esm,cjs --clean", @@ -26,7 +26,7 @@ "test:watch": "vitest" }, "devDependencies": { - "@floormap-tools/core": "workspace:*", + "@spatialkit/core": "workspace:*", "@testing-library/react": "^16.0.0", "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", @@ -36,7 +36,7 @@ "vitest": "^2.0.0" }, "peerDependencies": { - "@floormap-tools/core": "0.x", + "@spatialkit/core": "0.x", "react": "^18.0.0" } } diff --git a/packages/adapters/react/src/FloormapCanvas.tsx b/packages/adapters/react/src/SpatialCanvas.tsx similarity index 91% rename from packages/adapters/react/src/FloormapCanvas.tsx rename to packages/adapters/react/src/SpatialCanvas.tsx index 3971283..ff23d7a 100644 --- a/packages/adapters/react/src/FloormapCanvas.tsx +++ b/packages/adapters/react/src/SpatialCanvas.tsx @@ -6,7 +6,7 @@ import { type CSSProperties, type ReactNode, } from 'react'; -import type { Entity, EntityId, FloormapCore, LayerId } from '@floormap-tools/core'; +import type { Entity, EntityId, SpatialCore, LayerId } from '@spatialkit/core'; import { attachHandlers } from './internal/attachHandlers'; import { useEntities } from './internal/useEntities'; import { useViewportTransform } from './internal/useViewportTransform'; @@ -19,8 +19,8 @@ import type { GridOptions, SelectionOverlayStyle } from './types'; const DEFAULT_SELECTION_OVERLAY: SelectionOverlayStyle = {}; -export interface FloormapCanvasProps { - core: FloormapCore; +export interface SpatialCanvasProps { + core: SpatialCore; drawEntity: (entity: Entity, ctx: { selected: boolean }) => ReactNode; // Visual @@ -46,7 +46,7 @@ export interface FloormapCanvasProps { style?: CSSProperties; } -export const FloormapCanvas = memo(function FloormapCanvas({ +export const SpatialCanvas = memo(function SpatialCanvas({ core, drawEntity, grid = false, @@ -64,7 +64,7 @@ export const FloormapCanvas = memo(function FloormapCanvas({ snapToGrid, className, style, -}: FloormapCanvasProps) { +}: SpatialCanvasProps) { const svgRef = useRef(null); const viewportGRef = useRef(null); @@ -133,11 +133,11 @@ export const FloormapCanvas = memo(function FloormapCanvas({ return ( - + {grid !== false && } - + {core.scene.layers.map((layerId) => ( - + {(entitiesByLayer.get(layerId) ?? []).map((entity) => ( {drawEntity(entity, { selected: selection.has(entity.id) })} diff --git a/packages/adapters/react/src/__tests__/FloormapCanvas.test.tsx b/packages/adapters/react/src/__tests__/SpatialCanvas.test.tsx similarity index 81% rename from packages/adapters/react/src/__tests__/FloormapCanvas.test.tsx rename to packages/adapters/react/src/__tests__/SpatialCanvas.test.tsx index 21ad401..beee24d 100644 --- a/packages/adapters/react/src/__tests__/FloormapCanvas.test.tsx +++ b/packages/adapters/react/src/__tests__/SpatialCanvas.test.tsx @@ -1,9 +1,9 @@ // @vitest-environment jsdom import { describe, it, expect, vi, beforeAll } from 'vitest'; import { render, act } from '@testing-library/react'; -import { createCore, createEmptyScene } from '@floormap-tools/core'; -import type { Entity, EntityId, LayerId } from '@floormap-tools/core'; -import { FloormapCanvas } from '../FloormapCanvas'; +import { createCore, createEmptyScene } from '@spatialkit/core'; +import type { Entity, EntityId, LayerId } from '@spatialkit/core'; +import { SpatialCanvas } from '../SpatialCanvas'; beforeAll(() => { vi.stubGlobal( @@ -41,21 +41,21 @@ function makeSvgStubs(container: HTMLElement) { return svg; } -describe('FloormapCanvas', () => { +describe('SpatialCanvas', () => { it('renders an SVG element with viewport group', () => { const core = makeCore(); const { container } = render( - null} />, + null} />, ); expect(container.querySelector('svg')).toBeTruthy(); - expect(container.querySelector("g[data-fm='viewport']")).toBeTruthy(); - expect(container.querySelector("g[data-fm='objects']")).toBeTruthy(); + expect(container.querySelector("g[data-spatial='viewport']")).toBeTruthy(); + expect(container.querySelector("g[data-spatial='objects']")).toBeTruthy(); }); it('calls drawEntity for each entity in the scene', () => { const core = makeCore(); const drawEntity = vi.fn((_e: Entity) => null); - render(); + render(); expect(drawEntity).toHaveBeenCalledWith( expect.objectContaining({ id: 'e1' }), expect.objectContaining({ selected: false }), @@ -65,7 +65,7 @@ describe('FloormapCanvas', () => { it('renders JSX returned by drawEntity into the SVG', () => { const core = makeCore(); const { container } = render( - ( @@ -78,7 +78,7 @@ describe('FloormapCanvas', () => { it('re-renders with selected=true when entity is selected', () => { const core = makeCore(); const drawEntity = vi.fn((_e: Entity, _ctx: { selected: boolean }) => null); - render(); + render(); act(() => { core.setSelection(['e1' as EntityId], 'replace'); @@ -91,7 +91,7 @@ describe('FloormapCanvas', () => { it('re-renders when a new entity is added', () => { const core = makeCore(); const drawEntity = vi.fn((_e: Entity) => null); - render(); + render(); const callsBefore = drawEntity.mock.calls.length; act(() => { @@ -109,27 +109,27 @@ describe('FloormapCanvas', () => { it('renders a layer group per scene layer', () => { const core = makeCore(); const { container } = render( - null} />, + null} />, ); - expect(container.querySelector("[data-fm-layer='L']")).toBeTruthy(); + expect(container.querySelector("[data-spatial-layer='L']")).toBeTruthy(); }); it('renders SelectionOverlay when selectionOverlay is set', () => { const core = makeCore(); core.setSelection(['e1' as EntityId], 'replace'); const { container } = render( - null} selectionOverlay={{ stroke: '#ff0000' }} />, ); - expect(container.querySelector("g[data-fm='selection'] rect")).toBeTruthy(); + expect(container.querySelector("g[data-spatial='selection'] rect")).toBeTruthy(); }); it('entity drag moves entity in world space', () => { const core = makeCore(); - const { container } = render( null} />); + const { container } = render( null} />); const svg = makeSvgStubs(container); act(() => { @@ -146,7 +146,7 @@ describe('FloormapCanvas', () => { it('enableEntityDrag: false disables entity movement', () => { const core = makeCore(); const { container } = render( - null} enableEntityDrag={false} />, + null} enableEntityDrag={false} />, ); const svg = makeSvgStubs(container); @@ -163,7 +163,7 @@ describe('FloormapCanvas', () => { it('click on entity selects it via clickSelect', () => { const core = makeCore(); const { container } = render( - null} />, + null} />, ); const svg = makeSvgStubs(container); diff --git a/packages/adapters/react/src/__tests__/hooks.test.ts b/packages/adapters/react/src/__tests__/hooks.test.ts index 23ac24b..e489474 100644 --- a/packages/adapters/react/src/__tests__/hooks.test.ts +++ b/packages/adapters/react/src/__tests__/hooks.test.ts @@ -1,9 +1,9 @@ // @vitest-environment jsdom import { describe, it, expect } from 'vitest'; import { renderHook, act } from '@testing-library/react'; -import { createCore, createEmptyScene } from '@floormap-tools/core'; -import type { EntityId, LayerId } from '@floormap-tools/core'; -import { useFloormapCore, useSelection, useViewport } from '../index'; +import { createCore, createEmptyScene } from '@spatialkit/core'; +import type { EntityId, LayerId } from '@spatialkit/core'; +import { useSpatialCore, useSelection, useViewport } from '../index'; function makeCore() { return createCore({ @@ -12,10 +12,10 @@ function makeCore() { }); } -describe('useFloormapCore', () => { +describe('useSpatialCore', () => { it('returns a stable core instance across re-renders', () => { const { result, rerender } = renderHook(() => - useFloormapCore({ + useSpatialCore({ scene: createEmptyScene({ width: 100, height: 100 }, []), viewport: { zoom: 1, pan: { x: 0, y: 0 }, screenSize: { width: 800, height: 600 } }, }), @@ -28,7 +28,7 @@ describe('useFloormapCore', () => { it('accepts an initializer function called only once', () => { let calls = 0; const { result, rerender } = renderHook(() => - useFloormapCore(() => { + useSpatialCore(() => { calls++; return { scene: createEmptyScene({ width: 100, height: 100 }, []), diff --git a/packages/adapters/react/src/hooks/useCoreEvent.ts b/packages/adapters/react/src/hooks/useCoreEvent.ts index e423baa..b86a8ae 100644 --- a/packages/adapters/react/src/hooks/useCoreEvent.ts +++ b/packages/adapters/react/src/hooks/useCoreEvent.ts @@ -1,8 +1,8 @@ import { useEffect, useRef } from 'react'; -import type { FloormapCore } from '@floormap-tools/core'; +import type { SpatialCore } from '@spatialkit/core'; export function useCoreEvent( - core: FloormapCore, + core: SpatialCore, event: string, handler: (payload: T) => void, ): void { diff --git a/packages/adapters/react/src/hooks/useFloormapCore.ts b/packages/adapters/react/src/hooks/useFloormapCore.ts deleted file mode 100644 index 7150a07..0000000 --- a/packages/adapters/react/src/hooks/useFloormapCore.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { useRef } from 'react'; -import { createCore } from '@floormap-tools/core'; -import type { FloormapCore, Scene, Viewport } from '@floormap-tools/core'; - -export interface FloormapCoreOptions { - scene: Scene; - viewport: Viewport; -} - -export function useFloormapCore( - init: FloormapCoreOptions | (() => FloormapCoreOptions), -): FloormapCore { - const coreRef = useRef(null); - if (coreRef.current === null) { - const options = typeof init === 'function' ? init() : init; - coreRef.current = createCore(options); - } - return coreRef.current; -} diff --git a/packages/adapters/react/src/hooks/useSelection.ts b/packages/adapters/react/src/hooks/useSelection.ts index a448a4f..52a488a 100644 --- a/packages/adapters/react/src/hooks/useSelection.ts +++ b/packages/adapters/react/src/hooks/useSelection.ts @@ -1,7 +1,7 @@ import { useCallback, useRef, useSyncExternalStore } from 'react'; -import type { EntityId, FloormapCore } from '@floormap-tools/core'; +import type { EntityId, SpatialCore } from '@spatialkit/core'; -export function useSelection(core: FloormapCore): ReadonlySet { +export function useSelection(core: SpatialCore): ReadonlySet { const snapshotRef = useRef>(new Set(core.selection)); return useSyncExternalStore( diff --git a/packages/adapters/react/src/hooks/useSpatialCore.ts b/packages/adapters/react/src/hooks/useSpatialCore.ts new file mode 100644 index 0000000..674f66b --- /dev/null +++ b/packages/adapters/react/src/hooks/useSpatialCore.ts @@ -0,0 +1,19 @@ +import { useRef } from 'react'; +import { createCore } from '@spatialkit/core'; +import type { SpatialCore, Scene, Viewport } from '@spatialkit/core'; + +export interface SpatialCoreOptions { + scene: Scene; + viewport: Viewport; +} + +export function useSpatialCore( + init: SpatialCoreOptions | (() => SpatialCoreOptions), +): SpatialCore { + const coreRef = useRef(null); + if (coreRef.current === null) { + const options = typeof init === 'function' ? init() : init; + coreRef.current = createCore(options); + } + return coreRef.current; +} diff --git a/packages/adapters/react/src/hooks/useViewport.ts b/packages/adapters/react/src/hooks/useViewport.ts index fdae325..33c9f48 100644 --- a/packages/adapters/react/src/hooks/useViewport.ts +++ b/packages/adapters/react/src/hooks/useViewport.ts @@ -1,7 +1,7 @@ import { useCallback, useRef, useSyncExternalStore } from 'react'; -import type { FloormapCore, Viewport } from '@floormap-tools/core'; +import type { SpatialCore, Viewport } from '@spatialkit/core'; -export function useViewport(core: FloormapCore): Viewport { +export function useViewport(core: SpatialCore): Viewport { const snapshotRef = useRef({ ...core.viewport, pan: { ...core.viewport.pan } }); return useSyncExternalStore( diff --git a/packages/adapters/react/src/index.ts b/packages/adapters/react/src/index.ts index c865736..2a36248 100644 --- a/packages/adapters/react/src/index.ts +++ b/packages/adapters/react/src/index.ts @@ -1,8 +1,8 @@ -export { FloormapCanvas } from './FloormapCanvas'; -export type { FloormapCanvasProps } from './FloormapCanvas'; +export { SpatialCanvas } from './SpatialCanvas'; +export type { SpatialCanvasProps } from './SpatialCanvas'; -export { useFloormapCore } from './hooks/useFloormapCore'; -export type { FloormapCoreOptions } from './hooks/useFloormapCore'; +export { useSpatialCore } from './hooks/useSpatialCore'; +export type { SpatialCoreOptions } from './hooks/useSpatialCore'; export { useSelection } from './hooks/useSelection'; export { useViewport } from './hooks/useViewport'; diff --git a/packages/adapters/react/src/internal/GridLayer.tsx b/packages/adapters/react/src/internal/GridLayer.tsx index fe2d3fb..1d31d69 100644 --- a/packages/adapters/react/src/internal/GridLayer.tsx +++ b/packages/adapters/react/src/internal/GridLayer.tsx @@ -1,9 +1,9 @@ import { memo, useEffect, useRef } from 'react'; -import type { FloormapCore, Viewport } from '@floormap-tools/core'; +import type { SpatialCore, Viewport } from '@spatialkit/core'; import type { GridOptions } from '../types'; interface GridLayerProps { - core: FloormapCore; + core: SpatialCore; options: GridOptions; } @@ -20,7 +20,7 @@ export const GridLayer = memo(function GridLayer({ core, options }: GridLayerPro return core.on('viewport:change', paint); }, [core, options]); - return ; + return ; }); const NS = 'http://www.w3.org/2000/svg'; diff --git a/packages/adapters/react/src/internal/ResizeHandles.tsx b/packages/adapters/react/src/internal/ResizeHandles.tsx index 9b9020c..971458d 100644 --- a/packages/adapters/react/src/internal/ResizeHandles.tsx +++ b/packages/adapters/react/src/internal/ResizeHandles.tsx @@ -1,5 +1,5 @@ import { memo } from 'react'; -import type { Entity, EntityId } from '@floormap-tools/core'; +import type { Entity, EntityId } from '@spatialkit/core'; export type ResizeHandleStyle = { size?: number; @@ -45,7 +45,7 @@ export const ResizeHandles = memo(function ResizeHandles({ if (selected.length === 0) return null; return ( - + {selected.flatMap((entity) => DIRECTIONS.map((dir) => { const center = handleCenter(entity.bounds, dir); diff --git a/packages/adapters/react/src/internal/SelectionOverlay.tsx b/packages/adapters/react/src/internal/SelectionOverlay.tsx index 56d5de6..adaabdb 100644 --- a/packages/adapters/react/src/internal/SelectionOverlay.tsx +++ b/packages/adapters/react/src/internal/SelectionOverlay.tsx @@ -1,5 +1,5 @@ import { memo } from 'react'; -import type { Entity, EntityId } from '@floormap-tools/core'; +import type { Entity, EntityId } from '@spatialkit/core'; import type { SelectionOverlayStyle } from '../types'; interface SelectionOverlayProps { @@ -16,7 +16,7 @@ export const SelectionOverlay = memo(function SelectionOverlay({ const { stroke = '#2563eb', strokeWidth = 2, padding = 4, fill = 'none' } = style; return ( - + {entities .filter((e) => selection.has(e.id)) .map((entity) => { diff --git a/packages/adapters/react/src/internal/attachHandlers.ts b/packages/adapters/react/src/internal/attachHandlers.ts index 5a16b46..8f8a643 100644 --- a/packages/adapters/react/src/internal/attachHandlers.ts +++ b/packages/adapters/react/src/internal/attachHandlers.ts @@ -1,5 +1,5 @@ -import { snapToGrid, hitTestResizeHandles, applyResizeDelta } from '@floormap-tools/core'; -import type { FloormapCore, EntityId, Bounds, ResizeDirection } from '@floormap-tools/core'; +import { snapToGrid, hitTestResizeHandles, applyResizeDelta } from '@spatialkit/core'; +import type { SpatialCore, EntityId, Bounds, ResizeDirection } from '@spatialkit/core'; export type AttachHandlersOptions = { enableWheel: boolean; @@ -17,7 +17,7 @@ export type AttachHandlersOptions = { export function attachHandlers( svg: SVGSVGElement, - core: FloormapCore, + core: SpatialCore, options: AttachHandlersOptions, ) { const state = { diff --git a/packages/adapters/react/src/internal/useEntities.ts b/packages/adapters/react/src/internal/useEntities.ts index 7cf4ef0..d8e8e14 100644 --- a/packages/adapters/react/src/internal/useEntities.ts +++ b/packages/adapters/react/src/internal/useEntities.ts @@ -1,7 +1,7 @@ import { useCallback, useRef, useSyncExternalStore } from 'react'; -import type { Entity, FloormapCore } from '@floormap-tools/core'; +import type { Entity, SpatialCore } from '@spatialkit/core'; -export function useEntities(core: FloormapCore): Entity[] { +export function useEntities(core: SpatialCore): Entity[] { const snapshotRef = useRef([...core.scene.entities.values()]); return useSyncExternalStore( diff --git a/packages/adapters/react/src/internal/useViewportTransform.ts b/packages/adapters/react/src/internal/useViewportTransform.ts index e6e71e7..c886d71 100644 --- a/packages/adapters/react/src/internal/useViewportTransform.ts +++ b/packages/adapters/react/src/internal/useViewportTransform.ts @@ -1,8 +1,8 @@ import { type RefObject, useEffect } from 'react'; -import type { FloormapCore } from '@floormap-tools/core'; +import type { SpatialCore } from '@spatialkit/core'; export function useViewportTransform( - core: FloormapCore, + core: SpatialCore, ref: RefObject, ): void { useEffect(() => { diff --git a/packages/adapters/react/vitest.config.ts b/packages/adapters/react/vitest.config.ts index 105f732..6bb8ea9 100644 --- a/packages/adapters/react/vitest.config.ts +++ b/packages/adapters/react/vitest.config.ts @@ -7,7 +7,7 @@ const __dirname = fileURLToPath(new URL('.', import.meta.url)); export default defineConfig({ resolve: { alias: { - '@floormap-tools/core': resolve(__dirname, '../../core/src/index.ts'), + '@spatialkit/core': resolve(__dirname, '../../core/src/index.ts'), }, }, test: { diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index fe67230..3248d38 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,7 +1,7 @@ -# @floormap-tools/core +# @spatialkit/core ## 0.1.0 ### Minor Changes -- Initial release of @floormap-tools/core, @floormap-tools/svg, and @floormap-tools/react. +- Initial release of @spatialkit/core, @spatialkit/svg, and @spatialkit/react. diff --git a/packages/core/CLAUDE.md b/packages/core/CLAUDE.md index b3ab987..3aa5e4c 100644 --- a/packages/core/CLAUDE.md +++ b/packages/core/CLAUDE.md @@ -1,4 +1,4 @@ -# @floormap-tools/core — Claude Code Guide +# @spatialkit/core — Claude Code Guide Zero-dependency scene engine for 2D interactive editors. Manages entities, viewport transforms, event pub/sub, hit testing, and selection. Framework-agnostic — no DOM, no browser APIs anywhere in this package. @@ -6,10 +6,10 @@ Zero-dependency scene engine for 2D interactive editors. Manages entities, viewp ## Public API surface -Everything flows through `FloormapCore`, the object returned by `createCore()`. Renderers and adapters receive this object and subscribe to its events. +Everything flows through `SpatialCore`, the object returned by `createCore()`. Renderers and adapters receive this object and subscribe to its events. ```ts -import { createCore, createEmptyScene } from '@floormap-tools/core'; +import { createCore, createEmptyScene } from '@spatialkit/core'; const scene = createEmptyScene({ width: 2000, height: 1500 }, ['floor' as LayerId, 'furniture' as LayerId]); const core = createCore({ @@ -18,10 +18,10 @@ const core = createCore({ }); ``` -### `FloormapCore` interface +### `SpatialCore` interface ```ts -interface FloormapCore { +interface SpatialCore { scene: Scene; // mutable — entities live here viewport: Viewport; // mutable — zoom/pan state selection: Set; // mutable — current selection @@ -247,7 +247,7 @@ Use `vi.fn()` to spy on event bus handlers. Always call `core.on(event, handler) 1. New types → `types.ts` 2. Pure logic → appropriate module (`store`, `viewport`, `selection`, `picking`, `utils`) -3. Wire into `FloormapCore` interface + `createCore` factory in `core.ts` +3. Wire into `SpatialCore` interface + `createCore` factory in `core.ts` 4. Export from `index.ts` 5. Tests in `src/__tests__/.test.ts` diff --git a/packages/core/README.md b/packages/core/README.md index b4a3687..06bb9e4 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -1,20 +1,20 @@ -# @floormap-tools/core +# @spatialkit/core Scene engine for interactive 2D editors. Manages entities, viewport transforms, event pub/sub, hit testing, and selection. Framework-agnostic — no DOM, no browser APIs. ## Installation ```bash -npm install @floormap-tools/core +npm install @spatialkit/core # or -pnpm add @floormap-tools/core +pnpm add @spatialkit/core ``` ## Quick start ```ts -import { createCore, createEmptyScene } from '@floormap-tools/core'; -import type { EntityId, LayerId } from '@floormap-tools/core'; +import { createCore, createEmptyScene } from '@spatialkit/core'; +import type { EntityId, LayerId } from '@spatialkit/core'; const scene = createEmptyScene( { width: 2000, height: 1500 }, @@ -83,7 +83,7 @@ const core = createCore({ --- -### `FloormapCore` interface +### `SpatialCore` interface The object returned by `createCore`. Pass this to renderers and adapters. @@ -283,7 +283,7 @@ type SelectionMode = 'replace' | 'add' | 'toggle'; These are exported for use in renderers and application code: ```ts -import { createEmptyScene, clamp, containsPointAABB, expandBounds, unionBounds } from '@floormap-tools/core'; +import { createEmptyScene, clamp, containsPointAABB, expandBounds, unionBounds } from '@spatialkit/core'; clamp(value, min, max) // numeric clamp containsPointAABB(bounds, point) // true if point is inside bounds (inclusive) @@ -298,7 +298,7 @@ unionBounds(a, b) // smallest AABB containing both bounds ### Creating branded ids ```ts -import type { EntityId, LayerId } from '@floormap-tools/core'; +import type { EntityId, LayerId } from '@spatialkit/core'; // Option 1: cast at point of use core.add({ id: 'table-1' as EntityId, layer: 'furniture' as LayerId, ... }); diff --git a/packages/core/package.json b/packages/core/package.json index c5d3865..f7e695d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,5 +1,5 @@ { - "name": "@floormap-tools/core", + "name": "@spatialkit/core", "version": "0.1.0", "private": false, "type": "module", @@ -18,7 +18,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/floormap-tools/floormap.git" + "url": "https://github.com/spatialkit/spatial.git" }, "scripts": { "test": "vitest run", diff --git a/packages/core/src/core.ts b/packages/core/src/core.ts index 3a6d748..6b6a115 100644 --- a/packages/core/src/core.ts +++ b/packages/core/src/core.ts @@ -5,7 +5,7 @@ import { addEntity, getEntity, removeEntity, sceneBounds, updateEntity } from ". import type { Bounds, Entity, EntityId, Scene, SelectionMode, Vec2, Viewport } from "./types"; import { fitToBounds, panBy, screenToWorld, setZoomAt, worldToScreen } from "./viewport"; -export interface FloormapCore { +export interface SpatialCore { scene: Scene; viewport: Viewport; selection: Set; @@ -43,13 +43,13 @@ type CreateCoreProps = { viewport: Viewport; }; -export function createCore(props: CreateCoreProps): FloormapCore { +export function createCore(props: CreateCoreProps): SpatialCore { const bus = new EventBus(); const selection = new Set(); const scene = props.scene; const viewport = { ...props.viewport }; - const api: FloormapCore = { + const api: SpatialCore = { scene, viewport, selection, diff --git a/packages/examples/js-vanilla/CHANGELOG.md b/packages/examples/js-vanilla/CHANGELOG.md index e036a91..3827f69 100644 --- a/packages/examples/js-vanilla/CHANGELOG.md +++ b/packages/examples/js-vanilla/CHANGELOG.md @@ -1,9 +1,9 @@ -# @floormap-tools/example-js-vanilla +# @spatialkit/example-js-vanilla ## 0.0.1 ### Patch Changes - Updated dependencies - - @floormap-tools/core@0.1.0 - - @floormap-tools/svg@1.0.0 + - @spatialkit/core@0.1.0 + - @spatialkit/svg@1.0.0 diff --git a/packages/examples/js-vanilla/index.html b/packages/examples/js-vanilla/index.html index 3d0d28a..b7522c7 100644 --- a/packages/examples/js-vanilla/index.html +++ b/packages/examples/js-vanilla/index.html @@ -3,13 +3,13 @@ - Floormap — Vanilla TS + Spatial — Vanilla TS
- + Office Floor Plan
diff --git a/packages/examples/js-vanilla/package.json b/packages/examples/js-vanilla/package.json index 87b5009..cd4eaaf 100644 --- a/packages/examples/js-vanilla/package.json +++ b/packages/examples/js-vanilla/package.json @@ -1,5 +1,5 @@ { - "name": "@floormap-tools/example-js-vanilla", + "name": "@spatialkit/example-js-vanilla", "version": "0.0.1", "private": true, "scripts": { @@ -8,8 +8,8 @@ "preview": "vite preview" }, "dependencies": { - "@floormap-tools/core": "workspace:*", - "@floormap-tools/svg": "workspace:*" + "@spatialkit/core": "workspace:*", + "@spatialkit/svg": "workspace:*" }, "devDependencies": { "vite": "^6.0.0" diff --git a/packages/examples/js-vanilla/src/main.ts b/packages/examples/js-vanilla/src/main.ts index 56bb4e3..9f12b0f 100644 --- a/packages/examples/js-vanilla/src/main.ts +++ b/packages/examples/js-vanilla/src/main.ts @@ -1,9 +1,9 @@ -import { createCore, createEmptyScene, hitTestResizeHandles } from '@floormap-tools/core'; +import { createCore, createEmptyScene, hitTestResizeHandles } from '@spatialkit/core'; document.addEventListener('gesturestart', (e) => e.preventDefault(), { passive: false }); document.addEventListener('gesturechange', (e) => e.preventDefault(), { passive: false }); -import { mountSvgRenderer } from '@floormap-tools/svg'; -import type { EntityId, LayerId } from '@floormap-tools/core'; +import { mountSvgRenderer } from '@spatialkit/svg'; +import type { EntityId, LayerId } from '@spatialkit/core'; // ── Types ───────────────────────────────────────────────────────────────────── @@ -79,7 +79,7 @@ for (let row = 0; row < DESK_GRID.rows; row++) { const NS = 'http://www.w3.org/2000/svg'; -import type { Entity } from '@floormap-tools/core'; +import type { Entity } from '@spatialkit/core'; function drawEntity(entity: Entity, { g, selected }: { g: SVGGElement; selected: boolean }) { const data = entity.data as EntityData; diff --git a/packages/examples/js-vanilla/vite.config.ts b/packages/examples/js-vanilla/vite.config.ts index a0ef62c..2885f4b 100644 --- a/packages/examples/js-vanilla/vite.config.ts +++ b/packages/examples/js-vanilla/vite.config.ts @@ -8,8 +8,8 @@ export default defineConfig(({ command }) => ({ base: command === 'build' ? '/vanilla/' : '/', resolve: { alias: { - '@floormap-tools/core': resolve(__dirname, '../../core/src/index.ts'), - '@floormap-tools/svg': resolve(__dirname, '../../svg/src/index.ts'), + '@spatialkit/core': resolve(__dirname, '../../core/src/index.ts'), + '@spatialkit/svg': resolve(__dirname, '../../svg/src/index.ts'), }, }, })); diff --git a/packages/examples/react/CHANGELOG.md b/packages/examples/react/CHANGELOG.md index 770b901..6a922bd 100644 --- a/packages/examples/react/CHANGELOG.md +++ b/packages/examples/react/CHANGELOG.md @@ -1,9 +1,9 @@ -# @floormap-tools/example-react +# @spatialkit/example-react ## 0.0.1 ### Patch Changes - Updated dependencies - - @floormap-tools/core@0.1.0 - - @floormap-tools/react@1.0.0 + - @spatialkit/core@0.1.0 + - @spatialkit/react@1.0.0 diff --git a/packages/examples/react/index.html b/packages/examples/react/index.html index be1ed06..832e99f 100644 --- a/packages/examples/react/index.html +++ b/packages/examples/react/index.html @@ -3,7 +3,7 @@ - Floormap — React + Spatial — React diff --git a/packages/examples/react/package.json b/packages/examples/react/package.json index f6ee188..88f9015 100644 --- a/packages/examples/react/package.json +++ b/packages/examples/react/package.json @@ -1,5 +1,5 @@ { - "name": "@floormap-tools/example-react", + "name": "@spatialkit/example-react", "version": "0.0.1", "private": true, "scripts": { @@ -8,8 +8,8 @@ "preview": "vite preview" }, "dependencies": { - "@floormap-tools/core": "workspace:*", - "@floormap-tools/react": "workspace:*", + "@spatialkit/core": "workspace:*", + "@spatialkit/react": "workspace:*", "react": "^18.0.0", "react-dom": "^18.0.0" }, diff --git a/packages/examples/react/src/App.tsx b/packages/examples/react/src/App.tsx index 6c52c6b..e7b2196 100644 --- a/packages/examples/react/src/App.tsx +++ b/packages/examples/react/src/App.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useRef, useState } from 'react'; -import { FloormapCanvas, useFloormapCore, useSelection, useViewport } from '@floormap-tools/react'; -import { addEntity, createEmptyScene, hitTestResizeHandles } from '@floormap-tools/core'; -import type { Entity, EntityId, LayerId } from '@floormap-tools/core'; +import { SpatialCanvas, useSpatialCore, useSelection, useViewport } from '@spatialkit/react'; +import { addEntity, createEmptyScene, hitTestResizeHandles } from '@spatialkit/core'; +import type { Entity, EntityId, LayerId } from '@spatialkit/core'; // ── Types ────────────────────────────────────────────────────────────────────── @@ -109,7 +109,7 @@ function DeskShape({ entity, selected }: { entity: Entity; selected: boolean }) // ── App ──────────────────────────────────────────────────────────────────────── export function App() { - const core = useFloormapCore(() => ({ + const core = useSpatialCore(() => ({ scene: initialScene, viewport: { zoom: 1, pan: { x: 0, y: 0 }, screenSize: { width: 800, height: 600 } }, })); @@ -192,7 +192,7 @@ export function App() { return (
- Floormap + Spatial Office Floor Plan — React
@@ -230,7 +230,7 @@ export function App() { onPointerMove={handleCanvasPointerMove} onPointerLeave={() => setHoverCursor('')} > - ({ base: command === 'build' ? '/react/' : '/', resolve: { alias: { - '@floormap-tools/core': resolve(__dirname, '../../core/src/index.ts'), - '@floormap-tools/react': resolve(__dirname, '../../adapters/react/src/index.ts'), + '@spatialkit/core': resolve(__dirname, '../../core/src/index.ts'), + '@spatialkit/react': resolve(__dirname, '../../adapters/react/src/index.ts'), }, }, })); diff --git a/packages/svg/CHANGELOG.md b/packages/svg/CHANGELOG.md index feaabcf..b0c30bb 100644 --- a/packages/svg/CHANGELOG.md +++ b/packages/svg/CHANGELOG.md @@ -1,12 +1,12 @@ -# @floormap-tools/svg +# @spatialkit/svg ## 0.1.0 ### Minor Changes -- Initial release of @floormap-tools/core, @floormap-tools/svg, and @floormap-tools/react. +- Initial release of @spatialkit/core, @spatialkit/svg, and @spatialkit/react. ### Patch Changes - Updated dependencies - - @floormap-tools/core@0.1.0 + - @spatialkit/core@0.1.0 diff --git a/packages/svg/CLAUDE.md b/packages/svg/CLAUDE.md index 362c525..73deb29 100644 --- a/packages/svg/CLAUDE.md +++ b/packages/svg/CLAUDE.md @@ -1,13 +1,13 @@ -# @floormap-tools/svg — Claude Code Guide +# @spatialkit/svg — Claude Code Guide -SVG renderer and DOM event handlers for `@floormap-tools/core`. Subscribes to core events and paints the scene into an `` element. No production dependencies. +SVG renderer and DOM event handlers for `@spatialkit/core`. Subscribes to core events and paints the scene into an `` element. No production dependencies. --- ## Entry point ```ts -import { mountSvgRenderer } from '@floormap-tools/svg'; +import { mountSvgRenderer } from '@spatialkit/svg'; const renderer = mountSvgRenderer(core, { mount: svgElement, @@ -28,18 +28,18 @@ renderer.destroy(); // unsubscribe from core, remove DOM groups ``` - ← CSS matrix transform applied here - ← background grid lines - ← one per layer - - + ← CSS matrix transform applied here + ← background grid lines + ← one per layer + + ... - ← selection overlay rects - ← reserved for future use - ← optional, only if `defs` callback provided + ← selection overlay rects + ← reserved for future use + ← optional, only if `defs` callback provided ``` -**Transform matrix on ``:** +**Transform matrix on ``:** ``` matrix(zoom 0 0 zoom -pan.x*zoom -pan.y*zoom) ``` @@ -84,7 +84,7 @@ drawEntity: (entity: Entity, ctx: { g: SVGGElement; selected: boolean }) => void | Core event | What the renderer does | |-----------|------------------------| | `"viewport:change"` | Updates viewport transform matrix + repaints grid | -| `"entities:changed"` | Repaints `` (calls `drawEntity` for all entities) | +| `"entities:changed"` | Repaints `` (calls `drawEntity` for all entities) | | `"selection:change"` | Repaints objects (to pass updated `selected` flag) + repaints selection overlay | `rerender()` forces all three: objects, selection overlay, and grid. @@ -178,7 +178,7 @@ Clicking empty space in `'replace'` mode clears selection. In `'add'`/`'toggle'` 3. Subscribes to `"viewport:change"`, `"entities:changed"`, `"selection:change"` 4. Calls `attachHandlers` -**`paint()` helper** — pre-groups entities by `LayerId` in a single O(n) pass, then iterates `scene.layers` to create `` elements in z-order. Avoids the O(layers × entities) nested loop. +**`paint()` helper** — pre-groups entities by `LayerId` in a single O(n) pass, then iterates `scene.layers` to create `` elements in z-order. Avoids the O(layers × entities) nested loop. --- @@ -190,7 +190,7 @@ All SVG tests require jsdom. Use the `// @vitest-environment jsdom` pragma at th // @vitest-environment jsdom import { describe, it, expect, vi, beforeEach } from 'vitest'; import { mountSvgRenderer } from '../renderer'; -import { createCore } from '@floormap-tools/core'; +import { createCore } from '@spatialkit/core'; ``` **Required stubs** (jsdom doesn't implement these): @@ -223,11 +223,11 @@ At zoom=1 and pan=0, screen coords equal world coords. Entity at `bounds { x: 10 **Querying groups:** ```ts -svg.querySelector("g[data-fm='viewport']") -svg.querySelector("g[data-fm='objects']") -svg.querySelector("g[data-fm='grid']") -svg.querySelector("g[data-fm='selection']") -svg.querySelector("[data-fm-layer='myLayer']") +svg.querySelector("g[data-spatial='viewport']") +svg.querySelector("g[data-spatial='objects']") +svg.querySelector("g[data-spatial='grid']") +svg.querySelector("g[data-spatial='selection']") +svg.querySelector("[data-spatial-layer='myLayer']") ``` --- diff --git a/packages/svg/README.md b/packages/svg/README.md index 6a00c93..e938f0f 100644 --- a/packages/svg/README.md +++ b/packages/svg/README.md @@ -1,21 +1,21 @@ -# @floormap-tools/svg +# @spatialkit/svg -SVG renderer and DOM event handlers for [`@floormap-tools/core`](../core). Mounts into any `` element, subscribes to core events, and paints the scene automatically. Includes wheel zoom, drag-to-pan, pinch-to-zoom, click-to-select, a background grid, and a selection highlight overlay. +SVG renderer and DOM event handlers for [`@spatialkit/core`](../core). Mounts into any `` element, subscribes to core events, and paints the scene automatically. Includes wheel zoom, drag-to-pan, pinch-to-zoom, click-to-select, a background grid, and a selection highlight overlay. ## Installation ```bash -npm install @floormap-tools/core @floormap-tools/svg +npm install @spatialkit/core @spatialkit/svg # or -pnpm add @floormap-tools/core @floormap-tools/svg +pnpm add @spatialkit/core @spatialkit/svg ``` ## Quick start ```ts -import { createCore, createEmptyScene } from '@floormap-tools/core'; -import { mountSvgRenderer } from '@floormap-tools/svg'; -import type { EntityId, LayerId } from '@floormap-tools/core'; +import { createCore, createEmptyScene } from '@spatialkit/core'; +import { mountSvgRenderer } from '@spatialkit/svg'; +import type { EntityId, LayerId } from '@spatialkit/core'; const core = createCore({ scene: createEmptyScene({ width: 2000, height: 1500 }, ['layer' as LayerId]), @@ -233,15 +233,15 @@ The renderer builds and manages this group hierarchy inside your ``: ``` - ← viewport transform matrix applied here - ← background grid lines - ← entity groups, one per layer - - + ← viewport transform matrix applied here + ← background grid lines + ← entity groups, one per layer + + ... - ← selection overlay rects - ← reserved - ← present only if defs option is provided + ← selection overlay rects + ← reserved + ← present only if defs option is provided ``` **Viewport transform:** @@ -249,7 +249,7 @@ The renderer builds and manages this group hierarchy inside your ``: matrix(zoom 0 0 zoom -pan.x×zoom -pan.y×zoom) ``` -All children of `` — including grid and selection overlay — share the same camera transform. Entity coordinates in `drawEntity` can be used directly as world coordinates. +All children of `` — including grid and selection overlay — share the same camera transform. Entity coordinates in `drawEntity` can be used directly as world coordinates. The hierarchy is created once and is **idempotent** — calling `mountSvgRenderer` again on the same SVG (e.g. after a hot reload) will reuse the existing groups. diff --git a/packages/svg/package.json b/packages/svg/package.json index 9aa33e3..aac8951 100644 --- a/packages/svg/package.json +++ b/packages/svg/package.json @@ -1,5 +1,5 @@ { - "name": "@floormap-tools/svg", + "name": "@spatialkit/svg", "version": "0.1.0", "private": false, "type": "module", @@ -18,7 +18,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/floormap-tools/floormap.git" + "url": "https://github.com/spatialkit/spatial.git" }, "scripts": { "build": "tsup src/index.ts --dts --format esm,cjs --clean", @@ -26,11 +26,11 @@ "test:watch": "vitest" }, "devDependencies": { - "@floormap-tools/core": "workspace:*", + "@spatialkit/core": "workspace:*", "jsdom": "^27.2.0", "vitest": "^2.0.0" }, "peerDependencies": { - "@floormap-tools/core": "0.x" + "@spatialkit/core": "0.x" } } \ No newline at end of file diff --git a/packages/svg/src/__tests__/destroy.test.ts b/packages/svg/src/__tests__/destroy.test.ts index 401f516..af1d116 100644 --- a/packages/svg/src/__tests__/destroy.test.ts +++ b/packages/svg/src/__tests__/destroy.test.ts @@ -1,7 +1,7 @@ // @vitest-environment jsdom import { describe, it, expect, beforeEach } from 'vitest'; import { mountSvgRenderer } from '../renderer'; -import { createCore } from '@floormap-tools/core'; +import { createCore } from '@spatialkit/core'; function makeSetup() { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as SVGSVGElement; @@ -27,9 +27,9 @@ describe('Svg renderer - destroy DOM cleanup', () => { it('destroy() removes viewport group by default', () => { const renderer = mountSvgRenderer(core, { mount: svg, drawEntity: () => {} }); - expect(svg.querySelector("g[data-fm='viewport']")).toBeTruthy(); + expect(svg.querySelector("g[data-spatial='viewport']")).toBeTruthy(); renderer.destroy(); - expect(svg.querySelector("g[data-fm='viewport']")).toBeNull(); + expect(svg.querySelector("g[data-spatial='viewport']")).toBeNull(); }); it('destroy() removes defs element when present', () => { @@ -42,9 +42,9 @@ describe('Svg renderer - destroy DOM cleanup', () => { }, }); - expect(svg.querySelector("defs[data-fm='defs']")).toBeTruthy(); + expect(svg.querySelector("defs[data-spatial='defs']")).toBeTruthy(); renderer.destroy(); - expect(svg.querySelector("defs[data-fm='defs']")).toBeNull(); + expect(svg.querySelector("defs[data-spatial='defs']")).toBeNull(); }); it('destroy() with clearOnDestroy: false preserves DOM', () => { @@ -55,7 +55,7 @@ describe('Svg renderer - destroy DOM cleanup', () => { }); renderer.destroy(); - expect(svg.querySelector("g[data-fm='viewport']")).toBeTruthy(); + expect(svg.querySelector("g[data-spatial='viewport']")).toBeTruthy(); }); it('destroy() unsubscribes from core events (no repaint after destroy)', () => { diff --git a/packages/svg/src/__tests__/grid.test.ts b/packages/svg/src/__tests__/grid.test.ts index 003745e..c40947b 100644 --- a/packages/svg/src/__tests__/grid.test.ts +++ b/packages/svg/src/__tests__/grid.test.ts @@ -1,7 +1,7 @@ // @vitest-environment jsdom import { describe, it, expect, beforeEach } from 'vitest'; import { mountSvgRenderer } from '../renderer'; -import { createCore } from '@floormap-tools/core'; +import { createCore } from '@spatialkit/core'; function makeSetup() { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as SVGSVGElement; @@ -17,7 +17,7 @@ function makeSetup() { } function getGridG(svg: SVGSVGElement) { - return svg.querySelector("g[data-fm='grid']")!; + return svg.querySelector("g[data-spatial='grid']")!; } describe('Svg renderer - grid', () => { diff --git a/packages/svg/src/__tests__/handlers.test.ts b/packages/svg/src/__tests__/handlers.test.ts index 102f93a..6366829 100644 --- a/packages/svg/src/__tests__/handlers.test.ts +++ b/packages/svg/src/__tests__/handlers.test.ts @@ -1,7 +1,7 @@ // @vitest-environment jsdom import { describe, it, expect, beforeEach } from 'vitest'; import { mountSvgRenderer } from '../renderer'; -import { createCore } from '@floormap-tools/core'; +import { createCore } from '@spatialkit/core'; function makesvg() { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as SVGSVGElement; diff --git a/packages/svg/src/__tests__/pinch.test.ts b/packages/svg/src/__tests__/pinch.test.ts index 7b8f997..24ac316 100644 --- a/packages/svg/src/__tests__/pinch.test.ts +++ b/packages/svg/src/__tests__/pinch.test.ts @@ -1,7 +1,7 @@ // @vitest-environment jsdom import { describe, it, expect, beforeEach } from 'vitest'; import { mountSvgRenderer } from '../renderer'; -import { createCore } from '@floormap-tools/core'; +import { createCore } from '@spatialkit/core'; function makeSetup() { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as SVGSVGElement; diff --git a/packages/svg/src/__tests__/renderer.test.ts b/packages/svg/src/__tests__/renderer.test.ts index 9c80911..009e01f 100644 --- a/packages/svg/src/__tests__/renderer.test.ts +++ b/packages/svg/src/__tests__/renderer.test.ts @@ -1,7 +1,7 @@ // @vitest-environment jsdom import { describe, it, expect, vi, beforeEach } from 'vitest'; import { mountSvgRenderer } from '../renderer'; -import { createCore } from '@floormap-tools/core'; +import { createCore } from '@spatialkit/core'; function stubRect(svg: SVGSVGElement) { (svg as any).getBoundingClientRect = () => ({ @@ -75,8 +75,8 @@ describe('Svg renderer (smoke)', () => { }, }); - expect(svg.querySelector("g[data-fm='viewport']")).toBeTruthy(); - expect(svg.querySelector("g[data-fm='objects']")).toBeTruthy(); + expect(svg.querySelector("g[data-spatial='viewport']")).toBeTruthy(); + expect(svg.querySelector("g[data-spatial='objects']")).toBeTruthy(); expect(svg.querySelector('circle')).toBeTruthy(); @@ -122,7 +122,7 @@ describe('Svg renderer - event-driven repaints', () => { const core = makeCore(); mountSvgRenderer(core, { mount: svg, drawEntity: () => {} }); - const viewportG = svg.querySelector("g[data-fm='viewport']")!; + const viewportG = svg.querySelector("g[data-spatial='viewport']")!; const before = viewportG.getAttribute('transform'); core.zoomAt({ x: 400, y: 300 }, 0.5); diff --git a/packages/svg/src/__tests__/resize-handles.test.ts b/packages/svg/src/__tests__/resize-handles.test.ts index e5e52ba..3d311c1 100644 --- a/packages/svg/src/__tests__/resize-handles.test.ts +++ b/packages/svg/src/__tests__/resize-handles.test.ts @@ -1,7 +1,7 @@ // @vitest-environment jsdom import { describe, it, expect, beforeEach } from 'vitest'; import { mountSvgRenderer } from '../renderer'; -import { createCore } from '@floormap-tools/core'; +import { createCore } from '@spatialkit/core'; function makeSetup() { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as SVGSVGElement; @@ -35,7 +35,7 @@ function makeSetup() { } function getOverlaysG(svg: SVGSVGElement) { - return svg.querySelector("g[data-fm='overlays']")!; + return svg.querySelector("g[data-spatial='overlays']")!; } describe('paintResizeHandles', () => { diff --git a/packages/svg/src/__tests__/selection-overlay.test.ts b/packages/svg/src/__tests__/selection-overlay.test.ts index 30d0016..c55e468 100644 --- a/packages/svg/src/__tests__/selection-overlay.test.ts +++ b/packages/svg/src/__tests__/selection-overlay.test.ts @@ -1,7 +1,7 @@ // @vitest-environment jsdom import { describe, it, expect, beforeEach } from 'vitest'; import { mountSvgRenderer } from '../renderer'; -import { createCore } from '@floormap-tools/core'; +import { createCore } from '@spatialkit/core'; function makeSetup() { const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as SVGSVGElement; @@ -28,7 +28,7 @@ function makeSetup() { } function getSelectionG(svg: SVGSVGElement) { - return svg.querySelector("g[data-fm='selection']")!; + return svg.querySelector("g[data-spatial='selection']")!; } describe('Svg renderer - selection overlay', () => { diff --git a/packages/svg/src/__tests__/wheel.test.ts b/packages/svg/src/__tests__/wheel.test.ts index 81f5dc4..84134b0 100644 --- a/packages/svg/src/__tests__/wheel.test.ts +++ b/packages/svg/src/__tests__/wheel.test.ts @@ -1,7 +1,7 @@ // @vitest-environment jsdom import { describe, it, expect, beforeEach } from 'vitest'; import { mountSvgRenderer } from '../renderer'; -import { createCore } from '@floormap-tools/core'; +import { createCore } from '@spatialkit/core'; function stubRect(svg: SVGSVGElement) { (svg as any).getBoundingClientRect = () => ({ diff --git a/packages/svg/src/dom.ts b/packages/svg/src/dom.ts index 2c620ee..9764825 100644 --- a/packages/svg/src/dom.ts +++ b/packages/svg/src/dom.ts @@ -3,21 +3,21 @@ const NS = 'http://www.w3.org/2000/svg'; export function setupSvgRoot(svg: SVGSVGElement, defs?: (defs: SVGDefsElement) => void) { svg.setAttribute('xmlns', NS); - if (!svg.querySelector("g[data-fm='viewport']")) { + if (!svg.querySelector("g[data-spatial='viewport']")) { const viewportG = document.createElementNS(NS, 'g'); - viewportG.setAttribute('data-fm', 'viewport'); + viewportG.setAttribute('data-spatial', 'viewport'); const gridG = document.createElementNS(NS, 'g'); - gridG.setAttribute('data-fm', 'grid'); + gridG.setAttribute('data-spatial', 'grid'); const objectsG = document.createElementNS(NS, 'g'); - objectsG.setAttribute('data-fm', 'objects'); + objectsG.setAttribute('data-spatial', 'objects'); const selectionG = document.createElementNS(NS, 'g'); - selectionG.setAttribute('data-fm', 'selection'); + selectionG.setAttribute('data-spatial', 'selection'); const overlaysG = document.createElementNS(NS, 'g'); - overlaysG.setAttribute('data-fm', 'overlays'); + overlaysG.setAttribute('data-spatial', 'overlays'); viewportG.appendChild(gridG); viewportG.appendChild(objectsG); @@ -26,23 +26,23 @@ export function setupSvgRoot(svg: SVGSVGElement, defs?: (defs: SVGDefsElement) = svg.appendChild(viewportG); } - if (defs && !svg.querySelector("defs[data-fm='defs']")) { + if (defs && !svg.querySelector("defs[data-spatial='defs']")) { const defsElement = document.createElementNS(NS, 'defs') as SVGDefsElement; - defsElement.setAttribute('data-fm', 'defs'); + defsElement.setAttribute('data-spatial', 'defs'); svg.appendChild(defsElement); defs(defsElement); } } export function getGroups(svg: SVGSVGElement) { - const viewportG = svg.querySelector("g[data-fm='viewport']")!; + const viewportG = svg.querySelector("g[data-spatial='viewport']")!; return { viewportG: viewportG, - gridG: viewportG.querySelector("g[data-fm='grid']")!, - objectsG: viewportG.querySelector("g[data-fm='objects']")!, - selectionG: viewportG.querySelector("g[data-fm='selection']")!, - overlaysG: viewportG.querySelector("g[data-fm='overlays']")!, + gridG: viewportG.querySelector("g[data-spatial='grid']")!, + objectsG: viewportG.querySelector("g[data-spatial='objects']")!, + selectionG: viewportG.querySelector("g[data-spatial='selection']")!, + overlaysG: viewportG.querySelector("g[data-spatial='overlays']")!, }; } diff --git a/packages/svg/src/grid.ts b/packages/svg/src/grid.ts index ee56e4e..ef21a33 100644 --- a/packages/svg/src/grid.ts +++ b/packages/svg/src/grid.ts @@ -1,4 +1,4 @@ -import type { FloormapCore } from '@floormap-tools/core'; +import type { SpatialCore } from '@spatialkit/core'; import { clearChildren } from './dom'; export type GridOptions = { @@ -9,7 +9,7 @@ export type GridOptions = { const NS = 'http://www.w3.org/2000/svg'; -export function paintGrid(core: FloormapCore, gridG: SVGGElement, options: GridOptions = {}): void { +export function paintGrid(core: SpatialCore, gridG: SVGGElement, options: GridOptions = {}): void { const { size = 50, stroke = '#e5e7eb', strokeWidth = 1 } = options; const { zoom, pan, screenSize } = core.viewport; diff --git a/packages/svg/src/handlers.ts b/packages/svg/src/handlers.ts index 9923084..240b9f6 100644 --- a/packages/svg/src/handlers.ts +++ b/packages/svg/src/handlers.ts @@ -1,5 +1,5 @@ -import { FloormapCore, EntityId, snapToGrid, hitTestResizeHandles, applyResizeDelta } from '@floormap-tools/core'; -import type { Bounds, ResizeDirection } from '@floormap-tools/core'; +import { SpatialCore, EntityId, snapToGrid, hitTestResizeHandles, applyResizeDelta } from '@spatialkit/core'; +import type { Bounds, ResizeDirection } from '@spatialkit/core'; type AttachHandlersOptions = { enableWheel: boolean; @@ -17,7 +17,7 @@ type AttachHandlersOptions = { export function attachHandlers( svg: SVGSVGElement, - core: FloormapCore, + core: SpatialCore, options: AttachHandlersOptions, ) { const state = { diff --git a/packages/svg/src/index.ts b/packages/svg/src/index.ts index 34ed5ea..1d318c2 100644 --- a/packages/svg/src/index.ts +++ b/packages/svg/src/index.ts @@ -1,4 +1,4 @@ -import type { Entity, EntityId } from '@floormap-tools/core'; +import type { Entity, EntityId } from '@spatialkit/core'; import type { SelectionOverlayStyle } from './selection-overlay'; import type { GridOptions } from './grid'; diff --git a/packages/svg/src/renderer.ts b/packages/svg/src/renderer.ts index 453e6ff..3bc50dd 100644 --- a/packages/svg/src/renderer.ts +++ b/packages/svg/src/renderer.ts @@ -1,4 +1,4 @@ -import type { FloormapCore, Entity, LayerId } from '@floormap-tools/core'; +import type { SpatialCore, Entity, LayerId } from '@spatialkit/core'; import type { SvgRenderer, SvgRendererOptions } from './index'; import { setupSvgRoot, getGroups, clearChildren } from './dom'; import { attachHandlers } from './handlers'; @@ -6,7 +6,7 @@ import { paintSelectionOverlay } from './selection-overlay'; import { paintResizeHandles } from './resize-handles'; import { paintGrid } from './grid'; -export function mountSvgRenderer(core: FloormapCore, opts: SvgRendererOptions): SvgRenderer { +export function mountSvgRenderer(core: SpatialCore, opts: SvgRendererOptions): SvgRenderer { const { mount, drawEntity, @@ -86,13 +86,13 @@ export function mountSvgRenderer(core: FloormapCore, opts: SvgRendererOptions): detach(); if (clearOnDestroy) { groups.viewportG.remove(); - mount.querySelector("defs[data-fm='defs']")?.remove(); + mount.querySelector("defs[data-spatial='defs']")?.remove(); } }, }; } -function applyViewportTransform(core: FloormapCore, viewportG: SVGGElement) { +function applyViewportTransform(core: SpatialCore, viewportG: SVGGElement) { const { zoom, pan } = core.viewport; const a = zoom, @@ -107,7 +107,7 @@ function applyViewportTransform(core: FloormapCore, viewportG: SVGGElement) { } function paint( - core: FloormapCore, + core: SpatialCore, objectsG: SVGGElement, drawEntity: (e: Entity, ctx: { g: SVGGElement; selected: boolean }) => void, ) { @@ -125,7 +125,7 @@ function paint( for (const layer of core.scene.layers) { const g = document.createElementNS('http://www.w3.org/2000/svg', 'g'); - g.setAttribute('data-fm-layer', String(layer)); + g.setAttribute('data-spatial-layer', String(layer)); objectsG.appendChild(g); for (const e of byLayer.get(layer) ?? []) { diff --git a/packages/svg/src/resize-handles.ts b/packages/svg/src/resize-handles.ts index b56425d..096447c 100644 --- a/packages/svg/src/resize-handles.ts +++ b/packages/svg/src/resize-handles.ts @@ -1,4 +1,4 @@ -import type { FloormapCore } from '@floormap-tools/core'; +import type { SpatialCore } from '@spatialkit/core'; import { clearChildren } from './dom'; export type ResizeHandleStyle = { @@ -29,7 +29,7 @@ function handleCenter(bounds: { x: number; y: number; width: number; height: num } export function paintResizeHandles( - core: FloormapCore, + core: SpatialCore, overlaysG: SVGGElement, style: ResizeHandleStyle = {}, ): void { diff --git a/packages/svg/src/selection-overlay.ts b/packages/svg/src/selection-overlay.ts index eb1674b..62a0cc4 100644 --- a/packages/svg/src/selection-overlay.ts +++ b/packages/svg/src/selection-overlay.ts @@ -1,4 +1,4 @@ -import type { FloormapCore } from '@floormap-tools/core'; +import type { SpatialCore } from '@spatialkit/core'; import { clearChildren } from './dom'; export type SelectionOverlayStyle = { @@ -11,7 +11,7 @@ export type SelectionOverlayStyle = { const NS = 'http://www.w3.org/2000/svg'; export function paintSelectionOverlay( - core: FloormapCore, + core: SpatialCore, selectionG: SVGGElement, style: SelectionOverlayStyle = {}, ): void { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fa12f33..ee4b51e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,7 +44,7 @@ importers: packages/adapters/react: devDependencies: - '@floormap-tools/core': + '@spatialkit/core': specifier: workspace:* version: link:../../core '@testing-library/react': @@ -77,10 +77,10 @@ importers: packages/examples/js-vanilla: dependencies: - '@floormap-tools/core': + '@spatialkit/core': specifier: workspace:* version: link:../../core - '@floormap-tools/svg': + '@spatialkit/svg': specifier: workspace:* version: link:../../svg devDependencies: @@ -90,10 +90,10 @@ importers: packages/examples/react: dependencies: - '@floormap-tools/core': + '@spatialkit/core': specifier: workspace:* version: link:../../core - '@floormap-tools/react': + '@spatialkit/react': specifier: workspace:* version: link:../../adapters/react react: @@ -118,7 +118,7 @@ importers: packages/svg: devDependencies: - '@floormap-tools/core': + '@spatialkit/core': specifier: workspace:* version: link:../core jsdom: diff --git a/tsconfig.json b/tsconfig.json index f5abe29..00ca57a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,9 +5,9 @@ "jsx": "react-jsx", "lib": ["ES2020", "DOM", "DOM.Iterable"], "paths": { - "@floormap-tools/core": ["./packages/core/src/index.ts"], - "@floormap-tools/svg": ["./packages/svg/src/index.ts"], - "@floormap-tools/react": ["./packages/adapters/react/src/index.ts"] + "@spatialkit/core": ["./packages/core/src/index.ts"], + "@spatialkit/svg": ["./packages/svg/src/index.ts"], + "@spatialkit/react": ["./packages/adapters/react/src/index.ts"] } }, "include": [ diff --git a/vitest.config.ts b/vitest.config.ts index 2300628..b61cefb 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -7,9 +7,9 @@ const __dirname = fileURLToPath(new URL('.', import.meta.url)); export default defineConfig({ resolve: { alias: { - '@floormap-tools/core': resolve(__dirname, 'packages/core/src/index.ts'), - '@floormap-tools/svg': resolve(__dirname, 'packages/svg/src/index.ts'), - '@floormap-tools/react': resolve(__dirname, 'packages/adapters/react/src/index.ts'), + '@spatialkit/core': resolve(__dirname, 'packages/core/src/index.ts'), + '@spatialkit/svg': resolve(__dirname, 'packages/svg/src/index.ts'), + '@spatialkit/react': resolve(__dirname, 'packages/adapters/react/src/index.ts'), }, }, test: { diff --git a/wrangler.toml b/wrangler.toml index 851c934..8c793e7 100644 --- a/wrangler.toml +++ b/wrangler.toml @@ -1,2 +1,2 @@ -name = "floormap" +name = "spatial" compatibility_date = "2026-06-08"