ENG-2217 Rewrite asset links in Obsidian-origin markdown for Roam - #1398
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
1b550fd to
4ce0832
Compare
4ce0832 to
2950a2a
Compare
2950a2a to
28e9eaa
Compare
28e9eaa to
7bb3b3d
Compare
7bb3b3d to
1179bdb
Compare
1179bdb to
e0588dd
Compare
PR size/scope checkThis PR is over our review-size guideline.
Please split this into smaller PRs unless there is a clear reason the changes need to land together. If keeping it as one PR, please add a brief justification covering:
|
There was a problem hiding this comment.
Devin Review found 4 potential issues.
2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
| export const rewriteAssetLinks = ({ | ||
| markdown, | ||
| assets, | ||
| }: { | ||
| markdown: string; | ||
| assets: ResolvedAsset[]; | ||
| }): string => { |
There was a problem hiding this comment.
🔴 Asset rewriting never reaches imports
Imports never call rewriteAssetLinks. materializeSharedNode sends unchanged markdown to Roam, so imported vault assets remain broken.
Prompt for agents
Integrate asset materialization into apps/roam/src/utils/materializeSharedNode.ts before createImportedPage or updateImportedPage receives markdown. Fetch the source node's FileReference data, mirror each stored hash through mirrorAssetToRoamStorage sequentially as that utility requires, build ResolvedAsset entries from successful mirrors, and pass the fetched markdown through rewriteAssetLinks. Preserve unresolved tokens when a reference cannot be fetched or mirrored, and cover both initial imports and refreshes with integration tests.
Was this helpful? React with 👍 or 👎 to provide feedback.
| return markdown.replace( | ||
| LINK_PATTERN, | ||
| // One capture group per branch, in the order the pattern lists them. The trailing | ||
| // offset and input arguments the replacer also receives are simply not destructured. | ||
| (match: string, ...groups: (string | undefined)[]) => { |
There was a problem hiding this comment.
🟡 Code examples become live assets
When markdown demonstrates a recorded asset inside code, replace rewrites that example too. The imported page no longer preserves its source text.
Prompt for agents
Make rewriteAssetLinks syntax-aware so replacements occur only in markdown link and embed nodes, not inside inline code, fenced code blocks, or other literal markdown regions. A markdown parser is preferable to expanding the global regular expression. Add cases where the same sourceRef appears once as an actual embed and once inside inline and fenced code.
Was this helpful? React with 👍 or 👎 to provide feedback.
| // and it is the only statement available for a storage uid with no extension. | ||
| String.raw`\{\{\[\[(pdf|audio|video)\]\]:\s*(${URL_TOKEN})\s*\}\}`, // {{[[pdf]]: url}} | ||
| String.raw`\{\{(pdf|audio|video):\s*(${URL_TOKEN})\s*\}\}`, // {{pdf: url}} | ||
| String.raw`!\[\[([^\]|]+)(?:\|[^\]]*)?\]\]`, // ![[token]] or ![[token|alt]] |
There was a problem hiding this comment.
🟡 Wikilink aliases disappear
For ![[file|label]], LINK_PATTERN discards label. Non-media attachments then display a filename instead of the author's label.
Prompt for agents
Capture the optional alias in both wikilink branches, update the replacement capture-group mapping, and pass that alias as linkText. Preserve the current sourcePath fallback only when the alias is absent or empty. Add tests for aliased non-media attachments and image alt text.
Was this helpful? React with 👍 or 👎 to provide feedback.
| String.raw`!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)`, //  | ||
| // No `[` in the label, so `[](link)` cannot match here from the outer | ||
| // bracket: the branch fails, the scan advances one character, and the image branch | ||
| // takes the inner embed as it should. Alternation is tried per position, so ordering | ||
| // the image branch first is not enough on its own. | ||
| String.raw`\[([^\]\[]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)`, // [label](token) |
There was a problem hiding this comment.
🟡 Spaced Markdown destinations remain broken
Valid links like  never match LINK_PATTERN. Their recorded assets remain as unusable vault paths after import.
Prompt for agents
Extend standard Markdown destination parsing to support angle-bracket destinations, including spaces, while retaining support for optional titles and nested-image behavior. Normalize the captured destination by removing only its angle delimiters before asset lookup, and add image and ordinary-link tests using sourceRef values with spaces.
Was this helpful? React with 👍 or 👎 to provide feedback.
e0588dd to
0f5a93e
Compare
0f5a93e to
c1d603a
Compare
c20286f to
ef15f98
Compare
ef15f98 to
349c0e0
Compare
349c0e0 to
0f71a67
Compare
0f71a67 to
f225e53
Compare
f225e53 to
d77dd2c
Compare
d77dd2c to
0f834a4
Compare
0f834a4 to
e3c12d5
Compare
e3c12d5 to
a37d0c0
Compare
a37d0c0 to
b94dd69
Compare
b94dd69 to
f2ce51a
Compare
| }; | ||
|
|
||
| // A wikilink embed carries no separate text, so its label comes from the asset. | ||
| const linkText = imageRef ? (imageAlt ?? "") : (linkLabel ?? ""); |
There was a problem hiding this comment.
Wikilink embeds with alt text (e.g., ![[image.png|alt text]]) will lose their alt text. The regex pattern on line 241 captures only the token in a capturing group while the alt text portion (?:\|[^\]]*)? is non-capturing. When embedRef is matched, neither imageRef nor linkLabel is defined, so linkText becomes "", discarding any alt text that was present after the | in the wikilink syntax.
// Current - loses alt text:
const linkText = imageRef ? (imageAlt ?? "") : (linkLabel ?? "");
// Should capture wikilink alt text:
// Modify regex on line 241 to:
!\[\[([^\]|]+)(?:\|([^\]]*))?\]\]
// Then handle it:
const linkText = imageRef ? (imageAlt ?? "") : (embedAlt ?? linkLabel ?? "");Spotted by Graphite
Is this helpful? React 👍 or 👎 to let us know.
Reviewer brief
Given a markdown and assets, rewrite the links to use the firebase links.
We have to look up the file type to make the right kind of Roam link; since Obsidian does not have distinct link shapes, we have to infer from mimetype or file extension.
Verification
Unit tests. Real tests in the next PR.
Loom video
https://www.loom.com/share/ac85dc0a4df9411c84858e965dadb86f
Note: I did push a refactor from 2219 to 2217 after the loom, but no behaviour change.
Scope check
$scope-checkagainst ENG-2217 and the final diff.Done When: Addsmime-db(+@types/mime-db) as anapps/roamdependency.Done Whenrequires correct Roam syntax per media type, and the type set is not enumerable by hand: the extension table is derived from all ~1000mime-dbtypes (223 extensions) so an imported asset renders as the same file would if uploaded natively. Cost:db.jsonis not tree-shakeable, so ~204 KB lands inextension.js(23KB compressed); a build-time codegen of the table would remove it later.EXTENSION_KINDSdocstring; no Linear decision.Local delegated full review
$dg-delegated-full-reviewwhen no other full-review workflow is available.https://linear.app/discourse-graphs/issue/ENG-2217/rewrite-asset-links-in-obsidian-origin-markdown-for-roam