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
10 changes: 3 additions & 7 deletions console/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import { type RegisteredPage, useExtPages } from '@/lib/ui-slots'
import { cn } from '@/lib/utils'
import { Configuration } from '@/pages/Configuration'
import { ExtPage } from '@/pages/Ext'
import { Github } from '@/pages/Github'
import { TracesV2 } from '@/pages/TracesV2'
import { Workers } from '@/pages/Workers'

Expand Down Expand Up @@ -107,8 +106,6 @@ export function App() {
<Configuration theme={theme} onThemeChange={setTheme} />
) : view === 'workers' ? (
<Workers />
) : view === 'github' ? (
<Github />
) : view === 'ext' ? (
<ExtPage onMissing={onExtMissing} />
) : (
Expand Down Expand Up @@ -147,15 +144,14 @@ function Header({
onOpenShortcuts,
}: HeaderProps) {
// Optional-worker entries appear only while their worker is present; a
// Only github remains a first-party nav entry; worktrees, memory and browser
// moved to injected UI (gated by script presence, not a nav flag).
const { githubAvailable } = useConversationsCtx()
// Every per-worker page moved to injected UI; the first-party nav is just
// traces + workers, so the header no longer reads worker presence here.
// Injected pages: the runtime analogue of worker-presence gating —
// presence is the script being loaded, which already tracks worker
// connectedness via trigger GC.
const extPages = useExtPages()
const viewOptions: { value: string; label: string }[] = [
...buildViewOptions(githubAvailable),
...buildViewOptions(),
...extPages.map((page) => ({
value: extNavValue(page),
label: page.title,
Expand Down
19 changes: 0 additions & 19 deletions console/web/src/hooks/use-github-status.test.ts

This file was deleted.

43 changes: 0 additions & 43 deletions console/web/src/hooks/use-github-status.ts

This file was deleted.

2 changes: 2 additions & 0 deletions console/web/src/hooks/use-hash-route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,14 @@ describe('migrated-page hash redirects', () => {
expect(normalizeExtHash('#/worktrees')).toBe('#/ext/worktree')
expect(normalizeExtHash('#/memory')).toBe('#/ext/memory')
expect(normalizeExtHash('#/browser')).toBe('#/ext/browser')
expect(normalizeExtHash('#/github')).toBe('#/ext/github')
})

it('resolves the injected page id from a legacy hash', () => {
expect(extPageFromHash(normalizeExtHash('#/worktrees'))).toBe('worktree')
expect(extPageFromHash(normalizeExtHash('#/memory'))).toBe('memory')
expect(extPageFromHash(normalizeExtHash('#/browser'))).toBe('browser')
expect(extPageFromHash(normalizeExtHash('#/github'))).toBe('github')
})

it('passes non-migrated hashes through unchanged', () => {
Expand Down
24 changes: 11 additions & 13 deletions console/web/src/hooks/use-hash-route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { useCallback, useEffect, useRef, useState } from 'react'
// `chat` is no longer a routed view; it's always-rendered as the side dock
// in App.tsx. Hash routes only pick which view fills the right pane. The
// component spec sheet + streaming playground moved to Storybook, so the
// first-party routed views are `traces`, `workers`, `github`, and
// `configuration`. `ext` is the injectable-UI prefix: worker-contributed pages
// first-party routed views are `traces`, `workers`, and `configuration`.
// `ext` is the injectable-UI prefix: worker-contributed pages
// route at `#/ext/<page-id>` — deliberately outside the first-party names so an
// injected page can never collide with or shadow `#/traces`, `#/workers`, ….
// Pages that migrated to injected UI (worktrees, memory, browser) keep their old
// first-party hash working via a redirect to `#/ext/<id>` — see MIGRATED_ROUTES.
export type View = 'configuration' | 'traces' | 'workers' | 'github' | 'ext'
// Pages that migrated to injected UI (worktrees, memory, browser, github) keep
// their old first-party hash working via a redirect to `#/ext/<id>` — see
// MIGRATED_ROUTES.
export type View = 'configuration' | 'traces' | 'workers' | 'ext'

export interface WorkersConfigurationRoute {
configurationId: string | null
Expand Down Expand Up @@ -77,7 +78,7 @@ export function normalizeWorkersConfigurationHash(hash: string): string | null {
}

function routeFromHash(rawHash: string): View | null {
// Migrated pages (worktrees, memory, browser) resolve via `#/ext/<id>`.
// Migrated pages (worktrees, memory, browser, github) resolve via `#/ext/<id>`.
const hash = normalizeExtHash(rawHash)
if (hash === '' || hash === '#' || hash === '#/' || hash === '#/traces') {
return 'traces'
Expand All @@ -91,9 +92,6 @@ function routeFromHash(rawHash: string): View | null {
if (hash === '#/workers' || hash.startsWith('#/workers/')) {
return 'workers'
}
if (hash === '#/github') {
return 'github'
}
if (hash.startsWith('#/ext/')) {
return 'ext'
}
Expand Down Expand Up @@ -121,8 +119,6 @@ function hashFor(view: View): string {
return '#/traces'
case 'workers':
return '#/workers'
case 'github':
return '#/github'
case 'configuration':
return '#/configuration'
// `ext` needs a page id; navigation to a specific extension page goes
Expand All @@ -142,8 +138,9 @@ export function useHashRoute(): [View, (next: View) => void] {
viewRef.current = view

useEffect(() => {
// Rewrite a legacy migrated hash (`#/worktrees`, `#/memory`, `#/browser`)
// to its `#/ext/<id>` form so the URL bar matches the resolved page. The view
// Rewrite a legacy migrated hash (`#/worktrees`, `#/memory`, `#/browser`,
// `#/github`) to its `#/ext/<id>` form so the URL bar matches the resolved
// page. The view
// state already used the normalized hash, so this is cosmetic.
const normalized = normalizeExtHash(window.location.hash)
if (normalized !== window.location.hash) replaceHash(normalized)
Comment on lines +141 to 146

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

Normalize migrated hashes on every hash change.

The rewrite runs only on mount. Later navigation to #/github resolves the ext view but leaves the legacy hash in the URL bar. Move normalization into the hashchange handler too.

Proposed fix
 useEffect(() => {
-  const normalized = normalizeExtHash(window.location.hash)
-  if (normalized !== window.location.hash) replaceHash(normalized)
-  const handle = () => {
-    const next = routeFromHash(window.location.hash)
+  const handle = () => {
+    const normalized = normalizeExtHash(window.location.hash)
+    if (normalized !== window.location.hash) replaceHash(normalized)
+    const next = routeFromHash(normalized)
     if (next !== null && next !== viewRef.current) setView(next)
   }
+  handle()
   window.addEventListener('hashchange', handle)
📝 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
// Rewrite a legacy migrated hash (`#/worktrees`, `#/memory`, `#/browser`,
// `#/github`) to its `#/ext/<id>` form so the URL bar matches the resolved
// page. The view
// state already used the normalized hash, so this is cosmetic.
const normalized = normalizeExtHash(window.location.hash)
if (normalized !== window.location.hash) replaceHash(normalized)
const handle = () => {
const normalized = normalizeExtHash(window.location.hash)
if (normalized !== window.location.hash) replaceHash(normalized)
const next = routeFromHash(normalized)
if (next !== null && next !== viewRef.current) setView(next)
}
handle()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@console/web/src/hooks/use-hash-route.ts` around lines 141 - 146, Move the
normalizeExtHash/replaceHash logic from the mount-only path into the hashchange
handler so every navigation event rewrites legacy hashes such as `#/github` to the
corresponding `#/ext/`<id> URL. Preserve the existing normalized view-state
resolution and avoid relying solely on the initial mount normalization.

Expand Down Expand Up @@ -201,6 +198,7 @@ const MIGRATED_ROUTES: Record<string, string> = {
'#/worktrees': 'worktree',
'#/memory': 'memory',
'#/browser': 'browser',
'#/github': 'github',
}

/**
Expand Down
11 changes: 0 additions & 11 deletions console/web/src/lib/conversations-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
type ConversationsApi,
useConversations,
} from '@/hooks/use-conversations'
import { isGithubAvailable, useGithubStatus } from '@/hooks/use-github-status'
import {
type HarnessStatus,
isHarnessAvailable,
Expand Down Expand Up @@ -83,12 +82,6 @@ interface ConversationsContextValue extends ConversationsApi {
* backend.
*/
memoryAvailable: boolean
/**
* Whether the optional `github` worker is connected — gates the Github
* page nav entry and its `github::*` RPC. Only meaningful on the real
* backend.
*/
githubAvailable: boolean
}

const ConversationsContext = createContext<ConversationsContextValue | null>(
Expand Down Expand Up @@ -120,9 +113,6 @@ export function ConversationsProvider({
const memoryAvailable = isMemoryAvailable(
useMemoryStatus(backend.id === 'real'),
)
const githubAvailable = isGithubAvailable(
useGithubStatus(backend.id === 'real'),
)
const {
modelOptions,
catalogKeys,
Expand Down Expand Up @@ -171,7 +161,6 @@ export function ConversationsProvider({
shellAvailable,
worktreeAvailable,
memoryAvailable,
githubAvailable,
}

return (
Expand Down
Loading
Loading