Skip to content

Updated to latest version of ESlint ghost plugin#25713

Merged
ibalosh merged 2 commits intomainfrom
update-eslint-version
Dec 16, 2025
Merged

Updated to latest version of ESlint ghost plugin#25713
ibalosh merged 2 commits intomainfrom
update-eslint-version

Conversation

@ibalosh
Copy link
Contributor

@ibalosh ibalosh commented Dec 15, 2025

  • Updated to ESLint 3.4.4 ghost plugin which works with ESlint 9+ flat config
  • Updated e2e app and admin app typescript eslint plugin versions to resolve conflict with ghost plugin which uses newer version of typescript eslint
  • resolved errors when using new plugin

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 15, 2025

Warning

Rate limit exceeded

@ibalosh has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 7 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between b586a7f and 781b6ab.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (55)
  • apps/activitypub/src/api/activitypub.ts (1 hunks)
  • apps/activitypub/src/components/modals/new-note-modal.tsx (1 hunks)
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts (3 hunks)
  • apps/activitypub/src/utils/get-username.ts (1 hunks)
  • apps/activitypub/src/utils/image.ts (1 hunks)
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1 hunks)
  • apps/activitypub/src/views/preferences/components/profile.tsx (1 hunks)
  • apps/activitypub/src/views/profile/profile.tsx (1 hunks)
  • apps/admin-x-design-system/src/global/sortable-list.tsx (1 hunks)
  • apps/admin-x-design-system/src/typings.d.ts (1 hunks)
  • apps/admin-x-design-system/src/utils/format-url.ts (1 hunks)
  • apps/admin-x-framework/src/api/config.ts (1 hunks)
  • apps/admin-x-framework/src/api/recommendations.ts (1 hunks)
  • apps/admin-x-framework/src/test/acceptance.ts (1 hunks)
  • apps/admin-x-framework/src/test/test-utils.tsx (1 hunks)
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts (2 hunks)
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (2 hunks)
  • apps/admin-x-settings/src/typings.d.ts (1 hunks)
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts (2 hunks)
  • apps/admin-x-settings/src/utils/url.ts (1 hunks)
  • apps/admin/package.json (1 hunks)
  • apps/comments-ui/src/actions.ts (2 hunks)
  • apps/comments-ui/src/app-context.ts (1 hunks)
  • apps/comments-ui/src/components/content/forms/form.tsx (1 hunks)
  • apps/comments-ui/src/components/popup-box.tsx (1 hunks)
  • apps/comments-ui/src/typings.d.ts (1 hunks)
  • apps/comments-ui/src/utils/admin-api.ts (1 hunks)
  • apps/comments-ui/src/utils/api.ts (1 hunks)
  • apps/posts/src/hooks/use-post-success-modal.ts (1 hunks)
  • apps/posts/src/utils/link-helpers.ts (1 hunks)
  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Web/web.tsx (1 hunks)
  • apps/shade/src/components/features/color-picker/color-picker.tsx (1 hunks)
  • apps/shade/src/components/layout/header.tsx (7 hunks)
  • apps/shade/src/components/layout/heading.tsx (1 hunks)
  • apps/shade/src/components/layout/page.tsx (1 hunks)
  • apps/shade/src/components/layout/view-header.tsx (2 hunks)
  • apps/shade/src/components/ui/card.tsx (1 hunks)
  • apps/shade/src/lib/utils.ts (1 hunks)
  • apps/shade/src/typings.d.ts (1 hunks)
  • apps/signup-form/src/components/pages/form-page.tsx (1 hunks)
  • apps/signup-form/src/typings.d.ts (1 hunks)
  • apps/stats/src/hooks/use-feature-flag.tsx (1 hunks)
  • apps/stats/src/types/svg.d.ts (1 hunks)
  • apps/stats/src/utils/url-helpers.ts (5 hunks)
  • apps/stats/test/utils/tinybird-helpers.ts (1 hunks)
  • e2e/package.json (1 hunks)
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2 hunks)
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1 hunks)

Walkthrough

Removed a local patch in apps/admin/eslint.config.js and changed its parserOptions from projectService: true to project: ['./tsconfig.app.json', './tsconfig.node.json']. Bumped/added ESLint/TypeScript-eslint dependencies. Applied many small edits across the repo: replaced named catch bindings with bare catch {} in many modules, added ESLint suppression comments (notably inside *.svg typings and above empty-object-type interfaces), introduced an internal JSONArray type, added several Header-related interfaces and exported Header subcomponents in apps/shade, and made minor formatting/lint tweaks.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–30 minutes

  • Areas that may need extra attention:
    • apps/admin/eslint.config.js — verify ESLint rule behavior and TypeScript project resolution after removing the patch and changing parserOptions.
    • package.json / workspace resolutions / devDependencies — confirm the @typescript-eslint and eslint-plugin version changes do not break CI linting or editor integrations.
    • Bulk bare catch {} changes — ensure no removed error bindings were relied upon for logging, telemetry, or conditional logic.
    • apps/shade/src/components/layout/header.tsx — review new public interfaces and Header.* static subcomponents for typings and intended public API.
    • apps/admin-x-framework/src/api/config.ts — validate the introduced JSONArray type integrates correctly with existing JSONValue usage.
    • Server-side recommendation services (ghost core) and URL/JSON parsing sites — confirm observability and error handling expectations remain satisfied.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: updating to the latest ESLint ghost plugin version, which aligns with the primary objective stated in the PR description.
Description check ✅ Passed The description comprehensively covers the key changes: updating the ESLint ghost plugin to 3.4.4, updating TypeScript ESLint versions in e2e and admin apps, and resolving related errors.

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

Comment @coderabbitai help to get the list of available commands and usage tips.

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: 0

🧹 Nitpick comments (5)
apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (2)

33-41: Consider using an explicit if statement to avoid the ESLint suppression.

The logical AND pattern for conditional execution is valid but triggers the no-unused-expressions rule. Refactoring to an explicit if statement would eliminate the need for the suppression comment and improve clarity.

Apply this diff:

-                        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
-                        !!item.errors.label && clearError?.('label');
+                        if (item.errors.label) {
+                            clearError?.('label');
+                        }

Note: The !! double negation is also unnecessary since the if statement coerces to boolean automatically.


60-69: Consider using an explicit if statement to avoid the ESLint suppression.

Same pattern as above—refactoring to an explicit if statement would eliminate the suppression and improve code clarity.

Apply this diff:

-                        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
-                        !!item.errors.url && clearError?.('url');
+                        if (item.errors.url) {
+                            clearError?.('url');
+                        }
apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1)

16-16: LGTM! Type alias aligns with ESLint standardization.

The change is consistent with the ESLint-driven refactoring across PostAnalytics components. Note: This line includes a semicolon while the equivalent lines in web.tsx and newsletter.tsx do not—consider standardizing for consistency, though this is a minor stylistic detail.

apps/activitypub/src/views/profile/profile.tsx (1)

13-13: Tighten ProfileProps to reflect “no props” intent

Using object here makes Profile accept any non‑primitive props. If the component truly has no props, consider:

-type ProfileProps = object
+type ProfileProps = {}

(or Record<string, never>) for clearer intent and stricter typing.

apps/activitypub/src/components/modals/new-note-modal.tsx (1)

75-104: Stale comment still refers to an error variable

The switch to a bare catch is fine behavior‑wise, but the commented console.error('Failed to create post:', error); is now misleading because there’s no error binding anymore. Consider removing that comment or rephrasing it so it doesn’t rely on an unavailable variable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f82fc9a and d75e2cb.

📒 Files selected for processing (53)
  • apps/activitypub/src/api/activitypub.ts (1 hunks)
  • apps/activitypub/src/components/modals/new-note-modal.tsx (1 hunks)
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts (3 hunks)
  • apps/activitypub/src/utils/get-username.ts (1 hunks)
  • apps/activitypub/src/utils/image.ts (1 hunks)
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1 hunks)
  • apps/activitypub/src/views/preferences/components/profile.tsx (1 hunks)
  • apps/activitypub/src/views/profile/profile.tsx (1 hunks)
  • apps/admin-x-design-system/src/global/sortable-list.tsx (1 hunks)
  • apps/admin-x-design-system/src/typings.d.ts (1 hunks)
  • apps/admin-x-design-system/src/utils/format-url.ts (1 hunks)
  • apps/admin-x-framework/src/api/config.ts (1 hunks)
  • apps/admin-x-framework/src/api/recommendations.ts (1 hunks)
  • apps/admin-x-framework/src/test/acceptance.ts (1 hunks)
  • apps/admin-x-framework/src/test/test-utils.tsx (1 hunks)
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts (2 hunks)
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (2 hunks)
  • apps/admin-x-settings/src/typings.d.ts (1 hunks)
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts (1 hunks)
  • apps/admin-x-settings/src/utils/url.ts (1 hunks)
  • apps/comments-ui/src/actions.ts (2 hunks)
  • apps/comments-ui/src/app-context.ts (1 hunks)
  • apps/comments-ui/src/components/content/forms/form.tsx (1 hunks)
  • apps/comments-ui/src/components/popup-box.tsx (1 hunks)
  • apps/comments-ui/src/typings.d.ts (1 hunks)
  • apps/comments-ui/src/utils/admin-api.ts (1 hunks)
  • apps/comments-ui/src/utils/api.ts (1 hunks)
  • apps/posts/src/hooks/use-post-success-modal.ts (1 hunks)
  • apps/posts/src/utils/link-helpers.ts (1 hunks)
  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Web/web.tsx (1 hunks)
  • apps/shade/src/components/features/color-picker/color-picker.tsx (1 hunks)
  • apps/shade/src/components/layout/header.tsx (6 hunks)
  • apps/shade/src/components/layout/heading.tsx (1 hunks)
  • apps/shade/src/components/layout/page.tsx (1 hunks)
  • apps/shade/src/components/layout/view-header.tsx (2 hunks)
  • apps/shade/src/components/ui/card.tsx (1 hunks)
  • apps/shade/src/lib/utils.ts (1 hunks)
  • apps/shade/src/typings.d.ts (1 hunks)
  • apps/signup-form/src/components/pages/form-page.tsx (1 hunks)
  • apps/signup-form/src/typings.d.ts (1 hunks)
  • apps/stats/src/hooks/use-feature-flag.tsx (1 hunks)
  • apps/stats/src/types/svg.d.ts (1 hunks)
  • apps/stats/src/utils/url-helpers.ts (5 hunks)
  • apps/stats/test/utils/tinybird-helpers.ts (1 hunks)
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2 hunks)
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1 hunks)
✅ Files skipped from review due to trivial changes (6)
  • apps/shade/src/typings.d.ts
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx
  • apps/admin-x-framework/src/test/test-utils.tsx
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx
  • apps/comments-ui/src/components/popup-box.tsx
  • apps/admin-x-settings/src/typings.d.ts
🧰 Additional context used
📓 Path-based instructions (7)
apps/shade/src/components/layout/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in src/components/layout/*

Files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/layout/page.tsx
apps/shade/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/components/**/*.{ts,tsx}: Use PascalCase for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Always forward and merge className prop with cn(...) utility function
Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration
Prefer compound subcomponents (e.g., Header.Title, Header.Meta, Header.Actions) for multi-region components instead of many props
Use Tailwind CSS scoped via .shade class; dark mode uses .dark

Files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/ui/card.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/layout/page.tsx
apps/shade/src/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/**/*.{ts,tsx,js}: Use camelCase for function and variable names
Use the @ alias for internal imports (e.g., @/lib/utils)

Files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/lib/utils.ts
  • apps/shade/src/components/ui/card.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/layout/page.tsx
apps/shade/{src,test}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/{src,test}/**/*.{ts,tsx,js}: Run yarn lint after making changes to fix any ESLint errors and warnings before committing
Follow ESLint and tailwindcss/* plugin rules when writing styles

Files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/lib/utils.ts
  • apps/shade/src/components/ui/card.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/layout/page.tsx
apps/shade/src/lib/utils.ts

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Shared utilities (class merging, formatting, chart helpers) should be centralized in src/lib/utils.ts

Files:

  • apps/shade/src/lib/utils.ts
apps/shade/src/components/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Atomic UI components should be placed in src/components/ui/* and each component must have a corresponding *.stories.tsx file next to it for Storybook documentation

Files:

  • apps/shade/src/components/ui/card.tsx
apps/shade/src/components/features/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in src/components/features/*

Files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
🧠 Learnings (29)
📓 Common learnings
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.
Learnt from: niranjan-uma-shankar
Repo: TryGhost/Ghost PR: 24557
File: apps/admin-x-settings/src/components/settings/general/TimeZone.tsx:7-7
Timestamp: 2025-08-01T12:44:07.467Z
Learning: In Ghost development, PRs may depend on unpublished changes from SDK packages. When this occurs, TypeScript compilation errors for missing exports are expected and documented in the PR description until the dependency packages are published and updated. This is normal workflow for cross-repository feature development.
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24673
File: ghost/i18n/lib/i18n.js:34-35
Timestamp: 2025-11-24T11:12:15.712Z
Learning: In the Ghost i18n package (ghost/i18n/lib/i18n.js), changing existing locale codes requires backwards compatibility handling for users who have already configured those locales. Such changes should be done in a separate PR with migration logic rather than included in feature PRs.
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Learnt from: ErisDS
Repo: TryGhost/Ghost PR: 23644
File: ghost/admin/tests/unit/controllers/application-test.js:57-71
Timestamp: 2025-06-06T09:03:17.958Z
Learning: In Ghost admin, the hardcoded version filtering logic that checks for versions starting with "5." is acceptable for now. The team prefers to defer making this configurable until major updates become more frequent, at which point they'll create a better system.
Learnt from: troyciesco
Repo: TryGhost/Ghost PR: 24749
File: ghost/core/core/server/services/members/SingleUseTokenProvider.js:3-5
Timestamp: 2025-08-26T16:47:28.150Z
Learning: When checking for dependencies in Ghost project, ensure to look directly in the specific package.json files rather than relying only on automated searches, as otplib dependency exists at line 204 in ghost/core/package.json version "12.0.1"
📚 Learning: 2025-10-30T17:13:26.190Z
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.

Applied to files:

  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts
  • apps/comments-ui/src/utils/api.ts
📚 Learning: 2025-10-09T15:31:06.587Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25118
File: apps/portal/src/actions.js:160-173
Timestamp: 2025-10-09T15:31:06.587Z
Learning: When reviewing PRs that introduce feature-flagged changes (e.g., `labs?.membersSigninOTCAlpha`), avoid suggesting modifications to non-flagged code paths unless they're directly related to the PR's objectives. Keep the scope focused on the feature-flag-specific changes only.

Applied to files:

  • apps/admin-x-framework/src/test/acceptance.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Always follow ADRs in `../adr/` folder (ADR-0001: AAA pattern, ADR-0002: Page Objects)

Applied to files:

  • apps/admin-x-framework/src/test/acceptance.ts
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use factory pattern for all test data creation instead of hard-coded data or direct database manipulation

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Test names should be lowercase and follow the format 'what is tested - expected outcome'

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use `getByTestId()` only when semantic locators are unavailable, and suggest adding `data-testid` to Ghost codebase when needed

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/components/modals/new-note-modal.tsx
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
  • apps/comments-ui/src/actions.ts
  • apps/activitypub/src/views/preferences/components/profile.tsx
  • apps/posts/src/hooks/use-post-success-modal.ts
  • apps/activitypub/src/api/activitypub.ts
  • apps/comments-ui/src/utils/api.ts
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/components/modals/new-note-modal.tsx
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
  • apps/comments-ui/src/actions.ts
  • apps/posts/src/hooks/use-post-success-modal.ts
  • apps/activitypub/src/api/activitypub.ts
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx
📚 Learning: 2025-08-11T19:39:00.428Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.

Applied to files:

  • apps/comments-ui/src/actions.ts
  • apps/stats/test/utils/tinybird-helpers.ts
  • apps/comments-ui/src/utils/api.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/ui/card.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/ui/card.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/ui/card.tsx
  • apps/comments-ui/src/app-context.ts
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/layout/page.tsx
  • apps/activitypub/src/views/profile/profile.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/index.ts : Place new UI components under `src/components/ui` and export them from `src/index.ts`

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/ui/card.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/ui/card.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/layout/**/*.{ts,tsx} : Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in `src/components/layout/*`

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use Tailwind CSS scoped via `.shade` class; dark mode uses `.dark`

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-25T11:58:51.652Z
Learnt from: ibalosh
Repo: TryGhost/Ghost PR: 25525
File: apps/shade/src/shade-app.tsx:4-4
Timestamp: 2025-11-25T11:58:51.652Z
Learning: In apps/shade, the app wrapper file should be named `src/shade-app.tsx` (kebab-case) while the component itself is exported as `ShadeApp` (PascalCase). Context providers should be placed in `src/providers/*` using kebab-case filenames.

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use `camelCase` for function and variable names

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Always forward and merge `className` prop with `cn(...)` utility function

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-12-09T12:37:23.267Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25501
File: apps/shade/src/hooks/use-mobile.tsx:5-10
Timestamp: 2025-12-09T12:37:23.267Z
Learning: In the Ghost repository, this guideline applies to the shade admin app. For files under apps/shade that access browser globals (navigator, window, document) at module load time, SSR is not used, so typeof guards are not required. Reviewers should verify that such files remain client-side only and that no SSR context is introduced; apply this understanding to similarly structured files under apps/shade.

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/ui/card.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/lib/utils.ts : Shared utilities (class merging, formatting, chart helpers) should be centralized in `src/lib/utils.ts`

Applied to files:

  • apps/shade/src/lib/utils.ts
  • apps/shade/src/components/features/color-picker/color-picker.tsx
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.{ts,tsx} : Prefer less comments and give things clear names

Applied to files:

  • apps/comments-ui/src/typings.d.ts
  • apps/comments-ui/src/app-context.ts
  • apps/signup-form/src/typings.d.ts
📚 Learning: 2025-09-10T07:05:34.606Z
Learnt from: jonatansberg
Repo: TryGhost/Ghost PR: 24838
File: apps/shade/src/components/features/color-picker/color-picker.tsx:0-0
Timestamp: 2025-09-10T07:05:34.606Z
Learning: The Color library used in the color picker component handles input validation and clamping automatically, so manual validation of HSL and RGB values is not necessary when using Color constructors.

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
📚 Learning: 2025-04-23T15:44:52.549Z
Learnt from: 9larsons
Repo: TryGhost/Ghost PR: 21866
File: ghost/core/core/server/web/admin/middleware/serve-auth-frame-file.js:10-19
Timestamp: 2025-04-23T15:44:52.549Z
Learning: The existing implementation in `ghost/core/core/server/web/admin/middleware/serve-auth-frame-file.js` using `path.parse(req.url).base` is secure against path traversal attacks as it properly extracts only the filename component without any directory parts.

Applied to files:

  • apps/stats/src/utils/url-helpers.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/features/**/*.{ts,tsx} : Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in `src/components/features/*`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/ui/**/*.{ts,tsx} : Atomic UI components should be placed in `src/components/ui/*` and each component must have a corresponding `*.stories.tsx` file next to it for Storybook documentation

Applied to files:

  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/helpers/pages/**/*.ts : Expose locators as `public readonly` in Page Objects when used with assertions

Applied to files:

  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-07-14T12:20:35.268Z
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24346
File: apps/admin-x-settings/src/components/settings/growth/Network.tsx:8-12
Timestamp: 2025-07-14T12:20:35.268Z
Learning: The Network component toggle in `apps/admin-x-settings/src/components/settings/growth/Network.tsx` is intentionally implemented as static UI with a no-op onChange handler, as part of a UI-first development approach before connecting actual ActivityPub functionality.

Applied to files:

  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx
⏰ 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). (13)
  • GitHub Check: Acceptance tests (Node 22.18.0, mysql8)
  • GitHub Check: Acceptance tests (Node 22.18.0, sqlite3)
  • GitHub Check: Legacy tests (Node 22.18.0, mysql8)
  • GitHub Check: Legacy tests (Node 22.18.0, sqlite3)
  • GitHub Check: Comments-UI tests
  • GitHub Check: ActivityPub tests
  • GitHub Check: Ghost-CLI tests
  • GitHub Check: Signup-form tests
  • GitHub Check: Admin-X Settings tests
  • GitHub Check: Admin tests - Chrome
  • GitHub Check: Lint
  • GitHub Check: Unit tests (Node 22.18.0)
  • GitHub Check: Build & Push Docker Image
🔇 Additional comments (50)
apps/posts/src/utils/link-helpers.ts (1)

30-33: LGTM! Clean refactor removing unused error binding.

The error parameter was never referenced in the catch block, so removing it aligns with best practices and the updated ESLint rules.

apps/stats/src/hooks/use-feature-flag.tsx (1)

20-25: LGTM!

The bare catch is appropriate since the error was never used. The fallback behavior (empty object on parse failure) is preserved.

apps/comments-ui/src/app-context.ts (1)

118-118: LGTM: Consistent with ESLint plugin update.

The change to a bare catch clause is appropriate since the error was never referenced. This aligns with the optional catch binding feature (ES2019) and the updated eslint-plugin-ghost rules.

apps/stats/src/utils/url-helpers.ts (2)

12-24: LGTM: Bare catch is appropriate here.

The change from catch (error) to bare catch is correct since the error was never used—the function simply returns an empty string on failure. This aligns with the ESLint plugin updates.


15-15: LGTM: Formatting improvements enhance readability.

The blank line additions improve code organization by separating logical blocks within the functions.

Also applies to: 37-37, 53-53, 58-58, 106-106, 115-115

ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1)

175-190: Bare catch change is safe and keeps behavior identical

Removing the unused error binding and using catch { ... } keeps the same behavior: any failure in new URL(string) or the protocol check is still normalized to a ValidationError. No functional or type-safety concerns here; this is a clean, lint-driven tidy‑up.

apps/admin-x-settings/src/utils/link-to-github-releases.ts (1)

32-32: LGTM! Clean removal of unused error binding.

The bare catch syntax is valid and improves code clarity since the error was never referenced. This aligns with the ESLint plugin update enforcing removal of unused error bindings across the codebase.

apps/posts/src/views/PostAnalytics/Web/web.tsx (1)

26-26: LGTM! Type alias aligns with updated ESLint rules.

The change from an empty interface to type postAnalyticsProps = object is semantically equivalent and appears to be driven by the ESLint plugin update to version 3.4.4. This provides consistent type definition style across the codebase.

apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1)

17-17: LGTM! Consistent with ESLint-driven type definition standardization.

The type alias change matches the pattern applied across other PostAnalytics components and maintains the same type semantics.

ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2)

62-66: Bare catch preserves existing error handling behavior

The inner JSON.parse and outer network-level try blocks now use bare catch clauses, but the caught error was not previously used and the failure paths still return undefined. Behavior and control flow are unchanged and the lint-driven cleanup looks good.

Also applies to: 68-70


77-81: URL parsing catch change is a no-op behaviorally

Similarly, switching to a bare catch in #castUrl keeps the same behavior of returning null on invalid URLs while removing an unused error binding. No functional impact here.

apps/comments-ui/src/typings.d.ts (1)

1-7: LGTM! Appropriate lint suppression for ambient module declaration.

The ESLint disable directive is correctly applied to suppress the @typescript-eslint/no-require-imports rule for the CommonJS-style import syntax, which is appropriate in ambient module declarations where this syntax is sometimes necessary for TypeScript.

apps/admin-x-design-system/src/typings.d.ts (1)

1-7: LGTM! Consistent lint suppression applied.

The lint directive is appropriately applied, consistent with the pattern established across other SVG type definition files in this PR.

apps/signup-form/src/typings.d.ts (1)

1-7: LGTM! Lint suppression applied consistently.

The targeted eslint-disable-next-line directive appropriately handles the CommonJS import syntax required in this ambient module declaration.

apps/stats/src/types/svg.d.ts (1)

1-7: LGTM! Completes the consistent lint suppression pattern.

The lint directive is appropriately applied, maintaining consistency with the other SVG type definition files updated in this PR.

apps/comments-ui/src/actions.ts (2)

257-265: LGTM! Bare catch syntax correctly applied.

The error object was not used in the original catch block, so removing the binding is appropriate. The optimistic update pattern remains intact.


267-276: LGTM! Bare catch syntax correctly applied.

The error object was not used in the original catch block, so removing the binding is appropriate. The optimistic update pattern remains intact.

apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1)

23-27: Bare catch on URL parsing is appropriate here

The error value was never used; switching to catch {} keeps behavior identical while satisfying lint rules and avoiding an unused binding.

apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1)

103-117: onOk catch cleanup keeps behavior unchanged

Removing the unused error parameter in onOk’s try/catch keeps the same toast-based error handling and matches the lint-driven pattern used elsewhere in this PR.

apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1)

83-93: Submission catch block correctly drops unused error binding

Here the error object wasn’t used; changing to a bare catch keeps the same user-facing toast while simplifying the handler and aligning with the updated lint rules.

apps/shade/src/lib/utils.ts (1)

97-103: Bare catch keeps formatUrl behavior unchanged

The switch to a bare catch here is safe; the error value was not used, and the function still correctly falls back to {save: url, display: url} when URL construction fails.

apps/posts/src/hooks/use-post-success-modal.ts (1)

154-169: LocalStorage error handling remains safe

Dropping the unused error parameter in the catch keeps the intended behavior of silently ignoring localStorage/JSON failures and falling back cleanly. No functional change.

apps/admin-x-design-system/src/utils/format-url.ts (1)

44-50: Consistent bare catch in formatUrl

This matches the pattern used elsewhere: the unused error variable is removed while continuing to fall back to {save: url, display: url} on URL parse failures.

apps/activitypub/src/utils/image.ts (1)

10-28: imageUrlToDataUrl fallback behavior preserved

Switching to a bare catch keeps the same contract: on any fetch/parse error, the function returns the original URL as a safe fallback.

apps/admin-x-settings/src/utils/url.ts (1)

24-32: arePathsEqual retains same invalid-URL behavior

The bare catch keeps the logic of returning false when either URL string can’t be parsed, just without binding an unused error variable.

apps/stats/test/utils/tinybird-helpers.ts (1)

81-91: Test helper still produces the same error messages

Removing the unused error binding in the catch keeps the same behavior: on JSON parse failure, it falls back to bodyText when constructing the thrown Error.

apps/signup-form/src/components/pages/form-page.tsx (1)

23-39: Submit error handling unchanged aside from lint cleanup

The bare catch keeps the same UX: on any failure, loading is cleared and a generic “Something went wrong” message is shown. Only the unused error parameter was removed.

apps/activitypub/src/utils/get-username.ts (1)

5-9: Bare catch keeps username fallback behavior unchanged

The catch binding removal is safe here; the fallback @unknown@unknown is preserved and no error object was previously used.

apps/comments-ui/src/utils/api.ts (1)

312-319: Init labs error handling remains behavior‑preserving

Switching to a bare catch keeps the same behavior (resetting labs to {} on any settings fetch/parse error) and removes an unused binding.

apps/comments-ui/src/components/content/forms/form.tsx (1)

72-81: submitForm error state handling unchanged

Removing the error parameter from the catch block doesn’t alter behavior; the form still transitions to 'error' state on any failure.

apps/comments-ui/src/utils/admin-api.ts (1)

15-20: Admin iframe message parsing: behavior unchanged with bare catch

The bare catch keeps the early‑return behavior on invalid JSON while dropping an unused error variable, which is appropriate for this handler.

apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1)

48-58: Bluesky enable error path remains intact

Changing to a bare catch here keeps the same UX (clearing loading and showing the generic error toast) while avoiding an unused error parameter.

apps/activitypub/src/api/activitypub.ts (1)

252-260: getToken’s null‑on‑error contract preserved

The bare catch keeps getToken’s existing behavior of returning null on any failure, and call sites already tolerate a missing token, so this change is safe.

apps/admin-x-framework/src/test/acceptance.ts (1)

127-133: Labs settings parse failure handling unchanged

Switching to a bare catch here keeps the same behavior (throwing 'Failed to parse labs settings') while removing an unused error parameter in this test helper.

apps/shade/src/components/features/color-picker/color-picker.tsx (1)

369-391: Bare catch is appropriate here

The caught error wasn’t used, so switching to a bare catch keeps behavior identical while satisfying linting (no-unused-vars) and keeps the handler focused on just ignoring invalid input.

apps/admin-x-framework/test/unit/hooks/use-form.test.ts (2)

202-208: Removing the error binding in the test catch block is safe

This test only asserts that an error is thrown and state/onSaveError behavior, not the error object itself, so using a bare catch keeps semantics intact while avoiding an unused variable.


229-235: Consistent bare catch usage in async error test

Same here: the error instance isn’t inspected, so changing to catch {} is a no-op behavior-wise and keeps the test aligned with the new lint rules.

apps/activitypub/src/views/preferences/components/profile.tsx (1)

258-314: Bare catch blocks in handleCopy preserve behavior

Both inner and outer catch blocks only emit a toast and (outer) reset isProcessing, never using the error value, so switching to catch {} is behaviorally identical and matches the lint-driven style across the PR.

apps/activitypub/src/hooks/use-activity-pub-queries.ts (3)

360-403: Notification liked-cache try/catch remains robust

The catch block only returns current and never referenced the error, so moving to a bare catch keeps the safe fallback behavior unchanged while satisfying lint rules.


406-449: Notification repost-cache catch simplification is safe

Same pattern here: the catch just returns current; dropping the unused error parameter has no effect on cache semantics.


452-495: Notification reply-count cache catch simplification is safe

Again, the error value wasn’t used; retaining the return current fallback with a bare catch is functionally equivalent and keeps the code lint-clean.

apps/shade/src/components/layout/view-header.tsx (2)

4-6: Prop typing change to alias looks fine

Switching ViewHeaderActionsProps from an interface to type ViewHeaderActionsProps = React.HTMLAttributes<HTMLElement> keeps the component’s public surface the same and doesn’t affect runtime behavior.


31-31: Named exports unchanged

ViewHeader and ViewHeaderActions are still exported as before; no issues with the export shape from this module.

apps/shade/src/components/ui/card.tsx (1)

219-223: EmptyCard props alias is equivalent and safe

Changing EmptyCardProps from an interface to export type EmptyCardProps = React.ComponentPropsWithoutRef<'div'> doesn’t alter the component’s external API or behavior and aligns with the broader type-alias pattern in this PR.

