# Library Architecture and Workflow `library/`: builds `@penpot/library`; JS-facing in-memory Penpot file builder and `.penpot` ZIP exporter. Separate from main app runtime. ## Layout and commands - Source: `library/src/`; tests: `library/test/`; experimentation/docs: `playground/`, `docs/`; config: `shadow-cljs.edn`, `deps.edn`, `package.json`. - From `library/`: build `pnpm run build`; bundle helper `pnpm run build:bundle` or `./scripts/build`; tests `pnpm run test`; watch `pnpm run watch` / `pnpm run watch:test`; lint `pnpm run lint`; format check/fix `pnpm run check-fmt` / `pnpm run fmt`. - When changing file-format construction or export behavior in `common/`, consider whether `@penpot/library` should be tested because it constructs Penpot files outside the app UI. - Cross-cutting testing principles and anti-patterns: `mem:testing`. ## JS API and builder state - The JS build context wraps an atom and implements `IDeref`; `getInternalState` exposes the CLJ state converted to JS. - Public methods decode JS objects through the JSON transformer before calling common builder functions. Exceptions become JS `BuilderError` objects with enumerable `cause` and an `explain` getter for Malli explain data. - `create-build-context` can store an optional `referer`, later written into the export manifest. - The builder is stateful: call `addFile` before `addPage`. `addPage` resets the frame/group stack to the root and clears page-local naming state when the page closes. - `addBoard` and `addGroup` push onto the parent stack; matching close calls pop it. `closeGroup` requires at least one child and recalculates group geometry. Masked groups use the first child as mask and copy its geometry. - `commit-shape` emits `:add-obj` with `:ignore-touched true`, using the current parent, frame, and page from the stack. - Layer names are uniqued per current page; duplicate names get generated suffixes. - `addBool` converts an existing group into a bool shape and updates style/content/geometry via `:mod-obj` operations rather than adding a new object. - Media blobs are stored separately from file-media metadata; `add-file-media` requires a `BlobWrapper`. ## Export package - `.penpot` ZIPs include `manifest.json`, file/page/shape JSON, components/colors/typographies/tokens, media metadata, and media object blobs. - Path/bool shape `:content` is converted to vectors before JSON encoding. - File export intentionally includes only selected top-level attrs plus data options; color export removes `:file-id` and drops empty paths. - Manifest type is `penpot/export-files`, version 1, generated by `penpot-library/%version%`, with optional referer and file relations. - Export generation is sequential and lazy: delayed JSON/blob work is computed only as each zip entry is written, and the progress callback receives `{total,item,path}` after each entry. - The library has compatibility defaults for features/migrations in the common builder; do not assume it always exports with the newest app-default migrations/features.