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: 10 additions & 14 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ on:
env:
WAILS_VERSION: v3.0.0-alpha2.117

# Runners are Blacksmith. The cache actions below are the upstream ones on
# purpose: Blacksmith intercepts them and serves from a colocated cache, and its
# own useblacksmith/* forks are archived.
# Runners are GitHub-hosted.
jobs:
macos:
name: macOS (universal .dmg)
# Apple Silicon; the universal build compiles arm64 and amd64 then lipos
# them together, so the extra cores pay for themselves here.
runs-on: blacksmith-12vcpu-macos-15
# them together.
runs-on: macos-15
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -56,7 +54,7 @@ jobs:

linux:
name: Linux (.deb / .rpm / .AppImage)
runs-on: nugget-linux
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -116,9 +114,7 @@ jobs:

windows:
name: Windows (installer)
# Blacksmith's Windows runners are in public beta; swap back to
# `windows-latest` if this job turns flaky.
runs-on: windows-pc
runs-on: windows-2025
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -149,9 +145,9 @@ jobs:
run: go install github.com/wailsapp/wails/v3/cmd/wails3@${{ env.WAILS_VERSION }}

- name: Install NSIS
# The installer is built with makensis, which the Blacksmith Windows
# image does not ship (GitHub's older Windows images did). Chocolatey
# when the image has it, otherwise the official zip.
# The installer is built with makensis, which the windows-2025 image
# does not ship (GitHub's older Windows images did). Chocolatey when
# the image has it, otherwise the official zip.
shell: pwsh
run: |
if (Get-Command makensis -ErrorAction SilentlyContinue) {
Expand Down Expand Up @@ -198,7 +194,7 @@ jobs:

android:
name: Android (.apk)
runs-on: nugget-linux
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -282,7 +278,7 @@ jobs:
publish:
name: Publish release
needs: [macos, linux, windows, android]
runs-on: nugget-linux
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
Expand Down
18 changes: 8 additions & 10 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ env:
SING_BOX_TAGS: with_gvisor,with_quic,with_wireguard,with_utls,with_clash_api,with_dhcp,with_acme
WAILS_VERSION: v3.0.0-alpha2.117

# Runners are Blacksmith. Note the cache actions below are the upstream ones on
# purpose: Blacksmith intercepts them and serves from a colocated cache, and its
# own useblacksmith/* forks are archived.
# Runners are GitHub-hosted.
jobs:
test:
name: Go tests (${{ matrix.name }})
Expand All @@ -30,11 +28,11 @@ jobs:
matrix:
include:
- name: linux
runner: nugget-linux
runner: ubuntu-24.04
- name: macos
runner: blacksmith-6vcpu-macos-15
runner: macos-15
- name: windows
runner: windows-pc
runner: windows-2025
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -69,7 +67,7 @@ jobs:

android:
name: Build (android)
runs-on: nugget-linux
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

Expand Down Expand Up @@ -149,11 +147,11 @@ jobs:
matrix:
include:
- name: macos-arm64
runner: blacksmith-12vcpu-macos-15
runner: macos-15
- name: linux-amd64
runner: nugget-linux
runner: ubuntu-24.04
- name: windows-amd64
runner: windows-pc
runner: windows-2025
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
Expand Down
24 changes: 24 additions & 0 deletions frontend/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,15 @@
--status-connected: oklch(var(--brand-l) var(--brand-c) var(--brand-h));
--status-error: oklch(0.55 0.225 27.325);

/* Categorical chart hues, in fixed order; checked for colour-blind separation. */
--series-1: #2a78d6;
--series-2: #eb6834;
--series-3: #1baf7a;
--series-4: #eda100;
--series-5: #e87ba4;
--series-6: #008300;
--series-7: #4a3aa7;

--routing-apps: oklch(0.6 0.2 350);
--routing-domains: oklch(0.53 0.2 300);
--routing-ip: oklch(0.6 0.16 40);
Expand Down Expand Up @@ -258,6 +267,14 @@
--status-connected: oklch(var(--brand-l) var(--brand-c) var(--brand-h));
--status-error: oklch(0.704 0.191 22.216);

--series-1: #3987e5;
--series-2: #d95926;
--series-3: #199e70;
--series-4: #c98500;
--series-5: #d55181;
--series-6: #008300;
--series-7: #9085e9;

--routing-apps: oklch(0.72 0.19 350);
--routing-domains: oklch(0.7 0.18 300);
--routing-ip: oklch(0.75 0.16 45);
Expand Down Expand Up @@ -993,3 +1010,10 @@
:root[data-backdrop] [data-slot="sheet-content"] {
background-color: oklch(var(--ui-bg-l) var(--ui-c) var(--ui-h));
}

/* The sidebar's wrapper already carries the see-through tint; painting the
inner panel too doubled it inside the wrapper's padding, which showed as a
hard edge down the sidebar's left side. */
:root[data-backdrop] [data-slot="sidebar-inner"] {
background: transparent;
}
5 changes: 3 additions & 2 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,7 @@ function App() {
},
[profilesOf]
);
const profileIdsOf = useCallback((source: ConfigSource) => profilesOf(source).map((profile) => profile.id), [profilesOf]);
const handleCopySources = useCallback(
async (list: ConfigSource[]) => {
const links = list.map(linkOf).filter(Boolean);
Expand Down Expand Up @@ -1004,7 +1005,6 @@ function App() {
onMinimize={appWindow.minimize}
onMaximize={appWindow.toggleMaximize}
platform={platform}
status={connection.state.status}
/>
<SidebarInset
className={cn(
Expand Down Expand Up @@ -1208,12 +1208,13 @@ function App() {
onRefreshSources={handleRefreshSources}
onCopySources={handleCopySources}
linkOf={linkOf}
profileIdsOf={profileIdsOf}
onAdd={() => setIsModalOpen(true)}
/>
)}
</div>
{isMobile ? (
<BottomNav activeTab={activeTab} onTabChange={changeTab} status={connection.state.status} />
<BottomNav activeTab={activeTab} onTabChange={changeTab} />
) : null}
</div>
</SidebarInset>
Expand Down
26 changes: 0 additions & 26 deletions frontend/src/components/layout/AppSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
import { MacWindowControls } from "@/components/layout/MacWindowControls";
import { MessageKey, useI18n } from "@/lib/i18n";
import { cn } from "@/lib/utils";
import { ConnectionStatus } from "@/types";

interface AppSidebarProps {
activeTab: string;
Expand All @@ -23,7 +22,6 @@ interface AppSidebarProps {
onMinimize: () => void;
onMaximize: () => void;
platform?: string;
status: ConnectionStatus;
}

export const TABS = [
Expand All @@ -36,19 +34,6 @@ export const TABS = [
{ id: "logs", label: "nav.logs", icon: FileText },
] as const satisfies readonly { id: string; label: MessageKey; icon: unknown }[];

export const STATUS_DOT: Record<ConnectionStatus, string> = {
idle: "bg-status-idle",
connecting: "bg-status-connecting animate-pulse",
connected: "bg-status-connected",
error: "bg-status-error",
};

const STATUS_LABEL: Record<ConnectionStatus, MessageKey> = {
idle: "status.idle",
connecting: "status.connecting",
connected: "status.connected",
error: "status.error",
};

function AppSidebar({
activeTab,
Expand All @@ -57,7 +42,6 @@ function AppSidebar({
onMinimize,
onMaximize,
platform,
status,
}: AppSidebarProps) {
const { t, dir } = useI18n();
const isMac = platform === "macos";
Expand Down Expand Up @@ -104,16 +88,6 @@ function AppSidebar({
</SidebarContent>

<SidebarFooter className="gap-2">
{/* The tunnel state is visible from every screen, not only the
one screen that happens to be about connecting. */}
<div className="flex items-center gap-2 px-2 py-1.5 text-xs text-muted-foreground">
<span
className={cn("h-2 w-2 rounded-full shrink-0", STATUS_DOT[status])}
aria-hidden="true"
/>
<span className="truncate">{t(STATUS_LABEL[status])}</span>
</div>

<SidebarMenu>
<SidebarMenuItem>
<SidebarMenuButton
Expand Down
8 changes: 1 addition & 7 deletions frontend/src/components/layout/BottomNav.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { useState } from "react";
import { MoreHorizontal, Power, Settings, Signal, Waypoints } from "lucide-react";

import { STATUS_DOT, TABS } from "@/components/layout/AppSidebar";
import { TABS } from "@/components/layout/AppSidebar";
import { Sheet, SheetContent, SheetHeader, SheetTitle } from "@/components/ui/sheet";
import { MessageKey, useT } from "@/lib/i18n";
import { cn } from "@/lib/utils";
import { ConnectionStatus } from "@/types";

/** The screens with a place of their own in the bar; the rest are under More. */
const PRIMARY = [
Expand All @@ -25,11 +24,9 @@ const PRIMARY_IDS = new Set<string>(PRIMARY.map((tab) => tab.id));
export function BottomNav({
activeTab,
onTabChange,
status,
}: {
activeTab: string;
onTabChange: (tab: string) => void;
status: ConnectionStatus;
}) {
const t = useT();
const [more, setMore] = useState(false);
Expand Down Expand Up @@ -57,9 +54,6 @@ export function BottomNav({
<tab.icon size={19} aria-hidden="true" />
</span>
<span className="truncate">{t(tab.label)}</span>
{tab.id === "connection" ? (
<span className={cn("absolute end-[calc(50%-1.1rem)] top-1.5 h-2 w-2 rounded-full", STATUS_DOT[status])} aria-hidden="true" />
) : null}
</button>
))}
<button type="button" onClick={() => setMore(true)} aria-expanded={more} className={item(inMore)}>
Expand Down
Loading
Loading