From 650728dd5f0a14ac44204ac68eb5e868570aa73d Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Apr 2026 00:59:17 +0000 Subject: [PATCH 1/4] fix: skip editor side-effects during IME composition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Typing Chinese with the Windows IME caused characters to disappear and the cursor to jump because our custom plugins ran their update hooks during composition: the wikilink view update calls coordsAtPos and mutates an external menu element, the slash menu's shouldShow walks the doc and rebuilds the menu DOM, and the markdown listener fires setContent on the parent — each of which can interrupt the IME and make ProseMirror discard the in-flight composition. Skip all four call sites when view.composing is true. ProseMirror dispatches a transaction at compositionend, so menus and parent state catch up with the committed text on the next update. https://claude.ai/code/session_012rsJRUue9w7sBD3J5gmKWK --- frontend/src/components/Editor/Editor.jsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frontend/src/components/Editor/Editor.jsx b/frontend/src/components/Editor/Editor.jsx index fbc578b..e72559a 100644 --- a/frontend/src/components/Editor/Editor.jsx +++ b/frontend/src/components/Editor/Editor.jsx @@ -136,6 +136,7 @@ class SlashMenuView { content: this.content, debounce: 50, shouldShow: (view) => { + if (view.composing) return false const currentText = this.provider.getContent( view, (node) => ['paragraph', 'heading'].includes(node.type.name) @@ -230,6 +231,9 @@ class SlashMenuView { update(view) { this.view = view this.editorViewRef.current = view + // Don't query or mutate menu state mid-IME — coordsAtPos / DOM writes + // triggered here can drop in-flight composition characters on Windows. + if (view.composing) return this.provider.update(view) } @@ -453,6 +457,10 @@ const Editor = forwardRef(function Editor({ defaultValue = '', onChange, onDrawi return { update(view) { wikilinkMenu._view = view + // Bail during IME composition: coordsAtPos + DOM writes from + // show()/hide() can disturb the active composition on Windows, + // making characters disappear and the cursor jump. + if (view.composing) return const { state } = view const { selection } = state if (!(selection instanceof TextSelection)) { @@ -516,6 +524,12 @@ const Editor = forwardRef(function Editor({ defaultValue = '', onChange, onDrawi ctx.set(rootCtx, containerRef.current) ctx.set(defaultValueCtx, defaultValue) ctx.get(listenerCtx).markdownUpdated((_ctx, markdown) => { + // Skip parent state updates while IME composition is active — + // the React re-render they trigger can interrupt composition on + // Windows. ProseMirror dispatches a final transaction at + // compositionend, which fires the listener again with the + // committed text, so no input is lost. + if (editorViewRef.current?.composing) return onChangeRef.current?.(markdown) }) let slashMenuView = null From 730f366d784c1e9ed70f4a7768a9351fde137efa Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Apr 2026 01:06:30 +0000 Subject: [PATCH 2/4] ci: build and tag :beta images on beta branch pushes Trigger CI on beta branch pushes and let build-and-push run for refs/heads/beta. The existing docker/metadata-action's type=ref,event=branch already maps the branch name into the image tag, so pushes to beta will publish ghcr.io/.../{backend,frontend}:beta automatically. https://claude.ai/code/session_012rsJRUue9w7sBD3J5gmKWK --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd84168..f4d8832 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI/CD on: push: - branches: [ main ] + branches: [ main, beta ] tags: ['v*'] paths-ignore: - '**.md' @@ -126,7 +126,7 @@ jobs: if: | always() && github.event_name != 'pull_request' && - (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) && + (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/beta' || startsWith(github.ref, 'refs/tags/v')) && (startsWith(github.ref, 'refs/tags/v') || needs.changes.outputs.backend == 'true' || needs.changes.outputs.frontend == 'true') && From b19e078ee4e589b528ab7fff7c615385b34e92a0 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Apr 2026 01:08:25 +0000 Subject: [PATCH 3/4] ci: tag main as :latest and beta as :beta Replace the generic type=ref,event=branch tag (which produced :main / :beta from the branch name) with explicit raw rules so main publishes :latest and beta publishes :beta. Semver tag pushes still get :{version}, :{major.minor}, and the auto :latest from the default flavor; every build keeps its :{sha} tag for traceability. https://claude.ai/code/session_012rsJRUue9w7sBD3J5gmKWK --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4d8832..5f1c0d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -182,7 +182,8 @@ jobs: with: images: ${{ env.IMAGE_PREFIX }}/${{ matrix.component }} tags: | - type=ref,event=branch + type=raw,value=latest,enable={{is_default_branch}} + type=raw,value=beta,enable=${{ github.ref == 'refs/heads/beta' }} type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=sha,prefix= From a0796571582529931e63af42ef1651a69dc6af64 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Apr 2026 01:10:09 +0000 Subject: [PATCH 4/4] ci: restrict :latest tag to main branch only Set flavor.latest=false so semver tag pushes no longer auto-add :latest. The explicit type=raw,value=latest,enable={{is_default_branch}} rule keeps :latest pinned to main, so a release tag won't shadow whatever main is currently shipping. https://claude.ai/code/session_012rsJRUue9w7sBD3J5gmKWK --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f1c0d4..d10fd6e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -181,6 +181,8 @@ jobs: uses: docker/metadata-action@v5 with: images: ${{ env.IMAGE_PREFIX }}/${{ matrix.component }} + flavor: | + latest=false tags: | type=raw,value=latest,enable={{is_default_branch}} type=raw,value=beta,enable=${{ github.ref == 'refs/heads/beta' }}