Commit Graph

260 Commits

Author SHA1 Message Date
devin-ai-integration[bot]
65362f3efb
fix(dashboard): prevent blank email preview when switching themes (#1514)
## Problem

When switching themes in the email drafts/template editor, the preview
occasionally shows the loading spinner and then goes completely blank.

## Cause

`EmailPreview` remounts its preview subtree (and thus the `<iframe>`)
via a `key` on `EmailPreviewErrorBoundary` whenever the inputs that
affect the rendered output change. That key was built from the
template/theme TSX source + edit mode, but **omitted `themeId`**.

Switching only the selected theme changes `themeId` without changing the
source, so the key stayed the same. React then reused the existing
`<iframe>` element and merely mutated its `srcDoc` attribute in place.
Replacing `srcDoc` on a live sandboxed iframe is intermittently not
repainted by the browser, leaving a blank preview after the loading
state.

The old key also concatenated values without a separator, which could
collide.

## Fix

Include `themeId` (and `templateId` / `editableSource`) in the remount
key and build it with a collision-safe serialization. A theme switch now
remounts the preview subtree, so a fresh iframe is mounted and loads its
`srcDoc` reliably instead of mutating it in place. This also resets
stale error-boundary state when moving from a broken theme to a working
one.

Single-line, behavior-preserving change.


Link to Devin session:
https://app.devin.ai/sessions/b269b97b40db4015a8770f77a13c10ea

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes a blank email preview when switching themes in the dashboard
editor by remounting the preview iframe. The preview now reliably
updates on theme changes and resets stale error state.

- **Bug Fixes**
- Updated `EmailPreviewErrorBoundary` key to include `themeId`,
`templateId`, `editableSource`, `editMode`, and debounced theme/template
TSX sources, serialized with JSON and joined with a null delimiter.
- Remounts the iframe on theme switch instead of mutating `srcDoc`,
preventing intermittent blank renders.

<sup>Written for commit 331a0fa071.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1514?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
Co-authored-by: Armaan Jain <84474476+Developing-Gamer@users.noreply.github.com>
2026-06-03 18:31:58 -07:00
Aman Ganapathy
7c5034fae6
[Fix]: Can now switch subscriptions/ create checkout urls even when stripeAccountId is null with test mode (#1519)
### Context
Remote/Local emulator allow one to set up payments configs without being
provisioned stripe connected accounts. Previously, even if the user had
test mode toggled, they couldn't create checkout urls or switch
subscriptions. Now, they can thanks to the refactor.

### Out of scope
The switch route still needs to be reworked for when we want to consider
stripe-test mode or test mode - stripe switches. For now we should be
good
2026-06-03 18:05:12 -07:00
Konstantin Wohlwend
5835db6b54 Update missing docs redirects 2026-06-03 17:14:22 -07:00
BilalG1
501ae9fe61
PR 4: Rename Stack -> Hexclave: examples config module, app-internal symbols, crypto docs (#1534)
## What

Continues the **Stack Auth → Hexclave** rename for a set of safe,
internal-only surfaces. This intentionally avoids public-contract names.

### Changes
- **Examples** — renamed the user-facing config module
`stack.ts`/`stack.tsx` (and the `convex` / `lovable` `stack/`
directories) to `hexclave`, and updated every importer across
`.ts`/`.tsx`/`.jsx`. The public `app/handler/[...stack]/` route segment
is left unchanged.
- **apps/{dashboard,backend,internal-tool}** — renamed app-local
SDK-init symbols `stackClientApp → hexclaveClientApp` and
`getStackServerApp → getHexclaveServerApp`, and the dashboard
`StackCompanion` component → `HexclaveCompanion` (incl.
`useStackCompanion`, context types). The public
`StackClientApp`/`StackServerApp` SDK classes are **unchanged**.
- **packages/stack-shared** — added comments to the crypto / JWT / vault
`stack-*` literals documenting that they must **not** be renamed (key
derivation / JWKS / KMS-alias stability). The literals are
byte-identical.

### Deliberately excluded
- **`STACK_*` → `HEXCLAVE_*` env-var rename** — `HEXCLAVE_*` already
resolves via the dual-read layers (SDK env, dashboard `_inlineEnvVars`,
`getEnvVariable`). The remaining holdout is the docker post-build
sentinel path, which the codebase authors explicitly deferred and which
is tightly coupled to `entrypoint.sh` + untestable here. A blind rename
there risks silently breaking self-host/emulator bootstrap for ~zero
functional gain.
- **All public-contract names** — SDK class names, env vars, HTTP
headers (`x-stack-*`), and the `/handler` route convention.

## Verification
- `pnpm lint` — **29/29 passing**.
- `pnpm typecheck` — **28/29 passing**; the only failure is
`@hexclave/docs` (pre-existing missing fumadocs `.source` codegen,
untouched by this PR).
- Two rounds of adversarial multi-agent review; findings fixed:
string-literal collateral from the symbol sweep (CLI test fixtures + an
AI-prompt template) reverted, and a missed `.jsx` importer in
`examples/cjs-test` corrected.

## Notes
- Based on a `dev` snapshot from when the branch was cut (a couple
commits behind tip); the diff contains only the changes above.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Complete the internal “Stack” → “Hexclave” rename across examples,
app-local code, config tooling, and setup docs, and standardize env
output to HEXCLAVE_* with correct default API URL handling. Public SDK
classes, handler routes, and legacy env names keep working.

- **Refactors**
- Examples/config: `stack.*` files and `stack/` dirs →
`hexclave.*`/`hexclave/`; imports updated; keep `app/handler/[...stack]`
route.
- Apps: backend/dashboard/internal-tool now use `getHexclaveServerApp`
and `hexclaveClientApp`; dashboard `StackCompanion` →
`HexclaveCompanion`. Public `StackClientApp`/`StackServerApp` unchanged.
- Env/setup: Next.js and CLI generators write HEXCLAVE_* and omit API
URL when using https://api.stack-auth.com; CLI `doctor` and auth
resolution prefer HEXCLAVE_* (e.g. `HEXCLAVE_SECRET_SERVER_KEY`,
`HEXCLAVE_PROJECT_ID`) with `STACK_*` fallback.
- Config tooling: `stack-config-file` → `hexclave-config-file`, emitting
`HexclaveConfig`; imports updated across backend/dashboard/tooling.
- Shared/docs: added “do not rename” notes for crypto/JWT/vault
`stack-*` literals; regenerated setup prompt/docs to use
`hexclave.config.ts`, `hexclave dev`, and `src/hexclave/`.
- Tests: updated snapshots/assertions to expect `HexclaveConfig` and
HEXCLAVE_* env names.

- **Migration**
  - No action required. SDK and CLI read both HEXCLAVE_* and STACK_*.

<sup>Written for commit 8a891b4f6c.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/hexclave/hexclave/pull/1534?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Renamed internal app/client/server instances and companion/provider
components to the new product name across backend, dashboard, examples,
and tooling; imports updated accordingly.
* Updated generated environment variable names and CLI init/doctor
outputs to prefer the new product prefix.

* **Documentation**
* Added clarifying notes about vault/encryption and JWT/key labels to
avoid breaking existing encrypted data.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-03 12:09:20 -07:00
Konsti Wohlwend
9fa3a19920
Fix Docker builds for pnpm v11 (#1532) 2026-06-02 14:41:18 -07:00
Konstantin Wohlwend
2e1bfecb5f Fix types 2026-06-01 16:25:49 -07:00
BilalG1
609579abab
feat(hexclave): PR 3 — native @hexclave/* source rename + delete dual-publish wiring (#1482)
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
2026-05-29 15:21:59 -07:00
BilalG1
c753ab2831
fix(dashboard): show Stack Auth logo on left of rebrand modal (#1495)
## Summary

The Stack Auth → Hexclave rebrand modal added in
[#1493](https://github.com/hexclave/stack-auth/pull/1493) rendered the
**Hexclave** logo on both sides of its illustration instead of *Stack
Auth → Hexclave*.

## Root cause


[\`hexclave-rebrand-modal.tsx\`](https://github.com/hexclave/stack-auth/blob/dev/apps/dashboard/src/components/hexclave-rebrand-modal.tsx)
referenced \`/logo.svg\` and \`/logo-bright.svg\` for the left-hand
"Stack Auth" mark. But the visible-rebrand flip in
[#1481](https://github.com/hexclave/stack-auth/pull/1481) replaced those
files in \`apps/dashboard/public/\` with the Hexclave benzene mark — so
post-merge, both \`<Image>\` slots resolved to the Hexclave logo.

## Fix

- Restored the **pre-rebrand Stack Auth SVGs** (lifted from
\`57ff5d3ce~1\`, the commit before the brand-flip) under dedicated
filenames so they can't get clobbered the next time \`/logo.svg\` is
updated:
- \`apps/dashboard/public/stack-auth-logo.svg\` — black mark, light
theme
- \`apps/dashboard/public/stack-auth-logo-bright.svg\` — white mark,
dark theme
- Pointed the modal's left-hand \`<Image>\` pair at the new paths and
left a comment explaining why we don't share \`/logo.svg\` (so the next
person doesn't try to consolidate it back).

The originals on \`origin/dev\` (\`/logo.svg\`, \`/logo-bright.svg\` =
Hexclave) are untouched.

## Verification

- \`pnpm --filter @stackframe/dashboard lint\` — clean.
- Diff of
[hexclave-rebrand-modal.tsx](https://github.com/hexclave/stack-auth/blob/fix-stack-logo-in-modal/apps/dashboard/src/components/hexclave-rebrand-modal.tsx)
is two \`src=\` swaps plus a comment; the dismissal logic, gates, and
storage key are untouched.

<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes the rebrand modal to show the Stack Auth logo on the left instead
of rendering Hexclave on both sides. Restores pre-rebrand assets as
`stack-auth-logo.svg` and `stack-auth-logo-bright.svg` and updates the
modal to use them for light/dark themes.

<sup>Written for commit f4ed26266c.
Summary will update on new commits. <a
href="https://cubic.dev/pr/hexclave/stack-auth/pull/1495?utm_source=github">Review
in cubic</a></sup>

<!-- End of auto-generated description by cubic. -->



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Style**
* Updated the rebrand modal illustration assets to display Stack Auth
branding correctly.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1495?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-27 10:09:16 -07:00
BilalG1
57ff5d3ce9
feat(hexclave): PR 2 — visible rebrand (Hexclave brand goes public) (#1481)
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
## Summary

**Stacked on [#1475](https://github.com/hexclave/stack-auth/pull/1475)**
(`cl/hexclave-pr1`, the invisible compatibility layer). Diff vs that
base = the actual PR 2 code.

This is **PR 2 of the Stack Auth → Hexclave rebrand: the visible flip**.
Old wire identifiers (cookies, request/response headers, Bearer prefix,
JWT issuers, MCP tool name) keep working indefinitely via PR 1's
dual-accept. This PR flips every user-visible surface — package names
taught in docs, SDK class names in code examples, dashboard setup
snippets, page titles, error messages, email content, CLI binary,
default base URLs, GitHub repo slug, contributor guidance — to the
Hexclave brand.

See [`RENAME-TO-HEXCLAVE.md`](./RENAME-TO-HEXCLAVE.md) → *"PR 2: Rebrand
to Hexclave (visible)"* for the full per-work-area spec.

## What's implemented (per the plan's PR 2 scope)

- **SDK base URLs** flipped: `defaultBaseUrl` and
`defaultAnalyticsBaseUrl` in
[common.ts](packages/template/src/lib/stack-app/apps/implementations/common.ts:127)
→ `https://api.hexclave.com` / `https://r.hexclave.com`. PR 1's
[`getHardcodedFallbackUrls`](packages/stack-shared/src/utils/urls.tsx:199)
table now keys on the Hexclave domain.

- **Domain inventory sweep** (16 subdomains from the plan): every
`api/app/docs/discord/demo/mcp/skill/feedback/test/preview/r/api2/api.staging/idp-jwk-audience/built-with.stack-auth.com`
reference in production code, docs-mintlify, examples, READMEs, and
contributor guidance flipped to `*.hexclave.com`. Carve-outs: PR 1's
intentional JWT issuer dual-accept table in
[tokens.tsx](apps/backend/src/lib/tokens.tsx), the legacy `./docs/`
folder, the `unified-docs-widget` allowlist (deliberately accepts both
during DNS transition), and `url-targets.ts` hosted-component default
(baked into existing customer deploys).

- **`@deprecated` JSDoc** on every `Stack*` public export
([packages/template/src/lib/stack-app/index.ts](packages/template/src/lib/stack-app/index.ts)
+ [packages/template/src/index.ts](packages/template/src/index.ts)) —
`StackClientApp`, `StackServerApp`, `StackAdminApp` + every
constructor/options/JSON type, `StackHandler`, `StackProvider`,
`StackTheme`, `useStackApp`, `defineStackConfig`, `StackConfig`.
Hexclave\* aliases are now canonical.

- **Runtime `console.warn`**
([packages/template/src/internal/deprecation-warning.ts](packages/template/src/internal/deprecation-warning.ts))
— once-per-process when the SDK is loaded from a `@stackframe/*`
artifact. Detection uses the existing
`STACK_COMPILE_TIME_CLIENT_PACKAGE_VERSION_SENTINEL` (rewritten at build
time to e.g. `js @stackframe/stack@2.8.92` or `js
@hexclave/next@1.0.0`); `@hexclave/*` mirror artifacts short-circuit the
warning.

- **Tier 3 data migration**: new idempotent SQL migration
[`20260523000000_rename_internal_project_to_hexclave`](apps/backend/prisma/migrations/20260523000000_rename_internal_project_to_hexclave/migration.sql)
— updates the internal Project `displayName` 'Stack Dashboard' →
'Hexclave Dashboard' and `description` only if both still hold the
pre-rebrand defaults. Operator-renamed projects untouched, missing row
no-ops, re-runs are no-ops. [`seed.ts`](apps/backend/prisma/seed.ts:87)
default flipped. `getSharedEmailConfig("Stack Auth")` → `("Hexclave")`.

- **Tier 4 brand strings** (mechanical sweep, ~340 files):
- Page + OpenAPI titles (Hexclave API / Dashboard / REST API / Webhooks
API / Documentation). OpenAPI `info.description` documents
`X-Hexclave-*` headers as canonical with compat note on `X-Stack-*`.
- `HexclaveAssertionError` message text
([errors.tsx:71](packages/stack-shared/src/utils/errors.tsx:71)) — "an
error in Stack." → "an error in Hexclave."
- Known-error message templates
([known-errors.tsx](packages/stack-shared/src/known-errors.tsx)) flipped
to lead with `x-hexclave-*` + the new `docs.hexclave.com` URL; legacy
`x-stack-*` mentioned as compat aliases. **25 e2e test files updated in
lockstep**.
- Email content: failed-emails-digest body, sendTestEmail recipient (now
`sent-with-hexclave.com`), test-email-recipient default.
  - `CHANGELOG.md` title → "Hexclave Changelog".
- `AGENTS.md` env var convention: new vars prefix `HEXCLAVE_` /
`NEXT_PUBLIC_HEXCLAVE_` for Category A/B; legacy `STACK_*` explicitly
noted as accepted via PR 1's dual-read.

- **CLI / init wizard**:
- Every dashboard setup snippet, init-stack template, and docs-mintlify
page teaches `npx @hexclave/cli@latest init` (was
`@stackframe/stack-cli`).
[setup-page.tsx](apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/setup-page.tsx)
+
[link-existing-onboarding](apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/new-project/page-client-parts/link-existing-onboarding.tsx).
- [init-stack](packages/init-stack/src/index.ts:634)
`STACK_*_INSTALL_PACKAGE_NAME_OVERRIDE` defaults flipped to
`@hexclave/*`.
- Generated `stack/client.ts` / `stack/server.ts` import from
`@hexclave/next` and reference `HexclaveClientApp` /
`HexclaveServerApp`.
- Internal `StackAuthKeys` dashboard component renamed to
`HexclaveKeys`.

- **docs-mintlify rewrite** (legacy `./docs/` intentionally untouched
per scoping decision):
- **78 MDX files swept**.
`@stackframe/{react,stack,js,tanstack-start,...}` →
`@hexclave/{react,stack,js,...}` in install snippets and code blocks;
`Stack*` SDK class names → `Hexclave*` in all code examples; 'Stack
Auth' brand phrase → 'Hexclave'.
- `openapi/{server,admin,client,webhooks}.json` titles → 'Hexclave REST
API' / 'Hexclave Webhooks API'.

- **Generators flipped before regeneration**:
-
[`packages/stack-shared/src/helpers/init-prompt.ts`](packages/stack-shared/src/helpers/init-prompt.ts),
[`/ai/prompts.ts`](packages/stack-shared/src/ai/prompts.ts),
[`apps/backend/src/lib/ai/prompts.ts`](apps/backend/src/lib/ai/prompts.ts),
[`apps/backend/src/lib/ai/tools/create-email-{template,draft}.ts`](apps/backend/src/lib/ai/tools/create-email-template.ts),
[`apps/skills/src/app/route.ts`](apps/skills/src/app/route.ts) (taught
MCP tool → `ask_hexclave` with compat note; CLI binary teach →
`hexclave`),
[`docs-mintlify/snippets/home-prompt-island.jsx`](docs-mintlify/snippets/home-prompt-island.jsx),
[`packages/template/README.md`](packages/template/README.md) +
integrations/convex/component/README.md.
  - `generate-sdks` propagated changes to `packages/{react,stack,js}`.

- **OpenAPI dual-documentation**:
[`apps/backend/src/app/api/latest/route.ts`](apps/backend/src/app/api/latest/route.ts)
now lists `X-Hexclave-*` headers as primary documented schemas with
`X-Stack-*` duplicates marked `.optional()` (both accepted at runtime by
PR 1's normalize-at-proxy shim).

- **`@stackframe/emails` virtual module**: dual-aliased to
`@hexclave/emails` at the bundler boundary
([email-rendering.tsx:89](apps/backend/src/lib/email-rendering.tsx:89)).
Stored email templates continue to import from either name; new
AI-generated templates and the system prompt teach `@hexclave/emails`.

- **Tier 2 mirror-publish wiring** (new this PR, lays the groundwork for
`@hexclave/*` first publish):
-
[`scripts/rewrite-packages-to-hexclave.ts`](scripts/rewrite-packages-to-hexclave.ts)
— rewrites 9 publishable `@stackframe/*` → `@hexclave/*` `package.json`
files (reads `HEXCLAVE_VERSION` env or `--version=` flag), pins
cross-deps to the shared `@hexclave` version, registers `hexclave` bin
alongside `stack` for `@hexclave/cli`.
-
[`.github/workflows/npm-publish.yaml`](.github/workflows/npm-publish.yaml)
appended with rewrite-then-republish step. `pnpm publish` skips
already-on-npm versions so reruns are safe.

- **Sender email domain**: `noreply@stackframe.co` →
`noreply@sent-with-hexclave.com` (the dedicated transactional-sender
domain split per the plan, to isolate bulk deliverability from
`hexclave.com` reputation); `security@` / `team@stack-auth.com` inbound
mailboxes → `@hexclave.com`.

- **Self-host docs**: docker network / container names in the bash
examples flipped from `stack-auth` to `hexclave` (`hexclave-postgres`,
`hexclave-clickhouse`, `hexclave.env`). The docker image tag
`stackauth/server:latest` stays per the plan's locked decision.

- **GitHub repo slug**: `hexclave/stack-auth` → `hexclave/hexclave` in
every `package.json` `repository` field, README link, CHANGELOG
raw-asset URL.

## Carve-outs (deliberately untouched)

-
**[`apps/backend/src/lib/tokens.tsx`](apps/backend/src/lib/tokens.tsx)**
JWT issuer dual-accept table — PR 1 intentional infrastructure, kept
indefinitely.
- **Legacy `./docs/` folder** — per scoping decision (only
`docs-mintlify/` rewritten).
- **`unified-docs-widget` hostname allowlist** — accepts both
`.hexclave.com` (canonical) and `.stack-auth.com` (transition window)
for DNS rollout.
- **`url-targets.ts`** hosted-domain default
`.built-with-stack-auth.com` — wire identifier baked into existing
customer deploys; indefinite read-fallback.
- **Binary visual assets** (logos, favicons, OG images, README
screenshots) — out of scope for this PR. Need design work; tracked
separately.

## Verification

- **`pnpm typecheck`** on
`packages/{template,stack-shared,react,stack,js}` + `apps/dashboard`:
**all green**. The remaining backend / e-commerce-demo typecheck errors
are pre-existing (Prisma codegen output +
`./generated/api-versions.json` not present in fresh worktrees without
`pnpm run codegen-prisma` + a live DB) and unrelated to this diff.
- **`pnpm lint`** on the same 6 packages: all green.
- **Final grep** for residual `Stack Auth` / `stack-auth.com` /
`@stackframe/stack-cli@latest` references: zero outside the intentional
carve-outs above.
- **25 e2e test files updated in lockstep** with the known-error message
changes (asserted strings flipped to match the new x-hexclave-* +
compat-note messages).

## Deploy blockers (ops sequencing before this rebrand goes live)

This PR is code-complete, but the rebrand's visible surfaces (SDK
default URLs, dashboard links, npm READMEs, REST error messages, runtime
deprecation warning) all point at `*.hexclave.com` / `@hexclave/*`
resources that don't exist yet. None of these are fixable from a PR —
they're ops/registrar/npm work that has to be sequenced before merging
this to a release tag.

Suggested ordering, hardest blockers first:

### Tier 1 — required before customer-facing deploy (everything below
this line *will visibly break customers on day 1* if skipped)

1. **DNS + TLS for `api.hexclave.com` + `api1./api2.hexclave.com`** →
must point at the same backend that serves `api.stack-auth.com` (or a
backend that mirrors PR 1's dual-accept). The SDK's new `defaultBaseUrl`
is `https://api.hexclave.com`; every customer that relied on the old
default and upgrades to a post-PR2 SDK build sends API requests here.
Until this resolves, every default-config customer's API call NXDOMAINs.
2. **DNS for `app.hexclave.com`** → the dashboard. Referenced in the
SDK's default-error messages ("Please create a project on the Hexclave
dashboard at https://app.hexclave.com"), the init-stack flow's
`wizard-congrats` redirect, and the OAuth dashboard handoff.
3. **DNS for `docs.hexclave.com`** + Mintlify deploy → the SDK runtime
deprecation warning (`https://docs.hexclave.com/migration`), every
README, every "Learn more" link in the dashboard, and every REST API
error body (`/api/overview#authentication`) points here. The MDX is in
this PR; the docs build target needs DNS.
4. **DNS for `mcp.hexclave.com`** → the MCP server endpoint that every
taught agent integration (`claude mcp add ...`, `cursor`, `codex`,
`vscode`) registers. Until this resolves, every `npx
@hexclave/cli@latest init` MCP-registration step fails.
5. **Reserve the `@hexclave` npm scope + set repo variable
`HEXCLAVE_VERSION`** → the mirror-publish step in
`.github/workflows/npm-publish.yaml` is gated on this variable. Without
it, the entire taught onboarding command `npx @hexclave/cli@latest init`
404s from the npm registry, *and* every README that says "install
`@hexclave/next`" leads to install failure. Pick the initial version
intentionally (`1.0.0` or aligned to `@stackframe/stack`); don't accept
a silent default.

### Tier 2 — required before announcing the rebrand publicly (lookalike
or low-traffic surfaces, but visibly broken)

6. **DNS for `r.hexclave.com`** → the analytics beacon
`defaultAnalyticsBaseUrl`. Silent failure if missing (analytics drops),
but should land alongside Tier 1.
7. **Register `sent-with-hexclave.com` + full email auth (SPF / DKIM /
DMARC)** → the new default sender domain for shared-sender transactional
emails. Without it the dashboard "send test email" path emits bounces,
and shared-sender flows (`getSharedEmailConfig("Hexclave")`) deliver to
spam at best.
8. **MX + SPF / DMARC for `hexclave.com`** → `team@hexclave.com` and
`security@hexclave.com` mailboxes. The security disclosure mailbox is
referenced in [`.github/SECURITY.md`](.github/SECURITY.md);
`team@hexclave.com` is the actual recipient of internal feedback emails
sent at runtime by
[`apps/backend/src/lib/internal-feedback-emails.tsx`](apps/backend/src/lib/internal-feedback-emails.tsx).
Today, every runtime feedback email bounces.
9. **DNS for `skill.hexclave.com`** → the canonical AI-agent skill fetch
URL (the agent bootstrap pivot). Without it, the entire "agent downloads
`SKILL.md` from a known URL" flow taught in
[`packages/stack-shared/src/helpers/init-prompt.ts`](packages/stack-shared/src/helpers/init-prompt.ts)
fails.
10. **Create `github.com/hexclave/hexclave` as a public repo** (even as
a redirect to `hexclave/stack-auth`) **OR** rewrite every `package.json`
`"repository"` field + dashboard footer "view on GitHub" link to point
at `hexclave/stack-auth` (which already exists). Currently every npm
package page's "Repository" link is dead, and the dashboard's GitHub
button + dev-tool repo link are dead.

### Tier 3 — broken but low-visibility / low-traffic

11. **DNS for `discord.hexclave.com`** → Discord invite redirect, used
in every README's chip and the dashboard footer.
12. **DNS for `demo.hexclave.com`** → " Demo" badge in every npm
package README. Broken-image badge on the package page.
13. **DNS + TLS for `built-with-hexclave.com`** → optional
hosted-handler domain (the default reverted to
`.built-with-stack-auth.com` in this PR's carve-outs, so this only
matters for projects that manually flip).

## Other follow-ups (not deploy-blocking)

- **E2E snapshot regen across the full suite** for the dual-emitted
`x-hexclave-*` response headers (PR 1 follow-up; `vitest -u` in CI
absorbs).
- **Binary visual assets** — logos, favicons, OG images, README
screenshots; need design pass.
- **Backend OpenAPI fumadocs regen** in CI flow — the JSON files in
`docs-mintlify/openapi/` are committed but regen runs in CI. Verify the
workflow that does this still works against the post-PR2 source.
- **Backend typecheck infra debt** — needs `codegen-prisma` +
`codegen-route-info` to clear; pre-existing, unaffected by this PR.

## Test plan

- [ ] CI runs full e2e suite (with `vitest -u` to absorb residual
snapshot deltas, then committed back).
- [ ] Spot-check: new `@hexclave/cli init` (once published) generates
`hexclave.config.ts` and works against a fresh project.
- [ ] Spot-check: existing customer with `@stackframe/stack` import sees
the once-per-process `console.warn` recommending `@hexclave/next` on SDK
init.
- [ ] Manual: dashboard setup page renders the `npx @hexclave/cli@latest
init` snippet and the `x-hexclave-publishable-client-key` API header in
the curl example.
- [ ] Manual: a fresh `pnpm run prisma migrate` against a clean DB sets
the internal project displayName to 'Hexclave Dashboard'.

---------

Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
2026-05-26 19:18:20 -07:00
BilalG1
f7e389809e
feat(hexclave): PR 1 — wire compatibility layer (invisible) (#1475)
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
## Summary

**Stacked on #1468** (`docs/hexclave-rename-plan` — the plan doc). Diff
vs that base = the actual PR 1 code.

This is **PR 1 of the Hexclave rebrand: the invisible compatibility
layer**. Everything is additive. Old SDKs, old wire identifiers, and old
env var names keep working unchanged. The backend dual-accepts and
dual-emits; new SDK code emits `x-hexclave-*` headers and the
`hexclave_` Bearer prefix; cookies dual-write; env vars dual-read across
every category. **No user-visible rebranding lands here** — that's PR 2.

See [`RENAME-TO-HEXCLAVE.md`](./RENAME-TO-HEXCLAVE.md) → *"PR 1
implementation guide"* for the full per-work-area spec, file pointers,
and chosen approach.

## What's implemented (all 14 PR-1 work-areas)

- **SDK export aliases** — `Hexclave*` aliases for the user-facing
`Stack*` exports added in `packages/template`; codegen propagates them
to `@stackframe/{js,stack,react,tanstack-start}`. React-only aliases
correctly excluded from `@stackframe/js`. (`e60550a2`)
- **JWT issuer dual-accept** — `decodeAccessToken` accepts both
`api.stack-auth.com` and `api.hexclave.com` issuers. Signing unchanged.
(`fc781def`)
- **Request-header dual-accept** — backend + dashboard proxies normalize
`x-hexclave-*` → `x-stack-*` at the existing empty proxy hook (so
`smart-request.tsx` and every route schema keep working unchanged); CORS
allowlists extended via a derive-once helper. (`2a056eac`)
- **MCP `ask_hexclave`** — registered alongside `ask_stack_auth` via a
shared helper; `ask_stack_auth` behavior byte-identical. (`30ffd604`)
- **Dev-tool** — DOM ids + header emit switched.
`window.HexclaveDevTool` exposed alongside `window.StackDevTool`.
(`32131ea7`)
- **The big consolidated commit** (`7fed864a`):
- **Env vars** — central `getEnvVariable` prefix-transform (HEXCLAVE
first, STACK fallback); dashboard + template client env files dual-read;
`turbo.json` globalEnv; `NEXT_PUBLIC_STACK_PORT_PREFIX` renamed outright
across ~82 files including docker.
- **Cookies** — dual-write/dual-read auth (`stack-access`/`-refresh-*`
and custom-domain variants), OAuth-state
(`stack-oauth-{inner,outer}-*`), and low-risk cookies (`stack-is-https`,
`stack-last-seen-changelog-version`). Bypass sites patched (backend
OAuth callback, dashboard remote-dev auth route, impersonation snippets,
snapshot serializer).
- **Bearer prefix** — SDK token parser accepts both `stackauth_` and
`hexclave_`; emits `hexclave_`. Discovery correction: this is purely
SDK-internal — the backend never parses it.
- **Response headers** — backend dual-emits
`x-hexclave-{request-id,actual-status,known-error}`; SDKs dual-read (new
first, stack fallback).
- **SDK request-header emit switch** —
`client/server/admin-interface.ts` + dashboard `api-headers.ts` +
`internal-project-headers.ts` + `feedback-form.tsx` switched to
`x-hexclave-*`. Plus `stack_response_mode` query param.
- **Storage keys** — dev-tool / cli-auth / oauth-button / docs keys
renamed (straight); `stack:session-replay:v1` dual-read so in-progress
recordings survive SDK upgrades; `stack_mfa_attempt_code` dual-read.
- **Query params** — cross-domain params dual-emit/dual-accept via
shared helpers; backend `oauth/authorize` accepts
`hexclave_response_mode` and `stack_response_mode`; `stack-init-id`
renamed.
- **`Symbol.for`** — app-internals symbol gets a parallel
`Symbol.for("Hexclave--app-internals")` getter on each attach site (no
read-site churn — old symbol still attached). 3 file-private symbols
renamed outright.
- **Config discovery** — prefer `hexclave.config.ts`, fall back to
`stack.config.ts` at every discovery site (CLI / dashboard / backend /
local-emulator); `init` writes the new filename; CLI credentials path
migrates.
- **Internal renames** — `StackAssertionError`,
`StackClient/Server/AdminInterface` renamed outright (no alias, per the
"internal-only → rename" rule). ~264 files touched.
- **Review-pass fixes** (`21217fbe`) — three real bugs found by parallel
review agents and fixed:
- `snapshot-serializer.ts` was interpolating the whole
`keyedCookieNamePrefixes` array (`${arr}`) — adding a second prefix
would have corrupted **every** OAuth-cookie snapshot, not just new ones.
- **Docker port-prefix producer/consumer mismatch** —
`entrypoint.sh`/`run-emulator.sh`/cloud-init `user-data` were still
producing `NEXT_PUBLIC_STACK_PORT_PREFIX` while the dashboard sentinel +
consumers had been renamed; silent self-host regression (custom port
prefix would be ignored).
- **Missing `hexclave-oauth-inner-*` dual-write** in the OAuth authorize
route — callback's fallback masked it but the dual-write was specified
by the plan.
- Plus: `mcp.test.ts` tool-list assertions updated to include
`ask_hexclave`; two dashboard header-emit sites switched to
`x-hexclave-*` for consistency.
- **E2E snapshot serializer follow-up** (`4b16cc5d`) —
`x-hexclave-request-id` added to the hidden-headers list (mirroring
`x-stack-request-id` treatment), and 2 sample inline snapshots
regenerated in `projects.test.ts` to include the new dual-emitted
headers.

## Verification

- **`pnpm typecheck`** — clean (the fresh-worktree `@/.source` / Prisma
codegen gap in `stack-docs` is pre-existing and unrelated).
- **`pnpm lint`** — 29/29 packages green.
- **`pnpm exec turbo run build --filter=./packages/*`** — 13/13 packages
build (including `@stackframe/stack-cli` once the dashboard standalone
is present).
- **Live E2E** against a running backend on `cl/hexclave-pr1`:
- `pnpm test run
apps/e2e/tests/backend/endpoints/api/v1/internal/mcp.test.ts` — **6/6
pass** (verifies the new `ask_hexclave` tool — the hand-written inline
snapshot matched actual MCP server output).
- `pnpm test run
apps/e2e/tests/backend/endpoints/api/v1/internal/projects.test.ts` —
**11/11 pass** (verifies wire dual-accept + dual-emit end-to-end; the
snapshot serializer fix was found and applied during this check).

A four-agent parallel **review pass** also audited the full diff for
logic/runtime bugs across the work-areas (wire headers + JWT, cookies +
bearer + symbols, env vars, query params + config + MCP + aliases). All
in-slice review verdicts were ✓ except the three bugs listed above,
which are now fixed.

## Known follow-ups (out of scope for this PR)

- **E2E snapshots across the rest of the suite** — backend now
dual-emits `x-hexclave-{known-error,actual-status}` alongside
`x-stack-*`, which legitimately appears in inline snapshots throughout
`apps/e2e`. Two were regenerated here as a sample; the rest should regen
with `vitest -u` in CI.
- **Docker shell env vars beyond `PORT_PREFIX`** — `entrypoint.sh` still
reads `STACK_*` env vars directly (the JS-side `getEnvVariable`
transform doesn't help the shell). JS consumers dual-read so it works in
practice; full shell-level dual-read is a deeper self-host follow-up.
- **`@stackframe/stack-cli` build ordering** — pre-existing; needs
`build:rde-standalone` first. Not affected by this PR.

## Test plan

- [ ] CI runs full e2e suite (with `vitest -u` to absorb dual-emit
snapshot deltas, then committed back)
- [ ] Spot-check: an old SDK build (emitting only `x-stack-*`) still
authenticates against the new backend
- [ ] Spot-check: a new SDK (emitting `x-hexclave-*` / `Bearer
hexclave_*`) still authenticates against an old backend during deploy
ordering
- [ ] Manual: `npx @stackframe/stack-cli@latest init` (new onboarding
entrypoint) generates `hexclave.config.ts`
- [ ] Manual: existing `stack.config.ts`-only project still resolves (no
migration required)

---------

Co-authored-by: bilal <bilal@stack-auth.com>
2026-05-23 17:24:55 -07:00
Mantra
10df9b2b7b
Fix dashboard sandbox compile errors and switch smart model to Grok (#1476)
## Summary
- Forward Babel/JSX compile errors, runtime throws, and unhandled
rejections from the AI dashboard sandbox iframe to the parent composer
via `postMessage`, so users see actionable errors instead of a blank
preview
- Compile AI-generated dashboard source explicitly with
`Babel.transform` + try/catch (stored in `text/plain` to avoid Babel's
auto-handler swallowing parse errors) and add `crossorigin="anonymous"`
on the Babel script for readable cross-origin error messages
- Switch authenticated smart-tier model from
`moonshotai/kimi-k2.6:nitro` to `x-ai/grok-build-0.1`

## Test plan
- [ ] Generate a dashboard with valid AI code and confirm the preview
still renders
- [ ] Generate a dashboard with invalid JSX and confirm the composer
shows the compile error (not a blank iframe)
- [ ] Trigger a runtime error in generated dashboard code and confirm it
reaches the parent error boundary
- [ ] Verify authenticated smart-tier requests route to
`x-ai/grok-build-0.1`


Made with [Cursor](https://cursor.com)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Embedded dashboards now show a clear “Dashboard failed to compile”
message on compilation errors instead of a blank iframe.
* Dashboard runtime errors and unhandled promise rejections are captured
earlier and forwarded to the parent for improved visibility.

* **Updates**
* The authenticated AI model used for the "smart" quality has been
changed, affecting model selection for authenticated requests.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1476?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-23 12:34:37 -07:00
Armaan Jain
e42ec65c88
Payments app design fixes (#1375)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->

## Summary

This PR brings the Payments dashboard surfaces in line with the shared
design system: product creation, product-line / included-item dialogs,
auth-method toggles, payments empty states, and related layout polish.
Dialogs migrate from raw shadcn `Dialog` to `DesignDialog` with
consistent headers, footers, inputs, and selector dropdowns.

**Base:** `dev` → **Head:** `Payments-app-design-fixes`  
**Scope:** 31 files, ~+1.4k / −1.3k lines  
**Captured on:** local dev server (`internal` project), signed in as
`admin@example.com`

## Screenshots

Captured from `http://localhost:8101` (viewport: **1920×1200** standard,
**2560×1440** widescreen). Assets hosted in [this
gist](https://gist.github.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf).

> Red outlines on the **after** shots mark the new or changed UI
introduced by this PR.

### Create Product — payments form redesign

| | Before | After |
| --- | --- | --- |
| Light |
![payments-products-new-before-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-before-light.png)
|
![payments-products-new-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-after-light.png)
|
| Dark |
![payments-products-new-before-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-before-dark.png)
|
![payments-products-new-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-after-dark.png)
|

Widescreen:

| | Before | After |
| --- | --- | --- |
| Light |
![payments-products-new-before-light-wide](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-before-light-wide.png)
|
![payments-products-new-after-light-wide](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-after-light-wide.png)
|
| Dark |
![payments-products-new-before-dark-wide](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-before-dark-wide.png)
|
![payments-products-new-after-dark-wide](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-products-new-after-dark-wide.png)
|

### Product Lines onboarding — vertical centering fix

| | Before | After |
| --- | --- | --- |
| Light |
![payments-product-lines-before-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-product-lines-before-light.png)
|
![payments-product-lines-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-product-lines-after-light.png)
|
| Dark |
![payments-product-lines-before-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-product-lines-before-dark.png)
|
![payments-product-lines-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-product-lines-after-dark.png)
|

### Create Product Line dialog — `DesignDialog` migration

| | Before | After |
| --- | --- | --- |
| Light | *(legacy shadcn dialog on `dev` — open via Product Line →
Create new)* |
![dialog-create-product-line-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/dialog-create-product-line-after-light.png)
|
| Dark | |
![dialog-create-product-line-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/dialog-create-product-line-after-dark.png)
|

### Auth Methods — toggle row accessibility

| | Before | After |
| --- | --- | --- |
| Light |
![auth-methods-before-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/auth-methods-before-light.png)
|
![auth-methods-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/auth-methods-after-light.png)
|
| Dark |
![auth-methods-before-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/auth-methods-before-dark.png)
|
![auth-methods-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/auth-methods-after-dark.png)
|

### Other migrated surfaces (after only)

| Page | Light | Dark |
| --- | --- | --- |
| Payments settings |
![payments-settings-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-settings-after-light.png)
|
![payments-settings-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/payments-settings-after-dark.png)
|
| Sign-up rules |
![sign-up-rules-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/sign-up-rules-after-light.png)
|
![sign-up-rules-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/sign-up-rules-after-dark.png)
|
| Projects list (Create Project button) |
![projects-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/projects-after-light.png)
|
![projects-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/projects-after-dark.png)
|
| Playground / DesignDialog |
![playground-dialog-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/playground-dialog-after-light.png)
|
![playground-dialog-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/playground-dialog-after-dark.png)
|
| Included Item dialog |
![dialog-included-item-after-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/dialog-included-item-after-light.png)
|
![dialog-included-item-after-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/dialog-included-item-after-dark.png)
|

### Scroll behaviour — Sign-up Rules

| | Light | Dark |
| --- | --- | --- |
| Scroll |
![sign-up-rules-scroll-light](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/sign-up-rules-scroll-light.gif)
|
![sign-up-rules-scroll-dark](https://gist.githubusercontent.com/mantrakp04/ca3483d2b66b8e28f0872488df573ccf/raw/sign-up-rules-scroll-dark.gif)
|

## What's new

- **`DesignDialog`** extended with `customHeader`, `noBodyPadding`, and
section `className` hooks; Playground updated to showcase them.
- **Payments dialogs** (`CreateProductLineDialog`, `IncludedItemDialog`,
price edit, item dialog) migrated to design-system components.
- **Create Product** page uses `DesignButton`, `DesignInput`,
`DesignSelectorDropdown`, and refreshed header actions.
- **Auth Methods** toggle rows use semantic `<Label htmlFor>` instead of
click-capture divs.
- **Payments layout** empty-state card centers correctly; product-lines
onboarding slideshow vertically centers.
- **Backend** seed invariant for Growth product price; removed unused
import in product switch route.

## Notes for reviewers

- Dialog migrations preserve validation + async error handling
(`runAsynchronouslyWithAlert` where applicable).
- Included-item dialog uses a sentinel value for “Create new item” to
avoid colliding with real item IDs.
- `packages/stack` / `packages/js` are untouched; template +
dashboard-ui-components carry SDK-facing dialog changes.

## Test plan

- [x] Visual capture on `internal` project (`admin@example.com`) —
light/dark, standard + widescreen
- [ ] Create product flow: customer type → product line dropdown →
create line dialog
- [ ] Add included item dialog from create/edit product
- [ ] Auth Methods toggles (label click + switch)
- [ ] Payments product-lines onboarding slideshow at varied viewport
heights
- [ ] `pnpm typecheck` / `pnpm lint` / targeted E2E if API surface
changed

---------

Co-authored-by: nams1570 <amanganapathy@gmail.com>
Co-authored-by: mantrakp04 <mantrakp@gmail.com>
Co-authored-by: Mantra <87142457+mantrakp04@users.noreply.github.com>
2026-05-21 14:48:56 -07:00
Aman Ganapathy
be2ad595ad
fix: checkout flow for 0 dollar subscription (#1465)
### Context
There was a small bug via dashboard checkout flow where it would fail on
trying to create a checkout flow for a free product subscription because
no client secret is generated for a 0 dollar subscription.

### Summary of Changes
The flow should be fine now. There's special carve out logic for it.
That being said, users attempting to mimic a free plan grant are
encouraged to follow the `ensureFreePlan` pattern.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Free subscription selections now bypass Stripe payment processing,
streamlining checkout for zero-cost offerings.
* Purchase return flow now properly recognizes and activates free
subscriptions without requiring payment confirmation.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1465?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-21 14:33:55 -07:00
Konsti Wohlwend
3b2e991c78
Fix browser compatibility: guard requestIdleCallback and startViewTransition (#1464) 2026-05-21 14:10:51 -07:00
Konsti Wohlwend
002692e519
Compress oversized images client-side in AI chat (#1456) 2026-05-21 12:57:20 -07:00
devin-ai-integration[bot]
20b7921b93
Fix theme toggle in browsers without View Transitions (#1453)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/hexclave/stack-auth/blob/dev/CONTRIBUTING.md

-->

Fixes the dashboard theme toggle in browsers that do not support
`document.startViewTransition` by falling back to an immediate theme
change.

Link to Devin session:
https://app.devin.ai/sessions/c1f1deed2f1c4d42979df2ee949cf74d
Requested by: @madster456

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-05-20 18:32:00 -05:00
Armaan Jain
055304d3fd
Onboarding app redesign (#1370)
# Onboarding app redesign

Rolls out a unified dashboard visual language centered on `DesignCard`
groupings, a new canonical `DesignDialog`, and an inline live-preview
pattern. Touches the project listing, project overview, auth methods,
design language, onboarding, and sign-up rules surfaces. Reusable
primitives (`DesignCard`, `DesignDialog`, `MethodToggleRow`) replace
one-off layouts, and the project card now leads with **total users +
30-day signups** instead of a weekly-users tile.

**Base:** `dev` → **Head:** `onboarding-app-redesign`

> Red outlines on the "after" shots highlight the UI that changed in
this PR. Empty outlines = layout/chrome change with no data delta.

---

## Flagship: Project listing (`/projects`)

Project cards swap the weekly-users widget for a `ProjectUsersMetric`
(total user count + 30-day signups sparkline). Hover lifts the card; the
metrics row is now part of the card body instead of a footer strip.

|        | Light | Dark |
|--------|-------|------|
| Before | ![before
light](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/projects-before-light.png)
| ![before
dark](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/projects-before-dark.png)
|
| After | ![after
light](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/projects-after-light.png)
| ![after
dark](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/projects-after-dark.png)
|

## Flagship: Auth methods (`/projects/[id]/auth-methods`)

Full restructure: the horizontal `SettingCard` strips are replaced by
stacked `DesignCard` sections (Sign-in methods · Sign-up policies · User
deletion), with a sticky **live sign-in preview** column on the right.
Provider rows become `MethodToggleRow`s with inline configure actions.

|        | Light | Dark |
|--------|-------|------|
| Before | ![before
light](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/auth-methods-before-light.png)
| ![before
dark](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/auth-methods-before-dark.png)
|
| After | ![after
light](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/auth-methods-after-light.png)
| ![after
dark](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/auth-methods-after-dark.png)
|

## Flagship: Project overview (`/projects/[id]`)

Line + donut charts migrate to the shared `AnalyticsChart` component.
Referrers list gains a max-height + scroll affordance so it no longer
pushes neighbouring tiles off-screen.

|        | Light | Dark |
|--------|-------|------|
| Before | ![before
light](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/overview-before-light.png)
| ![before
dark](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/overview-before-dark.png)
|
| After | ![after
light](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/overview-after-light.png)
| ![after
dark](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/overview-after-dark.png)
|

## Other migrated surfaces

| Surface | Before (dark) | After (dark) | What changed |
|---------|---------------|--------------|--------------|
| `/projects/[id]/onboarding` |
![](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/onboarding-before-dark.png)
|
![](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/onboarding-after-dark.png)
| Email-verification toggle adopts the new `MethodToggleRow` +
confirmation `DesignDialog` variant |
| `/projects/[id]/sign-up-rules` |
![](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/sign-up-rules-before-dark.png)
|
![](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/sign-up-rules-after-dark.png)
| Rule builder rewrapped in `DesignCard`/`DesignAlert`/`DesignButton`
primitives |
| `/projects/[id]/design-language` |
![](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/design-language-before-dark.png)
|
![](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/design-language-after-dark.png)
| Adds a `DesignDialog` showcase section so consumers can see the
canonical modal styling |
| `/playground` |
![](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/playground-before-dark.png)
|
![](https://gist.githubusercontent.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7/raw/playground-after-dark.png)
| New `dialog` playground entry exercising the size/variant/icon-chip
permutations |

Light-mode counterparts for the long-tail surfaces are in the [companion
gist](https://gist.github.com/mantrakp04/ff6b32969cb08510860e94be7d67dbf7).

---

## What's new

- **`DesignDialog`**
(`packages/dashboard-ui-components/src/components/dialog.tsx`) —
canonical modal with configurable size/variant, optional icon chip, and
split header/body/footer regions. Replaces ad-hoc `Dialog` +
`DialogContent` usage across the dashboard.
- **`MethodToggleRow`** — shared row primitive used by auth-methods and
onboarding for "thing with a toggle and an inline configure CTA".
- **`ProjectUsersMetric`** — total users + 30-day signups sparkline;
powers the new project card metric and reuses the
`projects-weekly-users` backend route renamed to `projects-metrics`.
- **`action-dialog`** gains `keepOpenOnOutsideInteraction` and
`contentClassName` props so variant chrome can ride along through the
existing helper.
- Backend: new internal `projects-metrics` route + test;
`seed-dummy-data.ts` updated to populate the new metric.

## Notes for reviewers

- Reusable primitives (`DesignCard`, `DesignDialog`, `MethodToggleRow`)
live in `packages/dashboard-ui-components` — please flag any inline
duplications you spot.
- The auth-methods live-preview only renders at `lg+`. Below that
breakpoint the page falls back to the stacked card layout.
- The OAuth provider config dialogs adopt the new pill toggle for
**Shared keys / Custom OAuth credentials**; the underlying form fields
are unchanged.

## Test plan

- [ ] `/projects` — verify the metric tile renders both empty-state and
populated (Demo Project has 584 users seeded)
- [ ] `/projects/[id]/auth-methods` — toggle each method on/off, confirm
live preview updates in real time
- [ ] `/projects/[id]/auth-methods` — open a provider dialog, switch
between Shared / Custom keys, verify form state preserved
- [ ] `/projects/[id]/onboarding` — toggle email verification, confirm
the confirmation dialog variant
- [ ] `/projects/[id]/sign-up-rules` — verify rule builder still saves
correctly under the new chrome
- [ ] Mobile/`md` breakpoint — auth-methods falls back to stacked
layout, no overflow
- [ ] Dark mode parity on every flagship surface

<sub>Visuals captured via local dev server (`localhost:8101`) on
`admin@example.com` seeded account. Red outlines mark new/changed UI on
the "after" pass.</sub>

---------

Co-authored-by: mantrakp04 <mantrakp@gmail.com>
Co-authored-by: Mantra <87142457+mantrakp04@users.noreply.github.com>
2026-05-20 13:10:22 -07:00
BilalG1
512099ed23
Speed up dummy-project seeding (preview create-project ~15s → ~1.3s) (#1437)
## Summary

The internal `preview/create-project` endpoint was taking ~15s because
`seedDummyProject` created its dummy users one at a time through the
full `usersCrudHandlers.adminCreate` CRUD pipeline (one DB transaction +
config render per user, ~86 users). This reworks the seeding path to use
bulk inserts.

End-to-end, the endpoint's server-side handler time drops from
**~15,100ms → ~1,300ms** (~11× faster).

## Seeding changes (`seed-dummy-data.ts`)

- **`seedDummyUsers` — bulk insert.** Build every row (`ProjectUser`,
`ContactChannel`, `AuthMethod`, `ProjectUserOAuthAccount`,
`OAuthAuthMethod`, default permissions) up front with pre-generated
UUIDs, then insert via one `createMany` per table inside a single
transaction — replacing ~86 sequential `adminCreate` transactions.
Named-user team memberships are bulk-inserted the same way (`TeamMember`
+ `TeamMemberDirectPermission`). Idempotency is preserved with a single
up-front email lookup, so re-runs against an existing project still skip
existing users.
- **Native `randomUUID`.** The seed paths now use `node:crypto`'s
`randomUUID()` instead of stack-shared's `generateUuid()`. The
browser-safe polyfill calls `crypto.getRandomValues` ~31× per UUID (once
per template char, each with a fresh `Uint8Array(1)`); generating
thousands of seed UUIDs made that ~800ms of pure CPU in the
activity-event build alone.
- **`seedBulkSignupsAndActivity`.** Skip the redundant back-date
`UPDATE` for freshly-inserted users (`createMany` already writes correct
`createdAt`/`signedUpAt`), and flush ClickHouse events in larger,
parallel batches.
- **`seedDummyProject`.** Run `seedBulkSignupsAndActivity` concurrently
with the lighter remaining steps, and fold `seedDummyTransactions` into
the emails/activity/replays `Promise.all`.
- Removed the now-unused `syncSeedUserOauthProviders` helper.

The bulk path produces the same rows as the CRUD-handler path (verified
row-count equality during development). Webhooks / soft-limit checks are
intentionally not fired for seed data, consistent with the rest of the
seed.

## Also in this PR — preview-mode 404 fix
(`preview-project-redirect.tsx`)

While testing the above, the dashboard 404'd right after a preview
project was created. In preview mode the `/projects` page renders
`PreviewProjectRedirect`, which `POST`s
`/internal/preview/create-project` and then `router.push()`es to
`/projects/<new-id>` — but it never refreshed the client-side
owned-projects cache, so the `[projectId]` route's `useAdminApp()` read
a stale list, failed to find the just-created project, and called
`notFound()`.

Fixed by refreshing the owned-projects cache before navigating, matching
what the normal create-project flow in `page-client.tsx` already does.
(Pre-existing bug, not caused by the seeding change — but it surfaces
the seeding path, so it's bundled here.)

## Testing

`pnpm typecheck` and `pnpm lint` pass for both backend and dashboard.
The preview endpoint was exercised repeatedly during development (HTTP
200, projects created and populated correctly).

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Performance**
* Much faster bulk user and event seeding via larger, parallelized
batches and optimized backfilling.

* **Refactor**
* Dummy data seeding redesigned to be idempotent, deterministic, and
bulk-oriented; seeding tasks now overlap where safe.

* **Bug Fixes**
* Preview project flow validates client capabilities and refreshes the
local project list to avoid stale navigation.
  * Auto-login guarded to run only once to prevent duplicate sign-ins.

* **UI/UX**
* Walkthrough steps and sidebar behavior improved; walkthrough labels
and search keywords updated.

* **Chore**
* CLI identity command now resolves session authentication more
reliably.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1437?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-20 10:10:22 -07:00
Armaan Jain
5dbfb1ebab
Auth app redesign (#1367)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a reusable DesignDialog modal system (sizes, variants,
header/footer/headerContent, trigger/close controls).
* Added a documented "roids" skill and pinned it in the skills registry.

* **Documentation**
* Expanded design guide with comprehensive dialog usage patterns,
examples, and props.

* **Improvements**
  * Playground now previews and generates dialog code interactively.
* Auth methods and sign-up rules UIs migrated to the new design system.
* Action dialogs can opt to ignore outside interactions and accept
custom content classes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---

## Summary

Two things bundled together:

1. **New `DesignDialog` primitive** in
`@stackframe/dashboard-ui-components` — the canonical glassmorphic
dashboard modal shell. Exposes configurable sizes (`sm`→`7xl`/`full`),
`glassmorphic` vs `plain` variant, optional icon / title / description /
footer / custom header slots, and a `DesignDialogClose` companion.
Replaces the ad-hoc dialog wrappers scattered across the dashboard.
2. **Auth-app pages migrated onto the design-components system** —
`auth-methods` and `sign-up-rules` are rebuilt on `DesignCard` /
`DesignAlert` / `DesignButton` / `DesignBadge` / `DesignInput` /
`DesignMenu` / `DesignSelectorDropdown` / `DesignDialog`. Live
OAuth-page preview frame, glassmorphic confirmation dialogs, and a
redesigned rule-builder all live behind these new shells.

The design-language catalog page and the `/playground` component
explorer were both extended with full dialog showcases so the new
primitive has a single discoverable home.

**Base:** `dev` → **Head:** `auth-app-redesign`
**Scope:** 11 files changed · +2553 / −1151 lines

---

## Screenshots — before and after

> Captured locally against `http://localhost:8101` at 1440×900 with a
fresh project (`Demo Project`) created via the sign-up + new-project
flow. Dev-only overlays (outdated-version banner, console toasts) are
hidden via injected CSS for clarity.

### Auth methods — `/projects/<id>/auth-methods`

The big page-client rewrite. Before was a flat list of toggleable rows
with a live preview pinned to the right. After is a sectioned layout —
`SIGN-IN METHODS` and `SSO PROVIDERS` get uppercase subheaders, each
method gets a `DesignBadge` icon + description ("Classic email +
password credentials.", "One-time codes delivered by email.",
"Phishing-resistant device-bound credentials."), and empty states (e.g.
SSO with no providers configured) become real call-outs instead of plain
rows.

| Before (`dev`) | After (this PR) |
| --- | --- |
|
![](https://gist.githubusercontent.com/aadesh18/7789d1cf8622693a85f61cd7db91f5c2/raw/before-auth-methods__light.png)
|
![](https://gist.githubusercontent.com/aadesh18/7789d1cf8622693a85f61cd7db91f5c2/raw/after-auth-methods__light.png)
|
|
![](https://gist.githubusercontent.com/aadesh18/7789d1cf8622693a85f61cd7db91f5c2/raw/before-auth-methods__dark.png)
|
![](https://gist.githubusercontent.com/aadesh18/7789d1cf8622693a85f61cd7db91f5c2/raw/after-auth-methods__dark.png)
|

### Sign-up rules — `/projects/<id>/sign-up-rules`

Full rule-builder rewrite (CEL ↔ visual tree round-trip kept intact,
just dressed in the new design system). Before's empty state was a flat
alert + plain "Default action" row. After uses `DesignCard` variants —
`NO RULES YET` with an inline "Add your first rule" CTA, an "If no rules
match → Allow sign-up" surface, and a dedicated `TEST RULES` card
linking the simulator.

| Before (`dev`) | After (this PR) |
| --- | --- |
|
![](https://gist.githubusercontent.com/aadesh18/7789d1cf8622693a85f61cd7db91f5c2/raw/before-sign-up-rules__light.png)
|
![](https://gist.githubusercontent.com/aadesh18/7789d1cf8622693a85f61cd7db91f5c2/raw/after-sign-up-rules__light.png)
|
|
![](https://gist.githubusercontent.com/aadesh18/7789d1cf8622693a85f61cd7db91f5c2/raw/before-sign-up-rules__dark.png)
|
![](https://gist.githubusercontent.com/aadesh18/7789d1cf8622693a85f61cd7db91f5c2/raw/after-sign-up-rules__dark.png)
|

### Component playground — `/playground`

A new **Dialog** entry was added to the component selector. The before
shots show `dev` — the selector only listed Button (and a handful of
other primitives) and had no Dialog playground at all. The after shots
show the new entry: a props panel for `shape` / `size` / `variant` /
`title` / `description` / `headerIcon` / `footer` / `topRightClose`,
plus an "Open confirmation" button that mounts the live `DesignDialog`.

#### Closed (props panel + code preview)

| Before (`dev` — no Dialog entry) | After (this PR) |
| --- | --- |
|
![](https://gist.githubusercontent.com/aadesh18/7789d1cf8622693a85f61cd7db91f5c2/raw/before-playground-dialog__light.png)
|
![](https://gist.githubusercontent.com/aadesh18/7789d1cf8622693a85f61cd7db91f5c2/raw/after-playground-dialog__light.png)
|
|
![](https://gist.githubusercontent.com/aadesh18/7789d1cf8622693a85f61cd7db91f5c2/raw/before-playground-dialog__dark.png)
|
![](https://gist.githubusercontent.com/aadesh18/7789d1cf8622693a85f61cd7db91f5c2/raw/after-playground-dialog__dark.png)
|

> The "before" shots default to the Button playground because the Dialog
entry doesn't exist on `dev` — that's the change.

#### Open (glassmorphic surface in action)

The dialog itself — only available after this PR, so no `dev`
equivalent.

| Light | Dark |
| --- | --- |
|
![](https://gist.githubusercontent.com/aadesh18/7789d1cf8622693a85f61cd7db91f5c2/raw/after-playground-dialog-open__light.png)
|
![](https://gist.githubusercontent.com/aadesh18/7789d1cf8622693a85f61cd7db91f5c2/raw/after-playground-dialog-open__dark.png)
|
## What changed

- **New** `packages/dashboard-ui-components/src/components/dialog.tsx` —
the `DesignDialog` primitive. Props shape: `size` × `variant` × optional
`icon` / `title` / `description` / `headerContent` / `customHeader` /
`footer` slots, plus `trigger`, `noBodyPadding`, `hideTopCloseButton`,
and per-section `*ClassName` escape hatches. Exports `DesignDialog`,
`DesignDialogClose`, plus the `DesignDialogSize` / `DesignDialogVariant`
/ `DesignDialogProps` types.
- **Exports** wired through
`packages/dashboard-ui-components/src/index.ts` so consumers import from
`@stackframe/dashboard-ui-components` or, by extension, the dashboard's
local `@/components/design-components` barrel.
- **Auth methods page**
(`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/auth-methods/page-client.tsx`)
— full migration. Sign-in methods, OAuth provider list, dot-menu
actions, "Add disabled providers" search dialog, two confirmation
dialogs, sign-up policy block, user-deletion block. Old `Card` / `Input`
/ `Button` / `SettingCard` imports replaced with their design-component
counterparts. `providers.tsx` follows the same migration for the
per-provider config dialogs.
- **Sign-up rules page**
(`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/sign-up-rules/page-client.tsx`)
— the big 1830-line rewrite. Rule builder, empty state,
conditional-group editor, and tester sheet all rebuilt on the new
primitives. CEL ↔ visual-tree conversion (`parseCelToVisualTree` /
`visualTreeToCel`) is unchanged.
- **Design-language catalog**
(`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/design-language/page-client.tsx`)
— adds the Dialog section so the catalog reflects the new primitive.
- **Playground**
(`apps/dashboard/src/app/(main)/(outside-dashboard)/playground/page-client.tsx`)
— adds the Dialog entry with `Shape` / `Size` / `Variant` / `Title` /
`Description` / `Header Icon` / `Footer` / `Top-right close` controls
and live JSX generation.
- **Design guide** (`apps/dashboard/DESIGN-GUIDE.md`) — new "Dialogs"
section documenting when to reach for `DesignDialog` (default),
`DesignDrawer`, `ActionDialog`, or the raw `<Dialog>` primitives, plus
the canonical usage snippet.
- **Action dialog shim**
(`apps/dashboard/src/components/ui/action-dialog.tsx`) — small follow-on
edits so existing `ActionDialog` callers stay consistent with the new
look.

## Notes for reviewers

- **Start with**
`packages/dashboard-ui-components/src/components/dialog.tsx` — it's the
load-bearing piece. The two state machines worth eyeballing are the
`dialogSurfaceClasses` map (glassmorphic vs plain shells, including the
dark-mode ring/backdrop tweaks) and the header/body/footer composition
inside the main `DesignDialog` function.
- **Then** `sign-up-rules/page-client.tsx`. 1830 lines, but the diff is
mostly mechanical (Card→DesignCard, Button→DesignButton, etc.). The
interesting bits are the rule-row layout, the conditional-group editor,
and the simulator drawer — those received structural tweaks, not just
visual ones. The CEL serialization (`parseCelToVisualTree` /
`visualTreeToCel`) was deliberately left alone.
- **OAuth provider migration to non-pushable config** — a `// OAuth
client ID/secret are environment-level (not pushable)` comment was
removed from a couple of call-sites. Behaviour-equivalent (the call
already passes `pushable: false`), just trimmed because the new code is
cleaner. Flag if you want it kept.
- **Catalog routes are dashboard-internal**
(`/projects/<id>/design-language`, `/playground`) — exposed only in
dev/staging, not customer-facing. They exist so design changes have a
discoverable demo surface.
- **Live-preview frame on `auth-methods`** uses a real `<AuthPage>`
inside `BrowserFrame`, fed by the in-progress config. Verify your
changes still render correctly there if you touch `<AuthPage>` props.

## Test plan

- [ ] `/projects/<id>/auth-methods` — toggle each sign-in method; live
preview reflects the change; "Save changes" inline action works; "Add
SSO providers" dialog filters via the search input
- [ ] OAuth provider dot-menu — open the provider config dialog (now
`DesignDialog` glassmorphic), confirm the per-provider switches/inputs
save through the `useUpdateConfig` hook
- [ ] Sign-up confirmation dialogs — toggling "Allow new user sign-ups"
off and back on shows the new warning `DesignAlert`s inside the dialog
- [ ] `/projects/<id>/sign-up-rules` — add a rule, add a condition
group, run the tester sheet; CEL output unchanged vs `dev`
- [ ] `/projects/<id>/design-language` — Dialog showcase renders all
sizes/variants without overflow
- [ ] `/playground` → select **Dialog** — all prop combinations render;
generated code snippet matches the rendered component; "Open
confirmation" launches the glassmorphic shell
- [ ] Light + dark mode visual sanity across all four pages (screenshots
above are the canonical reference)

---------

Co-authored-by: Aadesh Kheria <kheriaaadesh@gmail.com>
2026-05-19 23:03:46 -07:00
Konsti Wohlwend
29cea48beb
Remote dev envs (#1435) 2026-05-19 15:54:18 -07:00
BilalG1
d0202eeef9
payments: rework refund flow to three-knob API (#1429)
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
## Summary
- Replaces per-entry refund schema with a flat `{ amount_usd,
revoke_product, end_subscription? }` shape; refund state is now derived
from bulldozer ledger rows (`refund:<sourceTxnId>:<uuid>`) instead of
the legacy `refundedAt` column, enabling multiple partial refunds up to
the remaining cap.
- Adds `invoice_id` for refunding any subscription invoice (start or
renewal), Stripe idempotency keys derived from `(tenancyId, sourceTxnId,
amount, prior_refunded)` so retries dedupe but intentional partials
don't collide, and a legacy backstop that rejects pre-rework
`refundedAt` purchases.
- Dashboard refund dialog rebuilt around the three toggles (revoke→end
coupling cascades into the UI); refund rows surface in the listing as
`type: "refund"` with `adjusted_by` linkage handling both new and legacy
formats.

## Implements
[STA2-52 — Build in refund logic for
payments](https://linear.app/stack-auth/issue/STA2-52/build-in-refund-logic-for-payments)

## Documented limitations (planned follow-up work)
These are called out in code comments and intentionally deferred to a
follow-up PR:
- **Cap-check race under concurrent refunds.** Bulldozer's embedded
`BEGIN/COMMIT` prevents an outer Prisma tx from scoping the writes, so
two concurrent refunds can both pass the cap check. Needs a
bulldozer-aware mutex or pending-refund-intent pattern. In practice
refunds are admin-only and rare, so the race window is small.
- **Stripe + DB non-atomicity on the DB-success → response-loss path.**
The Stripe idempotency key is keyed on `(tenancyId, sourceTxnId, amount,
priorRefunded)`, so a retry after Stripe-success → DB-fail self-heals
(Stripe dedupes; the next attempt writes the bulldozer row). The hole is
the reverse direction: if the bulldozer row commits but the response is
lost, a retry sees a higher `priorRefunded` and generates a fresh key —
Stripe would issue a second real refund. No out-of-band reconciliation
today.
- **Dashboard can't reach the `invoice_id` path.** Refund actions are
only enabled on `purchase` rows and the submit call never passes
`invoice_id`, so admins refunding a renewal must use the API directly.
Follow-up: enable the action on `subscription-renewal` rows and thread
`invoice_id` through.

## Architectural note
`active-subscription-end` and `item-quantity-expire` entries are **not**
emitted on the refund row itself. They're produced by the derived
sub-end transaction (`transactions.ts:158-228`) once Prisma
`subscription.endedAt` is updated, keeping the `expiresWhen` /
`when-repeated` semantics in one place. This is the main structural
divergence from the ticket's literal entry recipe.

## Review follow-ups addressed in this PR

**First-pass review:**
- **KnownError back-compat preserved**: `SubscriptionAlreadyRefunded` /
`OneTimePurchaseAlreadyRefunded` are once again thrown by the
legacy-`refundedAt` backstop, and `TestModePurchaseNonRefundable` is
thrown when an admin sends `amount_usd > 0` against a test-mode
purchase. Callers catching by error code keep working through the
rework.
- **Idempotency-key comment corrected**: now accurately describes the
`(tenancyId, sourceTxnId, amount, priorRefunded)` key and its
self-healing behaviour on the Stripe-success → DB-fail retry path (see
Documented limitations above for the remaining hole).
- **Renewal-invoice e2e coverage added**: new test sets up a live-mode
subscription via Stripe webhooks (`subscription_create` +
`subscription_cycle` invoices), refunds the renewal invoice via
`invoice_id`, and asserts the resulting `refund_transaction_id` starts
with `refund:sub-renewal:` and is linked back via `adjusted_by` on the
*renewal* row (not the start row). Plus negative cases:
cross-subscription `invoice_id` → 404, `invoice_id` on a one-time
purchase → SchemaError.

**Second-pass review:**
- **Idempotent sub-cancel error-code string fix**: the Stripe code for
re-cancelling an already-canceled sub is
`subscription_already_canceled`, not `subscription_canceled` — the
previous catch would have re-thrown.
- **End-only sub refund replay rejected**: when `amount=0, revoke=false,
end=true` and the sub is already `cancelAtPeriodEnd` or `endedAt`, throw
SchemaError. Otherwise `readPriorRefundSummary` doesn't see end-only
events and the call would be a forever-no-op accumulating empty refund
rows.
- **`revoke_product=true` with renewal `invoice_id` rejected**: the
product grant lives on the sub-start txn, not on renewal txns — a
renewal-scoped revocation would write a back-reference to a non-existent
entry. Forces admin to revoke against the start invoice (or the default
no-`invoice_id` call).
- **Refund row `id` matches the linkage**: the listing route now returns
the full refund txnId as `id` for `type: "refund"` rows so it matches
`adjusted_by.transaction_id` — the dashboard can join source rows to
their refund rows.
- **+2 e2e tests** for the above (end-only replay rejection,
revoke+renewal rejection).

**Third-pass review:**
- **Dashboard refund dialog seeds state on open**: previously the reset
block lived in `ActionDialog`'s `onOpenChange`, which doesn't fire on
the open transition for a controlled dialog. As a result the dialog
opened with the initial `useState` defaults (`amountUsd = '0'`), and an
admin submitting unchanged on a paid purchase would revoke/end at $0
instead of refunding the charged amount. The seed now runs in the menu
`onClick` before `setIsDialogOpen(true)`.
- **`SUBSCRIPTION_START_PRODUCT_GRANT_ENTRY_INDEX` corrected from 1 →
0**: the constant is persisted as `adjustedEntryIndex` on
product-revocation entries and copied through verbatim by
`mapLedgerEntry`. That mapper drops the hidden
`active-subscription-start` entry, so the public-API layout puts the
product grant at index 0. The prior value of `1` pointed at the
money-transfer entry (or out of range on test-mode subs) through the
public listing.
- **`amountTotal` cap gated behind a USD pre-flight**:
`SubscriptionInvoice` doesn't persist invoice currency, and the previous
code took `invoice.amountTotal` as USD cents directly. Now
`getTotalUsdStripeUnits` (which throws on non-USD pricing) is always
called first; `amountTotal` is only preferred as the actual cap after
that pre-flight succeeds.

## Test plan
- [x] `pnpm typecheck` — 28/28 pass
- [x] `pnpm lint` — 28/28 pass
- [x] `pnpm test run
apps/e2e/tests/backend/endpoints/api/v1/internal/transactions-refund.test.ts`
— **19/19 pass** (was 14/14 on the original PR; +3 for `invoice_id`
path: renewal refund happy path, unrelated `invoice_id` rejection,
`invoice_id` on OTP rejection; +2 for second-pass: end-only replay
rejection, revoke+renewal rejection)
- [x] curl smoke against
`/api/latest/internal/payments/transactions/refund` — unknown purchase →
404, no-op → 400, negative → 400, sub-revoke-without-end → 400
- [x] **Dashboard UI end-to-end re-run pending** — the original
agent-browser pass ran before the third-pass dialog-seed fix, so any
"money + revoke" submissions may have actually sent `amount_usd = "0"`.
Re-test before un-drafting: open the refund dialog from the menu,
confirm the amount field pre-fills with the charged amount, exercise
validation (negative / exceeds-cap / no-op), and submit both an
end-subscription-only sub refund and a money+revoke OTP refund; verify
bulldozer rows and Prisma `cancelAtPeriodEnd` updates.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Ledger-driven refund flow with stable refund IDs, invoice-aware
refunds, OTP/product-revocation support, tri-state end_action (now /
at-period-end / none), and API responses that include
refund_transaction_id.

* **Bug Fixes / Improvements**
* Deterministic Stripe idempotency, stronger replay protection,
refundable-amount caps, test-mode constraints, and transactions listing
updated to surface refunds.

* **Tests**
* Expanded unit and E2E coverage for new request shape, invoice paths,
money-unit conversion, and edge cases.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1429)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-15 19:29:21 -07:00
Armaan Jain
b526e3b367
Project transfer page redesign (#1309)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Reusable transfer confirmation UI with clear loading, success, and
error states.
* Neon-specific transfer flow added, guiding sign-in, account switching,
or accepting transfers.
* Custom integration transfer flow with streamlined confirm/check
behavior.
* Improved transfer sign-up redirect so users return to the correct page
after auth.

* **Bug Fixes**
  * Consistent messaging for missing/invalid/expired transfer codes.
  * Safer widget “Reload” handling when reset may be unavailable.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---

## Summary

Redesigns the **custom integration** project-transfer confirmation page
(`/integrations/custom/projects/transfer/confirm`) onto the new
design-components system (`DesignCard` + `DesignAlert` + `DesignButton`
+ `DesignInput`). The presentational shell is extracted into a reusable
`ProjectTransferConfirmView` so the route file only handles state + API
calls. The legacy Neon transfer page is split out unchanged into its own
client component to keep the existing Neon × Stack co-branded UI intact.

---

## Screenshots — before and after

> Captured against `http://localhost:8101` at 1280×900. Dev-only
overlays (outdated-version banner, console toast, DEV badge) are hidden
via injected CSS for clarity.

### Custom integration — missing transfer code

Visiting `/integrations/custom/projects/transfer/confirm` with no
`?code=…` query param.

| Before (`dev`) | After (this PR) |
| --- | --- |
|
![](https://gist.githubusercontent.com/aadesh18/85342c8cb1890b11e7b44c9baff34283/raw/before-custom-missing__light.png)
|
![](https://gist.githubusercontent.com/aadesh18/85342c8cb1890b11e7b44c9baff34283/raw/after-custom-missing__light.png)
|
|
![](https://gist.githubusercontent.com/aadesh18/85342c8cb1890b11e7b44c9baff34283/raw/before-custom-missing__dark.png)
|
![](https://gist.githubusercontent.com/aadesh18/85342c8cb1890b11e7b44c9baff34283/raw/after-custom-missing__dark.png)
|

Before was a raw `"Error: No transfer code provided."` line. After is a
dedicated `DesignAlert` with an explanation and recovery instructions.

### Custom integration — invalid / expired code (check endpoint fails)

| Before (`dev`) | After (this PR) |
| --- | --- |
|
![](https://gist.githubusercontent.com/aadesh18/85342c8cb1890b11e7b44c9baff34283/raw/before-custom-error__light.png)
|
![](https://gist.githubusercontent.com/aadesh18/85342c8cb1890b11e7b44c9baff34283/raw/after-custom-error__light.png)
|
|
![](https://gist.githubusercontent.com/aadesh18/85342c8cb1890b11e7b44c9baff34283/raw/before-custom-error__dark.png)
|
![](https://gist.githubusercontent.com/aadesh18/85342c8cb1890b11e7b44c9baff34283/raw/after-custom-error__dark.png)
|

Before showed the raw backend error string (`Request validation failed
on POST …`). After uses a `DesignCard` with the `ArrowsLeftRightIcon`, a
friendlier "This transfer can't continue" copy in an inline
`DesignAlert`, the Stack Auth logomark in the actions slot, and an
explicit **Close** button to dismiss.

### Neon integration — legacy UI preserved

The Neon page (`/integrations/neon/projects/transfer/confirm`) was
deliberately **not** redesigned — it still uses the Neon × Stack
co-branded card so partner-facing copy/branding stay identical. It's now
its own client component (`neon-transfer-confirm-page.tsx`) instead of
sharing the redesigned one.

| Before (`dev`) | After (this PR) |
| --- | --- |
|
![](https://gist.githubusercontent.com/aadesh18/85342c8cb1890b11e7b44c9baff34283/raw/before-neon-error__light.png)
|
![](https://gist.githubusercontent.com/aadesh18/85342c8cb1890b11e7b44c9baff34283/raw/after-neon-error__light.png)
|
|
![](https://gist.githubusercontent.com/aadesh18/85342c8cb1890b11e7b44c9baff34283/raw/before-neon-error__dark.png)
|
![](https://gist.githubusercontent.com/aadesh18/85342c8cb1890b11e7b44c9baff34283/raw/after-neon-error__dark.png)
|

Same shell on both sides — copy was tightened slightly ("Return to your
Neon dashboard and start the transfer again") and the raw API error
string is gone.

---

## What changed

- **New**
`apps/dashboard/src/components/project-transfer-confirm-view.tsx` —
purely presentational `ProjectTransferConfirmView`. Owns the
design-components shell, the loading spinner, the signed-in vs
signed-out branches of the success state (with `DesignInput` + "Use a
different account" button), and the error / missing-code alerts.
- **New**
`apps/dashboard/src/app/(main)/integrations/neon-transfer-confirm-page.tsx`
— extraction of the legacy Neon UI (Neon logo, Stack logo, "Project
transfer" header, Card / CardContent / CardFooter). Behaviour and copy
match the previous `transfer-confirm-page` exactly when `type ===
"neon"`.
- **Rewritten**
`apps/dashboard/src/app/(main)/integrations/transfer-confirm-page.tsx` —
now hard-coded to the `custom` integration (no more `type` prop), defers
UI to `ProjectTransferConfirmView`, and exports a
`TransferConfirmMissingCodeView` used by the route when `code` is absent
from the URL.
- **Route plumbing**
- `app/(main)/integrations/custom/projects/transfer/confirm/page.tsx` —
renders the redesigned flow, falls back to
`TransferConfirmMissingCodeView` when `code` is missing.
- `app/(main)/integrations/neon/projects/transfer/confirm/page.tsx` —
points at the new dedicated Neon client component.
- **New** `apps/dashboard/src/lib/stack-app-internals.ts` — consolidates
the symbol-keyed `getStackAppInternals(app)` helper (and
`stackAppInternalsSymbol`) into one module with a JSDoc explainer +
runtime type guard, replacing scattered `as any` casts.
- **New** `apps/dashboard/src/lib/transfer-utils.ts` —
`buildTransferSignUpUrl()` helper so the route file + the view stay in
sync on the `/handler/signup?after_auth_return_to=…` query construction.

---

## Bot review follow-ups addressed in this PR

- **Fail-loud assertions for unset handlers** in the success state of
`ProjectTransferConfirmView` (`StackAssertionError` instead of silent
no-op).
- **SSR safety:** moved every `window.location` read into client-only
handlers / `useEffect`s — the page was previously evaluating it at
module load.
- **Friendly error fallback** when the backend `/check` endpoint throws
— replaces the raw `KnownError<…>` message with "This transfer link is
invalid, has expired, or has already been used. Open the original link
from the partner or integrations dashboard, or start the transfer
again."
- **`runAsynchronouslyWithAlert`** around every async `onClick`
(Transfer, Sign in, Switch account, Close) so unhandled rejections
surface to the user.
- **JSX entity bug fix:** `&apos;` was a string-attribute literal, not a
JSX expression — converted to a JSX expression so it renders as `'`.
- **`window.close()` removal** in error state — replaced with a Close
button that resets local state, so users on a fresh tab (no opener)
aren't stuck.
- **`getStackAppInternals` consolidated** — previously three independent
copies (here + two in `projects/page-client.tsx`). Now one helper with a
runtime type guard instead of `as any`, plus a comment explaining the
symbol-keyed SDK escape hatch.
- **Widget-playground reset:** the original change here turned out to
duplicate a deliberate prior fix on `dev` (N2D4, `e68015909d "Fix
lint"`). Reverted in `fe92689eb` so we don't fight that fix.

---

## Notes for reviewers

- **Start with** `components/project-transfer-confirm-view.tsx`.
Everything reviewer-interesting is in the props shape
(`ProjectTransferConfirmUiState` union, `onPrimary` / `onCancel` /
`onSwitchAccount` callbacks). The route file just wires those to the
`getStackAppInternals(app).sendRequest(...)` calls.
- **The Neon page was intentionally not migrated.** Partner-facing
co-branding (Neon logo × Stack logo, "Neon would like to transfer…"
copy) is unchanged — flag it if you think it should be brought onto
design-components too, but the goal of this PR was only the custom flow.
- **API surface is unchanged** — same
`/integrations/custom/projects/transfer/confirm/check` and
`/integrations/custom/projects/transfer/confirm` endpoints, same request
bodies, same redirect to `/projects/{project_id}` on success.
- **Success state isn't in the screenshots** because reproducing it
locally needs a real transfer code (the `/check` endpoint validates the
code against the DB). It uses the same `DesignCard` shell with either a
`DesignInput` showing the receiving account + a "Use a different
account" outline button (signed-in branch), or a `DesignAlert
variant="info"` prompting sign-in (signed-out branch). Worth manually
testing on a real transfer before merging.

## Test plan

- [ ] Visit `/integrations/custom/projects/transfer/confirm` with no
`code` → renders the "transfer link is incomplete" alert (screenshots
above)
- [ ] Visit
`/integrations/custom/projects/transfer/confirm?code=invalid` → renders
the redesigned card with the friendly error inside a `DesignAlert
variant="error"` and a working Close button
- [ ] Trigger a real custom-integration transfer end to end → loading
spinner, success state, "Accept transfer" works while signed in, "Sign
in" deep-links to `/handler/signup?after_auth_return_to=…` while signed
out
- [ ] Visit `/integrations/neon/projects/transfer/confirm?code=…` →
unchanged legacy Neon × Stack co-branded card
- [ ] Light + dark mode visual sanity (screenshots above are the
canonical reference)

---------

Co-authored-by: Aadesh Kheria <kheriaaadesh@gmail.com>
Co-authored-by: aadesh18 <110230993+aadesh18@users.noreply.github.com>
2026-05-15 16:59:51 -07:00
Mantra
5cb9240bc3
refactor(dashboard): unify AI chat surfaces on assistant-ui Thread (#1427)
## Summary
- Replace the bespoke `ai-chat-shared` chat UI (used by ask-ai, the
stack companion widget, vibe coding chat, and the create-dashboard
preview) with the shared `assistant-ui` `Thread` component.
- Extract streaming request/format helpers into a new
`components/assistant-ui/chat-stream.ts` module so each surface only
owns its `ChatModelAdapter`.
- Add a reusable `ToolFallback` for tool-call rendering and delete the
now-unused `ai-chat-shared.tsx` (-1386 / +747 lines net).

Stacked on top of `refactor/data-grid-and-dashboard-surfaces`.

Base: `refactor/data-grid-and-dashboard-surfaces` → Head:
`refactor/assistant-ui-chat-surfaces` · 18 files changed

> Red outlines on the **after** shots mark the unified `assistant-ui`
`Thread` surface in each location.

## Screenshots

### Analytics → Tables — AI Query dialog

| | Before | After |
|---|---|---|
| **Light** | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/analytics-tables-ai-before-light.png"
width="480" /> | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/analytics-tables-ai-after-light.png"
width="480" /> |
| **Dark** | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/analytics-tables-ai-before-dark.png"
width="480" /> | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/analytics-tables-ai-after-dark.png"
width="480" /> |

### Stack Companion — chat widget

| | Before | After |
|---|---|---|
| **Light** | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/stack-companion-before-light.png"
width="480" /> | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/stack-companion-after-light.png"
width="480" /> |
| **Dark** | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/stack-companion-before-dark.png"
width="480" /> | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/stack-companion-after-dark.png"
width="480" /> |

### Ask-AI command palette (⌘K → Ask AI)

| | Before | After |
|---|---|---|
| **Light** | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/ask-ai-cmdk-before-light.png"
width="480" /> | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/ask-ai-cmdk-after-light.png"
width="480" /> |
| **Dark** | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/ask-ai-cmdk-before-dark.png"
width="480" /> | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/ask-ai-cmdk-after-dark.png"
width="480" /> |

### Email editor — embedded chat panel

| | Before | After |
|---|---|---|
| **Light** | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/email-editor-chat-before-light.png"
width="480" /> | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/email-editor-chat-after-light.png"
width="480" /> |
| **Dark** | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/email-editor-chat-before-dark.png"
width="480" /> | <img
src="https://gist.githubusercontent.com/mantrakp04/323851437f41145aab12a27fb6c392b4/raw/email-editor-chat-after-dark.png"
width="480" /> |

## Notes for reviewers

The four surfaces above all previously shared
`components/commands/ai-chat-shared.tsx` (516 lines, deleted). After
this PR they each own a thin `ChatModelAdapter` and render through
`components/assistant-ui/thread.tsx` + the new `chat-stream.ts` helpers.
Visual differences between **before** and **after** are intentional —
the `assistant-ui` `Thread` brings its own message bubbles,
scroll-to-bottom behaviour, composer, and `ToolFallback` rendering. The
email editor's chat panel is the surface where the behaviour change is
most visible (tool-call rendering now consistent with the rest of the
app).

Heaviest changes (lines):
- `components/stack-companion/ai-chat-widget.tsx` (571)
- `components/commands/ai-chat-shared.tsx` (516, deleted)
- `analytics/tables/ai-query-dialog.tsx` (429)
- `components/vibe-coding/chat-adapters.ts` (400)
- `components/assistant-ui/chat-stream.ts` (284, new)
- `components/commands/ask-ai.tsx` (274)
- `components/assistant-ui/thread.tsx` (115)
- `components/assistant-ui/tool-fallback.tsx` (113)

## Test plan
- [ ] `pnpm lint`
- [ ] `pnpm typecheck`
- [ ] Manually exercise each affected surface: command-center Ask AI,
stack-companion widget, vibe-coding chat, analytics tables AI query,
create-dashboard preview, email editor chat.
- [ ] Verify tool-call chips render consistently across all four
surfaces (uses the new `ToolFallback`).
- [ ] Verify streaming + cancel works on each adapter (`chat-stream.ts`
is shared).
2026-05-15 14:21:00 -07:00
Mantra
c808e23b7d
Data-grid overhaul + session-replays / team-payments dashboard surfaces (#1424)
## Summary

Refactors the dashboard data-grid into a smaller, URL-state-aware
primitive and lands several new dashboard surfaces around it: per-user
session replays, team-level analytics and payments, and pagination for
permission definitions. Also moves session replays out from under
`/analytics` to a top-level surface and adds a
`project_user.last_active_at` index that the new weekly-active metrics
depend on.

**Base:** `dev` → **Head:** `refactor/data-grid-and-dashboard-surfaces`
**Scope:** 91 files, +5,644 / −1,858. Assets in [this
gist](https://gist.github.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7).

## Screenshots

Captured from a local dev server (dashboard at `:8101`, dummy project
seeded with 26 users). Standard viewport **1920×1200**, widescreen
**2560×1440**.

### Users list — data-grid overhaul in context

| Light | Dark |
| --- | --- |
|
![users-list-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/users-list-light.png)
|
![users-list-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/users-list-dark.png)
|

Widescreen:

| Light | Dark |
| --- | --- |
|
![users-list-light-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/users-list-light-wide.png)
|
![users-list-dark-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/users-list-dark-wide.png)
|

### User detail — new session-replays card + weekly metrics

| Light | Dark |
| --- | --- |
|
![user-detail-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/user-detail-light.png)
|
![user-detail-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/user-detail-dark.png)
|

Widescreen:

| Light | Dark |
| --- | --- |
|
![user-detail-light-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/user-detail-light-wide.png)
|
![user-detail-dark-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/user-detail-dark-wide.png)
|

### Session replays — moved out of `/analytics`

| Light | Dark |
| --- | --- |
|
![session-replays-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/session-replays-light.png)
|
![session-replays-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/session-replays-dark.png)
|

Widescreen:

| Light | Dark |
| --- | --- |
|
![session-replays-light-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/session-replays-light-wide.png)
|
![session-replays-dark-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/session-replays-dark-wide.png)
|

### Project permissions — new pagination

| Light | Dark |
| --- | --- |
|
![project-permissions-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/project-permissions-light.png)
|
![project-permissions-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/project-permissions-dark.png)
|

Widescreen:

| Light | Dark |
| --- | --- |
|
![project-permissions-light-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/project-permissions-light-wide.png)
|
![project-permissions-dark-wide](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/project-permissions-dark-wide.png)
|

### Other migrated surfaces

| Page | Light | Dark |
| --- | --- | --- |
| Project picker |
![projects-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/projects-light.png)
|
![projects-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/projects-dark.png)
|
| Overview / setup |
![overview-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/overview-light.png)
|
![overview-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/overview-dark.png)
|
| Teams list |
![teams-list-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/teams-list-light.png)
|
![teams-list-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/teams-list-dark.png)
|
| Team permissions |
![team-permissions-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/team-permissions-light.png)
|
![team-permissions-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/team-permissions-dark.png)
|
| API keys |
![api-keys-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/api-keys-light.png)
|
![api-keys-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/api-keys-dark.png)
|

### Scroll behaviour — new data-grid on the users list

| Light | Dark |
| --- | --- |
|
![users-list-scroll-light](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/users-list-scroll-light.gif)
|
![users-list-scroll-dark](https://gist.githubusercontent.com/mantrakp04/01bf8db4c71ec7a119b73d6ee60717a7/raw/users-list-scroll-dark.gif)
|

## What's new

- **`packages/dashboard-ui-components/src/components/data-grid`** —
rewritten. Trimmed `data-grid.tsx` from ~1.7k LOC, split sizing logic
into `data-grid-sizing.ts`, added `use-url-state.ts` for URL-synced
state, and added `data-grid.test.tsx`.
- **Session replays** moved from `…/analytics/replays` to
`…/session-replays` (top-level surface). New `user-session-replays.tsx`
card on the user detail page; new internal `route.tsx` to feed it.
- **Teams** detail page gains `team-analytics.tsx` and
`team-payments.tsx`.
- **Permissions** — new shared `permission-definitions-pagination.ts`
consumed by both project and team permission CRUD routes.
- **Backend** — Prisma migration `add_project_user_last_active_at_idx` +
a `lastActiveAt` index that backs the new weekly-active metrics.
- **Polish** — `editable-input`, `inline-save-discard`, `settings.tsx`,
walkthrough steps, and several data-table components touched in line
with the data-grid rewrite.

## Notes for reviewers

- The data-grid rewrite changes the *shape* of state (now URL-synced),
not just internals. Consumers in
`apps/dashboard/src/components/data-table/*` were updated to match —
please scan those for any missed knobs.
- The `analytics/replays` → `session-replays` rename is git-tracked as
renames; diffs should be small in those files.
- New SDK surface in
`packages/template/src/lib/stack-app/session-replays/index.ts` and
additions in `admin-app-impl.ts` / `server-app-impl.ts` mean OpenAPI
specs (`docs-mintlify/openapi/{admin,client}.json`) regenerate; the diff
is mostly mechanical.

## Test plan

- [ ] `pnpm typecheck` clean
- [ ] `pnpm lint` clean
- [ ] Data-grid unit tests pass (`packages/dashboard-ui-components`)
- [ ] Manual: users list — column resize, sort, filter, paginate; URL
state reflects each change and survives reload
- [ ] Manual: user detail — session-replays card lists replays;
weekly-metrics card renders without `lastActiveAt` index migration
applied (i.e. on a fresh DB) and after applying it
- [ ] Manual: project + team permissions — pagination cursor advances
and stays consistent under search
- [ ] Manual: session-replays top-level page loads; old
`/analytics/replays/...` URL path is no longer expected to be linked
anywhere


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Session Replays app (embedded mode, search, sorting, share links)
  * Tabbed Team pages with Team Analytics and Team Payments dashboards
* Server-backed cursor pagination, debounced search, and infinite-scroll
for teams/users/permissions

* **UX**
* Permission and member tables refresh after edits; permission creation
triggers table refresh
  * Users list supports sorting by last-active

* **Performance**
  * Index added to speed ProjectUser last-active queries

* **Documentation**
  * API/SDK docs updated for pagination and new query params
* Contributor guidance: explicit git-safety rules added (no destructive
git ops without consent)

* **Tests**
  * Added e2e tests for pagination and filtering on list endpoints
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-15 14:16:47 -07:00
Aman Ganapathy
a9623d976a
[Refactor] [Fix] Remove default prod creation (#1350)
With the new bulldozer rework we dont support default products anymore.
Users are encouraged to currently manually handle granting products to
their end users.

We block api requests and new product creations that attempt to set no
price, and we remove any options to set include-by-default. We also
migrate users' existing product snapshots in `Subscriptions`,
`OneTimePurchases`, and `ProductVersions` to have no price set if it's
an include-by-default product. This will make it so that next time a
user goes onto their products page, they will be informed that the
pricing is invalid and it is no longer delivered by default.

Note, however, that these products will still be providing items and the
like to the users who have them.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Migrated legacy product snapshots so missing included-items no longer
break readers.
* Removed deprecated "include-by-default" pricing sentinel; pricing now
requires explicit price entries and write validation rejects the old
sentinel.

* **Chores**
* Simplified dashboard pricing flows: create/edit/save now use explicit
prices and surface an alert when a formerly implicit free plan needs an
explicit $0 price.
* Config overrides and stored data are auto-normalized to explicit price
objects.

* **Tests**
* Updated and added tests covering migration, validation, and switching
behavior for explicit prices.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: mantrakp04 <mantrakp@gmail.com>
Co-authored-by: Mantra <87142457+mantrakp04@users.noreply.github.com>
2026-05-15 10:38:33 -07:00
aadesh18
76023af9d6
Custom Dashboards Versioning fix (#1418)
This PR fixes the versioning error that we ran into for custom
dashboards. Now if the latest version of the packages does not work, we
fall back to the version that is one patch below the latest version. We
log this into sentry. If the fall back doesn't work either, we log that
into sentry as well and show the user an error message.

Apart from that, I also made changes to ensure dashboards with older
versions of the dashboard-ui-component package would still work. Each
dashboard now stores the version it was created with, as a comment at
the top of its source code, and we use that version when loading the
dashboard. When a dashboard gets edited via the AI chat, we re-stamp it
with the latest version of the package so it stays up to date.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved error handling and reporting for dashboard load failures;
host surfaces structured dependency errors for faster diagnostics.
* Added automatic fallback loading for missing resources to reduce load
failures.
* Fixed page height calculation so pages align correctly with the
viewport.

* **New Features**
* Generated and editor-provided dashboard code is now stamped with the
app version for clearer provenance.

* **UI/UX Improvements**
* Clearer, more informative error messages when custom dashboard loading
encounters issues.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/hexclave/stack-auth/pull/1418)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-12 12:35:13 -07:00
Konstantin Wohlwend
efa2153d47 Improve project overview weekly users 2026-05-12 11:45:22 -07:00
Mantra
227dac6567
feat(dashboard): add weekly users metrics for projects (#1412)
- Introduced a new API endpoint to fetch weekly and daily user metrics
for managed projects.
- Updated the dashboard to utilize this new endpoint, replacing the
previous daily active users data.
- Created a new component to visualize weekly users metrics in the
project cards.
- Refactored existing components to accommodate the new data structure
and ensure proper rendering of user activity charts.

This change enhances the analytics capabilities of the dashboard,
providing better insights into user engagement over time.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* New internal endpoint providing per-project weekly user totals and
7-day daily activity series.

* **Updates**
* Dashboard and project cards switched from DAU to weekly user metrics;
main metric shows weekly users and label reads "users/wk".
* Charts now display weekly-user-aware sparklines alongside daily
activity.

* **Tests**
* Added unit tests covering weekly aggregation and daily-series merging.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-08 11:00:50 -07:00
Mantra
68ae6d1f1c
[codex] Add TanStack Start SDK integration (#1399)
## Summary

- Adds the generated `@stackframe/tanstack-start` workspace package
registration.
- Adds TanStack Start platform macros/dependencies to the SDK template
and generator.
- Adds TanStack Start cookie/token-store support plus the handler SSR
guard needed by Start.

## Scope

This intentionally excludes Dashboard V2 routes, hooks, components, app
shell logic, and dashboard API type additions. Those stay in the
existing dashboard PR/branch.

## Validation

- `pnpm install --lockfile-only --ignore-scripts`
- `pnpm install --ignore-scripts`
- `pnpm -C packages/template lint
src/components-page/stack-handler-client.tsx src/lib/cookie.ts
src/lib/stack-app/apps/implementations/client-app-impl.ts`

Package typecheck was attempted with `pnpm -C packages/template
typecheck`, but the clean worktree lacks generated package declaration
outputs for workspace dependencies such as `@stackframe/stack-shared`
and `@stackframe/stack-ui`. Per repo instructions, package
builds/codegen are not run by agents.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* TanStack Start integration: published SDK package, example demo app,
dashboard onboarding flow, framework-aware CTAs/docs, and a
TanStack-specific provider for client-only auth routes.
* Improved client/server auth: safer runtime guards and consistent
cookie/token-store behavior across SSR and client.

* **Documentation**
* New Integrations guide and expanded getting-started/setup docs with
TanStack Start examples and env/key guidance.

* **Chores**
* Template, build, tooling, and demo config updates to support the new
platform.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-08 10:59:16 -07:00
Konstantin Wohlwend
5ccd8dfd38 Update GitHub URL 2026-05-06 15:17:01 -07:00
Madison
185bddec9e
[Dashboard] Redefine the user page with tabs and updated UI (#1351)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Tabbed user profile with Activity (30-day analytics, KPIs, daily
chart, top lists, recent events), Payments (transactions, subscriptions,
product/item balances) and an activity heatmap sidebar.
* New internal user-activity API and admin-facing activity hook; admin
API client can fetch per-user activity.

* **UI/UX Improvements**
* Unified menus, cards and tables; inline editable user details with
accept/revert; metadata editor validates JSON; country-code input has
draft editing; tabs support optional icons.

* **API**
* Transactions endpoint and admin transaction queries now support
optional customer-scoped filtering.

* **Tests**
  * End-to-end coverage for the user-activity endpoint.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

<img width="1326" height="752" alt="image"
src="https://github.com/user-attachments/assets/97c04dca-db59-4357-98b1-8eae5a7a3673"
/>

<img width="1142" height="251" alt="image"
src="https://github.com/user-attachments/assets/e1aa44fc-0d7e-436d-90a5-c7cb15155e24"
/>

<img width="1170" height="1125" alt="image"
src="https://github.com/user-attachments/assets/bf6659fd-a9b5-4ae6-a13d-dab9956ad650"
/>
2026-05-05 17:09:09 -05:00
BilalG1
9f79bfbe5c
fix(dashboard): collapsed email editor height; sandbox email-preview iframes (#1406)
## Summary

Two small dashboard fixes bundled together.

### 1. Email editor renders with zero height

The email template/theme/draft pages render `VibeCodeLayout`, whose
mobile and desktop root wrappers used `h-full`. The dashboard shell's
`<main>` (`sidebar-layout.tsx:750`) has no explicit height — its flex
parent uses `items-start`, so `<main>` shrinks to its content rather
than stretching. With no definite height up the chain, every `h-full`
along the way (sidebar-layout's inner div, the `data-full-bleed`
wrapper, `VibeCodeLayout`'s own root) resolves to `auto`, and since the
editor's content lives inside absolutely-positioned `ResizablePanel`s,
the wrapper collapses to ~0.

**Fix:** anchor `VibeCodeLayout`'s root wrappers to
viewport-minus-header instead of `h-full`. The values match what
`sidebar-layout.tsx:738` already uses for the sticky sidebar (`3.5rem`
light / `6rem` dark for the floating header card). With a definite
height at the top, the existing `flex-1` chains inside `VibeCodeLayout`
resolve correctly without any layout/architecture refactor in the
surrounding dashboard shell.

```diff
-<div className="flex flex-col h-full w-full overflow-hidden md:hidden">
+<div className="flex flex-col h-[calc(100dvh-3.5rem)] w-full overflow-hidden md:hidden">

-<div className="hidden md:flex flex-col h-full w-full overflow-hidden">
+<div className="hidden md:flex flex-col h-[calc(100vh-3.5rem)] dark:h-[calc(100vh-6rem)] w-full overflow-hidden">
```

Trade-off: the editor knows the dashboard header is `3.5rem` (`6rem`
dark). The same numbers are already hardcoded in `sidebar-layout.tsx`,
so this isn't a new coupling.

### 2. Sandbox the email-preview iframes

`EmailPreviewContent` and `EmailPreviewEditableContent` rendered
user-authored template HTML in iframes with no `sandbox` at all. With
`srcDoc`-rendered iframes treated as same-origin by default, that meant
any `<script>` (or `onerror=`, `javascript:` URL, etc.) inside a
template could read the dashboard's cookies/localStorage and call the
API as the viewing admin.

Set `sandbox="allow-scripts"` on both iframes:
- Iframe is forced into a unique opaque origin → no access to parent
cookies, `localStorage`, `sessionStorage`, or DOM.
- No `allow-same-origin`, so credentialed fetches to the dashboard API
don't carry the user's session (cookies aren't sent to a third-party
opaque origin under default `SameSite=Lax`; cross-origin responses also
unreadable due to CORS).
- No `allow-top-navigation` / `allow-forms` / `allow-popups` → template
can't redirect the parent tab, submit forms, or open windows.
- `allow-scripts` is required so the inline scripts we inject
(link-click prevention; the WYSIWYG editor that drives the `postMessage`
flow at `email-preview.tsx:413-435` and `:625-672`) can actually run.
Without it, the editor itself was broken and links navigated freely.

Note: `allow-scripts allow-same-origin` together would be equivalent to
no sandbox at all (the iframe could rewrite its own `sandbox` attribute
and escape), so we deliberately omit `allow-same-origin`.

**Residual risk (not addressed in this PR):** a malicious template
script can still `postMessage` a fake `stack_edit_commit` to the parent
— the parent's `e.source === iframeWindow` check passes because the
script *is* running in that iframe. The viewing admin would silently
apply attacker-chosen source-code edits on save. That's a cross-admin
UI-redress concern, not token exfiltration, and is best fixed with a CSP
nonce on the injected script (so user template `<script>` tags can't run
at all). Tracking as a follow-up.

## Test plan

- [ ] Open an email template editor — verify the preview, code panel,
and chat panel are all visible at full height (light + dark mode).
- [ ] Same for an email theme editor and an email draft editor in the
`draft` stage.
- [ ] Resize the window vertically — editor should fill the viewport
below the header without overflowing past the bottom.
- [ ] Click a link inside the rendered preview — should not navigate
(link-click prevention script works under `allow-scripts`).
- [ ] In edit mode, hover an editable text region, click to edit, type a
change, hit ✓ — change should round-trip through `postMessage` and
update the source.
- [ ] Sanity check: paste `<script>document.title='pwned'</script>` (or
`<img onerror=...>`) into a template, render preview — parent tab
title/cookies/etc. should be untouched (script runs in opaque origin,
can't reach parent).
2026-05-04 15:37:33 -07:00
aadesh18
ed8961069c
fix(dashboard): UI bug fixes (#1377)
## Summary

Rolling PR for dashboard UI bug fixes. Each fix is appended to the **Fix
log** below with before/after screenshots. This PR stays open until we
batch-merge or split.

---

## Fix log

### 1. Hide Alpha/Beta stage badges in onboarding "Select apps" tooltip

**Bug:** On the new-project onboarding, hovering an app card showed an
"Alpha" or "Beta" stage badge next to the app name in the tooltip. These
shouldn't be surfaced on the onboarding step.

**Fix:** Removed the stage badge from the onboarding app-card tooltip
only. The "Required" badge is preserved, and stage badges on other
surfaces (app management, app store, command palette) are unchanged.

#### Before / After — Beta (Payments)

| Before | After |
| --- | --- |
|
![before-payments](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/before-hover-beta-payments.png)
|
![after-payments](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/after-hover-beta-payments.png)
|

#### Before / After — Alpha (Onboarding)

| Before | After |
| --- | --- |
|
![before-onboarding](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/before-hover-alpha-onboarding.png)
|
![after-onboarding](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/after-hover-alpha-onboarding.png)
|

---

### 2. Eliminate full-page flash when advancing onboarding steps

**Bug:** Moving between onboarding steps (e.g. Configure authentication
→ Select email theme) briefly blanked out the entire page — only the
navbar remained visible for roughly two seconds — before the next step
rendered. It felt like a complete browser reload.

**Fix:** Contained the suspension inside the wizard. A local Suspense
boundary around the onboarding page means that when any data cache
refresh fires during the step advance, the suspension no longer bubbles
up to the site-wide loading indicator. The step-advance state update is
also marked as a React transition, so the current step stays rendered
until the next step is ready to commit. Net effect: the previous step is
visible throughout the save, then the next step swaps in without a blank
frame.

#### Before — full blank flash mid-transition

| Auth step (start) | Mid-transition (blank) | Email theme step (end) |
| --- | --- | --- |
|
![before-auth](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/before-01-auth-step.png)
|
![before-flash](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/before-02-suspense-flash.png)
|
![before-email](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/before-03-email-theme-step.png)
|

#### After — previous step stays visible, no blank frame

| Auth step (start) | Mid-transition (auth stays visible) | Email theme
step (end) |
| --- | --- | --- |
|
![after-auth](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/after-suspense-01-auth-step.png)
|
![after-mid](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/after-suspense-02-mid-transition.png)
|
![after-email](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/after-suspense-03-email-step.png)
|

---

### 3. Add a subtle back arrow to the onboarding timeline

**Bug:** The only way to return to a previous step in the new-project
onboarding was to click one of the tiny completed-step dots at the
bottom of the page — not discoverable, and easy to miss.

**Fix:** Added a small muted left-arrow next to the timeline dots.
Clicking it advances back one step. It's absolute-positioned so the dots
stay perfectly centered, and it hides itself on the first step (where
there's nothing to go back to).

#### Before / After — Select apps step

| Before — dots only | After — back arrow next to the dots |
| --- | --- |
|
![before-back-arrow](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/before-back-arrow-apps.png)
|
![after-back-arrow](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/after-back-arrow-apps.png)
|

### 4. Unify onboarding step styling — cards everywhere, no
glassmorphism

**Bug:** Step-to-step styling in the onboarding was inconsistent. The
Config and Email-theme steps used a glassmorphic surround
(`backdrop-blur`, translucent whites) while the other steps used solid
cards. Advancing from auth to email made it look like the visual
language had changed mid-flow.

**Fix:** Dropped the glassmorphic variants from the onboarding wizard.
The config-choice option cards, the email-theme container, and the
`ModeNotImplementedCard` surround all now use the same solid card
treatment (`bg-white/90` light, `bg-white/[0.06]` dark, with subtle
ring). One consistent surface across every step.

#### Before / After — Config choice step

| Before — glassmorphic | After — solid card |
| --- | --- |
|
![before-glass-config](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/before-glass-config-choice.png)
|
![after-glass-config](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/after-glass-config-choice.png?v=2)
|

#### Before / After — Email theme step

| Before — glassmorphic | After — solid card |
| --- | --- |
|
![before-glass-email](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/before-glass-email-theme.png)
|
![after-glass-email](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/after-glass-email-theme.png)
|

### 5. Add "Copy prompt" button on the project setup page

**Bug:** The post-project-creation setup page surfaces a terminal
command for every framework (Next.js, React, JS, Python), but there was
no one-click handoff for users who drive their setup through an AI
agent. Users had to manually copy the command, figure out whether the
Stack Auth MCP server got registered, and add it themselves if not.

**Fix:** Added a compact **✦ Copy prompt** button at the top-right above
the steps list. Clicking it copies a framework-aware prompt to the
clipboard — the prompt tells the user's AI agent to run the install
command for the currently-selected framework, then verify the Stack Auth
MCP server (`stack-auth`, transport `http`,
`https://mcp.stack-auth.com/`) is registered in its client config and
add it manually if the install didn't.

#### Before / After — Project setup page

| Before — no AI handoff | After — "Copy prompt" at the top-right |
| --- | --- |
|
![before-copy-prompt](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/before-copy-prompt-setup.png)
|
![after-copy-prompt](https://gist.githubusercontent.com/aadesh18/948fc31499e8bca4943199173cbe0e00/raw/after-copy-prompt-setup.png)
|

### 6. Disable email theme cards while the onboarding step is saving

**Bug:** On the "Select an email theme" step, the theme cards stayed
clickable after clicking Continue. Because we keep the previous step
visible during the step-advance transition (fix #2), users could click
through to a different theme mid-save — the server would then commit
whatever selection was active at click time, not the one on screen when
Continue was pressed.

**Fix:** Added `disabled={saving}` to the email theme buttons, matching
the same pattern the config-choice, apps-selection, and auth-setup steps
already follow. Added `disabled:cursor-not-allowed disabled:opacity-60`
so users get a clear visual signal that the cards are locked while the
save is in flight.

---

<!-- Append new fixes above this line. Template:
### N. <title>
**Bug:** …
**Fix:** …
#### Before / After
| Before | After |
| --- | --- |
| ![before](…) | ![after](…) |
-->

## Test plan

- [ ] Load the new-project onboarding "Select apps" step and hover every
app card — no Alpha/Beta badge appears.
- [ ] Hover a required app — "Required" badge still appears.
- [ ] Confirm app management tooltips, app store detail page, and
command palette still show stage badges (out of scope for this PR).
- [ ] Drive the onboarding from Configure authentication to Select email
theme — the auth panel stays rendered throughout the save phase and the
email panel swaps in without the site-wide loading indicator or a blank
content area.
- [ ] Repeat for other step transitions (Config → Apps, Apps → Auth,
Email → Domain, Domain → Payments) — same seamless behavior.
- [ ] From any step after Config, the back arrow appears to the left of
the dots. Clicking it goes back one step. On the first step, the arrow
is not rendered.
- [ ] Walk through every onboarding step. Container surface is visually
consistent across steps — no glassmorphic/card mismatch between Config,
Apps, Auth, Email Theme, Payments.
- [ ] On the project setup page, the "Copy prompt" button appears above
the steps (top-right). Clicking it copies the prompt for the
currently-selected framework (Next.js / React / JS / Python) and shows a
success toast.
- [ ] On the "Select an email theme" step, click Continue — the three
theme cards become visibly dimmed (`opacity-60`, `cursor-not-allowed`)
for the duration of the save and don't respond to clicks. Once the next
step renders they stop being visible anyway.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Added back navigation to onboarding wizard steps.
* Added "Copy prompt" button for framework-aware terminal commands with
MCP verification.
  * Added loading indicator during asynchronous operations.

* **UI/UX Improvements**
  * Updated card styling for unselected options.
  * Disabled email theme selection during save operations.
  * Removed stage badges (Alpha/Beta) from app cards.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-28 18:49:28 -07:00
Mantra
65d87a4836
Dashboard: DataGrid refactor + layout (stacked on overview-revamp) (#1338)
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
DB migration compat / Check if migrations changed (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Runs E2E Fallback Tests / E2E Fallback Tests (Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Lint & build / lint_and_build (24) (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
DB migration compat / No migration changes (skipped) (push) Has been cancelled
## Summary

Stacked on `overview-revamp` (now rebased against `dev`). Introduces a
first-class `DataGrid` component in
`@stackframe/dashboard-ui-components`, migrates every dashboard table
off the legacy `DesignDataTable` / hand-rolled `<Table>` pattern to it,
and ships a matching dashboard design guide.

Since the last writeup the `DataGrid` runtime has been substantially
rewritten: the virtualizer now supports `rowHeight="auto"` with
`estimatedRowHeight`, every column can opt into `cellOverflow: "wrap"`,
the toolbar + header stick under a configurable `stickyTop`, and the
seeded dummy data has been fleshed out so the migrated surfaces render
with realistic density. The AI-analytics prompt was also extended with
full schema docs for the auth / team / email / payments tables so
natural-language queries produce better SQL.

**Base:** `dev` → **Head:** `ui-fixes-minor`
**Scope:** 39 files, ~+6.5k / -2.4k

## Screenshots

Captured against the seeded Demo Project on the local dashboard
(`admin@example.com` via mock GitHub OAuth). Viewport: **1920×1200**
(standard) and **2560×1440** (widescreen). Assets hosted in [this
gist](https://gist.github.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9).

### Overview — revamped metrics + line chart

| Light | Dark |
| --- | --- |
|
![overview-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/overview-light.jpg)
|
![overview-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/overview-dark.jpg)
|

Widescreen:

| Light | Dark |
| --- | --- |
|
![overview-wide-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/overview-wide-light.jpg)
|
![overview-wide-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/overview-wide-dark.jpg)
|

### Users — DataGrid with seeded rows

| Light | Dark |
| --- | --- |
|
![users-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/users-light.jpg)
|
![users-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/users-dark.jpg)
|

Widescreen:

| Light | Dark |
| --- | --- |
|
![users-wide-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/users-wide-light.jpg)
|
![users-wide-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/users-wide-dark.jpg)
|

### Transactions — new DataGridToolbar + sticky chrome

| Light | Dark |
| --- | --- |
|
![transactions-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/transactions-light.jpg)
|
![transactions-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/transactions-dark.jpg)
|

Widescreen:

| Light | Dark |
| --- | --- |
|
![transactions-wide-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/transactions-wide-light.jpg)
|
![transactions-wide-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/transactions-wide-dark.jpg)
|

### Teams

| Light | Dark |
| --- | --- |
|
![teams-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/teams-light.jpg)
|
![teams-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/teams-dark.jpg)
|

Widescreen:

| Light | Dark |
| --- | --- |
|
![teams-wide-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/teams-wide-light.jpg)
|
![teams-wide-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/teams-wide-dark.jpg)
|

### Email Outbox

| Light | Dark |
| --- | --- |
|
![email-outbox-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/email-outbox-light.jpg)
|
![email-outbox-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/email-outbox-dark.jpg)
|

Widescreen:

| Light | Dark |
| --- | --- |
|
![email-outbox-wide-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/email-outbox-wide-light.jpg)
|
![email-outbox-wide-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/email-outbox-wide-dark.jpg)
|

### Payments — Customers

| Light | Dark |
| --- | --- |
|
![payments-customers-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/payments-customers-light.jpg)
|
![payments-customers-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/payments-customers-dark.jpg)
|

Widescreen:

| Light | Dark |
| --- | --- |
|
![payments-customers-wide-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/payments-customers-wide-light.jpg)
|
![payments-customers-wide-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/payments-customers-wide-dark.jpg)
|

### Sticky behaviour — scrolled views

Grids scrolled down ~600px. The page header is still pinned, and the
`DataGrid` toolbar + column header row stay put under it (backdrop-blur
+ `stickyTop` offset) while the virtualized body rows scroll past.
Compare the scrolled view against the top-of-page view above.

| Page | Light | Dark |
| --- | --- | --- |
| Users |
![users-light-scrolled](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/users-light-scrolled.jpg)
|
![users-dark-scrolled](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/users-dark-scrolled.jpg)
|
| Teams |
![teams-light-scrolled](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/teams-light-scrolled.jpg)
|
![teams-dark-scrolled](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/teams-dark-scrolled.jpg)
|
| Transactions |
![transactions-light-scrolled](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/transactions-light-scrolled.jpg)
|
![transactions-dark-scrolled](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/transactions-dark-scrolled.jpg)
|
| Payments Customers |
![payments-customers-light-scrolled](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/payments-customers-light-scrolled.jpg)
|
![payments-customers-dark-scrolled](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/payments-customers-dark-scrolled.jpg)
|
| Email Outbox |
![email-outbox-light-scrolled](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/email-outbox-light-scrolled.jpg)
|
![email-outbox-dark-scrolled](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/email-outbox-dark-scrolled.jpg)
|
| Analytics Tables |
![analytics-tables-light-scrolled](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/analytics-tables-light-scrolled.jpg)
|
![analytics-tables-dark-scrolled](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/analytics-tables-dark-scrolled.jpg)
|

### Other migrated surfaces

| Page | Light | Dark |
| --- | --- | --- |
| Analytics Tables |
![analytics-tables-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/analytics-tables-light.jpg)
|
![analytics-tables-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/analytics-tables-dark.jpg)
|
| Emails |
![emails-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/emails-light.jpg)
|
![emails-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/emails-dark.jpg)
|
| Email Sent |
![email-sent-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/email-sent-light.jpg)
|
![email-sent-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/email-sent-dark.jpg)
|
| Domains |
![domains-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/domains-light.jpg)
|
![domains-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/domains-dark.jpg)
|
| Webhooks |
![webhooks-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/webhooks-light.jpg)
|
![webhooks-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/webhooks-dark.jpg)
|
| External DB Sync |
![external-db-sync-light](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/external-db-sync-light.jpg)
|
![external-db-sync-dark](https://gist.githubusercontent.com/mantrakp04/2fe05ddbb2d2d7cd2d237027c909c1b9/raw/external-db-sync-dark.jpg)
|

## What's new

### `DataGrid` in `@stackframe/dashboard-ui-components`

A new, fully-typed, fully-controlled grid component under
`packages/dashboard-ui-components/src/components/data-grid/`. Single
source of truth for tabular UI across the dashboard.

Package files:
- `data-grid.tsx` — main grid renderer (virtualized rows, sticky toolbar
+ header)
- `data-grid-toolbar.tsx` — built-in toolbar (search, columns, density,
export)
- `data-grid-sizing.ts` — column width / flex / min-width resolution
- `state.ts` — state helpers (`createDefaultDataGridState`, sort /
select / paginate utilities, `exportToCsv`, date formatters)
- `strings.ts` — i18n string table + `resolveDataGridStrings`
- `types.ts` — public types (`DataGridColumnDef`, `DataGridProps`,
`DataGridState`, `DataGridDataSource`, etc.)
- `use-data-source.ts` — `useDataSource` hook with `client` / `server` /
`infinite` modes
- `index.ts` — package entrypoint

Features:
- Controlled state (`state` + `onChange`) covering sorting, pagination,
column visibility, column widths, column pinning, selection,
date-display mode, and quick search.
- Column definitions with `string` / `number` / `date` / `dateTime` /
`boolean` / `singleSelect` / `custom` types, custom `renderCell`, custom
sort comparators, per-column `parseValue` / `dateFormat`, pinning,
align, flex / min / max width.
- **Cell overflow control** — new `cellOverflow: "truncate" | "wrap"`
per column. `"wrap"` + `rowHeight="auto"` lets rows grow to fit
multi-line content.
- **Dynamic row heights** — `rowHeight` now accepts `"auto"` with an
`estimatedRowHeight` hint for the virtualizer, eliminating
scroll-position jank while rows are still being measured.
- **Sticky chrome with `stickyTop`** — the toolbar and header stick
under a caller-provided offset (matching the page header height) with a
proper blur backdrop. See the _Sticky behaviour — scrolled views_
section above for the visual.
- Client-side sort + quick-search + pagination via `useDataSource` —
consumer never pre-sorts / paginates.
- Server-side and async-generator data sources for streaming / cursor
pagination.
- Paginated and infinite-scroll UI modes.
- CSV export + clipboard copy.
- Row single / multi selection with shift-range anchor.
- Row + cell click / double-click callbacks.
- Pluggable toolbar / footer / empty / loading states and i18n strings.

### Dashboard design guide

New `apps/dashboard/DESIGN-GUIDE.md`: prescriptive, AI-readable source
of truth for dashboard UI. Documents when to use each
`design-components` primitive, the `DataGrid` canonical pattern, color /
typography / spacing / motion rules, route-specific guidance, and the
migration priority. Now also documents the new `cellOverflow` and
dynamic-`rowHeight` patterns, and marks `DesignDataTable` as deprecated
in favor of `DataGrid` + `useDataSource` + `createDefaultDataGridState`.

### Overview page revamp


`apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/(overview)/line-chart.tsx`
— line chart rewritten on top of the shared `AnalyticsChart` /
`DonutChartDisplay` primitives, feeding the revamped Overview.

### Data-table migrations

Every shared table under `apps/dashboard/src/components/data-table/` has
been rewritten on top of `DataGrid`:

- `api-key-table.tsx`
- `payment-product-table.tsx`
- `permission-table.tsx`
- `team-member-search-table.tsx`
- `team-member-table.tsx`
- `team-search-table.tsx`
- `team-table.tsx`
- `transaction-table.tsx` — now also wires in `DataGridToolbar` with
search / column visibility
- `user-search-picker.tsx`
- `user-table.tsx` — extracted `USER_TABLE_COLUMNS` for readability /
reuse

### Page adoption

Page-level tables migrated to `DataGrid` (or the new `useDataSource` +
`createDefaultDataGridState` pattern):

- `(overview)/line-chart.tsx`
- `analytics/tables/query-data-grid.tsx` (now with sticky header)
- `domains/page-client.tsx`
- `email-drafts/[draftId]/page-client.tsx`
- `email-outbox/page-client.tsx` (with `DataGridToolbar`)
- `email-sent/page-client.tsx`, `grouped-email-table.tsx`,
`sent-emails-view.tsx`
- `emails/page-client.tsx`
- `external-db-sync/page-client.tsx`
- `payments/layout.tsx`, `payments/customers/page-client.tsx`,
`payments/products/[productId]/page-client.tsx`
- `users/[userId]/page-client.tsx`
- `webhooks/page-client.tsx`, `webhooks/[endpointId]/page-client.tsx`
- `design-language/page-client.tsx`,
`design-language/realistic-demo/page-client.tsx`
- `playground/page-client.tsx`

### Backend & supporting changes

- `apps/backend/src/lib/ai/prompts.ts` — extends the AI-analytics prompt
with detailed schema docs for `contact_channels`, `teams`,
`team_member_profiles`, `team_permissions`, `team_invitations`,
`email_outboxes`, `project_permissions`, `notification_preferences`,
`refresh_tokens`, and `connected_accounts`, so natural-language queries
have richer context to compile against.
- `apps/backend/src/lib/seed-dummy-data.ts` — additional OAuth providers
on seed users, improving dummy-data coverage for the migrated tables
(visible on the Users grid).
- `apps/dashboard/src/app/globals.css` — adds `--data-grid-sticky-top`
token used to derive the grid's sticky offset under the page header.
- `packages/template/src/dev-tool/dev-tool-core.ts` — persist the
"closed" state when the user closes the dev-tool panel so it doesn't
reopen on next load.

## Notes for reviewers

- Rebased onto latest `dev`; conflict in `api-key-table.tsx` resolved by
keeping the `DataGrid` implementation (consistent with the other
migrated tables).
- `DesignDataTable` is still in the codebase but marked deprecated in
the design guide — new code must use `DataGrid`.
- `DataGrid` is fully controlled: consumers must pass state + onChange,
must feed `rows` from `useDataSource` (never raw arrays), and must
define columns outside the component or via `useMemo`. The guide's §4.12
spells this out.
- `rowHeight="auto"` is opt-in; the default fixed-height virtualization
path is unchanged and remains the fast path for dense, single-line grids
(users, transactions, etc.).
- Screenshots are JPEG this round — the local capture tooling's PNG path
was producing blank frames, so the new set is `.jpg` end-to-end. Same
viewports, same seeded project.

## Test plan

- [ ] `pnpm lint` passes
- [ ] `pnpm typecheck` passes
- [ ] Load the dashboard and verify every migrated surface renders,
sorts, searches, paginates, and handles row-click navigation:
  - [ ] Overview (line chart + donut metrics)
- [ ] Users list + user detail (teams, sessions, permissions, API keys)
  - [ ] Teams list + team detail (members, permissions)
  - [ ] Domains
  - [ ] Emails, email-sent, email-outbox, email-drafts
  - [ ] Webhooks list + endpoint detail
  - [ ] Payments customers, product detail, transactions (new toolbar)
  - [ ] External DB sync
  - [ ] Analytics query table (sticky header)
- [ ] Verify infinite-scroll surfaces (domains, etc.) load additional
rows on scroll
- [ ] Verify sticky header stays below the page header in light and dark
themes
- [ ] Verify CSV export produces correct output on a representative
table
- [ ] Verify column resize, visibility toggle, and sort work across
themes
- [ ] Verify `cellOverflow: "wrap"` rows grow to fit when
`rowHeight="auto"` and clip when `rowHeight` is numeric
- [ ] Spot-check AI analytics queries against the new schema context
(contact_channels, teams, email_outboxes, …)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **New Features**
* Unified table components across dashboard with improved infinite
pagination and quick search.

* **Improvements**
* Enhanced table performance with sticky headers and better row height
handling.
* Improved sorting, filtering, and data loading with consistent state
management.
  * Better visual consistency across all data grids and table layouts.

* **UI/Styling**
* Refined table styling for better text truncation and content wrapping.
  * Optimized layout spacing and alignment across dashboard tables.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Developing-Gamer <maxcodes11110@gmail.com>
Co-authored-by: Armaan Jain <84474476+Developing-Gamer@users.noreply.github.com>
Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
2026-04-27 13:50:24 -07:00
Konstantin Wohlwend
22ae47fe73 Replace Cmd with Ctrl on Windows computers 2026-04-17 17:04:30 -07:00
Armaan Jain
94dd22c1c5
Overview revamp (#1238) 2026-04-15 09:36:00 -07:00
Armaan Jain
654c97c56e
Onboarding redo (#1308) 2026-04-15 09:35:48 -07:00
BilalG1
c66bdfb5ae
Fix five dashboard UI issues (#1337)
## Summary

Fixes five independent UI bugs in the dashboard. Each is a narrow,
localized fix — no changes to shared table / card primitives.

### 1. Auth methods preview didn't update until save
Toggling Email/password, Magic link, or Passkey updated the switch UI
but the right-hand sign-in preview kept rendering the pre-save config
until "Save changes" was clicked. The preview was reading
`project.config` instead of the local pending state.

**Fix:** pass the computed local state (`passwordEnabled`, `otpEnabled`,
`passkeyEnabled`) into `AuthPage`'s `mockProject.config` so the preview
reflects toggles immediately.

| Before | After |
|---|---|
|
![before](b6d4f39f66/01-auth-methods-before.gif)
|
![after](b6d4f39f66/01-auth-methods-after.gif)
|

---

### 2. Email-drafts "New Draft" dropdown items stacked on two rows
Icon rendered above text in the dropdown because the icon was a child of
a non-flex inner wrapper inside `DropdownMenuItem` and phosphor icons
default to `display: block`.

**Fix:** use `DropdownMenuItem`'s built-in `icon` prop (which
absolute-positions the icon) instead of passing it as a child.

| Before | After |
|---|---|
|
![before](b6d4f39f66/02-email-drafts-before.png)
|
![after](b6d4f39f66/02-email-drafts-after.png)
|

---

### 3. Project-keys status filter: clicking options did nothing visible
`DesignDataTable` renders the toolbar outside the card when
`glassmorphic && !insideDesignCard`. The table instance was captured
once via `onTableReady`; filter clicks updated the table's internal
state (rows actually filtered to "No results") but the toolbar's parent
never re-rendered, so checkboxes, chip count, and button label stayed
frozen.

**Fix:** wrap `InternalApiKeyTable` in `DesignCard` so
`useInsideDesignCard()` returns true, `needsOwnCard` becomes false, and
the toolbar renders inside the `DataTable` where it re-renders normally.
No changes to the shared `DesignDataTable` component.

| Before | After |
|---|---|
|
![before](b6d4f39f66/03-project-keys-before.gif)
|
![after](b6d4f39f66/03-project-keys-after.gif)
|

---

### 4. Analytics "Tables" page only listed Events
`AVAILABLE_TABLES` was hardcoded to a single entry.

**Fix:** registered all 12 ClickHouse views that exist in the `default`
schema (events, users, contact_channels, teams, team_member_profiles,
team_permissions, team_invitations, email_outboxes, project_permissions,
notification_preferences, refresh_tokens, connected_accounts) with
sensible default sort columns. Widened `TableId` to `string`.

| Before | After |
|---|---|
|
![before](b6d4f39f66/04-analytics-tables-before.png)
|
![after](b6d4f39f66/04-analytics-tables-after.png)
|

---

### 5. Price input `$` prefix overlapped the number on prod
The Input composed `h-9 px-3 ... pl-7`. In production's CSS bundle order
`.px-3` declared after `.pl-7`, so `padding-left` resolved to 12px —
same as the prefix's `left-3` position — making `$` overlap the first
digit. The emulator's bundle happened to order them the other way, which
is why it only reproduced in prod. Verified with a devtools injection
that mimics the prod CSS ordering.

**Fix:** change `pl-7` → `!pl-7` in `repeating-input.tsx` so the prefix
padding wins regardless of CSS order.

| Before (prod CSS ordering) | After (same ordering) |
|---|---|
|
![before](b6d4f39f66/05-price-overlap-before.png)
|
![after](b6d4f39f66/05-price-overlap-after.png)
|

---

## Test plan

- [x] `pnpm --filter @stackframe/dashboard typecheck`
- [x] `pnpm --filter @stackframe/dashboard lint`
- [x] Manual verification of each issue against the local dev dashboard
at localhost:8101
- [ ] Reviewer: confirm no visual regressions on other `DesignDataTable`
usages (api-key-table is the only one wrapped here)
- [ ] Reviewer: confirm analytics queries on added tables work with the
signed-in user's permissions


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

## Release Notes

* **New Features**
* Added 12 new analytics tables to the dashboard for enhanced data
visibility and tracking.

* **Bug Fixes**
  * Fixed input styling issue with prefix alignment.

* **Style**
* Improved visual presentation of data tables with enhanced card
styling.
  * Refined dropdown menu icon display for better UI consistency.
* Enhanced authentication preview settings to reflect current
configuration state.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-14 19:38:52 -07:00
BilalG1
88d3317b22
local emulator security and features fixes (#1247)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

* **New Features**
* Added Stripe, OAuth, and Freestyle mock services to the local emulator
* Introduced `emulator run` CLI command to execute applications with
emulator credentials automatically injected
  * Enhanced credential management for local development

* **Improvements**
  * Improved ARM64 QEMU emulation with cross-architecture support
  * Better error detection and logging during emulator provisioning
  * Added example middleware configuration with authentication support
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-14 15:36:24 -07:00
Mantra
55b14bb409
dev tool indicator (#1272)
- Updated package versions for '@supabase/*' libraries to 2.99.2 and
'@supabase/ssr' to 0.9.0.
- Added new devDependencies for 'rimraf' and 'framer-motion' in the
pnpm-lock file.
- Modified Next.js configuration to conditionally omit 'X-Frame-Options'
in development mode for better integration with Stack Auth dev tools.
- Refactored component exports in the template package to include
tracking for dev tools.
- Introduced new dev tool components and context for improved logging
and state management.
- Added styles for the dev tool indicator and panel, ensuring a
consistent dark theme.
- Implemented fetch interception to log API calls and user
authentication events in the dev tool.

<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Release Notes

* **New Features**
* Added comprehensive Developer Tools interface with tabs for Overview,
Components, AI Chat, Console, Dashboard, and Support.
* Integrated AI Chat assistant within Developer Tools for enhanced
debugging.
  * Added component version tracking and update notifications.
  * Implemented API request logging and event monitoring.
* Enhanced feedback system with support for bug reports and feature
requests.

* **Bug Fixes**
* Fixed Content Security Policy headers for local development
environments.

* **Dependencies**
  * Added AI SDK integration packages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>
2026-04-13 17:43:03 -07:00
aadesh18
5573927429
Ask AI Huge Response (#1328)
This PR fixes the bug where analytics tool returns a lot of rows, which
results in huge token count. We do it by checking the number of
characters in the tool call, and if it is more than 50000 characters, we
send an error message rather than the rows and ask the ai to make more
focused queries.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* AI assistant shows friendlier, categorized error messages and captures
unexpected errors for diagnosis.
  * UI now displays classifier-derived, user-friendly AI error text.

* **Bug Fixes & Improvements**
* Enforced a hard size budget for SQL query results and gracefully
handles oversized responses.
* Centralized safer database error messaging to avoid leaking internal
details.
* Strengthened AI guidance to prefer narrower queries, safer column
selection, and pairing GROUP BY with ORDER BY + LIMIT.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-13 15:12:07 -07:00
aadesh18
8aa80ceb2c
AI in Stack Companion (#1297)
This PR puts the ask ai functionality into the ai stack companion, along
with persistent history.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* "Ask AI" chat sidebar with streaming assistant responses, progressive
word-by-word reveal, auto-scroll, Enter-to-send and Arrow-key
navigation, "Thinking…" and error indicators
* Chat UI primitives: inline/code blocks, smart links, copy-to-clipboard
for code/URLs, and expandable tool-result cards with copyable outputs

* **Bug Fixes**
* Prevented button/menu clicks inside list items from bubbling to parent
row handlers

* **Refactor**
* Chat rendering, streaming, parsing, and UI helpers consolidated into a
shared module and integrated into the sidebar widget
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
2026-04-13 18:40:32 +00:00
aadesh18
a0710f8807
fixed delete bug and removed console statements (#1313) 2026-04-08 17:14:30 -07:00
BilalG1
4f99c469fe
stack auth preview mode (#1307)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Preview mode: sandboxed experience with mock projects, placeholder
data, and disabled external integrations (payments, webhooks, email
rendering, session replays).
* One-click preview project creation and automatic preview sign-in for
quick access.

* **New Features — Walkthrough**
* Interactive guided walkthroughs with spotlight, animated cursor,
step-driven navigation, and targeted element hooks.

* **Style**
* UI/UX adjustments for preview: theme behavior, conditional
banners/alerts, informational alerts, and walkthrough attributes added
across pages.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-08 16:57:42 -07:00
Konstantin Wohlwend
add0d56be0 More CMD+K actions 2026-04-06 09:12:14 -07:00
Konstantin Wohlwend
9b1284dc9e Fraud Protection sub-app 2026-04-05 21:35:01 -07:00
Konstantin Wohlwend
d3ea2b9001 Add server-side flags for anonymous users 2026-04-03 10:43:34 -07:00
Konstantin Wohlwend
73f68c831f "Only anonymous" users filter
Some checks failed
all-good: Did all the other checks pass? / all-good (push) Has been cancelled
Ensure Prisma migrations are in sync with the schema / check_prisma_migrations (22.x) (push) Has been cancelled
Docker Server Build and Push / Docker Build and Push Server (push) Has been cancelled
Docker Server Build and Run / docker (push) Has been cancelled
Runs E2E API Tests (Local Emulator) / E2E Tests (Local Emulator, Node ${{ matrix.node-version }}) (22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (mock, 22.x) (push) Has been cancelled
Runs E2E API Tests / E2E Tests (Node ${{ matrix.node-version }}, Freestyle ${{ matrix.freestyle-mode }}) (prod, 22.x) (push) Has been cancelled
Runs E2E API Tests with custom port prefix / build (22.x) (push) Has been cancelled
Lint & build / lint_and_build (latest) (push) Has been cancelled
Mirror main branch to main-mirror-for-wdb / lint_and_build (push) Has been cancelled
Publish npm packages / publish (push) Has been cancelled
Dev Environment Test With Custom Base Port / restart-dev-and-test-with-custom-base-port (push) Has been cancelled
Dev Environment Test / restart-dev-and-test (push) Has been cancelled
Run setup tests with custom base port / setup-tests-with-custom-base-port (push) Has been cancelled
Run setup tests / setup-tests (push) Has been cancelled
Sync Main to Dev / sync-commits (push) Has been cancelled
TOC Generator / TOC Generator (push) Has been cancelled
2026-04-03 09:54:29 -07:00
Mantra
998b3662a5
Enhance error handling and logging in Emailable response validation (#1292)
- Updated `validateVerifyResponse` to capture errors for invalid or
unexpected responses.
- Improved handling of malformed responses in `checkEmailWithEmailable`,
ensuring a consistent return structure.
- Refactored `getDerivedSignUpCountryCode` to log errors for non-ISO
country codes.
- Simplified country code determination logic in
`createOrUpgradeAnonymousUserWithRules`.

<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Replaced country code dropdown selection with a direct text input
field for simpler data entry.
* Updated country code validation to accept any 2-letter code format,
improving flexibility.

* **Bug Fixes**
* Refined country code normalization logic across sign-up rules and user
profile pages for consistency.

* **Documentation**
* Clarified country code field messaging from "ISO code" to "2-letter
country code" terminology for better user guidance.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-04-01 09:21:43 -07:00
Mantra
cfa6204c2d
Replace Web3Forms with internal feedback emails (#1244)
## Summary
- replace the dashboard feedback form's Web3Forms submission with an
authenticated internal backend endpoint
- send support and feature-request notifications through Stack Auth's
native internal email pipeline
- share internal project auth headers in the dashboard and add backend
E2E coverage for support feedback

## Testing
- pnpm typecheck
- pnpm lint -- "src/components/feedback-form.tsx"
"src/components/stack-companion/feature-request-board.tsx"

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Internal feedback submission endpoint with automated internal email
notifications
* New internal email builder and sending utility; recipient list
configurable via env

* **Enhancements**
* Feedback form requires sign-in, disables submit when unauthenticated,
and tightens validation
  * Centralized header helper for authenticated internal requests
* Feature request board gates actions for signed-out users and improves
upvote/submit reliability
* Runtime retrieval/validation of the feature-tracking API key and
streamlined user handling

* **Tests**
* End-to-end tests covering internal feedback flows, validation, and
email delivery
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-23 17:07:37 -07:00