本文档记录了 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 究竟是什么,最清楚的方式不是一份功能清单 - 而是跟随一个素材,看它如何在人与人之间传递。来看一张本地化活动卡片在组织内部的流转过程:
- 创意人员制定规则。 设计师在 Design 工具中编写基础模板,将品牌的排版和色彩变量硬编码进去。他们做的不是一张卡片 - 而是把基础工作做一次,从此再也不用手动本地化。
- 开发者将其规模化。 同一个模板通过 CLI 接入夜间流水线,于是新的图表或新的语言版本会自动生成 - 没有设计师需要重新打开文件。
- 制作者直接使用它。 一位在飞机上离线的销售代表打开同一个工具,为客户会议生成一份完全符合品牌规范的演示文稿。不需要设计技能,不需要联网,不需要等待。
第二步中提到的“新图表”,就是像下面这样的渲染结果 - 由一段数据字符串和几个参数生成,全程没有任何人打开设计文件:
signed by Lollyvector SVG15 paths~1.3k nodes4 groups20 KB自己验证获取已签名文件
signed by Lollyvector SVG15 paths~1.3k nodes4 groups20 KB自己验证获取已签名文件
重点不在于 Lolly 对设计师好用,而且对开发者好用,而且对销售也好用,三者各自孤立。它是一场接力赛:创意人员的初始工作被开发者放大规模,进而赋能制作者。飞机上那位非技术背景代表所体验到的毫不费力,之所以可能,正是因为设计师定下了严谨标准,开发者又将其部署落地。
这就是力量倍增器。Lolly 不是为不同角色准备的一堆各自独立的工具 - 而是一条每个角色都会经手的确定性素材生命周期,每经过一双手,前一步的价值就被再放大一次。
一个工具,多种产出
品牌规则存在于工具中,而不在它产出的每个文件里(参见Lolly 与其他方案的比较)。把工具做对一次,此后每一份产出都会继承相同的字体、颜色和间距,无论是一张卡片,还是一份跨十几种语言、逐行排列的电子表格。你如何检查自己的工作、由谁来检查,依然由你决定;Lolly 只是让值得检查的东西变小,产出做得更快。
同一个确定性工具通过三种方式达到这一规模,产出的结果完全一致:
- 一个人,在应用内。
/pro批量表格:粘贴或导入行数据,每行生成一个完整素材,下载 zip 包。不需要设计技能,不需要等待。 - 一位开发者,从命令行。 CLI 以无界面方式运行同一个引擎和同一条渲染路径,因此该工具可以在脚本或夜间流水线中逐行处理。循环中的一句
lolly <tool> --field=…调用就是全部集成工作。 - 一个系统或一个 AI 代理,通过 MCP。 同一个工具以编程方式运行,保真度相同,有多少行就处理多少行。
signed by Lollyvector SVG133 paths~15k nodes185 groups1 image195 KB自己验证获取已签名文件
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自己验证获取已签名文件
同一个工具在手机宽度下,无需维护第二套布局:控件变成顶部的面板,预览占据整个屏幕,渲染按钮悬浮其上。
signed by Lollyvector SVG97 paths~3.3k nodes113 groups99 KB自己验证获取已签名文件
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 toolTUI
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 分区在画廊中始终最后渲染(排在所有其他分类之后,包括未来新增的分类)- 它是本地设备上的"离线工具"抽屉。
| 类别 | 示例 | 计划中 |
|---|---|---|
everyone | QR Code Generator, Quote Card, Email Signature, Logo, Wordmark, Audiogram, Battlecards, Sequence, Record | Employee Image Stationery |
designer | Brand Lockup, Design, Chart, Darkroom, Filter, Pose Geeko, Booklet | Font Outliner |
event | Meeting Planner, Event Name Badge, Wayfinding Signage, Calendar ICS, Booth Studio | Event Stationery, Bulk Name Badges, Room Agenda Cards |
product | - | CVE Alert, Product Release Announcement, Blog OG Image |
utility | Strip 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}} 消费具名变量。
signed by Lollyvector SVG39 paths~2.6k nodes54 groups2 images43 KB自己验证获取已签名文件
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.tokens | 1.0 | DTCG 设计令牌——品牌自身的基础元素 |
host.text | 1.0 | 通过 HarfBuzz WASM 实现文本转路径(wasm 能力标记依赖它的工具) |
host.media | 1.4 | 驱动 onFrame 钩子的实时摄像头帧。属于渐进增强,特意不受 camera 标志门控——这类工具在没有摄像头时仍能作为普通静态图像工具正常工作 |
host.color | 1.40 | 感知色彩数学:ΔEOK、WCAG + APCA 对比度、OKLab 渐变、分级断点、分类调色板、和谐配色方案(1.60)、CSS Color 4 混色与渐变烘焙(1.68)。纯函数且同步——外壳只需接入引擎的 makeColorApi(),无需自行实现任何逻辑,因此不会产生偏差 |
host.images | 1.60 | 在设备端解码 / 缩放 / 重新编码字节——即转换路径(HEIC → JPEG、压缩为 WebP、降采样)。在 Web 外壳中以惰性门面(lazy facade)形式提供,因此 HEIC 解码器永远不会进入启动分块 |
host.geom | 1.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 是一份契约。一旦发布:
- ID 永远不会更改,也不会被重复使用。
- 字节发生变化 → 在清单中升级
version。 - 被新资产取代 → 设置
deprecated: true,可选地设置replacedBy。 - 现有引用永远能被解析。
这使得已保存的工具状态和通过 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。)其中三种的分量比其余更重:
asset(配合filter和allowUpload)是通往全局资产系统的桥梁;allowUpload: false是品牌强制执行的杠杆,用于赞助商图块徽标等只允许使用库内资产的场景。用户上传的资产与库内资产使用相同的AssetRef结构,因此工具能以相同方式处理它们。blocks是一个可重复的字段组 - 一个输入内部的小型表格,在侧边面板中编辑,带有类型化/可区分的添加菜单以及每个块的资产字段。点击画布上渲染出的块会聚焦到该块所在的行。被meeting-planner、chart-creator、event-name-badge、wayfinding-signage、color-block和digi-ad使用。vector把一组固定数量的数字(例如一个变换)归入一个复合控件;file把用户自己的文件以字节形式保存在内存中,供设备端转换类工具使用(例如strip-data和compress-pdf)。
9. 模板是无逻辑的(使用 Handlebars,而非 EJS)
选择 Handlebars 而非 EJS 是刻意为之:
- 无逻辑。模板可以由非开发人员编写。
- 默认安全。
{{x}}会进行 HTML 转义;{{{x}}}是需要显式选用的原始输出。 - 模板中不允许任意 JS,意味着不存在逐模板的 XSS 审计面。
逻辑存放在 hooks.js 中,明确且可审查。可用的 Handlebars 辅助函数有:{{default}}、{{upper}}、{{lower}}、{{eq}}、{{markdown}}、{{asset ref}}、{{asset ref "property"}}(此外还有供同级 .ics/.vcf/.csv 模板使用的数据格式辅助函数 icsStamp/rfcText/csvCell)。
10. 工具可以组合其他工具
一个工具可以嵌入另一个工具的渲染结果,而无需工具间的相互导入 - 组合由引擎解析,而不是由工具代码解析。这有两种方式:
- 声明式清单 -
composes: [{ id, tool, inputs, format?, width?, height? }]。引擎渲染指定的子工具,并将结果以{{asset <id>}}的形式放入无逻辑模板中。目前event-name-badge就以 SVG 形式组合了qr-code。 - 可移植的嵌入 URL -
<img src="https://lolly.tools/tool/<id>.<ext>?<inputs>">。宿主环境会在本地渲染该子项(在本地渲染结果就绪之前会显示一个占位像素);不会向lolly.tools发起任何请求。
可以组合任意工具的渲染结果:当父级导出为 SVG 或 PDF 时,SVG 子项仍保持真正的矢量形式,导出为 PNG 时则清晰栅格化;PNG/JPG/WEBP 子项以图像形式嵌入。需要 compose 能力。被组合的子项是中间产物 - 从不加水印或标注溯源信息 - 且组合过程会优雅降级:如果某个宿主环境无法渲染某个子项,就直接省略该槽位,父级仍然照常渲染。
我们明确选择不做的事
- 模板中没有 EJS,也没有任意 JS。 XSS 攻击面为零。逻辑都放在
hooks.js中。 - 没有强制性的素材 CMS。 个人可以直接在应用内把自己的创意文件导入素材(即 素材 和品牌工作室) - 无需服务器,无需管理后台。工作以会话的形式传递:一个分享链接携带其输入项和设置(上传的文件通过
.lolly传递),同一份会话也可以通过备份或协作会话传递。掌控该部署的人此后可以把一个共享会话锁定为模板 - 打开链接,把其中的值记录为品牌包中该工具目录下的一个模板条目并提交 - 之后它就会出现在该工具的“New from template”选择器中,并可通过?template=<id>深链访问。Git 是部署方的锁定步骤,而不是创作者的。若需要共享且受治理的目录,组织可以用同样的方式管理素材目录,并通过 PR 审查来把关更新 - 这是一种可选的治理模型,而不是应用的强制要求。 - 没有强制性的 RBAC。 该开放应用默认公开访问;品牌风险通过成熟度标签与水印来管理。想要更严格控制的组织,可以在上述基础上叠加自己的身份验证以及经 Git 审查的目录。
- 没有中央数据库。 所有用户状态都保存在本地设备上;可选的同步功能会把它复制到用户选择的存储位置,而不是 Lolly 的数据库。
- 没有共享的工具/引擎代码路径。 引擎是开源的,
community/中与品牌无关的工具同样开源;而像私有的brands/suse/这样的品牌包,则按其自身条款携带自己的工具和目录。无论哪种情况,这种分离都被强制执行(engine/不会反向导入工具内容),从而保持这种拆分的整洁。
一次渲染的完整生命周期
用户打开 lolly.tools/#/tool/qr-code?url=https://suse.com&ecl=H:
- 启动(Boot)。 Web 端打开 IndexedDB,构建能力桥接(capability bridge),同步工具和资源目录(离线时从缓存加载)。
- 路由(Route)。 URL 哈希 →
tool视图,并提取qr-code和 URL 参数。 - 加载(Load)。
loadTool('qr-code', fetchFile)获取tool.json,依据 JSON Schema 进行校验,并获取template.html、styles.css和hooks.js源码。 - 解析 URL 状态。
parseUrlState将 URL 参数转换为初始输入值。资源引用(?logo=suse/logo/primary)被解析为轻量级的{ id, _unresolved: true }对象。 - 运行时(Runtime)。
createRuntime(tool, host, initialValues)构建输入模型(合并资料数据、默认值和初始值),通过host.assets.get()解析资源引用,加载 hooks(闭包作用域内的host,并非沙箱),调用hooks.onInit。 - 渲染(Render)。 Shell 订阅运行时;每次状态变化都会收到
{ model, hydrated }。它根据模型渲染输入控件,并将水合后的模板 HTML 写入#tool-canvas。 - 交互(Interact)。 用户在输入框中输入 →
runtime.setInput(id, value)→ 应用约束 → 调用hooks.onInput→ 重新水合 → 重新渲染。画布实时更新。 - 导出(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)而非工具本身插入水印。
signed by Lollyvector SVG77 paths~6.0k nodes87 groups3 images88 KB自己验证获取已签名文件
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 列举了每个引擎模块及其职责,而威胁模型与信任边界则记录了这条界线同时作为信任边界的位置。