Records what was verified against quartz-community/explorer: the anchoring engine, both SPA globals, the component resource API and SCSS handling all survive v5 unchanged, and the worker needs no changes at all. The changes are mechanical - import paths, and layout position moving from quartz.layout.ts into the package.json quartz manifest. Also captures the open questions to resolve during the port, notably whether the anchoring root is still article.popover-hint, and a carry-over checklist so the v5 migration does not drop the worker or docs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
6.5 KiB
Migrating Inline Comments to a Standalone Quartz 5 Plugin
Status: planned Prerequisite: the site is migrated to Quartz 5 (see migrating) Companion: inline-comments-design.md
Why
In v4 this feature lives inside the site repo as a custom component
(quartz/components/InlineComments.tsx + inline script + SCSS), wired up by
hand in quartz.layout.ts. Porting it to another site means copying files,
which drifts.
Quartz 5 replaces that model: components are standalone Git repositories
installed with npx quartz plugin add. That is exactly the right shape for
this feature, and it turns "port it to another site" into one command.
Findings: what actually changes
Verified against a real v5 community plugin
(quartz-community/explorer),
not just the docs.
Survives unchanged
- The whole anchoring engine.
inlineComments.inline.tsis plain DOM/TS whose only external import is@floating-ui/dom. - Both SPA globals we depend on. Confirmed present in v5 —
window.addCleanup(...)anddocument.addEventListener("nav", ...)are both used by explorer's own inline script. - The component resource API.
Component.css = styleandComponent.afterDOMLoaded = scriptare identical in v5, as issatisfies QuartzComponentConstructor. - SCSS.
tsup.config.tscompiles.scssthroughsassand.inline.tsthrough a nested esbuild pass, both loaded as text — same mental model as v4. - The worker.
serverless/inline-comments-worker/needs zero changes; it is independent infrastructure that only speaks HTTP.
Mechanical changes
| v4 | v5 |
|---|---|
import ... from "./types" |
from "@quartz-community/types" |
classNames from ../util/lang |
@quartz-community/utils/lang, or vendor a local copy (explorer vendors its own) |
import script from "./scripts/x.inline" |
"./scripts/x.inline.ts" with // @ts-expect-error |
Wiring in quartz.layout.ts |
quartz manifest block in package.json |
| Options as a TS object | YAML options: validated by optionSchema |
Do not import from
@jackyzha0/quartzorvfiledirectly — the v5 plugin docs call this out explicitly. Use the@quartz-community/*packages.
Target repo layout
Mirrors quartz-community/explorer:
quartz-inline-comments/
├── src/
│ ├── index.ts # export { default as InlineComments }
│ └── components/
│ ├── InlineComments.tsx
│ ├── scripts/inlineComments.inline.ts
│ └── styles/inlineComments.scss
├── types/globals.d.ts # addCleanup, CustomEventMap, *.scss module
├── package.json # deps + the `quartz` manifest block
├── tsup.config.ts # scss + .inline.ts esbuild loaders
├── tsconfig.json / tsconfig.build.json
└── README.md
The manifest
Layout position lives in package.json, not in any layout file:
"quartz": {
"name": "inline-comments",
"displayName": "Inline Comments",
"category": "component",
"quartzVersion": ">=5.0.0",
"defaultEnabled": true,
"defaultOrder": 50,
"components": {
"InlineComments": {
"displayName": "Inline Comments",
"defaultPosition": "afterBody",
"defaultPriority": 50
}
},
"optionSchema": {
"repo": { "type": "string" },
"repoId": { "type": "string" },
"category": { "type": "string" },
"categoryId": { "type": "string" },
"apiBase": { "type": "string" },
"mapping": { "type": "enum", "values": ["url", "pathname", "title"] }
}
}
All existing options are plain strings, so they survive the move to YAML unchanged — no option needs restructuring.
Dependencies
"dependencies": {
"@quartz-community/types": "github:quartz-community/types",
"@quartz-community/utils": "github:quartz-community/utils"
},
"peerDependencies": { "preact": "^10.0.0" }
@floating-ui/dom becomes a real dependency of the plugin (in v4 we relied on
it being a stock Quartz dep — a plugin cannot assume that).
Consumer experience
npx quartz plugin add github:logos-co/quartz-inline-comments
plugins:
- source: github:logos-co/quartz-inline-comments
enabled: true
options:
repo: logos-co/assembly
repoId: R_kgDOQUhKqA
category: Announcements
categoryId: DIC_kwDOQUhKqM4Cxur2
apiBase: https://inline-comments.inline-assembly.workers.dev
mapping: url
Any Quartz 5 site can then adopt this in one command. The worker can be shared
across sites (add the new origin to ALLOWED_ORIGINS, install the GitHub App on
the new repo, widen the read PAT) or deployed per-site.
Open questions to resolve during the port
- Is the anchoring root still
<article class="popover-hint">?getRoot()depends on it. Explorer is a sidebar component so it tells us nothing here. This is the single highest-risk unknown; if the markup changed it is a one-line fix, but it must be checked on a running v5 site. - Does
dist/need to be committed? Explorer commits itsdist/, andquartz plugin addinstalls over git rather than npm, which implies built output must be present in the repo. Confirm on first install; if so, add a CI workflow that builds and commitsdist/on release. - Does
optionSchemasupport required fields / defaults?repo,repoIdandapiBasehave no sensible default; ideally the schema can mark them required rather than failing at runtime. - Where should the repo live? Under
logos-co, or published toquartz-communityso other Quartz sites can use it.
Sequencing
- Migrate the site to Quartz 5 on a branch (production stays on v4).
- Build the plugin repo and validate against that branch.
quartz plugin addit, configure inquartz.config.yaml, verify end to end.- Cut production over.
The feature is offline between the moment quartz.layout.ts disappears and the
moment the plugin is installed — which is why the v5 work belongs on a branch.
Carry-over checklist
Beyond the component itself, these must survive the v5 migration:
serverless/inline-comments-worker/(unchanged, but must not be lost)docs-internal/(this file and the design doc).gitignorerules protecting.dev.varsand.wrangler/- Worker
ALLOWED_ORIGINSstill matches the production origin - CI deploy workflow retargeted from
v4to the new default branch