Interactive visual canvas for Giraffe components inside OpenForum. This guide explains the runtime, bundled tools, and current maintenance backlog.
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.
.giraffe.js, .giraffe.json, and common images (JPG/PNG/SVG/WEBP), converting them into live canvas items.paperScape.json alongside the addon and restore on reload.getForm() markup renders inside #editForm; otherwise a default remove-only fragment is shown.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./OpenForum/Giraffe/giraffe.js, Foundation's off-canvas shell, and OpenForum helpers such as OpenForum.loadJSON, saveJSON, and IntraQ.postRegisterItem/postRemoveItem, addFileProcessor, selectors for view/control/overlay layers, and setAllowDrag / select-box controls.Apps/measure.giraffe.js plus measure-recalibrate.html.fragment to calibrate distances and units.Apps/white-board.giraffe.js adds a simple drawing surface with selectable pens.Apps/intraq-connector.giraffe.js bridges to the developer console using the "PaperScape" queue.Apps/giraffe-editor.giraffe.js opens the standalone Giraffe editor preview.seed.giraffe.json and dropable.giraffe.json demonstrate serialised composites for import testing./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.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')); } }
});
PaperScape.toJson() captures each registered item with its serialised state and original source payload for replay.paperScape.json; large images inflate the save file and should be used sparingly.paperScape.json with no rotation or snapshots..giraffe.js module exporting target = YourConstructor; and implement getName(), getContainer(), plus optional toJson(), fromJson(), and getForm().paperScape.load(
"/path/to/script.giraffe.js
") to instantiate it at the pointer location.default.html.fragment) so openInterface() can render a configuration form.paperScape.addFileProcessor() to support new file types without modifying the core loader table.paperscape.js:391-396).fromJson() appends without clearing existing items, so repeated loads duplicate content (paperscape.js:27)..giraffe.js files execute via OpenForum.evaluate; only trusted sources should be used (paperscape.js:145)./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.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./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.
/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);
});
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" }
});
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);
}
});
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"
}
}
};
/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.