diff --git a/apps/desktop/src/app/DesktopApp.ts b/apps/desktop/src/app/DesktopApp.ts index e47eea0f1e9f..6a07bb06db9c 100644 --- a/apps/desktop/src/app/DesktopApp.ts +++ b/apps/desktop/src/app/DesktopApp.ts @@ -31,6 +31,7 @@ import * as DesktopShellEnvironment from "../shell/DesktopShellEnvironment.ts"; import * as DesktopState from "./DesktopState.ts"; import * as DesktopUpdates from "../updates/DesktopUpdates.ts"; import * as DesktopWslBackend from "../wsl/DesktopWslBackend.ts"; +import { desktopAppDisplayName } from "./desktopDistro.ts"; const DEFAULT_DESKTOP_BACKEND_PORT = 3773; const MAX_TCP_PORT = 65_535; @@ -131,7 +132,7 @@ const handleFatalStartupError = Effect.fn("desktop.startup.handleFatalStartupErr const wasQuitting = yield* Ref.getAndSet(state.quitting, true); if (!wasQuitting) { yield* electronDialog.showErrorBox( - "T3 Code failed to start", + `${desktopAppDisplayName()} failed to start`, `Stage: ${stage}\n${message}${detail}`, ); } diff --git a/apps/desktop/src/app/desktopDistro.ts b/apps/desktop/src/app/desktopDistro.ts index 52d680c93208..e9b5fc82c949 100644 --- a/apps/desktop/src/app/desktopDistro.ts +++ b/apps/desktop/src/app/desktopDistro.ts @@ -69,3 +69,19 @@ export function resolveRuntimeDesktopDistro(input: { linuxWmClass: input.isDevelopment ? "t3code-dev" : "t3code", }; } + +/** + * The product name for messages the user reads, resolved from the running + * build. Main-process code that only needs a name (an error dialog, a keyring + * hint) can call this instead of threading DesktopEnvironment through. + */ +export function desktopAppDisplayName( + env: NodeJS.ProcessEnv = process.env, + appPath?: string, +): string { + return resolveRuntimeDesktopDistro({ + env, + ...(appPath === undefined ? {} : { appPath }), + isDevelopment: false, + }).baseName; +} diff --git a/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.ts b/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.ts index 201492f0e4c1..93f896612fe6 100644 --- a/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.ts +++ b/apps/desktop/src/backend/DesktopLocalEnvironmentAuth.ts @@ -10,6 +10,7 @@ import * as Semaphore from "effect/Semaphore"; import * as HttpClient from "effect/unstable/http/HttpClient"; import * as DesktopBackendPool from "./DesktopBackendPool.ts"; +import { desktopAppDisplayName } from "../app/desktopDistro.ts"; export class DesktopLocalEnvironmentAuthBackendNotConfiguredError extends Schema.TaggedErrorClass()( "DesktopLocalEnvironmentAuthBackendNotConfiguredError", @@ -71,7 +72,7 @@ export const make = Effect.gen(function* () { httpBaseUrl: config.httpBaseUrl.href, credential, clientMetadata: { - label: "T3 Code Desktop", + label: `${desktopAppDisplayName()} Desktop`, deviceType: "desktop", }, }).pipe( diff --git a/apps/desktop/src/linuxSecretStorage.ts b/apps/desktop/src/linuxSecretStorage.ts index fe3e21eadb92..fa9be7ac106b 100644 --- a/apps/desktop/src/linuxSecretStorage.ts +++ b/apps/desktop/src/linuxSecretStorage.ts @@ -1,3 +1,5 @@ +import { desktopAppDisplayName } from "./app/desktopDistro.ts"; + export type LinuxPasswordStorePreference = | "auto" | "gnome-libsecret" @@ -112,11 +114,11 @@ export function resolveLinuxSecretStorageUnavailableMessage(input: { } function getGnomeKeyringRemediationMessage(): string { - return "T3 Code could not access GNOME Keyring to save this environment credential. Install and start GNOME Keyring, then restart T3 Code."; + return `${desktopAppDisplayName()} could not access GNOME Keyring to save this environment credential. Install and start GNOME Keyring, then restart ${desktopAppDisplayName()}.`; } function getKWalletRemediationMessage(): string { - return "T3 Code could not access KWallet to save this environment credential. Enable the KDE wallet subsystem in System Settings, then restart T3 Code."; + return `${desktopAppDisplayName()} could not access KWallet to save this environment credential. Enable the KDE wallet subsystem in System Settings, then restart ${desktopAppDisplayName()}.`; } // Advisory only: this picks between the GNOME Keyring and KWallet wording in the failure notice. It diff --git a/apps/desktop/src/ssh/DesktopSshEnvironment.ts b/apps/desktop/src/ssh/DesktopSshEnvironment.ts index 31e84ae995ed..bfaa5706980c 100644 --- a/apps/desktop/src/ssh/DesktopSshEnvironment.ts +++ b/apps/desktop/src/ssh/DesktopSshEnvironment.ts @@ -25,6 +25,7 @@ import * as HttpClient from "effect/unstable/http/HttpClient"; import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner"; import * as DesktopSshPasswordPrompts from "./DesktopSshPasswordPrompts.ts"; +import { desktopAppDisplayName } from "../app/desktopDistro.ts"; export type DesktopSshEnvironmentRuntimeServices = | ChildProcessSpawner.ChildProcessSpawner @@ -96,7 +97,7 @@ export function toSshPasswordPromptError( break; case "DesktopSshPromptWindowUnavailableError": case "DesktopSshPromptPresentationError": - message = "T3 Code window is not available for SSH authentication."; + message = `${desktopAppDisplayName()} window is not available for SSH authentication.`; break; case "DesktopSshPromptTimedOutError": message = `SSH authentication timed out for ${cause.destination}.`; diff --git a/apps/desktop/src/wsl/DesktopWslEnvironment.ts b/apps/desktop/src/wsl/DesktopWslEnvironment.ts index 164117727eaa..5a5da1b768ec 100644 --- a/apps/desktop/src/wsl/DesktopWslEnvironment.ts +++ b/apps/desktop/src/wsl/DesktopWslEnvironment.ts @@ -14,6 +14,7 @@ import { satisfiesSemverRange } from "@t3tools/shared/semver"; import * as DesktopEnvironment from "../app/DesktopEnvironment.ts"; import { parseWslDistroList, type WslDistro } from "./wslPathParsing.ts"; +import { desktopAppDisplayName } from "../app/desktopDistro.ts"; const PROCESS_TERMINATE_GRACE = Duration.seconds(1); const LIST_TIMEOUT = Duration.seconds(8); @@ -220,7 +221,7 @@ const NODE_PTY_PREBUILD_MISSING_EXIT_CODE = 4; export const formatNodePtyProbeFailureReason = (exitCode: number): string | null => exitCode === NODE_PTY_PREBUILD_MISSING_EXIT_CODE - ? "WSL support is missing from this T3 Code build: the packaged Linux node-pty binary was not included. Rebuild the Windows artifact with `--wsl-prebuild ` or install a build that includes WSL support." + ? `WSL support is missing from this ${desktopAppDisplayName()} build: the packaged Linux node-pty binary was not included. Rebuild the Windows artifact with \`--wsl-prebuild \` or install a build that includes WSL support.` : null; const NODE_PTY_PROBE_SCRIPT = ( diff --git a/apps/server/src/bin.ts b/apps/server/src/bin.ts index 35109ac9f6bc..5c19f90f215e 100644 --- a/apps/server/src/bin.ts +++ b/apps/server/src/bin.ts @@ -21,6 +21,7 @@ import { runServerCommand, serveCommand, startCommand } from "./cli/server.ts"; import { serviceCommand } from "./cli/service.ts"; import { servicePreflightCommand } from "./cli/servicePreflight.ts"; import { triageCommand } from "./cli/triage.ts"; +import { resolveAppDisplayName } from "./appDisplayName.ts"; const DESKTOP_OPEN_POLL_ATTEMPTS = 40; const DESKTOP_OPEN_POLL_DELAY = Duration.millis(500); @@ -50,7 +51,7 @@ const openProjectViaDesktopOrLiveServer = Effect.fn("openProjectViaDesktopOrLive } yield* Console.error( - "Launched T3 Code desktop, but it did not become ready in time. Falling back to the CLI server.", + `Launched ${resolveAppDisplayName()} desktop, but it did not become ready in time. Falling back to the CLI server.`, ); return false; }, @@ -85,7 +86,7 @@ const connectUnavailableCommand = Command.make("connect", { export const makeCli = ({ cloudEnabled = hasCloudPublicConfig } = {}) => Command.make("t3", { ...sharedServerCommandFlags }).pipe( Command.withDescription( - "Run the T3 Code server, or open a directory in the desktop app / a running server.", + `Run the ${resolveAppDisplayName()} server, or open a directory in the desktop app / a running server.`, ), Command.withHandler((flags) => Effect.gen(function* () { diff --git a/apps/server/src/plugins/CodexPluginMarketplace.ts b/apps/server/src/plugins/CodexPluginMarketplace.ts index c20a9a2c0f9c..4b9753804d14 100644 --- a/apps/server/src/plugins/CodexPluginMarketplace.ts +++ b/apps/server/src/plugins/CodexPluginMarketplace.ts @@ -60,6 +60,7 @@ import { deriveProviderInstanceConfigMap } from "../provider/Layers/ProviderInst import * as ServerSettings from "../serverSettings.ts"; import { collectUint8StreamText } from "../stream/collectUint8StreamText.ts"; import * as McpOAuthRuntime from "./McpOAuthRuntime.ts"; +import { resolveAppDisplayName } from "../appDisplayName.ts"; import { CHATGPT_PUBLIC_MARKETPLACE_NAME, CHATGPT_PUBLIC_PLUGIN_SEARCH_MAX_PAGES, @@ -3028,7 +3029,7 @@ const makeCodexPluginRuntime = ( yield* client.request("initialize", { clientInfo: { name: "t3code_plugin_marketplace", - title: "T3 Code Plugin Marketplace", + title: `${resolveAppDisplayName()} Plugin Marketplace`, version: "0.1.0", }, capabilities: { experimentalApi: true }, diff --git a/apps/server/src/plugins/McpOAuthRuntime.ts b/apps/server/src/plugins/McpOAuthRuntime.ts index 460062d74ef2..f3234f14967e 100644 --- a/apps/server/src/plugins/McpOAuthRuntime.ts +++ b/apps/server/src/plugins/McpOAuthRuntime.ts @@ -23,6 +23,7 @@ import { HostProcessEnvironment, HostProcessWorkingDirectory } from "@t3tools/sh import { resolveSpawnCommand } from "@t3tools/shared/shell"; import * as ProcessRunner from "../processRunner.ts"; +import { resolveAppDisplayName } from "../appDisplayName.ts"; export type McpOAuthHarness = Extract; @@ -517,7 +518,7 @@ export const make = (options: McpOAuthRuntimeOptions = {}) => yield* client.request("initialize", { clientInfo: { name: "t3code_mcp_oauth", - title: "T3 Code MCP Authentication", + title: `${resolveAppDisplayName()} MCP Authentication`, version: "0.1.0", }, capabilities: { experimentalApi: true }, diff --git a/apps/server/src/serverRuntimeStartup.ts b/apps/server/src/serverRuntimeStartup.ts index 03ceb6d37712..67bee1d5758a 100644 --- a/apps/server/src/serverRuntimeStartup.ts +++ b/apps/server/src/serverRuntimeStartup.ts @@ -39,6 +39,7 @@ import * as ProviderSessionReaper from "./provider/Services/ProviderSessionReape import * as SessionStartupReconciler from "./provider/Services/SessionStartupReconciler.ts"; import { forkParked } from "./serverActivation.ts"; import * as ServiceLauncherClient from "./cloud/serviceLauncherClient.ts"; +import { resolveAppDisplayName } from "./appDisplayName.ts"; import { formatHeadlessServeOutput, formatHostForUrl, @@ -445,7 +446,7 @@ export const make = (options?: StartupOptions) => const startupBrowserTarget = yield* resolveStartupBrowserTarget; if (serverConfig.mode !== "desktop") { yield* Effect.logInfo( - "Authentication required. Open T3 Code using the pairing URL.", + `Authentication required. Open ${resolveAppDisplayName()} using the pairing URL.`, ).pipe(Effect.annotateLogs({ pairingUrl: startupBrowserTarget })); } yield* runStartupPhase("browser.open", maybeOpenBrowser(startupBrowserTarget)); diff --git a/apps/server/src/startupAccess.ts b/apps/server/src/startupAccess.ts index 7df131669bab..9e0a03a00783 100644 --- a/apps/server/src/startupAccess.ts +++ b/apps/server/src/startupAccess.ts @@ -6,6 +6,7 @@ import { HttpServer } from "effect/unstable/http"; import { ServerConfig } from "./config.ts"; import * as EnvironmentAuth from "./auth/EnvironmentAuth.ts"; +import { resolveAppDisplayName } from "./appDisplayName.ts"; export interface HeadlessServeAccessInfo { readonly connectionString: string; @@ -121,7 +122,7 @@ export const renderTerminalQrCode = (value: string, margin = 2): string => { export const formatHeadlessServeOutput = (accessInfo: HeadlessServeAccessInfo): string => [ - "T3 Code server is ready.", + `${resolveAppDisplayName()} server is ready.`, `Connection string: ${accessInfo.connectionString}`, `Token: ${accessInfo.token}`, `Pairing URL: ${accessInfo.pairingUrl}`, diff --git a/apps/server/src/textGeneration/OpenCodeTextGeneration.ts b/apps/server/src/textGeneration/OpenCodeTextGeneration.ts index 88d5d7b84696..cdbf7e49bbc6 100644 --- a/apps/server/src/textGeneration/OpenCodeTextGeneration.ts +++ b/apps/server/src/textGeneration/OpenCodeTextGeneration.ts @@ -33,6 +33,7 @@ import { clampPullRequestRankings, } from "./TextGenerationUtils.ts"; import * as OpenCodeRuntime from "../provider/opencodeRuntime.ts"; +import { resolveAppDisplayName } from "../appDisplayName.ts"; const OPENCODE_TEXT_GENERATION_IDLE_TTL = "30 seconds"; @@ -397,7 +398,7 @@ export const makeOpenCodeTextGeneration = Effect.fn("makeOpenCodeTextGeneration" const session = yield* Effect.tryPromise({ try: () => client.session.create({ - title: `T3 Code ${input.operation}`, + title: `${resolveAppDisplayName()} ${input.operation}`, permission: [{ permission: "*", pattern: "*", action: "deny" }], }), catch: (cause) => diff --git a/apps/web/src/components/Icons.tsx b/apps/web/src/components/Icons.tsx index 59a91c618d31..39b690634b8b 100644 --- a/apps/web/src/components/Icons.tsx +++ b/apps/web/src/components/Icons.tsx @@ -705,12 +705,16 @@ export const MtModelIcon: Icon = ({ className, ...props }) => ( className={cn("fill-current", className)} aria-hidden > - {/* The Munim mark, same path the app icon draws. */} - + {/* The Munim mark, same path the app icon draws. Scaled about the centre so + it reads at the same optical size as the other provider marks, which + fill more of their own box. */} + + + ); diff --git a/assets/munim/munim-icon-dark-1024.png b/assets/munim/munim-icon-dark-1024.png index 54c75a5d435e..0084d0905f02 100644 Binary files a/assets/munim/munim-icon-dark-1024.png and b/assets/munim/munim-icon-dark-1024.png differ diff --git a/assets/munim/munim-icon-light-1024.png b/assets/munim/munim-icon-light-1024.png index c9280e659758..9e8ec1f23f3c 100644 Binary files a/assets/munim/munim-icon-light-1024.png and b/assets/munim/munim-icon-light-1024.png differ diff --git a/native/t3-desktop-mcp-rs/src/browser.rs b/native/t3-desktop-mcp-rs/src/browser.rs index 5ee02b4df69c..73ab3f3139f5 100644 --- a/native/t3-desktop-mcp-rs/src/browser.rs +++ b/native/t3-desktop-mcp-rs/src/browser.rs @@ -1,4 +1,4 @@ -//! Agent-owned Chrome tabs, via the T3 Code Chrome extension. +//! Agent-owned Chrome tabs, via the MT Code Chrome extension. //! //! Chrome owns the lifetime of a native messaging host: it spawns the host when //! the extension connects and speaks 4-byte-length-prefixed JSON over that @@ -172,7 +172,7 @@ impl BrowserBridge { pub fn call(&mut self, command: &str, args: &Value) -> Result { if !self.connected() { return Err(format!( - "browser_{command} needs the T3 Code Chrome extension, which is not connected. \ + "browser_{command} needs the MT Code Chrome extension, which is not connected. \ Install it from native/t3-chrome-extension, or use the desktop tools instead: \ get_app_state on the browser window, then click" )); diff --git a/native/t3-desktop-mcp/Sources/BrowserBridge.swift b/native/t3-desktop-mcp/Sources/BrowserBridge.swift index 932a2ba74e48..ead169883e80 100644 --- a/native/t3-desktop-mcp/Sources/BrowserBridge.swift +++ b/native/t3-desktop-mcp/Sources/BrowserBridge.swift @@ -396,7 +396,7 @@ final class BrowserBridge { lock.lock() guard clientFD >= 0 else { lock.unlock() - return .failure("the T3 Code Chrome extension is not connected") + return .failure("the MT Code Chrome extension is not connected") } nextID += 1 let id = nextID diff --git a/native/t3-desktop-mcp/Sources/main.swift b/native/t3-desktop-mcp/Sources/main.swift index c7ff574f49ab..05b39e2bf73f 100644 --- a/native/t3-desktop-mcp/Sources/main.swift +++ b/native/t3-desktop-mcp/Sources/main.swift @@ -2014,7 +2014,7 @@ func toolBrowserPressKey(_ args: [String: Any]) -> String { func toolBrowserCloseAllTabs(_ args: [String: Any]) -> String { guard BrowserBridge.shared.isConnected else { - return "error: the T3 Code Chrome extension is not connected" + return "error: the MT Code Chrome extension is not connected" } return bridgeText(BrowserBridge.shared.call("close_all_tabs")) { payload in let closed = payload["closed"] as? Int ?? 0