惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
M
MIT News - Artificial intelligence
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
F
Fortinet All Blogs
L
LangChain Blog
D
Docker
G
Google Developers Blog
B
Blog RSS Feed
Google DeepMind News
Google DeepMind News
Vercel News
Vercel News
Recent Announcements
Recent Announcements
MyScale Blog
MyScale Blog
Microsoft Azure Blog
Microsoft Azure Blog
A
About on SuperTechFans
T
The Blog of Author Tim Ferriss
Recorded Future
Recorded Future
I
InfoQ
博客园 - 【当耐特】
The Cloudflare Blog
P
Proofpoint News Feed
GbyAI
GbyAI
博客园 - 司徒正美
U
Unit 42
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
IT之家
IT之家
H
Hackread – Cybersecurity News, Data Breaches, AI and More
B
Blog
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
Stack Overflow Blog
Stack Overflow Blog
Microsoft Security Blog
Microsoft Security Blog
爱范儿
爱范儿
Jina AI
Jina AI
量子位
阮一峰的网络日志
阮一峰的网络日志
月光博客
月光博客
有赞技术团队
有赞技术团队
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
Y
Y Combinator Blog
Apple Machine Learning Research
Apple Machine Learning Research
V
V2EX
大猫的无限游戏
大猫的无限游戏
F
Full Disclosure
雷峰网
雷峰网
美团技术团队
酷 壳 – CoolShell
酷 壳 – CoolShell
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
SegmentFault 最新的问题

Hacker News: Show HN

