Skip to content

feat(share): public notes API with metadata for portfolio consumption#166

Merged
github-actions[bot] merged 8 commits into
developfrom
feature/public-notes-api
Mar 18, 2026
Merged

feat(share): public notes API with metadata for portfolio consumption#166
github-actions[bot] merged 8 commits into
developfrom
feature/public-notes-api

Conversation

@tomymaritano

@tomymaritano tomymaritano commented Mar 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Extended sharedNotes schema with tags, backlinks, wordCount, and notebookName columns (all NOT NULL DEFAULT — backward-compatible)
  • Added GET /share/public/:userId list endpoint with pagination (?limit=20&offset=0) and permissive CORS for cross-origin portfolio consumption
  • Enriched GET /share/:slug detail endpoint to return metadata (tags, backlinks, wordCount, notebookName)
  • Updated POST /share to accept and persist metadata fields (Zod defaults keep old clients working)
  • Desktop app now sends full note metadata (tags, wordCount, notebookName, backlinks) when sharing
  • Hand-corrected migration (0005) uses safe ALTER TABLE ADD COLUMN instead of Drizzle's auto-generated CREATE TABLE

Test plan

  • pnpm typecheck passes (17/17 tasks)
  • pnpm test passes (42/42 tests across 7 test files)
  • Manual: share a note from desktop → verify API receives tags/wordCount/notebookName
  • Manual: curl "https://api.readied.app/share/public/<userId>?limit=5" returns paginated notes with metadata
  • Manual: curl "https://api.readied.app/share/<slug>" returns enriched response with tags/backlinks
  • Manual: fetch from different origin (portfolio domain) succeeds for /share/public/* (CORS)
  • Run pnpm db:migrate on staging to verify migration applies cleanly

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Share notes with enhanced metadata including tags, word count, backlinks, and notebook information.
    • New public gallery feature allows discovering published notes by user with pagination support.
  • Improvements

    • Sharing workflow now seamlessly integrates notebook context and related metadata.
    • Database optimized with improved indexing for public notes.
    • Public sharing endpoints enhanced with cross-origin access support.

tomymaritano and others added 5 commits March 13, 2026 18:49
## Summary

Release 0.9.0 — a major milestone with AI features, sync stability, and
a complete website redesign.

- **Website redesign** with shadcn/ui + Magic UI
- **Auth UX rethink** — Enable Sync flow + middleware fix (500 → 401)
- **Sync stability** — error propagation, exponential backoff, abort on
logout, typed token refresh
- **Sync onboarding** — prompt after 5 notes + offline queue visibility
- **AI Commands (Cmd+K v1)** — command panel, settings, keybindings
- **AI Knowledge (Cmd+K v2)** — RAG, ask notes, related context
- **AI Extensibility** — plugin API, presets import/export
- **API documentation**

## Version bumps

| File | From | To |
|------|------|----|
| `package.json` | 0.2.0 | 0.9.0 |
| `apps/desktop/package.json` | 0.8.0 | 0.9.0 |

## Checklist

- [x] Version bumped in root `package.json`
- [x] Version bumped in `apps/desktop/package.json`
- [x] CHANGELOG.md updated
- [x] Tests pass
- [ ] QA smoke test on macOS
- [ ] QA smoke test on Windows
- [ ] Tag `v0.9.0` after merge

## Post-merge

After merging to `main`:
1. Tag `v0.9.0` on main
2. Merge `main` back into `develop`
3. Delete `release/0.9.0` branch

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* AI Assistant panel with modeful interface (chat/ask-notes modes),
configurable via settings
  * AI command presets support (export/import functionality)
  * Sync status monitoring with error tracking and pending change count
  * Enable Sync onboarding flow for new users
  * Redesigned website with modernized design system
* Settings panel for AI configuration (API key, model selection, context
limits)

* **Bug Fixes**
  * Improved token handling for deep-link authentication
  * Better sync error classification and exponential backoff
  * Enhanced device limit error handling

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary

- Adds `auto-tag.yml` workflow that was created during v0.9.0
development but missed the squash merge to main
- Without this workflow, release PRs don't trigger automatic tag
creation, breaking the release pipeline

## Context

The release pipeline chain is: `release/* PR merge → auto-tag creates
vX.Y.Z → release.yml builds + publishes`

This workflow was in `release/0.9.0` but the squash merge of PR #149
didn't include it, so v0.9.0 never got tagged or released.

## After merge

Once this is on main, I'll manually create the `v0.9.0` tag to trigger
the release build.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Release v0.9.0

### Highlights

- **Auth → Payment → Sync flow**: License-gated sync with smart step
routing in EnableSyncModal
- **Stripe checkout**: Monthly (€2/mo) and annual (€20/year) plan
selection
- **Sidebar drag-and-drop**: Grip handle, circular reference prevention,
child depth propagation
- **CI/CD**: Auto-deploy API — develop → staging, main → production
- **Design tokens**: SyncStatusIndicator uses CSS variables instead of
hardcoded colors
- **AI features**: Command execution, plugin bridge, knowledge system
(Phase 4-5)
- **UI cleanup**: Tailwind v4 canonical classes, removed dead
design-system package

### Changes since v0.8.0

- feat(sync): connect auth → payment → sync flow with license gating
(#154)
- chore: Tailwind v4 canonical classes + tsconfig cleanup (#151)
- feat(ai): complete Phase 4-5 — AI command execution + plugin bridge
(#150)
- feat(ai,plugins): complete Phase 4-5 — AI knowledge & extensibility
- feat(desktop,api): complete Phase 1-3 roadmap implementation

### Checklist

- [x] `pnpm typecheck` passes
- [x] `pnpm test` passes
- [x] `package.json` version is `0.9.0`
- [x] All features merged to develop first
- [x] API production secrets configured (Stripe, Resend)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

# Release Notes

* **New Features**
* Added AI-powered text commands (summarize, rewrite, tweet) with plugin
integration.
* Introduced drag-and-drop notebook reordering with automatic circular
reference protection.
  * Enhanced sync onboarding with license-based feature gating.
  * Improved magic link authentication with automatic verification.

* **Bug Fixes**
  * Fixed token refresh infinite loop handling.
  * Added URL encoding for verification links.
  * Improved cleanup for animated UI components.

* **Improvements**
  * Enhanced error handling for authentication and sync failures.
  * Added accessibility attributes (ARIA labels, semantic HTML).
  * Updated visual styling with new token-driven design system.

* **Refactor**
  * Migrated from design-system package to plugin-api package.
  * Consolidated design tokens and theme system.

* **Chores**
  * Updated GitHub Actions release automation.
  * Refreshed project documentation and contribution guidelines.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tion

Extend shared notes with tags, backlinks, wordCount, and notebookName.
Add GET /share/public/:userId list endpoint with pagination and CORS.
Enrich GET /share/:slug detail endpoint with metadata fields.
Desktop app now sends full note metadata when sharing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@vercel

vercel Bot commented Mar 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
readide Ready Ready Preview, Comment Mar 18, 2026 3:50pm

Request Review

@coderabbitai

coderabbitai Bot commented Mar 18, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ea24ed85-4708-4ef6-b22b-fdb34c8f1d92

📥 Commits

Reviewing files that changed from the base of the PR and between 287ee64 and 9c7f3aa.

📒 Files selected for processing (3)
  • apps/desktop/src/preload/index.ts
  • packages/api/src/index.ts
  • packages/api/src/routes/share.ts

📝 Walkthrough

Walkthrough

A feature expansion adds four new optional metadata fields (tags, backlinks, wordCount, notebookName) to shared notes. Changes span desktop IPC handlers, API client, preload API, note editor component, database schema with new indexed columns, API routes including a public listing endpoint, and permissive CORS policy for public share endpoints.

Changes

Cohort / File(s) Summary
Desktop IPC & API Client
apps/desktop/src/main/handlers/shareHandlers.ts, apps/desktop/src/main/services/apiClient.ts
Extended share:create handler and shareNote method signatures to accept optional tags, backlinks, wordCount, and notebookName fields; forwards extended payload through to API.
Preload API Surface
apps/desktop/src/preload/index.ts
Expanded ReadiedAPI.notes.create input type to include the four new optional metadata fields (tags, backlinks, wordCount, notebookName) for public API consumers.
UI Component Integration
apps/desktop/src/renderer/components/NoteEditor.tsx
Integrated notebook data fetch via useNotebook hook; enriched share payload to include tags, wordCount, notebookName, and computed backlinks from notebook context.
Database Schema
packages/api/src/db/schema.ts
Added four new columns to sharedNotes table with appropriate defaults (tags and backlinks as JSON, wordCount as integer, notebookName as text); added new composite index on (userId, isPublic).
Share Routes & Endpoints
packages/api/src/routes/share.ts
Added public GET /public/:userId listing endpoint with pagination; extended createShareSchema to validate and persist new metadata fields; routes now parse JSON fields and return enriched note data.
Cross-Origin Policy
packages/api/src/index.ts
Introduced permissive CORS configuration for /share/public/* endpoints allowing any origin with GET/OPTIONS methods and 24-hour max age, separate from restricted CORS on other API paths.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.43% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding a public notes API with metadata support for portfolio consumption.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/public-notes-api
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/components/NoteEditor.tsx (1)

167-176: ⚠️ Potential issue | 🟡 Minor

Prevent silent metadata drop when notebook data is still loading.

notebookName: notebook?.name ?? '' can send an empty notebook name if the user shares before the notebook query resolves.

💡 Suggested guard
   const handleShareOnWeb = useCallback(async () => {
     if (!note) return;
+    if (note.notebookId && !notebook) {
+      showToast('Notebook metadata is still loading. Please try again in a moment.', 'error');
+      return;
+    }
     const result = await window.readied.share.create({
       noteId: note.id,
       title: note.title,
       content: note.content,
       tags: note.tags,
       wordCount: note.wordCount,
       notebookName: notebook?.name ?? '',
       backlinks: (backlinks ?? []).map(bl => ({ noteId: bl.noteId, title: bl.noteTitle })),
     });

Also applies to: 183-183

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/renderer/components/NoteEditor.tsx` around lines 167 - 176,
In handleShareOnWeb, avoid sending an empty string for notebookName when
notebook data is still loading; instead guard so we only call
window.readied.share.create when notebook is resolved (or pass undefined/null to
explicitly indicate missing value). Update the share invocation in
handleShareOnWeb (and the similar use at the other occurrence around line 183)
to either early-return if notebook is loading, or set notebookName: notebook ?
notebook.name : undefined (not ''), and ensure UI disables the share action
while notebook query is pending so metadata is not silently dropped.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/app/`(marketing)/download/page.tsx:
- Line 72: Replace the arbitrary opacity bracket syntax in the span's className
(the token bg-white/[0.05] used in the element with class "inline-flex
items-center px-5 py-2 font-mono text-sm font-semibold rounded-full ...") with
the simpler Tailwind slash form bg-white/5 so it uses the idiomatic standard
opacity scale instead of an arbitrary value.

In `@apps/web/app/`(marketing)/pricing/page.tsx:
- Around line 30-32: The NumberTicker is forcing integer formatting for the
annual price which will round values when
proPricing.intervals.annual.amountCents isn't divisible by 100; update the
NumberTicker usage to allow the correct decimal precision (remove or change
decimalPlaces={0}) and compute/display annualPrice and monthlyPrice with
necessary decimal precision (monthlyPrice and annualPrice from
proPricing.intervals.*.amountCents / 100) or pass a dynamic decimalPlaces based
on whether amountCents % 100 === 0 so NumberTicker shows cents when needed.

In `@apps/web/app/docs/`[[...slug]]/page.tsx:
- Around line 13-28: This file defines a local mdxComponents object that
duplicates the shared registry; instead import and reuse the shared registry
export (defaultMdxComponents) and either use it directly or merge additional
local overrides via spreading (e.g., const mdxComponents = {
...defaultMdxComponents, Card, Cards, Callout, Step, Steps, Tab, Tabs,
Accordion, Accordions, File, Folder, Files, TypeTable };), removing the
duplicated definition so the single source of truth (defaultMdxComponents) is
used for MDX rendering.

In `@apps/web/components/Footer.tsx`:
- Around line 137-149: The Footer component duplicates anchor markup for social
links; extract the repeated anchor rendering into a small helper component or
function (e.g., SocialLink or renderSocialLink) and use it in both places where
socialLinks.map(...) is used to ensure consistent props (href, target,
rel="noopener noreferrer", aria-label, className, and icon). Update both
occurrences to call the helper with the social object (or its fields) so hover
styles and accessibility attributes stay in one place and prevent future drift.

In `@packages/api/drizzle/0005_slippery_the_fury.sql`:
- Around line 1-5: Remove the file-based SQL migration that alters the
shared_notes table (the statements adding tags, backlinks, word_count,
notebook_name and the index idx_shared_notes_user_public); this migration must
not live under packages/api. Delete this SQL artifact and instead apply the
schema changes via the project's configured drizzle-kit push workflow (update
the canonical Drizzle schema model or run the push command so the change is
recorded in the push-managed schema), ensuring no file-based migration remains
in packages/api.

In `@packages/api/drizzle/meta/0005_snapshot.json`:
- Around line 1-1008: The checked-in Drizzle snapshot (the JSON containing
tables like "devices", "magic_links", "newsletter", "plugin_catalog",
"shared_notes", "subscriptions", "sync_log", "users", etc.) must be removed from
the API package so the codebase has a single schema source of truth; delete the
file representing the snapshot and revert any changes that add it
(packages/api/drizzle/meta/0005_snapshot.json), keep your schema changes in
schema.ts, and then apply them using drizzle-kit push to generate migrations
instead of committing migration metadata into the repo.

In `@packages/api/src/routes/share.ts`:
- Around line 35-38: The schema currently allows negative values for the field
defined as wordCount (z.number().int().default(0)); update the validator to
reject negatives by adding a non-negative constraint (e.g., use .nonnegative()
or .min(0) on the z.number().int() chain for wordCount) so any incoming value is
validated before persisting, keeping the default(0) in place; locate the
wordCount schema entry alongside tags and backlinks in the share route and apply
the constraint there.
- Around line 25-27: The route-level CORS override in share.use('/public/*',
cors({ origin: '*' })) won't work; move the exception into the app-level CORS
setup in the main app (the global cors(...) configured in index.ts) by adding
conditional logic to its origin option that returns '*' for paths starting with
'/share/public/' and otherwise enforces the restricted-origins/credentials:true
behavior; update the app-level cors origin function to inspect req.path (or
req.url) and return '*' for that prefix so preflight and actual requests receive
the correct headers and remove the route-level cors call from share.use.

---

Outside diff comments:
In `@apps/desktop/src/renderer/components/NoteEditor.tsx`:
- Around line 167-176: In handleShareOnWeb, avoid sending an empty string for
notebookName when notebook data is still loading; instead guard so we only call
window.readied.share.create when notebook is resolved (or pass undefined/null to
explicitly indicate missing value). Update the share invocation in
handleShareOnWeb (and the similar use at the other occurrence around line 183)
to either early-return if notebook is loading, or set notebookName: notebook ?
notebook.name : undefined (not ''), and ensure UI disables the share action
while notebook query is pending so metadata is not silently dropped.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1d22281b-5c98-47e9-a62d-525517cf58a1

📥 Commits

Reviewing files that changed from the base of the PR and between f686ab6 and 287ee64.

📒 Files selected for processing (22)
  • .claude/settings.local.json
  • apps/desktop/src/main/handlers/shareHandlers.ts
  • apps/desktop/src/main/services/apiClient.ts
  • apps/desktop/src/preload/index.ts
  • apps/desktop/src/renderer/components/NoteEditor.tsx
  • apps/web/app/(marketing)/auth/verify/AuthVerifyContent.tsx
  • apps/web/app/(marketing)/changelog/page.tsx
  • apps/web/app/(marketing)/download/page.tsx
  • apps/web/app/(marketing)/pricing/page.tsx
  • apps/web/app/docs/[[...slug]]/page.tsx
  • apps/web/components/Footer.tsx
  • apps/web/components/Navbar.tsx
  • apps/web/components/landing/Hero.tsx
  • apps/web/components/landing/VideoGuides.tsx
  • apps/web/components/magicui/animated-shiny-text.tsx
  • apps/web/components/magicui/border-beam.tsx
  • apps/web/components/magicui/hero-video-dialog.tsx
  • packages/api/drizzle/0005_slippery_the_fury.sql
  • packages/api/drizzle/meta/0005_snapshot.json
  • packages/api/drizzle/meta/_journal.json
  • packages/api/src/db/schema.ts
  • packages/api/src/routes/share.ts

We&apos;re building something good. Leave your email to get notified.
</p>
<span className="inline-flex items-center px-5 py-2 font-mono text-sm font-semibold rounded-full bg-white/5 border border-border text-text-secondary">
<span className="inline-flex items-center px-5 py-2 font-mono text-sm font-semibold rounded-full bg-white/[0.05] border border-border text-text-secondary">

@coderabbitai coderabbitai Bot Mar 18, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Prefer the simpler slash syntax for standard opacity values.

Both bg-white/5 and bg-white/[0.05] produce the same 5% opacity, but the slash syntax is more idiomatic in Tailwind. Arbitrary value syntax (brackets) is typically reserved for values outside the default scale.

💅 Suggested reversion to simpler syntax
-              <span className="inline-flex items-center px-5 py-2 font-mono text-sm font-semibold rounded-full bg-white/[0.05] border border-border text-text-secondary">
+              <span className="inline-flex items-center px-5 py-2 font-mono text-sm font-semibold rounded-full bg-white/5 border border-border text-text-secondary">
📝 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
<span className="inline-flex items-center px-5 py-2 font-mono text-sm font-semibold rounded-full bg-white/[0.05] border border-border text-text-secondary">
<span className="inline-flex items-center px-5 py-2 font-mono text-sm font-semibold rounded-full bg-white/5 border border-border text-text-secondary">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/`(marketing)/download/page.tsx at line 72, Replace the arbitrary
opacity bracket syntax in the span's className (the token bg-white/[0.05] used
in the element with class "inline-flex items-center px-5 py-2 font-mono text-sm
font-semibold rounded-full ...") with the simpler Tailwind slash form bg-white/5
so it uses the idiomatic standard opacity scale instead of an arbitrary value.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Acknowledged — valid nitpick. Will use slash syntax in a follow-up.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@tomymaritano, sounds good! No rush — happy to see it cleaned up in a follow-up. 👍

Comment on lines +30 to +32
// Extract numeric values from price labels for NumberTicker
const monthlyPrice = proPricing.intervals.monthly.amountCents / 100;
const annualPrice = proPricing.intervals.annual.amountCents / 100;

@coderabbitai coderabbitai Bot Mar 18, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid forcing annual price to integer formatting.

At Line 126, decimalPlaces={0} can round displayed pricing if proPricing.intervals.annual.amountCents is not divisible by 100. This creates a UI correctness risk for future pricing updates.

💡 Proposed fix
-  const annualPrice = proPricing.intervals.annual.amountCents / 100;
+  const annualPriceCents = proPricing.intervals.annual.amountCents;
+  const annualPrice = annualPriceCents / 100;
+  const annualDecimalPlaces = annualPriceCents % 100 === 0 ? 0 : 2;
...
-                  <NumberTicker
-                    value={annualPrice}
-                    decimalPlaces={0}
-                    className="font-mono text-3xl sm:text-4xl font-bold text-accent"
-                  />
+                  <NumberTicker
+                    value={annualPrice}
+                    decimalPlaces={annualDecimalPlaces}
+                    className="font-mono text-3xl sm:text-4xl font-bold text-accent"
+                  />

Also applies to: 124-127

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/`(marketing)/pricing/page.tsx around lines 30 - 32, The
NumberTicker is forcing integer formatting for the annual price which will round
values when proPricing.intervals.annual.amountCents isn't divisible by 100;
update the NumberTicker usage to allow the correct decimal precision (remove or
change decimalPlaces={0}) and compute/display annualPrice and monthlyPrice with
necessary decimal precision (monthlyPrice and annualPrice from
proPricing.intervals.*.amountCents / 100) or pass a dynamic decimalPlaces based
on whether amountCents % 100 === 0 so NumberTicker shows cents when needed.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Acknowledged — outside the scope of this PR's changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@tomymaritano, understood — thanks for the acknowledgement! Would you like me to open a follow-up GitHub issue to track the dynamic decimalPlaces fix for the annual price NumberTicker, so it doesn't get lost?

(` ^◡^)っ✨


🧠 Learnings used
Learnt from: CR
Repo: tomymaritano/readide PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T20:03:25.242Z
Learning: Applies to apps/docs-site/**/*.{astro,ts,tsx} : Marketing pages must consume pricing configuration from packages/product-config/src/facade.ts, not hardcode values

