Skip to content

Conversation

aaryan610
Copy link
Member

@aaryan610 aaryan610 commented Aug 27, 2025

Description

This PR refactors the tsconfig of all the react libraries by-

  1. Adding all the common options to the base config file.
  2. Removing redundant flags from the package.json file.

This PR also adds build and dev commands to the i18n package.

Type of Change

  • Improvement (change that would cause existing functionality to not work as expected)

Summary by CodeRabbit

  • New Features

    • i18n and services now ship compiled artifacts (ESM/CJS) for improved packaging and compatibility.
  • Bug Fixes

    • Editor file restoration made more robust to avoid errors with invalid node types.
  • Refactor

    • Callout attribute enum and related types renamed, and bubble menu component props typed more strictly (public signatures updated).
  • Chores

    • Standardized build pipeline across packages (compile then bundle), removed forced minification, aligned type-check scripts, consolidated TS configs, added cleanup scripts, and added missing type definitions dependency.

Copy link
Contributor

coderabbitai bot commented Aug 27, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Repository-wide TypeScript/build standardization and targeted editor typing refactors: package build/check scripts unified to run tsc and tsup, several packages switched to dist-based publishing and added tsup configs, many tsconfig files simplified to rely on shared presets, and numerous editor-source type-only/import and typing adjustments (including renames for callout enums and EditorBubbleMenu API change).

Changes

Cohort / File(s) Summary
Build scripts & package.json updates
packages/editor/package.json, packages/hooks/package.json, packages/propel/package.json, packages/services/package.json, packages/shared-state/package.json, packages/ui/package.json, packages/utils/package.json
Standardized build to tsc && tsup (removed explicit --minify), changed check:types from tsc --noEmittsc, added/updated clean scripts where present; editor added @types/uuid devDependency.
Dist-based publishing & i18n bundling
packages/i18n/package.json, packages/services/package.json, packages/i18n/tsup.config.ts
Switched package entrypoints to dist (main, module, types), added files: ["dist/**"], introduced build/dev scripts and tsup config for i18n (esm/cjs, dts, minify, splitting, external react).
Per-package tsconfig updates
packages/editor/tsconfig.json, packages/hooks/tsconfig.json, packages/i18n/tsconfig.json, packages/propel/tsconfig.json, packages/services/tsconfig.json, packages/shared-state/tsconfig.json, packages/ui/tsconfig.json, packages/utils/tsconfig.json
Removed local jsx/lib overrides and exclude entries in favor of shared presets; many configs added noEmit: true or now extend @plane/typescript-config/react-library.json; include scopes preserved/adjusted.
Shared TypeScript presets
packages/typescript-config/base.json, packages/typescript-config/react-library.json
base.json adds strictNullChecks: true; react-library.json adds allowSyntheticDefaultImports, sourceMap, and top-level exclude.
Editor typing, enums & small runtime-safe tweaks
packages/editor/src/core/extensions/code/code-block.ts, packages/editor/src/core/extensions/emoji/suggestion.ts, packages/editor/src/core/hooks/use-file-upload.ts, packages/editor/src/core/plugins/file/restore.ts, packages/editor/src/core/components/menus/bubble-menu/node-selector.tsx, packages/editor/src/core/components/menus/menu-items.ts, packages/editor/src/core/extensions/callout/utils.ts, packages/editor/src/core/extensions/callout/types.ts, packages/editor/src/core/extensions/callout/block.tsx, packages/editor/src/core/extensions/emoji/extension.ts, packages/editor/src/core/extensions/mentions/utils.ts, packages/editor/src/core/extensions/slash-commands/root.tsx, packages/editor/src/core/components/menus/bubble-menu/root.tsx
Many type-only imports, removal of // @ts-expect-error suppressions, explicit casts/assertions (e.g., node.type.name as CORE_EXTENSIONS, catch (errPayload: any), items typed as EditorMenuItem[]), rename EAttributeNames → ECalloutAttributeNames and switch to computed attribute keys, capture of extension options for serializer, widened popup types and stabilized clientRect handling, and EditorBubbleMenu prop/API signature change to require editor in props. No deliberate runtime behavior changes beyond safer typings/guards.

Sequence Diagram(s)

sequenceDiagram
  participant Parent as Host Component
  participant EditorBubbleMenu as EditorBubbleMenu
  participant Child as Menu Child Components

  Note over Parent,EditorBubbleMenu: New explicit prop-based editor flow
  Parent->>EditorBubbleMenu: render(editor)
  EditorBubbleMenu->>EditorBubbleMenu: store local editor variable
  EditorBubbleMenu->>Child: pass `editor` prop to children
  Child-->>EditorBubbleMenu: query state / isActive using editor
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

🛠️refactor

Suggested reviewers

  • sriramveeraghanta
  • prateekshourya29
  • lifeiscontent

Poem

Hop hop—tsc hums, tsup takes flight,
Types snugged, enums renamed just right.
Editor props now passed with care,
A rabbit tidies imports there.
Cheers to builds that run at night! 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/library-tsconfig

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbit in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbit in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbit gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbit read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbit ignore or @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbit summary or @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbit or @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the TypeScript configuration across all React libraries to centralize common settings and standardize build processes.

  • Moves common TypeScript compiler options to the base configuration files
  • Standardizes build and type-checking commands across all packages
  • Adds build infrastructure and commands to the i18n package

Reviewed Changes

