Lolly
啟動應用程式

Assets and file utilities

Accept library assets or local files and return transformed output.

Part of Authoring Tools.

asset - library or device upload

An asset input opens the host's asset picker and stores the chosen AssetRef - uniform whether it came from the catalog or the user's device:

{
  "id": "logo",
  "type": "asset",
  "label": "Logo",
  "assetType": "image",    // vector | raster | image | video | audio | lottie | any - constrains the picker
  "allowUpload": true       // also let the user add an image from their device
}

assetType constrains what the picker offers: raster (bitmaps only), vector (SVG only - for inline-recolourable logos), image (any still image - raster _or_ vector, the right choice for a generic picture slot), video, audio (audiogram uses this), lottie or any (everything, including non-image assets). Prefer image over raster for photo/illustration slots so users can also pick or upload SVGs.

The Image row in the Filter tool - a thumbnail slot and a Choose asset button that opens the host's picker, with nothing about pickers in the manifestsigned by Lollyvector SVG自己驗證取得已簽署檔案9 paths706 nodes17 groups13 KBThe Image row in the Filter tool - a thumbnail slot and a Choose asset button that opens the host's picker, with nothing about pickers in the manifestsigned by Lollyvector SVG自己驗證取得已簽署檔案9 paths706 nodes17 groups13 KB

When allowUpload is true, the picker offers the user's personal image library alongside the catalog. Users add images from their device; the host stores the bytes verbatim (a silent re-encode would break a Content Credential's hard binding) and only offers to downscale when a file is genuinely huge. Metadata stripping is a separate, opt-in user preference (Strip metadata from uploads, default off). The library is not capped by count - the only limit is the device's own storage, checked before each write - and it is reusable across tools and managed in Profile → Storage → My images. SVG uploads are sanitised on ingest (script/handler stripping) and pass through without rasterising.

These images are device-local: their AssetRef.source is "user" and their user/… id is meaningful only on the device that holds the bytes, so they are omitted from shareable URLs (see docs/url-mode.md). Tools treat user and library assets identically - no tool code is involved in the upload.

Use any tool as an image (paste a Lolly link). Every asset input also accepts a Lolly tool link pasted into the picker's search box - a share link copied from another tool (…/#/tool/qr-code?url=…) or an embed URL (…/tool/qr-code.svg?…). The host renders that tool (via host.compose) and drops the result into the slot; the user can pick the render format and size before committing. This is the end-user counterpart to authored composes - no manifest declaration needed, and it works in every tool's image inputs by default. The picker offers SVG and bitmap render formats for any image slot (SVG is the default - it stays crisp and inlines as true vector in SVG/PDF export, and rasterises cleanly for PNG); a vector-typed slot is restricted to SVG. The chosen asset's identity is the canonical embed URL, so it persists in saved sessions and shareable links and re-renders on load - exactly like a library id. (The picker offers this whenever the shell can compose; the compose capability gates only authored composes, not this end-user path.)

file - the user's own file (on-device utilities)

A file input takes a file the user picks into memory and hands its raw bytes to the tool. It's the input shape for content-transform utilities - the "boring file jobs you'd otherwise hand to a stranger's website": strip EXIF, crop, compress, convert. Unlike asset (which is for brand imagery and goes through the catalog/upload library), a file is the user's own content that's processed and handed straight back, never stored or uploaded.

With layout: "canvas" a single file input stops being a sidebar row and becomes the working area itself - the drop zone strip-data opens with.

Strip Hidden Data's canvas - a drag-and-drop file zone with a Choose a file button and the note that nothing is uploadedsigned by Lollyvector SVG自己驗證取得已簽署檔案7 paths~6.2k nodes10 groups69 KBStrip Hidden Data's canvas - a drag-and-drop file zone with a Choose a file button and the note that nothing is uploadedsigned by Lollyvector SVG自己驗證取得已簽署檔案7 paths~6.2k nodes10 groups69 KB

{
  "id": "photo",
  "type": "file",
  "label": "Photo",
  "accept": ["image/jpeg", "image/png", ".jpg", ".png"],
  "maxSize": 52428800
}

The value is a FileRef: { __file: true, name, mime, size, bytes, url }. The bytes are a Uint8Array the hook reads directly (no host. call - the bytes ride in the value by design, because the portable host. surface has no file-read API). A file value is never serialised into a URL (binary has no shareable form) and never persisted - it lives only in memory on the device, which is the whole privacy point. In CLI transport a file param is a path the runner loads: --photo=./pic.jpg.

Producing output: the exportFile hook + privacy: "on-device"

A content-transform utility doesn't rasterise the canvas - it produces a transformed file. Declare the exportFile hook and mark the tool as an on-device utility:

{
  "status": "official",
  "privacy": "on-device",
  "render": { "width": 760, "height": 620, "formats": ["jpg"], "export": false, "actions": [] },
  "hooks": { "onInput": true, "exportFile": true }
}
function exportFile({ model }) {
  const inputs = Object.fromEntries(model.map(i => [i.id, i.value]));
  const f = inputs.photo;                       // the FileRef
  const cleaned = stripMetadata(f.bytes);       // your transform (pure bytes → bytes)
  return { bytes: cleaned, mime: f.mime, filename: f.name.replace(/(\.\w+)?$/, '-clean$1') };
}

In the template, a <button data-export-file>Download…</button> triggers the hook; the shell wraps the bytes in a Blob and delivers them via host.export.file (download on web, --output on the CLI). Use onInput/onInit to return extras the template displays (e.g. what metadata was found). strip-data is the reference implementation.

Back to Authoring Tools.