Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 31, 2025

  • Create remarkDirectiveTypst.ts - TypeScript directive handler for the typst container directive
  • Create assets/directive-typst/client.js - Client-side JavaScript for typst rendering
  • Create assets/directive-typst/style.css - Styles for the typst directive
  • Add typst directive to process.ts to register it with the remark plugins
  • Add i18n translations for typst in locales/en.json and locales/de.json
  • Add typst store to store.js for persisting user edits
  • Add VSCode snippet for typst in platforms/vscode/snipptes/hyperbook.code-snippets
  • Add VSCode syntax highlighting in platforms/vscode/syntaxes/hyperbook.json
  • Create documentation page for the typst element in website/en/book/elements/typst.md
  • Create German documentation page in website/de/book/elements/typst.md
  • Create test file remarkDirectiveTypst.test.ts with snapshots
  • Build markdown package and run tests (all passing)
  • Run code review and codeql checks (no issues)

Features

  • External file support via src attribute (e.g., :::typst{src="document.typ"})
  • Loading indicator (spinner) while Typst WASM module initializes
  • Copy button to copy Typst source code to clipboard
  • Height parameter supports CSS values (e.g., "100px", "50vh", "calc(100dvh - 200px)")
  • Default heights: auto for preview mode, calc(100dvh - 128px) for edit mode
  • Visual separation for document with border-radius
Original prompt

Implement a typst directive. It should have the option to show an editor for quickly editing the typst file. Otherwise it should work like this:

:::typst{mode="preview"}

= Hallo World!

:::

There should be two modes:

preview: displays the rendered typst file
edit: displays the rendered typst file alongside an editor for modifiying it.

Here is a raw html example on how to do it:

<!doctype html>

<title>Tiny Typst Previewer Example</title> <script type="module" src="https://cdn.jsdelivr.net/npm/@myriaddreamin/typst.ts/dist/esm/contrib/all-in-one-lite.bundle.js" id="typst"></script> Export To PDF <textarea id="input">Hello, Typst!</textarea>
<script> const input = document.getElementById('input'); const contentDiv = document.getElementById('content');
// Exports SVG and puts it into the `contentDiv`
const previewSvg = mainContent => {
  $typst.svg({ mainContent }).then(svg => {
    console.log(`rendered! SvgElement { len: ${svg.length} }`);
    // append svg text
    contentDiv.innerHTML = svg;

    const svgElem = contentDiv.firstElementChild;
    const width = Number.parseFloat(svgElem.getAttribute('width'));
    const height = Number.parseFloat(svgElem.getAttribute('height'));
    const cw = document.body.clientWidth - 40;
    svgElem.setAttribute('width', cw);
    svgElem.setAttribute('height', (height * cw) / width);
  });
};

// Exports PDF and downloads it
const exportPdf = mainContent =>
  $typst.pdf({ mainContent }).then(pdfData => {
    var pdfFile = new Blob([pdfData], { type: 'application/pdf' });

    // Creates element with <a> tag
    const link = document.createElement('a');
    // Sets file content in the object URL
    link.href = URL.createObjectURL(pdfFile);
    // Sets file name
    link.target = '_blank';
    // Triggers a click event to <a> tag to save file.
    link.click();
    URL.revokeObjectURL(link.href);
  });

/// Listens the 'load' event to initialize after loaded the bundle file from CDN (jsdelivr).
document.getElementById('typst').addEventListener('load', function () {
  /// Initializes the Typst compiler and renderer. Since we use "all-in-one-lite.bundle.js" instead of
  /// "all-in-one.bundle.js" we need to tell that the wasm module files can be loaded from CDN (jsdelivr).
  $typst.setCompilerInitOptions({
    getModule: () =>
      'https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-web-compiler/pkg/typst_ts_web_compiler_bg.wasm',
  });
  $typst.setRendererInitOptions({
    getModule: () =>
      'https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-renderer/pkg/typst_ts_renderer_bg.wasm',
  });

  /// Binds exportPdf action to the button
  document.getElementById('export').onclick = () => exportPdf(input.value);
  /// Binds previewSvg action to the textarea
  input.oninput = () => {
    previewSvg(input.value);
    input.style.height = '5px';
    input.style.height = input.scrollHeight + 'px';
  };
  /// Triggers the first preview.
  previewSvg(input.value);
});
</script> <style> body { margin: 0px; display: flex; flex-direction: column; align-items: center; }
#export {
  width: 100%;
  height: 20px;
}

#input {
  width: 100%;
  margin: 0;
  padding: 0;
  border-width: 0;
  height: auto;
}
</style>

Please look at the webide for the design and the general structure. In both modes there should be a download button for downloading the pdf.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@vercel
Copy link

vercel bot commented Dec 31, 2025

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

Project Deployment Review Updated (UTC)
hyperbook-4fwa Ready Ready Preview, Comment Dec 31, 2025 4:34pm

Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
Copilot AI changed the title [WIP] Add typst directive with preview and edit modes Implement typst directive with preview and edit modes Dec 31, 2025
Copilot AI requested a review from mikebarkmin December 31, 2025 11:05
@mikebarkmin
Copy link
Member

@copilot Do you have other recommandations for this directive?

Copy link
Contributor Author

Copilot AI commented Dec 31, 2025

@copilot Do you have other recommandations for this directive?

Here are some recommendations to enhance this directive:

  1. Copy button: Add a "Copy" button to copy the Typst source code to clipboard (similar to the pyide and webide directives)

  2. External file support: Add a src attribute to load Typst code from external .typ files (e.g., :::typst{src="document.typ"})

  3. Zoom/scale controls: Add zoom controls or a scale attribute since Typst documents can have varying dimensions

  4. Error display improvements: Show more detailed error messages with line numbers when Typst compilation fails

  5. Loading indicator: Add a loading spinner while the Typst WASM module initializes (it can take a moment on first load)

  6. Dark mode support: Ensure the preview container adapts to the hyperbook's dark mode theme

  7. Multi-page support: For longer documents, add pagination or scroll navigation for multi-page Typst output

Let me know if you'd like me to implement any of these enhancements!

@mikebarkmin
Copy link
Member

@copilot External file support is great. loading indicator. copy button.

…t directive

Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 31, 2025

@copilot External file support is great. loading indicator. copy button.

Implemented all three features in commit 1fca126:

  1. External file support: Use :::typst{src="document.typ"} to load from external files. Files are searched in public/, book/, or the current page's directory.

  2. Loading indicator: Shows a spinner with "Loading Typst..." text while the WASM module initializes.

  3. Copy button: Added to both preview and edit modes to copy source code to clipboard.

@mikebarkmin
Copy link
Member

@copilot Can you add a visual separation between the document (typst-doc) and the background. Maybe something like this: border-radius: 8px; box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.16) 0px 3px 6px;

Also style the copy and download buttons like the ones from the editor. So that they are attached to the bottom of the preview.

… bottom

Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 31, 2025

@copilot Can you add a visual separation between the document (typst-doc) and the background. Maybe something like this: border-radius: 8px; box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0,...

Applied the styling changes in commit b0013d2:

  1. Added visual separation to the document (SVG) with border-radius: 8px and box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.16) 0px 3px 6px

  2. Restyled the copy and download buttons to match the editor style - they are now attached to the bottom of the preview container with the same border styling as the editor buttons.

@mikebarkmin
Copy link
Member

@copilot The outline feature did not work, please remove it. But I want to have plus and minus buttons for scaling the preview. I also want a button for expand to container width and a show full page button.

Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 31, 2025

@copilot The outline feature did not work, please remove it. But I want to have plus and minus buttons for scaling the preview. I also want a button for expand to container width and a show full page ...

Implemented the zoom controls in commit 4e21562:

  1. Plus (+) and minus (-) buttons: Scale the preview up or down by 25% increments (range: 25% to 500%)
  2. Fit Width button: Scales the document to fit the container width
  3. Full Page button: Shows the document at 100% original size

Note: I didn't find any "outline feature" in the code to remove - could you clarify what you meant by this?

@mikebarkmin
Copy link
Member

@copilot Full page means one page should be visible without cut offs. Also add a height parameter to the directive. If it is not specific to should default to calc(100dvh - 128px) in edit mode and to auto in preview mode. Otherwise the specific height should be used for example "100px" or "50vh".

…t without cut-offs

Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 31, 2025

@copilot Full page means one page should be visible without cut offs. Also add a height parameter to the directive. If it is not specific to should default to calc(100dvh - 128px) in edit mode and to ...

Implemented in commit 772b8ab:

  1. Full Page button: Now scales the document to fit entirely within the container without cut-offs (uses min(scaleX, scaleY) to preserve aspect ratio)

  2. Height parameter: Now supports CSS values like 100px, 50vh, calc(100dvh - 200px)

  3. Default heights:

    • Preview mode: auto (document shows at natural height)
    • Edit mode: calc(100dvh - 128px) (fills viewport minus header)

@mikebarkmin
Copy link
Member

@copilot remove the scaling feature and the typst-preview box-shadow

Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Dec 31, 2025

@copilot remove the scaling feature and the typst-preview box-shadow

Removed in commit 6af0295:

  1. Scaling feature removed: Removed zoom in/out buttons, fit width button, and full page button along with all related JavaScript logic and CSS styles
  2. Box-shadow removed: Removed the box-shadow from .typst-preview svg (kept the border-radius)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants