feat(share): public notes API with metadata for portfolio consumption#166
Conversation
## 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>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughA 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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/components/NoteEditor.tsx (1)
167-176:⚠️ Potential issue | 🟡 MinorPrevent 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
📒 Files selected for processing (22)
.claude/settings.local.jsonapps/desktop/src/main/handlers/shareHandlers.tsapps/desktop/src/main/services/apiClient.tsapps/desktop/src/preload/index.tsapps/desktop/src/renderer/components/NoteEditor.tsxapps/web/app/(marketing)/auth/verify/AuthVerifyContent.tsxapps/web/app/(marketing)/changelog/page.tsxapps/web/app/(marketing)/download/page.tsxapps/web/app/(marketing)/pricing/page.tsxapps/web/app/docs/[[...slug]]/page.tsxapps/web/components/Footer.tsxapps/web/components/Navbar.tsxapps/web/components/landing/Hero.tsxapps/web/components/landing/VideoGuides.tsxapps/web/components/magicui/animated-shiny-text.tsxapps/web/components/magicui/border-beam.tsxapps/web/components/magicui/hero-video-dialog.tsxpackages/api/drizzle/0005_slippery_the_fury.sqlpackages/api/drizzle/meta/0005_snapshot.jsonpackages/api/drizzle/meta/_journal.jsonpackages/api/src/db/schema.tspackages/api/src/routes/share.ts
| We'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"> |
There was a problem hiding this comment.
🧹 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.
| <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.
There was a problem hiding this comment.
Acknowledged — valid nitpick. Will use slash syntax in a follow-up.
There was a problem hiding this comment.
@tomymaritano, sounds good! No rush — happy to see it cleaned up in a follow-up. 👍
| // Extract numeric values from price labels for NumberTicker | ||
| const monthlyPrice = proPricing.intervals.monthly.amountCents / 100; | ||
| const annualPrice = proPricing.intervals.annual.amountCents / 100; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Acknowledged — outside the scope of this PR's changes.
There was a problem hiding this comment.
@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
| const mdxComponents = { | ||
| ...defaultMdxComponents, | ||
| Card, | ||
| Cards, | ||
| Callout, | ||
| Step, | ||
| Steps, | ||
| Tab, | ||
| Tabs, | ||
| Accordion, | ||
| Accordions, | ||
| File, | ||
| Folder, | ||
| Files, | ||
| TypeTable, | ||
| }; |
There was a problem hiding this comment.
🧹 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.
There was a problem hiding this comment.
Acknowledged — good suggestion for reducing drift. Will address in a follow-up.
There was a problem hiding this comment.
@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
| <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> | ||
| ))} |
There was a problem hiding this comment.
🧹 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.
There was a problem hiding this comment.
Acknowledged — outside the scope of this PR's changes.
There was a problem hiding this comment.
@tomymaritano, understood — no worries! Feel free to address it in a future cleanup PR when convenient.
- 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>
Summary
sharedNotesschema withtags,backlinks,wordCount, andnotebookNamecolumns (allNOT NULL DEFAULT— backward-compatible)GET /share/public/:userIdlist endpoint with pagination (?limit=20&offset=0) and permissive CORS for cross-origin portfolio consumptionGET /share/:slugdetail endpoint to return metadata (tags, backlinks, wordCount, notebookName)POST /shareto accept and persist metadata fields (Zod defaults keep old clients working)0005) uses safeALTER TABLE ADD COLUMNinstead of Drizzle's auto-generatedCREATE TABLETest plan
pnpm typecheckpasses (17/17 tasks)pnpm testpasses (42/42 tests across 7 test files)curl "https://api.readied.app/share/public/<userId>?limit=5"returns paginated notes with metadatacurl "https://api.readied.app/share/<slug>"returns enriched response with tags/backlinks/share/public/*(CORS)pnpm db:migrateon staging to verify migration applies cleanly🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Improvements