Upload docs

This commit is contained in:
2026-03-22 03:04:31 +01:00
parent 99c5625b43
commit 9e5b520e91
98 changed files with 4540 additions and 684 deletions

View File

@@ -7,7 +7,7 @@
"vue": {
"src": "../../../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js",
"fileHash": "5e2bcecf",
"fileHash": "505179d7",
"needsInterop": false
},
"vitepress > @vue/devtools-api": {
@@ -19,13 +19,13 @@
"vitepress > @vueuse/core": {
"src": "../../../../../node_modules/@vueuse/core/index.mjs",
"file": "vitepress___@vueuse_core.js",
"fileHash": "f08e5a15",
"fileHash": "3e2c9d39",
"needsInterop": false
},
"@theme/index": {
"src": "../../../../../node_modules/vitepress/dist/client/theme-default/index.js",
"file": "@theme_index.js",
"fileHash": "442c9e5b",
"fileHash": "127b4204",
"needsInterop": false
}
},

View File

@@ -8,7 +8,11 @@ export default defineConfig({
outDir: '../../docs',
base: isDev ? '/absproxy/5174/sigpro/' : '/sigpro/',
// CONFIGURACIÓN DE VITE (Motor interno)
// AÑADIDO: Head para estilos
head: [
['link', { rel: 'stylesheet', href: 'https://cdn.jsdelivr.net/npm/daisyui@5/dist/full.css' }]
],
vite: {
outDir: '../../docs',
base: isDev ? '/absproxy/5174/sigpro/' : '/sigpro/',
@@ -24,6 +28,8 @@ export default defineConfig({
{ text: 'Home', link: '/' },
{ text: 'Guide', link: '/guide/getting-started' },
{ text: 'Api', link: '/api/quick' },
// AÑADIDO: UI en nav
{ text: 'UI', link: '/ui/introduction' },
],
sidebar: [
{
@@ -40,6 +46,7 @@ export default defineConfig({
{ text: 'Quick Start', link: '/api/quick' },
{ text: '$', link: '/api/$' },
{ text: '$.html', link: '/api/html' },
{ text: '$.router', link: '/api/router' },
{ text: '$.mount', link: '/api/mount' },
{ text: 'Tags', link: '/api/tags' },
]
@@ -48,11 +55,8 @@ export default defineConfig({
text: 'Plugins',
items: [
{ text: 'Quick Start', link: '/plugins/quick' },
{ text: '@core Router Plugin', link: '/plugins/core.router' },
{ text: '@core UI Plugin', link: '/plugins/core.ui' },
{ text: '@core UI Fetch', link: '/plugins/core.fetch' },
{ text: '@core UI Storage', link: '/plugins/core.storage' },
{ text: '@core UI Debug', link: '/plugins/core.debug' },
{ text: '@core Debug', link: '/plugins/core.debug' },
{ text: 'Custom', link: '/plugins/custom' },
]
},
@@ -61,6 +65,19 @@ export default defineConfig({
items: [
{ text: 'Vite Plugin', link: '/vite/plugin' },
]
},
{
text: 'UI Components',
items: [
{ text: 'Introduction', link: '/ui/introduction' },
{ text: 'Installation', link: '/ui/installation' },
{ text: 'Button', link: '/ui/button' },
{ text: 'Input', link: '/ui/input' },
{ text: 'Form Components', link: '/ui/form' },
{ text: 'Modal & Drawer', link: '/ui/modal' },
{ text: 'Navigation', link: '/ui/navigation' },
{ text: 'Layout', link: '/ui/layout' },
]
}
],
socialLinks: [

View File

@@ -1,46 +1,45 @@
# The Reactive Core: `$( )`
The `$` function is the heart of **SigPro**. It is a **Unified Reactive Constructor** that handles state, derivations, and side effects through a single, consistent interface.
The `$` function is the heart of **SigPro**. It is a **Unified Reactive Constructor** that handles state, derivations, and automatic persistence through a single, consistent interface.
## 1. The Constructor: `$( input )`
## 1. The Constructor: `$( input, [key] )`
Depending on what you pass into `$( )`, SigPro creates a different type of reactive primitive:
Depending on the arguments you pass, SigPro creates different reactive primitives:
| Input Type | Result | Internal Behavior |
| :--- | :--- | :--- |
| **Value** (String, Number, Object...) | **Signal** | Creates a piece of mutable state. |
| **Function** | **Computed / Effect** | Creates a derived value that tracks dependencies. |
| Argument | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **input** | `Value` / `Function` | **Yes** | Initial state or reactive logic. |
| **key** | `string` | No | If provided, the signal **persists** in `localStorage`. |
---
## 2. Signal (State)
## 2. Signal (State & Persistence)
A **Signal** is a "box" that holds a value. It provides a getter/setter function to interact with that value.
A **Signal** is a reactive "box" for data. SigPro now supports **Native Persistence**: if you provide a second argument (the `key`), the signal will automatically sync with `localStorage`.
* **When to use:** For data that changes over time (counters, user input, toggle states, API data).
* **Syntax:** `const $state = $(initialValue);`
* **Standard:** `const $count = $(0);`
* **Persistent:** `const $theme = $("light", "app-theme");` (Restores value on page reload).
### Example:
```javascript
const $name = $("Alice");
const $user = $("Guest", "session-user"); // Automatically saved/loaded
// Read the value (Getter)
console.log($name()); // "Alice"
// Read (Getter)
console.log($user());
// Update the value (Setter)
$name("Bob");
// Update (Setter + Auto-save to Disk)
$user("Alice");
// Update based on previous value
$name(current => current + " Smith");
// Functional Update
$user(prev => prev.toUpperCase());
```
---
## 3. Computed (Derived State)
When you pass a **function** to `$( )` that **returns a value**, SigPro creates a **Computed Signal**. It automatically tracks which signals are used inside it and re-runs only when they change.
When you pass a **function** that **returns a value**, SigPro creates a **Computed Signal**. It tracks dependencies and recalculates only when necessary.
* **When to use:** For values that depend on other signals (totals, filtered lists, formatted strings).
* **Syntax:** `const $derived = $(() => logic);`
### Example:
@@ -48,54 +47,56 @@ When you pass a **function** to `$( )` that **returns a value**, SigPro creates
const $price = $(100);
const $qty = $(2);
// Automatically tracks $price and $qty
// Auto-tracks $price and $qty
const $total = $(() => $price() * $qty());
console.log($total()); // 200
$qty(3); // $total updates to 300 automatically
```
---
## 4. Effects (Side Effects)
## 4. Effects (Reactive Actions)
An **Effect** is a function passed to `$( )` that **does not return a value** (or returns `undefined`). SigPro treats this as a subscription that performs an action whenever its dependencies change.
An **Effect** is a function that **does not return a value**. It performs an action (side effect) whenever the signals it "touches" change.
* **When to use:** For DOM manipulations, logging, or syncing with external APIs (LocalStorage, Fetch).
* **When to use:** Logging, manual DOM tweaks, or syncing with external APIs.
* **Syntax:** `$(() => { action });`
### Example:
```javascript
const $theme = $("light");
const $status = $("online");
// This effect runs every time $theme changes
// Runs every time $status changes
$(() => {
document.body.className = $theme();
console.log("Theme updated to:", $theme());
console.log("System status is now:", $status());
});
$theme("dark"); // Logs: Theme updated to: dark
```
---
## 5. Summary Table: Usage Guide
| Primitive | Logic Type | Returns Value? | Typical Use Case |
| Primitive | Logic Type | Persistence? | Typical Use Case |
| :--- | :--- | :--- | :--- |
| **Signal** | Static | Yes (Mutable) | `const $user = $("Guest")` |
| **Computed** | Read-only | Yes (Automatic) | `const $isLoggedIn = $(() => $user() !== "Guest")` |
| **Effect** | Imperative | No | `$(() => localStorage.setItem('user', $user()))` |
| **Signal** | Mutable State | **Yes** (Optional) | `$(0, 'counter')` |
| **Computed** | Derived / Read-only | No | `$(() => $a() + $b())` |
| **Effect** | Imperative Action | No | `$(() => alert($msg()))` |
---
## 💡 Pro Tip: Naming Convention
In SigPro, we use the **`$` prefix** (e.g., `$count`) for variables that hold a reactive function. This makes it easy to distinguish between a standard variable and a reactive one at a glance:
## 💡 Pro Tip: The Power of Native Persistence
In SigPro, you don't need external plugins for basic storage. By using the `key` parameter in a Signal, you gain:
1. **Zero Boilerplate:** No more `JSON.parse(localStorage.getItem(...))`.
2. **Instant Hydration:** The value is restored **before** the UI renders, preventing "flicker".
3. **Atomic Safety:** Data is saved to disk exactly when the signal changes, ensuring your app state is always safe.
---
### Naming Convention
We use the **`$` prefix** (e.g., `$count`) for reactive functions to distinguish them from static variables at a glance:
```javascript
let count = 0; // Static
const $count = $(0); // Reactive (Function)
let count = 0; // Static
const $count = $(0); // Reactive Signal
```

View File

@@ -1,6 +1,6 @@
# Application Mounter: `$.mount`
# Application Mounter: `$.router.mount` (Core)
The `$.mount` function is the entry point of your reactive world. It takes a **SigPro component** (or a plain DOM node) and injects it into the real document.
The `$.mount` function is the entry point of your reactive world. It takes a **SigPro component** (or a plain DOM node) and injects it into the real document, bridging the gap between your logic and the browser.
## 1. Syntax: `$.mount(node, [target])`
@@ -14,18 +14,19 @@ The `$.mount` function is the entry point of your reactive world. It takes a **S
## 2. Usage Scenarios
### A. The "Clean Slate" (Main Entry)
In a modern app (like our `main.js` example), you usually want to control the entire page. By default, `$.mount` clears the target's existing HTML before mounting.
In a modern app, you usually want to control the entire page. By default, `$.mount` clears the target's existing HTML before mounting your application.
```javascript
// src/main.js
import { $ } from 'SigPro';
import { $ } from 'sigpro';
import App from './App.js';
$.mount(App); // Mounts to <body> by default
// SigPro: No .then() needed, global tags are ready immediately
$.mount(App);
```
### B. Targeting a Specific Container
If you have an existing HTML structure and only want **SigPro** to manage a specific part (like a `#root` div), pass a CSS selector or a reference.
If you have an existing HTML structure and want **SigPro** to manage only a specific section (like a `#root` div), pass a CSS selector or a reference.
```html
<div id="sidebar"></div>
@@ -33,7 +34,7 @@ If you have an existing HTML structure and only want **SigPro** to manage a spec
```
```javascript
// Local mount to a specific ID
// Mount to a specific ID
$.mount(MyComponent, '#app-root');
// Or using a direct DOM reference
@@ -43,11 +44,11 @@ $.mount(SidebarComponent, sidebar);
---
## 3. Mounting with Pure HTML
One of SigPro's strengths is that it works perfectly alongside "Old School" HTML. You can create a reactive "island" inside a static page.
## 3. Creating "Reactive Islands"
One of SigPro's strengths is its ability to work alongside "Old School" static HTML. You can inject a reactive widget into any part of a legacy page.
```javascript
// A small reactive widget in a static .js file
// A small reactive widget
const CounterWidget = () => {
const $c = $(0);
return button({ onclick: () => $c(v => v + 1) }, [
@@ -55,41 +56,40 @@ const CounterWidget = () => {
]);
};
// Mount it into an existing div in your HTML
// Mount it into an existing div in your static HTML
$.mount(CounterWidget, '#counter-container');
```
---
## 4. How it Works (The "Wipe" Logic)
When `$.mount` is called, it performs two critical steps:
1. **Clearance:** It sets `target.innerHTML = ''`. This ensures no "zombie" HTML from previous renders or static placeholders interferes with your app.
2. **Injection:** It appends your component. If you passed a **Function**, it executes it first to get the DOM node.
## 4. How it Works (Lifecycle)
When `$.mount` is called, it performs three critical steps:
1. **Resolution:** If you passed a **Function**, it executes it once to generate the initial DOM node.
2. **Clearance:** It sets `target.innerHTML = ''`. This prevents "zombie" HTML or static placeholders from interfering with your app.
3. **Injection:** It appends the resulting node to the target.
---
## 5. Global vs. Local Scope
### Global (The "Framework" Way)
In a standard Vite/ESM project, you initialize SigPro globally in `main.js`. This makes the `$` and the tag helpers (`div`, `button`, etc.) available everywhere in your project.
In a standard Vite project, you initialize SigPro in your entry file. This makes `$` and the tag helpers (`div`, `button`, etc.) available globally for a clean, declarative developer experience.
```javascript
// main.js - Global Initialization
import 'SigPro';
// src/main.js
import { $ } from 'sigpro';
// Now any other file can just use:
// Any component in any file can now use:
$.mount(() => h1("Global App"));
```
### Local (The "Library" Way)
If you are worried about polluting the global `window` object, you can import and use SigPro locally within a specific module.
If you prefer to avoid polluting the `window` object, you can import and use SigPro locally within specific modules.
```javascript
// widget.js - Local usage
import { $ } from 'SigPro';
// widget.js
import { $ } from 'sigpro';
const myNode = $.html('div', 'Local Widget');
$.mount(myNode, '#widget-target');
@@ -97,12 +97,11 @@ $.mount(myNode, '#widget-target');
---
### Summary Cheat Sheet
## 6. Summary Cheat Sheet
| Goal | Code |
| :--- | :--- |
| **Mount to body** | `$.mount(App)` |
| **Mount to ID** | `$.mount(App, '#id')` |
| **Mount to Element** | `$.mount(App, myElement)` |
| **Reactive Widget** | `$.mount(() => div("Hi"), '#widget')` |
| **Direct Function** | `$.mount(() => div("Hi"), '#widget')` |

106
src/docs/api/router.md Normal file
View File

@@ -0,0 +1,106 @@
# Routing Engine: `$.router`
The `$.router` is SigPro's high-performance, hash-based navigation system. It connects the browser's URL directly to your reactive signals, enabling seamless page transitions without full reloads.
## 1. Core Features
* **Hash-based:** Works everywhere without special server configuration (using `#/path`).
* **Lazy Loading:** Pages are only downloaded when the user visits the route, keeping the initial bundle under 2KB.
* **Reactive:** The view updates automatically and surgically when the hash changes.
* **Dynamic Routes:** Built-in support for parameters like `/user/:id`.
---
## 2. Syntax: `$.router(routes)`
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **routes** | `Array<Object>` | **Yes** | An array of route definitions `{ path, component }`. |
---
## 3. Setting Up Routes
In your `App.js` (or a dedicated routes file), define your navigation map and inject it into your layout.
```javascript
const routes = [
{ path: '/', component: () => h1("Home Page") },
{
path: '/admin',
// Lazy Loading: This file is only fetched when needed
component: () => import('./pages/Admin.js')
},
{ path: '/user/:id', component: (params) => h2(`User ID: ${params.id}`) },
{ path: '*', component: () => div("404 - Page Not Found") }
];
export default () => div([
header([
h1("SigPro App"),
nav([
button({ onclick: () => $.router.go('/') }, "Home"),
button({ onclick: () => $.router.go('/admin') }, "Admin")
])
]),
// The router returns a reactive div that swaps content
main($.router(routes))
]);
```
---
## 4. Navigation (`$.router.go`)
To move between pages programmatically (e.g., inside an `onclick` event or after a successful fetch), use the `$.router.go` helper.
```javascript
button({
onclick: () => $.router.go('/admin')
}, "Go to Admin")
```
---
## 5. How it Works (Under the Hood)
The router tracks the `window.location.hash` and uses a reactive signal to trigger a re-render of the specific area where `$.router(routes)` is placed.
1. **Match:** It filters your route array to find the best fit, handling dynamic segments (`:id`) and fallbacks (`*`).
2. **Resolve:** * If it's a standard function, it executes it immediately.
* If it's a **Promise** (via `import()`), it renders a temporary `Loading...` state and swaps the content once the module arrives.
3. **Inject:** It replaces the previous DOM node with the new page content surgically using `replaceWith()`.
---
## 6. Integration with UI Components
Since the router is reactive, you can easily create "active" states in your navigation menus by checking the current hash.
```javascript
// Example of a reactive navigation link
const NavLink = (path, label) => {
const $active = $(() => window.location.hash === `#${path}`);
return button({
$class: () => $active() ? 'nav-active' : 'nav-link',
onclick: () => $.router.go(path)
}, label);
};
nav([
NavLink('/', 'Home'),
NavLink('/settings', 'Settings')
]);
```
---
## 7. Summary: Route Component Types
| Component Type | Behavior |
| :--- | :--- |
| **HTMLElement** | Rendered immediately. |
| **Function `(params) => ...`** | Executed with URL parameters and rendered. |
| **Promise / `import()`** | Triggers **Lazy Loading** with a loading state. |
| **String / Number** | Rendered as simple text inside a span. |

View File

@@ -8,18 +8,18 @@ You can install SigPro via your favorite package manager:
::: code-group
```bash [npm]
npm install SigPro
npm install sigpro
````
```bash [pnpm]
pnpm add SigPro
pnpm add sigpro
```
```bash [yarn]
yarn add SigPro
yarn add sigpro
```
```bash [bun]
bun add SigPro
bun add sigpro
```
:::

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
# Extending SigPro: `$.plugin`
The plugin system is the engine's way of growing. It allows you to inject new functionality directly into the `$` object or load external resources.
The plugin system is the engine's modular backbone. It allows you to inject new functionality directly into the `$` object, register custom global tags, or load external libraries seamlessly.
## 1. How Plugins Work
A plugin in **SigPro** is simply a function that receives the core instance. When you run `$.plugin(MyPlugin)`, the engine hands over the `$` object so the plugin can attach new methods or register global tags (like `div()`, `span()`, etc.).
A plugin in **SigPro** is a function that receives the core instance. When you call `$.plugin(MyPlugin)`, the engine hands over the `$` object so the plugin can attach new methods or extend the reactive system.
### Functional Plugin Example
```javascript
// A plugin that adds a simple logger to any signal
// A plugin that adds a simple watcher to any signal
const Logger = ($) => {
$.watch = (target, label = "Log") => {
$(() => console.log(`[${label}]:`, target()));
@@ -23,79 +23,72 @@ $.watch($count, "Counter"); // Now available globally via $
---
## 2. Initialization Patterns
## 2. Initialization Patterns (SigPro)
Since plugins often set up global variables (like the HTML tags), the order of initialization is critical. Here are the two ways to start your app:
Thanks to the **Synchronous Tag Engine**, you no longer need complex `import()` nesting. Global tags like `div()`, `span()`, and `button()` are ready the moment you import the Core.
### Option A: The "Safe" Async Start (Recommended)
This is the most robust way. It ensures all global tags (`div`, `button`, etc.) are created **before** your App code is even read by the browser.
```javascript
// main.js
import { $ } from 'sigpro';
import { UI, Router } from 'sigpro/plugins';
// 1. Load plugins first
$.plugin([UI, Router]).then(() => {
// 2. Import your app only after the environment is ready
import('./App.js').then(appFile => {
const MyApp = appFile.default;
$.mount(MyApp, '#app');
});
});
```
### Option B: Static Start (No Global Tags)
Use this only if you prefer **not** to use global tags and want to use `$.html` directly in your components. This allows for standard static imports.
### The "Natural" Start (Recommended)
This is the standard way to build apps. It's clean, readable, and supports standard ESM imports.
```javascript
// main.js
import { $ } from 'sigpro';
import { UI } from 'sigpro/plugins';
import MyApp from './App.js'; // Static import works here
import App from './App.js'; // Static import works perfectly!
// 1. Register plugins
$.plugin(UI);
$.mount(MyApp, '#app');
// 2. Mount your app directly
$.mount(App, '#app');
```
> **Warning:** In this mode, if `App.js` uses `div()` instead of `$.html('div')`, it will throw a `ReferenceError`.
---
## 3. Resource Plugins (External Scripts)
You can pass a **URL** or an **Array of URLs**. SigPro will inject them as `<script>` tags and return a Promise that resolves when the scripts are fully loaded and executed.
You can pass a **URL** or an **Array of URLs**. SigPro will inject them as `<script>` tags and return a **Promise** that resolves when the scripts are fully loaded. This is perfect for integrating heavy third-party libraries only when needed.
```javascript
// Loading external libraries as plugins
await $.plugin([
$.plugin([
'https://cdn.jsdelivr.net/npm/chart.js',
'https://cdn.example.com/custom-ui-lib.js'
]);
console.log("External resources are ready to use!");
]).then(() => {
console.log("External resources are ready to use!");
$.mount(DashboardApp);
});
```
---
## 4. Polymorphic Loading Reference
The `$.plugin` method adapts to whatever you throw at it:
The `$.plugin` method is smart; it adapts its behavior based on the input type:
| Input Type | Action | Behavior |
| :--- | :--- | :--- |
| **Function** | Executes `fn($)` | Synchronous / Immediate |
| **String (URL)** | Injects `<script src="...">` | Asynchronous (Returns Promise) |
| **Array** | Processes each item in the list | Returns Promise if any item is Async |
| **Function** | Executes `fn($)` | **Synchronous**: Immediate availability. |
| **String (URL)** | Injects `<script src="...">` | **Asynchronous**: Returns a Promise. |
| **Array** | Processes each item in the list | Returns a Promise if any item is a URL. |
---
## 💡 Pro Tip: Why the `.then()`?
## 💡 Pro Tip: When to use `.then()`?
Using `$.plugin([...]).then(...)` is like giving your app a "Pre-flight Check". It guarantees that:
1. All reactive methods are attached.
2. Global HTML tags are defined.
3. External libraries (like Chart.js) are loaded.
4. **The result:** Your components are cleaner, smaller, and error-free.
In **SigPro**, you only need `.then()` in two specific cases:
1. **External Assets:** When loading a plugin via a URL (CDN).
2. **Strict Dependency:** If your `App.js` requires a variable that is strictly defined inside an asynchronous external script (like `window.Chart`).
For everything else (UI components, Router, Local State), just call `$.plugin()` and continue with your code. It's that simple.
---
### Summary Cheat Sheet
| Goal | Code |
| :--- | :--- |
| **Local Plugin** | `$.plugin(myPlugin)` |
| **Multiple Plugins** | `$.plugin([UI, Router])` |
| **External Library** | `$.plugin('https://...').then(...)` |
| **Hybrid Load** | `$.plugin([UI, 'https://...']).then(...)` |

223
src/docs/public/sigpro.js Normal file
View File

@@ -0,0 +1,223 @@
/**
* SigPro - Atomic Unified Reactive Engine
* A lightweight, fine-grained reactivity system with built-in routing and plugin support.
* @author Gemini & User
*/
(() => {
/** @type {Function|null} Internal tracker for the currently executing reactive effect. */
let activeEffect = null;
/**
* @typedef {Object} SigPro
* @property {function(any|function, string=): Function} $ - Creates a Signal or Computed. Optional key for localStorage.
* @property {function(string, Object=, any=): HTMLElement} html - Creates a reactive HTML element.
* @property {function((HTMLElement|function), (HTMLElement|string)=): void} mount - Mounts a component to the DOM.
* @property {function(Array<Object>): HTMLElement} router - Initializes a hash-based router.
* @property {function(string): void} router.go - Programmatic navigation to a hash path.
* @property {function((function|string|Array<string>)): (Promise<SigPro>|SigPro)} plugin - Extends SigPro or loads external scripts.
*/
/**
* Creates a Signal (state) or a Computed/Effect (reaction).
* Supports optional persistence in localStorage.
* * @param {any|function} initial - Initial value or a function for computed logic.
* @param {string} [key] - Optional localStorage key for automatic state persistence.
* @returns {Function} A reactive accessor/mutator function.
*/
const $ = (initial, key) => {
const subs = new Set();
if (typeof initial === 'function') {
let cached;
const runner = () => {
const prev = activeEffect;
activeEffect = runner;
try {
const next = initial();
if (!Object.is(cached, next)) {
cached = next;
subs.forEach(s => s());
}
} finally { activeEffect = prev; }
};
runner();
return () => {
if (activeEffect) subs.add(activeEffect);
return cached;
};
}
if (key) {
const saved = localStorage.getItem(key);
if (saved !== null) {
try { initial = JSON.parse(saved); } catch (e) { }
}
}
return (...args) => {
if (args.length) {
const next = typeof args[0] === 'function' ? args[0](initial) : args[0];
if (!Object.is(initial, next)) {
initial = next;
if (key) localStorage.setItem(key, JSON.stringify(initial));
subs.forEach(s => s());
}
}
if (activeEffect) subs.add(activeEffect);
return initial;
};
};
/**
* Hyperscript engine to render reactive HTML nodes.
* @param {string} tag - The HTML tag name (e.g., 'div', 'button').
* @param {Object} [props] - Attributes, events (onclick), or reactive props ($value, $class).
* @param {any} [content] - String, Node, Array of nodes, or reactive function.
* @returns {HTMLElement} A live DOM element linked to SigPro signals.
*/
$.html = (tag, props = {}, content = []) => {
const el = document.createElement(tag);
if (typeof props !== 'object' || props instanceof Node || Array.isArray(props) || typeof props === 'function') {
content = props;
props = {};
}
for (let [key, val] of Object.entries(props)) {
if (key.startsWith('on')) {
el.addEventListener(key.toLowerCase().slice(2), val);
} else if (key.startsWith('$')) {
const attr = key.slice(1);
// Two-way binding for inputs
if ((attr === 'value' || attr === 'checked') && typeof val === 'function') {
const ev = attr === 'checked' ? 'change' : 'input';
el.addEventListener(ev, e => val(attr === 'checked' ? e.target.checked : e.target.value));
}
// Reactive attribute update
$(() => {
const v = typeof val === 'function' ? val() : val;
if (attr === 'value' || attr === 'checked') el[attr] = v;
else if (typeof v === 'boolean') el.toggleAttribute(attr, v);
else el.setAttribute(attr, v ?? '');
});
} else el.setAttribute(key, val);
}
const append = (c) => {
if (Array.isArray(c)) return c.forEach(append);
if (typeof c === 'function') {
const node = document.createTextNode('');
$(() => {
const res = c();
if (res instanceof Node) {
if (node.parentNode) node.replaceWith(res);
} else {
node.textContent = res ?? '';
}
});
return el.appendChild(node);
}
el.appendChild(c instanceof Node ? c : document.createTextNode(c ?? ''));
};
append(content);
return el;
};
const tags = ['div', 'span', 'p', 'button', 'h1', 'h2', 'h3', 'ul', 'ol', 'li', 'a', 'label', 'section', 'nav', 'main', 'header', 'footer', 'input', 'form', 'img', 'select', 'option', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'canvas', 'video', 'audio'];
tags.forEach(t => window[t] = (p, c) => $.html(t, p, c));
/**
* Application mounter.
* @param {HTMLElement|function} node - Root component or element to mount.
* @param {HTMLElement|string} [target=document.body] - Target element or CSS selector.
*/
$.mount = (node, target = document.body) => {
const el = typeof target === 'string' ? document.querySelector(target) : target;
if (el) {
el.innerHTML = '';
el.appendChild(typeof node === 'function' ? node() : node);
}
};
/**
* Initializes a reactive hash-based router.
* Maps URL hash changes to component rendering and supports Vite's dynamic imports.
* * @param {Array<{path: string, component: Function|Promise|HTMLElement}>} routes - Array of route objects.
* @returns {HTMLElement} A reactive div container that swaps content based on the current hash.
*/
$.router = (routes) => {
const sPath = $(window.location.hash.replace(/^#/, "") || "/");
window.addEventListener("hashchange", () => sPath(window.location.hash.replace(/^#/, "") || "/"));
return $.html('div', [
() => {
const current = sPath();
const cP = current.split('/').filter(Boolean);
const route = routes.find(r => {
const rP = r.path.split('/').filter(Boolean);
if (rP.length !== cP.length) return false;
return rP.every((part, i) => part.startsWith(':') || part === cP[i]);
}) || routes.find(r => r.path === "*");
if (!route) return $.html('h1', "404 - Not Found");
const rP = route.path.split('/').filter(Boolean);
const params = {};
rP.forEach((part, i) => {
if (part.startsWith(':')) params[part.slice(1)] = cP[i];
});
const result = typeof route.component === 'function' ? route.component(params) : route.component;
if (result instanceof Promise) {
const $lazyNode = $($.html('span', "Loading..."));
result.then(m => {
const content = m.default || m;
const finalView = typeof content === 'function' ? content(params) : content;
$lazyNode(finalView);
});
return () => $lazyNode();
}
return result instanceof Node ? result : $.html('span', String(result));
}
]);
};
/**
* Programmatically navigates to a specific path using the hash.
* * @param {string} path - The destination path (e.g., '/home' or 'settings').
* @example
* $.router.go('/profile/42');
*/
$.router.go = (path) => {
window.location.hash = path.startsWith('/') ? path : `/${path}`;
};
/**
* Polymorphic Plugin System.
* Registers internal functions or loads external .js files as plugins.
* @param {function|string|Array<string>} source - Plugin function or URL(s).
* @returns {Promise<SigPro>|SigPro} Resolves with the $ instance after loading or registering.
*/
$.plugin = (source) => {
if (typeof source === 'function') {
source($);
return $;
}
const urls = Array.isArray(source) ? source : [source];
return Promise.all(urls.map(url => new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = url;
script.async = true;
script.onload = () => {
console.log(`%c[SigPro] Plugin Loaded: ${url}`, "color: #51cf66; font-weight: bold;");
resolve();
};
script.onerror = () => reject(new Error(`[SigPro] Failed to load: ${url}`));
document.head.appendChild(script);
}))).then(() => $);
};
window.$ = $;
})();

114
src/docs/ui/button.md Normal file
View File

@@ -0,0 +1,114 @@
# Button Component
The `_button` component creates reactive buttons with built-in support for loading states, icons, badges, and disabled states.
## Basic Usage
<div id="basic-button-demo"></div>
```javascript
_button({ onclick: () => alert('Clicked!') }, 'Click Me')
```
## Loading State
The `$loading` signal automatically shows a spinner and disables the button.
<div id="loading-button-demo"></div>
```javascript
const $loading = $(false)
_button({
$loading: $loading,
onclick: async () => {
$loading(true)
await saveData()
$loading(false)
}
}, 'Save')
```
## Icons
Add icons to buttons using the `icon` prop.
<div id="icon-button-demo"></div>
```javascript
_button({ icon: '⭐' }, 'Favorite')
_button({ icon: '💾' }, 'Save')
_button({ icon: '🗑️', class: 'btn-error' }, 'Delete')
```
## Badges
Add badges to buttons for notifications or status indicators.
<div id="badge-button-demo"></div>
```javascript
_button({ badge: '3' }, 'Notifications')
_button({ badge: 'New', badgeClass: 'badge-secondary' }, 'Update Available')
```
## Button Variants
Use daisyUI classes to style your buttons.
<div id="variant-button-demo"></div>
```javascript
_button({ class: 'btn-primary' }, 'Primary')
_button({ class: 'btn-secondary' }, 'Secondary')
_button({ class: 'btn-outline' }, 'Outline')
_button({ class: 'btn-sm' }, 'Small')
```
## Counter Example
<div id="counter-demo"></div>
```javascript
const $count = $(0)
_button({
onclick: () => $count($count() + 1),
icon: '🔢'
}, () => `Count: ${$count()}`)
```
## Async Action Example
<div id="async-demo"></div>
```javascript
const $saving = $(false)
const $success = $(false)
_button({
$loading: $saving,
icon: '💾',
onclick: async () => {
$saving(true)
await saveToDatabase()
$saving(false)
$success(true)
setTimeout(() => $success(false), 2000)
}
}, 'Save')
```
## API Reference
| Prop | Type | Description |
|------|------|-------------|
| `$loading` | `Signal<boolean>` | Shows spinner and disables button |
| `$disabled` | `Signal<boolean>` | Disables the button |
| `icon` | `string \| Node` | Icon to display before text |
| `badge` | `string` | Badge text to display |
| `badgeClass` | `string` | Additional CSS classes for badge |
| `class` | `string \| function` | Additional CSS classes |
| `onclick` | `function` | Click event handler |
| `type` | `string` | Button type ('button', 'submit', etc.) |

112
src/docs/ui/form.md Normal file
View File

@@ -0,0 +1,112 @@
# Form Components
SigPro UI provides a complete set of reactive form components including select dropdowns, checkboxes, radio buttons, and range sliders.
## Select Dropdown (`_select`)
Creates a reactive dropdown select with options.
<div id="select-demo"></div>
```javascript
const $role = $('user')
_select({
label: 'User Role',
options: [
{ value: 'admin', label: 'Administrator' },
{ value: 'user', label: 'Standard User' },
{ value: 'guest', label: 'Guest' }
],
$value: $role
})
```
## Checkbox (`_checkbox`)
Reactive checkbox with label.
<div id="checkbox-demo"></div>
```javascript
const $agreed = $(false)
_checkbox({
label: 'I agree to the terms and conditions',
$value: $agreed
})
```
## Radio Button (`_radio`)
Radio buttons for selecting one option from a group.
<div id="radio-demo"></div>
```javascript
const $payment = $('credit')
_radio({ name: 'payment', label: 'Credit Card', value: 'credit', $value: $payment })
_radio({ name: 'payment', label: 'PayPal', value: 'paypal', $value: $payment })
_radio({ name: 'payment', label: 'Crypto', value: 'crypto', $value: $payment })
```
## Range Slider (`_range`)
Reactive range slider for numeric values.
<div id="range-demo"></div>
```javascript
const $volume = $(50)
_range({
label: 'Volume',
min: 0,
max: 100,
step: 1,
$value: $volume
})
```
## Complete Form Example
<div id="complete-form-demo"></div>
## API Reference
### `_select`
| Prop | Type | Description |
|------|------|-------------|
| `label` | `string` | Field label |
| `options` | `Array<{value: any, label: string}>` | Select options |
| `$value` | `Signal<any>` | Selected value signal |
### `_checkbox`
| Prop | Type | Description |
|------|------|-------------|
| `label` | `string` | Checkbox label |
| `$value` | `Signal<boolean>` | Checked state signal |
### `_radio`
| Prop | Type | Description |
|------|------|-------------|
| `name` | `string` | Radio group name |
| `label` | `string` | Radio option label |
| `value` | `any` | Value for this option |
| `$value` | `Signal<any>` | Group selected value signal |
### `_range`
| Prop | Type | Description |
|------|------|-------------|
| `label` | `string` | Slider label |
| `min` | `number` | Minimum value |
| `max` | `number` | Maximum value |
| `step` | `number` | Step increment |
| `$value` | `Signal<number>` | Current value signal |

137
src/docs/ui/input.md Normal file
View File

@@ -0,0 +1,137 @@
# Input Component
The `_input` component creates reactive form inputs with built-in support for labels, tooltips, error messages, and two-way binding.
## Basic Usage
<div id="basic-input-demo"></div>
```javascript
const $name = $('')
_input({
label: 'Name',
placeholder: 'Enter your name',
$value: $name
})
```
## With Tooltip
The `tip` prop adds an info badge with a tooltip.
<div id="tooltip-input-demo"></div>
```javascript
_input({
label: 'Username',
tip: 'Choose a unique username (min. 3 characters)',
placeholder: 'johndoe123',
$value: $username
})
```
## With Error Handling
The `$error` signal displays an error message and styles the input accordingly.
<div id="error-input-demo"></div>
```javascript
const $email = $('')
const $error = $(null)
const validate = (value) => {
if (value && !value.includes('@')) {
$error('Please enter a valid email address')
} else {
$error(null)
}
}
_input({
label: 'Email',
type: 'email',
placeholder: 'user@example.com',
$value: $email,
$error: $error,
oninput: (e) => validate(e.target.value)
})
```
## Input Types
The component supports all standard HTML input types.
<div id="types-input-demo"></div>
```javascript
_input({ label: 'Text', placeholder: 'Text input', $value: $text })
_input({ label: 'Password', type: 'password', placeholder: '••••••••', $value: $password })
_input({ label: 'Number', type: 'number', placeholder: '0', $value: $number })
```
## Two-Way Binding
The `$value` prop creates two-way binding between the input and the signal.
<div id="binding-input-demo"></div>
```javascript
const $message = $('Hello World')
_input({
label: 'Message',
$value: $message
})
// The input updates when signal changes, and vice versa
_button({ onclick: () => $message('Reset!') }, 'Reset Signal')
```
## API Reference
| Prop | Type | Description |
|------|------|-------------|
| `label` | `string` | Field label text |
| `tip` | `string` | Tooltip text shown on hover |
| `$value` | `Signal<any>` | Two-way bound value signal |
| `$error` | `Signal<string\|null>` | Error message signal |
| `type` | `string` | Input type (text, email, password, number, etc.) |
| `placeholder` | `string` | Placeholder text |
| `class` | `string \| function` | Additional CSS classes |
| `oninput` | `function` | Input event handler |
| `onchange` | `function` | Change event handler |
| `disabled` | `boolean` | Disabled state |
## Examples
### Registration Form Field
<div id="register-demo"></div>
```javascript
const $username = $('')
const $usernameError = $(null)
const $email = $('')
const $emailError = $(null)
_input({
label: 'Username',
placeholder: 'johndoe',
$value: $username,
$error: $usernameError,
oninput: (e) => validateUsername(e.target.value)
})
_input({
label: 'Email',
type: 'email',
placeholder: 'john@example.com',
$value: $email,
$error: $emailError,
oninput: (e) => validateEmail(e.target.value)
})
```

View File

@@ -0,0 +1,53 @@
# Installation
## Prerequisites
- Node.js 18 or higher
- A project with SigPro already installed
## Step 1: Install Dependencies
```bash
npm install -D tailwindcss @tailwindcss/vite daisyui@next
```
## Step 2: Configure Tailwind CSS v4
Create a CSS file (e.g., `src/app.css`):
```css
@import "tailwindcss";
@plugin "daisyui";
```
## Step 3: Import CSS in Your Entry Point
```javascript
// main.js
import './app.css';
import { $ } from 'sigpro';
import { UI } from 'sigpro/plugins';
$.plugin(UI).then(() => {
console.log('✅ UI Components ready');
import('./App.js').then(app => $.mount(app.default));
});
```
## Step 4: Verify Installation
<div id="test-install"></div>
## Troubleshooting
### Styles not applying?
- Make sure `app.css` is imported before any other code
- Check that Tailwind is properly configured in your build tool
### Components not found?
- Ensure `$.plugin(UI)` has completed before using components
- Check browser console for any loading errors
### Reactive updates not working?
- Make sure you're passing signals, not primitive values
- Use `$value` prop for two-way binding

View File

@@ -0,0 +1,33 @@
# UI Components
The **SigPro UI** plugin is a high-level component library built on top of the reactive core. It leverages **Tailwind CSS v4** for utility styling and **daisyUI v5** for semantic, themeable components.
## Features
- 🚀 **Fully Reactive**: Every component automatically updates with signals
- 🎨 **Themeable**: Supports all daisyUI themes out of the box
- 📱 **Responsive**: Designed to work on all devices
- 🔧 **Zero Dependencies**: Pure SigPro with no framework overhead
## Quick Demo
<div id="quick-demo"></div>
## What's Included
The UI plugin provides a comprehensive set of reactive components:
| Category | Components |
|----------|------------|
| **Actions** | `_button` |
| **Forms** | `_input`, `_select`, `_checkbox`, `_radio`, `_range` |
| **Layout** | `_fieldset`, `_accordion`, `_drawer` |
| **Navigation** | `_navbar`, `_menu`, `_tabs` |
| **Overlays** | `_modal`, `_dropdown` |
| **Feedback** | `_badge`, `_tooltip` |
## Next Steps
- [Installation Guide](/ui/installation) - Set up Tailwind and daisyUI
- [Button Component](/ui/button) - Explore the button component
- [Form Components](/ui/form) - Build reactive forms with validation

117
src/docs/ui/layout.md Normal file
View File

@@ -0,0 +1,117 @@
# Layout Components
Layout components for structuring your application with containers, sections, and collapsible panels.
## Fieldset (`_fieldset`)
Groups related form fields with a legend.
<div id="fieldset-demo"></div>
```javascript
_fieldset({ legend: 'Personal Information' }, [
_input({ label: 'Full Name', $value: $name }),
_input({ label: 'Email Address', type: 'email', $value: $email }),
_select({ label: 'Role', options: [...], $value: $role })
])
```
## Accordion (`_accordion`)
Collapsible content panels. Can be used as standalone or grouped.
### Single Accordion
<div id="single-accordion-demo"></div>
```javascript
_accordion({ title: 'What is SigPro UI?' }, [
$.html('p', {}, 'SigPro UI is a reactive component library...')
])
```
### Grouped Accordions (Radio Behavior)
When multiple accordions share the same `name`, only one can be open at a time.
<div id="grouped-accordion-demo"></div>
```javascript
// Grouped accordions - only one open at a time
_accordion({ title: 'Getting Started', name: 'faq' }, content1)
_accordion({ title: 'Installation', name: 'faq' }, content2)
_accordion({ title: 'Customization', name: 'faq' }, content3)
```
### Accordion with Open State
Control the initial open state with the `open` prop.
<div id="open-accordion-demo"></div>
```javascript
_accordion({ title: 'Open by Default', open: true }, [
$.html('p', {}, 'This accordion starts open.')
])
```
## Complete Layout Example
<div id="complete-layout-demo"></div>
## API Reference
### `_fieldset`
| Prop | Type | Description |
|------|------|-------------|
| `legend` | `string` | Fieldset title/legend text |
| `class` | `string \| function` | Additional CSS classes |
### `_accordion`
| Prop | Type | Description |
|------|------|-------------|
| `title` | `string` | Accordion header text |
| `name` | `string` | Group name for radio behavior (optional) |
| `open` | `boolean` | Initially open state (default: false) |
## Styling Tips
### Custom Fieldset Styling
```javascript
_fieldset({
legend: 'Custom Styled',
class: 'bg-primary/10 border-primary'
}, [
// content
])
```
### Custom Accordion Styling
```javascript
_accordion({
title: 'Styled Accordion',
class: 'bg-base-200'
}, [
// content
])
```
### Nested Layouts
Layout components can be nested to create complex structures:
```javascript
_fieldset({ legend: 'Main Section' }, [
_accordion({ title: 'Subsection 1' }, [
_input({ label: 'Field 1', $value: $field1 })
]),
_accordion({ title: 'Subsection 2' }, [
_input({ label: 'Field 2', $value: $field2 })
])
])
```

90
src/docs/ui/modal.md Normal file
View File

@@ -0,0 +1,90 @@
# Modal & Drawer Components
Overlay components for dialogs, side panels, and popups with reactive control.
## Modal (`_modal`)
A dialog component that appears on top of the page. The modal is completely removed from the DOM when closed, optimizing performance.
### Basic Modal
<div id="basic-modal-demo"></div>
```javascript
const $open = $(false)
_button({ onclick: () => $open(true) }, 'Open Modal')
_modal({ $open: $open, title: 'Welcome' }, [
$.html('p', {}, 'This is a simple modal dialog.'),
_button({ onclick: () => $open(false) }, 'Close')
])
```
### Modal with Actions
<div id="action-modal-demo"></div>
```javascript
const $open = $(false)
const $result = $(null)
_modal({ $open: $open, title: 'Confirm Delete' }, [
$.html('p', {}, 'Are you sure you want to delete this item?'),
_button({ class: 'btn-error', onclick: () => {
$result('Item deleted')
$open(false)
} }, 'Delete')
])
```
### Modal with Form
<div id="form-modal-demo"></div>
## Drawer (`_drawer`)
A sidebar panel that slides in from the side.
### Basic Drawer
<div id="basic-drawer-demo"></div>
```javascript
const $open = $(false)
_drawer({
id: 'my-drawer',
$open: $open,
content: $.html('div', {}, 'Main content'),
side: $.html('div', { class: 'p-4' }, [
$.html('h3', {}, 'Menu'),
$.html('ul', { class: 'menu' }, [
$.html('li', {}, [$.html('a', { onclick: () => $open(false) }, 'Close')])
])
])
})
```
### Drawer with Navigation Menu
<div id="nav-drawer-demo"></div>
## API Reference
### `_modal`
| Prop | Type | Description |
|------|------|-------------|
| `$open` | `Signal<boolean>` | Controls modal visibility |
| `title` | `string` | Modal title text |
### `_drawer`
| Prop | Type | Description |
|------|------|-------------|
| `id` | `string` | Unique identifier for the drawer |
| `$open` | `Signal<boolean>` | Controls drawer visibility |
| `content` | `HTMLElement` | Main content area |
| `side` | `HTMLElement` | Sidebar content |

124
src/docs/ui/navigation.md Normal file
View File

@@ -0,0 +1,124 @@
# Navigation Components
Navigation components for building menus, navbars, and tabs with reactive active states.
## Navbar (`_navbar`)
A responsive navigation bar with built-in styling.
<div id="navbar-demo"></div>
```javascript
const $active = $('Home')
_navbar({ class: 'shadow-md' }, [
div({ class: 'flex-1' }, [
a({ class: 'text-xl font-bold' }, 'MyApp')
]),
div({ class: 'flex-none gap-2' }, [
_button({
class: () => $active() === 'Home' ? 'btn-primary btn-sm' : 'btn-ghost btn-sm',
onclick: () => $active('Home')
}, 'Home'),
_button({
class: () => $active() === 'About' ? 'btn-primary btn-sm' : 'btn-ghost btn-sm',
onclick: () => $active('About')
}, 'About')
])
])
```
## Menu (`_menu`)
Vertical navigation menu with active state highlighting.
<div id="menu-demo"></div>
```javascript
const $selected = $('dashboard')
_menu({ items: [
{
label: 'Dashboard',
icon: '📊',
active: () => $selected() === 'dashboard',
onclick: () => $selected('dashboard')
},
{
label: 'Analytics',
icon: '📈',
active: () => $selected() === 'analytics',
onclick: () => $selected('analytics')
}
]})
```
## Tabs (`_tabs`)
Horizontal tabs with lifted styling and active state.
<div id="tabs-demo"></div>
```javascript
const $activeTab = $('profile')
_tabs({ items: [
{
label: 'Profile',
active: () => $activeTab() === 'profile',
onclick: () => $activeTab('profile')
},
{
label: 'Settings',
active: () => $activeTab() === 'settings',
onclick: () => $activeTab('settings')
}
]})
```
## Dropdown (`_dropdown`)
A dropdown menu that appears on click.
<div id="dropdown-demo"></div>
```javascript
const $selected = $(null)
_dropdown({ label: 'Options' }, [
li([a({ onclick: () => $selected('Edit') }, '✏️ Edit')]),
li([a({ onclick: () => $selected('Duplicate') }, '📋 Duplicate')]),
li([a({ onclick: () => $selected('Delete') }, '🗑️ Delete')])
])
```
## Complete Navigation Example
<div id="complete-nav-demo"></div>
## API Reference
### `_navbar`
| Prop | Type | Description |
|------|------|-------------|
| `class` | `string \| function` | Additional CSS classes |
### `_menu`
| Prop | Type | Description |
|------|------|-------------|
| `items` | `Array<{label: string, icon?: any, active?: boolean\|function, onclick: function}>` | Menu items |
### `_tabs`
| Prop | Type | Description |
|------|------|-------------|
| `items` | `Array<{label: string, active: boolean\|function, onclick: function}>` | Tab items |
### `_dropdown`
| Prop | Type | Description |
|------|------|-------------|
| `label` | `string` | Dropdown trigger text |
| `class` | `string \| function` | Additional CSS classes |

View File

@@ -1,10 +1,10 @@
# Vite Plugin: File-based Routing
The `sigproRouter` plugin for Vite automates route generation by scanning your pages directory. It creates a **virtual module** that you can import directly into your code, eliminating the need to maintain a manual routes array.
The `sigproRouter` plugin for Vite automates route generation by scanning your `pages` directory. It creates a **virtual module** that you can import directly into your code, eliminating the need to maintain a manual routes array.
## 1. Project Structure
To use the plugin, organize your files within the `src/pages` directory. The folder hierarchy directly determines your application's URL structure.
To use the plugin, organize your files within the `src/pages` directory. The folder hierarchy directly determines your application's URL structure. SigPro uses brackets `[param]` for dynamic segments.
```text
my-sigpro-app/
@@ -17,7 +17,7 @@ my-sigpro-app/
│ │ └── blog/
│ │ ├── index.js → #/blog
│ │ └── [slug].js → #/blog/:slug
│ ├── App.js (Optional App Shell)
│ ├── App.js (Main Layout)
│ └── main.js (Entry Point)
├── vite.config.js
└── package.json
@@ -27,7 +27,7 @@ my-sigpro-app/
## 2. Setup & Configuration
Add the plugin to your `vite.config.js`.
Add the plugin to your `vite.config.js`. It works out of the box with zero configuration.
```javascript
// vite.config.js
@@ -43,67 +43,80 @@ export default defineConfig({
## 3. Implementation
You can implement the router either directly in your entry point or inside an App component to support persistent layouts (like a navbar that doesn't re-render).
Thanks to **SigPro's synchronous initialization**, you no longer need to wrap your mounting logic in `.then()` blocks.
### Option A: Direct in `main.js`
Best for simple apps where the router occupies the entire viewport.
Ideal for single-page applications where the router controls the entire viewport.
```javascript
// src/main.js
import { $ } from 'sigpro';
import { Router } from 'sigpro/plugins';
import { routes } from 'virtual:sigpro-routes';
$.plugin(Router).then(() => {
$.mount(_router(routes), '#app');
});
// The Core already has $.router ready
$.mount($.router(routes), '#app');
```
### Option B: Inside `App.js` (With Layout)
Recommended for apps with a fixed Sidebar or Navbar.
### Option B: Inside `App.js` (Persistent Layout)
Recommended for professional apps with a fixed Sidebar or Navbar that should not re-render when changing pages.
```javascript
// src/main.js
import { $ } from 'sigpro';
import { Router } from 'sigpro/plugins';
import App from './App.js';
$.plugin(Router).then(() => {
import('./App.js').then(app => $.mount(app.default, '#app'));
});
$.mount(App, '#app');
// src/App.js
import { routes } from 'virtual:sigpro-routes';
export default () => {
return div({ class: 'layout' }, [
header([
h1("SigPro App"),
nav([
a({ href: '#/' }, "Home"),
a({ href: '#/blog' }, "Blog")
])
]),
// The router only swaps the content inside this <main> tag
main(_router(routes))
]);
};
export default () => div({ class: 'layout' }, [
header([
h1("SigPro App"),
nav([
button({ onclick: () => $.router.go('/') }, "Home"),
button({ onclick: () => $.router.go('/blog') }, "Blog")
])
]),
// Only the content inside <main> will be swapped reactively
main($.router(routes))
]);
```
---
## 4. Route Mapping Reference
| File Path | Generated Route | Logic |
The plugin follows a simple convention to transform your file system into a routing map.
| File Path | Generated Path | Description |
| :--- | :--- | :--- |
| `index.js` | `/` | Home page |
| `about.js` | `/about` | Static path |
| `[id].js` | `/:id` | Dynamic parameter |
| `blog/index.js` | `/blog` | Folder index |
| `_utils.js` | *Ignored* | Files starting with `_` are skipped |
| `index.js` | `/` | The application root. |
| `about.js` | `/about` | A static page. |
| `[id].js` | `/:id` | Dynamic parameter (passed to the component). |
| `blog/index.js` | `/blog` | Folder index page. |
| `_utils.js` | *Ignored* | Files starting with `_` are excluded from routing. |
---
## 5. Installation
## 5. How it Works (Vite Virtual Module)
The plugin generates a virtual module named `virtual:sigpro-routes`. This module exports an array of objects compatible with `$.router()`:
```javascript
// Internal representation generated by the plugin
export const routes = [
{ path: '/', component: () => import('/src/pages/index.js') },
{ path: '/users/:id', component: () => import('/src/pages/users/[id].js') },
// ...
];
```
Because it uses dynamic `import()`, Vite automatically performs **Code Splitting**, meaning each page is its own small JS file that only loads when the user navigates to it.
---
## 6. Installation
::: code-group
```bash [NPM]