Skip to content

MermaidImageRenderer: OFFICECLI_MERMAID_JS local asset override for render=image - #319

Open
hesong12 wants to merge 1 commit into
iOfficeAI:mainfrom
hesong12:feat/officecli-mermaid-js-env
Open

MermaidImageRenderer: OFFICECLI_MERMAID_JS local asset override for render=image#319
hesong12 wants to merge 1 commit into
iOfficeAI:mainfrom
hesong12:feat/officecli-mermaid-js-env

Conversation

@hesong12

Copy link
Copy Markdown

Files: src/officecli/Core/Diagram/MermaidImageRenderer.cs (ResolveMermaidJsRef(),
RefreshCacheIfPresent()), README.md

Summary

diagram --prop render=image's chrome-family backend renders with the real
mermaid.js. ResolveMermaidJsRef() resolves the script it points the page
at in three steps: an existing local cache → download from OfficeCLI's own
mirror → download from the jsDelivr CDN → give up and reference the CDN URL
live in the page. Every one of those needs a network path out. A host that
is offline, air-gapped, or sandboxed away from the network has no way to use
render=image at all, even when it already has a valid mermaid.min.js on
disk.

This PR adds OFFICECLI_MERMAID_JS: when set to a path that exists,
ResolveMermaidJsRef() returns a file:// URI for it directly — no cache
check, no download attempt, no network call of any kind — and
RefreshCacheIfPresent() (the daily background revalidation hook) returns
immediately instead of trying to reach the mirror. Unset, behaviour is
unchanged.

private static string? MermaidJsOverride
{
    get
    {
        var p = Environment.GetEnvironmentVariable("OFFICECLI_MERMAID_JS");
        return !string.IsNullOrWhiteSpace(p) && File.Exists(p) ? p : null;
    }
}

Same shape as the existing OFFICECLI_MMDC override two members away in the
same file.

Why

Same host as the sibling OFFICECLI_BROWSER PR: a desktop app that runs
inside a locked-down sandbox (network egress not guaranteed, no ability to
silently phone a CDN on the user's behalf) wants render=image diagrams to
work from an asset it ships and pins itself.

Validation

Toolchain: same scratch .NET 10 SDK as the sibling PRs.
mermaid.min.js downloaded once from
https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js into a scratch
directory (not committed, not part of the diff).

Precedence check (does the override actually get read, not just
"happen to work because the network was up anyway"): point
OFFICECLI_MERMAID_JS at a deliberately invalid JS file
(not_a_valid_mermaid_js_file();) and diagram add the same source on both
the unmodified main branch and this branch, network fully reachable on
both runs:

officecli create t.docx && officecli close t.docx

# BEFORE (unmodified main — ResolveMermaidJsRef doesn't know the var exists):
OFFICECLI_MERMAID_JS=/path/to/corrupt.js officecli add t.docx / \
  --type diagram --prop mermaid=$'erDiagram\n  A ||--o{ B : has' --prop render=image --json
# → {"success":true,"data":"Added diagram at /body/p[@paraId=00100000]", ...}
#   (ignores the var, downloads the real mermaid.js from the mirror/CDN, renders fine — 18.8s)

# AFTER (this branch):
OFFICECLI_MERMAID_JS=/path/to/corrupt.js officecli add t.docx / \
  --type diagram --prop mermaid=$'erDiagram\n  A ||--o{ B : has' --prop render=image --json
# → {"success":false,"error":{"error":"mermaid failed to render: mermaid is not defined",
#     "code":"internal_error"}}
#   (loaded the corrupt local file instead of downloading — proves the override took effect — 2.2s)

Offline functional check — the real command from the task, with the
valid local copy and the network genuinely unreachable
(http_proxy/https_proxy pointed at a closed port; a raw HttpClient
probe against the same target confirms this fails in <0.1s rather than
silently succeeding):

officecli create t.docx && officecli close t.docx
OFFICECLI_MERMAID_JS=/path/to/mermaid.min.js officecli add t.docx / \
  --type diagram --prop mermaid=$'erDiagram\n  A ||--o{ B : has' --prop render=image --json
# → {"success":true,"data":"Added diagram at /body/p[@paraId=00100000]", ...}   (7.6s)
officecli view t.docx outline
# → File: t.docx | 1 paragraphs | 0 tables | 1 images
officecli view t.docx screenshot -o t.png --json
# → PNG written, 21717 bytes, real ER diagram (A —has→ B, crow's-foot notation) — see U2-screenshot.png
ls ~/.officecli/cache/     # → No such file or directory: RefreshCacheIfPresent() never touched it

Compatibility

Additive only. The existing cache → mirror → CDN → live-CDN cascade in
ResolveMermaidJsRef() is untouched and still runs whenever
OFFICECLI_MERMAID_JS is unset. RefreshCacheIfPresent()'s existing
early-return (!File.Exists(CachedJsPath)) is untouched; the new check is
strictly earlier in the same method and only short-circuits when the new var
is set.

Offline / sandboxed hosts have no path to the mirror or the CDN that
render=image normally downloads mermaid.min.js from. Read
OFFICECLI_MERMAID_JS first, when set and the file exists: use it
directly as the <script src>, and skip both the download attempt and
the daily cache-refresh network call.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 92b4974db6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +414 to +415
if (MermaidJsOverride is { } overridePath)
return new Uri(Path.GetFullPath(overridePath)).AbsoluteUri;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor the local override in the styled ESM path

When a diagram specifies theme, layout, or look, RenderViaChrome selects BuildHtmlEsm and never calls this resolver; that path still imports MermaidEsmUrl and, for ELK, ElkEsmUrl from jsDelivr. Consequently, on the offline or sandboxed hosts this override targets, styled render=image requests still fail—or auto mode falls back to native rendering and loses the requested styling—despite OFFICECLI_MERMAID_JS being set.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant