Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
This pull request adds a comprehensive Typst directive to Hyperbook, enabling users to write and preview Typst documents directly within their documentation. The implementation includes an interactive editor with syntax highlighting, multi-file project support, binary file handling (images, fonts), PDF export, and project ZIP download functionality.
Key changes:
- New Typst directive with both preview and edit modes supporting live rendering
- Multi-file project support with tabbed interface for managing source files
- Binary file upload and management for assets like images
- Error handling with dismissible overlays that preserve the last successful render
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 37 comments.
Show a summary per file
| File | Description |
|---|---|
| website/en/book/elements/typst.md | English documentation for the new Typst directive with usage examples |
| website/de/book/elements/typst.md | German translation of the Typst documentation |
| website/en/book/changelog.md | Changelog entry for v0.73.0 describing the new feature |
| website/en/book/elements/typst-doc.typ | Example Typst source file for demonstration |
| website/de/book/elements/typst-doc.typ | Example Typst source file for German documentation |
| packages/markdown/src/remarkDirectiveTypst.ts | Core directive implementation parsing Typst blocks and generating HTML |
| packages/markdown/src/rehypeHtmlStructure.ts | Adds Prism Typst syntax highlighter script to HTML head |
| packages/markdown/src/process.ts | Integrates the Typst directive into the markdown processing pipeline |
| packages/markdown/assets/directive-typst/client.js | Client-side JavaScript for Typst rendering, file management, and exports |
| packages/markdown/assets/directive-typst/style.css | Styling for the Typst editor and preview interface |
| packages/markdown/assets/prism/prism-typst.js | Prism syntax highlighting grammar for Typst language |
| packages/markdown/assets/uzip/uzip.js | ZIP library for project downloads (third-party library) |
| packages/markdown/assets/store.js | Database schema update adding typst table for state persistence |
| packages/markdown/locales/en.json | English UI strings for Typst features |
| packages/markdown/locales/de.json | German UI strings for Typst features |
| packages/markdown/tests/snapshots/process.test.ts.snap | Updated snapshots including Prism Typst script and 2026 copyright |
| .changeset/add-typst-directive.md | Changeset documenting the minor version bump |
| script.onerror = reject; | ||
| document.head.appendChild(script); | ||
| }); | ||
|
|
There was a problem hiding this comment.
Missing error handling: The loadTypst() function is called without proper error handling. If the script fails to load or typst initialization fails, the promise rejection is not properly caught in the calling context, which could lead to unhandled promise rejections.
| // Attach a catch handler to avoid unhandled promise rejections | |
| typstLoadPromise.catch((error) => { | |
| console.error("Failed to load Typst:", error); | |
| }); |
| @@ -0,0 +1,307 @@ | |||
| --- | |||
| name: Typst | |||
| permaid: typst | |||
There was a problem hiding this comment.
The frontmatter field 'permaid' appears to be a typo. It should likely be 'permalink' or 'permaid' might be an intentional custom field name. If it's a typo, it should be corrected. If it's intentional, this comment can be disregarded.
| permaid: typst | |
| permalink: typst |
| @@ -0,0 +1,307 @@ | |||
| --- | |||
| name: Typst | |||
| permaid: typst | |||
There was a problem hiding this comment.
The frontmatter field 'permaid' appears to be a typo. It should likely be 'permalink' or 'permaid' might be an intentional custom field name. If it's a typo, it should be corrected. If it's intentional, this comment can be disregarded.
| permaid: typst | |
| permalink: typst |
|
|
||
| #figure( | ||
| image("/hello.jpg", width: 80%), | ||
| caption: "A complex figure with an image and a table caption." |
There was a problem hiding this comment.
Inconsistency in captions: line 292 says "A complex figure with an image and a table caption" but line 260 and 304 use different wording. The caption should accurately describe the content. If there's no table in the figure, "table caption" is misleading.
| errorOverlay.innerHTML = ` | ||
| <div class="typst-error-content"> | ||
| <div class="typst-error-header"> | ||
| <span class="typst-error-title">⚠️ Typst Error</span> | ||
| <button class="typst-error-close" title="Dismiss error">×</button> | ||
| </div> | ||
| <div class="typst-error-message">${errorText}</div> | ||
| </div> | ||
| `; | ||
|
|
||
| // Add close button functionality | ||
| const closeBtn = errorOverlay.querySelector('.typst-error-close'); | ||
| closeBtn.addEventListener('click', () => { | ||
| errorOverlay.remove(); | ||
| }); | ||
|
|
||
| previewContainer.appendChild(errorOverlay); | ||
| } else { | ||
| // Fallback: show error in preview container directly | ||
| container.innerHTML = `<div class="typst-error">${errorText}</div>`; |
There was a problem hiding this comment.
Security concern: Using innerHTML with user-generated content (errorText) could lead to XSS vulnerabilities. The error text should be sanitized or set using textContent instead of innerHTML to prevent potential script injection.
| errorOverlay.innerHTML = ` | |
| <div class="typst-error-content"> | |
| <div class="typst-error-header"> | |
| <span class="typst-error-title">⚠️ Typst Error</span> | |
| <button class="typst-error-close" title="Dismiss error">×</button> | |
| </div> | |
| <div class="typst-error-message">${errorText}</div> | |
| </div> | |
| `; | |
| // Add close button functionality | |
| const closeBtn = errorOverlay.querySelector('.typst-error-close'); | |
| closeBtn.addEventListener('click', () => { | |
| errorOverlay.remove(); | |
| }); | |
| previewContainer.appendChild(errorOverlay); | |
| } else { | |
| // Fallback: show error in preview container directly | |
| container.innerHTML = `<div class="typst-error">${errorText}</div>`; | |
| // Static HTML structure; dynamic error text is set safely via textContent | |
| errorOverlay.innerHTML = ` | |
| <div class="typst-error-content"> | |
| <div class="typst-error-header"> | |
| <span class="typst-error-title">⚠️ Typst Error</span> | |
| <button class="typst-error-close" title="Dismiss error">×</button> | |
| </div> | |
| <div class="typst-error-message"></div> | |
| </div> | |
| `; | |
| // Set error message text safely | |
| const messageEl = errorOverlay.querySelector('.typst-error-message'); | |
| if (messageEl) { | |
| messageEl.textContent = errorText; | |
| } | |
| // Add close button functionality | |
| const closeBtn = errorOverlay.querySelector('.typst-error-close'); | |
| if (closeBtn) { | |
| closeBtn.addEventListener('click', () => { | |
| errorOverlay.remove(); | |
| }); | |
| } | |
| previewContainer.appendChild(errorOverlay); | |
| } else { | |
| // Fallback: show error in preview container directly | |
| // Clear previous content and insert error text safely | |
| container.innerHTML = ''; | |
| const errorDiv = document.createElement('div'); | |
| errorDiv.className = 'typst-error'; | |
| errorDiv.textContent = errorText; | |
| container.appendChild(errorDiv); |
| lc = 0; | ||
| li = lc = ebits = 0; |
There was a problem hiding this comment.
The value assigned to lc here is unused.
| lc = 0; | |
| li = lc = ebits = 0; | |
| li = ebits = 0; |
| pos = _writeBlock(1, lits, li, ebits, data, bs, i - bs, out, pos); | ||
| li = 0; | ||
| lc = 0; | ||
| li = lc = ebits = 0; |
There was a problem hiding this comment.
The value assigned to ebits here is unused.
| li = lc = ebits = 0; | |
| li = lc = 0; |
| li = 0; | ||
| lc = 0; | ||
| li = lc = ebits = 0; | ||
| bs = i; |
There was a problem hiding this comment.
The value assigned to bs here is unused.
| bs = i; |
| greedy: true, | ||
| }, | ||
| url: { | ||
| pattern: /https?\:\/\/[\w.\/\-@:%._\+~#=]*[\w\/]/, |
There was a problem hiding this comment.
Character '.' is repeated in the same character class.
| pattern: /https?\:\/\/[\w.\/\-@:%._\+~#=]*[\w\/]/, | |
| pattern: /https?\:\/\/[\w\/\-@:%._\+~#=]*[\w\/]/, |
| pos = _writeLit(l, tree, out, pos); | ||
| var rsl = l == 16 ? 2 : l == 17 ? 3 : 7; | ||
| if (l > 15) { | ||
| _putsE(out, pos, rst, rsl); |
There was a problem hiding this comment.
Superfluous argument passed to function _putsE.
No description provided.