Learnt from: CR
Repo: tomymaritano/readide PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T20:03:25.242Z
Learning: Applies to packages/product-config/src/facade.ts : Pricing, plans, and guarantees configuration must be defined in packages/product-config/src/facade.ts as the single source of truth

Comment on lines +13 to +28
const mdxComponents = {
...defaultMdxComponents,
Card,
Cards,
Callout,
Step,
Steps,
Tab,
Tabs,
Accordion,
Accordions,
File,
Folder,
Files,
TypeTable,
};

@coderabbitai coderabbitai Bot Mar 18, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Reuse the shared MDX component registry to avoid drift.

This local map duplicates apps/web/mdx-components.tsx (Lines 11-28). Keeping both registries in sync manually is error-prone and can cause docs rendering inconsistencies over time.

♻️ Proposed refactor
-import defaultMdxComponents from 'fumadocs-ui/mdx';
-import { Card, Cards } from 'fumadocs-ui/components/card';
-import { Callout } from 'fumadocs-ui/components/callout';
-import { Step, Steps } from 'fumadocs-ui/components/steps';
-import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
-import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';
-import { File, Folder, Files } from 'fumadocs-ui/components/files';
-import { TypeTable } from 'fumadocs-ui/components/type-table';
+import { useMDXComponents } from '@/mdx-components';

