Skip to content

feat(ux): show empty-state UI when macro fails to load (NULL_DIAGRAM) #151

Description

@MrCoder

Summary

When a Forge macro fails to load (orphan customContentId, missing customContentId, throw before render, etc.), the viewer currently mounts NULL_DIAGRAM and the user sees a silent blank macro. They cannot tell the difference between a slow-loading page, a missing macro, and a permission error — and have no path to self-service recovery.

Current behavior

Viewer Where NULL_DIAGRAM is set Renderer behavior
sequence / mermaid / plantuml src/forgeIndex.ts:240 (doc = { ...NULL_DIAGRAM } when CC fail + legacy fallback fail) src/components/DiagramPortal.vue:3-5 has v-if for Mermaid / PlantUml / Sequence only; diagramType: 'unknown' matches nothing → renders empty
graph (DrawIO) src/forge-graph-viewer.ts:135 (store.state.diagram = doc ?? NULL_DIAGRAM) empty canvas
openapi src/forge-swagger-ui.ts:74 (same pattern) + window.updateSpec(OpenApiExample) shows the EXAMPLE spec, not the user's data misleading: looks like content loaded, but it's the example
embed src/forge-embed-viewer.ts:30 (same pattern) empty

The recovery banner at src/components/Viewer/GenericViewer.vue:64-77 (Recovered from backup) covers the case where load failed but legacy/sibling recovery succeeded — it does NOT cover the case where everything failed and NULL_DIAGRAM is mounted.

Impact

  • Users have no signal that the macro is broken — they assume "page still loading" and either wait or refresh forever
  • No path to self-service: a user who saw "Couldn't load — retry?" would just retry; today they have no button to click
  • Support burden: customer reports of "the diagram disappeared" require investigation that could be short-circuited by including the content id in the empty state

Proposed UX

When doc.diagramType === DiagramType.Unknown (sequence-class viewers) OR store.state.diagram === NULL_DIAGRAM (other viewers), render:

┌─────────────────────────────────────────────┐
│  ⚠  Couldn't load this diagram               │
│                                              │
│  Content ID: <customContentId>               │
│                                              │
│  [Retry]   [Report issue]                    │
└─────────────────────────────────────────────┘

Style consistent with the existing viewer-recovered-banner (gray, non-alarming) but with a clearer "this failed" icon.

Don't fire on transient blips

Production telemetry shows a non-trivial bucket of orphan events is bursty and often resolves on retry. Before showing the empty-state UI, do a single automatic retry of loadCustomContentWithOrphanRecovery after a 500-1000ms delay. Only if the second attempt also fails should the empty-state render.

This keeps the UI quiet for the API hiccups that the platform already silently recovers from on refresh.

Out of scope

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions