Lolly
打开应用

概述

signed by Lollyoverview.htmlHTMLpixels, not shapes12 KBAI generatedgenerated by Claude自己验证获取已签名文件

Lolly 图标 - 一支绿白相间的大棒棒糖

本文档记录了 Lolly 平台的目标、结构和架构决策,既反映了产品愿景,也反映了代码库的现状。

状态: Lolly 已经发布。1.0 版本于 2026 年 8 月 30 日以 MPL-2.0 开源发布,功能、默认设置和工具目录在各个版本之间仍会变化。引擎是确定性的,每个外壳背后都是同一条渲染路径。它的安全性尚未经过独立审计,采用情况的证据也只有几个月的历史。关于当前状态以及如何衡量采用情况,参见采用与治理。

如何阅读本页。 本页按顺序包含两类内容。前半部分是 为什么存在这个产品:问题、定位以及单个素材所经历的生命周期。 从全局视角开始则是 各层如何衔接:面向贡献者的架构文档,涵盖引擎/宿主/内容包的分层、 仓库结构、交付目标,以及约束平台每一次改动的各项承诺。如果你来这里是为了 修改代码库而非理解产品本身,请从全局视角开始阅读。

有两份配套文档比本页讲得更深入。仓库中的 engine/README.md 是引擎的逐模块地图,附有一张自动生成的表格,列出每个模块及其解析或写入的内容。 威胁模型与信任边界 是从信任边界角度重新阅读的同一份架构,任何关于引擎将什么视为不可信输入的问题, 都应该看这一页。


为什么存在这个产品

重复性的创意工作,可预测到不值得每次都动用专业人力,却又对质量敏感到不能在没有护栏的情况下外放。放任不管,只会走向三种结局之一:缓慢、不一致,或被锁定在供应商的模板里。

本平台正是对此的直接回应:

规模化的程序式创意与内容生产 - 素材由输入生成,规则只需设定一次,供任何需要的人使用。

Lolly 不是设计系统被发明出来的地方——而是它被生产出来的地方。可以把它想象成一台设计自动售货机:做出选择,得到结果。每一次都是如此。引擎会为您眼前的硬件所能达到的每种格式的最高质量而运作,而且同一个引擎在它所支持的每个平台上都生成同一份文件。

由此带来的结果是充裕:每场活动都有正确的标识牌,每条 CVE 警报都符合公司风格,每张标签都印得干净利落,每个邮件签名都保持最新 - 全部出自早已承载这些规则的工具。本平台处理的是可运营化的重复性创意工作,刻意不做定制化的创意工具 - 旗舰级作品仍然由设计师掌控。

概率式创新,确定性规模化

每一场关于 AI 在创意流程中角色的争论,最终都卡在同一个问题上:这其中哪部分该交给机器?这是个古老的问题,答案早已确定。抄写员和插画师早就在两种工具之间工作 - 一种是松散的草图,一切尚未固定,什么都可以尝试;另一种是印刷机,它令人生畏正是因为它是一锤定音的。草图是艺术发生的地方,印刷机则是作品得以传播的方式。从没有人把这两者混为一谈,而两者也都在不断进步 - 新的墨水、新的字体、新的印刷机 - 每一次进步都与它所服务的技艺和意图相协调。

Lolly 划的是同一条线。以概率的方式探索:一个模型、一位设计师、一个粗略的想法、一段走向无人预料之处的提示词。然后以确定性的方式规模化 - 能变成许多产出的东西是一个工具,而工具每次都会从可读的输入出发,以同样的方式渲染。探索之所以能保持自由,是因为下游没有任何环节依赖它两次落地一致。产出之所以值得信任,是因为它不是一次猜测。把 AI 实验转化为可预测、可复现的结果,并不是什么新学科,它正是那种最初让印刷品值得信赖的同一种分工。

信任创意过程,以严谨实现规模化。

相较于其他方案

Lolly includes lolly.work, the same project's separately deployed organisation service. Local creation needs no account; shared work and server rendering use the configured instance. Competitor scores: August 2026. Lolly coverage: 28 September 2026.

