feat(export): wire History + Logs export JSON handlers (#1354) (#1367)

* feat(export): wire History + Logs export JSON handlers (#1354)

Replaces the `todoNoop` placeholders in App.tsx with real handlers that
serialize the current entries and trigger a browser download via the
existing `downloadJsonFile` helper.

- Adds `buildExportFilename(kind, serverId, now?)` next to the download
  helper. Produces sortable, filesystem-safe names like
  `inspector-history-alpha-2026-03-17T10-00-42.123Z.json` (URL-encodes
  server ids that contain slashes / spaces / colons, swaps `:` for `-`
  in the ISO timestamp so the file is safe on Windows).
- Wires `onExportHistory` (full MessageEntry list) and `onExportLogs`
  (the LogEntryData list the screen renders).
- Routes the existing Network export through the same filename helper
  for consistency.

Unit tests cover the filename builder, including the no-server-id
fallback, special-character encoding, and the default-`now` shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(export): tighten buildExportFilename signature + docs

- Introduce an `ExportKind = "history" | "logs" | "network"` union and
  use it as the type of the `kind` parameter. Typos like "hisotry" now
  fail at compile time and the supported export kinds are documented
  inline.
- Clarify the JSDoc: the segment is omitted when `serverId` is falsy
  (undefined OR empty string), matching the existing `serverId ? ... :
  undefined` check. New test pins the empty-string branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(logging): remove Copy All button from LogStreamPanel

Export now produces the same JSON the Copy All button used to dump to
the clipboard, so the button is redundant. History and Network panels
have no equivalent — dropping Copy All also brings Logs into parity.

Removes the prop from LogStreamPanel + LoggingScreen + InspectorView +
App.tsx, and updates the matching tests/stories. Tests now assert the
button is *not* in the document so a future re-introduction would have
to update the assertion deliberately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Cliff Hall 2026-05-27 19:06:28 -04:00 • committed by GitHub
commit a7178280f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -30,3 +30,33 @@ export function downloadJsonFile(filename: string, json: string): void {
URL.revokeObjectURL(url);
}
}
/**
* The categories of in-memory data the Inspector can export. Tightening
* `kind` to this union catches typos at call sites and documents the
* stable on-disk filename prefix.
*/
export type ExportKind = "history" | "logs" | "network";
/**
* Build a sortable export filename in the shape
* `inspector-<kind>-<server-id>-<ISO timestamp>.json`. The timestamp uses
* the standard ISO-8601 form with `:` swapped for `-` so the result is
* safe on Windows (which disallows `:` in filenames). Server id is
* passed through `encodeURIComponent` for the same reason — config ids
* are user-supplied and may contain slashes / spaces / colons.
*
* When `serverId` is falsy (undefined or empty) the segment is omitted;
* the rest of the filename still uniquely identifies the export by kind
* + time.
*/
export function buildExportFilename(
kind: ExportKind,
serverId: string | undefined,
now: Date = new Date(),
): string {
const iso = now.toISOString().replace(/:/g, "-");
const id = serverId ? encodeURIComponent(serverId) : undefined;
const segments = ["inspector", kind, ...(id ? [id] : []), iso];
return `${segments.join("-")}.json`;
}