Refactor frontend into multi-package monorepo with EditorPort abstraction#589
Refactor frontend into multi-package monorepo with EditorPort abstraction#589hackerwins merged 21 commits intomainfrom
Conversation
…deMirror Add a thin EditorPort abstraction (getSelection, replaceRange, scrollIntoView, getContentWidth) so that intelligence, speech-to-text, and user-presence code no longer import from "codemirror" directly. The CMEditorAdapter annotates transactions with userEvent "input", letting yorkieSync handle Yorkie content sync automatically and eliminating dual-write patterns in external consumers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move frontend/ to packages/frontend/ and backend/ to packages/backend/ so all application packages live under a single directory. Update pnpm-workspace.yaml, CI workflows, Dockerfile, husky pre-commit hook, and setup scripts to reflect the new paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract editor-agnostic types (EditorPort, EditorModeType, PresenceInfo) and pure UI components (TooltipToggleButton, CodePairError, etc.) into a shared package. Also fix docker-compose build context path after backend move. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract CodeMirror editor, toolbar, preview, Yorkie sync plugins, and formatting utilities into a self-contained package. CMEditorSuite serves as the single entry point the app shell renders, communicating back via the EditorPort interface from @codepair/ui. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document the multi-editor monorepo design: package structure, dependency graph, EditorPort interface contract, data flow through the app shell, and step-by-step guide for adding a new editor package. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Important Review skippedToo many files! This PR contains 293 files, which is 143 over the limit of 150. You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/codemirror/src/plugins/intelligencePivot.ts (1)
25-25:⚠️ Potential issue | 🟡 MinorPre-existing typo:
relaitveshould berelative.While not part of this change, Line 25 has a typo in the CSS style.
🐛 Proposed fix
- pair.create("style", `position: relaitve;`), + pair.create("style", `position: relative;`),packages/codemirror/src/components/Editor.tsx (1)
56-96:⚠️ Potential issue | 🟠 MajorClear
editorPorton teardown to prevent stale references.When the editor unmounts,
editorPortstill points to a destroyed view. Downstream consumers rely on null-checks to avoid calling methods on it; reset it in the cleanup to propagate the null state through the context and store.🛠️ Suggested fix
return () => { view?.destroy(); + setEditorPort(null); };
🤖 Fix all issues with AI agents
In @.github/workflows/ci_backend.yaml:
- Around line 5-13: The workflow's path filter under the "paths:" key in
ci_backend.yaml currently only watches ".github/workflows/ci_backend.yaml" and
"packages/backend/**"; update both the push and pull_request path lists to also
include workspace-root dependency/config files (e.g., package.json,
package-lock.json, yarn.lock, pnpm-lock.yaml, pnpm-workspace.yaml, turbo.json,
and any other repo-level lock or workspace config) so changes to those files
trigger the backend CI; modify the two "paths:" arrays accordingly in the
existing file.
In @.github/workflows/ci_frontend.yaml:
- Around line 6-17: The workflow's path filters under the push and pull_request
triggers are missing root workspace and lockfile entries, so update the
triggers' paths arrays (the push and pull_request keys) to include the
repository root workspace config and lockfile (e.g., add "/" and your lockfile
name such as "pnpm-lock.yaml" or "package-lock.json") so changes to
workspace/lockfiles will also trigger the CI; modify the paths lists referenced
under push and pull_request in the YAML to contain those root/lockfile paths in
addition to the existing package globs.
In `@packages/codemirror/src/CMEditorSuite.tsx`:
- Around line 100-105: The current onMouseDown/onMouseUp pair in
CMEditorSuite.tsx can leave document.body.style.userSelect = "none" if the mouse
is released outside the element; to fix, when handling onMouseDown (the handler
referenced as onMouseDown) set document.body.style.userSelect = "none" and also
register a window-level one-time 'mouseup' listener (use { once: true } or
remove the listener inside the handler) that restores
document.body.style.userSelect = "auto"; ensure any existing listener is cleaned
up to avoid leaks and also restore userSelect in any component unmount cleanup
(e.g., via useEffect cleanup) so document.body.style.userSelect is never left
disabled.
In `@packages/codemirror/src/components/Preview.tsx`:
- Around line 21-29: Remove the duplicate addSoftLineBreak implementation in
Preview.tsx and instead import the shared function from its canonical export in
packages/frontend/src/features/document; delete the local function declaration,
add an import for addSoftLineBreak at the top of Preview.tsx, and ensure all
usages in the file call the imported addSoftLineBreak symbol (no other API
changes needed).
In `@packages/frontend/design/editor-port.md`:
- Around line 60-62: Update the documentation paths that reference the old
adapter location by replacing instances of
"features/editor/codemirror/CMEditorAdapter.ts" and the outdated file-structure
entries with the current monorepo package path
"packages/codemirror/src/CMEditorAdapter.ts"; update the file structure section
(the block currently showing lines with features/editor/...) to reflect the new
package layout so all references point to
packages/codemirror/src/CMEditorAdapter.ts and any sibling files are listed
under packages/codemirror/src.
In `@packages/frontend/src/features/editor/components/PreviewRevisionDialog.tsx`:
- Around line 31-32: In PreviewRevisionDialog.tsx the CSS imports use the
workspace alias while the codemirror package imports them with relative paths;
update the two import statements for editor.css and preview.css to use the same
relative path pattern used in packages/codemirror (e.g., change the imports that
currently reference "@codepair/codemirror/src/styles/..." to
"../../../codemirror/src/styles/...") so they align with Preview.tsx;
alternatively evaluate whether these styles should be consolidated into a single
shared import location to avoid duplication.
In `@packages/frontend/src/features/editor/shared/components/DocumentView.tsx`:
- Around line 55-58: The handler handleCodeKeyChange currently force-casts
CMCodeKeyType into the Redux CodeKeyType via "as unknown as", allowing invalid
values (like SUBLIME) to reach the CodeMirror component; replace this unsafe
cast with an explicit mapping function (e.g., mapCMToReduxKey or
mapReduxToCMKey) that converts between CMCodeKeyType and the store's CodeKeyType
(handle the SUBLIME -> DEFAULT mapping and preserve VIM), then call
dispatch(setCodeKeyType(mappedValue)) inside handleCodeKeyChange so only valid
CodeKeyType values are dispatched.
In
`@packages/frontend/src/features/intelligence/components/YorkieIntelligenceFooter.tsx`:
- Around line 24-27: The useMemo that computes width uses a non-null assertion
on editorStore.editorPort which can throw if editorPort is null; update
YorkieIntelligenceFooter to handle that by guarding editorStore.editorPort
before accessing getContentWidth (either early return from the component when
editorStore.editorPort is falsy, e.g. in YorkieIntelligenceFooter return null,
or compute width with a safe check/optional chaining and a sensible fallback
value) so that the width calculation and any calls to getContentWidth no longer
rely on the non-null assertion.
🧹 Nitpick comments (10)
.husky/pre-commit (2)
6-12: Harden changed-file iteration against whitespace in paths.The current
for FILE in $CHANGED_FILESwill split on spaces/newlines. Using a NUL-delimited diff avoids false detection when filenames contain whitespace.♻️ Suggested change
-CHANGED_FILES=$(git diff --cached --name-only) - -for FILE in $CHANGED_FILES; do +while IFS= read -r -d '' FILE; do if [[ "$FILE" =~ ^packages/frontend/ ]]; then LINT_FRONTEND=true elif [[ "$FILE" =~ ^packages/backend/ ]]; then LINT_BACKEND=true fi -done +done < <(git diff --cached --name-only -z)
14-33: Guardcdto avoid running lint in the wrong directory.If the package directory is missing or renamed,
cdfails andnpx lint-stagedruns from the prior working directory. A guardedcdkeeps the hook fail-fast and predictable.♻️ Suggested change
- cd packages/frontend + cd packages/frontend || { echo "packages/frontend not found"; exit 1; } npx lint-staged if [ $? -ne 0 ]; then echo "Frontend Linting & Formatting failed. Commit aborted." exit 1 fi cd ../.. @@ - cd packages/backend + cd packages/backend || { echo "packages/backend not found"; exit 1; } npx lint-staged if [ $? -ne 0 ]; then echo "Backend Linting & Formatting failed. Commit aborted." exit 1 fi cd ../..packages/codemirror/package.json (1)
15-18: Consider consistent version pinning for CodeMirror packages.Some CodeMirror packages use exact versions (e.g.,
@codemirror/commands:6.1.2,@codemirror/view:6.23.1) while others use caret ranges (e.g.,@codemirror/state:^6.5.4). Inconsistent pinning can lead to subtle incompatibilities when peer dependencies resolve differently.packages/codemirror/eslint.config.mjs (1)
18-24: Consider addingeslint-plugin-reactfor React component linting.The codemirror package contains React components (e.g.,
CMEditorSuite,Editor), but the config only includesreact: { version: "detect" }settings without the actual React plugin. The frontend config includesreact-hooksandreact-refreshplugins which would also benefit this package.♻️ Optional: Add React linting plugins
import typescriptPlugin from "@typescript-eslint/eslint-plugin"; import typescriptParser from "@typescript-eslint/parser"; import prettierPlugin from "eslint-plugin-prettier"; +import reactHooksPlugin from "eslint-plugin-react-hooks"; export default [ { // ... plugins: { "@typescript-eslint": typescriptPlugin, prettier: prettierPlugin, + "react-hooks": reactHooksPlugin, }, rules: { ...prettierPlugin.configs.recommended.rules, ...typescriptPlugin.configs.recommended.rules, + ...reactHooksPlugin.configs.recommended.rules, // ... }, }, ];packages/frontend/src/features/editor/store/editorSlice.ts (1)
42-44: Clarify the use ofObject.assignfor settingeditorPort.The
Object.assign(state, { editorPort: action.payload })pattern is unconventional for Redux Toolkit reducers. With Immer (used by RTK), direct assignment likestate.editorPort = action.payloadtypically works and is more readable. TheObject.assignapproach suggests there might be a specific reason—possibly to work around Immer's proxy handling of non-serializable objects likeEditorPort.If this is intentional to bypass Immer proxy issues, a brief inline comment would help future maintainers understand the design choice.
packages/codemirror/src/hooks/useFormatUtils.ts (1)
180-180: Consider adding a runtime guard before the type assertion.The cast
(editorPort as CMEditorAdapter).viewassumeseditorPortis always aCMEditorAdapter. While this hook lives in@codepair/codemirror(making CM-specific access acceptable), a defensive check would prevent runtime errors if the context ever provides a different implementation.🛡️ Optional defensive check
- applyFormat(format)((editorPort as CMEditorAdapter).view); + if (editorPort instanceof CMEditorAdapter) { + applyFormat(format)(editorPort.view); + }packages/frontend/design/editor-port.md (1)
33-35: Consider adding language specifiers to file path code blocks.Static analysis flagged code blocks without language specifiers. For file path listings, you could use
```textor```plaintextto satisfy the linter while keeping them visually distinct from executable code.Also applies to: 60-62, 98-113
packages/frontend/src/features/editor/port/EditorPort.ts (1)
1-11: Unify the EditorPort source to avoid type drift.Since EditorPort is already exported from
@codepair/ui, keeping another definition here risks divergence over time. Prefer a single source of truth and re-export it locally if needed.♻️ Suggested change
-export interface EditorPort { - getSelection(): { from: number; to: number }; - replaceRange( - from: number, - to: number, - insert: string, - selection?: { anchor: number; head?: number } - ): void; - scrollIntoView(pos: number): void; - getContentWidth(): number; -} +export type { EditorPort } from "@codepair/ui";packages/codemirror/src/CMEditorSuite.tsx (2)
15-15: Consider using a type-only import for clarity.The inline
import("@yorkie-js/sdk").Clientsyntax is valid but less readable than a standard type import. If there's no bundling or circular dependency concern, a top-level type import would be clearer.+import type { Client } from "@yorkie-js/sdk"; import { useCallback, useState, useRef } from "react"; ... export interface CMEditorSuiteProps { doc: CodePairDocType; - client: import("@yorkie-js/sdk").Client; + client: Client;
53-65: Memoize context value to prevent unnecessary re-renders.The
ctxValueobject is recreated on every render, causing allCMEditorContextconsumers to re-render even when none of the values have changed.♻️ Proposed fix using useMemo
+import { useCallback, useState, useRef, useMemo } from "react"; -import { useCallback, useState, useRef } from "react";- const ctxValue: CMEditorContextValue = { - doc, - client, - editorPort, - setEditorPort, - themeMode, - codeKey, - setCodeKey: onCodeKeyChange, - fileUploadEnabled, - handleUploadImage, - intelligenceEnabled, - width, - }; + const ctxValue: CMEditorContextValue = useMemo( + () => ({ + doc, + client, + editorPort, + setEditorPort, + themeMode, + codeKey, + setCodeKey: onCodeKeyChange, + fileUploadEnabled, + handleUploadImage, + intelligenceEnabled, + width, + }), + [ + doc, + client, + editorPort, + setEditorPort, + themeMode, + codeKey, + onCodeKeyChange, + fileUploadEnabled, + handleUploadImage, + intelligenceEnabled, + width, + ] + );
| paths: | ||
| - ".github/workflows/ci_backend.yaml" | ||
| - "backend/**" | ||
| - "packages/backend/**" | ||
| pull_request: | ||
| branches: | ||
| - main | ||
| paths: | ||
| - ".github/workflows/ci_backend.yaml" | ||
| - "backend/**" | ||
| - "packages/backend/**" |
There was a problem hiding this comment.
Include workspace root dependency files in the CI path filters.
Backend CI can break when workspace-level files change (e.g., lockfile or workspace config), but those edits won’t currently trigger this workflow.
🛠️ Suggested update
@@
paths:
- ".github/workflows/ci_backend.yaml"
- "packages/backend/**"
+ - "pnpm-lock.yaml"
+ - "pnpm-workspace.yaml"
@@
paths:
- ".github/workflows/ci_backend.yaml"
- "packages/backend/**"
+ - "pnpm-lock.yaml"
+ - "pnpm-workspace.yaml"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| paths: | |
| - ".github/workflows/ci_backend.yaml" | |
| - "backend/**" | |
| - "packages/backend/**" | |
| pull_request: | |
| branches: | |
| - main | |
| paths: | |
| - ".github/workflows/ci_backend.yaml" | |
| - "backend/**" | |
| - "packages/backend/**" | |
| paths: | |
| - ".github/workflows/ci_backend.yaml" | |
| - "packages/backend/**" | |
| - "pnpm-lock.yaml" | |
| - "pnpm-workspace.yaml" | |
| pull_request: | |
| branches: | |
| - main | |
| paths: | |
| - ".github/workflows/ci_backend.yaml" | |
| - "packages/backend/**" | |
| - "pnpm-lock.yaml" | |
| - "pnpm-workspace.yaml" |
🤖 Prompt for AI Agents
In @.github/workflows/ci_backend.yaml around lines 5 - 13, The workflow's path
filter under the "paths:" key in ci_backend.yaml currently only watches
".github/workflows/ci_backend.yaml" and "packages/backend/**"; update both the
push and pull_request path lists to also include workspace-root
dependency/config files (e.g., package.json, package-lock.json, yarn.lock,
pnpm-lock.yaml, pnpm-workspace.yaml, turbo.json, and any other repo-level lock
or workspace config) so changes to those files trigger the backend CI; modify
the two "paths:" arrays accordingly in the existing file.
| - ".github/workflows/ci_frontend.yaml" | ||
| - "frontend/**" | ||
| - "packages/frontend/**" | ||
| - "packages/ui/**" | ||
| - "packages/codemirror/**" | ||
| pull_request: | ||
| branches: | ||
| - main | ||
| paths: | ||
| - ".github/workflows/ci_frontend.yaml" | ||
| - "frontend/**" | ||
| - "packages/frontend/**" | ||
| - "packages/ui/**" | ||
| - "packages/codemirror/**" |
There was a problem hiding this comment.
Include root workspace/lockfile paths in filters to avoid skipped CI.
Changes to root workspace config or lockfile won’t trigger this workflow, so dependency updates could land unvalidated. Add the root workspace/lockfile paths to both push and pull_request.
✅ Suggested patch
paths:
- ".github/workflows/ci_frontend.yaml"
+ - "package.json"
+ - "pnpm-lock.yaml"
+ - "pnpm-workspace.yaml"
- "packages/frontend/**"
- "packages/ui/**"
- "packages/codemirror/**"
@@
paths:
- ".github/workflows/ci_frontend.yaml"
+ - "package.json"
+ - "pnpm-lock.yaml"
+ - "pnpm-workspace.yaml"
- "packages/frontend/**"
- "packages/ui/**"
- "packages/codemirror/**"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - ".github/workflows/ci_frontend.yaml" | |
| - "frontend/**" | |
| - "packages/frontend/**" | |
| - "packages/ui/**" | |
| - "packages/codemirror/**" | |
| pull_request: | |
| branches: | |
| - main | |
| paths: | |
| - ".github/workflows/ci_frontend.yaml" | |
| - "frontend/**" | |
| - "packages/frontend/**" | |
| - "packages/ui/**" | |
| - "packages/codemirror/**" | |
| - ".github/workflows/ci_frontend.yaml" | |
| - "package.json" | |
| - "pnpm-lock.yaml" | |
| - "pnpm-workspace.yaml" | |
| - "packages/frontend/**" | |
| - "packages/ui/**" | |
| - "packages/codemirror/**" | |
| pull_request: | |
| branches: | |
| - main | |
| paths: | |
| - ".github/workflows/ci_frontend.yaml" | |
| - "package.json" | |
| - "pnpm-lock.yaml" | |
| - "pnpm-workspace.yaml" | |
| - "packages/frontend/**" | |
| - "packages/ui/**" | |
| - "packages/codemirror/**" |
🤖 Prompt for AI Agents
In @.github/workflows/ci_frontend.yaml around lines 6 - 17, The workflow's path
filters under the push and pull_request triggers are missing root workspace and
lockfile entries, so update the triggers' paths arrays (the push and
pull_request keys) to include the repository root workspace config and lockfile
(e.g., add "/" and your lockfile name such as "pnpm-lock.yaml" or
"package-lock.json") so changes to workspace/lockfiles will also trigger the CI;
modify the paths lists referenced under push and pull_request in the YAML to
contain those root/lockfile paths in addition to the existing package globs.
| onMouseDown={() => { | ||
| document.body.style.userSelect = "none"; | ||
| }} | ||
| onMouseUp={() => { | ||
| document.body.style.userSelect = "auto"; | ||
| }} |
There was a problem hiding this comment.
userSelect may remain disabled if mouse is released outside the splitter.
If the user starts dragging the splitter and releases the mouse outside the <Paper> element, onMouseUp won't fire, leaving document.body.style.userSelect = "none" indefinitely. This would prevent text selection until the page reloads.
🛡️ Proposed fix using window-level listener
<Paper
id="splitter"
{...separatorProps}
onMouseDown={() => {
document.body.style.userSelect = "none";
+ const cleanup = () => {
+ document.body.style.userSelect = "auto";
+ window.removeEventListener("mouseup", cleanup);
+ };
+ window.addEventListener("mouseup", cleanup);
}}
- onMouseUp={() => {
- document.body.style.userSelect = "auto";
- }}
sx={{📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| onMouseDown={() => { | |
| document.body.style.userSelect = "none"; | |
| }} | |
| onMouseUp={() => { | |
| document.body.style.userSelect = "auto"; | |
| }} | |
| onMouseDown={() => { | |
| document.body.style.userSelect = "none"; | |
| const cleanup = () => { | |
| document.body.style.userSelect = "auto"; | |
| window.removeEventListener("mouseup", cleanup); | |
| }; | |
| window.addEventListener("mouseup", cleanup); | |
| }} | |
| sx={{ |
🤖 Prompt for AI Agents
In `@packages/codemirror/src/CMEditorSuite.tsx` around lines 100 - 105, The
current onMouseDown/onMouseUp pair in CMEditorSuite.tsx can leave
document.body.style.userSelect = "none" if the mouse is released outside the
element; to fix, when handling onMouseDown (the handler referenced as
onMouseDown) set document.body.style.userSelect = "none" and also register a
window-level one-time 'mouseup' listener (use { once: true } or remove the
listener inside the handler) that restores document.body.style.userSelect =
"auto"; ensure any existing listener is cleaned up to avoid leaks and also
restore userSelect in any component unmount cleanup (e.g., via useEffect
cleanup) so document.body.style.userSelect is never left disabled.
| function addSoftLineBreak(text: string) { | ||
| return text | ||
| .split("\n") | ||
| .map((line) => { | ||
| if (line.trim() === "") return ""; | ||
| else return line + " "; | ||
| }) | ||
| .join("\n"); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
rg -n "addSoftLineBreak" --type=ts --type=tsx -C2Repository: yorkie-team/codepair
Length of output: 91
🏁 Script executed:
rg "addSoftLineBreak" -C2Repository: yorkie-team/codepair
Length of output: 4577
Remove duplicate addSoftLineBreak utility; import from shared location.
This function is already exported from packages/frontend/src/features/document and used by other components (YorkieIntelligenceFeature.tsx, PreviewRevisionDialog.tsx). Remove the local definition in Preview.tsx and import from the canonical location to maintain a single source of truth.
🤖 Prompt for AI Agents
In `@packages/codemirror/src/components/Preview.tsx` around lines 21 - 29, Remove
the duplicate addSoftLineBreak implementation in Preview.tsx and instead import
the shared function from its canonical export in
packages/frontend/src/features/document; delete the local function declaration,
add an import for addSoftLineBreak at the top of Preview.tsx, and ensure all
usages in the file call the imported addSoftLineBreak symbol (no other API
changes needed).
| ``` | ||
| features/editor/codemirror/CMEditorAdapter.ts | ||
| ``` |
There was a problem hiding this comment.
File path in documentation appears outdated.
The adapter location is documented as features/editor/codemirror/CMEditorAdapter.ts, but based on the PR structure, the adapter is at packages/codemirror/src/CMEditorAdapter.ts. The file structure section (lines 98-113) also shows the old path structure.
Consider updating these paths to reflect the actual monorepo package layout.
🤖 Prompt for AI Agents
In `@packages/frontend/design/editor-port.md` around lines 60 - 62, Update the
documentation paths that reference the old adapter location by replacing
instances of "features/editor/codemirror/CMEditorAdapter.ts" and the outdated
file-structure entries with the current monorepo package path
"packages/codemirror/src/CMEditorAdapter.ts"; update the file structure section
(the block currently showing lines with features/editor/...) to reflect the new
package layout so all references point to
packages/codemirror/src/CMEditorAdapter.ts and any sibling files are listed
under packages/codemirror/src.
| import "@codepair/codemirror/src/styles/editor.css"; | ||
| import "@codepair/codemirror/src/styles/preview.css"; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Check if there are other consumers importing from `@codepair/codemirror/src/` paths
rg -n "@codepair/codemirror/src/" --type ts --type tsx --type css -g '!packages/codemirror/'Repository: yorkie-team/codepair
Length of output: 91
🏁 Script executed:
# Also check for vite config files to understand workspace resolution
find . -maxdepth 3 -name "vite.config.*" -o -name "vitest.config.*" | head -20Repository: yorkie-team/codepair
Length of output: 98
🏁 Script executed:
# Check if there's any documentation about this import pattern
find . -maxdepth 3 -name "*.md" | xargs grep -l "workspace" 2>/dev/null | head -10Repository: yorkie-team/codepair
Length of output: 142
🏁 Script executed:
# Search for `@codepair/codemirror/src/` imports with correct rg syntax
rg "@codepair/codemirror/src/" --type ts --glob '!packages/codemirror/**'Repository: yorkie-team/codepair
Length of output: 320
🏁 Script executed:
# Read the vite config
cat packages/frontend/vite.config.tsRepository: yorkie-team/codepair
Length of output: 673
🏁 Script executed:
# Check CHANGELOG for context about this import pattern
grep -A 5 -B 5 "@codepair/codemirror\|workspace" CHANGELOG.md | head -40Repository: yorkie-team/codepair
Length of output: 1927
🏁 Script executed:
# Check if there's a root vite config or workspace configuration
find . -maxdepth 2 -name "vite.config.*" -o -name "tsconfig.json" -o -name "jsconfig.json"Repository: yorkie-team/codepair
Length of output: 87
🏁 Script executed:
# Search for other CSS/style imports to see the standard pattern
rg "import.*\.css" --type ts -g '!node_modules' | head -30Repository: yorkie-team/codepair
Length of output: 772
🏁 Script executed:
# Check root package.json for workspace configuration
cat package.json | grep -A 20 "workspaces\|workspace"Repository: yorkie-team/codepair
Length of output: 46
🏁 Script executed:
# Check if there's a monorepo root config
ls -la | grep -E "^-.*\.(json|config)" | head -20Repository: yorkie-team/codepair
Length of output: 348
🏁 Script executed:
# Check codemirror package.json for exports configuration
cat packages/codemirror/package.jsonRepository: yorkie-team/codepair
Length of output: 2077
🏁 Script executed:
# Read root package.json to confirm workspace setup
cat package.jsonRepository: yorkie-team/codepair
Length of output: 1447
🏁 Script executed:
# Check if codemirror has a dist build or only src
ls -la packages/codemirror/ | head -20Repository: yorkie-team/codepair
Length of output: 481
Align CSS imports with the package's own import pattern.
The same CSS files are imported inconsistently within the monorepo. In packages/codemirror/src/components/Preview.tsx, these styles are imported using relative paths (../styles/editor.css), but in this file they use a workspace alias (@codepair/codemirror/src/styles/editor.css).
Change these imports to match the relative path pattern used by the package itself:
import "../../../codemirror/src/styles/editor.css";
import "../../../codemirror/src/styles/preview.css";Or better, consider if these styles should be imported from a single consolidated location to avoid duplication.
🤖 Prompt for AI Agents
In `@packages/frontend/src/features/editor/components/PreviewRevisionDialog.tsx`
around lines 31 - 32, In PreviewRevisionDialog.tsx the CSS imports use the
workspace alias while the codemirror package imports them with relative paths;
update the two import statements for editor.css and preview.css to use the same
relative path pattern used in packages/codemirror (e.g., change the imports that
currently reference "@codepair/codemirror/src/styles/..." to
"../../../codemirror/src/styles/...") so they align with Preview.tsx;
alternatively evaluate whether these styles should be consolidated into a single
shared import location to avoid duplication.
| const handleCodeKeyChange = useCallback( | ||
| (key: CMCodeKeyType) => { | ||
| dispatch(setCodeKeyType(key as unknown as CodeKeyType)); | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Locate all CodeKeyType definitions
rg -nP "export\s+(enum|type)\s+CodeKeyType|enum\s+CodeKeyType|type\s+CodeKeyType" -SRepository: yorkie-team/codepair
Length of output: 214
🏁 Script executed:
#!/bin/bash
# Read the first CodeKeyType definition
echo "=== packages/codemirror/src/types.ts ==="
head -20 packages/codemirror/src/types.ts
echo -e "\n=== packages/frontend/src/features/settings/store/configSlice.ts ==="
head -30 packages/frontend/src/features/settings/store/configSlice.tsRepository: yorkie-team/codepair
Length of output: 1103
🏁 Script executed:
#!/bin/bash
# Read DocumentView.tsx around lines 55-58
sed -n '45,70p' packages/frontend/src/features/editor/shared/components/DocumentView.tsxRepository: yorkie-team/codepair
Length of output: 562
🏁 Script executed:
#!/bin/bash
# Check imports at the top of DocumentView.tsx
head -40 packages/frontend/src/features/editor/shared/components/DocumentView.tsx | grep -E "import|from"Repository: yorkie-team/codepair
Length of output: 872
🏁 Script executed:
#!/bin/bash
# Find setCodeKeyType definition
rg -nB2 -A5 "setCodeKeyType" packages/frontend/src/features/settings/Repository: yorkie-team/codepair
Length of output: 2072
🏁 Script executed:
#!/bin/bash
# Search for other usages of the two CodeKeyType enums together
rg -n "CMCodeKeyType|CodeKeyType" packages/frontend/src/features/editor/shared/components/DocumentView.tsx
# Also check if there's any mapping or conversion logic elsewhere
rg -n "CMCodeKeyType.*CodeKeyType|CodeKeyType.*CMCodeKeyType" packages/frontend/src/Repository: yorkie-team/codepair
Length of output: 504
🏁 Script executed:
#!/bin/bash
# Check if there's any handling in ModeSwitcher or where this is consumed
rg -n "handleCodeKeyChange|codeKey" packages/frontend/src/features/editor/shared/components/DocumentView.tsx | head -20
# Also check ModeSwitcher to see what values it passes
cat packages/frontend/src/features/editor/shared/components/ModeSwitcher.tsx | head -50Repository: yorkie-team/codepair
Length of output: 1723
🏁 Script executed:
#!/bin/bash
# Check what values are actually passed and if there's a mismatch issue
rg -n "DEFAULT|SUBLIME|VIM" packages/codemirror/src/types.ts packages/frontend/src/features/settings/store/configSlice.ts
# Also check if there's any integration test or actual usage that shows the problem
rg -n "handleCodeKeyChange" packages/frontend/src/ -A 3 -B 3Repository: yorkie-team/codepair
Length of output: 2147
Unify or explicitly map between the two CodeKeyType enums.
Two separate CodeKeyType enums exist with divergent values:
@codepair/codemirror:DEFAULT,VIM- Redux settings store:
SUBLIME,VIM
The Redux store initializes with SUBLIME as the default, but the codemirror component doesn't support it—only DEFAULT and VIM. The as unknown as cast bypasses TypeScript's type safety, allowing the invalid SUBLIME value to reach the component at runtime. Either consolidate both enums to share the same values or add an explicit mapping function to convert between them safely.
🤖 Prompt for AI Agents
In `@packages/frontend/src/features/editor/shared/components/DocumentView.tsx`
around lines 55 - 58, The handler handleCodeKeyChange currently force-casts
CMCodeKeyType into the Redux CodeKeyType via "as unknown as", allowing invalid
values (like SUBLIME) to reach the CodeMirror component; replace this unsafe
cast with an explicit mapping function (e.g., mapCMToReduxKey or
mapReduxToCMKey) that converts between CMCodeKeyType and the store's CodeKeyType
(handle the SUBLIME -> DEFAULT mapping and preserve VIM), then call
dispatch(setCodeKeyType(mappedValue)) inside handleCodeKeyChange so only valid
CodeKeyType values are dispatched.
- Clear editorPort on Editor unmount to prevent stale references - Fix CSS typo: relaitve → relative in intelligencePivot - Fix splitter userSelect stuck when mouse released outside element - Memoize CMEditorContext value to reduce unnecessary re-renders - Add null safety for editorPort in YorkieIntelligenceFooter - Update outdated file paths in editor-port design doc - De-duplicate EditorPort: re-export from @codepair/ui instead of redefining - Add pnpm-lock.yaml and pnpm-workspace.yaml to CI path filters Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/codemirror/src/components/Preview.tsx (1)
107-136:⚠️ Potential issue | 🟡 MinorCancel the throttled updater during cleanup to avoid trailing setState.
Line 132–135 cleans up subscriptions, but any pending throttled call can still fire after unmount/doc switch.
🧹 Suggested fix
return () => { unsubscribe(); + throttledUpdatePreviewContent.cancel(); setContent(""); };Does lodash `throttle` return a function with `.cancel()` and `.flush()` that are safe to call in React effect cleanup?
🤖 Fix all issues with AI agents
In `@docs/editor-port-architecture.md`:
- Line 13: Several fenced code blocks in docs/editor-port-architecture.md lack
language identifiers (triggering markdownlint MD040); update each unlabeled
triple-backtick fence to include an appropriate language tag (e.g., change ```
to ```text for the package tree snippet containing "packages/ ui/ codemirror/
frontend/ backend/") and similarly add tags like typescript, tsx, yaml, or json
for the other unlabeled fences referenced around the other snippets (the ones at
the other reported locations). Locate the unlabeled fences by searching for the
shown snippet content (e.g., the package tree and other code examples) and add
the correct language identifier to each opening ``` so markdownlint no longer
warns.
In `@packages/backend/docker/docker-compose-full.yml`:
- Around line 35-40: Update the codepair-backend service's depends_on block to
include yorkie and yorkie-intelligence with the same health condition used for
mongo/minio/qdrant; specifically, in the docker-compose service definition for
codepair-backend add entries "yorkie: condition: service_healthy" and
"yorkie-intelligence: condition: service_healthy" so the backend waits for
YORKIE_API_ADDR and OLLAMA_HOST_URL targets to be healthy before startup.
In `@packages/codemirror/eslint.config.mjs`:
- Around line 7-8: Remove the ignores entry from the existing config object that
contains files: ["**/*.ts","**/*.tsx"] in eslint.config.mjs and add a separate
top-level config object whose sole property is ignores:
["dist","eslint.config.mjs"] so the ignore behavior is global; locate the
current config object (the one with files) and update it to no longer include
ignores, then create the new standalone config object with only ignores to
replicate .eslintignore semantics.
In `@packages/codemirror/src/components/EditorBottomBar.tsx`:
- Around line 1-20: The event type on handleOpen uses the missing React
namespace (React.MouseEvent); import the MouseEvent type directly from "react"
and update the handler signature to use that imported type instead of
React.MouseEvent so TypeScript compiles with jsx: "react-jsx" (locate handleOpen
in EditorBottomBar and the top imports to add import type { MouseEvent } from
"react").
In `@packages/codemirror/src/global.d.ts`:
- Line 6: Remove the bare module augmentation `declare module
"react-scroll-sync";` which masks the package's real types; delete that line so
TypeScript will pick up the package's own `./dist/index.d.ts` (or `@types`)
instead, then run a typecheck to verify imports using react-scroll-sync now have
proper typings.
In `@packages/frontend/design/editor-port.md`:
- Line 33: Update the three fenced code blocks that show file paths to include
the language specifier "text" so the linter and renderer handle them
consistently; specifically modify the blocks referencing
"packages/ui/src/types/EditorPort.ts",
"packages/codemirror/src/CMEditorAdapter.ts", and the tree block containing
"packages/ui/src/types/ └── EditorPort.ts" to use ```text instead of ``` to
ensure consistent rendering.
- Line 139: The doc suggests creating features/editor/prosemirror/, but to
follow the monorepo package pattern used by packages/codemirror/ you should
instead create a top-level sibling package packages/prosemirror/ (not nested
under frontend features); update any references in the spec and any proposed
import paths to point to packages/prosemirror/ and mirror the packaging/setup
used by packages/codemirror/ (package.json, build/config, exports) so the new
editor lives as its own package.
🧹 Nitpick comments (1)
packages/codemirror/src/plugins/intelligencePivot.ts (1)
5-7: Consider keeping shared DOM IDs in a single constants source to avoid drift.If other modules rely on these IDs, duplicating the strings here risks divergence over time. Centralizing them (or re-exporting from a shared constants module) keeps consistency.
♻️ Suggested refactor
-import * as pair from "lib0/pair"; +import * as pair from "lib0/pair"; +import { + INTELLIGENCE_HEADER_ID, + INTELLIGENCE_FOOTER_ID, +} from "../constants/intelligence"; -const INTELLIGENCE_HEADER_ID = "yorkie-intelligence-header"; -const INTELLIGENCE_FOOTER_ID = "yorkie-intelligence-footer";
|
|
||
| ## Package Structure | ||
|
|
||
| ``` |
There was a problem hiding this comment.
Add language identifiers to fenced code blocks (markdownlint MD040).
Several code fences lack a language tag; markdownlint warns on these. Add a language like text, typescript, tsx, yaml, or json as appropriate.
🧩 Suggested patch (apply similarly to other unlabeled fences)
-```
+```text
packages/
ui/ `@codepair/ui` — shared types, interfaces, pure components
codemirror/ `@codepair/codemirror` — CodeMirror 6 + yorkie.Text sync
frontend/ `@codepair/frontend` — app shell (routing, auth, workspace, editor slot)
backend/ `@codepair/backend` — NestJS API server
</details>
Also applies to: 23-23, 93-93, 128-128, 190-190
<details>
<summary>🧰 Tools</summary>
<details>
<summary>🪛 markdownlint-cli2 (0.20.0)</summary>
[warning] 13-13: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
</details>
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
In @docs/editor-port-architecture.md at line 13, Several fenced code blocks in
docs/editor-port-architecture.md lack language identifiers (triggering
markdownlint MD040); update each unlabeled triple-backtick fence to include an
appropriate language tag (e.g., change totext for the package tree
snippet containing "packages/ ui/ codemirror/ frontend/ backend/") and
similarly add tags like typescript, tsx, yaml, or json for the other unlabeled
fences referenced around the other snippets (the ones at the other reported
locations). Locate the unlabeled fences by searching for the shown snippet
content (e.g., the package tree and other code examples) and add the correct
language identifier to each opening ``` so markdownlint no longer warns.
</details>
<!-- fingerprinting:phantom:medusa:eagle -->
<!-- This is an auto-generated comment by CodeRabbit -->
| mongo: | ||
| condition: service_healthy | ||
| minio: | ||
| condition: service_healthy | ||
| qdrant: | ||
| condition: service_healthy |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat packages/backend/docker/docker-compose-full.ymlRepository: yorkie-team/codepair
Length of output: 4990
🌐 Web query:
Docker Compose service_started service_healthy condition support version
💡 Result:
Docker Compose supports depends_on long syntax with condition: values including:
service_started(equivalent to short syntax; only ensures the dependency container is started)service_healthy(waits until the dependency reports healthy, which requires ahealthcheck:)service_completed_successfully(waits until the dependency exits with code 0) (docs.docker.com)
What versions support this?
- In current Docker Compose (the
docker composev2 plugin / Compose Specification), thesecondition:values are documented and supported. (docs.docker.com) - Related fields in the same
depends_onlong syntax have explicit minimum versions:depends_on.<service>.restart→ introduced in Docker Compose 2.17.0depends_on.<service>.required→ introduced in Docker Compose 2.20.0 (docs.docker.com)
Important limitation (Swarm)
depends_on(and thereforecondition:) is ignored bydocker stack deployin Swarm mode, so you can’t rely onservice_healthyordering there. (github.com)
Citations:
- 1: https://docs.docker.com/reference/compose-file/services/
- 2: https://docs.docker.com/reference/compose-file/services/
- 3: https://docs.docker.com/reference/compose-file/services/
- 4: docker stack deploy startup order/dependencies moby/moby#40364
Add yorkie and yorkie-intelligence to depends_on in the codepair-backend service.
codepair-backend uses YORKIE_API_ADDR=http://yorkie:8080 and OLLAMA_HOST_URL=http://yorkie-intelligence:11434 at startup. These services must be available before the backend container initializes. Add them to depends_on to prevent race conditions and startup failures.
Suggested adjustment
depends_on:
mongo:
condition: service_healthy
minio:
condition: service_healthy
qdrant:
condition: service_healthy
+ yorkie:
+ condition: service_started
+ yorkie-intelligence:
+ condition: service_healthy🤖 Prompt for AI Agents
In `@packages/backend/docker/docker-compose-full.yml` around lines 35 - 40, Update
the codepair-backend service's depends_on block to include yorkie and
yorkie-intelligence with the same health condition used for mongo/minio/qdrant;
specifically, in the docker-compose service definition for codepair-backend add
entries "yorkie: condition: service_healthy" and "yorkie-intelligence:
condition: service_healthy" so the backend waits for YORKIE_API_ADDR and
OLLAMA_HOST_URL targets to be healthy before startup.
| files: ["**/*.ts", "**/*.tsx"], | ||
| ignores: ["dist", "eslint.config.mjs"], |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🌐 Web query:
ESLint flat config ignores property behavior documentation
💡 Result:
In ESLint flat config, the ignores behavior is documented mainly in these official pages:
- Migration guide (eslintrc → flat config): explains that to replicate
.eslintignore/ignorePatterns, you putignores: [...]in a config object with no other keys, and notes the important glob difference thattemp.jsin flat config only matches next to the config file—use**/temp.jsto match anywhere. It also notes dotfiles are not ignored by default in flat config (add"**/.*"if desired). [1] - “Ignore Files” (flat config) docs: explains ignoring via
globalIgnores([...])(for “completely ignore” / global ignore behavior), default ignore patterns (notablynode_modules/and.git/), how ignore patterns are resolved (e.g.,.config/vs**/.config/), and how ignored-file warnings work. [2] - ESLint blog (flat config intro): clarifies the two meanings:
ignoresalongsidefilesfilters which files a config object applies to.- a config object with only
ignoresmakes ESLint ignore those files entirely (similar to legacyignorePatterns). [3]
Sources: [1] ESLint Migration Guide (Ignore Files section) [2] ESLint “Ignore Files” (flat config) documentation [3] ESLint blog: “New config system, Part 2: Introduction to flat config”
🏁 Script executed:
cat packages/codemirror/eslint.config.mjsRepository: yorkie-team/codepair
Length of output: 864
Move ignores to a separate config object for global ignore behavior.
In ESLint flat config, ignores alongside other properties filters which files that config applies to. To globally ignore files (equivalent to .eslintignore), define a separate config object with only ignores:
♻️ Proposed fix
export default [
+ {
+ ignores: ["dist/**", "eslint.config.mjs"],
+ },
{
files: ["**/*.ts", "**/*.tsx"],
- ignores: ["dist", "eslint.config.mjs"],
languageOptions: {🤖 Prompt for AI Agents
In `@packages/codemirror/eslint.config.mjs` around lines 7 - 8, Remove the ignores
entry from the existing config object that contains files:
["**/*.ts","**/*.tsx"] in eslint.config.mjs and add a separate top-level config
object whose sole property is ignores: ["dist","eslint.config.mjs"] so the
ignore behavior is global; locate the current config object (the one with files)
and update it to no longer include ignores, then create the new standalone
config object with only ignores to replicate .eslintignore semantics.
| import { Button, Menu, MenuItem, Paper } from "@mui/material"; | ||
| import { useState } from "react"; | ||
| import { useCMEditorContext } from "../CMEditorContext"; | ||
| import { CodeKeyType } from "../types"; | ||
|
|
||
| export const BOTTOM_BAR_HEIGHT = 25; | ||
|
|
||
| interface EditorBottomBarProps { | ||
| width: number | string; | ||
| } | ||
|
|
||
| function EditorBottomBar(props: EditorBottomBarProps) { | ||
| const { width } = props; | ||
| const { codeKey, setCodeKey } = useCMEditorContext(); | ||
| const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null); | ||
| const open = Boolean(anchorEl); | ||
|
|
||
| const handleOpen = (event: React.MouseEvent<HTMLButtonElement>) => { | ||
| setAnchorEl(event.currentTarget); | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, check the actual file content
cat -n packages/codemirror/src/components/EditorBottomBar.tsx | head -30Repository: yorkie-team/codepair
Length of output: 1081
🏁 Script executed:
# Find and examine tsconfig
fd tsconfig.jsonRepository: yorkie-team/codepair
Length of output: 208
🏁 Script executed:
# Check package.json for React version and JSX settings
cat packages/codemirror/package.json | grep -A 5 -B 5 '"react"' || cat package.json | grep -A 5 -B 5 '"react"'Repository: yorkie-team/codepair
Length of output: 607
🏁 Script executed:
# Check the tsconfig for the codemirror package
cat packages/codemirror/tsconfig.jsonRepository: yorkie-team/codepair
Length of output: 577
🏁 Script executed:
# Verify MouseEvent is available directly from react
rg "export.*MouseEvent" node_modules/@types/react/index.d.ts 2>/dev/null | head -5 || echo "Checking React type definitions..."Repository: yorkie-team/codepair
Length of output: 46
🏁 Script executed:
# Check if other files in the repo use MouseEvent from react
rg "MouseEvent" packages/codemirror/src --type tsx --type ts -B 2 | head -30Repository: yorkie-team/codepair
Length of output: 91
🏁 Script executed:
# Check the React types more carefully - look for exported event types
python3 << 'EOF'
import json
import subprocess
# Try to find how MouseEvent is typically used
result = subprocess.run(['rg', 'React\\.Mouse', 'packages/', '--type', 'ts', '--type', 'tsx'],
capture_output=True, text=True)
print("Uses of React.MouseEvent:")
print(result.stdout[:1000] if result.stdout else "No results")
# Check for MouseEvent imports
result2 = subprocess.run(['rg', 'MouseEvent.*from.*react', 'packages/', '--type', 'ts', '--type', 'tsx'],
capture_output=True, text=True)
print("\nImports of MouseEvent from react:")
print(result2.stdout[:1000] if result2.stdout else "No results")
EOFRepository: yorkie-team/codepair
Length of output: 146
🏁 Script executed:
# Check how other event handlers are typed in the codemirror package
rg "event:" packages/codemirror/src -A 1 --type ts | head -40Repository: yorkie-team/codepair
Length of output: 754
🏁 Script executed:
# Look for other React event handler usages in the entire packages directory
rg "event.*HTMLElement|HTMLButtonElement" packages/ --type ts -B 1 -A 1 | head -50Repository: yorkie-team/codepair
Length of output: 2200
🏁 Script executed:
# Check imports in files using MouseEvent vs React.MouseEvent
echo "=== useFormatUtils.ts (uses MouseEvent) ===" && \
head -10 packages/codemirror/src/hooks/useFormatUtils.ts && \
echo -e "\n=== DownloadMenu.tsx (uses MouseEvent) ===" && \
head -10 packages/frontend/src/components/common/DownloadMenu.tsx && \
echo -e "\n=== DocumentHeader.tsx (uses React.MouseEvent) ===" && \
head -15 packages/frontend/src/components/headers/DocumentHeader.tsxRepository: yorkie-team/codepair
Length of output: 1528
🏁 Script executed:
# Check if React is imported in files that use React.MouseEvent
echo "=== EditorBottomBar.tsx (being reviewed) ===" && \
grep -n "^import" packages/codemirror/src/components/EditorBottomBar.tsx && \
echo -e "\n=== frontend EditorBottomBar.tsx ===" && \
grep -n "^import" packages/frontend/src/features/editor/shared/components/EditorBottomBar.tsx && \
echo -e "\n=== DocumentHeader.tsx ===" && \
grep -n "^import.*React" packages/frontend/src/components/headers/DocumentHeader.tsxRepository: yorkie-team/codepair
Length of output: 628
🏁 Script executed:
# Check if DocumentHeader actually uses React.MouseEvent or if there's something else
grep -n "React.MouseEvent\|React.MouseEventHandler" packages/frontend/src/components/headers/DocumentHeader.tsxRepository: yorkie-team/codepair
Length of output: 150
🏁 Script executed:
# Let's verify by checking what's actually exported from react for event types
python3 << 'EOF'
import subprocess
# Search for how React is used in the frontend package
result = subprocess.run([
'rg', 'React\\.', 'packages/frontend/src/components/headers/DocumentHeader.tsx',
'-n'
], capture_output=True, text=True)
print("React namespace usage in DocumentHeader.tsx:")
print(result.stdout if result.stdout else "No React.* usage found")
EOFRepository: yorkie-team/codepair
Length of output: 293
🏁 Script executed:
# Check full imports of DocumentHeader to see if React is imported as namespace
head -30 packages/frontend/src/components/headers/DocumentHeader.tsx | grep -n "import"Repository: yorkie-team/codepair
Length of output: 1257
Remove React namespace reference for MouseEvent type.
React.MouseEvent is used on line 18 but React namespace isn't imported. With jsx: "react-jsx", this will fail TypeScript compilation. Import the type directly instead, matching the pattern used elsewhere in the codebase.
🛠️ Proposed fix
-import { useState } from "react";
+import { useState, type MouseEvent } from "react";
@@
-const handleOpen = (event: React.MouseEvent<HTMLButtonElement>) => {
+const handleOpen = (event: MouseEvent<HTMLButtonElement>) => {🤖 Prompt for AI Agents
In `@packages/codemirror/src/components/EditorBottomBar.tsx` around lines 1 - 20,
The event type on handleOpen uses the missing React namespace
(React.MouseEvent); import the MouseEvent type directly from "react" and update
the handler signature to use that imported type instead of React.MouseEvent so
TypeScript compiles with jsx: "react-jsx" (locate handleOpen in EditorBottomBar
and the top imports to add import type { MouseEvent } from "react").
| declare module "markdown-it-task-checkbox"; | ||
| declare module "markdown-it-link-attributes"; | ||
| declare module "markdown-it-image-lazy-loading"; | ||
| declare module "react-scroll-sync"; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if `@types/react-scroll-sync` exists on npm
npm view `@types/react-scroll-sync` version 2>/dev/null && echo "=== `@types/react-scroll-sync` FOUND ===" || echo "=== `@types/react-scroll-sync` NOT FOUND ==="
# Check if react-scroll-sync includes its own types
echo ""
echo "=== Checking react-scroll-sync package ==="
npm view react-scroll-sync types typings --json 2>/dev/null | head -20Repository: yorkie-team/codepair
Length of output: 172
Remove the bare module declaration — react-scroll-sync includes native type definitions.
The package provides its own type definitions at ./dist/index.d.ts and @types/react-scroll-sync is also available on npm. The bare declare module "react-scroll-sync"; declaration masks these actual types, treating all exports as any. Simply removing this line allows TypeScript to use the proper type information from the package itself.
🤖 Prompt for AI Agents
In `@packages/codemirror/src/global.d.ts` at line 6, Remove the bare module
augmentation `declare module "react-scroll-sync";` which masks the package's
real types; delete that line so TypeScript will pick up the package's own
`./dist/index.d.ts` (or `@types`) instead, then run a typecheck to verify imports
using react-scroll-sync now have proper typings.
|
|
||
| ## Interface | ||
|
|
||
| ``` |
There was a problem hiding this comment.
Add language specifiers to fenced code blocks.
The fenced code blocks at lines 33, 60, and 98 lack language specifiers. While these are file path indicators, adding text as the language improves rendering consistency and satisfies the linter.
📝 Proposed fix
-```
+```text
packages/ui/src/types/EditorPort.ts- +text
packages/codemirror/src/CMEditorAdapter.ts
-```
+```text
packages/ui/src/types/
└── EditorPort.ts # Editor-agnostic interface
Also applies to: 60-60, 98-98
🧰 Tools
🪛 markdownlint-cli2 (0.20.0)
[warning] 33-33: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
In `@packages/frontend/design/editor-port.md` at line 33, Update the three fenced
code blocks that show file paths to include the language specifier "text" so the
linter and renderer handle them consistently; specifically modify the blocks
referencing "packages/ui/src/types/EditorPort.ts",
"packages/codemirror/src/CMEditorAdapter.ts", and the tree block containing
"packages/ui/src/types/ └── EditorPort.ts" to use ```text instead of ``` to
ensure consistent rendering.
|
|
||
| To add a new editor (e.g., ProseMirror), you would: | ||
|
|
||
| 1. Create `features/editor/prosemirror/` with its own components, hooks, and utils |
There was a problem hiding this comment.
Update hypothetical editor path to match monorepo pattern.
Line 139 suggests creating features/editor/prosemirror/ for a new editor, but this doesn't align with the monorepo structure. Based on how CodeMirror was extracted to packages/codemirror/, a new editor should be created as packages/prosemirror/ (a sibling package), not nested under frontend features.
📝 Proposed fix
To add a new editor (e.g., ProseMirror), you would:
-1. Create `features/editor/prosemirror/` with its own components, hooks, and utils
+1. Create `packages/prosemirror/` as a new package with its own components, hooks, and utils
2. Create `PMEditorAdapter.ts` implementing `EditorPort`
3. Dispatch `setEditorPort(new PMEditorAdapter(...))` from the ProseMirror editor component
-4. Wire the new editor into `DocumentView` via `index.ts`
+4. Wire the new editor into the frontend app shell via appropriate imports📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 1. Create `features/editor/prosemirror/` with its own components, hooks, and utils | |
| To add a new editor (e.g., ProseMirror), you would: | |
| 1. Create `packages/prosemirror/` as a new package with its own components, hooks, and utils | |
| 2. Create `PMEditorAdapter.ts` implementing `EditorPort` | |
| 3. Dispatch `setEditorPort(new PMEditorAdapter(...))` from the ProseMirror editor component | |
| 4. Wire the new editor into the frontend app shell via appropriate imports |
🤖 Prompt for AI Agents
In `@packages/frontend/design/editor-port.md` at line 139, The doc suggests
creating features/editor/prosemirror/, but to follow the monorepo package
pattern used by packages/codemirror/ you should instead create a top-level
sibling package packages/prosemirror/ (not nested under frontend features);
update any references in the spec and any proposed import paths to point to
packages/prosemirror/ and mirror the packaging/setup used by
packages/codemirror/ (package.json, build/config, exports) so the new editor
lives as its own package.
- Fix React.MouseEvent type error by importing MouseEvent directly from react - Add yorkie and yorkie-intelligence to codepair-backend depends_on - Separate ESLint ignores into standalone config object for global behavior Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/frontend/design/architecture.md (1)
17-18:⚠️ Potential issue | 🟡 MinorArchitecture doc still reflects pre‑monorepo editor layout.
Lines 17–69 and the “Feature-based Structure (After)” block (Lines 120–140) describefeatures/editor/codemirrorand addingfeatures/editor/prosemirror. The refactor moved editor implementations into siblingpackages/*(e.g.,packages/codemirror), so these examples should be updated to the new package-based layout.Also applies to: 49-69, 120-140
🤖 Fix all issues with AI agents
In @.husky/pre-commit:
- Around line 6-12: The pre-commit loop only sets LINT_FRONTEND and LINT_BACKEND
based on CHANGED_FILES, missing packages/ui and packages/codemirror; update the
loop to also detect files under ^packages/ui/ and ^packages/codemirror/ and set
new flags (e.g., LINT_UI and LINT_CODEMIRROR) accordingly, and then add/ensure
the corresponding conditional blocks later in the script check LINT_UI and
LINT_CODEMIRROR (similar to LINT_FRONTEND/LINT_BACKEND) to invoke lint-staged
for those packages.
In `@packages/codemirror/src/components/Editor.tsx`:
- Around line 98-112: The useEffect that sets up the CodeMirror instance in
Editor.tsx includes applyFormat in its dependency array but applyFormat is not
referenced inside the effect, causing unnecessary re-creation of the editor;
remove applyFormat from the dependency array (keep element, client, doc,
codeKey, themeMode, fileUploadEnabled, handleUploadImage, intelligenceEnabled,
updateFormatBar, setKeymapConfig, setupVimKeybindings, setEditorPort) so the
effect only runs when truly relevant values change.
In `@packages/codemirror/src/plugins/intelligencePivot.ts`:
- Around line 5-7: INTELLIGENCE_HEADER_ID and INTELLIGENCE_FOOTER_ID are
duplicated across packages; centralize them to a single shared module and update
imports. Move the constants currently defined in intelligencePivot.ts into a
shared export (for example in `@codepair/ui` or export them from
`@codepair/codemirror` as a single source-of-truth), delete the local definitions
of INTELLIGENCE_HEADER_ID and INTELLIGENCE_FOOTER_ID, and update any consumers
to import the shared constants instead of redefining them.
In `@packages/frontend/design/editor-port.md`:
- Around line 128-133: Update the documentation paths so they point to the new
monorepo package locations: replace occurrences of
"features/editor/codemirror/**" with "packages/editor/codemirror/**" and replace
"features/intelligence/utils/intelligencePivot.ts" with
"packages/intelligence/utils/intelligencePivot.ts" (or the exact package name
housing the intelligence ViewPlugin) in editor-port.md so the import-boundary
guidance matches the current packages/* layout.
In
`@packages/frontend/src/features/intelligence/components/YorkieIntelligenceFooter.tsx`:
- Around line 24-27: The width calculation in YorkieIntelligenceFooter uses
useMemo to compute width = (editorStore.editorPort?.getContentWidth() ?? 0) - 12
which yields -12 when editorPort is null; fix by preventing negative widths or
avoiding render when editorPort is missing—either clamp the result to a minimum
of 0 (e.g., ensure width = Math.max(0,
(editorStore.editorPort?.getContentWidth() ?? 0) - 12)) or early-return from the
YorkieIntelligenceFooter render when editorStore.editorPort is falsy (e.g., if
(!editorStore.editorPort) return null); update the width constant and any
dependent layout usage accordingly.
🧹 Nitpick comments (4)
packages/codemirror/src/plugins/intelligencePivot.ts (1)
33-35: Thecomparemethod appears unused.CodeMirror 6's
WidgetTypeinterface only useseq()for equality comparisons. Thecomparemethod is not part of the standard API and will never be called by the framework. Consider removing it to reduce confusion.🧹 Proposed cleanup
eq(widget: IntelligencePivotWidget) { return widget.selectionRange === this.selectionRange; } - - compare(widget: IntelligencePivotWidget) { - return widget.selectionRange === this.selectionRange; - }packages/codemirror/src/hooks/useFormatUtils.ts (1)
180-183: Type cast toCMEditorAdapterbreaks the EditorPort abstraction.Casting
editorPorttoCMEditorAdapterto access.viewcouples this hook to the CodeMirror implementation, defeating the purpose of the EditorPort abstraction. If a different editor is introduced, this code will fail.Consider adding a format method to the
EditorPortinterface or keeping format logic within theCMEditorAdapterimplementation:♻️ Suggested approach
Option 1: Add an
applyFormatmethod toEditorPort:interface EditorPort { // existing methods... applyFormat(formatType: FormatType): void; }Option 2: If format tooling is CodeMirror-specific, consider moving this hook entirely into the
@codepair/codemirrorpackage where the coupling is acceptable.packages/frontend/src/features/intelligence/components/YorkieIntelligenceFeature.tsx (1)
110-128: Normalize selection bounds before replaceRange.If
editorPort.getSelection()can return a reversed range (anchor > head), the current logic may insert at incorrect bounds. Consider normalizingfrom/to(or asserting the contract) before computingselectionFrom/selectionTo.🔧 Suggested guard
- const selection = editorStore.editorPort.getSelection(); - let from = selection.from; - const to = selection.to; + const { from: rawFrom, to: rawTo } = editorStore.editorPort.getSelection(); + let from = Math.min(rawFrom, rawTo); + const to = Math.max(rawFrom, rawTo);packages/codemirror/src/CMEditorSuite.tsx (1)
82-82: Consider making the fallback width configurable or documenting the choice.The hardcoded fallback of
800whenwidthis not a number works, but this magic number could be documented or derived from a constant for clarity.♻️ Optional: Extract to named constant
+const DEFAULT_WINDOW_WIDTH = 800; + function CMEditorSuite({ // ... }) { // ... - const windowWidth = typeof width === "number" ? width : 800; + const windowWidth = typeof width === "number" ? width : DEFAULT_WINDOW_WIDTH;
| for FILE in $CHANGED_FILES; do | ||
| if [[ "$FILE" =~ ^frontend/ ]]; then | ||
| if [[ "$FILE" =~ ^packages/frontend/ ]]; then | ||
| LINT_FRONTEND=true | ||
| elif [[ "$FILE" =~ ^backend/ ]]; then | ||
| elif [[ "$FILE" =~ ^packages/backend/ ]]; then | ||
| LINT_BACKEND=true | ||
| fi | ||
| done |
There was a problem hiding this comment.
Add lint coverage for new packages (ui, codemirror).
Line 7 only sets lint flags for frontend/backend. Changes in packages/ui or packages/codemirror won’t trigger lint-staged, so those packages can bypass pre-commit checks.
✅ Proposed fix (add flags + blocks for new packages)
LINT_FRONTEND=false
LINT_BACKEND=false
+LINT_UI=false
+LINT_CODEMIRROR=false
for FILE in $CHANGED_FILES; do
if [[ "$FILE" =~ ^packages/frontend/ ]]; then
LINT_FRONTEND=true
elif [[ "$FILE" =~ ^packages/backend/ ]]; then
LINT_BACKEND=true
+ elif [[ "$FILE" =~ ^packages/ui/ ]]; then
+ LINT_UI=true
+ elif [[ "$FILE" =~ ^packages/codemirror/ ]]; then
+ LINT_CODEMIRROR=true
fi
done
+# UI package
+if [ "$LINT_UI" = true ]; then
+ echo "Changes detected in ui. Linting & Formatting ui..."
+ cd packages/ui
+ npx lint-staged
+ if [ $? -ne 0 ]; then
+ echo "UI Linting & Formatting failed. Commit aborted."
+ exit 1
+ fi
+ cd ../..
+fi
+
+# CodeMirror package
+if [ "$LINT_CODEMIRROR" = true ]; then
+ echo "Changes detected in codemirror. Linting & Formatting codemirror..."
+ cd packages/codemirror
+ npx lint-staged
+ if [ $? -ne 0 ]; then
+ echo "CodeMirror Linting & Formatting failed. Commit aborted."
+ exit 1
+ fi
+ cd ../..
+fi🤖 Prompt for AI Agents
In @.husky/pre-commit around lines 6 - 12, The pre-commit loop only sets
LINT_FRONTEND and LINT_BACKEND based on CHANGED_FILES, missing packages/ui and
packages/codemirror; update the loop to also detect files under ^packages/ui/
and ^packages/codemirror/ and set new flags (e.g., LINT_UI and LINT_CODEMIRROR)
accordingly, and then add/ensure the corresponding conditional blocks later in
the script check LINT_UI and LINT_CODEMIRROR (similar to
LINT_FRONTEND/LINT_BACKEND) to invoke lint-staged for those packages.
| }, [ | ||
| element, | ||
| editorStore.client, | ||
| editorStore.doc, | ||
| configStore.codeKey, | ||
| client, | ||
| doc, | ||
| codeKey, | ||
| themeMode, | ||
| workspaceStore.data, | ||
| featureSettingStore.fileUpload?.enable, | ||
| dispatch, | ||
| createUploadUrl, | ||
| uploadFile, | ||
| fileUploadEnabled, | ||
| handleUploadImage, | ||
| intelligenceEnabled, | ||
| applyFormat, | ||
| updateFormatBar, | ||
| setKeymapConfig, | ||
| setupVimKeybindings, | ||
| setEditorPort, | ||
| ]); |
There was a problem hiding this comment.
Remove applyFormat from the dependency array — it's not used in the effect.
applyFormat is included in the dependencies but is never used inside the effect body. This causes unnecessary editor recreation whenever the applyFormat reference changes, destroying and rebuilding the entire CodeMirror instance.
🔧 Proposed fix
}, [
element,
client,
doc,
codeKey,
themeMode,
fileUploadEnabled,
handleUploadImage,
intelligenceEnabled,
- applyFormat,
updateFormatBar,
setKeymapConfig,
setupVimKeybindings,
setEditorPort,
]);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| }, [ | |
| element, | |
| editorStore.client, | |
| editorStore.doc, | |
| configStore.codeKey, | |
| client, | |
| doc, | |
| codeKey, | |
| themeMode, | |
| workspaceStore.data, | |
| featureSettingStore.fileUpload?.enable, | |
| dispatch, | |
| createUploadUrl, | |
| uploadFile, | |
| fileUploadEnabled, | |
| handleUploadImage, | |
| intelligenceEnabled, | |
| applyFormat, | |
| updateFormatBar, | |
| setKeymapConfig, | |
| setupVimKeybindings, | |
| setEditorPort, | |
| ]); | |
| }, [ | |
| element, | |
| client, | |
| doc, | |
| codeKey, | |
| themeMode, | |
| fileUploadEnabled, | |
| handleUploadImage, | |
| intelligenceEnabled, | |
| updateFormatBar, | |
| setKeymapConfig, | |
| setupVimKeybindings, | |
| setEditorPort, | |
| ]); |
🤖 Prompt for AI Agents
In `@packages/codemirror/src/components/Editor.tsx` around lines 98 - 112, The
useEffect that sets up the CodeMirror instance in Editor.tsx includes
applyFormat in its dependency array but applyFormat is not referenced inside the
effect, causing unnecessary re-creation of the editor; remove applyFormat from
the dependency array (keep element, client, doc, codeKey, themeMode,
fileUploadEnabled, handleUploadImage, intelligenceEnabled, updateFormatBar,
setKeymapConfig, setupVimKeybindings, setEditorPort) so the effect only runs
when truly relevant values change.
|
|
||
| const INTELLIGENCE_HEADER_ID = "yorkie-intelligence-header"; | ||
| const INTELLIGENCE_FOOTER_ID = "yorkie-intelligence-footer"; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for usages of these intelligence pivot IDs across the codebase
# to verify if they're referenced elsewhere and need to be shared.
echo "=== Searching for INTELLIGENCE_HEADER_ID / INTELLIGENCE_FOOTER_ID ==="
rg -n "INTELLIGENCE_HEADER_ID|INTELLIGENCE_FOOTER_ID"
echo ""
echo "=== Searching for the literal string values ==="
rg -n "yorkie-intelligence-header|yorkie-intelligence-footer"Repository: yorkie-team/codepair
Length of output: 2408
Consolidate constants to a shared location to avoid duplication.
The INTELLIGENCE_HEADER_ID and INTELLIGENCE_FOOTER_ID constants are currently defined in two places: @codepair/frontend (exported) and @codepair/codemirror (locally). Since both packages reference the same DOM element identifiers and the frontend's intelligence features query these elements by ID, these constants should be centralized—either exported from @codepair/codemirror and imported by the frontend, or defined in @codepair/ui for cross-package consumption. Duplicate definitions risk divergence and maintenance issues.
🤖 Prompt for AI Agents
In `@packages/codemirror/src/plugins/intelligencePivot.ts` around lines 5 - 7,
INTELLIGENCE_HEADER_ID and INTELLIGENCE_FOOTER_ID are duplicated across
packages; centralize them to a single shared module and update imports. Move the
constants currently defined in intelligencePivot.ts into a shared export (for
example in `@codepair/ui` or export them from `@codepair/codemirror` as a single
source-of-truth), delete the local definitions of INTELLIGENCE_HEADER_ID and
INTELLIGENCE_FOOTER_ID, and update any consumers to import the shared constants
instead of redefining them.
| After this change, `"codemirror"` and `"@codemirror"` imports only appear in: | ||
|
|
||
| - `features/editor/codemirror/**` — expected, this is the CM implementation | ||
| - `features/intelligence/utils/intelligencePivot.ts` — a CM ViewPlugin (moving it to `codemirror/` is a follow-up) | ||
|
|
||
| No other feature, shared component, or page imports from CodeMirror directly. |
There was a problem hiding this comment.
Update import-boundary paths to monorepo package locations.
Line 130 still lists features/editor/codemirror/**, and Line 131 references features/intelligence/...; these should match the new packages/* layout to prevent stale guidance.
📝 Suggested update
- - `features/editor/codemirror/**`
- - `features/intelligence/utils/intelligencePivot.ts`
+ - `packages/codemirror/**`
+ - `packages/frontend/src/features/intelligence/utils/intelligencePivot.ts`🤖 Prompt for AI Agents
In `@packages/frontend/design/editor-port.md` around lines 128 - 133, Update the
documentation paths so they point to the new monorepo package locations: replace
occurrences of "features/editor/codemirror/**" with
"packages/editor/codemirror/**" and replace
"features/intelligence/utils/intelligencePivot.ts" with
"packages/intelligence/utils/intelligencePivot.ts" (or the exact package name
housing the intelligence ViewPlugin) in editor-port.md so the import-boundary
guidance matches the current packages/* layout.
| const width = useMemo( | ||
| () => editorStore.cmView!.contentDOM.getBoundingClientRect().width - 12, | ||
| [editorStore.cmView] | ||
| () => (editorStore.editorPort?.getContentWidth() ?? 0) - 12, | ||
| [editorStore.editorPort] | ||
| ); |
There was a problem hiding this comment.
Potential negative width when editorPort is null.
The null safety improvement is good, but when editorPort is null, the width becomes 0 - 12 = -12, which could cause layout issues. Consider guarding the component render or using a minimum width:
🛡️ Proposed fix
const width = useMemo(
- () => (editorStore.editorPort?.getContentWidth() ?? 0) - 12,
+ () => Math.max((editorStore.editorPort?.getContentWidth() ?? 0) - 12, 0),
[editorStore.editorPort]
);Alternatively, guard the render:
if (!editorStore.editorPort) return null;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const width = useMemo( | |
| () => editorStore.cmView!.contentDOM.getBoundingClientRect().width - 12, | |
| [editorStore.cmView] | |
| () => (editorStore.editorPort?.getContentWidth() ?? 0) - 12, | |
| [editorStore.editorPort] | |
| ); | |
| const width = useMemo( | |
| () => Math.max((editorStore.editorPort?.getContentWidth() ?? 0) - 12, 0), | |
| [editorStore.editorPort] | |
| ); |
🤖 Prompt for AI Agents
In
`@packages/frontend/src/features/intelligence/components/YorkieIntelligenceFooter.tsx`
around lines 24 - 27, The width calculation in YorkieIntelligenceFooter uses
useMemo to compute width = (editorStore.editorPort?.getContentWidth() ?? 0) - 12
which yields -12 when editorPort is null; fix by preventing negative widths or
avoiding render when editorPort is missing—either clamp the result to a minimum
of 0 (e.g., ensure width = Math.max(0,
(editorStore.editorPort?.getContentWidth() ?? 0) - 12)) or early-return from the
YorkieIntelligenceFooter render when editorStore.editorPort is falsy (e.g., if
(!editorStore.editorPort) return null); update the width constant and any
dependent layout usage accordingly.
- Add pre-commit lint coverage for ui and codemirror packages - Add lint-staged config to ui and codemirror package.json - Remove unused applyFormat from Editor useEffect dependency array - Guard against negative width in YorkieIntelligenceFooter Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update root README.md package descriptions and paths to packages/ - Update frontend architecture.md to reflect CodeMirror extraction - Update editor-port.md import boundaries and new editor guide - Update backend README.md paths and relative links Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cture docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Define EditorSuiteProps<TDoc, TClient> in @codepair/ui as the shared editor component contract, enabling editor packages to be swapped without modifying the app shell. Unify CodeKeyType enum across @codepair/ui, @codepair/codemirror, and frontend settings to eliminate the `as unknown as` type hack in DocumentView. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rename CMEditorSuite → EditorSuite and CMEditorAdapter → EditorAdapter in the public API so that alternative editor packages can export the same names naturally without CM-prefixed aliases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tion, and export renaming - docs/editor-port-architecture.md: Add EditorSuiteProps and CodeKeyType sections, update CMEditorSuite references to EditorSuite, fix "How to Add a New Editor" guide - packages/frontend/README.md: Update directory structure to feature-based architecture, fix docs link path - packages/frontend/design/redux-query.md: Add feature slice paths Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- YorkieCodeMirrorDocType → YorkieDocType - YorkieCodeMirrorPresenceType → YorkiePresenceType - Enables proxy package strategy for editor swapping without frontend changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The codemirror package pinned @yorkie-js/sdk@0.6.41 while the frontend used 0.6.48, causing type incompatibilities at build time. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds `EditorPosition = number | number[]` union type to EditorPort so SmartEditor can use tree-index coordinates (number[]) while CodeMirror continues to use character offsets (number). - Export `EditorPosition` from @codepair/ui - Update all EditorPort method signatures to use EditorPosition - Add `getContent(): string` method to EditorPort interface - Implement `getContent()` in CMEditorAdapter - Fix arithmetic casts in codemirror/frontend (always number in CM context) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
What this PR does / why we need it:
Refactors CodePair's frontend from a single monolithic package into a three-package pnpm workspace and moves
backend/intopackages/for a consistent monorepo layout.The current frontend bundles all UI components, CodeMirror editor logic, and Yorkie collaboration into one package. This prevents adding a second editor (e.g., a
yorkie.Tree-based rich text editor) because CodeMirror usesyorkie.Text—these are incompatible CRDTs that cannot share a sync layer. Each editor must own its full vertical slice.
New package structure:
@codepair/ui— Editor-agnostic shared types (EditorPort,EditorModeType,PresenceInfo) and pure UI components@codepair/codemirror— Self-contained CodeMirror 6 vertical slice (editor, toolbar, preview, Yorkie sync), exposed via a singleCMEditorSuitecomponent@codepair/frontend— App shell (routing, auth, workspace) that renders editors through theEditorPortinterface@codepair/backend— Moved frombackend/topackages/backend/(no code changes)ESLint
no-restricted-importsrules enforce package boundaries at lint time. No separate build step is needed for library packages — Vite resolvesworkspace:*packages directly from source.Which issue(s) this PR fixes:
Fixes
Special notes for your reviewer:
EditorPortinterface is the key abstraction — app shell features (scroll-to-user, file export) call methods on it without knowing which editor is runningCMEditorSuiteaccepts anintelligenceSlotprop to render AI features without a reverse dependency from the editor package to the app shellListRevisionsRPC was not available in older versions)docs/editor-port-architecture.mdwith a step-by-step guide for adding a new editor packageDoes this PR introduce a user-facing change?:
Checklist:
Summary by CodeRabbit
Release Notes