Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions apps/server/src/cloud/cliAuthHtml.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
import { expect, it } from "@effect/vitest";

import { renderLoopbackAuthorizationCompleteHtml } from "./cliAuthHtml.ts";
import {
renderLoopbackAuthorizationCompleteHtml,
resolveLoopbackAuthorizationStage,
} from "./cliAuthHtml.ts";

it("renders the branded loopback authorization completion page", () => {
const html = renderLoopbackAuthorizationCompleteHtml();

expect(html).toContain("T3 Code");
expect(resolveLoopbackAuthorizationStage()).toBe("dev");
expect(html).toContain("T3 Code (Dev)");
expect(html).toContain('class="stage stage-dev"');
expect(html).not.toContain("Secure terminal handoff");
expect(html).toContain("You're connected");
expect(html).toContain("return to the terminal");
expect(html).toContain("Return to your terminal");
expect(html).not.toContain('class="next"');
expect(html).toContain('name="viewport"');
expect(html).not.toContain('class="status"');
});

it("renders the matching header treatment for each release channel", () => {
const nightly = renderLoopbackAuthorizationCompleteHtml("nightly");
const latest = renderLoopbackAuthorizationCompleteHtml("latest");

expect(nightly).toContain("T3 Code (Nightly)");
expect(nightly).toContain('class="stage stage-nightly"');
expect(latest).toContain('<p class="brand">T3 Code</p>');
expect(latest).not.toContain("(Latest)");
expect(latest).toContain('class="stage stage-latest"');
});
85 changes: 50 additions & 35 deletions apps/server/src/cloud/cliAuthHtml.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,22 @@
export function renderLoopbackAuthorizationCompleteHtml(): string {
export type LoopbackAuthorizationStage = "dev" | "nightly" | "latest";

declare const __T3CODE_BUILD_CHANNEL__: "nightly" | "latest" | undefined;

export function resolveLoopbackAuthorizationStage(): LoopbackAuthorizationStage {
return typeof __T3CODE_BUILD_CHANNEL__ === "undefined" ? "dev" : __T3CODE_BUILD_CHANNEL__;
}

const stageBrands = {
dev: "T3 Code (Dev)",
nightly: "T3 Code (Nightly)",
latest: "T3 Code",
} as const satisfies Record<LoopbackAuthorizationStage, string>;

export function renderLoopbackAuthorizationCompleteHtml(
stage: LoopbackAuthorizationStage = resolveLoopbackAuthorizationStage(),
): string {
const stageBrand = stageBrands[stage];

return `<!doctype html>
<html lang="en">
<head>
Expand Down Expand Up @@ -38,9 +56,16 @@ export function renderLoopbackAuthorizationCompleteHtml(): string {
overflow: hidden;
padding: 22px 24px;
color: white;
}
.stage-latest {
background:
radial-gradient(circle at 76% 18%, rgba(136, 204, 255, 0.52), transparent 38%),
linear-gradient(135deg, #2468df, #172f82);
}
.stage-dev {
background: linear-gradient(145deg, #5ab8fa 0%, #347ff8 46%, #1939bd 100%);
}
.stage::before {
.stage-dev::before {
content: "";
position: absolute;
inset: 0;
Expand All @@ -52,6 +77,22 @@ export function renderLoopbackAuthorizationCompleteHtml(): string {
linear-gradient(90deg, rgba(234, 246, 255, 0.12) 1px, transparent 1px);
background-size: 32px 32px, 32px 32px, 8px 8px, 8px 8px;
}
.stage-nightly {
background:
radial-gradient(22rem 8rem at 78% 18%, rgba(81, 101, 216, 0.42), transparent 58%),
linear-gradient(145deg, #07152f 0%, #151443 52%, #32155b 100%);
}
.stage-nightly::before {
content: "";
position: absolute;
inset: 0;
opacity: 0.78;
background-image:
radial-gradient(circle at 12px 12px, rgba(228, 234, 255, 0.9) 0 1px, transparent 1.5px),
radial-gradient(circle at 38px 28px, rgba(228, 234, 255, 0.58) 0 0.8px, transparent 1.3px),
radial-gradient(circle at 58px 9px, rgba(200, 215, 255, 0.72) 0 0.9px, transparent 1.4px);
background-size: 72px 48px, 96px 64px, 128px 56px;
}
.stage::after {
content: "";
position: absolute;
Expand All @@ -63,6 +104,10 @@ export function renderLoopbackAuthorizationCompleteHtml(): string {
.stage-content {
position: relative;
z-index: 1;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
.brand {
margin: 0;
Expand All @@ -73,21 +118,6 @@ export function renderLoopbackAuthorizationCompleteHtml(): string {
}
.brand { color: rgba(255, 255, 255, 0.82); }
.content { padding: 30px 32px 34px; }
.status {
width: 42px;
height: 42px;
display: grid;
place-items: center;
margin-top: -52px;
margin-bottom: 22px;
border: 4px solid white;
border-radius: 50%;
background: #2065df;
color: white;
font-size: 22px;
font-weight: 700;
box-shadow: 0 8px 22px rgba(25, 72, 177, 0.3);
}
.eyebrow {
margin: 0 0 8px;
color: #2866cc;
Expand All @@ -98,41 +128,26 @@ export function renderLoopbackAuthorizationCompleteHtml(): string {
}
h1 { margin: 0; font-size: clamp(26px, 5vw, 34px); line-height: 1.12; letter-spacing: -0.035em; }
.description { margin: 12px 0 0; color: #646975; font-size: 15px; line-height: 1.6; }
.next {
margin-top: 24px;
padding: 14px 16px;
border: 1px solid rgba(23, 25, 31, 0.1);
border-radius: 12px;
background: #f7f8fa;
color: #454a55;
font-size: 13px;
}
.next strong { color: #17191f; }
@media (prefers-color-scheme: dark) {
:root { background: #101115; color: #f1f3f7; }
body { background: radial-gradient(48rem 22rem at 50% -8rem, rgba(55, 102, 210, 0.2), transparent), #101115; }
main { border-color: rgba(255, 255, 255, 0.1); background: rgba(25, 27, 33, 0.96); }
.status { border-color: #191b21; }
.eyebrow { color: #77a8ff; }
.description { color: #a8adb8; }
.next { border-color: rgba(255, 255, 255, 0.1); background: #20232a; color: #b9bec8; }
.next strong { color: #f1f3f7; }
}
</style>
</head>
<body>
<main>
<header class="stage">
<header class="stage stage-${stage}" data-stage="${stage}">
<div class="stage-content">
<p class="brand">T3 Code</p>
<p class="brand">${stageBrand}</p>
</div>
</header>
<section class="content">
<div class="status" aria-hidden="true">✓</div>
<p class="eyebrow">Browser authorization complete</p>
<h1>You're connected</h1>
<p class="description">The authorization code was delivered securely to your waiting terminal.</p>
<div class="next"><strong>Next:</strong> return to the terminal to finish T3 Connect setup. You can close this window.</div>
<p class="description">Return to your terminal to finish setting up T3 Connect. You can close this window.</p>
</section>
</main>
</body>
Expand Down
3 changes: 3 additions & 0 deletions apps/server/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { defineConfig, mergeConfig } from "vite-plus";

import baseConfig from "../../vite.config.ts";
import { loadRepoEnv } from "../../scripts/lib/public-config.ts";
import packageJson from "./package.json" with { type: "json" };

const bundledPackagePrefixes = [
"@pierre/diffs",
Expand All @@ -16,6 +17,7 @@ export function shouldBundleCliDependency(id: string): boolean {
}

const repoEnv = loadRepoEnv();
const cliBuildChannel = packageJson.version.includes("-nightly.") ? "nightly" : "latest";

export default mergeConfig(
baseConfig,
Expand All @@ -42,6 +44,7 @@ export default mergeConfig(
js: "#!/usr/bin/env node\n",
},
define: {
__T3CODE_BUILD_CHANNEL__: JSON.stringify(cliBuildChannel),
__T3CODE_BUILD_RELAY_URL__: JSON.stringify(repoEnv.T3CODE_RELAY_URL?.trim() ?? ""),
__T3CODE_BUILD_CLERK_PUBLISHABLE_KEY__: JSON.stringify(
repoEnv.T3CODE_CLERK_PUBLISHABLE_KEY?.trim() ?? "",
Expand Down
4 changes: 4 additions & 0 deletions apps/web/src/branding.logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ export function formatAppDisplayName(input: {
readonly baseName: string;
readonly stageLabel: string;
}): string {
if (input.stageLabel.trim().toLowerCase() === "latest") {
return input.baseName;
}

return `${input.baseName} (${input.stageLabel})`;
}

Expand Down
11 changes: 11 additions & 0 deletions apps/web/src/branding.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@ describe("branding", () => {
expect(branding.APP_DISPLAY_NAME).toBe("T3 Code (Nightly)");
});

it("does not label the latest hosted app channel", async () => {
vi.stubEnv("VITE_HOSTED_APP_CHANNEL", "latest");

const branding = await import("./branding");

expect(branding.HOSTED_APP_CHANNEL).toBe("latest");
expect(branding.HOSTED_APP_CHANNEL_LABEL).toBe("Latest");
expect(branding.APP_STAGE_LABEL).toBe("Latest");
expect(branding.APP_DISPLAY_NAME).toBe("T3 Code");
});

it("ignores unknown hosted app channels", async () => {
vi.stubEnv("VITE_HOSTED_APP_CHANNEL", "preview");

Expand Down
Loading