- JavaScript 99.5%
- TypeScript 0.5%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| .githooks | ||
| client | ||
| scripts | ||
| vendor | ||
| .env.example | ||
| .gitignore | ||
| compose.dev.yml | ||
| config.test.ts | ||
| config.ts | ||
| highlight-markdown.test.ts | ||
| highlight-markdown.ts | ||
| highlight_service_register.ts | ||
| limedoc.project.json | ||
| plugin.devkit.json | ||
| README.md | ||
| shiki-block.test.ts | ||
| shiki-block.ts | ||
| shiki-service.ts | ||
| shiki-styles.test.ts | ||
| shiki.plugin.json | ||
| shiki.ts | ||
| shiki.worker.ts | ||
| shiki_client.worker.ts | ||
| shiki_highlight_client.tsx | ||
| vendor.lock.json | ||
| worker_permissions.ts | ||
| worker_url.test.ts | ||
| worker_url.ts | ||
Shiki syntax highlighting
Pre-render language-hinted fenced code blocks with Shiki before marked.
Requirements
- Docker (or Podman with
DOCKER_CMD=podman) - That's it — everything else runs inside containers.
Quick start
# 1. Pull the dev image and set up .env
./ldcmd setup
# 2. Start the dev environment (app + Redis)
./ldcmd up
# 3. Open http://localhost:5173 in your browser
# 4. Edit plugin sources — HMR applies automatically
# 5. Run tests
./ldcmd test
# 6. Check types and import boundaries
./ldcmd check
Directory layout
shiki.plugin.json # Plugin manifest
shiki.ts # Server entry (defineLimedocPlugin)
shiki_editor_tool.tsx # Browser client (builds to client/*.js)
client/ # Pre-built browser bundles (committed)
limedoc.project.json # Store metadata (package download URLs)
compose.dev.yml # Dev compose (uses the published limedoc-dev image)
ldcmd # Bridge script — the only thing you run on your host
.env.example # Env var template — copy to .env and fill in OIDC values
ldcmd commands
| Command | What it does |
|---|---|
./ldcmd setup |
Pull limedoc-dev image, copy .env.example → .env |
./ldcmd up |
Start dev environment |
./ldcmd down |
Stop dev environment |
./ldcmd logs [service] |
Follow container logs |
./ldcmd sh [service] |
Open shell in container (default: app-dev) |
./ldcmd test |
Run plugin tests |
./ldcmd fmt |
Format sources |
./ldcmd lint |
Lint sources |
./ldcmd check |
Type-check + import boundary check |
./ldcmd pack |
Build zip + sha256 (CI also publishes sig.json) |
./ldcmd publish |
Pack and upload (needs .forgejo.env / PACKAGE_TOKEN) |
./ldcmd plugin install <id> |
Install a plugin dependency into the running env |
./ldcmd plugin ls |
List installed plugins |
Building client bundles
# Inside the container (run after editing .tsx files)
./ldcmd sh
deno task plugin:build -- /limedoc/plugins/shiki.plugin
Or directly:
./ldcmd check # runs build as part of the check
Packing and publishing
# Build the installable zip + sha256 sidecar (local)
./ldcmd pack
# Publish (needs .forgejo.env with PACKAGE_TOKEN, PACKAGE_OWNER)
./ldcmd publish
CI (Forgejo Actions, .forgejo/workflows/publish.yml) on push to main publishes
zip + sha256 + sig.json to Packages and Releases. Pack CI signs the
content hash with OpenBao Transit (Forgejo Actions OIDC). Actions secret
PACKAGE_TOKEN only — PACKAGE_OWNER is in the workflow env.
Developing with plugin dependencies
Your plugin may depend on another plugin at runtime. Three ways to include it:
1. Install via ldcmd (easiest, needs the app running)
./ldcmd up
./ldcmd plugin install media # fetches from PACKAGE_STORE_URL in .env
Set PACKAGE_STORE_URL in .env to point at the Forgejo package registry:
PACKAGE_STORE_URL=https://git.belowtoxic.cloud/api/packages/limedoc-plugins
2. Local bind-mount (no running app needed)
Download the pack zip, unzip it next to this directory, then add a volume to compose.dev.yml:
app-dev:
volumes:
- .:/limedoc/plugins/shiki.plugin
# Add the dependency:
- ../other-plugin.plugin:/limedoc/plugins/other.plugin
Then ./ldcmd up — the app loads all *.plugin directories in plugins/.
3. Multi-repo development (two plugins at once)
Check out the other plugin repo next to this one:
cd ..
git clone git@git.belowtoxic.cloud:limedoc-plugins/other.git other.plugin
Then add it as a bind-mount in compose.dev.yml as shown above. Both plugins
are mounted into the same app instance. No special ldcmd support required —
the app discovers every *.plugin directory at startup.
Startup order: The app loads plugins alphabetically. If your plugin
requiresanother, the dependency must be present on disk for the load plan to resolve. The composedepends_onin the sidecar template ensures the worker sidecar is healthy before the app starts, but for plugin-to-plugin dependencies no extra compose configuration is needed.
CI / CD
Push to main → Forgejo Actions runs .forgejo/workflows/publish.yml:
- Tests pass inside the
limedoc-devcontainer - Client bundles built; boundary check passes
plugin:packproducesshiki-{version}.zip+.sha256- OpenBao signs the content hash →
shiki-{version}.sig.json - Zip, sha256, and sig.json uploaded to the generic package registry (store)
- Same artifacts attached to a Forgejo Release
v{version}(operators)
The app installs plugins from the generic package registry at PACKAGE_STORE_URL.
Using Podman instead of Docker
# In .env (or export before running):
DOCKER_CMD=podman
./ldcmd up
ldcmd detects podman compose automatically.