PurrrrrFocus: Pomodoro Timer App - App Store Workflow Engine — Multi-Step Orchestration for Bun RapidPhoto: Pro Photo Editor App - App Store GitHub - DheerG/swarms: Achieve extraordinary results with claude code across a variety of tasks SPICE simulation → oscilloscope → verification with Claude Code — Lucas Gerads Show HN: VCoding – A 5 MB native Windows IDE with no dynamic dependencies Show HN: LLMs don't hallucinate because they're bad at math, it's the format GitHub - Agent-FM/agentfm-core: AgentFM is a peer-to-peer network that turns everyday computers into a decentralized AI supercomputer. AgentFM lets you run massive AI workloads directly across a global mesh of idle CPUs and GPUs. Show HN: Tracking Top US Science Olympiad Alumni over Last 25 Years GitHub - Potarix/agent-hub: One place to talk to all your agents Show HN: Runtime security for AI agents(injection,tool abuse, data exfiltration) GitHub - dubeyKartikay/lazyspotify: Terminal Spotify client for macOS and Linux GitHub - the-banana-tool/king-louie: Easy to use GUI Personal AI Assistant. Win/Linux/Mac. Show HN I made my vacation rental bookable by AI agents–no Airbnb, 0% commission GitHub - basteez/jsf-autoreload: maven plugin to enable hot reload on jsf projects uvm32/hosts/host-gdbstub at main · ringtailsoftware/uvm32 GitHub - labsai/EDDI: Config-driven engine that turns JSON into production-grade AI agents. Multi-agent orchestration, 12+ LLM providers, MCP/A2A protocols, RAG, persistent memory, and enterprise compliance (EU AI Act, GDPR, HIPAA). Built on Quarkus. GitHub - glitchnsec/fortyone-oss: AI Executive Assistant Platform Quickstart | Alien GitHub - muxshed/shed: One stream in, or many. Every destination, simultaneously. No cloud middleman, no per-channel fees, no limits. GitHub - ocrbase-hq/ocrbase: 📄 PDF/IMG ->.MD/JSON Document OCR API for PaddleOCR and GLMOCR. Self-hostable. GitHub - impactjo/home-memory: MCP server that lets your AI assistant remember everything about your home. GitHub - Sets88/dbcls: DbCls is a powerful terminal database client that supports various databases GitHub - neptun2000/heor-agent-mcp GitHub - SeanFDZ/macmind: Single-layer transformer in HyperTalk for the classic Macintosh RollQuation: Math Puzzles - Apps on Google Play GitHub - dropbox/witchcraft Show HN: Agent-cache – Multi-tier LLM/tool/session caching for Valkey and Redis GitHub - opentalon/opentalon: OpenTalon is an open-source platform built from the ground up in Go as a robust alternative to OpenClaw LinkedIn™ 职位抓取工具 - Chrome 应用商店 GitHub - EdoardoBambini/Agent-Armor-Iaga: AI agents are getting tool access — shell, file system, databases, APIs, secrets. But **nobody is governing what they actually do with it**. Frameworks like LangChain, CrewAI, AutoGen, and Claude Code give agents the power to execute. Agent Armor gives you the power to control, audit, and approve every single action before it happens. HN Vibes — Week 15, Apr 7–13 2026 GitHub - chojs23/ec: Easy terminal-native 3-way git mergetool vim-like workflow GitHub - SethPyle376/hiraeth: Local AWS emulator focused on fast integration testing, with SQS support, SQLite-backed state, and a debug-friendly web UI. GitHub - JakOb-dotcom/cloud-sandbox-security-analysis: Technical analysis and Proof of Concept (PoC) regarding environment variable exfiltration in containerized cloud sandboxes via side-channel data leaks. Springboards - Flint Alpha Show HN: A simpler coding agent harness GitHub - audiodude/sudomake-friends GitHub - 256thFission/mini-mythos: OSS clone of Anthropic’s Mythos harness to locate C/C++ memory vulnerabilities Show HN: OpenParallax: OS-level privilege separation for AI agent execution Hacker News Sorted - Chrome 应用商店 Show HN: How to Install Docker on Ubuntu 24.04 LTS: Complete 2026 Guide GitHub - himanshudongre/smriti GitHub - sverrirsig/claude-control: macOS desktop dashboard for monitoring and managing multiple Claude Code sessions GitHub - ory/dockertest: Write better integration tests! Dockertest helps you boot up ephermal docker images for your Go tests with minimal work. Chiral - Chrome 应用商店 Show HN: Two Claudes collaborating through shared memory on a $100 mini-PC GitHub - pmichaillat/latex-cv: Minimalist LaTeX template for academic CVs GitHub - oguzbilgic/posse: A web UI for Anthropic Managed Agents. GitHub - sshiraz/depsly: Dependency risk analysis tool for npm packages ABI Add safari/agent-harness — Safari browser automation via safari-mcp by achiya-automation · Pull Request #212 · HKUDS/CLI-Anything GitHub - Halfblood-Prince/trustcheck: Verify PyPI package attestations and improve Python supply-chain security GitHub - oguzbilgic/kern-ai: Agents that do the work and show it. GitHub - bruits/satteri: High-performance Markdown and MDX processing for the JavaScript ecosystem GitHub - tylergibbs1/feedstock: High-performance web crawler and scraper for TypeScript, powered by Bun and Playwright GitHub - Grimm67123/grimmbot: The self-improving sandboxed and open-source AI agent. With persistent memory and scheduling. GitHub - whitevanillaskies/whitebloom: Local whiteboard that blooms. GitHub - hwdsl2/docker-whisper: Docker image for a self-hosted Whisper speech-to-text server with speaker diarization and OpenAI-compatible transcription and translation APIs. Powered by faster-whisper. Supports all Whisper models, NVIDIA GPU (CUDA) acceleration, JSON/SRT/VTT output, SSE streaming, offline mode, and multi-arch (amd64, arm64). GitHub - yisding/reviewwiggum GitHub - MarwanAlsoltany/serrors: Structured errors for Go: sentinel hierarchies, typed data, custom formatting, and slog integration. GitHub - soatok/age-php GitHub - Luthiraa/markitme GitHub - stagas/rtdiff: realtime git diff gui and AI-assisted commits GitHub - tombedor/excalicharts GitHub - wh1le/excalidraw-edit: Open and edit .excalidraw files from the terminal. Offline, auto-saves to disk. MalExt Sentry - Malicious Extension Scanner - Chrome 应用商店 GitHub - syi0808/asciianimesvg: Generate animated ASCII art SVGs from text. CLI, Rust library, WASM, and web editor. GitHub - zaina-ml/ml_forge: A visual-based graph node editor for training computer vision models. GitHub - anakin87/llm-rl-environments-lil-course: 🌱 A little course on Reinforcement Learning Environments for evaluating and training Language Models GitHub - takaakit/superpowers-uml: Superpowers-UML modifies Superpowers to ensure a software development workflow in which AI agents design through UML modeling. AdriByte Studio - Sviluppo Web e Soluzioni Digitali GitHub - chouligi/angel-copilot: Your personalized Angel Investment Advisor Show HN: MoodSense AI (ML and FastAPI and Gradio, Deployed on Hugging Face) Moodsense Ai - a Hugging Face Space by aman179102 GitHub - agenteractai/lodmem: Level Of Detail Context Management for Agents GitHub - ostefani/subnetlens: A fast, concurrent network scanner with a TUI and plain-text CLI, built in Go. It discovers live hosts on your network, scans their open ports, resolves hostnames, and fingerprints operating systems—delivered. Cyber Pulse: Agentic Intel - Apps on Google Play Whisper API: Self-Hostable Speech to Text Transcription The Agent-Web Protocol Stack: A Research Thesis GitHub - msmarkgu/RelayFreeLLM: A restful API designed to route user prompts to various AI model providers. Show HN: Provepy – A Python decorator that proves your code using Lean and LLMs Show HN: Pardonned.com – A searchable database of US Pardons GitHub - patrickdappollonio/dux: Dux is a terminal UI that lets you run multiple AI coding agents side by side, each in its own git worktree, with full companion terminals, macros, commit generation, and a command palette that knows more tricks than you do. kMC Crystal Simulator Show HN: HyperFlow – A self-improving agent framework built on LangGraph GitHub - stef41/vibescore: 🎵 Grade your vibe-coded project. One command, instant letter grade across security, quality, dependencies, and testing. GitHub - stef41/lmscan: 🔍 Detect AI-generated text and fingerprint which LLM wrote it. Open-source GPTZero alternative. Zero dependencies, works offline. imgur.com GitHub - visionscaper/collabmem: Enabling long-term collaboration with Agentic AI - building up episodic and world model memory over time with in-context awareness 在 Steam 上购买 FriedrichAI: Offline AI 立省 10% GitHub - atripati/ark: AI Runtime Kernel — a context operating system for AI agents. Eliminates tool bloat, loads only what’s needed, and gives LLMs their reasoning space back. GitHub - nowork-studio/toprank: Open-source Claude Code skills for SEO, SEM, Google Ads GitHub - tacomanator/sash: Lightweight macOS menu bar app for reliably cycling through windows of the current application. Appents | Social Media Management for Product-First Teams GitHub - pnhoang/youtube-spam-blocker: Automatically detects and hides spam messages in YouTube Live chat. Set rate limits, keyword filters, and block repeat offenders. GitHub - decisionnode/DecisionNode: CLI + Local MCP - A shared structured memory store across Claude Code, Cursor, Windsurf, Antigravity, and every MCP client. Semantically queryable. GitHub - AvaCodeSolutions/django-email-learning: An open source Django app for creating email-based learning platforms with IMAP integration and React frontend components. The $100K Gap in Kubernetes Security Tooling Function Calling Harness: From 6.75% to 100%
Demo Director — DemoDirector v0.1.5
ralmidani · 2026-05-11 · via Hacker News: Show HN

