mnfst 0.5.14

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.
Files changed (47) hide show
  1. package/LICENSE +11 -0
  2. package/README.md +58 -0
  3. package/dist/manifest.accordion.css +81 -0
  4. package/dist/manifest.appwrite.auth.js +6247 -0
  5. package/dist/manifest.appwrite.data.js +1586 -0
  6. package/dist/manifest.appwrite.presence.js +1845 -0
  7. package/dist/manifest.avatar.css +113 -0
  8. package/dist/manifest.button.css +79 -0
  9. package/dist/manifest.checkbox.css +58 -0
  10. package/dist/manifest.code.css +453 -0
  11. package/dist/manifest.code.js +958 -0
  12. package/dist/manifest.code.min.css +1 -0
  13. package/dist/manifest.components.js +737 -0
  14. package/dist/manifest.css +3124 -0
  15. package/dist/manifest.data.js +11413 -0
  16. package/dist/manifest.dialog.css +130 -0
  17. package/dist/manifest.divider.css +77 -0
  18. package/dist/manifest.dropdown.css +278 -0
  19. package/dist/manifest.dropdowns.js +378 -0
  20. package/dist/manifest.form.css +169 -0
  21. package/dist/manifest.icons.js +161 -0
  22. package/dist/manifest.input.css +129 -0
  23. package/dist/manifest.js +302 -0
  24. package/dist/manifest.localization.js +571 -0
  25. package/dist/manifest.markdown.js +738 -0
  26. package/dist/manifest.min.css +1 -0
  27. package/dist/manifest.radio.css +38 -0
  28. package/dist/manifest.resize.css +233 -0
  29. package/dist/manifest.resize.js +442 -0
  30. package/dist/manifest.router.js +1207 -0
  31. package/dist/manifest.sidebar.css +102 -0
  32. package/dist/manifest.slides.css +80 -0
  33. package/dist/manifest.slides.js +173 -0
  34. package/dist/manifest.switch.css +44 -0
  35. package/dist/manifest.table.css +74 -0
  36. package/dist/manifest.tabs.js +273 -0
  37. package/dist/manifest.tailwind.js +578 -0
  38. package/dist/manifest.theme.css +119 -0
  39. package/dist/manifest.themes.js +109 -0
  40. package/dist/manifest.toast.css +92 -0
  41. package/dist/manifest.toasts.js +285 -0
  42. package/dist/manifest.tooltip.css +156 -0
  43. package/dist/manifest.tooltips.js +331 -0
  44. package/dist/manifest.typography.css +341 -0
  45. package/dist/manifest.utilities.css +399 -0
  46. package/dist/manifest.utilities.js +3197 -0
  47. package/package.json +63 -0
@@ -0,0 +1,738 @@
1
+ /* Manifest Markdown */
2
+
3
+ // Cache for marked.js loading
4
+ let markedPromise = null;
5
+
6
+ // Cache for fetched markdown files to prevent duplicate requests
7
+ const markdownCache = new Map();
8
+
9
+ // Load marked.js from CDN
10
+ async function loadMarkedJS() {
11
+ if (typeof marked !== 'undefined') {
12
+ return marked;
13
+ }
14
+
15
+ // Return existing promise if already loading
16
+ if (markedPromise) {
17
+ return markedPromise;
18
+ }
19
+
20
+ markedPromise = new Promise((resolve, reject) => {
21
+ const script = document.createElement('script');
22
+ script.src = 'https://cdn.jsdelivr.net/npm/marked/marked.min.js';
23
+ script.onload = () => {
24
+ // Initialize marked.js
25
+ if (typeof marked !== 'undefined') {
26
+ resolve(marked);
27
+ } else {
28
+ console.error('[Manifest Markdown] Marked.js failed to load - marked is undefined');
29
+ markedPromise = null; // Reset so we can try again
30
+ reject(new Error('marked.js failed to load'));
31
+ }
32
+ };
33
+ script.onerror = (error) => {
34
+ console.error('[Manifest Markdown] Script failed to load:', error);
35
+ markedPromise = null; // Reset so we can try again
36
+ reject(error);
37
+ };
38
+ document.head.appendChild(script);
39
+ });
40
+
41
+ return markedPromise;
42
+ }
43
+
44
+ // Configure marked to preserve full language strings
45
+ async function configureMarked(marked) {
46
+ marked.use({
47
+ renderer: {
48
+ code(token) {
49
+ const lang = token.lang || '';
50
+ const text = token.text || '';
51
+ const escaped = token.escaped || false;
52
+
53
+ // Parse the language string to extract attributes
54
+ const attributes = parseLanguageString(lang);
55
+
56
+ // Build attributes for the x-code element
57
+ let xCodeAttributes = '';
58
+ if (attributes.title) {
59
+ xCodeAttributes += ` name="${attributes.title}"`;
60
+ }
61
+ if (attributes.language) {
62
+ xCodeAttributes += ` language="${attributes.language}"`;
63
+ }
64
+ if (attributes.numbers) {
65
+ xCodeAttributes += ' numbers';
66
+ }
67
+ if (attributes.copy) {
68
+ xCodeAttributes += ' copy';
69
+ }
70
+
71
+ // For x-code elements, use the raw text to preserve formatting
72
+ let code = text;
73
+ let preserveOriginal = '';
74
+
75
+ // For HTML language code blocks, preserve the original raw text to maintain indentation
76
+ if (attributes.language === 'html' || text.includes('<!DOCTYPE') || (text.includes('<html') && text.includes('<head') && text.includes('<body'))) {
77
+ // Store the original content in a data attribute to preserve indentation
78
+ preserveOriginal = ` data-original-content="${text.replace(/"/g, '&quot;')}"`;
79
+ }
80
+
81
+ // Always create an x-code element, with or without attributes
82
+ return `<x-code${xCodeAttributes}${preserveOriginal}>${code}</x-code>\n`;
83
+ }
84
+ },
85
+ // Configure marked to allow custom HTML tags
86
+ breaks: true,
87
+ gfm: true
88
+ });
89
+
90
+ // Add custom tokenizer for callout blocks
91
+ marked.use({
92
+ extensions: [{
93
+ name: 'callout',
94
+ level: 'block',
95
+ start(src) {
96
+ return src.match(/^:::/)?.index;
97
+ },
98
+ tokenizer(src) {
99
+ // Find the opening ::: and type
100
+ const openMatch = src.match(/^:::(.*?)(?:\n|$)/);
101
+ if (!openMatch) return;
102
+
103
+ // Parse the opening line for classes and icon
104
+ const openingLine = openMatch[1].trim();
105
+ let classes = '';
106
+ let iconValue = '';
107
+
108
+ // Match icon="value" pattern
109
+ const iconMatch = openingLine.match(/icon="([^"]+)"/);
110
+ if (iconMatch) {
111
+ iconValue = iconMatch[1];
112
+ }
113
+
114
+ // Get all class names (remove icon attribute first)
115
+ classes = openingLine.replace(/\s*icon="[^"]+"\s*/, '').trim();
116
+
117
+ const startPos = openMatch[0].length;
118
+
119
+ // Find the closing ::: from the remaining content
120
+ const remainingContent = src.slice(startPos);
121
+ const closeMatch = remainingContent.match(/\n:::/);
122
+
123
+ if (closeMatch) {
124
+ const content = remainingContent.slice(0, closeMatch.index);
125
+ const raw = openMatch[0] + content + closeMatch[0];
126
+
127
+ return {
128
+ type: 'callout',
129
+ raw: raw,
130
+ classes: classes,
131
+ iconValue: iconValue,
132
+ text: content.trim()
133
+ };
134
+ }
135
+ },
136
+ renderer(token) {
137
+ const classes = token.classes || '';
138
+ const iconValue = token.iconValue || '';
139
+
140
+ // For frame callouts, don't parse as markdown to avoid wrapping HTML in <p> tags
141
+ let parsedContent;
142
+ if (classes.includes('frame')) {
143
+ // Use raw content for frame callouts to preserve HTML structure
144
+ parsedContent = token.text;
145
+ } else {
146
+ // Parse the content as markdown to support nested markdown syntax
147
+ parsedContent = marked.parse(token.text);
148
+ }
149
+
150
+ const iconHtml = iconValue ? `<span x-icon="${iconValue}"></span>` : '';
151
+
152
+ // Create a temporary div to count top-level elements
153
+ const temp = document.createElement('div');
154
+ temp.innerHTML = parsedContent;
155
+ const elementCount = temp.children.length;
156
+
157
+ // Only wrap in a div if:
158
+ // 1. There are 2 or more elements AND
159
+ // 2. There's an icon (which needs the content to be wrapped as a sibling)
160
+ const needsWrapper = elementCount >= 2 && iconValue;
161
+ const wrappedContent = needsWrapper ?
162
+ `<div>${parsedContent}</div>` :
163
+ parsedContent;
164
+
165
+ return `<aside${classes ? ` class="${classes}"` : ''}>${iconHtml}${wrappedContent}</aside>\n`;
166
+ }
167
+ }]
168
+ });
169
+
170
+ // Configure marked to preserve custom HTML tags
171
+ marked.setOptions({
172
+ headerIds: false,
173
+ mangle: false
174
+ });
175
+ }
176
+
177
+ // Custom renderer for x-code-group to handle line breaks properly
178
+ function renderXCodeGroup(markdown) {
179
+ // Find x-code-group blocks and process them specially
180
+ const xCodeGroupRegex = /<x-code-group[^>]*>([\s\S]*?)<\/x-code-group>/g;
181
+
182
+ return markdown.replace(xCodeGroupRegex, (match, content) => {
183
+ // Ensure there's a line break after the opening tag if there isn't one
184
+ const processedContent = content.replace(/^(?!\s*\n)/, '\n');
185
+
186
+ return `<x-code-group>${processedContent}</x-code-group>`;
187
+ });
188
+ }
189
+
190
+ // Post-process HTML to enable checkboxes by removing disabled attribute
191
+ function enableCheckboxes(html) {
192
+ // Create a temporary DOM element to parse the HTML
193
+ const temp = document.createElement('div');
194
+ temp.innerHTML = html;
195
+
196
+ // Find all checkbox inputs and remove disabled attribute
197
+ const checkboxes = temp.querySelectorAll('input[type="checkbox"]');
198
+ checkboxes.forEach(checkbox => {
199
+ checkbox.removeAttribute('disabled');
200
+ });
201
+
202
+ return temp.innerHTML;
203
+ }
204
+
205
+ // Check if highlight.js is available
206
+ function isHighlightJsAvailable() {
207
+ return typeof window.hljs !== 'undefined';
208
+ }
209
+
210
+
211
+
212
+
213
+
214
+ // Parse language string to extract title and attributes
215
+ function parseLanguageString(languageString) {
216
+ if (!languageString || languageString.trim() === '') {
217
+ return { title: null, language: null, numbers: false, copy: false };
218
+ }
219
+
220
+ const parts = languageString.split(/\s+/);
221
+
222
+ const attributes = {
223
+ title: null,
224
+ language: null,
225
+ numbers: false,
226
+ copy: false
227
+ };
228
+
229
+ let i = 0;
230
+ while (i < parts.length) {
231
+ const part = parts[i];
232
+
233
+ // Check for attributes
234
+ if (part === 'numbers') {
235
+ attributes.numbers = true;
236
+ i++;
237
+ continue;
238
+ }
239
+
240
+ if (part === 'copy') {
241
+ attributes.copy = true;
242
+ i++;
243
+ continue;
244
+ }
245
+
246
+ // Check for quoted names (e.g., "Example")
247
+ if (part.startsWith('"') && part.endsWith('"')) {
248
+ // Single word quoted name
249
+ attributes.title = part.slice(1, -1);
250
+ i++;
251
+ continue;
252
+ } else if (part.startsWith('"')) {
253
+ // Multi-word quoted name
254
+ let fullName = part.slice(1);
255
+ i++;
256
+ while (i < parts.length) {
257
+ const nextPart = parts[i];
258
+ if (nextPart.endsWith('"')) {
259
+ fullName += ' ' + nextPart.slice(0, -1);
260
+ attributes.title = fullName;
261
+ i++;
262
+ break;
263
+ } else {
264
+ fullName += ' ' + nextPart;
265
+ i++;
266
+ }
267
+ }
268
+ continue;
269
+ }
270
+
271
+ // Store language identifiers (e.g., "css", "javascript", etc.)
272
+ // Use the first language identifier found
273
+ if (!attributes.language) {
274
+ attributes.language = part;
275
+ }
276
+ i++;
277
+ }
278
+
279
+ return attributes;
280
+ }
281
+
282
+ // Preload marked.js as soon as script loads
283
+ loadMarkedJS().catch(() => {
284
+ // Silently ignore errors during preload
285
+ });
286
+
287
+ // Initialize plugin when either DOM is ready or Alpine is ready
288
+ async function initializeMarkdownPlugin() {
289
+ try {
290
+ // Load marked.js
291
+ const marked = await loadMarkedJS();
292
+
293
+ // Configure marked with all our custom settings
294
+ await configureMarked(marked);
295
+
296
+ // Configure marked to generate heading IDs
297
+ marked.use({
298
+ renderer: {
299
+ heading(token) {
300
+ // Extract text and level from the token
301
+ const text = token.text || '';
302
+ const level = token.depth || 1;
303
+ const escapedText = text.toLowerCase().replace(/[^\w]+/g, '-').replace(/^-+|-+$/g, '');
304
+ return `<h${level} id="${escapedText}">${text}</h${level}>`;
305
+ }
306
+ }
307
+ });
308
+
309
+ // Check if there are any elements with x-markdown already on the page
310
+ const existingMarkdownElements = document.querySelectorAll('[x-markdown]');
311
+
312
+ // Register markdown directive
313
+ Alpine.directive('markdown', (el, { expression, modifiers }, { effect, evaluateLater }) => {
314
+
315
+ // Handle null/undefined expressions gracefully
316
+ if (!expression) {
317
+ return;
318
+ }
319
+
320
+ // Hide element initially to prevent flicker
321
+ el.style.opacity = '0';
322
+ el.style.transition = 'opacity 0.15s ease-in-out';
323
+
324
+ // Store original markdown content
325
+ let markdownSource = '';
326
+ let isUpdating = false;
327
+ let hasContent = false;
328
+
329
+ const normalizeContent = (content) => {
330
+ const lines = content.split('\n');
331
+ const commonIndent = lines
332
+ .filter(line => line.trim())
333
+ .reduce((min, line) => {
334
+ const indent = line.match(/^\s*/)[0].length;
335
+ return Math.min(min, indent);
336
+ }, Infinity);
337
+
338
+ return lines
339
+ .map(line => line.slice(commonIndent))
340
+ .join('\n')
341
+ .trim();
342
+ };
343
+
344
+ const updateContent = async (element, newContent = null) => {
345
+ if (isUpdating) return;
346
+ isUpdating = true;
347
+
348
+ try {
349
+ // Update source if new content provided
350
+ if (newContent !== null && newContent.trim() !== '') {
351
+ markdownSource = normalizeContent(newContent);
352
+ }
353
+
354
+ // Skip if no content
355
+ if (!markdownSource || markdownSource.trim() === '') {
356
+ element.style.opacity = '0';
357
+ return;
358
+ }
359
+
360
+ // Load marked.js and parse markdown
361
+ const marked = await loadMarkedJS();
362
+ const processedMarkdown = renderXCodeGroup(markdownSource);
363
+ let html = marked.parse(processedMarkdown);
364
+
365
+ // Post-process HTML to enable checkboxes (remove disabled attribute)
366
+ html = enableCheckboxes(html);
367
+
368
+ // Only update if content has changed and isn't empty
369
+ if (element.innerHTML !== html && html.trim() !== '') {
370
+ // Create a temporary container to hold the HTML
371
+ const temp = document.createElement('div');
372
+ temp.innerHTML = html;
373
+
374
+ // Replace the content
375
+ element.innerHTML = '';
376
+ while (temp.firstChild) {
377
+ element.appendChild(temp.firstChild);
378
+ }
379
+
380
+ // Show element with content
381
+ hasContent = true;
382
+ element.style.opacity = '1';
383
+ } else if (!hasContent) {
384
+ // Keep hidden if no valid content
385
+ element.style.opacity = '0';
386
+ }
387
+ } finally {
388
+ isUpdating = false;
389
+ }
390
+ };
391
+
392
+ // Handle inline markdown content (no expression or 'inline')
393
+ if (!expression || expression === 'inline') {
394
+ // Initial parse
395
+ markdownSource = normalizeContent(el.textContent);
396
+ updateContent(el);
397
+
398
+ // Set up mutation observer for streaming content
399
+ const observer = new MutationObserver((mutations) => {
400
+ let newContent = null;
401
+
402
+ for (const mutation of mutations) {
403
+ if (mutation.type === 'childList') {
404
+ const textNodes = Array.from(el.childNodes)
405
+ .filter(node => node.nodeType === Node.TEXT_NODE);
406
+ if (textNodes.length > 0) {
407
+ newContent = textNodes.map(node => node.textContent).join('');
408
+ break;
409
+ }
410
+ } else if (mutation.type === 'characterData') {
411
+ newContent = mutation.target.textContent;
412
+ break;
413
+ }
414
+ }
415
+
416
+ if (newContent && newContent.trim() !== '') {
417
+ updateContent(el, newContent);
418
+ }
419
+ });
420
+
421
+ observer.observe(el, {
422
+ characterData: true,
423
+ childList: true,
424
+ subtree: true,
425
+ characterDataOldValue: true
426
+ });
427
+
428
+ return;
429
+ }
430
+
431
+ // Handle expressions (file paths, inline strings, content references)
432
+ // Check if this is a simple string literal that needs to be quoted
433
+ let processedExpression = expression;
434
+ if (!expression.includes('+') && !expression.includes('`') && !expression.includes('${') &&
435
+ !expression.startsWith('$') && !expression.startsWith("'") && !expression.startsWith('"')) {
436
+ // Wrap simple string literals in quotes to prevent Alpine from treating them as expressions
437
+ processedExpression = `'${expression.replace(/'/g, "\\'")}'`;
438
+ }
439
+ const getMarkdownContent = evaluateLater(processedExpression);
440
+
441
+ // Track last processed content to prevent unnecessary re-renders
442
+ let lastProcessedContent = null;
443
+
444
+ effect(() => {
445
+ getMarkdownContent(async (pathOrContent) => {
446
+ // Reset visibility if content is empty/undefined
447
+ if (!pathOrContent || pathOrContent === undefined || pathOrContent === '') {
448
+ el.style.opacity = '0';
449
+ hasContent = false;
450
+ return;
451
+ }
452
+
453
+ if (pathOrContent === undefined) {
454
+ pathOrContent = expression;
455
+ }
456
+
457
+ // Check if this looks like a file path (contains .md, .markdown, or starts with /)
458
+ const isFilePath = typeof pathOrContent === 'string' &&
459
+ (pathOrContent.includes('.md') ||
460
+ pathOrContent.includes('.markdown') ||
461
+ pathOrContent.startsWith('/') ||
462
+ pathOrContent.includes('/'));
463
+
464
+ let markdownContent = pathOrContent;
465
+
466
+ // If it's a file path, fetch the content (with caching)
467
+ if (isFilePath) {
468
+ try {
469
+ // Ensure the path is absolute from project root
470
+ let resolvedPath = pathOrContent;
471
+
472
+ // If it's a relative path (doesn't start with /), make it absolute from root
473
+ if (!pathOrContent.startsWith('/')) {
474
+ resolvedPath = '/' + pathOrContent;
475
+ }
476
+
477
+ // Check cache first
478
+ if (markdownCache.has(resolvedPath)) {
479
+ markdownContent = markdownCache.get(resolvedPath);
480
+ } else {
481
+ const response = await fetch(resolvedPath);
482
+ if (response.ok) {
483
+ markdownContent = await response.text();
484
+ // Cache the content
485
+ markdownCache.set(resolvedPath, markdownContent);
486
+ } else {
487
+ console.warn(`[Manifest] Failed to fetch markdown file: ${resolvedPath}`);
488
+ markdownContent = `# Error Loading Content\n\nCould not load: ${resolvedPath}`;
489
+ // Cache error content too to prevent repeated failed requests
490
+ markdownCache.set(resolvedPath, markdownContent);
491
+ }
492
+ }
493
+ } catch (error) {
494
+ console.error(`[Manifest] Error fetching markdown file: ${pathOrContent}`, error);
495
+ markdownContent = `# Error Loading Content\n\nCould not load: ${pathOrContent}\n\nError: ${error.message}`;
496
+ // Cache error content to prevent repeated failed requests
497
+ if (resolvedPath) {
498
+ markdownCache.set(resolvedPath, markdownContent);
499
+ }
500
+ }
501
+ }
502
+
503
+ // Skip if content hasn't changed (prevents unnecessary re-renders)
504
+ if (markdownContent === lastProcessedContent) {
505
+ return;
506
+ }
507
+ lastProcessedContent = markdownContent;
508
+
509
+ // Skip empty content
510
+ if (!markdownContent || markdownContent.trim() === '') {
511
+ el.style.opacity = '0';
512
+ hasContent = false;
513
+ return;
514
+ }
515
+
516
+ const marked = await loadMarkedJS();
517
+ let html = marked.parse(markdownContent);
518
+
519
+ // Post-process HTML to enable checkboxes (remove disabled attribute)
520
+ html = enableCheckboxes(html);
521
+
522
+ // Only update DOM if HTML actually changed
523
+ if (el.innerHTML !== html) {
524
+ // Create temporary container
525
+ const temp = document.createElement('div');
526
+ temp.innerHTML = html;
527
+
528
+ el.innerHTML = '';
529
+ while (temp.firstChild) {
530
+ el.appendChild(temp.firstChild);
531
+ }
532
+
533
+ // Ensure Alpine processes the newly inserted HTML
534
+ if (window.Alpine && typeof window.Alpine.initTree === 'function') {
535
+ if (window.Alpine.nextTick) {
536
+ window.Alpine.nextTick(() => {
537
+ window.Alpine.initTree(el);
538
+ });
539
+ } else {
540
+ setTimeout(() => {
541
+ window.Alpine.initTree(el);
542
+ }, 0);
543
+ }
544
+ }
545
+ }
546
+
547
+ // Code highlighting is handled by manifest.code.js plugin
548
+
549
+ // Show content with fade-in
550
+ hasContent = true;
551
+ el.style.opacity = '1';
552
+
553
+ // Extract headings for anchor links
554
+ const headings = [];
555
+ const headingElements = el.querySelectorAll('h1, h2, h3');
556
+ headingElements.forEach(heading => {
557
+ headings.push({
558
+ id: heading.id,
559
+ text: heading.textContent,
560
+ level: parseInt(heading.tagName.charAt(1))
561
+ });
562
+ });
563
+
564
+ // Store headings in Alpine data if 'headings' modifier is used
565
+ if (modifiers.includes('headings')) {
566
+ // Generate a unique ID for this markdown section
567
+ const sectionId = 'markdown-' + Math.random().toString(36).substr(2, 9);
568
+ el.setAttribute('data-headings-section', sectionId);
569
+
570
+ // Store headings in a global registry
571
+ if (!window._induxHeadings) {
572
+ window._induxHeadings = {};
573
+ }
574
+ window._induxHeadings[sectionId] = headings;
575
+ }
576
+ });
577
+ });
578
+ });
579
+
580
+ // If there are existing elements with x-markdown, manually process them with proper Alpine context
581
+ if (existingMarkdownElements.length > 0) {
582
+
583
+ existingMarkdownElements.forEach(el => {
584
+ const expression = el.getAttribute('x-markdown');
585
+
586
+ // Create a temporary Alpine component context for this element
587
+ const tempComponent = Alpine.$data(el) || {};
588
+
589
+ // Use Alpine's evaluation system within the component context
590
+ const updateContent = async (element, newContent = null) => {
591
+ try {
592
+ if (!newContent) {
593
+ return;
594
+ }
595
+
596
+ // Load marked.js and parse markdown
597
+ const marked = await loadMarkedJS();
598
+ const processedMarkdown = renderXCodeGroup(newContent);
599
+ let html = marked.parse(processedMarkdown);
600
+
601
+ // Post-process HTML to enable checkboxes (remove disabled attribute)
602
+ html = html.replace(/<input type="checkbox"([^>]*?)disabled([^>]*?)>/g, '<input type="checkbox"$1$2>');
603
+
604
+ // Create temporary container
605
+ const temp = document.createElement('div');
606
+ temp.innerHTML = html;
607
+
608
+ element.innerHTML = '';
609
+ while (temp.firstChild) {
610
+ element.appendChild(temp.firstChild);
611
+ }
612
+
613
+ // Ensure Alpine processes the newly inserted HTML
614
+ // This is critical for data source expressions like $x.projects
615
+ // Try to wait for magic methods, but proceed anyway if not ready
616
+ const initAlpine = (retryCount = 0) => {
617
+ if (!window.Alpine || typeof window.Alpine.initTree !== 'function') {
618
+ if (retryCount < 5) {
619
+ setTimeout(() => initAlpine(retryCount + 1), 50);
620
+ }
621
+ return;
622
+ }
623
+
624
+ // Check if $x magic method is available
625
+ const xMagic = window.Alpine?.magic?.('x');
626
+ const hasXMagic = typeof xMagic === 'function';
627
+
628
+ // DEBUG: Log markdown Alpine initialization
629
+ const hasExampleComponents = element.querySelectorAll('[x-header-modified], [class*="header-modified"]').length > 0;
630
+ if (hasExampleComponents) {
631
+ console.log('[DEBUG markdown] Initializing Alpine for markdown with components', {
632
+ hasXMagic,
633
+ retryCount,
634
+ element: element.tagName,
635
+ componentCount: element.querySelectorAll('x-header-modified').length,
636
+ stack: new Error().stack.split('\n').slice(1, 5).join('\n')
637
+ });
638
+ }
639
+
640
+ // If magic method isn't ready, wait briefly but don't block forever
641
+ if (!hasXMagic && retryCount < 5) {
642
+ setTimeout(() => initAlpine(retryCount + 1), 50);
643
+ return;
644
+ }
645
+
646
+ // Use Alpine.nextTick if available, otherwise setTimeout
647
+ const scheduleInit = (fn) => {
648
+ if (window.Alpine?.nextTick) {
649
+ window.Alpine.nextTick(fn);
650
+ } else {
651
+ setTimeout(fn, 0);
652
+ }
653
+ };
654
+
655
+ scheduleInit(() => {
656
+ try {
657
+ window.Alpine.initTree(element);
658
+ } catch (e) {
659
+ console.error('[Manifest Markdown] Error initializing Alpine tree (updateContent):', e);
660
+ }
661
+ });
662
+ };
663
+
664
+ // Start initialization
665
+ initAlpine();
666
+
667
+ // Re-highlight code blocks after content update
668
+ // Code highlighting is handled by manifest.code.js plugin
669
+ } catch (error) {
670
+ console.error('[Manifest Markdown] Failed to process element:', error);
671
+ }
672
+ };
673
+
674
+ // Handle simple string expressions
675
+ if (expression.startsWith("'") && expression.endsWith("'")) {
676
+ const content = expression.slice(1, -1);
677
+ updateContent(el, content);
678
+ } else {
679
+ // For complex expressions, we need to force Alpine to re-process this element
680
+
681
+ // Remove and re-add the attribute to force Alpine to re-process it
682
+ const originalExpression = expression;
683
+ el.removeAttribute('x-markdown');
684
+
685
+ // Use a small delay to ensure the directive is registered
686
+ setTimeout(() => {
687
+ el.setAttribute('x-markdown', originalExpression);
688
+ }, 50);
689
+ }
690
+ });
691
+ }
692
+
693
+ } catch (error) {
694
+ console.error('[Manifest] Failed to initialize markdown plugin:', error);
695
+ }
696
+ }
697
+
698
+ // Track initialization to prevent duplicates
699
+ let markdownPluginInitialized = false;
700
+
701
+ async function ensureMarkdownPluginInitialized() {
702
+ if (markdownPluginInitialized) {
703
+ return;
704
+ }
705
+ if (!window.Alpine || typeof window.Alpine.directive !== 'function') {
706
+ return;
707
+ }
708
+
709
+ markdownPluginInitialized = true;
710
+ await initializeMarkdownPlugin();
711
+
712
+ // If elements with x-markdown already exist, process them
713
+ // This handles the case where the plugin loads after components are swapped in
714
+ if (window.Alpine && typeof window.Alpine.initTree === 'function') {
715
+ const existingMarkdownElements = document.querySelectorAll('[x-markdown]');
716
+ existingMarkdownElements.forEach(el => {
717
+ // Only process if not already processed by Alpine
718
+ if (!el.__x) {
719
+ window.Alpine.initTree(el);
720
+ }
721
+ });
722
+ }
723
+ }
724
+
725
+ // Expose on window for loader to call if needed
726
+ window.ensureMarkdownPluginInitialized = ensureMarkdownPluginInitialized;
727
+
728
+ // Handle both DOMContentLoaded and alpine:init
729
+ if (document.readyState === 'loading') {
730
+ document.addEventListener('DOMContentLoaded', ensureMarkdownPluginInitialized);
731
+ }
732
+
733
+ document.addEventListener('alpine:init', ensureMarkdownPluginInitialized);
734
+
735
+ // If Alpine is already initialized when this script loads, initialize immediately
736
+ if (window.Alpine && typeof window.Alpine.directive === 'function') {
737
+ ensureMarkdownPluginInitialized();
738
+ }