Download AGENTS.md from SharpMeow/unroll: direct link, hf CLI and curl.
- Browser
- Download file 5.93 kB
-
https://huggingface.co/spaces/SharpMeow/unroll/resolve/main/AGENTS.md
- Command line
-
hf download hf://spaces/SharpMeow/unroll/AGENTS.md
-
curl -L -o AGENTS.md https://huggingface.co/spaces/SharpMeow/unroll/resolve/main/AGENTS.md
Driving Unroll as an agent
Unroll is a static page. Everything it does is reachable without touching the mouse:
through the JavaScript API at window.unroll, through URL parameters, or by keyboard.
The full machine-readable reference is /api.json; await unroll.schema() returns
the same list at run time, including add-ons such as unroll.bench.
Rules of the API
- Every method returns a Promise of
{ ok: true, ... }or{ ok: false, error, code }. Nothing throws. - Arguments are validated; errors say what was wrong and what is allowed.
- Codes:
invalid,not_found,unavailable(not in this build or this scroll),timeout,conflict,failed,load_failed. - Screen coordinates (Destroy tools) are CSS pixels in the viewport. Scene coordinates (camera) are millimeters.
- If the scroll fails to load,
window.unrollstill offersgetState,version,openScrollandreload.
Start
await unroll.waitFor('ready'); // the scroll is loaded
const s = await unroll.getState(); // scroll, t, layer, winding, focus, panels, destroy, camera, description, url
await unroll.describe(); // the current view in words
await unroll.version(); // { version, date, sha, api }
Common tasks
| Task | Call |
|---|---|
| Unroll to a position | setUnroll(0.5) then waitFor('settled') |
| Help and schema | help(), help('destroy'), help('setLayer'), schema() |
| Animate | play(true), play(false) |
| Switch layer | setLayer('xray' | 'ink' | 'overlay') |
| Close-up of a winding | focus('w047', { view: 'overlay' }); close with focus(null) |
| Compare swipe | compare(true, 0.3) |
| Zoom and pan the close-up | closeup({ scale: 2, x: 40, y: 0 }), closeup({ fit: true }) |
| Panels | panel('about' | 'help' | 'open' | 'why', true/false) |
| Camera | camera.get(), camera.orbit(deg, deg), camera.zoom(f), camera.pan(dx, dy), camera.set([x,y,z],[x,y,z]), camera.reset() |
| Destroy | destroy.mode(true), destroy.tool('burn'), destroy.aim() (a point on the papyrus), destroy.tear(x, y, { dx, dy }), destroy.crush(x, y, { hold }), destroy.burn(x, y), destroy.water(x, y, { hold }), destroy.crumble(), destroy.reset(), destroy.stats(), destroy.tools() |
| Destroy Sandbox | destroy.sandbox(true), destroy.grab(x, y, { dx, dy }), destroy.shake() |
| Roll it back up | rollUp(true), rollUp(false) |
| Sound, fullscreen | sound(true), fullscreen(true) |
| Image of the view | screenshot() gives { dataURL } (PNG) |
| A keyboard shortcut | key('2'), key('Space') |
| Other scrolls | listScrolls(), openScroll(id) (the page reloads), prizeOnly(true) (filter the picker to prize eligible scrolls) |
| Settings and the control panel | ui.settings(), ui.setSetting('quality', 'low'), ui.resetSettings(), ui.settingsPanel(true), ui.moreControls(true), ui.skipIntro(), ui.glossary('voxel'), ui.glossarySearch('patch'), ui.actions(), ui.action('tool.burn') |
| Ink Bench | bench.open('compare'), bench.compare({ segment, layer, thresholds }), bench.metrics(), bench.close(); see docs/BENCH.md |
| Open your own | open.fromURL(url), open.fromFiles([{ name, data }]) with an ArrayBuffer, Uint8Array, Blob or base64 string; open.example(), open.validate(manifest), open.listLocal(), open.removeLocal(key) |
| Share the view | deepLink(true) |
Tool calls take screen points; a point that is not on the papyrus answers { ok: false, code: 'not_found' }.
destroy.aim() returns one that is.
Waiting and events
await unroll.waitFor('settled'); // unroll and camera at rest
await unroll.waitFor({ layer: 'xray', t: { gte: 0.5 } }, { timeout: 5000 });
const { id } = await unroll.on('change', (e) => console.log(e.changed));
await unroll.off(id);
Events: change, unroll, layer, focus, panel, destroy, camera, settled, scroll, extension, error.
Batches
await unroll.run([
['setLayer', 'overlay'],
['focus', 'w047', { view: 'compare' }],
['compare', true, 0.4],
['screenshot'],
], { continueOnError: false });
URL parameters
scroll (alias s), t, layer (alias l), focus, view, compare, panel, destroy, tool, crush, sandbox, cam.
They work in the query string or the hash (the hash wins), and the page keeps the hash up to date.
Selectors for browser automation
Every interactive element has a stable data-testid and a data-api attribute naming its API equivalent,
for example <button data-testid="layer-xray" data-api="setLayer('xray')">.
tests/coverage.mjs fails if any control lacks either one.
Embeds
In an iframe (?embed=1), host pages reach the same API with postMessage; see docs/EMBED.md.
embed.snippet() returns an iframe snippet for the current view.
Add-ons
Add-ons register a method group: unroll.register('bench', { run: fn }, { run: { summary, params } }).
A script that loads before the page can queue the same arguments on window.unrollExtensions.
Live sheet
/live.html streams a segment and a volume from the open data bucket and draws the volume on the sheet.
It has its own small API, window.liveSheet: load({ seg, vol, crop, level, depth, up, reduce }), state(),
values(), turn(deg), screenshot(), and the URL parameters in docs/LIVE.md.
Versions
version.json is the single source of truth (semver). Bump the minor version for a feature push and the
patch version for a fix with node tools/stamp_version.mjs minor|patch, add a line to CHANGELOG.md,
and run node tools/build_api_json.mjs.
Tests
node tests/coverage.mjs http://localhost:8080/
node tests/api.mjs http://localhost:8080/ [URL of a zip served with CORS]
node tests/livesheet.mjs # lib/livesheet.js, no network