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
12 changes: 10 additions & 2 deletions apps/mobile/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ExpoConfig } from "expo/config";

import { BRAND_ASSET_PATHS } from "../../scripts/lib/brand-assets.ts";
import { loadRepoEnv } from "../../scripts/lib/public-config.ts";
import { resolveChromeriaMobileIdentity } from "./chromeria.ts";

type AppVariant = "development" | "preview" | "production";

Expand Down Expand Up @@ -109,7 +110,14 @@ function resolveAppVariant(value: string | undefined): AppVariant {
}
}

const variant = VARIANT_CONFIG[APP_VARIANT];
const chromeria = resolveChromeriaMobileIdentity(repoEnv);
const variant = chromeria
? {
...VARIANT_CONFIG.production,
...chromeria.variant,
assets: { ...RELEASE_ASSETS, ...chromeria.assets },
}
: VARIANT_CONFIG[APP_VARIANT];
const iosBundleIdentifier = isIosPersonalTeamBuild
? personalTeamBundleIdentifier!
: variant.iosBundleIdentifier;
Expand Down Expand Up @@ -499,4 +507,4 @@ const config: ExpoConfig = {
owner: "pingdotgg",
};

export default config;
export default chromeria ? chromeria.apply(config) : config;
142 changes: 142 additions & 0 deletions apps/mobile/chromeria.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { afterEach, describe, expect, it, vi } from "vite-plus/test";

import { resolveChromeriaMobileIdentity } from "./chromeria.ts";

async function resolveConfig(env: Record<string, string | undefined>) {
vi.resetModules();
for (const [key, value] of Object.entries(env)) vi.stubEnv(key, value);
vi.doMock("../../scripts/lib/public-config.ts", () => ({ loadRepoEnv: () => env }));
return (await import("./app.config.ts")).default;
}

afterEach(() => {
vi.unstubAllEnvs();
vi.doUnmock("../../scripts/lib/public-config.ts");
});