apps/shade/src/components/layout/page.tsx (1)

4-16: PageProps interface→type refactor is non-breaking

Defining PageProps as a type alias for React.HTMLAttributes<HTMLDivElement> keeps the same prop surface while simplifying the typing style; the component implementation and usage remain unchanged.

apps/admin-x-framework/src/api/config.ts (1)

3-6: No breaking change concern here. JSONArray is only used internally within config.ts to define JSONValue and is not imported by any other files in the codebase. Making it a non-exported type alias has no impact on external code.

Likely an incorrect or invalid review comment.

apps/shade/src/components/layout/heading.tsx (1)

4-4: HeadingProps interface→type alias change is safe

Using type HeadingProps = React.HTMLAttributes<HTMLHeadingElement> is equivalent in shape to the previous extending interface and keeps the H1–H4 APIs unchanged, while matching the broader move to type aliases. As per coding guidelines, className merging via cn and layout placement remain correct.

apps/admin-x-framework/src/api/recommendations.ts (1)

25-27: Confirm the stricter RecommendationDeleteResponseType = object is intended

RecommendationEditResponseType being an alias of RecommendationResponseType keeps the previous interface extension behavior in practice, so that change is safe.

For RecommendationDeleteResponseType, changing from an empty interface to object makes the type slightly stricter than {} (it excludes primitives). That’s probably fine for a DELETE response that’s ignored or returns an empty/JSON object, but it’s worth confirming that existing call sites don’t rely on assigning other shapes here.

apps/shade/src/components/layout/header.tsx (1)

11-75: Header subcomponent prop interfaces→type aliases keep behavior unchanged

All Header*Props now alias PropsWithChildrenAndClassName instead of extending it as interfaces. That’s structurally equivalent, keeps children/className exactly as before, and fits the broader shift toward type aliases without affecting the Header compound subcomponents or their usage. This remains aligned with the shade layout guidelines (compound subcomponents + cn for className).

apps/admin-x-design-system/src/global/sortable-list.tsx (1)

17-18: The ESLint rule name @typescript-eslint/no-unsafe-function-type is correct and properly suppresses the warning about using the generic Function type. The TODO comment on line 16 appropriately flags that a stricter type alternative should be defined in the future.

@ibalosh ibalosh force-pushed the update-eslint-version branch from d75e2cb to 2500ab9 Compare December 15, 2025 13:02
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 (1)
apps/admin-x-design-system/src/global/sortable-list.tsx (1)

140-145: Update onMove type signature to reflect that overId can be undefined

The onMove prop is typed as (id: string, overId: string) => void, but when a drop occurs outside a valid target, event.over?.id is undefined, violating the type contract. The actual implementations (e.g., moveItem in useSortableIndexedList, the storybook example) already handle this by making overId optional, so the type signature should match reality:

-    onMove: (id: string, overId: string) => void;
+    onMove: (id: string, overId: string | undefined) => void;

Alternatively, add a guard in the handler to skip the callback when there's no valid drop target:

                    onDragEnd={(event) => {
+                        if (!event.over) {
+                            setDraggingId(null);
+                            return;
+                        }
-                        onMove(event.active.id as string, event.over?.id as string);
+                        onMove(event.active.id as string, event.over.id as string);
                        setDraggingId(null);
                    }}
🧹 Nitpick comments (4)
apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1)

87-93: LGTM! Consistent error handling.

The change to bare catch is correct since the error object wasn't being used. The code maintains consistency: error bindings are retained where they're used (line 60 in the delete handler) and removed where they're not.

Optional: Consider logging the error for debugging/monitoring purposes, even when showing a generic user message.

-            } catch {
+            } catch (error) {
+                console.error('Failed to save recommendation:', error);
                 showToast({
                     title: 'Something went wrong',
                     type: 'error',
                     message: 'Please try again later.'
                 });
             }
apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1)

112-117: LGTM! Aligns with ESLint rule update.

The bare catch syntax is appropriate here since the error binding was unused. This change is consistent with the ESLint plugin update mentioned in the PR title.

Optional: Consider capturing the error for debugging/telemetry purposes.

-            } catch {
+            } catch (error) {
+                console.error('Failed to add recommendation:', error);
                 showToast({
                     type: 'error',
                     title: 'Something went wrong when adding this recommendation, please try again.'
                 });
             }
apps/activitypub/src/views/preferences/components/profile.tsx (1)

272-314: Catch parameter removal maintains existing error UX/state

Dropping the error parameter from both inner and outer catch blocks doesn’t change behavior: failures still trigger the same toast message and setIsProcessing(false) paths, and the Clipboard API support check still flows into the outer catch as before. This aligns with the lint-driven cleanup without altering the image-copy UX.

Based on learnings, if you later revisit this area, you could consider consolidating the duplicated “Failed to copy image” toast into a small helper, but that’s not necessary for this PR.

apps/admin-x-design-system/src/global/sortable-list.tsx (1)

16-18: ESLint rule update is correct; consider tightening dragHandleListeners type in a future refactor

The switch to @typescript-eslint/no-unsafe-function-type is the appropriate rule name for the current plugin version. The Record<string, Function> type itself is still loose—since these listeners come from the @dnd-kit/sortable hook and are spread directly onto DOM elements, they could be typed more precisely (for instance, as a mapped type of the actual dnd-kit event handler shapes or Record<string, (...args: unknown[]) => void>). This doesn't need to block the current PR, but can be a follow-up improvement once you want to remove the ESLint suppression entirely.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d75e2cb and 2500ab9.

📒 Files selected for processing (53)
  • apps/activitypub/src/api/activitypub.ts (1 hunks)
  • apps/activitypub/src/components/modals/new-note-modal.tsx (1 hunks)
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts (3 hunks)
  • apps/activitypub/src/utils/get-username.ts (1 hunks)
  • apps/activitypub/src/utils/image.ts (1 hunks)
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1 hunks)
  • apps/activitypub/src/views/preferences/components/profile.tsx (1 hunks)
  • apps/activitypub/src/views/profile/profile.tsx (1 hunks)
  • apps/admin-x-design-system/src/global/sortable-list.tsx (1 hunks)
  • apps/admin-x-design-system/src/typings.d.ts (1 hunks)
  • apps/admin-x-design-system/src/utils/format-url.ts (1 hunks)
  • apps/admin-x-framework/src/api/config.ts (1 hunks)
  • apps/admin-x-framework/src/api/recommendations.ts (1 hunks)
  • apps/admin-x-framework/src/test/acceptance.ts (1 hunks)
  • apps/admin-x-framework/src/test/test-utils.tsx (1 hunks)
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts (2 hunks)
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (2 hunks)
  • apps/admin-x-settings/src/typings.d.ts (1 hunks)
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts (1 hunks)
  • apps/admin-x-settings/src/utils/url.ts (1 hunks)
  • apps/comments-ui/src/actions.ts (2 hunks)
  • apps/comments-ui/src/app-context.ts (1 hunks)
  • apps/comments-ui/src/components/content/forms/form.tsx (1 hunks)
  • apps/comments-ui/src/components/popup-box.tsx (1 hunks)
  • apps/comments-ui/src/typings.d.ts (1 hunks)
  • apps/comments-ui/src/utils/admin-api.ts (1 hunks)
  • apps/comments-ui/src/utils/api.ts (1 hunks)
  • apps/posts/src/hooks/use-post-success-modal.ts (1 hunks)
  • apps/posts/src/utils/link-helpers.ts (1 hunks)
  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Web/web.tsx (1 hunks)
  • apps/shade/src/components/features/color-picker/color-picker.tsx (1 hunks)
  • apps/shade/src/components/layout/header.tsx (6 hunks)
  • apps/shade/src/components/layout/heading.tsx (1 hunks)
  • apps/shade/src/components/layout/page.tsx (1 hunks)
  • apps/shade/src/components/layout/view-header.tsx (2 hunks)
  • apps/shade/src/components/ui/card.tsx (1 hunks)
  • apps/shade/src/lib/utils.ts (1 hunks)
  • apps/shade/src/typings.d.ts (1 hunks)
  • apps/signup-form/src/components/pages/form-page.tsx (1 hunks)
  • apps/signup-form/src/typings.d.ts (1 hunks)
  • apps/stats/src/hooks/use-feature-flag.tsx (1 hunks)
  • apps/stats/src/types/svg.d.ts (1 hunks)
  • apps/stats/src/utils/url-helpers.ts (5 hunks)
  • apps/stats/test/utils/tinybird-helpers.ts (1 hunks)
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2 hunks)
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • apps/posts/src/hooks/use-post-success-modal.ts
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx
🚧 Files skipped from review as they are similar to previous changes (31)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx
  • apps/comments-ui/src/components/popup-box.tsx
  • apps/posts/src/utils/link-helpers.ts
  • apps/comments-ui/src/actions.ts
  • apps/comments-ui/src/utils/api.ts
  • apps/stats/test/utils/tinybird-helpers.ts
  • apps/comments-ui/src/components/content/forms/form.tsx
  • apps/stats/src/utils/url-helpers.ts
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
  • apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx
  • apps/shade/src/lib/utils.ts
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx
  • apps/comments-ui/src/utils/admin-api.ts
  • apps/shade/src/components/layout/view-header.tsx
  • apps/comments-ui/src/app-context.ts
  • apps/admin-x-framework/src/test/test-utils.tsx
  • apps/shade/src/components/ui/card.tsx
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts
  • apps/shade/src/typings.d.ts
  • apps/activitypub/src/api/activitypub.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx
  • apps/admin-x-design-system/src/typings.d.ts
  • apps/stats/src/hooks/use-feature-flag.tsx
  • apps/activitypub/src/utils/get-username.ts
  • apps/admin-x-design-system/src/utils/format-url.ts
  • apps/signup-form/src/typings.d.ts
  • apps/stats/src/types/svg.d.ts
  • apps/admin-x-framework/src/api/recommendations.ts
  • apps/comments-ui/src/typings.d.ts
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
  • apps/shade/src/components/layout/header.tsx
🧰 Additional context used
📓 Path-based instructions (5)
apps/shade/src/components/features/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in src/components/features/*

Files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
apps/shade/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/components/**/*.{ts,tsx}: Use PascalCase for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Always forward and merge className prop with cn(...) utility function
Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration
Prefer compound subcomponents (e.g., Header.Title, Header.Meta, Header.Actions) for multi-region components instead of many props
Use Tailwind CSS scoped via .shade class; dark mode uses .dark

Files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/page.tsx
apps/shade/src/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/**/*.{ts,tsx,js}: Use camelCase for function and variable names
Use the @ alias for internal imports (e.g., @/lib/utils)

Files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/page.tsx
apps/shade/{src,test}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/{src,test}/**/*.{ts,tsx,js}: Run yarn lint after making changes to fix any ESLint errors and warnings before committing
Follow ESLint and tailwindcss/* plugin rules when writing styles

Files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/page.tsx
apps/shade/src/components/layout/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in src/components/layout/*

Files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/page.tsx
🧠 Learnings (21)
📓 Common learnings
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24673
File: ghost/i18n/lib/i18n.js:34-35
Timestamp: 2025-11-24T11:12:15.712Z
Learning: In the Ghost i18n package (ghost/i18n/lib/i18n.js), changing existing locale codes requires backwards compatibility handling for users who have already configured those locales. Such changes should be done in a separate PR with migration logic rather than included in feature PRs.
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Applies to ghost/i18n/locales/en/ghost.json : Add UI translations to `ghost/i18n/locales/en/ghost.json` for Admin UI features
Learnt from: niranjan-uma-shankar
Repo: TryGhost/Ghost PR: 24557
File: apps/admin-x-settings/src/components/settings/general/TimeZone.tsx:7-7
Timestamp: 2025-08-01T12:44:07.467Z
Learning: In Ghost development, PRs may depend on unpublished changes from SDK packages. When this occurs, TypeScript compilation errors for missing exports are expected and documented in the PR description until the dependency packages are published and updated. This is normal workflow for cross-repository feature development.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Admin-x React apps build to `apps/*/dist` using Vite, which are then copied by `ghost/admin/lib/asset-delivery` to `ghost/core/core/built/admin/assets/*`
Learnt from: ErisDS
Repo: TryGhost/Ghost PR: 23644
File: ghost/admin/tests/unit/controllers/application-test.js:57-71
Timestamp: 2025-06-06T09:03:17.958Z
Learning: In Ghost admin, the hardcoded version filtering logic that checks for versions starting with "5." is acceptable for now. The team prefers to defer making this configurable until major updates become more frequent, at which point they'll create a better system.
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.{ts,tsx} : Prefer less comments and give things clear names

Applied to files:

  • apps/admin-x-settings/src/typings.d.ts
📚 Learning: 2025-08-11T19:39:00.428Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.

Applied to files:

  • apps/activitypub/src/views/preferences/components/profile.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/lib/utils.ts : Shared utilities (class merging, formatting, chart helpers) should be centralized in `src/lib/utils.ts`

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use Tailwind CSS scoped via `.shade` class; dark mode uses `.dark`

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-09-10T07:05:34.606Z
Learnt from: jonatansberg
Repo: TryGhost/Ghost PR: 24838
File: apps/shade/src/components/features/color-picker/color-picker.tsx:0-0
Timestamp: 2025-09-10T07:05:34.606Z
Learning: The Color library used in the color picker component handles input validation and clamping automatically, so manual validation of HSL and RGB values is not necessary when using Color constructors.

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
📚 Learning: 2025-12-09T12:37:23.267Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25501
File: apps/shade/src/hooks/use-mobile.tsx:5-10
Timestamp: 2025-12-09T12:37:23.267Z
Learning: In the Ghost repository, this guideline applies to the shade admin app. For files under apps/shade that access browser globals (navigator, window, document) at module load time, SSR is not used, so typeof guards are not required. Reviewers should verify that such files remain client-side only and that no SSR context is introduced; apply this understanding to similarly structured files under apps/shade.

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/index.ts : Place new UI components under `src/components/ui` and export them from `src/index.ts`

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/layout/**/*.{ts,tsx} : Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in `src/components/layout/*`

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-25T11:58:51.652Z
Learnt from: ibalosh
Repo: TryGhost/Ghost PR: 25525
File: apps/shade/src/shade-app.tsx:4-4
Timestamp: 2025-11-25T11:58:51.652Z
Learning: In apps/shade, the app wrapper file should be named `src/shade-app.tsx` (kebab-case) while the component itself is exported as `ShadeApp` (PascalCase). Context providers should be placed in `src/providers/*` using kebab-case filenames.

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use `camelCase` for function and variable names

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/ui/**/*.{ts,tsx} : Atomic UI components should be placed in `src/components/ui/*` and each component must have a corresponding `*.stories.tsx` file next to it for Storybook documentation

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Always forward and merge `className` prop with `cn(...)` utility function

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/components/modals/new-note-modal.tsx
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/components/modals/new-note-modal.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/features/**/*.{ts,tsx} : Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in `src/components/features/*`

Applied to files:

  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-07-14T12:20:35.268Z
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24346
File: apps/admin-x-settings/src/components/settings/growth/Network.tsx:8-12
Timestamp: 2025-07-14T12:20:35.268Z
Learning: The Network component toggle in `apps/admin-x-settings/src/components/settings/growth/Network.tsx` is intentionally implemented as static UI with a no-op onChange handler, as part of a UI-first development approach before connecting actual ActivityPub functionality.

Applied to files:

  • apps/activitypub/src/views/profile/profile.tsx
🪛 Biome (2.1.2)
apps/activitypub/src/views/profile/profile.tsx

[error] 13-14: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ 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). (13)
  • GitHub Check: Comments-UI tests
  • GitHub Check: Ghost-CLI tests
  • GitHub Check: ActivityPub tests
  • GitHub Check: Signup-form tests
  • GitHub Check: Acceptance tests (Node 22.18.0, mysql8)
  • GitHub Check: Legacy tests (Node 22.18.0, mysql8)
  • GitHub Check: Admin-X Settings tests
  • GitHub Check: Unit tests (Node 22.18.0)
  • GitHub Check: Admin tests - Chrome
  • GitHub Check: Legacy tests (Node 22.18.0, sqlite3)
  • GitHub Check: Acceptance tests (Node 22.18.0, sqlite3)
  • GitHub Check: Lint
  • GitHub Check: Build & Push Docker Image
🔇 Additional comments (17)
apps/activitypub/src/views/profile/profile.tsx (1)

13-14: ESLint disable comment is appropriate for the linting update scope.

The @typescript-eslint/no-empty-object-type rule suppression is a pragmatic choice for this linting-focused PR. Since ProfileProps is legitimately empty (the component accepts no props), the disable comment keeps this PR focused on updating the ESLint version without requiring structural refactors.

apps/admin-x-settings/src/utils/url.ts (1)

24-32: Bare catch here is a clean, behavior‑preserving change

The error object wasn’t used, so switching to a bare catch keeps the “return false on parse failure” behavior while satisfying the updated ESLint rule and slightly reducing noise.

Please confirm your TS/JS target (and ESLint/parser config) is ES2019+ so optional catch binding is always supported across your runtimes.

ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (3)

62-66: LGTM: Unused error binding correctly removed.

The error binding was never referenced in the catch block. Removing it aligns with the ESLint upgrade while preserving the existing behavior of returning undefined on JSON parse failures.


47-70: LGTM: Unused error binding correctly removed.

The error binding was never referenced in the catch block. Removing it aligns with the ESLint upgrade while preserving the existing error handling strategy of returning undefined for DNS and network errors.


77-81: LGTM: Unused error binding correctly removed.

The error binding was never referenced in the catch block. Removing it aligns with the ESLint upgrade while preserving the existing behavior of returning null for invalid URLs.

apps/admin-x-settings/src/typings.d.ts (1)

4-9: Scoped lint-disable looks appropriate here

The @typescript-eslint/no-require-imports suppression is tightly scoped to the one require-style React import inside a .d.ts SVG module, which is a reasonable exception and has no runtime or typing impact. This matches the pattern used elsewhere in the repo for SVG typings and keeps the rule enabled everywhere else.

apps/admin-x-settings/src/utils/link-to-github-releases.ts (1)

32-32: LGTM!

The change from catch (e) to bare catch is correct since the error variable was never used. This cleanup aligns with the ESLint plugin update objective.

apps/activitypub/src/components/modals/new-note-modal.tsx (1)

96-104: LGTM! Bare catch aligns with linting requirements.

The change from a named catch parameter to a bare catch is appropriate since the error object wasn't being used (the console.error on line 101 is commented out). This aligns with the ESLint plugin update and maintains the existing error handling behavior—invoking onReplyError for replies while relying on centralized error handling at the hook level.

Based on learnings, error handling for ActivityPub mutations is already centralized at the hook level in use-activity-pub-queries.ts.

apps/shade/src/components/features/color-picker/color-picker.tsx (1)

369-390: Optional catch binding change looks correct

Switching from catch (error) to catch { is safe here because the error object was never used and the behavior (silently ignoring invalid color input) is unchanged. This is aligned with modern JS/TS and the updated ESLint rules.

Please confirm that your TypeScript/ES target already supports optional catch bindings (it should in modern setups) and that yarn lint/CI pass after this change.

apps/activitypub/src/utils/image.ts (1)

19-28: Behavior-preserving catch simplification looks good

The switch to a parameterless catch keeps the same fallback behavior (returning the original URL) while removing an unused error binding, which is appropriate here. No functional change introduced.

apps/signup-form/src/components/pages/form-page.tsx (1)

36-36: LGTM! Bare catch is appropriate here.

The error parameter was unused, so removing it aligns with ESLint best practices while preserving the exact same error handling behavior.

apps/admin-x-framework/src/test/acceptance.ts (1)

130-130: LGTM! Bare catch is appropriate here.

The error parameter was not being used in the catch block, so removing it aligns with ESLint best practices without changing behavior.

apps/shade/src/components/layout/heading.tsx (1)

4-4: LGTM! Type alias is preferred for simple extensions.

Converting from interface to type alias is appropriate when the type simply extends a single type without additional properties. This follows TypeScript best practices.

apps/shade/src/components/layout/page.tsx (1)

4-4: LGTM! Type alias is preferred for simple extensions.

Converting from interface to type alias is appropriate when the type simply extends a single type without additional properties. This follows TypeScript best practices and aligns with the pattern used across other layout components in this PR.

apps/admin-x-framework/src/api/config.ts (1)

5-5: No breaking change from removing JSONArray export.

JSONArray is not used anywhere outside this file and was only ever referenced internally within the definitions of JSONValue and JSONObject. The change from an exported interface to a non-exported type alias is safe and represents internal refactoring only.

apps/posts/src/views/PostAnalytics/Web/web.tsx (1)

26-26: Prop type alias update looks fine

Using a named postAnalyticsProps type alias here keeps the component signature consistent with the other PostAnalytics views and avoids empty-interface lint issues. No further changes needed in this file.

apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1)

16-16: Consistent prop typing with other analytics views

Defining postAnalyticsProps as a type alias and using it in React.FC<postAnalyticsProps> aligns Growth with the other PostAnalytics components and avoids empty-interface warnings. Looks good as-is.

@ibalosh ibalosh force-pushed the update-eslint-version branch 2 times, most recently from 5895daa to 495218a Compare December 15, 2025 13:19
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: 0

🧹 Nitpick comments (8)
apps/admin-x-design-system/src/typings.d.ts (1)

2-2: ESLint suppressions are consistent and appropriate.

The addition of @typescript-eslint/no-require-imports suppressions across all SVG typing files is consistent with the ESLint plugin update. The CommonJS-style import pattern is standard for ambient module declarations.

If you prefer to avoid suppressions entirely, consider modernizing to ES6 imports:

-    // eslint-disable-next-line @typescript-eslint/no-require-imports
-    import React = require('react');
+    import type * as React from 'react';

This change would be optional and could be applied across all four SVG typing files for consistency.

apps/posts/src/views/PostAnalytics/Web/web.tsx (1)

26-27: Consider using a type alias or removing the empty interface.

The empty interface with an ESLint suppression is functionally equivalent to {}. As Biome suggests, a type alias would be more appropriate, or you could remove it entirely if no props are needed.

Option 1: Use a type alias (as Biome suggests)

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type postAnalyticsProps = Record<string, never>;

Option 2: Remove the interface entirely

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
-
-const Web: React.FC<postAnalyticsProps> = () => {
+const Web: React.FC = () => {
apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1)

17-18: Consider using a type alias or removing the empty interface.

The empty interface with an ESLint suppression is functionally equivalent to {}. As Biome suggests, a type alias would be more appropriate, or you could remove it entirely if no props are needed.

Option 1: Use a type alias (as Biome suggests)

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type postAnalyticsProps = Record<string, never>;

Option 2: Remove the interface entirely

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
-
-const Newsletter: React.FC<postAnalyticsProps> = () => {
+const Newsletter: React.FC = () => {
apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1)

16-17: Consider using a type alias or removing the empty interface.

The empty interface with an ESLint suppression is functionally equivalent to {}. As Biome suggests, a type alias would be more appropriate, or you could remove it entirely if no props are needed.

Option 1: Use a type alias (as Biome suggests)

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type postAnalyticsProps = Record<string, never>;

Option 2: Remove the interface entirely

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
-
-const Growth: React.FC<postAnalyticsProps> = () => {
+const Growth: React.FC = () => {
apps/activitypub/src/views/profile/profile.tsx (1)

13-14: Avoid empty interface + lint disable by using a stricter props type

Instead of an empty interface plus an ESLint-disable, consider a concrete “no-props” type, e.g.:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface ProfileProps {}
+type ProfileProps = Record<string, never>;

This removes the need for the disable comment, satisfies Biome’s no-empty-interface rule, and clearly expresses that Profile should not receive any props.

apps/admin-x-framework/src/api/config.ts (1)

6-7: ESLint suppression is valid; consider type alias as cleaner alternative.

The eslint-disable comment correctly addresses the new no-empty-object-type rule for this intentionally empty interface. However, since JSONArray is simply extending an array type without adding properties, a type alias would be more idiomatic and eliminate the need for the lint suppression.

Consider this refactor:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface JSONArray extends Array<string|number|boolean|Date|JSONObject|JSONValue> {}
+type JSONArray = Array<string|number|boolean|Date|JSONObject|JSONValue>;
apps/shade/src/components/layout/view-header.tsx (1)

32-32: Exporting ViewHeaderActions surfaces a props/runtime mismatch – consider forwarding attributes

ViewHeaderActionsProps extends React.HTMLAttributes<HTMLElement>, but ViewHeaderActions only destructures children and doesn’t forward className or other attributes to the <div>. Now that ViewHeaderActions is exported, callers can legally pass className, aria-*, etc., but they’ll be dropped at runtime.

Consider updating the component to forward className via cn(...) and spread the remaining props onto the div:

-const ViewHeaderActions:React.FC<ViewHeaderActionsProps> = ({children}) => {
-    return (
-        <div className='flex items-center gap-2'>
-            {children}
-        </div>
-    );
-};
+const ViewHeaderActions:React.FC<ViewHeaderActionsProps> = ({
+    children,
+    className,
+    ...props
+}) => {
+    return (
+        <div
+            {...props}
+            className={cn('flex items-center gap-2', className)}
+        >
+            {children}
+        </div>
+    );
+};

This keeps the type contract honest and follows the className + cn(...) forwarding guideline.

As per coding guidelines, ...

apps/shade/src/components/layout/header.tsx (1)

103-104: Clarify whether HeaderProps should be part of the public type surface

HeaderProps is a nice centralisation of the Header’s prop type (PropsWithChildrenAndClassName + VariantProps<typeof headerVariants>), but it’s currently file-internal (no export).

If you intend consumers to use this type directly, consider exporting it, for example:

-interface HeaderProps extends PropsWithChildrenAndClassName, VariantProps<typeof headerVariants> {}
+export interface HeaderProps extends PropsWithChildrenAndClassName, VariantProps<typeof headerVariants> {}

or

export type HeaderProps = React.ComponentProps<typeof Header>;

If it’s meant to stay internal, the current setup is fine as-is.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2500ab9 and 5895daa.

📒 Files selected for processing (53)
  • apps/activitypub/src/api/activitypub.ts (1 hunks)
  • apps/activitypub/src/components/modals/new-note-modal.tsx (1 hunks)
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts (3 hunks)
  • apps/activitypub/src/utils/get-username.ts (1 hunks)
  • apps/activitypub/src/utils/image.ts (1 hunks)
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1 hunks)
  • apps/activitypub/src/views/preferences/components/profile.tsx (1 hunks)
  • apps/activitypub/src/views/profile/profile.tsx (1 hunks)
  • apps/admin-x-design-system/src/global/sortable-list.tsx (1 hunks)
  • apps/admin-x-design-system/src/typings.d.ts (1 hunks)
  • apps/admin-x-design-system/src/utils/format-url.ts (1 hunks)
  • apps/admin-x-framework/src/api/config.ts (1 hunks)
  • apps/admin-x-framework/src/api/recommendations.ts (1 hunks)
  • apps/admin-x-framework/src/test/acceptance.ts (1 hunks)
  • apps/admin-x-framework/src/test/test-utils.tsx (1 hunks)
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts (2 hunks)
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (2 hunks)
  • apps/admin-x-settings/src/typings.d.ts (1 hunks)
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts (1 hunks)
  • apps/admin-x-settings/src/utils/url.ts (1 hunks)
  • apps/comments-ui/src/actions.ts (2 hunks)
  • apps/comments-ui/src/app-context.ts (1 hunks)
  • apps/comments-ui/src/components/content/forms/form.tsx (1 hunks)
  • apps/comments-ui/src/components/popup-box.tsx (1 hunks)
  • apps/comments-ui/src/typings.d.ts (1 hunks)
  • apps/comments-ui/src/utils/admin-api.ts (1 hunks)
  • apps/comments-ui/src/utils/api.ts (1 hunks)
  • apps/posts/src/hooks/use-post-success-modal.ts (1 hunks)
  • apps/posts/src/utils/link-helpers.ts (1 hunks)
  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Web/web.tsx (1 hunks)
  • apps/shade/src/components/features/color-picker/color-picker.tsx (1 hunks)
  • apps/shade/src/components/layout/header.tsx (7 hunks)
  • apps/shade/src/components/layout/heading.tsx (1 hunks)
  • apps/shade/src/components/layout/page.tsx (1 hunks)
  • apps/shade/src/components/layout/view-header.tsx (2 hunks)
  • apps/shade/src/components/ui/card.tsx (1 hunks)
  • apps/shade/src/lib/utils.ts (1 hunks)
  • apps/shade/src/typings.d.ts (1 hunks)
  • apps/signup-form/src/components/pages/form-page.tsx (1 hunks)
  • apps/signup-form/src/typings.d.ts (1 hunks)
  • apps/stats/src/hooks/use-feature-flag.tsx (1 hunks)
  • apps/stats/src/types/svg.d.ts (1 hunks)
  • apps/stats/src/utils/url-helpers.ts (5 hunks)
  • apps/stats/test/utils/tinybird-helpers.ts (1 hunks)
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2 hunks)
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/ui/card.tsx
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts
🚧 Files skipped from review as they are similar to previous changes (27)
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts
  • apps/activitypub/src/api/activitypub.ts
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/comments-ui/src/components/popup-box.tsx
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx
  • apps/admin-x-framework/src/test/test-utils.tsx
  • apps/admin-x-settings/src/utils/url.ts
  • apps/shade/src/lib/utils.ts
  • apps/stats/src/utils/url-helpers.ts
  • apps/admin-x-design-system/src/global/sortable-list.tsx
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx
  • apps/stats/test/utils/tinybird-helpers.ts
  • apps/posts/src/hooks/use-post-success-modal.ts
  • apps/admin-x-framework/src/api/recommendations.ts
  • apps/admin-x-settings/src/typings.d.ts
  • apps/admin-x-design-system/src/utils/format-url.ts
  • apps/shade/src/typings.d.ts
  • apps/comments-ui/src/utils/api.ts
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/comments-ui/src/app-context.ts
  • apps/activitypub/src/utils/get-username.ts
  • apps/activitypub/src/utils/image.ts
  • apps/admin-x-framework/src/test/acceptance.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx
🧰 Additional context used
📓 Path-based instructions (4)
apps/shade/src/components/layout/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in src/components/layout/*

Files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
apps/shade/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/components/**/*.{ts,tsx}: Use PascalCase for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Always forward and merge className prop with cn(...) utility function
Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration
Prefer compound subcomponents (e.g., Header.Title, Header.Meta, Header.Actions) for multi-region components instead of many props
Use Tailwind CSS scoped via .shade class; dark mode uses .dark

Files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
apps/shade/src/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/**/*.{ts,tsx,js}: Use camelCase for function and variable names
Use the @ alias for internal imports (e.g., @/lib/utils)

Files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
apps/shade/{src,test}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/{src,test}/**/*.{ts,tsx,js}: Run yarn lint after making changes to fix any ESLint errors and warnings before committing
Follow ESLint and tailwindcss/* plugin rules when writing styles

Files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
🧠 Learnings (28)
📓 Common learnings
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24673
File: ghost/i18n/lib/i18n.js:34-35
Timestamp: 2025-11-24T11:12:15.712Z
Learning: In the Ghost i18n package (ghost/i18n/lib/i18n.js), changing existing locale codes requires backwards compatibility handling for users who have already configured those locales. Such changes should be done in a separate PR with migration logic rather than included in feature PRs.
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Admin-x React apps build to `apps/*/dist` using Vite, which are then copied by `ghost/admin/lib/asset-delivery` to `ghost/core/core/built/admin/assets/*`
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.
Learnt from: niranjan-uma-shankar
Repo: TryGhost/Ghost PR: 24557
File: apps/admin-x-settings/src/components/settings/general/TimeZone.tsx:7-7
Timestamp: 2025-08-01T12:44:07.467Z
Learning: In Ghost development, PRs may depend on unpublished changes from SDK packages. When this occurs, TypeScript compilation errors for missing exports are expected and documented in the PR description until the dependency packages are published and updated. This is normal workflow for cross-repository feature development.
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use factory pattern for all test data creation instead of hard-coded data or direct database manipulation

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Test names should be lowercase and follow the format 'what is tested - expected outcome'

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Always follow ADRs in `../adr/` folder (ADR-0001: AAA pattern, ADR-0002: Page Objects)

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Follow the AAA (Arrange, Act, Assert) pattern in test structure

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use `getByTestId()` only when semantic locators are unavailable, and suggest adding `data-testid` to Ghost codebase when needed

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Manual login should not be used - authentication is automatic via fixture

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/components/modals/new-note-modal.tsx
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/components/modals/new-note-modal.tsx
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-08-11T19:39:00.428Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.

Applied to files:

  • apps/activitypub/src/views/preferences/components/profile.tsx
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.{ts,tsx} : Prefer less comments and give things clear names

Applied to files:

  • apps/comments-ui/src/actions.ts
  • apps/signup-form/src/typings.d.ts
  • apps/activitypub/src/views/profile/profile.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/layout/**/*.{ts,tsx} : Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in `src/components/layout/*`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/index.ts : Place new UI components under `src/components/ui` and export them from `src/index.ts`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use Tailwind CSS scoped via `.shade` class; dark mode uses `.dark`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/features/**/*.{ts,tsx} : Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in `src/components/features/*`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-25T11:58:51.652Z
Learnt from: ibalosh
Repo: TryGhost/Ghost PR: 25525
File: apps/shade/src/shade-app.tsx:4-4
Timestamp: 2025-11-25T11:58:51.652Z
Learning: In apps/shade, the app wrapper file should be named `src/shade-app.tsx` (kebab-case) while the component itself is exported as `ShadeApp` (PascalCase). Context providers should be placed in `src/providers/*` using kebab-case filenames.

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/ui/**/*.{ts,tsx} : Atomic UI components should be placed in `src/components/ui/*` and each component must have a corresponding `*.stories.tsx` file next to it for Storybook documentation

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/lib/utils.ts : Shared utilities (class merging, formatting, chart helpers) should be centralized in `src/lib/utils.ts`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Always forward and merge `className` prop with `cn(...)` utility function

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-12-09T12:37:23.267Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25501
File: apps/shade/src/hooks/use-mobile.tsx:5-10
Timestamp: 2025-12-09T12:37:23.267Z
Learning: In the Ghost repository, this guideline applies to the shade admin app. For files under apps/shade that access browser globals (navigator, window, document) at module load time, SSR is not used, so typeof guards are not required. Reviewers should verify that such files remain client-side only and that no SSR context is introduced; apply this understanding to similarly structured files under apps/shade.

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in Ghost's ActivityPub module are thoroughly tested in `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`, which covers `generatePendingActivityId`, `isPendingActivity`, and `generatePendingActivity` functions.

Applied to files:

  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in the Ghost ActivityPub module are covered by tests in the file `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`.

Applied to files:

  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-11-05T16:49:21.124Z
Learnt from: rob-ghost
Repo: TryGhost/Ghost PR: 25356
File: apps/admin/src/hooks/user-preferences.ts:20-20
Timestamp: 2025-11-05T16:49:21.124Z
Learning: In apps/admin/src/hooks/user-preferences.ts, the query key for user preferences intentionally includes `user?.accessibility` (not just `user?.id`) so the cache automatically reacts to changes from any source (useEditUserPreferences, other editUser calls, external updates). Combined with `cacheTime: 0`, this design garbage-collects orphaned entries while keeping the active entry cached. This is a documented, intentional architectural decision.

Applied to files:

  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)

Applied to files:

  • apps/admin-x-design-system/src/typings.d.ts
📚 Learning: 2025-07-14T12:20:35.268Z
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24346
File: apps/admin-x-settings/src/components/settings/growth/Network.tsx:8-12
Timestamp: 2025-07-14T12:20:35.268Z
Learning: The Network component toggle in `apps/admin-x-settings/src/components/settings/growth/Network.tsx` is intentionally implemented as static UI with a no-op onChange handler, as part of a UI-first development approach before connecting actual ActivityPub functionality.

Applied to files:

  • apps/activitypub/src/views/profile/profile.tsx
🪛 Biome (2.1.2)
apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx

[error] 17-18: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Web/web.tsx

[error] 26-27: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/activitypub/src/views/profile/profile.tsx

[error] 13-14: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Growth/growth.tsx

[error] 16-17: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

🔇 Additional comments (16)
apps/posts/src/utils/link-helpers.ts (1)

30-30: LGTM! Clean linting fix.

The bare catch syntax correctly removes the unused error binding while preserving the fallback behavior.

apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1)

20-27: Bare catch is appropriate; behavior remains unchanged

The error value was unused and the intent is clearly to ignore invalid URLs, so switching to a bare catch is idiomatic and keeps the existing control flow intact.

apps/comments-ui/src/components/content/forms/form.tsx (1)

78-78: LGTM! Valid refactor aligning with ESLint rules.

The change to a bare catch block is correct since the error variable wasn't being used. This is a clean refactor enforced by the updated ESLint plugin.

apps/admin-x-settings/src/utils/link-to-github-releases.ts (1)

32-32: LGTM! Bare catch is appropriate here.

The change to bare catch syntax is correct since the error parameter isn't used. This aligns with the updated ESLint rules and produces cleaner code.

apps/comments-ui/src/utils/admin-api.ts (1)

15-20: Bare catch here is safe and keeps behavior unchanged

catch { is equivalent to the previous catch (err) because the error value wasn’t used. JSON parse failures still cause an early return, and there’s no change to control flow or observable behavior. This fits the lint-driven cleanup in this PR.

apps/admin-x-framework/test/unit/hooks/use-form.test.ts (1)

186-212: Bare catch usage in tests keeps behavior unchanged

Both error-handling tests still behave the same; the caught error value was never used, so switching to catch {} is fine and keeps intent clear.

Also applies to: 214-238

apps/signup-form/src/components/pages/form-page.tsx (1)

23-39: Submit error handler change is a no-op behaviorally

Swapping catch (_: any) for catch {} is safe here—the error value wasn’t used, and the user-facing error + loading reset logic is unchanged.

apps/activitypub/src/hooks/use-activity-pub-queries.ts (3)

360-403: Notifications “liked” cache: bare catch preserves existing fallback

The change to catch {} simply drops an unused parameter; on any exception you still return current, so runtime behavior is identical.


406-449: Notifications “repost” cache: no functional change from catch refactor

Same pattern here—the error value wasn’t used, and you still fall back to current on any failure, so cache behavior is unchanged.


452-495: Notifications reply-count cache: behaviorally equivalent catch

Again, moving to catch {} keeps the same “return current on error” behavior with no change to observable results.

apps/activitypub/src/components/modals/new-note-modal.tsx (1)

68-105: handlePost catch cleanup is safe

The caught error was unused; switching to catch {} keeps the same control flow (including onReplyError?.() and the finally-driven setIsPosting(false)).

apps/comments-ui/src/actions.ts (1)

257-276: like/unlike optimistic updates: catch refactor keeps rollback behavior

Using catch {} instead of catch (err) is fine here; errors are still swallowed and the optimistic updateCommentLikeState is reverted on failure as before.

apps/stats/src/hooks/use-feature-flag.tsx (1)

17-25: Labs JSON parsing: bare catch is fine

Dropping the unused error parameter in the parse try/catch has no behavioral impact; you still safely fall back to an empty labs object on invalid JSON.

apps/activitypub/src/views/preferences/components/profile.tsx (1)

258-315: Profile image copy: nested bare catches keep UX behavior

Both inner and outer catch blocks only showed a toast and reset isProcessing; removing the unused error binding doesn’t alter success/failure behavior or state cleanup.

apps/shade/src/components/layout/view-header.tsx (1)

4-5: Localized no-empty-object-type disable for named props alias looks fine

This interface is acting as a named alias for React.HTMLAttributes<HTMLElement> rather than introducing new fields, so the per-interface eslint-disable is a reasonable way to keep the public props name without fighting the rule. No changes needed here.

apps/shade/src/components/layout/header.tsx (1)

11-12: Scoped no-empty-object-type disables for Header subcomponent props are appropriate

Each of these interfaces is a named wrapper around PropsWithChildrenAndClassName used for the Header.* compound subcomponents, so they intentionally don’t add new fields. Using per-interface @typescript-eslint/no-empty-object-type disables keeps the lint rule active for other types while preserving clear, named props for the public Header.* API.

Based on learnings, this aligns with the preferred compound subcomponent pattern for Header.*.

Also applies to: 24-25, 40-41, 53-54, 66-67, 79-80

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: 0

🧹 Nitpick comments (4)
apps/posts/src/views/PostAnalytics/Web/web.tsx (1)

26-27: Consider using a type alias instead of suppressing the lint rule.

The empty interface could be replaced with a type alias to align with TypeScript best practices and avoid the need for the ESLint suppression.

Apply this diff:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type postAnalyticsProps = Record<string, never>;

Alternatively, if props are planned for the future, consider adding a comment to document that intent.

apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1)

16-17: Consider using a type alias instead of suppressing the lint rule.

The empty interface could be replaced with a type alias to align with TypeScript best practices and avoid the need for the ESLint suppression.

Apply this diff:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type postAnalyticsProps = Record<string, never>;

Alternatively, if props are planned for the future, consider adding a comment to document that intent.

apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1)

17-18: Consider using a type alias instead of suppressing the lint rule.

The empty interface could be replaced with a type alias to align with TypeScript best practices and avoid the need for the ESLint suppression.

Apply this diff:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type postAnalyticsProps = Record<string, never>;

Alternatively, if props are planned for the future, consider adding a comment to document that intent.

apps/activitypub/src/views/profile/profile.tsx (1)

13-14: Consider simplifying the empty interface.

The ESLint disable comment works, but empty interfaces are often unnecessary. Since this interface has no properties, you could:

  1. Remove the interface entirely and use React.FC without type parameters, or
  2. Use type ProfileProps = Record<string, never> for explicit empty typing

However, if you're planning to add props in the near future, the current approach is acceptable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5895daa and 495218a.

📒 Files selected for processing (53)
  • apps/activitypub/src/api/activitypub.ts (1 hunks)
  • apps/activitypub/src/components/modals/new-note-modal.tsx (1 hunks)
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts (3 hunks)
  • apps/activitypub/src/utils/get-username.ts (1 hunks)
  • apps/activitypub/src/utils/image.ts (1 hunks)
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1 hunks)
  • apps/activitypub/src/views/preferences/components/profile.tsx (1 hunks)
  • apps/activitypub/src/views/profile/profile.tsx (1 hunks)
  • apps/admin-x-design-system/src/global/sortable-list.tsx (1 hunks)
  • apps/admin-x-design-system/src/typings.d.ts (1 hunks)
  • apps/admin-x-design-system/src/utils/format-url.ts (1 hunks)
  • apps/admin-x-framework/src/api/config.ts (1 hunks)
  • apps/admin-x-framework/src/api/recommendations.ts (1 hunks)
  • apps/admin-x-framework/src/test/acceptance.ts (1 hunks)
  • apps/admin-x-framework/src/test/test-utils.tsx (1 hunks)
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts (2 hunks)
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (2 hunks)
  • apps/admin-x-settings/src/typings.d.ts (1 hunks)
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts (1 hunks)
  • apps/admin-x-settings/src/utils/url.ts (1 hunks)
  • apps/comments-ui/src/actions.ts (2 hunks)
  • apps/comments-ui/src/app-context.ts (1 hunks)
  • apps/comments-ui/src/components/content/forms/form.tsx (1 hunks)
  • apps/comments-ui/src/components/popup-box.tsx (1 hunks)
  • apps/comments-ui/src/typings.d.ts (1 hunks)
  • apps/comments-ui/src/utils/admin-api.ts (1 hunks)
  • apps/comments-ui/src/utils/api.ts (1 hunks)
  • apps/posts/src/hooks/use-post-success-modal.ts (1 hunks)
  • apps/posts/src/utils/link-helpers.ts (1 hunks)
  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Web/web.tsx (1 hunks)
  • apps/shade/src/components/features/color-picker/color-picker.tsx (1 hunks)
  • apps/shade/src/components/layout/header.tsx (7 hunks)
  • apps/shade/src/components/layout/heading.tsx (1 hunks)
  • apps/shade/src/components/layout/page.tsx (1 hunks)
  • apps/shade/src/components/layout/view-header.tsx (2 hunks)
  • apps/shade/src/components/ui/card.tsx (1 hunks)
  • apps/shade/src/lib/utils.ts (1 hunks)
  • apps/shade/src/typings.d.ts (1 hunks)
  • apps/signup-form/src/components/pages/form-page.tsx (1 hunks)
  • apps/signup-form/src/typings.d.ts (1 hunks)
  • apps/stats/src/hooks/use-feature-flag.tsx (1 hunks)
  • apps/stats/src/types/svg.d.ts (1 hunks)
  • apps/stats/src/utils/url-helpers.ts (5 hunks)
  • apps/stats/test/utils/tinybird-helpers.ts (1 hunks)
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2 hunks)
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • apps/admin-x-settings/src/typings.d.ts
  • apps/comments-ui/src/utils/api.ts
  • apps/comments-ui/src/typings.d.ts
🚧 Files skipped from review as they are similar to previous changes (30)
  • apps/stats/src/hooks/use-feature-flag.tsx
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx
  • apps/shade/src/components/ui/card.tsx
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts
  • apps/posts/src/hooks/use-post-success-modal.ts
  • apps/admin-x-framework/src/test/acceptance.ts
  • apps/admin-x-design-system/src/global/sortable-list.tsx
  • apps/signup-form/src/components/pages/form-page.tsx
  • apps/activitypub/src/utils/get-username.ts
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx
  • apps/stats/test/utils/tinybird-helpers.ts
  • apps/admin-x-framework/src/api/config.ts
  • apps/shade/src/components/layout/header.tsx
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx
  • apps/shade/src/components/layout/page.tsx
  • apps/admin-x-design-system/src/utils/format-url.ts
  • apps/comments-ui/src/actions.ts
  • apps/shade/src/components/layout/heading.tsx
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx
  • apps/stats/src/utils/url-helpers.ts
  • apps/posts/src/utils/link-helpers.ts
  • apps/comments-ui/src/utils/admin-api.ts
  • apps/signup-form/src/typings.d.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx
  • apps/admin-x-framework/src/test/test-utils.tsx
  • apps/activitypub/src/components/modals/new-note-modal.tsx
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts
🧰 Additional context used
📓 Path-based instructions (6)
apps/shade/src/lib/utils.ts

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Shared utilities (class merging, formatting, chart helpers) should be centralized in src/lib/utils.ts

Files:

  • apps/shade/src/lib/utils.ts
apps/shade/src/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/**/*.{ts,tsx,js}: Use camelCase for function and variable names
Use the @ alias for internal imports (e.g., @/lib/utils)

Files:

  • apps/shade/src/lib/utils.ts
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/typings.d.ts
apps/shade/{src,test}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/{src,test}/**/*.{ts,tsx,js}: Run yarn lint after making changes to fix any ESLint errors and warnings before committing
Follow ESLint and tailwindcss/* plugin rules when writing styles

Files:

  • apps/shade/src/lib/utils.ts
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/typings.d.ts
apps/shade/src/components/features/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in src/components/features/*

Files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
apps/shade/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/components/**/*.{ts,tsx}: Use PascalCase for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Always forward and merge className prop with cn(...) utility function
Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration
Prefer compound subcomponents (e.g., Header.Title, Header.Meta, Header.Actions) for multi-region components instead of many props
Use Tailwind CSS scoped via .shade class; dark mode uses .dark

Files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/view-header.tsx
apps/shade/src/components/layout/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in src/components/layout/*

Files:

  • apps/shade/src/components/layout/view-header.tsx
🧠 Learnings (25)
📓 Common learnings
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24673
File: ghost/i18n/lib/i18n.js:34-35
Timestamp: 2025-11-24T11:12:15.712Z
Learning: In the Ghost i18n package (ghost/i18n/lib/i18n.js), changing existing locale codes requires backwards compatibility handling for users who have already configured those locales. Such changes should be done in a separate PR with migration logic rather than included in feature PRs.
Learnt from: niranjan-uma-shankar
Repo: TryGhost/Ghost PR: 24557
File: apps/admin-x-settings/src/components/settings/general/TimeZone.tsx:7-7
Timestamp: 2025-08-01T12:44:07.467Z
Learning: In Ghost development, PRs may depend on unpublished changes from SDK packages. When this occurs, TypeScript compilation errors for missing exports are expected and documented in the PR description until the dependency packages are published and updated. This is normal workflow for cross-repository feature development.
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Admin-x React apps build to `apps/*/dist` using Vite, which are then copied by `ghost/admin/lib/asset-delivery` to `ghost/core/core/built/admin/assets/*`
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/lib/utils.ts : Shared utilities (class merging, formatting, chart helpers) should be centralized in `src/lib/utils.ts`

Applied to files:

  • apps/shade/src/lib/utils.ts
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Test names should be lowercase and follow the format 'what is tested - expected outcome'

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use factory pattern for all test data creation instead of hard-coded data or direct database manipulation

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Follow the AAA (Arrange, Act, Assert) pattern in test structure

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Always follow ADRs in `../adr/` folder (ADR-0001: AAA pattern, ADR-0002: Page Objects)

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Manual login should not be used - authentication is automatic via fixture

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use `getByTestId()` only when semantic locators are unavailable, and suggest adding `data-testid` to Ghost codebase when needed

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Prefer semantic locators (`getByRole`, `getByLabel`, `getByText`) over test IDs and never use CSS selectors, XPath, nth-child, or class names

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/comments-ui/src/components/popup-box.tsx
  • apps/comments-ui/src/app-context.ts
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-09-10T07:05:34.606Z
Learnt from: jonatansberg
Repo: TryGhost/Ghost PR: 24838
File: apps/shade/src/components/features/color-picker/color-picker.tsx:0-0
Timestamp: 2025-09-10T07:05:34.606Z
Learning: The Color library used in the color picker component handles input validation and clamping automatically, so manual validation of HSL and RGB values is not necessary when using Color constructors.

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
📚 Learning: 2025-12-09T12:37:23.267Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25501
File: apps/shade/src/hooks/use-mobile.tsx:5-10
Timestamp: 2025-12-09T12:37:23.267Z
Learning: In the Ghost repository, this guideline applies to the shade admin app. For files under apps/shade that access browser globals (navigator, window, document) at module load time, SSR is not used, so typeof guards are not required. Reviewers should verify that such files remain client-side only and that no SSR context is introduced; apply this understanding to similarly structured files under apps/shade.

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props

Applied to files:

  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/layout/**/*.{ts,tsx} : Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in `src/components/layout/*`

Applied to files:

  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.{ts,tsx} : Prefer less comments and give things clear names

Applied to files:

  • apps/shade/src/components/layout/view-header.tsx
  • apps/comments-ui/src/app-context.ts
  • apps/admin-x-framework/src/api/recommendations.ts
  • apps/activitypub/src/views/profile/profile.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/index.ts : Place new UI components under `src/components/ui` and export them from `src/index.ts`

Applied to files:

  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/views/preferences/components/profile.tsx
  • apps/activitypub/src/api/activitypub.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/views/preferences/components/profile.tsx
  • apps/activitypub/src/api/activitypub.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)

Applied to files:

  • apps/admin-x-design-system/src/typings.d.ts
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use Tailwind CSS scoped via `.shade` class; dark mode uses `.dark`

Applied to files:

  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case

Applied to files:

  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles

Applied to files:

  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-25T11:58:51.652Z
Learnt from: ibalosh
Repo: TryGhost/Ghost PR: 25525
File: apps/shade/src/shade-app.tsx:4-4
Timestamp: 2025-11-25T11:58:51.652Z
Learning: In apps/shade, the app wrapper file should be named `src/shade-app.tsx` (kebab-case) while the component itself is exported as `ShadeApp` (PascalCase). Context providers should be placed in `src/providers/*` using kebab-case filenames.

Applied to files:

  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Run `yarn lint` after making changes to fix any ESLint errors and warnings before committing

Applied to files:

  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/ui/**/*.{ts,tsx} : Atomic UI components should be placed in `src/components/ui/*` and each component must have a corresponding `*.stories.tsx` file next to it for Storybook documentation

Applied to files:

  • apps/shade/src/typings.d.ts
🪛 Biome (2.1.2)
apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx

[error] 17-18: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/activitypub/src/views/profile/profile.tsx

[error] 13-14: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Growth/growth.tsx

[error] 16-17: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Web/web.tsx

[error] 26-27: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

🔇 Additional comments (18)
apps/admin-x-settings/src/utils/url.ts (1)

27-32: LGTM!

Clean refactor removing the unused error parameter. The bare catch syntax is idiomatic when the error object isn't needed.

apps/comments-ui/src/components/popup-box.tsx (1)

6-8: ESLint rule rename looks correct

Switching the disable to @typescript-eslint/no-empty-object-type matches the newer rule name and keeps the existing empty Props pattern intact; no behavior change here and it aligns with the rest of the PR.

apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1)

19-31: Bare catch in validateUrl keeps behavior unchanged and removes an unused binding

Switching from catch (e) to a bare catch is correct here: the error object wasn’t used, and all exceptions from new URL(url) are still handled by setting errors.url. This aligns with linting expectations without altering validation behavior.

apps/admin-x-design-system/src/typings.d.ts (1)

2-2: LGTM! Correct suppression for ambient module declaration.

The no-require-imports rule must be disabled here because TypeScript's ambient module declarations require the import = require() syntax. This is the appropriate fix after updating the ESLint plugin.

apps/stats/src/types/svg.d.ts (1)

2-2: LGTM! Consistent with other SVG module declarations.

This matches the pattern applied across the codebase for handling the updated ESLint rules. The suppression is necessary for ambient module syntax.

apps/shade/src/typings.d.ts (1)

2-2: LGTM! Correct ESLint suppression.

The lint suppression is properly applied. As per coding guidelines, ensure yarn lint has been run to verify all ESLint rules are satisfied.

apps/activitypub/src/views/preferences/components/profile.tsx (2)

62-79: LGTM: Unused error parameter removed.

The removal of the unused error parameter from the catch block aligns with the ESLint plugin update. The error handling behavior (toast notification and state reset) remains unchanged.


258-315: LGTM: Unused error parameters removed from nested catch blocks.

Both the inner (line 307) and outer (line 311) catch blocks have had their unused error parameters removed, consistent with the ESLint plugin update. The error handling logic (toast notifications and processing state management) is preserved.

apps/comments-ui/src/components/content/forms/form.tsx (1)

78-78: LGTM!

The change from catch (e) to bare catch is safe and follows modern JavaScript best practices. Since the error variable was unused, removing the binding is appropriate and aligns with the updated ESLint rules.

apps/comments-ui/src/app-context.ts (1)

118-118: LGTM!

The change to bare catch is appropriate since the error is not used. The fallback behavior (returning an empty object) is preserved.

apps/admin-x-framework/test/unit/hooks/use-form.test.ts (1)

206-208: LGTM!

The bare catch {} syntax correctly handles expected exceptions in these tests. The change removes unused error bindings while preserving test behavior.

Also applies to: 233-235

apps/shade/src/lib/utils.ts (1)

101-101: LGTM!

The bare catch is appropriate here since the error is not used. The fallback behavior (returning the original URL when parsing fails) is preserved.

apps/activitypub/src/api/activitypub.ts (1)

257-257: LGTM!

The bare catch is appropriate since the error is currently unused. The fallback behavior (returning null) is preserved. Note that the TODO comment on line 258 suggests error tracking might be added in the future.

apps/activitypub/src/utils/image.ts (1)

25-25: LGTM!

The bare catch is appropriate since the error is not used. The fallback behavior (returning the original URL when conversion fails) is preserved, which is a sensible approach for handling CORS or fetch failures gracefully.

apps/shade/src/components/features/color-picker/color-picker.tsx (1)

388-388: LGTM!

The bare catch is appropriate here since invalid color inputs are intentionally ignored (as noted in the comment). The error binding was unnecessary for this use case.

apps/shade/src/components/layout/view-header.tsx (2)

4-5: Lint suppression around empty props interface is appropriate

Disabling @typescript-eslint/no-empty-object-type here keeps the existing ViewHeaderActionsProps shape without altering runtime behavior and is consistent with the pattern used across shade layout components.


32-32: Named export of ViewHeader components looks good

Exporting ViewHeader and ViewHeaderActions together matches the compound subcomponent pattern we use for layout pieces and keeps them discoverable from a single module. Based on learnings, this aligns with the shade layout guidelines.

apps/admin-x-framework/src/api/recommendations.ts (1)

25-29: ESLint suppressions on empty response interfaces are fine

The targeted @typescript-eslint/no-empty-object-type disables preserve the existing public response types for edit/delete mutations without changing any runtime behavior, which is appropriate for this ESLint-focused PR.

@ibalosh ibalosh force-pushed the update-eslint-version branch 2 times, most recently from c0f581d to b48e7c2 Compare December 15, 2025 14:00
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

🧹 Nitpick comments (4)
apps/activitypub/src/views/profile/profile.tsx (1)

13-14: Consider removing the empty interface or using an explicit no-props type.

The ESLint disable comment masks a valid warning. Since Profile receives no props, you have cleaner alternatives:

Option 1 (recommended): Remove the interface entirely

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface ProfileProps {}
-
-const Profile: React.FC<ProfileProps> = ({}) => {
+const Profile: React.FC = () => {

Option 2: Use an explicit no-props type if you want to document the intent

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface ProfileProps {}
+type ProfileProps = Record<string, never>;

Based on learnings, run yarn lint after making changes to fix any ESLint errors and warnings before committing.

apps/posts/src/views/PostAnalytics/Web/web.tsx (1)

26-27: Empty postAnalyticsProps interface – consider a type alias to satisfy linters without disables

This placeholder props type is fine functionally, but Biome flags the empty interface and you’re also having to suppress @typescript-eslint/no-empty-object-type. To keep the named props type while avoiding both lint issues, you could switch to a non-empty alias and drop the disable:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type postAnalyticsProps = Record<string, never>;

This keeps React.FC<postAnalyticsProps> intact, removes the need for the eslint-disable, and should make Biome happy as well.

apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1)

17-18: Align postAnalyticsProps with type-alias pattern to avoid empty-interface lint noise

Same as in web.tsx: the empty interface is harmless but trips Biome and requires an ESLint suppression. If you want to clean this up and stay consistent across analytics views, you could switch to a non-empty alias:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type postAnalyticsProps = Record<string, never>;

This keeps the component signature (React.FC<postAnalyticsProps>) while removing the eslint-disable and addressing Biome’s noEmptyInterface error.

apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1)

16-17: Optional: replace empty postAnalyticsProps interface with an alias (and drop the disable)

To keep the placeholder props type but avoid both the ESLint suppression and Biome’s noEmptyInterface error, you can mirror the pattern suggested for the other analytics views:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type postAnalyticsProps = Record<string, never>;

