Screenplay Editor
Professional Web-Based Screenplay Authoring as a Rails Mountable Engine
Project Overview
Objective: Build a drop-in screenplay authoring experience that any Rails 8 application can mount under a URL prefix, with the editing experience shipped as a first-class React + TypeScript + TipTap bundle and the server-side lifecycle managed by the host app's existing authentication, authorization, and background job stack.
Distribution Model: Packaged as a Ruby gem and published to a self-hosted Gitea Package Registry. Host applications consume it the same way they would consume any gem — add to the Gemfile, bundle install, mount the engine, run the migrations.
Industry Format: Follows the April Screenplay Format convention — Courier 12, fixed margins, ~55 lines per page, proper indentation rules per line type. Plain-text import/export uses the same convention so documents round-trip cleanly between this editor and any April-compatible tool. PDF export produces vector output with correct pagination.
+ React
Engine Architecture
👤 Authorization: Pundit Policies with Host Override
The engine ships a Pundit policy set keyed to industry-recognizable screenplay roles. The host app's User model implements a single method — can_access?(role_name) — and the engine wires the rest automatically. Hosts can override individual policies in app/policies/screenplay_editor/ to layer project-based or team-based logic on top of the defaults.
Guest
View public scripts only
Registered
Create scripts, view own
Premium
Edit + PDF/text export
Industry
Full editing, priority support
Collaborator
Project-based collaboration
Admin
Full access to all scripts
✍️ Authoring Experience
- Line types: scene headings, action, character, dialogue, parentheticals, transitions, shots, notes, dual dialogue
- Formatting enforced by the editor: Courier 12, fixed margins, ~55 lines per page, proper indent rules per line type
- Scene numbering: automatic, renumbers as scenes are inserted / reordered
- Basic revisions: version tracking, audit trail, activity log
- Title page generator: produces a correctly-formatted cover sheet
📤 Import / Export
- Plain-text import: April-style parser — round-trip clean with external tools
- PDF import: text-based PDF parser with confidence scoring
- Plain-text export: April-style output
- Vector PDF export: correct pagination; rendered via a Playwright-based Node.js sidecar
- Async by design: exports and imports run as Solid Queue background jobs so the editor never blocks
💬 Review Workflow
- Inline comments: anchored to document ranges (ProseMirror positions persisted as JSONB)
- Suggestions + resolutions: comments can be resolved / unresolved, preserving history
- Watermarks: on exported PDFs, configurable per script
- Draft → Review → Final → Archived status progression
- Admin oversight: host app's existing admin role sees all scripts and comments
📦 Distribution
- Gitea Package Registry: published as a standard Ruby gem
- Automated publish: push to
maintriggers the publish workflow on a self-hosted Gitea Actions runner - Local development: alternative
path:source for iterating against a host app without publishing - Test coverage: 186 RSpec examples — model specs, policy specs (all roles), job specs, request specs, integration specs
🐍 Rails Engine Side
- Rails 8 mountable engine: integer PKs, migrations, policies, jobs, controllers, views
- PostgreSQL: JSONB for ProseMirror document storage and comment range anchors
- SQLite parity: TEXT-with-serialization for JSONB columns, enabling lightweight test configs
- ActiveStorage: PDF attachments for exported documents
- Solid Queue: background jobs — no Redis dependency
- Solid Cable: real-time comment updates without Redis
- Solid Cache: parse/export caches without Redis
- Pundit: role-based policies with host-overrideable defaults
💻 Editor Frontend
- React + TypeScript: type-safe component model
- TipTap (ProseMirror): extensible rich-text editor with a clean extension API
- Custom node types: one per screenplay line category with formatting rules
- Vite build: engine-bundled asset pipeline, mounted through Rails static serving
💿 Framework-Agnostic Packages
- @studio/screenplay-model: types, schema, validation
- @studio/screenplay-parse: text and PDF import
- @studio/screenplay-export: text and HTML-for-PDF
- @studio/screenplay-cli: command-line conversions
- Isolation: core logic usable outside Rails — CLI batch conversions, external tooling, headless tests
🚀 Operations
- Gitea Actions CI: test + lint + security audit + gem build
- Automated publishing: push to
main→ build → publish to Gitea Package Registry - Playwright PDF sidecar: chromium-based, vector output, locked fonts
- Font bundling: Courier Prime under SIL OFL for license-clean distribution
🔧 Engineering Highlights
📦 Engine + Packages Separation
- Core screenplay logic lives in framework-agnostic TypeScript packages with their own tests
- Rails engine is a thin integration layer: controllers, models, jobs, policies
- CLI tool proves the core packages work outside Rails
📄 Industry Format Fidelity
- April Screenplay Format: Courier 12, fixed margins, proper indents
- Plain-text round-trips with external April-compatible tools
- PDF export is vector with correct pagination — not a screenshot
🛠 Host App Friendliness
- One-line mount:
mount ScreenplayEditor::Engine => "/screenplays" - Single method contract on User model:
can_access?(role_name) - Policies overridable per-host without forking the engine
- No Redis dependency: Solid Queue / Cable / Cache throughout
📊 Test Discipline
- 186 RSpec examples across models, policies, jobs, requests, integration
- All six user roles tested against every policy action
- Import/export jobs exercised end-to-end in specs
- CI runs on every push to
mainordevelopand on PRs
🎯 Project Impact
Ships a professional screenplay editor as reusable infrastructure. Any Rails 8 + Solid Stack application can mount this engine and get industry-format screenplay authoring, review, and export — without rebuilding the editor, parser, or PDF pipeline from scratch.
Demonstrates end-to-end gem productization. From source, to CI, to versioned releases published automatically to a self-hosted package registry, the project shows the full distribution lifecycle that a production internal tool needs — not just the code.
Integration target: the inaugural consumer is the SpinJockey Network platform, where registered and premium users can draft, collaborate on, and export screenplays as part of the broader film-production workflow.