Skip to content

Latest commit

 

History

History
136 lines (104 loc) · 9.48 KB

File metadata and controls

136 lines (104 loc) · 9.48 KB

Koenig

Koenig is Ghost's post editor. This directory contains the editor UI and its supporting packages, merged into the Ghost monorepo from the former TryGhost/Koenig repo with full git history.

Everything here resolves via workspace: specs — nothing in dev, CI, or the release archive installs these packages from npm. The npm-published versions exist for external consumers only (see Shipping).

Packages

Editor UI

Package What it is Consumed by
koenig-lexical The Lexical-based rich text editor (React). Ships as a UMD bundle with styles and SVGs inlined ghost/admin (bundled into admin assets at build time), apps/admin, apps/admin-x-framework
kg-simplemde Customised fork of SimpleMDE, used by koenig-lexical's markdown card koenig-lexical
kg-unsplash-selector React Unsplash image picker koenig-lexical, apps/admin

Lexical node definitions & rendering

These are plain TypeScript Node libraries. kg-default-nodes is the single source of truth for node rendering — both the editor and the server render through it.

Package What it is Consumed by
kg-default-nodes Lexical node definitions for all of Ghost's cards, including each node's HTML renderer. Must stay browser-safe (it runs in the editor as well as on the server) ghost/core, koenig-lexical, most other kg-* packages
kg-default-transforms Lexical node transforms (denesting, blockquote children, etc.) shared between editor and server koenig-lexical, kg-lexical-html-renderer, kg-html-to-lexical
kg-lexical-html-renderer Renders a serialized Lexical state to front-end/email HTML server-side (not editor DOM — output differs per target, e.g. <table> markup for email) ghost/core
kg-html-to-lexical Converts HTML strings into Lexical editor state (imports, API ?source=html) ghost/core
kg-converters Converts between serialized Mobiledoc and Lexical formats ghost/core, ghost/admin

Shared helpers

Package What it is Consumed by
kg-clean-basic-html Sanitises/normalises snippets of "basic HTML" (card captions etc.) ghost/core, ghost/admin, kg-default-nodes
kg-markdown-html-renderer Markdown → HTML rendering for the markdown card ghost/core, kg-default-nodes, kg-default-cards
kg-utils Small shared utilities (slugify) kg-default-cards, kg-lexical-html-renderer, kg-markdown-html-renderer

Legacy (Mobiledoc era)

Still consumed by ghost/core to render posts that have never been converted from Mobiledoc. Avoid new work here.

Package What it is Consumed by
kg-card-factory Card definition factory for the Mobiledoc renderer ghost/core
kg-default-cards Mobiledoc card definitions ghost/core

Development

There is no linking or per-package install step — run pnpm setup once from the monorepo root and everything resolves through the workspace.

Working on the editor (koenig-lexical)

Two modes:

  • Standalonepnpm dev inside koenig/koenig-lexical starts a demo app on http://localhost:5173 with all features enabled. Fastest feedback loop; no Ghost required.
  • Integratedpnpm dev:lexical from the monorepo root starts the full Ghost dev environment plus a rebuild watcher for the editor (and kg-default-nodes / kg-default-transforms) with a preview server on port 4173. Ghost Admin at http://localhost:2368/ghost loads your local editor build; changes appear after the few seconds it takes to rebuild.

See koenig-lexical/README.md for card-specific setup (Klipy API key for the gif card, CORS notes for bookmark/embed cards) and styling conventions.

Working on the kg-* Node libraries

The libraries consumed by ghost/core declare a source export condition pointing at their raw src/*.ts, listed before the compiled build/ entries. ghost/core's dev runner and Vitest configs activate that condition, so a source change in a kg-* package is picked up by a running Ghost dev server and by core's tests with no tsc rebuild. Production and the published npm tarballs ignore source and use build/.

You only need pnpm build (Nx handles the dependency order) for type-checking, the browser lanes, and production artifacts — not for the edit/run/test loop against ghost/core.

Testing

  • kg-* Node libraries share a Vitest base config — vitest.shared.ts (createKoenigVitestConfig). Run pnpm test:unit in the package for unit tests, pnpm test:types for type checks, and pnpm lint for lint. pnpm test runs the package's configured test suites and enforces coverage thresholds; packages with a posttest hook also run lint automatically.
  • koenig-lexical has Vitest unit tests and a large Playwright browser acceptance suite: pnpm test:unit, pnpm test:acceptance (headless by default; :headed, :report, and test:slowmo variants for debugging). See koenig-lexical/AGENTS.md. Ghost's end-to-end suite is the separate top-level e2e/ workspace.
  • kg-unsplash-selector also has a Playwright suite (pnpm test:acceptance).

Shipping

Ghost itself never installs these packages from npm:

  • Dev and CI resolve them from the workspace (workspace:~).
  • Ghost Admin bundles the editor at build time: ghost/admin/lib/asset-delivery copies koenig-lexical's UMD build into the admin assets, served at /ghost/assets/koenig-lexical/.
  • The release archive embeds the kg-* packages as component tarballs — ghost/core/scripts/pack.js discovers them transitively from ghost/core's dependencies and packs each one, so a deployed Ghost installs exactly the versions it was built with. src/ is excluded from each package's files array, so raw source is never shipped.

npm publishing (external consumers)

The Publish Packages workflow runs on stable Ghost release tags (no -rc prereleases), in parallel with Ghost's own publishing workflow. Release commits consume pending changesets and commit the resulting package versions before the tag is created.

The workflow runs scripts/publish-packages.js, which:

  1. Selects every publishable workspace package, including the public Koenig packages.
  2. Builds the selected packages and their workspace dependencies.
  3. Publishes each committed version that is not already on npm, in dependency order, rewriting workspace: ranges to published versions.

For an out-of-band publish, the same workflow has a workflow_dispatch escape hatch that can be narrowed to one package. The package version must already have been bumped, normally by consuming a changeset; the workflow does not calculate a new version itself.