Stream Facility
Development Log
A running record of what's been built, what's underway, and what's on the horizon. Updated as the platform grows.
Last updated 2026-07-12
74
Features built
0
In progress
2
Planned
Stream Facility Desktop — Phase 4A: License & Distribution
The desktop app now ships with a full license system: a signed JSON license file downloaded from your account page, verified at startup via HMAC-SHA256, and stored in the OS app data directory. Missing license shows a gate screen; expired license shows a banner but writing keeps working forever.
- ·License file: { payload, signature } where payload is JSON with authorId, email, version, and a 1-year validThrough date; HMAC-SHA256 signed with DESKTOP_LICENSE_SECRET
- ·Secret embedded at compile time via build.rs writing to $OUT_DIR — never read from env at runtime; dev builds use an all-zero placeholder automatically
- ·Startup check: Missing → LicenseGate screen (Get License opens browser, Import License File opens file dialog); Expired → crimson banner, full writing still works, sync disabled; Active → normal app
- ·Web: GET /api/desktop/license generates and serves the signed file as a browser download; /dashboard/desktop page with OS download links and the Download License button
- ·Import flow verifies HMAC before writing the file — no restart needed, React state updates immediately after import
- ·Desktop nav link added to the inside nav bar
Stream Facility Desktop — Phase 4D: Full-text Search
Search across all scenes in a draft from the Plan view. A 320px search panel slides in from the right, debounces input at 300ms, and shows up to 50 hits with a context snippet and direct navigate-to-scene on click.
- ·SQL LIKE query on TipTap JSON content — no FTS5 migration needed; fast enough for typical draft sizes
- ·Rust recursively extracts plain text from TipTap JSON nodes and builds a ~160-char snippet centred on the match
- ·Results grouped and ordered by chapter → scene; each hit shows chapter name, scene name, snippet, and word count
- ·Search and Sync panels are mutually exclusive — opening one closes the other
- ·Click any result to navigate directly to that scene in Write view
Stream Facility Desktop — Phase 4B: Settings & Planning Notes
A dedicated Settings page consolidates account info, platform URL, AI key management, and Write view typography preferences. Separately, chapter summaries and scene notes — fields that existed in the schema but were never shown — now appear as inline text areas in the Plan view on both desktop and web.
- ·Settings page: Account (email + sign out), Platform (editable URL saved to preferences.json), AI Assist (key status, set/remove), Write View (font size and line spacing toggles)
- ·Font size (Small/Medium/Large) and line spacing (Normal/Relaxed/Loose) stored in preferences.json; WriteView reads on mount and applies as CSS custom properties --write-font-size and --write-line-height
- ·Gear ⚙ icon added to Studio and Plan view toolbars
- ·Chapter summary: two-row textarea below chapter header, save-on-blur
- ·Scene notes: single-row textarea below each scene row, save-on-blur
- ·Web platform: new PATCH /api/studio/[draftId]/chapters/[chapterId] route for chapter summaries; both views updated
Stream Facility Desktop — Phase 3B: AI Write Assist
A ✦ Assist panel slides in on the right side of the Write view. Four modes — Continue, Improve, Brainstorm, Custom — send selected text or the full scene to Claude and return a response. The Anthropic API key is stored securely in the OS keychain, never on disk.
- ·Four modes: Continue (1–3 paragraph continuation), Improve (rewrite for clarity and rhythm), Brainstorm (3–5 concrete next ideas), Custom (author-specified instruction)
- ·Selected text used as focus if present; full scene text always included as context — no character limit
- ·Anthropic API key stored in OS keychain via the keyring crate — same keychain service as the sync auth token
- ·Insert at cursor inserts the response directly into the focused TipTap editor at cursor position
- ·Copy button for using the response outside the app; ✕ to dismiss without inserting
- ·Remove API key option in the panel footer resets to the key-setup view
Stream Facility Desktop — Phase 3A: Entity Sync & Markdown Export
Codex entities from the web platform sync to the desktop app and appear as amber highlights in the Write view. Clicking a highlighted name shows a popup with the entity's type, name, and summary. Drafts can be exported to Markdown with a native save dialog.
- ·Entity sync: GET /api/desktop/worlds lists the author's worlds; GET /api/desktop/world/[worldId]/entities returns all 6 codex types (characters, locations, items, creatures, organizations, concepts)
- ·Entities stored locally in a SQLite entities table — name, aliases (JSON), summary, entity_type, world_id
- ·Entity mention plugin ported from the web platform: ProseMirror decoration overlay highlights entity names in amber in Write view without changing stored TipTap JSON
- ·EntityPopup: click any highlighted entity name to see a fixed-position popup with type badge, name, and summary
- ·SyncPanel world picker: Sync Entities button opens a world list; selecting a world pulls entities into SQLite
- ·Markdown export: PlanView Export button opens a native save dialog; Rust tiptap_to_markdown() handles paragraph, heading, blockquote, lists, hr, and all inline marks
Stream Facility Desktop — Phase 2: Core Writing Loop
The full writing loop is working in the native desktop app: plan your draft structure, write in a continuous-scroll TipTap canvas, save version snapshots, and sync to the web platform. PlanView has full drag-and-drop reordering of acts, chapters, and scenes.
- ·PlanView: Acts → Chapters → Scenes hierarchy with @dnd-kit drag-and-drop, inline rename, collapse/expand, word count totals, Expand All / Collapse All
- ·WriteView: continuous-scroll TipTap canvas — all scenes render simultaneously; toolbar applies to the focused scene; Scene Break and Chapter Break insertable at cursor
- ·Scene version control: ⎘ Save Version and ⎇ Version History per scene; ⎘ Save Manuscript Version for a bulk snapshot across all scenes
- ·Sync panel: sign in with Stream Facility credentials, link local draft to a web draft, push dirty scenes, pull updates
- ·Desktop API routes on web platform: POST /api/desktop/auth/token (90-day JWT), GET /api/desktop/drafts, scene pull and push routes
- ·Entity mention plugin registered from launch with a live entity list — highlights update after every entity sync
Stream Facility Desktop — Phase 1: Foundation
A native desktop writing app built with Tauri v2 — roughly 5 MB vs Electron's 150 MB. A .sfstudio SQLite file is created or opened at launch. The Launcher screen handles New World, Open World, and a Recent Worlds list. All draft, chapter, scene, revision, and sync tables initialise on first open.
- ·Tauri v2 + React + Vite + TypeScript — one binary, no Electron, no Chromium bundle
- ·SQLite schema with WAL mode: drafts, acts, chapters, scenes, scene_revisions, sync_config — mirrors web platform MongoDB collections
- ·Project stored as a portable .sfstudio file (SQLite database) — copy the file to move the project
- ·Launcher: New World / Open World dialogs via Tauri file dialog; Recent Worlds list in the sidebar
- ·Full type-safe Rust command layer in src-tauri/src/commands/ with tauri-commands.ts wrapper on the frontend
- ·Same sf-* design tokens as the web platform via a copied @theme {} block — zero UI divergence
Ad System — Tier Gating, Admin Config & Subscription Visibility
Free-tier users see ads in the Lobby and Library; paid tiers see none. Ads are fully configurable from the admin dashboard — paste any provider's embed code and it goes live immediately, or leave blank for house ads promoting the Indie plan. Purchasing a paid subscription hides ads and the active plan is now clearly shown on the user's profile and in the admin users table.
- ·AdZone component: house ad (Indie upgrade CTA) with fallback; script tags injected via DOM creation so third-party scripts execute correctly in both RSC and client components
- ·Lobby: two ad placements (after Authors, between Coming Soon and New Releases); Library refactored to server wrapper + LibraryClient so showAds resolves server-side
- ·Admin → Ads page: paste any provider embed code (AdSense, Carbon, custom); status indicator (house ads vs. custom active); provider quick-reference guide; clear button
- ·Config API patched to merge per top-level key so Tier Config and Ads pages save independently without overwriting each other
- ·Stripe webhook fully wired: checkout.session.completed sets tier on purchase, customer.subscription.deleted reverts to free, invoice.payment_failed marks past_due
- ·Profile page: new Plan & Subscription section — tier badge, benefit summary, Manage Billing button (Stripe portal) for paid users, past-due warning with payment update CTA
- ·Admin Users table: tier cell now shows Stripe/manual source and subscription status inline for all paid-tier users
Codex Tag Expansion & Entity Page Skills
All major codex entity types now carry predefined tag fields — roles and traits for characters, faction/location/item/creature/organization tags, and series themes. Entity pages gain a skills snapshot field so skills active during a specific period override the base character profile in AI context.
- ·Predefined vocabulary: CHARACTER_ROLES, CHARACTER_TRAITS, CHARACTER_SKILLS, LOCATION_TAGS, FACTION_TAGS, CREATURE_TAGS, ITEM_TAGS, ORGANIZATION_TAGS, SERIES_THEMES in src/lib/codexTags.ts
- ·TagInput component extended with optional suggestions prop — clickable chips for one-click add
- ·Characters Skills tab: roles and traits TagInputs with predefined archetypes and personality tags
- ·Entity page skills snapshot: skills active during a specific in-world period; resolveEntityState merges page skills over base entity skills
- ·AI context: formatEntity() now emits Roles: and Tags: lines alongside existing Skills: and Traits:
Entity Pages — Temporal Connections
Entity pages now support page-to-page connections across all codex entity types. Each connection links a source page to the specific page of the target entity that overlaps the same in-world time period — so Amelia's Oct 2024–Dec 2025 page links to Iron Guild's page for the same era, not just the entity itself.
- ·45-label controlled vocabulary grouped by category (Affiliation, Opposition, Alliance, Social, Location-based, Possession, Event-based, Succession, Knowledge, Belief, General)
- ·Temporal overlap resolution: auto-finds and suggests the target entity's overlapping page; no-end-date pages treated as ongoing
- ·Bidirectional connections: author-controlled toggle writes a reverse connection to the target page with linked IDs for precise cleanup
- ·UI: entity type picker → debounced codex search → auto-resolved page → label dropdown → bidirectional toggle, all inline on each PageCard
- ·AI Write Assist: connections injected into context with 1-level-deep page fetch (role + bio from linked page)
Beta Reader Manuscript Watermarking
Invisible watermarks are now embedded in every scene shown to beta readers. If a manuscript excerpt surfaces outside the platform, the watermark identifies both the original author and the specific reader who leaked it.
- ·Two complementary layers applied simultaneously — one detectable by AI text extraction, one by AI vision analysis of screen captures
- ·Watermarks encode both author name and reader identity
- ·Fully invisible to humans at all reading distances and in all rendered contexts
- ·Applied only to beta readers — general public readers see nothing
- ·A decode utility can forensically recover the full payload from any extracted copy
Beta Reader Comment Threads
Beta readers and authors can now have full conversation threads under each scene of a manuscript. Readers in the same campaign see each other's comments. The author can reply from the Write view without leaving the editor. Every comment records which scene revision it was made on.
- ·Comment threads appear below scene prose in the ReadingTree (reader view) and in an inline panel inside the Write view (author view)
- ·Author comments appear with a gold Author badge; any participant can reply to any top-level comment
- ·Same-campaign scoping: readers see only their own campaign's thread plus general author notes
- ·Author replies are automatically visible in the correct campaign thread without any extra configuration
- ·Each comment records the scene revision it was made on — visible as a small badge on the comment
- ·Cmd/Ctrl+Enter submits from any textarea; no page reload required
- ·Author notified in-app whenever a beta reader posts a comment
Security Fix: Forge Access Gate & Admin Tier Guard
Two security issues corrected after the unified profile model eliminated the author role. Every logged-in user was being redirected to /pricing when clicking The Forge because the gate was still checking role === 'author'. Separately, the admin users page could silently downgrade any user's tier to 'free' when an admin changed their role.
- ·Lobby Forge link now gates on isLoggedIn instead of role === 'author' — any authenticated user reaches the dashboard; tier limits apply inside
- ·Admin users page ROLE_DEFAULT_TIER had reader: 'free' — removed; all accounts are role: 'reader' in the unified model so this entry would fire on every role change
- ·Only admin: 'pro' remains in ROLE_DEFAULT_TIER, which is the only role change that correctly implies a tier upgrade
- ·Files: src/app/(outside)/lobby/page.tsx, src/app/(admin)/generalmanagement/users/page.tsx
Studio: Scene Version Control
Authors can snapshot any scene, chapter, or entire manuscript with a single click. Revisions are stored per-scene and can be browsed and restored without leaving the Write view. Beta readers see badges showing which scenes are new or have been revised since their last read. Sidebar version buttons keep controls in view while scrolling through long manuscripts.
- ·scene_revisions collection stores per-scene snapshots: content, wordCount, optional label, createdAt — bulk saves share the same timestamp for easy grouping
- ·Per-scene ⎘ (Save Version) and ⎇ (Version History) in the scene header; history panel rendered above the editor so it is immediately visible without scrolling past prose
- ·Word count and version buttons styled in gold (text-sf-accent / text-sf-accent/60) to match chapter and scene headings
- ·Manuscript-level bulk checkpoint: '⎘ Save Manuscript Version' toolbar button; saves all scenes in a single insertMany; inline label input + '✓ N scenes saved' confirmation
- ·Chapter-level checkpoint via POST /api/studio/[draftId]/checkpoint with optional chapterId — same route, scoped to one chapter
- ·Left sidebar widened from w-64 to w-80; ⎘ per chapter, ⎇ + ⎘ per scene, always visible while scrolling; ⎇ scrolls to the scene and opens its history panel
- ·Sidebar history opener uses useRef<Map<string, () => void>> registration via an always-updating ref pattern to avoid stale closures across multiple concurrent TipTap instances
- ·Beta reader badges: scene_revision_reads collection tracks when each reader opens a scene; green Revised badge if revised since last read, purple New if never opened
- ·Plan view amber dot on scenes with beta comments newer than the latest revision
Unified Author/Reader Profile Model
Eliminated the author/reader registration toggle in favour of a single unified account. Author status is earned (and sticky) the moment a draft is made available or a book is set public. Free-tier users can access the Forge with configurable limits; all tier limits are now admin-editable via the management dashboard.
- ·isAuthor flag earned when draft accessMode→free/paid or book isPublic→true; never revoked
- ·isAuthor carried in JWT and refreshed every 5 minutes from DB
- ·Free tier gets Forge access: 1 world, 1 book, 0 series, 25 codex entries, 0 eras, 1 draft
- ·TierConfig expanded with maxSeries, maxCodexEntries, maxEras, maxDrafts, canAccessForge
- ·All 5 new limits are admin-configurable via /generalmanagement/tiers
- ·InsideNav unified — all users see the same nav including Forge and Studio links
- ·Discovery queries (lobby, authors, search) filter by isAuthor:true instead of role
- ·Registration page simplified to name/email/password — no role selection
- ·Migration endpoint at POST /api/admin/migrations/set-is-author backfills existing authors
- ·Public /readers/[id] profile works for all users; label shows Author or Member dynamically
Beta Reads in The Hearth
Approved beta readers now see their active manuscripts directly in the Library (The Hearth). A new Beta Reads section surfaces above the reading list tabs, showing each campaign with a deadline countdown, state-aware CTAs, and a review prompt when reading is complete.
- ·New GET /api/beta-reads returns approved, completed, and expired applications with campaign title, author name, and review status
- ·Crimson rule section header matches the wing personality of The Stage
- ·Left border stripe encodes state: crimson (active), muted gold (completed), grey (expired)
- ·Deadline countdown turns crimson at ≤3 days remaining
- ·Active reads show Read → link to /beta/{id}/read
- ·Completed reads prompt Leave review → if not yet reviewed, or show Reviewed ✓
- ·Section invisible to users with no beta history — no empty state
Beta Campaigns: Launch from Studio
Authors can now initiate or manage a beta reading campaign directly from the Studio manuscript toolbar. The Beta button sits alongside Plan, Write, Review, Chat, and Export — and knows whether a campaign already exists for this book.
- ·No active campaign: '+ New Campaign' button (crimson-tinted) appears in the Studio toolbar
- ·Active campaign: 'Beta ↗' (subtle bordered) links to the campaign manage page
- ·Studio layout fetches bookId, betaId, worldName, and seriesName from the draft record
- ·New campaign URL encodes ?bookId, ?world, ?series — context appears as a line under the form header
- ·New campaign form auto-selects the matching Studio draft and skips to step 2 with title and book pre-filled
- ·Beta campaign manage page shows '← Manuscript' breadcrumb link back to the Studio plan view
Lobby: Wing Directory
The lobby page now has a four-panel wing directory between the masthead and the following feed. Each panel is a clickable entryway to one of the platform's four areas — The Hearth (Library), Local & You (Authors), The Stage (Beta), and The Forge (Dashboard/Studio).
- ·gap-px bg-sf-border grid — the border colour is the gap; panels sit flush against each other
- ·Short h-px w-8 rule at the top of each panel encodes wing personality in colour
- ·The Hearth (gold): /library if signed in, /sign-in if not
- ·Local & You (dim gold): /authors — community discovery
- ·The Stage (crimson): /beta — open manuscripts for beta reading
- ·The Forge (subtle or border): /dashboard if author, /pricing if not
- ·sm:grid-cols-2 lg:grid-cols-4 — 2×2 on tablet, 4-wide on desktop
- ·Compass direction labels removed — those were internal book references, not UI copy
Slug URL System
All public and dashboard URLs now use human-readable slugs instead of raw MongoDB ObjectIds. Slugs are generated at creation time and lazily migrated on first visit for existing entities. The server wrapper pattern keeps client components decoupled from slug resolution.
- ·slugify() + resolveEntitySlug() in src/lib/slugUtils.ts handle creation and resolution
- ·Lazy migration: first ObjectId visit generates and saves the slug, then redirects
- ·Server wrapper pattern: thin page.tsx resolves slug → ObjectId; PageClient.tsx receives OID
- ·Dashboard pages updated: books, series, worlds, codex pages, timeline, currently-writing widget, global codex, persona links
- ·MongoDB projection caveat: restricted projections must explicitly include slug: 1
- ·Edit subroutes (/edit) use useParams() and continue to accept ObjectIds — slug support deferred
Email System: Verification, Daily Digest & Author Mailing Lists
The platform now has a complete email system built on Resend. New accounts must verify their email before accessing the dashboard. Authors get a daily digest of unread notifications instead of individual emails. Authors can collect subscribers via an external mailing list link or the built-in subscriber system with their own Resend key.
- ·Email verification: new accounts created with emailVerified=false; 24-hour token sent on registration; middleware blocks /dashboard and /studio until verified
- ·Daily notification digest cron at 08:00 UTC: groups unread notifications per author into one email; digestSentAt field prevents double-sending; per-author opt-out toggle in Profile
- ·Author mailing list — external path: paste a Mailchimp/ConvertKit/etc URL in Profile; 'Join my mailing list' button appears on public profile
- ·Author mailing list — platform path: store own Resend API key (AES-256-GCM encrypted) + verified from-address; native subscribe widget on public profile
- ·Subscriber endpoint rate-limited by IP; unsubscribe via tokenized one-click link appended to every campaign email
- ·Campaign sending batches 50 recipients at a time using author's own Resend key (author owns cost + CAN-SPAM compliance)
- ·NextAuth v5 split-config: auth.config.ts (edge-safe) used by middleware; auth.ts spreads it and adds Node.js-dependent Credentials provider
Security Hardening: Pre-Production Audit
Full security audit completed before moving to a live server. All HIGH and MEDIUM severity issues resolved. Covers authentication, authorization, injection prevention, rate limiting, content security headers, account data isolation, and ESLint security rules.
- ·JWT: 24-hour maxAge; 5-minute periodic DB re-validation; force sign-out on suspension, deletion, or post-change tokens; passwordChangedAt set on every password change
- ·Rate limiting on registration, login, forgot-password, email verification resend, and subscriber signup — all via login_attempts collection with namespaced keys
- ·Security headers: CSP, HSTS 2-year preload, X-Frame-Options DENY, X-Content-Type-Options, Referrer-Policy, Permissions-Policy
- ·Central middleware: auth + admin enforcement for /dashboard, /studio, /generalmanagement, /api/admin/
- ·IDOR fixed on gift code lookup (scoped to buyerAuthorId); IDOR on studio progress route (draftId ownership check)
- ·MongoDB injection prevented: entity collection allowlist in AI chat; status field allowlists on books and series
- ·Content-Disposition RFC 5987 encoding on manuscript filenames; magic bytes validation on DOCX/PDF import
- ·authors/me GET uses explicit field allowlist projection (excludes aiKeys, resendApiKey, stripeIds, suspended)
- ·Account deletion expanded to ~50 PII collections; admin/debug-config route deleted
- ·eslint-plugin-security installed with no-eval, no-implied-eval, no-new-func, @typescript-eslint/no-explicit-any as errors
WorldMapView: Interactive World Map with Era-Driven Time Player
The world detail page now shows an interactive map with entity pins and an era-anchored time player. Scrub through the world's timeline and watch pins appear and disappear as entities enter and leave each region. The time axis uses the same era coordinate system as the visual Gantt timeline.
- ·Era-anchored slider: min/max = first era startSortKey → last era endSortKey — same axis as the visual Gantt timeline
- ·Segmented era track with proportional color bands, active era at full opacity, red scrubber line
- ·Pins visible when their time range covers the current scrubber position (null bound = always visible on that side)
- ·Ghosted pins: hidden (time-filtered) pins rendered at 25% opacity so the author always sees all placements
- ·Click any pin to open a selected-pin card with entity name, type badge, label, and date range
- ·Current position shown as 'Era Name · specific date label'
- ·Returns null (no-op) when the world has no mapImageUrl — no empty state to manage
World Map Placements System
Authors can pin any codex entity onto the world map at one or more points in time. A MapTab component on entity detail pages provides a click-to-place map picker with mini-map thumbnail cards. Positions are stored as percentages so they work at any image resolution.
- ·map_placements collection: mapX/mapY as 0–100% of image dimensions; entityName denormalized for join-free world queries
- ·beginsSortKey / endsSortKey use worldDateToSortKey() — same formula as entity_pages for range queries
- ·GET /api/map-placements?worldId= returns all pins for WorldMapView; ?entityId+entityType= for MapTab
- ·POST auto-fetches entityName from the source collection across all 21 entity types via COLLECTION_MAP + NAME_FIELD tables
- ·MapTab: click-to-place picker with crosshair overlay, mini-map thumbnail cards, EraDateSection for time range
- ·MapTab now wired to Characters, Locations, Regions, Factions, Creatures, Items, and Organizations
- ·linkedEventIds checkbox picker in the placement form: link any placement to world events; count shown on thumbnail cards
- ·mapImageUrl field added to Location and Region APIs and detail pages (ImageUpload component)
Studio Write: Codex Entity Highlighting
Entity names from the codex are now highlighted in amber in the Write view as you type. Hovering any highlighted name shows a popup with the entity's current world-state — era-aware bio, role, status, and portrait. Persona aliases map to their parent character's card.
- ·ProseMirror decoration plugin — no changes to doc structure, pure visual overlay
- ·GET /api/studio/[draftId]/codex-entities fetches all entities for the draft's linked world
- ·Longer names matched first so 'Amelia Williams' takes precedence over 'Amelia'
- ·Persona aliases highlighted but popup shows the canonical parent character's codex card
- ·Hover popup fetches /api/world-state with the scene's era context for era-accurate state
- ·Popup shows portrait/image, type badge, role (accent), bio (3-line clamp), and link to codex
- ·160ms hover delay prevents flicker; popup hover cancels the close timer
Studio: Write Tab, Scene Persistence & Navigation Fixes
A Write tab now appears in the Studio toolbar alongside Plan, Review, Chat, and Export. The last scene visited in the Write view is persisted to localStorage so clicking Write returns you to exactly where you left off. Two bugs fixed: the Review page crashed with a MongoDB projection error, and the /write fallback redirect returned 404.
- ·Write tab added to StudioShell toolbar; links directly to the last-visited scene
- ·Last scene stored in localStorage under studio-last-scene-{draftId}
- ·Fallback /write route (server component) redirects to the first scene when no stored scene exists
- ·Scroll-to-scene on Write load deferred 300ms so TipTap editors render at full height first
- ·Review fix: invalid MongoDB mixed inclusion/exclusion projection replaced with conditional pure-inclusion
- ·Redirect fix: scene ownership verified via the draft document (scenes don't store authorId)
Personas: First Appears In Fix & Aliases Dirty State
Two character codex improvements: the persona creation form now correctly captures and persists the 'First Appears In' book and chapter (a bug that silently dropped the value), and the Aliases tab Save button is gated on a dirty state so it's only active after a change is made.
- ·Persona creation form adds dedicated 'First appears in book / Chapter' fields above the reveal section
- ·introducedIn and introducedInChapter sent to POST /api/character-personas and persisted on the persona doc
- ·Save Name Variants button disabled until a name variant is actually changed
- ·'Name variants saved.' confirmation shown for 3 seconds after a successful save
- ·aliasesDirty state set on any AliasInput onChange; cleared automatically on save
World State Resolver
A new GET /api/world-state endpoint takes an entity, an era, and an in-world date and returns the entity's state at that exact moment — base profile merged with the active Page snapshot. This is the core operation that AI chat and writing assist will use to answer 'what was X like in Year 847?'
- ·Accepts entityId, entityType, eraId, and sortKey (integer from worldDateToSortKey)
- ·Finds the Page snapshot whose period covers the requested date (inWorldBeginsSortKey ≤ sortKey ≤ inWorldEndsSortKey)
- ·Merges base entity fields with Page snapshot fields — Page fields win when non-empty
- ·Returns activePage metadata alongside the merged state for transparency
- ·Supports all six entity types: character, item, faction, location, creature, organization
Entity Pages: Date Sort Keys
Every Page now stores inWorldBeginsSortKey and inWorldEndsSortKey — integer values derived from the WorldDate object at save time. These enable reliable range queries for the state resolver and future AI chat system without relying on string comparison.
- ·worldDateToSortKey() added to src/lib/worldDate.ts
- ·Formula: effectiveYear × 100,000,000 + month × 1,000,000 + week × 10,000 + day × 100 + hour
- ·Coarser fields (millennium, century, decade) treated as year offsets for monotonic ordering
- ·String-format dates (free-text fallback) produce null sort key — they cannot be range-queried
- ·Computed on POST and recalculated on PUT in entity-pages routes
Codex: Persona Alias System
Aliases that represent a full alternate identity — 'Annabelle Westford' as a secret persona of 'Amelia Williams' — are now first-class character documents linked to their canonical character via a parent-child relationship. The connection stays hidden until the author-set reveal point, preventing spoilers from appearing on the codex page.
- ·Persona characters stored in the characters collection with isPersona: true + parentCharacterId
- ·Reveal gating: revealAtBook (ObjectId) + revealAtChapter (number) on the persona document
- ·Characters list and codex search both filter isPersona: { $ne: true } — personas never appear in listings
- ·Canonical character page shows a 'Persona Aliases' section in the Aliases tab
- ·Persona pages show an amber banner and an 'Identity Reveal' section with the gate settings
- ·DELETE on a canonical character cascades to delete all its persona aliases
- ·API route: GET/POST /api/character-personas
Codex: Entity Pages (Period Snapshots)
A new 'Pages' tab on character codex entries lets authors record period snapshots — what a character looked like, their role, and their status during a specific span of in-world time. Each page has two time axes: an in-world date range (for AI chat queries) and a narrative gate (for writing assist spoiler gating).
- ·Stored in entity_pages collection with entityId, entityType, eraId, inWorldBeginsDate/Label, inWorldEndsDate/Label, narrativeBeginsBook/Chapter
- ·Snapshot fields: bio, role, lifeStatus, hairColor, eyeColor, build, appearance, distinguishingFeatures, notes
- ·EntityPagesTab component handles full CRUD with collapsible physical fields
- ·Two time axes: in-world WorldDate range (AI chat) + narrative book/chapter gate (writing assist)
- ·API routes: GET/POST /api/entity-pages, GET/PUT/DELETE /api/entity-pages/[id]
Studio: In-World Date Anchoring
Acts and chapters in the Studio Plan view can now be anchored to an in-world era and date range. A clock icon on each row opens an inline date panel with an era selector and begins/ends text fields. The write page sidebar shows the active chapter's era and date below the chapter name.
- ·Click the ◷ icon on any act or chapter row to set its in-world period
- ·Era dropdown pulls from the world linked to the draft's book
- ·Begins and ends date labels stored on story_acts and story_chapters documents
- ·Date badge appears on the row when set; accent-colored ◷ icon stays visible as an indicator
- ·Write page sidebar shows era · begins date below each chapter name
- ·Creates a unified coordinate system: every scene position maps to both a narrative coordinate and a world date
Codex: Type Reassignment
Authors can move any codex entity from one type to another — a Character to a Creature, a Location to a Region, etc. A pre-confirmation panel shows exactly which type-specific fields actually have data and won't carry over, so nothing is lost by surprise.
- ·Moves the entity between MongoDB collections and redirects to the new page
- ·Common fields always carried over: name, description, aliases, world, era, images, Extra Notes
- ·Shows only fields with real data — no phantom warnings for empty fields
- ·Confirm checkbox required before the move is allowed
- ·Available on every codex entity detail page via '⇄ Change type…'
Codex Import: Duplicate Detection & Resolution
The import preview page now detects entries that already exist in the selected world and lets authors choose what to do with each one — insert a second copy, merge into the existing record, or rename before inserting.
- ·Duplicate check runs automatically when a world is selected (POST /api/import/check-duplicates)
- ·Flagged entries show an amber ⚠ duplicate badge
- ·Three per-entry actions: Insert new, Merge into existing (fills empty fields only), Rename & insert
- ·Rename shows a text input pre-filled with 'Name (imported)'
- ·Re-check button for when entry types are changed after the initial check
- ·Result screen shows inserted and merged counts separately
Codex: NovelCrafter Import
Upload a NovelCrafter .zip export and import any combination of its entities — characters, lore, locations, factions, and more — directly into the codex. A preview step lets authors include or exclude specific items before committing.
- ·Supports all 19 codex entity types
- ·Green ✓ / red ✕ toggle pair on every entry row — defaults to included
- ·Unmapped fields captured in Extra Notes on every imported document
- ·Imported entities show a review banner until first saved
- ·importedAt timestamp set on import; cleared on first save
- ·Thumbnail images extracted from the ZIP and uploaded to Cloudinary — stored as imageUrl on the inserted document; non-fatal if missing
Codex: 5 New Entity Types
Five genre-agnostic entity types added to the world-building codex — Conditions, Knowledge, Practices, Resources, and Works — bringing the total to 19. Each is fully wired into the tab view, search, import, type reassignment, and codex-search.
- ·Conditions: status effects, ailments, curses (effects, cause, duration, cure)
- ·Knowledge: lore entries, legends, discovered truths (reliability, source, body)
- ·Practices: rituals, professions, martial arts, methods (practitioners, steps)
- ·Resources: materials, currencies, commodities (uses, rarity, sources)
- ·Works: in-world books, artworks (creator, status, content/excerpts)
- ·Knowledge and Works use 'title' as primary name field (same pattern as Lore)
- ·codex-search architectural fix: dynamic nameField lookup corrects lore/knowledge/works search
Image Upload Optimization
Two complementary layers keep uploaded images compact. Client-side canvas resizing fires before the upload even starts. Cloudinary URL transforms ensure optimized delivery to readers regardless of what was stored.
- ·Client-side: landscape capped at 2400 px, portrait at 1200 px, files over 2 MB compressed
- ·Canvas → JPEG 0.88 quality output; GIFs pass through unchanged (preserves animation)
- ·Spinner visible during resize phase, not just during the HTTP upload
- ·Cloudinary delivery transform injected into stored URL: c_limit, w_2000, q_auto, f_auto
- ·f_auto serves WebP or AVIF to browsers that support it
Save Confirmation Flash
All 23 codex entity and content detail pages now show a green 'Saved ✓' flash for 2 seconds after a successful save — eliminating silent saves that looked like nothing happened.
- ·SaveButton component manages three states: Saving… / Saved ✓ / Save Changes
- ·Green background on success state for clear visual confirmation
- ·Button disabled during both saving and the 2-second success window
- ·Applied across all codex entity, book, series, excerpt, and era detail pages
Codex: Extra Notes & Import Metadata
Every codex entity now has an Extra Notes field for imported metadata and freeform author notes. Imported entities display a warning banner prompting review. Saving any entity clears the import flag.
- ·Extra Notes textarea on all 14 codex entity detail pages
- ·Visible on imported entities or when the field already has content
- ·importedAt set to null on every PUT save — banner shows once after import
- ·Saves with the main form — no separate submission needed
Codex: Navigation & Delete Consistency
All 14 codex entity detail pages now navigate back to the correct type-filtered codex list after delete. Breadcrumbs consistently show 'Codex' as the parent link rather than world or book context.
- ·Delete redirects to /dashboard/codex?type=<key> on all entity pages
- ·Breadcrumb: Codex › Entity Name with link to the type-filtered list
- ·Consistent across all 14 entity types
Continuous Scroll Write View
All scenes render simultaneously in one scrollable canvas, each with its own editor. No more clicking to load the next scene — scroll through the entire draft, click any scene in the sidebar to jump directly to it.
- ·Every scene has an independent TipTap editor instance
- ·Toolbar applies to whichever scene is focused
- ·Sidebar scene links scroll and auto-focus the target scene
- ·Scene Break and Chapter Break work on the active scene at cursor position
Plan View: Expand/Collapse All & Inline Scene Preview
Expand or collapse the entire draft outline in one click. Reveal any scene's prose directly in the Plan view without navigating to Write — useful for reviewing structure and content together.
- ·Expand All / Collapse All buttons in the Plan view header
- ·Per-scene toggle reveals inline prose preview below the scene row
- ·Content lazy-fetched on first expand and cached for repeated toggles
- ·Individual node toggles continue to work independently
Draft Access Mode & Status
Authors control who can read their manuscript via an access mode toggle on the draft. A status badge marks drafts as in-progress or finished.
- ·Private: manuscript hidden on the public book page (default)
- ·Free: full prose visible to any visitor
- ·Paid: chapter/scene structure visible; scene content gated
- ·Status badge (Draft / Finished) shown in Plan view
ReadingTree on Public Book Page
The public book detail page embeds the manuscript as an expandable act → chapter → scene tree. Approved beta readers get full prose access regardless of the draft's public access mode.
- ·TipTap JSON rendered inline — no editor overhead
- ·Reading progress checkboxes per scene, stored per user
- ·Expand All / Collapse All for the full tree
- ·Beta reader access granted via approved application with active deadline
- ·Multi-campaign support: access granted if approved under any campaign for the book
Beta Campaigns: Studio Draft Support
Beta campaigns can now use a Studio draft as the manuscript source — not just uploaded files. Selecting a Studio draft in the campaign wizard auto-fills the linked book and target audience.
- ·Campaign creation lists Studio drafts alongside uploaded manuscripts
- ·Selecting a Studio draft auto-populates Linked Book and Target Audience
- ·Approved readers land on the public book page (ReadingTree) instead of the legacy file reader
- ·manuscriptSource field distinguishes studio vs. upload campaigns
Beta Reader: Real-time Deadline Enforcement & Reapproval
Access is checked against the live deadline at every entry point — no gap between expiry and the nightly cron job. Authors can reapprove expired readers to grant a fresh deadline.
- ·All read-access checks gate on deadlineAt > now in real time
- ·Campaign detail page shows expired status immediately when deadline passes
- ·Days remaining counter on the reader's campaign page (amber < 7 days, red < 3 days)
- ·Reapprove button in Dashboard → Beta → Applications for expired readers
- ·Reapproval sends a fresh approval email with a new deadline
Beta Reader: Inline Scene Comments & Threads
Beta readers and authors hold full threaded conversations under each scene of a manuscript. The author replies from the Write view. Comment threads are campaign-scoped so readers see only their own campaign's discussion.
- ·Comment threads appear below scene prose in ReadingTree (reader) and as an inline panel in the Write view (author)
- ·Author badge in gold; any participant can reply to any top-level comment (one level deep)
- ·Same-campaign scoping — reader sees their campaign thread plus general author notes
- ·Each comment records which scene revision it was made against
- ·Author notified in-app when a reader posts; readers notified when author replies (fan-out via insertMany)
- ·Cmd/Ctrl+Enter submits; no page reload
- ·Plan view amber dot on scenes with unaddressed comments newer than the latest revision
Studio — Story Creator (Phases 1–4)
Full writing suite at /dashboard/studio. Four views per draft: Plan (structural outline), Write (TipTap editor), Review (word counts + codex heatmap), Chat (AI conversation). Available to Indie and Pro tiers.
- ·Acts → Chapters → Scenes hierarchy with drag-and-drop reorder
- ·Inline Scene Break and Chapter Break in the Write view
- ·Codex entity heatmap in Review (mentions per scene across all 14 entity types)
- ·AI Chat with +Context picker (Pro only)
- ·DOCX / Markdown / Plain Text export with scope selector
Studio — AI Write Assist & Prompt Library
A '✨ Write' panel in the Write view. Choose a prompt template, fill in variables, and the AI streams prose into a preview. Insert at cursor, regenerate, or fork the prompt into a personal library.
- ·6 built-in prompts: Draft from Beat, Continue Scene, First Draft, Expand, Dialogue, Rewrite
- ·Auto-injected context: draft title, chapter label, current scene content
- ·User prompt library with fork-from-system support
- ·Pro only; requires ANTHROPIC_API_KEY
Studio — Manuscript Import (DOCX / PDF)
Upload a DOCX or PDF to the Studio draft list and have it land as a fully structured draft — acts, chapters, and scenes auto-detected from the heading hierarchy.
- ·DOCX headings (H1/H2/H3) map to acts, chapters, and scenes
- ·Inline marks preserved: bold, italic, underline, strikethrough
- ·PDF imported as a single scene; author splits manually with Break tools
- ·Drag-and-drop import dialog with success screen and stats
Beta Reader System
Authors run beta reading campaigns for manuscripts. Readers discover campaigns, apply, read, and submit structured reviews. Deadline tracking, nightly expiry cron, and admin config included.
- ·Campaign discovery at /beta with genre filter and pagination
- ·Structured review form: per-category star ratings, comments, strengths/weaknesses
- ·Nightly cron auto-expires past-deadline applications and sends warnings
- ·Admin → Beta Config: default deadline days, warning window, max extensions
- ·Manuscript revision history with reader notification on new upload
Character Expansion: Physical, Aliases & Connections
Characters gained a four-tab detail page (Profile, Physical, Aliases, Connections), reveal-gated aliases for AI writing assist, and links to any codex entity.
- ·Physical fields: race/species, eye color, hair, height, weight, build
- ·Aliases with revealedIn book + chapter — AI context gating
- ·Connections tab: links to characters, locations, factions, events, items, vehicles
- ·New entity types: Items/Artifacts and Vehicles
World-Building Codex: 7 New Entity Types
Seven additional entity types added to the codex: Eras, Languages, Magic & Power Systems, Religions & Deities, Regions, Governments, and Organizations. All share the same author + world scoped pattern.
- ·Eras with structured WorldDate start/end and visual timeline integration
- ·Magic & Power Systems: source, cost/toll, limitations, rules
- ·Religions: type dropdown (Deity, Pantheon, Religion, Cult…)
- ·Codex nav dropdown in dashboard nav covers all 14 entity types
World Timeline Events & Visual Gantt
A visual Gantt chart for world timelines with proportional era columns, fractional year event positioning, zoom, pan, and bidirectional entity links.
- ·Era columns sized proportionally by year span
- ·Fractional year positioning: 15 March 2023 → 2023.18
- ·Adaptive tick marks: years, months, or days depending on zoom level
- ·Mouse wheel zoom and click-drag pan
- ·World events linked to any codex entity appear on that entity's Timeline tab
Cross-Entity Search
Search across Books, Series, Authors, Worlds, and Beta campaigns from a single search bar. Genre, audience, and rating filters. Sortable results per tab.
- ·Lobby masthead search bar navigates to /search?q=...
- ·Five tabs: Books, Series, Authors, Worlds, Beta, All
- ·Genre filter, audience filter, min-rating filter (Books only)
- ·URL-driven filters — shareable and bookmark-able
- ·Genre tag chips on all public pages link directly to filtered search
Reviews & Ratings
Readers rate and review any public book. One review per reader per book. Ratings surface on author profiles and search results.
- ·Star picker (1–5) with optional title and body
- ·Average rating and count shown on public book and author pages
- ·Edit and two-step delete for own reviews
- ·Authors cannot review their own books
- ·Recent Reviews section on public author profiles
Reader Profiles & Personal Library
Readers have public profiles showing reading stats, favorite genres, reviews, and an 'open to beta reading' badge. A personal library tracks Want to Read / Currently Reading / Finished.
- ·Public profile at /readers/[id] with hero banner
- ·Beta reads completed stat, open-to-beta badge
- ·Want to Read shelf (private), reading list (public)
- ·Favorite genres tag-picker on dashboard profile page
Social — Follows & Following Feed
Authors and readers can follow each other. The Lobby shows a Following feed with recent books, beta campaigns, and worlds from followed authors.
- ·Follow / Unfollow button on author and reader profiles
- ·Follower and following counts on profiles
- ·Following feed in Lobby: new books, open beta campaigns, new worlds (last 60 days)
Platform Shop
Three product types: subscription gift codes, digital content, and physical products. Gift codes redeemable at /redeem extend or grant a subscription tier.
- ·Gift certificates generate a one-time code after Stripe checkout
- ·Gifted tier stacks with paid tier; same-tier codes extend expiry
- ·Admin creates products with Stripe one-time price IDs
- ·Order history in Dashboard → Orders
Subscription System (Stripe)
Stripe billing for Indie and Pro tiers. Webhook handles subscription events. Admin can override any user's tier manually with a billing note.
- ·Checkout and customer portal via Stripe-hosted pages
- ·Webhook handles checkout.session.completed, subscription updated/deleted, payment failed
- ·Tier enforced live from DB — no stale JWT values
- ·Manual tier overrides in Admin → Users
Email Infrastructure (Resend)
Transactional email via Resend. Silent no-op when RESEND_API_KEY is absent — safe in CI. Wired for password reset, beta approval, manuscript revision notifications, and deadline warnings.
- ·Forgot password: 64-hex token, 1-hour expiry, self-sealing after use
- ·Beta approval email with deadline date and direct read link
- ·Manuscript revised: all approved readers notified of new version
- ·Deadline warning email alongside in-app notification
Admin Panel
Admin panel at /generalmanagement (security through obscurity). One-time setup at /stream-setup. Manages users, tiers, beta config, genres, shop products, and newsletter.
- ·Setup route self-seals after first admin is created
- ·Admin self-deletion blocked — must transfer rights first
- ·Tier config editable per tier (limits, features, ad visibility)
- ·Beta Config: deadline days, warning window, max extensions
Studio AI — User API Keys & Multi-Provider
Authors supply their own API key from Anthropic, OpenAI, or OpenRouter. Stream Facility does not provide AI access — authors pay their own provider directly. Keys are AES-256-GCM encrypted at rest and never returned to the client.
- ·Three providers: Anthropic, OpenAI, OpenRouter (200+ models via one key)
- ·Keys encrypted with AES-256-GCM — only the model and active provider are ever returned
- ·Per-provider model selector with curated list (Sonnet, Opus, Haiku, GPT-4o, Gemini, Llama, Mistral…)
- ·Managed in Dashboard → Profile → AI Keys
- ·Chat and Write Assist automatically use whichever provider and model is active
Digital Product Delivery
Automated file delivery for digital shop products. On purchase, the buyer receives a signed, time-limited download link. Physical products gain shipping address collection and admin fulfillment tracking.
- ·Signed download URL generated on webhook confirmation
- ·Files stored in Vercel Blob; access logged per download
- ·Physical: shipping address collected at Stripe checkout
- ·Admin marks orders shipped; buyer notified with tracking info
Social — Friends & Favorites
Two-way friend connections with a request/accept/decline flow, and a favorites system for bookmarking books, series, and worlds. FriendButton and FavoriteButton are wired to public pages. Dashboard pages manage both.
- ·friend_requests collection: symmetric request flow — send, accept, decline, cancel, unfriend; either party can end the connection
- ·FriendButton component: four states — Add Friend / Request Sent (cancel) / Accept+Decline / ✓ Friends (unfriend); wired to public author pages
- ·favorites collection: any logged-in user can favorite any public world, book, or series; toggle API returns updated public count
- ·FavoriteButton: heart icon with count, unauthenticated users see sign-in link; wired to world, book, and series public pages
- ·Dashboard → Friends: pending received requests with Accept/Decline, friends list with Unfriend, sent requests with Cancel
- ·Dashboard → Favorites: items grouped by type (Books → Series → Worlds) with inline Remove
- ·Gift shop 'From your network' recipient tab remains deferred — infrastructure is ready
Reader Passage Highlights
Logged-in readers can highlight any passage while reading free books or beta manuscripts. Four colors, optional notes per highlight, highlights persist across sessions. Analytics stats bar and public sharing now built.
- ·highlights collection stores userId, draftId, bookId, sceneId, sceneLabel, selectedText, color, optional note, isPublic: false
- ·Text selection in ReadingTree triggers a floating HighlightToolbar; onMouseDown preventDefault preserves the browser selection while clicking toolbar buttons
- ·Four swatches: yellow #fde68a, green #a7f3d0, blue #bfdbfe, pink #fbcfe8 — click to save immediately; 'Note' mode adds textarea and re-picker before saving
- ·highlightSegments() injects <mark> elements into React-rendered TipTap prose without a DOM traversal — works on text nodes, handles multiple highlights per text node
- ·Dashboard → Highlights: analytics stats bar (total, books, most-marked, colors) computed client-side; Share/Public toggle per highlight via PATCH /api/highlights/[id]
- ·Public page at /highlights/[id] renders passage, note, book, scene, sharer — no auth required; generateMetadata for SEO title
Beta Cancellation: Reader Email
When an author cancels a beta campaign, every approved reader now receives a personalised email alongside the existing in-app notification. The email is sent via the platform Resend key.
- ·betaCancelledEmail() template added to src/lib/email.ts
- ·DELETE /api/beta-requests/[id] now sends one email per affected reader before expiring their applications
- ·In-app notification still fires as before; email adds a second channel
Codex: Aliases, Map Tab & Entity Pages on Additional Entity Types
Factions, Creatures, Items, and Organizations now match the depth of Characters and Locations: reveal-gated name aliases, a Map tab for pinning entities on the world map, and an Entity Pages tab for period snapshots.
- ·AliasInput with revealedIn/revealChapter gating added to Factions, Creatures, Items, and Organizations
- ·MapTab added to all four entity types alongside existing Characters, Locations, and Regions
- ·EntityPagesTab added to Factions, Creatures, Items, Organizations, and Locations
- ·TimelineTab added to Organizations (was the only entity type missing it)
- ·API PUT handlers for all four entity types updated with aliases validation block
In-App Notification System
A complete in-app notification system: bell dropdown in the nav (with count badge and 5-item preview) and a full notifications page. Six event types trigger notifications — follows, friend requests, friend accepts, beta comments, beta replies, and beta applications.
- ·NotificationBell in InsideNav: fetches announcements + 5 most recent notifications on mount; red dot when unread; dropdown closes and marks read on link click
- ·TYPE_ICON map: ◎ beta_comment/reply, ◇ beta_application, ✦ friend_request/accepted, ↑ follow, ♦ gift_received, ✓ purchase_confirmed, ★ gift_redeemed
- ·follow notification fires on follow, resolves profile link from slug + isAuthor
- ·friend_request notification fires on POST /api/friends; friend_accepted fires on accept in PUT /api/friends/[id]
- ·beta_reply fan-out: author reply to a scene triggers insertMany to all readers who commented on that scene
- ·/dashboard/notifications page: full list, auto mark-all-read after 800ms, relative timestamps, unread badge
- ·Notifications card added to Dashboard home Social section (now 4-column grid)
- ·Daily digest cron already integrated — digestSentAt prevents double-sending
Studio: Scene-Level Date Anchoring & Structured WorldDates
In-World Date Anchoring extended to scene level. Acts, chapters, and scenes now store full structured WorldDate objects (not just label strings), enabling sort-key-based range queries for Write Assist and entity-level chat.
- ·◷ icon on scene rows in Plan view opens inline date panel — same era selector and begins/ends fields as act/chapter rows
- ·story_scenes gains eraId, eraName, inWorldBeginsDate/Label, inWorldEndsDate/Label
- ·story_acts and story_chapters gain inWorldBeginsDate/inWorldEndsDate WorldDate objects alongside existing label strings
- ·PATCH handlers compute inWorldBeginsSortKey / inWorldEndsSortKey via worldDateToSortKey() and store them for range queries
- ·resolveEntityState() utility in src/lib/studio/resolveEntityState.ts: merges entity_pages snapshot onto base entity at a given sortKey + optional eraId
Studio Write Assist: World State Integration
Write Assist now delivers era-accurate codex context to the AI. Entity descriptions in the prompt reflect the entity's state at the scene's exact position in the world timeline — resolved via the active entity_pages snapshot, not the base profile.
- ·generate route extracts sceneSortKey and sceneEraId from the scene document
- ·Each context ref resolved via resolveEntityState(db, entityId, collection, authorId, sceneSortKey, sceneEraId)
- ·resolveEntityState finds the entity_pages snapshot covering the sort key and merges snapshot fields onto the base entity
- ·If a character is a 'village guard' in Year 847 and a 'hero' in Year 850, the AI receives the Year 847 description when the scene is anchored there
Entity-Level AI Chat — World, Book, Series
World, book, and series detail pages now have a Chat tab for AI conversations anchored to that entity's full context — codex, draft structure, or series scope.
- ·Shared EntityChatView component with ContextGroup, ContextRef, MessageBubble types
- ·World chat: pulls from any of the 21 codex entity collections via ALLOWED_ENTITY_COLLECTIONS allowlist + CODEX_CATEGORIES grouping
- ·Book chat: scoped to the book's linked world and draft structure
- ·Series chat: scoped to the series and its associated books
- ·All three routes use the author's configured AI provider via getAiConfig() and createChatStream() — same path as Studio Chat
- ·Chat history persisted per author per entity
Write Assist — Alias Reveal Gating
The AI generate route now filters aliases from entity context based on the author's current chapter position, preventing spoiler aliases from leaking into the prompt before they're narratively revealed.
- ·formatEntity(doc, opts?) accepts { bookId, chapterNum } and filters aliases where revealedIn === bookId AND revealChapter > chapterNum
- ·chapter.order fetched from story_chapters and converted to 1-indexed chapterNum; draft.bookId passed as bookId
- ·Aliases without revealedIn, or gated to a different book, are always shown
Shop — Gift From Your Network
Gift certificate purchases can now target a friend directly from a 'From your network' tab in the recipient selector, without having to search by name or email.
- ·Two-tab switcher: Search (by name/email) and From your network
- ·Friends list loaded lazily from GET /api/friends on first tab switch
- ·Accepted friends appear as a scrollable list; clicking sets recipient and returns to the selected state
Story Pages — Global Browse
A new /dashboard/pages page lets authors see and filter all entity_pages records across their worlds in a single table.
- ·Server component with URL-driven type and world filters (chip tabs, no client-side state)
- ·Batch entity name lookup per collection; world name join
- ·Table columns: Entity, Type, World, Era, Date Range — responsive hidden columns
Entity Personas — Factions, Items, Locations
Factions and items can now have full persona aliases — separate codex entries representing cover identities, true names, or hidden forms — using the same reveal-gating model as character personas.
- ·Generic GET/POST /api/entity-personas?entityType=faction|item|location&parentId=X
- ·Persona documents stored in the same collection with isPersona: true and parentId
- ·Factions and items list pages exclude personas via isPersona: { $ne: true } query filter
- ·Personas section on faction and item detail pages: inline creation form with name, role, description, reveal book
- ·Characters list page now shows a Personas subsection for the global character personas browse
Pro Tier Exclusive Features
Pro tier is defined and gated but exclusive features beyond AI Chat are not yet designed. Known candidates include manuscript editing tools, priority discovery placement, and advanced analytics.
- ·Manuscript editing tools (comparable to NovelCrafter / AutoCrit)
- ·Priority placement in Lobby discovery
- ·Advanced analytics: read counts, time-on-page, completion rates
- ·Custom domain support for author profile pages
This log tracks platform features, not individual content. For questions or feedback, get in touch.