Behavior stays the same, but linting becomes cleaner and consistent across Growth/Web/Newsletter.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 495218a and b48e7c2.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (55)
  • apps/activitypub/src/api/activitypub.ts (1 hunks)
  • apps/activitypub/src/components/modals/new-note-modal.tsx (1 hunks)
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts (3 hunks)
  • apps/activitypub/src/utils/get-username.ts (1 hunks)
  • apps/activitypub/src/utils/image.ts (1 hunks)
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1 hunks)
  • apps/activitypub/src/views/preferences/components/profile.tsx (1 hunks)
  • apps/activitypub/src/views/profile/profile.tsx (1 hunks)
  • apps/admin-x-design-system/src/global/sortable-list.tsx (1 hunks)
  • apps/admin-x-design-system/src/typings.d.ts (1 hunks)
  • apps/admin-x-design-system/src/utils/format-url.ts (1 hunks)
  • apps/admin-x-framework/src/api/config.ts (1 hunks)
  • apps/admin-x-framework/src/api/recommendations.ts (1 hunks)
  • apps/admin-x-framework/src/test/acceptance.ts (1 hunks)
  • apps/admin-x-framework/src/test/test-utils.tsx (1 hunks)
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts (2 hunks)
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (2 hunks)
  • apps/admin-x-settings/src/typings.d.ts (1 hunks)
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts (2 hunks)
  • apps/admin-x-settings/src/utils/url.ts (1 hunks)
  • apps/admin/eslint.config.js (0 hunks)
  • apps/comments-ui/src/actions.ts (2 hunks)
  • apps/comments-ui/src/app-context.ts (1 hunks)
  • apps/comments-ui/src/components/content/forms/form.tsx (1 hunks)
  • apps/comments-ui/src/components/popup-box.tsx (1 hunks)
  • apps/comments-ui/src/typings.d.ts (1 hunks)
  • apps/comments-ui/src/utils/admin-api.ts (1 hunks)
  • apps/comments-ui/src/utils/api.ts (1 hunks)
  • apps/posts/src/hooks/use-post-success-modal.ts (1 hunks)
  • apps/posts/src/utils/link-helpers.ts (1 hunks)
  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Web/web.tsx (1 hunks)
  • apps/shade/src/components/features/color-picker/color-picker.tsx (1 hunks)
  • apps/shade/src/components/layout/header.tsx (7 hunks)
  • apps/shade/src/components/layout/heading.tsx (1 hunks)
  • apps/shade/src/components/layout/page.tsx (1 hunks)
  • apps/shade/src/components/layout/view-header.tsx (2 hunks)
  • apps/shade/src/components/ui/card.tsx (1 hunks)
  • apps/shade/src/lib/utils.ts (1 hunks)
  • apps/shade/src/typings.d.ts (1 hunks)
  • apps/signup-form/src/components/pages/form-page.tsx (1 hunks)
  • apps/signup-form/src/typings.d.ts (1 hunks)
  • apps/stats/src/hooks/use-feature-flag.tsx (1 hunks)
  • apps/stats/src/types/svg.d.ts (1 hunks)
  • apps/stats/src/utils/url-helpers.ts (5 hunks)
  • apps/stats/test/utils/tinybird-helpers.ts (1 hunks)
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2 hunks)
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1 hunks)
  • package.json (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/admin/eslint.config.js
✅ Files skipped from review due to trivial changes (1)
  • apps/admin-x-design-system/src/global/sortable-list.tsx
🚧 Files skipped from review as they are similar to previous changes (30)
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts
  • apps/activitypub/src/components/modals/new-note-modal.tsx
  • apps/admin-x-design-system/src/utils/format-url.ts
  • apps/activitypub/src/utils/image.ts
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx
  • apps/admin-x-framework/src/test/acceptance.ts
  • apps/activitypub/src/api/activitypub.ts
  • apps/activitypub/src/utils/get-username.ts
  • apps/signup-form/src/typings.d.ts
  • apps/comments-ui/src/components/content/forms/form.tsx
  • apps/stats/test/utils/tinybird-helpers.ts
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx
  • apps/comments-ui/src/utils/api.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/admin-x-framework/src/test/test-utils.tsx
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts
  • apps/comments-ui/src/actions.ts
  • apps/admin-x-settings/src/typings.d.ts
  • apps/stats/src/hooks/use-feature-flag.tsx
  • apps/admin-x-framework/src/api/recommendations.ts
  • apps/admin-x-design-system/src/typings.d.ts
  • package.json
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx
🧰 Additional context used
📓 Path-based instructions (6)
apps/shade/src/components/layout/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in src/components/layout/*

Files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/page.tsx
apps/shade/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/components/**/*.{ts,tsx}: Use PascalCase for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Always forward and merge className prop with cn(...) utility function
Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration
Prefer compound subcomponents (e.g., Header.Title, Header.Meta, Header.Actions) for multi-region components instead of many props
Use Tailwind CSS scoped via .shade class; dark mode uses .dark

Files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/ui/card.tsx
apps/shade/src/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/**/*.{ts,tsx,js}: Use camelCase for function and variable names
Use the @ alias for internal imports (e.g., @/lib/utils)

Files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/lib/utils.ts
  • apps/shade/src/components/ui/card.tsx
apps/shade/{src,test}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/{src,test}/**/*.{ts,tsx,js}: Run yarn lint after making changes to fix any ESLint errors and warnings before committing
Follow ESLint and tailwindcss/* plugin rules when writing styles

Files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/lib/utils.ts
  • apps/shade/src/components/ui/card.tsx
apps/shade/src/lib/utils.ts

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Shared utilities (class merging, formatting, chart helpers) should be centralized in src/lib/utils.ts

Files:

  • apps/shade/src/lib/utils.ts
apps/shade/src/components/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Atomic UI components should be placed in src/components/ui/* and each component must have a corresponding *.stories.tsx file next to it for Storybook documentation

Files:

  • apps/shade/src/components/ui/card.tsx
🧠 Learnings (28)
📓 Common learnings
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Run `yarn lint` after making changes to fix any ESLint errors and warnings before committing
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24673
File: ghost/i18n/lib/i18n.js:34-35
Timestamp: 2025-11-24T11:12:15.712Z
Learning: In the Ghost i18n package (ghost/i18n/lib/i18n.js), changing existing locale codes requires backwards compatibility handling for users who have already configured those locales. Such changes should be done in a separate PR with migration logic rather than included in feature PRs.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles
Learnt from: niranjan-uma-shankar
Repo: TryGhost/Ghost PR: 24557
File: apps/admin-x-settings/src/components/settings/general/TimeZone.tsx:7-7
Timestamp: 2025-08-01T12:44:07.467Z
Learning: In Ghost development, PRs may depend on unpublished changes from SDK packages. When this occurs, TypeScript compilation errors for missing exports are expected and documented in the PR description until the dependency packages are published and updated. This is normal workflow for cross-repository feature development.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Applies to ghost/i18n/locales/en/ghost.json : Add UI translations to `ghost/i18n/locales/en/ghost.json` for Admin UI features
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use factory pattern for all test data creation instead of hard-coded data or direct database manipulation

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Test names should be lowercase and follow the format 'what is tested - expected outcome'

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Follow the AAA (Arrange, Act, Assert) pattern in test structure

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Always follow ADRs in `../adr/` folder (ADR-0001: AAA pattern, ADR-0002: Page Objects)

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use `getByTestId()` only when semantic locators are unavailable, and suggest adding `data-testid` to Ghost codebase when needed

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/views/preferences/components/profile.tsx
  • apps/posts/src/hooks/use-post-success-modal.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/views/preferences/components/profile.tsx
  • apps/posts/src/hooks/use-post-success-modal.ts
📚 Learning: 2025-08-11T19:39:00.428Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.

Applied to files:

  • apps/posts/src/utils/link-helpers.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.{ts,tsx} : Prefer less comments and give things clear names

Applied to files:

  • apps/comments-ui/src/app-context.ts
  • apps/comments-ui/src/typings.d.ts
  • apps/comments-ui/src/components/popup-box.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration

Applied to files:

  • apps/comments-ui/src/app-context.ts
  • apps/comments-ui/src/typings.d.ts
  • apps/shade/src/components/layout/header.tsx
  • apps/comments-ui/src/components/popup-box.tsx
  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/ui/card.tsx
📚 Learning: 2025-04-23T15:44:52.549Z
Learnt from: 9larsons
Repo: TryGhost/Ghost PR: 21866
File: ghost/core/core/server/web/admin/middleware/serve-auth-frame-file.js:10-19
Timestamp: 2025-04-23T15:44:52.549Z
Learning: The existing implementation in `ghost/core/core/server/web/admin/middleware/serve-auth-frame-file.js` using `path.parse(req.url).base` is secure against path traversal attacks as it properly extracts only the filename component without any directory parts.

Applied to files:

  • apps/stats/src/utils/url-helpers.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)

Applied to files:

  • apps/comments-ui/src/typings.d.ts
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/index.ts : Place new UI components under `src/components/ui` and export them from `src/index.ts`

Applied to files:

  • apps/comments-ui/src/typings.d.ts
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/ui/card.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles

Applied to files:

  • apps/comments-ui/src/typings.d.ts
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Run `yarn lint` after making changes to fix any ESLint errors and warnings before committing

Applied to files:

  • apps/comments-ui/src/typings.d.ts
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/layout/**/*.{ts,tsx} : Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in `src/components/layout/*`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use Tailwind CSS scoped via `.shade` class; dark mode uses `.dark`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/features/**/*.{ts,tsx} : Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in `src/components/features/*`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-25T11:58:51.652Z
Learnt from: ibalosh
Repo: TryGhost/Ghost PR: 25525
File: apps/shade/src/shade-app.tsx:4-4
Timestamp: 2025-11-25T11:58:51.652Z
Learning: In apps/shade, the app wrapper file should be named `src/shade-app.tsx` (kebab-case) while the component itself is exported as `ShadeApp` (PascalCase). Context providers should be placed in `src/providers/*` using kebab-case filenames.

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/ui/**/*.{ts,tsx} : Atomic UI components should be placed in `src/components/ui/*` and each component must have a corresponding `*.stories.tsx` file next to it for Storybook documentation

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/lib/utils.ts : Shared utilities (class merging, formatting, chart helpers) should be centralized in `src/lib/utils.ts`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
  • apps/shade/src/lib/utils.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Always forward and merge `className` prop with `cn(...)` utility function

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-12-09T12:37:23.267Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25501
File: apps/shade/src/hooks/use-mobile.tsx:5-10
Timestamp: 2025-12-09T12:37:23.267Z
Learning: In the Ghost repository, this guideline applies to the shade admin app. For files under apps/shade that access browser globals (navigator, window, document) at module load time, SSR is not used, so typeof guards are not required. Reviewers should verify that such files remain client-side only and that no SSR context is introduced; apply this understanding to similarly structured files under apps/shade.

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/ui/card.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/hooks/**/*.{ts,tsx} : Custom React hooks should be placed in `src/hooks/*`

Applied to files:

  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use `camelCase` for function and variable names

Applied to files:

  • apps/shade/src/typings.d.ts
🪛 Biome (2.1.2)
apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx

[error] 17-18: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/activitypub/src/views/profile/profile.tsx

[error] 13-14: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Growth/growth.tsx

[error] 16-17: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Web/web.tsx

[error] 26-27: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ 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). (13)
  • GitHub Check: ActivityPub tests
  • GitHub Check: Unit tests (Node 22.18.0)
  • GitHub Check: Legacy tests (Node 22.18.0, sqlite3)
  • GitHub Check: Legacy tests (Node 22.18.0, mysql8)
  • GitHub Check: Acceptance tests (Node 22.18.0, sqlite3)
  • GitHub Check: Admin-X Settings tests
  • GitHub Check: Ghost-CLI tests
  • GitHub Check: Comments-UI tests
  • GitHub Check: Admin tests - Chrome
  • GitHub Check: Signup-form tests
  • GitHub Check: Lint
  • GitHub Check: Acceptance tests (Node 22.18.0, mysql8)
  • GitHub Check: Build & Push Docker Image
🔇 Additional comments (25)
apps/comments-ui/src/components/popup-box.tsx (1)

6-8: LGTM!

The ESLint directive update from ban-types to no-empty-object-type is the correct adaptation for newer @typescript-eslint versions, where the monolithic ban-types rule was split into more specific rules. This aligns with the ESLint plugin upgrade.

apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1)

23-27: Catch block change is behavior‑neutral and matches lint expectations

Switching from catch (_) to a bare catch {} keeps the same semantics (errors are ignored) while removing an unused variable, which aligns with the updated ESLint ghost plugin rules. No functional impact on URL handling or stats aggregation.

Please run your usual lint task (e.g. yarn lint) to confirm there are no remaining unused catch parameter warnings in this file after the plugin bump.

apps/comments-ui/src/utils/admin-api.ts (1)

15-20: Bare catch around JSON.parse is fine here

The error value was unused; switching to catch {} keeps the same “ignore invalid messages and return early” behavior and satisfies lint rules.

apps/posts/src/hooks/use-post-success-modal.ts (1)

155-168: Parameterless catch matches intentional “ignore localStorage errors” behavior

The error value wasn’t used; catch {} cleanly expresses the intent to ignore storage/parse failures without altering control flow.

apps/admin-x-framework/test/unit/hooks/use-form.test.ts (1)

202-208: Tests now use bare catch where the error value is unused

Both error-handling tests still exercise the rejection path and assert on side effects; dropping the unused catch parameter simply matches the intent and lint rules.

Also applies to: 229-235

apps/signup-form/src/components/pages/form-page.tsx (1)

23-39: Submit handler catch cleanup keeps behavior identical

The catch block already ignored the specific error; using catch {} keeps the same UX (reset loading + generic error) while avoiding an unused parameter.

apps/shade/src/lib/utils.ts (1)

97-103: formatUrl’s parameterless catch preserves existing fallback behavior

URL-construction failures still fall back to {save: url, display: url}; removing the unused error binding just satisfies eslint without changing semantics. Based on learnings, this remains correctly centralized in src/lib/utils.ts.

apps/admin-x-settings/src/utils/url.ts (1)

24-32: arePathsEqual keeps same semantics with bare catch

Returning false on URL-construction failure is preserved; dropping the unused error parameter is a straightforward lint-driven cleanup.

apps/comments-ui/src/app-context.ts (1)

114-120: useLabs now uses a parameterless catch while keeping its safe fallback

The hook still returns an empty labs object when context lookup fails; switching to catch {} simply removes an unused variable and aligns with the new eslint rules.

apps/activitypub/src/views/preferences/components/profile.tsx (1)

272-314: handleCopy’s nested catches correctly drop unused error parameters

Both inner and outer catch blocks still surface a generic “Failed to copy image” toast and reset isProcessing; using catch {} is a no-op behavior-wise and matches the linted style.

apps/posts/src/utils/link-helpers.ts (1)

30-33: LGTM! Unused error parameter removed.

The change from catch (error) to bare catch correctly removes the unused error binding while preserving the fallback behavior. This aligns with the updated ESLint rules.

apps/admin-x-framework/src/api/config.ts (1)

5-7: LGTM! Appropriate ESLint suppression for empty interface pattern.

The JSONArray interface correctly extends Array to create a recursive type definition used by JSONValue. The eslint-disable-next-line comment is appropriate since the empty interface body is intentional—the extends clause provides the necessary type structure.

apps/stats/src/utils/url-helpers.ts (2)

21-24: LGTM! Unused error parameter removed.

The change from catch (error) to bare catch is consistent with the ESLint update pattern, removing the unused error binding while maintaining the error-handling behavior.


15-20: LGTM! Formatting improvements from linter.

The blank line adjustments improve code readability and align with the updated ESLint formatting rules.

Also applies to: 37-38, 53-58, 106-115

apps/shade/src/components/ui/card.tsx (1)

219-220: Scoped lint suppression for EmptyCardProps looks good

The no-empty-object-type disable is narrowly scoped to this intentional marker interface and doesn’t affect runtime behavior. Fits the pattern used elsewhere in shade for empty prop interfaces.

apps/shade/src/components/layout/page.tsx (1)

4-5: PageProps lint suppression is appropriate and non-invasive

Disabling no-empty-object-type for this simple wrapper interface keeps the public API clean without changing how Page behaves.

apps/shade/src/components/layout/header.tsx (7)

11-22: HeaderAbove props lint suppression is reasonable

The targeted no-empty-object-type disable on HeaderAboveProps keeps the lightweight props shape while avoiding unnecessary lint noise; implementation continues to forward className via cn(...) correctly.


24-38: HeaderTitle props suppression keeps API simple

Using a scoped disable for the empty HeaderTitleProps interface is consistent with the rest of the Header API and doesn’t affect rendering or styling.


40-51: HeaderMeta props interface + lint suppression are fine

The empty HeaderMetaProps wrapper plus scoped eslint-disable cleanly convey intent without changing layout or behavior.


53-64: HeaderActionGroup props pattern matches the rest of Header

The empty props interface with a per-line suppression is consistent with other Header subcomponents and keeps types readable.


66-77: HeaderActions props suppression aligns with compound subcomponent pattern

Same scoped no-empty-object-type disable here maintains a simple, shared props shape for action areas; behavior is unchanged.


79-90: HeaderNav props interface and lint disable are consistent

The empty HeaderNavProps plus local suppression follow the same pattern as other Header regions and keep the compound API tidy.


103-115: HeaderProps typing and usage with headerVariants look correct

HeaderProps cleanly combines children/className with the header variant options, and Header continues to merge className through cn(headerVariants({variant, className})) as per shade guidelines. No issues spotted.

apps/shade/src/typings.d.ts (1)

2-2: LGTM – Consistent lint suppression.

Identical to the change in apps/comments-ui/src/typings.d.ts. The suppression is appropriate for this declaration file context.

apps/stats/src/types/svg.d.ts (1)

2-2: LGTM – Consistent with PR-wide pattern.

The lint suppression matches the approach used in other declaration files across this PR. No issues identified.

@codecov
Copy link

codecov bot commented Dec 15, 2025

Codecov Report

❌ Patch coverage is 50.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.10%. Comparing base (2820918) to head (781b6ab).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
...endations/service/RecommendationMetadataService.ts 33.33% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #25713      +/-   ##
==========================================
+ Coverage   72.08%   72.10%   +0.01%     
==========================================
  Files        1539     1539              
  Lines      117719   117730      +11     
  Branches    14102    14110       +8     
==========================================
+ Hits        84856    84885      +29     
+ Misses      31832    31831       -1     
+ Partials     1031     1014      -17     
Flag Coverage Δ
admin-tests 51.56% <ø> (ø)
e2e-tests 72.10% <50.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ibalosh ibalosh force-pushed the update-eslint-version branch from b48e7c2 to fff3de2 Compare December 15, 2025 14:46
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 (1)
apps/activitypub/src/hooks/use-activity-pub-queries.ts (1)

360-495: Guard notification like/repost counts against undefined values

In updateNotificationsLikedCache and updateNotificationsRepostCache the math assumes notification.post.likeCount / notification.post.repostCount are always defined:

likeCount: Math.max(
    liked ? notification.post.likeCount + 1 : notification.post.likeCount - 1,
    0
)

repostCount: Math.max(
    reposted ? notification.post.repostCount + 1 : notification.post.repostCount - 1,
    0
)

If either count is ever undefined these expressions produce NaN, and Math.max(NaN, 0) will stay NaN, corrupting the cached value. Note how updateNotificationsReplyCountCache already uses a ?? 0 fallback.

Consider aligning these to the safer pattern:

- likeCount: Math.max(liked ? notification.post.likeCount + 1 : notification.post.likeCount - 1, 0)
+ likeCount: Math.max(
+     liked
+         ? (notification.post.likeCount ?? 0) + 1
+         : (notification.post.likeCount ?? 0) - 1,
+     0
+ )

- repostCount: Math.max(reposted ? notification.post.repostCount + 1 : notification.post.repostCount - 1, 0)
+ repostCount: Math.max(
+     reposted
+         ? (notification.post.repostCount ?? 0) + 1
+         : (notification.post.repostCount ?? 0) - 1,
+     0
+ )

The new bare catch {} blocks look fine and preserve the existing fallback-to-current behavior.

🧹 Nitpick comments (6)
apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1)

16-17: Consider improving the interface naming and structure.

The interface name postAnalyticsProps doesn't follow the TypeScript convention of PascalCase for type names. Additionally, since this is an empty interface, it could be simplified to a type alias or removed entirely if no props are needed.

Apply this diff to use PascalCase naming and convert to a type alias:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type PostAnalyticsProps = Record<string, never>;

-const Growth: React.FC<postAnalyticsProps> = () => {
+const Growth: React.FC<PostAnalyticsProps> = () => {

Alternatively, if no props are expected, you can simplify to:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
-
-const Growth: React.FC<postAnalyticsProps> = () => {
+const Growth: React.FC = () => {
apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1)

17-18: Consider improving the interface naming and structure.

The interface name postAnalyticsProps doesn't follow the TypeScript convention of PascalCase for type names. Additionally, since this is an empty interface, it could be simplified to a type alias or removed entirely if no props are needed.

Apply this diff to use PascalCase naming and convert to a type alias:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type PostAnalyticsProps = Record<string, never>;

-const Newsletter: React.FC<postAnalyticsProps> = () => {
+const Newsletter: React.FC<PostAnalyticsProps> = () => {

Alternatively, if no props are expected, you can simplify to:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
-
-const Newsletter: React.FC<postAnalyticsProps> = () => {
+const Newsletter: React.FC = () => {
apps/posts/src/views/PostAnalytics/Web/web.tsx (1)

26-27: Consider improving the interface naming and structure.

The interface name postAnalyticsProps doesn't follow the TypeScript convention of PascalCase for type names. Additionally, since this is an empty interface, it could be simplified to a type alias or removed entirely if no props are needed.

This same pattern appears in all three PostAnalytics views (Growth, Newsletter, Web). If all components share the same empty props interface, consider defining it once in a shared location like ../components/post-analytics-types.ts to reduce duplication.

Apply this diff to use PascalCase naming and convert to a type alias:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type PostAnalyticsProps = Record<string, never>;

-const Web: React.FC<postAnalyticsProps> = () => {
+const Web: React.FC<PostAnalyticsProps> = () => {

Alternatively, if no props are expected, you can simplify to:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
-
-const Web: React.FC<postAnalyticsProps> = () => {
+const Web: React.FC = () => {
apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (1)

39-41: Scoped ESLint disable is acceptable; consider small refactor to avoid it

The inline eslint-disable-next-line comments are tightly scoped to the clearError expressions and introduce no runtime changes, which is appropriate for this ESLint-upgrade PR. If you’d like to avoid the suppression altogether, you could refactor both expressions to if statements:

-                        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
-                        !!item.errors.label && clearError?.('label');
+                        if (item.errors.label) {
+                            clearError?.('label');
+                        }
-                        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
-                        !!item.errors.url && clearError?.('url');
+                        if (item.errors.url) {
+                            clearError?.('url');
+                        }

This keeps the intent clear and satisfies the lint rule without needing disables, but treating it as optional here is reasonable given the PR’s scope.

Also applies to: 67-69

apps/activitypub/src/views/profile/profile.tsx (1)

13-14: Empty ProfileProps + ESLint suppression is fine; optional simplification

This is functionally harmless and matches the PR-wide lint pattern. If you don’t plan to add props soon, you could simplify and drop both the interface and the disable:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface ProfileProps {}
-
-const Profile: React.FC<ProfileProps> = ({}) => {
+const Profile: React.FC = () => {

That also sidesteps Biome’s noEmptyInterface warning, but it’s purely optional.

apps/comments-ui/src/actions.ts (1)

257-276: Like/unlike bare catches keep optimistic update behavior intact

Removing the unused error binding and using bare catch {} keeps the existing pattern: optimistic toggle via updateCommentLikeState and simple revert on failure. No behavioral change here.

If you ever want more observability, a non-blocking enhancement would be to log or surface a toast in these branches, but that can be deferred.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b48e7c2 and fff3de2.

📒 Files selected for processing (54)
  • apps/activitypub/src/api/activitypub.ts (1 hunks)
  • apps/activitypub/src/components/modals/new-note-modal.tsx (1 hunks)
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts (3 hunks)
  • apps/activitypub/src/utils/get-username.ts (1 hunks)
  • apps/activitypub/src/utils/image.ts (1 hunks)
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1 hunks)
  • apps/activitypub/src/views/preferences/components/profile.tsx (1 hunks)
  • apps/activitypub/src/views/profile/profile.tsx (1 hunks)
  • apps/admin-x-design-system/src/global/sortable-list.tsx (1 hunks)
  • apps/admin-x-design-system/src/typings.d.ts (1 hunks)
  • apps/admin-x-design-system/src/utils/format-url.ts (1 hunks)
  • apps/admin-x-framework/src/api/config.ts (1 hunks)
  • apps/admin-x-framework/src/api/recommendations.ts (1 hunks)
  • apps/admin-x-framework/src/test/acceptance.ts (1 hunks)
  • apps/admin-x-framework/src/test/test-utils.tsx (1 hunks)
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts (2 hunks)
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (2 hunks)
  • apps/admin-x-settings/src/typings.d.ts (1 hunks)
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts (2 hunks)
  • apps/admin-x-settings/src/utils/url.ts (1 hunks)
  • apps/comments-ui/src/actions.ts (2 hunks)
  • apps/comments-ui/src/app-context.ts (1 hunks)
  • apps/comments-ui/src/components/content/forms/form.tsx (1 hunks)
  • apps/comments-ui/src/components/popup-box.tsx (1 hunks)
  • apps/comments-ui/src/typings.d.ts (1 hunks)
  • apps/comments-ui/src/utils/admin-api.ts (1 hunks)
  • apps/comments-ui/src/utils/api.ts (1 hunks)
  • apps/posts/src/hooks/use-post-success-modal.ts (1 hunks)
  • apps/posts/src/utils/link-helpers.ts (1 hunks)
  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Web/web.tsx (1 hunks)
  • apps/shade/src/components/features/color-picker/color-picker.tsx (1 hunks)
  • apps/shade/src/components/layout/header.tsx (7 hunks)
  • apps/shade/src/components/layout/heading.tsx (1 hunks)
  • apps/shade/src/components/layout/page.tsx (1 hunks)
  • apps/shade/src/components/layout/view-header.tsx (2 hunks)
  • apps/shade/src/components/ui/card.tsx (1 hunks)
  • apps/shade/src/lib/utils.ts (1 hunks)
  • apps/shade/src/typings.d.ts (1 hunks)
  • apps/signup-form/src/components/pages/form-page.tsx (1 hunks)
  • apps/signup-form/src/typings.d.ts (1 hunks)
  • apps/stats/src/hooks/use-feature-flag.tsx (1 hunks)
  • apps/stats/src/types/svg.d.ts (1 hunks)
  • apps/stats/src/utils/url-helpers.ts (5 hunks)
  • apps/stats/test/utils/tinybird-helpers.ts (1 hunks)
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2 hunks)
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1 hunks)
  • package.json (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (32)
  • apps/activitypub/src/components/modals/new-note-modal.tsx
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx
  • apps/shade/src/lib/utils.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx
  • apps/stats/src/hooks/use-feature-flag.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/comments-ui/src/utils/api.ts
  • apps/admin-x-framework/src/test/test-utils.tsx
  • apps/signup-form/src/components/pages/form-page.tsx
  • apps/stats/test/utils/tinybird-helpers.ts
  • apps/posts/src/utils/link-helpers.ts
  • apps/stats/src/utils/url-helpers.ts
  • apps/activitypub/src/views/preferences/components/profile.tsx
  • apps/admin-x-settings/src/utils/url.ts
  • apps/comments-ui/src/app-context.ts
  • apps/activitypub/src/api/activitypub.ts
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts
  • apps/posts/src/hooks/use-post-success-modal.ts
  • apps/comments-ui/src/typings.d.ts
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx
  • apps/admin-x-settings/src/typings.d.ts
  • apps/shade/src/components/layout/page.tsx
  • apps/admin-x-framework/src/api/recommendations.ts
  • apps/signup-form/src/typings.d.ts
  • package.json
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
  • apps/shade/src/components/ui/card.tsx
  • apps/comments-ui/src/components/popup-box.tsx
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/comments-ui/src/utils/admin-api.ts
🧰 Additional context used
📓 Path-based instructions (4)
apps/shade/src/components/layout/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in src/components/layout/*

Files:

  • apps/shade/src/components/layout/header.tsx
apps/shade/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/components/**/*.{ts,tsx}: Use PascalCase for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Always forward and merge className prop with cn(...) utility function
Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration
Prefer compound subcomponents (e.g., Header.Title, Header.Meta, Header.Actions) for multi-region components instead of many props
Use Tailwind CSS scoped via .shade class; dark mode uses .dark

Files:

  • apps/shade/src/components/layout/header.tsx
apps/shade/src/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/**/*.{ts,tsx,js}: Use camelCase for function and variable names
Use the @ alias for internal imports (e.g., @/lib/utils)

Files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
apps/shade/{src,test}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/{src,test}/**/*.{ts,tsx,js}: Run yarn lint after making changes to fix any ESLint errors and warnings before committing
Follow ESLint and tailwindcss/* plugin rules when writing styles

Files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
🧠 Learnings (21)
📓 Common learnings
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Run `yarn lint` after making changes to fix any ESLint errors and warnings before committing
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24673
File: ghost/i18n/lib/i18n.js:34-35
Timestamp: 2025-11-24T11:12:15.712Z
Learning: In the Ghost i18n package (ghost/i18n/lib/i18n.js), changing existing locale codes requires backwards compatibility handling for users who have already configured those locales. Such changes should be done in a separate PR with migration logic rather than included in feature PRs.
Learnt from: niranjan-uma-shankar
Repo: TryGhost/Ghost PR: 24557
File: apps/admin-x-settings/src/components/settings/general/TimeZone.tsx:7-7
Timestamp: 2025-08-01T12:44:07.467Z
Learning: In Ghost development, PRs may depend on unpublished changes from SDK packages. When this occurs, TypeScript compilation errors for missing exports are expected and documented in the PR description until the dependency packages are published and updated. This is normal workflow for cross-repository feature development.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Applies to ghost/i18n/locales/en/ghost.json : Add UI translations to `ghost/i18n/locales/en/ghost.json` for Admin UI features
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Applies to ghost/i18n/locales/en/{portal,comments,signup-form,search}.json : Add UI translations to namespace-specific files in `ghost/i18n/locales/en/` for public UI apps (portal.json for Portal, comments.json for Comments UI, etc.)
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.{ts,tsx} : Prefer less comments and give things clear names

Applied to files:

  • apps/comments-ui/src/actions.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/layout/**/*.{ts,tsx} : Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in `src/components/layout/*`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use Tailwind CSS scoped via `.shade` class; dark mode uses `.dark`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/index.ts : Place new UI components under `src/components/ui` and export them from `src/index.ts`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/features/**/*.{ts,tsx} : Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in `src/components/features/*`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-25T11:58:51.652Z
Learnt from: ibalosh
Repo: TryGhost/Ghost PR: 25525
File: apps/shade/src/shade-app.tsx:4-4
Timestamp: 2025-11-25T11:58:51.652Z
Learning: In apps/shade, the app wrapper file should be named `src/shade-app.tsx` (kebab-case) while the component itself is exported as `ShadeApp` (PascalCase). Context providers should be placed in `src/providers/*` using kebab-case filenames.

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-06T05:35:41.162Z
Learnt from: danielraffel
Repo: TryGhost/Ghost PR: 25366
File: apps/admin/src/layout/app-sidebar/NavHeader.tsx:13-23
Timestamp: 2025-11-06T05:35:41.162Z
Learning: In apps/admin/src/layout/app-sidebar/NavHeader.tsx, the React component dispatches a synthetic KeyboardEvent to trigger the Ember keymaster.js search modal shortcut. This approach is known to have cross-browser reliability issues but was deferred for architectural refactoring in a separate PR. The recommended fix is to expose a global function or custom DOM event from the Ember app instead of relying on synthetic keyboard events with keymaster.js.

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/lib/utils.ts : Shared utilities (class merging, formatting, chart helpers) should be centralized in `src/lib/utils.ts`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Always forward and merge `className` prop with `cn(...)` utility function

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-12-09T12:37:23.267Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25501
File: apps/shade/src/hooks/use-mobile.tsx:5-10
Timestamp: 2025-12-09T12:37:23.267Z
Learning: In the Ghost repository, this guideline applies to the shade admin app. For files under apps/shade that access browser globals (navigator, window, document) at module load time, SSR is not used, so typeof guards are not required. Reviewers should verify that such files remain client-side only and that no SSR context is introduced; apply this understanding to similarly structured files under apps/shade.

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-10-09T15:31:06.587Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25118
File: apps/portal/src/actions.js:160-173
Timestamp: 2025-10-09T15:31:06.587Z
Learning: When reviewing PRs that introduce feature-flagged changes (e.g., `labs?.membersSigninOTCAlpha`), avoid suggesting modifications to non-flagged code paths unless they're directly related to the PR's objectives. Keep the scope focused on the feature-flag-specific changes only.

Applied to files:

  • apps/admin-x-framework/src/test/acceptance.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)

Applied to files:

  • apps/admin-x-design-system/src/typings.d.ts
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles

Applied to files:

  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Run `yarn lint` after making changes to fix any ESLint errors and warnings before committing

Applied to files:

  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/hooks/**/*.{ts,tsx} : Custom React hooks should be placed in `src/hooks/*`

Applied to files:

  • apps/shade/src/typings.d.ts
🪛 Biome (2.1.2)
apps/activitypub/src/views/profile/profile.tsx

[error] 13-14: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Growth/growth.tsx

[error] 16-17: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx

[error] 17-18: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Web/web.tsx

[error] 26-27: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ 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). (2)
  • GitHub Check: Setup
  • GitHub Check: Build & Push Docker Image
🔇 Additional comments (14)
apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1)

29-29: LGTM! Unused catch parameter removed.

The error variable was never referenced in the catch block, so removing it is correct and improves code cleanliness. The behavior is preserved—the catch block still handles invalid URL errors properly.

apps/admin-x-framework/src/test/acceptance.ts (1)

127-132: Bare catch binding change is safe and keeps behavior identical

The error object was not used in this block, so moving to a parameterless catch keeps semantics the same while satisfying linting rules. No further changes needed here.

apps/comments-ui/src/components/content/forms/form.tsx (1)

78-78: LGTM! Correctly removes unused error binding.

The change from catch (e) to a bare catch is appropriate since the error parameter was never used in the catch block. This aligns with modern ESLint best practices.

apps/admin-x-design-system/src/global/sortable-list.tsx (1)

17-17: ESLint directive correctly updated for the new plugin version.

The change from @typescript-eslint/ban-types to @typescript-eslint/no-unsafe-function-type aligns with TypeScript ESLint v8's migration path, where ban-types was removed and split into focused rules. no-unsafe-function-type is the appropriate replacement for suppressing warnings about the unsafe Function type on line 18.

ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1)

175-190: Bare catch in url getter preserves behavior

Switching to a bare catch {} here doesn’t change behavior—the method still normalizes all URL parse/protocol failures into a ValidationError with the same message. Looks good.

apps/activitypub/src/utils/get-username.ts (1)

5-8: getUsername bare catch is safe and keeps existing fallback

The switch to catch {} keeps the same behavior—invalid actor.id values still fall back to @unknown@unknown without leaking errors. All good.

apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1)

48-58: handleEnable bare catch keeps error UX the same

The move to catch {} doesn’t alter behavior—loading is reset and the generic error toast still shows on failure. This remains consistent with the interval-based confirmation logic below.

apps/activitypub/src/utils/image.ts (1)

10-28: imageUrlToDataUrl bare catch keeps graceful fallback

Switching to catch {} keeps the same “fallback to original URL on any failure” behavior. The FileReader and fetch failures are still safely handled, so this change looks good.

ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (1)

44-71: Bare catches in #fetchJSON / #castUrl keep existing fallbacks

The updated catch {} blocks continue to:

  • Treat JSON parse/HTTP/network failures as undefined from #fetchJSON, and
  • Treat invalid URLs as null from #castUrl.

Callers already handle those cases, so this is a pure syntax cleanup with no behavioral impact.

Also applies to: 73-82

apps/admin-x-design-system/src/utils/format-url.ts (1)

48-48: LGTM! Bare catch is appropriate.

The error parameter was never used, so removing it aligns with the ESLint update and maintains the same fallback behavior.

apps/admin-x-framework/src/api/config.ts (1)

6-7: LGTM! JSONArray type properly models JSON arrays.

The interface correctly defines recursive array structures for JSON values, and the lint suppression is appropriate for the empty-object-type pattern when extending built-in types.

apps/shade/src/components/layout/header.tsx (3)

11-11: LGTM! ESLint suppressions are consistently applied.

The suppressions are appropriate for interfaces that extend base types without adding new properties, which is a pattern required by the updated ESLint rules.

Also applies to: 24-24, 40-40, 53-53, 66-66, 79-79


104-104: LGTM! HeaderProps properly integrates CVA variants.

The interface correctly combines base props with variant support, enabling the Header component to accept the variant prop for layout variations.


116-121: Excellent! Compound component pattern aligns perfectly with coding guidelines.

The static subcomponent bindings enable the recommended compound component pattern (e.g., <Header.Title>, <Header.Meta>, <Header.Actions>), which improves API discoverability and follows the project's preference for multi-region components. Based on coding guidelines, this is the preferred approach over prop-heavy configuration.

@ibalosh ibalosh force-pushed the update-eslint-version branch from fff3de2 to bdf4848 Compare December 15, 2025 15:37
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: 0

♻️ Duplicate comments (2)
apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx (1)

782-783: Remove the errant backticks instead of suppressing ESLint.

The two backticks at the end of line 783 create an empty template literal expression, which is why the no-unused-expressions rule fires. The correct fix is to remove the backticks, not suppress the linter.

Apply this diff:

-            // eslint-disable-next-line @typescript-eslint/no-unused-expressions
-            const {meta: {sent_email_verification: [emailToVerify] = []} = {}} = await editNewsletter(formState); ``;
+            const {meta: {sent_email_verification: [emailToVerify] = []} = {}} = await editNewsletter(formState);
apps/stats/src/types/svg.d.ts (1)

1-6: Consistent with other SVG typings; existing refactor suggestion is optional.

This change aligns this file with the other *.svg typings and cleanly suppresses the new rule, which is enough to satisfy the eslint-plugin-ghost upgrade. There’s already a prior comment here suggesting switching to an ES import type to avoid the disable; that’s a nice-to-have refactor rather than a blocker—feel free to pick it up separately if you want to remove the suppression.

🧹 Nitpick comments (5)
apps/activitypub/src/views/profile/profile.tsx (1)

13-14: Consider removing the unused interface or converting to a type alias.

The ProfileProps interface is empty and the Profile component doesn't actually receive or use any props (line 140 destructures an empty object). Rather than suppressing the lint warning, you could:

  1. Remove entirely (preferred): Since no props are passed, just use React.FC without a type parameter
  2. Type alias: If keeping for future extensibility, use type ProfileProps = Record<string, never>;

Option 1: Remove the unused interface

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface ProfileProps {}
-
...
-const Profile: React.FC<ProfileProps> = ({}) => {
+const Profile: React.FC = () => {

Option 2: Use a type alias

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface ProfileProps {}
+type ProfileProps = Record<string, never>;
apps/admin-x-settings/src/typings.d.ts (1)

4-9: SVG typing change is fine; optional: avoid disable by using import type.

This matches the pattern used in other typings and unblocks the updated ESLint plugin, so I’m fine with it as-is. If you later want to reduce eslint-disable comments, you could switch to a type-only ES import and drop the suppression:

-declare module '*.svg' {
-    // eslint-disable-next-line @typescript-eslint/no-require-imports
-    import React = require('react');
+declare module '*.svg' {
+    import type * as React from 'react';

The rest of the declarations can stay the same.

apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1)

16-19: Empty props interface + ESLint suppression are fine; Biome hint is purely stylistic

Using an explicit postAnalyticsProps and suppressing @typescript-eslint/no-empty-object-type keeps this in line with the other PostAnalytics views and doesn’t affect behavior. Biome’s noEmptyInterface warning here is stylistic—if you ever want to appease it, you could switch to a type alias (e.g. type postAnalyticsProps = {};) or add a Biome-specific ignore, but it’s reasonable to leave as-is given the ESLint plugin constraints and existing pattern.

apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1)

17-19: ESLint suppression around empty props interface looks appropriate; Biome warning is low-priority

Adding the @typescript-eslint/no-empty-object-type suppression over postAnalyticsProps is a pragmatic way to keep the existing React.FC<postAnalyticsProps> signature without fighting the updated ESLint plugin. Biome also complains about empty interfaces, but that’s purely stylistic—if you care about Biome noise later, you could convert this to a type alias or add a Biome-specific ignore; it’s fine to keep as-is for this ESLint-focused PR.

apps/posts/src/views/PostAnalytics/Web/web.tsx (1)

26-29: Consistent props typing pattern; Biome’s noEmptyInterface note can be ignored or addressed later

This mirrors the other PostAnalytics views: an explicit postAnalyticsProps plus an ESLint disable for @typescript-eslint/no-empty-object-type, with no runtime impact. Biome’s suggestion to avoid empty interfaces is just style-level—if you want to satisfy it in future, you could switch to a type alias or add a Biome ignore, but it’s reasonable to keep this pattern unchanged for the ESLint plugin upgrade.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fff3de2 and bdf4848.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (54)
  • apps/activitypub/src/api/activitypub.ts (1 hunks)
  • apps/activitypub/src/components/modals/new-note-modal.tsx (1 hunks)
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts (3 hunks)
  • apps/activitypub/src/utils/get-username.ts (1 hunks)
  • apps/activitypub/src/utils/image.ts (1 hunks)
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1 hunks)
  • apps/activitypub/src/views/preferences/components/profile.tsx (1 hunks)
  • apps/activitypub/src/views/profile/profile.tsx (1 hunks)
  • apps/admin-x-design-system/src/global/sortable-list.tsx (1 hunks)
  • apps/admin-x-design-system/src/typings.d.ts (1 hunks)
  • apps/admin-x-design-system/src/utils/format-url.ts (1 hunks)
  • apps/admin-x-framework/src/api/config.ts (1 hunks)
  • apps/admin-x-framework/src/api/recommendations.ts (1 hunks)
  • apps/admin-x-framework/src/test/acceptance.ts (1 hunks)
  • apps/admin-x-framework/src/test/test-utils.tsx (1 hunks)
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts (2 hunks)
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (2 hunks)
  • apps/admin-x-settings/src/typings.d.ts (1 hunks)
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts (2 hunks)
  • apps/admin-x-settings/src/utils/url.ts (1 hunks)
  • apps/comments-ui/src/actions.ts (2 hunks)
  • apps/comments-ui/src/app-context.ts (1 hunks)
  • apps/comments-ui/src/components/content/forms/form.tsx (1 hunks)
  • apps/comments-ui/src/components/popup-box.tsx (1 hunks)
  • apps/comments-ui/src/typings.d.ts (1 hunks)
  • apps/comments-ui/src/utils/admin-api.ts (1 hunks)
  • apps/comments-ui/src/utils/api.ts (1 hunks)
  • apps/posts/src/hooks/use-post-success-modal.ts (1 hunks)
  • apps/posts/src/utils/link-helpers.ts (1 hunks)
  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Web/web.tsx (1 hunks)
  • apps/shade/src/components/features/color-picker/color-picker.tsx (1 hunks)
  • apps/shade/src/components/layout/header.tsx (7 hunks)
  • apps/shade/src/components/layout/heading.tsx (1 hunks)
  • apps/shade/src/components/layout/page.tsx (1 hunks)
  • apps/shade/src/components/layout/view-header.tsx (2 hunks)
  • apps/shade/src/components/ui/card.tsx (1 hunks)
  • apps/shade/src/lib/utils.ts (1 hunks)
  • apps/shade/src/typings.d.ts (1 hunks)
  • apps/signup-form/src/components/pages/form-page.tsx (1 hunks)
  • apps/signup-form/src/typings.d.ts (1 hunks)
  • apps/stats/src/hooks/use-feature-flag.tsx (1 hunks)
  • apps/stats/src/types/svg.d.ts (1 hunks)
  • apps/stats/src/utils/url-helpers.ts (5 hunks)
  • apps/stats/test/utils/tinybird-helpers.ts (1 hunks)
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2 hunks)
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1 hunks)
  • package.json (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (31)
  • apps/activitypub/src/utils/get-username.ts
  • apps/stats/test/utils/tinybird-helpers.ts
  • package.json
  • apps/activitypub/src/components/modals/new-note-modal.tsx
  • apps/signup-form/src/typings.d.ts
  • apps/admin-x-design-system/src/global/sortable-list.tsx
  • apps/shade/src/components/ui/card.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts
  • apps/admin-x-framework/src/test/acceptance.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx
  • apps/admin-x-design-system/src/typings.d.ts
  • apps/comments-ui/src/utils/api.ts
  • apps/comments-ui/src/actions.ts
  • apps/admin-x-settings/src/utils/url.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx
  • apps/admin-x-framework/src/test/test-utils.tsx
  • apps/shade/src/lib/utils.ts
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
  • apps/comments-ui/src/app-context.ts
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
  • apps/comments-ui/src/utils/admin-api.ts
  • apps/stats/src/hooks/use-feature-flag.tsx
  • apps/stats/src/utils/url-helpers.ts
  • apps/shade/src/components/layout/header.tsx
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx
  • apps/admin-x-framework/src/api/config.ts
  • apps/comments-ui/src/typings.d.ts
  • apps/shade/src/typings.d.ts
  • apps/posts/src/hooks/use-post-success-modal.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx
🧰 Additional context used
📓 Path-based instructions (4)
apps/shade/src/components/layout/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in src/components/layout/*

Files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
apps/shade/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/components/**/*.{ts,tsx}: Use PascalCase for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Always forward and merge className prop with cn(...) utility function
Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration
Prefer compound subcomponents (e.g., Header.Title, Header.Meta, Header.Actions) for multi-region components instead of many props
Use Tailwind CSS scoped via .shade class; dark mode uses .dark

Files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
apps/shade/src/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/**/*.{ts,tsx,js}: Use camelCase for function and variable names
Use the @ alias for internal imports (e.g., @/lib/utils)

Files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
apps/shade/{src,test}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/{src,test}/**/*.{ts,tsx,js}: Run yarn lint after making changes to fix any ESLint errors and warnings before committing
Follow ESLint and tailwindcss/* plugin rules when writing styles

Files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
🧠 Learnings (19)
📓 Common learnings
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Run `yarn lint` after making changes to fix any ESLint errors and warnings before committing
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Learnt from: niranjan-uma-shankar
Repo: TryGhost/Ghost PR: 24557
File: apps/admin-x-settings/src/components/settings/general/TimeZone.tsx:7-7
Timestamp: 2025-08-01T12:44:07.467Z
Learning: In Ghost development, PRs may depend on unpublished changes from SDK packages. When this occurs, TypeScript compilation errors for missing exports are expected and documented in the PR description until the dependency packages are published and updated. This is normal workflow for cross-repository feature development.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24673
File: ghost/i18n/lib/i18n.js:34-35
Timestamp: 2025-11-24T11:12:15.712Z
Learning: In the Ghost i18n package (ghost/i18n/lib/i18n.js), changing existing locale codes requires backwards compatibility handling for users who have already configured those locales. Such changes should be done in a separate PR with migration logic rather than included in feature PRs.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/layout/**/*.{ts,tsx} : Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in `src/components/layout/*`
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.{ts,tsx} : Prefer less comments and give things clear names

Applied to files:

  • apps/admin-x-settings/src/typings.d.ts
  • apps/stats/src/types/svg.d.ts
  • apps/shade/src/components/layout/heading.tsx
  • apps/activitypub/src/views/profile/profile.tsx
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/views/preferences/components/profile.tsx
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx
  • apps/activitypub/src/api/activitypub.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/views/preferences/components/profile.tsx
  • apps/activitypub/src/api/activitypub.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)

Applied to files:

  • apps/stats/src/types/svg.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/hooks/**/*.{ts,tsx} : Custom React hooks should be placed in `src/hooks/*`

Applied to files:

  • apps/stats/src/types/svg.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/layout/**/*.{ts,tsx} : Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in `src/components/layout/*`

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/comments-ui/src/components/popup-box.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/index.ts : Place new UI components under `src/components/ui` and export them from `src/index.ts`

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/comments-ui/src/components/popup-box.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use Tailwind CSS scoped via `.shade` class; dark mode uses `.dark`

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/ui/**/*.{ts,tsx} : Atomic UI components should be placed in `src/components/ui/*` and each component must have a corresponding `*.stories.tsx` file next to it for Storybook documentation

Applied to files:

  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/features/**/*.{ts,tsx} : Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in `src/components/features/*`

Applied to files:

  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Always forward and merge `className` prop with `cn(...)` utility function

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
📚 Learning: 2025-12-09T12:37:23.267Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25501
File: apps/shade/src/hooks/use-mobile.tsx:5-10
Timestamp: 2025-12-09T12:37:23.267Z
Learning: In the Ghost repository, this guideline applies to the shade admin app. For files under apps/shade that access browser globals (navigator, window, document) at module load time, SSR is not used, so typeof guards are not required. Reviewers should verify that such files remain client-side only and that no SSR context is introduced; apply this understanding to similarly structured files under apps/shade.

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
📚 Learning: 2025-07-14T12:20:35.268Z
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24346
File: apps/admin-x-settings/src/components/settings/growth/Network.tsx:8-12
Timestamp: 2025-07-14T12:20:35.268Z
Learning: The Network component toggle in `apps/admin-x-settings/src/components/settings/growth/Network.tsx` is intentionally implemented as static UI with a no-op onChange handler, as part of a UI-first development approach before connecting actual ActivityPub functionality.

Applied to files:

  • apps/activitypub/src/views/profile/profile.tsx
📚 Learning: 2025-10-30T17:13:26.190Z
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.

Applied to files:

  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx
🪛 Biome (2.1.2)
apps/activitypub/src/views/profile/profile.tsx

[error] 13-14: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Web/web.tsx

[error] 26-27: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Growth/growth.tsx

[error] 16-17: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx

[error] 17-18: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

🔇 Additional comments (18)
apps/comments-ui/src/components/popup-box.tsx (1)

7-8: ESLint rule update on empty object type looks correct

Switching the directive to @typescript-eslint/no-empty-object-type is consistent with the updated ESLint/TS rules and keeps the existing TODO without introducing runtime changes. This is appropriate for this PR’s scope.

apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1)

48-60: LGTM! Error binding correctly removed.

The unused error parameter has been removed from the catch block while preserving the error handling behavior (loading state reset and toast notification).

apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1)

19-33: LGTM! Unused error binding removed.

The catch block correctly removes the unused error parameter while maintaining the URL validation error handling.

apps/comments-ui/src/components/content/forms/form.tsx (1)

67-91: LGTM! Error binding correctly removed.

The unused error parameter has been removed while preserving the error state management in the form submission handler.

apps/activitypub/src/views/preferences/components/profile.tsx (1)

258-315: LGTM! Unused error bindings removed from nested error handling.

Both catch blocks in the nested try-catch structure correctly remove unused error parameters while preserving the toast error notifications and processing state cleanup.

apps/activitypub/src/api/activitypub.ts (1)

252-261: LGTM! Unused error binding removed.

The catch block correctly removes the unused error parameter while maintaining the null return behavior. The TODO comment about Sentry remains relevant if error logging is needed in the future.

apps/admin-x-settings/src/utils/link-to-github-releases.ts (2)

1-2: LGTM! TypeScript suppression added.

The @ts-expect-error comment correctly documents that the semver subpath import lacks type definitions.


8-36: LGTM! Unused error binding removed.

The catch block correctly removes the unused error parameter while preserving the fallback behavior (empty string return).

apps/signup-form/src/components/pages/form-page.tsx (1)

14-40: LGTM! Unused error binding removed.

The catch block correctly removes the unused error parameter (previously marked with underscore) while preserving the error state management and toast notification.

apps/posts/src/utils/link-helpers.ts (1)

15-34: LGTM! Unused error binding removed.

The catch block correctly removes the unused error parameter while preserving the fallback behavior (returning the original URL when parsing fails).

apps/activitypub/src/utils/image.ts (1)

25-28: LGTM!

The bare catch block is appropriate here since the error was unused — the function simply returns the original URL as a fallback regardless of the error type. No loss of functionality or debugging capability.

ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2)

64-70: LGTM!

The bare catch blocks in #fetchJSON appropriately swallow parsing and network errors, returning undefined as a fallback. The error objects were unused, so this change has no functional impact.


77-81: LGTM!

The #castUrl method correctly returns null for invalid URLs. The bare catch is appropriate since the error details are irrelevant to the fallback behavior.

apps/admin-x-design-system/src/utils/format-url.ts (1)

46-50: LGTM!

The bare catch is appropriate — the function gracefully handles invalid URLs by returning the original input, and the specific error details aren't needed for this fallback behavior.

apps/admin-x-framework/src/api/recommendations.ts (1)

25-29: LGTM!

The eslint-disable-next-line comments are appropriate here. RecommendationEditResponseType serves as a semantic type alias for the response type, and RecommendationDeleteResponseType correctly represents a DELETE response with no body. These intentionally empty interfaces improve API documentation and type safety.

apps/shade/src/components/layout/heading.tsx (1)

4-5: Scoped ESLint suppression for HeadingProps looks good

The @typescript-eslint/no-empty-object-type disable is correctly limited to HeadingProps, letting you keep the exported interface alias over React.HTMLAttributes<HTMLHeadingElement> with no runtime impact. Please just verify yarn lint passes with the updated eslint-plugin-ghost.

apps/shade/src/components/layout/page.tsx (1)

4-5: ESLint disable around PageProps is minimal and appropriate

The empty-object-type suppression is narrowly scoped to PageProps, preserving the existing interface-based props alias without changing the Page component’s runtime behavior. Please confirm the updated eslint-plugin-ghost runs cleanly (yarn lint).

apps/shade/src/components/layout/view-header.tsx (1)

4-6: ViewHeaderActionsProps lint suppression and exports are consistent

The added @typescript-eslint/no-empty-object-type disable is correctly scoped to ViewHeaderActionsProps, allowing the existing interface alias pattern without altering the ViewHeader/ViewHeaderActions API or behavior; the named exports remain unchanged. Please verify the new eslint-plugin-ghost configuration passes lint for this file.

Also applies to: 32-32

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: 0

♻️ Duplicate comments (1)
apps/stats/src/types/svg.d.ts (1)

2-3: Same refactor suggestion applies here.

A previous review comment already suggested using import type * as React from 'react'; instead of suppressing the linter. Please refer to that comment for the recommended change.

🧹 Nitpick comments (8)
apps/admin-x-settings/src/typings.d.ts (1)

5-6: Prefer ES6 import type syntax over suppressing the linter.

Since React is only used for type references here, using import type is cleaner and avoids the need for ESLint suppression.

-    // eslint-disable-next-line @typescript-eslint/no-require-imports
-    import React = require('react');
+    import type * as React from 'react';
apps/comments-ui/src/components/content/forms/form.tsx (1)

78-81: Consider logging the error for better observability.

While the removal of the unused error binding aligns with the ESLint update, the catch block silently swallows errors without any logging. This could make debugging submit failures difficult in production environments.

Consider adding error logging to improve observability:

-        } catch {
+        } catch (error) {
+            console.error('Failed to submit comment:', error);
             setProgress('error');
             return;
         }
apps/activitypub/src/views/profile/profile.tsx (1)

13-14: Align empty props type with Biome and avoid the ESLint suppression

interface ProfileProps {} is effectively {} and is being flagged by Biome (noEmptyInterface), while the new ESLint disable only silences @typescript-eslint/no-empty-object-type. To keep the component’s public API unchanged but simplify linting, consider switching to a type alias and dropping the suppression:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface ProfileProps {}
+type ProfileProps = Record<string, never>;

React.FC<ProfileProps> will still work as before (including children via PropsWithChildren), but both ESLint and Biome should be happier without ignores. If you intentionally want an “extendable marker” interface, you may instead want to keep the interface and add a matching Biome ignore comment alongside this ESLint disable.

apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1)

16-19: Consider removing the empty interface or using a type alias.

