@udx/md2html 1.3.1 → 1.4.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@udx/md2html",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "Magazine-quality Markdown to HTML converter with professional styling",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -16,16 +16,16 @@
16
16
  /* Floating navigation styles - clean and minimal */
17
17
  .chapter-navigation {
18
18
  position: fixed;
19
- top: 50%;
20
- transform: translateY(-50%);
21
- left: 20px;
19
+ top: 0;
20
+ left: 0;
22
21
  width: var(--chapter-nav-width);
23
- max-height: 80vh;
22
+ height: 100vh;
24
23
  overflow-y: auto;
25
- background-color: transparent;
26
- padding: 10px 0;
24
+ background-color: #fff;
25
+ padding: 40px 20px 60px 20px;
27
26
  z-index: 100;
28
- scrollbar-width: none; /* Hide scrollbar for Firefox */
27
+ scrollbar-width: thin; /* Show thin scrollbar for Firefox */
28
+ scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
29
29
  transition: transform 0.3s ease;
30
30
  border: 0 none transparent !important;
31
31
  box-shadow: none !important;
@@ -34,9 +34,18 @@
34
34
  -moz-box-shadow: none !important;
35
35
  }
36
36
 
37
- /* Hide scrollbar for Chrome, Safari and Opera */
37
+ /* Show thin scrollbar for Chrome, Safari and Opera */
38
38
  .chapter-navigation::-webkit-scrollbar {
39
- display: none;
39
+ width: 4px;
40
+ }
41
+
42
+ .chapter-navigation::-webkit-scrollbar-track {
43
+ background: transparent;
44
+ }
45
+
46
+ .chapter-navigation::-webkit-scrollbar-thumb {
47
+ background-color: rgba(0, 0, 0, 0.2);
48
+ border-radius: 2px;
40
49
  }
41
50
 
42
51
  .chapter-nav-list {
@@ -181,14 +190,22 @@
181
190
  }
182
191
 
183
192
  /* Add padding to the content to prevent floating nav from overlapping */
193
+ /* Content is centered in the remaining space to the right of the sidebar */
184
194
  .chapter-content {
185
- padding-left: calc(var(--chapter-nav-width) + 40px);
195
+ /* Leave space for sidebar on the left - push content further right */
196
+ margin-left: calc(var(--chapter-nav-width) + 120px);
197
+ /* Add margin on the right for balance */
198
+ margin-right: 80px;
199
+ /* Remove padding-left since we're using margin */
200
+ padding-left: 0;
201
+ padding-right: 0;
186
202
  }
187
203
 
188
204
  /* Responsive adjustments */
189
205
  @media (max-width: 1024px) {
190
206
  .chapter-content {
191
- padding-left: calc(var(--chapter-nav-width) + 30px);
207
+ margin-left: calc(var(--chapter-nav-width) + 60px);
208
+ margin-right: 40px;
192
209
  }
193
210
  }
194
211
 
@@ -202,12 +219,14 @@
202
219
  }
203
220
 
204
221
  .chapter-content {
205
- padding-left: 20px;
206
- padding-right: 20px;
222
+ margin-left: 20px;
223
+ margin-right: 20px;
224
+ padding-left: 0;
225
+ padding-right: 0;
207
226
  }
208
227
  }
209
228
 
210
- /* Hide nav when printing */
229
+ /* Hide nav when printing - let browser print dialog control all margins */
211
230
  @media print {
212
231
  .chapter-navigation,
213
232
  .mobile-chapter-nav {
@@ -215,6 +234,12 @@
215
234
  }
216
235
 
217
236
  .chapter-content {
218
- padding-left: 0;
237
+ /* Remove all margins and padding so browser print dialog controls page margins */
238
+ margin-left: 0 !important;
239
+ margin-right: 0 !important;
240
+ padding-left: 0 !important;
241
+ padding-right: 0 !important;
242
+ max-width: 100% !important;
243
+ width: 100% !important;
219
244
  }
220
245
  }
package/static/scripts.js CHANGED
@@ -44,90 +44,16 @@ document.addEventListener('DOMContentLoaded', (event) => {
44
44
  }
45
45
  });
46
46
 