Hex.pm Hexdocs.pm

Narrated, highlighted, animated demos for Phoenix LiveView — author with Tidewave and play right in your app.

demo_director is the loop between an AI agent and a saved product demo: the agent drives your Phoenix LiveView app live through Tidewave Web's browser_eval tab while you watch and refine; you save the resulting sequence to priv/demos/<name>.exs; your team replays it later — from a web page, from a Mix task, from a <script> tag in onboarding docs.

The runtime drives the page hands-off: a yellow subtitle bar narrates what's happening word by word, a red highlight ring tracks the next element, and characters get typed into form fields at a readable speed.

Tidewave is only needed during authoring. Replay works against any Phoenix LiveView app with demo_director installed; the runtime dependency is phoenix_live_view and nothing more.

What it isn't

This is not a guided-tour library like shepherd.js, intro.js, or driver.js. Those wait for the user to click each step in turn. demo_director is the opposite: the runtime is the clicker. The user watches; the demo plays itself, end-to-end. Use it for sales demos, screencast replacement, onboarding videos that you want to keep in sync with the live app, and quick "look at this feature" links you can paste into Slack.

If you want a tour where the user drives, use shepherd / driver / intro. If you want a movie of your app that runs against the real DOM, use this.

What's in the package

  • Six top-level helperssubtitle/1, highlight/1, fill/2, fill_typed/3, click/1, wait/1 — each emits a JavaScript string that the runtime evals (or that gets saved into a .exs script).
  • A HEEx attribute helperdemo_id/1 — that drops a data-demo-id on any element, so selectors stay stable when LiveView patches the DOM.
  • An overlay component<.demo_director_overlay /> — that renders the subtitle bar, highlight ring, and the package's CSS + JS.
  • A router macrodemo_director "/demo-director" — that mounts the static-asset plug, the playback channel, and the demos listing page at the path of your choice (default /demo-director).
  • A web demos browser at <mount> (also <mount>/demos) — lists every saved demo with a Play button per row.
  • A Mix taskmix demo_director.play <name> — replays a saved demo against the running dev server, navigating the browser to the demo's starting route if needed.
  • An Igniter-based install taskmix igniter.install demo_director — that wires the router macro and seeds your AGENTS.md / CLAUDE.md with the agent contract.