The ESLint suppression and empty interface pattern is consistent with the ESLint update, but per Biome's suggestion, empty interfaces are semantically unclear. You have cleaner alternatives:

  • Option 1 (simplest): Remove the interface entirely and use React.FC directly (it defaults to empty props):

    -// eslint-disable-next-line @typescript-eslint/no-empty-object-type
    -interface postAnalyticsProps {}
    -
    -const Growth: React.FC<postAnalyticsProps> = () => {
    +const Growth: React.FC = () => {
  • Option 2: Use a type alias if you want to keep the named type:

    -// eslint-disable-next-line @typescript-eslint/no-empty-object-type
    -interface postAnalyticsProps {}
    +type postAnalyticsProps = Record<string, never>;
apps/posts/src/views/PostAnalytics/Web/web.tsx (1)

26-29: Consider removing the empty interface or using a type alias.

Same pattern as in growth.tsx. Empty interfaces are semantically unclear—cleaner alternatives:

  • Option 1 (simplest): Use React.FC directly (defaults to empty props):

    -// eslint-disable-next-line @typescript-eslint/no-empty-object-type
    -interface postAnalyticsProps {}
    -
    -const Web: React.FC<postAnalyticsProps> = () => {
    +const Web: React.FC = () => {
  • Option 2: Use a type alias:

    -// eslint-disable-next-line @typescript-eslint/no-empty-object-type
    -interface postAnalyticsProps {}
    +type postAnalyticsProps = Record<string, never>;
apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1)

17-18: Consider removing the empty interface or using a type alias.

This pattern appears in all three PostAnalytics view files (growth.tsx, web.tsx, and newsletter.tsx). Empty interfaces are semantically unclear—cleaner alternatives:

  • Option 1 (simplest): Use React.FC directly (defaults to empty props):

    -// eslint-disable-next-line @typescript-eslint/no-empty-object-type
    -interface postAnalyticsProps {}

    Then update line 67:

    -const Newsletter: React.FC<postAnalyticsProps> = () => {
    +const Newsletter: React.FC = () => {
  • Option 2: Use a type alias:

    -// eslint-disable-next-line @typescript-eslint/no-empty-object-type
    -interface postAnalyticsProps {}
    +type postAnalyticsProps = Record<string, never>;

Since this pattern is consistent across all three files, you might want to apply the same refactor to all of them in one pass.

apps/shade/src/components/layout/view-header.tsx (2)

4-5: Consider refactoring to avoid the ESLint suppression.

The interface extends React.HTMLAttributes<HTMLElement> but adds no additional properties, triggering the new ESLint rule. Additionally, ViewHeaderActions (lines 7-13) doesn't forward these inherited attributes to the rendered div.

Consider one of these approaches:

Option 1: Remove the interface and use the base type directly

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface ViewHeaderActionsProps extends React.HTMLAttributes<HTMLElement> {}
-
-const ViewHeaderActions:React.FC<ViewHeaderActionsProps> = ({children}) => {
+const ViewHeaderActions: React.FC<React.HTMLAttributes<HTMLElement>> = ({children}) => {

Option 2: Forward the HTML attributes properly

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface ViewHeaderActionsProps extends React.HTMLAttributes<HTMLElement> {}

-const ViewHeaderActions:React.FC<ViewHeaderActionsProps> = ({children}) => {
+const ViewHeaderActions: React.FC<ViewHeaderActionsProps> = ({children, ...props}) => {
     return (
-        <div className='flex items-center gap-2'>
+        <div className='flex items-center gap-2' {...props}>
             {children}
         </div>
     );

As per coding guidelines for apps/shade components, if HTMLAttributes are declared, they should be forwarded to enable standard DOM props like data-*, aria-*, etc.


15-30: Consider forwarding HTML attributes from the interface.

ViewHeaderProps extends React.HTMLAttributes<HTMLElement> but only className is forwarded (line 19-24). Other inherited attributes like data-*, aria-*, id, etc. are not passed to the rendered element.

Apply this diff to forward all HTML attributes:

-const ViewHeader:React.FC<ViewHeaderProps> = ({className, children}) => {
+const ViewHeader: React.FC<ViewHeaderProps> = ({className, children, ...props}) => {
     const [headerComponent, actionsComponent] = React.Children.toArray(children);
 
     return (
-        <header className='sticky top-0 z-50 -mx-8 bg-white/70 backdrop-blur-md dark:bg-black'>
+        <header className='sticky top-0 z-50 -mx-8 bg-white/70 backdrop-blur-md dark:bg-black' {...props}>

Also note: className?: string; on line 16 is redundant since it's already included in React.HTMLAttributes<HTMLElement>.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdf4848 and 57d944e.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (55)
  • apps/activitypub/src/api/activitypub.ts (1 hunks)
  • apps/activitypub/src/components/modals/new-note-modal.tsx (1 hunks)
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts (3 hunks)
  • apps/activitypub/src/utils/get-username.ts (1 hunks)
  • apps/activitypub/src/utils/image.ts (1 hunks)
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1 hunks)
  • apps/activitypub/src/views/preferences/components/profile.tsx (1 hunks)
  • apps/activitypub/src/views/profile/profile.tsx (1 hunks)
  • apps/admin-x-design-system/src/global/sortable-list.tsx (1 hunks)
  • apps/admin-x-design-system/src/typings.d.ts (1 hunks)
  • apps/admin-x-design-system/src/utils/format-url.ts (1 hunks)
  • apps/admin-x-framework/src/api/config.ts (1 hunks)
  • apps/admin-x-framework/src/api/recommendations.ts (1 hunks)
  • apps/admin-x-framework/src/test/acceptance.ts (1 hunks)
  • apps/admin-x-framework/src/test/test-utils.tsx (1 hunks)
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts (2 hunks)
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (2 hunks)
  • apps/admin-x-settings/src/typings.d.ts (1 hunks)
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts (2 hunks)
  • apps/admin-x-settings/src/utils/url.ts (1 hunks)
  • apps/admin/package.json (1 hunks)
  • apps/comments-ui/src/actions.ts (2 hunks)
  • apps/comments-ui/src/app-context.ts (1 hunks)
  • apps/comments-ui/src/components/content/forms/form.tsx (1 hunks)
  • apps/comments-ui/src/components/popup-box.tsx (1 hunks)
  • apps/comments-ui/src/typings.d.ts (1 hunks)
  • apps/comments-ui/src/utils/admin-api.ts (1 hunks)
  • apps/comments-ui/src/utils/api.ts (1 hunks)
  • apps/posts/src/hooks/use-post-success-modal.ts (1 hunks)
  • apps/posts/src/utils/link-helpers.ts (1 hunks)
  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Web/web.tsx (1 hunks)
  • apps/shade/src/components/features/color-picker/color-picker.tsx (1 hunks)
  • apps/shade/src/components/layout/header.tsx (7 hunks)
  • apps/shade/src/components/layout/heading.tsx (1 hunks)
  • apps/shade/src/components/layout/page.tsx (1 hunks)
  • apps/shade/src/components/layout/view-header.tsx (2 hunks)
  • apps/shade/src/components/ui/card.tsx (1 hunks)
  • apps/shade/src/lib/utils.ts (1 hunks)
  • apps/shade/src/typings.d.ts (1 hunks)
  • apps/signup-form/src/components/pages/form-page.tsx (1 hunks)
  • apps/signup-form/src/typings.d.ts (1 hunks)
  • apps/stats/src/hooks/use-feature-flag.tsx (1 hunks)
  • apps/stats/src/types/svg.d.ts (1 hunks)
  • apps/stats/src/utils/url-helpers.ts (5 hunks)
  • apps/stats/test/utils/tinybird-helpers.ts (1 hunks)
  • e2e/package.json (1 hunks)
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2 hunks)
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/comments-ui/src/typings.d.ts
🚧 Files skipped from review as they are similar to previous changes (32)
  • apps/signup-form/src/components/pages/form-page.tsx
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
  • apps/admin-x-design-system/src/utils/format-url.ts
  • apps/posts/src/utils/link-helpers.ts
  • apps/admin-x-framework/src/test/test-utils.tsx
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
  • apps/signup-form/src/typings.d.ts
  • apps/activitypub/src/api/activitypub.ts
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts
  • apps/activitypub/src/components/modals/new-note-modal.tsx
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/stats/src/utils/url-helpers.ts
  • apps/comments-ui/src/actions.ts
  • apps/admin-x-design-system/src/typings.d.ts
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts
  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/ui/card.tsx
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx
  • apps/posts/src/hooks/use-post-success-modal.ts
  • apps/comments-ui/src/components/popup-box.tsx
  • apps/admin-x-design-system/src/global/sortable-list.tsx
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx
  • apps/shade/src/lib/utils.ts
  • apps/admin-x-framework/src/api/config.ts
  • apps/shade/src/typings.d.ts
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx
  • apps/comments-ui/src/utils/admin-api.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx
  • apps/shade/src/components/layout/header.tsx
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts
🧰 Additional context used
📓 Path-based instructions (5)
apps/shade/src/components/layout/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in src/components/layout/*

Files:

  • apps/shade/src/components/layout/view-header.tsx
apps/shade/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/components/**/*.{ts,tsx}: Use PascalCase for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Always forward and merge className prop with cn(...) utility function
Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration
Prefer compound subcomponents (e.g., Header.Title, Header.Meta, Header.Actions) for multi-region components instead of many props
Use Tailwind CSS scoped via .shade class; dark mode uses .dark

Files:

  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
apps/shade/src/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/**/*.{ts,tsx,js}: Use camelCase for function and variable names
Use the @ alias for internal imports (e.g., @/lib/utils)

Files:

  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
apps/shade/{src,test}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/{src,test}/**/*.{ts,tsx,js}: Run yarn lint after making changes to fix any ESLint errors and warnings before committing
Follow ESLint and tailwindcss/* plugin rules when writing styles

Files:

  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
apps/shade/src/components/features/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in src/components/features/*

Files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
🧠 Learnings (21)
📓 Common learnings
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Run `yarn lint` after making changes to fix any ESLint errors and warnings before committing
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props
Learnt from: niranjan-uma-shankar
Repo: TryGhost/Ghost PR: 24557
File: apps/admin-x-settings/src/components/settings/general/TimeZone.tsx:7-7
Timestamp: 2025-08-01T12:44:07.467Z
Learning: In Ghost development, PRs may depend on unpublished changes from SDK packages. When this occurs, TypeScript compilation errors for missing exports are expected and documented in the PR description until the dependency packages are published and updated. This is normal workflow for cross-repository feature development.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Applies to ghost/i18n/locales/en/ghost.json : Add UI translations to `ghost/i18n/locales/en/ghost.json` for Admin UI features
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25501
File: apps/shade/src/hooks/use-mobile.tsx:5-10
Timestamp: 2025-12-09T12:37:30.664Z
Learning: The Ghost admin apps/shade/** components do not use SSR, so accessing browser APIs like `navigator`, `window`, or `document` at module load time is safe and does not require typeof guards.
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.{ts,tsx} : Prefer less comments and give things clear names

Applied to files:

  • apps/comments-ui/src/app-context.ts
  • apps/admin-x-settings/src/typings.d.ts
  • apps/stats/src/types/svg.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration

Applied to files:

  • apps/comments-ui/src/app-context.ts
  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
📚 Learning: 2025-08-12T18:33:15.524Z
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.

Applied to files:

  • e2e/package.json
📚 Learning: 2025-08-26T16:47:28.150Z
Learnt from: troyciesco
Repo: TryGhost/Ghost PR: 24749
File: ghost/core/core/server/services/members/SingleUseTokenProvider.js:3-5
Timestamp: 2025-08-26T16:47:28.150Z
Learning: When checking for dependencies in Ghost project, ensure to look directly in the specific package.json files rather than relying only on automated searches, as otplib dependency exists at line 204 in ghost/core/package.json version "12.0.1"

Applied to files:

  • e2e/package.json
📚 Learning: 2025-11-25T14:28:50.351Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Analytics using Tinybird should reference scripts in `ghost/core/core/server/data/tinybird/scripts/` and datafiles in `ghost/core/core/server/data/tinybird/`

Applied to files:

  • apps/stats/test/utils/tinybird-helpers.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props

Applied to files:

  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/index.ts : Place new UI components under `src/components/ui` and export them from `src/index.ts`

Applied to files:

  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/layout/**/*.{ts,tsx} : Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in `src/components/layout/*`

Applied to files:

  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case

Applied to files:

  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use Tailwind CSS scoped via `.shade` class; dark mode uses `.dark`

Applied to files:

  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
📚 Learning: 2025-11-25T11:58:51.652Z
Learnt from: ibalosh
Repo: TryGhost/Ghost PR: 25525
File: apps/shade/src/shade-app.tsx:4-4
Timestamp: 2025-11-25T11:58:51.652Z
Learning: In apps/shade, the app wrapper file should be named `src/shade-app.tsx` (kebab-case) while the component itself is exported as `ShadeApp` (PascalCase). Context providers should be placed in `src/providers/*` using kebab-case filenames.

Applied to files:

  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-12-09T12:37:23.267Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25501
File: apps/shade/src/hooks/use-mobile.tsx:5-10
Timestamp: 2025-12-09T12:37:23.267Z
Learning: In the Ghost repository, this guideline applies to the shade admin app. For files under apps/shade that access browser globals (navigator, window, document) at module load time, SSR is not used, so typeof guards are not required. Reviewers should verify that such files remain client-side only and that no SSR context is introduced; apply this understanding to similarly structured files under apps/shade.

Applied to files:

  • apps/shade/src/components/layout/view-header.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Always follow ADRs in `../adr/` folder (ADR-0001: AAA pattern, ADR-0002: Page Objects)

Applied to files:

  • apps/admin-x-framework/src/test/acceptance.ts
📚 Learning: 2025-10-09T15:31:06.587Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25118
File: apps/portal/src/actions.js:160-173
Timestamp: 2025-10-09T15:31:06.587Z
Learning: When reviewing PRs that introduce feature-flagged changes (e.g., `labs?.membersSigninOTCAlpha`), avoid suggesting modifications to non-flagged code paths unless they're directly related to the PR's objectives. Keep the scope focused on the feature-flag-specific changes only.

Applied to files:

  • apps/admin-x-framework/src/test/acceptance.ts
📚 Learning: 2025-10-21T13:04:40.040Z
Learnt from: jonatansberg
Repo: TryGhost/Ghost PR: 24967
File: apps/admin/vite.config.ts:1-8
Timestamp: 2025-10-21T13:04:40.040Z
Learning: In Vite config files (vite.config.ts, vite.config.js), __dirname can be used directly even when the package.json has "type": "module". Vite handles config file transpilation in a way that makes __dirname available, so no ESM shim (fileURLToPath/dirname) is needed.

Applied to files:

  • apps/admin/package.json
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/lib/utils.ts : Shared utilities (class merging, formatting, chart helpers) should be centralized in `src/lib/utils.ts`

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
📚 Learning: 2025-09-10T07:05:34.606Z
Learnt from: jonatansberg
Repo: TryGhost/Ghost PR: 24838
File: apps/shade/src/components/features/color-picker/color-picker.tsx:0-0
Timestamp: 2025-09-10T07:05:34.606Z
Learning: The Color library used in the color picker component handles input validation and clamping automatically, so manual validation of HSL and RGB values is not necessary when using Color constructors.

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)

Applied to files:

  • apps/stats/src/types/svg.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/hooks/**/*.{ts,tsx} : Custom React hooks should be placed in `src/hooks/*`

Applied to files:

  • apps/stats/src/types/svg.d.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/views/preferences/components/profile.tsx
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx
🪛 Biome (2.1.2)
apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx

[error] 17-18: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Web/web.tsx

[error] 26-27: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/activitypub/src/views/profile/profile.tsx

[error] 13-14: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Growth/growth.tsx

[error] 16-17: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

🔇 Additional comments (14)
apps/comments-ui/src/app-context.ts (1)

114-121: Bare catch in useLabs keeps behavior while cleaning up unused variable

Switching to a parameterless catch is correct here: the error object wasn’t used and the function still safely falls back to {} when context is unavailable. This aligns with modern JS and the ESLint update without changing runtime behavior.

apps/admin-x-settings/src/utils/url.ts (1)

30-32: LGTM!

Using optional catch binding (bare catch) is appropriate here since the error object was never used. This aligns with modern ES2019+ conventions and the ESLint plugin update.

e2e/package.json (1)

32-32: Version bump aligns with broader ESLint tooling upgrade.

The typescript-eslint update from 8.46.1 to 8.49.0 is a patch-level bump consistent with the PR's goal of updating ESLint tooling across the monorepo. Verify that the concurrent updates to eslint-plugin-ghost and the widespread catch block refactoring mentioned in the PR description work correctly with this version.

apps/admin/package.json (1)

41-41: Version bump consistent across the monorepo.

The typescript-eslint update to 8.49.0 matches the corresponding bump in e2e/package.json and supports the PR's goal of aligning ESLint tooling. The version is compatible with the already-pinned eslint 9.37.0. Ensure the ESLint configuration changes and linting rules work correctly with this version across the admin application.

apps/activitypub/src/views/preferences/components/profile.tsx (1)

307-314: Bare catch blocks here are fine and keep behavior unchanged

Both inner and outer catch blocks in handleCopy never referenced the error value, so switching to parameterless catch {} is safe and keeps the same toasts and setIsProcessing(false) behavior while satisfying the updated ESLint rules.

apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1)

53-58: LGTM!

The removal of the unused error parameter from the catch block is valid. The error object was never referenced, and this change aligns with ESLint's no-unused-vars rule while maintaining identical behavior. Based on learnings, error handling for ActivityPub mutations is centralized at the hook level, so this component-level catch appropriately focuses on local state management and user feedback.

apps/admin-x-framework/src/test/acceptance.ts (1)

127-132: Bare catch here is safe and behavior‑neutral

The caught error value wasn’t used, so switching to a parameterless catch keeps the same failure behavior while satisfying the lint rule. No concerns with this change.

apps/stats/test/utils/tinybird-helpers.ts (1)

84-89: LGTM! Bare catch is appropriate here.

The error parameter was unused, so removing it aligns with the updated ESLint rules. The behavior is preserved—JSON parse failures still result in errorMessage = bodyText.

apps/comments-ui/src/utils/api.ts (1)

312-319: LGTM! Bare catch is appropriate here.

The error parameter was unused, so the bare catch syntax aligns with the updated ESLint rules. The fallback behavior is preserved—failures default to an empty labs object.

apps/stats/src/hooks/use-feature-flag.tsx (1)

20-25: LGTM! Bare catch is appropriate here.

The error parameter was unused, so removing it aligns with the updated ESLint rules. The behavior is preserved—JSON parse failures still fall back to an empty object.

apps/activitypub/src/utils/image.ts (1)

11-28: LGTM! Bare catch is appropriate here.

The error parameter was unused, so the bare catch syntax aligns with the updated ESLint rules. The fallback behavior is preserved—conversion failures still return the original URL.

apps/activitypub/src/utils/get-username.ts (1)

5-9: LGTM! Bare catch is appropriate here.

The error parameter was unused, so removing it aligns with the updated ESLint rules. The behavior is preserved—URL parsing failures still return the default '@unknown@unknown' value.

apps/admin-x-framework/src/api/recommendations.ts (1)

25-29: LGTM! ESLint suppressions are appropriate for marker interfaces.

The @typescript-eslint/no-empty-object-type suppressions are correctly applied to these marker interfaces. Both RecommendationEditResponseType and RecommendationDeleteResponseType serve as nominal types for API responses, so the empty object types are intentional.

apps/shade/src/components/features/color-picker/color-picker.tsx (1)

369-390: Bare catch is appropriate here and keeps behavior unchanged

Switching from catch (error) to catch { is safe: the error value was never used, and invalid color inputs are still intentionally ignored. This cleanly satisfies the updated ESLint rules without altering runtime behavior.

Please confirm your usual lint/build pipeline (e.g. yarn lint apps/shade) passes with the updated ESLint plugin and this syntax in place.

@ibalosh ibalosh force-pushed the update-eslint-version branch from 57d944e to 571f135 Compare December 16, 2025 09:24
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: 0

♻️ Duplicate comments (3)
apps/admin-x-settings/src/typings.d.ts (1)

5-6: Consider using ES6 import type syntax instead of suppressing the linter.

A past review comment on a similar file suggested refactoring to modern ES6 import syntax. Instead of suppressing @typescript-eslint/no-require-imports, you can use import type * as React from 'react' which eliminates the need for the ESLint directive.

Apply this diff:

-    // eslint-disable-next-line @typescript-eslint/no-require-imports
-    import React = require('react');
+    import type * as React from 'react';

Note: This same pattern appears in apps/admin-x-design-system/src/typings.d.ts and apps/signup-form/src/typings.d.ts. Consider applying this refactor consistently across all three files.

apps/admin-x-design-system/src/typings.d.ts (1)

2-3: Duplicate: ES6 import type syntax preferred.

This issue was already identified in a previous review comment. The suggestion to use import type * as React from 'react' instead of suppressing the linter applies here.

apps/signup-form/src/typings.d.ts (1)

2-3: Consider ES6 import type syntax (same as other files).

This follows the same pattern as the other typings files. As suggested in previous reviews, refactoring to import type * as React from 'react' would eliminate the need for the ESLint suppression comment.

Apply this diff:

-    // eslint-disable-next-line @typescript-eslint/no-require-imports
-    import React = require('react');
+    import type * as React from 'react';
🧹 Nitpick comments (4)
apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (2)

39-40: Consider refactoring to an if statement for clarity.

While the ESLint suppression correctly addresses the warning, using a logical-AND expression for control flow is less clear than an explicit if statement. Refactoring would eliminate the need for the suppression and improve readability.

Apply this diff:

-                        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
-                        !!item.errors.label && clearError?.('label');
+                        if (item.errors.label) {
+                            clearError?.('label');
+                        }

67-68: Consider refactoring to an if statement for clarity.

Similar to the previous occurrence, this logical-AND expression for control flow would be clearer as an explicit if statement.

Apply this diff:

-                        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
-                        !!item.errors.url && clearError?.('url');
+                        if (item.errors.url) {
+                            clearError?.('url');
+                        }
apps/activitypub/src/views/profile/profile.tsx (1)

13-14: Address the root cause instead of suppressing the warning.

The empty ProfileProps interface serves no purpose since the component takes no props. Rather than suppressing the ESLint rule, consider one of these solutions:

Option 1 (simplest): Remove the interface and type parameter entirely:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface ProfileProps {}
-
-const Profile: React.FC<ProfileProps> = ({}) => {
+const Profile: React.FC = () => {

Option 2: If you want to be explicit about no props, use a type alias:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface ProfileProps {}
+type ProfileProps = Record<string, never>;
apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1)

16-17: Consider replacing empty interfaces with type aliases or removing them.

The ESLint suppressions silence the rule rather than addressing the underlying code smell. Empty interfaces are equivalent to {} and add unnecessary complexity. This pattern appears in three files:

  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx (lines 16-17)
  • apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (lines 17-18)
  • apps/posts/src/views/PostAnalytics/Web/web.tsx (lines 26-27)

Option 1 (Simplest): Use React.FC without type parameter

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
-
-const Growth: React.FC<postAnalyticsProps> = () => {
+const Growth: React.FC = () => {

Option 2: Use type alias (as suggested by Biome)

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type postAnalyticsProps = {};

Apply similar changes to the other two files.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57d944e and 571f135.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (57)
  • apps/activitypub/src/api/activitypub.ts (1 hunks)
  • apps/activitypub/src/components/modals/new-note-modal.tsx (1 hunks)
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts (3 hunks)
  • apps/activitypub/src/utils/get-username.ts (1 hunks)
  • apps/activitypub/src/utils/image.ts (1 hunks)
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1 hunks)
  • apps/activitypub/src/views/preferences/components/profile.tsx (1 hunks)
  • apps/activitypub/src/views/profile/profile.tsx (1 hunks)
  • apps/admin-x-design-system/src/global/sortable-list.tsx (1 hunks)
  • apps/admin-x-design-system/src/typings.d.ts (1 hunks)
  • apps/admin-x-design-system/src/utils/format-url.ts (1 hunks)
  • apps/admin-x-framework/src/api/config.ts (1 hunks)
  • apps/admin-x-framework/src/api/recommendations.ts (1 hunks)
  • apps/admin-x-framework/src/test/acceptance.ts (1 hunks)
  • apps/admin-x-framework/src/test/test-utils.tsx (1 hunks)
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts (2 hunks)
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (2 hunks)
  • apps/admin-x-settings/src/typings.d.ts (1 hunks)
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts (2 hunks)
  • apps/admin-x-settings/src/utils/url.ts (1 hunks)
  • apps/admin/eslint.config.js (0 hunks)
  • apps/admin/package.json (1 hunks)
  • apps/comments-ui/src/actions.ts (2 hunks)
  • apps/comments-ui/src/app-context.ts (1 hunks)
  • apps/comments-ui/src/components/content/forms/form.tsx (1 hunks)
  • apps/comments-ui/src/components/popup-box.tsx (1 hunks)
  • apps/comments-ui/src/typings.d.ts (1 hunks)
  • apps/comments-ui/src/utils/admin-api.ts (1 hunks)
  • apps/comments-ui/src/utils/api.ts (1 hunks)
  • apps/posts/src/hooks/use-post-success-modal.ts (1 hunks)
  • apps/posts/src/utils/link-helpers.ts (1 hunks)
  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Web/web.tsx (1 hunks)
  • apps/shade/src/components/features/color-picker/color-picker.tsx (1 hunks)
  • apps/shade/src/components/layout/header.tsx (7 hunks)
  • apps/shade/src/components/layout/heading.tsx (1 hunks)
  • apps/shade/src/components/layout/page.tsx (1 hunks)
  • apps/shade/src/components/layout/view-header.tsx (2 hunks)
  • apps/shade/src/components/ui/card.tsx (1 hunks)
  • apps/shade/src/lib/utils.ts (1 hunks)
  • apps/shade/src/typings.d.ts (1 hunks)
  • apps/signup-form/src/components/pages/form-page.tsx (1 hunks)
  • apps/signup-form/src/typings.d.ts (1 hunks)
  • apps/stats/src/hooks/use-feature-flag.tsx (1 hunks)
  • apps/stats/src/types/svg.d.ts (1 hunks)
  • apps/stats/src/utils/url-helpers.ts (5 hunks)
  • apps/stats/test/utils/tinybird-helpers.ts (1 hunks)
  • e2e/package.json (1 hunks)
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2 hunks)
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1 hunks)
  • package.json (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/admin/eslint.config.js
✅ Files skipped from review due to trivial changes (1)
  • apps/stats/src/utils/url-helpers.ts
🚧 Files skipped from review as they are similar to previous changes (31)
  • apps/stats/src/hooks/use-feature-flag.tsx
  • apps/admin-x-design-system/src/utils/format-url.ts
  • apps/comments-ui/src/components/popup-box.tsx
  • apps/admin-x-framework/src/test/test-utils.tsx
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts
  • apps/posts/src/hooks/use-post-success-modal.ts
  • apps/comments-ui/src/app-context.ts
  • apps/comments-ui/src/utils/admin-api.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx
  • apps/comments-ui/src/typings.d.ts
  • apps/admin/package.json
  • apps/stats/src/types/svg.d.ts
  • apps/comments-ui/src/utils/api.ts
  • apps/activitypub/src/api/activitypub.ts
  • apps/admin-x-framework/src/api/recommendations.ts
  • apps/activitypub/src/views/preferences/components/profile.tsx
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx
  • apps/shade/src/typings.d.ts
  • apps/admin-x-framework/src/api/config.ts
  • apps/shade/src/lib/utils.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx
  • apps/comments-ui/src/actions.ts
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts
  • apps/shade/src/components/ui/card.tsx
  • apps/activitypub/src/utils/get-username.ts
  • apps/admin-x-framework/src/test/acceptance.ts
  • package.json
  • apps/activitypub/src/utils/image.ts
  • apps/stats/test/utils/tinybird-helpers.ts
  • apps/shade/src/components/layout/header.tsx
🧰 Additional context used
📓 Path-based instructions (5)
apps/shade/src/components/layout/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in src/components/layout/*

Files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
apps/shade/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/components/**/*.{ts,tsx}: Use PascalCase for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Always forward and merge className prop with cn(...) utility function
Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration
Prefer compound subcomponents (e.g., Header.Title, Header.Meta, Header.Actions) for multi-region components instead of many props
Use Tailwind CSS scoped via .shade class; dark mode uses .dark

Files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/view-header.tsx
apps/shade/src/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/**/*.{ts,tsx,js}: Use camelCase for function and variable names
Use the @ alias for internal imports (e.g., @/lib/utils)

Files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/view-header.tsx
apps/shade/{src,test}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/{src,test}/**/*.{ts,tsx,js}: Run yarn lint after making changes to fix any ESLint errors and warnings before committing
Follow ESLint and tailwindcss/* plugin rules when writing styles

Files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/view-header.tsx
apps/shade/src/components/features/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in src/components/features/*

Files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
🧠 Learnings (31)
📓 Common learnings
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24673
File: ghost/i18n/lib/i18n.js:34-35
Timestamp: 2025-11-24T11:12:15.712Z
Learning: In the Ghost i18n package (ghost/i18n/lib/i18n.js), changing existing locale codes requires backwards compatibility handling for users who have already configured those locales. Such changes should be done in a separate PR with migration logic rather than included in feature PRs.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Run `yarn lint` after making changes to fix any ESLint errors and warnings before committing
Learnt from: niranjan-uma-shankar
Repo: TryGhost/Ghost PR: 24557
File: apps/admin-x-settings/src/components/settings/general/TimeZone.tsx:7-7
Timestamp: 2025-08-01T12:44:07.467Z
Learning: In Ghost development, PRs may depend on unpublished changes from SDK packages. When this occurs, TypeScript compilation errors for missing exports are expected and documented in the PR description until the dependency packages are published and updated. This is normal workflow for cross-repository feature development.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Admin-x React apps build to `apps/*/dist` using Vite, which are then copied by `ghost/admin/lib/asset-delivery` to `ghost/core/core/built/admin/assets/*`
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Applies to ghost/i18n/locales/en/ghost.json : Add UI translations to `ghost/i18n/locales/en/ghost.json` for Admin UI features
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use factory pattern for all test data creation instead of hard-coded data or direct database manipulation

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Follow the AAA (Arrange, Act, Assert) pattern in test structure

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Test names should be lowercase and follow the format 'what is tested - expected outcome'

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Manual login should not be used - authentication is automatic via fixture

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Always follow ADRs in `../adr/` folder (ADR-0001: AAA pattern, ADR-0002: Page Objects)

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use `getByTestId()` only when semantic locators are unavailable, and suggest adding `data-testid` to Ghost codebase when needed

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/components/modals/new-note-modal.tsx
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/components/modals/new-note-modal.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/layout/**/*.{ts,tsx} : Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in `src/components/layout/*`

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/admin-x-design-system/src/typings.d.ts
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/index.ts : Place new UI components under `src/components/ui` and export them from `src/index.ts`

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use Tailwind CSS scoped via `.shade` class; dark mode uses `.dark`

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.{ts,tsx} : Prefer less comments and give things clear names

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/admin-x-design-system/src/typings.d.ts
  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/signup-form/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/ui/**/*.{ts,tsx} : Atomic UI components should be placed in `src/components/ui/*` and each component must have a corresponding `*.stories.tsx` file next to it for Storybook documentation

Applied to files:

  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/admin-x-settings/src/typings.d.ts
  • apps/admin-x-design-system/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/features/**/*.{ts,tsx} : Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in `src/components/features/*`

Applied to files:

  • apps/shade/src/components/layout/page.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Always forward and merge `className` prop with `cn(...)` utility function

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
📚 Learning: 2025-12-09T12:37:23.267Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25501
File: apps/shade/src/hooks/use-mobile.tsx:5-10
Timestamp: 2025-12-09T12:37:23.267Z
Learning: In the Ghost repository, this guideline applies to the shade admin app. For files under apps/shade that access browser globals (navigator, window, document) at module load time, SSR is not used, so typeof guards are not required. Reviewers should verify that such files remain client-side only and that no SSR context is introduced; apply this understanding to similarly structured files under apps/shade.

Applied to files:

  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/view-header.tsx
📚 Learning: 2025-07-14T12:20:35.268Z
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24346
File: apps/admin-x-settings/src/components/settings/growth/Network.tsx:8-12
Timestamp: 2025-07-14T12:20:35.268Z
Learning: The Network component toggle in `apps/admin-x-settings/src/components/settings/growth/Network.tsx` is intentionally implemented as static UI with a no-op onChange handler, as part of a UI-first development approach before connecting actual ActivityPub functionality.

Applied to files:

  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx
📚 Learning: 2025-11-06T05:35:41.162Z
Learnt from: danielraffel
Repo: TryGhost/Ghost PR: 25366
File: apps/admin/src/layout/app-sidebar/NavHeader.tsx:13-23
Timestamp: 2025-11-06T05:35:41.162Z
Learning: In apps/admin/src/layout/app-sidebar/NavHeader.tsx, the React component dispatches a synthetic KeyboardEvent to trigger the Ember keymaster.js search modal shortcut. This approach is known to have cross-browser reliability issues but was deferred for architectural refactoring in a separate PR. The recommended fix is to expose a global function or custom DOM event from the Ember app instead of relying on synthetic keyboard events with keymaster.js.

Applied to files:

  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/hooks/**/*.{ts,tsx} : Custom React hooks should be placed in `src/hooks/*`

Applied to files:

  • apps/admin-x-design-system/src/typings.d.ts
📚 Learning: 2025-11-25T14:28:50.351Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Ember admin uses `AdminXComponent` to dynamically import React apps with Suspense and error boundaries

Applied to files:

  • apps/admin-x-design-system/src/typings.d.ts
📚 Learning: 2025-08-11T19:39:00.428Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.

Applied to files:

  • apps/posts/src/utils/link-helpers.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/lib/utils.ts : Shared utilities (class merging, formatting, chart helpers) should be centralized in `src/lib/utils.ts`

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
📚 Learning: 2025-09-10T07:05:34.606Z
Learnt from: jonatansberg
Repo: TryGhost/Ghost PR: 24838
File: apps/shade/src/components/features/color-picker/color-picker.tsx:0-0
Timestamp: 2025-09-10T07:05:34.606Z
Learning: The Color library used in the color picker component handles input validation and clamping automatically, so manual validation of HSL and RGB values is not necessary when using Color constructors.

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
📚 Learning: 2025-08-12T18:33:15.524Z
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.

Applied to files:

  • e2e/package.json
📚 Learning: 2025-08-26T16:47:28.150Z
Learnt from: troyciesco
Repo: TryGhost/Ghost PR: 24749
File: ghost/core/core/server/services/members/SingleUseTokenProvider.js:3-5
Timestamp: 2025-08-26T16:47:28.150Z
Learning: When checking for dependencies in Ghost project, ensure to look directly in the specific package.json files rather than relying only on automated searches, as otplib dependency exists at line 204 in ghost/core/package.json version "12.0.1"

Applied to files:

  • e2e/package.json
🪛 Biome (2.1.2)
apps/posts/src/views/PostAnalytics/Web/web.tsx

[error] 26-27: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Growth/growth.tsx

[error] 16-17: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/activitypub/src/views/profile/profile.tsx

[error] 13-14: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx

[error] 17-18: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

⏰ 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). (2)
  • GitHub Check: Build & Push Docker Image
  • GitHub Check: Setup
🔇 Additional comments (17)
apps/posts/src/utils/link-helpers.ts (1)

30-30: LGTM! Clean removal of unused catch parameter.

The parameterless catch syntax is appropriate here since the error wasn't being used. This aligns with the ESLint upgrade and removes unnecessary variable declarations.

apps/signup-form/src/components/pages/form-page.tsx (1)

36-39: LGTM! Clean refactor removing unused error parameter.

The change to a bare catch block is appropriate since the error parameter was never used. The error handling logic remains intact—setting loading state and displaying a user-friendly message.

apps/shade/src/components/features/color-picker/color-picker.tsx (1)

388-390: LGTM! Unused error binding removed.

The error parameter was unused in the catch block, and removing it aligns with the updated ESLint rules. The behavior remains unchanged—invalid colors are still ignored as intended.

apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1)

29-29: LGTM! Unused error variable correctly removed.

The error parameter was unused in the catch block, and removing it aligns with the ESLint 9+ upgrade. The error handling behavior remains unchanged.

apps/admin-x-settings/src/utils/url.ts (1)

30-30: LGTM! Unused error variable correctly removed.

The error parameter was unused in the catch block, and removing it aligns with the ESLint 9+ upgrade. The error handling behavior (returning false) remains unchanged.

apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1)

112-112: LGTM! Unused error variable correctly removed.

The error parameter was unused in the catch block, and removing it aligns with the ESLint 9+ upgrade. The error handling behavior (showing a generic error toast) remains unchanged.

apps/comments-ui/src/components/content/forms/form.tsx (1)

78-78: LGTM! Unused catch parameter correctly removed.

The error parameter was never referenced in the catch block, so removing it satisfies the updated ESLint rules without changing behavior.

apps/admin-x-settings/src/utils/link-to-github-releases.ts (2)

1-1: LGTM - appropriate type suppression for untyped subpath import.

The @ts-expect-error directive correctly handles the missing type definitions for the semver subpath import.


33-33: LGTM - removed unused error parameter.

Switching to a parameterless catch block is cleaner since the error wasn't being used.

apps/shade/src/components/layout/page.tsx (1)

4-5: LGTM - appropriate ESLint suppression for type alias pattern.

The no-empty-object-type suppression is correct for this interface, which serves as a named type alias extending React.HTMLAttributes<HTMLDivElement>. This pattern provides better API documentation and allows for future extensibility without breaking changes.

apps/shade/src/components/layout/heading.tsx (1)

4-5: LGTM - consistent ESLint suppression for type alias.

The no-empty-object-type suppression follows the same pattern as other layout components, providing a named type alias for React.HTMLAttributes<HTMLHeadingElement> that supports future extensibility.

apps/shade/src/components/layout/view-header.tsx (1)

4-5: LGTM - consistent ESLint suppression for subcomponent props.

The no-empty-object-type suppression is appropriate for ViewHeaderActionsProps, maintaining consistency with other layout component prop interfaces while providing a named type for the compound subcomponent pattern.

apps/admin-x-design-system/src/global/sortable-list.tsx (1)

16-18: ESLint directive update aligns with the plugin upgrade.

The change from @typescript-eslint/ban-types to @typescript-eslint/no-unsafe-function-type is the correct replacement for typescript-eslint v8. The TODO comment indicates awareness that a stricter type could be used, though this may be constrained by the @dnd-kit library's API.

apps/admin-x-framework/test/unit/hooks/use-form.test.ts (1)

206-208: LGTM - proper ESLint compliance fix.

Removing the unused error variable from these catch blocks is appropriate since the error is not referenced and these blocks only handle expected test exceptions.

Also applies to: 233-235

apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1)

55-58: LGTM - ESLint compliance with proper error handling.

The error variable was unused, and user-facing error handling via the toast message is preserved.

apps/activitypub/src/components/modals/new-note-modal.tsx (1)

96-104: LGTM - consistent with centralized error handling pattern.

The unused error variable is removed while preserving the error handling flow. Based on learnings, error handling for ActivityPub mutations is centralized at the hook level in use-activity-pub-queries.ts, making the error binding unnecessary here.

e2e/package.json (1)

32-32: Version 8.49.0 is current and secure.

The package version 8.49.0 is the latest release, with no direct vulnerabilities found in Snyk's vulnerability database. The minor version bump from 8.46.1 aligns with the PR objective to resolve dependency conflicts.

@ibalosh ibalosh enabled auto-merge (squash) December 16, 2025 09:28
@ibalosh ibalosh disabled auto-merge December 16, 2025 09:32
@github-actions
Copy link
Contributor

React E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 20262880714 -n playwright-report-react -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

1 similar comment
@github-actions
Copy link
Contributor

React E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 20262880714 -n playwright-report-react -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

@ibalosh ibalosh force-pushed the update-eslint-version branch from b26ccd4 to 1d9b041 Compare December 16, 2025 11:33
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: 2

♻️ Duplicate comments (2)
apps/comments-ui/src/typings.d.ts (1)

2-2: Previous review suggestion still applies.

The ESLint suppression is valid for this declaration file syntax. However, as noted in the previous review, consider implementing a config-level override for *.d.ts files to eliminate the need for individual suppressions across multiple declaration files.

apps/shade/src/typings.d.ts (1)

2-3: Use ES6 import type syntax instead of suppressing the linter.

A previous review comment already suggested replacing this CommonJS-style import with modern ES6 syntax. Since this PR is specifically updating ESLint to enforce stricter rules, now is the ideal time to fix the underlying issue rather than suppressing it.

As per coding guidelines, follow ESLint rules. Apply this diff:

-    // eslint-disable-next-line @typescript-eslint/no-require-imports
-    import React = require('react');
+    import type * as React from 'react';
🧹 Nitpick comments (11)
apps/comments-ui/src/components/popup-box.tsx (1)

6-8: Address the TODO: use proper typing for components with no props.

The ESLint directive update is correct for the migration. However, the TODO comment indicates this type definition needs attention. In TypeScript, {} doesn't mean "no props"—it means "any non-nullish value."

Consider one of these alternatives:

-// TODO: figure out what this type should be?
-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-type Props = {};
-
-const PopupBox: React.FC<Props> = () => {
+const PopupBox: React.FC = () => {

Or, if you prefer explicit typing:

-// TODO: figure out what this type should be?
-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-type Props = {};
+type Props = Record<string, never>;
 
 const PopupBox: React.FC<Props> = () => {

Do you want me to open an issue to track removing this TODO and fixing the Props type?

apps/admin-x-framework/src/api/config.ts (1)

6-7: Consider using type instead of interface to avoid the ESLint suppression.

The interface with an empty body triggers the no-empty-object-type rule. Since JSONArray is a simple recursive type alias with no need for declaration merging, using type is more idiomatic and eliminates the suppression:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface JSONArray extends Array<string|number|boolean|Date|JSONObject|JSONValue> {}
+type JSONArray = Array<string|number|boolean|Date|JSONObject|JSONValue>;

TypeScript handles circular references equally well with both approaches, but type is generally preferred for simple aliases in modern TypeScript.

apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1)

16-17: Consider using a type alias instead of an empty interface.

The empty postAnalyticsProps interface could be replaced with a type alias to avoid the ESLint suppression. Additionally, follow PascalCase convention for type names.

Apply this diff:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type PostAnalyticsProps = Record<string, never>;

And update the component signature:

-const Growth: React.FC<postAnalyticsProps> = () => {
+const Growth: React.FC<PostAnalyticsProps> = () => {
apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1)

17-18: Consider using a type alias instead of an empty interface.

The empty postAnalyticsProps interface could be replaced with a type alias to avoid the ESLint suppression. Additionally, follow PascalCase convention for type names.

Apply this diff:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type PostAnalyticsProps = Record<string, never>;

And update the component signature:

-const Newsletter: React.FC<postAnalyticsProps> = () => {
+const Newsletter: React.FC<PostAnalyticsProps> = () => {
apps/posts/src/views/PostAnalytics/Web/web.tsx (1)

26-27: Consider using a type alias instead of an empty interface.

The empty postAnalyticsProps interface could be replaced with a type alias to avoid the ESLint suppression. Additionally, follow PascalCase convention for type names.

Apply this diff:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type PostAnalyticsProps = Record<string, never>;

And update the component signature:

-const Web: React.FC<postAnalyticsProps> = () => {
+const Web: React.FC<PostAnalyticsProps> = () => {
apps/shade/src/components/layout/header.tsx (1)

11-12: Consider using type aliases for interfaces that don't add properties.

The six props interfaces (HeaderAboveProps, HeaderTitleProps, HeaderMetaProps, HeaderActionGroupProps, HeaderActionsProps, HeaderNavProps) extend PropsWithChildrenAndClassName without adding additional properties. Type aliases would be cleaner and avoid ESLint suppressions.

Apply this diff:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface HeaderAboveProps extends PropsWithChildrenAndClassName {}
+type HeaderAboveProps = PropsWithChildrenAndClassName;

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface HeaderTitleProps extends PropsWithChildrenAndClassName {}
+type HeaderTitleProps = PropsWithChildrenAndClassName;

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface HeaderMetaProps extends PropsWithChildrenAndClassName {}
+type HeaderMetaProps = PropsWithChildrenAndClassName;

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface HeaderActionGroupProps extends PropsWithChildrenAndClassName {}
+type HeaderActionGroupProps = PropsWithChildrenAndClassName;

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface HeaderActionsProps extends PropsWithChildrenAndClassName {}
+type HeaderActionsProps = PropsWithChildrenAndClassName;

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface HeaderNavProps extends PropsWithChildrenAndClassName {}
+type HeaderNavProps = PropsWithChildrenAndClassName;

Also applies to: 24-25, 40-41, 53-54, 66-67, 79-80

apps/activitypub/src/views/profile/profile.tsx (1)

13-14: Consider refactoring to a type alias.

The ESLint suppression resolves the immediate linting issue, but as Biome suggests, an empty interface is semantically equivalent to {}. If no props are expected, consider using a type alias instead:

type ProfileProps = Record<string, never>;

Or simply omit the props type if the component truly requires no props.

apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (1)

39-40: ESLint suppressions are justified.

The suppressions are necessary for the side-effect expressions (!!item.errors.X && clearError?.('X')). While unconventional, this pattern is functionally correct.

Optionally, consider refactoring to if statements for better readability:

-// eslint-disable-next-line @typescript-eslint/no-unused-expressions
-!!item.errors.label && clearError?.('label');
+if (item.errors.label) {
+    clearError?.('label');
+}

This avoids the need for ESLint suppressions and makes the intent clearer.

Also applies to: 67-68

apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1)

87-87: Bare catch is appropriate, but consider logging for debugging.

The error binding was unused, so the bare catch block is correct. However, for production debugging, consider logging the error even when showing a generic message to users.

Optionally add error logging:

-} catch {
+} catch (error) {
+    console.error('Failed to save recommendation:', error); // eslint-disable-line no-console
     showToast({
ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (1)

64-64: Bare catch blocks are appropriate, but consider logging for observability.

The error bindings were unused, so the bare catch blocks are correct. The service gracefully handles failures by returning undefined or null.

For server-side observability, consider adding error logging in the #fetchJSON method:

 } catch {
+    // Log network/DNS errors for observability
+    // (avoid logging in #castUrl as URL parse errors are expected)
     return undefined;
 }

This helps diagnose issues in production without changing the public API.

Also applies to: 68-68, 79-79

apps/comments-ui/src/actions.ts (1)

262-262: Bare catch blocks are appropriate for optimistic UI pattern.

The error bindings were unused—the catch blocks only revert the optimistic state changes. The bare catch syntax is correct.

For debugging failed like/unlike actions, consider logging errors:

-} catch {
+} catch (error) {
+    console.error('Failed to like comment:', error); // eslint-disable-line no-console
     dispatchAction('updateCommentLikeState', {id: comment.id, liked: false});
 }

This helps diagnose API failures while maintaining the same user experience.

Also applies to: 273-273

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b26ccd4 and 1d9b041.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (56)
  • apps/activitypub/src/api/activitypub.ts (1 hunks)
  • apps/activitypub/src/components/modals/new-note-modal.tsx (1 hunks)
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts (3 hunks)
  • apps/activitypub/src/utils/get-username.ts (1 hunks)
  • apps/activitypub/src/utils/image.ts (1 hunks)
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1 hunks)
  • apps/activitypub/src/views/preferences/components/profile.tsx (1 hunks)
  • apps/activitypub/src/views/profile/profile.tsx (1 hunks)
  • apps/admin-x-design-system/src/global/sortable-list.tsx (1 hunks)
  • apps/admin-x-design-system/src/typings.d.ts (1 hunks)
  • apps/admin-x-design-system/src/utils/format-url.ts (1 hunks)
  • apps/admin-x-framework/src/api/config.ts (1 hunks)
  • apps/admin-x-framework/src/api/recommendations.ts (1 hunks)
  • apps/admin-x-framework/src/test/acceptance.ts (1 hunks)
  • apps/admin-x-framework/src/test/test-utils.tsx (1 hunks)
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts (2 hunks)
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (2 hunks)
  • apps/admin-x-settings/src/typings.d.ts (1 hunks)
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts (2 hunks)
  • apps/admin-x-settings/src/utils/url.ts (1 hunks)
  • apps/admin/package.json (1 hunks)
  • apps/comments-ui/src/actions.ts (2 hunks)
  • apps/comments-ui/src/app-context.ts (1 hunks)
  • apps/comments-ui/src/components/content/forms/form.tsx (1 hunks)
  • apps/comments-ui/src/components/popup-box.tsx (1 hunks)
  • apps/comments-ui/src/typings.d.ts (1 hunks)
  • apps/comments-ui/src/utils/admin-api.ts (1 hunks)
  • apps/comments-ui/src/utils/api.ts (1 hunks)
  • apps/posts/src/hooks/use-post-success-modal.ts (1 hunks)
  • apps/posts/src/utils/link-helpers.ts (1 hunks)
  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Web/web.tsx (1 hunks)
  • apps/shade/src/components/features/color-picker/color-picker.tsx (1 hunks)
  • apps/shade/src/components/layout/header.tsx (7 hunks)
  • apps/shade/src/components/layout/heading.tsx (1 hunks)
  • apps/shade/src/components/layout/page.tsx (1 hunks)
  • apps/shade/src/components/layout/view-header.tsx (2 hunks)
  • apps/shade/src/components/ui/card.tsx (1 hunks)
  • apps/shade/src/lib/utils.ts (1 hunks)
  • apps/shade/src/typings.d.ts (1 hunks)
  • apps/signup-form/src/components/pages/form-page.tsx (1 hunks)
  • apps/signup-form/src/typings.d.ts (1 hunks)
  • apps/stats/src/hooks/use-feature-flag.tsx (1 hunks)
  • apps/stats/src/types/svg.d.ts (1 hunks)
  • apps/stats/src/utils/url-helpers.ts (5 hunks)
  • apps/stats/test/utils/tinybird-helpers.ts (1 hunks)
  • e2e/package.json (1 hunks)
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2 hunks)
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1 hunks)
  • package.json (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx
🚧 Files skipped from review as they are similar to previous changes (28)
  • apps/shade/src/lib/utils.ts
  • apps/comments-ui/src/utils/api.ts
  • apps/posts/src/utils/link-helpers.ts
  • apps/stats/src/utils/url-helpers.ts
  • apps/signup-form/src/components/pages/form-page.tsx
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
  • apps/activitypub/src/api/activitypub.ts
  • apps/comments-ui/src/utils/admin-api.ts
  • apps/admin-x-framework/src/api/recommendations.ts
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts
  • apps/shade/src/components/ui/card.tsx
  • apps/admin-x-design-system/src/typings.d.ts
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
  • apps/activitypub/src/views/preferences/components/profile.tsx
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx
  • e2e/package.json
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx
  • apps/stats/src/types/svg.d.ts
  • apps/activitypub/src/utils/image.ts
  • apps/shade/src/components/layout/page.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/activitypub/src/utils/get-username.ts
  • apps/shade/src/components/layout/heading.tsx
  • apps/admin-x-settings/src/utils/url.ts
  • apps/admin/package.json
  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/signup-form/src/typings.d.ts
🧰 Additional context used
📓 Path-based instructions (4)
apps/shade/src/components/layout/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in src/components/layout/*

Files:

  • apps/shade/src/components/layout/header.tsx
apps/shade/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/components/**/*.{ts,tsx}: Use PascalCase for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Always forward and merge className prop with cn(...) utility function
Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration
Prefer compound subcomponents (e.g., Header.Title, Header.Meta, Header.Actions) for multi-region components instead of many props
Use Tailwind CSS scoped via .shade class; dark mode uses .dark

Files:

  • apps/shade/src/components/layout/header.tsx
apps/shade/src/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/**/*.{ts,tsx,js}: Use camelCase for function and variable names
Use the @ alias for internal imports (e.g., @/lib/utils)

Files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
apps/shade/{src,test}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/{src,test}/**/*.{ts,tsx,js}: Run yarn lint after making changes to fix any ESLint errors and warnings before committing
Follow ESLint and tailwindcss/* plugin rules when writing styles

Files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
🧠 Learnings (33)
📓 Common learnings
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Run `yarn lint` after making changes to fix any ESLint errors and warnings before committing
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use Tailwind CSS scoped via `.shade` class; dark mode uses `.dark`
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use `camelCase` for function and variable names
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/layout/**/*.{ts,tsx} : Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in `src/components/layout/*`
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/lib/utils.ts : Shared utilities (class merging, formatting, chart helpers) should be centralized in `src/lib/utils.ts`
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Always follow ADRs in `../adr/` folder (ADR-0001: AAA pattern, ADR-0002: Page Objects)

Applied to files:

  • apps/admin-x-framework/src/test/acceptance.ts
📚 Learning: 2025-10-09T15:31:06.587Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25118
File: apps/portal/src/actions.js:160-173
Timestamp: 2025-10-09T15:31:06.587Z
Learning: When reviewing PRs that introduce feature-flagged changes (e.g., `labs?.membersSigninOTCAlpha`), avoid suggesting modifications to non-flagged code paths unless they're directly related to the PR's objectives. Keep the scope focused on the feature-flag-specific changes only.

Applied to files:

  • apps/admin-x-framework/src/test/acceptance.ts
📚 Learning: 2025-11-06T05:35:41.162Z
Learnt from: danielraffel
Repo: TryGhost/Ghost PR: 25366
File: apps/admin/src/layout/app-sidebar/NavHeader.tsx:13-23
Timestamp: 2025-11-06T05:35:41.162Z
Learning: In apps/admin/src/layout/app-sidebar/NavHeader.tsx, the React component dispatches a synthetic KeyboardEvent to trigger the Ember keymaster.js search modal shortcut. This approach is known to have cross-browser reliability issues but was deferred for architectural refactoring in a separate PR. The recommended fix is to expose a global function or custom DOM event from the Ember app instead of relying on synthetic keyboard events with keymaster.js.

Applied to files:

  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.{ts,tsx} : Prefer less comments and give things clear names

Applied to files:

  • apps/comments-ui/src/app-context.ts
  • apps/comments-ui/src/typings.d.ts
  • apps/comments-ui/src/actions.ts
  • apps/shade/src/typings.d.ts
  • apps/admin-x-settings/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration

Applied to files:

  • apps/comments-ui/src/app-context.ts
  • apps/comments-ui/src/typings.d.ts
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
  • apps/comments-ui/src/components/popup-box.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)

Applied to files:

  • apps/comments-ui/src/typings.d.ts
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/index.ts : Place new UI components under `src/components/ui` and export them from `src/index.ts`

Applied to files:

  • apps/comments-ui/src/typings.d.ts
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
  • apps/comments-ui/src/components/popup-box.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles

Applied to files:

  • apps/comments-ui/src/typings.d.ts
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-08-01T12:44:07.467Z
Learnt from: niranjan-uma-shankar
Repo: TryGhost/Ghost PR: 24557
File: apps/admin-x-settings/src/components/settings/general/TimeZone.tsx:7-7
Timestamp: 2025-08-01T12:44:07.467Z
Learning: In Ghost development, PRs may depend on unpublished changes from SDK packages. When this occurs, TypeScript compilation errors for missing exports are expected and documented in the PR description until the dependency packages are published and updated. This is normal workflow for cross-repository feature development.

Applied to files:

  • apps/comments-ui/src/typings.d.ts
  • package.json
📚 Learning: 2025-03-13T09:02:50.102Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/views/Feed/components/NewPostModal.tsx:29-34
Timestamp: 2025-03-13T09:02:50.102Z
Learning: In the Ghost ActivityPub module, error handling for mutations is handled at the hook level (in use-activity-pub-queries.ts) rather than in individual components. This allows for centralized error handling across the application.

Applied to files:

  • apps/activitypub/src/components/modals/new-note-modal.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/layout/**/*.{ts,tsx} : Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in `src/components/layout/*`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use Tailwind CSS scoped via `.shade` class; dark mode uses `.dark`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-25T11:58:51.652Z
Learnt from: ibalosh
Repo: TryGhost/Ghost PR: 25525
File: apps/shade/src/shade-app.tsx:4-4
Timestamp: 2025-11-25T11:58:51.652Z
Learning: In apps/shade, the app wrapper file should be named `src/shade-app.tsx` (kebab-case) while the component itself is exported as `ShadeApp` (PascalCase). Context providers should be placed in `src/providers/*` using kebab-case filenames.

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/features/**/*.{ts,tsx} : Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in `src/components/features/*`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/ui/**/*.{ts,tsx} : Atomic UI components should be placed in `src/components/ui/*` and each component must have a corresponding `*.stories.tsx` file next to it for Storybook documentation

Applied to files:

  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/lib/utils.ts : Shared utilities (class merging, formatting, chart helpers) should be centralized in `src/lib/utils.ts`

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Always forward and merge `className` prop with `cn(...)` utility function

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-12-09T12:37:23.267Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25501
File: apps/shade/src/hooks/use-mobile.tsx:5-10
Timestamp: 2025-12-09T12:37:23.267Z
Learning: In the Ghost repository, this guideline applies to the shade admin app. For files under apps/shade that access browser globals (navigator, window, document) at module load time, SSR is not used, so typeof guards are not required. Reviewers should verify that such files remain client-side only and that no SSR context is introduced; apply this understanding to similarly structured files under apps/shade.

Applied to files:

  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-25T14:28:50.351Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Analytics using Tinybird should reference scripts in `ghost/core/core/server/data/tinybird/scripts/` and datafiles in `ghost/core/core/server/data/tinybird/`

Applied to files:

  • apps/stats/test/utils/tinybird-helpers.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Run `yarn lint` after making changes to fix any ESLint errors and warnings before committing

Applied to files:

  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/hooks/**/*.{ts,tsx} : Custom React hooks should be placed in `src/hooks/*`

Applied to files:

  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use `camelCase` for function and variable names

Applied to files:

  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-05T16:42:12.989Z
Learnt from: rob-ghost
Repo: TryGhost/Ghost PR: 25356
File: apps/admin/test-utils/fixtures/query-client.tsx:17-35
Timestamp: 2025-11-05T16:42:12.989Z
Learning: In apps/admin/test-utils/fixtures/query-client.tsx, the createTestQueryClient function is intentionally duplicated from admin-x-framework to reduce external dependencies in the admin app's test utilities.

Applied to files:

  • apps/admin-x-framework/src/test/test-utils.tsx
📚 Learning: 2025-05-27T15:35:31.779Z
Learnt from: 9larsons
Repo: TryGhost/Ghost PR: 23550
File: apps/admin-x-framework/test/utils/mockFetch.ts:9-9
Timestamp: 2025-05-27T15:35:31.779Z
Learning: The codebase is currently using Vitest 0.34.3, which has limited type definitions. Modern Vitest types like MockedFunction are not available in this version, so `any` types are used as a temporary workaround in test utilities.

Applied to files:

  • apps/admin-x-framework/src/test/test-utils.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Use the existing setup (Vitest + Testing Library + jsdom) when adding tests

Applied to files:

  • apps/admin-x-framework/src/test/test-utils.tsx
📚 Learning: 2025-09-02T13:06:50.918Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24779
File: ghost/core/test/unit/server/services/members/members-api/controllers/RouterController.test.js:1388-1392
Timestamp: 2025-09-02T13:06:50.918Z
Learning: In Ghost's test files, sinon.restore() in afterEach hooks handles cleanup of all sinon modifications including fake timers, spies, and stubs, so explicit restoration of individual sinon features like clock.restore() is typically not needed when there's a top-level sinon.restore() call.

Applied to files:

  • apps/admin-x-framework/src/test/test-utils.tsx
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Never use hard-coded waits like `waitForTimeout()`

Applied to files:

  • apps/admin-x-framework/src/test/test-utils.tsx
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use Playwright's auto-waiting capabilities and run tests multiple times to ensure stability

Applied to files:

  • apps/admin-x-framework/src/test/test-utils.tsx
📚 Learning: 2025-08-12T18:33:15.524Z
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.

Applied to files:

  • package.json
📚 Learning: 2025-08-26T16:47:28.150Z
Learnt from: troyciesco
Repo: TryGhost/Ghost PR: 24749
File: ghost/core/core/server/services/members/SingleUseTokenProvider.js:3-5
Timestamp: 2025-08-26T16:47:28.150Z
Learning: When checking for dependencies in Ghost project, ensure to look directly in the specific package.json files rather than relying only on automated searches, as otplib dependency exists at line 204 in ghost/core/package.json version "12.0.1"

Applied to files:

  • package.json
🪛 Biome (2.1.2)
apps/posts/src/views/PostAnalytics/Growth/growth.tsx

[error] 16-17: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Web/web.tsx

[error] 26-27: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/activitypub/src/views/profile/profile.tsx

[error] 13-14: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx

[error] 17-18: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

🔇 Additional comments (14)
apps/admin-x-settings/src/typings.d.ts (1)

5-5: LGTM! Targeted suppression for declaration file syntax.

The suppression is appropriately scoped and necessary for the legacy TypeScript import syntax used in this SVG module declaration. This is a standard pattern in .d.ts files.

apps/shade/src/components/layout/header.tsx (1)

92-122: Excellent compound component architecture!

The Header component implementation follows best practices:

  • Uses compound subcomponents (Header.Title, Header.Meta, Header.Actions) as recommended in the coding guidelines
  • Implements CVA for variant management
  • Properly forwards and merges className with cn() utility
  • Uses grid-area attributes for flexible layout control

Based on learnings and coding guidelines.

apps/activitypub/src/components/modals/new-note-modal.tsx (1)

96-104: LGTM! Bare catch block is appropriate here.

The error variable was unused in the catch block, so removing the binding aligns with modern ESLint best practices. The error handling behavior (calling onReplyError and managing loading state) is preserved.

apps/admin-x-design-system/src/global/sortable-list.tsx (1)

16-18: LGTM! ESLint directive updated for TypeScript-ESLint v8.

The rule name change from ban-types to no-unsafe-function-type reflects the update to TypeScript-ESLint v8. The TODO comment correctly notes this is known tech debt for future refinement.

apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1)

53-59: LGTM! Appropriate bare catch block.

The error variable was not used in the catch block. The error handling (toast notification and loading state management) functions correctly without the error binding.

apps/stats/test/utils/tinybird-helpers.ts (1)

84-89: LGTM! Bare catch is appropriate for intentionally ignored errors.

The error was already marked as unused with the underscore prefix. The bare catch syntax is cleaner and the fallback to bodyText on JSON parse failure is preserved.

apps/comments-ui/src/app-context.ts (1)

114-121: LGTM! Bare catch with appropriate fallback.

The error variable was unused, and the fallback behavior (returning an empty object) is preserved. This is a clean simplification.

apps/admin-x-framework/src/test/test-utils.tsx (1)

238-241: LGTM! ESLint directive correctly updated.

The rule name changed from no-var-requires to no-require-imports in TypeScript-ESLint v8. The dynamic require is appropriate for this test utility context.

apps/posts/src/hooks/use-post-success-modal.ts (1)

156-169: LGTM! Bare catch appropriate for silently handling localStorage errors.

The error was intentionally ignored as indicated by the comment. The bare catch syntax is cleaner for this use case where localStorage failures should be silently handled.

apps/stats/src/hooks/use-feature-flag.tsx (1)

22-22: LGTM!

The transition to a bare catch block is appropriate here. The error was already being ignored, and the fallback behavior remains correct.

apps/admin-x-design-system/src/utils/format-url.ts (1)

48-48: LGTM!

The bare catch block is appropriate for URL parsing errors. The fallback behavior correctly preserves the original URL when parsing fails.

apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1)

29-29: LGTM!

The bare catch block is appropriate for URL validation. The error message is set regardless of the specific parse error, making the error binding unnecessary.

apps/admin-x-framework/src/test/acceptance.ts (1)

130-130: LGTM!

The bare catch block is appropriate in this test utility. The original error is re-thrown as a more descriptive error message, so the caught error binding was unused.

package.json (1)

81-83: Resolution pinning looks correct.

Pinning all three @typescript-eslint packages to 8.49.0 ensures compatibility across the monorepo. These versions are current, published recently as the latest releases, and no security vulnerabilities are present. This is the correct approach when resolving peer dependency conflicts with the updated ghost plugin.

Comment on lines +78 to 81
} catch {
setProgress('error');
return;
}
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 | 🟠 Major

Add error logging for observability.

The bare catch block swallows the error without logging, making it impossible to debug form submission failures in production. While the UI correctly shows an error state, developers need visibility into why submissions fail (network issues, validation errors, server errors, etc.).

Consider adding at least console error logging:

-        } catch {
+        } catch (error) {
+            console.error('Form submission failed:', error);
             setProgress('error');
             return;
         }
📝 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
} catch {
setProgress('error');
return;
}
} catch (error) {
console.error('Form submission failed:', error);
setProgress('error');
return;
}
🤖 Prompt for AI Agents
In apps/comments-ui/src/components/content/forms/form.tsx around lines 78 to 81,
the catch block suppresses errors; update it to accept the caught error (e) and
log it for observability before setting progress to 'error' and returning. Use a
clear, contextual log entry (e.g., console.error or logger.error) that includes
the error object and a short message indicating form submission failure so
developers can see stack/response details in production logs.

@ibalosh ibalosh force-pushed the update-eslint-version branch from 1d9b041 to b586a7f Compare December 16, 2025 11:42
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: 0

♻️ Duplicate comments (4)
apps/admin-x-design-system/src/typings.d.ts (1)

2-3: Prefer ES6 import type syntax over suppressing the linter.

Instead of suppressing the @typescript-eslint/no-require-imports rule, refactor to use modern ES6 import syntax. Since React is only used for type references here, import type is appropriate.

Apply this diff:

-    // eslint-disable-next-line @typescript-eslint/no-require-imports
-    import React = require('react');
+    import type * as React from 'react';
package.json (1)

96-96: Verify that version 3.4.4 exists on npm.

The version 3.4.4 may not be available on the npm registry. This will cause installation failures if the version doesn't exist.

Run the following script to verify:

#!/bin/bash
# Check available versions of eslint-plugin-ghost

npm view eslint-plugin-ghost versions --json | jq -r '.[]' | grep -E '^3\.4\.'
apps/shade/src/typings.d.ts (1)

2-3: Prefer ES6 import type syntax over suppressing the linter.

Instead of suppressing the @typescript-eslint/no-require-imports rule, refactor to use modern ES6 import syntax. Since React is only used for type references here, import type is appropriate.

As per coding guidelines, run yarn lint after making changes to ensure ESLint compliance.

Apply this diff:

-    // eslint-disable-next-line @typescript-eslint/no-require-imports
-    import React = require('react');
+    import type * as React from 'react';
apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx (1)

782-783: Remove errant backticks instead of suppressing ESLint.

The line has two backticks (``) at the end that create an empty template literal expression, which triggers the no-unused-expressions rule. Rather than suppressing the rule, remove the backticks.

Apply this diff:

-            // eslint-disable-next-line @typescript-eslint/no-unused-expressions
-            const {meta: {sent_email_verification: [emailToVerify] = []} = {}} = await editNewsletter(formState); ``;
+            const {meta: {sent_email_verification: [emailToVerify] = []} = {}} = await editNewsletter(formState);
🧹 Nitpick comments (13)
apps/activitypub/src/utils/image.ts (1)

25-28: LGTM! Bare catch is appropriate here.

The removal of the unused error binding is correct and aligns with ESLint best practices. The fallback behavior is preserved.

For improved observability during debugging, consider logging the error before returning the fallback:

-    } catch {
+    } catch (error) {
+        console.warn('Failed to convert image URL to data URL:', error);
         // Return original URL as fallback if conversion fails
         return url;
     }

However, this is entirely optional and depends on your observability strategy.

apps/activitypub/src/views/profile/profile.tsx (1)

13-14: Consider converting to a type alias to eliminate the ESLint suppression.

Both Biome and TypeScript ESLint flag empty interfaces. Since ProfileProps has no properties, a type alias is more appropriate and removes the need for the suppression comment.

Apply this diff:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface ProfileProps {}
+type ProfileProps = Record<string, never>;

Alternatively, if the interface is a placeholder for future props, you could use:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface ProfileProps {}
+// Placeholder for future props
+type ProfileProps = Record<string, never>;
apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1)

112-112: LGTM! Clean lint fix.

Removing the unused error binding is correct and aligns with the ESLint upgrade. The error was not being used in the catch block, so this change makes that explicit.

Optional suggestion: Consider logging the error to the console for debugging purposes, even though it's not displayed to users:

-            } catch {
+            } catch (error) {
+                // eslint-disable-next-line no-console
+                console.error('Failed to add recommendation:', error);
                 showToast({

This would help with debugging in development without changing the user experience, though it's outside the scope of this ESLint upgrade PR.

apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (1)

39-40: Consider refactoring to explicit if statements.

The ESLint suppressions silence warnings about unused expressions in the condition && sideEffect() pattern. While this pattern is idiomatic, refactoring to explicit if statements would be clearer and eliminate the need for suppression comments.

Apply this diff to refactor both occurrences:

                     if (e.key === 'Enter') {
                         e.preventDefault();
                         addItem?.();
                     }
-                    // eslint-disable-next-line @typescript-eslint/no-unused-expressions
-                    !!item.errors.label && clearError?.('label');
+                    if (item.errors.label) {
+                        clearError?.('label');
+                    }
                 }}
                     if (e.key === 'Enter') {
                         e.preventDefault();
                         const urls = formatUrl((e.target as HTMLInputElement).value, baseUrl, true);
                         updateItem?.({url: urls.save || ''});
                         addItem?.();
                     }
-                    // eslint-disable-next-line @typescript-eslint/no-unused-expressions
-                    !!item.errors.url && clearError?.('url');
+                    if (item.errors.url) {
+                        clearError?.('url');
+                    }
                 }}