Capability Lolly+ lolly.work Penpotopen design CanvaAffinity
Cavalry
Adobedesktop pro Brand DAMFrontify
Bynder
Cloudinarymedia pipeline Figmaonline pro Render APIsBannerbear
Placid
Creatomate
Overall completenessunweighted mean - weight rows for your own context · columns are sorted by this row 84 70 57 48 41 41 41 34
Production maturity & track recordCanva, Adobe, Figma, Bynder/Frontify: a decade-plus at massive scale. Render APIs: years in production pipelines. Penpot: shipping, large community, younger at enterprise scale. Lolly: version 1.0 public since 30 August 2026 under MPL-2.0, and SUSE runs it as an internal deployment; no usage data is shared yet and there are no public case studies yet. Cloudinary: 2012, enterprise media infrastructure at global scale.
25 75 100 100 100 100 100 75
Mass generation from data (CSV / API)Lolly: batch grid and CLI, plus lolly.work's recoverable server jobs and batches with retries, cancellation and retained outputs. Canva: Bulk Create + Autofill API - real, but Enterprise-gated, async, text/image fields only; Affinity Publisher adds desktop data merge. Adobe: InDesign data merge and scripting. Figma: Buzz fills templates from CSV or XLSX, free in beta (Aug 2026) - cloud- and account-gated, 50. Penpot: open API/MCP, not purpose-built. Render APIs: this is their entire product - account- and cloud-gated, so 75 under the method rule. DAM: Studio-style batch create and resize. Cloudinary: URL-driven overlays and named transforms derive variants at scale - account-gated, 75.
100 50 50 50 75 75 50 75
Offline & air-gap operationLolly: standalone creation works offline once its assets are available; lolly.work's organisation services need a reachable instance, which can run on an internal network. The Canva column is scored on its best family member: Affinity (free since Oct 2025) runs offline as a desktop suite once a verified account activates it - 75, the same deduction Adobe takes. Canva's own Offline (2026) still edits pre-synced designs only, per device, in a 14-day window. Adobe: desktop apps run offline; licensing phones home. Figma: cached-file viewing and limited editing. Penpot: self-host on your infra behind a firewall (server required). Cloud APIs and DAM: none.
100 75 75 75 0 0 25 0
On-device rendering - data never leavesLolly: local rendering and conversion in-browser or CLI need no upload. Choosing lolly.work's shared storage, work collabs or server rendering sends the relevant data to the configured instance. Adobe: local desktop rendering, but cloud services and telemetry in the suite. Figma: canvas renders locally, files live in Figma's cloud. Penpot: 90 - rendering happens in the browser and the save target is a server that can be your own sovereign private cloud, even your own laptop, with private export throughout; only the server hop separates it from Lolly. Canva itself is server-side by design; the column's 75 is Affinity - local desktop rendering, account activation and telemetry, the same shape as Adobe. Render APIs, DAM: server-side by design.
100 90 75 75 0 0 25 0
Hard brand constraints (structural)Lolly: templates fix layout rules and expose permitted inputs. lolly.work adds group access, input policies, shared catalogues, approvals and audit records. Copy and usage rights can still need review. Canva: element locks - permission-based, admin-set and static: no template logic such as conditional logo switching or responsive recomposition at fill time, and Canva AI does not yet respect Brand Controls - 50. Render APIs: templates are fixed (only declared fields vary) but no governance layer. DAM: locked templates with restrictions. Figma: Buzz templates hold locked guidelines at fill time; the design surface stays review-enforced - 50. Penpot: tokens and systems enforced by review, not runtime. Adobe: libraries, little enforcement. Cloudinary: presets and Enterprise-gated transformation templates fix operations, not brand layout - 50.
100 50 50 25 75 50 50 75
C2PA provenance, signed at creationAdobe: the broadest shipped implementation (Photoshop, Lightroom, Premiere, Firefly) - signing happens locally in the desktop apps and in the cloud for the Content Authenticity web app, and nothing signs without an Adobe account and an Adobe-provisioned identity, so it is account-gated end to end: 75 under the method rule. Lolly: optional on-device signing without an account, plus lolly.work server signing with a configured organisation identity. A verifier must trust the signer's root to show a trusted identity. Version 1.0 is public and SUSE runs it internally, but no usage data is shared yet, so 75. Penpot: 50 via the official Lolly Export plugin - the same on-device engine signing Lolly uses, opt-in, as in Lolly (and disclosed plainly: it is Lolly's own plugin). No evidence of C2PA writing in Canva, Figma, render APIs or DAM as of Aug 2026. Cloudinary: 50 - signs images on delivery (fl_c2pa, CAI member since 2020), attesting delivery by Cloudinary rather than creation by you.
75 50 0 75 0 50 0 0
CLI, pipeline & AI-agent automationLolly: CLI, TUI, MCP server and URL mode, plus lolly.work's authenticated render API, recoverable batches and governed delivery. One engine across the local and server paths. Render APIs: API-first, webhooks, integrations - account- and cloud-gated, so 75 under the method rule. Penpot: open API, plugins, official MCP server. Canva: Connect API - Enterprise-gated, async, rate-limited. Adobe: UXP/ExtendScript plus Firefly APIs. Figma: strong REST/plugin API, no render pipeline. DAM: asset-management APIs. Cloudinary: API-first media pipeline - account-gated, 75.
100 75 50 50 50 75 50 75
Live collaboration (real-time co-editing)Figma: the scale benchmark - 200 simultaneous editors, up to 500 people in a file - account and cloud required. Canva: mature real-time editing across the product, same precondition. Penpot: real-time multiplayer with live cursors; self-hosting lifts the vendor-account precondition, but a server is still required. Lolly: direct two-device private collabs without an account, plus authenticated work collabs through lolly.work with shared edits, cursors and saved team sessions. Partial reflects the current single-node work deployment and limited operating history. Private collabs work without internet on a shared network; work collabs need the organisation's instance. Adobe: Live Co-Editing in beta, cloud documents only. Render APIs: no editing surface. DAM: comments and approvals, not canvas co-editing. Cloudinary: DAM comments and workflows, no canvas co-editing.
50 75 75 25 25 25 75 0
No design skill requiredCanva: the category benchmark for ease. DAM: fill a locked template. Lolly: fill in fields using an existing tool, or publish a tool from Design through Share with rules. lolly.work manages access and input policy for the team. Render APIs: swaps design skill for developer skill. Adobe, Figma, Penpot: professional tools. Cloudinary: the media library is easy; transformations are developer territory.
100 25 100 25 100 50 25 50
Open source, self-hosted, no lock-inPenpot: MPL-licensed, 51k+ stars, self-host via Docker/K8s - fully verifiable today. Lolly and lolly.work: MPL-2.0 open source in separate repositories, both self-hostable. lolly.work provides the organisation services as part of the project. The current 75 reflects the young project and limited deployment history. Everything else is proprietary SaaS or proprietary desktop.
75 100 0 0 0 0 0 0
Cost at production scaleLolly + lolly.work and Penpot: no per-seat software licence charge for the open-source self-hosted path; hardware, storage and operations remain costs. Render APIs: metered - roughly $0.005–0.049 per image, credits often expiring monthly. Canva/Figma: per-seat; Canva's automation needs Enterprise, though Affinity itself is free since Oct 2025. Adobe: premium per-seat suite. DAM: sales-led enterprise quotes, MAU or seat models. Cloudinary: credit-metered across transforms, storage and bandwidth - 25.
100 100 50 25 25 25 50 25
Annual medium enterprise spendIndicative list-price arithmetic for a medium enterprise - think 500-5000 people - August 2026: a guess range, not a quote; deals vary and the seat mix dominates. Ring = how much of the board's largest annual spend (US$500k) you keep; the number in the ring is each range's upper bound. Canva (US$75k-400k; Affinity free since Oct 2025 pulls the design-seat share down), Adobe (US$100k-500k) and Figma (US$30k-250k) assume the realistic creative-seat share of an organisation this size - tens to hundreds of seats, not a licence for every employee. Render APIs price by usage (US$5k-60k). DAM is quote-only, commonly US$50k-250k and up. Penpot is hand-set at 99: "your server" can be your own laptop - a small technical hurdle at well under 1% of an Adobe-scale spend. Lolly + lolly.work: US$0 in software licence charges for self-hosting. The figure excludes devices, organisation hosting, storage, support and administration; these need a separate budget. Standalone Lolly does not need the organisation service. Cloudinary: US$30k-150k, credit-based; enterprise contracts commonly sit near US$82k.
$0 $0 $400k $500k $250k+ $150k $250k $60k

当前各类创意工具的能力完整度,调研于 2026 年 8 月。评分标准:0 分为不具备,25 分为需变通实现,50 分为具备但受限或部分实现,75 分为较强但有保留,100 分为核心能力。

signed by Lollypositioning-comparison.htmlHTMLpixels, not shapes40 KBAI generatedgenerated by GPT-6自己验证获取已签名文件

差距很明显:如今已经发布的产品中,没有一个能同时提供约束优先、可离线、低技能门槛、内部可及的产出。Lolly 甚至还包含一个开放画布 - Design - 其中颜色、字体和素材都遵循品牌全局设定,因此自由排版依然是约束优先的。它不是一套不受约束的设计套件:设计师制作定制化的旗舰内容时,仍然使用 Illustrator 和 Figma。用这个工具可以组合出各种排列。

库中每个工具都是一张卡片,按类别分组,方便制作者挑选并直接开始 signed by Lollyvector SVG367 paths~62k nodes773 groups3 images1,453 KB自己验证获取已签名文件库中每个工具都是一张卡片,按类别分组,方便制作者挑选并直接开始 signed by Lollyvector SVG367 paths~62k nodes776 groups3 images1,454 KB自己验证获取已签名文件

适用场景: 快速生成可运营化的创意素材 - 活动方块图、姓名徽章、签名、CVE 警报、二维码、社交卡片、托运标签、结构化报告。

不适用场景: 定制化的主视觉内容。


一场活动的生命周期

要看清 Lolly 究竟是什么,最清楚的方式不是一份功能清单 - 而是跟随一个素材,看它如何在人与人之间传递。来看一张本地化活动卡片在组织内部的流转过程:

  1. 创意人员制定规则。 设计师在 Design 工具中编写基础模板,将品牌的排版和色彩变量硬编码进去。他们做的不是一张卡片 - 而是把基础工作做一次,从此再也不用手动本地化。
  2. 开发者将其规模化。 同一个模板通过 CLI 接入夜间流水线,于是新的图表或新的语言版本会自动生成 - 没有设计师需要重新打开文件。
  3. 制作者直接使用它。 一位在飞机上离线的销售代表打开同一个工具,为客户会议生成一份完全符合品牌规范的演示文稿。不需要设计技能,不需要联网,不需要等待。

第二步中提到的“新图表”,就是像下面这样的渲染结果 - 由一段数据字符串和几个参数生成,全程没有任何人打开设计文件:

一张带标题的堆叠面积图,三个数据系列以冷色调分层显示,坐标轴、图例和标题均由模板自动排布,而非手动放置 signed by Lollyvector SVG15 paths~1.3k nodes4 groups20 KB自己验证获取已签名文件一张带标题的堆叠面积图,三个数据系列以冷色调分层显示,坐标轴、图例和标题均由模板自动排布,而非手动放置 signed by Lollyvector SVG15 paths~1.3k nodes4 groups20 KB自己验证获取已签名文件

重点不在于 Lolly 对设计师好用,而且对开发者好用,而且对销售也好用,三者各自孤立。它是一场接力赛:创意人员的初始工作被开发者放大规模,进而赋能制作者。飞机上那位非技术背景代表所体验到的毫不费力,之所以可能,正是因为设计师定下了严谨标准,开发者又将其部署落地。

这就是力量倍增器。Lolly 不是为不同角色准备的一堆各自独立的工具 - 而是一条每个角色都会经手的确定性素材生命周期,每经过一双手,前一步的价值就被再放大一次。


一个工具,多种产出

品牌规则存在于工具中,而不在它产出的每个文件里(参见Lolly 与其他方案的比较)。把工具做对一次,此后每一份产出都会继承相同的字体、颜色和间距,无论是一张卡片,还是一份跨十几种语言、逐行排列的电子表格。你如何检查自己的工作、由谁来检查,依然由你决定;Lolly 只是让值得检查的东西变小,产出做得更快。

同一个确定性工具通过三种方式达到这一规模,产出的结果完全一致:

全新安装下的批量模式:一行空白等待选择工具,整个表格界面及其 Render 按钮在任何数据到来之前就已就绪 signed by Lollyvector SVG133 paths~15k nodes185 groups1 image195 KB自己验证获取已签名文件全新安装下的批量模式:一行空白等待选择工具,整个表格界面及其 Render 按钮在任何数据到来之前就已就绪 signed by Lollyvector SVG133 paths~15k nodes185 groups1 image195 KB自己验证获取已签名文件

一套由设计师一次性定下的品牌约束;三条通往同一产出的路径。


全局视角:各层如何衔接

从这里往下的内容都是架构。这张图把整个系统一览无遗:工具在最上层 是数据,中间的引擎对任何平台都一无所知,它下方的宿主层 实现同一份契约,而目录则提供内容。

                ┌─────────────────────────────────────────────┐
                │              Tools (data, not code)         │
                │   tool.json + template.html + hooks.js?     │
                └─────────────────────────────────────────────┘
                                    ▲
                                    │ talks to via Capability Bridge v1
                                    ▼
                ┌─────────────────────────────────────────────┐
                │                  Engine                     │
                │   loader · validator · runtime · template   │
                │   inputs · url-mode                         │
                │   PLATFORM AGNOSTIC. Knows nothing of DOM,  │
                │   filesystem, or You.                       │
                └─────────────────────────────────────────────┘
                                    ▲
                                    │ implements HostV1
                                    ▼
        ┌──────────────┬──────────────┬──────────────┬──────────────┐
        │  Web Shell   │ Tauri Desktop│ Tauri Mobile │  CLI Shell   │
        │   (PWA)      │              │              │              │
        └──────────────┴──────────────┴──────────────┴──────────────┘
                                    ▲
                                    │ fetches from
                                    ▼
                ┌─────────────────────────────────────────────┐
                │              Catalogs                       │
                │   catalog/tools/index.json + tool dirs      │
                │   catalog/assets/index.json + asset files   │
                └─────────────────────────────────────────────┘

仓库结构

Lolly 是一个仓库。engine/、schemas/、scripts/、tests/、api/、docs/、community/、brands/lolly-start/、每个 shells/ 以及两个 services/ 都是其中的普通目录。唯一的例外是 brands/suse,一个私有的 git 子模块,存放 SUSE 工具包和目录,是可选加入的,在公开克隆中不存在。给定构建读取哪些工具包由内容配置文件(profiles.json)决定,按进程解析,而不是全局切换。克隆命令以及私有工具包内的更改如何提交,请参见构建指南 » 获取源代码。

lolly/
├── engine/           # Platform-agnostic core. Open source (MPL-2.0).
│   └── src/
│       ├── index.ts          # public surface - loader, runtime, template, inputs, url-mode
│       ├── loader.ts         # fetches and validates tool files
│       ├── runtime.ts        # orchestrates the 5-step lifecycle
│       ├── template.ts       # Handlebars hydration + annotateTemplate
│       ├── inputs.ts         # manifest → runtime input model
│       ├── url-mode.ts       # URL ↔ input state round-trip
│       ├── validate.ts       # JSON Schema validation of manifests
│       ├── compose.ts        # resolve nested tool renders (composes)
│       ├── embed.ts          # parse portable lolly.tools embed URLs
│       └── bridge/
│           └── host-v1.ts    # type re-export of the @lolly-tools/core contract
│
├── shells/
│   ├── web/          # PWA - hosted online; primary distribution
│   │   └── src/
│   │       ├── main.ts           # boot, routing
│   │       ├── theme.ts          # theme apply/persist (FOUC prevention)
│   │       ├── bridge/           # web implementations of HostV1 APIs
│   │       │   ├── index.ts      # compose all bridge pieces
│   │       │   ├── db.ts         # IndexedDB setup
│   │       │   ├── state.ts      # host.state - saved edits
│   │       │   ├── profile.ts    # host.profile - user details
│   │       │   ├── assets.ts     # host.assets - catalog + user uploads
│   │       │   ├── clipboard.ts  # host.clipboard
│   │       │   ├── export.ts     # host.export - rasterise/serialize
│   │       │   ├── net.ts        # host.net - allowlisted fetch
│   │       │   └── media.ts      # host.media - live camera frames (onFrame)
│   │       ├── catalog/
│   │       │   └── sync.ts       # boot-time catalog sync + offline cache
│   │       ├── styles/           # app-wide CSS (app.css, picker.css, tokens.css)
│   │       └── views/
│   │           ├── gallery.ts    # tool library listing + saved-state cards
│   │           ├── tool.ts       # mounts one tool (inputs + canvas + actions)
│   │           ├── picker.ts     # asset picker UI (invoked by host.assets)
│   │           ├── profile.ts    # user details editor
│   │           ├── projects.ts   # /p - folders of saved sessions (nested; folder/selection export)
│   │           └── free-canvas.ts # free-canvas editor overlay for render.layout:"editor" tools
│   │
│   ├── cli/          # Node.js CLI - same engine, headless jsdom
│   │   ├── bin/lolly.ts
│   │   └── src/
│   │       ├── run.ts    # loadTool → createRuntime → export → write file
│   │       └── bridge.ts # CLI implementation of HostV1
│   │
│   ├── tui/          # Interactive terminal shell (Ink) - reuses the CLI bridge
│   │   └── src/
│   │       ├── main.tsx  # full-screen app: Gallery / Projects / Profile / ToolView
│   │       └── bridge.ts # CLI bridge + on-disk state under ~/.lolly
│   │
│   ├── tauri-desktop/ # downloadable desktop app
│   └── tauri-mobile/  # iOS/Android app
│
├── community/        # the brand-agnostic tool pack - data, not code. Public (MPL-2.0).
│                     #   A SELECTION follows; a profile mounts these plus whatever
│                     #   tools the active brand pack carries of its own.
│   ├── qr-code/
│   ├── snippet/
│   ├── countdown-timer/
│   ├── color-palette/
│   ├── street-map/        # offline vector city-block maps
│   ├── url-shot/          # "URL Screenshot" (capture capability)
│   ├── strip-data/        # on-device metadata strip - JPEG/PNG/SVG/PDF (file in → clean file out)
│   ├── compress-pdf/      # on-device PDF compressor - recompresses images (file in → smaller file out)
│   ├── chart/             # SVG charts from structured data
│   ├── filter/            # photo effects in one tool - halftone/scanline/posterize/voronoi (vector), duotone/pixel-stretch/imperfections (raster)
│   ├── meeting-planner/   # global timezone meeting scheduler
│   ├── calendar-ics/      # event → .ics calendar file plus a card
│   ├── wayfinding-signage/ # event signage; directions blocks auto-fit label text
│   ├── text-helper/       # on-device text workbench (format/decode/hash/de-identify)
│   ├── design/     # "Design" - freeform WYSIWYG editor canvas (render.layout: editor)
│   ├── multi-page-pdf/    # multi-page PDF document - cover, flowing content blocks, back page
│   ├── diagram-builder/   # org / layercake / process / cycle / pyramid diagrams
│   ├── logo-wall/         # many logos → auto-packed grid
│   ├── icon/              # favicon .ico / png / svg from text + colours
│   └── lottie-digi-ad/    # animated Lottie ad banners
│
├── brands/            # brand packs - a catalog each, and optionally tools of their own
│   ├── lolly-start/   # the blank starter brand, owned here
│   │   └── catalog/
│   │       ├── tools/index.json    # tool registry, generated per brand
│   │       └── assets/
│   │           ├── index.json      # asset registry
│   │           └── lolly/...       # logo, palette, tokens
│   └── suse/          # PRIVATE submodule - the SUSE tools and the SUSE catalog
│
├── schemas/          # JSON Schema for tool.json, asset entries, AssetRef
├── scripts/          # build-catalog-index.ts, checksum-assets.ts, validate-catalog.ts
├── tests/            # engine tests
└── docs/             # this file + authoring guides + positioning

平台交付模型

本平台运行在多个界面之上 - web PWA、Tauri 桌面端/移动端、可脚本化的 CLI,以及交互式 TUI。它们全部使用同一个引擎和同一套工具文件。

Web(PWA) - 主要分发渠道

托管在 SUSE 掌控的 URL 上。一旦 service worker 缓存了工具和素材,即可离线使用。大多数员工、供应商和合作伙伴将在此处使用本平台。无需账号 - 状态按设备存储在 IndexedDB 中。

web 宿主是基于单一布局的响应式设计。桌面端上,工具由一个可调整大小的控件侧栏和一个预览舞台组成,支持触控板原生的画布导航(Cmd/Ctrl+滚轮或双指缩放以光标为中心,空格键或中键拖动平移,0/1/+/− 按键,以及一个 Fit/% HUD)。移动端(≤640px)上,控件变为一个顶部锚定的面板,带有拖拽把手,可在窥视/半屏/全屏之间吸附(点击切换),覆盖在静态的全屏预览之上;一个悬浮的 Render 按钮会在底部弹出面板中打开 Export 控件。触控操作可在预览上双指缩放和拖动平移。两端的渲染路径和导出控件完全一致 - 只有外围界面会重新排布。

