Compare commits

...
Author SHA1 Message Date
EthanHealy01 fb28de4d5e Merge branch 'main' into dev/ChangeBrowserLabelToMatchWorktree 2026-07-09 21:03:23 +01:00
EthanHealy01 eb754d12c3 feat(dev): prefix browser tab title with worktree name in dev
When running task dev / dev:all / dev:saas / dev:portal from a worktree,
the frontend dev server injects the worktree folder basename (e.g. wt1)
as a build-time constant, and the app prefixes the browser tab title with
it so concurrent worktrees are distinguishable instead of all reading
"Stirling PDF".

Only the folder basename is exposed (never path/host/user), and only at
vite dev-serve time — production builds inject an empty string and the
feature compiles to a no-op. Desktop (tauri dev) is unaffected.
2026-07-09 14:19:33 +01:00
5 changed files with 65 additions and 1 deletions
+6
View File
@@ -80,6 +80,12 @@ tasks:
OPEN: '{{.OPEN | default ""}}'
env:
BACKEND_URL: '{{.BACKEND_URL}}'
# Dev-only browser-tab label so concurrent worktrees are distinguishable.
# Only the worktree folder basename (e.g. "wt1") is exposed — never the
# full path, hostname, or user. Consumed at dev-serve time by vite.config
# and dropped from production builds.
STIRLING_DEV_LABEL:
sh: basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
cmds:
- npx vite editor --mode {{.MODE}} --port {{.PORT}}{{if .OPEN}} --open{{end}}
@@ -0,0 +1,39 @@
/**
* Prefixes the browser tab title with the current worktree name during local
* development so multiple concurrently-running worktrees (e.g. wt1, wt2, spdf1)
* are distinguishable at a glance instead of all showing "Stirling PDF".
*
* The label is injected as a build-time constant by vite.config, sourced from
* the top-level dev tasks. It is an empty string in production builds, so this
* whole feature compiles away to a no-op outside `vite` dev-serve.
*/
const LABEL =
typeof __DEV_WORKTREE_LABEL__ === "string" ? __DEV_WORKTREE_LABEL__ : "";
export function applyDevWorktreeLabel(): void {
if (!LABEL || typeof document === "undefined") {
return;
}
const prefix = `[${LABEL}] `;
const ensurePrefixed = () => {
if (!document.title.startsWith(prefix)) {
// The app rewrites document.title on route/tool changes; re-apply the
// prefix on top of whatever the app just set.
document.title = prefix + document.title;
}
};
ensurePrefixed();
const titleEl = document.querySelector("title");
if (titleEl) {
new MutationObserver(ensurePrefixed).observe(titleEl, {
childList: true,
characterData: true,
subtree: true,
});
}
}
+3
View File
@@ -14,9 +14,12 @@ import { BrowserRouter } from "react-router-dom";
import App from "@app/App";
import "@app/i18n"; // Initialize i18next
import { BASE_PATH } from "@app/constants/app";
import { applyDevWorktreeLabel } from "@app/utils/applyDevWorktreeLabel";
import { startEagerWasmCompilation } from "@app/services/wasmPrecompiler";
applyDevWorktreeLabel();
if (typeof window !== "undefined") {
const scheduleCompilation = () => {
if (typeof requestIdleCallback === "function") {
+7
View File
@@ -29,3 +29,10 @@ interface ImportMetaEnv {
interface ImportMeta {
readonly env: ImportMetaEnv;
}
/**
* Dev-only worktree folder basename injected by vite.config at dev-serve time
* (empty string in production builds). Used to prefix the browser tab title so
* concurrent worktrees are distinguishable.
*/
declare const __DEV_WORKTREE_LABEL__: string;
+10 -1
View File
@@ -182,7 +182,13 @@ const TSCONFIG_MAP: Record<BuildMode, string> = {
prototypes: "./tsconfig.prototypes.vite.json",
};
export default defineConfig(async ({ mode }) => {
export default defineConfig(async ({ mode, command }) => {
// Dev-only browser-tab label (worktree folder basename) surfaced by the
// top-level dev tasks so concurrent worktrees have distinguishable tabs.
// Only injected during `vite` (dev serve) — never baked into a production
// build — and carries only the folder name, no path/host/user info.
const devWorktreeLabel =
command === "serve" ? (process.env.STIRLING_DEV_LABEL ?? "") : "";
// Load env files relative to this config (frontend/editor/), regardless of
// where the build was invoked from. The previous `process.cwd()` worked when
// this file lived at frontend/, but after the editor was moved under
@@ -250,6 +256,9 @@ export default defineConfig(async ({ mode }) => {
};
return {
define: {
__DEV_WORKTREE_LABEL__: JSON.stringify(devWorktreeLabel),
},
plugins: [
react(),
...(runSubpath ? [subpathBareRedirectPlugin(runSubpath)] : []),