Download api.json from SharpMeow/unroll: direct link, hf CLI and curl.
- Browser
- Download file 51.1 kB
-
https://huggingface.co/spaces/SharpMeow/unroll/resolve/main/api.json
- Command line
-
hf download hf://spaces/SharpMeow/unroll/api.json
-
curl -L -o api.json https://huggingface.co/spaces/SharpMeow/unroll/resolve/main/api.json
51.1 kB
| { | |
| "name": "Unroll", | |
| "app": { | |
| "version": "1.7.0", | |
| "date": "2026-10-24", | |
| "sha": "e5067cd" | |
| }, | |
| "apiVersion": "1.0.0", | |
| "docs": { | |
| "llms": "/llms.txt", | |
| "agents": "/AGENTS.md", | |
| "addingAScroll": "/docs/ADDING_A_SCROLL.md", | |
| "changelog": "/CHANGELOG.md", | |
| "embed": "/docs/EMBED.md", | |
| "oembed": "/oembed.json" | |
| }, | |
| "global": "window.unroll", | |
| "conventions": [ | |
| "Every method returns a Promise of { ok: true, ... } or { ok: false, error, code }. Nothing throws.", | |
| "Error codes: invalid (bad arguments), not_found, unavailable (feature missing in this build or scroll), timeout, conflict, failed, load_failed.", | |
| "Screen coordinates are CSS pixels of the viewport; scene coordinates are millimeters.", | |
| "Add-ons register more groups with unroll.register(group, methods, specs), for example unroll.bench. They appear in unroll.schema() with extension: true.", | |
| "Every interactive element has data-testid and data-api naming its API equivalent." | |
| ], | |
| "embed": { | |
| "url": "https://sharpmeow-unroll.static.hf.space/index.html?embed=1", | |
| "params": { | |
| "embed": "1: compact layout, click to interact", | |
| "autoplay": "1: play on load", | |
| "controls": "0: hide the controls" | |
| }, | |
| "iframeAllow": "fullscreen; clipboard-write" | |
| }, | |
| "postMessage": { | |
| "description": "Host pages drive an embedded viewer with postMessage. Every method above is callable by name; on, off and register are replaced by subscribe and unsubscribe.", | |
| "toViewer": { | |
| "call": { | |
| "unroll": 1, | |
| "id": "number|string", | |
| "method": "string (an API method name)", | |
| "args": "array" | |
| }, | |
| "subscribe": { | |
| "unroll": 1, | |
| "id": "number|string", | |
| "method": "subscribe", | |
| "args": [ | |
| "eventName" | |
| ] | |
| }, | |
| "unsubscribe": { | |
| "unroll": 1, | |
| "id": "number|string", | |
| "method": "unsubscribe", | |
| "args": [ | |
| "eventName" | |
| ] | |
| } | |
| }, | |
| "fromViewer": { | |
| "ready": { | |
| "unroll": 1, | |
| "type": "ready", | |
| "version": "{ version, date, sha, api }", | |
| "methods": "array of method names" | |
| }, | |
| "result": { | |
| "unroll": 1, | |
| "type": "result", | |
| "id": "the id you sent", | |
| "result": "{ ok: true, ... } | { ok: false, error, code }" | |
| }, | |
| "event": { | |
| "unroll": 1, | |
| "type": "event", | |
| "event": "string", | |
| "payload": "object" | |
| } | |
| }, | |
| "forbidden": [ | |
| "open.listLocal", | |
| "open.removeLocal" | |
| ] | |
| }, | |
| "methods": { | |
| "version": { | |
| "group": "meta", | |
| "summary": "The app version from version.json: { version (semver), date, sha, api }. Also in api.json and Bench export bundles.", | |
| "call": "window.unroll.version()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "help": { | |
| "group": "meta", | |
| "summary": "A short guide: how calls work, a quick start and the methods by group. help(\"destroy\") narrows to a group, help(\"setLayer\") to one method.", | |
| "call": "window.unroll.help(topic?)", | |
| "params": [ | |
| { | |
| "name": "topic", | |
| "type": [ | |
| "string" | |
| ], | |
| "required": false, | |
| "description": "A method or group name" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "schema": { | |
| "group": "meta", | |
| "summary": "This API: version, every method with its parameters, events, URL parameters and keyboard actions.", | |
| "call": "window.unroll.schema()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "getState": { | |
| "group": "meta", | |
| "summary": "The whole page state as JSON: scroll, unroll position, layer, winding, close-up, panels, Destroy mode, camera, readout and a text description of the view.", | |
| "call": "window.unroll.getState()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "describe": { | |
| "group": "meta", | |
| "summary": "A plain-text description of what is on screen now (the same text screen readers get).", | |
| "call": "window.unroll.describe()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "links": { | |
| "group": "meta", | |
| "summary": "Every link on the page (credits, licenses, docs) with its text and URL.", | |
| "call": "window.unroll.links()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "deepLink": { | |
| "group": "meta", | |
| "summary": "A URL that restores the current state (scroll, t, layer, focus, compare, panel, Destroy mode; camera too if asked).", | |
| "call": "window.unroll.deepLink(camera?)", | |
| "params": [ | |
| { | |
| "name": "camera", | |
| "type": [ | |
| "boolean" | |
| ], | |
| "required": false, | |
| "description": "Include the camera position (cam=...)" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "on": { | |
| "group": "meta", | |
| "summary": "Subscribe to an event; the callback gets a JSON payload. Returns { ok, id } for off().", | |
| "call": "window.unroll.on(event, callback)", | |
| "params": [ | |
| { | |
| "name": "event", | |
| "type": [ | |
| "string" | |
| ], | |
| "required": true, | |
| "description": "Event name", | |
| "enum": [ | |
| "change", | |
| "unroll", | |
| "layer", | |
| "focus", | |
| "panel", | |
| "destroy", | |
| "camera", | |
| "settled", | |
| "scroll", | |
| "extension", | |
| "error" | |
| ] | |
| }, | |
| { | |
| "name": "callback", | |
| "type": [ | |
| "function" | |
| ], | |
| "required": true, | |
| "description": "Called with (payload)" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "off": { | |
| "group": "meta", | |
| "summary": "Remove a subscription made with on().", | |
| "call": "window.unroll.off(id)", | |
| "params": [ | |
| { | |
| "name": "id", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "The id on() returned" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "run": { | |
| "group": "meta", | |
| "summary": "Run a batch of calls in order. Each step is [\"method\", ...args] or { \"method\", \"args\" }; dotted names reach groups (e.g. \"camera.orbit\"). Stops at the first failure unless options.continueOnError.", | |
| "call": "window.unroll.run(steps, options?)", | |
| "params": [ | |
| { | |
| "name": "steps", | |
| "type": [ | |
| "array" | |
| ], | |
| "required": true, | |
| "description": "The calls" | |
| }, | |
| { | |
| "name": "options", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": false, | |
| "description": "{ continueOnError: boolean }" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "waitFor": { | |
| "group": "meta", | |
| "summary": "Wait until the state matches. condition is \"ready\", \"settled\" (unroll and camera at rest), \"overlayLoaded\", or an object of dotted state paths to values or to { eq, ne, gte, lte, gt, lt, in }.", | |
| "call": "window.unroll.waitFor(condition, options?)", | |
| "params": [ | |
| { | |
| "name": "condition", | |
| "type": [ | |
| "string", | |
| "object" | |
| ], | |
| "required": true, | |
| "description": "What to wait for" | |
| }, | |
| { | |
| "name": "options", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": false, | |
| "description": "{ timeout: ms (default 10000) }" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "reload": { | |
| "group": "meta", | |
| "summary": "Reload the page (keeps the URL state).", | |
| "call": "window.unroll.reload()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "listScrolls": { | |
| "group": "scrolls", | |
| "summary": "Scrolls in the registry and scrolls opened in this browser (local).", | |
| "call": "window.unroll.listScrolls()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "openScroll": { | |
| "group": "scrolls", | |
| "summary": "Open a scroll by registry id or local key. The page reloads; call waitFor(\"ready\") after the new page loads.", | |
| "call": "window.unroll.openScroll(id)", | |
| "params": [ | |
| { | |
| "name": "id", | |
| "type": [ | |
| "string" | |
| ], | |
| "required": true, | |
| "description": "Registry id or local key (local-...)" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "setUnroll": { | |
| "group": "view", | |
| "summary": "Set the unroll position, 0 rolled to 1 fully unrolled. Animates unless instant.", | |
| "call": "window.unroll.setUnroll(t, options?)", | |
| "params": [ | |
| { | |
| "name": "t", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Position", | |
| "minimum": 0, | |
| "maximum": 1 | |
| }, | |
| { | |
| "name": "options", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": false, | |
| "description": "{ instant: boolean }" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "play": { | |
| "group": "view", | |
| "summary": "Start or stop the automatic unroll (toggles if omitted).", | |
| "call": "window.unroll.play(on?)", | |
| "params": [ | |
| { | |
| "name": "on", | |
| "type": [ | |
| "boolean" | |
| ], | |
| "required": false, | |
| "description": "Play or pause" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "setLayer": { | |
| "group": "view", | |
| "summary": "Show a layer: \"xray\", \"ink\" or \"overlay\" (aliases \"ct\", \"gpu\", or the manifest layer id).", | |
| "call": "window.unroll.setLayer(id)", | |
| "params": [ | |
| { | |
| "name": "id", | |
| "type": [ | |
| "string" | |
| ], | |
| "required": true, | |
| "description": "Layer" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "focus": { | |
| "group": "view", | |
| "summary": "Open the close-up of a winding (current winding if omitted), or close it with null. options.view picks the tab: \"main\", \"overlay\" or \"compare\".", | |
| "call": "window.unroll.focus(winding?, options?)", | |
| "params": [ | |
| { | |
| "name": "winding", | |
| "type": [ | |
| "string", | |
| "number", | |
| "null" | |
| ], | |
| "required": false, | |
| "description": "Winding id such as \"w059\" (or its number, 59), null to close" | |
| }, | |
| { | |
| "name": "options", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": false, | |
| "description": "{ view: \"main\" | \"overlay\" | \"compare\" }" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "compare": { | |
| "group": "view", | |
| "summary": "Compare swipe in the close-up: on/off and the split position 0..1 (0 shows only the overlay, 1 only the main ink). Opens the close-up on the current winding if needed.", | |
| "call": "window.unroll.compare(on, pos?)", | |
| "params": [ | |
| { | |
| "name": "on", | |
| "type": [ | |
| "boolean" | |
| ], | |
| "required": true, | |
| "description": "Show the compare view" | |
| }, | |
| { | |
| "name": "pos", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": false, | |
| "description": "Split position", | |
| "minimum": 0, | |
| "maximum": 1 | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "closeup": { | |
| "group": "view", | |
| "summary": "Zoom and pan the close-up image: { scale } multiplies the zoom, { x, y } pans in CSS px, { fit: true } fits it.", | |
| "call": "window.unroll.closeup(options)", | |
| "params": [ | |
| { | |
| "name": "options", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": true, | |
| "description": "{ scale, x, y, fit }" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "panel": { | |
| "group": "view", | |
| "summary": "Open or close a panel: \"about\", \"help\", \"open\" (Open your own), \"why\" (why two ink layers), \"embed\" (the iframe snippet). Toggles if open is omitted.", | |
| "call": "window.unroll.panel(name, open?)", | |
| "params": [ | |
| { | |
| "name": "name", | |
| "type": [ | |
| "string" | |
| ], | |
| "required": true, | |
| "description": "Panel", | |
| "enum": [ | |
| "about", | |
| "help", | |
| "open", | |
| "why", | |
| "embed" | |
| ] | |
| }, | |
| { | |
| "name": "open", | |
| "type": [ | |
| "boolean" | |
| ], | |
| "required": false, | |
| "description": "Open or close" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "sound": { | |
| "group": "view", | |
| "summary": "Crackle sound for Destroy on or off (browsers need a user gesture first to play audio).", | |
| "call": "window.unroll.sound(on)", | |
| "params": [ | |
| { | |
| "name": "on", | |
| "type": [ | |
| "boolean" | |
| ], | |
| "required": true, | |
| "description": "Sound on" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "fullscreen": { | |
| "group": "view", | |
| "summary": "Enter or leave full screen (browsers may refuse without a user gesture).", | |
| "call": "window.unroll.fullscreen(on?)", | |
| "params": [ | |
| { | |
| "name": "on", | |
| "type": [ | |
| "boolean" | |
| ], | |
| "required": false, | |
| "description": "Full screen" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "screenshot": { | |
| "group": "view", | |
| "summary": "A PNG of the view as a data URL. options.clean leaves out the title overlay; options.download also saves it like the camera button.", | |
| "call": "window.unroll.screenshot(options?)", | |
| "params": [ | |
| { | |
| "name": "options", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": false, | |
| "description": "{ clean: boolean, download: boolean }" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "key": { | |
| "group": "view", | |
| "summary": "Press a keyboard shortcut, as a person would (see schema().keys).", | |
| "call": "window.unroll.key(key, options?)", | |
| "params": [ | |
| { | |
| "name": "key", | |
| "type": [ | |
| "string" | |
| ], | |
| "required": true, | |
| "description": "Key name, e.g. \"t\", \"ArrowRight\", \"?\"" | |
| }, | |
| { | |
| "name": "options", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": false, | |
| "description": "{ shift: boolean }" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "reset": { | |
| "group": "view", | |
| "summary": "Back to the start: close panels, leave Destroy, put every sheet back, roll up, reset the camera and layer.", | |
| "call": "window.unroll.reset()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "camera.get": { | |
| "group": "camera", | |
| "summary": "Camera position, target, distance, azimuth and polar angle (degrees), field of view.", | |
| "call": "window.unroll.camera.get()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "camera.orbit": { | |
| "group": "camera", | |
| "summary": "Orbit around the target by degrees.", | |
| "call": "window.unroll.camera.orbit(dAzimuth, dPolar?)", | |
| "params": [ | |
| { | |
| "name": "dAzimuth", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Degrees around the vertical axis" | |
| }, | |
| { | |
| "name": "dPolar", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": false, | |
| "description": "Degrees up or down" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "camera.zoom": { | |
| "group": "camera", | |
| "summary": "Zoom by a factor (2 is twice as close, 0.5 twice as far).", | |
| "call": "window.unroll.camera.zoom(factor)", | |
| "params": [ | |
| { | |
| "name": "factor", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Zoom factor", | |
| "minimum": 0.01, | |
| "maximum": 100 | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "camera.pan": { | |
| "group": "camera", | |
| "summary": "Pan the view in millimeters along the screen (x right, y up).", | |
| "call": "window.unroll.camera.pan(dx, dy?)", | |
| "params": [ | |
| { | |
| "name": "dx", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "mm right" | |
| }, | |
| { | |
| "name": "dy", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": false, | |
| "description": "mm up" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "camera.set": { | |
| "group": "camera", | |
| "summary": "Put the camera at a position looking at a target, both [x, y, z] in scene millimeters.", | |
| "call": "window.unroll.camera.set(position, target?)", | |
| "params": [ | |
| { | |
| "name": "position", | |
| "type": [ | |
| "array" | |
| ], | |
| "required": true, | |
| "description": "[x, y, z]" | |
| }, | |
| { | |
| "name": "target", | |
| "type": [ | |
| "array" | |
| ], | |
| "required": false, | |
| "description": "[x, y, z]" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "camera.reset": { | |
| "group": "camera", | |
| "summary": "Fly back to the default view for the current position (the V key).", | |
| "call": "window.unroll.camera.reset()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "destroy.mode": { | |
| "group": "destroy", | |
| "summary": "Enter or leave Destroy mode (damage the scroll as it is, rolled up or unrolled).", | |
| "call": "window.unroll.destroy.mode(on?)", | |
| "params": [ | |
| { | |
| "name": "on", | |
| "type": [ | |
| "boolean" | |
| ], | |
| "required": false, | |
| "description": "Destroy mode on" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "destroy.tool": { | |
| "group": "destroy", | |
| "summary": "Pick the Destroy tool used by clicks and drags: \"tear\", \"crush\", \"burn\" or \"water\".", | |
| "call": "window.unroll.destroy.tool(name)", | |
| "params": [ | |
| { | |
| "name": "name", | |
| "type": [ | |
| "string" | |
| ], | |
| "required": true, | |
| "description": "Tool", | |
| "enum": [ | |
| "tear", | |
| "crush", | |
| "burn", | |
| "water" | |
| ] | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "destroy.aim": { | |
| "group": "destroy", | |
| "summary": "A screen point that is on the papyrus right now, nearest the center of the view: { x, y, winding }. Use it for the tool calls.", | |
| "call": "window.unroll.destroy.aim()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "destroy.tear": { | |
| "group": "destroy", | |
| "summary": "Rip the papyrus with a drag from (x, y). Enters Destroy mode and picks Tear. Returns damage stats before and after.", | |
| "call": "window.unroll.destroy.tear(x, y, options?)", | |
| "params": [ | |
| { | |
| "name": "x", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Screen x in CSS pixels from the left of the page", | |
| "minimum": 0 | |
| }, | |
| { | |
| "name": "y", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Screen y in CSS pixels from the top", | |
| "minimum": 0 | |
| }, | |
| { | |
| "name": "options", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": false, | |
| "description": "Optional { dx, dy, steps, ms }: drag vector in CSS px (default 90, -200), number of pointer moves (default 24), time per move in ms (default 16)" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "destroy.crush": { | |
| "group": "destroy", | |
| "summary": "Press at (x, y) to crumble the papyrus; options.hold (ms, default 700) presses harder and deeper.", | |
| "call": "window.unroll.destroy.crush(x, y, options?)", | |
| "params": [ | |
| { | |
| "name": "x", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Screen x in CSS pixels from the left of the page", | |
| "minimum": 0 | |
| }, | |
| { | |
| "name": "y", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Screen y in CSS pixels from the top", | |
| "minimum": 0 | |
| }, | |
| { | |
| "name": "options", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": false, | |
| "description": "Optional { hold }: how long to press, in ms (0 to 5000)" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "destroy.burn": { | |
| "group": "destroy", | |
| "summary": "Ignite the papyrus at (x, y); embers then creep along the fibers.", | |
| "call": "window.unroll.destroy.burn(x, y, options?)", | |
| "params": [ | |
| { | |
| "name": "x", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Screen x in CSS pixels from the left of the page", | |
| "minimum": 0 | |
| }, | |
| { | |
| "name": "y", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Screen y in CSS pixels from the top", | |
| "minimum": 0 | |
| }, | |
| { | |
| "name": "options", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": false, | |
| "description": "Optional { hold }: how long to press, in ms (0 to 5000)" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "destroy.water": { | |
| "group": "destroy", | |
| "summary": "Pour water at (x, y); options.hold (ms, default 900) widens the wet patch.", | |
| "call": "window.unroll.destroy.water(x, y, options?)", | |
| "params": [ | |
| { | |
| "name": "x", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Screen x in CSS pixels from the left of the page", | |
| "minimum": 0 | |
| }, | |
| { | |
| "name": "y", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Screen y in CSS pixels from the top", | |
| "minimum": 0 | |
| }, | |
| { | |
| "name": "options", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": false, | |
| "description": "Optional { hold }: how long to press, in ms (0 to 5000)" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "destroy.reset": { | |
| "group": "destroy", | |
| "summary": "Undo all damage, clear the debris and put the Sandbox sheets back.", | |
| "call": "window.unroll.destroy.reset()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "destroy.sandbox": { | |
| "group": "destroy", | |
| "summary": "Sandbox on or off: the strip laid flat with full fracture physics (Destroy mode only).", | |
| "call": "window.unroll.destroy.sandbox(on?)", | |
| "params": [ | |
| { | |
| "name": "on", | |
| "type": [ | |
| "boolean" | |
| ], | |
| "required": false, | |
| "description": "Sandbox on" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "destroy.grab": { | |
| "group": "destroy", | |
| "summary": "In the Sandbox, grab the papyrus at (x, y) and pull (crushes instead while the Crush tool is picked). Returns breaks before and after.", | |
| "call": "window.unroll.destroy.grab(x, y, options?)", | |
| "params": [ | |
| { | |
| "name": "x", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Screen x in CSS pixels from the left of the page", | |
| "minimum": 0 | |
| }, | |
| { | |
| "name": "y", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Screen y in CSS pixels from the top", | |
| "minimum": 0 | |
| }, | |
| { | |
| "name": "options", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": false, | |
| "description": "Optional { dx, dy, steps, ms }: drag vector in CSS px (default 90, -200), number of pointer moves (default 24), time per move in ms (default 16)" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "destroy.crumble": { | |
| "group": "destroy", | |
| "summary": "Crumble the whole scroll: it breaks up from the edges into a heap of dust. destroy.reset() restores it.", | |
| "call": "window.unroll.destroy.crumble()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "prizeOnly": { | |
| "group": "scrolls", | |
| "summary": "Show only prize eligible scrolls in the scroll picker, on or off (toggles when omitted). Returns { prizeOnly, visible }.", | |
| "call": "window.unroll.prizeOnly(on)", | |
| "params": [ | |
| { | |
| "name": "on", | |
| "type": [ | |
| "boolean" | |
| ], | |
| "required": true, | |
| "description": "Only prize eligible scrolls" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "destroy.shake": { | |
| "group": "destroy", | |
| "summary": "Sandbox only: knock the table, loosening random bits.", | |
| "call": "window.unroll.destroy.shake()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "destroy.stats": { | |
| "group": "destroy", | |
| "summary": "Damage numbers (masks, embers, particles, shards, puddles) and Sandbox numbers (nodes, links, breaks, debris).", | |
| "call": "window.unroll.destroy.stats()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "destroy.tools": { | |
| "group": "destroy", | |
| "summary": "The Destroy tools in this build.", | |
| "call": "window.unroll.destroy.tools()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "rollUp": { | |
| "group": "view", | |
| "summary": "Roll the scroll back up with a steady rewind (the Roll up button); false stops it. Toggles if omitted.", | |
| "call": "window.unroll.rollUp(on?)", | |
| "params": [ | |
| { | |
| "name": "on", | |
| "type": [ | |
| "boolean" | |
| ], | |
| "required": false, | |
| "description": "Rolling" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "embed.snippet": { | |
| "group": "embed", | |
| "summary": "The iframe snippet for this viewer: { url, html }. options: { state (start at the current view, default true), autoplay, controls (default true), width, height }.", | |
| "call": "window.unroll.embed.snippet(options?)", | |
| "params": [ | |
| { | |
| "name": "options", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": false, | |
| "description": "{ state, autoplay, controls, width, height }" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "embed.info": { | |
| "group": "embed", | |
| "summary": "Embed mode: { embedded (?embed=1), framed (inside an iframe), active (the scene has the pointer and wheel), controls, autoplay }.", | |
| "call": "window.unroll.embed.info()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "embed.activate": { | |
| "group": "embed", | |
| "summary": "In embed mode, hand the pointer, touch and wheel to the scene (like clicking the \"Click to interact\" layer), or give them back with false.", | |
| "call": "window.unroll.embed.activate(on?)", | |
| "params": [ | |
| { | |
| "name": "on", | |
| "type": [ | |
| "boolean" | |
| ], | |
| "required": false, | |
| "description": "Activate" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "open.fromURL": { | |
| "group": "open", | |
| "summary": "Open your own: fetch a manifest.json or zip from a URL (the server must allow CORS). Nothing is uploaded. On success the page reloads into the new scroll.", | |
| "call": "window.unroll.open.fromURL(url)", | |
| "params": [ | |
| { | |
| "name": "url", | |
| "type": [ | |
| "string" | |
| ], | |
| "required": true, | |
| "description": "http(s) URL" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "open.fromFiles": { | |
| "group": "open", | |
| "summary": "Open your own from file contents: [{ name, data }] where data is an ArrayBuffer, a Uint8Array, a Blob or a base64 string. A zip, or a manifest.json plus its files. On success the page reloads.", | |
| "call": "window.unroll.open.fromFiles(files)", | |
| "params": [ | |
| { | |
| "name": "files", | |
| "type": [ | |
| "array" | |
| ], | |
| "required": true, | |
| "description": "Files" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "open.example": { | |
| "group": "open", | |
| "summary": "Build the synthetic example package. Returns it as base64 (options.download also saves the zip).", | |
| "call": "window.unroll.open.example(options?)", | |
| "params": [ | |
| { | |
| "name": "options", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": false, | |
| "description": "{ download: boolean }" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "open.listLocal": { | |
| "group": "open", | |
| "summary": "Scrolls kept in this browser.", | |
| "call": "window.unroll.open.listLocal()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "open.removeLocal": { | |
| "group": "open", | |
| "summary": "Remove a scroll kept in this browser.", | |
| "call": "window.unroll.open.removeLocal(key)", | |
| "params": [ | |
| { | |
| "name": "key", | |
| "type": [ | |
| "string" | |
| ], | |
| "required": true, | |
| "description": "Local key" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "open.validate": { | |
| "group": "open", | |
| "summary": "Check a manifest object with the page validator, without opening anything.", | |
| "call": "window.unroll.open.validate(manifest)", | |
| "params": [ | |
| { | |
| "name": "manifest", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": true, | |
| "description": "The manifest" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.dragRes": { | |
| "group": "ct", | |
| "summary": "Lower render resolution while the camera moves: \"off\", \"light\" (0.75x) or \"strong\" (0.5x). Without an argument, returns the state.", | |
| "call": "window.unroll.ct.dragRes(mode?)", | |
| "params": [ | |
| { | |
| "name": "mode", | |
| "type": [ | |
| "string" | |
| ], | |
| "required": false, | |
| "description": "off, light or strong", | |
| "enum": [ | |
| "off", | |
| "light", | |
| "strong" | |
| ] | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.setWindow": { | |
| "group": "ct", | |
| "summary": "X-ray window and level on the 3D scroll, both 0..1 (low = level - window / 2).", | |
| "call": "window.unroll.ct.setWindow(window, level)", | |
| "params": [ | |
| { | |
| "name": "window", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Width, 0.005..1" | |
| }, | |
| { | |
| "name": "level", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Center, 0..1" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.autoContrast": { | |
| "group": "ct", | |
| "summary": "X-ray auto contrast from the CT atlas percentiles (default from Settings, 1 and 99).", | |
| "call": "window.unroll.ct.autoContrast(pLow?, pHigh?)", | |
| "params": [ | |
| { | |
| "name": "pLow", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": false, | |
| "description": "Low percentile" | |
| }, | |
| { | |
| "name": "pHigh", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": false, | |
| "description": "High percentile" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.fullRange": { | |
| "group": "ct", | |
| "summary": "Show the full X-ray brightness range.", | |
| "call": "window.unroll.ct.fullRange()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.contrast": { | |
| "group": "ct", | |
| "summary": "X-ray window/level state.", | |
| "call": "window.unroll.ct.contrast()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.open": { | |
| "group": "ct", | |
| "summary": "Open the CT volume (MIP in a slab) around a papyrus point: { winding, u, v } or { voxel: [x, y, z] } or { x, y } screen point, with level? (0..5) and size? (64, 128, 192, 256). Streams OME-Zarr chunks from the public S3 bucket.", | |
| "call": "window.unroll.ct.open(where)", | |
| "params": [ | |
| { | |
| "name": "where", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": true, | |
| "description": "Point" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.close": { | |
| "group": "ct", | |
| "summary": "Close the CT volume panel.", | |
| "call": "window.unroll.ct.close()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.state": { | |
| "group": "ct", | |
| "summary": "CT volume state: point, level, size, voxel size, source URL, slab, window, load stats, labels.", | |
| "call": "window.unroll.ct.state()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.setSlab": { | |
| "group": "ct", | |
| "summary": "Slab clip of the CT volume: { on, thickness (voxels), offset (voxels), tiltX, tiltY (degrees), face (point the camera down the normal) }.", | |
| "call": "window.unroll.ct.setSlab(slab)", | |
| "params": [ | |
| { | |
| "name": "slab", | |
| "type": [ | |
| "object" | |
| ], | |
| "required": true, | |
| "description": "Slab fields to change" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.volumeWindow": { | |
| "group": "ct", | |
| "summary": "Window and level of the CT volume MIP, both 0..1.", | |
| "call": "window.unroll.ct.volumeWindow(window, level)", | |
| "params": [ | |
| { | |
| "name": "window", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Width" | |
| }, | |
| { | |
| "name": "level", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "Center" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.volumeAuto": { | |
| "group": "ct", | |
| "summary": "Auto contrast of the CT volume from its own histogram.", | |
| "call": "window.unroll.ct.volumeAuto()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.setDetail": { | |
| "group": "ct", | |
| "summary": "Detail level of the CT volume: 0 is the full 9.4 µm scan, each step halves the resolution. Reloads.", | |
| "call": "window.unroll.ct.setDetail(level)", | |
| "params": [ | |
| { | |
| "name": "level", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "0..5" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.setSize": { | |
| "group": "ct", | |
| "summary": "Cube size of the CT volume in voxels. Reloads.", | |
| "call": "window.unroll.ct.setSize(size)", | |
| "params": [ | |
| { | |
| "name": "size", | |
| "type": [ | |
| "number" | |
| ], | |
| "required": true, | |
| "description": "64, 128, 192 or 256" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.pick": { | |
| "group": "ct", | |
| "summary": "Turn pick mode on or off: the next click on the papyrus opens the CT volume there.", | |
| "call": "window.unroll.ct.pick(on?)", | |
| "params": [ | |
| { | |
| "name": "on", | |
| "type": [ | |
| "boolean" | |
| ], | |
| "required": false, | |
| "description": "On" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.image": { | |
| "group": "ct", | |
| "summary": "The CT volume view as a PNG data URL.", | |
| "call": "window.unroll.ct.image()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.labels": { | |
| "group": "ct", | |
| "summary": "Labeled segments: color every traced segment inside the CT volume cube. Returns the segments found with their colors and voxel counts.", | |
| "call": "window.unroll.ct.labels(on?)", | |
| "params": [ | |
| { | |
| "name": "on", | |
| "type": [ | |
| "boolean" | |
| ], | |
| "required": false, | |
| "description": "On" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.highlight": { | |
| "group": "ct", | |
| "summary": "Highlight one labeled segment by winding id (null clears).", | |
| "call": "window.unroll.ct.highlight(id)", | |
| "params": [ | |
| { | |
| "name": "id", | |
| "type": [ | |
| "string", | |
| "null" | |
| ], | |
| "required": true, | |
| "description": "Winding id, for example w035" | |
| } | |
| ], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| }, | |
| "ct.source": { | |
| "group": "ct", | |
| "summary": "Where the CT volume of the current scroll streams from (OME-Zarr on s3://vesuvius-challenge-open-data) and the levels it has.", | |
| "call": "window.unroll.ct.source()", | |
| "params": [], | |
| "returns": "Promise<{ ok: true, ... } | { ok: false, error: string, code: string }>" | |
| } | |
| }, | |
| "events": { | |
| "change": "Any state change: { changed: [paths], state }", | |
| "unroll": "The unroll position settled: { t }", | |
| "layer": "The layer changed: { layer }", | |
| "focus": "The close-up opened, closed or changed tab: { focus }", | |
| "panel": "A panel opened or closed: { panels }", | |
| "destroy": "Destroy mode, crush or a tool was used: { destroy }", | |
| "camera": "The camera came to rest somewhere new: { camera }", | |
| "settled": "Unroll and camera at rest: { state }", | |
| "scroll": "A scroll is about to open (the page reloads): { id }", | |
| "extension": "An add-on registered a method group, for example bench: { group, methods }", | |
| "error": "A call failed: { method, error }" | |
| }, | |
| "urlParams": { | |
| "scroll": "Registry id or local key (alias s)", | |
| "t": "Unroll position 0..1", | |
| "layer": "xray, ink or overlay (alias l; gpu and ct also work)", | |
| "focus": "Winding id: open its close-up", | |
| "view": "Close-up tab: main, overlay or compare", | |
| "compare": "Open the compare swipe at this split position 0..1 (on the focus winding, or the current one)", | |
| "panel": "about, help, open, why or embed", | |
| "embed": "1 for the compact embed layout with click to interact (query string only)", | |
| "autoplay": "1 to play the unroll on load (embed mode)", | |
| "controls": "0 to hide the controls (embed mode)", | |
| "destroy": "1 to start in Destroy mode", | |
| "tool": "Destroy tool: tear, crush, burn or water (implies destroy=1)", | |
| "crush": "1: the same as tool=crush", | |
| "sandbox": "1 to start in the Destroy Sandbox", | |
| "cam": "Camera px,py,pz,tx,ty,tz in scene millimeters", | |
| "shot": "In the query string: a clean frame for share images" | |
| }, | |
| "keys": { | |
| "1": "setLayer(\"xray\")", | |
| "2": "setLayer(\"ink\")", | |
| "3": "setLayer(\"overlay\")", | |
| "Space": "play", | |
| "ArrowRight": "setUnroll(t + 0.01)", | |
| "ArrowLeft": "setUnroll(t - 0.01)", | |
| "Shift+Arrow": "setUnroll(t +/- 0.05)", | |
| "Home": "setUnroll(0)", | |
| "End": "setUnroll(1)", | |
| "r": "focus()", | |
| "v": "camera.reset()", | |
| "t": "destroy.mode()", | |
| "x": "destroy.tool(\"tear\")", | |
| "c": "destroy.tool(\"crush\")", | |
| "b": "destroy.tool(\"burn\")", | |
| "w": "destroy.tool(\"water\")", | |
| "u": "rollUp()", | |
| "f": "fullscreen()", | |
| "i": "panel(\"about\")", | |
| "?": "panel(\"help\")", | |
| "Escape": "close panels", | |
| "canvas focused: arrows": "camera.orbit", | |
| "canvas focused: + and -": "camera.zoom", | |
| "canvas focused: Shift+arrows": "camera.pan", | |
| "canvas focused: Enter": "focus()", | |
| "close-up focused: + - arrows 0": "closeup()" | |
| }, | |
| "manifestSchema": { | |
| "$schema": "https://json-schema.org/draft/2020-12/schema", | |
| "title": "Unroll scroll manifest", | |
| "type": "object", | |
| "required": [ | |
| "schema", | |
| "id", | |
| "name", | |
| "voxelMM", | |
| "geometry", | |
| "windings", | |
| "layers" | |
| ], | |
| "properties": { | |
| "schema": { | |
| "const": "unroll-scroll/1" | |
| }, | |
| "id": { | |
| "type": "string", | |
| "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" | |
| }, | |
| "name": { | |
| "type": "string", | |
| "maxLength": 120 | |
| }, | |
| "voxelMM": { | |
| "type": "number", | |
| "exclusiveMinimum": 0, | |
| "exclusiveMaximum": 10 | |
| }, | |
| "base": { | |
| "type": "string", | |
| "description": "Relative folder that paths start from" | |
| }, | |
| "geometry": { | |
| "type": "object", | |
| "required": [ | |
| "format" | |
| ], | |
| "properties": { | |
| "format": { | |
| "enum": [ | |
| "grid-int16", | |
| "mesh" | |
| ], | |
| "description": "grid-int16 is for registry scrolls only" | |
| }, | |
| "units": { | |
| "enum": [ | |
| "voxels", | |
| "mm" | |
| ] | |
| }, | |
| "axis": { | |
| "enum": [ | |
| "z", | |
| "y" | |
| ] | |
| }, | |
| "index": { | |
| "type": "string", | |
| "description": "Relative path inside the package: no URL, no leading slash, no \"..\"" | |
| }, | |
| "bin": { | |
| "type": "string", | |
| "description": "Relative path inside the package: no URL, no leading slash, no \"..\"" | |
| } | |
| } | |
| }, | |
| "windings": { | |
| "description": "A list of { id, mesh } or { range: [first, last], mesh: pattern with {id}, outermost }", | |
| "oneOf": [ | |
| { | |
| "type": "array", | |
| "items": { | |
| "type": "object", | |
| "required": [ | |
| "id" | |
| ], | |
| "properties": { | |
| "id": { | |
| "type": "string" | |
| }, | |
| "mesh": { | |
| "type": "string", | |
| "description": ".obj, .ply" | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "type": "object", | |
| "required": [ | |
| "range" | |
| ] | |
| } | |
| ] | |
| }, | |
| "outermost": { | |
| "enum": [ | |
| "first", | |
| "last" | |
| ] | |
| }, | |
| "layers": { | |
| "type": "array", | |
| "minItems": 1, | |
| "items": { | |
| "type": "object", | |
| "required": [ | |
| "id", | |
| "role", | |
| "label" | |
| ], | |
| "properties": { | |
| "id": { | |
| "type": "string" | |
| }, | |
| "role": { | |
| "enum": [ | |
| "ct", | |
| "ink", | |
| "overlay" | |
| ] | |
| }, | |
| "label": { | |
| "type": "string" | |
| }, | |
| "shortLabel": { | |
| "type": "string", | |
| "description": "Label where space is tight (layer chip, compare swipe)" | |
| }, | |
| "resolutionUM": { | |
| "type": "number", | |
| "exclusiveMinimum": 0, | |
| "description": "Resolution in micrometers" | |
| }, | |
| "windings": { | |
| "description": "\"all\", a list of ids, or { range: [first, last] }" | |
| }, | |
| "texture": { | |
| "type": "string", | |
| "description": "Per-winding image path pattern containing {id}" | |
| }, | |
| "files": { | |
| "type": "object", | |
| "additionalProperties": { | |
| "type": "string", | |
| "description": "Relative path inside the package: no URL, no leading slash, no \"..\"" | |
| }, | |
| "description": "Winding id to image path, overriding texture" | |
| }, | |
| "atlas": { | |
| "type": "string", | |
| "description": "Relative path inside the package: no URL, no leading slash, no \"..\"" | |
| }, | |
| "atlasFallback": { | |
| "type": "string", | |
| "description": "Relative path inside the package: no URL, no leading slash, no \"..\"" | |
| }, | |
| "hi": { | |
| "type": "string", | |
| "description": "Close-up image pattern containing {id}" | |
| }, | |
| "stats": { | |
| "type": "string", | |
| "description": "Relative path inside the package: no URL, no leading slash, no \"..\"" | |
| }, | |
| "source": { | |
| "type": "string", | |
| "description": "Where the layer comes from" | |
| }, | |
| "aboutHtml": { | |
| "type": "string", | |
| "description": "Registry scrolls only" | |
| } | |
| } | |
| }, | |
| "description": "One layer per role; at least one ct or ink layer" | |
| }, | |
| "description": { | |
| "type": "string" | |
| }, | |
| "credits": { | |
| "type": "object", | |
| "properties": { | |
| "text": { | |
| "type": "string" | |
| }, | |
| "url": { | |
| "type": "string", | |
| "format": "uri" | |
| }, | |
| "citation": { | |
| "type": "string" | |
| }, | |
| "holder": { | |
| "type": "string" | |
| } | |
| } | |
| }, | |
| "dataTerms": { | |
| "type": "object", | |
| "properties": { | |
| "license": { | |
| "type": "string" | |
| }, | |
| "url": { | |
| "type": "string", | |
| "format": "uri" | |
| }, | |
| "notes": { | |
| "type": "string" | |
| } | |
| } | |
| }, | |
| "display": { | |
| "type": "object", | |
| "description": "Registry scrolls only" | |
| } | |
| }, | |
| "x-imageExtensions": [ | |
| "png", | |
| "jpg", | |
| "jpeg", | |
| "webp", | |
| "tif", | |
| "tiff" | |
| ], | |
| "x-meshExtensions": [ | |
| "obj", | |
| "ply" | |
| ], | |
| "x-limits": { | |
| "openYourOwn": { | |
| "maxFileBytes": 67108864, | |
| "maxTotalBytes": 268435456, | |
| "maxZipBytes": 134217728, | |
| "maxWindings": 64, | |
| "maxVertices": 2000000, | |
| "maxTextureSide": 8192 | |
| }, | |
| "registry": { | |
| "maxFileBytes": 209715200, | |
| "maxTotalBytes": 2000000000, | |
| "maxZipBytes": 134217728, | |
| "maxWindings": 400, | |
| "maxVertices": 2000000, | |
| "maxTextureSide": 8192 | |
| } | |
| } | |
| } | |
| } | |