-
-
Notifications
You must be signed in to change notification settings - Fork 12
Typst #1063
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Typst #1063
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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); | ||
| }); | ||
|
|
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 | |||
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 | |||
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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." |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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>`; |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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; |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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; |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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; |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The value assigned to bs here is unused.
| bs = i; |
| greedy: true, | ||
| }, | ||
| url: { | ||
| pattern: /https?\:\/\/[\w.\/\-@:%._\+~#=]*[\w\/]/, |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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); |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Superfluous argument passed to function _putsE.
No description provided.