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.onHistoryEvent

event, 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.