From ba4f64eeb46e32a78b86f4855ef3b5df029b4ed2 Mon Sep 17 00:00:00 2001 From: JK Date: Sat, 6 Jun 2026 11:08:14 +0200 Subject: [PATCH] Refactor documentation and enhance features for TeXcore plugin - Updated PDF export documentation to clarify features and settings. - Improved quick preview functionality with detailed technical architecture. - Enhanced equation search and autocomplete sections for better user guidance. - Streamlined snippets documentation for command usage and text transformations. - Revamped TikZ diagrams section to include graphical editor details and usage instructions. - Revised getting started guide for clearer step-by-step setup instructions. - Added new navigation and configuration options in the documentation structure. - Updated plugin manifest files with improved descriptions for clarity. --- README.md | 4 +- docs/configuration/index.md | 39 +++ docs/configuration/settings.md | 190 ++++--------- docs/features/callout-support.md | 164 +++-------- docs/features/equations.md | 149 ++++------ docs/features/index.md | 41 +++ docs/features/pdf-export.md | 217 ++++----------- docs/features/quick-preview.md | 82 ++---- docs/features/search.md | 162 +++-------- docs/features/snippets.md | 53 ++-- docs/features/tikz.md | 105 ++++--- docs/getting-started.md | 101 ++++--- docs/index.md | 96 +++---- eslint.config.mjs | 1 + manifest-beta.json | 2 +- manifest.json | 2 +- mkdocs.yml | 2 + package.json | 2 +- scratch/test_compile.ts | 215 --------------- scratch/test_user_compile.ts | 256 ------------------ src/features/export-pdf/Progress.ts | 30 +- .../tikz-editor/components/canvas-grid.ts | 18 +- .../tikz-editor/components/left-sidebar.ts | 88 ++++-- .../tikz-editor/components/right-sidebar.ts | 73 +++-- src/features/tikz-editor/tikz-editor-modal.ts | 47 +++- src/features/tikz/live-preview-overlay.ts | 8 +- 26 files changed, 696 insertions(+), 1451 deletions(-) create mode 100644 docs/configuration/index.md create mode 100644 docs/features/index.md delete mode 100644 scratch/test_compile.ts delete mode 100644 scratch/test_user_compile.ts diff --git a/README.md b/README.md index 87212b1..0fc29d4 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ # TeXcore for Obsidian -A minimalistic, high-performance LaTeX assistant for automatic equation numbering, referencing, and on-demand TikZ rendering in [Obsidian.md](https://obsidian.md/). +A minimalistic, high-performance LaTeX assistant for automatic equation numbering, referencing, and TikZ rendering with basic GUI editing in [Obsidian.md](https://obsidian.md/) to transforms your vault into a rich ecosystem for scientific drafting and study. + + ๐Ÿ“š **[Full Documentation & Guides](https://youfoundjk.github.io/TeXcore/)** diff --git a/docs/configuration/index.md b/docs/configuration/index.md new file mode 100644 index 0000000..dbc5f26 --- /dev/null +++ b/docs/configuration/index.md @@ -0,0 +1,39 @@ +# Configuration Overview Matrix + +TeXcore is highly customizable. Use this matrix to understand the setting groups and click the links to explore detailed configurations on the [Settings Reference](settings.md) page. + +--- + +## Settings Group Matrix + +The plugin settings are divided into five main categories. Below is an overview of what is in each section: + +| Settings Category | One-Line Overview | Core Customization | Detailed Link | +| :--- | :--- | :--- | :--- | +| **Numbering & Referencing** | Adjust equation counting, prefixes, suffixes, and numbering styles. | Arabic/Roman numerals, tags display, and lazy numbering toggle. | [Go to Section :octicons-arrow-right-16:](settings.md#equation-numbering-referencing) | +| **Autocomplete & Search** | Customize editor triggers and math suggestions popups. | Suggestions trigger sequence, fuzzy/simple search method. | [Go to Section :octicons-arrow-right-16:](settings.md#autocomplete-search) | +| **PDF Export Options** | Adjust layout geometry, titles, background, and header templates. | Orientation, print background toggle, outline heading depth. | [Go to Section :octicons-arrow-right-16:](settings.md#pdf-export) | +| **Advanced PDF Settings** | Configure HTML page decoration headers/footers and sniper selections. | HTML variables class name mapping, CSS snippet filters. | [Go to Section :octicons-arrow-right-16:](settings.md#advanced-pdf-settings) | +| **Snippet Macros** | Manage custom LaTeX snippets inserted via command palette. | LaTeX template commands, custom snippet shortcuts. | [Go to Section :octicons-arrow-right-16:](settings.md#snippets) | + +--- + +## Highlighted Settings + +Explore settings key categories to customize your math publishing workspace: + +
+ +- :material-tune: **[Typographic Numbering](settings.md#equation-numbering-referencing)** + + Change equation counter styles to Arabic (`1`), uppercase Roman (`I`), or alphabetic (`a`). Append custom text like `Eq.` prefix or `.` suffix. + +- :material-laptop: **[Dynamic Suggestions](settings.md#autocomplete-search)** + + Control whether autocomplete triggers via `\eqref` or another custom code. Toggle whether the suggestion list renders math or shows raw LaTeX syntax. + +- :material-file-document-outline: **[Print Design](settings.md#pdf-export)** + + Customize output HTML headers/footers with variables such as page number, document date, and document title. + +
diff --git a/docs/configuration/settings.md b/docs/configuration/settings.md index decac7f..e4d3f7d 100644 --- a/docs/configuration/settings.md +++ b/docs/configuration/settings.md @@ -1,171 +1,77 @@ # Settings Reference -Complete reference for all TeXcore settings. +Configure TeXcore to adapt to your academic writing workflow. This page outlines all settings grouped by feature categories. + +--- ## Equation Numbering & Referencing -### Number only referenced equations -- **Type:** Toggle -- **Default:** On -- **Description:** Only add equation numbers to equations that have at least one reference in the note. Unreferenced equations remain unnumbered. +Adjust how equations are counted, tagged, and linked inside Obsidian. For details on how math indexing operates, see the [Equation Numbering Guide](../features/equations.md). -### Equation number prefix -- **Type:** Text -- **Default:** (empty) -- **Description:** Text to prepend to equation numbers. Example: `ยง` โ†’ `(ยง1)` - -### Equation number suffix -- **Type:** Text -- **Default:** (empty) -- **Description:** Text to append to equation numbers. Example: `.` โ†’ `(1.)` - -### Equation number initial count -- **Type:** Number -- **Default:** `1` -- **Description:** Starting number for equation counting. Set to `0` to start from zero. - -### Equation number style -- **Type:** Dropdown -- **Default:** `arabic` -- **Options:** - -| Style | Output | -|-------|--------| -| `arabic` | 1, 2, 3, ... | -| `alph` | a, b, c, ... | -| `Alph` | A, B, C, ... | -| `roman` | i, ii, iii, ... | -| `Roman` | I, II, III, ... | - -### Reference link prefix -- **Type:** Text -- **Default:** (empty) -- **Description:** Text before equation references. Example: `Eq.` โ†’ `Eq.(1)` - -### Reference link suffix -- **Type:** Text -- **Default:** (empty) -- **Description:** Text after equation references. - -### Show note title in equation link -- **Type:** Toggle -- **Default:** On -- **Description:** Display format for links. On: `Note Title > (1)`, Off: `(1)` +| Setting Name | Type & Default | Description | +| :--- | :--- | :--- | +| **Number only referenced equations** | Toggle (`On`) | Only add number tags to equations that have at least one active reference in the note. Unreferenced equations remain unnumbered. | +| **Equation number prefix** | Text (empty) | Appends custom characters before the equation number. Example: `ยง` renders as `(ยง1)`. | +| **Equation number suffix** | Text (empty) | Appends custom characters after the equation number. Example: `.` renders as `(1.)`. | +| **Equation number initial count** | Number (`1`) | The starting integer for equation counting. Set to `0` to begin from zero. | +| **Equation number style** | Dropdown (`arabic`) | The numeric formatting system used. Options: `arabic` (1, 2, 3), `alph` (a, b, c), `Alph` (A, B, C), `roman` (i, ii, iii), `Roman` (I, II, III). | +| **Reference link prefix** | Text (empty) | Character string injected before references. Example: `Eq.` renders as `Eq.(1)`. | +| **Reference link suffix** | Text (empty) | Character string injected after references. | +| **Show note title in equation link** | Toggle (`On`) | Format for display links. `On` resolves to `Note Title > (1)`; `Off` resolves to `(1)`. | --- ## Autocomplete & Search -### Enable autocompletion -- **Type:** Toggle -- **Default:** On -- **Description:** Enable the `\eqref` trigger autocomplete system. +Customize the autocompletion popup and fuzzy search indexing behaviors. For search behavior explanations, consult the [Search and Autocomplete Guide](../features/search.md). -### Trigger for autocompletion -- **Type:** Text -- **Default:** `\eqref` -- **Description:** Character sequence that opens the equation suggestion popup. +| Setting Name | Type & Default | Description | +| :--- | :--- | :--- | +| **Enable autocompletion** | Toggle (`On`) | Enables the popup suggestion list while editing. | +| **Trigger for autocompletion** | Text (`\eqref`) | The character trigger sequence that opens the autocomplete suggestions dropdown. | +| **Render math in suggestions** | Toggle (`On`) | Render LaTeX formulas dynamically inside suggestion listings. Disabling shows raw code. | +| **Search method** | Dropdown (`Fuzzy`) | Matches query to equations. Options: `Fuzzy` (allows typos/partial matching) or `Simple` (exact substring matching). | +| **Modifier to jump** | Key Selection (`Mod`) (1) | Keyboard modifier key held down when selecting a suggestion to jump to the equation instead of inserting it. | +| **Show modifier instruction** | Toggle (`On`) | Shows hint key instructions inside the autocomplete popup window. | +| **Open location** | Dropdown (`Current tab`) | Select editor layout focus when jumping. Options: `Current tab`, `Split right`, `Split down`, `New tab`, `New window`. | -### Render math in suggestions -- **Type:** Toggle -- **Default:** On -- **Description:** Show rendered LaTeX in the suggestion list. Off shows raw LaTeX text. +1. `Mod` defaults to ++ctrl++ on Windows/Linux and ++cmd++ on macOS. --- ## PDF Export -### Add file name as title -- **Type:** Toggle -- **Default:** On -- **Description:** Include the file name as an `

` heading at the top of the PDF. +Configure default options for the compiled PDF output. Learn about layout customization on the [PDF Export Page](../features/pdf-export.md). -### Display headers -- **Type:** Toggle -- **Default:** On -- **Description:** Show header on each PDF page. - -### Display footer -- **Type:** Toggle -- **Default:** On -- **Description:** Show footer on each PDF page. - -### Print background -- **Type:** Toggle -- **Default:** Off -- **Description:** Include background colors and images in PDF output. - -### Generate tagged PDF -- **Type:** Toggle -- **Default:** Off -- **Description:** Create accessible PDF with document structure tags (experimental). - -### Max headings level of the outline -- **Type:** Dropdown (1-6) -- **Default:** `6` -- **Description:** Maximum heading level to include in PDF bookmarks/outline. - -### PDF metadata -- **Type:** Toggle -- **Default:** Off -- **Description:** Include frontmatter fields (title, author, keywords) in PDF metadata. +| Setting Name | Type & Default | Description | +| :--- | :--- | :--- | +| **Add file name as title** | Toggle (`On`) | Prepends the markdown file name as an `

` element at the start of the compiled PDF. | +| **Display headers** | Toggle (`On`) | Renders headers on each page. Custom templates can be set under Advanced settings. | +| **Display footer** | Toggle (`On`) | Renders footers on each page. | +| **Print background** | Toggle (`Off`) | Includes editor background colors and styling assets in PDF print output. | +| **Generate tagged PDF** | Toggle (`Off`) | Generates accessible PDF document structures containing layout tags (experimental). | +| **Max headings level of outline** | Dropdown (`6`) | Maximum outline depth (h1โ€“h6) parsed to build PDF bookmark trees. | +| **PDF metadata** | Toggle (`Off`) | Extracts yaml frontmatter parameters to populate PDF document metadata fields. | --- ## Advanced PDF Settings -### Header Template -- **Type:** Textarea -- **Default:** -```html -
- -
-``` -- **Description:** HTML template for page headers. Use classes: `date`, `title`, `url`, `pageNumber`, `totalPages`. +Customize HTML layout templates and manage style overrides during compilation. -### Footer Template -- **Type:** Textarea -- **Default:** -```html -
- / -
-``` -- **Description:** HTML template for page footers. - -### Add timestamp to output file name -- **Type:** Toggle -- **Default:** Off -- **Description:** Append export timestamp to PDF filename. - -### Select CSS snippets -- **Type:** Toggle -- **Default:** Off -- **Description:** Enable selection of disabled CSS snippets to apply to PDF export only. - -### Limit concurrent renders -- **Type:** Text -- **Default:** `5` -- **Description:** Maximum parallel file renders during batch export. +| Setting Name | Type & Default | Description | +| :--- | :--- | :--- | +| **Header Template** | Textarea (standard layout) | Custom HTML template for page headers. Supports class variable tags (e.g. `date`, `title`). | +| **Footer Template** | Textarea (standard layout) | Custom HTML template for page footers. Supports class variable tags (e.g. `pageNumber`, `totalPages`). | +| **Add timestamp to filename** | Toggle (`Off`) | Appends the export compile timestamp to the output `.pdf` filename. | +| **Select CSS snippets** | Toggle (`Off`) | Allows choosing disabled CSS snippets to compile and apply to PDF layout only. | +| **Limit concurrent renders** | Text (`5`) | Limit concurrent parallel rendering tasks during folder/batch exports. | --- -## Debug +## Snippets & Debugging -### Debug mode -- **Type:** Toggle -- **Default:** Off -- **Description:** Enable debug button in export modal to open DevTools for the preview webview. - ---- - -## Snippets - -### Manage Snippets -Access via the **Add Snippet** button. Each snippet has: - -- **Name:** Display name in command palette -- **Content:** LaTeX code to insert - -Snippets are stored in `data.json` with the plugin settings. +| Setting Name | Type & Default | Description | +| :--- | :--- | :--- | +| **Manage Snippets** | Modal Dialog | Add, edit, or remove custom LaTeX command snippets triggered via the Command Palette. | +| **Debug mode** | Toggle (`Off`) | Enables the **Debug** button in the PDF export dialog to open Electron DevTools for inspecting the render webview. | diff --git a/docs/features/callout-support.md b/docs/features/callout-support.md index eb40337..216c031 100644 --- a/docs/features/callout-support.md +++ b/docs/features/callout-support.md @@ -1,141 +1,53 @@ # Callout Support -Use math blocks inside Obsidian callouts with automatic indentation handling. +TeXcore seamlessly integrates with Obsidian's native Callout system. Writing display math inside block quotes requires prepending the callout character `>` to every single line. TeXcore parses these prefixes automatically to keep your formula coordinates indexed, numbered, and styled correctly without breaking the callout container. -## The Challenge +--- -Obsidian callouts require `>` prefixes on every line: +## Indentation Syntax -```markdown -> [!note] -> This is a callout with -> multiple lines. -``` +When drafting formulas inside a callout box, make sure that all lines inside the math block maintain the correct prefix. -Math blocks inside callouts need the same treatment: +=== "Standard Callout" + ```markdown + > [!theorem] Green's Theorem + > $$ + > \oint_C (L \, dx + M \, dy) = \iint_D \left(\frac{\partial M}{\partial x} - \frac{\partial L}{\partial y}\right) dx \, dy + > % id: eq-greens + > $$ + ``` -```markdown -> [!theorem] -> $$ -> E = mc^2 -> % id: eq-callout -> $$ -``` +=== "Nested Callouts" + ```markdown + > [!note] Parent Callout + > Outer text body. + > > [!tip] Nested Equation + > > $$ + > > e^{i\pi} + 1 = 0 + > > % id: eq-euler + > > $$ + ``` -Without proper indentation, the math block breaks the callout. +--- -## Automatic Handling +## Automatic Fix Command -The plugin automatically handles callout prefixes when: +Inconsistent callout prefixes (like a missing `>` on the `% id:` line or closing `$$`) will cause Obsidian to break out of the callout box. To resolve this, use the automatic fix tool: -- Adding `\tag{}` commands -- Inserting ID comments -- Managing multi-line equations +1. Open the Command Palette using ++ctrl+p++ (or ++cmd+p++ on macOS). +2. Type and select `Fix callout equations in active note`. +3. TeXcore will scan your active note, detect broken display blocks inside callouts, and automatically prepend missing prefixes or normalize spacing to a clean `> `. -### Example +| Prefix Error Pattern | Fix Applied | Result | +| :--- | :--- | :--- | +| **Missing `>` prefix on `% id` line** | Injects missing callout character. | `> % id: eq-name` | +| **Missing `>` prefix on closing `$$`** | Injects missing callout character. | `> $$` | +| **Inconsistent spacing (e.g. `> $$`)** | Normalizes prefix spacing. | `> $$` | -**Input (with proper prefixes):** -```markdown -> [!theorem] Einstein's Equation -> $$ -> E = mc^2 -> % id: eq-einstein -> $$ -``` +--- -**After plugin processing:** -```markdown -> [!theorem] Einstein's Equation -> $$ -> E = mc^2 \tag{1} -> % id: eq-einstein -> $$ -``` +## PDF Export & Preview Compatibility -The plugin preserves the `>` prefix on all lines. - -## Fix Command - -If your callout equations have broken indentation, use the fix command: - -1. Open Command Palette (`Ctrl/Cmd + P`) -2. Search for "Fix callout equations in active note" -3. Run the command - -This scans all math blocks and fixes any incorrect indentation. - -### What It Fixes - -| Before | After | -|--------|-------| -| Missing `>` on ID line | Adds `>` prefix | -| Missing `>` on closing `$$` | Adds `>` prefix | -| Inconsistent spacing | Normalizes to `> ` | - -## Nested Callouts - -The plugin supports nested callouts: - -```markdown -> [!note] -> Outer callout -> > [!tip] -> > $$ -> > x^2 + y^2 = z^2 -> > % id: eq-nested -> > $$ -``` - -The prefix `> > ` is detected and preserved on all lines. - -## Supported Formats - -The plugin recognizes these callout prefix patterns: - -| Pattern | Example | -|---------|---------| -| Standard | `> ` | -| Nested | `> > ` | -| Triple nested | `> > > ` | -| With leading space | ` > ` | - -## Live Preview Behavior - -In Live Preview mode: - -1. The opening `$$` line's prefix is detected -2. All subsequent lines use the same prefix -3. The closing `$$` line uses the same prefix - -This ensures consistent rendering across the entire math block. - -## Common Issues - -### Breaking Out of Callout - -If your math appears below the callout: - -```markdown -> [!note] -> Some text -$$ -E = mc^2 -$$ -``` - -**Fix:** Add `>` prefix to the math block lines, or use the fix command. - -### Missing ID After Fix - -If the ID disappears after a fix: - -1. The ID line may have had invalid characters -2. Manually re-add: `> % id: eq-xxx` - -### PDF Export - -Callout math blocks export correctly to PDF with: - -- Callout styling preserved -- Math rendering intact -- Equation numbers displayed +Callouts containing math blocks fully support all other TeXcore features: +- **Hover Previews**: Hovering over inline references correctly renders the callout box context inside the [Page Preview Popup](quick-preview.md). +- **PDF Compilation**: The callout color styles, borders, and equation number tags are preserved in the compiled [PDF Export](pdf-export.md). diff --git a/docs/features/equations.md b/docs/features/equations.md index f83c753..566acd2 100644 --- a/docs/features/equations.md +++ b/docs/features/equations.md @@ -1,131 +1,94 @@ -# Equation Numbering & Referencing +# Equation & Sub-Equation Numbering -The core feature of TeXcore is automatic equation numbering using LaTeX `\tag{}` commands and smart referencing via internal links. +The core engine of TeXcore provides automatic equation numbering using LaTeX `\tag{}` commands, allowing you to generate academic-grade numbering schemes inside Obsidian. This feature is heavily integrated with the plugin's [Autocomplete System](search.md) and [Quick Hover Previews](quick-preview.md) to keep references synchronized in both Live Preview and Reading View. -## How Equation IDs Work +--- -Unlike Obsidian's built-in block references (`^block-id` after content), this plugin uses LaTeX comments inside math blocks: +## Defining Equation Identifiers + +Unlike Obsidian's standard markdown block references which append identifiers at the end of elements, TeXcore indexes equations using LaTeX comments inside the display math block. Add a `% id: eq-name` comment line immediately before the closing `$$` delimiter. The prefix `eq-` is required, followed by alphanumeric characters or hyphens. ```latex $$ E = mc^2 -% id: eq-einstein +% id: eq-einstein (1) $$ ``` -The `% id: eq-einstein` comment: +1. This comment is invisible in the final rendered note but is parsed by TeXcore's background indexing system to build the active note cache. -- Is invisible in rendered output (it's a LaTeX comment) -- Creates a unique identifier for the equation -- Must use the format `eq-` followed by alphanumeric characters and hyphens -- Must be on its own line before the closing `$$` +--- -## Automatic Numbering +## Lazy Numbering Mechanism -When an equation has an ID and is referenced somewhere in the note, the plugin automatically inserts a `\tag{}` command: +To keep your document layout clean, TeXcore uses **lazy numbering** by default. An equation is only assigned a right-aligned number tag if it is actively referenced somewhere in the vault. If the equation is not referenced, no tag is injected, keeping margins clear. You can disable this behavior to force-number all equations in the [Settings Reference](configuration/settings.md#number-only-referenced-equations). -**Before (what you write):** -```latex -$$ -E = mc^2 -% id: eq-einstein -$$ +=== "1. Editor Code" + Write the math block and reference it inline using Obsidian double brackets: + + ```latex + $$ + E = mc^2 + % id: eq-einstein + $$ + + As shown in [[#^eq-einstein]], mass is equivalent to energy. + ``` -See equation [[#^eq-einstein]]. -``` +=== "2. Generated Output" + The compiler automatically appends the `\tag{}` macro and renders the link with the assigned index: + + $$ + E = mc^2 \tag{1} + $$ + + As shown in [(1)](#), mass is equivalent to energy. -**After (what the plugin generates):** -```latex -$$ -E = mc^2 \tag{1} -% id: eq-einstein -$$ +--- -See equation [[#^eq-einstein]]. -``` +## Numbering Customization -!!! note "Number Only Referenced Equations" - By default, equations are only numbered if they're referenced somewhere. This can be changed in settings. +Configure the layout of your math indices globally via the [Settings Panel](configuration/settings.md#equation-numbering-referencing). Numbering formats support a variety of typographic styles, prefixes, and suffixes. -## Number Styles +### Number Styles Matrix -Configure the numbering style in plugin settings: +| Style Name | Description | Output Example | +| :--- | :--- | :--- | +| `arabic` | Standard Hindu-Arabic numerals. | `(1)`, `(2)`, `(3)` | +| `alph` | Lowercase Latin alphabetical indexing. | `(a)`, `(b)`, `(c)` | +| `Alph` | Uppercase Latin alphabetical indexing. | `(A)`, `(B)`, `(C)` | +| `roman` | Lowercase Roman numerals. | `(i)`, `(ii)`, `(iii)` | +| `Roman` | Uppercase Roman numerals. | `(I)`, `(II)`, `(III)` | -| Style | Example Output | -|-------|----------------| -| `arabic` | 1, 2, 3, ... | -| `alph` | a, b, c, ... | -| `Alph` | A, B, C, ... | -| `roman` | i, ii, iii, ... | -| `Roman` | I, II, III, ... | +!!! info "Prefixes & Suffixes" + Prepending prefixes (e.g., `Eq.`) or appending suffixes (e.g., `.`) will modify the output. For example, a prefix of `Eq.` with style `arabic` renders as `(Eq.1)`. A suffix of `.` renders as `(1.)`. See [Reference Link Customization](configuration/settings.md#reference-link-prefix) for additional details. -## Prefix and Suffix +--- -Add custom text before/after equation numbers: +## Sub-Equation System -| Setting | Value | Result | -|---------|-------|--------| -| Prefix: `ยง` | - | (ยง1) | -| Suffix: `.` | - | (1.) | -| Prefix: `Eq.` | - | (Eq.1) | - -## Sub-Equations - -For multi-line equations, reference individual lines using sub-indices: +Multi-line equations (such as systems of equations using LaTeX `align` or split blocks) can be individually referenced using sub-indices. Reference rows sequentially by appending the row number to the parent ID: ```latex $$ +\begin{aligned} a &= b + c \\ d &= e + f +\end{aligned} % id: eq-system $$ -First equation: [[#^eq-system-1]] -Second equation: [[#^eq-system-2]] +Referencing row 1: [[#^eq-system-1]] +Referencing row 2: [[#^eq-system-2]] ``` -The plugin generates: +The plugin automatically parses the structure and appends sub-tags such as `\tag{1.1}` to the first equation line and `\tag{1.2}` to the second. -- `\tag{1.1}` for the first row -- `\tag{1.2}` for the second row +--- -## Referencing Equations +## Inserting References -### Using Autocomplete +To reference an equation quickly, type your trigger (default is `\eqref`) to activate the autocomplete dialog. Use your keyboard arrow keys to scroll, and press ++enter++ to insert the formatted wiki-link. -1. Type your trigger string (default: `\eqref`) -2. A suggestion popup appears with all equations in the current note -3. Select an equation using arrow keys or mouse -4. Press Enter to insert `[[#^eq-id]]` - -### Jump to Equation - -Hold the modifier key (default: `Ctrl`/`Cmd`) while pressing Enter on a suggestion to jump to the equation instead of inserting a link. - -### Reference Display Format - -Configure how equation links are displayed: - -| Setting | Display Format | -|---------|----------------| -| Show note title: ON | `Note Title > (1)` | -| Show note title: OFF | `(1)` | -| Reference prefix: `Eq.` | `Eq.(1)` | -| Reference suffix: none | `(1)` | - -## Live Preview vs Reading View - -The plugin works in both Obsidian views: - -- **Live Preview**: Uses CodeMirror extensions for real-time numbering -- **Reading View**: Uses Markdown post-processors for rendered output - -Both views stay synchronized - editing in one updates the other. - -## Equation Cache - -The plugin maintains a cache of all equations in the current note for fast lookup. This cache is: - -- Built when a note is opened -- Updated on every document change -- Used for autocomplete suggestions and hover previews +!!! tip "Jump-to-Equation Shortcut" + Holding the ++ctrl++ key (or ++cmd++ on macOS) while pressing ++enter++ on a suggestion in the autocomplete list will instantly jump your cursor to the equation definition instead of inserting the link. diff --git a/docs/features/index.md b/docs/features/index.md new file mode 100644 index 0000000..77250d6 --- /dev/null +++ b/docs/features/index.md @@ -0,0 +1,41 @@ +# Features Overview Matrix + +Discover the full capabilities of TeXcore. Use this matrix to quickly navigate between features and learn how to configure them inside your vault. + +--- + +## Interactive Feature Matrix + +The following matrix provides a side-by-side view of all available modules, how they enhance your Obsidian writing, and where to configure their properties. + +| Feature Module | One-Line Overview | Documentation | Configuration Property | +| :--- | :--- | :--- | :--- | +| **Equation Numbering** | Automatic indexing and tag insertion for LaTeX math blocks. | [Read Guide :octicons-arrow-right-16:](equations.md) | [Number Style & Prefix](configuration/settings.md#equation-numbering-referencing) | +| **PDF Export** | Advanced PDF renderer featuring live preview pane and snippets. | [Read Guide :octicons-arrow-right-16:](pdf-export.md) | [Templates & Margins](configuration/settings.md#pdf-export) | +| **LaTeX Snippets** | Custom LaTeX macros map to shortcut commands for fast editing. | [Read Guide :octicons-arrow-right-16:](snippets.md) | [Manage Snippets](configuration/settings.md#snippets) | +| **Equation Search** | Vault-wide fuzzy search index modal to locate and insert links. | [Read Guide :octicons-arrow-right-16:](search.md) | [Autocomplete & Search](configuration/settings.md#autocomplete-search) | +| **Quick Preview** | Real-time preview popups and link resolution on hover. | [Read Guide :octicons-arrow-right-16:](quick-preview.md) | [Render Math in Suggestions](configuration/settings.md#render-math-in-suggestions) | +| **TikZ Diagrams** | Render vector graphics using native TikZ scripts and component packages. | [Read Guide :octicons-arrow-right-16:](tikz.md) | [Component Library](configuration/settings.md#snippets) | +| **Callout Support** | Auto-alignment and indentation parsing inside Obsidian callouts. | [Read Guide :octicons-arrow-right-16:](callout-support.md) | [Fix Indentation](configuration/settings.md#debug) | + +--- + +## Advanced Feature Highlights + +To help you decide which features fit your academic drafting workflow, explore the specialized sub-systems: + +
+ +- :material-sigma: **[Equation & Sub-equation Numbering](equations.md)** + + Supports both standard single-line formulas and multi-line sub-equations with custom templates (e.g., `(1.1)`, `(1.2)`). Includes customizable numbering bases (Arabic, Roman, Alphabetic). + +- :material-file-pdf-box: **[Advanced PDF Printing](pdf-export.md)** + + Features a dual-pane interface with responsive styling, page counters, and margins. Directly utilizes Obsidian's CSS snippets to style headers and footers. + +- :material-draw-pen: **[TikZ & SVG Vector Rendering](tikz.md)** + + Write vector scripts using LaTeX packages (e.g., `circuitikz`, `pgfplots`) and see them compiled dynamically. Features an interactive sidebar library for drag-and-drop circuit components. + +
diff --git a/docs/features/pdf-export.md b/docs/features/pdf-export.md index f403948..9b81e41 100644 --- a/docs/features/pdf-export.md +++ b/docs/features/pdf-export.md @@ -1,176 +1,75 @@ -# PDF Export +# PDF Compilation & Export -Export your notes to PDF with full equation rendering, customizable layouts, and live preview. +TeXcore provides an advanced PDF rendering module to export your mathematical notes with full math layout support. Accessible via ++ctrl+p++ (search `Export current file to PDF`) or by right-clicking any file/folder in the file explorer to choose **Better Export PDF**, this modal splits into a real-time responsive 2/3 preview pane and a 1/3 export configuration sidebar. -## Opening the Export Dialog - -### Via Command Palette -1. Press `Ctrl/Cmd + P` -2. Search for "Export current file to PDF" -3. The export modal opens with a live preview - -### Via File Menu -Right-click a file or folder in the file explorer โ†’ **Better Export PDF** - -### Folder Export Options -For folders, you get additional options: - -- **Export each file to PDF**: Creates separate PDFs for each file -- **Generate TOC.md file**: Creates a table of contents file - -## Export Modal - -The export dialog has two panels: - -| Left Panel (2/3) | Right Panel (1/3) | -|------------------|-------------------| -| Live PDF preview | Export settings | - -### Preview Features -- Real-time preview updates as you change settings -- Scales to fit the container -- Shows print dimensions for custom page sizes - -## Page Settings - -### Page Size -Choose from standard sizes or custom dimensions: - -| Size | Dimensions (mm) | -|------|-----------------| -| A0 - A6 | Standard ISO sizes | -| Letter | 215.9 ร— 279.4 | -| Legal | 215.9 ร— 355.6 | -| Tabloid | 279.4 ร— 431.8 | -| Ledger | 431.8 ร— 279.4 | -| Custom | User-defined | - -For custom sizes, enter width and height in millimeters. - -### Margins - -| Type | Description | -|------|-------------| -| None | No margins | -| Default | 10mm all sides | -| Small | Minimal margins | -| Custom | User-defined per side | - -Custom margins are specified in millimeters for top, bottom, left, and right. - -### Orientation -- **Portrait**: Standard vertical layout -- **Landscape**: Horizontal layout - -### Downscale Percent -Reduce content size (0-100%). Useful for fitting more content per page. - -## Header & Footer - -### Toggle Display -Enable/disable headers and footers separately. - -### Template Variables -Use these classes in your HTML templates: - -| Class | Content | -|-------|---------| -| `date` | Formatted print date | -| `title` | Document title | -| `url` | Document location | -| `pageNumber` | Current page number | -| `totalPages` | Total page count | - -### Example Templates - -**Header (centered title):** -```html -
- -
-``` - -**Footer (page numbers):** -```html -
- / -
-``` - -## Additional Options - -### Include File Name as Title -Adds an `

` with the file name at the top of the PDF. - -### Print Background -Include background colors and images in the PDF. - -### Generate Tagged PDF -Creates an accessible PDF with document structure tags. Note: This is experimental. - -### Open After Export -Automatically open the PDF after export completes. - -### CSS Snippets -Apply custom CSS to the exported PDF: - -1. Create a CSS file in `.obsidian/snippets/` -2. Keep the snippet **disabled** in Obsidian settings -3. Select it in the export dialog dropdown - -The snippet CSS will only apply to the PDF, not your regular Obsidian view. - -## Advanced Settings - -### Max Heading Level -Control which headings appear in the PDF outline/bookmarks (h1-h6). - -### PDF Metadata -Add frontmatter fields to PDF metadata: - -```yaml --- -title: My Document -author: John Doe -keywords: math, equations -subject: Academic Paper + +## Core Layout Settings + +Customize document geometry, printing limits, and margins before compilation. + +| Control Option | Description | Typical Options | +| :--- | :--- | :--- | +| **Page Size** | Select standard sheet geometry or define millimeters. | `A4`, `Letter`, `Legal`, `Tabloid`, or `Custom` | +| **Orientation** | Vertical portrait layout or horizontal landscape. | `Portrait`, `Landscape` | +| **Margins** | Inner safety margins specified in millimeters per edge. | `None`, `Default (10mm)`, `Small`, or `Custom` | +| **Downscale** | Scales content down (0โ€“100%) to fit complex tables/formulas. | Integer slider percentage | + --- -``` -### Timestamp in Filename -Append export timestamp to the output filename. +## Headers & Footers Configuration -### Concurrent Renders -For batch exports, limit concurrent file processing (default: 5). +Headers and footers render HTML dynamically using dedicated style selectors. -## Batch Export +### HTML Template Classes +Include these target element class names in your header/footer HTML code. TeXcore will parse and replace them with print values: -When exporting a folder: +- ``: Resolves to note file title. +- ``: Resolves to current page index. +- ``: Resolves to total compiled pages. +- ``: Inserts local print timestamp. +- ``: Inserts workspace source path. -1. Right-click the folder -2. Select **Export to PDF...** โ†’ **Export each file to PDF** -3. Choose output directory -4. All files export in parallel with progress indicator +=== "Header Template" + ```html +
+ +
+ ``` -## Table of Contents +=== "Footer Template" + ```html +
+ / +
+ ``` -Generate a TOC file for folder exports: +--- -1. Right-click a folder -2. Select **Export to PDF...** โ†’ **Generate TOC.md file** -3. A `_TOC_.md` file is created with links to all files +## Accessing Custom CSS Styling -The TOC uses frontmatter `toc: true` to enable special processing during export. +Style your PDF print outputs independently of Obsidian's active theme. -## Troubleshooting +1. Write a custom `.css` template sheet and save it inside your vault folder: `.obsidian/snippets/`. +2. Keep this snippet **disabled** in Obsidian settings. This prevents it from altering your editor UI. +3. Open the TeXcore Export Dialog, and choose your stylesheet in the dropdown menu. The snippet's styling will be injected exclusively during PDF compilation. -### Debug Mode -Enable debug mode in settings to access the **Debug** button in the export modal, which opens browser DevTools for the preview webview. +--- -### Common Issues +## Advanced Compiler Operations -| Issue | Solution | -|-------|----------| -| Equations not rendering | Wait for preview to fully load | -| CSS not applying | Ensure snippet is disabled in Obsidian | -| Export fails | Check file permissions on output path | +???+ info "Batch Compilation & Tables of Contents" + When right-clicking a folder, you can choose **Export each file to PDF** to compile all children in parallel. The **Generate TOC.md file** action generates a table of contents file (`_TOC_.md`) automatically. Configure parallel limits under the [Settings Reference](configuration/settings.md#limit-concurrent-renders). + +???+ warning "Metadata & Bookmark Outlines" + If enabled in [Settings Panel](configuration/settings.md#pdf-metadata), TeXcore compiles Frontmatter fields (`title`, `author`, `keywords`, `subject`) into metadata tags inside the output PDF. Outlines index heading levels 1 through 6 dynamically depending on user preferences. + +--- + +## Troubleshooting Print Errors + +!!! failure "Common Rendering Obstacles" + - **Broken Equations / Untagged Math**: If preview is incomplete, wait a few seconds before hitting export. MathJax runs asynchronously and needs time to process large nodes. + - **Missing Snippet Styles**: Double check that your snippet stylesheet is in `.obsidian/snippets/` and is *disabled* in standard Obsidian settings. + - **Write Failures**: Ensure the output path is writable and not locked by another PDF reader app. + - **Webview Crashes**: If errors persist, enable **Debug Mode** in the [Settings Reference](configuration/settings.md#debug-mode) and click the **Debug** button in the modal to inspect the Electron DevTools container. diff --git a/docs/features/quick-preview.md b/docs/features/quick-preview.md index 3be7fae..12365cd 100644 --- a/docs/features/quick-preview.md +++ b/docs/features/quick-preview.md @@ -1,72 +1,42 @@ -# Quick Preview +# Quick Hover Previews -Hover over equation links to see a rendered preview popup. +TeXcore integrates with Obsidian's core page preview mechanics to display real-time, mathematical popups whenever you hover over an equation link like `[[#^eq-einstein]]` or highlight items in the [Autocomplete Suggestions Menu](search.md). -## Overview +--- -When you hover over an equation reference like `[[#^eq-einstein]]`, a popup appears showing: +## Technical Architecture -- The rendered equation -- Context around the equation - -This uses Obsidian's built-in Page Preview plugin, enhanced to work with equation block references. - -## How It Works - -The plugin patches Obsidian's `page-preview` internal plugin to: - -1. Detect equation links (format: `[[#^eq-xxx]]`) -2. Look up the equation in the cache -3. Scroll the preview to the correct line +The preview lookup hooks into Obsidian's internal `page-preview` engine. TeXcore intercepts link hover events, references the identifier against the active document's equation cache, locates the line offset, and scrolls the popup container to display the equation in focus. ```mermaid sequenceDiagram - participant User - participant PagePreview - participant Plugin - participant Cache + participant User as Editor hover + participant PP as Page Preview Plugin + participant C as TeXcore Cache + participant V as Preview Popup View - User->>PagePreview: Hover on [[#^eq-id]] - PagePreview->>Plugin: onLinkHover - Plugin->>Cache: Lookup eq-id - Cache-->>Plugin: Line number - Plugin->>PagePreview: Show preview at line - PagePreview->>User: Popup displayed + User->>PP: Hover [[#^eq-id]] + PP->>C: Query eq-id coordinate + C-->>PP: Return line number + PP->>V: Compile MathJax & scroll to line + V->>User: Display preview popup ``` -## Requirements +--- -The Page Preview core plugin must be enabled: +## Requirements & Setup -1. **Settings** โ†’ **Core plugins** -2. Enable **Page preview** +!!! important "Enable Core Page Preview" + This feature relies on Obsidian's core architecture. Open **Settings** โ†’ **Core plugins** and toggle **Page preview** to **Enable**. If this core dependency is deactivated, TeXcore will log a console warning and hover previews will fallback to standard text previews without viewport scrolling. -If disabled, the plugin logs a message and hover previews won't work. +--- -## Preview Content +## Troubleshooting Previews -The popup shows the full note context around the equation, similar to hovering over any Obsidian internal link. The equation is scrolled into view automatically. +If preview boxes fail to load or scroll incorrectly, consult the diagnostic table below: -## Autocomplete Preview - -When using the `\eqref` autocomplete: - -- Hover over suggestions to preview equations -- Uses the same preview system -- Works in both search modal and editor suggest - -## Configuration - -No additional configuration needed. The feature works automatically when: - -- Page Preview core plugin is enabled -- Equations have valid `% id: eq-xxx` comments -- Equation cache is up to date - -## Troubleshooting - -| Issue | Solution | -|-------|----------| -| No popup appears | Enable Page Preview core plugin | -| Wrong content shown | Equation cache may be stale, edit the file to refresh | -| Preview doesn't scroll | Equation ID may be malformed | +| Symptom | Cause | Solution | +| :--- | :--- | :--- | +| **No preview modal opens** | Page Preview core plugin is toggled off. | Toggle on Page Preview in Obsidian settings. | +| **Formula text displays raw LaTeX** | MathJax rendering is disabled for suggestions. | Toggle on [Render Math in Suggestions](configuration/settings.md#render-math-in-suggestions). | +| **Preview focuses the wrong line** | Malformed `% id: eq-name` or stale index cache. | Touch-edit the note content to trigger a cache re-index. | diff --git a/docs/features/search.md b/docs/features/search.md index 12e0211..e6507ad 100644 --- a/docs/features/search.md +++ b/docs/features/search.md @@ -1,152 +1,52 @@ # Equation Search & Autocomplete -Find and reference equations quickly using fuzzy search and autocomplete. +TeXcore offers a robust, keyboard-driven interface to search, preview, and link equations inside your vault. This module consists of two main interfaces: an editor-integrated **Autocomplete Popup** and a dedicated **Search Modal**. -## Autocomplete +--- -### Triggering Autocomplete +## Editor Autocomplete Popup -Type the trigger string (default: `\eqref`) in the editor to open the suggestion popup: +Trigger autocompletion anywhere in your notes by typing your configured trigger string (default is `\eqref`). A dropdown menu instantly pops up at your cursor, displaying all equations matching your query along with their parsed line numbers and live math previews. -```markdown -As shown in \eqref - โ†‘ popup appears here -``` +| Action Key | Result | Description | +| :--- | :--- | :--- | +| `โ†‘` / `โ†“` | Highlight suggestion | Navigate the list of cached equations. | +| ++enter++ | Insert reference link | Appends `[[#^eq-id]]` at cursor. | +| ++ctrl+enter++ (1) | Jump to definition | Instantly shifts editor focus to the math block declaration. | +| ++escape++ | Close popup | Discards current search query. | -### Suggestion List +1. Maps to ++cmd+enter++ on macOS environments. Instructions are displayed inline inside the popup if configured in settings. -The popup shows all equations in the current note with: +!!! info "Automatic Identifier Generation" + If you reference an equation that has no identifier tag, TeXcore automatically generates a unique ID (e.g. `% id: eq-b5x3e2`), appends it before the closing `$$`, and inserts the resolved link to ensure referencing consistency. -- Rendered math preview (can be disabled) -- Line number where the equation appears -- Sorted by relevance to your query +--- -### Selecting a Suggestion +## Vault Search Modal -| Action | Result | -|--------|--------| -| `โ†‘` / `โ†“` | Navigate suggestions | -| `Enter` | Insert `[[#^eq-id]]` link | -| `Mod + Enter` | Jump to equation location | -| `Esc` | Close popup | -| Click | Select and insert | +Access the dedicated note-wide search dialog by triggering ++ctrl+p++ and running `Search equations in active note`. This modal provides an expanded viewport displaying equation previews on hover. It uses two search algorithms configurable in your [Autocomplete Settings](configuration/settings.md#autocomplete-search): -!!! tip "Modifier Key" - The "jump" modifier is `Ctrl` on Windows/Linux or `Cmd` on macOS by default. Configure this in settings. +- **Fuzzy Search (Default)**: Matches partial strings and typos (e.g., query `intgl` matches `\integral` or `\int`) using Obsidian's internal `prepareFuzzySearch` utility. +- **Simple Search**: Matches exact substrings (e.g., query `int` matches `\int` and `point` but not `frac`) using Obsidian's `prepareSimpleSearch` utility. -### After Selection +--- -When you select an equation: +## Navigation Target Configuration -1. If the equation has an ID, the link is inserted immediately -2. If no ID exists, one is auto-generated and inserted +When jumping to equation definitions via the autocomplete suggestion list, you can configure the editor target split layout in [Settings Reference](configuration/settings.md#open-location): -Inserted link format: -```markdown -[[#^eq-gaussian]] -``` +=== "Standard Navigation (Default)" + Resolves the jump within your active tab view, replacing current focus. -With `insertSpace` enabled (default): -```markdown -[[#^eq-gaussian]] -``` +=== "Split Tab Right" + Splits the active pane vertically and focuses the equation on the right-hand panel. -## Search Modal +=== "New Window / Split Down" + Opens a detached editor window or horizontal split viewport to show equations side-by-side. -### Opening the Modal +--- -1. Press `Ctrl/Cmd + P` -2. Search for "Search equations in active note" -3. Or use the hotkey if you've assigned one +## Performance Notes & Best Practices -### Modal Features - -The search modal provides: - -- Full equation search across the active note -- Same fuzzy/simple search as autocomplete -- Keyboard navigation -- Quick preview on hover - -## Search Methods - -### Fuzzy Search (Default) - -Matches partial strings with typos allowed: - -| Query | Matches | -|-------|---------| -| `intgl` | `\integral`, `\int` | -| `frac` | `\frac`, `fraction` | -| `sumx` | `\sum_{x}`, `\sum_x` | - -Uses Obsidian's built-in `prepareFuzzySearch` algorithm. - -### Simple Search - -Exact substring matching: - -| Query | Matches | -|-------|---------| -| `int` | `\int`, `\integral`, `point` | -| `frac` | `\frac`, `fraction` | - -Uses Obsidian's `prepareSimpleSearch` algorithm. - -Toggle between methods in plugin settings. - -## Configuration - -### Autocomplete Settings - -| Setting | Description | Default | -|---------|-------------|---------| -| Enable autocompletion | Toggle autocomplete feature | โœ… On | -| Trigger string | Text that opens the popup | `\eqref` | -| Render math in suggestions | Show rendered or raw LaTeX | โœ… On | - -### Navigation Settings - -| Setting | Description | Default | -|---------|-------------|---------| -| Modifier to jump | Key for jump action | `Mod` | -| Show modifier instruction | Display keyboard hints | โœ… On | -| Open location | Where to open file when jumping | Current tab | - -### Jump Location Options - -When jumping to an equation, choose target: - -| Option | Behavior | -|--------|----------| -| Current tab | Replace current view | -| Split right | Open in right split | -| Split down | Open in bottom split | -| New tab | Open in new tab | -| New window | Open in new window | - -## Tips - -### Custom Trigger - -Change the trigger string to avoid conflicts: - -- `\ref` - Shorter alternative -- `#eq` - Hash-based trigger -- `@eq` - Symbol-based trigger - -### Performance - -The search operates on the active note only: - -- No vault-wide scanning -- Instant results -- Works offline - -### Combining with Snippets - -1. Create a snippet with equation template -2. Insert using snippet command -3. Fill in the equation -4. Add ID immediately -5. Reference later with `\eqref` +- **Vault Performance**: The search indexing index is scoped to the **active note only**. This prevents vault-wide scan lag, resulting in instant search evaluations even on low-end hardware. +- **Custom Triggers**: You can change the trigger string in [Autocomplete & Search Settings](configuration/settings.md#autocomplete-search) to `#eq` or `@eq` if you want to avoid overlaps with other LaTeX parser plugins. diff --git a/docs/features/snippets.md b/docs/features/snippets.md index d86ffe0..8e90733 100644 --- a/docs/features/snippets.md +++ b/docs/features/snippets.md @@ -1,51 +1,36 @@ -# Command Snippets +# Editor Command Snippets -Use command palette snippets for fast note metadata and text transformations. +TeXcore features quick helper shortcuts to automate note metadata setup and perform text capitalization transforms inside the editor. These commands are registered globally and can be triggered via the command palette using ++ctrl+p++ (or ++cmd+p++ on macOS). -## Available Commands +--- -### Add Tags +## Frontmatter Metadata Injection -Adds this frontmatter block at the top of the active note: +The **Add Tags** command injects a standardized frontmatter YAML block at the very top of your active note, facilitating quick categorization and alias indexing: ```yaml --- tags: - - + - aliases: - - + - --- ``` -How to use: +!!! note "Duplication Prevention" + If the plugin detects that your note already starts with a valid YAML frontmatter block (delimiters `---`), the command terminates safely and does not write duplicates. -1. Open Command Palette (`Ctrl/Cmd + P`) -2. Search for "Add Tags" -3. Run the command +--- -Behavior: +## Editor Text Transformations -- Inserts the frontmatter block at the top of the file -- If frontmatter already exists at the top of the note, no change is made +The **Run Text Transform Snippet** command processes your highlighted editor text (or the active cursor line if no text is selected) through predefined formatting filters. You can use this with multi-cursor selections. -### Run Text Transform Snippet +| Transformation | Output Example | Typical Use Case | +| :--- | :--- | :--- | +| **Kebab Case** | `example-text-string` | Fast URL or note alias linking. | +| **Title Kebab Case** | `Example-Text-String` | Title heading slugifications. | +| **Title Case** | `Example Text String` | Formatting article or note headings. | +| **Clean Zotero Highlight** | Reformats HTML tags to raw markdown | Converting Zotero desktop highlights into blockquotes. | -Applies a built-in transformation to the selected text, or to the current line if no text is selected. - -How to use: - -1. Open Command Palette (`Ctrl/Cmd + P`) -2. Search for "Run Text Transform Snippet" -3. Choose a transform from the list - -Built-in transforms: - -- Kebab Case (`my-selected-text`) -- Title Kebab Case (`My-Selected-Text`) -- Title Case (`My Selected Text`) -- Clean Zotero Highlight Line (`...` to quote + source format) - -## Notes - -- Multi-cursor selections are supported for text transforms -- Transform commands show a notice indicating whether changes were applied +When any transformation runs, TeXcore displays a notice toast in the top-right corner indicating whether the change was successfully applied. diff --git a/docs/features/tikz.md b/docs/features/tikz.md index 86e63dd..529b812 100644 --- a/docs/features/tikz.md +++ b/docs/features/tikz.md @@ -1,83 +1,72 @@ -# TikZ Diagrams +# TikZ Diagrams & Graphical Editor -Render LaTeX TikZ diagrams (` ```tikz `) fully offline and on-demand inside your notes. - -## Overview - -Unlike other plugins that require an internet connection to fetch heavy dependencies or freeze your UI during TeX compilation, this plugin leverages a decoupled, worker-based compiler. - -- **Fully Offline** โ€” Requires no internet connection. All TeX format files and packages are cached locally in the plugin folder. -- **Lazy Loaded Assets** โ€” Core components (`tex.wasm.gz` and `core.dump.gz`) are read from disk, decompressed, and cached in memory on the first compile. Supplementary packages and libraries (like `circuitikz`, `pgfplots`, or `tikz-cd`) are only loaded and decompressed from disk when requested in your TikZ block's preamble. -- **Web Worker Concurrency** โ€” TeX compilation is CPU-bound. Offloading it to a background Web Worker ensures your Obsidian interface remains smooth and responsive. -- **Theme Integration** โ€” Hardcoded dark strokes and fills adapt automatically to Obsidian's active dark or light themes if dark-mode color inversion is enabled. +TeXcore integrates an offline, background-compiled TikZ vector graphics renderer and a fully interactive **TikZ Graphical Editor**. You can write standard LaTeX TikZ scripts directly inside code blocks or edit them visually using a drag-and-drop CAD-like interface. --- -## How It Works +## Architecture & Worker Compilation -1. The Markdown code block processor detects a ` ```tikz ` block. -2. The plugin parses the code preamble for package requirements (e.g. `\usepackage{pgfplots}` or `\usetikzlibrary{calc}`). -3. The main thread loads the WASM engine, core dump, and resolved package files, decompresses them natively using Node's `zlib` library, and transfers the buffers to the Web Worker. -4. The background worker spins up a virtual filesystem in memory, loads the TeX format dump, and compiles the TikZ block to a binary DVI format. -5. The main thread receives the DVI binary, translates it to SVG using `dvi2html` and the secure browser `DOMParser` API, and inserts it into the note. +To prevent Obsidian UI freezes during heavy TeX compilations, TeXcore utilizes a decoupled **Web Worker Compiler** that operates fully offline: ```mermaid sequenceDiagram - participant Main as Obsidian Main Thread + participant Main as Obsidian Editor participant Worker as TikZJax Web Worker + participant Disk as Local Storage (WASM) - Note over Main: Markdown block processor triggered - Main->>Main: Parse preamble (detect packages & libraries) - Main->>Main: Read & decompress required .gz assets from local disk - Main->>Worker: Spawn worker & postMessage(startCompile, { code, files }) - Note over Worker: Populate virtual filesystem in memory - Worker->>Worker: Run TeX WebAssembly engine (Sync) - Worker->>Worker: Output DVI format - Worker->>Main: postMessage(compileSuccess, { dviData }) - Main->>Main: Translate DVI to SVG using dvi2html & DOMParser - Main->>Main: Render SVG inside Note View + Main->>Disk: Read & decompress core assets (tex.wasm.gz, core.dump.gz) + Main->>Worker: Spawn thread & send decompressed WebAssembly buffers + Note over Worker: Spin up virtual memory filesystem + Main->>Worker: Post compile command (TikZ code) + Worker->>Worker: Sync compile to DVI format + Worker-->>Main: Return DVI binary output + Main->>Main: Translate DVI to SVG using dvi2html & insert into note ``` --- -## Usage +## Editor Workflows -Create a code block in your markdown document and identify it with `tikz`. You can include packages in the preamble: +=== "Using Code Blocks" + Create a code block in your markdown document and identify it with `tikz`. You can include packages like `pgfplots` or libraries in the preamble: + + ```tikz + \usepackage{pgfplots} + \begin{document} + \begin{tikzpicture} + \begin{axis}[xlabel=$x$, ylabel=$y$] + \addplot[color=red,mark=x] coordinates { + (2,-3) (3,5) (4,7) + }; + \end{axis} + \end{tikzpicture} + \end{document} + ``` -```tikz -\usepackage{pgfplots} -\begin{document} -\begin{tikzpicture} - \begin{axis}[ - xlabel=$x$, - ylabel=$y$ - ] - \addplot[color=red,mark=x] coordinates { - (2,-3) - (3,5) - (4,7) - }; - \end{axis} -\end{tikzpicture} -\end{document} -``` +=== "Using the Graphical Editor" + 1. Click the **Edit visually** button on any rendered TikZ SVG block, or use the Command Palette (++ctrl+p++ โ†’ `Insert display math` or search for the TikZ Editor commands). + 2. A CAD-style modal opens: + - **Left Sidebar**: Component library featuring core shapes (lines, circles, rectangles) and package modules. + - **Center Canvas**: Interactive grid with zoom (++ctrl+scroll++), panning (++spacebar+drag++), and snapping (toggle via ++g++ or ++h++). + - **Right Sidebar**: Property editor (adjust color, bold, italics, font size, stroke thickness) and raw TikZ Code synchronization. + 3. Press ++ctrl+z++ to undo and ++ctrl+y++ to redo actions. + 4. Click the **Update block** button to compile and insert the code block into your note. --- -## Asset Origins & Lineage +## Component Package Manager -The lazy-loaded assets stored inside the `tikzjax-assets` folder are compiled/extracted from: +For complex diagrams (like circuits or flowcharts), the Graphical Editor features a **Component Package Manager** accessed via the footer button in the Left Sidebar. -1. **[kisonecat/tikzjax](https://github.com/kisonecat/tikzjax)**: The original browser-based TeX compiler created by Jim Fowler, which compiles TeX's Pascal source to WebAssembly via `web2js`. -2. **[drgrice1/tikzjax](https://github.com/drgrice1/tikzjax)**: Glenn Rice's fork which introduced Web Worker support and support for additional LaTeX packages and libraries. -3. **[artisticat1/obsidian-tikzjax](https://github.com/artisticat1/obsidian-tikzjax)**: The Obsidian plugin wrapper created by `artisticat1` which packaged these components for offline usage in Obsidian. +!!! info "Package Installation Flow" + Click **Manage Packages** and search for libraries like `circuitikz`. Installing a package decompresses and caches its TeX dependency files (`.tex`/`.sty`) in memory, dynamically expanding your visual element sidebar with custom shapes (e.g. resistors, logic gates, operational amplifiers) that can be drag-dropped onto the grid canvas. --- -## Troubleshooting +## Diagnostics & Troubleshooting -| Issue | Cause | Solution | -|-------|-------|----------| -| **Diagram shows empty container** | Core assets are missing or disabled | Ensure the `tikzjax-assets` folder is present inside the plugin directory under `.obsidian/plugins/TeXcore/tikzjax-assets/`. | -| **Error: Package not found** | The requested LaTeX package is not supported or not present | Only packages and libraries pre-compiled into the assets are supported. Ensure the package name matches standard packages (like `pgfplots`, `circuitikz`, `chemfig`, `tikz-cd`). | -| **Compile hangs or freezes** | Complex intersections or recursive computations exceed WebAssembly boundaries | Break down complex calculations or verify diagram logic for syntax loops. | +!!! warning "Common TikZ Compilation Hurdles" + - **Empty Container / Failed rendering**: Verify that the `tikzjax-assets` folder exists inside your plugin installation directory: `.obsidian/plugins/TeXcore/tikzjax-assets/`. + - **Package Not Found Error**: Double-check spelling inside your block preamble. Only packages included in the asset registry are available offline. + - **Canvas Panning Locked**: Ensure you hold down the ++spacebar++ while dragging on the canvas container to pan the grid layout. + - **Compilation Timed Out**: Avoid infinite loops or extremely dense coordinate calculations that exceed WebAssembly heap size allocations. diff --git a/docs/getting-started.md b/docs/getting-started.md index a794a31..4e99cc0 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,86 +1,79 @@ -# Getting Started +# Getting Started with TeXcore + +Follow this guide to install, configure, and draft your first indexed equations in Obsidian. + +--- ## Installation -### From Community Plugins +You can install TeXcore either directly from the official Obsidian Community Plugins store or manually by compiling the releases. -1. Open Obsidian Settings โ†’ **Community plugins** -2. Click **Browse** and search for "TeXcore" -3. Click **Install**, then **Enable** +=== "Community Store (Recommended)" + 1. Navigate to ++ctrl+comma++ (or ++cmd+comma++ on macOS) to open **Obsidian Settings** and select **Community plugins**. + 2. Click **Browse**, search for `TeXcore`, and click **Install**. + 3. Once installed, click **Enable** to activate the plugin. -### Manual Installation +=== "Manual Installation" + 1. Download the latest `main.js`, `manifest.json`, and `styles.css` from the [Latest Releases](https://github.com/YouFoundJK/TeXcore/releases). + 2. Open your Obsidian Vault directory and navigate to the hidden directory: `.obsidian/plugins/`. + 3. Create a folder named `TeXcore` and paste the three downloaded files into it. + 4. Go to **Settings** โ†’ **Community plugins** and toggle TeXcore to **Enable**. -1. Download `main.js`, `manifest.json`, and `styles.css` from the [latest release](https://github.com/YouFoundJK/TeXcore/releases) -2. Create folder: `/.obsidian/plugins/TeXcore/` -3. Copy the downloaded files into this folder -4. Restart Obsidian and enable the plugin +--- -## Your First Equation +## Step-by-Step Setup -### Step 1: Create a Display Math Block +Let's write, trigger, and link your first math reference. -Write a display math block using `$$` delimiters: +```mermaid +graph TD + A[Create display math $$ block] --> B[Add % id: eq-name inside] + B --> C[Type \eqref autocomplete trigger] + C --> D[Select reference from popup] + D --> E[Link resolved with tag number] +``` +### 1. Write the Math Block +Open any note in your vault and create a standard display math block: ```latex $$ \int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2} $$ ``` -### Step 2: Add an Equation ID - -Add a LaTeX comment with a unique ID on a new line before the closing `$$`: - +### 2. Add the Identifier +To index the equation, insert a LaTeX comment (`% id: `) on the line immediately preceding the closing `$$` delimiter: ```latex $$ \int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2} % id: eq-gaussian $$ ``` +!!! tip "ID Format Constraints" + Your identifier must start with `eq-` followed by letters, numbers, or hyphens. Examples: `eq-gaussian`, `eq-step-1`, or `eq-ref-3`. -!!! tip "ID Format" - IDs must start with `eq-` followed by alphanumeric characters and hyphens. Examples: `eq-main`, `eq-theorem-1`, `eq-proof-step-2` - -### Step 3: Reference the Equation - -Type `\eqref` to trigger the autocomplete menu, then select your equation: +### 3. Insert the Reference +Type `\eqref` (the default trigger) in your text. An autocomplete dropdown listing all active note equations will pop up. Select `eq-gaussian` and press ++enter++ to insert `[[#^eq-gaussian]]`. ```markdown -The Gaussian integral [[#^eq-gaussian]] is fundamental in probability theory. +The integral [[#^eq-gaussian]] resolves to a constant. ``` -### Step 4: View the Result +### 4. Observe the Rendered Result +Obsidian will now display a right-aligned number tag `(1)` next to the equation, and the inline link will render as clickable text displaying `(1)`. Hovering over it displays a rich popup preview of the math block. -The plugin automatically: +--- -- Adds `\tag{1}` to number the equation -- Renders the reference as a clickable link showing `(1)` -- Shows a preview popup when you hover over the link +## Interactive Command Palette Actions -## Basic Workflow +Press ++ctrl+p++ (or ++cmd+p++) to trigger the command palette. TeXcore exposes several key commands: -```mermaid -graph LR - A[Write equation] --> B[Add % id: eq-xxx] - B --> C[Type \eqref] - C --> D[Select from suggestions] - D --> E[Link inserted] -``` +| Command | Action Description | Core Feature Link | +| :--- | :--- | :--- | +| `Insert display math` | Inserts a template display math block containing the `% id:` line. | [Equations](features/equations.md) | +| `Search equations in active note` | Opens the search modal to browse all equation cache references. | [Search](features/search.md) | +| `Fix callout equations in active note` | Fixes missing or misaligned callout markers on math blocks. | [Callouts](features/callout-support.md) | +| `Export current file to PDF` | Opens the PDF compilation window with preview and print settings. | [PDF Export](features/pdf-export.md) | +| `Insert LaTeX Snippet` | Displays saved snippets for fast editor insertion. | [Snippets](features/snippets.md) | -## Commands - -Access these commands via the Command Palette (`Ctrl/Cmd + P`): - -| Command | Description | -|---------|-------------| -| **Insert display math** | Insert `$$...$$` block with ID template | -| **Search equations in active note** | Open equation search modal | -| **Fix callout equations in active note** | Fix indentation in callout math blocks | -| **Export current file to PDF** | Open PDF export dialog | -| **Insert LaTeX Snippet** | Insert a saved LaTeX snippet | - -## Next Steps - -- [Equation Numbering](features/equations.md) - Learn about numbering styles and sub-equations -- [PDF Export](features/pdf-export.md) - Export notes to PDF with equations -- [Settings Reference](configuration/settings.md) - Configure all plugin options +For further customization, consult our [Settings Reference](configuration/settings.md) or explore the [Features Matrix Overview](features/index.md) to see what is possible. diff --git a/docs/index.md b/docs/index.md index e44aa5d..180b58f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,75 +1,79 @@ -# TeXcore +# TeXcore: LaTeX-Grade Referencing in Obsidian -A minimalistic Obsidian plugin for automatic equation numbering, referencing with support for PDE exports and . +Welcome to **TeXcore** (:material-sigma:), the ultimate Obsidian plugin designed to bridge the gap between markdown notes and publication-ready LaTeX documents. By introducing automated equation numbering, smart referencing, and advanced styling modules, TeXcore transforms your vault into a rich ecosystem for scientific drafting and study. +[Getting Started Guide :octicons-arrow-right-24:](getting-started.md){: .md-button .md-button--primary } +[Settings Reference :octicons-gear-24:](configuration/settings.md){: .md-button } +--- -## Key Features +## Capabilities Overview + +Our core features are organized into logical modules designed to enhance your equation workflows:
-- :material-numeric: **Automatic Numbering** +- :material-numeric: **[Equation Numbering](features/equations.md)** - --- + LaTeX-like automatic tag insertion with multi-style options (`arabic`, `roman`, `alph`) and multi-line sub-equation tracking. - Equations are automatically numbered using LaTeX `\tag{}` commands with 5 numbering styles. +- :material-link-variant: **[Smart Referencing](features/quick-preview.md)** -- :material-link-variant: **Smart Referencing** + Autocompleted backlinks utilizing `[[#^eq-id]]` linking. Supports hover popups, click-navigation, and live synchronizations. - --- +- :material-file-pdf-box: **[PDF Export](features/pdf-export.md)** - Reference equations using `[[#^eq-id]]` links with live preview and hover popups. + Interactive PDF compiling with side-by-side preview widgets, customizable page headers/footers, margins, and custom CSS snippet injection. -- :material-file-pdf-box: **PDF Export** +- :material-code-braces: **[LaTeX Snippets](features/snippets.md)** - --- + Quick-insertion presets mapped to keyboard triggers, allowing faster math notation drafting without repetitive typing. - Full-featured PDF export with live preview, customizable margins, headers, and CSS support. +- :material-magnify: **[Equation Search](features/search.md)** -- :material-code-braces: **LaTeX Snippets** + Note-wide lookup indexing that simplifies equation retrieval and reference updates through an intuitive, keyboard-first modal. - --- +- :material-card-text-outline: **[Callout Support](features/callout-support.md)** - Create custom LaTeX snippets accessible via command palette. + Seamless integration with native callouts, preserving Markdown formatting rules while automatically indexing equations. -- :material-magnify: **Equation Search** +- :material-vector-square: **[TikZ Diagrams](features/tikz.md)** - --- - - Search and insert equation references using fuzzy or simple search. - -- :material-card-text-outline: **Callout Support** - - --- - - Math blocks work inside Obsidian callouts with automatic indentation handling. + Real-time rendering of vector graphical TikZ scripts directly inside your notes, backed by an isolated component library.
-## Quick Example +--- -Write your equation with an ID comment: +## Quick Start Demonstration -```latex -$$ -E = mc^2 -% id: eq-einstein -$$ -``` +Writing and referencing equations in TeXcore is extremely straightforward. -Reference it anywhere in the same note: +=== "1. Writing the Equation" + To set up a trackable equation, use standard display math delimiters `$$` and insert a LaTeX comment stating the identifier: + + ```latex + $$ + E = mc^2 + % id: eq-einstein + $$ + ``` -```markdown -As shown in [[#^eq-einstein]], energy and mass are equivalent. -``` +=== "2. Referencing the Equation" + Simply type your trigger (default: `\eqref`) to select the equation via autocompletion, inserting an Obsidian reference link: + + ```markdown + As demonstrated by Einstein in [[#^eq-einstein]], mass and energy are equivalent. + ``` -The equation will be automatically numbered as `(1)` and the link will display as a clickable reference. +=== "3. Rendered Output" + The equation will be automatically indexed, adding a right-aligned numbering tag. The reference link resolves to a neat hyperlink: + + $$ + E = mc^2 \tag{1} + $$ + + As demonstrated by Einstein in [(1)](#), mass and energy are equivalent. -## Getting Started - -[:octicons-arrow-right-24: Installation Guide](getting-started.md) - -## Special Thanks to -- [LaTeX-like Theorem & Equation Referencer](https://ryotaushio.github.io/obsidian-latex-theorem-equation-referencer/) by Ryota Ushio on which this is inspired. -- PDF export motivated by [better-export-pdf](https://github.com/l1xnan/obsidian-better-export-pdf) -- [obsidian-tikzjax](https://github.com/artisticat1/obsidian-tikzjax) by [artisticat1](https://github.com/artisticat1) \ No newline at end of file +!!! note "Smart Numbering" + By default, TeXcore employs **lazy numbering**. Equations are only assigned a tag when they are actively referenced elsewhere in your note. This ensures clean note margins and prevents cluttered layouts. Read more in the [Settings Reference](configuration/settings.md#number-only-referenced-equations). \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs index 7526b4e..a550884 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -112,6 +112,7 @@ export default tseslint.config( // Type assertions '@typescript-eslint/no-unnecessary-type-assertion': 'warn', + '@typescript-eslint/no-unsafe-argument': 'warn', '@typescript-eslint/prefer-as-const': 'warn', '@typescript-eslint/no-unsafe-return': 'warn', diff --git a/manifest-beta.json b/manifest-beta.json index 25f8fe4..234d5de 100644 --- a/manifest-beta.json +++ b/manifest-beta.json @@ -3,7 +3,7 @@ "name": "TeXcore", "version": "0.0.3", "minAppVersion": "1.3.5", - "description": "Automatic equation numbering, Tex Diagrams rendering, and more.", + "description": "Automatic equation numbering, Tex Diagrams rendering, and more to transforms your vault into a rich ecosystem for scientific drafting and study.", "author": "Jovi Koikkara", "authorUrl": "https://github.com/YouFoundJK", "fundingUrl": "https://github.com/YouFoundJK", diff --git a/manifest.json b/manifest.json index 4d15b62..93f5385 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "name": "TeXcore", "version": "0.0.2", "minAppVersion": "1.3.5", - "description": "Automatic equation numbering, Tex Diagrams rendering, and more.", + "description": "Automatic equation numbering, Tex Diagrams rendering, and more to transforms your vault into a rich ecosystem for scientific drafting and study.", "author": "Jovi Koikkara", "authorUrl": "https://github.com/YouFoundJK", "fundingUrl": "https://github.com/YouFoundJK", diff --git a/mkdocs.yml b/mkdocs.yml index bba8e3d..0f93b78 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -112,6 +112,7 @@ nav: - Home: index.md - Getting Started: getting-started.md - Features: + - Overview: features/index.md - Equation Numbering: features/equations.md - PDF Export: features/pdf-export.md - LaTeX Snippets: features/snippets.md @@ -120,4 +121,5 @@ nav: - TikZ Diagrams: features/tikz.md - Callout Support: features/callout-support.md - Configuration: + - Overview: configuration/index.md - Settings Reference: configuration/settings.md diff --git a/package.json b/package.json index 27909a9..fdde892 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "TeXcore", "version": "0.0.1", - "description": "Automatic equation numbering, Tex Diagrams rendering, and more.", + "description": "Automatic equation numbering, Tex Diagrams rendering, and more to transforms your vault into a rich ecosystem for scientific drafting and study.", "main": "main.js", "packageManager": "pnpm@11.1.2+sha512.415a1cc25974731e75455c1468371be74c5aa5fb7621b50d4056d222451609f11412f23fd602e6169f1e060466641f798597e1be961a10688836a67b16569499", "scripts": { diff --git a/scratch/test_compile.ts b/scratch/test_compile.ts deleted file mode 100644 index 47772a5..0000000 --- a/scratch/test_compile.ts +++ /dev/null @@ -1,215 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import zlib from 'zlib'; -import { dvi2html } from 'dvi2html'; -import { Writable } from 'stream'; - -// 1. Mock the Worker self environment -let messageHandler: ((e: any) => Promise) | null = null; -const globalSelf = { - addEventListener(type: string, handler: any) { - if (type === 'message') { - messageHandler = handler; - } - }, - postMessage(data: any) { - if (data.type === 'success') { - console.log('SUCCESS: DVI compiled, size =', data.dvi.length); - void convertDviToSvg(data.dvi); - } else if (data.type === 'error') { - console.error('COMPILE ERROR:', data.error); - } - } -}; -(global as any).self = globalSelf; - -// 2. Load the worker module -require('../src/features/tikz/tikzjax/tikzjax.worker'); - -// 3. Helper to decompress assets -const assetsDir = './tikzjax-assets'; -function loadDecompressed(filename: string): Uint8Array { - const content = fs.readFileSync(path.join(assetsDir, filename)); - const decompressed = zlib.gunzipSync(content); - return new Uint8Array(decompressed); -} - -// 4. Run compilation -async function run() { - const wasm = loadDecompressed('tex.wasm.gz'); - const core = loadDecompressed('core.dump.gz'); - - const files: Record = { - 'tex.wasm': wasm, - 'core.dump': core - }; - - const code = `\\begin{tikzpicture}[scale=1.8, transform shape, thick] - -\\node at (-0.2,0.5) {$\\Gamma \\Delta \\Theta \\Lambda \\Xi \\Pi \\Sigma \\Upsilon \\Phi \\Psi \\Omega$}; - -\\fill (0.5,1) circle (2pt); -\\fill (1,0) circle (2pt); -\\draw (0,0) circle (2pt); - -\\draw (0.5,1)--(1,0); -\\draw (0.5,1)--(0,0); -\\draw (0,0)--(1,0); - -\\end{tikzpicture}`; - - console.log('Starting compile...'); - if (messageHandler) { - await messageHandler({ - data: { - type: 'compile', - code, - files - } - }); - } else { - console.error('No message handler registered!'); - } -} - -import { dviParser, mergeText, specials, Machines } from 'dvi2html'; - -// 5. Convert compiled DVI to SVG using dvi2html -async function convertDviToSvg(dvi: Uint8Array) { - let html = ''; - const page = new Writable({ - write(chunk, _, callback) { - html += String(chunk); - callback(); - } - }); - - async function* streamBuffer() { - yield Buffer.from(dvi); - return; - } - - const parser = specials.papersize( - specials.svg(specials.color(mergeText(dviParser(streamBuffer())))) - ); - - const machine = new Machines.HTML(page); - let currentPageIndex = -1; - const pageSizes: { width: number; height: number }[] = []; - - // Override putSVG to correctly track svgDepth and handle - (machine as any).putSVG = function (svgStr: string) { - const self = this as any; - const left = self.position.h * self.pointsPerDviUnit; - const top = self.position.v * self.pointsPerDviUnit; - - self.svgDepth = self.svgDepth || 0; - self.svgDepth += (svgStr.match(//g) || []).length; - - let replacedSvg = svgStr.replace( - '', - '' - ); - replacedSvg = replacedSvg.replace( - '', - '' - ); - - replacedSvg = replacedSvg.replace(/{\?x}/g, left.toString()); - replacedSvg = replacedSvg.replace(/{\?y}/g, top.toString()); - self.output.write(replacedSvg); - }; - - (machine as any).putText = function (text: number[]) { - const self = this as any; - let textWidth = 0; - let textHeight = 0; - let textDepth = 0; - let htmlText = ''; - for (let i = 0; i < text.length; i++) { - const c = text[i]; - const metrics = self.font.metrics.characters[c]; - if (metrics === undefined) { - throw Error(`Could not find font metric for ${c}`); - } - textWidth += metrics.width; - textHeight = Math.max(textHeight, metrics.height); - textDepth = Math.max(textDepth, metrics.depth); - if (c < 32) { - htmlText += `&#${127 + c + 32 + 2};`; // Shift by 2 instead of 4 - } else { - htmlText += String.fromCharCode(c); - } - } - const dviUnitsPerFontUnit = - (self.font.metrics.designSize / 1048576.0) * (65536 / 1048576); - const left = self.position.h * self.pointsPerDviUnit; - const height = textHeight * self.pointsPerDviUnit * dviUnitsPerFontUnit; - const top = self.position.v * self.pointsPerDviUnit; - const fontsize = - (self.font.metrics.designSize / 1048576.0) * - self.font.scaleFactor / - self.font.designSize; - - if (self.svgDepth === 0) { - self.output.write( - `${htmlText}\n` - ); - } else { - const bottom = self.position.v * self.pointsPerDviUnit; - self.output.write( - `${htmlText}\n` - ); - } - return ( - (textWidth * dviUnitsPerFontUnit * self.font.scaleFactor) / - self.font.designSize - ); - }; - - for await (const command of parser) { - if (command.opcode === 139) { - // Bop - if (currentPageIndex >= 0) { - page.write(''); - } - currentPageIndex++; - pageSizes[currentPageIndex] = { width: 0, height: 0 }; - page.write(`
`); - } else if ('width' in command && 'height' in command) { - if (currentPageIndex >= 0) { - pageSizes[currentPageIndex] = { - width: (command as any).width, - height: (command as any).height - }; - } - } - command.execute(machine); - } - - if (currentPageIndex >= 0) { - page.write('
'); - } - - console.log('Custom parser finished.'); - console.log('Page sizes:', pageSizes); - - console.log('--- HTML OUTPUT ---'); - console.log(html.substring(0, 1500)); - console.log('--- END HTML OUTPUT ---'); - - // Verify by regex matching the structured divs - const pageRegex = /
([\s\S]*?)<\/div>/g; - let match; - while ((match = pageRegex.exec(html)) !== null) { - const pageNum = match[1]; - const pageContent = match[2]; - const hasSvg = pageContent.includes(' { - console.error('Run failed:', err); -}); diff --git a/scratch/test_user_compile.ts b/scratch/test_user_compile.ts deleted file mode 100644 index 5125fd7..0000000 --- a/scratch/test_user_compile.ts +++ /dev/null @@ -1,256 +0,0 @@ -import fs from 'fs'; -import path from 'path'; -import zlib from 'zlib'; -import { dviParser, mergeText, specials } from 'dvi2html'; -import HTMLMachine from 'dvi2html/lib/html'; -import { Writable } from 'stream'; - -// 1. Mock the Worker self environment -let messageHandler: ((e: any) => Promise) | null = null; -const globalSelf = { - addEventListener(type: string, handler: any) { - if (type === 'message') { - messageHandler = handler; - } - }, - postMessage(data: any) { - if (data.type === 'success') { - console.log('SUCCESS: DVI compiled, size =', data.dvi.length); - void convertDviToSvg(data.dvi); - } else if (data.type === 'error') { - console.error('COMPILE ERROR:', data.error); - } - } -}; -(global as any).self = globalSelf; - -// 2. Load the worker module -require('../src/features/tikz/tikzjax/tikzjax.worker'); - -// 3. Helper to decompress assets -const assetsDir = './tikzjax-assets'; -function loadDecompressed(filename: string): Uint8Array { - const content = fs.readFileSync(path.join(assetsDir, filename)); - const decompressed = zlib.gunzipSync(content); - return new Uint8Array(decompressed); -} - -// 4. Run compilation -async function run() { - const wasm = loadDecompressed('tex.wasm.gz'); - const core = loadDecompressed('core.dump.gz'); - - const files: Record = { - 'tex.wasm': wasm, - 'core.dump': core - }; - - const code = `\\begin{tikzpicture}[scale=1.5, transform shape, thick, shift={(0.0,-0.5)}] - -% Gamma -\\node at (0,0.5) {$\\Gamma =$}; - -\\begin{scope}[shift={(0.5,0)}] -\\fill (0.5,1) circle (2pt); -\\fill (0,0) circle (2pt); -\\fill (1,0) circle (2pt); - -\\draw (0.5,1)--(0,0); -\\draw (0.5,1)--(1,0); -\\draw (0,0)--(1,0); -\\end{scope} - -% arrow -\\draw[->, line width=1.5pt] (2.0,0.5)--(2.5,0.5); - -% derivative -\\node[scale=1.2] at (4.0,0.5) {$\\frac{\\delta \\Gamma}{\\delta \\eta(r,r')} = \\frac{1}{2}$}; - -% result graph -\\begin{scope}[shift={(5.3,0)}] -\\fill (0.5,1) circle (2pt); -\\draw (0,0) circle (2pt); -\\draw (1,0) circle (2pt); - -\\draw (0.5,1)--(0,0); -\\draw (0.5,1)--(1,0); - -\\node at (0,-0.35) {$\\mathbf r$}; -\\node at (1,-0.35) {$\\mathbf r'$}; -\\end{scope} - -\\end{tikzpicture}`; - - console.log('Starting compile...'); - if (messageHandler) { - await messageHandler({ - data: { - type: 'compile', - code, - files - } - }); - } else { - console.error('No message handler registered!'); - } -} - -interface FontMetricCharacter { - width: number; - height: number; - depth: number; -} - -interface DviParsedCommand { - opcode?: number; - width?: number; - height?: number; - execute(machine: HTMLMachine): void; -} - -class CustomHTMLMachine extends HTMLMachine { - override putSVG(svgStr: string): void { - const left = this.position.h * this.pointsPerDviUnit; - const top = this.position.v * this.pointsPerDviUnit; - - this.svgDepth = this.svgDepth || 0; - this.svgDepth += (svgStr.match(//g) || []).length; - - let replacedSvg = svgStr.replace( - '', - `` - ); - replacedSvg = replacedSvg.replace( - '', - `` - ); - - replacedSvg = replacedSvg.replace(/{\?x}/g, left.toString()); - replacedSvg = replacedSvg.replace(/{\?y}/g, top.toString()); - this.output.write(replacedSvg); - } - - override putRule(rule: any): void { - const a = rule.a * this.pointsPerDviUnit; - const b = rule.b * this.pointsPerDviUnit; - const left = this.position.h * this.pointsPerDviUnit; - const bottom = this.position.v * this.pointsPerDviUnit; - const top = bottom - a; - - if (this.svgDepth === 0) { - this.output.write( - `\n` - ); - } else { - this.output.write( - `\n` - ); - } - } - - override putText(text: number[] | Buffer): number { - let textWidth = 0; - let textHeight = 0; - let textDepth = 0; - let htmlText = ''; - const chars = this.font.metrics.characters as Record; - - console.log(`[DEBUG] putText: Font = ${this.font.name}, text bytes =`, Array.from(text)); - - for (let i = 0; i < text.length; i++) { - const c = text[i]; - const metrics = chars[c]; - if (metrics === undefined) { - throw new Error(`Could not find font metric for ${c}`); - } - textWidth += metrics.width; - textHeight = Math.max(textHeight, metrics.height); - textDepth = Math.max(textDepth, metrics.depth); - if (c < 32) { - const shifted = Math.floor(c / 10) * 12 + (c % 10) + 161; - console.log(` char = ${c} (special) -> shifted = ${shifted}`); - htmlText += `&#${shifted};`; - } else { - console.log(` char = ${c} -> '${String.fromCharCode(c)}'`); - htmlText += String.fromCharCode(c); - } - } - - const dviUnitsPerFontUnit = (this.font.metrics.designSize / 1048576.0) * (65536 / 1048576); - const left = this.position.h * this.pointsPerDviUnit; - const height = textHeight * this.pointsPerDviUnit * dviUnitsPerFontUnit; - const top = this.position.v * this.pointsPerDviUnit; - const fontsize = - ((this.font.metrics.designSize / 1048576.0) * this.font.scaleFactor) / this.font.designSize; - - if (this.svgDepth === 0) { - this.output.write( - `${htmlText}\n` - ); - } else { - const bottom = this.position.v * this.pointsPerDviUnit; - this.output.write( - `${htmlText}\n` - ); - } - return (textWidth * dviUnitsPerFontUnit * this.font.scaleFactor) / this.font.designSize; - } -} - -// 5. Convert compiled DVI to SVG -async function convertDviToSvg(dvi: Uint8Array) { - let html = ''; - const page = new Writable({ - write(chunk, _, callback) { - html += String(chunk); - callback(); - } - }); - - async function* streamBuffer() { - yield Buffer.from(dvi); - return; - } - - const parser = specials.papersize( - specials.svg(specials.color(mergeText(dviParser(streamBuffer())))) - ); - - const machine = new CustomHTMLMachine(page); - let currentPageIndex = -1; - const pageSizes: { width: number; height: number }[] = []; - - for await (const rawCommand of parser) { - const command = rawCommand as DviParsedCommand; - if (command.opcode === 139) { - // Bop - if (currentPageIndex >= 0) { - page.write('
'); - } - currentPageIndex++; - pageSizes[currentPageIndex] = { width: 0, height: 0 }; - page.write(`
`); - } else if (command.width !== undefined && command.height !== undefined) { - if (currentPageIndex >= 0) { - pageSizes[currentPageIndex] = { - width: command.width, - height: command.height - }; - } - } - command.execute(machine); - } - - if (currentPageIndex >= 0) { - page.write('
'); - } - - console.log('--- HTML OUTPUT ---'); - console.log(html); - console.log('--- END HTML OUTPUT ---'); -} - -run().catch(err => { - console.error('Run failed:', err); -}); diff --git a/src/features/export-pdf/Progress.ts b/src/features/export-pdf/Progress.ts index 5cef9bb..fcd2d98 100644 --- a/src/features/export-pdf/Progress.ts +++ b/src/features/export-pdf/Progress.ts @@ -27,9 +27,9 @@ export class Progress { const iconSpan = itemDiv.createSpan({ cls: 'progress-icon' }); // Lucide-like loader SVG (with some basic style to rotate if desired) - // eslint-disable-next-line @microsoft/sdl/no-inner-html - iconSpan.innerHTML = ` - + const parser = new DOMParser(); + const doc = parser.parseFromString( + ` @@ -38,8 +38,11 @@ export class Progress { - - `; + `, + 'image/svg+xml' + ); + const svg = doc.documentElement; + iconSpan.appendChild(activeDocument.importNode(svg, true)); itemDiv.createSpan({ text: param.file.name }); @@ -54,8 +57,7 @@ export class Progress { if (!activeDocument.getElementById('progress-spin-style')) { const style = activeDocument.createElement('style'); style.id = 'progress-spin-style'; - // eslint-disable-next-line @microsoft/sdl/no-inner-html - style.innerHTML = ` + style.textContent = ` @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } @@ -72,13 +74,17 @@ export class Progress { if (itemDiv) { const iconSpan = itemDiv.querySelector('.progress-icon'); if (iconSpan) { - // Lucide-like square-check-big SVG - iconSpan.innerHTML = ` - + iconSpan.textContent = ''; + const parser = new DOMParser(); + const doc = parser.parseFromString( + ` - - `; + `, + 'image/svg+xml' + ); + const svg = doc.documentElement; + iconSpan.appendChild(activeDocument.importNode(svg, true)); } } } diff --git a/src/features/tikz-editor/components/canvas-grid.ts b/src/features/tikz-editor/components/canvas-grid.ts index 9306c85..4f504f0 100644 --- a/src/features/tikz-editor/components/canvas-grid.ts +++ b/src/features/tikz-editor/components/canvas-grid.ts @@ -184,8 +184,10 @@ export class CanvasGrid { svgWrap.style.color = selectedVertices.some(v => v.elementId === elem.id) ? 'var(--text-accent)' : elem.style.color || 'var(--text-normal)'; - // eslint-disable-next-line no-unsanitized/property - svgWrap.innerHTML = elem.svgMarkup; + + const parser = new DOMParser(); + const doc = parser.parseFromString(elem.svgMarkup, 'image/svg+xml'); + svgWrap.appendChild(activeDocument.importNode(doc.documentElement, true)); innerG.appendChild(svgWrap); group.appendChild(innerG); @@ -310,8 +312,10 @@ export class CanvasGrid { const svgWrap = activeDocument.createElementNS(svgNS, 'g'); svgWrap.setCssStyles({ color: 'var(--text-accent)' }); - // eslint-disable-next-line no-unsanitized/property - svgWrap.innerHTML = activeTemplate.svgMarkup; + + const parser = new DOMParser(); + const doc = parser.parseFromString(activeTemplate.svgMarkup, 'image/svg+xml'); + svgWrap.appendChild(activeDocument.importNode(doc.documentElement, true)); innerG.appendChild(svgWrap); this.wiresOverlayEl.appendChild(innerG); } else { @@ -397,8 +401,10 @@ export class CanvasGrid { const svgRadius = elem.radius * 2.5; svg = svg.replace(/r="\d+(\.\d+)?"/g, `r="${svgRadius}"`); } - // eslint-disable-next-line no-unsanitized/property, @microsoft/sdl/no-inner-html - visual.innerHTML = svg; + + const parser = new DOMParser(); + const doc = parser.parseFromString(svg, 'image/svg+xml'); + visual.appendChild(activeDocument.importNode(doc.documentElement, true)); el.appendChild(visual); if (elem.label) { diff --git a/src/features/tikz-editor/components/left-sidebar.ts b/src/features/tikz-editor/components/left-sidebar.ts index 7ed2e5f..0f94d0f 100644 --- a/src/features/tikz-editor/components/left-sidebar.ts +++ b/src/features/tikz-editor/components/left-sidebar.ts @@ -20,8 +20,11 @@ export class LeftSidebar { iconHtml: string ) => { const btn = toolbar.createEl('button', { cls: 'tool-btn', title: label }); - // eslint-disable-next-line no-unsanitized/property, @microsoft/sdl/no-inner-html - btn.innerHTML = iconHtml; + + const parser = new DOMParser(); + const doc = parser.parseFromString(iconHtml, 'image/svg+xml'); + btn.appendChild(activeDocument.importNode(doc.documentElement, true)); + if (this.context.getActiveTool() === tool) btn.addClass('active'); btn.onclick = () => { this.context.handleSelectTool(tool); @@ -36,8 +39,8 @@ export class LeftSidebar { // Wire button const wireBtn = toolbar.createEl('button', { cls: 'tool-btn', title: 'Wire [w]' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - wireBtn.innerHTML = `
`; + + wireBtn.createDiv({ cls: 'wire-icon' }); if (this.context.getActiveTool() === 'wire') wireBtn.addClass('active'); wireBtn.onclick = () => { this.context.handleSelectTool('wire'); @@ -61,8 +64,14 @@ export class LeftSidebar { cls: 'tool-btn', title: 'Snap to grid (full) [g]' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - snapBtn.innerHTML = ``; + + const snapParser = new DOMParser(); + const snapDoc = snapParser.parseFromString( + ``, + 'image/svg+xml' + ); + snapBtn.appendChild(activeDocument.importNode(snapDoc.documentElement, true)); + if (this.context.isSnapToGrid() && !this.context.isHalfGrid()) snapBtn.addClass('active'); snapBtn.onclick = () => { this.context.setSnappingMode('grid'); @@ -84,8 +93,14 @@ export class LeftSidebar { cls: 'tool-btn', title: 'Unsnapped (free movement) [u]' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - unsnapBtn.innerHTML = ``; + + const unsnapParser = new DOMParser(); + const unsnapDoc = unsnapParser.parseFromString( + ``, + 'image/svg+xml' + ); + unsnapBtn.appendChild(activeDocument.importNode(unsnapDoc.documentElement, true)); + if (!this.context.isSnapToGrid()) unsnapBtn.addClass('active'); unsnapBtn.onclick = () => { this.context.setSnappingMode('none'); @@ -93,9 +108,14 @@ export class LeftSidebar { // 2. Search const searchBox = this.containerEl.createDiv({ cls: 'search-box' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - searchBox.createEl('span', { cls: 'search-icon' }).innerHTML = - ``; + + const searchIconSpan = searchBox.createEl('span', { cls: 'search-icon' }); + const searchParser = new DOMParser(); + const searchDoc = searchParser.parseFromString( + ``, + 'image/svg+xml' + ); + searchIconSpan.appendChild(activeDocument.importNode(searchDoc.documentElement, true)); const searchInput = searchBox.createEl('input', { type: 'text', placeholder: 'Search for Component...' @@ -115,16 +135,27 @@ export class LeftSidebar { const pkgHeader = pkgSection.createDiv({ cls: 'packages-header' }); const pkgTitle = pkgHeader.createDiv({ cls: 'title' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - pkgTitle.createSpan().innerHTML = ``; + + const pkgTitleSpan = pkgTitle.createSpan(); + const pkgParser = new DOMParser(); + const pkgDoc = pkgParser.parseFromString( + ``, + 'image/svg+xml' + ); + pkgTitleSpan.appendChild(activeDocument.importNode(pkgDoc.documentElement, true)); pkgTitle.createSpan({ text: ' Packages' }); const addPkgBtn = pkgHeader.createEl('button', { cls: 'add-pkg-btn', title: 'Manage packages' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - addPkgBtn.innerHTML = ``; + + const addPkgParser = new DOMParser(); + const addPkgDoc = addPkgParser.parseFromString( + ``, + 'image/svg+xml' + ); + addPkgBtn.appendChild(activeDocument.importNode(addPkgDoc.documentElement, true)); addPkgBtn.onclick = () => { this.context.togglePackageManager(); }; @@ -136,8 +167,8 @@ export class LeftSidebar { popHeader.createSpan({ text: 'Manage Packages' }); const closePopover = popHeader.createEl('button', { cls: 'close-btn' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - closePopover.innerHTML = '×'; + + closePopover.textContent = 'ร—'; closePopover.onclick = () => { this.context.togglePackageManager(); }; @@ -156,8 +187,13 @@ export class LeftSidebar { } else if (this.context.getInstallingPackage() === pkg.name) { const btn = actions.createEl('button', { cls: 'pkg-btn loading' }); btn.disabled = true; - // eslint-disable-next-line @microsoft/sdl/no-inner-html - btn.innerHTML = ``; + const spinSpan = btn.createSpan({ cls: 'spin' }); + const loaderParser = new DOMParser(); + const loaderDoc = loaderParser.parseFromString( + ``, + 'image/svg+xml' + ); + spinSpan.appendChild(activeDocument.importNode(loaderDoc.documentElement, true)); } else { const btn = actions.createEl('button', { cls: 'pkg-btn install', text: 'Install' }); btn.onclick = () => this.context.handleInstallPackage(pkg.name); @@ -232,8 +268,12 @@ export class LeftSidebar { cls: `pin-btn${isPinned ? ' pinned' : ''}`, title: isPinned ? 'Pinned in Basic' : 'Pin to Basic' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - pinBtn.innerHTML = ``; + const pinParser = new DOMParser(); + const pinDoc = pinParser.parseFromString( + ``, + 'image/svg+xml' + ); + pinBtn.appendChild(activeDocument.importNode(pinDoc.documentElement, true)); pinBtn.onclick = e => { e.stopPropagation(); if (isPinned) { @@ -246,8 +286,10 @@ export class LeftSidebar { } const svgContainer = card.createDiv({ cls: 'svg-container' }); - // eslint-disable-next-line no-unsanitized/property, @microsoft/sdl/no-inner-html - svgContainer.innerHTML = comp.svgMarkup; + + const compSvgParser = new DOMParser(); + const compSvgDoc = compSvgParser.parseFromString(comp.svgMarkup, 'image/svg+xml'); + svgContainer.appendChild(activeDocument.importNode(compSvgDoc.documentElement, true)); card.createEl('span', { cls: 'comp-label', text: comp.name }); card.onclick = () => { diff --git a/src/features/tikz-editor/components/right-sidebar.ts b/src/features/tikz-editor/components/right-sidebar.ts index 4a4f510..d9c9601 100644 --- a/src/features/tikz-editor/components/right-sidebar.ts +++ b/src/features/tikz-editor/components/right-sidebar.ts @@ -17,9 +17,14 @@ export class RightSidebar { cls: `tab-btn${activeTab === 'edit' ? ' active' : ''}`, text: 'Edit component' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - editTabBtn.createSpan({ cls: 'tab-icon' }).innerHTML = - ` `; + + const editSpan = editTabBtn.createSpan({ cls: 'tab-icon' }); + const editParser = new DOMParser(); + const editDoc = editParser.parseFromString( + ` `, + 'image/svg+xml' + ); + editSpan.appendChild(activeDocument.importNode(editDoc.documentElement, true)); editTabBtn.onclick = () => { this.context.switchTab('edit'); }; @@ -28,9 +33,14 @@ export class RightSidebar { cls: `tab-btn${activeTab === 'code' ? ' active' : ''}`, text: 'Code' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - codeTabBtn.createSpan({ cls: 'tab-icon' }).innerHTML = - ` `; + + const codeSpan = codeTabBtn.createSpan({ cls: 'tab-icon' }); + const codeParser = new DOMParser(); + const codeDoc = codeParser.parseFromString( + ` `, + 'image/svg+xml' + ); + codeSpan.appendChild(activeDocument.importNode(codeDoc.documentElement, true)); codeTabBtn.onclick = () => { this.context.switchTab('code'); }; @@ -45,9 +55,10 @@ export class RightSidebar { if (selectedElements.length === 1) { const selectedElement = selectedElements[0]; const editPanel = tabContent.createDiv({ cls: 'edit-panel' }); - // eslint-disable-next-line no-unsanitized/property, @microsoft/sdl/no-inner-html - editPanel.createDiv({ cls: 'section-title' }).innerHTML = - `Edit component: ${selectedElement.name}`; + + const titleDiv = editPanel.createDiv({ cls: 'section-title' }); + titleDiv.createSpan({ text: 'Edit component: ' }); + titleDiv.createSpan({ cls: 'comp-name', text: selectedElement.name }); // 1. Label text input const labelGroup = editPanel.createDiv({ cls: 'control-group' }); @@ -269,16 +280,23 @@ export class RightSidebar { // 5. Delete element const deleteWrap = editPanel.createDiv({ cls: 'delete-btn-wrap' }); const deleteBtn = deleteWrap.createEl('button', { cls: 'delete-btn' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - deleteBtn.innerHTML = ` Delete Element`; + + const deleteParser = new DOMParser(); + const deleteDoc = deleteParser.parseFromString( + ``, + 'image/svg+xml' + ); + deleteBtn.appendChild(activeDocument.importNode(deleteDoc.documentElement, true)); + deleteBtn.createSpan({ text: ' Delete Element' }); deleteBtn.onclick = () => { this.context.handleDeleteElement(selectedElement.id); }; } else if (selectedElements.length > 1) { const editPanel = tabContent.createDiv({ cls: 'edit-panel' }); - // eslint-disable-next-line no-unsanitized/property, @microsoft/sdl/no-inner-html - editPanel.createDiv({ cls: 'section-title' }).innerHTML = - `Edit multiple components (${selectedElements.length})`; + + const titleDiv = editPanel.createDiv({ cls: 'section-title' }); + titleDiv.createSpan({ text: 'Edit multiple components ' }); + titleDiv.createSpan({ cls: 'comp-name', text: `(${selectedElements.length})` }); const firstElement = selectedElements[0]; @@ -388,15 +406,22 @@ export class RightSidebar { // Delete elements const deleteWrap = editPanel.createDiv({ cls: 'delete-btn-wrap' }); const deleteBtn = deleteWrap.createEl('button', { cls: 'delete-btn' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - deleteBtn.innerHTML = ` Delete All Selected`; + + const deleteParser = new DOMParser(); + const deleteDoc = deleteParser.parseFromString( + ``, + 'image/svg+xml' + ); + deleteBtn.appendChild(activeDocument.importNode(deleteDoc.documentElement, true)); + deleteBtn.createSpan({ text: ' Delete All Selected' }); deleteBtn.onclick = () => { selectedElements.forEach(el => this.context.handleDeleteElement(el.id)); }; } else { - // eslint-disable-next-line @microsoft/sdl/no-inner-html - tabContent.createDiv({ cls: 'empty-state' }).innerHTML = - `

Select a component on the canvas to configure its styles and properties.

`; + const emptyState = tabContent.createDiv({ cls: 'empty-state' }); + emptyState.createEl('p', { + text: 'Select a component on the canvas to configure its styles and properties.' + }); } } else { // Code tab @@ -413,8 +438,14 @@ export class RightSidebar { const codeActions = codePanel.createDiv({ cls: 'code-actions' }); const copyBtn = codeActions.createEl('button', { cls: 'action-btn secondary' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - copyBtn.innerHTML = ` Copy Code`; + + const copyParser = new DOMParser(); + const copyDoc = copyParser.parseFromString( + ``, + 'image/svg+xml' + ); + copyBtn.appendChild(activeDocument.importNode(copyDoc.documentElement, true)); + copyBtn.createSpan({ text: ' Copy Code' }); copyBtn.onclick = () => { if (this.context.isCodeDirty()) { void navigator.clipboard.writeText(this.context.getEditableCode()); diff --git a/src/features/tikz-editor/tikz-editor-modal.ts b/src/features/tikz-editor/tikz-editor-modal.ts index c06ecc8..3ec5a53 100644 --- a/src/features/tikz-editor/tikz-editor-modal.ts +++ b/src/features/tikz-editor/tikz-editor-modal.ts @@ -8,7 +8,7 @@ import { type TikzPackage, type SelectedVertex } from './types'; -import { showNotice } from 'utils/obsidian'; +import { showNotice, setCssProps } from 'utils/obsidian'; import { LeftSidebar } from './components/left-sidebar'; import { CanvasGrid } from './components/canvas-grid'; import { RightSidebar } from './components/right-sidebar'; @@ -524,10 +524,11 @@ export class TikzEditorModal extends Modal implements TikzEditorContext { // Set custom CSS classes and sizes this.modalEl.addClass('tikz-editor-modal'); - // eslint-disable-next-line obsidianmd/no-static-styles-assignment - containerEl.style.setProperty('--dialog-width', '95vw'); - // eslint-disable-next-line obsidianmd/no-static-styles-assignment - containerEl.style.setProperty('--dialog-height', '90vh'); + + setCssProps(containerEl, { + '--dialog-width': '95vw', + '--dialog-height': '90vh' + }); // Title of the modal this.titleEl.setText('TikZ graphical editor'); @@ -640,8 +641,13 @@ export class TikzEditorModal extends Modal implements TikzEditorContext { const controls = canvasAreaEl.createDiv({ cls: 'tikz-canvas-controls' }); const zoomOutBtn = controls.createEl('button', { title: 'Zoom out [ctrl + -]' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - zoomOutBtn.innerHTML = ``; + + const zoomOutParser = new DOMParser(); + const zoomOutDoc = zoomOutParser.parseFromString( + ``, + 'image/svg+xml' + ); + zoomOutBtn.appendChild(activeDocument.importNode(zoomOutDoc.documentElement, true)); zoomOutBtn.onclick = () => { this.zoom = Math.max(this.zoom - 0.1, 0.5); zoomLabel.textContent = `${Math.round(this.zoom * 100)}%`; @@ -651,8 +657,13 @@ export class TikzEditorModal extends Modal implements TikzEditorContext { const zoomLabel = controls.createSpan({ cls: 'zoom-label', text: '100%' }); const zoomInBtn = controls.createEl('button', { title: 'Zoom in [ctrl + +]' }); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - zoomInBtn.innerHTML = ``; + + const zoomInParser = new DOMParser(); + const zoomInDoc = zoomInParser.parseFromString( + ``, + 'image/svg+xml' + ); + zoomInBtn.appendChild(activeDocument.importNode(zoomInDoc.documentElement, true)); zoomInBtn.onclick = () => { this.zoom = Math.min(this.zoom + 0.1, 2.0); zoomLabel.textContent = `${Math.round(this.zoom * 100)}%`; @@ -663,14 +674,24 @@ export class TikzEditorModal extends Modal implements TikzEditorContext { const undoBtn = controls.createEl('button', { title: 'Undo [Ctrl + Z]' }); undoBtn.disabled = true; - // eslint-disable-next-line @microsoft/sdl/no-inner-html - undoBtn.innerHTML = ``; + + const undoParser = new DOMParser(); + const undoDoc = undoParser.parseFromString( + ``, + 'image/svg+xml' + ); + undoBtn.appendChild(activeDocument.importNode(undoDoc.documentElement, true)); undoBtn.onclick = () => this.handleUndo(); const redoBtn = controls.createEl('button', { title: 'Redo [Ctrl + Y]' }); redoBtn.disabled = true; - // eslint-disable-next-line @microsoft/sdl/no-inner-html - redoBtn.innerHTML = ``; + + const redoParser = new DOMParser(); + const redoDoc = redoParser.parseFromString( + ``, + 'image/svg+xml' + ); + redoBtn.appendChild(activeDocument.importNode(redoDoc.documentElement, true)); redoBtn.onclick = () => this.handleRedo(); // Workspace diff --git a/src/features/tikz/live-preview-overlay.ts b/src/features/tikz/live-preview-overlay.ts index 66e623f..9a79141 100644 --- a/src/features/tikz/live-preview-overlay.ts +++ b/src/features/tikz/live-preview-overlay.ts @@ -127,8 +127,12 @@ class TikzLivePreviewOverlay { }; const pencilBtn = doc.createElement('button'); - // eslint-disable-next-line @microsoft/sdl/no-inner-html - pencilBtn.innerHTML = ``; + const pencilParser = new DOMParser(); + const pencilDoc = pencilParser.parseFromString( + ``, + 'image/svg+xml' + ); + pencilBtn.appendChild(activeDocument.importNode(pencilDoc.documentElement, true)); setCssProps(pencilBtn, { padding: '4px', display: 'flex',