A batch covering auth, files, the AI assistant, Notion, the docs site, and a couple of database-advisor cleanups.
Added
- Multiple AI model profiles (
components/AISettingsPanel.tsx,app/api/ai/credentials/route.ts,lib/ai/config.ts). AI settings is now a managed list: a user can save several profiles — each its own provider, model, endpoint, and (encrypted) API key — and pick which one is active; the active profile drives the AI panel.ai_credentialsis reshaped from one-row-per-user to many (id PK,label,is_active, one-active-per-user index); the API gains list / create / update / set-active / delete. Note: this migration was never successfully applied before (it referenced the droppedpublic.touch_updated_at()), so applying it now creates the table fresh. - PDF cards (
components/cards/FileCard.tsx,components/Canvas.tsx). Dropped PDFs show a first-page thumbnail on the canvas and double-click (or the expand button) opens a full, scrollable lightbox — reusing the existing image/video/embed preview modal. Data-URL PDFs are rendered via a blob URL (browsers blockdata:inside iframes). Size is capped by auth state: 5 MB signed out (inlined locally), 25 MB signed in (uploaded to thecardsSupabase Storage bucket via the newlib/supabase/upload.ts); over-limit drops are rejected with a clear toast. - AI summary → Notion (
app/api/integrations/notion/export/route.ts,components/NotionPicker.tsx). The Notion export gains an Include AI summary option that prepends the space's AI briefing (the saved one, or a freshly generated one) above the exported cards. - Richer AI context (
lib/ai-enrich.ts). Map cards are resolved through the Nominatim geocoder so the model sees the real place, kind, and coordinates instead of a bare search string; uploaded PDFs have their text extracted (unpdf); file cards are now described to the model at all.
Changed
- Shapes picker (
components/Toolbar.tsx). The diagram-shapes flyout was redrawn from a cramped two-column icon+text list into a tidy tile grid — each glyph centered in its own swatch with a caption, split into Flowchart and Architecture rows (Frame joins the architecture row), so it's an even, scannable 4-up instead of a list with an orphan "Frame" row. - Releases page (
app/(docs)/releases/,lib/changelog.ts). Releases are grouped by day (same-day entries collapse under one date), each shows a short summary, and a Read details button opens a blog-style permalink at/releases/<slug>. The month filter / TOC is unchanged. - Email templates (
supabase/templates/). The sign-in emails now use the real ClipSpaces logo image instead of a CSS letter-box. - AI board context moved to
lib/ai/board.ts, shared by the AI route and the Notion export (no behavioural change to chat/brief).
Fixed
- Toolbar active state invisible (
components/Toolbar.tsx). The active tool was marked by a floatinglayoutIdindicator layer behind the button, which could fail to paint over the toolbar's backdrop-blur — leaving just a white glyph on a transparent button (notably in light mode). The active fill is now painted directly on the button (solidvar(--accent)+ white glyph), so it's unmistakable in both themes. - Sign-in code length mismatch (
components/EmailOtpForm.tsx,supabase/config.toml). The hosted project issues 8-digit codes, but the form announced "a 6-digit code" and was spaced for 6 — so the real code didn't fit the copy or the field. The form is now length-agnostic (fits up to 10, tighter letter-spacing) and localotp_lengthmatches the hosted 8. - Paste leaked onto the canvas (
components/Canvas.tsx). The global paste handler fired even when a form field was focused, so pasting a sign-in code also dropped a card on the board behind the popover. It now ignores pastes that land in inputs/textareas/contenteditable; the OTP field also sanitizes pasted codes (stripping spaces/dashes).
Security
accept_space_invite(supabase/migrations/20260630000100_…). Documented why it must staySECURITY DEFINER(acknowledged advisor lint 0029) and re-asserted least-privilege grants.- Leaked-password protection — noted in
supabase/config.tomlas a hosted-dashboard toggle to enable (clears the advisor WARN; no real surface since auth is passwordless OTP).