Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
59674ad
feat: migrate to ep_plugin_helpers padToggle for User + Pad Wide sett…
JohnMcLear May 7, 2026
7e76a58
feat(client): add LRU cache helper
JohnMcLear May 7, 2026
1fe2804
feat(client): text-node wrapper helper (jsdom-tested)
JohnMcLear May 7, 2026
adaa36e
feat(client): hljs adapter returning {start, end, cls} ranges
JohnMcLear May 7, 2026
2a83522
lint: async-ify lruCache test callbacks (mocha/no-synchronous-tests)
JohnMcLear May 7, 2026
34a2b84
feat(client): syntaxRenderer module with cache + auto-detect
JohnMcLear May 7, 2026
272ffbb
feat: switch render path to acePostWriteDomLineHTML; remove v0.1 cont…
JohnMcLear May 7, 2026
8a45683
test: lift fixme on multi-user repro 2 (now passing)
JohnMcLear May 7, 2026
6ebdcdd
feat(client): highlightRegistry — wraps CSS Custom Highlights API
JohnMcLear May 7, 2026
e90a82c
feat: pivot to CSS Custom Highlights — no DOM mutation
JohnMcLear May 7, 2026
7678d8a
test: switch span.hljs assertions to CSS.highlights inspection
JohnMcLear May 7, 2026
6c373ef
test: single-line 'while' — covers caret + highlight in one keystroke
JohnMcLear May 7, 2026
ea7ce27
test: fix focus race + skip dark-mode (v0.3 follow-up)
JohnMcLear May 7, 2026
7e6623f
chore: bump to 0.3.0; rewrite README around CSS Custom Highlights
JohnMcLear May 7, 2026
9d98a6e
fix(client): cache safety + multi-class spans + initial paint
JohnMcLear May 8, 2026
73493cb
feat(client): code-editor indent / Tab / Shift+Tab
JohnMcLear May 8, 2026
b4a611c
feat(css): brighter light-mode blues + dark-mode palette + admin inde…
JohnMcLear May 8, 2026
d4ae832
test: codeIndent + hljsAdapter unit tests + initial-paint + dark-mode
JohnMcLear May 8, 2026
a09a590
docs: add CLAUDE.md — architecture + lessons from v0.1->v0.2->v0.3 pi…
JohnMcLear May 8, 2026
2604b79
feat: indent-size picker via padSelect helper
JohnMcLear May 8, 2026
9488d01
chore: prep v0.1.0 — version, deps, README, CI workflows
JohnMcLear May 8, 2026
402caa5
fix: CodeQL findings + add demo gif + README
JohnMcLear May 8, 2026
17ba683
test: mark two CI-flaky cases test.fixme
JohnMcLear May 8, 2026
3cae6c9
fix: scope plugin's editor side-effects to active-highlighting only
JohnMcLear May 8, 2026
0730f9e
test: codeIndent — cover the explicit-pick gating
JohnMcLear May 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/automerge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Dependabot Automerge
permissions:
contents: write
pull-requests: write
# `actions: write` lets the post-merge step kick off Node.js Package on
# the default branch via `gh workflow run`. Without this, automerge'd
# PRs land on main but the on-push release job never fires (GitHub
# Actions intentionally suppresses on:push triggers when the push is
# authenticated with GITHUB_TOKEN).
actions: write
on:
workflow_run:
workflows:
- Node.js Package
types:
- completed

jobs:
automerge:
if: >
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.actor.login == 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Automerge
id: automerge
uses: "pascalgn/automerge-action@v0.16.4"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MERGE_METHOD: squash
MERGE_LABELS: ""
MERGE_RETRY_SLEEP: "100000"

- name: Trigger release on default branch
# `pascalgn/automerge-action` exits 0 whether or not it merged. Skip
# the dispatch when nothing was actually merged so we don't kick a
# phantom release run on every Dependabot Automerge invocation.
if: steps.automerge.outputs.mergeResult == 'merged'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run test-and-release.yml --ref ${{ github.event.repository.default_branch }}
75 changes: 75 additions & 0 deletions .github/workflows/backend-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Backend Tests

# any branch is useful for testing before a PR is submitted
on:
workflow_call:

