# PaperScape Technical Overview

Interactive visual canvas for Giraffe components inside OpenForum. This guide explains the runtime, bundled tools, and current maintenance backlog.

## 1. Overview

PaperScape runs under `/OpenForum/AddOn/PaperScape` and embeds a layered, animated canvas built on the Giraffe rendering toolkit. Authors can drag in scripted components, compose scenes, and interact with supporting tools without leaving the browser.

The controller initialises a full-screen canvas, touch/pen support, and an off-canvas side menu that surfaces actions and registered items. Modal editing forms reuse OpenForum binding to configure each object at runtime.

## 2. Runtime Features

- Drop ingestion: Handles `.giraffe.js`, `.giraffe.json`, and common images (JPG/PNG/SVG/WEBP), converting them into live canvas items.
- Item registry: Every added object is registered and listed in the "Items" menu for quick re-selection of edit dialogs.
- Panning & zoom: Background composites implement click-drag panning plus wheel-based zoom, keeping large diagrams manageable.
- Scene persistence: Save/Load actions serialise the active scene to `paperScape.json` alongside the addon and restore on reload.
- Modal editing: When available, `getForm()` markup renders inside `#editForm`; otherwise a default remove-only fragment is shown.

## 3. Architecture Highlights

- `page.js` bootstraps `PaperScape`, sets up the off-canvas menu, and binds Save/Load/Full Screen actions.
- `paperscape.js` (v0.0.5) orchestrates canvas layers, interaction state, drag-and-drop processing, select-box toggles, and item registration.
- Dependencies include `/OpenForum/Giraffe/giraffe.js`, Foundation's off-canvas shell, and OpenForum helpers such as `OpenForum.loadJSON`, `saveJSON`, and `IntraQ`.
- Extension hooks: `postRegisterItem`/`postRemoveItem`, `addFileProcessor`, selectors for view/control/overlay layers, and `setAllowDrag` / select-box controls.
- Drop handlers use a processor table so new file types can be registered dynamically.

## 4. Bundled Apps & Assets

- Measure tool: `Apps/measure.giraffe.js` plus `measure-recalibrate.html.fragment` to calibrate distances and units.
- Whiteboard: `Apps/white-board.giraffe.js` adds a simple drawing surface with selectable pens.
- IntraQ connector: `Apps/intraq-connector.giraffe.js` bridges to the developer console using the "PaperScape" queue.
- Giraffe editor launcher: `Apps/giraffe-editor.giraffe.js` opens the standalone Giraffe editor preview.
- Seed JSON: `seed.giraffe.json` and `dropable.giraffe.json` demonstrate serialised composites for import testing.

## 5. Developer Utilities

- `/OpenForum/AddOn/PaperScape/Developer` provides dual StandaloneEditor panes for JavaScript and JSON, streaming results through `OpenForum.IntraQ`.
- `Developer/test.js` shows runtime scripting against live canvas items (e.g., updating the IntraQ connector message).
- `Developer/vis-prog-intraq.js` illustrates integrating an external "Visual Programming" component via queue messages.
- Modal Dialog API: invoke `paperScape.dialog.show(options)` to pop a reusable overlay with PaperScape-styled controls.

    paperScape.dialog.show({
      title: "Storage Manager",
      message: "Load or save your scene.",
      fields:
        { name: "path", label: "Server Path", value: "/HomeLab/Scene.json" },
        { name: "autosave", label: "Autosave Key", value: "PaperScape/autosave" }
      ,
      actions:
        { label: "Load", style: "primary", onClick(ctx) { loadState(ctx.getValue('path')); ctx.close(); } },
        { label: "Save", style: "secondary", onClick(ctx) { saveState(ctx.getValue('path')); } }

    });

## 6. State Persistence

- `PaperScape.toJson()` captures each registered item with its serialised state and original source payload for replay.
- Image drops store data URLs inside `paperScape.json`; large images inflate the save file and should be used sparingly.
- Scenes are currently versionless—saving overwrites `paperScape.json` with no rotation or snapshots.

## 7. Extending PaperScape

1. Create a `.giraffe.js` module exporting `target = YourConstructor;` and implement `getName()`, `getContainer()`, plus optional `toJson()`, `fromJson()`, and `getForm()`.
2. Drop the script onto the canvas or call `paperScape.load(
   "/path/to/script.giraffe.js
   ")` to instantiate it at the pointer location.
3. Provide modal edit markup (e.g., `default.html.fragment`) so `openInterface()` can render a configuration form.
4. Use `paperScape.addFileProcessor()` to support new file types without modifying the core loader table.

## 8. Known Issues & Risks

- Shadow presets: The low-shadow instance overwrites the exported high-shadow reference (`paperscape.js`:391-396).
- Duplicate load: `fromJson()` appends without clearing existing items, so repeated loads duplicate content (`paperscape.js`:27).
- Security: Dropped `.giraffe.js` files execute via `OpenForum.evaluate`; only trusted sources should be used (`paperscape.js`:145).

## 9. Command Bus API

`/OpenForum/AddOn/PaperScape/commandBus.js` publishes the shared command bus so every embedded app, service worker, or MCP agent can participate in delete / undo / redo flows without touching PaperScape internals.