-const mdxComponents = {
-  ...defaultMdxComponents,
-  Card,
-  Cards,
-  Callout,
-  Step,
-  Steps,
-  Tab,
-  Tabs,
-  Accordion,
-  Accordions,
-  File,
-  Folder,
-  Files,
-  TypeTable,
-};
+const mdxComponents = useMDXComponents({});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/app/docs/`[[...slug]]/page.tsx around lines 13 - 28, This file
defines a local mdxComponents object that duplicates the shared registry;
instead import and reuse the shared registry export (defaultMdxComponents) and
either use it directly or merge additional local overrides via spreading (e.g.,
const mdxComponents = { ...defaultMdxComponents, Card, Cards, Callout, Step,
Steps, Tab, Tabs, Accordion, Accordions, File, Folder, Files, TypeTable };),
removing the duplicated definition so the single source of truth
(defaultMdxComponents) is used for MDX rendering.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Acknowledged — good suggestion for reducing drift. Will address in a follow-up.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@tomymaritano, sounds good! Would you like me to open a GitHub issue to track the follow-up refactor (consolidating the MDX component registry in apps/web/app/docs/[[...slug]]/page.tsx to reuse useMDXComponents from @/mdx-components)? That way it won't get lost between PRs.


🧠 Learnings used
Learnt from: CR
Repo: tomymaritano/readide PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T20:03:25.242Z
Learning: Applies to **/(hooks|src/hooks)/useNavigation.tsx : Data filtering derived from NavigationState must be implemented in useNavigation.tsx only, never in Sidebar or other components

Learnt from: CR
Repo: tomymaritano/readide PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T20:03:25.242Z
Learning: Applies to apps/desktop/src/**/Sidebar.tsx : Sidebar component must use useNavigation() hook and emit navigation actions, never directly filter or manage navigation state

Comment on lines +137 to +149
<div className="flex items-center gap-3">
{socialLinks.map(social => (
<a
key={social.href}
href={social.href}
target="_blank"
rel="noopener noreferrer"
aria-label={social.label}
className="flex h-8 w-8 items-center justify-center rounded-lg text-text-muted transition-colors hover:bg-white/[0.06] hover:text-text-secondary"
>
{social.icon}
</a>
))}

@coderabbitai coderabbitai Bot Mar 18, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Extract duplicated social-link rendering into a helper.

Line [137]-Line [149] repeats anchor markup already used in Line [116]-Line [126]. This will drift over time (e.g., rel, aria-label, hover styles).

♻️ Proposed refactor
 export default function Footer() {
   const year = new Date().getFullYear();
+  const renderSocialLink = (
+    social: (typeof socialLinks)[number],
+    className: string,
+    showLabel = false,
+  ) => (
+    <a
+      key={social.href}
+      href={social.href}
+      target="_blank"
+      rel="noopener noreferrer"
+      aria-label={social.label}
+      className={className}
+    >
+      {social.icon}
+      {showLabel ? <span>{social.label}</span> : null}
+    </a>
+  );

   return (
@@
-            {socialLinks.map(social => (
-              <a
-                key={social.href}
-                href={social.href}
-                target="_blank"
-                rel="noopener noreferrer"
-                aria-label={social.label}
-                className="inline-flex w-fit items-center gap-2 text-[13px] text-text-secondary transition-colors hover:text-text-primary"
-              >
-                {social.icon}
-                <span>{social.label}</span>
-              </a>
-            ))}
+            {socialLinks.map(social =>
+              renderSocialLink(
+                social,
+                'inline-flex w-fit items-center gap-2 text-[13px] text-text-secondary transition-colors hover:text-text-primary',
+                true,
+              ),
+            )}
@@
-            {socialLinks.map(social => (
-              <a
-                key={social.href}
-                href={social.href}
-                target="_blank"
-                rel="noopener noreferrer"
-                aria-label={social.label}
-                className="flex h-8 w-8 items-center justify-center rounded-lg text-text-muted transition-colors hover:bg-white/[0.06] hover:text-text-secondary"
-              >
-                {social.icon}
-              </a>
-            ))}
+            {socialLinks.map(social =>
+              renderSocialLink(
+                social,
+                'flex h-8 w-8 items-center justify-center rounded-lg text-text-muted transition-colors hover:bg-white/[0.06] hover:text-text-secondary',
+              ),
+            )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/components/Footer.tsx` around lines 137 - 149, The Footer component
duplicates anchor markup for social links; extract the repeated anchor rendering
into a small helper component or function (e.g., SocialLink or renderSocialLink)
and use it in both places where socialLinks.map(...) is used to ensure
consistent props (href, target, rel="noopener noreferrer", aria-label,
className, and icon). Update both occurrences to call the helper with the social
object (or its fields) so hover styles and accessibility attributes stay in one
place and prevent future drift.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Acknowledged — outside the scope of this PR's changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@tomymaritano, understood — no worries! Feel free to address it in a future cleanup PR when convenient.

Comment thread packages/api/drizzle/0005_slippery_the_fury.sql Outdated
Comment thread packages/api/drizzle/meta/0005_snapshot.json Outdated
Comment thread packages/api/src/routes/share.ts Outdated
Comment thread packages/api/src/routes/share.ts
tomymaritano and others added 3 commits March 18, 2026 12:46
- Remove file-based migration artifacts (use drizzle-kit push workflow)
- Move public CORS to app-level middleware (fixes preflight interception)
- Reject negative wordCount values in Zod schema

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot enabled auto-merge (squash) March 18, 2026 15:49
@github-actions github-actions Bot merged commit a472580 into develop Mar 18, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant