| author | |
| committer | |
| log | ebe8afb1df357423a0e036b388af3e739d640205 |
| tree | 50611ae39a4c88a08adb009841c1046dd8dddbbb |
| parent | bf76535fe4da71d8de7b8bd5ffa0d2086b7af8d0 |
| signature | Signed by PGP key B5690EEEBB952194 |
10 files changed, 58 insertions(+), 3 deletions(-)
apps/web/public/apple-touch-icon.png| Binary files a/apps/web/public/apple-touch-icon.png and b/apps/web/public/apple-touch-icon.png differ |
apps/web/public/favicon-16x16.png| Binary files a/apps/web/public/favicon-16x16.png and b/apps/web/public/favicon-16x16.png differ |
apps/web/public/favicon-32x32.png| Binary files a/apps/web/public/favicon-32x32.png and b/apps/web/public/favicon-32x32.png differ |
apps/web/public/favicon.ico| Binary files a/apps/web/public/favicon.ico and b/apps/web/public/favicon.ico differ |
assets/README.md+1-1| ... | ... | @@ -8,7 +8,7 @@ The three Icon Composer projects are the source of truth for full application ic |
| 8 | 8 | |
| 9 | 9 | Each project uses `text.svg` for the T3 mark and `background.svg` when the background is a vector layer. Additional layers use semantic names that describe their role and placement. |
| 10 | 10 | |
| 11 | Run `vp run icons:export` from the repository root to regenerate the tracked iOS, Linux, Windows, and web assets. Run `vp run icons:check` to verify that those generated assets match their sources without changing files. | |
| 11 | Run `vp run icons:export` from the repository root to regenerate the tracked iOS, Linux, Windows, and web assets. The development web exports are also copied to `apps/web/public` for the browser favicon and splash screen. Run `vp run icons:check` to verify that the generated assets and public copies match their sources without changing files. | |
| 12 | 12 | |
| 13 | 13 | Exporting requires Icon Composer 2 or newer on macOS. The script selects the newest compatible exporter from Xcode or a standalone Icon Composer installation and pins design generation 26. Set `ICON_COMPOSER_TOOL` to the full path of `Icon Composer.app/Contents/Executables/ictool` to override automatic discovery. |
| 14 | 14 |
scripts/build-desktop-artifact.test.ts+6| ... | ... | @@ -30,6 +30,7 @@ import { |
| 30 | 30 | resolveDesktopBuildIconAssets, |
| 31 | 31 | resolveDesktopProductName, |
| 32 | 32 | resolveDesktopUpdateChannel, |
| 33 | resolveDesktopWebAssetBrand, | |
| 33 | 34 | resolveGitHubPublishConfig, |
| 34 | 35 | resolveMockUpdateServerPort, |
| 35 | 36 | resolveMockUpdateServerUrl, |
| ... | ... | @@ -102,6 +103,11 @@ it.layer(NodeServices.layer)("build-desktop-artifact", (it) => { |
| 102 | 103 | }); |
| 103 | 104 | }); |
| 104 | 105 | |
| 106 | it("switches the bundled splash and favicon branding for nightly versions", () => { | |
| 107 | assert.equal(resolveDesktopWebAssetBrand("0.0.17"), "production"); | |
| 108 | assert.equal(resolveDesktopWebAssetBrand("0.0.17-nightly.20260413.42"), "nightly"); | |
| 109 | }); | |
| 110 | ||
| 105 | 111 | it.effect("resolves GitHub desktop publish config from Effect config", () => |
| 106 | 112 | Effect.gen(function* () { |
| 107 | 113 | const latestConfig = yield* resolveGitHubPublishConfig("latest").pipe( |
scripts/build-desktop-artifact.ts+13-1| ... | ... | @@ -10,7 +10,12 @@ import rootPackageJson from "../package.json" with { type: "json" }; |
| 10 | 10 | import desktopPackageJson from "../apps/desktop/package.json" with { type: "json" }; |
| 11 | 11 | import serverPackageJson from "../apps/server/package.json" with { type: "json" }; |
| 12 | 12 | |
| 13 | import { BRAND_ASSET_PATHS } from "./lib/brand-assets.ts"; | |
| 13 | import { applyWebBrandAssets } from "./apply-web-brand-assets.ts"; | |
| 14 | import { | |
| 15 | BRAND_ASSET_PATHS, | |
| 16 | resolveWebAssetBrandForChannel, | |
| 17 | type WebAssetBrand, | |
| 18 | } from "./lib/brand-assets.ts"; | |
| 14 | 19 | import { getDefaultBuildArch } from "./lib/build-target-arch.ts"; |
| 15 | 20 | import { loadRepoEnv } from "./lib/public-config.ts"; |
| 16 | 21 | import { resolveCatalogDependencies } from "./lib/resolve-catalog.ts"; |
| ... | ... | @@ -1324,6 +1329,10 @@ export function resolveDesktopUpdateChannel(version: string): "latest" | "nightl |
| 1324 | 1329 | return /-nightly\.\d{8}\.\d+$/.test(version) ? "nightly" : "latest"; |
| 1325 | 1330 | } |
| 1326 | 1331 | |
| 1332 | export function resolveDesktopWebAssetBrand(version: string): WebAssetBrand { | |
| 1333 | return resolveWebAssetBrandForChannel(resolveDesktopUpdateChannel(version)); | |
| 1334 | } | |
| 1335 | ||
| 1327 | 1336 | export function resolveDesktopBuildIconAssets(version: string): DesktopBuildIconAssets { |
| 1328 | 1337 | if (resolveDesktopUpdateChannel(version) === "nightly") { |
| 1329 | 1338 | return { |
| ... | ... | @@ -1665,6 +1674,9 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( |
| 1665 | 1674 | }); |
| 1666 | 1675 | } |
| 1667 | 1676 | |
| 1677 | const webAssetBrand = resolveDesktopWebAssetBrand(appVersion); | |
| 1678 | yield* applyWebBrandAssets(webAssetBrand, "apps/server/dist/client"); | |
| 1679 | yield* Effect.log(`[desktop-artifact] Applied ${webAssetBrand} web client branding.`); | |
| 1668 | 1680 | yield* validateBundledClientAssets(path.dirname(bundledClientEntry)); |
| 1669 | 1681 | |
| 1670 | 1682 | yield* fs.makeDirectory(path.join(stageAppDir, "apps/desktop"), { recursive: true }); |
scripts/export-brand-icons.ts+11-1| ... | ... | @@ -13,7 +13,7 @@ import * as Stream from "effect/Stream"; |
| 13 | 13 | import { Command, Flag } from "effect/unstable/cli"; |
| 14 | 14 | import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; |
| 15 | 15 | |
| 16 | import { BRAND_ASSET_PATHS } from "./lib/brand-assets.ts"; | |
| 16 | import { BRAND_ASSET_PATHS, DEVELOPMENT_PUBLIC_ICON_OVERRIDES } from "./lib/brand-assets.ts"; | |
| 17 | 17 | import { encodePngIco, readPngDimensions, WINDOWS_ICON_SIZES } from "./lib/icon-export.ts"; |
| 18 | 18 | |
| 19 | 19 | const DESIGN_GENERATION = 26; |
| ... | ... | @@ -751,6 +751,16 @@ export const exportBrandIcons = Effect.fn("exportBrandIcons")(function* (checkOn |
| 751 | 751 | } |
| 752 | 752 | } |
| 753 | 753 | |
| 754 | for (const override of DEVELOPMENT_PUBLIC_ICON_OVERRIDES) { | |
| 755 | const sourceContents = generated.get(override.sourceRelativePath); | |
| 756 | if (sourceContents === undefined) { | |
| 757 | return yield* Effect.die( | |
| 758 | new Error(`Generated development web icon is missing: ${override.sourceRelativePath}`), | |
| 759 | ); | |
| 760 | } | |
| 761 | generated.set(override.targetRelativePath, sourceContents); | |
| 762 | } | |
| 763 | ||
| 754 | 764 | if (checkOnly) { |
| 755 | 765 | const stale = yield* Effect.filter( |
| 756 | 766 | [...generated.entries()], |
scripts/lib/brand-assets.test.ts+22| ... | ... | @@ -3,6 +3,7 @@ import { describe, expect, it } from "vite-plus/test"; |
| 3 | 3 | import { |
| 4 | 4 | BRAND_ASSET_PATHS, |
| 5 | 5 | DEVELOPMENT_ICON_OVERRIDES, |
| 6 | DEVELOPMENT_PUBLIC_ICON_OVERRIDES, | |
| 6 | 7 | PUBLISH_ICON_OVERRIDES, |
| 7 | 8 | resolveWebAssetBrandForChannel, |
| 8 | 9 | resolveWebIconOverrides, |
| ... | ... | @@ -37,6 +38,27 @@ describe("brand-assets", () => { |
| 37 | 38 | }); |
| 38 | 39 | }); |
| 39 | 40 | |
| 41 | it("maps development web assets to the public splash and favicon files", () => { | |
| 42 | expect(DEVELOPMENT_PUBLIC_ICON_OVERRIDES).toEqual([ | |
| 43 | { | |
| 44 | sourceRelativePath: BRAND_ASSET_PATHS.developmentWebFaviconIco, | |
| 45 | targetRelativePath: "apps/web/public/favicon.ico", | |
| 46 | }, | |
| 47 | { | |
| 48 | sourceRelativePath: BRAND_ASSET_PATHS.developmentWebFavicon16Png, | |
| 49 | targetRelativePath: "apps/web/public/favicon-16x16.png", | |
| 50 | }, | |
| 51 | { | |
| 52 | sourceRelativePath: BRAND_ASSET_PATHS.developmentWebFavicon32Png, | |
| 53 | targetRelativePath: "apps/web/public/favicon-32x32.png", | |
| 54 | }, | |
| 55 | { | |
| 56 | sourceRelativePath: BRAND_ASSET_PATHS.developmentWebAppleTouchIconPng, | |
| 57 | targetRelativePath: "apps/web/public/apple-touch-icon.png", | |
| 58 | }, | |
| 59 | ]); | |
| 60 | }); | |
| 61 | ||
| 40 | 62 | it("can target hosted web dist directly", () => { |
| 41 | 63 | expect(resolveWebIconOverrides("production", "apps/web/dist")).toContainEqual({ |
| 42 | 64 | sourceRelativePath: BRAND_ASSET_PATHS.productionWebAppleTouchIconPng, |
scripts/lib/brand-assets.ts+5| ... | ... | @@ -101,4 +101,9 @@ export function resolveWebIconOverrides( |
| 101 | 101 | |
| 102 | 102 | export const DEVELOPMENT_ICON_OVERRIDES = resolveWebIconOverrides("development", "dist/client"); |
| 103 | 103 | |
| 104 | export const DEVELOPMENT_PUBLIC_ICON_OVERRIDES = resolveWebIconOverrides( | |
| 105 | "development", | |
| 106 | "apps/web/public", | |
| 107 | ); | |
| 108 | ||
| 104 | 109 | export const PUBLISH_ICON_OVERRIDES = resolveWebIconOverrides("production", "dist/client"); |