diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 072482b..8274af9 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -4,6 +4,7 @@ on:
push:
branches:
- master
+ - slatehtml
jobs:
build:
runs-on: ubuntu-latest
@@ -11,15 +12,15 @@ jobs:
permissions:
contents: write
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Setup Node.js
- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4
with:
- node-version: '18'
+ node-version: "22"
- - name: Build docs
- run: node build-docs.js
+ - name: Build docs index
+ run: node build-docs.cjs
- name: Check for changes
id: check_changes
diff --git a/.gitignore b/.gitignore
index 30bc162..dd6e803 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
-/node_modules
\ No newline at end of file
+node_modules/
+dist/
+*.log
+.DS_Store
diff --git a/README.md b/README.md
index 7066ae1..b29ea6a 100644
--- a/README.md
+++ b/README.md
@@ -1,247 +1,83 @@
# Docs Viewer
-A modern, accessible documentation viewer for Markdown files with live search, navigation, and mobile support.
+Static documentation viewer built on **slatehtml** + **slatehtml-ui**: panel shell, live search, wiki links, and markdown as slate blocks (inline via `slate-rich-text`).
-## Features
+## Install (consumer sites)
-- 🔍 **Live search** with keyboard shortcuts (Alt+S) and result caching
-- 📱 **Mobile-friendly** responsive design with focus trap
-- 🎯 **Keyboard navigation** support (arrow keys, Enter, Escape)
-- 📑 **Auto-generated document outline** with collapsible headers
-- 🔗 **Wiki-style internal linking** with `[[Page Title]]` syntax
-- 🖨️ **Print-friendly** styling
-- ♿ **ARIA-compliant accessibility** (aria-current, role attributes, focus management)
-- 🌙 **Dark theme** with CSS custom properties
-- ⚡ **Performance optimized** with document caching and lazy loading
-- 📊 **Loading indicators** with animated progress bars
-
-
-## Screenshots
-
-
-
-
-
-
-
-
-
-
-
-## Quick Start
-
-1. Install dependencies:
```bash
-npm install
+npm install github:litruv/Docs-Viewer#slatehtml
+# peers / siblings as needed:
+npm install slatehtml slatehtml-ui
```
-2. Copy the example configuration:
-```bash
-cp example.index.json index.json
-```
-Then modify `index.json` with your site's metadata, author info, and social links.
-
-3. Create your documentation structure:
-```
-docs/
- ├── images/ # Place images here
- ├── index.md # Main landing page
- └── ... other .md files
-```
-
-4. Build the documentation index:
-```bash
-npm run build
-```
-This will scan your docs folder and update `index.json` with the document structure.
-
-5. Start the development server:
-```bash
-npm start
-```
-
-## Documentation Structure
-
-### File Organization
-
-- Place all documentation files in the `docs/` directory
-- Store images and video in `docs/images/`
-- Use `.md` extension for Markdown files
-
-### Markdown Files
-
-Each Markdown file can include YAML frontmatter:
-
-```markdown
----
-title: Page Title
-description: Page description
-sort: 1 # Optional: controls sidebar order
-thumbnail: images/thumb.png # Optional: for OG images
----
-
-# Content starts here
-```
-
-### Folder Structure
-
-To create sections, make a folder and add a matching Markdown file:
-
-```
-docs/
- ├── getting-started/
- │ ├── getting-started.md # Folder index
- │ ├── installation.md
- │ └── configuration.md
- └── index.md
-```
-
-## Special Features
-
-### Wiki Links
-
-Use double brackets for internal links:
-```markdown
-[[Page Title]]
-[[Page Title|Custom Text]]
-```
-
-### Images
-
-Store images in `docs/images/` and reference them:
-```markdown
-
-# or
-![[picture.png]]
-```
-
-### Headers
-
-Headers are automatically added to the right sidebar outline and are collapsible.
-
-## Development
-
-### Recommended Editor
-
-We recommend using [Obsidian.md](https://obsidian.md) as your editor for the documentation files. The `docs/.obsidian` directory includes a custom plugin that provides enhanced editing features:
-
-- Displays page titles in the file explorer instead of filenames
-- Shows frontmatter-defined sort order in the file list
-- Automatically updates file ordering based on the `sort` property
-- Makes folder and document organization more intuitive
-
-To use the plugin:
-1. Open the `docs` folder as an Obsidian vault
-2. The plugin will be automatically loaded
-3. The file explorer will now show your document titles and sorting order
-
-### Project Structure
-
-```
-.
-├── docs/ # Documentation files
-├── js/ # Application modules
-│ ├── EventBus.js # Pub-sub event system
-│ ├── IndexService.js # Document index management
-│ ├── SearchService.js # Search with caching
-│ ├── DOMService.js # DOM manipulation & accessibility
-│ ├── DocumentService.js # Markdown loading & caching
-│ ├── NavigationService.js # Browser history handling
-│ └── Documentation.js # Main orchestrator
-├── build-docs.js # Documentation builder
-├── index.html # Main viewer
-└── styles.css # Styling
-```
-
-### Architecture
-
-The application uses a modular ES6 architecture with:
-
-- **EventBus**: Pub-sub pattern for decoupled communication between services
-- **Services**: Single-responsibility modules for search, DOM, documents, and navigation
-- **Caching**: LRU caches for search results (50 entries) and documents (20 entries)
-- **Lazy Loading**: Images use `loading="lazy"` for improved performance
-
-
-### Building
-
-The build process:
-1. Scans the `docs/` directory
-2. Edits `index.json` with document metadata
-
-A GitHub Actions workflow is included that automatically:
-- Runs on every push to the master branch
-- Executes the build process
-- Commits and pushes any changes to `index.json`
-- Ensures your documentation index stays in sync with your content
-
-This means you can edit your documentation directly on GitHub, and the index will be automatically updated.
-
-If you don't want to use Github actions, you can use npm run build
-
-### Cloudflare Pages
-
-1. Create a new repository on GitHub.
-2. Push your code to the repository.
-3. Go to [Cloudflare Pages](https://pages.cloudflare.com/) and connect your GitHub repository.
-4. Configure the build settings:
- - **Production branch:** `main` (or your main branch name)
- - **Build command:** Leave empty
- - **Build output directory:** `/` (root)
-5. Save and deploy.
-
-#### Optional: Cloudflare Worker for OG/Twitter Tags
-
-For improved SEO and social sharing, you can use a Cloudflare Worker to dynamically generate OG/Twitter tags.
-
-1. Create a new Cloudflare Worker using the code in `cloudflare-worker.js`.
-2. Set the `SITE_URL` environment variable to where your site will be located, e.g., `https://example.com/docs/`
-3. Set the `DOCS_URL` environment variable to the URL where your documentation files are hosted (usually your Cloudflare Pages URL).
-4. Configure a route in your Cloudflare account to route all requests to your Cloudflare Pages site through the worker.
-
-## Configuration
-
-### index.json
+Local sibling checkout:
```json
{
- "defaultPage": "home",
- "showDocsLink": true,
- "metadata": {
- "title": "Site Title",
- "description": "Site description",
- "site_name": "Documentation"
- },
- "author": {
- "name": "Author Name",
- "role": "Role",
- "socials": [
- {
- "icon": "fab fa-github",
- "url": "https://github.com/username",
- "title": "GitHub"
- }
- ]
+ "dependencies": {
+ "docs-viewer": "file:../Docs-Viewer",
+ "slatehtml": "file:../slatehtml",
+ "slatehtml-ui": "file:../slatehtml/packages/slatehtml-ui"
}
}
```
-The `showDocsLink` option controls whether a "Docs" link is shown in the UI navigation. Set to `false` to hide it.
+## App usage
-The build process generates the documents part for `index.json`
-## Contributing
+```html
+
+
+
+
+```
-1. Fork the repository
-2. Create a feature branch
-3. Submit a pull request
+```js
+import "slatehtml";
+import "slatehtml-ui/app-bar";
+import "slatehtml-ui/icon";
+import "slatehtml-ui/text-field";
+import "slatehtml-ui/side-bar";
+import "slatehtml-ui/text";
+import "slatehtml-ui/progress";
+import "slatehtml-ui/alert";
+import "slatehtml-ui/divider";
+import { configure } from "slatehtml-ui/configure";
+import { fontAwesomeSvg } from "slatehtml-ui/icons/fontawesome";
+import { mountDocs } from "docs-viewer";
+import "docs-viewer/prose.css";
-## Dependencies
+configure({ icons: fontAwesomeSvg });
+await mountDocs("slate-docs-viewer");
+```
-- [marked.js](https://marked.js.org/) - Markdown parsing
-- [highlight.js](https://highlightjs.org/) - Syntax highlighting
-- [Font Awesome](https://fontawesome.com/) - Icons
+Vite needs the UMC plugin with UI + this package’s `src`:
-## License
+```js
+import { umc } from "slatehtml/umc/vite";
+import { createRequire } from "node:module";
+import { dirname, join } from "node:path";
-MIT License - see LICENSE file for details.
+const require = createRequire(import.meta.url);
+const uiSrc = join(dirname(require.resolve("slatehtml-ui/package.json")), "src");
+const docsSrc = join(dirname(require.resolve("docs-viewer/package.json")), "src");
+export default {
+ plugins: [umc({ roots: [uiSrc, docsSrc] })],
+};
+```
+
+## Develop this repo
+
+```bash
+npm install # expects ../slatehtml checkout
+npm run build:index
+npm run dev
+```
+
+## Content contract
+
+Same as before:
+
+- `docs/**/*.md` + YAML frontmatter
+- `index.json` metadata; `documents` from `npm run build:index`
+- Wiki `[[Page Title]]`, embeds `![[image.png]]`
diff --git a/build-docs.js b/build-docs.cjs
similarity index 100%
rename from build-docs.js
rename to build-docs.cjs
diff --git a/demo/main.js b/demo/main.js
new file mode 100644
index 0000000..6c2d543
--- /dev/null
+++ b/demo/main.js
@@ -0,0 +1,24 @@
+import "slatehtml";
+
+import "slatehtml-ui/icon";
+import "slatehtml-ui/side-bar";
+import "slatehtml-ui/progress";
+import "slatehtml-ui/alert";
+import "slatehtml-ui/divider";
+import "slatehtml-ui/bottom-nav";
+import "slatehtml-ui/bottom-nav-item";
+import "slatehtml-ui/collapse";
+
+import { configure } from "slatehtml-ui/configure";
+import { fontAwesomeSvg } from "slatehtml-ui/icons/fontawesome";
+import { mountDocs } from "../src/index.js";
+import "../src/prose.css";
+import "./site.css";
+
+configure({
+ icons: fontAwesomeSvg,
+ iconSize: "15",
+});
+
+await mountDocs("slate-docs-viewer", { indexUrl: "./index.json" });
+document.documentElement.setAttribute("data-ready", "");
diff --git a/demo/site.css b/demo/site.css
new file mode 100644
index 0000000..000e89d
--- /dev/null
+++ b/demo/site.css
@@ -0,0 +1,26 @@
+/* Match live Docs-Viewer tokens (lit.ruv.wtf/docs). */
+
+:root {
+ --bg: #1a1a1a;
+ --ink: #e0e0e0;
+ --muted: #a0a0a0;
+ --panel: #252526;
+ --line: #404040;
+ --accent: #2196f3;
+ --docs-accent: #2196f3;
+ --docs-panel: #252526;
+ --docs-line: #404040;
+ --docs-muted: #a0a0a0;
+ --slate-font: system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans",
+ sans-serif;
+ --slate-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
+ color-scheme: dark;
+}
+
+body {
+ margin: 0;
+ min-height: 100%;
+ background: var(--bg);
+ color: var(--ink);
+ font-family: var(--slate-font);
+}
diff --git a/index.html b/index.html
index b43727f..acfdf00 100644
--- a/index.html
+++ b/index.html
@@ -1,179 +1,24 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Skip to content
-
-
-
-
-
-

-
/
-
Documentation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ Docs Viewer
+
+
+
+
+
+
diff --git a/index.json b/index.json
new file mode 100644
index 0000000..681edbf
--- /dev/null
+++ b/index.json
@@ -0,0 +1,37 @@
+{
+ "defaultPage": "home",
+ "customCSS": "custom/theme.css",
+ "metadata": {
+ "title": "Project Documentation",
+ "description": "Comprehensive documentation for various projects.",
+ "thumbnail": "img/default-thumbnail.png",
+ "site_name": "DocsHub"
+ },
+ "author": {
+ "name": "John Doe",
+ "role": "Software Engineer",
+ "socials": [
+ {
+ "icon": "fab fa-github",
+ "url": "https://github.com/johndoe",
+ "title": "GitHub - JohnDoe"
+ },
+ {
+ "icon": "fab fa-youtube",
+ "url": "https://youtube.com/c/JohnDoe",
+ "title": "YouTube - JohnDoe"
+ },
+ {
+ "icon": "fab fa-discord",
+ "url": "https://discordapp.com/users/1234567890",
+ "title": "Discord - @JohnDoe"
+ },
+ {
+ "icon": "fa-brands fa-bluesky",
+ "url": "https://bsky.app/profile/john.doe.dev",
+ "title": "Bluesky - john.doe.dev"
+ }
+ ]
+ },
+ "documents": []
+}
\ No newline at end of file
diff --git a/js/DOMService.js b/js/DOMService.js
deleted file mode 100644
index 88d68d0..0000000
--- a/js/DOMService.js
+++ /dev/null
@@ -1,576 +0,0 @@
- /**
- * Shows or hides the Docs link in the sidebar based on config.
- * @param {boolean} show - Whether to show the Docs link.
- */
- setShowDocsLink(show) {
- const docsLink = document.querySelector('.github-link');
- if (docsLink) {
- docsLink.style.display = show ? '' : 'none';
- }
- }
-/**
- * Service responsible for DOM manipulation and UI rendering.
- * @class DOMService
- */
-export class DOMService {
- /**
- * Creates a new DOMService instance.
- * @param {EventBus} eventBus - The event bus for communication.
- */
- constructor(eventBus) {
- /** @type {EventBus} */
- this.eventBus = eventBus;
-
- /** @type {DOMElements} */
- this.elements = {
- content: document.getElementById('document-content'),
- outline: document.getElementById('document-outline'),
- fileIndex: document.getElementById('file-index'),
- titleText: document.querySelector('.title-text .page-title'),
- leftSidebar: document.querySelector('.left-sidebar'),
- menuButton: document.querySelector('.menu-button'),
- header: document.querySelector('title-bar'),
- searchInput: document.getElementById('search-input'),
- searchResults: document.getElementById('search-results'),
- clearSearch: document.getElementById('clear-search')
- };
-
- /** @type {number} */
- this.headerOffset = 60;
- }
-
- /**
- * Sets up the mobile menu toggle functionality.
- */
- setupMobileMenu() {
- this.elements.menuButton.addEventListener('click', () => {
- const isExpanded = this.elements.leftSidebar.classList.toggle('show');
- this.elements.menuButton.setAttribute('aria-expanded', isExpanded ? 'true' : 'false');
-
- if (isExpanded) {
- this.enableFocusTrap();
- } else {
- this.disableFocusTrap();
- }
- });
-
- this.elements.content.addEventListener('click', () => {
- this.elements.leftSidebar.classList.remove('show');
- this.elements.menuButton.setAttribute('aria-expanded', 'false');
- this.disableFocusTrap();
- });
- }
-
- /**
- * Enables focus trap within the mobile sidebar.
- * @private
- */
- enableFocusTrap() {
- this.focusTrapHandler = (e) => {
- if (window.innerWidth > 1000) return;
- if (!this.elements.leftSidebar.classList.contains('show')) return;
-
- const focusableElements = this.elements.leftSidebar.querySelectorAll(
- 'a[href], button, input, [tabindex]:not([tabindex="-1"])'
- );
- const firstFocusable = focusableElements[0];
- const lastFocusable = focusableElements[focusableElements.length - 1];
-
- if (e.key === 'Tab') {
- if (e.shiftKey && document.activeElement === firstFocusable) {
- e.preventDefault();
- lastFocusable.focus();
- } else if (!e.shiftKey && document.activeElement === lastFocusable) {
- e.preventDefault();
- firstFocusable.focus();
- }
- }
-
- if (e.key === 'Escape') {
- this.elements.leftSidebar.classList.remove('show');
- this.elements.menuButton.setAttribute('aria-expanded', 'false');
- this.elements.menuButton.focus();
- this.disableFocusTrap();
- }
- };
-
- document.addEventListener('keydown', this.focusTrapHandler);
-
- const firstFocusable = this.elements.leftSidebar.querySelector(
- 'a[href], button, input, [tabindex]:not([tabindex="-1"])'
- );
- if (firstFocusable) {
- firstFocusable.focus();
- }
- }
-
- /**
- * Disables the focus trap.
- * @private
- */
- disableFocusTrap() {
- if (this.focusTrapHandler) {
- document.removeEventListener('keydown', this.focusTrapHandler);
- this.focusTrapHandler = null;
- }
- }
-
- /**
- * Sets the main content area HTML.
- * @param {string} html - The HTML content to render.
- */
- setContent(html) {
- this.elements.content.innerHTML = html;
- this.elements.content.className = 'markdown-content';
- hljs.highlightAll();
- }
-
- /**
- * Sets the document title and page title display.
- * @param {string} title - The title to set.
- */
- setTitle(title) {
- document.title = `${window.originalDocTitle} / ${title}`;
- this.elements.titleText.textContent = title;
- }
-
- /**
- * Displays an error message in the content area.
- * @param {string} message - The error message to display.
- */
- setError(message) {
- this.elements.content.innerHTML = `${message}
`;
- }
-
- /**
- * Creates a file index item (folder or file) in the sidebar.
- * @param {Document} doc - The document to create an item for.
- * @param {HTMLElement} container - The container element to append to.
- * @param {number} [level=0] - The nesting level for indentation.
- */
- createFileIndexItem(doc, container, level = 0) {
- if (doc.type === 'folder') {
- this.createFolderItem(doc, container, level);
- } else {
- this.createFileItem(doc, container, level);
- }
- }
-
- /**
- * Creates a folder item in the sidebar.
- * @param {Document} doc - The folder document.
- * @param {HTMLElement} container - The container element.
- * @param {number} level - The nesting level.
- * @private
- */
- createFolderItem(doc, container, level) {
- const folderDiv = document.createElement('div');
- const isOpen = doc.defaultOpen === true;
- folderDiv.className = 'folder' + (isOpen ? ' open' : '');
- folderDiv.dataset.path = doc.title;
- folderDiv.style.paddingLeft = `${level * 0.8}rem`;
-
- const folderHeader = document.createElement('div');
- folderHeader.className = 'folder-header';
- folderHeader.setAttribute('role', 'treeitem');
- folderHeader.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
- folderHeader.setAttribute('tabindex', '0');
-
- const iconClass = doc.icon || `fas fa-folder${isOpen ? '-open' : ''}`;
-
- if (doc.path && doc.metadata?.showfolderpage !== false) {
- folderHeader.innerHTML = this.createFolderHeaderWithFile(iconClass, doc);
- this.setupFolderListeners(folderDiv, folderHeader, doc);
- } else {
- folderHeader.innerHTML = this.createFolderHeaderBasic(iconClass, doc);
- this.setupBasicFolderListeners(folderDiv, folderHeader, doc);
- }
-
- const folderContent = document.createElement('div');
- folderContent.className = 'folder-content';
- doc.items.forEach(item => this.createFileIndexItem(item, folderContent, level + 1));
-
- folderDiv.appendChild(folderHeader);
- folderDiv.appendChild(folderContent);
- container.appendChild(folderDiv);
- }
-
- /**
- * Sets up listeners for basic folder toggle (no folder page).
- * @param {HTMLElement} folderDiv - The folder container element.
- * @param {HTMLElement} folderHeader - The folder header element.
- * @param {Document} doc - The folder document.
- * @private
- */
- setupBasicFolderListeners(folderDiv, folderHeader, doc) {
- folderHeader.addEventListener('click', () => {
- folderDiv.classList.toggle('open');
- const isExpanded = folderDiv.classList.contains('open');
- folderHeader.setAttribute('aria-expanded', isExpanded ? 'true' : 'false');
- if (!doc.icon) {
- const icon = folderHeader.querySelector('.folder-icon');
- icon.classList.toggle('fa-folder-closed');
- icon.classList.toggle('fa-folder-open');
- }
- });
-
- folderHeader.addEventListener('keydown', (e) => {
- if (e.key === 'Enter' || e.key === ' ') {
- e.preventDefault();
- folderHeader.click();
- }
- });
- }
-
- /**
- * Creates a file item link in the sidebar.
- * @param {Document} doc - The file document.
- * @param {HTMLElement} container - The container element.
- * @param {number} level - The nesting level.
- * @private
- */
- createFileItem(doc, container, level) {
- const link = document.createElement('a');
- link.href = `?${doc.slug}`;
- link.textContent = doc.title || doc.path.split('/').pop().replace('.md', '');
- link.dataset.path = doc.path;
- link.dataset.slug = doc.slug;
- link.style.paddingLeft = `${(level * 0.6) + 0.8}rem`;
- link.setAttribute('role', 'treeitem');
-
- link.onclick = (e) => {
- e.preventDefault();
- this.setLinkLoading(link);
- this.eventBus.emit('navigation:requested', { slug: doc.slug });
- history.pushState(null, '', link.href);
- if (window.innerWidth <= 1000) {
- this.elements.leftSidebar.classList.remove('show');
- }
- };
-
- container.appendChild(link);
- }
-
- /**
- * Sets a link to loading state.
- * @param {HTMLAnchorElement} link - The link element.
- */
- setLinkLoading(link) {
- this.clearAllLoading();
- link.classList.add('loading');
- }
-
- /**
- * Clears loading state from all links.
- */
- clearAllLoading() {
- this.elements.fileIndex.querySelectorAll('a.loading, .folder-link.loading').forEach(link => {
- link.classList.remove('loading');
- });
- }
-
- /**
- * Updates the active document highlight in the sidebar.
- * @param {string} path - The path of the active document.
- */
- updateActiveDocument(path) {
- this.clearAllLoading();
- this.elements.fileIndex.querySelectorAll('a').forEach(link => {
- const isActive = link.dataset.path === path;
- link.classList.toggle('active', isActive);
- if (isActive) {
- link.setAttribute('aria-current', 'page');
- } else {
- link.removeAttribute('aria-current');
- }
- });
- }
-
- /**
- * Creates the document outline from headings.
- * @param {NodeListOf} headings - The heading elements.
- * @returns {Map} Map of headings to their outline links.
- */
- createOutline(headings) {
- this.elements.outline.innerHTML = '';
- const headingLinks = new Map();
-
- headings.forEach(heading => {
- if (!heading.id) {
- heading.id = heading.textContent.toLowerCase()
- .replace(/[^\w\s-]/g, '')
- .replace(/\s+/g, '-');
- }
-
- const link = this.createOutlineLink(heading);
- headingLinks.set(heading, link);
- this.elements.outline.appendChild(link);
- this.addHeadingFoldToggle(heading);
- });
-
- return headingLinks;
- }
-
- /**
- * Creates HTML for a folder header with a file link.
- * @param {string} iconClass - The icon CSS class.
- * @param {Document} doc - The folder document.
- * @returns {string} The HTML string.
- * @private
- */
- createFolderHeaderWithFile(iconClass, doc) {
- const showFolderPage = doc.showfolderpage !== 'false';
- return `
-
-
-
- ${doc.title}
- ${showFolderPage ? `
-
-
- ` : ''}`;
- }
-
- /**
- * Creates HTML for a basic folder header without file link.
- * @param {string} iconClass - The icon CSS class.
- * @param {Document} doc - The folder document.
- * @returns {string} The HTML string.
- * @private
- */
- createFolderHeaderBasic(iconClass, doc) {
- return `
-
-
-
- ${doc.title}`;
- }
-
- /**
- * Sets up event listeners for a folder with a navigable page.
- * @param {HTMLElement} folderDiv - The folder container element.
- * @param {HTMLElement} folderHeader - The folder header element.
- * @param {Document} doc - The folder document.
- * @private
- */
- setupFolderListeners(folderDiv, folderHeader, doc) {
- folderHeader.addEventListener('click', (e) => {
- if (e.target.closest('.folder-link')) {
- e.preventDefault();
- const folderLink = e.target.closest('.folder-link');
- this.setFolderLinkLoading(folderLink);
- this.eventBus.emit('navigation:requested', { slug: doc.slug });
- history.pushState(null, '', `?${doc.slug}`);
- if (window.innerWidth <= 1000) {
- this.elements.leftSidebar.classList.remove('show');
- }
- return;
- }
-
- folderDiv.classList.toggle('open');
- const isExpanded = folderDiv.classList.contains('open');
- folderHeader.setAttribute('aria-expanded', isExpanded ? 'true' : 'false');
- if (!doc.icon) {
- const icon = folderHeader.querySelector('.folder-icon');
- icon.classList.toggle('fa-folder-closed');
- icon.classList.toggle('fa-folder-open');
- }
- });
-
- folderHeader.addEventListener('keydown', (e) => {
- if (e.key === 'Enter' || e.key === ' ') {
- e.preventDefault();
- if (e.target.closest('.folder-link')) {
- e.target.closest('.folder-link').click();
- } else {
- folderHeader.click();
- }
- }
- });
- }
-
- /**
- * Sets a folder link to loading state.
- * @param {HTMLAnchorElement} link - The folder link element.
- */
- setFolderLinkLoading(link) {
- this.clearAllLoading();
- link.classList.add('loading');
- }
-
- /**
- * Scrolls smoothly to an element with header offset.
- * @param {HTMLElement} element - The element to scroll to.
- */
- scrollToElement(element) {
- if (!element) return;
- const rect = element.getBoundingClientRect();
- const absoluteElementTop = rect.top + window.scrollY;
- const middle = absoluteElementTop - (this.headerOffset + 20);
-
- window.scrollTo({
- top: middle,
- behavior: 'smooth'
- });
- }
-
- /**
- * Creates an outline link for a heading.
- * @param {HTMLHeadingElement} heading - The heading element.
- * @returns {HTMLAnchorElement} The created link element.
- * @private
- */
- createOutlineLink(heading) {
- const link = document.createElement('a');
- link.href = `${window.location.pathname}${window.location.search}#${heading.id}`;
- link.textContent = heading.textContent;
- link.style.paddingLeft = (heading.tagName[1] * 15) + 'px';
-
- link.onclick = (e) => {
- e.preventDefault();
- history.pushState(null, '', link.href);
- this.scrollToElement(heading);
- heading.classList.remove('highlight');
- void heading.offsetWidth;
- heading.classList.add('highlight');
- };
-
- return link;
- }
-
- /**
- * Adds a fold/unfold toggle button to a heading.
- * @param {HTMLHeadingElement} heading - The heading element.
- * @private
- */
- addHeadingFoldToggle(heading) {
- const toggleBtn = document.createElement('span');
- toggleBtn.innerHTML = ``;
- toggleBtn.style.cursor = 'pointer';
- toggleBtn.style.userSelect = 'none';
- toggleBtn.style.marginLeft = '0.5em';
- toggleBtn.style.display = 'inline-flex';
- toggleBtn.style.alignItems = 'center';
-
- toggleBtn.addEventListener('click', (e) => {
- e.stopPropagation();
- const svg = toggleBtn.querySelector('svg');
- const isFolded = svg.style.transform === 'rotate(90deg)';
- svg.style.transform = isFolded ? 'rotate(0deg)' : 'rotate(90deg)';
-
- let next = heading.nextElementSibling;
- const currentLevel = parseInt(heading.tagName[1]);
-
- while (next) {
- if (!/^H[1-6]$/.test(next.tagName)) {
- next.style.display = isFolded ? 'none' : '';
- next = next.nextElementSibling;
- } else {
- const nextLevel = parseInt(next.tagName[1]);
- if (nextLevel <= currentLevel) break;
- next.style.display = isFolded ? 'none' : '';
- next = next.nextElementSibling;
- }
- }
- });
-
- heading.appendChild(toggleBtn);
- }
-
- /**
- * Sets up search input event handlers.
- * @param {SearchService} searchService - The search service instance.
- */
- setupSearch(searchService) {
- let searchTimeout;
-
- this.elements.searchInput.addEventListener('input', (e) => {
- clearTimeout(searchTimeout);
- const query = e.target.value;
-
- searchTimeout = setTimeout(() => {
- const results = searchService.search(query);
- this.renderSearchResults(results);
- }, 200);
- });
-
- this.elements.clearSearch.addEventListener('click', () => {
- this.elements.searchInput.value = '';
- this.elements.searchResults.innerHTML = '';
- this.elements.searchResults.style.display = 'none';
- this.elements.searchResults.setAttribute('aria-hidden', 'true');
- });
- }
-
- /**
- * Renders search results in the dropdown.
- * @param {Array} results - The search results to render.
- */
- renderSearchResults(results) {
- const container = this.elements.searchResults;
- container.innerHTML = '';
-
- if (results.length === 0 || !this.elements.searchInput.value) {
- container.style.display = 'none';
- container.setAttribute('aria-hidden', 'true');
- return;
- }
-
- container.setAttribute('aria-hidden', 'false');
- const resultCount = results.length === 1 ? '1 search result found' : `${results.length} search results found`;
- container.setAttribute('aria-label', resultCount);
-
- results.forEach(result => {
- const div = document.createElement('div');
- div.className = 'search-result';
-
- const icon = document.createElement('i');
- icon.className = result.type === 'folder' ? 'fas fa-folder' :
- result.type === 'header' ? 'fas fa-hashtag' : 'fas fa-file-alt';
-
- const link = document.createElement('a');
- link.href = `?${result.slug}`;
- link.innerHTML = `
- ${icon.outerHTML}
-
-
${result.title}
-
${result.location}
-
- `;
-
- link.addEventListener('click', (e) => {
- e.preventDefault();
- this.elements.searchInput.value = '';
- container.style.display = 'none';
- const [baseSlug, hash] = result.slug.split('#');
- history.pushState(null, '', link.href);
- this.eventBus.emit('navigation:requested', {
- slug: baseSlug,
- hash: hash ? `#${hash}` : '',
- fromSearch: true
- });
- });
-
- div.appendChild(link);
- container.appendChild(div);
- });
-
- container.style.display = 'block';
- }
-}
-
-/**
- * @typedef {Object} DOMElements
- * @property {HTMLElement} content - The main content container.
- * @property {HTMLElement} outline - The document outline container.
- * @property {HTMLElement} fileIndex - The file index container.
- * @property {HTMLElement} titleText - The page title element.
- * @property {HTMLElement} leftSidebar - The left sidebar element.
- * @property {HTMLElement} menuButton - The mobile menu button.
- * @property {HTMLElement} header - The header element.
- * @property {HTMLInputElement} searchInput - The search input field.
- * @property {HTMLElement} searchResults - The search results container.
- * @property {HTMLElement} clearSearch - The clear search button.
- */
diff --git a/js/DocumentService.js b/js/DocumentService.js
deleted file mode 100644
index 1560840..0000000
--- a/js/DocumentService.js
+++ /dev/null
@@ -1,303 +0,0 @@
-/**
- * Service responsible for loading and processing markdown documents.
- * @class DocumentService
- */
-export class DocumentService {
- /**
- * Creates a new DocumentService instance.
- * @param {EventBus} eventBus - The event bus for communication.
- * @param {IndexService} indexService - The index service for document lookups.
- */
- constructor(eventBus, indexService) {
- /** @type {EventBus} */
- this.eventBus = eventBus;
- /** @type {IndexService} */
- this.indexService = indexService;
- /** @type {Promise} */
- this.markedPromise = this.initializeMarked();
- /** @type {Map} */
- this.documentCache = new Map();
- /** @type {number} */
- this.cacheMaxSize = 20;
- }
-
- /**
- * Initializes the marked library with custom renderer.
- * @returns {Promise} Promise resolving to the marked instance.
- * @private
- */
- async initializeMarked() {
- const marked = await new Promise((resolve) => {
- if (typeof window.marked !== 'undefined') {
- resolve(window.marked);
- } else {
- window.addEventListener('load', () => resolve(window.marked));
- }
- });
-
- const renderer = new marked.Renderer();
- this.setupRenderer(renderer);
-
- marked.setOptions({
- breaks: true,
- gfm: true,
- renderer: renderer
- });
-
- return marked;
- }
-
- /**
- * Configures the marked renderer with custom handlers.
- * @param {marked.Renderer} renderer - The renderer to configure.
- * @private
- */
- setupRenderer(renderer) {
- const originalLink = renderer.link.bind(renderer);
- const originalImage = renderer.image.bind(renderer);
-
- renderer.code = this.renderCode;
-
- renderer.image = (href, title, text) => {
- const titleAttr = title ? ` title="${title}"` : '';
- return `
`;
- };
-
- renderer.link = (href, title, text) => {
- const isExternal = href.startsWith('http');
- const attrs = isExternal ? ' target="_blank" rel="noopener noreferrer"' : '';
- const link = originalLink(href, title, text);
- if (!isExternal && href.startsWith('?')) {
- return link.replace(/^ {
- const escapedText = text.toLowerCase()
- .replace(/[^\w\s-]/g, '')
- .replace(/\s+/g, '-');
-
- const id = escapedText;
-
- return ``;
- };
- }
-
- /**
- * Renders a code block with syntax highlighting.
- * @param {string} code - The code content.
- * @param {string} language - The language for highlighting.
- * @returns {string} The rendered HTML.
- * @private
- */
- renderCode(code, language) {
- let highlighted;
- if (language && hljs.getLanguage(language)) {
- highlighted = hljs.highlight(code, { language }).value;
- } else {
- highlighted = hljs.highlightAuto(code).value;
- language = '';
- }
- return `${highlighted}
`;
- }
-
- /**
- * Extracts frontmatter metadata from document content.
- * @param {string} content - The raw document content.
- * @returns {ExtractedDocument} The extracted metadata and content.
- */
- extractMetadata(content) {
- const lines = content.trim().split('\n');
- let metadata = {};
- let contentStart = 0;
-
- if (lines[0].trim() === '---') {
- let endMetadata = lines.findIndex((line, index) => index > 0 && line.trim() === '---');
- if (endMetadata !== -1) {
- const frontmatterEntries = lines.slice(1, endMetadata)
- .map(line => line.match(/^([\w-]+):\s*(.*)$/))
- .filter(Boolean)
- .map(([, key, value]) => {
- if (key === 'defaultOpen') {
- return [key, value.trim().toLowerCase() === 'true'];
- } else if (key === 'sort') {
- return [key, parseInt(value.trim(), 10)];
- } else {
- return [key, value.trim()];
- }
- });
-
- metadata = Object.fromEntries(frontmatterEntries);
- contentStart = endMetadata + 1;
- }
- }
-
- return {
- metadata,
- content: lines.slice(contentStart).join('\n').trim()
- };
- }
-
- /**
- * Loads and processes a document from a path.
- * @param {string} path - The document path to load.
- * @returns {Promise} The loaded document data.
- * @throws {Error} If the document fails to load.
- */
- async loadDocument(path) {
- if (this.documentCache.has(path)) {
- return this.documentCache.get(path);
- }
-
- try {
- const [response, marked] = await Promise.all([
- fetch(path),
- this.markedPromise
- ]);
-
- let rawContent = await response.text();
- const { metadata, content } = this.extractMetadata(rawContent);
-
- if (metadata.defaultOpen !== undefined) {
- metadata.defaultOpen = metadata.defaultOpen === true || metadata.defaultOpen === 'true';
- }
-
- if (metadata.sort !== undefined) {
- metadata.sort = typeof metadata.sort === 'number' ? metadata.sort : parseInt(metadata.sort, 10);
- }
-
- const basePath = path.substring(0, path.lastIndexOf('/'));
- const indexDoc = this.findDocInIndex(path);
-
- let processedContent = this.processWikiLinks(content);
- processedContent = this.processImages(processedContent, basePath);
- const titleContent = metadata.title || indexDoc?.title || path.split('/').pop().replace('.md', '');
- processedContent = this.ensureTitle(processedContent, titleContent);
-
- const result = {
- content: processedContent,
- metadata,
- marked,
- title: titleContent
- };
-
- if (this.documentCache.size >= this.cacheMaxSize) {
- const firstKey = this.documentCache.keys().next().value;
- this.documentCache.delete(firstKey);
- }
- this.documentCache.set(path, result);
-
- return result;
- } catch (error) {
- throw new Error('Failed to load document');
- }
- }
-
- /**
- * Finds a document in the global index by path.
- * @param {string} path - The document path.
- * @returns {Document|null} The found document or null.
- * @private
- */
- findDocInIndex(path) {
- let doc = window._indexData.documents.find(d => d.path === path);
-
- if (!doc) {
- for (const d of window._indexData.documents) {
- if (d.type === 'folder' && d.items) {
- doc = d.items.find(item => item.path === path);
- if (doc) break;
- }
- }
- }
- return doc;
- }
-
- /**
- * Ensures the content has a title heading.
- * @param {string} content - The document content.
- * @param {string} title - The title to add.
- * @returns {string} The content with title.
- * @private
- */
- ensureTitle(content, title) {
- content = content.replace(/^#\s+.*$/m, '').trim();
- return `# ${title}\n\n${content}`;
- }
-
- /**
- * Processes wiki-style links [[Link]] into markdown links.
- * @param {string} content - The content to process.
- * @returns {string} The processed content.
- * @private
- */
- processWikiLinks(content) {
- return content.replace(/\[\[(.*?)\]\]/g, (match, linkText) => {
- const [targetTitle, displayText] = linkText.split('|').map(s => s.trim());
- if (targetTitle.match(/\.(png|jpg|jpeg|gif|mp4|webm)$/i)) {
- return match;
- }
-
- const doc = this.indexService.findDocumentByTitle(
- window._indexData.documents,
- targetTitle
- );
- return doc ? `[${displayText || doc.title}](?${doc.slug})` : match;
- });
- }
-
- /**
- * Processes wiki-style image embeds into HTML.
- * @param {string} content - The content to process.
- * @param {string} basePath - The base path for relative images.
- * @returns {string} The processed content.
- * @private
- */
- processImages(content, basePath) {
- return content.replace(/!\[\[(.*?)\]\]/g, (match, filename) => {
- const mediaPath = `./docs/images/${filename}`;
-
- if (filename.toLowerCase().endsWith('.mp4')) {
- return `\n\n\n`;
- }
-
- return `\n
\n\n`;
- });
- }
-
- /**
- * Clears the entire document cache.
- */
- clearCache() {
- this.documentCache.clear();
- }
-
- /**
- * Invalidates a specific document from the cache.
- * @param {string} path - The document path to invalidate.
- */
- invalidateDocument(path) {
- this.documentCache.delete(path);
- }
-}
-
-/**
- * @typedef {Object} ExtractedDocument
- * @property {Object} metadata - The document metadata.
- * @property {string} content - The document content without frontmatter.
- */
-
-/**
- * @typedef {Object} LoadedDocument
- * @property {string} content - The processed markdown content.
- * @property {Object} metadata - The document metadata.
- * @property {marked} marked - The marked instance.
- * @property {string} title - The document title.
- */
diff --git a/js/Documentation.js b/js/Documentation.js
deleted file mode 100644
index b7f32b5..0000000
--- a/js/Documentation.js
+++ /dev/null
@@ -1,381 +0,0 @@
-import { EventBus } from './EventBus.js';
-import { IndexService } from './IndexService.js';
-import { SearchService } from './SearchService.js';
-import { DOMService } from './DOMService.js';
-import { DocumentService } from './DocumentService.js';
-import { NavigationService } from './NavigationService.js';
-
-/**
- * Main documentation application class that orchestrates all services.
- * @class Documentation
- */
-class Documentation {
- constructor() {
- /** @type {EventBus} */
- this.eventBus = new EventBus();
- /** @type {IndexService} */
- this.indexService = new IndexService();
- /** @type {SearchService} */
- this.searchService = new SearchService(this.eventBus, this.indexService);
- /** @type {DOMService} */
- this.domService = new DOMService(this.eventBus);
- /** @type {DocumentService} */
- this.documentService = new DocumentService(this.eventBus, this.indexService);
- /** @type {NavigationService} */
- this.navigationService = new NavigationService(this.eventBus, this.documentService);
- /** @type {IndexData|null} */
- this.indexData = null;
-
- this.setupEventListeners();
- this.setupKeyboardShortcuts();
- }
-
- /**
- * Loads a custom CSS file if specified in the index.json.
- * @param {string} customCSSPath - Path to the custom CSS file relative to document root.
- */
- loadCustomCSS(customCSSPath) {
- if (!customCSSPath) return;
-
- const link = document.createElement('link');
- link.rel = 'stylesheet';
- link.type = 'text/css';
- link.href = customCSSPath;
- document.head.appendChild(link);
- }
-
- /**
- * Sets up application-wide event listeners.
- * @private
- */
- setupEventListeners() {
- this.eventBus.on('navigation:requested', async ({ slug, hash }) => {
- slug = slug.replace(/^[?=]/, '');
- await this.loadDocumentBySlug(slug, hash);
- });
-
- this.eventBus.on('document:load', async ({ path }) => {
- await this.loadDocument(path);
- });
-
- window.addEventListener('load', () => {
- this.domService.setupMobileMenu();
- this.initialize();
- });
-
- document.addEventListener('click', async (e) => {
- const target = e.target.closest('a[data-internal="true"]');
- if (target) {
- e.preventDefault();
- const slug = target.href.split('?').pop();
- history.pushState(null, '', target.href);
- await this.loadDocumentBySlug(slug);
- }
- });
- }
-
- /**
- * Sets up keyboard shortcuts for navigation and search.
- * @private
- */
- setupKeyboardShortcuts() {
- document.addEventListener('keydown', (e) => {
- const isTyping = ['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName) ||
- document.activeElement.isContentEditable;
-
- if (this.isSearchShortcut(e) && !isTyping) {
- e.preventDefault();
- this.focusSearch();
- }
-
- if ((e.key === 'ArrowUp' || e.key === 'ArrowDown') && e.altKey && !isTyping) {
- e.preventDefault();
- this.navigatePages(e.key === 'ArrowDown' ? 1 : -1);
- }
- });
- }
-
- /**
- * Checks if the key event is a search shortcut.
- * @param {KeyboardEvent} e - The keyboard event.
- * @returns {boolean} True if it's a search shortcut.
- * @private
- */
- isSearchShortcut(e) {
- return ((e.key === 's' || e.key === 'S') && !e.ctrlKey && !e.metaKey &&
- (!e.altKey || (e.altKey && (e.key === 's' || e.key === 'S'))));
- }
-
- /**
- * Focuses the search input and opens mobile sidebar if needed.
- * @private
- */
- focusSearch() {
- const searchInput = document.getElementById('search-input');
- if (searchInput) {
- searchInput.focus();
-
- const leftSidebar = document.querySelector('.left-sidebar');
- const menuButton = document.querySelector('.menu-button');
- if (window.innerWidth <= 1000 && leftSidebar && !leftSidebar.classList.contains('show')) {
- leftSidebar.classList.add('show');
- if (menuButton) {
- menuButton.setAttribute('aria-expanded', 'true');
- }
- }
- }
- }
-
- /**
- * Navigates to the next or previous page in the file index.
- * @param {number} direction - 1 for next, -1 for previous.
- * @private
- */
- navigatePages(direction) {
- const fileLinks = Array.from(document.querySelectorAll('#file-index a'));
- if (fileLinks.length === 0) return;
-
- const activeLink = document.querySelector('#file-index a.active');
- if (!activeLink) return;
-
- const activeIndex = fileLinks.indexOf(activeLink);
- if (activeIndex === -1) return;
-
- let targetIndex;
- if (direction === 1) {
- targetIndex = activeIndex < fileLinks.length - 1 ? activeIndex + 1 : 0;
- } else {
- targetIndex = activeIndex > 0 ? activeIndex - 1 : fileLinks.length - 1;
- }
-
- const targetLink = fileLinks[targetIndex];
- if (targetLink) {
- targetLink.click();
- targetLink.scrollIntoView({ block: 'nearest' });
- }
- }
-
- /**
- * Initializes the documentation application.
- * @returns {Promise}
- */
- async initialize() {
- try {
- const response = await fetch('index.json');
- if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
-
- const data = await response.json();
- this.indexData = data;
- window._indexData = data;
-
- if (data.customCSS) {
- this.loadCustomCSS(data.customCSS);
- }
-
- // Show/hide Docs link in sidebar
- this.domService.setShowDocsLink(data.showDocsLink !== false);
-
- this.searchService.buildSearchIndex(this.indexData.documents);
- this.domService.setupSearch(this.searchService);
-
- this.populateAuthorInfo(data.author);
- window.originalDocTitle = data.metadata.site_name || 'Documentation';
- document.title = window.originalDocTitle;
-
- this.domService.elements.fileIndex.innerHTML = '';
- this.indexData.documents.forEach(doc =>
- this.domService.createFileIndexItem(doc, this.domService.elements.fileIndex));
-
- const search = window.location.search;
- const slug = search === '' || search === '?'
- ? this.indexData.defaultPage
- : search.replace(/^\?/, '');
-
- await this.loadDocumentBySlug(slug);
-
- if (window.location.hash) {
- setTimeout(() => {
- const element = document.getElementById(window.location.hash.slice(1));
- if (element) this.domService.scrollToElement(element);
- }, 100);
- }
- } catch (error) {
- this.domService.setError(`Failed to load documentation index: ${error.message}`);
- }
- }
-
- /**
- * Populates the author information in the sidebar.
- * @param {Author} author - The author data.
- */
- populateAuthorInfo(author) {
- const subtitleName = document.querySelector('.name');
- const subtitleRole = document.querySelector('.role');
- if (!subtitleName || !subtitleRole) return;
-
- subtitleName.textContent = author.name || '';
- subtitleRole.textContent = author.role || '';
-
- const socials = document.querySelector('.social-links');
- if (socials) {
- socials.innerHTML = '';
- if (author.socials) {
- author.socials.forEach(s => {
- const link = document.createElement('a');
- link.href = s.url;
- link.target = '_blank';
- link.title = s.title;
- link.innerHTML = ``;
- socials.appendChild(link);
- });
- }
- }
- }
-
- /**
- * Loads a document by its slug.
- * @param {string} slug - The document slug.
- * @param {string} [hash] - Optional hash for scrolling to a section.
- * @returns {Promise}
- */
- async loadDocumentBySlug(slug, hash) {
- const [baseSlug] = slug.split('#');
- const doc = this.indexService.findDocumentBySlug(this.indexData.documents, baseSlug);
-
- if (!doc) {
- this.domService.setError(`Document not found: ${baseSlug}`);
- return;
- }
-
- if (doc.type === 'folder') {
- if (doc.path) {
- await this.loadDocument(doc.path, hash);
- } else if (doc.items?.length > 0) {
- await this.loadDocument(doc.items[0].path, hash);
- } else {
- this.domService.setError('This folder is empty.');
- }
- return;
- }
-
- await this.loadDocument(doc.path, hash);
- }
-
- /**
- * Loads and renders a document from a path.
- * @param {string} path - The document path.
- * @param {string} [hash] - Optional hash for scrolling to a section.
- * @param {boolean} [fromSearch=false] - Whether navigation came from search.
- * @returns {Promise}
- */
- async loadDocument(path, hash, fromSearch = false) {
- try {
- const { content, metadata, marked, title } = await this.documentService.loadDocument(path);
-
- this.domService.setTitle(title);
- this.domService.setContent(marked.parse(content));
- this.domService.updateActiveDocument(path);
-
- const headings = document.querySelectorAll('h2, h3, h4, h5, h6');
- const headingLinks = this.domService.createOutline(headings);
-
- headings.forEach(heading => {
- heading.addEventListener('click', (e) => {
- if (e.target.closest('svg') || e.target.closest('.header-anchor')) return;
-
- const id = heading.id;
- if (id) {
- history.pushState(null, '', `${window.location.pathname}${window.location.search}#${id}`);
- this.domService.scrollToElement(heading);
- heading.classList.remove('highlight');
- void heading.offsetWidth;
- heading.classList.add('highlight');
- }
- });
- });
-
- this.setupScrollObserver(headings, headingLinks);
-
- window._currentPath = path;
-
- if (hash) {
- const element = document.getElementById(hash.slice(1));
- if (element) {
- const delay = fromSearch ? 300 : 100;
- setTimeout(() => {
- this.domService.scrollToElement(element);
- element.classList.remove('highlight');
- void element.offsetWidth;
- element.classList.add('highlight');
- }, delay);
- }
- } else {
- window.scrollTo({ top: 0, behavior: 'smooth' });
- }
- } catch (error) {
- this.domService.setError('Error loading document. Please try again.');
- }
- }
-
- /**
- * Sets up an intersection observer for outline highlighting.
- * @param {NodeListOf} headings - The heading elements.
- * @param {Map} headingLinks - Map of headings to links.
- * @returns {IntersectionObserver} The created observer.
- * @private
- */
- setupScrollObserver(headings, headingLinks) {
- const observer = new IntersectionObserver(
- (entries) => {
- const visibleHeadings = entries
- .filter(entry => entry.isIntersecting)
- .sort((a, b) => a.target.offsetTop - b.target.offsetTop);
-
- if (visibleHeadings.length) {
- this.domService.elements.outline
- .querySelectorAll('a')
- .forEach(a => a.classList.remove('active'));
-
- const link = headingLinks.get(visibleHeadings[0].target);
- if (link) link.classList.add('active');
- }
- },
- {
- rootMargin: '-48px 0px -60% 0px',
- threshold: [0, 0.25, 0.5, 0.75, 1]
- }
- );
-
- headings.forEach(heading => observer.observe(heading));
- return observer;
- }
-}
-
-/**
- * @typedef {Object} IndexData
- * @property {string} defaultPage - The default page slug.
- * @property {Object} metadata - Site metadata.
- * @property {Author} author - Author information.
- * @property {Array} documents - The document tree.
- * @property {string} [customCSS] - Optional custom CSS path.
- */
-
-/**
- * @typedef {Object} Author
- * @property {string} [name] - Author name.
- * @property {string} [role] - Author role.
- * @property {Array} [socials] - Social media links.
- */
-
-/**
- * @typedef {Object} Social
- * @property {string} url - The social media URL.
- * @property {string} title - The link title.
- * @property {string} icon - The Font Awesome icon class.
- */
-
-window.originalDocTitle = document.title;
-const docs = new Documentation();
-
-// Expose to global scope for console access
-window.docs = docs;
diff --git a/js/EventBus.js b/js/EventBus.js
deleted file mode 100644
index 738cb1c..0000000
--- a/js/EventBus.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * A simple publish-subscribe event bus for decoupled communication between components.
- * @class EventBus
- */
-export class EventBus {
- constructor() {
- /** @type {Object.} */
- this.events = {};
- }
-
- /**
- * Subscribe to an event.
- * @param {string} event - The event name to subscribe to.
- * @param {Function} callback - The callback function to execute when the event is emitted.
- * @returns {Function} An unsubscribe function that removes this listener.
- */
- on(event, callback) {
- if (!this.events[event]) {
- this.events[event] = [];
- }
- this.events[event].push(callback);
- return () => this.off(event, callback);
- }
-
- /**
- * Unsubscribe from an event.
- * @param {string} event - The event name to unsubscribe from.
- * @param {Function} callback - The callback function to remove.
- */
- off(event, callback) {
- if (!this.events[event]) return;
- this.events[event] = this.events[event].filter(cb => cb !== callback);
- }
-
- /**
- * Emit an event to all subscribers.
- * @param {string} event - The event name to emit.
- * @param {*} data - The data to pass to all subscribers.
- */
- emit(event, data) {
- if (!this.events[event]) return;
- this.events[event].forEach(callback => callback(data));
- }
-}
diff --git a/js/IndexService.js b/js/IndexService.js
deleted file mode 100644
index 28cf04e..0000000
--- a/js/IndexService.js
+++ /dev/null
@@ -1,89 +0,0 @@
-/**
- * Service responsible for document index operations and lookups.
- * @class IndexService
- */
-export class IndexService {
- /**
- * Finds a document by its slug in the document tree.
- * @param {Array} documents - The documents to search.
- * @param {string} slug - The slug to find.
- * @returns {Document|null} The found document or null.
- */
- findDocumentBySlug(documents, slug) {
- for (const doc of documents) {
- if (doc.slug === slug) return doc;
- if (doc.type === 'folder') {
- const found = this.findDocumentBySlug(doc.items, slug);
- if (found) return found;
- }
- }
- return null;
- }
-
- /**
- * Finds a document by its title, path, or slug.
- * @param {Array} documents - The documents to search.
- * @param {string} title - The title to find.
- * @returns {Document|null} The found document or null.
- */
- findDocumentByTitle(documents, title) {
- for (const doc of documents) {
- if (doc.type === 'folder') {
- const found = this.findDocumentByTitle(doc.items, title);
- if (found) return found;
- } else if (
- doc.title === title ||
- doc.path.endsWith(title + '.md') ||
- doc.slug === title.toLowerCase().replace(/ /g, '-')
- ) {
- return doc;
- }
- }
- return null;
- }
-
- /**
- * Finds all parent folders for a given document path.
- * @param {Array} documents - The documents to search.
- * @param {string} path - The path to find parents for.
- * @param {Array} [parentFolders=[]] - Accumulated parent folders.
- * @returns {Array} The parent folders.
- */
- findParentFolders(documents, path, parentFolders = []) {
- for (const doc of documents) {
- if (doc.type === 'folder') {
- const found = doc.items.find(item => {
- if (item.path === path) return true;
- if (item.type === 'folder') {
- return this.findParentFolders([item], path).length > 0;
- }
- return false;
- });
-
- if (found) {
- parentFolders.push(doc);
- doc.items.forEach(item => {
- if (item.type === 'folder') {
- this.findParentFolders([item], path, parentFolders);
- }
- });
- }
- }
- }
- return parentFolders;
- }
-}
-
-/**
- * @typedef {Object} Document
- * @property {string} title - The document title.
- * @property {string} [path] - The file path.
- * @property {string} slug - The URL slug.
- * @property {'folder'|'file'} [type] - The document type.
- * @property {Array} [items] - Child documents for folders.
- * @property {Array} [headers] - Document headers.
- * @property {string} [icon] - Custom icon class.
- * @property {boolean} [defaultOpen] - Whether folder is open by default.
- * @property {string} [showfolderpage] - Whether to show folder page.
- * @property {Object} [metadata] - Additional metadata.
- */
diff --git a/js/NavigationService.js b/js/NavigationService.js
deleted file mode 100644
index 21a3bf0..0000000
--- a/js/NavigationService.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * Service responsible for handling navigation events and browser history.
- * @class NavigationService
- */
-export class NavigationService {
- /**
- * Creates a new NavigationService instance.
- * @param {EventBus} eventBus - The event bus for communication.
- * @param {DocumentService} documentService - The document service.
- */
- constructor(eventBus, documentService) {
- /** @type {EventBus} */
- this.eventBus = eventBus;
- /** @type {DocumentService} */
- this.documentService = documentService;
- this.setupEventListeners();
- }
-
- /**
- * Sets up browser history and internal link event listeners.
- * @private
- */
- setupEventListeners() {
- window.addEventListener('popstate', async () => {
- const search = window.location.search;
- const hash = window.location.hash;
- const slug = (search === '' || search === '?') ?
- window._indexData.defaultPage :
- search.replace(/^\?/, '').split('#')[0];
-
- this.eventBus.emit('navigation:requested', { slug, hash });
- });
-
- document.addEventListener('click', async (e) => {
- const target = e.target.closest('a[data-internal="true"]');
- if (target) {
- e.preventDefault();
- const slug = target.href.split('?').pop();
- history.pushState(null, '', `?${slug}`);
- this.eventBus.emit('navigation:requested', { slug });
- }
- });
- }
-}
diff --git a/js/SearchService.js b/js/SearchService.js
deleted file mode 100644
index 4dd6f1e..0000000
--- a/js/SearchService.js
+++ /dev/null
@@ -1,200 +0,0 @@
-/**
- * Service responsible for building and querying the search index.
- * @class SearchService
- */
-export class SearchService {
- /**
- * Creates a new SearchService instance.
- * @param {EventBus} eventBus - The event bus for communication.
- * @param {IndexService} indexService - The index service for document lookups.
- */
- constructor(eventBus, indexService) {
- /** @type {EventBus} */
- this.eventBus = eventBus;
- /** @type {IndexService} */
- this.indexService = indexService;
- /** @type {Array} */
- this.searchIndex = [];
- /** @type {Map>} */
- this.searchCache = new Map();
- /** @type {number} */
- this.cacheMaxSize = 50;
- }
-
- /**
- * Builds the search index from a list of documents.
- * @param {Array} documents - The documents to index.
- */
- buildSearchIndex(documents) {
- this.searchIndex = [];
- this.searchCache.clear();
- this.processDocuments(documents);
- }
-
- /**
- * Recursively processes documents and adds them to the search index.
- * @param {Array} documents - The documents to process.
- * @param {string} [parentPath=''] - The parent path for breadcrumb display.
- * @private
- */
- processDocuments(documents, parentPath = '') {
- documents.forEach(doc => {
- if (doc.type === 'folder') {
- this.processFolderDocument(doc, parentPath);
- } else {
- this.processFileDocument(doc, parentPath);
- }
- });
- }
-
- /**
- * Processes a folder document and its children.
- * @param {Document} doc - The folder document to process.
- * @param {string} parentPath - The parent path for breadcrumb display.
- * @private
- */
- processFolderDocument(doc, parentPath) {
- const currentPath = parentPath ? `${parentPath} / ${doc.title}` : doc.title;
-
- if (doc.path) {
- if (doc.showfolderpage !== 'false') {
- this.searchIndex.push({
- title: doc.title,
- path: doc.path,
- slug: doc.slug,
- location: currentPath,
- type: 'folder'
- });
- }
-
- if (doc.headers) {
- this.addHeadersToIndex(doc, currentPath, doc.title);
- }
- }
-
- if (doc.items) {
- this.processDocuments(doc.items, currentPath);
- }
- }
-
- /**
- * Processes a file document.
- * @param {Document} doc - The file document to process.
- * @param {string} parentPath - The parent path for breadcrumb display.
- * @private
- */
- processFileDocument(doc, parentPath) {
- this.searchIndex.push({
- title: doc.title,
- path: doc.path,
- slug: doc.slug,
- location: parentPath,
- type: 'file'
- });
-
- if (doc.headers) {
- this.addHeadersToIndex(doc, parentPath, doc.title);
- }
- }
-
- /**
- * Adds document headers to the search index.
- * @param {Document} doc - The document containing headers.
- * @param {string} location - The location path for display.
- * @param {string} docTitle - The document title.
- * @private
- */
- addHeadersToIndex(doc, location, docTitle) {
- doc.headers.forEach(header => {
- this.searchIndex.push({
- title: header,
- path: doc.path,
- slug: `${doc.slug}#${this.slugifyHeader(header)}`,
- location: `${location} / ${docTitle}`,
- type: 'header'
- });
- });
- }
-
- /**
- * Converts a header text to a URL-friendly slug.
- * @param {string} header - The header text to slugify.
- * @returns {string} The slugified header.
- * @private
- */
- slugifyHeader(header) {
- return header.toLowerCase()
- .replace(/[^\w\s-]/g, '')
- .replace(/\s+/g, '-');
- }
-
- /**
- * Searches the index for documents matching the query.
- * @param {string} query - The search query.
- * @returns {Array} The search results sorted by relevance.
- */
- search(query) {
- if (!query) return [];
- query = query.toLowerCase();
-
- if (this.searchCache.has(query)) {
- return this.searchCache.get(query);
- }
-
- const results = this.searchIndex
- .map(item => this.scoreItem(item, query))
- .filter(item => item.score > 0)
- .sort((a, b) => b.score - a.score)
- .slice(0, 10);
-
- if (this.searchCache.size >= this.cacheMaxSize) {
- const firstKey = this.searchCache.keys().next().value;
- this.searchCache.delete(firstKey);
- }
- this.searchCache.set(query, results);
-
- return results;
- }
-
- /**
- * Calculates a relevance score for a search index item.
- * @param {SearchIndexItem} item - The item to score.
- * @param {string} query - The search query (lowercase).
- * @returns {SearchResult} The item with its score.
- * @private
- */
- scoreItem(item, query) {
- const titleLower = item.title.toLowerCase();
- let score = 0;
-
- if (titleLower === query) score = 100;
- else if (titleLower.startsWith(query)) score = 80;
- else if (titleLower.includes(query)) score = 60;
- else if (item.path.toLowerCase().includes(query)) score = 40;
- else if (item.location.toLowerCase().includes(query)) score = 20;
-
- if (item.type === 'header') score += 5;
-
- return { ...item, score };
- }
-
- /**
- * Clears the search result cache.
- */
- clearCache() {
- this.searchCache.clear();
- }
-}
-
-/**
- * @typedef {Object} SearchIndexItem
- * @property {string} title - The item title.
- * @property {string} path - The file path.
- * @property {string} slug - The URL slug.
- * @property {string} location - The breadcrumb location.
- * @property {'folder'|'file'|'header'} type - The item type.
- */
-
-/**
- * @typedef {SearchIndexItem & {score: number}} SearchResult
- */
diff --git a/package-lock.json b/package-lock.json
index afbb519..ff2ad98 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,728 +1,1111 @@
{
"name": "docs-viewer",
- "version": "1.0.0",
+ "version": "2.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "docs-viewer",
- "version": "1.0.0",
- "devDependencies": {
- "live-server": "^1.2.2"
- }
- },
- "node_modules/accepts": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
- "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mime-types": "~2.1.34",
- "negotiator": "0.6.3"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/anymatch": {
"version": "2.0.0",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
- "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "micromatch": "^3.1.4",
- "normalize-path": "^2.1.1"
- }
- },
- "node_modules/anymatch/node_modules/normalize-path": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
- "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
- "dev": true,
"license": "MIT",
"dependencies": {
- "remove-trailing-separator": "^1.0.1"
+ "highlight.js": "^11.11.1",
+ "marked": "^15.0.12",
+ "slatehtml": "file:../slatehtml",
+ "slatehtml-ui": "file:../slatehtml/packages/slatehtml-ui"
},
- "engines": {
- "node": ">=0.10.0"
+ "devDependencies": {
+ "vite": "^6.3.5"
}
},
- "node_modules/apache-crypt": {
- "version": "1.2.6",
- "resolved": "https://registry.npmjs.org/apache-crypt/-/apache-crypt-1.2.6.tgz",
- "integrity": "sha512-072WetlM4blL8PREJVeY+WHiUh1R5VNt2HfceGS8aKqttPHcmqE5pkKuXPz/ULmJOFkc8Hw3kfKl6vy7Qka6DA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "unix-crypt-td-js": "^1.1.4"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/apache-md5": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/apache-md5/-/apache-md5-1.1.8.tgz",
- "integrity": "sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/arr-diff": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
- "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/arr-flatten": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
- "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/arr-union": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
- "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/array-unique": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
- "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/assign-symbols": {
+ "../slatehtml": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
- "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==",
- "dev": true,
"license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/async-each": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz",
- "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==",
- "dev": true,
- "funding": [
- {
- "type": "individual",
- "url": "https://paulmillr.com/funding/"
- }
+ "workspaces": [
+ "packages/*"
],
- "license": "MIT"
- },
- "node_modules/atob": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
- "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
- "dev": true,
- "license": "(MIT OR Apache-2.0)",
- "bin": {
- "atob": "bin/atob.js"
- },
- "engines": {
- "node": ">= 4.5.0"
+ "devDependencies": {
+ "playwright": "^1.62.0",
+ "prismjs": "^1.30.0",
+ "vite": "^6.3.5"
}
},
- "node_modules/base": {
- "version": "0.11.2",
- "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
- "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cache-base": "^1.0.1",
- "class-utils": "^0.3.5",
- "component-emitter": "^1.2.1",
- "define-property": "^1.0.0",
- "isobject": "^3.0.1",
- "mixin-deep": "^1.2.0",
- "pascalcase": "^0.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/base/node_modules/define-property": {
+ "../slatehtml/packages/slatehtml-ui": {
"version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
- "dev": true,
"license": "MIT",
"dependencies": {
- "is-descriptor": "^1.0.0"
+ "@fortawesome/fontawesome-svg-core": "^7.3.1",
+ "@fortawesome/free-brands-svg-icons": "^7.3.1",
+ "@fortawesome/free-regular-svg-icons": "^7.3.1",
+ "@fortawesome/free-solid-svg-icons": "^7.3.1",
+ "fuzzysort": "^3.1.0",
+ "lucide": "^1.27.0"
},
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/basic-auth": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
- "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "5.1.2"
+ "devDependencies": {
+ "slatehtml": "file:../..",
+ "vite": "^6.3.5"
},
- "engines": {
- "node": ">= 0.8"
+ "peerDependencies": {
+ "slatehtml": "*"
}
},
- "node_modules/batch": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
- "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/bcryptjs": {
- "version": "2.4.3",
- "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
- "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/binary-extensions": {
- "version": "1.13.1",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
- "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/bindings": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
- "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
"dev": true,
"license": "MIT",
"optional": true,
- "dependencies": {
- "file-uri-to-path": "1.0.0"
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
- "node_modules/braces": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
- "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "arr-flatten": "^1.1.0",
- "array-unique": "^0.3.2",
- "extend-shallow": "^2.0.1",
- "fill-range": "^4.0.0",
- "isobject": "^3.0.1",
- "repeat-element": "^1.1.2",
- "snapdragon": "^0.8.1",
- "snapdragon-node": "^2.0.1",
- "split-string": "^3.0.2",
- "to-regex": "^3.0.1"
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@napi-rs/lzma-linux-x64-gnu": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz",
+ "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^22.20 || ^24.12 || >=25"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz",
+ "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz",
+ "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz",
+ "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz",
+ "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz",
+ "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz",
+ "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz",
+ "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz",
+ "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz",
+ "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz",
+ "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz",
+ "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz",
+ "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz",
+ "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz",
+ "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz",
+ "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz",
+ "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz",
+ "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz",
+ "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz",
+ "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "libc": [
+ "musl"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz",
+ "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz",
+ "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz",
+ "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz",
+ "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz",
+ "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz",
+ "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
},
"engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/cache-base": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
- "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "collection-visit": "^1.0.0",
- "component-emitter": "^1.2.1",
- "get-value": "^2.0.6",
- "has-value": "^1.0.0",
- "isobject": "^3.0.1",
- "set-value": "^2.0.0",
- "to-object-path": "^0.3.0",
- "union-value": "^1.0.0",
- "unset-value": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/chokidar": {
- "version": "2.1.8",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
- "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "anymatch": "^2.0.0",
- "async-each": "^1.0.1",
- "braces": "^2.3.2",
- "glob-parent": "^3.1.0",
- "inherits": "^2.0.3",
- "is-binary-path": "^1.0.0",
- "is-glob": "^4.0.0",
- "normalize-path": "^3.0.0",
- "path-is-absolute": "^1.0.0",
- "readdirp": "^2.2.1",
- "upath": "^1.1.1"
+ "node": ">=18"
},
"optionalDependencies": {
- "fsevents": "^1.2.7"
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
}
},
- "node_modules/class-utils": {
- "version": "0.3.6",
- "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
- "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "arr-union": "^3.1.0",
- "define-property": "^0.2.5",
- "isobject": "^3.0.0",
- "static-extend": "^0.1.1"
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/highlight.js": {
+ "version": "11.11.1",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
+ "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/marked": {
+ "version": "15.0.12",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz",
+ "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==",
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
},
"engines": {
- "node": ">=0.10.0"
+ "node": ">= 18"
}
},
- "node_modules/class-utils/node_modules/define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "node_modules/nanoid": {
+ "version": "3.3.16",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
+ "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
"dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
"license": "MIT",
- "dependencies": {
- "is-descriptor": "^0.1.0"
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
},
"engines": {
- "node": ">=0.10.0"
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
- "node_modules/class-utils/node_modules/is-descriptor": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz",
- "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/collection-visit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
- "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "map-visit": "^1.0.0",
- "object-visit": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/colors": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
- "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.1.90"
- }
- },
- "node_modules/component-emitter": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz",
- "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/connect": {
- "version": "3.7.0",
- "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
- "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "2.6.9",
- "finalhandler": "1.1.2",
- "parseurl": "~1.3.3",
- "utils-merge": "1.0.1"
- },
- "engines": {
- "node": ">= 0.10.0"
- }
- },
- "node_modules/copy-descriptor": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
- "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/core-util-is": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
- "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/cors": {
- "version": "2.8.5",
- "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
- "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "object-assign": "^4",
- "vary": "^1"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/decode-uri-component": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
- "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/define-property": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
- "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^1.0.2",
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/depd": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/destroy": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
- "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8",
- "npm": "1.2.8000 || >= 1.4.16"
- }
- },
- "node_modules/duplexer": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
- "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/ee-first": {
+ "node_modules/picocolors": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
- "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
- "license": "MIT"
+ "license": "ISC"
},
- "node_modules/encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "node_modules/picomatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">= 0.8"
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
}
},
- "node_modules/escape-html": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
- "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/etag": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/event-stream": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz",
- "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==",
+ "node_modules/postcss": {
+ "version": "8.5.25",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz",
+ "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==",
"dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
"license": "MIT",
"dependencies": {
- "duplexer": "~0.1.1",
- "from": "~0",
- "map-stream": "~0.1.0",
- "pause-stream": "0.0.11",
- "split": "0.3",
- "stream-combiner": "~0.0.4",
- "through": "~2.3.1"
- }
- },
- "node_modules/expand-brackets": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
- "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^2.3.3",
- "define-property": "^0.2.5",
- "extend-shallow": "^2.0.1",
- "posix-character-classes": "^0.1.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
+ "nanoid": "^3.3.16",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
},
"engines": {
- "node": ">=0.10.0"
+ "node": "^10 || ^12 || >=14"
}
},
- "node_modules/expand-brackets/node_modules/define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "node_modules/rollup": {
+ "version": "4.62.4",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz",
+ "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "is-descriptor": "^0.1.0"
+ "@types/estree": "1.0.9"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
},
"engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/expand-brackets/node_modules/is-descriptor": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz",
- "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
},
- "engines": {
- "node": ">= 0.4"
+ "optionalDependencies": {
+ "@napi-rs/lzma-linux-x64-gnu": "1.5.1",
+ "@rollup/rollup-android-arm-eabi": "4.62.4",
+ "@rollup/rollup-android-arm64": "4.62.4",
+ "@rollup/rollup-darwin-arm64": "4.62.4",
+ "@rollup/rollup-darwin-x64": "4.62.4",
+ "@rollup/rollup-freebsd-arm64": "4.62.4",
+ "@rollup/rollup-freebsd-x64": "4.62.4",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.62.4",
+ "@rollup/rollup-linux-arm-musleabihf": "4.62.4",
+ "@rollup/rollup-linux-arm64-gnu": "4.62.4",
+ "@rollup/rollup-linux-arm64-musl": "4.62.4",
+ "@rollup/rollup-linux-loong64-gnu": "4.62.4",
+ "@rollup/rollup-linux-loong64-musl": "4.62.4",
+ "@rollup/rollup-linux-ppc64-gnu": "4.62.4",
+ "@rollup/rollup-linux-ppc64-musl": "4.62.4",
+ "@rollup/rollup-linux-riscv64-gnu": "4.62.4",
+ "@rollup/rollup-linux-riscv64-musl": "4.62.4",
+ "@rollup/rollup-linux-s390x-gnu": "4.62.4",
+ "@rollup/rollup-linux-x64-gnu": "4.62.4",
+ "@rollup/rollup-linux-x64-musl": "4.62.4",
+ "@rollup/rollup-openbsd-x64": "4.62.4",
+ "@rollup/rollup-openharmony-arm64": "4.62.4",
+ "@rollup/rollup-win32-arm64-msvc": "4.62.4",
+ "@rollup/rollup-win32-ia32-msvc": "4.62.4",
+ "@rollup/rollup-win32-x64-gnu": "4.62.4",
+ "@rollup/rollup-win32-x64-msvc": "4.62.4",
+ "fsevents": "~2.3.2"
}
},
- "node_modules/extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-extendable": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/extglob": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
- "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "array-unique": "^0.3.2",
- "define-property": "^1.0.0",
- "expand-brackets": "^2.1.4",
- "extend-shallow": "^2.0.1",
- "fragment-cache": "^0.2.1",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/extglob/node_modules/define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/faye-websocket": {
- "version": "0.11.4",
- "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
- "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "websocket-driver": ">=0.5.1"
- },
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/file-uri-to-path": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
- "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/fill-range": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
- "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "extend-shallow": "^2.0.1",
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1",
- "to-regex-range": "^2.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/finalhandler": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
- "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "2.6.9",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "on-finished": "~2.3.0",
- "parseurl": "~1.3.3",
- "statuses": "~1.5.0",
- "unpipe": "~1.0.0"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/for-in": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
- "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/fragment-cache": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
- "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "map-cache": "^0.2.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/fresh": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
- "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/from": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
- "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/fsevents": {
- "version": "1.2.13",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
- "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
- "deprecated": "Upgrade to fsevents v2 to mitigate potential security issues",
+ "node_modules/rollup/node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -730,1634 +1113,133 @@
"os": [
"darwin"
],
- "dependencies": {
- "bindings": "^1.5.0",
- "nan": "^2.12.1"
- },
"engines": {
- "node": ">= 4.0"
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
- "node_modules/function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
+ "node_modules/slatehtml": {
+ "resolved": "../slatehtml",
+ "link": true
},
- "node_modules/get-value": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
- "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
+ "node_modules/slatehtml-ui": {
+ "resolved": "../slatehtml/packages/slatehtml-ui",
+ "link": true
},
- "node_modules/glob-parent": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
- "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^3.1.0",
- "path-dirname": "^1.0.0"
- }
- },
- "node_modules/glob-parent/node_modules/is-glob": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
- "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-extglob": "^2.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/has-value": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
- "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "get-value": "^2.0.6",
- "has-values": "^1.0.0",
- "isobject": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/has-values": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
- "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-number": "^3.0.0",
- "kind-of": "^4.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/has-values/node_modules/kind-of": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
- "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-buffer": "^1.1.5"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/hasown": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
- "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/http-auth": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/http-auth/-/http-auth-3.1.3.tgz",
- "integrity": "sha512-Jbx0+ejo2IOx+cRUYAGS1z6RGc6JfYUNkysZM4u4Sfk1uLlGv814F7/PIjQQAuThLdAWxb74JMGd5J8zex1VQg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "apache-crypt": "^1.1.2",
- "apache-md5": "^1.0.6",
- "bcryptjs": "^2.3.0",
- "uuid": "^3.0.0"
- },
- "engines": {
- "node": ">=4.6.1"
- }
- },
- "node_modules/http-errors": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
- "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "depd": "2.0.0",
- "inherits": "2.0.4",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "toidentifier": "1.0.1"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/http-errors/node_modules/statuses": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
- "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/http-parser-js": {
- "version": "0.5.9",
- "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz",
- "integrity": "sha512-n1XsPy3rXVxlqxVioEWdC+0+M+SQw0DpJynwtOPo1X+ZlvdzTLtDBIJJlDQTnwZIFJrZSzSGmIOUdP8tu+SgLw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/is-accessor-descriptor": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz",
- "integrity": "sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.0"
- },
- "engines": {
- "node": ">= 0.10"
- }
- },
- "node_modules/is-binary-path": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
- "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "binary-extensions": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/is-data-descriptor": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz",
- "integrity": "sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.0"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/is-descriptor": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.3.tgz",
- "integrity": "sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-extglob": "^2.1.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-number": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
- "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "kind-of": "^3.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-plain-object": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
- "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-windows": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
- "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/is-wsl": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
- "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/isobject": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
- "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-buffer": "^1.1.5"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/live-server": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/live-server/-/live-server-1.2.2.tgz",
- "integrity": "sha512-t28HXLjITRGoMSrCOv4eZ88viHaBVIjKjdI5PO92Vxlu+twbk6aE0t7dVIaz6ZWkjPilYFV6OSdMYl9ybN2B4w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "chokidar": "^2.0.4",
- "colors": "1.4.0",
- "connect": "^3.6.6",
- "cors": "latest",
- "event-stream": "3.3.4",
- "faye-websocket": "0.11.x",
- "http-auth": "3.1.x",
- "morgan": "^1.9.1",
- "object-assign": "latest",
- "opn": "latest",
- "proxy-middleware": "latest",
- "send": "latest",
- "serve-index": "^1.9.1"
- },
- "bin": {
- "live-server": "live-server.js"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/map-cache": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
- "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/map-stream": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz",
- "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==",
- "dev": true
- },
- "node_modules/map-visit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
- "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "object-visit": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/micromatch": {
- "version": "3.1.10",
- "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
- "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "braces": "^2.3.1",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "extglob": "^2.0.4",
- "fragment-cache": "^0.2.1",
- "kind-of": "^6.0.2",
- "nanomatch": "^1.2.9",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/micromatch/node_modules/extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/micromatch/node_modules/is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.4"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/micromatch/node_modules/kind-of": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
- "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/mime-db": {
- "version": "1.52.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mime-types": {
- "version": "2.1.35",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mime-db": "1.52.0"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/mixin-deep": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
- "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "for-in": "^1.0.2",
- "is-extendable": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/mixin-deep/node_modules/is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.4"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/morgan": {
- "version": "1.10.1",
- "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz",
- "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "basic-auth": "~2.0.1",
- "debug": "2.6.9",
- "depd": "~2.0.0",
- "on-finished": "~2.3.0",
- "on-headers": "~1.1.0"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/nan": {
- "version": "2.22.2",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz",
- "integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/nanomatch": {
- "version": "1.2.13",
- "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
- "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "arr-diff": "^4.0.0",
- "array-unique": "^0.3.2",
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "fragment-cache": "^0.2.1",
- "is-windows": "^1.0.2",
- "kind-of": "^6.0.2",
- "object.pick": "^1.3.0",
- "regex-not": "^1.0.0",
- "snapdragon": "^0.8.1",
- "to-regex": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/nanomatch/node_modules/extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/nanomatch/node_modules/is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.4"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/nanomatch/node_modules/kind-of": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
- "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/negotiator": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
- "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-assign": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
- "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-copy": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
- "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "copy-descriptor": "^0.1.0",
- "define-property": "^0.2.5",
- "kind-of": "^3.0.3"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-copy/node_modules/define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object-copy/node_modules/is-descriptor": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz",
- "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/object-visit": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
- "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/object.pick": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
- "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "isobject": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/on-finished": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
- "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ee-first": "1.1.1"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/on-headers": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
- "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/opn": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz",
- "integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==",
- "deprecated": "The package has been renamed to `open`",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-wsl": "^1.1.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/parseurl": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
- "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/pascalcase": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
- "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/path-dirname": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
- "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/pause-stream": {
- "version": "0.0.11",
- "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
- "integrity": "sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==",
- "dev": true,
- "license": [
- "MIT",
- "Apache2"
- ],
- "dependencies": {
- "through": "~2.3"
- }
- },
- "node_modules/posix-character-classes": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
- "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/process-nextick-args": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/proxy-middleware": {
- "version": "0.15.0",
- "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz",
- "integrity": "sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/range-parser": {
+ "node_modules/source-map-js": {
"version": "1.2.1",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
- "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/readable-stream": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
- "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "node_modules/readdirp": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
- "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.1.11",
- "micromatch": "^3.1.10",
- "readable-stream": "^2.0.2"
- },
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/regex-not": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
- "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "extend-shallow": "^3.0.2",
- "safe-regex": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/regex-not/node_modules/extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/regex-not/node_modules/is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.4"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/remove-trailing-separator": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
- "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/repeat-element": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
- "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/repeat-string": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
- "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10"
- }
- },
- "node_modules/resolve-url": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
- "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==",
- "deprecated": "https://github.com/lydell/resolve-url#deprecated",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/ret": {
- "version": "0.1.15",
- "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
- "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.12"
- }
- },
- "node_modules/safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/safe-regex": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
- "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ret": "~0.1.10"
- }
- },
- "node_modules/send": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/send/-/send-1.1.0.tgz",
- "integrity": "sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^4.3.5",
- "destroy": "^1.2.0",
- "encodeurl": "^2.0.0",
- "escape-html": "^1.0.3",
- "etag": "^1.8.1",
- "fresh": "^0.5.2",
- "http-errors": "^2.0.0",
- "mime-types": "^2.1.35",
- "ms": "^2.1.3",
- "on-finished": "^2.4.1",
- "range-parser": "^1.2.1",
- "statuses": "^2.0.1"
- },
- "engines": {
- "node": ">= 18"
- }
- },
- "node_modules/send/node_modules/debug": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
- "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/send/node_modules/encodeurl": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
- "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/send/node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/send/node_modules/on-finished": {
- "version": "2.4.1",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
- "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ee-first": "1.1.1"
- },
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/send/node_modules/statuses": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
- "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/serve-index": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
- "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "accepts": "~1.3.4",
- "batch": "0.6.1",
- "debug": "2.6.9",
- "escape-html": "~1.0.3",
- "http-errors": "~1.6.2",
- "mime-types": "~2.1.17",
- "parseurl": "~1.3.2"
- },
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/serve-index/node_modules/depd": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
- "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/serve-index/node_modules/http-errors": {
- "version": "1.6.3",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
- "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "depd": "~1.1.2",
- "inherits": "2.0.3",
- "setprototypeof": "1.1.0",
- "statuses": ">= 1.4.0 < 2"
- },
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/serve-index/node_modules/inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/serve-index/node_modules/setprototypeof": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
- "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/set-value": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
- "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "extend-shallow": "^2.0.1",
- "is-extendable": "^0.1.1",
- "is-plain-object": "^2.0.3",
- "split-string": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/setprototypeof": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
- "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/snapdragon": {
- "version": "0.8.2",
- "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
- "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "base": "^0.11.1",
- "debug": "^2.2.0",
- "define-property": "^0.2.5",
- "extend-shallow": "^2.0.1",
- "map-cache": "^0.2.2",
- "source-map": "^0.5.6",
- "source-map-resolve": "^0.5.0",
- "use": "^3.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/snapdragon-node": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
- "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-property": "^1.0.0",
- "isobject": "^3.0.0",
- "snapdragon-util": "^3.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/snapdragon-node/node_modules/define-property": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
- "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/snapdragon-util": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
- "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "kind-of": "^3.2.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/snapdragon/node_modules/define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/snapdragon/node_modules/is-descriptor": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz",
- "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/source-map": {
- "version": "0.5.7",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
- "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
- "node_modules/source-map-resolve": {
- "version": "0.5.3",
- "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
- "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
- "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated",
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
"dev": true,
"license": "MIT",
"dependencies": {
- "atob": "^2.1.2",
- "decode-uri-component": "^0.2.0",
- "resolve-url": "^0.2.1",
- "source-map-url": "^0.4.0",
- "urix": "^0.1.0"
- }
- },
- "node_modules/source-map-url": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
- "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
- "deprecated": "See https://github.com/lydell/source-map-url#deprecated",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/split": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz",
- "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "through": "2"
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
},
"engines": {
- "node": "*"
- }
- },
- "node_modules/split-string": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
- "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "extend-shallow": "^3.0.0"
+ "node": ">=12.0.0"
},
- "engines": {
- "node": ">=0.10.0"
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
}
},
- "node_modules/split-string/node_modules/extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
+ "node_modules/vite": {
+ "version": "6.4.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz",
+ "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==",
"dev": true,
"license": "MIT",
"dependencies": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
},
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/split-string/node_modules/is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.4"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/static-extend": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
- "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-property": "^0.2.5",
- "object-copy": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/static-extend/node_modules/define-property": {
- "version": "0.2.5",
- "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
- "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-descriptor": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/static-extend/node_modules/is-descriptor": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.7.tgz",
- "integrity": "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-accessor-descriptor": "^1.0.1",
- "is-data-descriptor": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/statuses": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
- "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.6"
- }
- },
- "node_modules/stream-combiner": {
- "version": "0.0.4",
- "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz",
- "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "duplexer": "~0.1.1"
- }
- },
- "node_modules/string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "safe-buffer": "~5.1.0"
- }
- },
- "node_modules/through": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/to-object-path": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
- "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "kind-of": "^3.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/to-regex": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
- "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "define-property": "^2.0.2",
- "extend-shallow": "^3.0.2",
- "regex-not": "^1.0.2",
- "safe-regex": "^1.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/to-regex-range": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
- "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-number": "^3.0.0",
- "repeat-string": "^1.6.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/to-regex/node_modules/extend-shallow": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
- "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "assign-symbols": "^1.0.0",
- "is-extendable": "^1.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/to-regex/node_modules/is-extendable": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
- "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-plain-object": "^2.0.4"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/toidentifier": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
- "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.6"
- }
- },
- "node_modules/union-value": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
- "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "arr-union": "^3.1.0",
- "get-value": "^2.0.6",
- "is-extendable": "^0.1.1",
- "set-value": "^2.0.1"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/unix-crypt-td-js": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/unix-crypt-td-js/-/unix-crypt-td-js-1.1.4.tgz",
- "integrity": "sha512-8rMeVYWSIyccIJscb9NdCfZKSRBKYTeVnwmiRYT2ulE3qd1RaDQ0xQDP+rI3ccIWbhu/zuo5cgN8z73belNZgw==",
- "dev": true,
- "license": "BSD-3-Clause"
- },
- "node_modules/unpipe": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
- "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/unset-value": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
- "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "has-value": "^0.3.1",
- "isobject": "^3.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/unset-value/node_modules/has-value": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
- "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "get-value": "^2.0.3",
- "has-values": "^0.1.4",
- "isobject": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/unset-value/node_modules/has-value/node_modules/isobject": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
- "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "isarray": "1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/unset-value/node_modules/has-values": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
- "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/upath": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
- "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4",
- "yarn": "*"
- }
- },
- "node_modules/urix": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
- "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==",
- "deprecated": "Please see https://github.com/lydell/urix#deprecated",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/use": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
- "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/utils-merge": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
- "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4.0"
- }
- },
- "node_modules/uuid": {
- "version": "3.4.0",
- "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
- "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
- "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
- "dev": true,
- "license": "MIT",
"bin": {
- "uuid": "bin/uuid"
- }
- },
- "node_modules/vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.8"
- }
- },
- "node_modules/websocket-driver": {
- "version": "0.7.4",
- "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
- "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "http-parser-js": ">=0.5.1",
- "safe-buffer": ">=5.1.0",
- "websocket-extensions": ">=0.1.1"
+ "vite": "bin/vite.js"
},
"engines": {
- "node": ">=0.8.0"
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
}
},
- "node_modules/websocket-extensions": {
- "version": "0.1.4",
- "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
- "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "node_modules/vite/node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
- "license": "Apache-2.0",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
"engines": {
- "node": ">=0.8.0"
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
}
}
diff --git a/package.json b/package.json
index c109e1f..d320b39 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,54 @@
{
"name": "docs-viewer",
- "version": "1.0.0",
- "description": "Documentation viewer application",
+ "version": "2.0.0",
+ "description": "Static documentation viewer on slatehtml + slatehtml-ui",
+ "type": "module",
+ "main": "./src/index.js",
+ "module": "./src/index.js",
+ "exports": {
+ ".": "./src/index.js",
+ "./prose.css": "./src/prose.css",
+ "./build-docs": "./build-docs.cjs",
+ "./package.json": "./package.json"
+ },
+ "files": [
+ "src",
+ "build-docs.cjs",
+ "README.md",
+ "LICENSE"
+ ],
+ "sideEffects": [
+ "./src/index.js",
+ "./src/prose.css",
+ "**/*.umc",
+ "**/*.css",
+ "**/*.js"
+ ],
"scripts": {
- "start": "live-server --port=8080 --no-browser",
- "build": "node build-docs.js"
+ "dev": "vite",
+ "build": "node build-docs.cjs && vite build",
+ "build:index": "node build-docs.cjs",
+ "preview": "vite preview",
+ "start": "vite"
+ },
+ "dependencies": {
+ "highlight.js": "^11.11.1",
+ "marked": "^15.0.12",
+ "slatehtml": "file:../slatehtml",
+ "slatehtml-ui": "file:../slatehtml/packages/slatehtml-ui"
},
"devDependencies": {
- "live-server": "^1.2.2"
+ "vite": "^6.3.5"
+ },
+ "keywords": [
+ "docs",
+ "markdown",
+ "documentation",
+ "slatehtml"
+ ],
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/litruv/Docs-Viewer.git"
}
}
diff --git a/src/app.js b/src/app.js
new file mode 100644
index 0000000..d9c96c2
--- /dev/null
+++ b/src/app.js
@@ -0,0 +1,425 @@
+import { EventBus } from "./services/event-bus.js";
+import { IndexService } from "./services/index-service.js";
+import { SearchService } from "./services/search-service.js";
+import { DocumentService } from "./services/document-service.js";
+import { NavigationService } from "./services/navigation-service.js";
+import { addContent } from "slatehtml/umc";
+
+/** Replace children via widget `set` when available, else `addContent`. */
+function fill(host, specs) {
+ if (!host) return;
+ if (typeof host.set === "function") {
+ host.set(...(specs || []));
+ return;
+ }
+ host.replaceChildren();
+ if (specs?.length) addContent(host, specs);
+}
+
+/**
+ * Orchestrates index load, navigation, search, and markdown rendering
+ * against a stamped host.
+ */
+export class DocsApp {
+ /**
+ * @param {HTMLElement} host slate-docs-viewer element
+ * @param {{ indexUrl?: string }} [options]
+ */
+ constructor(host, options = {}) {
+ this.host = host;
+ this.indexUrl = options.indexUrl || host.getAttribute("index") || "./index.json";
+ this.eventBus = new EventBus();
+ this.indexService = new IndexService();
+ this.searchService = new SearchService(this.eventBus, this.indexService);
+ this.documentService = new DocumentService(
+ this.eventBus,
+ this.indexService,
+ () => this.indexData
+ );
+ this.navigationService = new NavigationService(this.eventBus, () =>
+ this.indexData?.defaultPage || "home"
+ );
+ this.indexData = null;
+ this._outlineObserver = null;
+ this._searchTimer = null;
+
+ this.$ = {
+ nav: () => host.querySelector("[data-docs-nav]"),
+ outline: () => host.querySelector("[data-docs-outline]"),
+ markdown: () => host.querySelector("[data-docs-markdown]"),
+ error: () => host.querySelector("[data-docs-error]"),
+ progress: () => host.querySelector("[data-docs-progress]"),
+ articleScroll: () => host.querySelector("[data-docs-article-scroll]"),
+ siteName: () => host.querySelector("[data-docs-site-name]"),
+ authorRole: () => host.querySelector("[data-docs-author-role]"),
+ socials: () => host.querySelector("[data-docs-socials]"),
+ search: () => host.querySelector("[data-docs-search]"),
+ searchPanel: () => host.querySelector("[data-docs-search-panel]"),
+ };
+ }
+
+ async start() {
+ this.bindChrome();
+ this.eventBus.on("navigation:requested", ({ slug, hash }) => {
+ this.loadDocumentBySlug(slug, hash);
+ });
+
+ try {
+ const res = await fetch(this.indexUrl);
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
+ this.indexData = await res.json();
+ window._indexData = this.indexData;
+ this.applyIndexChrome(this.indexData);
+ this.searchService.buildSearchIndex(this.indexData.documents || []);
+
+ const nav = this.$.nav();
+ nav?.setDocuments?.(this.indexData.documents || []);
+
+ const { slug, hash } = this.navigationService.readLocation();
+ await this.loadDocumentBySlug(slug, hash);
+ } catch (err) {
+ this.showError(`Failed to load documentation index: ${err.message}`);
+ }
+ }
+
+ bindChrome() {
+ const nav = this.$.nav();
+ nav?.addEventListener("navigate", (e) => {
+ const slug = e.detail?.slug;
+ if (!slug) return;
+ this.navigationService.navigate(slug);
+ });
+
+ const outline = this.$.outline();
+ outline?.addEventListener("navigate", (e) => {
+ const id = e.detail?.id;
+ if (!id) return;
+ this.scrollToId(id, true);
+ });
+
+ const md = this.$.markdown();
+ md?.addEventListener("rendered", (e) => {
+ const headings = e.detail?.headings || [];
+ outline?.setHeadings?.(headings);
+ this.setupOutlineObserver(headings);
+ });
+ md?.addEventListener("headingclicked", (e) => {
+ const id = e.detail?.id;
+ if (!id) return;
+ this.scrollToId(id, true);
+ });
+
+ const search = this.$.search();
+ const onSearch = () => {
+ clearTimeout(this._searchTimer);
+ this._searchTimer = setTimeout(() => {
+ const value =
+ search?.getAttribute?.("text") ||
+ search?.textContent ||
+ "";
+ this.renderSearch(value);
+ }, 60);
+ };
+ search?.addEventListener("textchanged", onSearch);
+ search?.addEventListener("input", onSearch);
+ search?.addEventListener("keyup", onSearch);
+
+ document.addEventListener("keydown", (e) => {
+ const typing =
+ ["INPUT", "TEXTAREA"].includes(document.activeElement?.tagName) ||
+ document.activeElement?.isContentEditable ||
+ document.activeElement?.tagName === "EDITABLETEXT" ||
+ document.activeElement?.localName === "editabletext" ||
+ document.activeElement?.localName === "umc-editabletext";
+ if ((e.key === "s" || e.key === "S") && (e.altKey || (!e.ctrlKey && !e.metaKey && !typing))) {
+ if (!e.altKey && typing) return;
+ e.preventDefault();
+ this.focusSearch();
+ }
+ if (e.key === "Escape") this.hideSearchPanel();
+ });
+
+ document.addEventListener("click", (e) => {
+ const panel = this.$.searchPanel();
+ const searchField = this.$.search();
+ const block = this.host.querySelector(".docs-viewer-search-block");
+ if (!panel || panel.hasAttribute("hidden")) return;
+ if (block?.contains(e.target)) return;
+ if (panel.contains(e.target) || searchField?.contains(e.target)) return;
+ this.hideSearchPanel();
+ });
+ }
+
+ applyIndexChrome(data) {
+ const site = data.metadata?.site_name || data.metadata?.title || "Docs";
+ const siteEl = this.$.siteName();
+ if (siteEl) siteEl.setAttribute("text", site);
+ document.title = site;
+
+ const logo = this.host.querySelector("[data-docs-logo]");
+ if (logo) {
+ logo.setAttribute("alt", site);
+ const logoUrl = (data.metadata?.logo || this.host.getAttribute("logo") || "./img/logo.png").trim();
+ if (logoUrl) {
+ logo.setAttribute("src", logoUrl);
+ logo.removeAttribute("hidden");
+ siteEl?.setAttribute("hidden", "");
+ }
+ }
+
+ const role = this.$.authorRole();
+ if (role) {
+ role.setAttribute("text", data.author?.role || "");
+ role.toggleAttribute("hidden", !data.author?.role);
+ }
+
+ const socials = this.$.socials();
+ if (socials) {
+ const entries = (data.author?.socials || []).map((s) => {
+ const icon = faClassToIconName(s.icon);
+ const label = shortSocialLabel(s.title || s.url || "link");
+ // value carries the URL so selection can open it
+ return `${encodeURIComponent(s.url)}|${label}|${icon}`;
+ });
+ socials.setAttribute("options", entries.join(","));
+ socials.setAttribute("labels", "hide");
+ if (!socials._docsSocialBound) {
+ socials._docsSocialBound = true;
+ socials.addEventListener("selectionchanged", (e) => {
+ const raw = e.detail?.value || "";
+ let url = raw;
+ try {
+ url = decodeURIComponent(raw);
+ } catch {
+ /* keep raw */
+ }
+ if (/^https?:\/\//i.test(url)) {
+ window.open(url, "_blank", "noopener,noreferrer");
+ }
+ // don't leave a "selected" social highlighted
+ socials.removeAttribute("selected");
+ });
+ }
+ }
+
+ if (data.customCSS) {
+ const link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = data.customCSS;
+ document.head.appendChild(link);
+ }
+ }
+
+ async loadDocumentBySlug(slug, hash = "") {
+ const base = String(slug || "").split("#")[0];
+ const doc = this.indexService.findDocumentBySlug(this.indexData?.documents || [], base);
+ if (!doc) {
+ this.showError(`Document not found: ${base}`);
+ return;
+ }
+ if (doc.type === "folder") {
+ if (doc.path) await this.loadDocument(doc.path, hash, doc.slug);
+ else if (doc.items?.[0]?.path) {
+ await this.loadDocument(doc.items[0].path, hash, doc.items[0].slug);
+ } else this.showError("This folder is empty.");
+ return;
+ }
+ await this.loadDocument(doc.path, hash, doc.slug);
+ }
+
+ async loadDocument(path, hash = "", slug = "") {
+ this.setProgress(true);
+ this.clearError();
+ try {
+ const { content, title } = await this.documentService.loadDocument(path);
+ const md = this.$.markdown();
+ if (md) {
+ md.__mdContent = null;
+ md.setAttribute("content", content);
+ }
+ document.title = `${title} · ${this.indexData?.metadata?.site_name || "Docs"}`;
+
+ const fromPath = findSlugByPath(this.indexData?.documents || [], path);
+ this.$.nav()?.setAttribute("selected", fromPath || slug || "");
+
+ const scroll = this.$.articleScroll();
+ if (hash) {
+ const id = hash.replace(/^#/, "");
+ requestAnimationFrame(() => setTimeout(() => this.scrollToId(id, false), 40));
+ } else if (scroll) {
+ scroll.scrollTop = 0;
+ }
+ } catch (err) {
+ this.showError(err.message || "Error loading document.");
+ } finally {
+ this.setProgress(false);
+ }
+ }
+
+ setupOutlineObserver(headings) {
+ this._outlineObserver?.disconnect();
+ const root = this.$.articleScroll();
+ if (!root || !headings?.length) return;
+
+ const map = new Map(headings.map((h) => [h.el, h.id]));
+ this._outlineObserver = new IntersectionObserver(
+ (entries) => {
+ const visible = entries
+ .filter((e) => e.isIntersecting)
+ .sort((a, b) => a.target.offsetTop - b.target.offsetTop);
+ if (!visible.length) return;
+ const id = map.get(visible[0].target);
+ if (id) this.$.outline()?.setAttribute("active", id);
+ },
+ { root, rootMargin: "-48px 0px -55% 0px", threshold: [0, 0.25, 1] }
+ );
+ for (const h of headings) {
+ if (h.el) this._outlineObserver.observe(h.el);
+ }
+ }
+
+ scrollToId(id, pushHash) {
+ const el = this.host.querySelector(`#${CSS.escape(id)}`);
+ const scroll = this.$.articleScroll();
+ if (!el || !scroll) return;
+ // Open any collapsed ancestors so the heading is visible (docs fold).
+ let node = el.parentElement;
+ while (node && node !== this.host) {
+ if (node.localName === "slate-collapse" && !node.hasAttribute("open")) {
+ if (typeof node.toggle === "function") node.toggle(true);
+ else node.setAttribute("open", "");
+ }
+ node = node.parentElement;
+ }
+ const top = el.offsetTop - 12;
+ scroll.scrollTo({ top, behavior: "smooth" });
+ if (pushHash) {
+ const { slug } = this.navigationService.readLocation();
+ history.pushState(null, "", `?${slug}#${id}`);
+ }
+ el.classList.remove("md-heading-flash");
+ void el.offsetWidth;
+ el.classList.add("md-heading-flash");
+ }
+
+ renderSearch(query) {
+ const panel = this.$.searchPanel();
+ if (!panel) return;
+ const q = String(query || "").trim();
+ if (!q) {
+ this.hideSearchPanel();
+ return;
+ }
+ const results = this.searchService.search(q);
+ panel.removeAttribute("hidden");
+ const specs = results.length
+ ? results.map((r) => ({
+ tag: "border",
+ class: "docs-search-hit",
+ role: "option",
+ tabindex: "0",
+ "data-search-slug": r.slug,
+ children: [
+ { tag: "textblock", class: "docs-search-hit-title", text: r.title },
+ {
+ tag: "textblock",
+ class: "docs-search-hit-meta",
+ text: r.location || r.type,
+ },
+ ],
+ }))
+ : [
+ {
+ tag: "textblock",
+ class: "docs-search-hit-meta",
+ text: "No results",
+ },
+ ];
+ fill(panel, specs);
+ panel.onclick = (e) => {
+ const hit = e.target.closest?.("[data-search-slug]");
+ if (!hit) return;
+ const slug = hit.getAttribute("data-search-slug");
+ this.hideSearchPanel();
+ const search = this.$.search();
+ if (search) search.setAttribute("text", "");
+ this.navigationService.navigate(slug);
+ };
+ }
+
+ hideSearchPanel() {
+ this.$.searchPanel()?.setAttribute("hidden", "");
+ }
+
+ focusSearch() {
+ const field = this.$.search();
+ field?.focus?.();
+ const sidebar = this.host.querySelector("#docs-viewer-sidebar");
+ if (sidebar?.getAttribute("layout") === "drawer" && !sidebar.hasAttribute("open")) {
+ sidebar.setAttribute("open", "");
+ }
+ }
+
+ setProgress(on) {
+ this.$.progress()?.toggleAttribute("hidden", !on);
+ }
+
+ showError(message) {
+ const el = this.$.error();
+ if (!el) return;
+ el.removeAttribute("hidden");
+ el.setAttribute("text", message);
+ const md = this.$.markdown();
+ if (md) {
+ md.__mdContent = null;
+ md.setAttribute("content", "");
+ }
+ }
+
+ clearError() {
+ const el = this.$.error();
+ if (!el) return;
+ el.setAttribute("hidden", "");
+ el.removeAttribute("text");
+ }
+}
+
+function findSlugByPath(documents, path) {
+ for (const doc of documents || []) {
+ if (doc.path === path) return doc.slug;
+ if (doc.type === "folder" && doc.items) {
+ const found = findSlugByPath(doc.items, path);
+ if (found) return found;
+ }
+ }
+ return "";
+}
+
+/** Map Font Awesome class strings to slate-icon names (FA provider). */
+export function faClassToIconName(iconClass) {
+ const parts = String(iconClass || "")
+ .split(/\s+/)
+ .filter(Boolean);
+ const namePart = parts.find(
+ (p) =>
+ p.startsWith("fa-") &&
+ !["fa-solid", "fa-brands", "fa-regular", "fa-sharp"].includes(p) &&
+ p !== "fa"
+ );
+ const raw = (namePart || "link").replace(/^fa-/, "");
+ if (parts.some((p) => p.includes("brands") || p === "fab")) return `fab:${raw}`;
+ if (parts.some((p) => p.includes("regular") || p === "far")) return `far:${raw}`;
+ if (parts.some((p) => p.includes("solid") || p === "fas")) return `fas:${raw}`;
+ return raw;
+}
+
+function shortSocialLabel(title) {
+ const t = String(title || "").trim();
+ if (/github/i.test(t)) return "GitHub";
+ if (/youtube/i.test(t)) return "YouTube";
+ if (/steam/i.test(t)) return "Steam";
+ if (/discord/i.test(t)) return "Discord";
+ if (/bluesky|bsky/i.test(t)) return "Bluesky";
+ const head = t.split(/\s[-–—]\s/)[0]?.trim() || t;
+ return head.slice(0, 12) || "Link";
+}
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..e083a32
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,73 @@
+/**
+ * docs-viewer — documentation viewer on slatehtml + slatehtml-ui.
+ *
+ * import "slatehtml";
+ * import { mountDocs } from "docs-viewer";
+ * import "docs-viewer/prose.css";
+ *
+ * await mountDocs(document.querySelector("slate-docs-viewer"));
+ */
+
+import { deferCustomElementDefines } from "slatehtml/umc";
+import { DocsApp, faClassToIconName } from "./app.js";
+import { markdownToSpecs, collectHeadings } from "./markdown/render.js";
+import { EventBus } from "./services/event-bus.js";
+import { IndexService } from "./services/index-service.js";
+import { SearchService } from "./services/search-service.js";
+import { DocumentService } from "./services/document-service.js";
+import { NavigationService } from "./services/navigation-service.js";
+
+const modules = import.meta.glob(["./widgets/*.umc"]);
+
+await deferCustomElementDefines(async () => {
+ await Promise.all(
+ Object.entries(modules).map(async ([path, load]) => {
+ try {
+ await load();
+ } catch (err) {
+ console.error(`[slatehtml-docs] failed to load ${path}`, err);
+ }
+ })
+ );
+});
+
+/**
+ * Mount / start the docs app on a (or create one).
+ * @param {ParentNode|string|null} target
+ * @param {{ indexUrl?: string, create?: boolean }} [options]
+ * @returns {Promise}
+ */
+export async function mountDocs(target = "slate-docs-viewer", options = {}) {
+ let host =
+ typeof target === "string" ? document.querySelector(target) : target;
+
+ if (!host && options.create !== false) {
+ host = document.createElement("slate-docs-viewer");
+ if (options.indexUrl) host.setAttribute("index", options.indexUrl);
+ document.body.appendChild(host);
+ }
+ if (!host) throw new Error("mountDocs: no slate-docs-viewer host found");
+
+ // Wait a frame so UMC Construct/stamp finishes.
+ await Promise.resolve();
+ await new Promise((r) => requestAnimationFrame(() => r()));
+
+ const app = new DocsApp(host, {
+ indexUrl: options.indexUrl || host.getAttribute("index") || "./index.json",
+ });
+ await app.start();
+ host.__docsApp = app;
+ return app;
+}
+
+export {
+ DocsApp,
+ faClassToIconName,
+ markdownToSpecs,
+ collectHeadings,
+ EventBus,
+ IndexService,
+ SearchService,
+ DocumentService,
+ NavigationService,
+};
diff --git a/src/markdown/decorate.js b/src/markdown/decorate.js
new file mode 100644
index 0000000..7979984
--- /dev/null
+++ b/src/markdown/decorate.js
@@ -0,0 +1,24 @@
+/**
+ * Light post-pass for marked HTML: wiki `?slug` links + lazy images.
+ * Content is trusted site markdown — no tag stripping.
+ */
+
+export function decorateHtml(html) {
+ if (!html) return "";
+ if (typeof document === "undefined") return String(html);
+
+ const tpl = document.createElement("template");
+ tpl.innerHTML = String(html);
+ for (const a of tpl.content.querySelectorAll("a[href]")) {
+ const href = a.getAttribute("href") || "";
+ if (href.startsWith("?")) a.setAttribute("data-internal", "true");
+ else if (href.startsWith("http")) {
+ a.setAttribute("target", "_blank");
+ a.setAttribute("rel", "noopener noreferrer");
+ }
+ }
+ for (const img of tpl.content.querySelectorAll("img:not([loading])")) {
+ img.setAttribute("loading", "lazy");
+ }
+ return tpl.innerHTML;
+}
diff --git a/src/markdown/preprocess.js b/src/markdown/preprocess.js
new file mode 100644
index 0000000..127abf5
--- /dev/null
+++ b/src/markdown/preprocess.js
@@ -0,0 +1,60 @@
+/**
+ * Frontmatter, wiki links, and wiki image embeds (Docs-Viewer compatible).
+ */
+
+export function extractMetadata(content) {
+ const lines = String(content || "").trim().split("\n");
+ let metadata = {};
+ let contentStart = 0;
+
+ if (lines[0]?.trim() === "---") {
+ const endMetadata = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
+ if (endMetadata !== -1) {
+ const entries = lines
+ .slice(1, endMetadata)
+ .map((line) => line.match(/^([\w-]+):\s*(.*)$/))
+ .filter(Boolean)
+ .map(([, key, value]) => {
+ if (key === "defaultOpen") {
+ return [key, value.trim().toLowerCase() === "true"];
+ }
+ if (key === "sort") return [key, parseInt(value.trim(), 10)];
+ return [key, value.trim()];
+ });
+ metadata = Object.fromEntries(entries);
+ contentStart = endMetadata + 1;
+ }
+ }
+
+ return {
+ metadata,
+ content: lines.slice(contentStart).join("\n").trim(),
+ };
+}
+
+export function ensureTitle(content, title) {
+ const stripped = String(content || "")
+ .replace(/^#\s+.*$/m, "")
+ .trim();
+ return `# ${title}\n\n${stripped}`;
+}
+
+export function processWikiLinks(content, indexService, documents) {
+ return String(content || "").replace(/\[\[(.*?)\]\]/g, (match, linkText) => {
+ const [targetTitle, displayText] = linkText.split("|").map((s) => s.trim());
+ if (/\.(png|jpg|jpeg|gif|webp|mp4|webm)$/i.test(targetTitle)) return match;
+ const doc = indexService.findDocumentByTitle(documents, targetTitle);
+ if (!doc) return match;
+ return `[${displayText || doc.title}](?${doc.slug})`;
+ });
+}
+
+export function processImages(content, _basePath) {
+ return String(content || "").replace(/!\[\[(.*?)\]\]/g, (match, filename) => {
+ const mediaPath = `./docs/images/${filename}`;
+ if (filename.toLowerCase().endsWith(".mp4") || filename.toLowerCase().endsWith(".webm")) {
+ return `\n\n\n\n`;
+ }
+ return `\n\n\n\n`;
+ });
+}
diff --git a/src/markdown/render.js b/src/markdown/render.js
new file mode 100644
index 0000000..3e775bf
--- /dev/null
+++ b/src/markdown/render.js
@@ -0,0 +1,263 @@
+import { marked } from "marked";
+import hljs from "highlight.js/lib/core";
+import javascript from "highlight.js/lib/languages/javascript";
+import typescript from "highlight.js/lib/languages/typescript";
+import xml from "highlight.js/lib/languages/xml";
+import css from "highlight.js/lib/languages/css";
+import json from "highlight.js/lib/languages/json";
+import bash from "highlight.js/lib/languages/bash";
+import cpp from "highlight.js/lib/languages/cpp";
+import c from "highlight.js/lib/languages/c";
+import python from "highlight.js/lib/languages/python";
+import markdown from "highlight.js/lib/languages/markdown";
+import { decorateHtml } from "./decorate.js";
+import { slugifyHeader } from "./slug.js";
+
+hljs.registerLanguage("javascript", javascript);
+hljs.registerLanguage("js", javascript);
+hljs.registerLanguage("typescript", typescript);
+hljs.registerLanguage("ts", typescript);
+hljs.registerLanguage("xml", xml);
+hljs.registerLanguage("html", xml);
+hljs.registerLanguage("css", css);
+hljs.registerLanguage("json", json);
+hljs.registerLanguage("bash", bash);
+hljs.registerLanguage("sh", bash);
+hljs.registerLanguage("shell", bash);
+hljs.registerLanguage("cpp", cpp);
+hljs.registerLanguage("c", c);
+hljs.registerLanguage("python", python);
+hljs.registerLanguage("py", python);
+hljs.registerLanguage("markdown", markdown);
+hljs.registerLanguage("md", markdown);
+
+const HEADING_KIND = {
+ 1: "title",
+ 2: "section",
+ 3: "subtitle",
+ 4: "body",
+ 5: "body",
+ 6: "hint",
+};
+
+marked.setOptions({ breaks: true, gfm: true });
+
+/**
+ * Convert markdown source into slate `create()` specs (blocks as widgets,
+ * inline as HTML in slate-rich-text). Headings depth≥2 wrap following
+ * content in (docs-viewer fold behavior).
+ */
+export function markdownToSpecs(source) {
+ const tokens = marked.lexer(String(source || ""));
+ return tokensToSpecs(tokens);
+}
+
+function tokensToSpecs(tokens) {
+ const list = tokens || [];
+ const specs = [];
+ let i = 0;
+ while (i < list.length) {
+ const token = list[i];
+ if (token.type === "heading" && token.depth >= 2) {
+ const level = token.depth;
+ const bodyTokens = [];
+ i += 1;
+ while (i < list.length) {
+ const next = list[i];
+ if (next.type === "heading" && next.depth <= level) break;
+ bodyTokens.push(next);
+ i += 1;
+ }
+ const bodySpecs = tokensToSpecsFlat(bodyTokens);
+ specs.push(collapseSectionSpec(token, bodySpecs));
+ continue;
+ }
+ const spec = tokenToSpec(token);
+ if (spec) {
+ if (Array.isArray(spec)) specs.push(...spec);
+ else specs.push(spec);
+ }
+ i += 1;
+ }
+ return specs;
+}
+
+/** Flat map (no further heading→collapse grouping). */
+function tokensToSpecsFlat(tokens) {
+ const specs = [];
+ for (const token of tokens || []) {
+ const spec = tokenToSpec(token);
+ if (!spec) continue;
+ if (Array.isArray(spec)) specs.push(...spec);
+ else specs.push(spec);
+ }
+ return specs;
+}
+
+function headingSpec(token) {
+ const text = inlineText(token);
+ const id = slugifyHeader(text);
+ return {
+ tag: "slate-rich-text",
+ kind: `h${token.depth}`,
+ class: `md-h md-h${token.depth}`,
+ id,
+ html: decorateHtml(inlineHtml(token)),
+ "data-heading": String(token.depth),
+ "data-heading-text": text,
+ };
+}
+
+function collapseSectionSpec(headingToken, bodySpecs) {
+ const text = inlineText(headingToken);
+ const id = slugifyHeader(text);
+ return {
+ tag: "slate-collapse",
+ class: "md-collapse",
+ kind: "section",
+ open: "",
+ title: text,
+ id,
+ "data-heading": String(headingToken.depth),
+ "data-heading-text": text,
+ children: bodySpecs,
+ };
+}
+
+function tokenToSpec(token) {
+ switch (token.type) {
+ case "space":
+ return null;
+ case "heading":
+ return headingSpec(token);
+ case "paragraph":
+ return {
+ tag: "slate-rich-text",
+ kind: "paragraph",
+ class: "md-p",
+ html: decorateHtml(inlineHtml(token)),
+ };
+ case "text":
+ return {
+ tag: "slate-rich-text",
+ kind: "paragraph",
+ class: "md-p",
+ html: decorateHtml(inlineHtml(token)),
+ };
+ case "blockquote":
+ return {
+ tag: "border",
+ kind: "well",
+ class: "md-blockquote",
+ padding: "10 14",
+ children: tokensToSpecsFlat(token.tokens || []),
+ };
+ case "hr":
+ return { tag: "slate-divider", class: "md-hr" };
+ case "code":
+ return codeBlockSpec(token.text, token.lang);
+ case "list":
+ return listSpec(token);
+ case "table":
+ return tableSpec(token);
+ case "html":
+ return {
+ tag: "slate-rich-text",
+ kind: "html",
+ class: "md-html",
+ html: decorateHtml(token.text || token.raw || ""),
+ };
+ default:
+ if (token.raw) {
+ return {
+ tag: "slate-rich-text",
+ kind: "paragraph",
+ class: "md-p",
+ html: decorateHtml(marked.parse(token.raw)),
+ };
+ }
+ return null;
+ }
+}
+
+function listSpec(token) {
+ const tag = token.ordered ? "ol" : "ul";
+ // Keep list semantics in HTML for accessibility / nesting, styled via prose.
+ const html = decorateHtml(marked.Parser.parse([token]));
+ return {
+ tag: "slate-rich-text",
+ kind: "list",
+ class: `md-list md-list-${tag}`,
+ html,
+ };
+}
+
+function tableSpec(token) {
+ const html = decorateHtml(marked.Parser.parse([token]));
+ return {
+ tag: "slate-rich-text",
+ kind: "table",
+ class: "md-table-wrap",
+ html,
+ };
+}
+
+function codeBlockSpec(code, lang) {
+ let highlighted;
+ let language = lang || "";
+ try {
+ if (language && hljs.getLanguage(language)) {
+ highlighted = hljs.highlight(code, { language }).value;
+ } else {
+ const auto = hljs.highlightAuto(code);
+ highlighted = auto.value;
+ language = auto.language || "";
+ }
+ } catch {
+ highlighted = escapeHtml(code);
+ }
+ const cls = `hljs${language ? ` language-${language}` : ""}`;
+ return {
+ tag: "border",
+ class: "md-code-block",
+ padding: "16",
+ children: [
+ {
+ tag: "slate-rich-text",
+ kind: "code",
+ class: "md-code",
+ html: decorateHtml(`${highlighted}
`),
+ },
+ ],
+ };
+}
+
+function inlineHtml(token) {
+ if (token.tokens) return marked.Parser.parseInline(token.tokens);
+ if (token.text) return escapeHtml(token.text);
+ return "";
+}
+
+function inlineText(token) {
+ if (!token.tokens) return String(token.text || "").trim();
+ return token.tokens.map((t) => t.text || t.raw || "").join("").trim();
+}
+
+function escapeHtml(s) {
+ return String(s)
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """);
+}
+
+/** Collect outline entries from rendered article host. */
+export function collectHeadings(root) {
+ if (!root) return [];
+ return [...root.querySelectorAll("[data-heading]")].map((el) => ({
+ id: el.id || "",
+ level: Number(el.getAttribute("data-heading") || "2"),
+ text: el.getAttribute("data-heading-text") || el.textContent || "",
+ el,
+ }));
+}
diff --git a/src/markdown/slug.js b/src/markdown/slug.js
new file mode 100644
index 0000000..d8bd7e6
--- /dev/null
+++ b/src/markdown/slug.js
@@ -0,0 +1,7 @@
+/** Header text → URL fragment id (Docs-Viewer compatible). */
+export function slugifyHeader(header) {
+ return String(header || "")
+ .toLowerCase()
+ .replace(/[^\w\s-]/g, "")
+ .replace(/\s+/g, "-");
+}
diff --git a/src/prose.css b/src/prose.css
new file mode 100644
index 0000000..0938c12
--- /dev/null
+++ b/src/prose.css
@@ -0,0 +1,89 @@
+/* Article / highlight extras for docs-viewer (import alongside the viewer). */
+
+.md-collapse {
+ margin: 0;
+}
+
+/* Depth rhythm for nested heading collapses (OG markdown-content h2/h3). */
+.md-collapse[data-heading="3"] .slate-collapse-header {
+ margin: 2rem 0 0.75rem;
+}
+
+.md-collapse[data-heading="3"] .slate-collapse-title-text {
+ font-size: 1.5rem;
+ font-weight: 600;
+}
+
+.md-collapse[data-heading="4"] .slate-collapse-header,
+.md-collapse[data-heading="5"] .slate-collapse-header,
+.md-collapse[data-heading="6"] .slate-collapse-header {
+ margin: 1.5rem 0 0.5rem;
+}
+
+.md-collapse[data-heading="4"] .slate-collapse-title-text,
+.md-collapse[data-heading="5"] .slate-collapse-title-text,
+.md-collapse[data-heading="6"] .slate-collapse-title-text {
+ font-size: 1.15rem;
+ font-weight: 600;
+}
+
+.md-heading-flash {
+ animation: docs-heading-flash 1.1s ease;
+}
+
+@keyframes docs-heading-flash {
+ 0% {
+ background: color-mix(in srgb, var(--accent, #2196f3) 35%, transparent);
+ }
+ 100% {
+ background: transparent;
+ }
+}
+
+/* highlight.js-ish tokens (minimal, theme via currentColor) */
+.hljs {
+ color: inherit;
+ background: transparent;
+}
+.hljs-comment,
+.hljs-quote {
+ opacity: 0.55;
+ font-style: italic;
+}
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-built_in,
+.hljs-name,
+.hljs-tag {
+ color: var(--docs-syntax-keyword, #c6a0f6);
+}
+.hljs-string,
+.hljs-title,
+.hljs-section,
+.hljs-attribute,
+.hljs-literal,
+.hljs-template-tag,
+.hljs-template-variable,
+.hljs-type,
+.hljs-addition {
+ color: var(--docs-syntax-string, #a6da95);
+}
+.hljs-deletion,
+.hljs-selector-attr,
+.hljs-selector-pseudo,
+.hljs-meta {
+ color: var(--docs-syntax-meta, #ed8796);
+}
+.hljs-doctag,
+.hljs-number,
+.hljs-regexp {
+ color: var(--docs-syntax-number, #f5a97f);
+}
+.hljs-symbol,
+.hljs-variable,
+.hljs-template-variable,
+.hljs-link,
+.hljs-selector-id,
+.hljs-selector-class {
+ color: var(--docs-syntax-symbol, #8aadf4);
+}
diff --git a/src/services/document-service.js b/src/services/document-service.js
new file mode 100644
index 0000000..ae9174d
--- /dev/null
+++ b/src/services/document-service.js
@@ -0,0 +1,77 @@
+import { marked } from "marked";
+import { processWikiLinks, processImages, extractMetadata, ensureTitle } from "../markdown/preprocess.js";
+
+/**
+ * Load markdown docs, preprocess wiki links / images, cache results.
+ * Rendering to slate specs is handled by markdown/render.js.
+ */
+export class DocumentService {
+ constructor(eventBus, indexService, getIndexData) {
+ this.eventBus = eventBus;
+ this.indexService = indexService;
+ this.getIndexData = getIndexData;
+ this.documentCache = new Map();
+ this.cacheMaxSize = 20;
+ marked.setOptions({ breaks: true, gfm: true });
+ }
+
+ extractMetadata(content) {
+ return extractMetadata(content);
+ }
+
+ findDocInIndex(path) {
+ const data = this.getIndexData?.() || window._indexData;
+ const docs = data?.documents || [];
+ let doc = docs.find((d) => d.path === path);
+ if (doc) return doc;
+ for (const d of docs) {
+ if (d.type === "folder" && d.items) {
+ doc = d.items.find((item) => item.path === path);
+ if (doc) return doc;
+ // one more nesting level common in unreal-docs
+ for (const child of d.items) {
+ if (child.type === "folder" && child.items) {
+ doc = child.items.find((item) => item.path === path);
+ if (doc) return doc;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ async loadDocument(path) {
+ if (this.documentCache.has(path)) return this.documentCache.get(path);
+
+ const response = await fetch(path);
+ if (!response.ok) throw new Error(`Failed to load ${path}`);
+ const rawContent = await response.text();
+ const { metadata, content } = extractMetadata(rawContent);
+
+ const data = this.getIndexData?.() || window._indexData;
+ const indexDoc = this.findDocInIndex(path);
+ const title =
+ metadata.title || indexDoc?.title || path.split("/").pop().replace(/\.md$/, "");
+
+ let processed = processWikiLinks(content, this.indexService, data?.documents || []);
+ const basePath = path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : ".";
+ processed = processImages(processed, basePath);
+ processed = ensureTitle(processed, title);
+
+ const result = { content: processed, metadata, title, path };
+ if (this.documentCache.size >= this.cacheMaxSize) {
+ const firstKey = this.documentCache.keys().next().value;
+ this.documentCache.delete(firstKey);
+ }
+ this.documentCache.set(path, result);
+ return result;
+ }
+
+ clearCache() {
+ this.documentCache.clear();
+ }
+
+ invalidateDocument(path) {
+ this.documentCache.delete(path);
+ }
+}
diff --git a/src/services/event-bus.js b/src/services/event-bus.js
new file mode 100644
index 0000000..2ee57cc
--- /dev/null
+++ b/src/services/event-bus.js
@@ -0,0 +1,22 @@
+/** Simple pub-sub bus for docs viewer services. */
+export class EventBus {
+ constructor() {
+ this.events = {};
+ }
+
+ on(event, callback) {
+ if (!this.events[event]) this.events[event] = [];
+ this.events[event].push(callback);
+ return () => this.off(event, callback);
+ }
+
+ off(event, callback) {
+ if (!this.events[event]) return;
+ this.events[event] = this.events[event].filter((cb) => cb !== callback);
+ }
+
+ emit(event, data) {
+ if (!this.events[event]) return;
+ for (const callback of this.events[event]) callback(data);
+ }
+}
diff --git a/src/services/index-service.js b/src/services/index-service.js
new file mode 100644
index 0000000..972ae07
--- /dev/null
+++ b/src/services/index-service.js
@@ -0,0 +1,76 @@
+/** Document tree lookups against index.json. */
+export class IndexService {
+ findDocumentBySlug(documents, slug) {
+ for (const doc of documents || []) {
+ if (doc.slug === slug) return doc;
+ if (doc.type === "folder" && doc.items) {
+ const found = this.findDocumentBySlug(doc.items, slug);
+ if (found) return found;
+ }
+ }
+ return null;
+ }
+
+ findDocumentByTitle(documents, title) {
+ const want = String(title || "").trim();
+ if (!want) return null;
+ const wantLower = want.toLowerCase();
+ const wantSlug = wantLower.replace(/\s+/g, "-");
+
+ for (const doc of documents || []) {
+ if (doc.type === "folder" && doc.items) {
+ const found = this.findDocumentByTitle(doc.items, title);
+ if (found) return found;
+ continue;
+ }
+ if (docMatchesTitle(doc, want, wantLower, wantSlug)) return doc;
+ }
+ return null;
+ }
+
+ findParentFolders(documents, path, parentFolders = []) {
+ for (const doc of documents || []) {
+ if (doc.type !== "folder" || !doc.items) continue;
+ const found = doc.items.find((item) => {
+ if (item.path === path) return true;
+ if (item.type === "folder") {
+ return this.findParentFolders([item], path).length > 0;
+ }
+ return false;
+ });
+ if (found) {
+ parentFolders.push(doc);
+ for (const item of doc.items) {
+ if (item.type === "folder") {
+ this.findParentFolders([item], path, parentFolders);
+ }
+ }
+ }
+ }
+ return parentFolders;
+ }
+
+ /** Flatten file docs (and folder pages) in sidebar order. */
+ flattenDocuments(documents, out = []) {
+ for (const doc of documents || []) {
+ if (doc.type === "folder") {
+ if (doc.path && doc.showfolderpage !== "false") out.push(doc);
+ if (doc.items) this.flattenDocuments(doc.items, out);
+ } else {
+ out.push(doc);
+ }
+ }
+ return out;
+ }
+}
+
+function docMatchesTitle(doc, want, wantLower, wantSlug) {
+ if (!doc) return false;
+ if (doc.title === want) return true;
+ if (String(doc.title || "").toLowerCase() === wantLower) return true;
+ if (doc.path && doc.path.toLowerCase().endsWith(`/${wantSlug}.md`)) return true;
+ if (doc.path && doc.path.toLowerCase().endsWith(`${wantSlug}.md`)) return true;
+ if (doc.slug === wantSlug) return true;
+ if (doc.slug && doc.slug.toLowerCase().endsWith(`/${wantSlug}`)) return true;
+ return false;
+}
diff --git a/src/services/navigation-service.js b/src/services/navigation-service.js
new file mode 100644
index 0000000..d3d1cfb
--- /dev/null
+++ b/src/services/navigation-service.js
@@ -0,0 +1,54 @@
+/** Browser history + internal `?slug` link clicks. */
+export class NavigationService {
+ constructor(eventBus, getDefaultPage) {
+ this.eventBus = eventBus;
+ this.getDefaultPage = getDefaultPage;
+ this.setupEventListeners();
+ }
+
+ setupEventListeners() {
+ window.addEventListener("popstate", () => {
+ const { slug, hash } = this.readLocation();
+ this.eventBus.emit("navigation:requested", { slug, hash });
+ });
+
+ document.addEventListener("click", (e) => {
+ const target = e.target.closest?.("a[data-internal='true']");
+ if (!target) return;
+ e.preventDefault();
+ const raw = target.getAttribute("href") || "";
+ const slug = raw.includes("?") ? raw.split("?").pop() : raw.replace(/^\?/, "");
+ const [base, frag] = slug.split("#");
+ history.pushState(null, "", `?${base}${frag ? `#${frag}` : ""}`);
+ this.eventBus.emit("navigation:requested", {
+ slug: base,
+ hash: frag ? `#${frag}` : "",
+ });
+ });
+ }
+
+ readLocation() {
+ const search = window.location.search;
+ const hash = window.location.hash || "";
+ const slug =
+ search === "" || search === "?"
+ ? this.getDefaultPage?.() || "home"
+ : search.replace(/^\?/, "").split("#")[0];
+ return { slug, hash };
+ }
+
+ navigate(slug, { replace = false } = {}) {
+ const [base, frag] = String(slug || "").split("#");
+ const url = `?${base}${frag ? `#${frag}` : ""}`;
+ if (replace) history.replaceState(null, "", url);
+ else history.pushState(null, "", url);
+ this.eventBus.emit("navigation:requested", {
+ slug: base,
+ hash: frag ? `#${frag}` : hashFromUrl(),
+ });
+ }
+}
+
+function hashFromUrl() {
+ return window.location.hash || "";
+}
diff --git a/src/services/search-service.js b/src/services/search-service.js
new file mode 100644
index 0000000..71ad86f
--- /dev/null
+++ b/src/services/search-service.js
@@ -0,0 +1,100 @@
+import { slugifyHeader } from "../markdown/slug.js";
+
+/** Title/header search with a small LRU result cache. */
+export class SearchService {
+ constructor(eventBus, indexService) {
+ this.eventBus = eventBus;
+ this.indexService = indexService;
+ this.searchIndex = [];
+ this.searchCache = new Map();
+ this.cacheMaxSize = 50;
+ }
+
+ buildSearchIndex(documents) {
+ this.searchIndex = [];
+ this.searchCache.clear();
+ this.processDocuments(documents);
+ }
+
+ processDocuments(documents, parentPath = "") {
+ for (const doc of documents || []) {
+ if (doc.type === "folder") this.processFolderDocument(doc, parentPath);
+ else this.processFileDocument(doc, parentPath);
+ }
+ }
+
+ processFolderDocument(doc, parentPath) {
+ const currentPath = parentPath ? `${parentPath} / ${doc.title}` : doc.title;
+ if (doc.path) {
+ if (doc.showfolderpage !== "false") {
+ this.searchIndex.push({
+ title: doc.title,
+ path: doc.path,
+ slug: doc.slug,
+ location: currentPath,
+ type: "folder",
+ });
+ }
+ if (doc.headers) this.addHeadersToIndex(doc, currentPath, doc.title);
+ }
+ if (doc.items) this.processDocuments(doc.items, currentPath);
+ }
+
+ processFileDocument(doc, parentPath) {
+ this.searchIndex.push({
+ title: doc.title,
+ path: doc.path,
+ slug: doc.slug,
+ location: parentPath,
+ type: "file",
+ });
+ if (doc.headers) this.addHeadersToIndex(doc, parentPath, doc.title);
+ }
+
+ addHeadersToIndex(doc, location, docTitle) {
+ for (const header of doc.headers || []) {
+ this.searchIndex.push({
+ title: header,
+ path: doc.path,
+ slug: `${doc.slug}#${slugifyHeader(header)}`,
+ location: `${location} / ${docTitle}`,
+ type: "header",
+ });
+ }
+ }
+
+ search(query) {
+ if (!query) return [];
+ const q = query.toLowerCase();
+ if (this.searchCache.has(q)) return this.searchCache.get(q);
+
+ const results = this.searchIndex
+ .map((item) => this.scoreItem(item, q))
+ .filter((item) => item.score > 0)
+ .sort((a, b) => b.score - a.score)
+ .slice(0, 10);
+
+ if (this.searchCache.size >= this.cacheMaxSize) {
+ const firstKey = this.searchCache.keys().next().value;
+ this.searchCache.delete(firstKey);
+ }
+ this.searchCache.set(q, results);
+ return results;
+ }
+
+ scoreItem(item, query) {
+ const titleLower = item.title.toLowerCase();
+ let score = 0;
+ if (titleLower === query) score = 100;
+ else if (titleLower.startsWith(query)) score = 80;
+ else if (titleLower.includes(query)) score = 60;
+ else if (item.path?.toLowerCase().includes(query)) score = 40;
+ else if (item.location?.toLowerCase().includes(query)) score = 20;
+ if (item.type === "header") score += 5;
+ return { ...item, score };
+ }
+
+ clearCache() {
+ this.searchCache.clear();
+ }
+}
diff --git a/src/widgets/slate-docs-nav.umc b/src/widgets/slate-docs-nav.umc
new file mode 100644
index 0000000..93b80a6
--- /dev/null
+++ b/src/widgets/slate-docs-nav.umc
@@ -0,0 +1,309 @@
+--- html ---
+
+
+--- style ---
+self {
+ display: block; /* umc-layout-ok */
+ box-sizing: border-box;
+ width: 100%; /* umc-layout-ok */
+ color: inherit;
+ font: inherit;
+ min-width: 0;
+}
+
+.docs-nav-folder {
+ --widget-padding: 0;
+ --widget-border: none;
+ --widget-background: transparent;
+}
+
+.docs-nav-folder-face {
+ --widget-padding: 0.4rem 0.8rem;
+ --widget-radius: 4px;
+ --widget-border: none;
+ --widget-background: transparent;
+ cursor: pointer;
+}
+
+.docs-nav-folder-face:hover {
+ --widget-background: rgba(255, 255, 255, 0.04);
+ background: var(--widget-background);
+}
+
+.docs-nav-folder-label {
+ font-size: 0.875rem;
+ font-weight: 600;
+}
+
+.docs-nav-folder-body {
+ padding-left: 0.5rem; /* umc-layout-ok */
+}
+
+.docs-nav-link {
+ --widget-padding: 0.4rem 0.8rem;
+ --widget-radius: 4px;
+ --widget-border: none;
+ --widget-background: transparent;
+ cursor: pointer;
+}
+
+.docs-nav-link:hover {
+ --widget-background: rgba(255, 255, 255, 0.04);
+ background: var(--widget-background);
+}
+
+.docs-nav-link[active] {
+ --widget-background: color-mix(in srgb, var(--accent, #2196f3) 28%, transparent);
+ background: var(--widget-background);
+ color: #fff;
+}
+
+.docs-nav-link-label {
+ font-size: 0.875rem;
+}
+
+.docs-nav-link-icon,
+.docs-nav-folder-icon {
+ color: var(--muted, #a0a0a0);
+ flex: 0 0 auto; /* umc-layout-ok */
+}
+
+.docs-nav-link[active] .docs-nav-link-icon {
+ color: inherit;
+}
+
+--- script ---
+/**
+ * Document tree for the left rail.
+ * Uses index.json icons (FA class strings) when present — same as classic Docs-Viewer.
+ * Attrs: selected (slug)
+ * Events: navigate { slug }
+ */
+
+function folderOpen(doc, selected, parents) {
+ if (doc.defaultOpen) return true;
+ if (selected && parents.has(doc.slug)) return true;
+ return false;
+}
+
+function parentSlugSet(documents, selectedSlug) {
+ const parents = new Set();
+ if (!selectedSlug) return parents;
+ walk(documents, []);
+ function walk(docs, trail) {
+ for (const doc of docs || []) {
+ if (doc.slug === selectedSlug) {
+ for (const s of trail) parents.add(s);
+ return true;
+ }
+ if (doc.type === "folder" && doc.items) {
+ if (walk(doc.items, [...trail, doc.slug])) return true;
+ }
+ }
+ return false;
+ }
+ return parents;
+}
+
+/** FA class list from index.json → slate-icon name (fas:plug, fab:github, …). */
+function faToSlateIcon(raw, fallback = "") {
+ const parts = String(raw || "")
+ .split(/\s+/)
+ .filter(Boolean);
+ if (!parts.length) return fallback;
+ const namePart = parts.find(
+ (p) =>
+ p.startsWith("fa-") &&
+ !["fa-solid", "fa-brands", "fa-regular", "fa-sharp", "fas", "fab", "far", "fa"].includes(p)
+ );
+ if (!namePart) return fallback;
+ let name = namePart.replace(/^fa-/, "");
+ // Classic Docs-Viewer used file-alt; FA6 free solid aliases file-lines.
+ if (name === "file-alt") name = "file-lines";
+ if (parts.some((p) => p.includes("brands") || p === "fab")) return `fab:${name}`;
+ if (parts.some((p) => p.includes("regular") || p === "far")) return `far:${name}`;
+ return `fas:${name}`;
+}
+
+function folderIcon(doc, open) {
+ if (doc.icon) return faToSlateIcon(doc.icon, "fas:folder");
+ return open ? "fas:folder-open" : "fas:folder";
+}
+
+function fileIcon(doc) {
+ if (!doc.icon) return "";
+ return faToSlateIcon(doc.icon, "");
+}
+
+function linkSpec(doc, selected) {
+ const icon = fileIcon(doc);
+ const row = [];
+ if (icon) {
+ row.push({
+ tag: "slate-icon",
+ class: "docs-nav-link-icon",
+ name: icon,
+ size: "13",
+ });
+ }
+ row.push({
+ tag: "textblock",
+ class: "docs-nav-link-label",
+ text: doc.title || doc.slug,
+ });
+ return {
+ tag: "border",
+ class: "docs-nav-link",
+ active: doc.slug === selected ? "" : undefined,
+ role: "link",
+ tabindex: "0",
+ "data-slug": doc.slug,
+ children: [
+ {
+ tag: "horizontalbox",
+ gap: icon ? "8" : "0",
+ valign: "center",
+ children: row,
+ },
+ ],
+ };
+}
+
+function folderSpec(doc, selected, parents) {
+ const open = folderOpen(doc, selected, parents);
+ const kids = [];
+ if (doc.path && doc.showfolderpage !== "false") kids.push(linkSpec(doc, selected));
+ for (const item of doc.items || []) {
+ kids.push(item.type === "folder" ? folderSpec(item, selected, parents) : linkSpec(item, selected));
+ }
+ const custom = !!doc.icon;
+ return {
+ tag: "verticalbox",
+ class: "docs-nav-folder",
+ gap: "2",
+ "data-folder": doc.slug,
+ "data-folder-custom-icon": custom ? "" : undefined,
+ children: [
+ {
+ tag: "border",
+ class: "docs-nav-folder-face",
+ role: "button",
+ tabindex: "0",
+ "data-toggle-folder": doc.slug,
+ children: [
+ {
+ tag: "horizontalbox",
+ gap: "8",
+ valign: "center",
+ children: [
+ {
+ tag: "slate-icon",
+ class: "docs-nav-folder-icon",
+ name: folderIcon(doc, open),
+ size: "13",
+ "data-folder-icon": "",
+ },
+ {
+ tag: "textblock",
+ class: "docs-nav-folder-label",
+ text: doc.title || doc.slug,
+ },
+ ],
+ },
+ ],
+ },
+ {
+ tag: "verticalbox",
+ class: "docs-nav-folder-body",
+ gap: "2",
+ hidden: open ? undefined : "",
+ "data-folder-body": doc.slug,
+ children: kids,
+ },
+ ],
+ };
+}
+
+function buildTree(documents, selected) {
+ const parents = parentSlugSet(documents, selected);
+ return (documents || []).map((doc) =>
+ doc.type === "folder" ? folderSpec(doc, selected, parents) : linkSpec(doc, selected)
+ );
+}
+
+export default defineUmc({
+ tag: "slate-docs-nav",
+ attrs: {
+ selected: "",
+ documents: "",
+ },
+
+ Construct(el, api) {
+ el.setDocuments = (docs) => {
+ el.__documents = Array.isArray(docs) ? docs : [];
+ el.__navDirty = true;
+ sync(el, api);
+ };
+
+ el.addEventListener("click", (e) => {
+ const toggle = e.target.closest?.("[data-toggle-folder]");
+ if (toggle && el.contains(toggle)) {
+ const slug = toggle.getAttribute("data-toggle-folder");
+ const folder = el.querySelector(`[data-folder="${CSS.escape(slug)}"]`);
+ const body = el.querySelector(`[data-folder-body="${CSS.escape(slug)}"]`);
+ const icon = toggle.querySelector("[data-folder-icon]");
+ if (body) {
+ const open = body.hasAttribute("hidden");
+ if (open) body.removeAttribute("hidden");
+ else body.setAttribute("hidden", "");
+ // Classic Docs-Viewer: only default folder icons swap open/closed.
+ if (icon && folder && !folder.hasAttribute("data-folder-custom-icon")) {
+ icon.setAttribute("name", open ? "fas:folder-open" : "fas:folder");
+ }
+ }
+ return;
+ }
+ const link = e.target.closest?.("[data-slug]");
+ if (link && el.contains(link)) {
+ api.emit("navigate", { slug: link.getAttribute("data-slug") });
+ }
+ });
+
+ el.addEventListener("keydown", (e) => {
+ if (e.key !== "Enter" && e.key !== " ") return;
+ const link = e.target.closest?.("[data-slug], [data-toggle-folder]");
+ if (!link || !el.contains(link)) return;
+ e.preventDefault();
+ link.click();
+ });
+ },
+
+ SynchronizeProperties(el, api) {
+ sync(el, api);
+ },
+});
+
+function sync(el, api) {
+ let docs = el.__documents;
+ if (!docs) {
+ const raw = el.getAttribute("documents") || "";
+ if (raw) {
+ try {
+ docs = JSON.parse(raw);
+ } catch {
+ docs = [];
+ }
+ } else {
+ docs = [];
+ }
+ }
+ const selected = el.getAttribute("selected") || "";
+ const key = `${selected}::${docs.length}::${el.__navDirty || false}`;
+ if (el.__navKey === key && !el.__navDirty) return;
+ el.__navKey = key;
+ el.__navDirty = false;
+ el.set(...buildTree(docs, selected));
+}
+
+--- preview ---
+
diff --git a/src/widgets/slate-docs-outline.umc b/src/widgets/slate-docs-outline.umc
new file mode 100644
index 0000000..fc2cd20
--- /dev/null
+++ b/src/widgets/slate-docs-outline.umc
@@ -0,0 +1,124 @@
+--- html ---
+
+
+--- style ---
+self {
+ display: block; /* umc-layout-ok */
+ box-sizing: border-box;
+ width: 100%; /* umc-layout-ok */
+ color: inherit;
+ font: inherit;
+ min-width: 0;
+}
+
+.docs-outline-empty {
+ font-size: 12px;
+ color: var(--muted, #a0a0a0);
+}
+
+.docs-outline-link {
+ --widget-padding: 0.35rem 0.75rem;
+ --widget-radius: 0;
+ --widget-border: none;
+ --widget-background: transparent;
+ cursor: pointer;
+ border-left: 2px solid transparent;
+}
+
+.docs-outline-link:hover {
+ color: #fff;
+}
+
+.docs-outline-link[active] {
+ border-left-color: var(--accent, #2196f3);
+ color: #fff;
+}
+
+.docs-outline-label {
+ font-size: 12.5px;
+ line-height: 1.35;
+ color: var(--muted, #a0a0a0);
+}
+
+.docs-outline-link[active] .docs-outline-label,
+.docs-outline-link:hover .docs-outline-label {
+ color: inherit;
+}
+
+--- script ---
+/**
+ * On-page heading outline.
+ * Call el.setHeadings([{ id, level, text }]).
+ * Attrs: active (heading id)
+ * Events: navigate { id }
+ */
+
+function outlineSpecs(headings, active) {
+ if (!headings?.length) {
+ return [{ tag: "textblock", class: "docs-outline-empty", text: "" }];
+ }
+ return headings
+ .filter((h) => h.level >= 2)
+ .map((h) => ({
+ tag: "border",
+ class: "docs-outline-link",
+ active: h.id && h.id === active ? "" : undefined,
+ role: "link",
+ tabindex: "0",
+ "data-outline-id": h.id,
+ style: { paddingLeft: `${10 + Math.max(0, h.level - 2) * 10}px` },
+ children: [
+ {
+ tag: "textblock",
+ class: "docs-outline-label",
+ text: h.text || h.id,
+ },
+ ],
+ }));
+}
+
+export default defineUmc({
+ tag: "slate-docs-outline",
+ attrs: {
+ active: "",
+ },
+
+ Construct(el, api) {
+ el.__headings = [];
+ el.setHeadings = (list) => {
+ el.__headings = Array.isArray(list) ? list : [];
+ el.__outlineDirty = true;
+ sync(el);
+ };
+
+ el.addEventListener("click", (e) => {
+ const link = e.target.closest?.("[data-outline-id]");
+ if (!link || !el.contains(link)) return;
+ api.emit("navigate", { id: link.getAttribute("data-outline-id") || "" });
+ });
+
+ el.addEventListener("keydown", (e) => {
+ if (e.key !== "Enter" && e.key !== " ") return;
+ const link = e.target.closest?.("[data-outline-id]");
+ if (!link || !el.contains(link)) return;
+ e.preventDefault();
+ link.click();
+ });
+ },
+
+ SynchronizeProperties(el) {
+ sync(el);
+ },
+});
+
+function sync(el) {
+ const active = el.getAttribute("active") || "";
+ const key = `${active}::${el.__headings?.length || 0}::${el.__outlineDirty || false}`;
+ if (el.__outlineKey === key && !el.__outlineDirty) return;
+ el.__outlineKey = key;
+ el.__outlineDirty = false;
+ el.set(...outlineSpecs(el.__headings || [], active));
+}
+
+--- preview ---
+
diff --git a/src/widgets/slate-docs-viewer.umc b/src/widgets/slate-docs-viewer.umc
new file mode 100644
index 0000000..2dda749
--- /dev/null
+++ b/src/widgets/slate-docs-viewer.umc
@@ -0,0 +1,371 @@
+--- html ---
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+--- style ---
+self {
+ display: block; /* umc-layout-ok */
+ box-sizing: border-box;
+ width: 100%; /* umc-layout-ok */
+ height: 100%; /* umc-layout-ok */
+ min-height: 100vh; /* umc-layout-ok, app shell */
+ color: var(--ink, #e0e0e0);
+ background: var(--bg, #1a1a1a);
+ font-family: var(--slate-font, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);
+ --accent: var(--docs-accent, #2196f3);
+ --line: var(--docs-line, #404040);
+ --panel: var(--docs-panel, #252526);
+ --muted: var(--docs-muted, #a0a0a0);
+}
+
+.docs-viewer-root,
+.docs-viewer-shell {
+ height: 100%; /* umc-layout-ok */
+}
+
+.docs-viewer-brand {
+ flex: 0 0 auto; /* umc-layout-ok */
+}
+
+.docs-viewer-logo-img {
+ display: block; /* umc-layout-ok */
+ height: 24px; /* umc-layout-ok — OG .brand-logo */
+ width: auto; /* umc-layout-ok */
+ max-width: 100%; /* umc-layout-ok */
+ object-fit: contain;
+}
+
+.docs-viewer-logo-img[hidden] {
+ display: none !important;
+}
+
+.docs-viewer-menu {
+ --widget-background: transparent;
+ --widget-border: none;
+ cursor: pointer;
+ display: none; /* umc-layout-ok, drawer only */
+}
+
+self:has(#docs-viewer-sidebar[layout="drawer"][open]) .docs-viewer-menu {
+ display: block; /* umc-layout-ok */
+}
+
+.docs-viewer-menu-fab {
+ --widget-background: var(--panel);
+ --widget-border: 1px solid var(--line);
+ --widget-radius: 8px;
+ cursor: pointer;
+ z-index: 15; /* umc-layout-ok */
+ display: none; /* umc-layout-ok */
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
+}
+
+self:has(#docs-viewer-sidebar[layout="drawer"]:not([open])) .docs-viewer-menu-fab {
+ display: block; /* umc-layout-ok */
+}
+
+.docs-viewer-logo {
+ font-size: 18px;
+ font-weight: 700;
+ letter-spacing: -0.03em;
+ text-align: center;
+}
+
+.docs-viewer-author-role {
+ font-size: 0.75rem;
+ color: var(--muted);
+ line-height: 1.35;
+ text-align: center;
+ width: 100%; /* umc-layout-ok — center multiline taglines */
+}
+
+.docs-viewer-nav {
+ --sidebar-width: 300px;
+ border-right: 1px solid var(--line);
+ background: var(--panel);
+}
+
+.docs-viewer-outline-rail {
+ --sidebar-width: 240px;
+ --sidebar-bg: var(--bg, #1a1a1a);
+ border: none;
+ background: var(--bg, #1a1a1a);
+}
+
+.docs-viewer-outline-rail .slate-sidebar-panel {
+ --widget-background: var(--bg, #1a1a1a);
+ background: var(--bg, #1a1a1a);
+ border: none !important;
+}
+
+.docs-viewer-search-block {
+ border-bottom: 1px solid var(--line);
+ position: relative; /* umc-layout-ok, anchor results */
+ z-index: 20; /* umc-layout-ok */
+}
+
+.docs-viewer-search-box {
+ --widget-background: var(--bg);
+ --widget-border: 1px solid var(--line);
+ --widget-radius: 6px;
+ --widget-padding: 0 10px;
+ background: var(--widget-background);
+ border: var(--widget-border);
+ border-radius: var(--widget-radius);
+}
+
+.docs-viewer-search-input {
+ --widget-border: none;
+ --widget-background: transparent;
+ --widget-padding: 0;
+ font-size: 14px;
+ min-height: 34px; /* umc-layout-ok */
+ color: inherit;
+}
+
+.docs-viewer-search-hint {
+ font-size: 11px;
+ color: var(--muted);
+ opacity: 0.7;
+ flex: 0 0 auto; /* umc-layout-ok */
+}
+
+.docs-viewer-search-panel {
+ position: absolute; /* umc-layout-ok */
+ left: 12px; /* umc-layout-ok */
+ right: 12px; /* umc-layout-ok */
+ top: calc(100% - 4px); /* umc-layout-ok */
+ background: var(--panel);
+ border: 1px solid var(--line);
+ border-radius: 6px;
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.45);
+ max-height: min(50vh, 360px); /* umc-layout-ok */
+ overflow: auto; /* umc-layout-ok */
+ z-index: 30; /* umc-layout-ok */
+}
+
+.docs-viewer-search-panel[hidden] {
+ display: none !important;
+}
+
+.docs-search-hit {
+ --widget-padding: 8px 10px;
+ --widget-radius: 0;
+ --widget-border: none;
+ --widget-background: transparent;
+ cursor: pointer;
+ border-bottom: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
+}
+
+.docs-search-hit:last-child {
+ border-bottom: none;
+}
+
+.docs-search-hit:hover {
+ --widget-background: rgba(33, 150, 243, 0.12);
+ background: var(--widget-background);
+}
+
+.docs-search-hit-title {
+ font-size: 13px;
+ font-weight: 550;
+}
+
+.docs-search-hit-meta {
+ font-size: 11px;
+ color: var(--muted);
+ white-space: nowrap; /* umc-layout-ok */
+ overflow: hidden; /* umc-layout-ok */
+ text-overflow: ellipsis; /* umc-layout-ok */
+}
+
+.docs-viewer-footer {
+ border-top: 1px solid var(--line);
+ flex: 0 0 auto; /* umc-layout-ok */
+}
+
+.docs-viewer-socials {
+ --widget-background: transparent;
+ --widget-border: none;
+ --widget-padding: 4px 6px 8px;
+ border-top: none;
+}
+
+.docs-viewer-socials slate-bottom-nav-item {
+ opacity: 0.7;
+}
+
+.docs-viewer-socials slate-bottom-nav-item:hover {
+ opacity: 1;
+ color: var(--accent, #2196f3);
+}
+
+.docs-viewer-article-scroll {
+ background: var(--bg);
+}
+
+.docs-viewer-progress-wrap {
+ --widget-padding: 0;
+ --widget-border: none;
+ --widget-radius: 0;
+ --widget-background: transparent;
+}
+
+@media print {
+ .docs-viewer-nav,
+ .docs-viewer-outline-rail,
+ .docs-viewer-menu,
+ .docs-viewer-menu-fab,
+ .docs-viewer-search-block,
+ .docs-viewer-footer,
+ .docs-viewer-progress-wrap {
+ display: none !important;
+ }
+
+ self {
+ height: auto; /* umc-layout-ok */
+ min-height: 0;
+ background: #fff;
+ color: #111;
+ }
+}
+
+--- script ---
+/**
+ * Documentation viewer shell.
+ *
+ * import { mountDocs } from "docs-viewer";
+ * mountDocs(document.querySelector("slate-docs-viewer"));
+ */
+export default defineUmc({
+ tag: "slate-docs-viewer",
+ attrs: {
+ index: "./index.json",
+ logo: "./img/logo.png",
+ },
+
+ Construct(el) {
+ el.setAttribute("role", "application");
+ el.setAttribute("aria-label", "Documentation viewer");
+ },
+
+ SynchronizeProperties(el) {
+ const logoUrl = (el.getAttribute("logo") || "./img/logo.png").trim();
+ const img = el.querySelector("[data-docs-logo]");
+ const text = el.querySelector("[data-docs-site-name]");
+ if (img) {
+ if (logoUrl) {
+ img.setAttribute("src", logoUrl);
+ img.removeAttribute("hidden");
+ text?.setAttribute("hidden", "");
+ } else {
+ img.setAttribute("hidden", "");
+ text?.removeAttribute("hidden");
+ }
+ }
+ },
+});
+
+--- preview ---
+
diff --git a/src/widgets/slate-markdown.umc b/src/widgets/slate-markdown.umc
new file mode 100644
index 0000000..d783f8f
--- /dev/null
+++ b/src/widgets/slate-markdown.umc
@@ -0,0 +1,100 @@
+--- html ---
+
+
+--- style ---
+self {
+ display: block; /* umc-layout-ok */
+ box-sizing: border-box;
+ width: 100%; /* umc-layout-ok */
+ color: inherit;
+ font: inherit;
+ min-width: 0;
+}
+
+.self-loading {
+ opacity: 0.65;
+}
+
+.md-code-block {
+ --widget-padding: 1rem;
+ --widget-radius: 8px;
+ --widget-background: var(--panel, #252526);
+ --widget-border: 1px solid var(--line, #404040);
+ background: var(--widget-background);
+ border: var(--widget-border);
+ border-radius: var(--widget-radius);
+ margin: 1rem 0; /* umc-layout-ok */
+ overflow-x: auto; /* umc-layout-ok */
+ box-sizing: border-box;
+}
+
+.md-code-block .md-code,
+.md-code-block slate-rich-text {
+ margin: 0;
+}
+
+.md-blockquote {
+ --widget-border: none;
+ --widget-radius: 4px;
+ border-left: 4px solid var(--accent, currentColor);
+ margin: 1rem 0; /* umc-layout-ok */
+ opacity: 0.92;
+}
+
+.md-hr {
+ margin: 3rem 0; /* umc-layout-ok, OG hr */
+}
+
+--- script ---
+import { markdownToSpecs, collectHeadings } from "../markdown/render.js";
+
+/**
+ * Markdown → slate block tree (inline via slate-rich-text).
+ *
+ *
+ *
+ * Attrs: content (markdown source)
+ * Events: rendered { headings }, headingclicked { id, text }
+ */
+export default defineUmc({
+ tag: "slate-markdown",
+ attrs: {
+ content: "",
+ },
+
+ Construct(el, api) {
+ el.addEventListener("click", (e) => {
+ const heading = e.target.closest?.("[data-heading]");
+ if (!heading || !el.contains(heading)) return;
+ if (e.target.closest("a, button")) return;
+ const id = heading.id;
+ if (!id) return;
+ api.emit("headingclicked", {
+ id,
+ text: heading.getAttribute("data-heading-text") || heading.textContent || "",
+ });
+ });
+ },
+
+ SynchronizeProperties(el, api) {
+ const content = el.getAttribute("content") || "";
+ if (el.__mdContent === content) return;
+ el.__mdContent = content;
+ const specs = markdownToSpecs(content);
+ el.set(...specs);
+ const headings = collectHeadings(el);
+ api.emit("rendered", { headings });
+ },
+});
+
+--- preview ---
+
diff --git a/src/widgets/slate-rich-text.umc b/src/widgets/slate-rich-text.umc
new file mode 100644
index 0000000..5f18993
--- /dev/null
+++ b/src/widgets/slate-rich-text.umc
@@ -0,0 +1,195 @@
+--- html ---
+
+--- style ---
+self {
+ display: block; /* umc-layout-ok, prose leaf */
+ box-sizing: border-box;
+ width: 100%; /* umc-layout-ok */
+ color: inherit;
+ font: inherit;
+ font-size: 1rem;
+ line-height: 1.7;
+ min-width: 0;
+}
+
+self[kind="h1"] {
+ font-size: 2.5rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ line-height: 1.2;
+ margin: 0 0 1.5rem; /* umc-layout-ok, OG .markdown-content h1 */
+ cursor: pointer;
+}
+
+self[kind="h2"] {
+ font-size: 1.8rem;
+ font-weight: 600;
+ letter-spacing: -0.01em;
+ line-height: 1.3;
+ margin: 0; /* umc-layout-ok — slate-collapse supplies section rhythm */
+ cursor: pointer;
+ flex: 1 1 auto; /* umc-layout-ok */
+ min-width: 0; /* umc-layout-ok */
+}
+
+self[kind="h3"] {
+ font-size: 1.5rem;
+ font-weight: 600;
+ line-height: 1.35;
+ margin: 0; /* umc-layout-ok */
+ cursor: pointer;
+ flex: 1 1 auto; /* umc-layout-ok */
+ min-width: 0; /* umc-layout-ok */
+}
+
+self[kind="h4"],
+self[kind="h5"],
+self[kind="h6"] {
+ font-size: 1.1rem;
+ font-weight: 600;
+ margin: 1.25em 0 0.4em; /* umc-layout-ok */
+ cursor: pointer;
+}
+
+self[kind="paragraph"] {
+ font-size: 1rem;
+ line-height: 1.7;
+ margin: 1rem 0; /* umc-layout-ok, OG p */
+}
+
+self[kind="list"],
+self[kind="table"],
+self[kind="html"],
+self[kind="code"] {
+ margin: 1rem 0; /* umc-layout-ok */
+}
+
+self[kind="code"] {
+ margin: 0;
+}
+
+self a {
+ color: var(--accent, #2196f3);
+ text-decoration: none;
+}
+
+self a:hover {
+ text-decoration: underline;
+ text-underline-offset: 2px;
+}
+
+self code {
+ font-family: var(--slate-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
+ font-size: 0.9em;
+ padding: 0.2em 0.4em; /* umc-layout-ok */
+ border-radius: 1px;
+ background: rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ color: #e0e0e0;
+}
+
+self pre {
+ display: block; /* umc-layout-ok */
+ margin: 0;
+ font-family: var(--slate-font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
+ font-size: 0.9rem;
+ line-height: 1.5;
+ white-space: pre;
+ overflow-x: auto; /* umc-layout-ok */
+ background: transparent;
+ border: none;
+ padding: 0; /* umc-layout-ok — chrome lives on .md-code-block */
+}
+
+self pre code,
+self pre code.hljs,
+self code.hljs {
+ display: block; /* umc-layout-ok */
+ padding: 0; /* umc-layout-ok */
+ margin: 0;
+ background: none;
+ border: none;
+ border-radius: 0;
+ font-size: 0.9rem;
+ line-height: 1.5;
+ color: inherit;
+ white-space: inherit;
+}
+
+self img,
+self video {
+ max-width: 100%; /* umc-layout-ok */
+ height: auto; /* umc-layout-ok */
+ border-radius: 8px;
+ display: block; /* umc-layout-ok */
+ margin: 1rem 0; /* umc-layout-ok */
+}
+
+self ul,
+self ol {
+ margin: 1rem 0; /* umc-layout-ok */
+ padding-left: 1.35em; /* umc-layout-ok */
+}
+
+self li {
+ margin: 0.35em 0; /* umc-layout-ok */
+}
+
+self table {
+ width: 100%; /* umc-layout-ok */
+ border-collapse: separate;
+ border-spacing: 0;
+ font-size: 0.9rem;
+ margin: 1em 0; /* umc-layout-ok */
+}
+
+self th,
+self td {
+ border: none;
+ border-bottom: 1px solid var(--line, #404040);
+ padding: 0.75rem; /* umc-layout-ok */
+ text-align: left;
+}
+
+self th {
+ font-weight: 600;
+ background: rgba(255, 255, 255, 0.15);
+}
+
+self td {
+ background: rgba(255, 255, 255, 0.03);
+}
+
+--- script ---
+import { decorateHtml } from "../markdown/decorate.js";
+
+/**
+ * Inline / HTML island leaf for markdown.
+ *
+ *
+ *
+ * Attrs: html, kind (h1…h6 | paragraph | list | table | code | html)
+ */
+export default defineUmc({
+ tag: "slate-rich-text",
+ attrs: {
+ html: "",
+ kind: "paragraph",
+ },
+
+ SynchronizeProperties(el) {
+ const next = decorateHtml(el.getAttribute("html") || "");
+ if (el.__richHtml === next) return;
+ el.__richHtml = next;
+ el.innerHTML = next;
+ },
+});
+
+--- preview ---
+
+
+
+
diff --git a/styles.css b/styles.css
deleted file mode 100644
index aa8dade..0000000
--- a/styles.css
+++ /dev/null
@@ -1,1175 +0,0 @@
-:root {
- --doc-sidebar-width: 300px;
- --doc-outline-width: 240px;
- --ifm-color-primary: #2196f3;
- --ifm-color-primary-dark: #1976d2;
- --ifm-background-color: #1a1a1a;
- --ifm-background-surface-color: #252526;
- --ifm-color-content: #e0e0e0;
- --ifm-color-content-secondary: #a0a0a0;
- --ifm-link-color: #2196f3;
- --ifm-border-color: #404040;
- --title-bar-height: 48px;
- --sidebar-transition: transform 0.3s ease;
-}
-
-body {
- margin: 0;
- padding: 0;
- font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;
- background: var(--ifm-background-color);
- color: var(--ifm-color-content);
-}
-
-.container {
- display: flex;
- flex-direction: column;
-}
-
-.title-bar {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 1000;
- height: var(--title-bar-height);
- background-color: var(--ifm-background-surface-color);
- border-bottom: 1px solid var(--ifm-border-color);
- display: flex;
- align-items: center;
- padding: 0 1rem;
-}
-
-.content-container {
- margin-top: var(--title-bar-height);
- display: flex;
- min-height: calc(100vh - var(--title-bar-height));
-}
-
-.left-sidebar {
- position: fixed;
- top: var(--title-bar-height);
- left: 0;
- bottom: 0;
- width: var(--doc-sidebar-width);
- background: var(--ifm-background-surface-color);
- display: flex;
- flex-direction: column;
- border-right: 1px solid var(--ifm-border-color);
- transform: none;
- transition: none;
- z-index: 999;
-}
-
-.search-container {
- padding: 1rem;
- border-bottom: 1px solid var(--ifm-border-color);
- position: relative; /* Add this */
-}
-
-.search-box {
- position: relative;
- display: flex;
- align-items: center;
- background: var(--ifm-background-color);
- border: 1px solid var(--ifm-border-color);
- border-radius: 6px;
- padding: 0.5rem;
-}
-
-.search-box i {
- color: var(--ifm-color-content-secondary);
- font-size: 0.9rem;
-}
-
-.search-box .fa-search {
- margin-right: 0.5rem;
-}
-
-.search-box .fa-times {
- cursor: pointer;
- opacity: 0.6;
- transition: opacity 0.2s;
- margin-left: 0;
-}
-
-.search-box .fa-times:hover {
- opacity: 1;
-}
-
-.search-box input {
- background: none;
- border: none;
- color: var(--ifm-color-content);
- font-size: 0.9rem;
- width: 100%;
- outline: none;
-}
-
-.search-box input::placeholder {
- color: var(--ifm-color-content-secondary);
- opacity: 0.6;
-}
-
-#search-results {
- position: absolute;
- top: calc(var(--title-bar-height) + 3.5rem); /* Change this */
- left: 0; /* Change this */
- right: 0; /* Change this */
- background: var(--ifm-background-surface-color);
- border: 1px solid var(--ifm-border-color);
- border-radius: 6px;
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
- z-index: 1001; /* Increase z-index */
- max-height: calc(100vh - var(--title-bar-height) - 4rem);
- overflow-y: auto;
- margin: 0 1rem; /* Add margins */
- display: none;
-}
-
-.search-result {
- border-bottom: 1px solid var(--ifm-border-color);
-}
-
-.search-result:last-child {
- border-bottom: none;
-}
-
-.search-result a {
- display: flex;
- align-items: center;
- padding: 0.75rem 1rem;
- color: var(--ifm-color-content);
- text-decoration: none;
- gap: 0.75rem;
-}
-
-.search-result a:hover {
- background: rgba(255, 255, 255, 0.05);
-}
-
-.search-result i {
- color: var(--ifm-color-content-secondary);
- font-size: 0.9rem;
- width: 16px;
-}
-
-.search-result-content {
- flex: 1;
- min-width: 0;
-}
-
-.search-result-title {
- font-weight: 500;
- margin-bottom: 0.25rem;
-}
-
-.search-result-path {
- font-size: 0.8rem;
- color: var(--ifm-color-content-secondary);
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.right-sidebar {
- position: fixed;
- top: var(--title-bar-height);
- right: 0;
- bottom: 0;
- width: var(--doc-outline-width);
- background: var(--ifm-background-color);
- overflow-y: auto;
- border-left: none;
-}
-
-.content {
- margin-left: var(--doc-sidebar-width);
- margin-right: var(--doc-outline-width);
- padding: 2rem 3rem;
- min-height: calc(100vh - var(--title-bar-height));
-}
-
-#file-index {
- flex: 1;
- overflow-y: auto;
- padding-top: 20px;
-}
-
-@media (max-width: 1000px) {
- .menu-button {
- display: flex !important;
- }
-
- .left-sidebar {
- left: -100%;
- width: min(var(--doc-sidebar-width), 80vw);
- transition: left 0.3s ease;
- box-shadow: none;
- }
-
- .left-sidebar.show {
- left: 0;
- box-shadow: 2px 0 8px rgba(0, 0, 0, 0.2);
- }
-
- .right-sidebar {
- display: none;
- }
-
- .content {
- margin-left: 0;
- margin-right: 0;
- padding: 1rem;
- }
-
- .markdown-content {
- padding: 0 0.5rem;
- }
-
- #file-index {
- padding-top: 0.75rem;
- }
-}
-
-@media (max-width: 480px) {
- .content {
- padding: 0.75rem 0.5rem;
- }
-
- .markdown-content {
- padding: 0 0.25rem;
- }
-}
-
-.menu-button {
- display: none;
- background: none;
- border: none;
- color: var(--ifm-color-content);
- padding: 8px;
- cursor: pointer;
- width: 40px;
- height: 40px;
- align-items: center;
- justify-content: center;
-}
-
-.menu-button i {
- font-size: 1.4rem;
-}
-
-.menu-button:hover {
- color: var(--ifm-color-primary);
-}
-
-.sidebar {
- color: var(--ifm-color-content);
- overflow-y: auto;
- border-color: var(--ifm-border-color);
- height: calc(100vh - var(--title-bar-height));
-}
-
-#file-index a {
- color: var(--ifm-color-content);
- text-decoration: none;
- display: block;
- padding: 0.4rem 0.8rem;
- font-size: 0.875rem;
- position: relative;
- transition: background-color 0.2s ease;
-}
-
-#file-index a.loading {
- pointer-events: none;
- color: var(--ifm-color-content-secondary);
-}
-
-#file-index a.loading::after {
- content: '';
- position: absolute;
- left: 0;
- bottom: 0;
- height: 2px;
- background: var(--ifm-color-primary);
- animation: loading-progress 1s ease-in-out infinite;
-}
-
-@keyframes loading-progress {
- 0% {
- width: 0%;
- left: 0;
- }
- 50% {
- width: 70%;
- left: 0;
- }
- 100% {
- width: 0%;
- left: 100%;
- }
-}
-
-#file-index a:hover {
- background-color: rgba(0, 0, 0, 0.05);
- color: var(--ifm-color-primary);
- border-left: 2px solid var(--ifm-color-primary);
-}
-
-#file-index a.active {
- color: var(--ifm-color-primary);
- background-color: rgba(33, 150, 243, 0.1);
- border-left: 2px solid var(--ifm-color-primary);
-}
-
-.folder {
- color: var(--ifm-color-content);
- padding-right: 0;
-}
-
-.folder-header {
- display: flex;
- align-items: center;
- padding: 0.4rem 0.8rem;
- cursor: pointer;
- user-select: none;
- font-size: 0.875rem;
- width: 100%;
- position: relative;
- box-sizing: border-box;
-}
-
-.folder-icons {
- display: flex;
- align-items: center;
- gap: 0.25rem;
- color: var(--ifm-color-content-secondary);
- min-width: 1.5em;
- margin-right: 0.25rem;
- order: 1;
-}
-
-.folder-header span {
- order: 2;
- margin-right: auto;
-}
-
-.folder-link {
- display: flex;
- align-items: center;
- color: var(--ifm-color-content-secondary);
- opacity: 0.6;
- font-size: 0.8em;
- padding: 2px 8px;
- border-radius: 3px;
- position: absolute;
- right: 0;
- top: 50%;
- transform: translateY(-50%);
- transition: opacity 0.2s ease;
-}
-
-.folder-link.loading {
- opacity: 1;
- color: var(--ifm-color-primary);
-}
-
-.folder-link.loading i {
- animation: spin 1s linear infinite;
-}
-
-@keyframes spin {
- from { transform: rotate(0deg); }
- to { transform: rotate(360deg); }
-}
-
-.folder-header:hover .folder-link {
- opacity: 0.8;
-}
-
-.folder-link:hover {
- opacity: 1 !important;
- color: var(--ifm-color-primary);
- background-color: rgba(255, 255, 255, 0.05);
-}
-
-.folder-content {
- display: none;
- padding-left: 0.6rem;
-}
-
-.folder.open > .folder-content {
- display: block;
-}
-
-.folder .folder-header:hover .folder-icon {
- color: var(--ifm-color-primary);
-}
-
-.social-links {
- padding: 0.5rem 1rem;
- display: flex;
- justify-content: center;
- gap: 1rem;
- flex-wrap: wrap;
- background: var(--ifm-background-surface-color);
-}
-
-.social-links a {
- color: var(--ifm-color-content-secondary);
- font-size: 1.2rem;
- transition: color 0.2s;
-}
-
-social-links a:hover {
- color: var(--ifm-color-primary);
-}
-
-.subtitle {
- margin-top: auto;
- text-align: center;
- padding: 0.5rem 1rem;
- font-size: 0.8rem;
- color: var(--ifm-color-content-secondary);
- background: var(--ifm-background-surface-color);
-}
-
-.subtitle .name {
- color: var(--ifm-color-content);
- font-weight: 500;
-}
-
-.subtitle .role {
- display: block;
- font-size: 0.75rem;
- margin-top: 0.2rem;
-}
-
-.content {
- flex: 1;
- background-color: var(--ifm-background-color);
- min-width: 0;
- color: var(--ifm-color-content);
- height: 100%;
- padding-bottom: calc(2rem + env(safe-area-inset-bottom));
-}
-
-#file-index a, #document-outline a {
- color: var(--ifm-color-content);
- text-decoration: none;
- display: block;
- padding: 0.4rem 0.8rem;
- font-size: 0.875rem;
-}
-
-#file-index a:hover, #document-outline a:hover {
- background-color: rgba(0, 0, 0, 0.05);
- color: var(--ifm-color-primary);
-}
-
-#document-outline {
- padding: 1rem;
-}
-
-#document-outline a {
- color: var(--ifm-color-content-secondary);
- border-left: 2px solid transparent;
- margin: 2px 0;
- padding: 0.25em 1rem;
- transition: all 0.2s ease;
-}
-
-#document-outline a.active {
- color: var(--ifm-color-primary);
- border-left-color: var(--ifm-color-primary);
- background: rgba(33, 150, 243, 0.1);
-}
-
-.markdown-content {
- line-height: 1.7;
- font-size: 1rem;
- max-width: 130ch;
- margin: 0 auto;
-}
-
-.markdown-content h1 {
- font-size: 2.5rem;
- font-weight: 700;
- margin-bottom: 1.5rem;
- text-align: left;
-}
-
-.markdown-content h2 {
- font-size: 1.8rem;
- margin-top: 2.5rem;
- margin-bottom: 1rem;
- font-weight: 600;
- text-align: left;
-}
-
-.markdown-content h3 {
- font-size: 1.5rem;
- margin-top: 2rem;
- font-weight: 600;
- text-align: left;
-}
-
-.markdown-content a {
- color: var(--ifm-link-color);
- text-decoration: none;
-}
-
-.markdown-content a:hover {
- text-decoration: underline;
-}
-
-.markdown-content p {
- margin: 1rem 0;
-}
-
-.markdown-content img {
- max-width: 100%;
- height: auto;
- border-radius: 8px;
-}
-
-.markdown-content table {
- border-collapse: separate;
- border-spacing: 0;
- width: 100%;
- margin: 1em 0;
- font-size: 0.9rem;
-}
-
-.markdown-content th {
- background-color: rgba(255, 255, 255, 0.15);
- font-weight: 600;
- border: none;
- border-bottom: 1px solid var(--ifm-border-color);
- text-align: left;
- padding: 0.75rem;
-}
-
-.markdown-content th:first-child {
- border-top-left-radius: 10px;
-}
-
-.markdown-content th:last-child {
- border-top-right-radius: 10px;
-}
-
-.markdown-content td {
- border: none;
- border-bottom: 1px solid var(--ifm-border-color);
- padding: 0.75rem;
- background-color: rgba(255, 255, 255, 0.03);
-}
-
-.markdown-content tr:last-child td {
- border-bottom: none;
-}
-
-.markdown-content tr:last-child td:first-child {
- border-bottom-left-radius: 10px;
-}
-
-.markdown-content tr:last-child td:last-child {
- border-bottom-right-radius: 10px;
-}
-
-.markdown-content code:not([class*="language-"]) {
- background-color: rgba(255, 255, 255, 0.1);
- padding: 0.2em 0.4em;
- border-radius: 1px;
- font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
- font-size: 0.9em;
- color: #e0e0e0;
- border: 1px solid rgba(255, 255, 255, 0.1);
-}
-
-.markdown-content blockquote {
- border-left: 4px solid var(--ifm-color-primary);
- margin: 1rem 0;
- padding: 0.5rem 1rem;
- background-color: var(--ifm-background-surface-color);
- border-radius: 4px;
- color: var(--ifm-color-content-secondary);
-}
-
-.markdown-content video {
- max-width: 100%;
- height: auto;
- margin: 1rem 0;
- border-radius: 8px;
- box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
- position: relative;
- z-index: 1;
-}
-
-.markdown-content hr {
- margin: 3rem 0;
- border: none;
- border-bottom: 1px solid var(--ifm-border-color);
-}
-
-.markdown-content pre {
- background-color: var(--ifm-background-surface-color);
- border: 1px solid var(--ifm-border-color);
- border-radius: 8px;
- padding: 1rem;
- margin: 1rem 0;
- overflow-x: auto;
-}
-
-.markdown-content pre code {
- background: none;
- border: none;
- padding: 0;
- font-size: 0.9rem;
- line-height: 1.5;
- color: inherit;
-}
-
-::-webkit-scrollbar {
- width: 7px;
- height: 7px;
-}
-
-::-webkit-scrollbar-track {
- background: var(--ifm-background-color);
-}
-
-::-webkit-scrollbar-thumb {
- background-color: #505050;
- border-radius: 4px;
-}
-
-::-webkit-scrollbar-thumb:hover {
- background-color: #606060;
-}
-
-::-webkit-scrollbar-button {
- display: none !important;
- width: 0 !important;
- height: 0 !important;
- background: transparent !important;
-}
-
-::-webkit-scrollbar-corner {
- background-color: transparent;
-}
-
-* {
- scrollbar-width: thin;
- scrollbar-color: #505050 var(--ifm-background-color);
-}
-
-@keyframes highlight {
- 0% { background-color: rgba(33, 150, 243, 0.2); }
- 100% { background-color: transparent; }
-}
-
-.markdown-content h1.highlight,
-.markdown-content h2.highlight,
-.markdown-content h3.highlight,
-.markdown-content h4.highlight,
-.markdown-content h5.highlight,
-.markdown-content h6.highlight {
- animation: highlight 2s ease-out;
-}
-
-.title-text {
- display: flex;
- align-items: center;
- gap: 0.5rem;
-}
-
-.brand-logo {
- height: 24px;
- width: auto;
- display: block;
-}
-
-.title-text .divider {
- padding: 0 0 0 10px;
-}
-
-.title-text .divider,
-.title-text .page-title {
- font-size: 0.9rem;
-}
-
-#document-outline a.active {
- color: var(--ifm-color-primary);
- border-left-color: var(--ifm-color-primary);
- background: rgba(33, 150, 243, 0.1);
-}
-
-.github-link {
- padding: 1rem;
- text-align: center;
- font-size: 0.875rem;
- background: var(--ifm-background-surface-color);
- border-top: 1px solid var(--ifm-border-color);
-}
-
-.github-link a {
- color: var(--ifm-color-content-secondary);
- text-decoration: none;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 0.5rem;
-}
-
-.github-link a:hover {
- color: var(--ifm-color-primary);
-}
-
-.clickable-header {
- cursor: pointer;
- position: relative;
-}
-.clickable-header:hover {
- color: #3498db;
-}
-.clickable-header.highlight {
- animation: highlight-fade 1.5s;
-}
-@keyframes highlight-fade {
- 0% { background-color: rgba(52, 152, 219, 0.2); }
- 100% { background-color: transparent; }
-}
-
-/* Add a skip link for keyboard navigation */
-.skip-to-content {
- position: absolute;
- top: -40px;
- left: 0;
- padding: 8px;
- z-index: 2000;
- background: var(--ifm-color-primary);
- color: white;
- font-weight: bold;
- transition: top 0.2s;
-}
-
-.skip-to-content:focus {
- top: 0;
- outline: 2px solid white;
-}
-
-/* Improve focus styles for better keyboard navigation */
-/* Remove the conflicting focus style rules */
-/* Remove these conflicting rules */
-*:focus {
- outline: none !important;
-}
-
-*:focus-visible {
- outline: 2px solid var(--ifm-color-primary) !important;
- outline-offset: 2px !important;
-}
-
-a:focus,
-button:focus,
-input:focus,
-.folder-header:focus,
-.clickable-header:focus {
- outline: none !important;
-}
-
-a:focus-visible,
-button:focus-visible,
-input:focus-visible,
-.folder-header:focus-visible,
-.clickable-header:focus-visible {
- outline: none !important;
-}
-
-/* Add clean focus style rules */
-/* This allows keyboard focus to be visible but hides it for mouse clicks */
-:focus {
- outline: none !important;
-}
-
-/* Only show focus styles for keyboard navigation */
-:focus-visible {
- outline: 2px solid var(--ifm-color-primary) !important;
- outline-offset: 2px !important;
-}
-
-/* Force focus visible off for mouse interaction */
-:focus:not(:focus-visible) {
- outline: none !important;
-}
-
-/* Ensure contrast for links and interactive elements */
-.folder-header:hover {
- background-color: rgba(255, 255, 255, 0.05);
-}
-
-/* Keep the skip link visible when it gets focus */
-.skip-to-content:focus {
- top: 0;
- outline: 2px solid white !important;
-}
-
-/* Don't hide keyboard focus styles on mobile */
-@media (max-width: 1000px) {
- :focus-visible {
- outline: 2px solid var(--ifm-color-primary) !important;
- }
-
- .menu-button {
- display: flex !important;
- }
-
- .left-sidebar {
- left: -100%;
- width: min(var(--doc-sidebar-width), 80vw);
- transition: left 0.3s ease;
- box-shadow: none;
- }
-
- .left-sidebar.show {
- left: 0;
- box-shadow: 2px 0 8px rgba(0, 0, 0, 0.2);
- }
-
- .right-sidebar {
- display: none;
- }
-
- .content {
- margin-left: 0;
- margin-right: 0;
- padding: 1rem;
- }
-
- .markdown-content {
- padding: 0 0.5rem;
- }
-
- #file-index {
- padding-top: 0.75rem;
- }
-}
-
-/* Make sure aria-hidden elements aren't focusable */
-[aria-hidden="true"] {
- pointer-events: none;
-}
-
-/* Don't hide focus-visible on mobile */
-@media (max-width: 1000px) {
- *:focus-visible {
- outline: 2px solid var(--ifm-color-primary) !important;
- }
-
- .menu-button {
- display: flex !important;
- }
-
- .left-sidebar {
- left: -100%;
- width: min(var(--doc-sidebar-width), 80vw);
- transition: left 0.3s ease;
- box-shadow: none;
- }
-
- .left-sidebar.show {
- left: 0;
- box-shadow: 2px 0 8px rgba(0, 0, 0, 0.2);
- }
-
- .right-sidebar {
- display: none;
- }
-
- .content {
- margin-left: 0;
- margin-right: 0;
- padding: 1rem;
- }
-
- .markdown-content {
- padding: 0 0.5rem;
- }
-
- #file-index {
- padding-top: 0.75rem;
- }
-}
-
-/* Style the keyboard shortcut indicator */
-.keyboard-shortcut {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 18px;
- height: 18px;
- border: 1px solid var(--ifm-color-content-secondary);
- border-radius: 3px;
- font-size: 0.75rem;
- margin-right: 0.5rem;
- color: var(--ifm-color-content-secondary);
- opacity: 0.7;
- user-select: none;
-}
-
-/* Style the Alt+S keyboard shortcut */
-.keyboard-shortcut-alt {
- width: auto;
- padding: 0 4px;
- font-size: 0.7rem;
-}
-
-/* Hide keyboard shortcut on mobile */
-@media (max-width: 768px) {
- .keyboard-shortcut {
- display: none;
- }
-}
-
-/* Print styles... */
-@media print {
- .title-bar,
- .left-sidebar,
- .right-sidebar,
- .menu-button,
- .social-links,
- .github-link,
- .subtitle {
- display: none !important;
- }
-
- .content {
- margin: 0 !important;
- padding: 0 !important;
- max-width: 100% !important;
- }
-
- .content-container {
- margin: 0 !important;
- }
-
- body {
- background: white !important;
- color: black !important;
- font-size: 12pt !important;
- line-height: 1.5 !important;
- margin: 0.25in !important; /* Reduced margins */
- }
-
- /* Remove the logo and redundant breadcrumb display */
- .content-container::before,
- .content-container::after {
- display: none !important;
- }
-
- /* Simple print header */
- .print-header {
- display: block !important;
- margin-bottom: 15pt;
- border-bottom: 1pt solid #ddd;
- padding-bottom: 8pt;
- }
-
- .print-header .site-name {
- font-weight: bold;
- font-size: 14pt;
- }
-
- .print-header .page-title {
- font-size: 12pt;
- color: #555;
- }
-
- .markdown-content {
- padding: 0 !important;
- font-size: 11pt !important;
- line-height: 1.4 !important;
- max-width: none !important;
- }
-
- .markdown-content a {
- color: #000 !important;
- text-decoration: underline !important;
- word-wrap: break-word !important;
- }
-
- .markdown-content a[href^="http"]:after {
- content: " (" attr(href) ")";
- font-size: 9pt;
- color: #555;
- }
-
- .markdown-content pre,
- .markdown-content code {
- white-space: pre-wrap !important;
- border: 1px solid #ddd !important;
- page-break-inside: avoid !important;
- background-color: #f5f5f5 !important;
- color: #000 !important;
- }
-
- .markdown-content img {
- max-width: 100% !important;
- page-break-inside: avoid !important;
- border: none !important;
- box-shadow: none !important;
- }
-
- .markdown-content h1,
- .markdown-content h2,
- .markdown-content h3,
- .markdown-content h4,
- .markdown-content h5,
- .markdown-content h6 {
- page-break-after: avoid !important;
- break-after: avoid !important;
- color: #000 !important;
- }
-
- .markdown-content p,
- .markdown-content ul,
- .markdown-content ol,
- .markdown-content table {
- page-break-inside: avoid !important;
- }
-
- .markdown-content h1 {
- font-size: 20pt !important;
- margin: 20pt 0 15pt 0 !important;
- }
-
- .markdown-content h2 {
- font-size: 16pt !important;
- margin: 16pt 0 10pt 0 !important;
- }
-
- .markdown-content h3 {
- font-size: 14pt !important;
- margin: 14pt 0 8pt 0 !important;
- }
-
- .markdown-content p,
- .markdown-content ul,
- .markdown-content ol {
- margin: 6pt 0 !important;
- }
-
- .markdown-content pre,
- .markdown-content blockquote {
- margin: 10pt 0 !important;
- padding: 8pt 10pt !important;
- border-left: 4pt solid #ddd !important;
- background-color: #f8f8f8 !important;
- }
-
- .markdown-content table {
- margin: 10pt 0 !important;
- font-size: 10pt !important;
- border-collapse: collapse !important;
- width: 100% !important;
- border: 1px solid #ddd !important;
- }
-
- .markdown-content td,
- .markdown-content th {
- padding: 6pt 8pt !important;
- border: 1px solid #ddd !important;
- background-color: #fff !important;
- }
-
- .markdown-content th {
- background-color: #f0f0f0 !important;
- font-weight: bold !important;
- }
-
- /* Fix clickable header styles for print */
- .clickable-header:hover {
- color: inherit !important;
- }
-
- /* Add page breaks before major sections */
- .markdown-content h1 {
- page-break-before: always !important;
- }
-
- /* First h1 shouldn't have a page break before it */
- .markdown-content h1:first-child {
- page-break-before: avoid !important;
- }
-
- /* Hide interactive elements */
- .markdown-content video,
- .markdown-content .header-anchor,
- .markdown-content svg {
- display: none !important;
- }
-
- /* Make URLs for links visible */
- @page {
- margin: 0.5in !important;
- }
-
- /* Simple print header with logo */
- .print-header {
- display: flex !important;
- align-items: center;
- margin-bottom: 15pt;
- border-bottom: 1pt solid #ddd;
- padding-bottom: 8pt;
- }
-
- /* Style the logo in print mode */
- .print-header .brand-logo {
- height: 30px;
- width: auto;
- display: block;
- filter: brightness(0); /* Make logo black */
- border-radius: 0 !important; /* Remove any border radius */
- }
-
- /* Style the title text section */
- .print-header .print-title-text {
- display: flex;
- align-items: center;
- }
-
- .print-header .divider {
- padding: 0 5px;
- font-size: 14pt;
- color: #555;
- }
-
- .print-header .page-title {
- font-size: 14pt;
- color: #333;
- }
-
- /* Remove the old print title elements */
- .print-title,
- .content-container::before,
- .content-container::after {
- display: none !important;
- }
-}
-
-/* Add this rule to specifically remove the outline from the document content area when clicked */
-#document-content:focus {
- outline: none !important;
-}
-
-/* But keep it for keyboard navigation */
-#document-content:focus-visible {
- outline: 2px solid var(--ifm-color-primary) !important;
- outline-offset: 2px !important;
-}
-
-/* Also make sure any other clickable elements in the document content don't get outlines */
-#document-content *:focus:not(:focus-visible) {
- outline: none !important;
-}
\ No newline at end of file
diff --git a/vite.config.js b/vite.config.js
new file mode 100644
index 0000000..3d2d76f
--- /dev/null
+++ b/vite.config.js
@@ -0,0 +1,62 @@
+import { defineConfig } from "vite";
+import { createRequire } from "node:module";
+import { cpSync, existsSync } from "node:fs";
+import { dirname, join } from "node:path";
+import { fileURLToPath } from "node:url";
+import { umc } from "slatehtml/umc/vite";
+
+const require = createRequire(import.meta.url);
+const here = dirname(fileURLToPath(import.meta.url));
+const uiSrc = join(dirname(require.resolve("slatehtml-ui/package.json")), "src");
+const docsSrc = join(here, "src");
+
+function copyDocsStatic() {
+ const paths = ["index.json", "docs", "img", "example.index.json"];
+ return {
+ name: "copy-docs-static",
+ writeBundle(outputOptions) {
+ const outDir = outputOptions.dir || join(here, "dist");
+ for (const name of paths) {
+ const from = join(here, name);
+ if (!existsSync(from)) continue;
+ cpSync(from, join(outDir, name), { recursive: true });
+ }
+ },
+ };
+}
+
+export default defineConfig({
+ root: here,
+ base: "./",
+ plugins: [umc({ roots: [uiSrc, docsSrc] }), copyDocsStatic()],
+ server: {
+ fs: {
+ allow: [here, join(here, "..", "slatehtml")],
+ },
+ },
+ build: {
+ target: "es2022",
+ outDir: "dist",
+ emptyOutDir: true,
+ },
+ resolve: {
+ alias: [
+ {
+ find: "slatehtml-ui/configure",
+ replacement: join(uiSrc, "configure.js"),
+ },
+ {
+ find: "slatehtml-ui/icons/fontawesome",
+ replacement: join(uiSrc, "fontawesome-icons.js"),
+ },
+ {
+ find: "slatehtml-ui/icons/lucide",
+ replacement: join(uiSrc, "lucide-icons.js"),
+ },
+ {
+ find: "docs-viewer/prose.css",
+ replacement: join(docsSrc, "prose.css"),
+ },
+ ],
+ },
+});