Also applies to: 67-68

apps/admin-x-framework/src/test/test-utils.tsx (1)

239-240: ESLint directive update looks good.

The updated rule name correctly aligns with the newer TypeScript ESLint plugin.

Optional: Consider modernizing to dynamic import.

The require() could be replaced with a dynamic import() for better TypeScript alignment:

-    // eslint-disable-next-line @typescript-eslint/no-require-imports
-    const vitest = require('vitest');
+    const vitest = await import('vitest');

Note: This would require making mockTimers() async or restructuring the function. Given the current approach works and the PR focuses on ESLint updates, this can be deferred.

ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (1)

47-70: Consider adding error logging for observability.

The removal of unused error bindings is correct and aligns with the ESLint update. However, the comment on lines 45-46 mentions catching DNS errors specifically, and these errors are now silently swallowed without any logging.

Consider adding basic error logging to aid debugging in production:

-        } catch {
+        } catch (err) {
+            // Log for observability, but don't throw to avoid HTTP 500
+            // (covers DNS errors and other request failures)
             return undefined;
         }

If logging infrastructure is available, capturing these errors would make troubleshooting recommendation fetching issues much easier.

apps/posts/src/views/PostAnalytics/Web/web.tsx (1)

26-27: Consider using a type alias instead of suppressing the linter.

