Skip to content
Closed
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
Binary file modified apps/web/public/apple-touch-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/web/public/favicon-16x16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/web/public/favicon-32x32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified apps/web/public/favicon.ico
Binary file not shown.
19 changes: 19 additions & 0 deletions apps/web/src/branding.logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@ export function formatAppDisplayName(input: {
return `${input.baseName} (${input.stageLabel})`;
}

export function formatDistributionBadgeLabel(input: {
readonly distributionName: string;
readonly stageLabel: string;
readonly stageIdentifiedByArtwork: boolean;
}): string {
if (input.stageIdentifiedByArtwork) {
return input.distributionName;
}

const normalizedStageLabel = input.stageLabel.trim().toLowerCase();
if (normalizedStageLabel === "dev") {
return `${input.distributionName} Dev`;
}
if (normalizedStageLabel === "nightly") {
return `${input.distributionName} Nightly`;
}
return input.distributionName;
}

/**
* Whether the sidebar v2 beta is on by default for a build stage.
*
Expand Down
31 changes: 31 additions & 0 deletions apps/web/src/branding.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { afterEach, describe, expect, it, vi } from "vite-plus/test";
import {
formatDistributionBadgeLabel,
resolveServerBackedAppDisplayName,
resolveServerBackedAppStageLabel,
resolveSidebarV2Default,
Expand Down Expand Up @@ -74,6 +75,36 @@ describe("branding", () => {
});

describe("branding logic", () => {
it("keeps fork identity visible while artwork carries the build stage", () => {
expect(
formatDistributionBadgeLabel({
distributionName: "yngatech",
stageLabel: "Nightly",
stageIdentifiedByArtwork: true,
}),
).toBe("yngatech");
});

it.each(["Nightly", "Dev"])("adds the %s stage when artwork is not visible", (stageLabel) => {
expect(
formatDistributionBadgeLabel({
distributionName: "yngatech",
stageLabel,
stageIdentifiedByArtwork: false,
}),
).toBe(`yngatech ${stageLabel}`);
});

it("does not add stable labels to the distribution badge", () => {
expect(
formatDistributionBadgeLabel({
distributionName: "yngatech",
stageLabel: "Latest",
stageIdentifiedByArtwork: false,
}),
).toBe("yngatech");
});

it("returns Nightly for nightly primary server versions", () => {
expect(
resolveServerBackedAppStageLabel({
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/branding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,7 @@ export const APP_STAGE_LABEL =
export const APP_DISPLAY_NAME =
injectedDesktopAppBranding?.displayName ??
formatAppDisplayName({ baseName: APP_BASE_NAME, stageLabel: APP_STAGE_LABEL });
export const APP_DISTRIBUTION_NAME = "yngatech";
export const APP_REPOSITORY = "yngatech/t3code";
export const APP_VERSION = import.meta.env.APP_VERSION || "0.0.0";
export const APP_COMMIT_HASH = import.meta.env.APP_COMMIT_HASH || "unknown";
58 changes: 31 additions & 27 deletions apps/web/src/components/SidebarStageBackdrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ export function useSidebarStageBackdropVariant(enabled = true): SidebarStageBack
return resolveSidebarStageBackdropVariant(useEnvironmentStageLabel(), enabled);
}

/** Stage-channel header art; palettes mirror the per-channel app icons in `assets/`. */
/**
* Stage-channel header art; palettes mirror the per-channel app icons in
* `assets/`. Same geometry as upstream, but this yngatech fork keys the
* palettes to a petrol/teal colorway as the distribution signature.
*/
export function SidebarStageBackdrop({ variant }: { variant: SidebarStageBackdropVariant }) {
return (
<div
Expand Down Expand Up @@ -120,9 +124,9 @@ function NightlySkyArt() {
gradientUnits="userSpaceOnUse"
spreadMethod="reflect"
>
<stop stopColor="#07152F" />
<stop offset="0.5" stopColor="#151443" />
<stop offset="1" stopColor="#32155B" />
<stop stopColor="#05191F" />
<stop offset="0.5" stopColor="#0A2B34" />
<stop offset="1" stopColor="#10484F" />
</linearGradient>
<radialGradient
id={glowId}
Expand All @@ -132,20 +136,20 @@ function NightlySkyArt() {
gradientTransform="translate(216 18) rotate(137) scale(120 84)"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#5165D8" stopOpacity="0.4" />
<stop offset="0.5" stopColor="#283075" stopOpacity="0.16" />
<stop offset="1" stopColor="#111635" stopOpacity="0" />
<stop stopColor="#38B9C6" stopOpacity="0.4" />
<stop offset="0.5" stopColor="#1A5764" stopOpacity="0.16" />
<stop offset="1" stopColor="#0A242C" stopOpacity="0" />
</radialGradient>
<linearGradient id={cloudId} x1="0" y1="60" x2="288" y2="96" gradientUnits="userSpaceOnUse">
<stop stopColor="#4EA4FF" stopOpacity="0.5" />
<stop offset="0.52" stopColor="#696FEA" stopOpacity="0.62" />
<stop offset="1" stopColor="#A85BEA" stopOpacity="0.5" />
<stop stopColor="#4EDCE2" stopOpacity="0.5" />
<stop offset="0.52" stopColor="#38A9B6" stopOpacity="0.62" />
<stop offset="1" stopColor="#5FE0BF" stopOpacity="0.5" />
</linearGradient>
<filter id={softId} x="-24" y="-24" width="336" height="144" filterUnits="userSpaceOnUse">
<feGaussianBlur stdDeviation="4" />
</filter>
<pattern id={starsId} width="288" height="96" patternUnits="userSpaceOnUse">
<g fill="#E4EAFF">
<g fill="#E0FBF7">
{NIGHTLY_STARS.map((star) => (
<circle
key={`${star.cx}-${star.cy}`}
Expand All @@ -156,7 +160,7 @@ function NightlySkyArt() {
/>
))}
</g>
<g stroke="#C8D7FF" strokeLinecap="round" strokeOpacity="0.7" strokeWidth="0.6">
<g stroke="#C4F2EA" strokeLinecap="round" strokeOpacity="0.7" strokeWidth="0.6">
{NIGHTLY_SPARKLES.map((sparkle) => (
<g key={`${sparkle.x}-${sparkle.y}`}>
<path d={`M${sparkle.x - 1.5} ${sparkle.y}H${sparkle.x + 1.5}`} />
Expand Down Expand Up @@ -233,9 +237,9 @@ function DevBlueprintArt() {
gradientTransform="translate(216 14) rotate(137) scale(120 84)"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#D4F6FF" stopOpacity="0.4" />
<stop offset="0.52" stopColor="#65C8FF" stopOpacity="0.16" />
<stop offset="1" stopColor="#276AF1" stopOpacity="0" />
<stop stopColor="#DFFFFA" stopOpacity="0.4" />
<stop offset="0.52" stopColor="#5FE3D6" stopOpacity="0.16" />
<stop offset="1" stopColor="#1B8FA0" stopOpacity="0" />
</radialGradient>
<radialGradient
id={celesteGlowId}
Expand All @@ -245,9 +249,9 @@ function DevBlueprintArt() {
gradientTransform="translate(474 44) rotate(166) scale(156 92)"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#D2FFFF" stopOpacity="0.34" />
<stop offset="0.5" stopColor="#48DCF5" stopOpacity="0.18" />
<stop offset="1" stopColor="#277EF1" stopOpacity="0" />
<stop stopColor="#D9FFF5" stopOpacity="0.34" />
<stop offset="0.5" stopColor="#48F0DC" stopOpacity="0.18" />
<stop offset="1" stopColor="#1FA0A0" stopOpacity="0" />
</radialGradient>
<radialGradient
id={violetGlowId}
Expand All @@ -257,20 +261,20 @@ function DevBlueprintArt() {
gradientTransform="translate(704 18) rotate(145) scale(132 88)"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#D9D8FF" stopOpacity="0.3" />
<stop offset="0.52" stopColor="#7C8BFF" stopOpacity="0.14" />
<stop offset="1" stopColor="#3155DF" stopOpacity="0" />
<stop stopColor="#DCFFEF" stopOpacity="0.3" />
<stop offset="0.52" stopColor="#6FE3C0" stopOpacity="0.14" />
<stop offset="1" stopColor="#1E9C86" stopOpacity="0" />
</radialGradient>
<pattern id={minorGridId} width="8" height="8" patternUnits="userSpaceOnUse">
<path d="M8 0H0V8" stroke="#EAF6FF" strokeOpacity="0.14" strokeWidth="0.5" />
<path d="M8 0H0V8" stroke="#EAFFFB" strokeOpacity="0.14" strokeWidth="0.5" />
</pattern>
<pattern id={majorGridId} width="32" height="32" patternUnits="userSpaceOnUse">
<path d="M32 0H0V32" stroke="#EAF6FF" strokeOpacity="0.26" strokeWidth="0.6" />
<path d="M32 0H0V32" stroke="#EAFFFB" strokeOpacity="0.26" strokeWidth="0.6" />
</pattern>
<pattern id={rulerId} width="32" height="6" patternUnits="userSpaceOnUse">
<path
d="M4 0V2.5M12 0V2.5M20 0V4M28 0V2.5"
stroke="#DDF7FF"
stroke="#DDFBF5"
strokeOpacity="0.5"
strokeWidth="0.5"
/>
Expand All @@ -281,7 +285,7 @@ function DevBlueprintArt() {
<rect width="768" height="96" fill={`url(#${violetGlowId})`} />
</pattern>
<pattern id={annotationsId} width="768" height="96" patternUnits="userSpaceOnUse">
<g stroke="#DDF7FF" strokeLinecap="round" strokeOpacity="0.6" strokeWidth="0.7">
<g stroke="#DDFBF5" strokeLinecap="round" strokeOpacity="0.6" strokeWidth="0.7">
<path d="M180 64H264" strokeDasharray="5 4" />
<path d="M180 61V67M264 61V67" />
<path d="M276 10V44" strokeDasharray="4 4" strokeOpacity="0.5" />
Expand All @@ -294,7 +298,7 @@ function DevBlueprintArt() {
<path d="M590 67V73M724 67V73" strokeOpacity="0.55" />
</g>

<g stroke="#DDF7FF" strokeLinecap="round" strokeOpacity="0.55" strokeWidth="0.6">
<g stroke="#DDFBF5" strokeLinecap="round" strokeOpacity="0.55" strokeWidth="0.6">
<g>
<path d="M34 60L38 64M38 60L34 64" />
</g>
Expand All @@ -318,7 +322,7 @@ function DevBlueprintArt() {
</g>
</g>

<g stroke="#DDF7FF" strokeOpacity="0.35" strokeWidth="0.6">
<g stroke="#DDFBF5" strokeOpacity="0.35" strokeWidth="0.6">
<circle cx="196" cy="38" r="13" strokeDasharray="3.5 4" />
<path d="M196 33V43M191 38H201" strokeOpacity="0.6" strokeWidth="0.4" />
<circle cx="414" cy="64" r="10" strokeDasharray="2.5 3.5" />
Expand Down
32 changes: 31 additions & 1 deletion apps/web/src/components/settings/SettingsPanels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,13 @@ import { createModelSelection } from "@t3tools/shared/model";
import * as Duration from "effect/Duration";
import * as Equal from "effect/Equal";
import * as Schema from "effect/Schema";
import { APP_VERSION, HOSTED_APP_CHANNEL, HOSTED_APP_CHANNEL_LABEL } from "../../branding";
import {
APP_COMMIT_HASH,
APP_REPOSITORY,
APP_VERSION,
HOSTED_APP_CHANNEL,
HOSTED_APP_CHANNEL_LABEL,
} from "../../branding";
import {
canCheckForUpdate,
getDesktopUpdateButtonTooltip,
Expand Down Expand Up @@ -220,6 +226,28 @@ function AboutVersionTitle() {
);
}

/**
* Update provenance for this distribution: which repository produced the
* running build and at which commit. Rendered as the version row's status line
* so "what would an update install?" is answerable at a glance.
*/
function AboutBuildProvenance() {
const commitLabel = APP_COMMIT_HASH === "unknown" ? "unknown" : APP_COMMIT_HASH.slice(0, 10);

return (
<span className="inline-flex min-w-0 flex-wrap items-center gap-x-1.5 gap-y-1 font-mono text-[11px]">
<span className="sr-only">Built from repository </span>
<span className="rounded-sm border border-teal-700/20 bg-teal-500/8 px-1 py-0.5 text-teal-700 dark:border-teal-300/20 dark:bg-teal-400/10 dark:text-teal-300">
{APP_REPOSITORY}
</span>
<span aria-hidden className="text-muted-foreground/50">
·
</span>
<span title={APP_COMMIT_HASH}>commit {commitLabel}</span>
</span>
);
}

function AboutVersionSection() {
const updateState = useDesktopUpdateState();
const [isChangingUpdateChannel, setIsChangingUpdateChannel] = useState(false);
Expand Down Expand Up @@ -348,6 +376,7 @@ function AboutVersionSection() {
<SettingsRow
title={<AboutVersionTitle />}
description={description}
status={<AboutBuildProvenance />}
control={
<Tooltip>
<TooltipTrigger
Expand Down Expand Up @@ -2098,6 +2127,7 @@ export function GeneralSettingsPanel() {
<SettingsRow
title={<AboutVersionTitle />}
description="Current version of the application."
status={<AboutBuildProvenance />}
/>
)}
<SettingsRow
Expand Down
43 changes: 27 additions & 16 deletions apps/web/src/components/sidebar/SidebarChrome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { SettingsIcon } from "lucide-react";
import { memo, useCallback } from "react";
import { Link, useNavigate } from "@tanstack/react-router";

import { APP_DISTRIBUTION_NAME, APP_REPOSITORY } from "../../branding";
import { formatDistributionBadgeLabel } from "../../branding.logic";
import { useEnvironmentIdentificationMode } from "../../hooks/useSettings";
import { cn } from "../../lib/utils";
import {
resolveEnvironmentIdentificationPillLabel,
resolveSidebarStageBackdropVariant,
SidebarStageBackdrop,
useEnvironmentStageLabel,
Expand Down Expand Up @@ -34,10 +35,16 @@ export const SidebarChromeHeader = memo(function SidebarChromeHeader({
stageLabel,
environmentIdentificationMode === "artwork",
);
const pillLabel =
environmentIdentificationMode === "pill"
? resolveEnvironmentIdentificationPillLabel(stageLabel)
: null;
// Distribution axis of the identity system: this fork build's `yngatech`
// badge stays visible in every identification mode. When Nightly/Dev artwork
// already answers "which build am I in?", the badge carries only the
// distribution name; without artwork it absorbs the stage label so the old
// stage-only pill is never duplicated.
const distributionBadgeLabel = formatDistributionBadgeLabel({
distributionName: APP_DISTRIBUTION_NAME,
stageLabel,
stageIdentifiedByArtwork: backdropVariant !== null,
});

return (
<SidebarHeader
Expand All @@ -51,20 +58,24 @@ export const SidebarChromeHeader = memo(function SidebarChromeHeader({
className={cn(
"relative z-10 md:hidden",
backdropVariant &&
"[:hover,[data-pressed]]:bg-white/15 focus-visible:ring-white/90 focus-visible:ring-offset-blue-700 [&_svg]:stroke-white/90! [&_svg]:opacity-100! [&_svg]:hover:stroke-white!",
"[:hover,[data-pressed]]:bg-white/15 focus-visible:ring-white/90 focus-visible:ring-offset-teal-800 [&_svg]:stroke-white/90! [&_svg]:opacity-100! [&_svg]:hover:stroke-white!",
)}
/>
<SidebarBrand onBackdrop={backdropVariant !== null} />
{pillLabel ? (
<Badge
className="relative z-10 ml-1 rounded-full px-1.5 text-muted-foreground"
data-environment-identification="pill"
size="sm"
variant="secondary"
>
{pillLabel}
</Badge>
) : null}
<Badge
className={cn(
"relative z-10 ml-1.5 min-w-0 rounded-full px-1.5 tracking-tight",
backdropVariant
? "border-white/25 bg-teal-950/45 text-teal-50 dark:bg-teal-950/45"
: "border-teal-700/25 bg-teal-500/8 text-teal-700 dark:border-teal-300/20 dark:bg-teal-400/10 dark:text-teal-300",
)}
data-distribution-badge={APP_DISTRIBUTION_NAME}
size="sm"
title={`${APP_REPOSITORY} distribution build`}
variant="outline"
>
<span className="truncate">{distributionBadgeLabel}</span>
</Badge>
</SidebarHeader>
);
});
Expand Down
14 changes: 8 additions & 6 deletions apps/web/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -310,16 +310,18 @@ html[data-mobile-composer-route-transition="true"]::view-transition-old(t3-mobil
);
}

/* yngatech fork colorway: same blueprint geometry, petrol/teal paper
instead of upstream's blue. Mirrors assets/dev/app-icon.icon. */
.stage-blueprint {
--stage-bp-top: #67c2ff;
--stage-bp-mid: #347ff8;
--stage-bp-bottom: #1538d0;
--stage-bp-top: #5cd6ce;
--stage-bp-mid: #219eae;
--stage-bp-bottom: #0a5b72;
}

.dark .stage-blueprint {
--stage-bp-top: #3a7ad1;
--stage-bp-mid: #2050ae;
--stage-bp-bottom: #101f6e;
--stage-bp-top: #35939c;
--stage-bp-mid: #1b6675;
--stage-bp-bottom: #0b3540;
}

.workspace-topbar {
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/vite-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface ImportMetaEnv {
readonly VITE_RELAY_OTLP_TRACES_DATASET: string;
readonly VITE_RELAY_OTLP_TRACES_TOKEN: string;
readonly APP_VERSION: string;
readonly APP_COMMIT_HASH: string;
}

interface ImportMeta {
Expand Down
Loading
Loading