- `registerCommandParticipant()` wires handlers into a shared dispatcher and exposes per-participant `pushHistoryEntry()`.
- `pushHistoryEntry()` populates the shared stack, emits telemetry, and broadcasts queue packets unless `broadcast:false`.
- `onHistoryEvent()` reports `push`/`undo`/`redo`/`clear`/`telemetry`/`error` events for dashboards or debugging overlays.
- Legacy DOM shims dispatch `paperscape:history` and `paperscape:command` events while also listening for legacy `paperscape:pushHistory` dispatches.
- `commandBus.capturePointer()` and `captureSelection()` keep context records (pointer, selected IDs) synchronized so handlers always receive rich metadata.
- The core canvas registers as PaperScape Core, so deleting any item now pushes an undoable history entry and queued delete/undo/redo commands execute automatically.
- The Database Model View app now emits delta-based history entries for schema resets, table edits/moves, and foreign key deletes so undo/redo (and queue subscribers) only move the changed fragments.
- Floating undo/redo buttons render at the bottom of the viewport, subscribe to the command bus for enable states, and dispatch undo/redo without needing keyboard shortcuts.

`/OpenForum/AddOn/PaperScape/paperscape-queue.js` now includes `sendCommand`, `requestDelete`, `requestUndo`, `requestRedo`, and `pushHistoryEntry` helpers so remote services can submit edits via the same IntraQ channel.

### TypeScript Definitions

`/OpenForum/AddOn/PaperScape/commandBus.d.ts` declares `PaperScapeCommandBus`, participant handles, and strongly typed history entry contracts so TypeScript apps can import ambient definitions.

    const handle = PaperScapeCommandBus.registerCommandParticipant({
      id: "ObjectModeler",
      commands: "delete", "undo", "redo",
      onCommand(command, context) {
        if (command === "delete" && context.selection?.length) {
          removeEntity(context.selection0?.id);
        }
      }
    });

    handle.pushHistoryEntry({
      id: "obj.delete-field",
      label: "Delete Field",
      payload: { entityId, fieldId },
      undo: "obj.restore-field",
      redo(context) {
        deleteField(context.payload.fieldId);
      }
    });

    PaperScapeCommandBus.onHistoryEventevent, entry) => {
      if (event === "telemetry") console.debug("CmdBus", entry);
    });

### Remote Queue Example

    const queue = new PaperScapeQueue({ appName: "MCPAgent", auth: { token: "bearer abc123" } });
    queue.requestDelete({ selection: undefined });
    queue.pushHistoryEntry({
      id: "mcp.update-name",
      label: "Rename Entity",
      undoCommand: "objectmodel.undo-rename",
      redoCommand: "objectmodel.redo-rename",
      payload: { entityId: "db.User", previous: "User", next: "Users" }
    });

### Vanilla JS Example

    const undoHandle = PaperScapeCommandBus.registerCommandParticipant({
      id: "Whiteboard",
      onCommand(command, ctx) {
        if (command === "undo") {
          replayStroke(ctx);
        }
      }
    });

    document.addEventListener("paperscape:history", (evt) => {
      const { event, entry } = evt.detail;
      if (event === "push") {
        console.info("Stack updated:", entry.label);
      }
    });

### Legacy DOM Signals

Older modules can emit `paperscape:pushHistory` or `paperscape:command` events instead of calling the JS API directly.

    document.dispatchEvent(new CustomEvent("paperscape:pushHistory", {
      detail: {
        entry: {
          id: "legacy.move",
          label: "Move Anchor",
          undo: "legacy.move.undo",
          redo: "legacy.move.redo"
        }
      }
    };

### Collaboration Queue Bridge

`/OpenForum/AddOn/PaperScape/collaboration-bridge.js` links the server-side `/OpenForum/MessageQueue` service with PaperScape’s IntraQ channel. The bridge polls the per-user queue at `/OpenForum/Users/<user>/CollaborationQueue`, forwards messages tagged with `channel: "collaboration"` into the PaperScape command bus, and republishes local history pushes back to the queue so MCP agents see the same deltas.

Collaboration envelopes follow `/OpenForum/AddOn/PaperScape/collaboration-schema.json`. A short guide is available at `/OpenForum/AddOn/PaperScape/collaboration-guide.md`.

    OpenForum.includeScript("/OpenForum/MessageQueue/MessageQueue.js");
    OpenForum.loadScript("/OpenForum/AddOn/PaperScape/paperscape-queue.js");
    OpenForum.loadScript("/OpenForum/AddOn/PaperScape/collaboration-bridge.js");

    var bridge = new PaperScapeCollaborationBridge({
      serverQueue: "/OpenForum/Users/Guest/CollaborationQueue",
      channel: "collaboration",
      instanceId: "PaperScapeClient-" + Math.random().toString(36).slice(2),
      paperQueueOptions: { appName: "PaperScapeCollab" }
    });

Remote agents send JSON messages to the shared queue using the same channel. The bridge ignores its own origin ids so it is safe to publish/receive in the same session.

    {
      "schema": "/OpenForum/AddOn/PaperScape/collaboration-schema.json",
      "schemaVersion": "1.0",
      "channel": "collaboration",
      "origin": "mcp-agent",
      "messageType": "history-entry",
      "packet": {
        "action": "history-entry",
        "entry": {
          "id": "dbmv.move-173238",
          "label": "Move users",
          "payload": { ... delta ... }
        }
      }
    }

The same module can be reused by other collaborative apps (e.g., document editors) by instantiating it with a different channel string.

For MCP agents, use the `paperscape_queue` tool (under `/OpenForum/AddOn/ClaudeMCP`) to push or pull JSON envelopes from `/OpenForum/Users/<user>/CollaborationQueue`. The tool automatically prefixes messages with the `origin:` header expected by `MessageQueue.js`.

## 10. Next Steps

- Exercise regression coverage for the updated registration, naming, and removal flow across saved scenes.
- Add a "New Scene" or "Clear Canvas" action before loading new JSON.
- Surface this documentation and quick references directly inside the PaperScape UI.