mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(site): simplify prerelease css (#24514)
> 🤖 This PR was modified by Coder Agent on behalf of Jake Howell Removes the bottom-line navbar effect in `<NavbarView />` and cleans up prerelease CSS. - Remove `relative` from `<NavbarView />` as it was redundant with the `sticky` class. - Refactor `getPrereleaseFlag` to simplify version classification. - Clean up unused prerelease CSS from `index.css`. <img width="920" height="83" alt="image" src="https://github.com/user-attachments/assets/84e351e6-d7a2-4fe2-a331-27c651266256" />
This commit is contained in:
@@ -156,50 +156,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
/* Map each stripe variant to a color token so the
|
||||
pseudo-element rules can stay DRY. */
|
||||
.navbar-stripe-devel {
|
||||
--stripe-color: var(--content-warning);
|
||||
}
|
||||
|
||||
.navbar-stripe-rc {
|
||||
--stripe-color: var(--border-sky);
|
||||
}
|
||||
|
||||
/* Thin stripe bars at the top and bottom edges of the
|
||||
navbar. Using pseudo-elements keeps the stripes out of
|
||||
the content area so nav links stay readable. */
|
||||
.navbar-stripe-devel::before,
|
||||
.navbar-stripe-devel::after,
|
||||
.navbar-stripe-rc::before,
|
||||
.navbar-stripe-rc::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 4px;
|
||||
background: repeating-linear-gradient(
|
||||
-45deg,
|
||||
transparent,
|
||||
transparent 4px,
|
||||
hsl(var(--stripe-color) / 0.5) 4px,
|
||||
hsl(var(--stripe-color) / 0.5) 8px
|
||||
);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.navbar-stripe-devel::before,
|
||||
.navbar-stripe-rc::before {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.navbar-stripe-devel::after,
|
||||
.navbar-stripe-rc::after {
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
|
||||
@@ -16,7 +16,7 @@ import type { ProxyContextValue } from "#/contexts/ProxyContext";
|
||||
import { useEmbeddedMetadata } from "#/hooks/useEmbeddedMetadata";
|
||||
import { useDashboard } from "#/modules/dashboard/useDashboard";
|
||||
import { NotificationsInbox } from "#/modules/notifications/NotificationsInbox/NotificationsInbox";
|
||||
import { isDevBuild, isRcBuild } from "#/utils/buildInfo";
|
||||
import { getPrereleaseFlag } from "#/utils/buildInfo";
|
||||
import { cn } from "#/utils/cn";
|
||||
import { DeploymentDropdown } from "./DeploymentDropdown";
|
||||
import { MobileMenu } from "./MobileMenu";
|
||||
@@ -61,16 +61,27 @@ export const NavbarView: FC<NavbarViewProps> = ({
|
||||
canCreateChat,
|
||||
proxyContextValue,
|
||||
}) => {
|
||||
const isDev = buildInfo ? isDevBuild(buildInfo) : false;
|
||||
const isRc = buildInfo ? isRcBuild(buildInfo) : false;
|
||||
const isPreRelease = isDev || isRc;
|
||||
const prerelease = getPrereleaseFlag(buildInfo);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"sticky top-0 bg-surface-primary z-40 border-0 border-b border-solid h-[72px] min-h-[72px] flex items-center leading-none px-6 relative",
|
||||
isRc ? "navbar-stripe-rc" : isDev ? "navbar-stripe-devel" : undefined,
|
||||
"sticky top-0 bg-surface-primary z-40 border-0 border-b border-solid h-[72px] min-h-[72px] flex items-center leading-none px-6",
|
||||
prerelease &&
|
||||
cn(
|
||||
"[&:before]:content-[''] [&:before]:absolute [&:before]:left-0",
|
||||
"[&:before]:right-0 [&:before]:h-1 [&:before]:top-0",
|
||||
"[&:before]:bg-[repeating-linear-gradient(-45deg,_transparent,_transparent_4px,_hsl(var(--stripe-color)_/_0.5)_4px,_hsl(var(--stripe-color)_/_0.5)_8px)]",
|
||||
),
|
||||
)}
|
||||
style={{
|
||||
"--stripe-color":
|
||||
prerelease === "rc"
|
||||
? "var(--border-sky)"
|
||||
: prerelease === "devel"
|
||||
? "var(--content-warning)"
|
||||
: undefined,
|
||||
}}
|
||||
>
|
||||
<NavLink to="/workspaces">
|
||||
{logo_url ? (
|
||||
@@ -82,7 +93,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
|
||||
|
||||
<NavItems className="ml-4" user={user} canCreateChat={canCreateChat} />
|
||||
|
||||
{isPreRelease && buildInfo?.version && (
|
||||
{prerelease && buildInfo?.version && (
|
||||
<a
|
||||
href={buildInfo.external_url}
|
||||
target="_blank"
|
||||
@@ -90,7 +101,7 @@ export const NavbarView: FC<NavbarViewProps> = ({
|
||||
className="absolute top-0 left-1/2 -translate-x-1/2 no-underline z-10"
|
||||
>
|
||||
<Badge
|
||||
variant={isRc ? "info" : "warning"}
|
||||
variant={prerelease === "rc" ? "info" : "warning"}
|
||||
size="sm"
|
||||
className="font-mono rounded-t-none border-t-0"
|
||||
>
|
||||
@@ -262,8 +273,9 @@ function idleTasksLabel(count: number) {
|
||||
|
||||
const AgentsNavItem: FC<{ canCreateChat: boolean }> = ({ canCreateChat }) => {
|
||||
const { experiments, buildInfo } = useDashboard();
|
||||
const prerelease = getPrereleaseFlag(buildInfo);
|
||||
const experimentEnabled =
|
||||
experiments.includes("agents") || isDevBuild(buildInfo);
|
||||
experiments.includes("agents") || prerelease === "devel";
|
||||
|
||||
if (!experimentEnabled || !canCreateChat) {
|
||||
return null;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from "#/components/Sidebar/Sidebar";
|
||||
import { Stack } from "#/components/Stack/Stack";
|
||||
import type { Permissions } from "#/modules/permissions";
|
||||
import { isDevBuild } from "#/utils/buildInfo";
|
||||
import { getPrereleaseFlag } from "#/utils/buildInfo";
|
||||
|
||||
interface DeploymentSidebarViewProps {
|
||||
/** Site-wide permissions. */
|
||||
@@ -54,7 +54,8 @@ export const DeploymentSidebarView: FC<DeploymentSidebarViewProps> = ({
|
||||
</SidebarNavItem>
|
||||
)}
|
||||
{permissions.viewDeploymentConfig &&
|
||||
(experiments.includes("oauth2") || isDevBuild(buildInfo)) && (
|
||||
(experiments.includes("oauth2") ||
|
||||
getPrereleaseFlag(buildInfo) === "devel") && (
|
||||
<SidebarNavItem href="/deployment/oauth2-provider/apps">
|
||||
OAuth2 Applications
|
||||
</SidebarNavItem>
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
SidebarNavItem,
|
||||
} from "#/components/Sidebar/Sidebar";
|
||||
import { useDashboard } from "#/modules/dashboard/useDashboard";
|
||||
import { isDevBuild } from "#/utils/buildInfo";
|
||||
import { getPrereleaseFlag } from "#/utils/buildInfo";
|
||||
|
||||
interface SidebarProps {
|
||||
user: User;
|
||||
@@ -45,7 +45,8 @@ export const Sidebar: FC<SidebarProps> = ({ user }) => {
|
||||
<SidebarNavItem href="external-auth" icon={GitIcon}>
|
||||
External Authentication
|
||||
</SidebarNavItem>
|
||||
{(experiments.includes("oauth2") || isDevBuild(buildInfo)) && (
|
||||
{(experiments.includes("oauth2") ||
|
||||
getPrereleaseFlag(buildInfo) === "devel") && (
|
||||
<SidebarNavItem href="oauth2-provider" icon={ShieldIcon}>
|
||||
OAuth2 Applications
|
||||
</SidebarNavItem>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { BuildInfoResponse } from "#/api/typesGenerated";
|
||||
import { isDevBuild, isRcBuild } from "./buildInfo";
|
||||
import { getPrereleaseFlag } from "./buildInfo";
|
||||
|
||||
const baseBuildInfo: BuildInfoResponse = {
|
||||
agent_api_version: "1.0",
|
||||
@@ -13,88 +13,97 @@ const baseBuildInfo: BuildInfoResponse = {
|
||||
telemetry: false,
|
||||
};
|
||||
|
||||
describe("isDevBuild", () => {
|
||||
it("returns true for -devel versions", () => {
|
||||
describe("getPrereleaseFlag", () => {
|
||||
it("returns devel for -devel versions", () => {
|
||||
expect(
|
||||
isDevBuild({ ...baseBuildInfo, version: "v2.16.0-devel+abc123" }),
|
||||
).toBe(true);
|
||||
getPrereleaseFlag({
|
||||
...baseBuildInfo,
|
||||
version: "v2.16.0-devel+abc123",
|
||||
}),
|
||||
).toBe("devel");
|
||||
});
|
||||
|
||||
it("returns true for bare -devel versions", () => {
|
||||
expect(isDevBuild({ ...baseBuildInfo, version: "v2.32.0-devel" })).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns true for v0.0.0", () => {
|
||||
expect(isDevBuild({ ...baseBuildInfo, version: "v0.0.0" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for release versions", () => {
|
||||
expect(isDevBuild({ ...baseBuildInfo, version: "v2.16.0" })).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for RC versions", () => {
|
||||
expect(isDevBuild({ ...baseBuildInfo, version: "v2.32.0-rc.1" })).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns true for combined rc+devel versions", () => {
|
||||
it("returns devel for bare -devel versions", () => {
|
||||
expect(
|
||||
isDevBuild({
|
||||
getPrereleaseFlag({
|
||||
...baseBuildInfo,
|
||||
version: "v2.32.0-devel",
|
||||
}),
|
||||
).toBe("devel");
|
||||
});
|
||||
|
||||
it("returns devel for v0.0.0", () => {
|
||||
expect(
|
||||
getPrereleaseFlag({
|
||||
...baseBuildInfo,
|
||||
version: "v0.0.0",
|
||||
}),
|
||||
).toBe("devel");
|
||||
});
|
||||
|
||||
it("returns undefined for release versions", () => {
|
||||
expect(
|
||||
getPrereleaseFlag({
|
||||
...baseBuildInfo,
|
||||
version: "v2.16.0",
|
||||
}),
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns rc for RC versions", () => {
|
||||
expect(
|
||||
getPrereleaseFlag({
|
||||
...baseBuildInfo,
|
||||
version: "v2.32.0-rc.1",
|
||||
}),
|
||||
).toBe("rc");
|
||||
});
|
||||
|
||||
it("returns devel when version contains both rc and -devel (devel wins)", () => {
|
||||
expect(
|
||||
getPrereleaseFlag({
|
||||
...baseBuildInfo,
|
||||
version: "v2.33.0-rc.1-devel+727ec00f7",
|
||||
}),
|
||||
).toBe(true);
|
||||
).toBe("devel");
|
||||
});
|
||||
|
||||
it("returns false for empty version", () => {
|
||||
expect(isDevBuild({ ...baseBuildInfo, version: "" })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isRcBuild", () => {
|
||||
it("returns true for -rc.0 versions", () => {
|
||||
expect(isRcBuild({ ...baseBuildInfo, version: "v2.32.0-rc.0" })).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for -rc.1 with build metadata", () => {
|
||||
it("returns undefined for empty version", () => {
|
||||
expect(
|
||||
isRcBuild({ ...baseBuildInfo, version: "v2.32.0-rc.1+abc123" }),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true for higher RC numbers", () => {
|
||||
expect(isRcBuild({ ...baseBuildInfo, version: "v2.32.0-rc.12" })).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns false for release versions", () => {
|
||||
expect(isRcBuild({ ...baseBuildInfo, version: "v2.16.0" })).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for devel versions", () => {
|
||||
expect(
|
||||
isRcBuild({ ...baseBuildInfo, version: "v2.16.0-devel+abc123" }),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for empty version", () => {
|
||||
expect(isRcBuild({ ...baseBuildInfo, version: "" })).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for versions with rc but no dot", () => {
|
||||
expect(isRcBuild({ ...baseBuildInfo, version: "v2.32.0-rc" })).toBe(false);
|
||||
});
|
||||
|
||||
it("returns true for combined rc+devel versions", () => {
|
||||
expect(
|
||||
isRcBuild({
|
||||
getPrereleaseFlag({
|
||||
...baseBuildInfo,
|
||||
version: "v2.33.0-rc.1-devel+727ec00f7",
|
||||
version: "",
|
||||
}),
|
||||
).toBe(true);
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns rc for -rc.0 and build metadata", () => {
|
||||
expect(
|
||||
getPrereleaseFlag({
|
||||
...baseBuildInfo,
|
||||
version: "v2.32.0-rc.0",
|
||||
}),
|
||||
).toBe("rc");
|
||||
expect(
|
||||
getPrereleaseFlag({
|
||||
...baseBuildInfo,
|
||||
version: "v2.32.0-rc.1+abc123",
|
||||
}),
|
||||
).toBe("rc");
|
||||
expect(
|
||||
getPrereleaseFlag({
|
||||
...baseBuildInfo,
|
||||
version: "v2.32.0-rc.12",
|
||||
}),
|
||||
).toBe("rc");
|
||||
});
|
||||
|
||||
it("returns undefined when rc segment lacks a dot", () => {
|
||||
expect(
|
||||
getPrereleaseFlag({
|
||||
...baseBuildInfo,
|
||||
version: "v2.32.0-rc",
|
||||
}),
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
+19
-18
@@ -23,27 +23,28 @@ export const getStaticBuildInfo = () => {
|
||||
return CACHED_BUILD_INFO;
|
||||
};
|
||||
|
||||
// Check if the current build is a development build.
|
||||
// Development builds have versions containing "-devel" or "v0.0.0".
|
||||
// This matches the backend's buildinfo.IsDev() logic.
|
||||
export const isDevBuild = (input: BuildInfoResponse): boolean => {
|
||||
const version = input.version;
|
||||
if (!version) {
|
||||
return false;
|
||||
type PrereleaseFlag = "devel" | "rc" | undefined;
|
||||
|
||||
/** Classifies the dashboard build version for dev vs RC styling and experiments. */
|
||||
export const getPrereleaseFlag = (
|
||||
input?: BuildInfoResponse,
|
||||
): PrereleaseFlag => {
|
||||
// If no input is provided, return undefined.
|
||||
if (!input) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const version = input.version;
|
||||
|
||||
// Check for dev version pattern (contains "-devel") or no version (v0.0.0)
|
||||
return version.includes("-devel") || version === "v0.0.0";
|
||||
};
|
||||
|
||||
// Check if the current build is a release candidate. Release
|
||||
// candidates have versions containing "-rc." (e.g. v2.32.0-rc.0,
|
||||
// v2.32.0-rc.1+abc123, v2.33.0-rc.1-devel+727ec00f7).
|
||||
export const isRcBuild = (input: BuildInfoResponse): boolean => {
|
||||
const version = input.version;
|
||||
if (!version) {
|
||||
return false;
|
||||
if (version.includes("-devel") || version === "v0.0.0") {
|
||||
return "devel";
|
||||
}
|
||||
|
||||
return version.includes("-rc.");
|
||||
// Check if the current build is a release candidate. Release
|
||||
// candidates have versions containing "-rc." (e.g. v2.32.0-rc.0,
|
||||
// v2.32.0-rc.1+abc123, v2.33.0-rc.1-devel+727ec00f7).
|
||||
if (version.includes("-rc.")) {
|
||||
return "rc";
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user