Skip to content

Crate Web v0.1.0: Full workspace with AI research, OpenUI, and team features#2

Open
tmoody1973 wants to merge 472 commits intorelease/v0.1.0from
main
Open

Crate Web v0.1.0: Full workspace with AI research, OpenUI, and team features#2
tmoody1973 wants to merge 472 commits intorelease/v0.1.0from
main

Conversation

@tmoody1973
Copy link
Copy Markdown
Owner

What changed

Complete Crate Web workspace implementation — from bare Next.js scaffold to a fully functional AI music research platform with persistent chat, dynamic UI components, team key sharing, multi-model support, and AgentMail integration.

Why

Building the web companion to Crate CLI so Radio Milwaukee team members and external users can access AI-powered music research through a browser without needing a terminal.

Changes

Core Workspace

  • Sidebar with crates, starred/recent sessions, artifacts browser, full-text search
  • Persistent chat with Convex — messages, sessions, and artifacts survive page reloads
  • Artifact slide-in panel (Claude-style) that opens when AI generates OpenUI components
  • Keyboard shortcuts (Cmd+K search, Cmd+N new chat, Cmd+B sidebar, Shift+S settings)

OpenUI Dynamic Components

  • AlbumEntry, TrackItem, AlbumGrid, TrackList with cover art (Discogs → Bandcamp → iTunes fallback)
  • SaveToCollectionButton, AddToPlaylist, AutoSavePlaylist (dedup via Convex queries)
  • Custom stream adapter bridging CrateAgent SSE events to OpenUI's ChatProvider

Multi-Model Support

  • ModelSelector dropdown: Claude Sonnet 4.6, Haiku 4.5, GPT-4o, GPT-4.1, Gemini 2.5 Flash/Pro, Llama 4, DeepSeek R1, Mistral Large
  • OpenRouter integration — set ANTHROPIC_BASE_URL to OpenRouter endpoint for non-Anthropic models
  • Model choice persisted to localStorage

Team Features

  • Org key sharing: admin shares encrypted API keys with @domain teammates
  • Priority chain: user keys → org shared keys → embedded Tier 1 keys
  • AgentMail integration: send research to Slack (y3v9l8q1c8s3d4n6@88nine.slack.com) or any email
  • Perplexity-style response action bar: Copy, Slack, Email, Share under every AI response

Bug Fixes

  • Duplicate playlist creation on React re-renders (Convex query dedup)
  • Duplicate artifacts (content hash + Convex single source of truth)
  • Markdown not rendering (react-markdown v10 import fix)
  • Horizontal overflow in chat (CSS overflow-hidden + break-words)

Testing

  • TypeScript type check passes (tsc --noEmit)
  • Next.js production build passes (next build)
  • Tested locally with Clerk auth, Convex real-time, OpenRouter models
  • Edge cases: empty playlists, missing API keys, org key fallback chain

Notes for CodeRabbit

  • This is a retroactive PR covering ~30 commits of rapid iteration. Some patterns evolved mid-build (e.g., artifact dedup moved from useRef to Convex queries).
  • OpenUI components use a custom line-oriented language (OpenUI Lang) — the splitContent() parser is intentionally simple.
  • AgentMail SDK requires @x402/fetch peer dependency (installed explicitly).
  • The AGENTMAIL_API_KEY env var fallback in /api/email is intentional for team usage where not every user needs their own key.

Related

  • Project: Crate Web (companion to Crate CLI)
  • Stack: Next.js 15, Convex, Clerk, OpenUI, Tailwind CSS
  • Branch from: 6ed38df (initial Next.js scaffold)

🤖 Generated with Claude Code

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 12, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2eec3409-fdc7-4731-b194-0d66fbae04d1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch main

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.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 13, 2026

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

Project Deployment Actions Updated (UTC)
crate-web Ready Ready Preview, Comment Apr 8, 2026 8:05pm

…pty embeds

- SaveTinyDeskButton now searches YouTube for each connected artist
- Carries over pullQuote, sonicElements, keyWorks to companion nodes
- VideoInfluenceChain skips iframe embed when videoId is empty
…companion save

- 30s max duration on enrich API for Vercel
- 5s timeout per YouTube scrape to prevent hanging
- Block save when influence chain has zero connections
- New /api/tinydesk/save endpoint handles all enrichment + Convex write
- SaveTinyDeskButton simplified to thin POST caller
- Convex mutation rejects empty nodes
- Empty companion pages show "Generate Musical DNA in Crate" link
- Added deleteBySlug mutation for companion cleanup
- Deleted broken Nora Brown companion (empty nodes)
Shows what Crate is doing based on the last completed step:
"Analyzing influence connections...", "Reading reviews and citations...",
"Building playlist...", etc.
- Before/after split screen: "What You See" (YouTube) vs "What Crate Reveals" (influence chain)
- Split screen is dominant element, heading reduced to label
- Mobile: cards first, video second
- How It Works 3-step strip
- DNA-Ready artists horizontal scroll section
- Renamed from "Tiny Desk Catalog" to "Tiny Desk DNA"
- Updated OG metadata and copy
- Extract resolveYoutubeId/resolveGenre/resolveConnectionVideos to src/lib/tinydesk-enrichment.ts (was duplicated in save + enrich routes)
- Extract TinyDeskPicker to src/components/workspace/tinydesk-picker.tsx (was inline in chat-panel)
- Move getThinkingLabel to src/lib/tool-labels.ts (co-located with getToolLabel)
- chat-panel.tsx: 1471 → 1269 lines
- save/route.ts: 208 → 124 lines
- enrich/route.ts: 108 → 41 lines
- Header: Crate logo × Tiny Desk DNA logo side by side
- Hero: SVG logo replaces text heading, sr-only h1 for accessibility
- Companion pages show "Watch on NPR" link when sourceUrl exists in catalog
- Header: Tiny Desk DNA logo 70px, hero logo 120px
- New section between DJ Showcase and Pricing with before/after preview
- Shows Beth Gibbons influence chain preview with source badges + sonic tags
- "EXPLORE TINY DESK DNA" CTA linking to /tinydesk
- Added "TINY DESK DNA" to landing page nav
- New feature section with companion pages, /tinydesk command, community catalog
- Updated project structure with tinydesk components, API routes, and lib
Every research session now silently captures tool results into per-artist
wiki pages. Session-end synthesis via Haiku merges data, deduplicates,
and flags contradictions between sources.

- convex/schema.ts: wikiPages, wikiIndexEntries, wikiLogEntries tables
- convex/wiki.ts: CRUD mutations, access-controlled queries, Haiku
  synthesis action with retry, internal query/mutation bridge
- src/lib/wiki-ingest.ts: per-tool extractors for 9 data sources
- src/lib/agentic-loop.ts: onToolComplete callback for wiki ingestion
- src/app/api/chat/route.ts: wire wiki upserts + session-end synthesis
- src/app/wiki/[username]/[slug]/page.tsx: V2-C split layout detail page
- src/app/wiki/page.tsx: bold editorial index with empty state
- src/components/sidebar/wiki-section.tsx: sidebar badge with entry count
- src/lib/slug.ts: shared canonical slugify utility

Security: getBySlug uses Convex auth (ctx.auth.getUserIdentity()) for
access control. Private pages only visible to authenticated owner.
ConvexHttpClient in server components doesn't carry Clerk JWT tokens,
so ctx.auth.getUserIdentity() was always null. Private pages returned
null → 404 for the owner.

Fix: pass viewerClerkId from Clerk auth() in the server component to
the Convex query, which checks it against the page owner's clerkId.
…ty toggle

- Index page title: "{First Name}'s Music Wiki" instead of generic "Your Music Wiki"
- Crate logo in header matching /tinydesk pattern (sticky, dark blue bg)
- Interactive visibility segmented control (Private | Unlisted | Public) on detail page
- Owner sees toggle, visitors see read-only badge
- Public entries show green badge on index page
- "Back to Chat" link in index header
Previous extractors tried to parse specific JSON shapes per tool but
missed most data. New approach: find artist name via common field
patterns, pass the FULL tool result content through to Haiku synthesis.
The synthesis step structures everything.

Also set ANTHROPIC_API_KEY in Convex env so synthesis actually runs.
…r display

Synthesis was failing with ArgumentValidationError because Haiku returns
extra fields (snippet, type, name) that the strict Convex validators reject.
Now sanitizes all fields to match validator shapes exactly.

Detail page now formats raw JSON sections into readable text — extracts
influence connections, review snippets, and readable fields instead of
dumping raw JSON.
Three performance and UX improvements:

1. Shared wiki: getSharedBySlug query finds the richest wiki page for
   an artist across ALL users. Everyone's research compounds.

2. Wiki-first response: before running the full agentic loop for
   /influence, /story, /prep commands, check if wiki data exists.
   Inject existing knowledge as system prompt context so the LLM
   builds on prior research instead of starting from scratch.

3. Parallel tool execution: executeTools now fires all tools with
   Promise.all instead of running them sequentially. When Claude
   requests 3 tools in one turn, they run concurrently.
   Before: tool1 (3s) → tool2 (2s) → tool3 (4s) = 9s
   After:  tool1 + tool2 + tool3 = 4s (max of three)
- toggleVisibility/archivePage: verify ownership via Convex auth
  (ctx.auth.getUserIdentity) instead of trusting client-supplied userId
- getSharedBySlug: filter out private pages to prevent leaking
  private research into other users' wiki-first context
- Cap wiki-first context injection to 3000 chars to prevent
  context window bloat
- Remove userId prop from VisibilityToggle component
Remaining items from code review:

1. usernameSlug: added field + by_username_slug index to users table.
   getBySlug now queries by index instead of scanning all users.
   Computed on user create/update in users.upsert mutation.

2. Scheduled synthesis: replaced fire-and-forget convex.action()
   from serverless route with scheduleSynthesis mutation that uses
   ctx.scheduler.runAfter(). Synthesis survives function termination.
   synthesizeWikiPage is now an internalAction.

3. Test suite: 30 tests across 2 files.
   - 21 tests for wiki-ingest.ts extractors (all tool servers,
     edge cases, JSON→readable conversion, special characters)
   - 9 tests for slug.ts (spaces, ampersands, unicode, empty)
   - Added vitest as dev dependency + config

Also fixed: Last.fm tag objects vs Bandcamp tag strings, Discogs year
extraction, items array handling, text pattern matching for influence
chain content.
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