* feat(cloud): Keep accounts, orders and sign-in answers in Cloudflare D1
Move the server's storage from unstorage's KV mount to a D1 database
(binding DB), with one table per kind of record. Balances are columns
changed by a single statement; records that nothing queries inside are
JSON in a TEXT column.
Existing KV data is not copied over.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* feat(cloud): Store accounts and purchases as rows in D1
Balances and purchases were JSON blobs, read, edited and written back, so
two webhooks or a charge racing a payment could each read the old total
and one write would win. A balance now changes by a statement relative to
itself, and a purchase leaves `pending` in the same D1 batch that adds its
credits, so a replayed Paddle or SePay webhook cannot credit twice.
The tables are `users`, `transactions` (one row per purchase, keyed by
provider and id) and `sign_ins`. Short SePay transfers add up in a column,
with `short_by` recording the last one so a retried webhook is counted once.
The first request drops the tables of the JSON layout. Nothing from them is
carried over.
* feat(cloud): Sign in with Google directly and drop Firebase Auth
Sign-in now runs Google's OAuth flow through the cloud server's own callback and issues a session the app holds, replacing the Firebase Auth SDK and the nonce-based sign-in. Accounts stay in D1.
* Reprice credit packs and celebrate arrivals on pay pages
The packs were 25k/60k/100k ₫ for 1M/3.3M/6M credits, about 6 to 9.5 times Anthropic's price, and the bonus badges did not match the figures. They are now 50k/100k/200k ₫ for 10M/22M/48M credits, one rate of 200,000 credits per 1,000 ₫ with 10% and 20% bonuses, so each badge matches its number. The smallest pack moved up because Paddle's fixed $0.50 fee took 58% of 25k ₫.
The payment pages now show a credits card when a purchase lands: `/checkout` waits for the Paddle webhook to credit the transaction, and `/pay/<code>` shows it when the bank transfer arrives. The Paddle checkout is prefilled with the signed-in account's email, found or created as a Paddle customer, and the overlay is closed so it does not hide the card.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* fix(cloud): Meter streamed answers in a task Workers keeps running
The first metering version charged from inside a pull on the response stream. The CLI closes the connection on message_stop, one chunk before Anthropic's body ends, and on workerd a closed client tears down the pending read, so the charge almost never ran. The body is now tee'd: one branch goes to the client, and the other is read to its end by a task registered with event.waitUntil before the response is sent.
The credits meter also re-asks two seconds after a turn ends, because the last charge can land after the first ask. This also removes landing.html.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* Draft a split into several commits and commit them in order
A staged change that is several things got one message from the draft, which is the wrong answer for it. The draft now answers with a plan of at most five commits, each with its files and its message. The commit box shows a card per commit to edit, and Merge into one puts the messages back in the box.
Committing the series refuses rather than guesses when the staged set has changed since the draft, or a planned file also has unstaged edits, since either would commit something other than what was shown.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* Show the credits each Studio AI turn took off the balance
The usage line under a reply ends with the credits the turn cost, and a chat's total sums them. The figure is priced per model in main at the end of the turn, mirroring the server's weighting, and written on the line so a chat that moved between a login and credits keeps what each turn was paid with. Turns on a login get no figure.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* feat(cloud): Explain credits per reply on the landing page
Adds a FAQ entry on how far a pack goes, and works the demo's reply line and balance from the same weighting the app shows.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* Move the composer's MCP servers into a dialog with per-tool switches
A popover was too small for a connector's forty tools. The dialog lists servers down the left and the chosen server's tools on the right, with a filter. The toolbar keeps a chip only while some tool is switched off, since that is a fact about the next message.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* Poll the credits balance every two seconds while the window is visible
A balance moved by another window, or a checkout paid on a phone, showed only at the next turn or focus. A hidden window skips the poll, and focus re-asks when it comes back.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* Open a shell in a folder from the file tree's New terminal here
The shell's directory is checked to be inside the folder and to be a directory, so a path outside the folder is refused rather than opened. The dock keeps the directory across a restart, as it does the shell id.
Walk the file index every time ⌘P is opened
The watchers only follow the folders the tree has open, so a file created since the last open was invisible to ⌘P until Refresh. The held list stays on screen until the new walk lands.
* Give a chat reply's code fences a Copy button on hover
The reply's one Copy on the turn's cost line copies the whole answer, which is not what anyone wants from a single command in it. MarkdownView takes a copyButtons prop, on by default for a file and opted into by chat replies.
* Show a chat's pictures on a fixed stage with a thumbnail strip
Sized to the picture, the viewer's arrows moved with every picture's shape, so the next click landed on nothing. The stage is now fixed, and every picture in the chat sits in a strip beneath it.
View a chat reply's tables large in a dialog
A table in a reply is often wider than its bubble and scrolls sideways, which is no way to read one. A View button on hover opens a copy of the table at the width of a dialog.
* Escape payment pages with an html helper and a shared shell
The two payment pages built their HTML by string concatenation, with an
escape() call wherever a value landed. One missed call was an XSS hole,
and the page chrome was duplicated across both.
html`` now escapes every value it is given. raw() and json() mark the
few strings that are trusted markup or script literals. payment-page.ts
holds the shared document shell, so /pay and /checkout share the palette,
the app mark and the status card.
The transfer page's account number, amount and transfer note get Copy
buttons. The amount is copied as bare digits, because a bank app's amount
field rejects the dotted form. The checkout page shows its state as an
icon.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* Draw the loaders as the VS lettermark
The chat's Working… now writes the lettermark stroke by stroke, and the
spinner runs a light along a faint copy of it. Both replace the three dots
and the 3x3 grid. The remaining Loader2 call sites use Spinner, and reduced
motion draws the glyph whole and lets it breathe instead.
* Strike the credits coin with the VS lettermark
The credits badge and the buy dialog's coins used a lucide sparkle and coin icons, which read as generic AI rather than this app. They now carry the same lettermark the loaders draw: white on the gradient in the badge, and struck into the gold face in the dialog. Below 18px the strike is left off, since the letters would be a smudge.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* Stop tracking wrangler's deploy output in vsclaude-cloud
`wrangler deploy` writes `.wrangler/` next to `.output/`, so ignore it in git and in prettier the same way.
Stop addFolder taking a template mark from the renderer
Pass the two fields by name rather than spreading the input. A spread let the renderer set `template` on any folder, which would make it trashable.
Show a recovery screen instead of a blank window on render errors
React unmounts the whole tree when rendering throws, which leaves the window empty with no way back short of quitting. The boundary offers a reload and a copy of the error details.
Rethrow D1 failures from the column check instead of reading them as missing
hasColumn returned false for any error, and a false drops the transactions table. Only SQLite's own "no such column/table" now counts as the old layout; anything else, such as D1 being down, throws.
Draw the sign-in page in the payment pages' shell
The standalone sign-in document is replaced by the shared payment-page shell with one outcome card for each state. The nonce route passes the new `false` argument to authorizeUrl.
* Remove worktrees as projects, and the commit plan with them
Worktrees are gone as a feature: no dialogs, no status marks, no nested project tree, and `main/worktrees.ts` is deleted. A checkout an older build made under `workspace/worktrees/` stays in the manifest as an ordinary top-level project, so nothing on disk is removed. The commit plan goes with the commit box's split step, and the commit box now only drafts a message from the staged diff. CLAUDE.md and docs/design.md record the removal.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* Write credits as dollars and price packs at the market rate
Credits were written as counts beside token counts, so a 67-token Opus reply charged "1.3M credits" looked like a miscount. Balances and charges now read as dollars, and the integer unit stays underneath for storage, webhooks and the server log. The packs are repriced to 2.000 ₫ per dollar, which is near the going rate for API credit in Vietnam. A $10 pack for 20.000 ₫ is added as bank transfer only, because Paddle's fixed fee would eat most of a card payment at that size. The server refuses a card checkout for it.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* Add a background picture behind the studio canvas
Chosen in Settings › Appearance and copied into the workspace, so the studio does not depend on where the file was picked. It is kept out of the settings bag, which is rewritten whole on every change. With a picture set, cards go translucent so it shows through their edges too.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* Show the running turn's model and tokens beside Working…
The usage line only lands with the result, so a long turn said nothing
about which model it was on or what it had spent until it was over. The
assistant frames already carry both: liveUsageOf sums them per message
id (subagents included, as the result line counts them) and a `live`
event, never written down, puts them on the loader's row until the
turn's own line replaces it. No cost — the SDK reports that on the
result alone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Count a running turn's tokens as its response is written
The running count only moved when a whole assistant message arrived, so a long response looked stalled. Stream events now estimate output from the text as it is written (about four characters a token) until `message_delta` gives the real figure. Updates are throttled to one per 120 ms. `includePartialMessages` is output-only, so the request and its cached prefix are unchanged.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
* Hide Developer Tools from the View menu in packaged builds
Packaged users are not the people debugging the app, and the toggle was the first item they saw under View. It now appears only in development builds.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Nguyen Thanh Duong (Harry) <nguyen.thanh.duong@azoom.jp>
Co-authored-by: Claude Haiku 5.5 <noreply@anthropic.com>