Skip to content

[feat] Make browser tabs describe the active page - #5704

Merged
mmabrouk merged 2 commits into
release/v0.108.1from
feat/browser-page-titles
Aug 3, 2026
Merged

[feat] Make browser tabs describe the active page#5704
mmabrouk merged 2 commits into
release/v0.108.1from
feat/browser-page-titles

Conversation

@mmabrouk

@mmabrouk mmabrouk commented Aug 3, 2026

Copy link
Copy Markdown
Member

Context

Agenta showed the same marketing title across product pages, agents, and chats. Multiple open tabs were hard to distinguish because the title did not follow the active page or session.

Changes

Pages now own semantic titles through a small shared PageTitle formatter.

Before: Agenta – the open-source workspace for building and running agents

After examples:

  • Home | Agenta
  • Marketing Coworker | Agenta for an empty agent chat
  • Draft the launch plan | Agenta after the first message
  • Observability | Marketing Coworker inside an agent

The active chat title follows the selected session and reacts to its first message or rename. Session titles are normalized and capped at 60 Unicode code points with an ellipsis. Primary project and agent navigation pages also receive stable titles.

The global marketing title remains a synchronous loading and unknown-route fallback. The asynchronous cloud scripts no longer write a competing title, so they cannot overwrite page-owned titles after mount.

Scope and risk

This is frontend-only. It adds no API, backend, storage, analytics, or data-fetching changes. Agent names come from the already-loaded current workflow artifact.

Authentication, workspace selection, archive, and deep-detail routes remain on the safe fallback. They are documented as a separate follow-up so this PR stays reviewable.

The main regression risk is title precedence between the global fallback and page heads. The fallback now mounts synchronously before page content, and live EE verification covered both project and agent contexts.

How to review

  1. Start with web/oss/src/components/PageTitle/ for formatting and fallback behavior.
  2. Review PlaygroundPageTitle.tsx and activeSessionTitleAtomFamily for empty-session, active-session, and rename behavior.
  3. Check the observability and primary route wrappers for semantic ownership.
  4. Confirm GlobalScripts owns the only fallback title and CloudScripts no longer competes.

Tests / notes

  • pnpm --filter @agenta/oss exec vitest run --reporter=default src/components/PageTitle/utils.test.ts src/components/AgentChatSlice/assets/pageTitle.test.ts src/components/AgentChatSlice/state/sessions.pageTitle.test.ts (12 passed)
  • pnpm lint-fix (passed; two existing TanStack Virtual compiler warnings)
  • pnpm type-check (OSS and EE passed)
  • git diff --check (passed)
  • Live EE dev verification: Home, project Observability, Settings, empty agent chat, first-message transition, and agent Observability all showed the expected title.
  • Independent review completed. Follow-up review found no remaining code or test blockers.

What to QA

  • Open Home, Prompts, Agents, Test sets, Evaluators, Evaluation runs, Annotation Queues, Observability, and Settings. Each tab should show the page name followed by | Agenta.
  • Open a new empty agent chat. The tab should show the agent name followed by | Agenta.
  • Send the first message. The tab should switch to the session title followed by | Agenta; a long title should end with an ellipsis and stay within 60 characters before the separator.
  • Switch sessions or rename the active session. The tab should update without a reload.
  • Open Overview, Registry, Evaluations, and Observability inside an agent. The tab should show the page name followed by the agent name.
  • Change Settings or Observability tabs and open a drawer or inspector. The containing page title should stay unchanged.

@dosubot dosubot Bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Aug 3, 2026
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview Aug 3, 2026 7:38pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added descriptive browser titles across home, project, settings, workflow, observability, and playground pages.
    • Chat titles now reflect session names or the first user message, with agent-name fallbacks.
    • Added context-aware workflow titles and consistent title formatting, including safe truncation.
  • Documentation

    • Added design, research, implementation plan, and status documentation for browser page titles.
  • Tests

    • Added coverage for title formatting, truncation, chat title selection, and session title updates.

Walkthrough