jobs:
withplugins:
# run on pushes to any branch
# run on PRs from external forks
if: |
(github.event_name != 'pull_request')
|| (github.event.pull_request.head.repo.id != github.event.pull_request.base.repo.id)
name: with Plugins
runs-on: ubuntu-latest
steps:
-
name: Install libreoffice
uses: awalsh128/cache-apt-pkgs-action@v1.6.0
with:
packages: libreoffice libreoffice-pdfimport
version: 1.0
-
name: Install etherpad core
uses: actions/checkout@v6
with:
repository: ether/etherpad-lite
path: etherpad-lite
- uses: actions/setup-node@v6
name: Install Node.js
with:
node-version: 22
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
version: 10
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v5
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
-
name: Checkout plugin repository
uses: actions/checkout@v6
with:
path: plugin
- name: Remove tests
working-directory: ./etherpad-lite
run: rm -rf ./src/tests/backend/specs
-
name: Install Etherpad core dependencies
working-directory: ./etherpad-lite
run: bin/installDeps.sh
- name: Install plugin
working-directory: ./etherpad-lite
run: |
pnpm run plugins i --path ../../plugin
-
name: Run the backend tests
working-directory: ./etherpad-lite/src
run: |
shopt -s globstar
res=$(find ./plugin_packages -path "*/static/tests/backend/specs/*" 2>/dev/null | wc -l)
if [ $res -eq 0 ]; then
echo "No backend tests found"
else
npx cross-env NODE_ENV=production mocha --import=tsx --timeout 120000 --recursive node_modules/ep_*/static/tests/backend/specs/**
fi
41 changes: 41 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: "CodeQL"

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
schedule:
- cron: "26 5 * * 2"

jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write

strategy:
fail-fast: false
matrix:
language: [ javascript ]

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
queries: +security-and-quality

- name: Autobuild
uses: github/codeql-action/autobuild@v4

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{ matrix.language }}"
72 changes: 72 additions & 0 deletions .github/workflows/frontend-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Publicly credit Sauce Labs because they generously support open source
# projects.
name: Frontend Tests

on:
workflow_call:

jobs:
test-frontend:
runs-on: ubuntu-latest

steps:
-
name: Check out Etherpad core
uses: actions/checkout@v6
with:
repository: ether/etherpad-lite
path: etherpad-lite
- uses: actions/setup-node@v6
name: Install Node.js
with:
node-version: 22
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
version: 10
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v5
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
-
name: Checkout plugin repository
uses: actions/checkout@v6
with:
path: plugin
-
name: Install Etherpad core dependencies
working-directory: ./etherpad-lite
run: bin/installDeps.sh
- name: Install plugin
working-directory: ./etherpad-lite
run: |
pnpm run plugins i --path ../../plugin
- name: Create settings.json
working-directory: ./etherpad-lite
run: cp ./src/tests/settings.json settings.json
- name: Run the frontend tests
working-directory: ./etherpad-lite
shell: bash
run: |
pnpm run dev &
connected=false
can_connect() {
curl -sSfo /dev/null http://localhost:9001/ || return 1
connected=true
}
now() { date +%s; }
start=$(now)
while [ $(($(now) - $start)) -le 30 ] && ! can_connect; do
sleep 1
done
cd src
pnpm exec playwright install chromium --with-deps
pnpm run test-ui --project=chromium
121 changes: 121 additions & 0 deletions .github/workflows/npmpublish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# This workflow will run tests using node and then publish a package to the npm registry when a release is created
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
#
# Publishing uses npm Trusted Publishing (OIDC) — no NPM_TOKEN secret is
# required. Each package must have a trusted publisher configured on npmjs.com
# pointing at this workflow file. See:
# https://docs.npmjs.com/trusted-publishers

name: Node.js Package

on:
workflow_call:

jobs:
publish-npm:
runs-on: ubuntu-latest
permissions:
contents: write # for the atomic version-bump push (branch + tag)
id-token: write # for npm OIDC trusted publishing
steps:
- uses: actions/setup-node@v6
with:
# OIDC trusted publishing needs npm >= 11.5.1, which requires
# Node >= 20.17.0. setup-node's `20` resolves to the latest
# 20.x, which satisfies that.
node-version: 20
registry-url: https://registry.npmjs.org/
- name: Upgrade npm to >=11.5.1 (required for trusted publishing)
run: npm install -g npm@latest
- name: Check out Etherpad core
uses: actions/checkout@v6
with:
repository: ether/etherpad-lite
- uses: pnpm/action-setup@v6
name: Install pnpm
with:
version: 10
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v5
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
-
uses: actions/checkout@v6
with:
fetch-depth: 0
-
name: Bump version (patch)
id: bump
run: |
LATEST_TAG=$(git describe --tags --abbrev=0) || exit 1
NEW_COMMITS=$(git rev-list --count "${LATEST_TAG}"..) || exit 1
# No new commits since the last tag → nothing to publish.
# Setting `bumped=false` lets the publish step below skip
# itself instead of trying to republish the existing version
# (which fails with "cannot publish over previously published
# versions"). Only manual `gh workflow run` invocations on a
# tag-current branch hit this path; on:push always sees ≥1
# new commit because the push event is what triggered us.
if [ "${NEW_COMMITS}" -le 0 ]; then
echo "bumped=false" >> "${GITHUB_OUTPUT}"
exit 0
fi
git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
pnpm i --frozen-lockfile
# `pnpm version patch` bumps package.json, makes a commit, and creates
# a `v<new-version>` tag. Capture the new tag name from package.json
# rather than parsing pnpm's output, which has historically varied.
# Bump the patch component directly with Node. pnpm/action-setup@v6
# sometimes installs pnpm 11 pre-releases even when version: 10.x is
# requested (pnpm/action-setup#225); those pre-releases either skip
# the git commit/tag or reject --no-git-tag-version as unknown.
# Doing the bump in Node sidesteps both failure modes.
NEW_VERSION=$(node -e "const fs=require('fs');const p=require('./package.json');const v=p.version.split('.');v[2]=String(Number(v[2])+1);p.version=v.join('.');fs.writeFileSync('./package.json',JSON.stringify(p,null,2)+'\n');console.log(p.version);")
NEW_TAG="v${NEW_VERSION}"
git add package.json
git commit -m "${NEW_TAG}"
git tag -a "${NEW_TAG}" -m "${NEW_TAG}"
# CRITICAL: use --atomic so the branch update and the tag update
# succeed (or fail) as a single transaction on the server. The old
# `git push --follow-tags` was non-atomic per ref: if a concurrent
# publish run won the race, the branch fast-forward would be rejected
# but the tag push would still land — leaving a dangling tag with no
# matching commit on the branch. Subsequent runs would then forever
# try to bump to the same already-existing tag and fail with
# `tag 'vN+1' already exists`. With --atomic, a rejected branch push
# rejects the tag push too, and the next workflow tick can retry
# cleanly against the up-to-date refs.
git push --atomic origin "${GITHUB_REF_NAME}" "${NEW_TAG}"
echo "bumped=true" >> "${GITHUB_OUTPUT}"
# This is required if the package has a prepare script that uses something
# in dependencies or devDependencies.
-
if: steps.bump.outputs.bumped == 'true'
run: pnpm i
# `npm publish` must come after `git push` otherwise there is a race
# condition: If two PRs are merged back-to-back then master/main will be
# updated with the commits from the second PR before the first PR's
# workflow has a chance to push the commit generated by `npm version
# patch`. This causes the first PR's `git push` step to fail after the
# package has already been published, which in turn will cause all future
# workflow runs to fail because they will all attempt to use the same
# already-used version number. By running `npm publish` after `git push`,
# back-to-back merges will cause the first merge's workflow to fail but
# the second's will succeed.
#
# Use `npm publish` directly (not `pnpm publish`) because OIDC trusted
# publishing requires npm CLI >= 11.5.1 and `pnpm publish` shells out to
# whichever `npm` is on PATH; calling `npm` directly avoids any shim
# ambiguity.
- name: Publish to npm via OIDC
if: steps.bump.outputs.bumped == 'true'
run: npm publish --provenance --access public
32 changes: 32 additions & 0 deletions .github/workflows/test-and-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: Node.js Package
on:
push:
# Invoked by automerge.yml after a Dependabot PR is merged. GitHub
# Actions doesn't fire on:push when the push is authored by GITHUB_TOKEN
# (the automerge action's only available identity), so without this
# dispatch trigger the release job never runs after auto-merges.
workflow_dispatch:

# id-token: write must be granted here so the reusable npmpublish workflow
# can request an OIDC token for npm trusted publishing.
permissions:
contents: write
id-token: write

jobs:
backend:
uses: ./.github/workflows/backend-tests.yml
secrets: inherit
frontend:
uses: ./.github/workflows/frontend-tests.yml
secrets: inherit
release:
if: ${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main' }}
needs:
- backend
- frontend
permissions:
contents: write # for the version bump push
id-token: write # for npm OIDC trusted publishing
uses: ./.github/workflows/npmpublish.yml
secrets: inherit
Loading
Loading