Two ways to drive a demo

  1. Live, by an agent. The agent emits helper calls into Tidewave Web's browser_eval tab as the user prompts a tour. Useful for exploration and for authoring.
  2. Replayable, from a saved .exs. Once a demo is good, save the call sequence to priv/demos/<name>.exs. Anyone can replay it from <mount> (one click) or mix demo_director.play <name> (one command) — no agent in the loop, no LLM in the runtime path.

Selectors are passed through to document.querySelector — pick whatever already-stable handle the host markup gives you (an id, an attribute, or data-demo-id if you need a dedicated tag). See Selector contract below for the full picture.

Quick start

Add to your Phoenix app

demo_director assumes the host app already has a working Phoenix LiveView setup (a Phoenix.LiveView.Socket declaration in the endpoint, a router, and the standard layouts). The installer doesn't bootstrap LiveView from scratch.

# mix.exs
def deps do
  [
    {:demo_director, "~> 0.1", only: :dev},
    {:igniter, "~> 0.6", only: :dev}
  ]
end

Then:

mix deps.get
mix igniter.install demo_director

The install task does four things automatically:

  1. Wires the router macro under an if Application.compile_env(:my_app, :dev_routes) block.
  2. Adds the playback socket to your endpoint, after the existing Phoenix.LiveView.Socket.
  3. Adds config :demo_director, pubsub: <OtpApp>.PubSub to config/dev.exs (using the conventional mix phx.new PubSub name; if your PubSub server is named differently, edit the value after install).
  4. Seeds AGENTS.md / CLAUDE.md with the agent contract.

It then prints a reminder for the one step that stays manual: rendering the overlay component in your dev-time root layout.

Or wire it up by hand

A few small additions to your app, all gated behind the standard Phoenix :dev_routes flag.

1. Mount the routes in router.ex. The static-asset / play / demos plug must NOT go through protect_from_forgery, so use a bare scope:

