Skip to content

FE-376: Add a DOCX preview to the entity editor - #9995

Merged
0xRezaa merged 45 commits into
mainfrom
reza/fe-376-docx-file-previewing-in-hash
Oct 16, 2026
Merged

0xRezaa merged 45 commits into
mainfrom
reza/fe-376-docx-file-previewing-in-hash

Conversation

@0xRezaa

@0xRezaa 0xRezaa commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

🌟 Purpose

The entity editor's File Preview now shows .docx files as pages instead of a file icon, with rough parity to the PDF preview. The file is drawn in the browser, so it never leaves HASH. Closes FE-376 (internal).

Same as the PDF preview: paging with a page counter, zoom, full screen, search with a case toggle and next / previous, select and copy, read-only, Download, and a fresh state when the File URL changes.

Different for DOCX:

  • No page thumbnails.
  • Search marks only the current hit, and skips headers, footers and footnotes.
  • Zoom stays between 25% and 400%.
  • A file folio cannot draw falls back to the usual file icon and Download.

🔍 Approach

  • One file knows the renderer. @stll/folio-react (folio) is used read-only and imported only in docx-preview/folio-document.tsx. The rest of the preview uses its DocxDocumentControls.
  • Shared where the previews overlap. Both previews use the search bar FilePreviewSearch and fetch the file with fetchPresignedFileUrl. The PDF preview behaves as before.
  • Untrusted documents stay inert. No scripts run, javascript: and data: links do nothing, and nothing is requested from outside HASH.
  • Loaded on demand. folio (about 820 KB gzipped) downloads only when a DOCX preview opens, with English messages only.

Why folio: of three browser DOCX libraries that passed fixed criteria (in-browser, licence, size, speed, no outbound requests), folio lets readers select and copy text, which eigenpal docx-editor's view mode blocks, and stays usable with the JavaScript JIT blocked (first page up to 2.6 s, against about 1.2 s in Chrome). @silurus/ooxml scored lower on fidelity and backing.

Dependencies:

  • folio's ProseMirror and yjs peers that HASH lacks are added. HASH keeps its older prosemirror-commands, -inputrules, -keymap and -dropcursor, which the read-only preview works with.
  • @stll/folio-react 0.28.0, @stll/folio-core 0.60.0 and @stll/docx-core 0.37.0 are younger than the 7-day npmMinimalAgeGate, so .yarnrc.yml pre-approves exactly these versions. They carry npm provenance from verified, tagged commits and add no install scripts, network calls or known advisories.
  • Two .d.ts shims expose folio's messages/en and ai-edits subpaths to the frontend's moduleResolution: "node".

🛡 Tests

file-preview/docx.spec.ts has one test per behaviour: paging, zoom and its limits, the page counter, search, read-only, copy, a hostile document, File URL edits, .doc files and unreadable .docx files. The PDF tests from #9972 move to file-preview/pdf.spec.ts. Test documents are generated in code, so the PR adds no binary file.

⚠️ Known issues

  • Tracked changes and comment highlights show; folio can't hide them.
  • folio doesn't draw charts, shape groups, or EMF and WMF pictures.
  • folio has no page-change event, so the page counter also polls every 500 ms.
  • .doc files keep the icon view.

🐾 Follow-ups

Already on main in the PDF preview, so left out here:

  • FE-1963: one shared search box that runs one search per pause in typing.
  • FE-1964: run the search again once the document's text has loaded.

🚫 Blocked by

@0xRezaa
0xRezaa added this pull request to stack #9996 October 12, 2026 07:19
@vercel

vercel Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
hash Ready Ready Preview Oct 16, 2026 5:34pm UTC
petrinaut Ready Ready Preview Oct 16, 2026 5:34pm UTC
petrinaut-docs Ready Ready Preview Oct 16, 2026 5:34pm UTC
1 Skipped Deployment
Project Deployment Actions Updated
hashdotdesign-tokens Ignored Ignored Preview Oct 16, 2026 5:34pm UTC

Request Review

@github-actions github-actions Bot added area/deps Relates to third-party dependencies (area) area/apps > hash* Affects HASH (a `hash-*` app) type/eng > frontend Owned by the @frontend team area/tests New or updated tests area/tests > playwright New or updated Playwright tests area/apps labels Oct 12, 2026
@0xRezaa 0xRezaa self-assigned this Oct 12, 2026
@CLAassistant

CLAassistant commented Oct 12, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@codspeed

codspeed Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

⚠️ 6 benchmarks measured no execution time

Nothing ran under measurement, usually because the compiler removed the code under test. These results are not comparable, so they count as unchanged.

Preventing compiler optimizations

⚠️ 1 benchmark spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

ℹ️ 6 benchmarks have no flame graph

