feat(export): export menu wiring and dialog chrome match the mock#1555

openedSep 21, 2026byMMatt Donovan

Summary

This ticket groups 8 places where the app has drifted from, or is missing part of, the 2026-09-14 design mock, so they can be fixed together. The direction for every design row here is mock: the app moves to the design. The maintainer ruled on 2026-09-20 that no design gap should be left to drift.

Design rows

  • Menu: which item opens the dialog (drifted, Export dialog, row). Both menus carry the same two items in the same order, but the wiring is swapped. In the mock, Export case file opens the export dialog and Export evidence report is a dead stub that only closes the menu. In the app, Export evidence report opens the dialog and Export case file runs a case-archive export immediately, with no dialog and no confirmation, so a tester following the mock gets a file-save dialog for a different artifact.
    • Mock: Birdbrain.dc.html:279-282 and 283-286
    • App: src/renderer/components/export/ExportMenu.tsx:149-159 and 160-167
  • Dialog: subtitle or purpose line (mock-ahead, Export dialog, row). The mock puts a one-line explanation under the heading, the same sentence the Dashboard first-run card uses. The app's dialog opens straight into the demo notice or the Scope row with no explanation of what the dialog produces.
    • Mock: Birdbrain.dc.html:3708
    • App: none
  • Dialog: header close button (mock-ahead, Export dialog, row). The mock has a 28px labelled close button in the top-right of the header. The app has no header close control: the only ways out of the form are the footer Cancel button and clicking the backdrop. Neither side binds escape.
    • Mock: Birdbrain.dc.html:3710-3712
    • App: src/renderer/components/export/ExportDialog.tsx:226-237
  • Dialog: scope row chrome (drifted, Export dialog, row). The mock draws Scope as a bordered surface-filled box: a scope-dependent icon, a small muted caption, then the value right-aligned in bold primary text. The app renders a plain label plus one line of secondary text with no container, no icon and no emphasis, so the row reads as body copy rather than as a stated scope.
    • Mock: Birdbrain.dc.html:3716-3720
    • App: src/renderer/components/export/ExportDialog.tsx:241-247
  • Dialog: selected-item count on the disclosure row (mock-ahead, Export dialog, row). The mock right-aligns a live count on the collapsed disclosure row, so the operator knows how much is included without expanding it. The app's row ends at the label, collapsed or expanded; a port would need a computed denominator, since the app's checklist length varies by export class.
    • Mock: Birdbrain.dc.html:3739, value at 6149
    • App: src/renderer/components/export/ExportDialog.tsx:291-304
  • Dialog: purpose or authority field placement (drifted, Export dialog, row). The placeholder and caption are identical, but the mock nests the field inside the custody card between the field grid and the signature rules, where it reads as a cover-sheet line being filled in. The app floats it as a standalone section below the custody sentence and the pinned list, detached from any statement of where the value ends up, which follows from the dropped card.
    • Mock: Birdbrain.dc.html:3774-3777
    • App: src/renderer/components/export/ExportDialog.tsx:448-456
  • Dialog: primary action button label and icon (drifted, Export dialog, row). Mock: an accent button with a file-out icon naming the case file. App: a plain accent button reading Export, or Try again after a failure, with no icon. The mock's Cancel is outline where the app's is ghost.
    • Mock: Birdbrain.dc.html:3816-3819
    • App: src/renderer/components/export/ExportDialog.tsx:478-480
  • Dialog: width (drifted, Export dialog, row). The mock's dialog is 560px wide, which is what lets the custody field grid sit in two columns and the checklist rows carry a right-aligned meta string. The app is 480px. The width difference is downstream of the missing grid and meta column rather than independent of them.
    • Mock: Birdbrain.dc.html:3703 (560px)
    • App: src/renderer/components/export/ExportDialog.tsx:202 (480px)

Related open issues

#1335

Source

  • Design rows: docs/plans/2026-09-19-mock-reconciliation.md at 5902b28f0b71. Mock line numbers refer to docs/design-handoff/2026-09-14-design-project-export/Birdbrain.dc.html.

Direction-list rows, added 2026-09-23

These rows were held out of the first grouping because moving the app to the mock touched a recorded ruling or an evidence surface. The maintainer read the direction list on 2026-09-23 and decided each of them mock: the app moves to the design. Source: docs/plans/2026-09-20-design-gap-ticket-batch.md.

  • Dialog: chain-of-custody preview card (mock-ahead, Export dialog, row). The mock previews the cover sheet: a card headed Chain of custody with a chain-intact pill and verified count, a two-column grid of nine read-only fields (case, case number, examiner, organisation, exported, contents, ledger root hash, signing key, tool), ruled signature and date lines, and a footer saying the sheet is generated at export time and signed with the local key. The app renders one sentence, so nothing about this case is previewed. The earlier ruling was narrow, to hide or restyle the card for a working copy and to say operator; dropping the grid was not ruled.
    • App: src/renderer/components/export/ExportDialog.tsx:398-410
  • Dialog: manifest-scope note under the scope row (drifted, Export dialog, row). The mock always prints the mono note, naming the case scope for a whole-case export. The app renders it only for a selection, so a whole-case export states nothing about what the manifest records. Add the whole-case line and keep the app's sentence saying the manifest chain ships complete.
    • App: src/renderer/components/export/ExportDialog.tsx:248-253
  • Dialog: per-item metadata column (mock-ahead, Export dialog, row). Every mock checklist row carries a right-aligned muted meta string for this case: formats with item count and size, hash per artifact, note count, selector and hit counts, signed status. The app's rows are label-only, so nothing tells the operator how large the package will be or how many notes are included before they export. Add the four strings that were not ruled out; skip the two that were.
    • App: src/renderer/components/export/ExportDialog.tsx:348-358

The custody preview card is evidence-adjacent, so the pull request owes an Evidence impact section and goes to human review.