describe("Chromeria mobile identity", () => {
it.each([undefined, "0", "true"])("requires the explicit switch (%s)", (flag) => {
expect(
resolveChromeriaMobileIdentity({
CHROMERIA_MOBILE: flag,
CHROMERIA_IOS_BUILD_NUMBER: "invalid",
}),
).toBeNull();
});

it.each(["0", "000", "-1", "1.2", "4x", "1e2"])(
"rejects invalid upload build number %s",
(buildNumber) => {
expect(() =>
resolveChromeriaMobileIdentity({
CHROMERIA_MOBILE: "1",
CHROMERIA_IOS_BUILD_NUMBER: buildNumber,
}),
).toThrow("CHROMERIA_IOS_BUILD_NUMBER must be a positive integer.");
},
);

it.each([undefined, "", " "])("allows an unallocated build number (%s)", (buildNumber) => {
const identity = resolveChromeriaMobileIdentity({
CHROMERIA_MOBILE: "1",
CHROMERIA_IOS_BUILD_NUMBER: buildNumber,
});
expect(identity?.apply({ name: "Chromeria", slug: "t3-code" }).ios).not.toHaveProperty(
"buildNumber",
);
});

it("resolves production identity, extension identifiers and isolated OTA configuration", async () => {
const config = await resolveConfig({
APP_VARIANT: "production",
CHROMERIA_MOBILE: "1",
CHROMERIA_IOS_BUILD_NUMBER: " 4 ",
});
expect(config).toMatchObject({
name: "Chromeria",
scheme: "t3code",
icon: "../../assets/chromeria/chromeria-ios-1024.png",
updates: { enabled: false },
ios: {
bundleIdentifier: "md.toolbox.chromeria.mobile",
appleTeamId: "MCBD97AVW6",
buildNumber: "4",
icon: "../../assets/chromeria/chromeria-ios-1024.png",
entitlements: {
"keychain-access-groups": ["$(AppIdentifierPrefix)md.toolbox.chromeria.mobile"],
},
},
android: { package: "md.toolbox.chromeria.mobile" },
extra: {
clerk: { publishableKey: null, jwtTemplate: null },
relay: { url: null },
},
});
expect(config.updates).toEqual({ enabled: false });
expect(config).not.toHaveProperty("owner");
expect(config.extra).not.toHaveProperty("eas");
expect(config.plugins).toContainEqual([
"expo-widgets",
expect.objectContaining({
bundleIdentifier: "md.toolbox.chromeria.mobile.widgets",
groupIdentifier: "group.md.toolbox.chromeria.mobile",
}),
]);
expect(config.plugins).toContainEqual([
"expo-sharing",
expect.objectContaining({
ios: expect.objectContaining({
enabled: true,
extensionBundleIdentifier: "md.toolbox.chromeria.mobile.sharing",
appGroupId: "group.md.toolbox.chromeria.mobile",
}),
}),
]);
expect(config.plugins).toContainEqual([
"expo-splash-screen",
expect.objectContaining({ image: "../../assets/chromeria/chromeria-icon-1024.png" }),
]);
});

it("preserves optional Connect configuration from the final PR209 decision", async () => {
const config = await resolveConfig({
CHROMERIA_MOBILE: "1",
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY: "pk_test_fixture",
EXPO_PUBLIC_CLERK_JWT_TEMPLATE: "fixture-template",
EXPO_PUBLIC_CLERK_GOOGLE_IOS_URL_SCHEME: "fixture-oauth",
T3CODE_RELAY_URL: "https://relay.example.test",
});
expect(config.extra).toMatchObject({
clerk: { publishableKey: "pk_test_fixture", jwtTemplate: "fixture-template" },
relay: { url: "https://relay.example.test" },
EXPO_PUBLIC_CLERK_GOOGLE_IOS_URL_SCHEME: "fixture-oauth",
});
expect(config.updates).toEqual({ enabled: false });
expect(config.extra).not.toHaveProperty("eas");
});

it("retains the reduced-capability personal-team path", async () => {
const config = await resolveConfig({
APP_VARIANT: "production",
CHROMERIA_MOBILE: "1",
CHROMERIA_IOS_BUILD_NUMBER: "4",
T3CODE_IOS_PERSONAL_TEAM: "1",
T3CODE_IOS_PERSONAL_TEAM_BUNDLE_ID: "md.toolbox.chromeria.mobile",
});
expect(config.ios).toMatchObject({
bundleIdentifier: "md.toolbox.chromeria.mobile",
appleTeamId: "MCBD97AVW6",
buildNumber: "4",
});
expect(config.plugins).toContainEqual([
"@clerk/expo",
expect.objectContaining({ appleSignIn: false }),
]);
expect(config.plugins).toContainEqual([
"expo-sharing",
expect.objectContaining({ ios: expect.objectContaining({ enabled: false }) }),
]);
expect(config.plugins).not.toContainEqual(["expo-widgets", expect.anything()]);
expect(config.plugins).toContain("./plugins/withoutIosPersonalTeamCapabilities.cjs");
});
});
48 changes: 48 additions & 0 deletions apps/mobile/chromeria.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import type { ExpoConfig } from "expo/config";

import { BRAND_ASSET_PATHS } from "../../scripts/lib/brand-assets.ts";