The PR adds frontend browser page-title documentation, shared title formatting, reactive agent-session titles, workflow-aware playground titles, and static titles across project and observability routes. It also removes competing demo title metadata.

Changes

Browser page-title system

Layer / File(s) Summary
Title contract and formatting
docs/design/browser-page-titles/*, web/oss/src/components/PageTitle/*
Defines title rules, normalization, Unicode-safe truncation, fallback formatting, and declarative PageTitle components.
Agent session title state
web/oss/src/components/AgentChatSlice/assets/*, web/oss/src/components/AgentChatSlice/state/*
Derives titles from persisted session names or first user messages, with agent-name fallback and reactive active-session updates.
Playground title wiring
web/oss/src/components/Playground/*, web/oss/src/components/PlaygroundRouter/*
Adds titles for onboarding, agent chats, empty workflow state, and standard playground views.
Route title coverage
web/oss/src/pages/w/..., web/oss/src/components/pages/observability/*, web/oss/src/components/Scripts/*
Adds titles to project, workflow, observability, and onboarding routes while removing the competing CloudScripts title block.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PlaygroundPageTitle
  participant ActiveSessionState
  participant AgentTitleSelector
  participant PageTitle
  participant Browser
  PlaygroundPageTitle->>ActiveSessionState: Read active session title data
  ActiveSessionState->>AgentTitleSelector: Provide session title and first user message
  AgentTitleSelector->>PageTitle: Provide selected title part
  PageTitle->>Browser: Set formatted document title
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 60.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main change: semantic browser tab titles for the active page.
Description check ✅ Passed The description directly explains the semantic page-title changes, affected states, scope, testing, and review steps.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/browser-page-titles

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mmabrouk mmabrouk added the need review Agent updated; awaiting Mahmoud's review label Aug 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9a23e50f-7bb4-4319-b606-bd629c802505

📥 Commits

Reviewing files that changed from the base of the PR and between 69e2165 and b6b7185.

📒 Files selected for processing (30)
  • docs/design/browser-page-titles/README.md
  • docs/design/browser-page-titles/context.md
  • docs/design/browser-page-titles/plan.md
  • docs/design/browser-page-titles/research.md
  • docs/design/browser-page-titles/status.md
  • web/ee/src/components/Scripts/assets/CloudScripts.tsx
  • web/oss/src/components/AgentChatSlice/assets/pageTitle.test.ts
  • web/oss/src/components/AgentChatSlice/assets/pageTitle.ts
  • web/oss/src/components/AgentChatSlice/state/sessions.pageTitle.test.ts
  • web/oss/src/components/AgentChatSlice/state/sessions.ts
  • web/oss/src/components/PageTitle/WorkflowPageTitle.tsx
  • web/oss/src/components/PageTitle/index.tsx
  • web/oss/src/components/PageTitle/utils.test.ts
  • web/oss/src/components/PageTitle/utils.ts
  • web/oss/src/components/Playground/Playground.tsx
  • web/oss/src/components/Playground/PlaygroundPageTitle.tsx
  • web/oss/src/components/PlaygroundRouter/index.tsx
  • web/oss/src/components/Scripts/GlobalScripts.tsx
  • web/oss/src/components/pages/observability/index.tsx
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/agents/index.tsx
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/annotations/index.tsx
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/apps/[app_id]/evaluations/index.tsx
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/apps/[app_id]/overview/index.tsx
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/apps/[app_id]/variants/index.tsx
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/apps/index.tsx
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/evaluations/index.tsx
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/evaluators/index.tsx
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/prompts/index.tsx
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/settings/index.tsx
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/testsets/index.tsx

Comment on lines +11 to +18
| Context | Title |
| --- | --- |
| Home | `Home | Agenta` |
| Empty agent chat | `<Agent name> | Agenta` |
| Agent chat after the session starts | `<Session title, at most 60 characters> | Agenta` |
| Project observability | `Observability | Agenta` |
| Agent observability | `Observability | <Agent name>` |
| Settings | `Settings | Agenta` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Keep each title example in one table cell.

The literal | characters in lines 13-18 are parsed as extra table separators. markdownlint reports MD056, and rendered documentation can lose part of each example. Encode or escape the separator in every value.

Proposed fix
-| Home | `Home | Agenta` |
+| Home | `Home` &`#124`; `Agenta` |

Apply the same change to all rows containing |.

🧰 Tools
🪛 markdownlint-cli2 (0.23.1)

[warning] 13-13: Table column count
Expected: 2; Actual: 3; Too many cells, extra data will be missing

(MD056, table-column-count)


[warning] 14-14: Table column count
Expected: 2; Actual: 3; Too many cells, extra data will be missing

(MD056, table-column-count)


[warning] 15-15: Table column count
Expected: 2; Actual: 3; Too many cells, extra data will be missing

(MD056, table-column-count)


[warning] 16-16: Table column count
Expected: 2; Actual: 3; Too many cells, extra data will be missing

(MD056, table-column-count)


[warning] 17-17: Table column count
Expected: 2; Actual: 3; Too many cells, extra data will be missing

(MD056, table-column-count)


[warning] 18-18: Table column count
Expected: 2; Actual: 3; Too many cells, extra data will be missing

(MD056, table-column-count)

Source: Linters/SAST tools

Comment on lines +7 to +15
export const truncateTitlePart = (value: string, maxLength: number): string => {
const normalized = normalizeTitlePart(value)
const characters = Array.from(normalized)
if (characters.length <= maxLength) return normalized
return `${characters
.slice(0, Math.max(0, maxLength - 1))
.join("")
.trimEnd()}…`
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Handle non-positive maxLength explicitly.

For maxLength equal to 0 or a negative value, the current code returns . That result has one code point and exceeds the requested maximum. Validate the limit or return an empty string for zero before adding the ellipsis. Add boundary tests.

Proposed fix
 export const truncateTitlePart = (value: string, maxLength: number): string => {
+    if (!Number.isInteger(maxLength) || maxLength < 0) {
+        throw new RangeError("maxLength must be a non-negative integer")
+    }
+    if (maxLength === 0) return ""
     const normalized = normalizeTitlePart(value)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const truncateTitlePart = (value: string, maxLength: number): string => {
const normalized = normalizeTitlePart(value)
const characters = Array.from(normalized)
if (characters.length <= maxLength) return normalized
return `${characters
.slice(0, Math.max(0, maxLength - 1))
.join("")
.trimEnd()}`
}
export const truncateTitlePart = (value: string, maxLength: number): string => {
if (!Number.isInteger(maxLength) || maxLength < 0) {
throw new RangeError("maxLength must be a non-negative integer")
}
if (maxLength === 0) return ""
const normalized = normalizeTitlePart(value)
const characters = Array.from(normalized)
if (characters.length <= maxLength) return normalized
return `${characters
.slice(0, Math.max(0, maxLength - 1))
.join("")
.trimEnd()}`
}

Comment on lines +7 to +11
const WorkflowPageTitle = ({title}: {title: string}) => {
const workflow = useAtomValue(currentWorkflowContextAtom).workflow
const workflowName = workflow?.name || workflow?.slug

return <PageTitle title={title} context={workflowName} />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve the global fallback while workflow context loads.

When currentWorkflowContextAtom has no workflow, this component still renders PageTitle with title. The formatter then sets a value such as Evaluations | Agenta instead of the documented global Agenta loading fallback. Render the fallback until a workflow name or slug is available.

Proposed fix
     const workflow = useAtomValue(currentWorkflowContextAtom).workflow
     const workflowName = workflow?.name || workflow?.slug

+    if (!workflowName) return <PageTitle />
     return <PageTitle title={title} context={workflowName} />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const WorkflowPageTitle = ({title}: {title: string}) => {
const workflow = useAtomValue(currentWorkflowContextAtom).workflow
const workflowName = workflow?.name || workflow?.slug
return <PageTitle title={title} context={workflowName} />
const WorkflowPageTitle = ({title}: {title: string}) => {
const workflow = useAtomValue(currentWorkflowContextAtom).workflow
const workflowName = workflow?.name || workflow?.slug
if (!workflowName) return <PageTitle />
return <PageTitle title={title} context={workflowName} />

Comment on lines +2 to +14
import WorkflowPageTitle from "@/oss/components/PageTitle/WorkflowPageTitle"
import RequireWorkflowKind from "@/oss/components/RequireWorkflowKind"
import {useAppId} from "@/oss/hooks/useAppId"

const AppEvaluationsPage = () => {
const appId = useAppId()
return (
<RequireWorkflowKind allowed={["app", "evaluator"]} currentRoute="evaluations">
<EvaluationsView scope="app" appId={appId} />
</RequireWorkflowKind>
<>
<WorkflowPageTitle title="Evaluations" />
<RequireWorkflowKind allowed={["app", "evaluator"]} currentRoute="evaluations">
<EvaluationsView scope="app" appId={appId} />
</RequireWorkflowKind>
</>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Render WorkflowPageTitle inside RequireWorkflowKind.

At Line [10], WorkflowPageTitle is a sibling of the guard. At Lines [11]-[13], the guard can render loading, not-found, or redirect states without its children. The title can therefore use an empty or previous workflow context during navigation instead of the global fallback title.

Move the title into the guarded fragment.

Proposed fix
 return (
-    <>
-        <WorkflowPageTitle title="Evaluations" />
-        <RequireWorkflowKind allowed={["app", "evaluator"]} currentRoute="evaluations">
+    <RequireWorkflowKind allowed={["app", "evaluator"]} currentRoute="evaluations">
+        <>
+            <WorkflowPageTitle title="Evaluations" />
             <EvaluationsView scope="app" appId={appId} />
-        </RequireWorkflowKind>
-    </>
+        </>
+    </RequireWorkflowKind>
 )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import WorkflowPageTitle from "@/oss/components/PageTitle/WorkflowPageTitle"
import RequireWorkflowKind from "@/oss/components/RequireWorkflowKind"
import {useAppId} from "@/oss/hooks/useAppId"
const AppEvaluationsPage = () => {
const appId = useAppId()
return (
<RequireWorkflowKind allowed={["app", "evaluator"]} currentRoute="evaluations">
<EvaluationsView scope="app" appId={appId} />
</RequireWorkflowKind>
<>
<WorkflowPageTitle title="Evaluations" />
<RequireWorkflowKind allowed={["app", "evaluator"]} currentRoute="evaluations">
<EvaluationsView scope="app" appId={appId} />
</RequireWorkflowKind>
</>
import WorkflowPageTitle from "`@/oss/components/PageTitle/WorkflowPageTitle`"
import RequireWorkflowKind from "`@/oss/components/RequireWorkflowKind`"
import {useAppId} from "`@/oss/hooks/useAppId`"
const AppEvaluationsPage = () => {
const appId = useAppId()
return (
<RequireWorkflowKind allowed={["app", "evaluator"]} currentRoute="evaluations">
<>
<WorkflowPageTitle title="Evaluations" />
<EvaluationsView scope="app" appId={appId} />
</>
</RequireWorkflowKind>
)

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Preview URL https://gateway-pr-5704.up.railway.app/w
Project agenta-oss-clone-spike
Image tag pr-5704-a64a26d
Status Deployed
Railway logs Open logs
Workflow logs View workflow run
Updated at 2026-08-03T19:47:12.367Z

EE ships its own copies of the project- and app-scoped evaluations pages
instead of re-exporting the OSS twins, so the new titles never reached
them. Both kept the marketing fallback on cloud.

The session auto-title cut at 60 UTF-16 units, so an emoji straddling the
cap left a lone surrogate that rendered as a replacement character in the
tab. Cut on code points instead, and drop a trailing lone surrogate when
the title arrives already cut from the server.
@mmabrouk
mmabrouk changed the base branch from main to release/v0.108.1 August 3, 2026 19:41
@mmabrouk
mmabrouk merged commit 2d43bce into release/v0.108.1 Aug 3, 2026
41 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

frontend need review Agent updated; awaiting Mahmoud's review size:XL This PR changes 500-999 lines, ignoring generated files. ux

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant