@tdocs/tdocs 1.0.2 → 1.0.4

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/build/compiler.js CHANGED
@@ -44,7 +44,13 @@ renderer.paragraph = function ({ tokens }) {
44
44
  const text = this.parser.parseInline(tokens);
45
45
  return applyAttributes(text, (t) => `<p>${t}</p>\n`);
46
46
  };
47
- marked_1.marked.use({ renderer });
47
+ renderer.br = function () {
48
+ return '</p>\n<p>';
49
+ };
50
+ marked_1.marked.use({
51
+ renderer,
52
+ breaks: true
53
+ });
48
54
  marked_1.marked.use((0, marked_highlight_1.markedHighlight)({
49
55
  langPrefix: 'hljs language-',
50
56
  highlight(code, lang) {
@@ -55,14 +61,83 @@ marked_1.marked.use((0, marked_highlight_1.markedHighlight)({
55
61
  // Phase 2: Markdown Compilation
56
62
  // This module will iterate through the AST and transform raw Markdown strings into valid HTML strings.
57
63
  function compileMarkdown(ast) {
64
+ // Regex to find things like {{ installation.html }} or { installation.html }
65
+ const injectionRegex = /\{\{?\s*([a-zA-Z0-9_.-]+)\s*\}?\}/g;
58
66
  // Recursively compile markdown content to html
59
67
  function compileNode(node) {
68
+ let generatedHtml = `<h1>${node.title}</h1>\n`;
60
69
  if (node.markdownContent) {
61
- node.htmlContent = marked_1.marked.parse(node.markdownContent);
62
- }
63
- else {
64
- node.htmlContent = '';
70
+ // 1. Strip base indentation from all lines so Marked doesn't treat the whole block as code
71
+ let lines = node.markdownContent.split('\n');
72
+ const nonEmptyLines = lines.filter(l => l.trim().length > 0);
73
+ if (nonEmptyLines.length > 0) {
74
+ const minIndent = Math.min(...nonEmptyLines.map(l => l.match(/^[ \t]*/)?.[0].length || 0));
75
+ if (minIndent > 0) {
76
+ lines = lines.map(line => line.trim().length > 0 ? line.substring(minIndent) : '');
77
+ }
78
+ }
79
+ // 2. Custom DSL Pre-processor
80
+ let listMode = 'none';
81
+ let listIndent = 0;
82
+ let olCounter = 1;
83
+ for (let i = 0; i < lines.length; i++) {
84
+ const line = lines[i];
85
+ const trimmed = line.trim();
86
+ const hMatch = line.match(/^([ \t]*)h([1-6]):[ \t]*(.*)$/);
87
+ if (hMatch) {
88
+ lines[i] = `${hMatch[1]}${'#'.repeat(parseInt(hMatch[2]))} ${hMatch[3].trimEnd()}`;
89
+ listMode = 'none';
90
+ continue;
91
+ }
92
+ if (trimmed === 'list:') {
93
+ listMode = 'ul';
94
+ listIndent = line.match(/^[ \t]*/)?.[0].length || 0;
95
+ lines[i] = '';
96
+ continue;
97
+ }
98
+ if (trimmed === 'numberlist:') {
99
+ listMode = 'ol';
100
+ listIndent = line.match(/^[ \t]*/)?.[0].length || 0;
101
+ olCounter = 1;
102
+ lines[i] = '';
103
+ continue;
104
+ }
105
+ if (listMode !== 'none') {
106
+ const currentIndent = line.match(/^[ \t]*/)?.[0].length || 0;
107
+ if (trimmed === '') {
108
+ // Ignore empty lines
109
+ }
110
+ else if (currentIndent <= listIndent) {
111
+ listMode = 'none'; // Exit list mode on dedent
112
+ // Need to process this line normally now since it's not a list item
113
+ const hMatch = line.match(/^([ \t]*)h([1-6]):[ \t]*(.*)$/);
114
+ if (hMatch) {
115
+ lines[i] = `${hMatch[1]}${'#'.repeat(parseInt(hMatch[2]))} ${hMatch[3].trimEnd()}`;
116
+ }
117
+ }
118
+ else {
119
+ const relativeIndent = Math.max(0, currentIndent - listIndent - 2);
120
+ const spaces = ' '.repeat(relativeIndent);
121
+ if (listMode === 'ul') {
122
+ lines[i] = `${spaces}- ${trimmed}`;
123
+ }
124
+ else if (listMode === 'ol') {
125
+ lines[i] = `${spaces}${olCounter++}. ${trimmed}`;
126
+ }
127
+ }
128
+ }
129
+ }
130
+ let processedContent = lines.join('\n');
131
+ // 3. Inject HTML Partials
132
+ processedContent = processedContent.replace(injectionRegex, (match, varName) => {
133
+ if (ast.partials && ast.partials[varName]) {
134
+ return ast.partials[varName];
135
+ }
136
+ return match; // Keep string intact if var not found
137
+ });
138
+ generatedHtml += marked_1.marked.parse(processedContent);
65
139
  }
140
+ node.htmlContent = generatedHtml;
66
141
  if (node.children && node.children.length > 0) {
67
142
  node.children.forEach(compileNode);
68
143
  }
package/build/parser.js CHANGED
@@ -5,16 +5,36 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.parseTDoc = parseTDoc;
7
7
  const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
8
9
  // The config is simple key-value pairs, let's parse it manually.
9
10
  function parseTDoc(filePath) {
10
11
  const fileContent = fs_1.default.readFileSync(filePath, 'utf-8');
11
12
  let config = {};
13
+ let partials = {};
12
14
  let rawBody = fileContent;
13
- if (fileContent.startsWith('@config')) {
14
- const endConfigIdx = fileContent.indexOf('---');
15
+ // 1. Extract Imports (Custom HTML Snippets)
16
+ const importRegex = /^import\s+([^\s]+)\s+from\s+['"]([^'"]+)['"]/gm;
17
+ let match;
18
+ while ((match = importRegex.exec(fileContent)) !== null) {
19
+ const variableName = match[1];
20
+ const relativePath = match[2];
21
+ const tdocDirectory = path_1.default.dirname(filePath);
22
+ const absolutePath = path_1.default.resolve(tdocDirectory, relativePath);
23
+ if (fs_1.default.existsSync(absolutePath)) {
24
+ partials[variableName] = fs_1.default.readFileSync(absolutePath, 'utf-8');
25
+ }
26
+ else {
27
+ console.warn(`Warning: Could not find imported file ${absolutePath}`);
28
+ }
29
+ }
30
+ // Remove processed imports from rawBody
31
+ rawBody = rawBody.replace(importRegex, '').trim();
32
+ // 2. Parse Config Frontmatter
33
+ if (rawBody.startsWith('@config')) {
34
+ const endConfigIdx = rawBody.indexOf('---');
15
35
  if (endConfigIdx !== -1) {
16
- const configBlock = fileContent.substring('@config'.length, endConfigIdx).trim();
17
- rawBody = fileContent.substring(endConfigIdx + 3).trim();
36
+ const configBlock = rawBody.substring('@config'.length, endConfigIdx).trim();
37
+ rawBody = rawBody.substring(endConfigIdx + 3).trim();
18
38
  // Parse key: value
19
39
  const configLines = configBlock.split('\n');
20
40
  for (const line of configLines) {
@@ -32,10 +52,11 @@ function parseTDoc(filePath) {
32
52
  }
33
53
  }
34
54
  }
35
- // 2. Parse structural markers and markdown content
55
+ // 3. Parse structural markers and markdown content
36
56
  const tree = buildTree(rawBody);
37
57
  return {
38
58
  config,
59
+ partials,
39
60
  tree
40
61
  };
41
62
  }
@@ -51,7 +72,7 @@ function buildTree(content) {
51
72
  if (match) {
52
73
  // It's a structural marker
53
74
  const indentStr = match[1];
54
- const title = match[2];
75
+ const title = match[2].trimEnd();
55
76
  const level = indentStr.length;
56
77
  const newNode = {
57
78
  id: title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, ''),
@@ -90,7 +111,7 @@ function buildTree(content) {
90
111
  // Clean up trailing whitespace on markdown content
91
112
  const cleanMarkdown = (nodes) => {
92
113
  for (const node of nodes) {
93
- node.markdownContent = node.markdownContent.trim();
114
+ node.markdownContent = node.markdownContent.trimEnd();
94
115
  cleanMarkdown(node.children);
95
116
  }
96
117
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tdocs/tdocs",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Custom Static Site Generator for rapid documentation prototyping",
5
5
  "main": "./build/index.js",
6
6
  "bin": {
@@ -67,7 +67,7 @@
67
67
 
68
68
  // Render the first item by default if exists
69
69
  if (tree.length > 0) {
70
- sidebar.firstChild.click();
70
+ sidebar.firstElementChild.click();
71
71
  }
72
72
  </script>
73
73
  </body>
@@ -1,14 +1,32 @@
1
+ @import url('https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500&family=Poppins:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400&display=swap');
2
+
1
3
  :root {
2
- --bg-color: #ffffff;
3
- --text-color: #333333;
4
- --sidebar-bg: #f5f5f5;
5
- --header-bg: #1a1a1a;
6
- --header-text: #ffffff;
7
- --accent-color: #0066cc;
4
+ --bg-color: #0d0d10;
5
+ --bg-surface: #141418;
6
+ --bg-elevated: #1a1a20;
7
+ --bg-hover: #1e1e26;
8
+ --text-color: #e8e8f0;
9
+ --text-muted: #888899;
10
+ --text-subtle: #555566;
11
+ --sidebar-bg: #111115;
12
+ --header-bg: #0a0a0d;
13
+ --header-text: #f0f0f8;
14
+ --accent-color: #7c6af7;
15
+ --accent-glow: rgba(124, 106, 247, 0.15);
16
+ --accent-dim: rgba(124, 106, 247, 0.4);
17
+ --border-color: rgba(255,255,255,0.06);
18
+ --border-active: rgba(124, 106, 247, 0.5);
19
+ --code-bg: #0f0f14;
20
+ --radius: 8px;
21
+ --radius-sm: 5px;
22
+ }
23
+
24
+ * {
25
+ box-sizing: border-box;
8
26
  }
9
27
 
10
28
  .tdoc-body {
11
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
29
+ font-family: 'Poppins', sans-serif;
12
30
  margin: 0;
13
31
  padding: 0;
14
32
  background-color: var(--bg-color);
@@ -16,64 +34,314 @@
16
34
  display: flex;
17
35
  flex-direction: column;
18
36
  min-height: 100vh;
37
+ font-size: 15px;
38
+ line-height: 1.7;
39
+ -webkit-font-smoothing: antialiased;
19
40
  }
20
41
 
42
+ /* ── Header ── */
21
43
  .tdoc-header {
22
44
  background-color: var(--header-bg);
23
45
  color: var(--header-text);
24
- padding: 1rem 2rem;
46
+ padding: 0 2rem;
47
+ height: 56px;
48
+ display: flex;
49
+ align-items: center;
50
+ border-bottom: 1px solid var(--border-color);
51
+ position: sticky;
52
+ top: 0;
53
+ z-index: 100;
54
+ backdrop-filter: blur(12px);
25
55
  }
26
56
 
27
57
  .tdoc-site-title {
28
58
  margin: 0;
59
+ font-family: 'Poppins', sans-serif;
60
+ font-weight: 600;
61
+ font-style: bold;
62
+ font-size: 1.3rem;
63
+ letter-spacing: 0.01em;
64
+ color: var(--header-text);
65
+ display: flex;
66
+ align-items: center;
67
+ gap: 0.5rem;
29
68
  }
30
69
 
70
+ .tdoc-site-title::before {
71
+ content: '';
72
+ display: inline-block;
73
+ width: 8px;
74
+ height: 8px;
75
+ border-radius: 50%;
76
+ background: var(--accent-color);
77
+ box-shadow: 0 0 10px var(--accent-color);
78
+ flex-shrink: 0;
79
+ }
80
+
81
+ /* ── Layout ── */
31
82
  .tdoc-layout {
32
83
  display: flex;
33
84
  flex: 1;
34
85
  }
35
86
 
87
+ /* ── Sidebar ── */
36
88
  .tdoc-sidebar {
37
- width: 250px;
89
+ width: 260px;
90
+ flex-shrink: 0;
38
91
  background-color: var(--sidebar-bg);
39
- padding: 2rem 1rem;
40
- border-right: 1px solid #e0e0e0;
92
+ padding: 1.5rem 0.75rem;
93
+ border-right: 1px solid var(--border-color);
94
+ position: sticky;
95
+ top: 56px;
96
+ height: calc(100vh - 56px);
97
+ overflow-y: auto;
98
+ scrollbar-width: thin;
99
+ scrollbar-color: var(--text-subtle) transparent;
41
100
  }
42
101
 
102
+ .tdoc-sidebar::-webkit-scrollbar {
103
+ width: 4px;
104
+ }
105
+ .tdoc-sidebar::-webkit-scrollbar-track {
106
+ background: transparent;
107
+ }
108
+ .tdoc-sidebar::-webkit-scrollbar-thumb {
109
+ background: var(--text-subtle);
110
+ border-radius: 4px;
111
+ }
112
+
113
+ /* ── Nav Items ── */
43
114
  .tdoc-nav-item {
44
115
  margin-left: var(--tdoc-indent, 0px);
45
- padding: 5px 0;
116
+ padding: 6px 10px;
46
117
  cursor: pointer;
47
- color: var(--accent-color);
118
+ color: var(--text-muted);
119
+ font-size: 0.84rem;
120
+ font-weight: 400;
121
+ border-radius: var(--radius-sm);
122
+ transition: color 0.15s ease, background 0.15s ease;
123
+ position: relative;
124
+ letter-spacing: 0.01em;
125
+ line-height: 1.4;
126
+ margin-bottom: 1px;
127
+ }
128
+
129
+ .tdoc-nav-item[data-level="0"] {
130
+ font-size: 0.8rem;
131
+ font-weight: 600;
132
+ text-transform: uppercase;
133
+ letter-spacing: 0.08em;
134
+ color: var(--text-subtle);
135
+ margin-top: 1rem;
136
+ padding: 4px 10px;
137
+ }
138
+
139
+ .tdoc-nav-item[data-level="0"]:first-child {
140
+ margin-top: 0;
141
+ }
142
+
143
+ .tdoc-nav-item[data-level="2"] {
144
+ font-size: 0.85rem;
145
+ font-weight: 400;
48
146
  }
49
147
 
50
148
  .tdoc-nav-item:hover {
51
- text-decoration: underline;
149
+ color: var(--text-color);
150
+ background: var(--bg-hover);
151
+ text-decoration: none;
52
152
  }
53
153
 
54
154
  .tdoc-nav-item.tdoc-active {
55
- font-weight: bold;
56
- color: var(--text-color);
155
+ color: var(--accent-color);
156
+ background: var(--accent-glow);
157
+ font-weight: 500;
57
158
  }
58
159
 
59
- .tdoc-nav-item.tdoc-has-children {
60
- /* Give users a target to add an accordion arrow or bolding if they want */
160
+ .tdoc-nav-item.tdoc-active::before {
161
+ content: '';
162
+ position: absolute;
163
+ left: 0;
164
+ top: 50%;
165
+ transform: translateY(-50%);
166
+ width: 3px;
167
+ height: 60%;
168
+ background: var(--accent-color);
169
+ border-radius: 0 2px 2px 0;
170
+ box-shadow: 0 0 8px var(--accent-color);
61
171
  }
62
172
 
173
+ /* ── Content Area ── */
63
174
  .tdoc-content {
64
175
  flex: 1;
65
- padding: 2rem 4rem;
66
- max-width: 900px;
176
+ padding: 3rem 4rem;
177
+ max-width: 820px;
178
+ animation: contentFade 0.2s ease;
179
+ }
180
+
181
+ @keyframes contentFade {
182
+ from { opacity: 0; transform: translateY(6px); }
183
+ to { opacity: 1; transform: translateY(0); }
184
+ }
185
+
186
+ /* ── Markdown Typography ── */
187
+ .tdoc-content h1 {
188
+ font-family: 'Poppins', sans-serif;
189
+ font-weight: 400;
190
+ font-size: 2.2rem;
191
+ line-height: 1.2;
192
+ color: var(--text-color);
193
+ margin: 0 0 1rem 0;
194
+ letter-spacing: -0.02em;
195
+ border-bottom: 1px solid var(--border-color);
196
+ padding-bottom: 0.75rem;
67
197
  }
68
198
 
199
+ .tdoc-content h2 {
200
+ font-family: 'Poppins', sans-serif;
201
+ font-weight: 600;
202
+ font-size: 1.35rem;
203
+ color: var(--text-color);
204
+ margin: 2.5rem 0 0.75rem;
205
+ letter-spacing: -0.01em;
206
+ }
207
+
208
+ .tdoc-content h3 {
209
+ font-weight: 600;
210
+ font-size: 1.1rem;
211
+ color: var(--text-color);
212
+ margin: 2rem 0 0.5rem;
213
+ }
214
+
215
+ .tdoc-content p {
216
+ color: #c0c0d0;
217
+ margin: 0 0 1.2rem;
218
+ }
219
+
220
+ .tdoc-content a {
221
+ color: var(--accent-color);
222
+ text-decoration: none;
223
+ border-bottom: 1px solid var(--accent-dim);
224
+ transition: border-color 0.15s, color 0.15s;
225
+ }
226
+
227
+ .tdoc-content a:hover {
228
+ color: #a090ff;
229
+ border-bottom-color: #a090ff;
230
+ }
231
+
232
+ .tdoc-content ol,
233
+ .tdoc-content ul {
234
+ padding-left: 1.5rem;
235
+ color: #c0c0d0;
236
+ margin: 0 0 1.2rem;
237
+ }
238
+
239
+ .tdoc-content li {
240
+ margin-bottom: 0.4rem;
241
+ }
242
+
243
+ .tdoc-content code {
244
+ font-family: 'Geist Mono', monospace;
245
+ font-size: 0.83em;
246
+ background: var(--code-bg);
247
+ color: #b4a8ff;
248
+ padding: 0.15em 0.45em;
249
+ border-radius: 4px;
250
+ border: 1px solid rgba(124, 106, 247, 0.2);
251
+ }
252
+
253
+ .tdoc-content pre {
254
+ background: var(--code-bg);
255
+ border: 1px solid var(--border-color);
256
+ border-radius: var(--radius);
257
+ padding: 1.25rem 1.5rem;
258
+ overflow-x: auto;
259
+ margin: 1.5rem 0;
260
+ }
261
+
262
+ .tdoc-content pre code {
263
+ background: none;
264
+ border: none;
265
+ padding: 0;
266
+ font-size: 0.88rem;
267
+ color: #d0d0e8;
268
+ }
269
+
270
+ .tdoc-content blockquote {
271
+ border-left: 3px solid var(--accent-color);
272
+ background: var(--accent-glow);
273
+ margin: 1.5rem 0;
274
+ padding: 0.75rem 1.25rem;
275
+ border-radius: 0 var(--radius) var(--radius) 0;
276
+ color: #b0b0cc;
277
+ }
278
+
279
+ .tdoc-content table {
280
+ width: 100%;
281
+ border-collapse: collapse;
282
+ margin: 1.5rem 0;
283
+ font-size: 0.9rem;
284
+ }
285
+
286
+ .tdoc-content th {
287
+ text-align: left;
288
+ padding: 0.6rem 1rem;
289
+ background: var(--bg-elevated);
290
+ color: var(--text-muted);
291
+ font-weight: 600;
292
+ font-size: 0.8rem;
293
+ text-transform: uppercase;
294
+ letter-spacing: 0.06em;
295
+ border-bottom: 1px solid var(--border-color);
296
+ }
297
+
298
+ .tdoc-content td {
299
+ padding: 0.6rem 1rem;
300
+ border-bottom: 1px solid var(--border-color);
301
+ color: #c0c0d0;
302
+ }
303
+
304
+ .tdoc-content tr:last-child td {
305
+ border-bottom: none;
306
+ }
307
+
308
+ /* ── Alert / Warning Box ── */
309
+ .text-red {
310
+ color: #ff6b6b;
311
+ }
312
+
313
+ .alert-box {
314
+ border: 1px solid rgba(255, 107, 107, 0.35);
315
+ background: rgba(255, 107, 107, 0.07);
316
+ padding: 0.9rem 1.25rem;
317
+ margin: 1.5rem 0;
318
+ border-radius: var(--radius);
319
+ font-size: 0.9rem;
320
+ font-family: 'Poppins', sans-serif;
321
+ font-weight: 500;
322
+ letter-spacing: 0;
323
+ display: flex;
324
+ align-items: center;
325
+ gap: 0.6rem;
326
+ }
327
+
328
+ .alert-box::before {
329
+ content: '⚠';
330
+ font-size: 1rem;
331
+ color: #ff6b6b;
332
+ }
333
+
334
+ /* ── Footer ── */
69
335
  .tdoc-footer {
70
336
  padding: 1rem 2rem;
71
337
  text-align: center;
72
- border-top: 1px solid #e0e0e0;
73
- font-size: 0.9rem;
74
- color: #666;
338
+ border-top: 1px solid var(--border-color);
339
+ font-size: 0.8rem;
340
+ background: var(--header-bg);
75
341
  }
76
342
 
77
343
  .tdoc-footer-text {
78
344
  margin: 0;
79
- }
345
+ color: var(--text-subtle);
346
+ letter-spacing: 0.03em;
347
+ }