/**
* Chromeria (toolboxmd fork) identity for the mobile app, used by app.config.ts.
*
* `CHROMERIA_MOBILE=1` builds the production variant as Chromeria under the
* fork's Apple team. OTA updates are off so the app never loads upstream's EAS
* bundles; TestFlight uploads deliver new builds instead, and App Store Connect
* needs a higher `CHROMERIA_IOS_BUILD_NUMBER` for every upload.
* The URL scheme stays `t3code`: widget deep links and linking prefixes use it.
*/
export function resolveChromeriaMobileIdentity(env: Readonly<Record<string, string | undefined>>) {
if (env.CHROMERIA_MOBILE !== "1") return null;

const buildNumber = env.CHROMERIA_IOS_BUILD_NUMBER?.trim();
if (buildNumber && !/^0*[1-9]\d*$/.test(buildNumber)) {
throw new Error("CHROMERIA_IOS_BUILD_NUMBER must be a positive integer.");
}

return {
variant: {
appName: "Chromeria",
iosBundleIdentifier: "md.toolbox.chromeria.mobile",
androidPackage: "md.toolbox.chromeria.mobile",
},
assets: {
appIcon: `../../${BRAND_ASSET_PATHS.chromeriaIosIconPng}`,
iosIcon: `../../${BRAND_ASSET_PATHS.chromeriaIosIconPng}`,
splashIcon: `../../${BRAND_ASSET_PATHS.chromeriaIconPng}`,
},
apply(config: ExpoConfig): ExpoConfig {
const { owner: _upstreamOwner, ...rest } = config;
const { eas: _upstreamEasProject, ...extra } = config.extra ?? {};
return {
...rest,
updates: { enabled: false },
ios: {
...config.ios,
appleTeamId: "MCBD97AVW6",
...(buildNumber ? { buildNumber } : {}),
},
extra,
};
},
};
}
Binary file added assets/chromeria/chromeria-ios-1024.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 12 additions & 4 deletions docs/fork-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,27 +155,33 @@ are evidence for review, not proof that a feature has been adopted upstream.
```json
{
"id": "branding",
"purpose": "Give desktop and web the Chromeria name, icons and independent desktop identity.",
"purpose": "Give desktop, web and the iOS app the Chromeria name, icons and independent identity.",
"issues": [
"https://github.com/toolboxmd/t3code/issues/12",
"https://github.com/toolboxmd/t3code/issues/13",
"https://github.com/toolboxmd/t3code/issues/14"
"https://github.com/toolboxmd/t3code/issues/14",
"https://github.com/toolboxmd/chromeria/issues/205"
],
"prs": [
"https://github.com/toolboxmd/t3code/pull/12",
"https://github.com/toolboxmd/t3code/pull/13",
"https://github.com/toolboxmd/t3code/pull/14"
"https://github.com/toolboxmd/t3code/pull/14",
"https://github.com/toolboxmd/chromeria/pull/209"
],
"newFiles": [
"apps/mobile/chromeria.ts",
"apps/mobile/chromeria.test.ts",
"apps/web/public/chromeria-mark.png",
"assets/chromeria/chromeria-icon-1024.png",
"assets/chromeria/chromeria-ios-1024.png",
"assets/chromeria/chromeria-web-apple-touch-180.png",
"assets/chromeria/chromeria-web-favicon-16x16.png",
"assets/chromeria/chromeria-web-favicon-32x32.png",
"assets/chromeria/chromeria-web-favicon.ico",
"assets/chromeria/chromeria-windows.ico"
],
"upstreamFiles": [
"apps/mobile/app.config.ts",
"apps/desktop/package.json",
"apps/desktop/src/app/DesktopAppIdentity.test.ts",
"apps/desktop/src/app/DesktopAppIdentity.ts",
Expand Down Expand Up @@ -213,7 +219,9 @@ are evidence for review, not proof that a feature has been adopted upstream.
"wordmark",
"favicon",
"Chromeria",
"auto-update"
"auto-update",
"bundleIdentifier",
"appleTeamId"
]
}
```
Expand Down
3 changes: 3 additions & 0 deletions docs/map.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ flowchart TD
%% OB: context
UA["Upstream absorption"]
%% UA: #370 PR #371 #166 PR #291
MI["Mobile identity"]
%% MI: #205 PR #209
BR["Bounded event reads"]
%% BR: #256 PR #267
PU["Provider updates"]
Expand Down Expand Up @@ -67,6 +69,7 @@ flowchart TD
VT --> ST
OB -.-> PM
UA --> PZ
MI --> UA
CK --- SP
TF -.-> SN
BR --- PU
Expand Down
1 change: 1 addition & 0 deletions scripts/fork-upstream-edits.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ apps/desktop/src/app/DesktopPreReadyFileSystem.test.ts
apps/desktop/src/app/DesktopPreReadyPlatform.test.ts
apps/desktop/src/app/DesktopUserData.test.ts
apps/desktop/src/app/DesktopUserData.ts
apps/mobile/app.config.ts
apps/server/src/auth/PairingGrantStore.test.ts
apps/server/src/provider/acp/AcpSessionRuntime.processTree.test.ts
apps/server/src/auth/RpcAuthorization.ts
Expand Down
2 changes: 2 additions & 0 deletions scripts/lib/brand-assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export const BRAND_ASSET_PATHS = {
// Chromeria (toolboxmd fork) brand, used by stable desktop and web builds.
// The 1024px source is the macOS rounded-square icon; the rest derive from it.
chromeriaIconPng: "assets/chromeria/chromeria-icon-1024.png",
// Opaque, full-bleed iOS app icon: the source scaled 108% over its navy background.
chromeriaIosIconPng: "assets/chromeria/chromeria-ios-1024.png",
chromeriaWindowsIconIco: "assets/chromeria/chromeria-windows.ico",
chromeriaWebFaviconIco: "assets/chromeria/chromeria-web-favicon.ico",
chromeriaWebFavicon16Png: "assets/chromeria/chromeria-web-favicon-16x16.png",
Expand Down
Loading