vimd 0.5.6 → 0.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -32,6 +32,11 @@ export declare class PandocParser implements Parser {
32
32
  * @returns true if pandoc is installed and accessible
33
33
  */
34
34
  isAvailable(): Promise<boolean>;
35
+ /**
36
+ * Get the path to the LaTeX metadata Lua filter.
37
+ * Supports both development mode (src/) and production mode (dist/).
38
+ */
39
+ private getLuaFilterPath;
35
40
  /**
36
41
  * Build pandoc command arguments from config.
37
42
  */
@@ -1 +1 @@
1
- {"version":3,"file":"pandoc-parser.d.ts","sourceRoot":"","sources":["../../../src/core/parser/pandoc-parser.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,OAAO,CAAC;AAYhD;;;;GAIG;AACH,qBAAa,YAAa,YAAW,MAAM;IACzC,QAAQ,CAAC,IAAI,YAAY;IACzB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,UAAU,CAAC,CAAa;IAChC,OAAO,CAAC,UAAU,CAAe;IAEjC;;;;;OAKG;gBAED,MAAM,GAAE,OAAO,CAAC,YAAY,CAAM,EAClC,UAAU,CAAC,EAAE,UAAU,EACvB,UAAU,GAAE,YAAyB;IAOvC;;;;OAIG;IACG,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkB7C;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IASrC;;OAEG;IACH,OAAO,CAAC,eAAe;CAwCxB"}
1
+ {"version":3,"file":"pandoc-parser.d.ts","sourceRoot":"","sources":["../../../src/core/parser/pandoc-parser.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAKjE;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,OAAO,CAAC;AAYhD;;;;GAIG;AACH,qBAAa,YAAa,YAAW,MAAM;IACzC,QAAQ,CAAC,IAAI,YAAY;IACzB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,UAAU,CAAC,CAAa;IAChC,OAAO,CAAC,UAAU,CAAe;IAEjC;;;;;OAKG;gBAED,MAAM,GAAE,OAAO,CAAC,YAAY,CAAM,EAClC,UAAU,CAAC,EAAE,UAAU,EACvB,UAAU,GAAE,YAAyB;IAOvC;;;;OAIG;IACG,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkB7C;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IASrC;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAWxB;;OAEG;IACH,OAAO,CAAC,eAAe;CAmDxB"}
@@ -1,5 +1,10 @@
1
1
  // src/core/parser/pandoc-parser.ts
2
2
  import { execSync } from 'child_process';
3
+ import fs from 'fs-extra';
4
+ import * as path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
3
8
  /**
4
9
  * Default pandoc configuration for high-quality HTML output.
5
10
  */
@@ -61,14 +66,38 @@ export class PandocParser {
61
66
  return false;
62
67
  }
63
68
  }
69
+ /**
70
+ * Get the path to the LaTeX metadata Lua filter.
71
+ * Supports both development mode (src/) and production mode (dist/).
72
+ */
73
+ getLuaFilterPath() {
74
+ // Try production path first (dist/filters/)
75
+ const prodPath = path.join(__dirname, '../../filters/latex-metadata.lua');
76
+ if (fs.existsSync(prodPath)) {
77
+ return prodPath;
78
+ }
79
+ // Fallback to development path (filters/ from project root)
80
+ const devPath = path.join(__dirname, '../../../filters/latex-metadata.lua');
81
+ return devPath;
82
+ }
64
83
  /**
65
84
  * Build pandoc command arguments from config.
66
85
  */
67
86
  buildPandocArgs() {
68
87
  const args = [];
69
- // Basic options
70
- args.push(`--from=${this.fromFormat}`);
88
+ // Basic options - add +raw_tex for latex to preserve unknown commands
89
+ const fromFormatWithExt = this.fromFormat === 'latex'
90
+ ? 'latex+raw_tex'
91
+ : this.fromFormat;
92
+ args.push(`--from=${fromFormatWithExt}`);
71
93
  args.push('--to=html');
94
+ // Apply Lua filter for LaTeX metadata extraction
95
+ if (this.fromFormat === 'latex') {
96
+ const filterPath = this.getLuaFilterPath();
97
+ if (fs.existsSync(filterPath)) {
98
+ args.push(`--lua-filter=${filterPath}`);
99
+ }
100
+ }
72
101
  if (this.config.standalone) {
73
102
  args.push('--standalone');
74
103
  }
@@ -0,0 +1,135 @@
1
+ -- filters/latex-metadata.lua
2
+ -- Extract metadata from AGU journal format LaTeX and insert as HTML at document start
3
+
4
+ local extracted = {
5
+ title = nil,
6
+ authors = nil,
7
+ affiliations = {},
8
+ corresponding = nil,
9
+ keypoints = {},
10
+ abstract = nil
11
+ }
12
+
13
+ -- Convert \affil{n} to <sup>n</sup>
14
+ local function process_affil(text)
15
+ return text:gsub('\\affil{(%d+)}', '<sup>%1</sup>')
16
+ end
17
+
18
+ -- Extract title and abstract from Meta (pandoc processes these as metadata)
19
+ function Meta(meta)
20
+ if meta.title then
21
+ extracted.title = pandoc.utils.stringify(meta.title)
22
+ end
23
+ if meta.abstract then
24
+ extracted.abstract = pandoc.utils.stringify(meta.abstract)
25
+ end
26
+ return meta
27
+ end
28
+
29
+ function RawBlock(el)
30
+ if el.format:match('latex') or el.format:match('tex') then
31
+ local text = el.text
32
+
33
+ -- Extract \authors{...}
34
+ local authors = text:match('\\authors%s*{(.+)}')
35
+ if authors then
36
+ extracted.authors = process_affil(authors)
37
+ end
38
+
39
+ -- Extract \affiliation{n}{...}
40
+ local affil_num, affil_text = text:match('\\affiliation%s*{(%d+)}%s*{(.+)}')
41
+ if affil_num and affil_text then
42
+ extracted.affiliations[tonumber(affil_num)] = affil_text
43
+ end
44
+
45
+ -- Extract \correspondingauthor{name}{email}
46
+ local corr_name, corr_email = text:match('\\correspondingauthor%s*{([^}]+)}%s*{([^}]+)}')
47
+ if corr_name and corr_email then
48
+ extracted.corresponding = { name = corr_name, email = corr_email }
49
+ end
50
+
51
+ -- Extract \begin{keypoints}...\end{keypoints}
52
+ local keypoints_content = text:match('\\begin{keypoints}(.-)\\end{keypoints}')
53
+ if keypoints_content then
54
+ -- Extract each \item content
55
+ for item in keypoints_content:gmatch('\\item%s*([^\n]+)') do
56
+ -- Trim whitespace
57
+ item = item:gsub('^%s*', ''):gsub('%s*$', '')
58
+ if item ~= '' then
59
+ table.insert(extracted.keypoints, item)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ return el
65
+ end
66
+
67
+ function Pandoc(doc)
68
+ local header_blocks = {}
69
+
70
+ -- 1. Title
71
+ if extracted.title then
72
+ table.insert(header_blocks, pandoc.RawBlock('html',
73
+ '<h1 class="vimd-title">' .. extracted.title .. '</h1>'))
74
+ end
75
+
76
+ -- 2. Authors
77
+ if extracted.authors then
78
+ table.insert(header_blocks, pandoc.RawBlock('html',
79
+ '<div class="vimd-authors">' .. extracted.authors .. '</div>'))
80
+ end
81
+
82
+ -- 3. Affiliations
83
+ if next(extracted.affiliations) then
84
+ local affil_html = '<div class="vimd-affiliations">'
85
+ -- Sort by affiliation number
86
+ local sorted_keys = {}
87
+ for k in pairs(extracted.affiliations) do
88
+ table.insert(sorted_keys, k)
89
+ end
90
+ table.sort(sorted_keys)
91
+ for _, i in ipairs(sorted_keys) do
92
+ local affil = extracted.affiliations[i]
93
+ affil_html = affil_html .. '<div class="vimd-affiliation"><sup>' .. i .. '</sup> ' .. affil .. '</div>'
94
+ end
95
+ affil_html = affil_html .. '</div>'
96
+ table.insert(header_blocks, pandoc.RawBlock('html', affil_html))
97
+ end
98
+
99
+ -- 4. Corresponding author
100
+ if extracted.corresponding then
101
+ local corr_html = '<div class="vimd-corresponding">Corresponding author: '
102
+ .. extracted.corresponding.name .. ' (<a href="mailto:' .. extracted.corresponding.email .. '">'
103
+ .. extracted.corresponding.email .. '</a>)</div>'
104
+ table.insert(header_blocks, pandoc.RawBlock('html', corr_html))
105
+ end
106
+
107
+ -- 5. Key Points
108
+ if #extracted.keypoints > 0 then
109
+ local kp_html = '<div class="vimd-keypoints"><h2>Key Points</h2><ul>'
110
+ for _, item in ipairs(extracted.keypoints) do
111
+ kp_html = kp_html .. '<li>' .. item .. '</li>'
112
+ end
113
+ kp_html = kp_html .. '</ul></div>'
114
+ table.insert(header_blocks, pandoc.RawBlock('html', kp_html))
115
+ end
116
+
117
+ -- 6. Abstract
118
+ if extracted.abstract then
119
+ table.insert(header_blocks, pandoc.RawBlock('html',
120
+ '<div class="vimd-abstract"><h2>Abstract</h2><p>' .. extracted.abstract .. '</p></div>'))
121
+ end
122
+
123
+ -- Insert at document start (in reverse order to maintain order)
124
+ for i = #header_blocks, 1, -1 do
125
+ table.insert(doc.blocks, 1, header_blocks[i])
126
+ end
127
+
128
+ return doc
129
+ end
130
+
131
+ return {
132
+ { Meta = Meta },
133
+ { RawBlock = RawBlock },
134
+ { Pandoc = Pandoc }
135
+ }
@@ -29,11 +29,15 @@
29
29
  </style>
30
30
  <script>
31
31
  MathJax = {
32
- loader: {load: ['[tex]/bussproofs', '[tex]/ams', '[tex]/physics']},
32
+ loader: {load: ['[tex]/bussproofs', '[tex]/ams', '[tex]/physics', '[tex]/boldsymbol']},
33
33
  tex: {
34
- packages: {'[+]': ['bussproofs', 'ams', 'physics']},
34
+ packages: {'[+]': ['bussproofs', 'ams', 'physics', 'boldsymbol']},
35
35
  inlineMath: [['$', '$'], ['\\(', '\\)']],
36
- displayMath: [['$$', '$$'], ['\\[', '\\]']]
36
+ displayMath: [['$$', '$$'], ['\\[', '\\]']],
37
+ tags: 'ams',
38
+ macros: {
39
+ bm: ['\\boldsymbol{#1}', 1]
40
+ }
37
41
  }
38
42
  };
39
43
  </script>
@@ -27,11 +27,15 @@
27
27
  </style>
28
28
  <script>
29
29
  MathJax = {
30
- loader: {load: ['[tex]/bussproofs', '[tex]/ams', '[tex]/physics']},
30
+ loader: {load: ['[tex]/bussproofs', '[tex]/ams', '[tex]/physics', '[tex]/boldsymbol']},
31
31
  tex: {
32
- packages: {'[+]': ['bussproofs', 'ams', 'physics']},
32
+ packages: {'[+]': ['bussproofs', 'ams', 'physics', 'boldsymbol']},
33
33
  inlineMath: [['$', '$'], ['\\(', '\\)']],
34
- displayMath: [['$$', '$$'], ['\\[', '\\]']]
34
+ displayMath: [['$$', '$$'], ['\\[', '\\]']],
35
+ tags: 'ams',
36
+ macros: {
37
+ bm: ['\\boldsymbol{#1}', 1]
38
+ }
35
39
  }
36
40
  };
37
41
  </script>
@@ -29,11 +29,15 @@
29
29
  </style>
30
30
  <script>
31
31
  MathJax = {
32
- loader: {load: ['[tex]/bussproofs', '[tex]/ams', '[tex]/physics']},
32
+ loader: {load: ['[tex]/bussproofs', '[tex]/ams', '[tex]/physics', '[tex]/boldsymbol']},
33
33
  tex: {
34
- packages: {'[+]': ['bussproofs', 'ams', 'physics']},
34
+ packages: {'[+]': ['bussproofs', 'ams', 'physics', 'boldsymbol']},
35
35
  inlineMath: [['$', '$'], ['\\(', '\\)']],
36
- displayMath: [['$$', '$$'], ['\\[', '\\]']]
36
+ displayMath: [['$$', '$$'], ['\\[', '\\]']],
37
+ tags: 'ams',
38
+ macros: {
39
+ bm: ['\\boldsymbol{#1}', 1]
40
+ }
37
41
  }
38
42
  };
39
43
  </script>
@@ -643,6 +643,105 @@ body mark {
643
643
  }
644
644
  }
645
645
 
646
+ /* ============================================
647
+ LaTeX Metadata
648
+ ============================================ */
649
+ .vimd-authors {
650
+ font-size: 1.1em;
651
+ margin-bottom: 0.5em;
652
+ color: #2c3e50;
653
+ text-align: center;
654
+ }
655
+
656
+ .vimd-authors sup {
657
+ color: #c0392b;
658
+ font-weight: bold;
659
+ }
660
+
661
+ .vimd-affiliations {
662
+ font-size: 0.9em;
663
+ color: #7f8c8d;
664
+ margin-bottom: 0.5em;
665
+ text-align: center;
666
+ }
667
+
668
+ .vimd-affiliation {
669
+ margin-bottom: 0.25em;
670
+ }
671
+
672
+ .vimd-affiliation sup {
673
+ color: #c0392b;
674
+ font-weight: bold;
675
+ margin-right: 0.25em;
676
+ }
677
+
678
+ .vimd-corresponding {
679
+ font-size: 0.9em;
680
+ color: #7f8c8d;
681
+ font-style: italic;
682
+ margin-bottom: 1.5em;
683
+ padding-bottom: 1em;
684
+ border-bottom: 1px solid #ecf0f1;
685
+ text-align: center;
686
+ }
687
+
688
+ .vimd-corresponding a {
689
+ color: #2980b9;
690
+ }
691
+
692
+ /* LaTeX document title */
693
+ .vimd-title {
694
+ font-size: 1.8em;
695
+ font-weight: bold;
696
+ text-align: center;
697
+ margin-bottom: 0.5em;
698
+ color: #2c3e50;
699
+ }
700
+
701
+ /* Key Points box */
702
+ .vimd-keypoints {
703
+ background-color: #f8f9fa;
704
+ border: 1px solid #ecf0f1;
705
+ border-radius: 4px;
706
+ padding: 1em;
707
+ margin: 1em 0;
708
+ }
709
+
710
+ .vimd-keypoints h2 {
711
+ font-size: 1.1em;
712
+ margin-top: 0;
713
+ margin-bottom: 0.5em;
714
+ color: #2c3e50;
715
+ }
716
+
717
+ .vimd-keypoints ul {
718
+ margin: 0;
719
+ padding-left: 1.5em;
720
+ }
721
+
722
+ .vimd-keypoints li {
723
+ margin-bottom: 0.3em;
724
+ }
725
+
726
+ /* Abstract section */
727
+ .vimd-abstract {
728
+ border-left: 4px solid #2980b9;
729
+ padding-left: 1em;
730
+ margin: 1em 0;
731
+ }
732
+
733
+ .vimd-abstract h2 {
734
+ font-size: 1.1em;
735
+ margin-top: 0;
736
+ margin-bottom: 0.5em;
737
+ color: #2c3e50;
738
+ }
739
+
740
+ .vimd-abstract p {
741
+ margin: 0;
742
+ text-align: justify;
743
+ }
744
+
646
745
  /* ============================================
647
746
  Responsive
648
747
  ============================================ */
@@ -516,6 +516,100 @@ body a:focus {
516
516
  outline-offset: -2px;
517
517
  }
518
518
 
519
+ /* LaTeX metadata styles */
520
+ .vimd-authors {
521
+ font-size: 1.1em;
522
+ margin-bottom: 0.5em;
523
+ color: #e6edf3;
524
+ }
525
+
526
+ .vimd-authors sup {
527
+ color: #58a6ff;
528
+ font-weight: bold;
529
+ }
530
+
531
+ .vimd-affiliations {
532
+ font-size: 0.9em;
533
+ color: #8b949e;
534
+ margin-bottom: 0.5em;
535
+ }
536
+
537
+ .vimd-affiliation {
538
+ margin-bottom: 0.25em;
539
+ }
540
+
541
+ .vimd-affiliation sup {
542
+ color: #58a6ff;
543
+ font-weight: bold;
544
+ margin-right: 0.25em;
545
+ }
546
+
547
+ .vimd-corresponding {
548
+ font-size: 0.9em;
549
+ color: #8b949e;
550
+ font-style: italic;
551
+ margin-bottom: 1.5em;
552
+ padding-bottom: 1em;
553
+ border-bottom: 1px solid #30363d;
554
+ }
555
+
556
+ .vimd-corresponding a {
557
+ color: #58a6ff;
558
+ }
559
+
560
+ /* LaTeX document title */
561
+ .vimd-title {
562
+ font-size: 1.8em;
563
+ font-weight: bold;
564
+ text-align: center;
565
+ margin-bottom: 0.5em;
566
+ color: #c9d1d9;
567
+ }
568
+
569
+ /* Key Points box */
570
+ .vimd-keypoints {
571
+ background-color: #161b22;
572
+ border: 1px solid #30363d;
573
+ border-radius: 6px;
574
+ padding: 1em;
575
+ margin: 1em 0;
576
+ }
577
+
578
+ .vimd-keypoints h2 {
579
+ font-size: 1.1em;
580
+ margin-top: 0;
581
+ margin-bottom: 0.5em;
582
+ color: #c9d1d9;
583
+ }
584
+
585
+ .vimd-keypoints ul {
586
+ margin: 0;
587
+ padding-left: 1.5em;
588
+ }
589
+
590
+ .vimd-keypoints li {
591
+ margin-bottom: 0.3em;
592
+ }
593
+
594
+ /* Abstract section */
595
+ .vimd-abstract {
596
+ border-left: 4px solid #58a6ff;
597
+ padding-left: 1em;
598
+ margin: 1em 0;
599
+ }
600
+
601
+ .vimd-abstract h2 {
602
+ font-size: 1.1em;
603
+ margin-top: 0;
604
+ margin-bottom: 0.5em;
605
+ color: #c9d1d9;
606
+ }
607
+
608
+ .vimd-abstract p {
609
+ margin: 0;
610
+ text-align: justify;
611
+ }
612
+
519
613
  /* Responsive */
520
614
  @media (max-width: 768px) {
521
615
  body {
@@ -516,6 +516,100 @@ body a:focus {
516
516
  outline-offset: -2px;
517
517
  }
518
518
 
519
+ /* LaTeX metadata styles */
520
+ .vimd-authors {
521
+ font-size: 1.1em;
522
+ margin-bottom: 0.5em;
523
+ color: #1f2328;
524
+ }
525
+
526
+ .vimd-authors sup {
527
+ color: #0969da;
528
+ font-weight: bold;
529
+ }
530
+
531
+ .vimd-affiliations {
532
+ font-size: 0.9em;
533
+ color: #656d76;
534
+ margin-bottom: 0.5em;
535
+ }
536
+
537
+ .vimd-affiliation {
538
+ margin-bottom: 0.25em;
539
+ }
540
+
541
+ .vimd-affiliation sup {
542
+ color: #0969da;
543
+ font-weight: bold;
544
+ margin-right: 0.25em;
545
+ }
546
+
547
+ .vimd-corresponding {
548
+ font-size: 0.9em;
549
+ color: #656d76;
550
+ font-style: italic;
551
+ margin-bottom: 1.5em;
552
+ padding-bottom: 1em;
553
+ border-bottom: 1px solid #d0d7de;
554
+ }
555
+
556
+ .vimd-corresponding a {
557
+ color: #0969da;
558
+ }
559
+
560
+ /* LaTeX document title */
561
+ .vimd-title {
562
+ font-size: 1.8em;
563
+ font-weight: bold;
564
+ text-align: center;
565
+ margin-bottom: 0.5em;
566
+ color: #1f2328;
567
+ }
568
+
569
+ /* Key Points box */
570
+ .vimd-keypoints {
571
+ background-color: #f6f8fa;
572
+ border: 1px solid #d0d7de;
573
+ border-radius: 6px;
574
+ padding: 1em;
575
+ margin: 1em 0;
576
+ }
577
+
578
+ .vimd-keypoints h2 {
579
+ font-size: 1.1em;
580
+ margin-top: 0;
581
+ margin-bottom: 0.5em;
582
+ color: #1f2328;
583
+ }
584
+
585
+ .vimd-keypoints ul {
586
+ margin: 0;
587
+ padding-left: 1.5em;
588
+ }
589
+
590
+ .vimd-keypoints li {
591
+ margin-bottom: 0.3em;
592
+ }
593
+
594
+ /* Abstract section */
595
+ .vimd-abstract {
596
+ border-left: 4px solid #0969da;
597
+ padding-left: 1em;
598
+ margin: 1em 0;
599
+ }
600
+
601
+ .vimd-abstract h2 {
602
+ font-size: 1.1em;
603
+ margin-top: 0;
604
+ margin-bottom: 0.5em;
605
+ color: #1f2328;
606
+ }
607
+
608
+ .vimd-abstract p {
609
+ margin: 0;
610
+ text-align: justify;
611
+ }
612
+
519
613
  /* Responsive */
520
614
  @media (max-width: 768px) {
521
615
  body {
@@ -513,6 +513,102 @@ body i {
513
513
  }
514
514
  }
515
515
 
516
+ /* ============================================
517
+ LaTeX Metadata
518
+ ============================================ */
519
+ .vimd-authors {
520
+ font-size: 1.1em;
521
+ margin-bottom: 0.5em;
522
+ color: #222;
523
+ }
524
+
525
+ .vimd-authors sup {
526
+ color: #555;
527
+ font-weight: bold;
528
+ }
529
+
530
+ .vimd-affiliations {
531
+ font-size: 0.9em;
532
+ color: #666;
533
+ margin-bottom: 0.5em;
534
+ }
535
+
536
+ .vimd-affiliation {
537
+ margin-bottom: 0.25em;
538
+ }
539
+
540
+ .vimd-affiliation sup {
541
+ color: #555;
542
+ font-weight: bold;
543
+ margin-right: 0.25em;
544
+ }
545
+
546
+ .vimd-corresponding {
547
+ font-size: 0.9em;
548
+ color: #666;
549
+ font-style: italic;
550
+ margin-bottom: 1.5em;
551
+ padding-bottom: 1em;
552
+ border-bottom: 1px solid #ddd;
553
+ }
554
+
555
+ .vimd-corresponding a {
556
+ color: #333;
557
+ }
558
+
559
+ /* LaTeX document title */
560
+ .vimd-title {
561
+ font-size: 1.8em;
562
+ font-weight: bold;
563
+ text-align: center;
564
+ margin-bottom: 0.5em;
565
+ color: #333;
566
+ }
567
+
568
+ /* Key Points box */
569
+ .vimd-keypoints {
570
+ background-color: #fafafa;
571
+ border: 1px solid #ddd;
572
+ border-radius: 4px;
573
+ padding: 1em;
574
+ margin: 1em 0;
575
+ }
576
+
577
+ .vimd-keypoints h2 {
578
+ font-size: 1.1em;
579
+ margin-top: 0;
580
+ margin-bottom: 0.5em;
581
+ color: #333;
582
+ }
583
+
584
+ .vimd-keypoints ul {
585
+ margin: 0;
586
+ padding-left: 1.5em;
587
+ }
588
+
589
+ .vimd-keypoints li {
590
+ margin-bottom: 0.3em;
591
+ }
592
+
593
+ /* Abstract section */
594
+ .vimd-abstract {
595
+ border-left: 3px solid #333;
596
+ padding-left: 1em;
597
+ margin: 1em 0;
598
+ }
599
+
600
+ .vimd-abstract h2 {
601
+ font-size: 1.1em;
602
+ margin-top: 0;
603
+ margin-bottom: 0.5em;
604
+ color: #333;
605
+ }
606
+
607
+ .vimd-abstract p {
608
+ margin: 0;
609
+ text-align: justify;
610
+ }
611
+
516
612
  /* ============================================
517
613
  Responsive
518
614
  ============================================ */
@@ -613,6 +613,102 @@ body i {
613
613
  }
614
614
  }
615
615
 
616
+ /* ============================================
617
+ LaTeX Metadata
618
+ ============================================ */
619
+ .vimd-authors {
620
+ font-size: 1.1em;
621
+ margin-bottom: 0.5em;
622
+ color: #d4d4d4;
623
+ }
624
+
625
+ .vimd-authors sup {
626
+ color: #4ec9b0;
627
+ font-weight: bold;
628
+ }
629
+
630
+ .vimd-affiliations {
631
+ font-size: 0.9em;
632
+ color: #9cdcfe;
633
+ margin-bottom: 0.5em;
634
+ }
635
+
636
+ .vimd-affiliation {
637
+ margin-bottom: 0.25em;
638
+ }
639
+
640
+ .vimd-affiliation sup {
641
+ color: #4ec9b0;
642
+ font-weight: bold;
643
+ margin-right: 0.25em;
644
+ }
645
+
646
+ .vimd-corresponding {
647
+ font-size: 0.9em;
648
+ color: #9cdcfe;
649
+ font-style: italic;
650
+ margin-bottom: 1.5em;
651
+ padding-bottom: 1em;
652
+ border-bottom: 1px solid #3c3c3c;
653
+ }
654
+
655
+ .vimd-corresponding a {
656
+ color: #569cd6;
657
+ }
658
+
659
+ /* LaTeX document title */
660
+ .vimd-title {
661
+ font-size: 1.8em;
662
+ font-weight: bold;
663
+ text-align: center;
664
+ margin-bottom: 0.5em;
665
+ color: #d4d4d4;
666
+ }
667
+
668
+ /* Key Points box */
669
+ .vimd-keypoints {
670
+ background-color: #252526;
671
+ border: 1px solid #3c3c3c;
672
+ border-radius: 4px;
673
+ padding: 1em;
674
+ margin: 1em 0;
675
+ }
676
+
677
+ .vimd-keypoints h2 {
678
+ font-size: 1.1em;
679
+ margin-top: 0;
680
+ margin-bottom: 0.5em;
681
+ color: #d4d4d4;
682
+ }
683
+
684
+ .vimd-keypoints ul {
685
+ margin: 0;
686
+ padding-left: 1.5em;
687
+ }
688
+
689
+ .vimd-keypoints li {
690
+ margin-bottom: 0.3em;
691
+ }
692
+
693
+ /* Abstract section */
694
+ .vimd-abstract {
695
+ border-left: 4px solid #569cd6;
696
+ padding-left: 1em;
697
+ margin: 1em 0;
698
+ }
699
+
700
+ .vimd-abstract h2 {
701
+ font-size: 1.1em;
702
+ margin-top: 0;
703
+ margin-bottom: 0.5em;
704
+ color: #d4d4d4;
705
+ }
706
+
707
+ .vimd-abstract p {
708
+ margin: 0;
709
+ text-align: justify;
710
+ }
711
+
616
712
  /* ============================================
617
713
  Responsive
618
714
  ============================================ */
@@ -0,0 +1,135 @@
1
+ -- filters/latex-metadata.lua
2
+ -- Extract metadata from AGU journal format LaTeX and insert as HTML at document start
3
+
4
+ local extracted = {
5
+ title = nil,
6
+ authors = nil,
7
+ affiliations = {},
8
+ corresponding = nil,
9
+ keypoints = {},
10
+ abstract = nil
11
+ }
12
+
13
+ -- Convert \affil{n} to <sup>n</sup>
14
+ local function process_affil(text)
15
+ return text:gsub('\\affil{(%d+)}', '<sup>%1</sup>')
16
+ end
17
+
18
+ -- Extract title and abstract from Meta (pandoc processes these as metadata)
19
+ function Meta(meta)
20
+ if meta.title then
21
+ extracted.title = pandoc.utils.stringify(meta.title)
22
+ end
23
+ if meta.abstract then
24
+ extracted.abstract = pandoc.utils.stringify(meta.abstract)
25
+ end
26
+ return meta
27
+ end
28
+
29
+ function RawBlock(el)
30
+ if el.format:match('latex') or el.format:match('tex') then
31
+ local text = el.text
32
+
33
+ -- Extract \authors{...}
34
+ local authors = text:match('\\authors%s*{(.+)}')
35
+ if authors then
36
+ extracted.authors = process_affil(authors)
37
+ end
38
+
39
+ -- Extract \affiliation{n}{...}
40
+ local affil_num, affil_text = text:match('\\affiliation%s*{(%d+)}%s*{(.+)}')
41
+ if affil_num and affil_text then
42
+ extracted.affiliations[tonumber(affil_num)] = affil_text
43
+ end
44
+
45
+ -- Extract \correspondingauthor{name}{email}
46
+ local corr_name, corr_email = text:match('\\correspondingauthor%s*{([^}]+)}%s*{([^}]+)}')
47
+ if corr_name and corr_email then
48
+ extracted.corresponding = { name = corr_name, email = corr_email }
49
+ end
50
+
51
+ -- Extract \begin{keypoints}...\end{keypoints}
52
+ local keypoints_content = text:match('\\begin{keypoints}(.-)\\end{keypoints}')
53
+ if keypoints_content then
54
+ -- Extract each \item content
55
+ for item in keypoints_content:gmatch('\\item%s*([^\n]+)') do
56
+ -- Trim whitespace
57
+ item = item:gsub('^%s*', ''):gsub('%s*$', '')
58
+ if item ~= '' then
59
+ table.insert(extracted.keypoints, item)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ return el
65
+ end
66
+
67
+ function Pandoc(doc)
68
+ local header_blocks = {}
69
+
70
+ -- 1. Title
71
+ if extracted.title then
72
+ table.insert(header_blocks, pandoc.RawBlock('html',
73
+ '<h1 class="vimd-title">' .. extracted.title .. '</h1>'))
74
+ end
75
+
76
+ -- 2. Authors
77
+ if extracted.authors then
78
+ table.insert(header_blocks, pandoc.RawBlock('html',
79
+ '<div class="vimd-authors">' .. extracted.authors .. '</div>'))
80
+ end
81
+
82
+ -- 3. Affiliations
83
+ if next(extracted.affiliations) then
84
+ local affil_html = '<div class="vimd-affiliations">'
85
+ -- Sort by affiliation number
86
+ local sorted_keys = {}
87
+ for k in pairs(extracted.affiliations) do
88
+ table.insert(sorted_keys, k)
89
+ end
90
+ table.sort(sorted_keys)
91
+ for _, i in ipairs(sorted_keys) do
92
+ local affil = extracted.affiliations[i]
93
+ affil_html = affil_html .. '<div class="vimd-affiliation"><sup>' .. i .. '</sup> ' .. affil .. '</div>'
94
+ end
95
+ affil_html = affil_html .. '</div>'
96
+ table.insert(header_blocks, pandoc.RawBlock('html', affil_html))
97
+ end
98
+
99
+ -- 4. Corresponding author
100
+ if extracted.corresponding then
101
+ local corr_html = '<div class="vimd-corresponding">Corresponding author: '
102
+ .. extracted.corresponding.name .. ' (<a href="mailto:' .. extracted.corresponding.email .. '">'
103
+ .. extracted.corresponding.email .. '</a>)</div>'
104
+ table.insert(header_blocks, pandoc.RawBlock('html', corr_html))
105
+ end
106
+
107
+ -- 5. Key Points
108
+ if #extracted.keypoints > 0 then
109
+ local kp_html = '<div class="vimd-keypoints"><h2>Key Points</h2><ul>'
110
+ for _, item in ipairs(extracted.keypoints) do
111
+ kp_html = kp_html .. '<li>' .. item .. '</li>'
112
+ end
113
+ kp_html = kp_html .. '</ul></div>'
114
+ table.insert(header_blocks, pandoc.RawBlock('html', kp_html))
115
+ end
116
+
117
+ -- 6. Abstract
118
+ if extracted.abstract then
119
+ table.insert(header_blocks, pandoc.RawBlock('html',
120
+ '<div class="vimd-abstract"><h2>Abstract</h2><p>' .. extracted.abstract .. '</p></div>'))
121
+ end
122
+
123
+ -- Insert at document start (in reverse order to maintain order)
124
+ for i = #header_blocks, 1, -1 do
125
+ table.insert(doc.blocks, 1, header_blocks[i])
126
+ end
127
+
128
+ return doc
129
+ end
130
+
131
+ return {
132
+ { Meta = Meta },
133
+ { RawBlock = RawBlock },
134
+ { Pandoc = Pandoc }
135
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vimd",
3
- "version": "0.5.6",
3
+ "version": "0.5.7",
4
4
  "description": "Real-time Markdown preview tool with pandoc (view markdown)",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -30,6 +30,7 @@
30
30
  "files": [
31
31
  "dist",
32
32
  "templates",
33
+ "filters",
33
34
  "README.md",
34
35
  "LICENSE"
35
36
  ],
@@ -29,11 +29,15 @@
29
29
  </style>
30
30
  <script>
31
31
  MathJax = {
32
- loader: {load: ['[tex]/bussproofs', '[tex]/ams', '[tex]/physics']},
32
+ loader: {load: ['[tex]/bussproofs', '[tex]/ams', '[tex]/physics', '[tex]/boldsymbol']},
33
33
  tex: {
34
- packages: {'[+]': ['bussproofs', 'ams', 'physics']},
34
+ packages: {'[+]': ['bussproofs', 'ams', 'physics', 'boldsymbol']},
35
35
  inlineMath: [['$', '$'], ['\\(', '\\)']],
36
- displayMath: [['$$', '$$'], ['\\[', '\\]']]
36
+ displayMath: [['$$', '$$'], ['\\[', '\\]']],
37
+ tags: 'ams',
38
+ macros: {
39
+ bm: ['\\boldsymbol{#1}', 1]
40
+ }
37
41
  }
38
42
  };
39
43
  </script>
@@ -27,11 +27,15 @@
27
27
  </style>
28
28
  <script>
29
29
  MathJax = {
30
- loader: {load: ['[tex]/bussproofs', '[tex]/ams', '[tex]/physics']},
30
+ loader: {load: ['[tex]/bussproofs', '[tex]/ams', '[tex]/physics', '[tex]/boldsymbol']},
31
31
  tex: {
32
- packages: {'[+]': ['bussproofs', 'ams', 'physics']},
32
+ packages: {'[+]': ['bussproofs', 'ams', 'physics', 'boldsymbol']},
33
33
  inlineMath: [['$', '$'], ['\\(', '\\)']],
34
- displayMath: [['$$', '$$'], ['\\[', '\\]']]
34
+ displayMath: [['$$', '$$'], ['\\[', '\\]']],
35
+ tags: 'ams',
36
+ macros: {
37
+ bm: ['\\boldsymbol{#1}', 1]
38
+ }
35
39
  }
36
40
  };
37
41
  </script>
@@ -29,11 +29,15 @@
29
29
  </style>
30
30
  <script>
31
31
  MathJax = {
32
- loader: {load: ['[tex]/bussproofs', '[tex]/ams', '[tex]/physics']},
32
+ loader: {load: ['[tex]/bussproofs', '[tex]/ams', '[tex]/physics', '[tex]/boldsymbol']},
33
33
  tex: {
34
- packages: {'[+]': ['bussproofs', 'ams', 'physics']},
34
+ packages: {'[+]': ['bussproofs', 'ams', 'physics', 'boldsymbol']},
35
35
  inlineMath: [['$', '$'], ['\\(', '\\)']],
36
- displayMath: [['$$', '$$'], ['\\[', '\\]']]
36
+ displayMath: [['$$', '$$'], ['\\[', '\\]']],
37
+ tags: 'ams',
38
+ macros: {
39
+ bm: ['\\boldsymbol{#1}', 1]
40
+ }
37
41
  }
38
42
  };
39
43
  </script>