feat: reimplement typlite by html export (#1684)

* dev: init markdown file

* dev: typlite element derive

* feat: pass tests

refactor lib.rs to separated files (#1692)

feat(typlite): Docx export and export markdown in cmark-writer (#1698)

* feat: docx export support

* refactor: simplify DocxConverter structure and improve content handling

* tests: add binary insta for docx

* feat: add MathBlock style and improve frame rendering in DocxConverter

* fix: enhance paragraph creation(silly method)

* fix: enhance math equation rendering

* use md5 instead of docx binary

* feat: enhance list numbering and paragraph handling in DocxConverter

* feat: add all_elements test

* refactor

* reimpl md export in cmark-writer

* feat: add support for highlight tag in MarkdownConverter

* feat: refactor LaTeXConverter to improve element processing and add new methods

* fmt

* Refactor DOCX converter to improve list handling and document structure

- Introduced separate methods for creating ordered and unordered list numbering.
- Enhanced list management by tracking next numbering IDs.
- Consolidated paragraph and run management within the DocxConverter.
- Improved image processing with better error handling and placeholder support.
- Streamlined the handling of various HTML elements, including headings, lists, and images.
- Added functionality for processing captions and preformatted blocks.
- Updated methods for processing inline styles and links.

* feat: update cmark-writer to version 0.2.0

* feat: refactor code block handling in DOCX converter for improved readability

* refactor: refactor DOCX converter to enhance document structure

* refactor docx to separated files

* update instas

* fmt

* chore: update cmark-writer version to 0.3.0

* fix: ol custom value

* feat: table and grid processing

* use cmark-writer's ast node for consistency

* fix: update snapshot hashes for document generation tests

* fix: add preamble

* update snapshot hashes

* refactor DOCX conversion: Split writer functionality into separate module, enhance image processing, and clean up utility functions

* update comments in LaTeX and Markdown converters for clarity and consistency

* fmt

* delete utils

* feat: support figure node by custom node in cmark-writer

* fix

* fix: frame

* feat: enhance table conversion logic in MarkdownConverter

* refactor: simplify FigureNode implementation by removing CustomNode trait

* chore: update cmark-writer to version 0.5.0

* fix: update figure and raw inline snapshots for consistency

* fix: update snapshot hashes and correct caption reference in markdown.typ

* refactor proj structure

* feat: update CompileArgs to support multiple output files and remove debug option

* docs: update README to clarify usage of multiple output formats and comment out feature section

* remove DocxConverter module

* feat: impl assets-path feature and add ExternalFrameNode for handling external frames and update writers to support it

* feat: enhance HTML element conversion to include attributes and children handling

* feat: update cmark-writer to 0.6.1 and refactor related code

* fix: update snapshots for figure caption, list, outline, and docx generation

* feat: refactor HTML element conversion to use create_html_element method and enhance table processing

* fix

* feat: add HighlightNode for highlighted text and integrate with HTML to AST parser and LaTeX writer

* refactoor

* update tests

Co-Authored-By: Hong Jiarong <me@jrhim.com>

* feat: revert latex/docx conversions

* fix: warnings

* bad: convert docs

* build: remove other cargo deps

* build: update cargo.lock

* test: update snapshot

* chore: remove useless parser trait

* feat: annotate v1

* feat: annotate v2

* test: update snapshot

* question: is it a bug?

* test: update bad snapshot

---------

Co-authored-by: Hong Jiarong <me@jrhim.com>
This commit is contained in:
Myriad-Dreamin 2025-04-30 22:12:25 +08:00 committed by GitHub
parent 170dd7b948
commit f35d1056ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
58 changed files with 2755 additions and 950 deletions

View file

@ -2,7 +2,6 @@
source: crates/tinymist-query/src/analysis.rs
expression: "snap.join(\"\\n\")"
input_file: crates/tinymist-query/src/fixtures/docs/blocky2.typ
snapshot_kind: text
---
= docstings
Pattern(..)@41..42 in /s0.typ -> DocString { docs: Some("This is X\nNote: This is not Y"), var_bounds: {}, vars: {}, res_ty: None }
Pattern(..)@41..42 in /s0.typ -> DocString { docs: Some("This is X Note: This is not Y"), var_bounds: {}, vars: {}, res_ty: None }

View file

@ -2,7 +2,6 @@
source: crates/tinymist-query/src/analysis.rs
expression: "snap.join(\"\\n\")"
input_file: crates/tinymist-query/src/fixtures/docs/multiple_line.typ
snapshot_kind: text
---
= docstings
Pattern(..)@45..46 in /s0.typ -> DocString { docs: Some("This is X.\nNote: this is not Y."), var_bounds: {}, vars: {}, res_ty: None }
Pattern(..)@45..46 in /s0.typ -> DocString { docs: Some("This is X. Note: this is not Y."), var_bounds: {}, vars: {}, res_ty: None }

View file

@ -2,9 +2,8 @@
source: crates/tinymist-query/src/hover.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/hover/annotate_dict_param.typ
snapshot_kind: text
---
{
"contents": "```typc\nlet show-example(\n ..options: arguments,\n inherited-scope: dictionary = (:),\n) = none;\n```\n\n---\n\n- <!-- typlite:begin:list-item 0 -->inherited-scope (dictionary): Definitions that are made available to the entire parsed\n module. This parameter is only used internally.<!-- typlite:end:list-item 0 -->\n\n# Rest Parameters\n\n## options\n\n```typc\ntype: arguments\n```\n\n\n\n# Named Parameters\n\n## inherited-scope\n\n```typc\ntype: dictionary\n```\n\nDefinitions that are made available to the entire parsed\n module. This parameter is only used internally.",
"contents": "```typc\nlet show-example(\n ..options: arguments,\n inherited-scope: dictionary = (:),\n) = none;\n```\n\n---\n\n- <!-- typlite:begin:list-item 0 -->inherited-scope (dictionary): Definitions that are made available to the entire parsed module. This parameter is only used internally.<!-- typlite:end:list-item 0 -->\n\n# Rest Parameters\n\n## options\n\n```typc\ntype: arguments\n```\n\n\n\n# Named Parameters\n\n## inherited-scope\n\n```typc\ntype: dictionary\n```\n\nDefinitions that are made available to the entire parsed module. This parameter is only used internally.",
"range": "7:20:7:32"
}

View file

@ -2,9 +2,8 @@
source: crates/tinymist-query/src/hover.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/hover/annotate_dict_param2.typ
snapshot_kind: text
---
{
"contents": "```typc\nlet inherited-scope = dictionary;\n```\n\n---\n\nDefinitions that are made available to the entire parsed\n module. This parameter is only used internally.",
"contents": "```typc\nlet inherited-scope = dictionary;\n```\n\n---\n\nDefinitions that are made available to the entire parsed module. This parameter is only used internally.",
"range": "6:21:6:36"
}

View file

@ -4,6 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/hover/annotate_docs_error.typ
---
{
"contents": "```typc\nlet speaker-note(\n note: any,\n mode: str = \"typ\",\n setting: (any) => any = Closure(..),\n) = none;\n```\n\n---\n\nSpeaker notes are a way to add additional information to your slides that is not visible to the audience. This can be useful for providing additional context or reminders to yourself.\n\n## Example\n\n```typ\n#speaker-note[This is a speaker note]\n\n```\n```\nRender Error\ncompiling node: error: unknown variable: speaker-note at \"/__render__.typ\":214..226\nHint: if you meant to use subtraction, try adding spaces around the minus sign: \\`speaker - note\\`\n\n```\n\n# Positional Parameters\n\n## note\n\n```typc\ntype: \n```\n\n\n\n# Named Parameters\n\n## mode\n\n```typc\ntype: \"typ\"\n```\n\n\n\n## setting (named)\n\n```typc\ntype: (any) => any\n```\n\n",
"contents": "```typc\nlet speaker-note(\n note: any,\n mode: str = \"typ\",\n setting: (any) => any = Closure(..),\n) = none;\n```\n\n---\n\n```\nfailed to parse docs: failed to convert to markdown: convert source for main file: [SourceDiagnostic { severity: Error, span: Span(..), message: \"unknown variable: example\", trace: [Import], hints: [] }]\n```\n\n````typ\nSpeaker notes are a way to add additional information to your slides that is not visible to the audience. This can be useful for providing additional context or reminders to yourself.\n\n== Example\n\n#example(```typ\n#speaker-note[This is a speaker note]\n```)\n````\n\n# Positional Parameters\n\n## note\n\n```typc\ntype: \n```\n\n\n\n# Named Parameters\n\n## mode\n\n```typc\ntype: \"typ\"\n```\n\n\n\n## setting (named)\n\n```typc\ntype: (any) => any\n```\n\n",
"range": "11:20:11:32"
}

View file

@ -2,9 +2,8 @@
source: crates/tinymist-query/src/hover.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/hover/module_alias.typ
snapshot_kind: text
---
{
"contents": "### Sampled Values\n```typc\n<module themod>\n```\n\n---\n\n# The Module (Alias)",
"contents": "### Sampled Values\n```typc\n<module themod>\n```\n\n---\n\n## The Module (Alias)\n",
"range": "2:24:2:31"
}

View file

@ -2,9 +2,8 @@
source: crates/tinymist-query/src/hover.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/hover/module_path.typ
snapshot_kind: text
---
{
"contents": "# Some Module",
"contents": "## Some Module\n",
"range": "0:29:0:46"
}

View file

@ -2,9 +2,8 @@
source: crates/tinymist-query/src/hover.rs
expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/hover/module_var.typ
snapshot_kind: text
---
{
"contents": "### Sampled Values\n```typc\n<module themod>\n```\n\n---\n\n# The Module",
"contents": "### Sampled Values\n```typc\n<module themod>\n```\n\n---\n\n## The Module\n",
"range": "2:24:2:30"
}

View file

@ -4,6 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/hover/render_equation.typ
---
{
"contents": "```typc\nlet lam(\n A: type,\n B: type,\n) = dictionary;\n```\n\n---\n\nLambda constructor.\n\nTyping Rule:\n\n<p align=\"center\"><img alt=\"typst-block\" src=\"data:image-hash/svg+xml;base64,redacted\" /></p>\n\n# Positional Parameters\n\n## A\n\n```typc\ntype: type\n```\n\nThe type of the argument.\n - <!-- typlite:begin:list-item 1 -->It can be also regarded as the condition of the proposition.<!-- typlite:end:list-item 1 -->\n\n## B (positional)\n\n```typc\ntype: type\n```\n\nThe type of the body.\n - <!-- typlite:begin:list-item 1 -->It can be also regarded as the conclusion of the proposition.<!-- typlite:end:list-item 1 -->",
"contents": "```typc\nlet lam(\n A: any,\n B: any,\n) = dictionary;\n```\n\n---\n\nLambda constructor.\n\nTyping Rule:\n\n<img alt=\"typst-block\" src=\"data:image-hash/svg+xml;base64,redacted\" />\n\n- <!-- typlite:begin:list-item 0 -->A (type): The type of the argument.\n \n - <!-- typlite:begin:list-item 0 -->It can be also regarded as the condition of the proposition.<!-- typlite:end:list-item 0 -->\n \n <!-- typlite:end:list-item 0 -->\n- <!-- typlite:begin:list-item 0 -->B (type): The type of the body.\n \n - <!-- typlite:begin:list-item 0 -->It can be also regarded as the conclusion of the proposition.<!-- typlite:end:list-item 0 -->\n \n <!-- typlite:end:list-item 0 -->\n\n# Positional Parameters\n\n## A\n\n```typc\ntype: \n```\n\n\n\n## B (positional)\n\n```typc\ntype: \n```\n\n",
"range": "12:20:12:23"
}

View file

@ -4,6 +4,6 @@ expression: "JsonRepr::new_redacted(result, &REDACT_LOC)"
input_file: crates/tinymist-query/src/fixtures/hover/render_equation_no_html.typ
---
{
"contents": "```typc\nlet lam(\n A: type,\n B: type,\n) = dictionary;\n```\n\n---\n\nLambda constructor.\n\nTyping Rule:\n\n```typc\n$ (Γ , x : A ⊢ M : B #h(2em) Γ ⊢ a:B)/(Γ ⊢ λ (x : A) → M : π (x : A) → B) $\n```\n\n# Positional Parameters\n\n## A\n\n```typc\ntype: type\n```\n\nThe type of the argument.\n - It can be also regarded as the condition of the proposition.\n\n## B (positional)\n\n```typc\ntype: type\n```\n\nThe type of the body.\n - It can be also regarded as the conclusion of the proposition.",
"contents": "```typc\nlet lam(\n A: any,\n B: any,\n) = dictionary;\n```\n\n---\n\nLambda constructor.\n\nTyping Rule:\n\n<img alt=\"typst-block\" src=\"data:image-hash/svg+xml;base64,redacted\" />\n\n- A (type): The type of the argument.\n \n - It can be also regarded as the condition of the proposition.\n \n \n- B (type): The type of the body.\n \n - It can be also regarded as the conclusion of the proposition.\n\n# Positional Parameters\n\n## A\n\n```typc\ntype: \n```\n\n\n\n## B (positional)\n\n```typc\ntype: \n```\n\n",
"range": "14:20:14:23"
}

View file

@ -317,7 +317,12 @@ impl fmt::Display for JsonRepr {
let mut ser = Serializer::with_formatter(w, PrettyFormatter::with_indent(b" "));
self.0.serialize(&mut ser).unwrap();
f.write_str(&String::from_utf8(ser.into_inner().into_inner().unwrap()).unwrap())
let res = String::from_utf8(ser.into_inner().into_inner().unwrap()).unwrap();
// replace Span(number) to Span(..)
static REG: LazyLock<regex::Regex> =
LazyLock::new(|| regex::Regex::new(r#"Span\((\d+)\)"#).unwrap());
let res = REG.replace_all(&res, "Span(..)");
f.write_str(&res)
}
}