All deployments
Peter Theill
deployed a feature
3 days ago
Peter Theill
deployed a styling change
3 days ago
4 days ago
✨ Enhance entity change proposal components
✨ Introduce GuideDanmark sync feature
✨ Introduce Supplier Dates and Event Next Steps Cards
✨ Introduce SupplierBookingLink component
✨ Introduce UsagePage component and tests
✨ Introduce address validation for event publication
✨ Introduce entity navigation and improve UI
✨ Introduce image effects and editable audio metadata
✨ Introduce media generation draft functionality
✨ Introduce new features and improvements
✨ Introduce photo metadata patching feature
✨ Introduce source crop functionality
✨ Introduce text selection and annotation features
✨ Introduce video agent projection and media toasts
Peter Theill
deployed a styling change
5 days ago
Peter Theill
deployed a CI/build change
6 days ago
Peter Theill
deployed a dependency update
6 days ago
6 days ago
⬆️ Upgrade to Rails 8.1 and update gems
refreshed error pages), restoring app customizations that were overwritten
parse options
- Bump rails to ~> 8.1.3 and run app:update (adds bin/ci, config/ci.rb,
refreshed error pages), restoring app customizations that were overwritten
- Enable load_defaults 8.1
- Update all gems; relax sqlite3 and aws-sdk-s3 pins
- Pin json to ~> 2.21 until Active Support supports json 3.x keyword-only
parse options
- Add Solid Queue 1.7 batches migration and regenerate queue schema
- Add brakeman.ignore for the intentional presigned S3 redirect
Peter Theill
deployed a feature
8 days ago
Peter Theill
deployed a bug fix
12 days ago
Peter Theill
deployed a styling change
14 days ago
14 days ago
✨ Enrich the pricing page with a comparison matrix, facts and FAQ
Move price and button into a tinted footer band on each card, with the
cards as a subgrid so footers align however the taglines wrap. Below
the cards: an included-on-every-plan grid, a feature matrix driven by
Plan entitlements, three billing facts, a pricing FAQ with JSON-LD and
the shared call to action. State that prices exclude VAT on the pricing
and billing pages, and mirror the matrix and FAQ in llms-full.txt.
💄 Replace pricing avatars with Notion-style ink illustrationscards as a subgrid so footers align however the taglines wrap. Below
the cards: an included-on-every-plan grid, a feature matrix driven by
Plan entitlements, three billing facts, a pricing FAQ with JSON-LD and
the shared call to action. State that prices exclude VAT on the pricing
and billing pages, and mirror the matrix and FAQ in llms-full.txt.
Three hand-drawn characters (one bulb, juggling bulbs, mad scientist)
as transparent PNGs with a baked-in white halo, sitting astride each
card's top edge. Move the Most popular badge to the top-left so it
clears the figure, and send guests on the free tier to the waitlist
while registration is closed.
as transparent PNGs with a baked-in white halo, sitting astride each
card's top edge. Move the Most popular badge to the top-left so it
clears the figure, and send guests on the free tier to the waitlist
while registration is closed.
Peter Theill
deployed a styling change
14 days ago
14 days ago
✨ Redesign transactional mail and cover every account event
All mail now shares one monochrome layout (gray ground, hairline card,
wordmark with the single emerald dot) built from inline-styled helpers in
MailerHelper: eyebrow, heading, black or hairline button, one gray panel,
mono block, status rows. Warnings are plain text with a bold lead-in, never
a coloured box. Subjects drop their emoji.
New mails, each hooked at the model or service so every entry point fires:
developer-mode extension install steps
directories, one row per directory with submit link and mode
activate_plan!, grant_plan!, cancel_plan! and the Stripe webhook
The unused batch_complete mail is removed. Previews for every variant live
under test/mailers/previews with in-memory sample records, so /rails/mailers
renders on an empty database. Adds mailer and hook tests, and a launch.json
attach config for the dev server on :3109.
💄 Share one page header across public pages, replacing breadcrumbswordmark with the single emerald dot) built from inline-styled helpers in
MailerHelper: eyebrow, heading, black or hairline button, one gray panel,
mono block, status rows. Warnings are plain text with a bold lead-in, never
a coloured box. Subjects drop their emoji.
New mails, each hooked at the model or service so every entry point fires:
- UserMailer#welcome on account creation, listing owned packages and the
developer-mode extension install steps
- ProjectMailer#created when an owned package is added (waitlist drafts skip)
- DirectorySubmissionMailer#submissions_created when a maker queues
directories, one row per directory with submit link and mode
- WaitlistMailer#confirmation on a new waitlist signup
- BillingMailer plan_activated / payment_failed / plan_cancelled from
activate_plan!, grant_plan!, cancel_plan! and the Stripe webhook
The unused batch_complete mail is removed. Previews for every variant live
under test/mailers/previews with in-memory sample records, so /rails/mailers
renders on an empty database. Adds mailer and hook tests, and a launch.json
attach config for the dev server on :3109.
Every public page (directories, guides, use cases, pricing, about, help,
legal, extension, WebMCP) now opens with the same `shared/page_header`
partial inside a `.page` frame, so the content edge and heading rhythm
stop jumping between sections. The eyebrow links back to the section
index with a dotted underline instead of a breadcrumb trail.
Also: hand-drawn plan avatars on the pricing tiers, a grayscale footer
logo that colours on hover, a base rule so every enabled button shows a
pointer cursor, and the seed admin address moved to support@launchto.win.
legal, extension, WebMCP) now opens with the same `shared/page_header`
partial inside a `.page` frame, so the content edge and heading rhythm
stop jumping between sections. The eyebrow links back to the section
index with a dotted underline instead of a breadcrumb trail.
Also: hand-drawn plan avatars on the pricing tiers, a grayscale footer
logo that colours on hover, a base rule so every enabled button shows a
pointer cursor, and the seed admin address moved to support@launchto.win.
Peter Theill
deployed a feature
14 days ago
14 days ago
Merge branch 'launch-page-product-insights':whale: Ship headless Chromium in the production image for JavaScript-rendered landing pages
editor's three-column grid has room; keep the hero, URL step and guest
preview card centred at max-w-3xl.
"Fetching project details" status is rendered server-side, and the
controller autofills on connect without the 500ms delay, so the input
no longer flashes before the details replace it.
PageRendererService (and the Playwright directory automation) need a
Playwright CLI and Chromium at runtime. The final stage now copies Node from
node:22-trixie-slim (same Debian release as the Ruby base), installs the
playwright package pinned to the version the playwright-ruby-client gem
speaks, runs playwright install --with-deps chromium into /ms-playwright,
and sets PLAYWRIGHT_CLI so the app uses the global CLI instead of npx.
Browsers are world-readable so the non-root rails user can launch them.
Verified by building the image locally and rendering a JavaScript page in
it as the rails user.
✨ Give the product analysis real material: render JS shells, read linked pages, search the webPlaywright CLI and Chromium at runtime. The final stage now copies Node from
node:22-trixie-slim (same Debian release as the Ruby base), installs the
playwright package pinned to the version the playwright-ruby-client gem
speaks, runs playwright install --with-deps chromium into /ms-playwright,
and sets PLAYWRIGHT_CLI so the app uses the global CLI instead of npx.
Browsers are world-readable so the non-root rails user can launch them.
Verified by building the image locally and rendering a JavaScript page in
it as the rails user.
Single-page apps such as q.cv ship an empty <div id="root"> so the scrape
had nothing to give the model. Three changes:
optional and self-disabling when the CLI is missing) whenever the static
HTML has fewer than 40 readable words; the scraper re-extracts insights,
description and social image from the rendered DOM. Page requests to
private networks are blocked at the route level.
(static, trimmed) and passes them alongside the landing page, and turns on
OpenRouter's web-search plugin so the model can learn how the product is
used, compared and received. Toggle with credentials openrouter.web_search
or OPENROUTER_WEB_SEARCH. The model is now asked even when the landing page
had no text, told explicitly that it is a JavaScript app.
thinking against it and returned empty content; empty replies now log the
finish reason and usage. The result carries a sources block, shown as the
panel subtitle.
Tests stub the credential lookups so they no longer depend on whether the
master key is present.
🔐 Add OpenRouter api_key and model to credentials♻️ Route the product analysis through OpenRouter so the model is a config choicehad nothing to give the model. Three changes:
- PageRendererService renders the page in headless Chromium (Playwright,
optional and self-disabling when the CLI is missing) whenever the static
HTML has fewer than 40 readable words; the scraper re-extracts insights,
description and social image from the rendered DOM. Page requests to
private networks are blocked at the route level.
- ProductAnalysisService fetches up to two linked pricing/about/docs pages
(static, trimmed) and passes them alongside the landing page, and turns on
OpenRouter's web-search plugin so the model can learn how the product is
used, compared and received. Toggle with credentials openrouter.web_search
or OPENROUTER_WEB_SEARCH. The model is now asked even when the landing page
had no text, told explicitly that it is a JavaScript app.
- Output budget raised to 8000 tokens because reasoning models count their
thinking against it and returned empty content; empty replies now log the
finish reason and usage. The result carries a sources block, shown as the
panel subtitle.
Tests stub the credential lookups so they no longer depend on whether the
master key is present.
ProductAnalysisService now talks to OpenRouter's OpenAI-compatible chat
endpoint via the ruby-openai gem already in the bundle. The model comes from
OPENROUTER_MODEL / credentials openrouter.model (default
anthropic/claude-sonnet-5), the key from OPENROUTER_API_KEY /
credentials openrouter.api_key. Strict json_schema output is requested first
and the call is retried on a plain JSON-only prompt when a model rejects it;
fenced or prefixed JSON is tolerated. Result cache is keyed by model + URL.
✨ Show what we learned about the product on /launch, with an AI read of the landing pageendpoint via the ruby-openai gem already in the bundle. The model comes from
OPENROUTER_MODEL / credentials openrouter.model (default
anthropic/claude-sonnet-5), the key from OPENROUTER_API_KEY /
credentials openrouter.api_key. Strict json_schema output is requested first
and the call is retried on a plain JSON-only prompt when a model rejects it;
fenced or prefixed JSON is tolerated. Result cache is keyed by model + URL.
The preview step now aims for "they really understood my product":
scraper already fetched: detected stack (Next.js, Stripe, Plausible…),
linked pages (pricing, docs, changelog…), platform and business signals
(iOS app, free trial, SOC 2…), headings, calls to action, contact emails,
structured data and word count. Rendered in a new "At a glance" card.
low effort) to explain the product back: summary, audience, key features,
differentiators, comparable products, pricing model, a directory-ready
pitch, launch angles and suggested tags. Served by GET /projects/analysis
as a second step after /metadata, reusing the cached page text; results
cached per URL for a day; rate limited; hidden entirely when no Anthropic
key is configured.
by side, the AI panel below with a loading skeleton, then the CTA. Signed-in
makers get the same panels next to the editor, and suggested tags,
comparable products and the pitch flow into the package fields.
💄 Widen /launch to match the package pages and skip the URL input flash on prefilled URLs- ProductInsightsService extracts deterministic facts from the page the
scraper already fetched: detected stack (Next.js, Stripe, Plausible…),
linked pages (pricing, docs, changelog…), platform and business signals
(iOS app, free trial, SOC 2…), headings, calls to action, contact emails,
structured data and word count. Rendered in a new "At a glance" card.
- ProductAnalysisService asks Claude (claude-opus-5, structured JSON output,
low effort) to explain the product back: summary, audience, key features,
differentiators, comparable products, pricing model, a directory-ready
pitch, launch angles and suggested tags. Served by GET /projects/analysis
as a second step after /metadata, reusing the cached page text; results
cached per URL for a day; rate limited; hidden entirely when no Anthropic
key is configured.
- /launch rearranged at package-page width: product card + glance card side
by side, the AI panel below with a loading skeleton, then the CTA. Signed-in
makers get the same panels next to the editor, and suggested tags,
comparable products and the pitch flow into the package fields.
- All scraped/AI text is rendered with DOM APIs instead of innerHTML.
- Wrap the page in max-w-6xl (same as packages index/show/edit) so the
editor's three-column grid has room; keep the hero, URL step and guest
preview card centred at max-w-3xl.
- When arriving with ?url= the URL card is rendered hidden and the
"Fetching project details" status is rendered server-side, and the
controller autofills on connect without the 500ms delay, so the input
no longer flashes before the details replace it.
Peter Theill
deployed a feature
14 days ago