The flamegraph could not be generated because no execution data was recorded when the benchmark was run. Ensure that the benchmark has not been optimized out.

✅ 98 untouched benchmarks


Comparing reza/fe-376-docx-file-previewing-in-hash (bee0252) with reza/fe-47-update-prosemirror-packages (dbc18f1)

Open in CodSpeed

FE-47 moves HASH to the ProseMirror versions folio uses, so each
ProseMirror package installs once and folio's imports no longer need
redirecting to folio-core's copies. `next.config.js` is back to its
state before this branch.
folio-core 0.60.0 expects the host to install ProseMirror, yjs,
`y-prosemirror` and `y-protocols`. The frontend now lists the ones it
lacked, and raises `prosemirror-commands`, `-inputrules`, `-keymap` and
`-dropcursor` to folio's ranges, so each installs once.

folio-react 0.28.0, folio-core 0.60.0 and docx-core 0.37.0 are younger
than the 7-day age gate, so `npmPreapprovedPackages` admits exactly
these versions.
Editing an entity's "File URL" property changes the URL of the mounted
PDF or DOCX preview, and the preview kept the old file's page, zoom and
search state. The PDF preview then drew the new file at the old zoom and
asked for a page it does not have. Keying both previews by the file URL
mounts a fresh preview for each file.

The file-preview spec gains a test per preview that edits the File URL
to a second file and checks its page, zoom and search state.
`file-preview.spec.ts` becomes `file-preview/pdf.spec.ts` and
`file-preview/docx.spec.ts`. Their helpers, test document and generators
move to `shared/file-preview/`.

The File URL tests now check only that the new file shows after an edit
made on a later page, the case where the PDF preview showed no page at
all. Both formats share one test body, and `editFileUrl` focuses the
value cell through the grid's accessibility table instead of clicking a
pixel offset and walking rows with the arrow keys.
…w tests

Focusing the cell while the grid has no selection let the canvas's focus
handler move the selection back to the first cell, so Enter opened no
editor and both File URL tests timed out.
`generatePdf` and `generateDocx` take the pages to render, and the specs
pass `testDocumentPages` themselves. The `generateTestPdf` and
`generateTestDocx` shorthands, whose pages defaulted to the test
document, are gone, and `FileFormat.generate` points at the generators
directly.
…ersions

folio asks for newer versions of these four as peers, but the read-only
preview runs on the versions HASH's editors already use. Keeping them
leaves the supply-chain status editor on the versions it was tested with
and stops this branch changing `hash-isomorphic-utils`. Yarn now warns
about the four unmet peers.
folio's dependencies ask for `@babel/runtime@^7.29.7` and `reselect@^5.2.0`,
which the locked 7.29.7 and 5.2.0 already satisfy, so the lockfile resolves
them there instead of moving HASH to 7.29.10 and 5.3.0.
The test clicked the centre of the page, which is blank, so it could not
catch a link that runs script. It now clicks each link's text, waits for
a popup, and counts requests from every tab.
The page polling now goes through useEffectEvent instead of useCallback, so the hooks need no manual memoization.
folio's ctrl+wheel zoom changes the zoom without telling HASH, so the zoom label went stale. With it off, ctrl+wheel zooms the browser, as in the PDF preview.
Zooming out had no floor, so the fifth click took the pages to 0%. The bounds are folio's own.
getFolioMessages bundles all 17 locales. Importing the English catalog alone takes about 83 KB gzip off the DOCX preview chunk.
Nothing depends on the search function's identity, so it needs no useCallback with React Compiler on.
handleError becomes a plain function, so the download effect only depends on the URL and needs no useCallback with React Compiler on.
The DOCX download effect no longer depends on it, so it needs no useCallback with React Compiler on.

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2a35212. Configure here.

Comment thread apps/hash-frontend/src/pages/shared/file-preview-search.tsx

This branch was successfully deployed

5 active (2 outdated) deployments
Preview – petrinaut-docs — 2a352122 Deployed Oct 16, 2026 by vercel[bot]
Preview – hash — 2a352122 Deployed Oct 16, 2026 by vercel[bot]
Preview – petrinaut — 2a352122 Deployed Oct 16, 2026 by vercel[bot]
Preview – hashdotdesign-tokens — 8f029310 Deployed Oct 16, 2026 by vercel[bot]
pull-request — bee02528 Deployed Oct 16, 2026 by 0xRezaa via Sourcemaps (@apps/hash-ai-worker-ts) #40118
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apps > hash* Affects HASH (a `hash-*` app) area/apps area/deps Relates to third-party dependencies (area) area/tests > playwright New or updated Playwright tests area/tests New or updated tests type/eng > frontend Owned by the @frontend team

Development

Successfully merging this pull request may close these issues.

4 participants