if Application.compile_env(:my_app, :dev_routes) do
  import DemoDirector.Router

  scope "/dev" do
    demo_director "/demo-director"
  end
end

2. Add the playback socket to your endpoint, alongside the existing Phoenix.LiveView.Socket:

socket "/director/socket", DemoDirector.PlaybackSocket,
  websocket: true,
  longpoll: false

3. Tell the package which PubSub server you use (typically in config/dev.exs):

config :demo_director, pubsub: MyApp.PubSub

4. Render the overlay in your dev-time root layout (lib/my_app_web/components/layouts/root.html.heex):

<DemoDirector.Components.demo_director_overlay />

This step stays manual on purpose. The other wiring lives in .ex files — Igniter can edit those via the Elixir AST and keep the edit idempotent across re-runs. Root layouts are HEEx, and HEEx isn't parsed as an AST the way Elixir is, so editing it programmatically means string-level surgery against a file users frequently customize (branding, analytics, custom font links, conditional <body> markup). The risk of corrupting a heavily-edited layout outweighed the convenience of auto-injecting one line. The component itself is already prod-safe — it returns empty markup whenever the demo_director/1 router macro hasn't registered a mount path (which is exactly what happens when :dev_routes is off), so the line can sit unconditionally inside <body> and you won't see it in production.

5. (When needed) tag interactive elements with data-demo-id. Most existing markup is already targetable — semantic ids (<section id="vitals">), form ids the labels point at, and attribute selectors all work. Reach for data-demo-id only when no such handle exists, or when an element would be hard to disambiguate otherwise:

import DemoDirector.HEEx

~H"""
<button {demo_id("save-prescription")}>Save</button>
"""

See Selector contract below for the resolver order.

Direct a demo live (with an AI agent)

