Add pop-out button to open a section in its own tab (ticket 036)

Each of the four primary sections (Log viewer, Params, Metrics,
Dashboards) gets a PopOutButton next to its CollapseToggle/HelpIcon that
opens `?section=<id>` in a new tab via window.open. App.tsx branches on
that query param to render just the one section full-page, with no
NavMenu/FilterBar/sibling chrome — Metrics/Dashboards get the range that
was active at pop-out time via startMs/endMs query params (they have no
LogTimeline of their own to pick one), Params gets a small self-contained
params fetch since it has no LogPreview sibling to share state with, and
the popped-out Log viewer owns its own copy of the log/timeline polling
state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
This commit is contained in:
Hugh Kaznowski
2026-09-05 16:49:12 +01:00
co-authored by Claude Sonnet 5
parent 7db1e0f055
commit 0c4b596e3c
6 changed files with 206 additions and 48 deletions
@@ -1,4 +1,5 @@
import { useState } from 'react'
import type { TimeRange } from './LogTimeline'
/// localStorage key for which of the four primary sections (log viewer,
/// params, metrics, dashboards) are collapsed (ticket 035). Mirrors
@@ -57,3 +58,34 @@ export function CollapseToggle({ collapsed, onToggle }: { collapsed: boolean; on
</button>
)
}
/// The four primary sections that can be popped out into their own tab
/// (ticket 036) — matches the `?section=` query param a standalone page reads.
export type PopOutSectionId = 'log' | 'params' | 'metrics' | 'dashboards'
function popOutUrl(section: PopOutSectionId, range?: TimeRange | null): string {
const query = new URLSearchParams({ section })
if (range) {
query.set('startMs', String(range.startMs))
query.set('endMs', String(range.endMs))
}
return `?${query.toString()}`
}
/// Opens this section, full-page and with no surrounding chrome, in a new
/// tab. `range` is only needed for sections without their own LogTimeline —
/// the popped-out page has no way to pick a range itself, so the current one
/// is carried over via query params (see `App.tsx`'s standalone rendering).
export function PopOutButton({ section, range }: { section: PopOutSectionId; range?: TimeRange | null }) {
return (
<button
type="button"
onClick={() => window.open(popOutUrl(section, range), '_blank')}
title="Open in new tab"
aria-label="Open in new tab"
className="flex h-4 w-4 shrink-0 items-center justify-center rounded-full border border-border-soft text-[10px] leading-none text-text-faint hover:border-border-strong hover:text-text-secondary"
>
↗
</button>
)
}