47
- // Smart table sizing - auto-fit columns based on content
47
+ // Table enhancement - add responsive wrapper and basic styling
48
+ // Note: We do NOT set inline styles for table-layout or width to avoid conflicts with CSS
48
49
  document.querySelectorAll('table').forEach(table => {
49
- // Use auto layout for smart column sizing
50
- table.style.tableLayout = 'auto';
51
- table.style.width = 'auto';
52
- table.style.maxWidth = '100%';
53
- table.classList.add('auto-size');
54
-
55
50
  // Add responsive wrapper if not already wrapped
56
51
  if (table.parentElement.tagName !== 'DIV' || !table.parentElement.classList.contains('table-responsive')) {
57
52
  const wrapper = document.createElement('div');
58
- wrapper.className = 'table-responsive overflow-x-auto my-8';
59
- wrapper.style.maxWidth = '100%';
53
+ wrapper.className = 'table-responsive';
60
54
  table.parentNode.insertBefore(wrapper, table);
61
55
  wrapper.appendChild(table);
62
56
  }
63
-
64
- // Analyze columns and apply smart sizing
65
- const rows = table.querySelectorAll('tr');
66
- if (rows.length > 0) {
67
- const headerCells = table.querySelectorAll('th');
68
- const numCols = headerCells.length || (rows[0] ? rows[0].querySelectorAll('td').length : 0);
69
-
70
- // Analyze each column's content
71
- for (let colIdx = 0; colIdx < numCols; colIdx++) {
72
- let isNumeric = true;
73
- let isCurrency = true;
74
- let isCompact = true;
75
- let maxLength = 0;
76
-
77
- rows.forEach(row => {
78
- const cells = row.querySelectorAll('th, td');
79
- if (cells[colIdx]) {
80
- const text = cells[colIdx].textContent.trim();
81
- maxLength = Math.max(maxLength, text.length);
82
-
83
- // Check if numeric (including decimals and commas)
84
- if (!/^[\d,.\-\s]+$/.test(text) && text !== '') {
85
- isNumeric = false;
86
- }
87
-
88
- // Check if currency (starts with $ or ends with .00)
89
- if (!/^\$?[\d,]+\.?\d*$/.test(text) && text !== '') {
90
- isCurrency = false;
91
- }
92
-
93
- // Compact if short content (less than 15 chars)
94
- if (text.length > 15) {
95
- isCompact = false;
96
- }
97
- }
98
- });
99
-
100
- // Apply column classes based on analysis
101
- rows.forEach(row => {
102
- const cells = row.querySelectorAll('th, td');
103
- if (cells[colIdx]) {
104
- if (isCurrency && maxLength <= 15) {
105
- cells[colIdx].classList.add('col-currency');
106
- } else if (isNumeric && maxLength <= 10) {
107
- cells[colIdx].classList.add('col-numeric');
108
- } else if (isCompact && maxLength <= 10) {
109
- cells[colIdx].classList.add('col-compact');
110
- } else {
111
- cells[colIdx].classList.add('col-text');
112
- }
113
- }
114
- });
115
- }
116
- }
117
-
118
- // Add proper classes and styles to table headers
119
- table.querySelectorAll('th').forEach(th => {
120
- th.classList.add('bg-gray-50');
121
- th.classList.add('font-semibold');
122
- th.style.padding = '0.5rem 0.75rem';
123
- th.style.borderBottom = '2px solid #e5e7eb';
124
- });
125
-
126
- // Add proper classes and styles to table cells
127
- table.querySelectorAll('td').forEach(td => {
128
- td.style.padding = '0.5rem 0.75rem';
129
- td.style.borderBottom = '1px solid #e5e7eb';
130
- });
131
57
  });
132
58
 
133
59
  // Remove any blue highlights
@@ -227,10 +153,11 @@ document.addEventListener('DOMContentLoaded', (event) => {
227
153
 
228
154
  window.updateActiveChapter = () => {
229
155
  try {
230
- // Only target H2 headings
231
- const headings = Array.from(document.querySelectorAll('h2'));
156
+ // Target H2 and H3 headings for navigation
157
+ const allHeadings = Array.from(document.querySelectorAll('h2, h3'));
158
+ const h2Headings = Array.from(document.querySelectorAll('h2'));
232
159
 
233
- if (!headings.length || !chapterNavItems.length) return;
160
+ if (!allHeadings.length || !chapterNavItems.length) return;
234
161
 
235
162
  // Calculate which heading is most visible in the viewport
236
163
  const viewportHeight = window.innerHeight;
@@ -239,7 +166,7 @@ document.addEventListener('DOMContentLoaded', (event) => {
239
166
  let bestVisibleHeading = null;
240
167
  let bestVisibleScore = -1;
241
168
 
242
- headings.forEach(heading => {
169
+ allHeadings.forEach(heading => {
243
170
  if (!heading) return;
244
171
 
245
172
  const rect = heading.getBoundingClientRect();
@@ -261,10 +188,29 @@ document.addEventListener('DOMContentLoaded', (event) => {
261
188
  }
262
189
  });
263
190
 
191
+ // Find the parent H2 for the current heading (if it's an H3)
192
+ const findParentH2 = (heading) => {
193
+ if (!heading || heading.tagName === 'H2') return heading;
194
+
195
+ // Walk backwards through all headings to find the parent H2
196
+ const headingIndex = allHeadings.indexOf(heading);
197
+ for (let i = headingIndex - 1; i >= 0; i--) {
198
+ if (allHeadings[i].tagName === 'H2') {
199
+ return allHeadings[i];
200
+ }
201
+ }
202
+ return null;
203
+ };
204
+
264
205
  if (bestVisibleHeading) {
265
206
  const headingId = bestVisibleHeading.getAttribute('id');
266
207
  const headingText = bestVisibleHeading.textContent.trim();
267
208
 
209
+ // Get parent H2 if current heading is H3
210
+ const parentH2 = findParentH2(bestVisibleHeading);
211
+ const parentH2Text = parentH2 ? parentH2.textContent.trim() : null;
212
+ const parentH2Id = parentH2 ? parentH2.getAttribute('id') : null;
213
+
268
214
  chapterNavItems.forEach(item => {
269
215
  if (!item) return;
270
216
 
@@ -272,22 +218,30 @@ document.addEventListener('DOMContentLoaded', (event) => {
272
218
  if (!navLink) return;
273
219
 
274
220
  const itemText = navLink.textContent.trim();
275
- const isActive = itemText === headingText || item.getAttribute('data-chapter-id') === headingId;
221
+ const itemId = item.getAttribute('data-chapter-id');
222
+
223
+ // Check if this nav item matches the current heading OR its parent H2
224
+ const isCurrentHeading = itemText === headingText || itemId === headingId;
225
+ const isParentSection = parentH2 && (itemText === parentH2Text || itemId === parentH2Id);
226
+ const isActive = isCurrentHeading || isParentSection;
276
227
 
277
228
  if (isActive) {
278
229
  item.classList.add('active');
279
230
 
280
- const navContainer = document.querySelector('.chapter-navigation');
281
- if (navContainer) {
282
- const itemOffset = item.offsetTop;
283
- const navHeight = navContainer.offsetHeight;
284
- const itemHeight = item.offsetHeight;
231
+ // Only scroll to and update text for the most specific (current) heading
232
+ if (isCurrentHeading) {
233
+ const navContainer = document.querySelector('.chapter-navigation');
234
+ if (navContainer) {
235
+ const itemOffset = item.offsetTop;
236
+ const navHeight = navContainer.offsetHeight;
237
+ const itemHeight = item.offsetHeight;
238
+
239
+ navContainer.scrollTop = itemOffset - (navHeight / 2) + (itemHeight / 2);
240
+ }
285
241
 
286
- navContainer.scrollTop = itemOffset - (navHeight / 2) + (itemHeight / 2);
287
- }
288
-
289
- if (currentChapterText) {
290
- currentChapterText.textContent = itemText;
242
+ if (currentChapterText) {
243
+ currentChapterText.textContent = itemText;
244
+ }
291
245
  }
292
246
  } else {
293
247
  item.classList.remove('active');
@@ -302,7 +256,10 @@ document.addEventListener('DOMContentLoaded', (event) => {
302
256
  if (!link) return;
303
257
 
304
258
  const itemText = link.textContent.trim();
305
- const isActive = itemText === headingText || item.getAttribute('data-chapter-id') === headingId;
259
+ const itemId = item.getAttribute('data-chapter-id');
260
+ const isCurrentHeading = itemText === headingText || itemId === headingId;
261
+ const isParentSection = parentH2 && (itemText === parentH2Text || itemId === parentH2Id);
262
+ const isActive = isCurrentHeading || isParentSection;
306
263
 
307
264
  if (isActive) {
308
265
  item.classList.add('active');
@@ -310,8 +267,8 @@ document.addEventListener('DOMContentLoaded', (event) => {
310
267
  item.classList.remove('active');
311
268
  }
312
269
  });
313
- } else if (headings.length > 0) {
314
- const firstHeading = headings[0];
270
+ } else if (h2Headings.length > 0) {
271
+ const firstHeading = h2Headings[0];
315
272
  const firstHeadingText = firstHeading.textContent.trim();
316
273
 
317
274
  chapterNavItems.forEach(item => {
package/static/styles.css CHANGED
@@ -1,4 +1,10 @@
1
1
  @media print {
2
+ /* Remove all margins so browser print dialog controls page margins */
3
+ html, body {
4
+ margin: 0 !important;
5
+ padding: 0 !important;
6
+ }
7
+
2
8
  pre, pre code {
3
9
  white-space: pre-wrap !important;
4
10
  overflow-x: hidden !important;
@@ -27,11 +33,15 @@
27
33
  .content-container {
28
34
  max-width: 100% !important;
29
35
  width: 100% !important;
36
+ margin: 0 !important;
37
+ padding: 0 !important;
30
38
  }
31
39
 
32
- article {
40
+ article, main, section {
33
41
  width: 100% !important;
34
42
  max-width: 100% !important;
43
+ margin: 0 !important;
44
+ padding: 0 !important;
35
45
  }
36
46
  }
37
47
 
@@ -182,42 +192,41 @@ blockquote + p {
182
192
  margin-top: 2em;
183
193
  }
184
194
 
185
- /* Enhanced table styling with true full width enforcement */
195
+ /* Enhanced table styling - use auto layout for natural column sizing */
186
196
  .prose table,
187
197
  table.w-full,
188
198
  article table,
189
199
  div table,
190
200
  .table-responsive table {
191
- width: 100% !important;
192
- min-width: 100% !important;
193
- max-width: 100% !important;
194
- margin: 2em 0 !important;
195
- border-collapse: collapse !important;
196
- table-layout: fixed !important;
197
- display: table !important;
201
+ width: 100%;
202
+ margin: 2em 0;
203
+ border-collapse: collapse;
204
+ table-layout: auto;
205
+ display: table;
198
206
  }
199
207
 
200
208
  /* Table wrapper to handle overflow properly */
201
209
  .table-responsive,
202
210
  div.overflow-x-auto {
203
- width: 100% !important;
204
- min-width: 100% !important;
205
- max-width: 100% !important;
206
- overflow-x: auto !important;
207
- margin: 2em 0 !important;
208
- display: block !important;
211
+ width: 100%;
212
+ max-width: 100%;
213
+ overflow-x: auto;
214
+ margin: 2em 0;
215
+ display: block;
209
216
  }
210
217
 
211
218
  .prose table th, .prose table td {
212
- padding: 0.75em 1em !important;
213
- border: 1px solid rgba(0, 0, 0, 0.1) !important;
214
- text-align: left !important;
215
- vertical-align: top !important;
219
+ padding: 0.75em 1em;
220
+ border: 1px solid rgba(0, 0, 0, 0.1);
221
+ text-align: left;
222
+ vertical-align: top;
223
+ word-wrap: break-word;
224
+ overflow-wrap: break-word;
216
225
  }
217
226
 
218
227
  .prose table th {
219
- font-weight: 600 !important;
220
- background-color: rgba(0, 0, 0, 0.05) !important;
228
+ font-weight: 600;
229
+ background-color: rgba(0, 0, 0, 0.05);
221
230
  }
222
231
 
223
232
  /* Grid-based tables for special formatting needs */
@@ -709,48 +718,30 @@ main > svg,
709
718
  }
710
719
 
711
720
  /* ============================================
712
- SMART TABLE SIZING
721
+ TABLE STYLING
713
722
  ============================================ */
714
723
 
715
- /* Auto-sized tables - columns fit content */
716
- table.auto-size {
724
+ /* Tables use auto layout to fit content naturally */
725
+ table {
717
726
  table-layout: auto;
718
- width: auto;
719
- max-width: 100%;
720
- }
721
-
722
- /* Compact columns for short content (numbers, codes) */
723
- table .col-compact {
724
- width: 1%;
725
- white-space: nowrap;
726
- }
727
-
728
- /* Numeric columns - right aligned, compact */
729
- table .col-numeric {
730
- width: 1%;
731
- white-space: nowrap;
732
- text-align: right;
733
- font-variant-numeric: tabular-nums;
734
- }
735
-
736
- /* Currency columns */
737
- table .col-currency {
738
- width: 1%;
739
- white-space: nowrap;
740
- text-align: right;
741
- font-variant-numeric: tabular-nums;
727
+ width: 100%;
728
+ border-collapse: collapse;
742
729
  }
743
730
 
744
- /* Description/text columns - expand to fill */
745
- table .col-text {
746
- width: auto;
731
+ /* Ensure table cells don't overlap */
732
+ table th,
733
+ table td {
734
+ padding: 0.75em 1em;
735
+ border: 1px solid rgba(0, 0, 0, 0.1);
736
+ text-align: left;
737
+ vertical-align: top;
738
+ word-wrap: break-word;
739
+ overflow-wrap: break-word;
747
740
  }
748
741
 
749
- /* Shrink-to-fit table wrapper */
750
- .table-auto-wrapper {
751
- display: inline-block;
752
- max-width: 100%;
753
- overflow-x: auto;
742
+ table th {
743
+ font-weight: 600;
744
+ background-color: rgba(0, 0, 0, 0.05);
754
745
  }
755
746
 
756
747
  /* Print table sizing */