桌面分栏视图 - 左侧是根据清单生成的控件,右侧是实时画布 signed by Lollyvector SVG131 paths~6.1k nodes158 groups1 image142 KB自己验证获取已签名文件

同一个工具在手机宽度下,无需维护第二套布局:控件变成顶部的面板,预览占据整个屏幕,渲染按钮悬浮其上。

430px 宽屏幕上的 Audiogram——上方是控制面板,下方是成品方形作品,还有浮动的渲染按钮 signed by Lollyvector SVG97 paths~3.3k nodes113 groups99 KB自己验证获取已签名文件430px 宽屏幕上的 Audiogram——上方是控制面板,下方是成品方形作品,还有浮动的渲染按钮 signed by Lollyvector SVG97 paths~3.3k nodes113 groups100 KB自己验证获取已签名文件

批处理模式(/pro)。 Web 端还提供一个电子表格式的批处理网格(shells/web/src/pro/),可跨一个或多个工具一次渲染多行。它支持 CSV/TSV 往返导入导出以及电子表格粘贴、按行设置模板/格式/尺寸/单位/dpi、带实时预览的区块编辑器侧边栏、可折叠的导出列、按行的"相关性"标签栏、左侧拖拽手柄行重排、两步删除确认、已保存的批处理会话和 .zip 下载。这就是"批量内容生成"定位背后的一对多界面。

Tauri 桌面端 / 移动端

打包的原生应用(通过 Tauri 实现小体积)。提供完整的离线可用性、面向依赖 CLI 的工具(PDF Smasher、Font Outliner)的文件系统访问,以及摄像头访问。计划于 2026 年年中进行工具增强。

CLI

lolly <tool-id> [--input=value ...] --output=file.png

桌面用户可以从终端调用许多工具。CLI 端加载同一个引擎,创建一个 jsdom DOM,运行同一条渲染路径并写出文件。URL 模式就是传输方式 - CLI 不是一个独立的实现。这保证了 CLI 和 GUI 的输出完全一致。

lolly qr-code --url=https://suse.com --output=qr.svg
lolly quotes --quote="Ship it." --output=quote.png
lolly                        # lists available tools
lolly qr-code                # lists inputs for that tool

TUI

pnpm run tui

CLI 的交互式对应物:一个全屏、以键盘为先的终端应用(基于 Ink 构建),用于浏览工具、填写输入、保存项目和导出 - 全程无需 GUI。它的宿主桥接层对无 DOM 的格式(SVG/EMF/EPS/HTML + 文本/数据)复用了 CLI 的实现,并在 ~/.lolly 下增加了磁盘状态存储,以及一个可选的行内预览。除此之外,它还有一个浏览器渲染层:一个受限的无头 Chromium(与 MCP 服务器安装的是同一个),按需生成光栅图/PDF/视频以及实时 URL 抓取 - 驱动一份构建好的 Web 端副本,使输出保持一致,并且只在你首次导出此类格式时才启动。因此 url-shot(带裁剪 + 重新配色 + 矢量 PDF/SVG)以及每一个光栅/pdf 工具也都能在终端中运行。参见 TUI 指南。

无论你身处哪个端,仪表盘的"Capabilities"标签页都是该平台所声明能力的完整地图,已分组整理,无需打开任何一个工具即可阅读。


工具分类

工具在其清单中通过 category 标记,用于画廊分组。

各行按画廊分区顺序列出。utility 分区在画廊中始终最后渲染(排在所有其他分类之后,包括未来新增的分类)- 它是本地设备上的"离线工具"抽屉。

类别示例计划中
everyoneQR Code Generator, Quote Card, Email Signature, Logo, Wordmark, Audiogram, Battlecards, Sequence, RecordEmployee Image Stationery
designerBrand Lockup, Design, Chart, Darkroom, Filter, Pose Geeko, BookletFont Outliner
eventMeeting Planner, Event Name Badge, Wayfinding Signage, Calendar ICS, Booth StudioEvent Stationery, Bulk Name Badges, Room Agenda Cards
product-CVE Alert, Product Release Announcement, Blog OG Image
utilityStrip Hidden Data, Text, Compress PDF, Convert Image, Convert Font, Redact, Run Web Code, Screen Capture, URL Screenshot单位/格式转换器,更多本机隐私实用工具

