Skip to content

Custom domain (rebuilt on main) - #151

Merged
jelveh merged 10 commits into
mainfrom
custom-domain-v2
Nov 4, 2026
Merged

jelveh merged 10 commits into
mainfrom
custom-domain-v2

Conversation

@jelveh

@jelveh jelveh commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Do not merge yet: this waits until custom domains are available to all accounts, as #141 did.

Rebuilds #141 on current main. Since #141 branched, #149 (publish from chat) and #150 changed publishing a lot, so this ports the custom-domain feature onto the new code instead of merging the old branch.

What changed while porting

  • Publishing. doPublish is now a thin popover wrapper around publishProject(ui). The reserved-name logic moved into publishProject, with the name passed as ui.reserved. This logic covers reusing a name left by a failed try, and deleting it when a different name is typed. The popover-only parts stay in doPublish: the "Pick or add your domain in Puter's window" progress text, the share URL, and landing on the site view.
  • The chat's Publish card also waits while the own-domain route reserves the name. Before the port, only the popover's button did. The card now re-renders and shows the usual throttled toast instead of doing nothing.
  • Changing the address. The "your domain stops working" question moved into changePublishedAddress, so the popover and the chat's Change-address card both ask it. It's asked before the busy checks. The change stops if the user opens another project or the address moves while the question is open. It returns { changed: false, cancelled: true }, which leaves the popover editor open and the card unchanged. Settings asks before its waits, as in custom domain #141.
  • savePublishedFields combines both sides. It keeps the PR's optional fields and in-lock domain updates, plus main's AI-note argument.
  • Tests. The doPublish source checks now use the new signature. The checks in test-custom-domain.mjs that read the old doPublish source now check the new structure. There are new checks for the cancel paths. custom domain #141's reserved-name tests in test-publish-rollback.mjs still run.

Not changed (possible follow-ups)

  • The chat's Publish card and the AI's hidden "site is live at …" notes still show the puter.site address when a domain is connected. That address still works, but it isn't the one to share.

Review fixes

Each is its own commit, with a test that fails without it.

  • Progress note. The "Pick or add your domain in Puter's window" note stayed on after an own-domain publish, so a later publish from the chat's card showed it. It now clears when that publish ends.
  • Reserved address after reopening. After an own-domain publish failed, reopening the popover filled the field with a random name. Publish then created that site and deleted the reserved one the domain was attached to. The field now starts with the reserved name.
  • Typed address on retry. Use custom domain ignored a newly typed name and went back to the reserved one. It now uses the typed name, as Publish does, and removes the reserved site it replaces.
  • Deleting a project now also removes an address reserved for a first publish that never landed. Before, it stayed on the account and used up a site slot.
  • Address-change warning. It said a domain "stops working" even when the domain wasn't working yet. A domain still waiting for DNS, or already lost, is now described as set up for the current address.
  • Keyboard focus. Check now, Connect again and Change domain are aria-disabled while busy instead of disabled, so focus stays on them through the re-render. Each DNS record's copy button has its own data-action, so a re-render no longer moves focus to the site's Copy link button.
  • Performance. Connecting a domain used to download every published project's full conversation file, to find the one that had the domain. The project list now keeps each project's domain hostname (publishedDomainHost), and only that project is updated.
  • Layout. A long puter.site address on the "Also at" line scrolled the popover sideways (56px on a phone, measured in headless Chrome). It now ends in an ellipsis, with the full address in a tooltip.

Still open: the reserved name for an own-domain publish is kept in memory only. After a page reload, a failed try's reserved site is forgotten and stays on the account.

Testing

  • npm test passes, and so does vite build.
  • Not tried in a browser against a live Puter account with a real domain.

jelveh and others added 10 commits November 4, 2026 12:31
Rebuilt from #141 (by Reynaldi Chernando) on top of main, where publishing
now runs through publishProject (the popover and the chat's Publish card)
and address changes through changePublishedAddress.

- "Use custom domain" / "Change domain" beside the site address opens
  Puter's domain dialog (puter.hosting.attach). The free plan gets the
  upgrade dialog, which now lists the benefit.
- Before a first publish, the own-domain route reserves the name, opens the
  dialog and publishes behind it. publishProject takes the reserved name
  (ui.reserved), reuses one a failed try left behind, and holds back any
  other publish, including the chat card's, while the name is reserved.
- The popover checks a pending domain until it serves the site, and a
  connected one each time it opens. Once working, the domain is the
  address the popover, share view, copy button and sidebar show.
- Changing the address asks first when a domain depends on it. The
  question sits in changePublishedAddress, so the popover and the chat
  card both ask, ahead of the busy checks. Settings asks too.
- savePublishedFields keeps fields a caller leaves out, and changes
  domains inside the file lock, alongside main's AI note.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The popover's progress note says "Pick or add your domain in Puter's
window" while a first publish goes out with its domain. The flag behind
it stayed on afterwards, so a later publish from the chat's Publish card
showed it too when the popover was opened mid-publish. It now clears
when that publish ends, whether it worked or not.
An own-domain first publish reserves its puter.site name and connects
the domain to it before the publish runs. When that publish failed and
the user closed the popover, the next open filled the address field
with a random name. Publish then created that site and deleted the
reserved one, leaving the domain on an address that no longer exists.
The field now starts with the reserved name.
After an own-domain first publish that didn't land, Use custom domain
went back to the name that try had reserved, even when the user had
typed another one, and put that name back in the field. A plain Publish
already uses the typed name. Use custom domain now does too, and removes
the reserved site it replaces so it doesn't take one of the account's
site slots. A typed name that is taken leaves the reserved one in place.
The own-domain route reserves a project's puter.site name before its
first publish. When that publish never landed, the name is in neither
of the URLs deleteChat removes, so deleting the project left the site
on the account, holding one of its limited site slots.
Before an address change, the warning told every user with a domain that
it "shows your site" and "stops working". A domain still waiting for
its DNS records, or one already found not working, does neither. Those
now read that the domain is set up for the current address and won't
work with the new one.
Check now, Connect again and Change domain were disabled while they
worked. The popover re-renders to show that, and a disabled button can't
hold focus, so focus fell back to the popover itself and a keyboard user
had to tab back to where they were. They are now aria-disabled while
busy, which keeps them focusable, and their click handlers ignore them
until they are free.
The popover re-renders when a turn ends or a domain check changes
something, and puts focus back on the control that had it, found by its
first class and data-action. The record copy buttons had no data-action
and share their first class with the site's Copy link button, so focus
on one jumped to Copy link at the top. Each now has its own.
Connecting a domain takes it off any other project that had it. To find
that project, every published project's saved file was downloaded in
parallel, and each file holds the project's whole conversation, so one
connect could pull tens of megabytes. The project list now keeps each
project's domain hostname (publishedDomainHost, in any state), and only
the project listed with it is updated, with no other reads.
Once a domain works, the popover shows the site's puter.site address on
an "Also at" line under the URL row. That line could neither wrap nor
shrink, so a long address scrolled the popover sideways: 56px on a phone
in a headless Chrome check, 7px on desktop. It now ends in an ellipsis
like the URL row above it, with the whole address in its title.
@jelveh
jelveh merged commit 45af3fe into main Nov 4, 2026
1 check passed
@jelveh jelveh mentioned this pull request Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant