fix: work-package block exports as a real markdown link#134
Conversation
…s a real link Previously toExternalHTML returned a bare `<div data-...>#123</div>`. When blocksToMarkdownLossy serialized it, the result was effectively empty (the markdown serializer drops the data-only wrapper). For browser copy-to-clipboard and the server-side description export in hocuspocus, work-package blocks ended up as nothing — a regression. Now toExternalHTML emits `<a href="${linkToWorkPackage(wpid)}" data-...>#123</a>`, which serializes to `[#123](https://.../wp/123)` in markdown and renders as a clickable link in any HTML target. The data-* attributes are preserved on the anchor so the inverse `parse` continues to round-trip. Defensive: skip rendering when wpid is missing or non-positive (matches the guard in linkToWorkPackage, avoids the helper throwing during export). Tested with a jsdom-backed unit test that invokes the spec's implementation.toExternalHTML directly and asserts the anchor href / text. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Inline work-package chips (openProjectWorkPackageInline) had the same
markdown-conversion regression as the block variant: the bare
`<span data-...>#wpid</span>` was dropped by blocksToMarkdownLossy,
leaving the chip's id out of the saved description entirely.
Now toExternalHTML emits `<a href="${linkToWorkPackage(numericWpid)}" data-...>#wpid</a>`,
which serializes to `[#wpid](https://.../wp/wpid)` in markdown.
Defensive: keep skipping pending: placeholders; also require the wpid
parses to a positive number before calling linkToWorkPackage (the helper
throws on invalid ids).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Visible link text in the markdown export now mirrors the chip variant: xxs (tiny inline) → "#106" ([#106](url)) xs (compact inline) → "##106" ([##106](url)) s (regular inline) → "###106" ([###106](url)) m / l / xl (block + preview) → "###106" ([###106](url)) A shared `hashesForSize(size)` helper lives next to the WpSize types (lib/components/WorkPackage/types.ts) so both the block and inline React specs use the same mapping. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Thanks, this will be really useful! I wanted to merge this today already but didn't have the time to properly look into it. I hope I get to it tomorrow 🙂 |
judithroth
left a comment
There was a problem hiding this comment.
Thanks for this contribution!
However, this didn't work for me, copypaste of work package links still is empty:
Screencast.from.2026-05-26.09-32-22.webm
Could it be that this depends on the BlockNote update?
However, we also still need to make this support semantic IDs.
| toExternalHTML: ({ block }) => { | ||
| const { wpid, size, initialized } = block.props; | ||
| if (!wpid) return <></>; | ||
| if (!wpid || wpid <= 0) return <></>; |
There was a problem hiding this comment.
We have to make sure this also works with semantic IDs and will correctly use the semantic ID for the copy-paste content
There was a problem hiding this comment.
Good point. Do we also have the semantic ID in a wp link block? We probably need first add it to the block's JSON. Then, our Markdown renderer needs to check if that semantic ID is there. If so, use it, else fall back to DB ID.
| const { wpid, instanceId, size } = inlineContent.props; | ||
| if (!wpid || wpid.startsWith("pending:")) return <></>; | ||
| const numericWpid = Number(wpid); | ||
| if (!Number.isFinite(numericWpid) || numericWpid <= 0) return <></>; |
There was a problem hiding this comment.
Same here, this should respect the semantic IDs
@judithroth Yes, copy and paste first needs to have the BlockNote bump. That is due to shadow dom fixes that the newer BlockNote brings. |
|
@judithroth We probably want to prioritize BlockNote update over this fix here. |
Precodition
BlockNote was bumped to >=0.0.51.
Summary
Fixes a regression where work-package block content was effectively lost when serialized for clipboard copy or hocuspocus's server-side description (markdown) export.
toExternalHTMLused to return a plain<div data-...>#123</div>. The markdown serializer dropped it. Now we emit<a href=\"${linkToWorkPackage(wpid)}\" data-...>#123</a>, which markdown-serializes to a proper[#123](https://.../wp/123).Details
lib/components/BlockWorkPackage/spec.tsx: replace<div>with<a href>; defensive skip for missing or non-positive wpid (avoidslinkToWorkPackagethrowing during export).parsestill inspectsdata-block-content-typeregardless of tag, so paste-from-HTML round-trips unchanged.spec.implementation.toExternalHTML(...)directly and asserts the rendered anchor's href / text / data-attrs.Test plan
npm run test:unit— 35 tests pass (4 new)npm run build— cleanRefs