Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@weave-md/basic

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@weave-md/basic - npm Package Compare versions

Comparing version
0.2.0-alpha.0
to
0.3.0-alpha.0
+38
-0
dist/render/index.js

@@ -6,2 +6,3 @@ import { toHast } from 'mdast-util-to-hast';

import katex from 'katex';
import { parseMdast } from '@weave-md/parse';
/**

@@ -177,4 +178,41 @@ * Convert mdast tree to HTML string.

});
// Transform sub (inline substitution) to HTML
visit(tree, 'sub', (node, index, parent) => {
if (index === undefined || !parent)
return;
const rawInitial = node.data?.rawInitial ?? '';
const rawReplacement = node.data?.rawReplacement ?? '';
// Parse and render both initial and replacement content to support :math
const initialHtml = renderInlineContent(rawInitial, options);
const replacementHtml = renderInlineContent(rawReplacement, options);
// Base64 encode to avoid quote escaping issues with nested content
const encodedReplacement = Buffer.from(replacementHtml).toString('base64');
// Detect redacted text (black block characters)
const isRedacted = /^[█▓▒░■□▪▫]+$/.test(rawInitial.trim());
const className = isRedacted ? 'weave-sub weave-sub-redacted' : 'weave-sub';
const html = `<span class="${className}" data-replacement-b64="${encodedReplacement}">${initialHtml}</span>`;
parent.children[index] = { type: 'html', value: html };
return SKIP;
});
}
/**
* Parse and render inline content (for :sub replacement text)
*/
function renderInlineContent(content, options) {
if (!content)
return '';
// Parse the content as mdast
const tree = parseMdast(content);
// Transform any sub/math nodes in the parsed tree
transformWeaveNodesToHtml(tree, { renderMath: options.renderMath ?? true, footnotes: [] });
// Convert to hast and then to HTML
const hast = toHast(tree, { allowDangerousHtml: true });
if (!hast)
return escapeHtml(content);
// Get the inner HTML (skip the wrapping paragraph if present)
const html = hastToHtml(hast, { allowDangerousHtml: true });
// Strip wrapping <p> tags if present (since this is inline content)
return html.replace(/^<p>/, '').replace(/<\/p>\s*$/, '');
}
/**
* Get plain text content from mdast children

@@ -181,0 +219,0 @@ */

+1
-1
/**
* Basic HTML/JS/CSS template for static export with footnote and overlay support
*/
export declare const HTML_TEMPLATE = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>{{TITLE}}</title>\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css\" integrity=\"sha384-n8MVd4RsNIU0tAv4ct0nTaAbDJwPJzDEaqSD1odI+WdtXRGWt2kTvGFasHpSy3SV\" crossorigin=\"anonymous\">\n <style>\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n line-height: 1.6;\n color: #333;\n background: #fff;\n padding: 2rem 1rem;\n }\n\n .weave-document {\n max-width: 42rem;\n margin: 0 auto;\n position: relative;\n }\n\n .weave-section {\n margin-bottom: 3rem;\n }\n\n h1 {\n font-size: 2.25rem;\n font-weight: 700;\n }\n\n h2 {\n margin-top: 1.5rem;\n margin-bottom: 1rem;\n font-size: 1.75rem;\n font-weight: 600;\n }\n\n h3 {\n margin-top: 1.5rem;\n margin-bottom: 0.75rem;\n font-size: 1.25rem;\n font-weight: 600;\n }\n\n p {\n margin-bottom: 1rem;\n }\n\n /* Node links */\n .weave-node-link {\n color: #0066cc;\n text-decoration: none;\n border-bottom: 1px solid #0066cc;\n cursor: pointer;\n position: relative;\n }\n\n .weave-node-link:hover {\n background: #f0f7ff;\n }\n\n /* Overlay anchor icon (for empty text links) */\n .weave-icon {\n width: 1.2em;\n height: 1.2em;\n vertical-align: -0.2em;\n }\n\n .weave-overlay-anchor,\n .weave-inline-anchor {\n display: inline;\n color: #0066cc;\n cursor: pointer;\n }\n\n /* Plus/minus toggle for inline anchors */\n .weave-inline-anchor .weave-icon-minus {\n display: none;\n }\n\n .weave-inline-anchor.expanded .weave-icon-plus {\n display: none;\n }\n\n .weave-inline-anchor.expanded .weave-icon-minus {\n display: inline;\n }\n\n .weave-overlay-anchor:hover .weave-icon,\n .weave-inline-anchor:hover .weave-icon {\n fill: #f0f7ff;\n }\n\n /* Inline expandable content */\n .weave-inline-trigger {\n color: #0066cc;\n text-decoration: none;\n border-bottom: 1px solid #0066cc;\n cursor: pointer;\n }\n\n .weave-inline-trigger:hover {\n background: #f0f7ff;\n }\n\n .weave-inline-trigger.expanded {\n background: #e8f4ff;\n border-bottom: 2px solid #0066cc;\n }\n\n\n .weave-inline-content {\n display: block;\n margin: 1rem 0;\n padding: 1rem;\n background: #f8f9fa;\n border-left: 3px solid #0066cc;\n border-radius: 4px;\n }\n\n .weave-inline-content.hidden {\n display: none;\n }\n\n .weave-inline-content p:last-child {\n margin-bottom: 0;\n }\n\n /* Footnote references */\n .weave-footnote-ref {\n font-size: 0.75em;\n vertical-align: super;\n }\n\n .weave-footnote-ref a {\n color: #0066cc;\n text-decoration: none;\n }\n\n .weave-footnote-ref a:hover {\n background: #f0f7ff;\n }\n\n /* Text-linked footnote references */\n .weave-footnote-link {\n color: #0066cc;\n text-decoration: none;\n }\n\n .weave-footnote-link-text {\n border-bottom: 1px solid #0066cc;\n }\n\n .weave-footnote-link:hover {\n background: #f0f7ff;\n }\n\n .weave-footnote-link sup {\n font-size: 0.75em;\n margin-left: 0.1em;\n }\n\n /* Footnotes section */\n .weave-footnotes-separator {\n margin: 3rem 0 2rem;\n border: none;\n border-top: 1px solid #ddd;\n }\n\n .weave-footnotes {\n font-size: 0.9em;\n color: #666;\n }\n\n .weave-footnotes-list {\n list-style: none;\n padding: 0;\n }\n\n .weave-footnote {\n display: grid;\n grid-template-columns: 2.5em 1fr;\n margin-bottom: 1rem;\n }\n\n .weave-footnote-marker {\n text-align: left;\n }\n\n .weave-footnote-backref {\n text-decoration: none;\n color: #0066cc;\n }\n\n .weave-footnote-backref:hover {\n background: #f0f7ff;\n }\n\n .weave-footnote-content {\n min-width: 0;\n }\n\n .weave-footnote-content p:first-child {\n display: inline;\n }\n\n .weave-footnote-content p + p {\n margin-top: 0.5rem;\n }\n\n /* Overlay - bigfoot-style tooltip */\n .weave-overlay {\n position: fixed;\n z-index: 10000;\n box-sizing: border-box;\n max-width: min(22rem, calc(100vw - 20px));\n display: inline-block;\n background: #fafafa;\n border-radius: 0.5em;\n border: 1px solid #c3c3c3;\n box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);\n opacity: 0;\n transform: scale(0.1) translateZ(0);\n transform-origin: 50% 0;\n transition: opacity 0.25s ease, transform 0.25s ease;\n pointer-events: none;\n }\n\n .weave-overlay.active {\n opacity: 0.97;\n transform: scale(1) translateZ(0);\n pointer-events: auto;\n }\n\n .weave-overlay.above {\n transform-origin: 50% 100%;\n }\n\n /* Tooltip arrow */\n .weave-overlay-tooltip {\n position: absolute;\n width: 0;\n height: 0;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n }\n\n .weave-overlay.below .weave-overlay-tooltip {\n top: -10px;\n border-bottom: 10px solid #c3c3c3;\n }\n\n .weave-overlay.below .weave-overlay-tooltip::after {\n content: '';\n position: absolute;\n top: 2px;\n left: -9px;\n border-left: 9px solid transparent;\n border-right: 9px solid transparent;\n border-bottom: 9px solid #fafafa;\n }\n\n .weave-overlay.above .weave-overlay-tooltip {\n bottom: -10px;\n border-top: 10px solid #c3c3c3;\n }\n\n .weave-overlay.above .weave-overlay-tooltip::after {\n content: '';\n position: absolute;\n bottom: 2px;\n left: -9px;\n border-left: 9px solid transparent;\n border-right: 9px solid transparent;\n border-top: 9px solid #fafafa;\n }\n\n .weave-overlay-content {\n position: relative;\n }\n\n .weave-overlay-main-wrapper {\n max-height: 15em;\n overflow: auto;\n }\n\n .weave-overlay-body {\n padding: 0.6em 0.8em;\n line-height: 1.5;\n font-size: 0.95em;\n color: #333;\n }\n\n .weave-overlay-body p {\n margin: 0;\n }\n\n .weave-overlay-body p + p {\n margin-top: 0.5em;\n }\n\n /* Math blocks */\n .weave-math-block {\n margin: 1.5rem 0;\n overflow-x: auto;\n }\n\n /* Media */\n .weave-media {\n margin: 1.5rem auto;\n text-align: center;\n }\n\n .weave-media img,\n .weave-media video,\n .weave-media iframe {\n max-width: 100%;\n width: 100%;\n height: auto;\n display: block;\n margin: 0 auto;\n }\n\n .weave-media video {\n background: #000;\n }\n\n .weave-media iframe {\n background: #000;\n }\n\n /* Fallback aspect ratio for embeds without explicit width/height */\n .weave-media iframe:not([width]):not([height]) {\n aspect-ratio: 16 / 9;\n }\n\n .weave-media figcaption {\n margin-top: 0.5rem;\n font-size: 0.9em;\n color: #666;\n font-style: italic;\n }\n\n /* Gallery Carousel */\n .weave-gallery {\n position: relative;\n overflow: hidden;\n }\n\n .weave-gallery figure {\n display: none;\n margin: 0;\n }\n\n .weave-gallery figure.active {\n display: block;\n }\n\n .weave-gallery img {\n border-radius: 4px;\n }\n\n .weave-gallery-nav {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n background: rgba(0,0,0,0.5);\n color: white;\n border: none;\n padding: 0.75rem;\n cursor: pointer;\n font-size: 1.25rem;\n border-radius: 4px;\n z-index: 10;\n }\n\n .weave-gallery-nav:hover {\n background: rgba(0,0,0,0.7);\n }\n\n .weave-gallery-prev { left: 0.5rem; }\n .weave-gallery-next { right: 0.5rem; }\n\n .weave-gallery-dots {\n display: flex;\n justify-content: center;\n gap: 0.5rem;\n margin-top: 0.75rem;\n }\n\n .weave-gallery-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #ccc;\n border: none;\n cursor: pointer;\n padding: 0;\n }\n\n .weave-gallery-dot.active {\n background: #0066cc;\n }\n\n /* Tables */\n table {\n border-collapse: collapse;\n width: 100%;\n margin: 1rem 0;\n }\n\n th, td {\n border: 1px solid #ddd;\n padding: 0.5rem 0.75rem;\n text-align: left;\n }\n\n th {\n background: #f5f5f5;\n font-weight: 600;\n }\n\n tr:nth-child(even) {\n background: #fafafa;\n }\n\n /* Code blocks */\n pre {\n background: #f5f5f5;\n padding: 1rem;\n border-radius: 4px;\n overflow-x: auto;\n margin: 1rem 0;\n }\n\n code {\n font-family: 'Monaco', 'Menlo', 'Courier New', monospace;\n font-size: 0.9em;\n }\n\n /* Preformatted - preserves spacing, matches paragraph spacing */\n .weave-preformatted {\n white-space: pre-wrap;\n margin-bottom: 1rem;\n }\n\n /* Mobile */\n @media (max-width: 640px) {\n body {\n padding: 1rem 0.75rem;\n }\n }\n </style>\n</head>\n<body>\n <main class=\"weave-document\">\n {{CONTENT}}\n </main>\n\n <!-- Overlay container -->\n <div class=\"weave-overlay\" id=\"weave-overlay\">\n <div class=\"weave-overlay-main-wrapper\">\n <div class=\"weave-overlay-body\" id=\"weave-overlay-body\"></div>\n </div>\n <div class=\"weave-overlay-tooltip\" id=\"weave-overlay-tooltip\"></div>\n </div>\n\n <script>\n // Section content lookup\n const sections = {{SECTIONS_DATA}};\n\n // Overlay handling\n const overlay = document.getElementById('weave-overlay');\n const overlayBody = document.getElementById('weave-overlay-body');\n\n let currentTrigger = null;\n\n const overlayTooltip = document.getElementById('weave-overlay-tooltip');\n\n function positionOverlay() {\n if (!currentTrigger) return;\n \n // Use getClientRects() to handle wrapped inline elements\n // Pick the last rect (end of link) for better UX\n const rects = currentTrigger.getClientRects();\n const rect = rects.length > 0 ? rects[rects.length - 1] : currentTrigger.getBoundingClientRect();\n const viewportHeight = window.innerHeight;\n \n // Use content container bounds instead of viewport for horizontal positioning\n // This keeps overlay within content area, leaving margins free for side notes\n const container = document.querySelector('.weave-document');\n const containerRect = container.getBoundingClientRect();\n \n const overlayHeight = overlay.offsetHeight;\n const overlayWidth = overlay.offsetWidth;\n \n // Trigger center is the anchor - arrow MUST point here\n const triggerCenterX = rect.left + (rect.width / 2);\n \n // Check space above and below\n const spaceBelow = viewportHeight - rect.bottom;\n const spaceAbove = rect.top;\n const showBelow = spaceBelow >= overlayHeight + 15 || spaceBelow > spaceAbove;\n \n const arrowMinEdge = 15; // min distance from arrow center to overlay edge\n const screenEdgePadding = 8; // minimum distance from screen edge for shadow visibility\n \n // Bounds: prefer container, but always keep minimum distance from screen edges\n const boundsLeft = Math.max(screenEdgePadding, containerRect.left);\n const boundsRight = Math.min(window.innerWidth - screenEdgePadding, containerRect.right);\n \n // Position overlay so arrow can reach the trigger\n // Arrow must be at triggerCenterX, and arrow must be within [arrowMinEdge, overlayWidth - arrowMinEdge]\n let leftMin = triggerCenterX - (overlayWidth - arrowMinEdge);\n let leftMax = triggerCenterX - arrowMinEdge;\n \n // Start centered on trigger\n let left = triggerCenterX - (overlayWidth / 2);\n \n // Ensure arrow can reach trigger (clamp to valid range)\n left = Math.max(leftMin, Math.min(leftMax, left));\n \n // Now clamp to container bounds\n // Clamp right first, then left (left takes priority so shadow is visible)\n if (left + overlayWidth > boundsRight) {\n left = boundsRight - overlayWidth;\n }\n if (left < boundsLeft) {\n left = boundsLeft;\n }\n \n overlay.style.left = left + 'px';\n \n // Arrow position = trigger center relative to overlay left edge\n const arrowLeftPx = triggerCenterX - left;\n overlayTooltip.style.left = arrowLeftPx + 'px';\n overlayTooltip.style.transform = 'translateX(-50%)';\n \n // Position vertically\n overlay.classList.remove('above', 'below');\n if (showBelow) {\n overlay.style.top = (rect.bottom + 10) + 'px';\n overlay.classList.add('below');\n } else {\n overlay.style.top = (rect.top - overlayHeight - 10) + 'px';\n overlay.classList.add('above');\n }\n }\n\n function openOverlay(sectionId, triggerElement) {\n const section = sections[sectionId];\n if (!section) return;\n\n overlayBody.innerHTML = section.html;\n currentTrigger = triggerElement;\n \n overlay.classList.add('active');\n positionOverlay();\n }\n\n // Reposition on scroll/resize to keep arrow attached\n window.addEventListener('scroll', () => {\n if (overlay.classList.contains('active')) {\n positionOverlay();\n }\n }, true);\n \n window.addEventListener('resize', () => {\n if (overlay.classList.contains('active')) {\n positionOverlay();\n }\n });\n\n function closeOverlay() {\n overlay.classList.remove('active');\n currentTrigger = null;\n }\n\n // Click handlers\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) closeOverlay();\n });\n\n // Escape key\n document.addEventListener('keydown', (e) => {\n if (e.key === 'Escape' && overlay.classList.contains('active')) {\n closeOverlay();\n }\n });\n\n // Click to open overlay\n document.addEventListener('click', (e) => {\n const link = e.target.closest('.weave-node-link, .weave-overlay-anchor');\n \n // Close overlay if clicking outside\n if (!link && !e.target.closest('.weave-overlay')) {\n if (overlay.classList.contains('active')) {\n closeOverlay();\n }\n return;\n }\n \n if (!link) return;\n\n const display = link.dataset.display;\n const nodeId = link.dataset.nodeId;\n\n if (display === 'overlay' && nodeId) {\n e.preventDefault();\n \n // Toggle overlay if clicking same trigger\n if (overlay.classList.contains('active') && currentTrigger === link) {\n closeOverlay();\n } else {\n openOverlay(nodeId, link);\n }\n }\n });\n\n // Inline expand/collapse handling\n document.addEventListener('click', (e) => {\n const trigger = e.target.closest('.weave-inline-trigger, .weave-inline-anchor');\n if (!trigger) return;\n\n e.preventDefault();\n \n const inlineId = trigger.dataset.inlineId;\n let content = document.getElementById('weave-inline-' + inlineId);\n \n // If content doesn't exist, create it\n if (!content) {\n const section = sections[inlineId];\n if (!section) return;\n \n content = document.createElement('div');\n content.id = 'weave-inline-' + inlineId;\n content.className = 'weave-inline-content';\n content.innerHTML = section.html;\n content.style.display = 'none';\n \n // Insert after the parent paragraph\n const paragraph = trigger.closest('p');\n if (paragraph && paragraph.parentNode) {\n paragraph.parentNode.insertBefore(content, paragraph.nextSibling);\n }\n }\n \n // Toggle visibility\n const isHidden = content.style.display === 'none';\n content.style.display = isHidden ? 'block' : 'none';\n trigger.classList.toggle('expanded', isHidden);\n });\n\n // Footnote backlink tracking - remember which reference was clicked\n document.addEventListener('click', (e) => {\n const link = e.target.closest('.weave-footnote-ref a, a.weave-footnote-link');\n if (!link) return;\n \n const refId = link.id;\n if (!refId) return;\n \n // Extract footnote number from the href (e.g., #fn-3 -> 3)\n const href = link.getAttribute('href');\n if (!href || !href.startsWith('#fn-')) return;\n const fnNum = href.replace('#fn-', '');\n \n // Find the backref link in the footnote and update it\n const backref = document.querySelector('#fn-' + fnNum + ' .weave-footnote-backref');\n if (backref) {\n backref.setAttribute('href', '#' + refId);\n }\n \n // Navigate to the footnote\n e.preventDefault();\n const footnoteId = 'fn-' + fnNum;\n const footnote = document.getElementById(footnoteId);\n if (footnote) {\n footnote.scrollIntoView({ behavior: 'smooth' });\n // Update URL fragment without triggering navigation\n history.pushState(null, '', '#' + footnoteId);\n }\n });\n\n // Backref click - scroll and clear hash\n document.addEventListener('click', (e) => {\n const backref = e.target.closest('.weave-footnote-backref');\n if (!backref) return;\n \n e.preventDefault();\n const href = backref.getAttribute('href');\n if (!href) return;\n \n const target = document.getElementById(href.replace('#', ''));\n if (target) {\n target.scrollIntoView({ behavior: 'smooth' });\n history.pushState(null, '', window.location.pathname + window.location.search);\n }\n });\n\n // Video start time handling\n document.querySelectorAll('video[data-start]').forEach(video => {\n const startTime = parseFloat(video.dataset.start);\n if (!isNaN(startTime)) {\n video.currentTime = startTime;\n video.addEventListener('loadedmetadata', () => {\n video.currentTime = startTime;\n }, { once: true });\n }\n });\n\n // Gallery carousel initialization\n document.querySelectorAll('.weave-gallery').forEach(gallery => {\n const figures = gallery.querySelectorAll('figure');\n if (figures.length <= 1) return;\n \n // Set first figure as active\n figures[0].classList.add('active');\n \n // Create navigation buttons\n const prevBtn = document.createElement('button');\n prevBtn.className = 'weave-gallery-nav weave-gallery-prev';\n prevBtn.innerHTML = '&#10094;';\n prevBtn.setAttribute('aria-label', 'Previous');\n \n const nextBtn = document.createElement('button');\n nextBtn.className = 'weave-gallery-nav weave-gallery-next';\n nextBtn.innerHTML = '&#10095;';\n nextBtn.setAttribute('aria-label', 'Next');\n \n gallery.insertBefore(prevBtn, gallery.firstChild);\n gallery.insertBefore(nextBtn, gallery.querySelector('figcaption') || null);\n \n // Create dots\n const dotsContainer = document.createElement('div');\n dotsContainer.className = 'weave-gallery-dots';\n figures.forEach((_, i) => {\n const dot = document.createElement('button');\n dot.className = 'weave-gallery-dot' + (i === 0 ? ' active' : '');\n dot.setAttribute('aria-label', 'Go to slide ' + (i + 1));\n dot.dataset.index = i;\n dotsContainer.appendChild(dot);\n });\n const caption = gallery.querySelector('figcaption');\n if (caption) {\n gallery.insertBefore(dotsContainer, caption);\n } else {\n gallery.appendChild(dotsContainer);\n }\n \n let current = 0;\n \n const showSlide = (index) => {\n figures.forEach((f, i) => f.classList.toggle('active', i === index));\n dotsContainer.querySelectorAll('.weave-gallery-dot').forEach((d, i) => \n d.classList.toggle('active', i === index)\n );\n current = index;\n };\n \n prevBtn.addEventListener('click', () => {\n showSlide((current - 1 + figures.length) % figures.length);\n });\n \n nextBtn.addEventListener('click', () => {\n showSlide((current + 1) % figures.length);\n });\n \n dotsContainer.addEventListener('click', (e) => {\n const dot = e.target.closest('.weave-gallery-dot');\n if (dot) showSlide(parseInt(dot.dataset.index));\n });\n });\n </script>\n</body>\n</html>\n";
export declare const HTML_TEMPLATE = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>{{TITLE}}</title>\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css\" integrity=\"sha384-n8MVd4RsNIU0tAv4ct0nTaAbDJwPJzDEaqSD1odI+WdtXRGWt2kTvGFasHpSy3SV\" crossorigin=\"anonymous\">\n <style>\n * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n line-height: 1.6;\n color: #333;\n background: #fff;\n padding: 2rem 1rem;\n }\n\n .weave-document {\n max-width: 42rem;\n margin: 0 auto;\n position: relative;\n }\n\n .weave-section {\n margin-bottom: 3rem;\n }\n\n h1 {\n font-size: 2.25rem;\n font-weight: 700;\n }\n\n h2 {\n margin-top: 1.5rem;\n margin-bottom: 1rem;\n font-size: 1.75rem;\n font-weight: 600;\n }\n\n h3 {\n margin-top: 1.5rem;\n margin-bottom: 0.75rem;\n font-size: 1.25rem;\n font-weight: 600;\n }\n\n p {\n margin-bottom: 1rem;\n }\n\n /* Node links */\n .weave-node-link {\n color: #0066cc;\n text-decoration: none;\n border-bottom: 1px solid #0066cc;\n cursor: pointer;\n position: relative;\n }\n\n .weave-node-link:hover {\n background: #f0f7ff;\n }\n\n /* Overlay anchor icon (for empty text links) */\n .weave-icon {\n width: 1.2em;\n height: 1.2em;\n vertical-align: -0.2em;\n }\n\n .weave-overlay-anchor,\n .weave-inline-anchor {\n display: inline;\n color: #0066cc;\n cursor: pointer;\n }\n\n /* Plus/minus toggle for inline anchors */\n .weave-inline-anchor .weave-icon-minus {\n display: none;\n }\n\n .weave-inline-anchor.expanded .weave-icon-plus {\n display: none;\n }\n\n .weave-inline-anchor.expanded .weave-icon-minus {\n display: inline;\n }\n\n .weave-overlay-anchor:hover .weave-icon,\n .weave-inline-anchor:hover .weave-icon {\n fill: #f0f7ff;\n }\n\n /* Inline expandable content */\n .weave-inline-trigger {\n color: #0066cc;\n text-decoration: none;\n border-bottom: 1px solid #0066cc;\n cursor: pointer;\n }\n\n .weave-inline-trigger:hover {\n background: #f0f7ff;\n }\n\n .weave-inline-trigger.expanded {\n background: #e8f4ff;\n border-bottom: 2px solid #0066cc;\n }\n\n /* Inline substitution */\n .weave-sub {\n color: #0066cc;\n text-decoration: none;\n border-bottom: 1px solid #0066cc;\n cursor: pointer;\n }\n\n .weave-sub:hover {\n background: #f0f7ff;\n }\n\n .weave-sub.expanded {\n color: inherit;\n border-bottom: none;\n cursor: default;\n background: none;\n }\n\n /* Nested subs inside expanded subs should still be styled as links */\n .weave-sub.expanded .weave-sub:not(.expanded) {\n color: #0066cc;\n border-bottom: 1px solid #0066cc;\n cursor: pointer;\n }\n\n /* Redacted style - black blocks that are still clickable */\n .weave-sub-redacted:not(.expanded) {\n color: inherit;\n border-bottom: none;\n cursor: pointer;\n background: none;\n }\n\n .weave-sub-redacted:not(.expanded):hover {\n opacity: 0.7;\n }\n\n .weave-inline-content {\n display: block;\n margin: 1rem 0;\n padding: 1rem;\n background: #f8f9fa;\n border-left: 3px solid #0066cc;\n border-radius: 4px;\n }\n\n .weave-inline-content.hidden {\n display: none;\n }\n\n .weave-inline-content p:last-child {\n margin-bottom: 0;\n }\n\n /* Footnote references */\n .weave-footnote-ref {\n font-size: 0.75em;\n vertical-align: super;\n }\n\n .weave-footnote-ref a {\n color: #0066cc;\n text-decoration: none;\n }\n\n .weave-footnote-ref a:hover {\n background: #f0f7ff;\n }\n\n /* Text-linked footnote references */\n .weave-footnote-link {\n color: #0066cc;\n text-decoration: none;\n }\n\n .weave-footnote-link-text {\n border-bottom: 1px solid #0066cc;\n }\n\n .weave-footnote-link:hover {\n background: #f0f7ff;\n }\n\n .weave-footnote-link sup {\n font-size: 0.75em;\n margin-left: 0.1em;\n }\n\n /* Footnotes section */\n .weave-footnotes-separator {\n margin: 3rem 0 2rem;\n border: none;\n border-top: 1px solid #ddd;\n }\n\n .weave-footnotes {\n font-size: 0.9em;\n color: #666;\n }\n\n .weave-footnotes-list {\n list-style: none;\n padding: 0;\n }\n\n .weave-footnote {\n display: grid;\n grid-template-columns: 2.5em 1fr;\n margin-bottom: 1rem;\n }\n\n .weave-footnote-marker {\n text-align: left;\n }\n\n .weave-footnote-backref {\n text-decoration: none;\n color: #0066cc;\n }\n\n .weave-footnote-backref:hover {\n background: #f0f7ff;\n }\n\n .weave-footnote-content {\n min-width: 0;\n }\n\n .weave-footnote-content p:first-child {\n display: inline;\n }\n\n .weave-footnote-content p + p {\n margin-top: 0.5rem;\n }\n\n /* Overlay - bigfoot-style tooltip */\n .weave-overlay {\n position: fixed;\n z-index: 10000;\n box-sizing: border-box;\n max-width: min(22rem, calc(100vw - 20px));\n display: inline-block;\n background: #fafafa;\n border-radius: 0.5em;\n border: 1px solid #c3c3c3;\n box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.3);\n opacity: 0;\n transform: scale(0.1) translateZ(0);\n transform-origin: 50% 0;\n transition: opacity 0.25s ease, transform 0.25s ease;\n pointer-events: none;\n }\n\n .weave-overlay.active {\n opacity: 0.97;\n transform: scale(1) translateZ(0);\n pointer-events: auto;\n }\n\n .weave-overlay.above {\n transform-origin: 50% 100%;\n }\n\n /* Tooltip arrow */\n .weave-overlay-tooltip {\n position: absolute;\n width: 0;\n height: 0;\n border-left: 10px solid transparent;\n border-right: 10px solid transparent;\n }\n\n .weave-overlay.below .weave-overlay-tooltip {\n top: -10px;\n border-bottom: 10px solid #c3c3c3;\n }\n\n .weave-overlay.below .weave-overlay-tooltip::after {\n content: '';\n position: absolute;\n top: 2px;\n left: -9px;\n border-left: 9px solid transparent;\n border-right: 9px solid transparent;\n border-bottom: 9px solid #fafafa;\n }\n\n .weave-overlay.above .weave-overlay-tooltip {\n bottom: -10px;\n border-top: 10px solid #c3c3c3;\n }\n\n .weave-overlay.above .weave-overlay-tooltip::after {\n content: '';\n position: absolute;\n bottom: 2px;\n left: -9px;\n border-left: 9px solid transparent;\n border-right: 9px solid transparent;\n border-top: 9px solid #fafafa;\n }\n\n .weave-overlay-content {\n position: relative;\n }\n\n .weave-overlay-main-wrapper {\n max-height: 15em;\n overflow: auto;\n }\n\n .weave-overlay-body {\n padding: 0.6em 0.8em;\n line-height: 1.5;\n font-size: 0.95em;\n color: #333;\n }\n\n .weave-overlay-body p {\n margin: 0;\n }\n\n .weave-overlay-body p + p {\n margin-top: 0.5em;\n }\n\n /* Math blocks */\n .weave-math-block {\n margin: 1.5rem 0;\n overflow-x: auto;\n }\n\n /* Media */\n .weave-media {\n margin: 1.5rem auto;\n text-align: center;\n }\n\n .weave-media img,\n .weave-media video,\n .weave-media iframe {\n max-width: 100%;\n width: 100%;\n height: auto;\n display: block;\n margin: 0 auto;\n }\n\n .weave-media video {\n background: #000;\n }\n\n .weave-media iframe {\n background: #000;\n }\n\n /* Fallback aspect ratio for embeds without explicit width/height */\n .weave-media iframe:not([width]):not([height]) {\n aspect-ratio: 16 / 9;\n }\n\n .weave-media figcaption {\n margin-top: 0.5rem;\n font-size: 0.9em;\n color: #666;\n font-style: italic;\n }\n\n /* Gallery Carousel */\n .weave-gallery {\n position: relative;\n overflow: hidden;\n }\n\n .weave-gallery figure {\n display: none;\n margin: 0;\n }\n\n .weave-gallery figure.active {\n display: block;\n }\n\n .weave-gallery img {\n border-radius: 4px;\n }\n\n .weave-gallery-nav {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n background: rgba(0,0,0,0.5);\n color: white;\n border: none;\n padding: 0.75rem;\n cursor: pointer;\n font-size: 1.25rem;\n border-radius: 4px;\n z-index: 10;\n }\n\n .weave-gallery-nav:hover {\n background: rgba(0,0,0,0.7);\n }\n\n .weave-gallery-prev { left: 0.5rem; }\n .weave-gallery-next { right: 0.5rem; }\n\n .weave-gallery-dots {\n display: flex;\n justify-content: center;\n gap: 0.5rem;\n margin-top: 0.75rem;\n }\n\n .weave-gallery-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #ccc;\n border: none;\n cursor: pointer;\n padding: 0;\n }\n\n .weave-gallery-dot.active {\n background: #0066cc;\n }\n\n /* Tables */\n table {\n border-collapse: collapse;\n width: 100%;\n margin: 1rem 0;\n }\n\n th, td {\n border: 1px solid #ddd;\n padding: 0.5rem 0.75rem;\n text-align: left;\n }\n\n th {\n background: #f5f5f5;\n font-weight: 600;\n }\n\n tr:nth-child(even) {\n background: #fafafa;\n }\n\n /* Code blocks */\n pre {\n background: #f5f5f5;\n padding: 1rem;\n border-radius: 4px;\n overflow-x: auto;\n margin: 1rem 0;\n }\n\n code {\n font-family: 'Monaco', 'Menlo', 'Courier New', monospace;\n font-size: 0.9em;\n }\n\n /* Preformatted - preserves spacing, matches paragraph spacing */\n .weave-preformatted {\n white-space: pre-wrap;\n margin-bottom: 1rem;\n }\n\n /* Mobile */\n @media (max-width: 640px) {\n body {\n padding: 1rem 0.75rem;\n }\n }\n </style>\n</head>\n<body>\n <main class=\"weave-document\">\n {{CONTENT}}\n </main>\n\n <!-- Overlay container -->\n <div class=\"weave-overlay\" id=\"weave-overlay\">\n <div class=\"weave-overlay-main-wrapper\">\n <div class=\"weave-overlay-body\" id=\"weave-overlay-body\"></div>\n </div>\n <div class=\"weave-overlay-tooltip\" id=\"weave-overlay-tooltip\"></div>\n </div>\n\n <script>\n // Section content lookup\n const sections = {{SECTIONS_DATA}};\n\n // Overlay handling\n const overlay = document.getElementById('weave-overlay');\n const overlayBody = document.getElementById('weave-overlay-body');\n\n let currentTrigger = null;\n\n const overlayTooltip = document.getElementById('weave-overlay-tooltip');\n\n function positionOverlay() {\n if (!currentTrigger) return;\n \n // Use getClientRects() to handle wrapped inline elements\n // Pick the last rect (end of link) for better UX\n const rects = currentTrigger.getClientRects();\n const rect = rects.length > 0 ? rects[rects.length - 1] : currentTrigger.getBoundingClientRect();\n const viewportHeight = window.innerHeight;\n \n // Use content container bounds instead of viewport for horizontal positioning\n // This keeps overlay within content area, leaving margins free for side notes\n const container = document.querySelector('.weave-document');\n const containerRect = container.getBoundingClientRect();\n \n const overlayHeight = overlay.offsetHeight;\n const overlayWidth = overlay.offsetWidth;\n \n // Trigger center is the anchor - arrow MUST point here\n const triggerCenterX = rect.left + (rect.width / 2);\n \n // Check space above and below\n const spaceBelow = viewportHeight - rect.bottom;\n const spaceAbove = rect.top;\n const showBelow = spaceBelow >= overlayHeight + 15 || spaceBelow > spaceAbove;\n \n const arrowMinEdge = 15; // min distance from arrow center to overlay edge\n const screenEdgePadding = 8; // minimum distance from screen edge for shadow visibility\n \n // Bounds: prefer container, but always keep minimum distance from screen edges\n const boundsLeft = Math.max(screenEdgePadding, containerRect.left);\n const boundsRight = Math.min(window.innerWidth - screenEdgePadding, containerRect.right);\n \n // Position overlay so arrow can reach the trigger\n // Arrow must be at triggerCenterX, and arrow must be within [arrowMinEdge, overlayWidth - arrowMinEdge]\n let leftMin = triggerCenterX - (overlayWidth - arrowMinEdge);\n let leftMax = triggerCenterX - arrowMinEdge;\n \n // Start centered on trigger\n let left = triggerCenterX - (overlayWidth / 2);\n \n // Ensure arrow can reach trigger (clamp to valid range)\n left = Math.max(leftMin, Math.min(leftMax, left));\n \n // Now clamp to container bounds\n // Clamp right first, then left (left takes priority so shadow is visible)\n if (left + overlayWidth > boundsRight) {\n left = boundsRight - overlayWidth;\n }\n if (left < boundsLeft) {\n left = boundsLeft;\n }\n \n overlay.style.left = left + 'px';\n \n // Arrow position = trigger center relative to overlay left edge\n const arrowLeftPx = triggerCenterX - left;\n overlayTooltip.style.left = arrowLeftPx + 'px';\n overlayTooltip.style.transform = 'translateX(-50%)';\n \n // Position vertically\n overlay.classList.remove('above', 'below');\n if (showBelow) {\n overlay.style.top = (rect.bottom + 10) + 'px';\n overlay.classList.add('below');\n } else {\n overlay.style.top = (rect.top - overlayHeight - 10) + 'px';\n overlay.classList.add('above');\n }\n }\n\n function openOverlay(sectionId, triggerElement) {\n const section = sections[sectionId];\n if (!section) return;\n\n overlayBody.innerHTML = section.html;\n currentTrigger = triggerElement;\n \n overlay.classList.add('active');\n positionOverlay();\n }\n\n // Reposition on scroll/resize to keep arrow attached\n window.addEventListener('scroll', () => {\n if (overlay.classList.contains('active')) {\n positionOverlay();\n }\n }, true);\n \n window.addEventListener('resize', () => {\n if (overlay.classList.contains('active')) {\n positionOverlay();\n }\n });\n\n function closeOverlay() {\n overlay.classList.remove('active');\n currentTrigger = null;\n }\n\n // Click handlers\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) closeOverlay();\n });\n\n // Escape key\n document.addEventListener('keydown', (e) => {\n if (e.key === 'Escape' && overlay.classList.contains('active')) {\n closeOverlay();\n }\n });\n\n // Click to open overlay\n document.addEventListener('click', (e) => {\n const link = e.target.closest('.weave-node-link, .weave-overlay-anchor');\n \n // Close overlay if clicking outside\n if (!link && !e.target.closest('.weave-overlay')) {\n if (overlay.classList.contains('active')) {\n closeOverlay();\n }\n return;\n }\n \n if (!link) return;\n\n const display = link.dataset.display;\n const nodeId = link.dataset.nodeId;\n\n if (display === 'overlay' && nodeId) {\n e.preventDefault();\n \n // Toggle overlay if clicking same trigger\n if (overlay.classList.contains('active') && currentTrigger === link) {\n closeOverlay();\n } else {\n openOverlay(nodeId, link);\n }\n }\n });\n\n // Inline expand/collapse handling\n document.addEventListener('click', (e) => {\n const trigger = e.target.closest('.weave-inline-trigger, .weave-inline-anchor');\n if (!trigger) return;\n\n e.preventDefault();\n \n const inlineId = trigger.dataset.inlineId;\n let content = document.getElementById('weave-inline-' + inlineId);\n \n // If content doesn't exist, create it\n if (!content) {\n const section = sections[inlineId];\n if (!section) return;\n \n content = document.createElement('div');\n content.id = 'weave-inline-' + inlineId;\n content.className = 'weave-inline-content';\n content.innerHTML = section.html;\n content.style.display = 'none';\n \n // Insert after the parent paragraph\n const paragraph = trigger.closest('p');\n if (paragraph && paragraph.parentNode) {\n paragraph.parentNode.insertBefore(content, paragraph.nextSibling);\n }\n }\n \n // Toggle visibility\n const isHidden = content.style.display === 'none';\n content.style.display = isHidden ? 'block' : 'none';\n trigger.classList.toggle('expanded', isHidden);\n });\n\n // Inline substitution click handling\n document.addEventListener('click', (e) => {\n const sub = e.target.closest('.weave-sub');\n if (!sub || sub.classList.contains('expanded')) return;\n\n e.preventDefault();\n const encodedReplacement = sub.dataset.replacementB64;\n if (encodedReplacement) {\n // Decode base64 with proper UTF-8 handling\n const binary = atob(encodedReplacement);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n const replacement = new TextDecoder().decode(bytes);\n sub.innerHTML = replacement;\n sub.classList.add('expanded');\n }\n });\n\n // Footnote backlink tracking - remember which reference was clicked\n document.addEventListener('click', (e) => {\n const link = e.target.closest('.weave-footnote-ref a, a.weave-footnote-link');\n if (!link) return;\n \n const refId = link.id;\n if (!refId) return;\n \n // Extract footnote number from the href (e.g., #fn-3 -> 3)\n const href = link.getAttribute('href');\n if (!href || !href.startsWith('#fn-')) return;\n const fnNum = href.replace('#fn-', '');\n \n // Find the backref link in the footnote and update it\n const backref = document.querySelector('#fn-' + fnNum + ' .weave-footnote-backref');\n if (backref) {\n backref.setAttribute('href', '#' + refId);\n }\n \n // Navigate to the footnote\n e.preventDefault();\n const footnoteId = 'fn-' + fnNum;\n const footnote = document.getElementById(footnoteId);\n if (footnote) {\n footnote.scrollIntoView({ behavior: 'smooth' });\n // Update URL fragment without triggering navigation\n history.pushState(null, '', '#' + footnoteId);\n }\n });\n\n // Backref click - scroll and clear hash\n document.addEventListener('click', (e) => {\n const backref = e.target.closest('.weave-footnote-backref');\n if (!backref) return;\n \n e.preventDefault();\n const href = backref.getAttribute('href');\n if (!href) return;\n \n const target = document.getElementById(href.replace('#', ''));\n if (target) {\n target.scrollIntoView({ behavior: 'smooth' });\n history.pushState(null, '', window.location.pathname + window.location.search);\n }\n });\n\n // Video start time handling\n document.querySelectorAll('video[data-start]').forEach(video => {\n const startTime = parseFloat(video.dataset.start);\n if (!isNaN(startTime)) {\n video.currentTime = startTime;\n video.addEventListener('loadedmetadata', () => {\n video.currentTime = startTime;\n }, { once: true });\n }\n });\n\n // Gallery carousel initialization\n document.querySelectorAll('.weave-gallery').forEach(gallery => {\n const figures = gallery.querySelectorAll('figure');\n if (figures.length <= 1) return;\n \n // Set first figure as active\n figures[0].classList.add('active');\n \n // Create navigation buttons\n const prevBtn = document.createElement('button');\n prevBtn.className = 'weave-gallery-nav weave-gallery-prev';\n prevBtn.innerHTML = '&#10094;';\n prevBtn.setAttribute('aria-label', 'Previous');\n \n const nextBtn = document.createElement('button');\n nextBtn.className = 'weave-gallery-nav weave-gallery-next';\n nextBtn.innerHTML = '&#10095;';\n nextBtn.setAttribute('aria-label', 'Next');\n \n gallery.insertBefore(prevBtn, gallery.firstChild);\n gallery.insertBefore(nextBtn, gallery.querySelector('figcaption') || null);\n \n // Create dots\n const dotsContainer = document.createElement('div');\n dotsContainer.className = 'weave-gallery-dots';\n figures.forEach((_, i) => {\n const dot = document.createElement('button');\n dot.className = 'weave-gallery-dot' + (i === 0 ? ' active' : '');\n dot.setAttribute('aria-label', 'Go to slide ' + (i + 1));\n dot.dataset.index = i;\n dotsContainer.appendChild(dot);\n });\n const caption = gallery.querySelector('figcaption');\n if (caption) {\n gallery.insertBefore(dotsContainer, caption);\n } else {\n gallery.appendChild(dotsContainer);\n }\n \n let current = 0;\n \n const showSlide = (index) => {\n figures.forEach((f, i) => f.classList.toggle('active', i === index));\n dotsContainer.querySelectorAll('.weave-gallery-dot').forEach((d, i) => \n d.classList.toggle('active', i === index)\n );\n current = index;\n };\n \n prevBtn.addEventListener('click', () => {\n showSlide((current - 1 + figures.length) % figures.length);\n });\n \n nextBtn.addEventListener('click', () => {\n showSlide((current + 1) % figures.length);\n });\n \n dotsContainer.addEventListener('click', (e) => {\n const dot = e.target.closest('.weave-gallery-dot');\n if (dot) showSlide(parseInt(dot.dataset.index));\n });\n });\n </script>\n</body>\n</html>\n";
export declare function renderTemplate(options: {

@@ -6,0 +6,0 @@ title: string;

@@ -121,3 +121,40 @@ /**

/* Inline substitution */
.weave-sub {
color: #0066cc;
text-decoration: none;
border-bottom: 1px solid #0066cc;
cursor: pointer;
}
.weave-sub:hover {
background: #f0f7ff;
}
.weave-sub.expanded {
color: inherit;
border-bottom: none;
cursor: default;
background: none;
}
/* Nested subs inside expanded subs should still be styled as links */
.weave-sub.expanded .weave-sub:not(.expanded) {
color: #0066cc;
border-bottom: 1px solid #0066cc;
cursor: pointer;
}
/* Redacted style - black blocks that are still clickable */
.weave-sub-redacted:not(.expanded) {
color: inherit;
border-bottom: none;
cursor: pointer;
background: none;
}
.weave-sub-redacted:not(.expanded):hover {
opacity: 0.7;
}
.weave-inline-content {

@@ -665,2 +702,22 @@ display: block;

// Inline substitution click handling
document.addEventListener('click', (e) => {
const sub = e.target.closest('.weave-sub');
if (!sub || sub.classList.contains('expanded')) return;
e.preventDefault();
const encodedReplacement = sub.dataset.replacementB64;
if (encodedReplacement) {
// Decode base64 with proper UTF-8 handling
const binary = atob(encodedReplacement);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
const replacement = new TextDecoder().decode(bytes);
sub.innerHTML = replacement;
sub.classList.add('expanded');
}
});
// Footnote backlink tracking - remember which reference was clicked

@@ -667,0 +724,0 @@ document.addEventListener('click', (e) => {

{
"name": "@weave-md/basic",
"version": "0.2.0-alpha.0",
"version": "0.3.0-alpha.0",
"description": "Reference implementation of Weave Markdown - CLI, renderer, and exporter",

@@ -20,2 +20,6 @@ "type": "module",

],
"scripts": {
"build": "tsc",
"test": "cd ../.. && pnpm vitest run packages/basic/test"
},
"keywords": [

@@ -36,11 +40,11 @@ "weave",

"peerDependencies": {
"@weave-md/core": "0.2.0-alpha.0"
"@weave-md/core": "workspace:*"
},
"dependencies": {
"@weave-md/parse": "workspace:*",
"@weave-md/validate": "workspace:*",
"hast-util-to-html": "^9.0.5",
"katex": "^0.16.9",
"mdast-util-to-hast": "^13.2.1",
"unist-util-visit": "^5.0.0",
"@weave-md/parse": "0.2.0-alpha.0",
"@weave-md/validate": "0.2.0-alpha.0"
"unist-util-visit": "^5.0.0"
},

@@ -51,10 +55,6 @@ "devDependencies": {

"@types/node": "^20.10.0",
"@weave-md/core": "workspace:*",
"typescript": "^5.7.0",
"vitest": "^1.6.0",
"@weave-md/core": "0.2.0-alpha.0"
},
"scripts": {
"build": "tsc",
"test": "cd ../.. && pnpm vitest run packages/basic/test"
"vitest": "^1.6.0"
}
}
}
MIT License
Copyright (c) 2025 weavepage
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.