If you have Tidewave Web or any other tool that gives an AI agent a browser.eval-equivalent surface, the agent can drive a demo live. Paste a sequence of helper calls (the agent's prompts produce these for you):

window.DemoDirector.subtitle("Let's add a prescription.");
await new Promise(r => setTimeout(r, 1500));
window.DemoDirector.highlight("save-prescription");
await new Promise(r => setTimeout(r, 800));
await window.DemoDirector.fillTyped("medication-search", "Atenolol", 35);
await new Promise(r => setTimeout(r, 1200));
window.DemoDirector.click("save-prescription");

Or generated from Elixir:

[
  DemoDirector.subtitle("Let's add a prescription."),
  DemoDirector.wait(1500),
  DemoDirector.highlight("save-prescription"),
  DemoDirector.wait(800),
  DemoDirector.fill_typed("medication-search", "Atenolol"),
  DemoDirector.wait(1200),
  DemoDirector.click("save-prescription")
]
|> Enum.join("\n")

Replay a saved demo

Once a demo is good, save it to priv/demos/<name>.exs. The first comments in the file double as metadata:

# Demo: add a prescription for Mrs. Lee.
# @start_at "/patients/mrs-lee"

alias DemoDirector, as: DD

steps = [
  DD.subtitle("Let's add a prescription."),
  DD.wait(1500),
  DD.highlight("add-prescription-button"),
  DD.wait(800),
  DD.click("add-prescription-button"),
  # …
]

IO.puts(Enum.join(steps, "\n"))

Then either:

  • Open <mount> in your browser (e.g. http://localhost:4000/dev/demo-director) to see every saved demo with a Play button. Clicking Play navigates the browser to the demo's @start_at and runs it. (<mount>/demos works too — same listing.)
  • Run mix demo_director.play <name> from a second terminal. The task prints a clickable URL like http://localhost:4000/dev/demo-director/demos/<name>/play. Opening it stashes the demo in sessionStorage, redirects to @start_at, and the overlay there picks it up on load.

You don't need an agent to write a demo — the helpers compose like any other Elixir code. Pick a starting route, list the elements you want to walk through, and turn each step into a helper call:

# priv/demos/onboarding.exs
# Demo: walk a new user through their first post.
# @start_at "/"

alias DemoDirector, as: DD

steps = [
  DD.subtitle("Welcome! Let's create your first post."),
  DD.wait(1800),

  DD.subtitle("Click 'New post' to get started."),
  DD.highlight("new-post-button"),
  DD.wait(900),
  DD.click("new-post-button"),
  DD.wait(1200),

  DD.subtitle("Title goes here. Try something descriptive."),
  DD.highlight("post-title-input"),
  DD.wait(700),
  DD.fill_typed("post-title-input", "My first post"),
  DD.wait(1500),

  DD.subtitle("That's the basics — fill in the rest at your own pace."),
  DD.highlight(nil),
  DD.wait(2400),
  DD.subtitle(nil)
]

IO.puts(Enum.join(steps, "\n"))

Two things to know:

  1. Pick the most stable selector that already exists. The runtime tries data-demo-id first, then falls back to document.querySelector, so any of these work as targets:

    DemoDirector.click("save-prescription")          # data-demo-id
    DemoDirector.click("#save-prescription")         # element id
    DemoDirector.click("button[name=publish]")       # attribute selector

    Prefer existing handles that the host app already uses for its own purposes — semantic ids the sidebar nav links to, form-field ids labels point at — those are the ones the host has the strongest incentive to keep stable. Reach for data-demo-id (via the demo_id/1 HEEx helper) when no such handle exists, or when the element you want to target has nothing distinctive about it (icon buttons, repeated rows). Either way, don't author against :nth-child chains or deep descendant paths — those break the moment a sibling moves.

  2. Pace generously. Subtitles reveal word-by-word at ~110ms/word — the trailing wait should be at least the reveal duration plus a beat. fill_typed defaults to 35ms/char, which reads naturally; lower for filler text, higher for content the viewer is meant to read. For LiveView-driven inputs with phx-debounce, allow the debounce window plus a server roundtrip (~600–1200ms total) before reading the resulting DOM state.

Test by saving and opening <mount>/demos. The new entry shows up automatically.

Try the example app

The package ships with a minimal Phoenix LiveView blog app — a post composer, a public reader view, and a comments section — wired up in dev.exs so the package can be smoke-tested end-to-end against a real running app without a separate Mix project.

mix deps.get
mix dev

Open http://localhost:4000/dev/demo-director and click Play on any of the four bundled demos. Or run them from a second terminal:

mix demo_director.play compose_post
mix demo_director.play fix_validation_errors
mix demo_director.play search_drafts
mix demo_director.play reader_comment

The four demos exercise:

  • compose_post.exs — fill out every guardrail-checked field, watch the bar go green, click publish
  • fix_validation_errors.exs — open a half-written draft, fix each validation error in turn
  • search_drafts.exs — live search filtering as fill_typed fires phx-change per keystroke
  • reader_comment.exs — open a published post, leave a comment, watch the comment stream in via Phoenix.PubSub

Compatibility

ComponentRequiredTested with
Elixir~> 1.151.18.x, 1.19.x
Phoenixvia phoenix_live_view ~> 1.01.7.x, 1.8.x
Phoenix.LiveView~> 1.01.1.x
Tidewaveoptional — only for live agent authoring; replay works without ittested against tidewave ~> 0.5 (specifically 0.5.6)
Igniter~> 0.6, optional (only used by the install task)0.7.x, 0.8.x

Selector contract

The runtime resolves targets in two passes: first as data-demo-id, then — if no match — as a raw CSS selector via document.querySelector. So all of these work:

DemoDirector.click("save-prescription")     # data-demo-id="save-prescription"
DemoDirector.click("#save-prescription")    # element id
DemoDirector.click(".btn-primary")          # class
DemoDirector.click("button[type=submit]")   # attribute

The package's recommendation is to pick the most stable handle that already exists in the host's markup — semantic ids the host already uses for in-page navigation or <label for=""> are the strongest candidates because the host has its own reasons to keep them stable. Reach for data-demo-id (via the demo_id/1 HEEx helper) when no such handle exists, or for elements that would be hard to disambiguate otherwise (icon buttons, repeated rows). Avoid :nth-child chains and deep descendant paths — those break the moment a sibling moves.

data-demo-id lookup runs first, so if you tag an element specifically for the demo, that tag wins over any unrelated CSS selector match.

Production stripping

The integration is gated by Phoenix's standard Application.compile_env(:my_app, :dev_routes) pattern — the same flag that gates live_dashboard and Plug.Swoosh.MailboxPreview. With the flag off (the default in :prod), the router macro never compiles, the static-asset plug is never mounted, and the overlay component renders nothing. The data-demo-id attributes themselves still render — a few extra bytes per element, no outgoing requests, no JS hooks, no overlay.

If you want stricter prod stripping, wrap your demo_id/1 call sites in your own Mix.env() == :dev block; this helper deliberately stays side-effect-free.

Caveats

Demos write real records. This is not a dry run against your DB. A demo that types into a form and clicks Submit creates real records, sends real emails, queues real jobs. Keep the package gated behind :dev_routes (or your equivalent), and only enable it in environments where it's safe for forms to hit the real data layer. A sandboxed demo-session primitive (transactional rollback at the end of a demo, throwaway test data) is on the deferred-features list.

Localhost-only playback by default. The <mount>/play HTTP endpoint that receives broadcasts from mix demo_director.play rejects non-loopback IPs. If you want demos to be playable from a non-localhost staging instance, that gate needs to be relaxed — currently requires a code change.

Troubleshooting

Clicking Play does nothing / the overlay never appears. Check the overlay is rendered in your dev-time root layout (<DemoDirector.Components.demo_director_overlay />) and that the router macro is mounted under a non-:browser-piped scope. The static asset plug must NOT pass through protect_from_forgery — the playback POST is cross-origin from the agent's perspective and CSRF will reject it.

Overlay component renders empty even though the route exists. The overlay only renders when Application.get_env(:demo_director, :mount_path) is set. The router macro registers it via an @on_load callback baked into your router module, which fires on every module load. If the env still ends up nil at runtime — usually because of an unusual release-build or umbrella-app setup that strips beam metadata — the safe escape hatch is to set the path explicitly:

# config/dev.exs
config :demo_director, mount_path: "/dev/demo-director"

If you ever see this in vanilla mix phx.server, please open an issue — the macro is supposed to make this Just Work, and the workaround above shouldn't be necessary.

/director/socket 404s in the browser console. Add the playback socket to your endpoint, alongside the existing Phoenix.LiveView.Socket:

socket "/director/socket", DemoDirector.PlaybackSocket,
  websocket: true,
  longpoll: false

Channel join fails with KeyError: key :pubsub not found. Tell the package which PubSub server to broadcast on:

config :demo_director, pubsub: MyApp.PubSub

The Mix task says "could not find a DemoDirector mount." The probe couldn't find a live server. Check mix dev is running, and that DD_HOST (default http://localhost:4000) points at the right host:port. If you're using a non-default mount path, pass --url explicitly.

Subtitle reveals out of sync with the action / the demo runs in parallel. Make sure fill_typed calls are awaited on the JS side (the helper emits await window.DemoDirector.fillTyped(...) automatically as of v0.1; older saved scripts may need re-generating).

Typed characters get dropped mid-typing. The runtime tracks its own typing buffer and self-corrects DOM mismatches on the final character (with a console.warn if it had to correct). If you see warnings, the host page is morphing the input mid-typing — usually fine, but worth confirming the field has stable identity across re-renders.

Demo runs in a background tab feels glacial. That's the browser's setTimeout throttling for inactive tabs. The demo isn't broken; it'll resume normal speed when the tab returns to the foreground.

License

Apache-2.0. See LICENSE.md.