Rather than suppressing the no-empty-object-type rule, you can use a more idiomatic approach:

Option 1 (simplest): Remove the interface entirely and use React.FC without explicit props:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
-
-const Web: React.FC<postAnalyticsProps> = () => {
+const Web: React.FC = () => {

Option 2: Use a type alias with PascalCase naming:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type PostAnalyticsProps = Record<string, never>;

-const Web: React.FC<postAnalyticsProps> = () => {
+const Web: React.FC<PostAnalyticsProps> = () => {
apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1)

17-18: Consider using a type alias instead of suppressing the linter.

Rather than suppressing the no-empty-object-type rule, you can use a more idiomatic approach:

Option 1 (simplest): Remove the interface entirely and use React.FC without explicit props:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
-
-const Newsletter: React.FC<postAnalyticsProps> = () => {
+const Newsletter: React.FC = () => {

Option 2: Use a type alias with PascalCase naming:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type PostAnalyticsProps = Record<string, never>;

-const Newsletter: React.FC<postAnalyticsProps> = () => {
+const Newsletter: React.FC<PostAnalyticsProps> = () => {
apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1)

16-17: Consider using a type alias instead of suppressing the linter.

Rather than suppressing the no-empty-object-type rule, you can use a more idiomatic approach:

Option 1 (simplest): Remove the interface entirely and use React.FC without explicit props:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
-
-const Growth: React.FC<postAnalyticsProps> = () => {
+const Growth: React.FC = () => {

Option 2: Use a type alias with PascalCase naming:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface postAnalyticsProps {}
+type PostAnalyticsProps = Record<string, never>;

-const Growth: React.FC<postAnalyticsProps> = () => {
+const Growth: React.FC<PostAnalyticsProps> = () => {
apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1)

87-87: LGTM: Unused error binding removed for ESLint compliance.

The error variable was not used in the catch block, so removing it aligns with the ESLint update objectives.

Optional: Consider logging the error for debugging.

While the generic user-facing message is appropriate, consider at least logging the error or passing it to handleError for debugging purposes, similar to how the delete operation handles errors (line 60). This would preserve error details for troubleshooting without exposing them to users.

-            } catch {
+            } catch (error) {
                 showToast({
                     title: 'Something went wrong',
                     type: 'error',
                     message: 'Please try again later.'
                 });
+                handleError(error, {withToast: false});
             }
apps/shade/src/components/layout/header.tsx (1)

11-90: Consider using type aliases to avoid ESLint suppressions.

The six subcomponent interfaces extend PropsWithChildrenAndClassName without adding properties, requiring ESLint suppression comments. Using type aliases instead of interface would accomplish the same goal without triggering the no-empty-object-type rule.

Apply this pattern to eliminate the suppression comments:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-interface HeaderAboveProps extends PropsWithChildrenAndClassName {}
+type HeaderAboveProps = PropsWithChildrenAndClassName;

Repeat for HeaderTitleProps, HeaderMetaProps, HeaderActionGroupProps, HeaderActionsProps, and HeaderNavProps.

apps/shade/src/components/ui/card.tsx (2)

219-220: Consider using a type alias instead of an empty interface.

The empty interface with an ESLint suppression can be replaced with a type alias for a cleaner implementation:

-// eslint-disable-next-line @typescript-eslint/no-empty-object-type
-export interface EmptyCardProps extends React.ComponentPropsWithoutRef<'div'> {}
+export type EmptyCardProps = React.ComponentPropsWithoutRef<'div'>;

This eliminates the need for the suppression comment while achieving the same result.


222-232: Consider leveraging the existing Card component to reduce duplication.

The EmptyCard component duplicates styling that exists in the Card component's 'outline' variant. While the current implementation is correct, you could reduce duplication by either:

  1. Using the Card component directly with variant="outline"
  2. Extracting shared styling into a reusable CVA variant

This would make future style updates easier to maintain across related components.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d9b041 and b586a7f.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (56)
  • apps/activitypub/src/api/activitypub.ts (1 hunks)
  • apps/activitypub/src/components/modals/new-note-modal.tsx (1 hunks)
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts (3 hunks)
  • apps/activitypub/src/utils/get-username.ts (1 hunks)
  • apps/activitypub/src/utils/image.ts (1 hunks)
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx (1 hunks)
  • apps/activitypub/src/views/preferences/components/profile.tsx (1 hunks)
  • apps/activitypub/src/views/profile/profile.tsx (1 hunks)
  • apps/admin-x-design-system/src/global/sortable-list.tsx (1 hunks)
  • apps/admin-x-design-system/src/typings.d.ts (1 hunks)
  • apps/admin-x-design-system/src/utils/format-url.ts (1 hunks)
  • apps/admin-x-framework/src/api/config.ts (1 hunks)
  • apps/admin-x-framework/src/api/recommendations.ts (1 hunks)
  • apps/admin-x-framework/src/test/acceptance.ts (1 hunks)
  • apps/admin-x-framework/src/test/test-utils.tsx (1 hunks)
  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts (2 hunks)
  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal-confirm.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/edit-recommendation-modal.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx (1 hunks)
  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx (2 hunks)
  • apps/admin-x-settings/src/typings.d.ts (1 hunks)
  • apps/admin-x-settings/src/utils/link-to-github-releases.ts (2 hunks)
  • apps/admin-x-settings/src/utils/url.ts (1 hunks)
  • apps/admin/package.json (1 hunks)
  • apps/comments-ui/src/actions.ts (2 hunks)
  • apps/comments-ui/src/app-context.ts (1 hunks)
  • apps/comments-ui/src/components/content/forms/form.tsx (1 hunks)
  • apps/comments-ui/src/components/popup-box.tsx (1 hunks)
  • apps/comments-ui/src/typings.d.ts (1 hunks)
  • apps/comments-ui/src/utils/admin-api.ts (1 hunks)
  • apps/comments-ui/src/utils/api.ts (1 hunks)
  • apps/posts/src/hooks/use-post-success-modal.ts (1 hunks)
  • apps/posts/src/utils/link-helpers.ts (1 hunks)
  • apps/posts/src/views/PostAnalytics/Growth/growth.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx (1 hunks)
  • apps/posts/src/views/PostAnalytics/Web/web.tsx (1 hunks)
  • apps/shade/src/components/features/color-picker/color-picker.tsx (1 hunks)
  • apps/shade/src/components/layout/header.tsx (7 hunks)
  • apps/shade/src/components/layout/heading.tsx (1 hunks)
  • apps/shade/src/components/layout/page.tsx (1 hunks)
  • apps/shade/src/components/layout/view-header.tsx (2 hunks)
  • apps/shade/src/components/ui/card.tsx (1 hunks)
  • apps/shade/src/lib/utils.ts (1 hunks)
  • apps/shade/src/typings.d.ts (1 hunks)
  • apps/signup-form/src/components/pages/form-page.tsx (1 hunks)
  • apps/signup-form/src/typings.d.ts (1 hunks)
  • apps/stats/src/hooks/use-feature-flag.tsx (1 hunks)
  • apps/stats/src/types/svg.d.ts (1 hunks)
  • apps/stats/src/utils/url-helpers.ts (5 hunks)
  • apps/stats/test/utils/tinybird-helpers.ts (1 hunks)
  • e2e/package.json (1 hunks)
  • ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2 hunks)
  • ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1 hunks)
  • package.json (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/stats/src/types/svg.d.ts
🚧 Files skipped from review as they are similar to previous changes (32)
  • e2e/package.json
  • apps/stats/test/utils/tinybird-helpers.ts
  • apps/comments-ui/src/components/popup-box.tsx
  • apps/admin-x-framework/src/test/acceptance.ts
  • apps/activitypub/src/views/preferences/components/bluesky-sharing.tsx
  • apps/activitypub/src/components/modals/new-note-modal.tsx
  • apps/activitypub/src/api/activitypub.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/add-recommendation-modal.tsx
  • apps/signup-form/src/typings.d.ts
  • apps/comments-ui/src/components/content/forms/form.tsx
  • apps/shade/src/lib/utils.ts
  • apps/admin-x-design-system/src/utils/format-url.ts
  • apps/stats/src/hooks/use-feature-flag.tsx
  • apps/posts/src/utils/link-helpers.ts
  • apps/admin-x-design-system/src/global/sortable-list.tsx
  • apps/activitypub/src/views/preferences/components/profile.tsx
  • apps/shade/src/components/layout/view-header.tsx
  • apps/activitypub/src/hooks/use-activity-pub-queries.ts
  • apps/comments-ui/src/utils/admin-api.ts
  • apps/posts/src/hooks/use-post-success-modal.ts
  • apps/admin/package.json
  • apps/admin-x-framework/src/api/config.ts
  • apps/comments-ui/src/utils/api.ts
  • apps/admin-x-settings/src/utils/url.ts
  • apps/shade/src/components/layout/page.tsx
  • apps/stats/src/utils/url-helpers.ts
  • apps/admin-x-settings/src/typings.d.ts
  • apps/signup-form/src/components/pages/form-page.tsx
  • apps/comments-ui/src/typings.d.ts
  • apps/admin-x-settings/src/components/settings/growth/recommendations/incoming-recommendation-list.tsx
  • apps/comments-ui/src/app-context.ts
  • apps/activitypub/src/utils/get-username.ts
🧰 Additional context used
📓 Path-based instructions (6)
apps/shade/src/components/features/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in src/components/features/*

Files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
apps/shade/src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/components/**/*.{ts,tsx}: Use PascalCase for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Always forward and merge className prop with cn(...) utility function
Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration
Prefer compound subcomponents (e.g., Header.Title, Header.Meta, Header.Actions) for multi-region components instead of many props
Use Tailwind CSS scoped via .shade class; dark mode uses .dark

Files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/ui/card.tsx
apps/shade/src/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/src/**/*.{ts,tsx,js}: Use camelCase for function and variable names
Use the @ alias for internal imports (e.g., @/lib/utils)

Files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/ui/card.tsx
apps/shade/{src,test}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

apps/shade/{src,test}/**/*.{ts,tsx,js}: Run yarn lint after making changes to fix any ESLint errors and warnings before committing
Follow ESLint and tailwindcss/* plugin rules when writing styles

Files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/ui/card.tsx
apps/shade/src/components/layout/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in src/components/layout/*

Files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
apps/shade/src/components/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/shade/AGENTS.md)

Atomic UI components should be placed in src/components/ui/* and each component must have a corresponding *.stories.tsx file next to it for Storybook documentation

Files:

  • apps/shade/src/components/ui/card.tsx
🧠 Learnings (38)
📓 Common learnings
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Run `yarn lint` after making changes to fix any ESLint errors and warnings before committing
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use Tailwind CSS scoped via `.shade` class; dark mode uses `.dark`
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use `camelCase` for function and variable names
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/lib/utils.ts : Shared utilities (class merging, formatting, chart helpers) should be centralized in `src/lib/utils.ts`
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/index.ts : Place new UI components under `src/components/ui` and export them from `src/index.ts`
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration
📚 Learning: 2025-11-05T16:42:12.989Z
Learnt from: rob-ghost
Repo: TryGhost/Ghost PR: 25356
File: apps/admin/test-utils/fixtures/query-client.tsx:17-35
Timestamp: 2025-11-05T16:42:12.989Z
Learning: In apps/admin/test-utils/fixtures/query-client.tsx, the createTestQueryClient function is intentionally duplicated from admin-x-framework to reduce external dependencies in the admin app's test utilities.

Applied to files:

  • apps/admin-x-framework/src/test/test-utils.tsx
📚 Learning: 2025-05-27T15:35:31.779Z
Learnt from: 9larsons
Repo: TryGhost/Ghost PR: 23550
File: apps/admin-x-framework/test/utils/mockFetch.ts:9-9
Timestamp: 2025-05-27T15:35:31.779Z
Learning: The codebase is currently using Vitest 0.34.3, which has limited type definitions. Modern Vitest types like MockedFunction are not available in this version, so `any` types are used as a temporary workaround in test utilities.

Applied to files:

  • apps/admin-x-framework/src/test/test-utils.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Use the existing setup (Vitest + Testing Library + jsdom) when adding tests

Applied to files:

  • apps/admin-x-framework/src/test/test-utils.tsx
📚 Learning: 2025-09-02T13:06:50.918Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24779
File: ghost/core/test/unit/server/services/members/members-api/controllers/RouterController.test.js:1388-1392
Timestamp: 2025-09-02T13:06:50.918Z
Learning: In Ghost's test files, sinon.restore() in afterEach hooks handles cleanup of all sinon modifications including fake timers, spies, and stubs, so explicit restoration of individual sinon features like clock.restore() is typically not needed when there's a top-level sinon.restore() call.

Applied to files:

  • apps/admin-x-framework/src/test/test-utils.tsx
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Never use hard-coded waits like `waitForTimeout()`

Applied to files:

  • apps/admin-x-framework/src/test/test-utils.tsx
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use Playwright's auto-waiting capabilities and run tests multiple times to ensure stability

Applied to files:

  • apps/admin-x-framework/src/test/test-utils.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use CVA (Class Variance Authority) for component variants when useful instead of heavy prop configuration

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/typings.d.ts
  • apps/admin-x-design-system/src/typings.d.ts
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/ui/card.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/lib/utils.ts : Shared utilities (class merging, formatting, chart helpers) should be centralized in `src/lib/utils.ts`

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use Tailwind CSS scoped via `.shade` class; dark mode uses `.dark`

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-09-10T07:05:34.606Z
Learnt from: jonatansberg
Repo: TryGhost/Ghost PR: 24838
File: apps/shade/src/components/features/color-picker/color-picker.tsx:0-0
Timestamp: 2025-09-10T07:05:34.606Z
Learning: The Color library used in the color picker component handles input validation and clamping automatically, so manual validation of HSL and RGB values is not necessary when using Color constructors.

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
📚 Learning: 2025-12-09T12:37:23.267Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25501
File: apps/shade/src/hooks/use-mobile.tsx:5-10
Timestamp: 2025-12-09T12:37:23.267Z
Learning: In the Ghost repository, this guideline applies to the shade admin app. For files under apps/shade that access browser globals (navigator, window, document) at module load time, SSR is not used, so typeof guards are not required. Reviewers should verify that such files remain client-side only and that no SSR context is introduced; apply this understanding to similarly structured files under apps/shade.

Applied to files:

  • apps/shade/src/components/features/color-picker/color-picker.tsx
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/ui/card.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use the `@` alias for internal imports (e.g., `@/lib/utils`)

Applied to files:

  • apps/shade/src/typings.d.ts
  • apps/admin-x-design-system/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/index.ts : Place new UI components under `src/components/ui` and export them from `src/index.ts`

Applied to files:

  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
  • apps/shade/src/components/ui/card.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Follow ESLint and `tailwindcss/*` plugin rules when writing styles

Applied to files:

  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/heading.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Use `PascalCase` for component identifiers in filenames while keeping ShadCN-generated files in kebab-case

Applied to files:

  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/{src,test}/**/*.{ts,tsx,js} : Run `yarn lint` after making changes to fix any ESLint errors and warnings before committing

Applied to files:

  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-25T11:58:51.652Z
Learnt from: ibalosh
Repo: TryGhost/Ghost PR: 25525
File: apps/shade/src/shade-app.tsx:4-4
Timestamp: 2025-11-25T11:58:51.652Z
Learning: In apps/shade, the app wrapper file should be named `src/shade-app.tsx` (kebab-case) while the component itself is exported as `ShadeApp` (PascalCase). Context providers should be placed in `src/providers/*` using kebab-case filenames.

Applied to files:

  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/hooks/**/*.{ts,tsx} : Custom React hooks should be placed in `src/hooks/*`

Applied to files:

  • apps/shade/src/typings.d.ts
  • apps/admin-x-design-system/src/typings.d.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/ui/**/*.{ts,tsx} : Atomic UI components should be placed in `src/components/ui/*` and each component must have a corresponding `*.stories.tsx` file next to it for Storybook documentation

Applied to files:

  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Prefer compound subcomponents (e.g., `Header.Title`, `Header.Meta`, `Header.Actions`) for multi-region components instead of many props

Applied to files:

  • apps/shade/src/typings.d.ts
  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.{ts,tsx} : Prefer less comments and give things clear names

Applied to files:

  • apps/shade/src/typings.d.ts
  • apps/admin-x-design-system/src/typings.d.ts
  • apps/shade/src/components/layout/heading.tsx
  • apps/comments-ui/src/actions.ts
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/**/*.{ts,tsx,js} : Use `camelCase` for function and variable names

Applied to files:

  • apps/shade/src/typings.d.ts
📚 Learning: 2025-11-25T14:28:50.351Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-25T14:28:50.351Z
Learning: Ember admin uses `AdminXComponent` to dynamically import React apps with Suspense and error boundaries

Applied to files:

  • apps/admin-x-design-system/src/typings.d.ts
📚 Learning: 2025-10-30T17:13:26.190Z
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.

Applied to files:

  • apps/admin-x-settings/src/components/settings/email/newsletters/newsletter-detail-modal.tsx
📚 Learning: 2025-07-14T12:20:35.268Z
Learnt from: sagzy
Repo: TryGhost/Ghost PR: 24346
File: apps/admin-x-settings/src/components/settings/growth/Network.tsx:8-12
Timestamp: 2025-07-14T12:20:35.268Z
Learning: The Network component toggle in `apps/admin-x-settings/src/components/settings/growth/Network.tsx` is intentionally implemented as static UI with a no-op onChange handler, as part of a UI-first development approach before connecting actual ActivityPub functionality.

Applied to files:

  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx
📚 Learning: 2025-11-06T05:35:41.162Z
Learnt from: danielraffel
Repo: TryGhost/Ghost PR: 25366
File: apps/admin/src/layout/app-sidebar/NavHeader.tsx:13-23
Timestamp: 2025-11-06T05:35:41.162Z
Learning: In apps/admin/src/layout/app-sidebar/NavHeader.tsx, the React component dispatches a synthetic KeyboardEvent to trigger the Ember keymaster.js search modal shortcut. This approach is known to have cross-browser reliability issues but was deferred for architectural refactoring in a separate PR. The recommended fix is to expose a global function or custom DOM event from the Ember app instead of relying on synthetic keyboard events with keymaster.js.

Applied to files:

  • apps/admin-x-settings/src/components/settings/site/navigation/navigation-item-editor.tsx
📚 Learning: 2025-08-12T18:33:15.524Z
Learnt from: cmraible
Repo: TryGhost/Ghost PR: 24658
File: ghost/admin/package.json:3-3
Timestamp: 2025-08-12T18:33:15.524Z
Learning: In Ghost's admin package.json, third-party packages like ember-cli-postcss, ember-exam, and ember-power-select have their own independent versioning schemes that are unrelated to Ghost's version numbers. Version number coincidences between Ghost versions and these packages should not trigger update suggestions.

Applied to files:

  • package.json
📚 Learning: 2025-08-01T12:44:07.467Z
Learnt from: niranjan-uma-shankar
Repo: TryGhost/Ghost PR: 24557
File: apps/admin-x-settings/src/components/settings/general/TimeZone.tsx:7-7
Timestamp: 2025-08-01T12:44:07.467Z
Learning: In Ghost development, PRs may depend on unpublished changes from SDK packages. When this occurs, TypeScript compilation errors for missing exports are expected and documented in the PR description until the dependency packages are published and updated. This is normal workflow for cross-repository feature development.

Applied to files:

  • package.json
📚 Learning: 2025-08-26T16:47:28.150Z
Learnt from: troyciesco
Repo: TryGhost/Ghost PR: 24749
File: ghost/core/core/server/services/members/SingleUseTokenProvider.js:3-5
Timestamp: 2025-08-26T16:47:28.150Z
Learning: When checking for dependencies in Ghost project, ensure to look directly in the specific package.json files rather than relying only on automated searches, as otplib dependency exists at line 204 in ghost/core/package.json version "12.0.1"

Applied to files:

  • package.json
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/layout/**/*.{ts,tsx} : Reusable layout containers (Page, Heading, Header, ViewHeader, ErrorPage) should be placed in `src/components/layout/*`

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/features/**/*.{ts,tsx} : Higher-level, opinionated components (e.g., PostShareModal, SourceTabs) should be placed in `src/components/features/*`

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-26T11:05:59.314Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: apps/shade/AGENTS.md:0-0
Timestamp: 2025-11-26T11:05:59.314Z
Learning: Applies to apps/shade/src/components/**/*.{ts,tsx} : Always forward and merge `className` prop with `cn(...)` utility function

Applied to files:

  • apps/shade/src/components/layout/heading.tsx
  • apps/shade/src/components/layout/header.tsx
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Test names should be lowercase and follow the format 'what is tested - expected outcome'

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use factory pattern for all test data creation instead of hard-coded data or direct database manipulation

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Follow the AAA (Arrange, Act, Assert) pattern in test structure

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Always follow ADRs in `../adr/` folder (ADR-0001: AAA pattern, ADR-0002: Page Objects)

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
📚 Learning: 2025-11-24T17:29:43.865Z
Learnt from: CR
Repo: TryGhost/Ghost PR: 0
File: e2e/AGENTS.md:0-0
Timestamp: 2025-11-24T17:29:43.865Z
Learning: Applies to e2e/**/*.test.ts : Use `getByTestId()` only when semantic locators are unavailable, and suggest adding `data-testid` to Ghost codebase when needed

Applied to files:

  • apps/admin-x-framework/test/unit/hooks/use-form.test.ts
🪛 Biome (2.1.2)
apps/posts/src/views/PostAnalytics/Growth/growth.tsx

[error] 16-17: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Web/web.tsx

[error] 26-27: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/activitypub/src/views/profile/profile.tsx

[error] 13-14: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

apps/posts/src/views/PostAnalytics/Newsletter/newsletter.tsx

[error] 17-18: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

🔇 Additional comments (13)
apps/shade/src/components/features/color-picker/color-picker.tsx (1)

388-390: LGTM! Clean lint fix aligned with ESLint 9+ upgrade.

Removing the unused error binding from the catch clause is correct since the error is intentionally ignored. This change preserves the existing behavior while satisfying the new ESLint rules.

apps/admin-x-framework/test/unit/hooks/use-form.test.ts (1)

206-206: LGTM! Clean ESLint compliance update.

The catch blocks correctly use bare catch {} syntax since the error objects were never referenced. The test behavior remains unchanged—both tests still verify that errors are properly thrown and handled.

Also applies to: 233-233

ghost/core/core/server/services/recommendations/service/UnsafeData.ts (1)

188-188: LGTM! Valid ESLint compliance fix.

Removing the unused error variable in the catch block is correct. The error was never referenced, and bare catch syntax is the appropriate choice here.

apps/comments-ui/src/actions.ts (1)

257-276: LGTM! Clean removal of unused error bindings.

The bare catch {} blocks are appropriate here since the error objects were never used. Both functions handle failures by reverting the optimistic state update, which is the intended behavior.

ghost/core/core/server/services/recommendations/service/RecommendationMetadataService.ts (2)

62-66: LGTM! Error binding correctly removed.

The unused error binding has been removed while preserving the error handling behavior. JSON parse failures will continue to return undefined as expected.


73-82: LGTM! URL parsing error handling is appropriate.

The removal of the unused error binding is correct. Returning null for invalid URLs is a sensible defensive pattern.

apps/shade/src/components/layout/header.tsx (1)

116-123: Excellent implementation of the compound subcomponent pattern.

The static property assignments and dual export approach (both Header.Title and HeaderTitle) provide flexibility while adhering to the compound component guideline. This creates a clean, composable API for header layouts.

Based on coding guidelines, the compound subcomponent pattern is the preferred approach for multi-region components.

apps/shade/src/components/ui/card.tsx (1)

219-245: Verify ESLint compliance.

As per coding guidelines, ensure you run yarn lint after these changes to confirm all ESLint errors and warnings are resolved with the new plugin version.

Based on learnings, apps/shade files should be linted before committing.

apps/admin-x-settings/src/utils/link-to-github-releases.ts (2)

33-33: LGTM!

The bare catch {} is appropriate since the error object is not used. The function simply returns an empty string on any error, which is consistent with the existing error handling pattern.


1-1: Remove the unnecessary @ts-expect-error directive.

The @types/semver package provides type definitions that explicitly import and export types for semver/functions/parse, so the subpath has proper type support. The directive should be removed unless the project explicitly does not use @types/semver.

Likely an incorrect or invalid review comment.

package.json (1)

81-81: LGTM!

The parser resolution ensures consistent @typescript-eslint/parser version across all workspaces, which helps prevent conflicts with the updated ESLint plugin.

apps/shade/src/components/layout/heading.tsx (1)

4-5: LGTM!

The ESLint suppression is appropriate here. The HeadingProps interface serves as a semantic type alias for React.HTMLAttributes<HTMLHeadingElement>, providing clear documentation and type identity even though it adds no additional properties. This is a common pattern in component libraries.

apps/admin-x-framework/src/api/recommendations.ts (1)

25-26: LGTM!

The ESLint suppressions are appropriate for these API response type definitions. RecommendationEditResponseType provides semantic clarity by extending the base response type, while RecommendationDeleteResponseType defines the contract for delete operations that return no data. These empty interfaces serve as documentation and maintain type safety for API mutations.

Also applies to: 28-29

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments