@docmd/live 0.4.0 → 0.4.3

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,26 +1,52 @@
1
1
  {
2
2
  "name": "@docmd/live",
3
- "version": "0.4.0",
3
+ "version": "0.4.3",
4
4
  "description": "Browser-based editor engine for docmd",
5
5
  "bin": {
6
- "docmd-live": "./bin/docmd-live.js"
6
+ "docmd-live": "bin/docmd-live.js"
7
+ },
8
+ "scripts": {
9
+ "build": "node -e \"require('./src/build.js').build()\""
7
10
  },
8
11
  "files": [
9
12
  "bin",
13
+ "src",
10
14
  "dist",
11
15
  "index.js",
12
16
  "src/build.js"
13
17
  ],
14
18
  "dependencies": {
15
- "esbuild": "^0.20.0",
19
+ "esbuild": "^0.27.3",
16
20
  "buffer": "^6.0.3",
17
21
  "serve": "^14.2.1",
18
- "@docmd/ui": "0.4.0",
19
- "@docmd/themes": "0.4.0",
20
- "@docmd/parser": "0.4.0"
22
+ "@docmd/parser": "^0.4.3",
23
+ "@docmd/ui": "^0.4.3",
24
+ "@docmd/themes": "^0.4.3"
21
25
  },
22
- "license": "MIT",
23
- "scripts": {
24
- "build": "node -e \"require('./src/build.js').build()\""
25
- }
26
+ "keywords": [
27
+ "docmd",
28
+ "editor",
29
+ "browser",
30
+ "live",
31
+ "parser",
32
+ "markdown",
33
+ "minimalist",
34
+ "zero-config",
35
+ "site-generator",
36
+ "docs"
37
+ ],
38
+ "author": {
39
+ "name": "Ghazi",
40
+ "url": "https://mgks.dev"
41
+ },
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/docmd-io/docmd.git"
45
+ },
46
+ "bugs": {
47
+ "url": "https://github.com/docmd-io/docmd/issues"
48
+ },
49
+ "homepage": "https://docmd.io",
50
+ "funding": "https://github.com/sponsors/mgks",
51
+ "license": "MIT"
26
52
  }
@@ -0,0 +1,102 @@
1
+ const { createMarkdownProcessor, processContent } = require('@docmd/parser/src/markdown-processor');
2
+ const { renderTemplate } = require('@docmd/parser/src/html-renderer');
3
+ const templates = require('virtual:docmd-templates');
4
+
5
+ // Expose the compile function to the window.docmd global
6
+ function compile(markdown, config = {}) {
7
+ const defaults = {
8
+ siteTitle: 'Live Preview',
9
+ theme: { defaultMode: 'light', name: 'default', codeHighlight: true },
10
+ ...config
11
+ };
12
+
13
+ // 1. Process Markdown
14
+ const md = createMarkdownProcessor(defaults);
15
+ const result = processContent(markdown, md, defaults);
16
+
17
+ if (!result) return '<p>Error parsing markdown</p>';
18
+
19
+ // Since we are in the browser, we assume assets are served at ./assets/
20
+ const assetsRoot = './assets';
21
+
22
+ // 1. CSS Injection
23
+ const cssTags = [
24
+ `<link rel="stylesheet" href="${assetsRoot}/css/docmd-main.css">`
25
+ ];
26
+
27
+ if (defaults.theme.codeHighlight !== false) {
28
+ const mode = defaults.theme.defaultMode === 'dark' ? 'dark' : 'light';
29
+ cssTags.push(`<link rel="stylesheet" href="${assetsRoot}/css/docmd-highlight-${mode}.css">`);
30
+ }
31
+
32
+ if (defaults.theme.name && defaults.theme.name !== 'default') {
33
+ cssTags.push(`<link rel="stylesheet" href="${assetsRoot}/css/docmd-theme-${defaults.theme.name}.css">`);
34
+ }
35
+
36
+ cssTags.push(`<link rel="stylesheet" href="${assetsRoot}/css/docmd-live-preview.css">`);
37
+
38
+ // 2. JS Injection
39
+ const jsTags = [
40
+ `<script src="${assetsRoot}/js/docmd-main.js"></script>`
41
+ ];
42
+
43
+ // 3. Theme Init Script
44
+ let themeInitScript = '';
45
+ if (templates['partials/theme-init.js']) {
46
+ themeInitScript = `<script>${templates['partials/theme-init.js']}</script>`;
47
+ }
48
+
49
+ // 4. Prepare Data
50
+ const pageData = {
51
+ content: result.htmlContent,
52
+ frontmatter: result.frontmatter,
53
+ headings: result.headings,
54
+ config: defaults,
55
+ buildHash: 'live',
56
+ siteTitle: defaults.siteTitle,
57
+ pageTitle: result.frontmatter.title || 'Untitled',
58
+ description: result.frontmatter.description || '',
59
+ defaultMode: defaults.theme.defaultMode,
60
+
61
+ // Navigation Stub
62
+ navigationHtml: '',
63
+ relativePathToRoot: './',
64
+ outputPath: 'index.html',
65
+ currentPagePath: '/index',
66
+ prevPage: null,
67
+ nextPage: null,
68
+
69
+ // Inject the constructed assets
70
+ pluginHeadScriptsHtml: cssTags.join('\n'),
71
+ pluginBodyScriptsHtml: jsTags.join('\n'),
72
+ themeInitScript: themeInitScript,
73
+
74
+ // Helpers
75
+ faviconLinkHtml: '',
76
+ logo: defaults.logo,
77
+ sidebarConfig: { collapsible: false },
78
+ theme: defaults.theme,
79
+ customCssFiles: [], customJsFiles: [],
80
+ sponsor: {}, footer: '', footerHtml: '',
81
+ isActivePage: true,
82
+ editUrl: null, editLinkText: ''
83
+ };
84
+
85
+ // 5. Render
86
+ const templateName = result.frontmatter.noStyle ? 'no-style.ejs' : 'layout.ejs';
87
+ const templateStr = templates[templateName];
88
+
89
+ if (!templateStr) return `Template ${templateName} not found`;
90
+
91
+ const options = {
92
+ includer: (originalPath) => {
93
+ let name = originalPath.endsWith('.ejs') ? originalPath : originalPath + '.ejs';
94
+ if (templates[name]) return { template: templates[name] };
95
+ return null;
96
+ }
97
+ };
98
+
99
+ return renderTemplate(templateStr, pageData, options);
100
+ }
101
+
102
+ module.exports = { compile };
@@ -0,0 +1,33 @@
1
+ /*
2
+ * Styles specific to the Live Editor Preview Iframe
3
+ */
4
+
5
+ /* Always hide TOC and Footer Actions in preview to save space */
6
+ .toc-sidebar,
7
+ .page-footer-actions {
8
+ display: none !important;
9
+ }
10
+
11
+ /* Ensure the main content takes full width since TOC is gone */
12
+ .content-layout {
13
+ display: block !important;
14
+ }
15
+
16
+ /* Hide Sidebar specifically on mobile in the preview */
17
+ /* (In Split View on mobile, space is very limited) */
18
+ @media (max-width: 768px) {
19
+ .sidebar {
20
+ display: none !important;
21
+ }
22
+
23
+ .main-content-wrapper {
24
+ margin-left: 0 !important;
25
+ }
26
+ }
27
+
28
+ /* Adjust header to look better without sidebar on mobile */
29
+ @media (max-width: 768px) {
30
+ .sidebar-toggle-button {
31
+ display: none !important;
32
+ }
33
+ }
@@ -0,0 +1,275 @@
1
+ /* Source file from the docmd project — https://github.com/docmd-io/docmd */
2
+
3
+ :root {
4
+ --header-height: 50px;
5
+ --border-color: #e0e0e0;
6
+ --bg-color: #f9fafb;
7
+ --primary-color: #007bff;
8
+ --resizer-width: 8px
9
+ }
10
+
11
+ body {
12
+ margin: 0;
13
+ height: 100vh;
14
+ display: flex;
15
+ flex-direction: column;
16
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
17
+ overflow: hidden
18
+ }
19
+
20
+ .top-bar {
21
+ height: var(--header-height);
22
+ background: #fff;
23
+ border-bottom: 1px solid var(--border-color);
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: space-between;
27
+ padding: 0 1rem;
28
+ flex-shrink: 0
29
+ }
30
+
31
+ .logo {
32
+ font-weight: 700;
33
+ font-size: 1.1rem;
34
+ display: flex;
35
+ align-items: center;
36
+ gap: 12px
37
+ }
38
+
39
+ .logo span {
40
+ background: var(--primary-color);
41
+ color: #fff;
42
+ padding: 2px 6px;
43
+ border-radius: 4px;
44
+ font-size: .75rem;
45
+ text-transform: uppercase
46
+ }
47
+
48
+ .back-link {
49
+ display: flex;
50
+ width: 24px;
51
+ height: 24px;
52
+ background-color: #f0f0f0;
53
+ border-radius: 100%;
54
+ align-items: center;
55
+ justify-content: center;
56
+ color: #666;
57
+ transition: color 0.2s, transform .2s;
58
+ text-decoration: none;
59
+ padding: 4px;
60
+ }
61
+
62
+ .back-link:hover {
63
+ color: var(--primary-color);
64
+ background: #f0f0f0;
65
+ transform: translateX(-2px)
66
+ }
67
+
68
+ .logo .docmd-logo {
69
+ color: inherit;
70
+ text-decoration: none;
71
+ transition: .5s;
72
+ }
73
+
74
+ .logo .docmd-logo:hover {
75
+ color: var(--primary-color);
76
+ }
77
+
78
+ .view-switcher {
79
+ display: flex;
80
+ background: #f0f0f0;
81
+ padding: 3px;
82
+ border-radius: 8px;
83
+ gap: 0;
84
+ }
85
+
86
+ .view-btn {
87
+ border: none;
88
+ background: transparent;
89
+ padding: 6px 12px;
90
+ border-radius: 6px;
91
+ cursor: pointer;
92
+ font-size: 0.85rem;
93
+ color: #666;
94
+ font-weight: 500;
95
+ transition: background 0.15s, color 0.15s;
96
+ }
97
+
98
+ .view-btn:hover {
99
+ color: #333;
100
+ }
101
+
102
+ .view-btn.active {
103
+ background: #fff;
104
+ color: #000;
105
+ box-shadow: 0 1px 3px #0000001a;
106
+ font-weight: 600;
107
+ }
108
+
109
+ .workspace {
110
+ flex: 1;
111
+ display: flex;
112
+ position: relative;
113
+ overflow: hidden
114
+ }
115
+
116
+ .pane {
117
+ height: 100%;
118
+ display: flex;
119
+ flex-direction: column;
120
+ min-width: 300px;
121
+ background: #fff
122
+ }
123
+
124
+ .pane-header {
125
+ padding: 8px 16px;
126
+ font-size: .75rem;
127
+ font-weight: 600;
128
+ text-transform: uppercase;
129
+ color: #888;
130
+ background: var(--bg-color);
131
+ border-bottom: 1px solid var(--border-color);
132
+ flex-shrink: 0
133
+ }
134
+
135
+ .editor-pane {
136
+ width: 50%
137
+ }
138
+
139
+ textarea#input {
140
+ flex: 1;
141
+ border: none;
142
+ resize: none;
143
+ padding: 20px;
144
+ font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
145
+ font-size: 14px;
146
+ line-height: 1.6;
147
+ outline: none;
148
+ background: var(--bg-color)
149
+ }
150
+
151
+ .preview-pane {
152
+ flex: 1;
153
+ background: #fff
154
+ }
155
+
156
+ iframe#preview {
157
+ width: 100%;
158
+ height: 100%;
159
+ border: none;
160
+ display: block
161
+ }
162
+
163
+ .resizer {
164
+ width: var(--resizer-width);
165
+ background: var(--bg-color);
166
+ border-left: 1px solid var(--border-color);
167
+ border-right: 1px solid var(--border-color);
168
+ cursor: col-resize;
169
+ display: flex;
170
+ align-items: center;
171
+ justify-content: center;
172
+ transition: background .2s;
173
+ z-index: 10
174
+ }
175
+
176
+ .resizer:hover,
177
+ .resizer.resizing {
178
+ background: #e0e0e0
179
+ }
180
+
181
+ .resizer::after {
182
+ content: "||";
183
+ color: #aaa;
184
+ font-size: 10px;
185
+ letter-spacing: 1px
186
+ }
187
+
188
+ body.mode-single .resizer {
189
+ display: none
190
+ }
191
+
192
+ body.mode-single .pane {
193
+ width: 100% !important;
194
+ min-width: 0
195
+ }
196
+
197
+ body.mode-single .editor-pane {
198
+ display: none
199
+ }
200
+
201
+ body.mode-single .preview-pane {
202
+ display: none
203
+ }
204
+
205
+ body.mode-single.show-editor .editor-pane {
206
+ display: flex
207
+ }
208
+
209
+ body.mode-single.show-preview .preview-pane {
210
+ display: flex
211
+ }
212
+
213
+ @media (max-width: 768px) {
214
+ .desktop-only {
215
+ display: none !important
216
+ }
217
+
218
+ .resizer {
219
+ display: none !important
220
+ }
221
+
222
+ .pane {
223
+ width: 100% !important
224
+ }
225
+
226
+ .editor-pane {
227
+ display: none
228
+ }
229
+
230
+ .preview-pane {
231
+ display: none
232
+ }
233
+
234
+ body.mobile-tab-editor .editor-pane {
235
+ display: flex
236
+ }
237
+
238
+ body.mobile-tab-preview .preview-pane {
239
+ display: flex
240
+ }
241
+
242
+ .mobile-tabs {
243
+ display: flex !important;
244
+ position: fixed;
245
+ bottom: 0;
246
+ left: 0;
247
+ right: 0;
248
+ height: 50px;
249
+ background: #fff;
250
+ border-top: 1px solid var(--border-color);
251
+ z-index: 100
252
+ }
253
+
254
+ .mobile-tab-btn {
255
+ flex: 1;
256
+ border: none;
257
+ background: transparent;
258
+ font-weight: 600;
259
+ color: #888;
260
+ cursor: pointer
261
+ }
262
+
263
+ .mobile-tab-btn.active {
264
+ color: var(--primary-color);
265
+ border-top: 2px solid var(--primary-color)
266
+ }
267
+
268
+ .workspace {
269
+ padding-bottom: 50px
270
+ }
271
+ }
272
+
273
+ .mobile-tabs {
274
+ display: none
275
+ }
package/src/index.html ADDED
@@ -0,0 +1,189 @@
1
+ <!-- Source file from the docmd project — https://github.com/docmd-io/docmd -->
2
+
3
+ <!DOCTYPE html>
4
+ <html lang="en">
5
+ <head>
6
+ <meta charset="UTF-8">
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
8
+ <title>Docmd Live Editor</title>
9
+ <meta name="description" content="Real-time Markdown preview and editor powered by docmd.">
10
+
11
+ <link rel="stylesheet" href="docmd-live.css">
12
+ <script src="docmd-live.js"></script>
13
+
14
+ <script async src="https://www.googletagmanager.com/gtag/js?id=G-VCMQ0MCSHN"></script>
15
+ <script>
16
+ window.dataLayer = window.dataLayer || [];
17
+ function gtag() { dataLayer.push(arguments); }
18
+ gtag('js', new Date());
19
+ gtag('config', 'G-VCMQ0MCSHN');
20
+ </script>
21
+ </head>
22
+
23
+ <body class="mode-split">
24
+
25
+ <!-- Top Bar -->
26
+ <div class="top-bar">
27
+ <div class="logo">
28
+ <!--<a href="/" class="back-link" id="back-btn" title="Previous Page" aria-label="Back to previous page">
29
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none"
30
+ stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
31
+ <path d="m12 19-7-7 7-7" />
32
+ <path d="M19 12H5" />
33
+ </svg>
34
+ </a>-->
35
+ <a href="https://docmd.io" class="docmd-logo" title="Back to home" aria-label="Back to homepage"><svg width="24" height="24" id="icon-feather" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20.24 12.24a6 6 0 0 0-8.49-8.49L5 10.5V19h8.5z"></path><line x1="16" y1="8" x2="2" y2="22"></line><line x1="17.5" y1="15" x2="9" y2="15"></line></svg></a> docmd <span>Live</span>
36
+ </div>
37
+
38
+ <!-- Unified 3-Way Switcher -->
39
+ <div class="view-switcher desktop-only">
40
+ <button class="view-btn active" onclick="setMode('split')" data-mode="split">Split</button>
41
+ <button class="view-btn" onclick="setMode('editor')" data-mode="editor">Editor</button>
42
+ <button class="view-btn" onclick="setMode('preview')" data-mode="preview">Preview</button>
43
+ </div>
44
+ </div>
45
+
46
+ <!-- Workspace -->
47
+ <div class="workspace" id="workspace">
48
+ <!-- Editor -->
49
+ <div class="pane editor-pane" id="editorPane">
50
+ <div class="pane-header">Markdown</div>
51
+ <textarea id="input" spellcheck="false">
52
+ ---
53
+ title: My Documentation
54
+ description: Start editing to see changes instantly.
55
+ ---
56
+
57
+ # Hello World
58
+
59
+ This is a **live** preview.
60
+
61
+ ::: callout tip
62
+ Try resizing the window or switching to mobile view!
63
+ :::
64
+
65
+ ## Features
66
+ 1. Responsive Design
67
+ 2. Split or Tabbed view
68
+ 3. Instant Rendering
69
+ </textarea>
70
+ </div>
71
+
72
+ <!-- Resizer Handle -->
73
+ <div class="resizer" id="resizer"></div>
74
+
75
+ <!-- Preview -->
76
+ <div class="pane preview-pane" id="previewPane">
77
+ <div class="pane-header">Preview</div>
78
+ <iframe id="preview"></iframe>
79
+ </div>
80
+ </div>
81
+
82
+ <!-- Mobile Bottom Tabs (Keep for small screens) -->
83
+ <div class="mobile-tabs">
84
+ <button class="mobile-tab-btn active" onclick="setMode('editor')" id="mob-edit">Editor</button>
85
+ <button class="mobile-tab-btn" onclick="setMode('preview')" id="mob-prev">Preview</button>
86
+ </div>
87
+
88
+ <script>
89
+ // --- Core Logic ---
90
+ const input = document.getElementById('input');
91
+ const preview = document.getElementById('preview');
92
+ const backBtn = document.getElementById('back-btn');
93
+
94
+ function render() {
95
+ try {
96
+ let html = docmd.compile(input.value, {
97
+ siteTitle: 'My Project',
98
+ search: false,
99
+ theme: { name: 'sky', defaultMode: 'light' },
100
+ sidebar: { collapsible: false }
101
+ });
102
+
103
+ // Injections
104
+ html = html.replace('<head>', '<head><base target="_blank">');
105
+ const customStyle = `
106
+ <style>
107
+ .sidebar-header { display: none !important; }
108
+ .sidebar-nav { margin-top: 1rem; }
109
+ </style>
110
+ `;
111
+ html = html.replace('</body>', `${customStyle}</body>`);
112
+
113
+ const doc = preview.contentWindow.document;
114
+ doc.open();
115
+ doc.write(html);
116
+ doc.close();
117
+ } catch (e) { console.error(e); }
118
+ }
119
+
120
+ let timer;
121
+ input.addEventListener('input', () => {
122
+ clearTimeout(timer);
123
+ timer = setTimeout(render, 300);
124
+ });
125
+ render();
126
+
127
+ // --- Resizer Logic ---
128
+ const resizer = document.getElementById('resizer');
129
+ const editorPane = document.getElementById('editorPane');
130
+ const workspace = document.getElementById('workspace');
131
+ let isResizing = false;
132
+
133
+ resizer.addEventListener('mousedown', (e) => {
134
+ isResizing = true;
135
+ resizer.classList.add('resizing');
136
+ preview.style.pointerEvents = 'none';
137
+ document.body.style.cursor = 'col-resize';
138
+ });
139
+
140
+ document.addEventListener('mousemove', (e) => {
141
+ if (!isResizing) return;
142
+ const containerWidth = workspace.offsetWidth;
143
+ const newEditorWidth = (e.clientX / containerWidth) * 100;
144
+ if (newEditorWidth > 15 && newEditorWidth < 85) {
145
+ editorPane.style.width = newEditorWidth + '%';
146
+ }
147
+ });
148
+
149
+ document.addEventListener('mouseup', () => {
150
+ if (isResizing) {
151
+ isResizing = false;
152
+ resizer.classList.remove('resizing');
153
+ preview.style.pointerEvents = 'auto';
154
+ document.body.style.cursor = 'default';
155
+ }
156
+ });
157
+
158
+ // --- Unified View Logic ---
159
+ function setMode(mode) {
160
+ // 1. Update UI Buttons (Desktop)
161
+ document.querySelectorAll('.view-switcher .view-btn').forEach(btn => {
162
+ btn.classList.toggle('active', btn.dataset.mode === mode);
163
+ });
164
+
165
+ // 2. Update Mobile Buttons
166
+ const mobMode = mode === 'split' ? 'editor' : mode; // Map split to editor on mobile
167
+ document.getElementById('mob-edit').classList.toggle('active', mobMode === 'editor');
168
+ document.getElementById('mob-prev').classList.toggle('active', mobMode === 'preview');
169
+
170
+ // 3. Update Layout Classes
171
+ // Reset
172
+ document.body.classList.remove('mode-split', 'mode-single', 'show-editor', 'show-preview');
173
+
174
+ if (mode === 'split') {
175
+ document.body.classList.add('mode-split');
176
+ } else {
177
+ document.body.classList.add('mode-single');
178
+ document.body.classList.add('show-' + mode);
179
+ }
180
+ }
181
+
182
+ // --- Back Button ---
183
+ /*
184
+ if (window.history.length <= 1) backBtn.style.display = 'none';
185
+ else backBtn.addEventListener('click', (e) => { e.preventDefault(); window.history.back(); });
186
+ */
187
+ </script>
188
+ </body>
189
+ </html>
package/src/shims.js ADDED
@@ -0,0 +1 @@
1
+ import { Buffer } from 'buffer'; globalThis.Buffer = Buffer;