这些单元格是示例,而非清单。存在哪些工具取决于你挂载的 profile,而不取决于本页面:品牌包可以添加自己的工具,也可以排除某个它不想发布的社区工具。catalog/tools/index.json - 由清单生成,也是画廊实际读取的注册表 - 才是权威列表;要统计某个 profile 挂载了多少工具,应统计清单文件本身(ls community//tool.json brands//tools/*/tool.json),而不要相信写在这里的数字。(同一个工具 id 若出现在两个包中,只会挂载一次,以胜出的包为准。)

工具还按状态分类:official(品牌已批准,无水印)、community(外部贡献)、experimental(导出带水印)。大部分工具库是 official;较新的工作室类工具和抓取类工具在稳定下来之前往往处于 community 或 experimental 状态。每个端都会显示该徽章,让读者在打开工具之前就知道自己拿到的是什么 - 而且,和上面的分类单元格一样,各状态下具体包含哪些工具变化太快,这里就不一一列举了。请以画廊或生成的索引为准。

Design 是第一个基于 render.layout: "editor" 自由画布模式构建的工具 - 一个无外壳、直接操作的界面,你可以拖动、缩放、旋转和吸附文本、形状和图片方框,然后通过与其他工具相同的渲染路径导出。

Strip Hidden Data 是第一个本机实用工具(privacy: "on-device"):一个内容转换工具,接收 你 提供的文件,完全在浏览器中处理,并返回一份干净的副本 - 从不上传,从不加水印,不附加溯源信息。Text 是第二个 - 一个用于日常粘贴到网站类工作的本机工作台(JSON 格式化、JWT 解码、Base64、URL 编码/解码、SHA 哈希)。Compress PDF 是第三个 - 它通过重新压缩 PDF 中的图像来缩小体积,同样完全在本机完成。这个标记及其徽章文字 "Runs on your device - nothing is uploaded"(在你的设备上运行 - 不会上传任何内容)现在涵盖了整个转换工具集:Strip Hidden Data、Text、Compress PDF、Convert Image(HEIC/TIFF/AVIF → WebP/JPG/PNG)、Convert Font、Redact(销毁图像、SVG 或 PDF 中的区域)、Prompt Card 以及 Rebrand(在配置文件挂载该工具时原地重新设定 .pptx 的主题)。这是一个隐私实用工具类别,用来替代将机密文件交给单一用途的网站处理。

"工具"抽屉,其中每张卡片都是一个用来转换你已有文件的工具 signed by Lollyvector SVG124 paths~29k nodes324 groups395 KB自己验证获取已签名文件"工具"抽屉,其中每张卡片都是一个用来转换你已有文件的工具 signed by Lollyvector SVG124 paths~29k nodes324 groups396 KB自己验证获取已签名文件

注意:category 和 status 会从每个 tool.json 反规范化到 catalog/tools/index.json(画廊读取的注册表)中。清单文件是唯一真实来源 - 该索引由 pnpm run build:catalog 生成,如果已提交的索引与清单文件不一致,pnpm run validate:catalog 会使 CI 失败。


架构承诺

这些决策已经确定。改变其中任何一项都是一项重大工程 - 它们塑造了代码库中的每一个其他决策。

1. 声明式工具,辅以命令式逃生舱

一个工具由一个清单(tool.json)+ 一个模板(template.html)+ 可选的 hooks.js 组成。

输入由清单声明。 而不是模板。输入不会从 Handlebars 标记中推断得出。清单是契约;模板通过 {{id}} 消费具名变量。

Street Map 的控件堆栈 - 城市下拉菜单、主题选择、粗细滑块和颜色触发器,每一个都源自清单中的一行 signed by Lollyvector SVG39 paths~2.6k nodes54 groups2 images43 KB自己验证获取已签名文件Street Map 的控件堆栈 - 城市下拉菜单、主题选择、粗细滑块和颜色触发器,每一个都源自清单中的一行 signed by Lollyvector SVG39 paths~2.6k nodes54 groups2 images43 KB自己验证获取已签名文件

Hooks 是可选的。 大多数工具是纯声明式的 - 清单 + 模板就足够了。需要计算值的工具(QR 编码、图表数据整形)提供 hooks.js,暴露具名的生命周期函数(onInit、onInput、onFrame - 面向运动响应式工具的逐帧实时摄像头钩子 - onLevel、beforeExport、afterExport、exportFile - Strip Hidden Data 等本地设备工具使用的文件进/文件出转换路径 - 以及 exportStill,供拥有自己的深度光栅化流程的工具使用)。宿主通过 new Function('host', …) 加载 hooks,并将能力桥接层作为闭包作用域注入。这是一份可移植性契约,而不是一个安全沙箱:hooks 仍然运行在页面 realm 中,在浏览器端可以触及 window/fetch/document - host. 是受支持的可移植接口,而不是强制边界。异步 hook 的结果有时间限制(onInit 5 秒、onInput 2 秒、beforeExport/afterExport 5 秒、exportFile/exportStill 10 秒),超时的结果会被丢弃;失控的同步*钩子则无法被抢占。因此,不受信任的第三方 hook 代码在 Worker 隔离上线之前都不能安全运行。

这一点之所以重要,是因为:声明式工具可以由非开发者编写。如果每个工具都是一个 Web 应用,那么"创建/维护主力模板的技能有限"这条风险说明就会变成一个永久性瓶颈。

2. 工具和资产是数据,而不是打包代码

Web 和 Tauri 应用在启动时从已知 URL 拉取工具和资产目录,将其缓存到本地,并基于当地已有的内容运行。添加一个新的活动图块或季节性资产不需要发布新版应用。

资产字节通过 SHA-256 校验和来防止 CDN 投毒。资产的 id + version 驱动缓存失效。

3. 能力桥接层是工具所能看到的唯一 API

工具从不触及模板区域之外的 DOM,从不直接调用 fetch,也从不读取文件系统。它们调用带版本号的 host.* 方法。这份契约的权威定义位于 packages/core/src/host-v1.ts - 即工具作者 SDK @lolly-tools/core,因此第三方可以在不依赖引擎的情况下基于它构建;engine/src/bridge/host-v1.ts 是它的类型再导出,引擎/端代码仍从该路径不变地导入:

桥接 API作用
host.profile用户的名字、邮箱、头像、城市等。通过 bindToProfile 预填输入。
host.assets目录查询、资产解析、宿主提供的选择器 UI。
host.state保存/加载输入槽位。Web 端用 IndexedDB,Tauri 端用文件系统,CLI 端用内存。
host.clipboard将文本或图片写入剪贴板(带平台回退方案)。
host.export光栅化或序列化渲染目标。对 experimental 状态的工具应用水印。
host.net白名单内的 fetch - 仅在工具声明了 "network" 能力时可用。(目前没有任何已发布的工具使用它。)

可选的、增量式接口只有在某个端提供它们时才会出现。其中一些是受能力开关限制的- 仅在工具声明了相应标志时才会暴露:host.compose(嵌入另一个工具的渲染 - compose)、host.capture(供 URL Screenshot 使用的页面抓取 - capture)以及 host.recorder(供录制类工具使用的麦克风/摄像头/屏幕捕获 - microphone / camera / screen)。其余的则是特性检测式的- 只要该端能够提供就会出现,工具会为不能提供的端保留一个回退方案。

这里列出少数几个代表性接口,展示其覆盖范围 - Host API 记录了每一个,packages/core/src/host-v1.ts 就是契约本身:

接口起始版本提供的功能
host.tokens1.0DTCG 设计令牌——品牌自身的基础元素
host.text1.0通过 HarfBuzz WASM 实现文本转路径(wasm 能力标记依赖它的工具)
host.media1.4驱动 onFrame 钩子的实时摄像头帧。属于渐进增强,特意不受 camera 标志门控——这类工具在没有摄像头时仍能作为普通静态图像工具正常工作
host.color1.40感知色彩数学:ΔEOK、WCAG + APCA 对比度、OKLab 渐变、分级断点、分类调色板、和谐配色方案(1.60)、CSS Color 4 混色与渐变烘焙(1.68)。纯函数且同步——外壳只需接入引擎的 makeColorApi(),无需自行实现任何逻辑,因此不会产生偏差
host.images1.60在设备端解码 / 缩放 / 重新编码字节——即转换路径(HEIC → JPEG、压缩为 WebP、降采样)。在 Web 外壳中以惰性门面(lazy facade)形式提供,因此 HEIC 解码器永远不会进入启动分块
host.geom1.64精确的矢量几何运算:路径布尔运算、偏移、描边转填充、样条降阶、简化、命中检测。同样是纯函数、同步,并从引擎接入(makeGeomApi());失败情况会被返回,而不是被抛出

其余接口遵循相同的规则,并与之一并记录:pdf(1.8)和 pptx(1.58)用于本地设备文档处理,audio(1.71)和 speech(1.96)用于片段分析和本地设备 TTS/转录,viz(1.72)用于 MilkDrop 占位契约,codec(1.100)和 layers(1.102)用于深位色和分层位图输出,upscale(1.101)和 matte(1.103)用于本地设备模型,raster(1.105)供自行处理像素的 hooks 使用,connectors(1.106)用于导出安全的箭头,c2pa(1.85)用于对最终字节签名。数量在增长;规则不会变。

可声明的能力有:network、filesystem、clipboard、camera、microphone、screen、ffmpeg、wasm、capture、compose。(screen 于 1.54 加入,是通过 host.recorder 进行的屏幕捕获 - 用户在浏览器原生 UI 中选择屏幕/窗口/标签页;这与 capture 不同,后者是对工具自己指定的 URL 进行光栅化。)

同一个工具能在浏览器、Tauri 和无头 CLI 中运行,是因为每个端都实现了这个接口 - 工具本身永远不知道自己身处哪一个端。

这个桥接层是带版本号的。新增方法属于次版本更新。移除或改变签名则需要主版本号升级。当 v2 发布时,v1 必须继续可用。

4. 资产 ID 是永久性的

suse/logo/primary 是一份契约。一旦发布:

这使得已保存的工具状态和通过 URL 分享的链接能够历经数年依然有效。

5. URL 模式是一等公民

每一个输入都必须能够表示为一个 URL 参数:

lolly.tools/#/tool/qr-code?url=https://suse.com&ecl=H

仅凭这个链接本身,不需要其他任何东西,就是完成的成品资产 signed by Lollyvector SVG55 paths~2.6k nodes38 groups60 KB自己验证获取已签名文件仅凭这个链接本身,不需要其他任何东西,就是完成的成品资产 signed by Lollyvector SVG55 paths~2.6k nodes38 groups61 KB自己验证获取已签名文件

CLI 模式就是换了一种传输方式的 URL 模式 - CLI 端从 argv 构建一个 URL 状态对象,并运行同一条引擎管线。只有一条渲染路径。CLI 不可能与 GUI 产生偏差,因为它根本不是一个独立的实现。

url-mode.ts 负责这个往返过程(解析与序列化)。有一组保留参数永远不会作为输入转发给工具:输出控制类(format、export、copy、filename、width/w、height/h、unit、dpi)、印刷与来源相关的旋钮(bleed、marks、profile、password、c2pa、imprint、durable、meta、hdr、depth、cuts),以及状态载体(template、z - "最短链接"打包令牌 - 和 zx,即同一令牌以密码加密后的版本)。engine/src/url-mode.ts 中的 RESERVED 集合是权威定义,并由一个测试固定住;URL 模式 记录了它们中的每一个,包括这里未列出的少数几个。URL 模式中的资产类输入以其 id 序列化;运行时会在填充之前通过 host.assets.get() 解析它们。width/height 是以 unit 为单位的数值(默认为 px,也可以是 mm/cm/in/pt/pc);使用物理单位时,dpi 决定光栅分辨率。它们设置画布文档尺寸,并预填导出尺寸面板。

因为每一个输入都随链接一起传递,参数的改变就意味着一份不同的成品资产。整个这套调色板,不过是一个种子色、一种和声方案和一个阶梯数量:

四种色相下的九个色阶,皆由链接中携带的同一个种子颜色生长而来 signed by Lollyvector SVG10 groups25 KB自己验证获取已签名文件四种色相下的九个色阶,皆由链接中携带的同一个种子颜色生长而来 signed by Lollyvector SVG10 groups25 KB自己验证获取已签名文件

6. 存储通过桥接层进行,而非直接访问

Web 端:IndexedDB。Tauri:文件系统。CLI:内存中。工具只能看到 host.state.save(slot, data) 和 host.state.load(slot)。不使用 localStorage - 它太小,无法存放二进制数据(blob)。

用户可以为每个工具保存多个具名的编辑槽位,之后再返回各个会话。无需创建账户;状态是按设备保存的。由于桥接层是唯一的接缝,这种按设备保存的状态也是可移植的:shells/web/src/data-transfer.ts 通过 host.profile/host.state/host.assets 把所有内容读出,打包成一个 lolly-backup 压缩包,可导入到任何其他安装环境中 - 这是无需服务器就能实现“迁移到新设备”的离线方案(完整规范见 docs/data-transfer.md)。多设备同步已经建立在这之上,把同一个包发送到用户选择的存储位置(docs/sync.md)。

7. 成熟度标签按设计说明工具是什么

每个工具都在其清单中声明 status: official | community | experimental。图库按状态排序。实验性工具会自动为导出内容加水印 - 水印由 host.export.render 添加,而非工具本身,因此非官方工具作者无法选择关闭它。

这是对“图库中每个工具分量相同”这一感知风险的结构性回应。团队围绕目录建立的任何流程都叠加在其之上。

8. 工具输入通过清单进行类型化,资产也不例外

输入声明一个 type:text、longtext、number、boolean、color、select、asset、date、time、datetime-local、url、blocks、vector、table 和 file。宿主环境会根据清单为每种类型渲染通用控件 - 工具本身不需要编写任何控件代码。(从用户资料预填不是一种类型 - 任何输入都可以携带 bindToProfile。)其中三种的分量比其余更重:

9. 模板是无逻辑的(使用 Handlebars,而非 EJS)

选择 Handlebars 而非 EJS 是刻意为之:

逻辑存放在 hooks.js 中,明确且可审查。可用的 Handlebars 辅助函数有:{{default}}、{{upper}}、{{lower}}、{{eq}}、{{markdown}}、{{asset ref}}、{{asset ref "property"}}(此外还有供同级 .ics/.vcf/.csv 模板使用的数据格式辅助函数 icsStamp/rfcText/csvCell)。

10. 工具可以组合其他工具

一个工具可以嵌入另一个工具的渲染结果,而无需工具间的相互导入 - 组合由引擎解析,而不是由工具代码解析。这有两种方式:

可以组合任意工具的渲染结果:当父级导出为 SVG 或 PDF 时,SVG 子项仍保持真正的矢量形式,导出为 PNG 时则清晰栅格化;PNG/JPG/WEBP 子项以图像形式嵌入。需要 compose 能力。被组合的子项是中间产物 - 从不加水印或标注溯源信息 - 且组合过程会优雅降级:如果某个宿主环境无法渲染某个子项,就直接省略该槽位,父级仍然照常渲染。


我们明确选择不做的事


一次渲染的完整生命周期

用户打开 lolly.tools/#/tool/qr-code?url=https://suse.com&ecl=H:

  1. 启动(Boot)。 Web 端打开 IndexedDB,构建能力桥接(capability bridge),同步工具和资源目录(离线时从缓存加载)。
  2. 路由(Route)。 URL 哈希 → tool 视图,并提取 qr-code 和 URL 参数。
  3. 加载(Load)。 loadTool('qr-code', fetchFile) 获取 tool.json,依据 JSON Schema 进行校验,并获取 template.html、styles.css 和 hooks.js 源码。
  4. 解析 URL 状态。 parseUrlState 将 URL 参数转换为初始输入值。资源引用(?logo=suse/logo/primary)被解析为轻量级的 { id, _unresolved: true } 对象。
  5. 运行时(Runtime)。 createRuntime(tool, host, initialValues) 构建输入模型(合并资料数据、默认值和初始值),通过 host.assets.get() 解析资源引用,加载 hooks(闭包作用域内的 host,并非沙箱),调用 hooks.onInit。
  6. 渲染(Render)。 Shell 订阅运行时;每次状态变化都会收到 { model, hydrated }。它根据模型渲染输入控件,并将水合后的模板 HTML 写入 #tool-canvas。
  7. 交互(Interact)。 用户在输入框中输入 → runtime.setInput(id, value) → 应用约束 → 调用 hooks.onInput → 重新水合 → 重新渲染。画布实时更新。
  8. 导出(Export)。 用户点击“下载(PNG)” → runtime.export(canvasNode, 'png') → host.export.render(通过 dom-to-image-more 光栅化;SVG/PDF 则通过专用的 DOM 遍历矢量化器)→ blob → host.export.download。工具可以选择加入的格式范围很广,schemas/tool.schema.json 中的 render.formats 枚举是这方面的权威定义 - 光栅图和浮点光栅图、矢量图和裁切文件、印刷/CMYK、动态、可编辑文档(pptx、docx、odt)、调色板以及数据/文本输出、音频和字体文件。URL Mode 列出了每个 id 及其产出内容。音频和其他类型一样也在该枚举中(wav、mp3、m4a、opus,由 audiogram 和录制类工具声明);此外,录制工具的 render.capture 模式会驱动 host.recorder,其录制结果以浏览器所录制的任意容器格式的完整 Blob 形式返回。(设置了 render.export: false 的工具 - 例如 Color Palette、Countdown Timer、Strip Hidden Data、Text、Compress PDF - 会隐藏下载/格式/尺寸控件。)物理单位在此处按格式转换(PDF → 真实页面点数,光栅图 → 按 DPI 转换为像素并带有 pHYs 数据块)。作者/溯源元数据(作者、工具、来源 - 由 engine/src/metadata.ts 构建)按格式嵌入:PNG iTXt、JPEG EXIF、PDF 信息字典、SVG <metadata>、GIF 注释。实验性工具会由宿主(host)而非工具本身插入水印。

?options 打开的导出面板:文件名与格式配对、输出尺寸,以及写入文件的控件 signed by Lollyvector SVG77 paths~6.0k nodes87 groups3 images88 KB自己验证获取已签名文件?options 打开的导出面板:文件名与格式配对、输出尺寸,以及写入文件的控件 signed by Lollyvector SVG77 paths~6.0k nodes87 groups3 images88 KB自己验证获取已签名文件

在 Tauri 中生命周期相同。在 CLI 中生命周期也相同 - jsdom 提供无头 DOM;输出写入文件或 stdout。


开源状态

代码采用 MPL-2.0 许可。 engine/、shells/、services/、schemas/ 和 docs/*.ts 均以 MPL-2.0 开源 - 这是一个面向品牌工具的、厂商中立的脚手架平台,全部内容都在同一个公开仓库中,lolly-tools/lolly。

工具内容以品牌包(brand pack)形式分发,每个品牌包都有各自的条款(参见该包的 NOTICE.md)。community/ 是本仓库中的一个目录,其品牌无关的工具同样采用 MPL-2.0。brands/suse/ 是私有的 suse-lolly 包,也是唯一的子模块:包含 SUSE 工具和 SUSE 目录,归 SUSE 专有,包括其授权的 PremiumBeat 音乐。brands/lolly-start/ 是本仓库拥有的空白起始品牌。字体以 SIL 开放字体许可证 1.1(SIL Open Font License 1.1)的形式包含在品牌包中 - SUSE 包中含有 SUSE 和 SUSE Mono 字体。

仓库根目录下并不存在 tools/ 或 catalog/ 目录。packages/node-shell/src/content-roots.ts 会在读取时根据 profiles.json 回答“工具 <id> 位于何处”以及“目录在哪里”,因此配置文件(profile)是按进程给出答案的,不需要预先组装出一整棵目录树。只有在构建输出中 - dist/、RPM 包、容器镜像 - 才会写入真正的 tools/ + catalog/ 目录对,因为浏览器需要通过 HTTP 获取这两个路径。

这种拆分被强制执行——engine/ 不会反向导入工具内容——因此平台与内容之间的边界始终保持整洁。


引擎的边界与宿主环境的起点

如果能用纯数据 + Handlebars 描述 → 引擎。 如果涉及 DOM、文件系统、网络或任何浏览器/操作系统 API → 宿主环境。

这条界线是刻意划清的。引擎是开源部分。任何了解 SUSE、特定平台或运行环境的内容都不会进入其中。

若要了解更深一层的细节,engine/README.md 列举了每个引擎模块及其职责,而威胁模型与信任边界则记录了这条界线同时作为信任边界的位置。