Copilot reviewed 23 out of 24 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/typescript-config/react-library.json Adds common compiler options and exclude patterns
packages/typescript-config/base.json Adds strictNullChecks to base configuration
packages/*/tsconfig.json Removes redundant compiler options and exclude patterns
packages/*/package.json Standardizes build and type-checking scripts
packages/i18n/ Adds tsup configuration and build/dev commands
packages/editor/ Updates to use new configuration and fixes TypeScript issues

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +69 to 70
} catch (errPayload: any) {
const error = errPayload?.response?.data?.error || "Something went wrong";
Copy link
Preview

Copilot AI Aug 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using any type defeats the purpose of TypeScript's type safety. Consider using unknown instead and properly type guard the error payload, or define a proper error type interface.

Suggested change
} catch (errPayload: any) {
const error = errPayload?.response?.data?.error || "Something went wrong";
} catch (errPayload: unknown) {
let error = "Something went wrong";
if (
typeof errPayload === "object" &&
errPayload !== null &&
"response" in errPayload &&
typeof (errPayload as any).response === "object" &&
(errPayload as any).response !== null &&
"data" in (errPayload as any).response &&
typeof (errPayload as any).response.data === "object" &&
(errPayload as any).response.data !== null &&
"error" in (errPayload as any).response.data
) {
error = (errPayload as any).response.data.error;
}

Copilot uses AI. Check for mistakes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a good suggestion, however, I would write it differently, e.g.

function isResponseError(err): err is ResponseError {
   ...
}

then your code would look like:

let error = "Something went wrong";

if (isResponseError(err)) {
   error = err.response.data.error;
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/i18n/package.json (1)

23-29: Add React and MobX-React as peers and externalize in your bundle

We’ve confirmed that the i18n package directly imports from both React and mobx-react at runtime, so React must be declared as a peer dependency and excluded from your bundle to avoid duplicate copies and inflated output.

Files importing React or mobx-react at runtime:

  • packages/i18n/src/hooks/use-translation.ts (line 1: import { useContext } from "react")
  • packages/i18n/src/context/index.tsx (lines 1–2: import { observer } from "mobx-react"; import React, { createContext } from "react";)

Please apply the following updates:

  1. package.json (packages/i18n/package.json)
      "dependencies": {
        "@plane/utils": "workspace:*",
        "intl-messageformat": "^10.7.11",
        "mobx": "^6.13.5",
        "mobx-react": "^9.1.0",
        "lodash": "^4.17.21"
      },
  • "peerDependencies": {
  •  "react": ">=18",
    
  •  "react-dom": ">=18"
    
  • },

2. **tsup.config.ts** (packages/i18n/tsup.config.ts)  
```diff
export default defineConfig({
  entry: ["src/index.ts"],
-    external: ["react"],
+    external: ["react", "react-dom", "mobx", "mobx-react"],
  format: ["cjs", "esm"],
  dts: true,
});

These changes will:

  • Prevent bundling React and React-DOM, ensuring consumers manage the React version.
  • Avoid shipping a second copy of mobx-react and its React dependency.
  • Keep your bundle lean by externalizing all peer libraries.

Let me know if you need any help tweaking the config!

🧹 Nitpick comments (21)
packages/hooks/tsconfig.json (1)

3-3: Standardize include path style across packages

Hooks uses "./src" while other packages (e.g., propel) use "src". Pick one style repo-wide to avoid churn in diffs and IDE globs. Suggest "src" for consistency.

Apply:

-  "include": ["./src"]
+  "include": ["src"]
packages/i18n/tsconfig.json (1)

3-7: Remove redundant resolveJsonModule; base already enables it

packages/typescript-config/base.json sets resolveJsonModule: true, so this override is unnecessary noise. Also consider aligning include path style with others.

Apply:

   "extends": "@plane/typescript-config/react-library.json",
-  "compilerOptions": {
-    "resolveJsonModule": true
-  },
-  "include": ["./src"]
+  "include": ["src"]
packages/typescript-config/base.json (2)

16-16: strictNullChecks is redundant when strict is true

TypeScript’s strict already enables strictNullChecks. Keeping both is benign but adds noise. Recommend removing for clarity.

Apply:

-    "strictNullChecks": true,

7-8: Consider enabling incremental builds for dev ergonomics

incremental: false can slow local type-check cycles in a large monorepo. Consider flipping to true in a dev-only override or per-package tsconfig to speed up inner loop.

packages/utils/package.json (1)

14-16: Minification flag removed — confirm bundle size expectations

Previous build used "--minify". If size budgets matter, either restore "--minify" or enable minify in tsup.config.ts.

packages/hooks/package.json (1)

14-18: Avoid emitting on type-check; revert to noEmit.

“check:types”: “tsc” writes build artifacts during CI/local checks. Prefer noEmit for speed and clean trees; keep “tsc && tsup” in build for d.ts generation.

   "scripts": {
-    "build": "tsc && tsup",
+    "build": "tsc && tsup",
     "dev": "tsup --watch",
     "check:lint": "eslint . --max-warnings 6",
-    "check:types": "tsc",
+    "check:types": "tsc --noEmit",
     "check:format": "prettier --check \"**/*.{ts,tsx,md,json,css,scss}\"",
packages/shared-state/package.json (1)

10-16: Type-checking emits files but this package has no build; use noEmit or add build.

With “check:types”: “tsc”, outputs may be emitted (to dist or alongside sources) causing churn. Either switch to noEmit or add a proper build that writes to dist and points main/types there.

Option A (no build):

-    "check:types": "tsc",
+    "check:types": "tsc --noEmit",

Option B (add build; adjust entrypoints accordingly):

+    "build": "tsc",

Then ensure tsconfig outDir points to dist and update:

-  "main": "./src/index.ts",
-  "types": "./src/index.ts",
+  "main": "./dist/index.js",
+  "types": "./dist/index.d.ts",
packages/editor/src/core/hooks/use-file-upload.ts (1)

69-75: Prefer unknown over any + narrow the error shape.

Use unknown in catch and extract a message safely; avoids disabling type-safety across the block.

-      } catch (errPayload: any) {
-        const error = errPayload?.response?.data?.error || "Something went wrong";
-        console.error(error);
+      } catch (errPayload: unknown) {
+        const error = extractUploadError(errPayload);
+        console.error(error);
       } finally {

Add once in this module (outside this range):

function extractUploadError(e: unknown): string {
  if (typeof e === "object" && e !== null) {
    const maybe = e as { response?: { data?: { error?: unknown } } };
    const msg = maybe.response?.data?.error;
    if (typeof msg === "string") return msg;
  }
  return e instanceof Error ? e.message : "Something went wrong";
}
packages/propel/package.json (1)

8-8: Don’t emit during type-check for Storybook-only package.

This package doesn’t build a dist; switch to noEmit to avoid stray outputs.

-    "check:types": "tsc",
+    "check:types": "tsc --noEmit",
packages/ui/package.json (1)

21-21: "check:types" currently emits files; consider noEmit to avoid redundant builds.

If CI uses this for type-check only, swap to --noEmit to speed up and prevent dist churn.

-    "check:types": "tsc",
+    "check:types": "tsc --noEmit",
packages/editor/src/core/extensions/emoji/suggestion.ts (1)

30-35: Prefer a type guard over .filter(Boolean) to drop the final type assertion.

This tightens types and avoids as EmojiItem[].

-      const defaultEmojis = DEFAULT_EMOJIS.map((name) =>
-        filteredEmojis.find((emoji) => emoji.shortcodes.includes(name) || emoji.name === name)
-      )
-        .filter(Boolean)
-        .slice(0, 5);
-      return defaultEmojis as EmojiItem[];
+      const defaultEmojis = DEFAULT_EMOJIS.map((name) =>
+        filteredEmojis.find((emoji) => emoji.shortcodes.includes(name) || emoji.name === name)
+      )
+        .filter((e): e is EmojiItem => Boolean(e))
+        .slice(0, 5);
+      return defaultEmojis;
packages/editor/package.json (2)

25-27: Guarantee both CJS and ESM outputs for the declared exports.

Exports declare both ./dist/lib.js (require) and ./dist/lib.mjs (import). Ensure tsup actually emits both formats.

Quick script tweak:

-    "build": "tsc && tsup",
+    "build": "tsc && tsup --format esm,cjs",

Or add a tsup.config.ts mirroring i18n (with dts: false) to standardize formats and avoid duplication.

To verify after a build, confirm these paths exist:

  • dist/index.mjs
  • dist/lib.js
  • dist/lib.mjs

28-28: Optional: make type-checks non-emitting to speed CI.

Aligns with typical “check” semantics and prevents accidental dist changes during checks.

-    "check:types": "tsc",
+    "check:types": "tsc --noEmit",
packages/i18n/tsup.config.ts (1)

7-13: Avoid duplicate .d.ts generation with "tsc && tsup".

Since packages run tsc before tsup, generating declarations in tsup is redundant and can cause conflicts.

-  dts: true,
+  dts: false,
+  sourcemap: true,

If you prefer tsup to own types instead, drop the tsc step from the package’s build script and keep dts: true.

packages/i18n/package.json (1)

7-12: Dist entry points look good; add exports map for Node resolution and future-proofing.

Expose explicit subpath exports to avoid deep imports and enable conditional exports.

   "main": "./dist/index.js",
   "module": "./dist/index.mjs",
   "types": "./dist/index.d.ts",
+  "exports": {
+    ".": {
+      "types": "./dist/index.d.ts",
+      "import": "./dist/index.mjs",
+      "require": "./dist/index.js"
+    }
+  },
   "files": [
     "dist/**"
   ],
packages/services/package.json (3)

22-26: Consider externalizing axios to avoid bundling it into every consumer.

For libraries, keeping runtime deps external reduces duplicate copies across apps. If you adopt tsup.config.ts, set external: ["axios"].


6-11: Add exports map for correctness on Node’s ESM resolver and tooling.

   "main": "./dist/index.js",
   "module": "./dist/index.mjs",
   "types": "./dist/index.d.ts",
+  "exports": {
+    ".": {
+      "types": "./dist/index.d.ts",
+      "import": "./dist/index.mjs",
+      "require": "./dist/index.js"
+    }
+  },
   "files": [
     "dist/**"
   ],

13-17: Rely on tsup for bundling and leverage the existing noEmit in your tsconfig

The react-library.json base config already sets "noEmit": true, so running tsc won’t produce any JS artifacts by default. To keep things DRY and prevent duplicate builds:

• In your packages/services/package.json scripts:

  • Remove the standalone tsc from the build command and let tsup handle both bundling and emission.
  • Keep type‐checking in its own step. Since noEmit is inherited, tsc won’t write files—but you can add --noEmit if you want to make that explicit.

Suggested diff (optional refactor):

   "scripts": {
-    "build": "tsc && tsup",
+    "build": "tsup",
     "dev": "tsup --watch",
     "check:lint": "eslint . --max-warnings 62",
-    "check:types": "tsc",
+    "check:types": "tsc --noEmit",  // optional: explicit noEmit flag, though it's already in your tsconfig
     "check:format": "prettier --check \"**/*.{ts,tsx,md,json,css,scss}\"",
   }

This keeps bundling and type‐checking clearly separated and avoids running tsc twice.

packages/services/tsconfig.json (1)

2-4: Extends react-library for a non-React package; consider a leaner base.

If services doesn’t use JSX, extend a non-React base to avoid unnecessary DOM typings and mismatched assumptions.

-  "extends": "@plane/typescript-config/react-library.json",
+  "extends": "@plane/typescript-config/base.json",
   "include": ["./src"]
packages/utils/tsconfig.json (1)

2-4: Same as services: prefer a non-React base if no JSX is used.

Keeps the config minimal and prevents accidental DOM/JSX leakage into utils.

-  "extends": "@plane/typescript-config/react-library.json",
+  "extends": "@plane/typescript-config/base.json",
   "include": ["./src"]
packages/editor/tsconfig.json (1)

6-6: Reconsider disabling strict mode for a library.

Library outputs benefit from strict for better DX and earlier bug surfacing. If turning it off is intentional, add a short rationale in the README or tsconfig comment, or scope relaxations via targeted ts-ignore/tsconfig overrides instead.

-    "strict": false,
+    "strict": true,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4a3c172 and b71f8bf.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (23)
  • packages/editor/package.json (2 hunks)
  • packages/editor/src/core/extensions/code/code-block.ts (0 hunks)
  • packages/editor/src/core/extensions/emoji/suggestion.ts (1 hunks)
  • packages/editor/src/core/hooks/use-file-upload.ts (1 hunks)
  • packages/editor/src/core/plugins/file/restore.ts (1 hunks)
  • packages/editor/tsconfig.json (1 hunks)
  • packages/hooks/package.json (1 hunks)
  • packages/hooks/tsconfig.json (1 hunks)
  • packages/i18n/package.json (2 hunks)
  • packages/i18n/tsconfig.json (1 hunks)
  • packages/i18n/tsup.config.ts (1 hunks)
  • packages/propel/package.json (1 hunks)
  • packages/propel/tsconfig.json (1 hunks)
  • packages/services/package.json (1 hunks)
  • packages/services/tsconfig.json (1 hunks)
  • packages/shared-state/package.json (1 hunks)
  • packages/shared-state/tsconfig.json (1 hunks)
  • packages/typescript-config/base.json (1 hunks)
  • packages/typescript-config/react-library.json (1 hunks)
  • packages/ui/package.json (1 hunks)
  • packages/ui/tsconfig.json (1 hunks)
  • packages/utils/package.json (1 hunks)
  • packages/utils/tsconfig.json (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/editor/src/core/extensions/code/code-block.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (8)
packages/hooks/tsconfig.json (1)

1-4: No changes required: JSX mode correctly inherited

The shared config at packages/typescript-config/react-library.json already defines

"compilerOptions": {
  "jsx": "react-jsx"
}

so removing per-file JSX settings is safe.

packages/propel/tsconfig.json (1)

3-3: LGTM — minimal tsconfig aligns with shared config

include is scoped to src and the rest is inherited from react-library.json. No issues spotted.

packages/utils/package.json (1)

37-45: Toolchain Versions Are Consistent—No Action Required

All packages in packages/**/package.json declare TypeScript version 5.8.3, and every package that depends on tsup uses version 8.4.0. No mismatches were detected.

packages/editor/src/core/extensions/emoji/suggestion.ts (1)

30-30: LGTM: inferred callback type is sufficient.

Removing the explicit EmojiItem annotation here is safe and keeps the code cleaner.

packages/i18n/package.json (1)

14-18: No duplicate JS artifacts – base config enforces noEmit=true
The packages/i18n/tsconfig.json extends @plane/typescript-config/react-library.json, which already sets "noEmit": true. As a result, running tsc in both your "build" and "check:types" scripts performs type-checking only and does not emit any JS files, so there’s no risk of clobbering or duplicate output.

If you want to make the no-emit behavior more explicit, you can optionally update the "check:types" script to:

"check:types": "tsc --noEmit"

—but this change is purely stylistic and not strictly necessary.

Likely an incorrect or invalid review comment.

packages/editor/tsconfig.json (2)

15-15: Include looks fine.

Matches typical library layouts; inherits exclude from the base config.


2-2: Verify TS config resolution post-install

Centralizing on the shared react-library.json config is a great move—this reduces per-package drift and duplication. I’ve confirmed:

• The packages/typescript-config package includes react-library.json in its files array.
packages/editor/package.json declares "@plane/typescript-config": "workspace:*" as a dependency.

After running a fresh install, TypeScript should be able to resolve
"extends": "@plane/typescript-config/react-library.json" from within packages/editor. Please verify locally, for example:

cd packages/editor
npm install
npx tsc --showConfig
packages/typescript-config/react-library.json (1)

10-10: Exclude is appropriate for shared config.

Keeps transient outputs and deps out of the program graph across all libraries.

Comment on lines 9 to +13
"paths": {
"@/*": ["./src/core/*"],
"@/styles/*": ["./src/styles/*"],
"@/plane-editor/*": ["./src/ce/*"]
},
"strictNullChecks": true,
"allowSyntheticDefaultImports": true
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify alias mappings match your bundler/tsup config.

"@/" → "./src/core/" and "@/plane-editor/" → "./src/ce/" are nonstandard for "@/"; ensure build/test tooling (tsup, jest, eslint/import) resolve these the same way to avoid runtime/IDE mismatches.


🏁 Script executed:

#!/bin/bash
# 1) Find all "@/..." imports in the editor source
rg -nP --glob 'packages/editor/src/**' -e 'from\s+[\"'\'']@/|require\([\"'\'']@/' -C1

# 2) Check tsup config (or bundler config) for matching alias rules
fd -a -g 'tsup*.{ts,js}' packages/editor | xargs -r rg -n 'alias|resolve|tsconfig|paths'

Length of output: 56861


Ensure your tsconfig path aliases are honored by your bundler, tests, and IDE

It looks like packages/editor/tsconfig.json defines these custom aliases:

"paths": {
  "@/*": ["./src/core/*"],
  "@/styles/*": ["./src/styles/*"],
  "@/plane-editor/*": ["./src/ce/*"]
}

but no matching alias or plugin entries were found in your tsup configuration. Without explicit support, imports like
import { Foo } from "@/components/foo"
will fail at build/test time or be unresolved in the IDE.

To fix this, update your toolchain:

  • Tsup configuration
    Create or update packages/editor/tsup.config.ts (or .js) to include a tsconfig‐paths plugin, for example:

    import { defineConfig } from 'tsup';
    import tsconfigPaths from 'esbuild-plugin-tsconfig-paths';
    
    export default defineConfig({
      entry: ['src/index.ts'],
      esbuildPlugins: [tsconfigPaths()],
      // …other tsup options
    });
  • Jest mapping
    In your Jest config (jest.config.js/.ts), add moduleNameMapper entries:

    moduleNameMapper: {
      '^@/(.*)$': '<rootDir>/src/core/$1',
      '^@/styles/(.*)$': '<rootDir>/src/styles/$1',
      '^@/plane-editor/(.*)$': '<rootDir>/src/ce/$1',
    },
  • ESLint resolver
    In .eslintrc.js, enable TypeScript path resolution:

    settings: {
      'import/resolver': {
        typescript: { project: 'packages/editor/tsconfig.json' }
      }
    }

Verifying and aligning these configurations will prevent runtime errors, test failures, and IDE import warnings.

🤖 Prompt for AI Agents
In packages/editor/tsconfig.json lines 9-13 the project defines path aliases but
your build/test/IDE configs don’t honor them; add a tsup config
(packages/editor/tsup.config.ts) that registers an esbuild tsconfig-paths plugin
so esbuild resolves the "@/..." imports at bundle time, add Jest
moduleNameMapper entries in the package’s jest.config.js/ts to map '^@/(.*)$' ->
'<rootDir>/src/core/$1', '^@/styles/(.*)$' -> '<rootDir>/src/styles/$1',
'^@/plane-editor/(.*)$' -> '<rootDir>/src/ce/$1', and update .eslintrc.js
settings to enable import/resolver typescript pointing to
packages/editor/tsconfig.json so the IDE and linter can resolve the aliases.

Comment on lines 1 to 4
{
"extends": "@plane/typescript-config/react-library.json",
"compilerOptions": {
"jsx": "react",
"lib": ["esnext", "dom"]
},
"include": ["."],
"exclude": ["dist", "build", "node_modules"]
"include": ["."]
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify base config sets JSX and libs appropriately after removal here.

Ensure @plane/typescript-config/react-library.json defines jsx (react-jsx or preserve), lib (dom, esnext), and strictness flags.


🏁 Script executed:

#!/bin/bash
echo "react-library.json:"
cat packages/typescript-config/react-library.json 2>/dev/null || true

Length of output: 367


🏁 Script executed:

#!/bin/bash
echo "packages/typescript-config/base.json compilerOptions:"
sed -n '1,200p' packages/typescript-config/base.json || true

Length of output: 661


JSX Configuration, Library Targets, and Strictness Flags Verified

I’ve confirmed that packages/ui/tsconfig.json extends react-library.json, which in turn extends base.json, and the combined settings are:

  • JSX
    ​ • Set to "react-jsx" in react-library.json.
  • Libraries
    ​ • Inherited from base.json: ["es2022", "DOM", "DOM.Iterable"].
  • Strictness
    ​ • strict: true and strictNullChecks: true in base.json.

Since the review requested lib to include esnext, please update packages/typescript-config/base.json accordingly:

• File: packages/typescript-config/base.json
• Location: inside compilerOptions.lib

   "compilerOptions": {
-    "lib": ["es2022", "DOM", "DOM.Iterable"],
+    "lib": ["ESNext", "DOM", "DOM.Iterable"],
     ...
   }

With that change, you’ll have:

  • JSX → react-jsx
  • Libs → ESNext, DOM, DOM.Iterable
  • Full strictness → strict: true, strictNullChecks: true
📝 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.

Suggested change
{
"extends": "@plane/typescript-config/react-library.json",
"compilerOptions": {
"jsx": "react",
"lib": ["esnext", "dom"]
},
"include": ["."],
"exclude": ["dist", "build", "node_modules"]
"include": ["."]
}
// File: packages/typescript-config/base.json
{
"compilerOptions": {
"lib": ["ESNext", "DOM", "DOM.Iterable"],
}
}
🤖 Prompt for AI Agents
In packages/typescript-config/base.json (modify the compilerOptions.lib
section), the lib array currently lacks "esnext"; update compilerOptions.lib to
include "esnext" (case-insensitive ESNext) alongside "DOM" and "DOM.Iterable" so
downstream configs (like packages/ui extending react-library.json) receive
ESNext libs while preserving existing entries; ensure the JSON remains valid and
run a quick tsc or lint to verify no breakage.

@lifeiscontent
Copy link
Collaborator

@aaryan610 just a thought, but since you're already upgrading tsup, might be worth taking a look at tsdown it would potentially improve our build times substantially.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
packages/editor/src/core/extensions/slash-commands/root.tsx (1)

58-90: Guard against missing clientRect and simplify popup typing.
Without a guard, tippy may receive undefined getReferenceClientRect and break; use ReturnType instead of the (Instance[] & Instance) hack.

-const renderItems: SuggestionOptions["render"] = () => {
+const renderItems: SuggestionOptions["render"] = () => {
   let component: ReactRenderer<CommandListInstance, SlashCommandsMenuProps> | null = null;
-  let popup: (Instance[] & Instance) | null = null;
+  let popup: ReturnType<typeof tippy> | null = null;
   return {
-    onStart: (props) => {
+    onStart: (props) => {
+      if (!props.clientRect) return;
       component = new ReactRenderer<CommandListInstance, SlashCommandsMenuProps>(SlashCommandsMenu, {
         props,
         editor: props.editor,
       });
packages/editor/src/core/extensions/callout/utils.ts (3)

27-31: Use EAttributeNames for fallback keys.

Align fallback with the enum-based schema.

Apply:

-  const fallBackValues: TStoredLogoValue = {
-    "data-logo-in-use": "emoji",
-    "data-emoji-unicode": DEFAULT_CALLOUT_BLOCK_ATTRIBUTES["data-emoji-unicode"],
-    "data-emoji-url": DEFAULT_CALLOUT_BLOCK_ATTRIBUTES["data-emoji-url"],
-  };
+  const fallBackValues: TStoredLogoValue = {
+    [EAttributeNames.LOGO_IN_USE]: "emoji",
+    [EAttributeNames.EMOJI_UNICODE]:
+      DEFAULT_CALLOUT_BLOCK_ATTRIBUTES[EAttributeNames.EMOJI_UNICODE],
+    [EAttributeNames.EMOJI_URL]:
+      DEFAULT_CALLOUT_BLOCK_ATTRIBUTES[EAttributeNames.EMOJI_URL],
+  };

46-49: Return emoji branch with enum keys.

Keep access/indexing consistent with the enum.

Apply:

-        return {
-          "data-logo-in-use": "emoji",
-          "data-emoji-unicode": parsedData.emoji.value || DEFAULT_CALLOUT_BLOCK_ATTRIBUTES["data-emoji-unicode"],
-          "data-emoji-url": parsedData.emoji.url || DEFAULT_CALLOUT_BLOCK_ATTRIBUTES["data-emoji-url"],
-        };
+        return {
+          [EAttributeNames.LOGO_IN_USE]: "emoji",
+          [EAttributeNames.EMOJI_UNICODE]:
+            parsedData.emoji.value ||
+            DEFAULT_CALLOUT_BLOCK_ATTRIBUTES[EAttributeNames.EMOJI_UNICODE],
+          [EAttributeNames.EMOJI_URL]:
+            parsedData.emoji.url ||
+            DEFAULT_CALLOUT_BLOCK_ATTRIBUTES[EAttributeNames.EMOJI_URL],
+        };

53-56: Return icon branch with enum keys.

Same consistency fix for icon attributes.

Apply:

-        return {
-          "data-logo-in-use": "icon",
-          "data-icon-name": parsedData.icon.name || DEFAULT_CALLOUT_BLOCK_ATTRIBUTES["data-icon-name"],
-          "data-icon-color": parsedData.icon.color || DEFAULT_CALLOUT_BLOCK_ATTRIBUTES["data-icon-color"],
-        };
+        return {
+          [EAttributeNames.LOGO_IN_USE]: "icon",
+          [EAttributeNames.ICON_NAME]:
+            parsedData.icon.name ||
+            DEFAULT_CALLOUT_BLOCK_ATTRIBUTES[EAttributeNames.ICON_NAME],
+          [EAttributeNames.ICON_COLOR]:
+            parsedData.icon.color ||
+            DEFAULT_CALLOUT_BLOCK_ATTRIBUTES[EAttributeNames.ICON_COLOR],
+        };
🧹 Nitpick comments (8)
packages/editor/src/core/extensions/emoji/extension.ts (2)

15-15: Nit: destructure only what you need to tighten the closure.

Slightly clearer and avoids closing over the entire options object.

-    const extensionOptions = this.options;
+    const { emojis } = this.options;
...
-          const emojiItem = shortcodeToEmoji(node.attrs.name, extensionOptions.emojis);
+          const emojiItem = shortcodeToEmoji(node.attrs.name, emojis);

19-19: Guard against missing shortcodes[0] in alt text.

If shortcodes is empty/undefined, the alt text currently renders undefined. Fallback to the emoji name.

-            state.write(`\n![${emojiItem.name}-${emojiItem.shortcodes[0]}](${emojiItem?.fallbackImage})\n`);
+            const shortcode = emojiItem.shortcodes?.[0] ?? emojiItem.name;
+            state.write(`\n![${emojiItem.name}-${shortcode}](${emojiItem.fallbackImage})\n`);
packages/i18n/tsconfig.json (1)

4-4: Confirm resolveJsonModule necessity

Keep resolveJsonModule: true only if this package imports JSON (likely for locale catalogs). If not, drop it to minimize config surface.

packages/editor/src/core/components/menus/menu-items.ts (1)

250-278: Avoid end-of-array type assertion; prefer satisfies for safer typing.
Removes a potentially unsound cast while preserving type guarantees.

Apply:

 export const getEditorMenuItems = (editor: Editor | null): EditorMenuItem<TEditorCommands>[] => {
   if (!editor) return [];
-
-  return [
+  const items = [
     TextItem(editor),
     HeadingOneItem(editor),
     HeadingTwoItem(editor),
     HeadingThreeItem(editor),
     HeadingFourItem(editor),
     HeadingFiveItem(editor),
     HeadingSixItem(editor),
     BoldItem(editor),
     ItalicItem(editor),
     UnderLineItem(editor),
     StrikeThroughItem(editor),
     BulletListItem(editor),
     TodoListItem(editor),
     CodeItem(editor),
     NumberedListItem(editor),
     QuoteItem(editor),
     TableItem(editor),
     ImageItem(editor),
     HorizontalRuleItem(editor),
     LinkItem(editor),
     TextColorItem(editor),
     BackgroundColorItem(editor),
     TextAlignItem(editor),
-  ] as EditorMenuItem<TEditorCommands>[];
+  ] satisfies EditorMenuItem<TEditorCommands>[];
+  return items;
 };

If the repo is on TypeScript < 4.9, keep the current assertion or upgrade TS.

packages/editor/src/core/components/menus/bubble-menu/node-selector.tsx (1)

34-47: Redundant cast on items; it’s already typed.
Drop the trailing assertion to reduce noise.

   const items: EditorMenuItem<TEditorCommands>[] = [
     TextItem(editor),
     HeadingOneItem(editor),
     HeadingTwoItem(editor),
     HeadingThreeItem(editor),
     HeadingFourItem(editor),
     HeadingFiveItem(editor),
     HeadingSixItem(editor),
     BulletListItem(editor),
     NumberedListItem(editor),
     TodoListItem(editor),
     QuoteItem(editor),
     CodeItem(editor),
-  ] as EditorMenuItem<TEditorCommands>[];
+  ];
packages/editor/src/core/extensions/mentions/utils.ts (1)

17-19: Tighten popup typing with ReturnType.
Removes the need for the array+instance intersection.

   () => {
     const { searchCallback } = props;
     let component: ReactRenderer<CommandListInstance, MentionsListDropdownProps> | null = null;
-    let popup: (Instance[] & Instance) | null = null;
+    let popup: ReturnType<typeof tippy> | null = null;

Optionally, also convert the import to a type-only or drop it if unused after this change.

packages/editor/src/core/extensions/callout/utils.ts (2)

34-39: Don’t sanitize before JSON.parse; sanitize at sink.

HTML-sanitizing the JSON string can corrupt it and cause unnecessary parse failures. Parse raw, then sanitize only when inserting into the DOM.

Apply:

-    const storedData = sanitizeHTML(localStorage.getItem("editor-calloutComponent-logo") ?? "");
+    const storedData = localStorage.getItem("editor-calloutComponent-logo");
     if (storedData) {
       let parsedData: TEmojiLogoProps;
       try {
         parsedData = JSON.parse(storedData);

69-74: Return null when the background key is absent to match the signature.

Current code returns "" instead of null when the item is missing.

Apply:

-export const getStoredBackgroundColor = (): string | null => {
-  if (typeof window !== "undefined") {
-    return sanitizeHTML(localStorage.getItem("editor-calloutComponent-background") ?? "");
-  }
-  return null;
-};
+export const getStoredBackgroundColor = (): string | null => {
+  if (typeof window === "undefined") return null;
+  const raw = localStorage.getItem("editor-calloutComponent-background");
+  return raw === null ? null : sanitizeHTML(raw);
+};
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b71f8bf and 419339e.

📒 Files selected for processing (15)
  • packages/editor/src/core/components/menus/bubble-menu/node-selector.tsx (1 hunks)
  • packages/editor/src/core/components/menus/menu-items.ts (4 hunks)
  • packages/editor/src/core/extensions/callout/utils.ts (1 hunks)
  • packages/editor/src/core/extensions/emoji/extension.ts (1 hunks)
  • packages/editor/src/core/extensions/mentions/utils.ts (3 hunks)
  • packages/editor/src/core/extensions/slash-commands/root.tsx (3 hunks)
  • packages/editor/tsconfig.json (1 hunks)
  • packages/hooks/tsconfig.json (1 hunks)
  • packages/i18n/tsconfig.json (1 hunks)
  • packages/propel/tsconfig.json (1 hunks)
  • packages/services/tsconfig.json (1 hunks)
  • packages/shared-state/tsconfig.json (1 hunks)
  • packages/typescript-config/react-library.json (1 hunks)
  • packages/ui/tsconfig.json (1 hunks)
  • packages/utils/tsconfig.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • packages/utils/tsconfig.json
  • packages/ui/tsconfig.json
  • packages/editor/tsconfig.json
  • packages/hooks/tsconfig.json
  • packages/propel/tsconfig.json
  • packages/typescript-config/react-library.json
  • packages/shared-state/tsconfig.json
  • packages/services/tsconfig.json
🧰 Additional context used
🧬 Code graph analysis (4)
packages/editor/src/core/components/menus/bubble-menu/node-selector.tsx (2)
packages/editor/src/core/components/menus/menu-items.ts (1)
  • EditorMenuItem (57-63)
packages/editor/src/core/types/editor.ts (1)
  • TEditorCommands (25-53)
packages/editor/src/core/extensions/slash-commands/root.tsx (2)
packages/editor/src/core/helpers/tippy.ts (1)
  • CommandListInstance (1-3)
packages/editor/src/core/extensions/slash-commands/command-menu.tsx (1)
  • SlashCommandsMenuProps (10-14)
packages/editor/src/core/extensions/mentions/utils.ts (3)
packages/editor/src/core/types/mention.ts (1)
  • TMentionHandler (21-25)
packages/editor/src/core/helpers/tippy.ts (1)
  • CommandListInstance (1-3)
packages/editor/src/core/extensions/mentions/mentions-list-dropdown.tsx (1)
  • MentionsListDropdownProps (13-17)
packages/editor/src/core/components/menus/menu-items.ts (1)
packages/editor/src/core/types/editor.ts (1)
  • TEditorCommands (25-53)
🔇 Additional comments (8)
packages/editor/src/core/extensions/emoji/extension.ts (1)

10-10: Good fix: capture options to avoid incorrect this in serializer.

Capturing this.options outside the serialize method prevents accidental this rebinding issues. This makes the serializer robust.

packages/i18n/tsconfig.json (2)

7-7: No ambient .d.ts files found outside src—"include": ["./src"] is safe as is.


4-5: Verify .d.ts emission for @plane/i18n build
noEmit: true disables tsc output—ensure tsup’s dts option (or a dedicated tsconfig.build.json) actually generates .d.ts files under packages/i18n/dist. Run:

pnpm -F @plane/i18n build  
find packages/i18n/dist -type f -name "*.d.ts"

and confirm at least one declaration file is present.

packages/editor/src/core/components/menus/menu-items.ts (3)

1-1: Good switch to type-only import for Editor.
Reduces bundle risk and keeps runtime clean.


21-21: Importing LucideIcon as a type is correct.
Prevents unnecessary runtime import from lucide-react.


73-73: Nice: heading levels gated via Extract.
Keeps typings resilient if TEditorCommands evolves.

packages/editor/src/core/extensions/mentions/utils.ts (2)

2-3: Type-only imports look good.
Removes value imports and avoids unnecessary runtime bits.


46-54: Good: stable rect on update.
Computing clientRect and supplying a function eliminates flicker and matches tippy expectations.

@aaryan610
Copy link
Member Author

@aaryan610 just a thought, but since you're already upgrading tsup, might be worth taking a look at tsdown it would potentially improve our build times substantially.

We are already working on switching to tsdown on a separate branch.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/editor/src/core/extensions/callout/block.tsx (1)

26-26: Unify attribute key access via enum and null→undefined mapping across callout extension
Replace every hard-coded "data-*" key in packages/editor/src/core/extensions/callout/* with the matching ECalloutAttributeNames enum and, in all onSelect handlers, type val as string | null mapping null → undefined for updateAttributes (while still writing null to storage).
Affected files:

  • block.tsx (line 26 & onSelect)
  • logo-selector.tsx (all blockAttributes["data-…"] and newLogoValue keys)
  • extension-config.ts (attrs["data-…"] in rendering)
  • extension.tsx ("data-background" in stored values)
packages/editor/src/core/extensions/callout/extension-config.ts (1)

42-51: Replace serializer magic strings with enum keys to prevent drift

Use ECalloutAttributeNames for all attribute reads.

-          const attrs = node.attrs as TCalloutBlockAttributes;
-          const logoInUse = attrs["data-logo-in-use"];
+          const attrs = node.attrs as TCalloutBlockAttributes;
+          const logoInUse = attrs[ECalloutAttributeNames.LOGO_IN_USE];
           // add callout logo
           if (logoInUse === "emoji") {
             state.write(
-              `> <img src="${attrs["data-emoji-url"]}" alt="${attrs["data-emoji-unicode"]}" width="30px" />\n`
+              `> <img src="${attrs[ECalloutAttributeNames.EMOJI_URL]}" alt="${attrs[ECalloutAttributeNames.EMOJI_UNICODE]}" width="30px" />\n`
             );
           } else {
-            state.write(`> <icon>${attrs["data-icon-name"]} icon</icon>\n`);
+            state.write(`> <icon>${attrs[ECalloutAttributeNames.ICON_NAME]} icon</icon>\n`);
           }
packages/editor/src/core/components/menus/bubble-menu/root.tsx (1)

134-160: Prevent potential event-listener leaks on unmount

mousemove and mouseup listeners added inside handleMouseDown aren’t removed if the component unmounts before mouseup. Use an AbortController stored in a ref to guarantee cleanup.

   const [isSelecting, setIsSelecting] = useState(false);
   // refs
-  const menuRef = useRef<HTMLDivElement>(null);
+  const menuRef = useRef<HTMLDivElement>(null);
+  const dragControllerRef = useRef<AbortController | null>(null);
@@
   useEffect(() => {
     function handleMouseDown(e: MouseEvent) {
       if (menuRef.current?.contains(e.target as Node)) return;
 
-      function handleMouseMove() {
-        if (!editor?.state.selection.empty) {
-          setIsSelecting(true);
-          document.removeEventListener("mousemove", handleMouseMove);
-        }
-      }
-
-      function handleMouseUp() {
-        setIsSelecting(false);
-        document.removeEventListener("mousemove", handleMouseMove);
-        document.removeEventListener("mouseup", handleMouseUp);
-      }
-
-      document.addEventListener("mousemove", handleMouseMove);
-      document.addEventListener("mouseup", handleMouseUp);
+      // abort any previous sequence
+      dragControllerRef.current?.abort();
+      const controller = new AbortController();
+      dragControllerRef.current = controller;
+      const { signal } = controller;
+
+      const handleMouseMove = () => {
+        if (!editor?.state.selection.empty) {
+          setIsSelecting(true);
+          controller.abort(); // removes both listeners via signal
+        }
+      };
+
+      const handleMouseUp = () => {
+        setIsSelecting(false);
+        controller.abort();
+      };
+
+      document.addEventListener("mousemove", handleMouseMove, { signal });
+      document.addEventListener("mouseup", handleMouseUp, { signal });
     }
 
     document.addEventListener("mousedown", handleMouseDown);
 
     return () => {
       document.removeEventListener("mousedown", handleMouseDown);
+      dragControllerRef.current?.abort();
     };
   }, [editor]);

Also applies to: 61-63

♻️ Duplicate comments (1)
packages/editor/src/core/extensions/callout/utils.ts (1)

12-20: Migration to enum-based computed keys completed—nice

Defaults, fallback, and return paths now consistently use ECalloutAttributeNames. This removes a whole class of silent breakages on key renames.

Also applies to: 28-31, 46-60

🧹 Nitpick comments (5)
packages/editor/src/core/extensions/callout/extension-config.ts (1)

28-34: Type the reducer to avoid implicit any and future key typos

Add a reducer generic so the accumulator isn’t any.

-      ...Object.values(ECalloutAttributeNames).reduce((acc, value) => {
+      ...Object.values(ECalloutAttributeNames).reduce<Record<string, { default: unknown }>>((acc, value) => {
         acc[value] = {
           default: DEFAULT_CALLOUT_BLOCK_ATTRIBUTES[value],
         };
         return acc;
-      }, {}),
+      }, {}),
packages/editor/src/core/extensions/callout/utils.ts (2)

73-78: Return null when no stored background (avoid empty-string sentinel)

Current code returns "" which blurs “unset” vs “empty”. Prefer null when absent.

 export const getStoredBackgroundColor = (): string | null => {
   if (typeof window !== "undefined") {
-    return sanitizeHTML(localStorage.getItem("editor-calloutComponent-background") ?? "");
+    const raw = localStorage.getItem("editor-calloutComponent-background");
+    return raw ? sanitizeHTML(raw) : null;
   }
   return null;
 };

69-71: Nit: centralize localStorage keys

Consider constants for "editor-calloutComponent-logo" and "editor-calloutComponent-background" to prevent typos.

Also applies to: 80-88

packages/editor/src/core/components/menus/bubble-menu/root.tsx (2)

64-71: Memoize formattingItems to avoid per-render reallocation

formattingItems recreates command objects on every render. It’s small but hot code; memoize by editor.

-import { FC, useEffect, useState, useRef } from "react";
+import { FC, useEffect, useState, useRef, useMemo } from "react";
@@
-  const formattingItems = {
-    code: CodeItem(editor),
-    bold: BoldItem(editor),
-    italic: ItalicItem(editor),
-    underline: UnderLineItem(editor),
-    strikethrough: StrikeThroughItem(editor),
-    "text-align": TextAlignItem(editor),
-  } satisfies {
+  const formattingItems = useMemo(() => ({
+    code: CodeItem(editor),
+    bold: BoldItem(editor),
+    italic: ItalicItem(editor),
+    underline: UnderLineItem(editor),
+    strikethrough: StrikeThroughItem(editor),
+    "text-align": TextAlignItem(editor),
+  }), [editor]) satisfies {
     [K in TEditorCommands]?: EditorMenuItem<K>;
   };

Also applies to: 2-2


75-89: Avoid shadowing editor inside selector for readability

The selector receives { editor } but you also close over the outer editor via formattingItems. Rename the param to prevent confusion.

-  const editorState: EditorStateType = useEditorState({
-    editor,
-    selector: ({ editor }: { editor: Editor }) => ({
+  const editorState: EditorStateType = useEditorState({
+    editor,
+    selector: ({ editor: ed }: { editor: Editor }) => ({
       code: formattingItems.code.isActive(),
       bold: formattingItems.bold.isActive(),
       italic: formattingItems.italic.isActive(),
       underline: formattingItems.underline.isActive(),
       strikethrough: formattingItems.strikethrough.isActive(),
       left: formattingItems["text-align"].isActive({ alignment: "left" }),
       right: formattingItems["text-align"].isActive({ alignment: "right" }),
       center: formattingItems["text-align"].isActive({ alignment: "center" }),
-      color: COLORS_LIST.find((c) => TextColorItem(editor).isActive({ color: c.key })),
-      backgroundColor: COLORS_LIST.find((c) => BackgroundColorItem(editor).isActive({ color: c.key })),
+      color: COLORS_LIST.find((c) => TextColorItem(ed).isActive({ color: c.key })),
+      backgroundColor: COLORS_LIST.find((c) => BackgroundColorItem(ed).isActive({ color: c.key })),
     }),
   });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2e52e6e and e8f21a8.

📒 Files selected for processing (5)
  • packages/editor/src/core/components/menus/bubble-menu/root.tsx (10 hunks)
  • packages/editor/src/core/extensions/callout/block.tsx (2 hunks)
  • packages/editor/src/core/extensions/callout/extension-config.ts (3 hunks)
  • packages/editor/src/core/extensions/callout/types.ts (2 hunks)
  • packages/editor/src/core/extensions/callout/utils.ts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
packages/editor/src/core/components/menus/bubble-menu/root.tsx (3)
packages/editor/src/core/components/menus/menu-items.ts (7)
  • CodeItem (171-177)
  • BoldItem (107-113)
  • ItalicItem (115-121)
  • UnderLineItem (123-129)
  • StrikeThroughItem (131-137)
  • TextAlignItem (239-248)
  • EditorMenuItem (57-63)
packages/editor/src/core/types/editor.ts (1)
  • TEditorCommands (25-53)
packages/editor/src/core/components/menus/bubble-menu/alignment-selector.tsx (1)
  • TextAlignmentSelector (16-82)
packages/editor/src/core/extensions/callout/extension-config.ts (1)
packages/editor/src/core/extensions/callout/utils.ts (1)
  • DEFAULT_CALLOUT_BLOCK_ATTRIBUTES (12-20)
packages/editor/src/core/extensions/callout/utils.ts (1)
packages/editor/src/core/extensions/callout/types.ts (3)
  • TCalloutBlockAttributes (21-26)
  • TCalloutBlockEmojiAttributes (16-19)
  • TCalloutBlockIconAttributes (11-14)
🔇 Additional comments (5)
packages/editor/src/core/extensions/callout/block.tsx (1)

9-9: Enum rename alignment looks good

Import updated to ECalloutAttributeNames; matches downstream usage.

packages/editor/src/core/extensions/callout/extension-config.ts (1)

65-66: LGTM on parseHTML selector

Selector now uses the enum and default value—consistent with addAttributes.

packages/editor/src/core/extensions/callout/types.ts (2)

21-25: Confirm null handling strategy for BACKGROUND

BACKGROUND is typed as string | undefined. If the UI clears color with null (used by storage to delete), ensure call sites coerce null→undefined (see block.tsx suggestion) rather than widening this type.


1-9: Enum rename + computed-key types: good move

String-enum values keep the DOM attributes stable while strengthening typing at use sites.

packages/editor/src/core/components/menus/bubble-menu/root.tsx (1)

54-56: Destructuring props for editor is a nice cleanup

Keeps subsequent references concise and consistent.

@@ -1,4 +1,4 @@
import { BubbleMenu, BubbleMenuProps, Editor, isNodeSelection, useEditorState } from "@tiptap/react";
import { BubbleMenu, type BubbleMenuProps, type Editor, isNodeSelection, useEditorState } from "@tiptap/react";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect imports for Editor and isNodeSelection

Editor and isNodeSelection are not exported by @tiptap/react in most versions. Import them from @tiptap/core to avoid type/runtime issues.

-import { BubbleMenu, type BubbleMenuProps, type Editor, isNodeSelection, useEditorState } from "@tiptap/react";
+import { BubbleMenu, type BubbleMenuProps, useEditorState } from "@tiptap/react";
+import { type Editor, isNodeSelection } from "@tiptap/core";
📝 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.

Suggested change
import { BubbleMenu, type BubbleMenuProps, type Editor, isNodeSelection, useEditorState } from "@tiptap/react";
// packages/editor/src/core/components/menus/bubble-menu/root.tsx
import { BubbleMenu, type BubbleMenuProps, useEditorState } from "@tiptap/react";
import { type Editor, isNodeSelection } from "@tiptap/core";
🤖 Prompt for AI Agents
In packages/editor/src/core/components/menus/bubble-menu/root.tsx around line 1,
the current import pulls Editor and isNodeSelection from "@tiptap/react" which
is incorrect in many versions; update the imports so BubbleMenu, BubbleMenuProps
and useEditorState stay imported from "@tiptap/react" while Editor and
isNodeSelection are imported from "@tiptap/core" to ensure correct types and
runtime behavior.

Comment on lines +30 to +32
type EditorBubbleMenuProps = Omit<BubbleMenuProps, "children" | "editor"> & {
editor: Editor;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Align the required editor prop with the null-check

Prop type requires Editor, but you also guard for falsy editor at runtime. Choose one:

  • Make the prop nullable (Editor | null) and keep the guard, or
  • Keep Editor required and remove the guard.

Given useEditor returns Editor | null initially, prefer nullable prop.

-type EditorBubbleMenuProps = Omit<BubbleMenuProps, "children" | "editor"> & {
-  editor: Editor;
-};
+type EditorBubbleMenuProps = Omit<BubbleMenuProps, "children" | "editor"> & {
+  editor: Editor | null;
+};
@@
-  if (!editor) return null;
+  if (!editor) return null; // keep guard if prop can be null

If you keep Editor required, drop the guard instead.

Also applies to: 162-163

🤖 Prompt for AI Agents
In packages/editor/src/core/components/menus/bubble-menu/root.tsx around lines
30-32 (also apply to lines 162-163), the prop type declares editor as required
but the component guards against a falsy editor at runtime; change the prop type
to accept Editor | null and keep the runtime guard. Update the
EditorBubbleMenuProps declaration to use Editor | null, update any related prop
consumers/ts usages to pass nullable editors, and keep the existing
early-return/null-check logic in the component so TypeScript and runtime
behavior are consistent.

Comment on lines +119 to +121
if (editor) {
editor.storage.link.isBubbleMenuOpen = true;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Defensive guard for editor.storage.link

Mutating editor.storage.link assumes the Link extension is present. Add a guard to avoid crashes when Link is not loaded.

-      onShow: () => {
-        if (editor) {
-          editor.storage.link.isBubbleMenuOpen = true;
-        }
-      },
+      onShow: () => {
+        if (editor && editor.storage?.link) {
+          editor.storage.link.isBubbleMenuOpen = true;
+        }
+      },
@@
-      onHidden: () => {
-        if (editor) {
-          editor.storage.link.isBubbleMenuOpen = false;
-        }
+      onHidden: () => {
+        if (editor && editor.storage?.link) {
+          editor.storage.link.isBubbleMenuOpen = false;
+        }

Also applies to: 124-126

🤖 Prompt for AI Agents
In packages/editor/src/core/components/menus/bubble-menu/root.tsx around lines
119-121 (and similarly 124-126), the code unconditionally mutates
editor.storage.link which assumes the Link extension exists; add a defensive
guard that checks editor.storage and editor.storage.link (or uses a safe
accessor) before setting isBubbleMenuOpen to true/false so we avoid runtime
crashes when the Link extension is not loaded; if the storage or link object is
missing, skip the mutation (or initialize a minimal link storage object if
appropriate for your app).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants