Update Docs

This commit is contained in:
2026-03-23 01:11:09 +01:00
parent 7630217643
commit a285f2550c
92 changed files with 1091 additions and 3535 deletions

View File

@@ -7,8 +7,7 @@ export default defineConfig({
description: "Minimalist Reactive Library",
outDir: '../../docs',
base: isDev ? '/absproxy/5174/sigpro/' : '/sigpro/',
// AÑADIDO: Head para estilos
head: [
['link', { rel: 'stylesheet', href: 'https://cdn.jsdelivr.net/npm/daisyui@5/dist/full.css' }]
],
@@ -26,18 +25,15 @@ export default defineConfig({
logo: '/logo.svg',
nav: [
{ text: 'Home', link: '/' },
{ text: 'Guide', link: '/guide/getting-started' },
{ text: 'Install', link: '/install' },
{ text: 'Api', link: '/api/quick' },
// AÑADIDO: UI en nav
{ text: 'UI', link: '/ui/introduction' },
],
sidebar: [
{
text: 'Introduction',
items: [
{ text: 'What is SigPro?', link: '/' },
{ text: 'Why', link: '/guide/why' },
{ text: 'Guide', link: '/guide/getting-started' },
{ text: 'Installation', link: '/install' },
{ text: 'Vite Plugin', link: '/vite/plugin' },
]
},
{
@@ -55,28 +51,13 @@ export default defineConfig({
text: 'Plugins',
items: [
{ text: 'Quick Start', link: '/plugins/quick' },
{ text: '@core UI Plugin', link: '/plugins/core.ui' },
{ text: '@core Debug', link: '/plugins/core.debug' },
{ text: 'Custom', link: '/plugins/custom' },
]
},
{
text: 'Vite Router Plugin',
text: 'Examples',
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' },
{ text: 'Demo Core', link: '/examples' }
]
}
],

View File

@@ -1,102 +1,142 @@
# The Reactive Core: `$( )`
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, [key] )`
Depending on the arguments you pass, SigPro creates different reactive primitives:
| Argument | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **input** | `Value` / `Function` | **Yes** | Initial state or reactive logic. |
| **key** | `string` | No | If provided, the signal **persists** in `localStorage`. |
The `$` function is a **Unified Reactive Constructor**. It detects the type of input you provide and returns the appropriate reactive primitive.
---
## 2. Signal (State & Persistence)
## 1. Signals (Atomic State)
A **Signal** is the simplest form of reactivity. It holds a single value (string, number, boolean, null).
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`.
* **Standard:** `const $count = $(0);`
* **Persistent:** `const $theme = $("light", "app-theme");` (Restores value on page reload).
### Example:
### **Option A: Standard Signal (RAM)**
Ideal for volatile state that shouldn't persist after a page refresh.
```javascript
const $user = $("Guest", "session-user"); // Automatically saved/loaded
const $count = $(0);
// Read (Getter)
console.log($user());
// Usage:
$count(); // Getter: returns 0
$count(10); // Setter: updates to 10
$count(c => c + 1); // Functional update: updates to 11
```
// Update (Setter + Auto-save to Disk)
$user("Alice");
### **Option B: Persistent Signal (Disk)**
By adding a `key`, SigPro links the signal to `localStorage`.
```javascript
// Syntax: $(initialValue, "storage-key")
const $theme = $("light", "app-theme");
// Functional Update
$user(prev => prev.toUpperCase());
// It restores the value from disk automatically on load.
// When you update it, it saves to disk instantly:
$theme("dark"); // localStorage.getItem("app-theme") is now "dark"
```
---
## 3. Computed (Derived State)
## 2. Stores (Reactive Objects)
A **Store** is a proxy that wraps an **Object**. SigPro makes every property reactive recursively. You access and set properties as if they were individual signals.
When you pass a **function** that **returns a value**, SigPro creates a **Computed Signal**. It tracks dependencies and recalculates only when necessary.
### **Option A: Standard Store (RAM)**
```javascript
const user = $({
name: "Alice",
profile: { bio: "Developer" }
});
* **Syntax:** `const $derived = $(() => logic);`
// Getter: Call the property as a function
console.log(user.name()); // "Alice"
// Setter: Pass the value to the property function
user.name("Bob");
// Nested updates work exactly the same:
user.profile.bio("Architect");
```
### **Option B: Persistent Store (Disk)**
The most powerful way to save complex state. The **entire object tree** is serialized to JSON and kept in sync with the disk.
```javascript
const settings = $({
volume: 50,
notifications: true
}, "user-settings");
// Any change in the object triggers a disk sync:
settings.volume(100); // The whole JSON is updated in localStorage
```
---
## 3. Stores (Reactive Arrays)
When you pass an **Array**, SigPro tracks changes to the list. You can use standard methods or access indexes as reactive getters.
```javascript
const $list = $(["Item 1", "Item 2"]);
// Get by index
console.log($list[0]()); // "Item 1"
// Update by index
$list[0]("Updated Item");
// Note: For adding/removing items, use standard array methods
// which SigPro makes reactive (push, pop, splice, etc.)
```
---
## 4. Computed (Derived Logic)
A **Computed Signal** is a read-only value that depends on other signals. It is defined by passing a **function that returns a value**.
### Example:
```javascript
const $price = $(100);
const $qty = $(2);
const $tax = $(0.21);
// Auto-tracks $price and $qty
const $total = $(() => $price() * $qty());
$qty(3); // $total updates to 300 automatically
```
---
## 4. Effects (Reactive Actions)
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:** Logging, manual DOM tweaks, or syncing with external APIs.
* **Syntax:** `$(() => { action });`
### Example:
```javascript
const $status = $("online");
// Runs every time $status changes
$(() => {
console.log("System status is now:", $status());
// This function HAS a return statement
const $total = $(() => {
return $price() * (1 + $tax());
});
// Usage (Read-only):
console.log($total()); // 121
$price(200);
console.log($total()); // 242 (Auto-updated)
```
---
## 5. Summary Table: Usage Guide
| Primitive | Logic Type | Persistence? | Typical Use Case |
| :--- | :--- | :--- | :--- |
| **Signal** | Mutable State | **Yes** (Optional) | `$(0, 'counter')` |
| **Computed** | Derived / Read-only | No | `$(() => $a() + $b())` |
| **Effect** | Imperative Action | No | `$(() => alert($msg()))` |
---
## 💡 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:
## 5. Effects (Reactive Actions)
An **Effect** is used for side-effects. It is defined by passing a **function that does NOT return a value**. It runs once immediately and then re-runs whenever its dependencies change.
```javascript
let count = 0; // Static
const $count = $(0); // Reactive Signal
const $name = $("Alice");
// This function has NO return statement (Side-effect)
$(() => {
console.log("The name changed to:", $name());
document.title = `Profile: ${$name()}`;
});
$name("Bob"); // Triggers the console.log and updates document.title
```
---
## 6. Summary: Input Mapping
| If you pass... | SigPro creates a... | Access Method |
| :--- | :--- | :--- |
| **A Value** | **Signal** | `$var()` / `$var(val)` |
| **An Object** | **Store** | `obj.prop()` / `obj.prop(val)` |
| **An Array** | **Array Store** | `arr[i]()` / `arr.push()` |
| **Function (returns)** | **Computed** | `$comp()` (Read-only) |
| **Function (no return)** | **Effect** | Automatically executed |
---
## 💡 Naming Convention: The `$` Prefix
To keep your code clean, always prefix your reactive variables with `$`. This tells you at a glance that you need to call it as a function to get its value.
```javascript
const name = "Static"; // Just a string
const $name = $("Alice"); // A Reactive Signal
```

View File

@@ -1,4 +1,4 @@
# Application Mounter: `$.router.mount` (Core)
# Application Mounter: `$.mount`
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.

View File

@@ -1,99 +1,176 @@
# Quick API Reference
# Quick API Reference
This is a high-level summary of the **SigPro** core API. For detailed guides and edge cases, please refer to the specific documentation for each module.
SigPro is a minimal yet powerful engine. Here is a complete overview of its capabilities.
## 1. Core Reactivity: `$( )`
## 1. Core API Summary
The `$` function is a polymorphic constructor. It creates **Signals** (state) or **Computed Effects** (logic) based on the input type.
| Function | Description | Example |
| :--- | :--- | :--- |
| **`$(val, key?)`** | Creates a Signal, Computed, or Store (with optional persistence). | `const $n = $(0)` |
| **`$.html()`** | The base engine to create reactive HTMLElements. | `$.html('div', {}, 'Hi')` |
| **`Tags`** | Global helpers (div, span, button, etc.) built on top of `$.html`. | `div("Hello SigPro")` |
| **`$.mount()`** | Mounts a component into a target element (clears target first). | `$.mount(App, '#app')` |
| **`$.router()`** | Hash-based router with dynamic params and lazy loading. | `$.router(routes)` |
| **`$.plugin()`** | Extends SigPro or loads external scripts/plugins. | `$.plugin(MyPlugin)` |
| Usage | Input Type | Returns | Description |
| :--- | :--- | :--- | :--- |
| **Signal** | `any` | `Function` | A getter/setter for reactive state. |
| **Computed** | `Function` | `Function` | A read-only signal that auto-updates when its dependencies change. |
---
**Example:**
## 2. The Power of `$` (Reactivity)
The `$` function adapts to whatever you pass to it:
### **Signals & Persistent State**
Reactive values in RAM or synced with `localStorage`.
```javascript
const $count = $(0); // Signal
const $double = $(() => $count() * 2); // Computed
const $count = $(0); // Simple Signal
const $theme = $('dark', 'app-theme'); // Persistent Signal (Disk)
$count(10); // Update value
console.log($count()); // Get value: 10
```
### **Computed Signals**
Read-only signals that update automatically when their dependencies change.
```javascript
const $double = $(() => $count() * 2);
```
### **Reactive Stores (Objects + Disk)**
Transforms an object into a reactive tree. If a `key` is provided, the **entire structure** persists.
```javascript
// Store in RAM + Disk (Auto-syncs nested properties)
const state = $({
user: { name: 'Natxo' },
settings: { dark: true }
}, 'my-app-state');
// Accessing properties (they become signals)
state.user.name(); // Get: 'Natxo'
state.user.name('Guest'); // Set & Sync to Disk: 'my-app-state_user_name'
```
---
## 2. Rendering Engine: `$.html`
### **3. UI Creation: Constructor vs. Direct Tags**
SigPro uses a hyperscript-style engine to create live DOM nodes.
SigPro provides the `$.html` engine for defining any element and global "Sugar Tags" for rapid development.
| Argument | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **tag** | `string` | Yes | Standard HTML tag (e.g., 'div', 'button'). |
| **props** | `Object` | No | Attributes (`id`), Events (`onclick`), or Reactive Props (`$value`). |
| **content** | `any` | No | String, Node, Array, or Reactive Function. |
::: code-group
**Example:**
```javascript
$.html('button', { onclick: () => alert('Hi!') }, 'Click Me');
```javascript [Engine Constructor ($.html)]
// 1. DEFINE: Create a custom piece of UI
// This returns a real DOM element ready to be used.
const MyHero = $.html('section', { class: 'hero' }, [
h1("Internal Title")
]);
// 2. USE: Nest it inside other elements like a standard tag
const Page = () => div([
MyHero, // We just drop the variable here
p("This paragraph is outside the Hero section.")
]);
$.mount(Page, '#app');
```
```javascript [Global Helpers (Tags)]
// Use pre-defined global tags to compose layouts instantly.
// No need to define them, just call them.
const Page = () => div({ id: 'main' }, [
section({ class: 'hero' }, [
h1("Direct Global Tag"),
p("Building UI without boilerplate.")
]),
button({ onclick: () => alert('Hi!') }, "Click Me")
]);
$.mount(Page, '#app');
```
:::
---
## 3. Global Helpers (Tag Proxies)
### **Technical Breakdown**
To keep your code clean, SigPro automatically exposes common HTML tags to the global scope.
* **`$.html(tag, props, children)`**: This is the core factory. Use it when you need to create an element dynamically or when working with **Custom Elements / Web Components**.
* **`Global Tags (div, p, etc.)`**: These are shortcut functions that SigPro injects into the `window` object. They internally call `$.html` for you, making your component code much cleaner and easier to read.
| Category | Available Tags |
| :--- | :--- |
| **Layout** | `div`, `section`, `main`, `nav`, `header`, `footer`, `span` |
| **Typography** | `h1`, `h2`, `h3`, `p`, `label`, `a`, `li`, `ul`, `ol` |
| **Forms** | `input`, `button`, `form`, `select`, `option` |
| **Media** | `img`, `video`, `audio`, `canvas` |
---
**Example:**
### **Key Difference**
* **`$.html`**: Acts as a **constructor**. Use it when you want to "bake" a specific structure (like a Section that *always* contains an H1) into a single variable.
* **`Global Tags`**: Act as **scaffolding**. Use them to wrap different contents dynamically as you build your views.
### **Global Tags (Standard Syntax)**
SigPro declares standard tags in the global scope so you don't have to import them.
```javascript
// No imports needed!
div([
h1("Title"),
button("Ok")
const Card = (title, $val) => div({ class: 'card' }, [
h2(title),
p("Reactive content below:"),
input({
type: 'number',
$value: $val, // Automatic Two-way binding
$style: () => $val() > 10 ? 'color: red' : 'color: green'
})
]);
```
---
## 4. Mounting & Plugins
## 4. Mounting: `$.mount`
Methods to initialize your application and extend the engine.
The entry point of your application. It links your JavaScript logic to a specific DOM element.
| Method | Signature | Description |
| :--- | :--- | :--- |
| **`$.mount`** | `(node, target)` | Wipes the target (default: `body`) and renders the component. |
| **`$.plugin`** | `(source)` | Registers a function or loads external `.js` scripts as plugins. |
```html
<div id="app"></div>
```
**Example:**
```javascript
$.plugin([UI, Router]);
$.mount(App, '#root');
// In your main.js
const App = () => main([
h1("Welcome to SigPro"),
p("Everything here is reactive.")
]);
// Usage: $.mount(component, selectorOrElement)
$.mount(App, '#app');
```
---
## 5. Reactive Syntax Cheat Sheet
## 5. Navigation: `$.router`
| Feature | Syntax | Description |
| :--- | :--- | :--- |
| **Text Binding** | `p(["Value: ", $sig])` | Updates text content automatically. |
| **Attributes** | `div({ id: $sig })` | Static attribute assignment. |
| **Reactive Attr** | `div({ $class: $sig })` | Attribute updates when `$sig` changes. |
| **Two-way Binding**| `input({ $value: $sig })`| Syncs input value and signal automatically. |
| **Conditional** | `div(() => $sig() > 0 ? "Yes" : "No")` | Re-renders only the content when the condition changes. |
A robust hash-based router (`#/path`) that handles view switching automatically.
```javascript
const routes = [
{ path: '/', component: Home },
{ path: '/user/:id', component: (params) => h1(`User ID: ${params.id}`) },
{ path: '/admin', component: () => import('./Admin.js') }, // Native Lazy Loading
{ path: '*', component: () => p("404 - Not Found") }
];
// Initialize and mount the router
$.mount($.router(routes), '#app');
// Programmatic navigation
$.router.go('/user/42');
```
---
## 6. Plugins: `$.plugin`
Extend the engine or load external dependencies.
## Summary Table
```javascript
// 1. Function-based plugin
$.plugin(($) => {
$.myHelper = () => console.log("Plugin active!");
});
| Feature | SigPro Approach | Benefit |
| :--- | :--- | :--- |
| **Update Logic** | Fine-grained (Surgical) | Blazing fast updates. |
| **DOM** | Native Nodes | Zero abstraction cost. |
| **Syntax** | Pure JavaScript | No build-tool lock-in. |
| **Footprint** | Modular | Load only what you use. |
// 2. Load external scripts
await $.plugin('https://cdn.example.com/library.js');
```

View File

@@ -1,160 +1,128 @@
# Global Tag Helpers
In **SigPro**, you don't need to write `$.html('div', ...)` every time. To keep your code clean and readable, the engine automatically generates global helper functions for all standard HTML tags.
In **SigPro**, you don't need to write `$.html('div', ...)` every time. To keep your code clean and readable, the engine automatically generates global helper functions for all standard HTML tags upon initialization.
## 1. How it Works
When SigPro initializes, it runs a proxy loop that creates a function for every common HTML tag and attaches it to the `window` object.
SigPro iterates through an internal manifest of standard HTML tags and attaches a wrapper function for each one directly to the `window` object. This creates a native "DSL" (Domain Specific Language) that looks like a template engine but is **100% standard JavaScript**.
* **Traditional:** `$.html('button', { onclick: ... }, 'Click')`
* **Under the hood:** `$.html('button', { onclick: ... }, 'Click')`
* **SigPro Style:** `button({ onclick: ... }, 'Click')`
This approach gives you a "DSL" (Domain Specific Language) that feels like HTML but is actually **pure JavaScript**.
---
## 2. The Global Registry
## 2. The Complete Global Registry
The following tags are available globally by default:
The following tags are injected into the global scope and are ready to use as soon as SigPro loads:
| Category | Available Functions |
| Category | Available Global Functions |
| :--- | :--- |
| **Layout** | `div`, `span`, `section`, `main`, `nav`, `header`, `footer`, `article`, `aside` |
| **Typography** | `h1`, `h2`, `h3`, `p`, `ul`, `ol`, `li`, `a`, `label`, `strong`, `em` |
| **Forms** | `form`, `input`, `button`, `select`, `option`, `textarea` |
| **Table** | `table`, `thead`, `tbody`, `tr`, `th`, `td` |
| **Media** | `img`, `video`, `audio`, `canvas`, `svg` |
| **Structure** | `div`, `span`, `p`, `section`, `nav`, `main`, `header`, `footer`, `article`, `aside` |
| **Typography** | `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `ul`, `ol`, `li`, `dl`, `dt`, `dd`, `strong`, `em`, `code`, `pre`, `small`, `i`, `b`, `u`, `mark` |
| **Interactive** | `button`, `a`, `label`, `br`, `hr`, `details`, `summary` |
| **Forms** | `form`, `input`, `select`, `option`, `textarea`, `fieldset`, `legend` |
| **Tables** | `table`, `thead`, `tbody`, `tr`, `th`, `td`, `tfoot`, `caption` |
| **Media & Graphics** | `img`, `canvas`, `video`, `audio`, `svg`, `path`, `iframe` |
> "In SigPro, tags are not 'magic' strings handled by a compiler. They are **functional imitations** of HTML elements. Every time you call `div()`, you are executing a standard JavaScript function that returns a real DOM element. This gives you the speed of a specialized DSL with the transparency of pure JS."
::: danger WARNING: GLOBAL NAMING COLLISIONS
Since **SigPro** injects these helpers directly into the `window` object, they are regular JavaScript functions. This means **they can be overwritten**.
If you declare a variable, constant, or function with the same name as an HTML tag (e.g., `const div = ...` or `function p()`), you will **nullify or shadow** the built-in SigPro helper for that tag in your current scope.
**Best Practice:** To avoid conflicts, always use **PascalCase** for your custom components (e.g., `UserCard`, `AppHeader`) to distinguish them from the **lowercase** global HTML helpers.
:::
---
## 3. Usage Patterns
## 3. Usage Patterns (Argument Flexibility)
The tag functions are highly flexible and accept arguments in different orders to suit your coding style.
The tag functions are "smart". They detect whether you are passing attributes, content, or both.
### A. Attributes + Content
The most common pattern.
The standard way to build complex nodes.
```javascript
div({ class: 'card' }, [
h1("Title"),
p("Description")
div({ class: 'container', id: 'main-wrapper' }, [
h1("Welcome"),
p("This is SigPro.")
]);
```
### B. Content Only
If you don't need attributes, you can skip the object entirely.
### B. Content Only (The "Skipper")
If you don't need attributes, you can pass the content (string, array, or function) as the **first and only** argument.
```javascript
div([
h1("Just Content"),
p("No attributes object needed here.")
section([
h2("No Attributes Needed"),
button("Click Me")
]);
```
### C. Simple Text
For elements that only contain a string.
### C. Primitive Content
For simple tags, you can just pass a string or a number.
```javascript
button("Submit"); // Equivalent to <button>Submit</button>
h1("Hello World");
span(42);
```
---
## 4. Reactive Tags
## 4. Reactive Attributes & Content
Since these helpers are just wrappers around `$.html`, they support full reactivity out of the box.
These helpers fully support SigPro's reactivity. Attributes starting with `$` are automatically tracked.
```javascript
const $loading = $(true);
const $count = $(0);
div([
$loading() ? span("Loading...") : h1("Data Ready!"),
div({ class: 'counter-app' }, [
h2(["Current Count: ", $count]), // Auto-unwrapping text content
button({
$disabled: $loading, // Reactive attribute
onclick: () => $loading(false)
}, "Stop Loading")
onclick: () => $count(c => c + 1),
$style: () => $count() > 5 ? "color: red" : "color: green" // Reactive style
}, "Increment")
]);
```
---
## 5. Under the Hood
## 5. Technical Implementation
If you are curious about how this happens without a compiler, here is the logic inside the SigPro core:
As seen in the SigPro core, the engine registers these tags dynamically. This means **zero imports** are needed for UI creation in your component files.
```javascript
const tags = ['div', 'span', 'p', 'button', ...];
tags.forEach(tag => {
window[tag] = (props, content) => $.html(tag, props, content);
});
// Internal SigPro loop
tags.forEach(t => window[t] = (p, c) => $.html(t, p, c));
```
Because these are attached to `window`, they are available in any file in your project as soon as SigPro is loaded, making your components look like this:
```javascript
// No imports required for tags!
export default () =>
section({ id: 'hero' }, [
h1("Fast. Atomic. Simple."),
p("Built with SigPro.")
]);
```
Because they are real functions, you get full IDE autocompletion and valid JS syntax highlighting without needing special plugins like JSX.
---
## 6. Full Comparison: SigPro vs. Standard HTML
## 6. Comparison: Logic to UI
To better understand the translation, here is a complete example of a **User Card** component. Notice how **SigPro** attributes with the `$` prefix map to reactive behavior, while standard attributes remain static.
Here is how a dynamic **Task Item** component translates from SigPro logic to the final DOM structure.
::: code-group
```javascript [SigPro (JS)]
const $online = $(true);
export const UserCard = () => (
div({ class: 'user-card' }, [
img({ src: 'avatar.png', alt: 'User' }),
div({ class: 'info' }, [
h2("John Doe"),
p({
$class: () => $online() ? 'status-on' : 'status-off'
}, [
"Status: ",
() => $online() ? "Online" : "Offline"
])
]),
button({
onclick: () => $online(!$online())
}, "Toggle Status")
```javascript [SigPro Component]
const Task = (title, $done) => (
li({ class: 'task-item' }, [
input({
type: 'checkbox',
$checked: $done // Two-way reactive binding
}),
span({
$style: () => $done() ? "text-decoration: line-through" : ""
}, title)
])
);
```
```html [Equivalent HTML Structure]
<div class="user-card">
<img src="avatar.png" alt="User">
<div class="info">
<h2>John Doe</h2>
<p class="status-on">
Status: Online
</p>
</div>
<button>Toggle Status</button>
</div>
```html [Rendered HTML]
<li class="task-item">
<input type="checkbox" checked>
<style="text-decoration: line-through">Buy milk</span>
</li>
```
:::
### What is happening here?
1. **Structure:** The hierarchy is identical. `div([...])` in JS translates directly to nested tags in HTML.
2. **Attributes:** `class` is set once. `$class` is "live"; SigPro listens to the `$online` signal and updates the class name without re-rendering the whole card.
3. **Content:** The array `[...]` in SigPro is the equivalent of the children inside an HTML tag.
4. **Reactivity:** The function `() => $online() ? ...` creates a **TextNode** in the HTML that changes its text content surgically whenever the signal toggles.
---
## 💡 Best Practices
1. **Destructuring:** If you prefer not to rely on global variables, you can destructure them from `window` or `$` (though in SigPro, using them globally is the intended "clean" way).
2. **Custom Tags:** If you need a tag that isn't in the default list (like a Web Component), you can still use the base engine: `$.html('my-custom-element', { ... })`.

55
src/docs/examples.md Normal file
View File

@@ -0,0 +1,55 @@
# Live Playground
Experience **SigPro's** fine-grained reactivity in real-time. Feel free to tweak the signal values in the editor!
<iframe width="100%" height="600" src="//jsfiddle.net/natxocc/spwran02/4/embedded/" frameborder="0" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>
```
---
### 2. Best Practices for Documentation
* **Tab Selection:** You can control which tabs are active by default by changing the URL segment after `/embedded/`.
* `js,result`: Shows the logic and the output.
* `html,js,result`: Shows the base structure, the logic, and the output.
* **Height Management:** For complex Store examples, increase the `height` attribute to `500` or `600` so the code is readable without internal scrolling.
* **Responsive Width:** Keeping `width="100%"` ensures the fiddle scales correctly on tablets and mobile devices.
---
### 3. Advanced: The "Fiddle" Component (Optional)
If you plan to have 10+ examples, you can create a global Vue component in VitePress. This keeps your Markdown files clean and allows you to change the theme or default height for all fiddles at once.
**Create `.vitepress/theme/components/Fiddle.vue`:**
```vue
<template>
<div class="fiddle-wrapper" style="margin: 20px 0;">
<iframe
width="100%"
:height="height"
:src="`//jsfiddle.net/natxocc/${id}/embedded/${tabs}/dark/`"
frameborder="0"
loading="lazy">
</iframe>
</div>
</template>
<script setup>
defineProps({
id: String, // e.g., "spwran02/4"
height: { default: '400' },
tabs: { default: 'js,result' }
})
</script>
```
**Usage in Markdown:**
```markdown
Check out this store example:
<Fiddle id="spwran02/4" height="500" />
```
---
### Why this is perfect for SigPro:
Because SigPro is **zero-dependency** and runs directly in the browser, your JSFiddle code will be exactly what the user copies into their own `index.html`. There is no hidden "build step" confusing the learner.

View File

@@ -1,76 +0,0 @@
# Getting Started
**SigPro** is a lightweight, atomic reactive engine designed to build modern web interfaces with zero overhead. It focuses on high performance through fine-grained reactivity.
## 1. Installation
You can install SigPro via your favorite package manager:
::: code-group
```bash [npm]
npm install sigpro
````
```bash [pnpm]
pnpm add sigpro
```
```bash [yarn]
yarn add sigpro
```
```bash [bun]
bun add sigpro
```
:::
## 2\. Basic Usage
The core of SigPro is the `$` function, which creates reactive state (Signals) and computed effects.
Create a `main.js` file and try this:
```javascript
import { $ } from 'SigPro';
// 1. Create a reactive signal
const $name = $("World");
// 2. Define a reactive component
const App = () => div({ class: 'container' }, [
h1(["Hello, ", $name, "!"]),
input({
type: 'text',
$value: $name, // Two-way binding
placeholder: 'Enter your name...'
}),
button({
onclick: () => $name("SigPro")
}, "Set to SigPro")
]);
// 3. Mount the application
$.mount(App, '#app');
```
## 3\. How it Works
SigPro doesn't use a Virtual DOM. Instead, it creates real DOM nodes and binds them directly to your data:
1. **Signals**: `$(value)` creates a getter/setter function.
2. **Reactivity**: When you pass a signal or a function to a DOM element, SigPro automatically creates a subscription.
3. **Fine-Grained Updates**: Only the specific text node or attribute linked to the signal updates when the value changes.
## 4\. Global Tags
By default, SigPro exports common HTML tags to the global scope (`window`) when initialized. This allows you to write clean, declarative UI without importing every single tag:
```javascript
// Instead of $.html('div', ...), just use:
div([
h1("Clean Syntax"),
p("No more boilerplate.")
]);
```

View File

@@ -1,78 +0,0 @@
# Why SigPro?
After years of building applications with React, Vue, and Svelte—investing countless hours mastering unique mental models, proprietary syntaxes, and complex build tools—we reached a realization: the web platform has evolved, but frameworks have become layers of abstraction that often move us further away from the browser.
**SigPro** is the answer to a simple question: **Why fight the platform when we can embrace it?**
## The Modern Web is Ready
SigPro bypasses the overhead of the Virtual DOM and heavy compilers by using modern browser primitives. It treats the DOM as a first-class citizen, not as a side effect of a state change.
| Browser Primitive | What It Enables |
| :--- | :--- |
| **Closures & Proxies** | Automatic dependency tracking without heavy overhead. |
| **ES Modules** | Native modularity and lazy loading without complex bundlers. |
| **Direct DOM APIs** | Surgical updates that are faster than any reconciliation algorithm. |
| **Microtask Queues** | Batching updates efficiently to ensure 60fps performance. |
---
## The SigPro Philosophy
SigPro strips away the complexity, delivering a reactive programming model that feels like a framework but stays remarkably close to Vanilla JS:
* **No JSX transformations** Pure JavaScript functions.
* **No Virtual DOM** Direct, fine-grained DOM manipulation.
* **No proprietary syntax** If you know JS, you know SigPro.
* **Zero Build Step Required** It can run directly in the browser via ESM.
```javascript
// Pure, Atomic, Reactive.
const $count = $(0);
const Counter = () => div([
p(["Count: ", $count]),
button({ onclick: () => $count(c => c + 1) }, "Increment")
]);
```
---
## Performance Comparison
SigPro isn't just lighter; it's architecturally faster because it skips the "diffing" phase entirely.
| Metric | SigPro | SolidJS | Svelte | Vue | React |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **Bundle Size (gzip)** | 🥇 **< 2KB** | 🥈 7KB | 🥉 16KB | 20KB | 45KB |
| **Architecture** | **Atomic** | **Atomic** | **Compiled** | **V-DOM** | **V-DOM** |
| **Initial Render** | 🥇 **Fastest** | 🥈 Fast | 🥉 Fast | Average | Slow |
| **Update Perf** | 🥇 **Surgical** | 🥇 Surgical | 🥈 Fast | 🥉 Average | Slow |
| **Dependencies** | 🥇 **0** | 🥇 0 | 🥇 0 | 🥈 2 | 🥉 5+ |
| **Build Step** | 🥇 **Optional** | 🥈 Required | 🥈 Required | 🥇 Optional | 🥈 Required |
---
## 🔑 Core Principles
SigPro is built on four fundamental pillars:
### 📡 Atomic Reactivity
Automatic dependency tracking with no manual subscriptions. When a signal changes, only the **exact** text nodes or attributes that depend on it update—instantly and surgically.
### ⚡ Surgical DOM Updates
No Virtual DOM diffing. No tree reconciliation. We don't guess what changed; we know exactly where the update needs to happen. Performance scales with your data, not the size of your component tree.
### 🧩 Plugin-First Architecture
The core is a tiny, powerful engine. Need Routing? Fetching? Global UI? Just plug it in. This keeps your production bundles "pay-only-for-what-you-use."
### 🔬 Predictable & Transparent
There is no "magic" hidden in a black-box compiler. What you write is what the browser executes. Debugging is straightforward because there is no framework layer between your code and the DevTools.
---
> "SigPro returns the joy of web development by making the browser the hero again."

View File

@@ -4,93 +4,58 @@ layout: home
hero:
name: SigPro
text: Atomic Unified Reactive Engine
tagline: Fine-grained reactivity, built-in routing, and modular plugins. All under 2KB.
tagline: High-precision atomic reactivity. No Virtual DOM. No compiler. No dependencies.
image:
src: /logo.svg
alt: SigPro Logo
actions:
- theme: brand
text: Get Started
link: /guide/getting-started
link: /install
- theme: alt
text: View on GitHub
link: https://github.com/natxocc/sigpro
features:
- title: Atomic Reactivity
details: Powered by Signals. Only updates what changes. No Virtual DOM overhead, no heavy re-renders.
- title: Zero Dependencies
details: Written in pure Vanilla JS. Maximum performance with the smallest footprint possible.
- title: Modular Ecosystem
details: Official plugins for UI components, dynamic Routing, Fetch, and Storage. Load only what you need.
- title: ⚛️ Atomic Reactivity
details: Powered by fine-grained Signals. Forget about whole-component re-renders; SigPro updates only the specific text node or attribute that changed.
- title: 🚀 Zero Virtual DOM
details: By eliminating the V-DOM diffing layer, SigPro performs surgical, direct manipulations on the real DOM, removing memory and CPU overhead.
- title: 🛠️ No Compiler Required
details: Pure Vanilla JS. No Babel, no JSX, no complex build steps. Standard JavaScript that runs natively in the browser with maximum performance.
- title: 📦 Ultra-Lightweight
details: The core engine—including reactivity, DOM creation, persistence, and routing—is under 2KB. Perfect for performance-critical applications.
---
## Why SigPro?
## Redefining Modern Reactivity
SigPro isn't just another framework; it's a high-performance engine. It strips away the complexity of massive bundles and returns to the essence of the web, enhanced with reactive superpowers.
SigPro is not just another framework; it is a **high-performance engine**. While other libraries add layers of abstraction that slow down execution, SigPro returns to the essence of the web, leveraging the power of modern browser engines.
### The Core in Action
### Why SigPro?
```javascript
import { $ } from 'sigpro2';
#### ⚡️ Surgical DOM Efficiency
Unlike React or Vue, SigPro doesn't compare element trees. When a signal changes, SigPro knows exactly which DOM node depends on it and updates it instantly. It is **reactive precision** at its finest.
// A reactive state Signal
const $count = $(0);
#### 🔌 Modular Plugin System
The core is sacred. Any extra functionality—Routing, UI Helpers, or State Persistence—is integrated through a polymorphic plugin system. Load only what your application truly needs.
// A Computed signal that updates automatically
const $double = $(() => $count() * 2);
#### 💾 Native Persistence
SigPro features first-class support for `localStorage`. Synchronizing your application state with persistent storage is as simple as providing a key when initializing your Signal.
// UI that breathes with your data
const Counter = () => div([
h1(["Count: ", $count]),
p(["Double: ", $double]),
button({ onclick: () => $count(c => c + 1) }, "Increment")
]);
$.mount(Counter);
```
#### 🚦 Built-in Hash Routing
A robust routing system that supports **Native Lazy Loading** out of the box. Load your components only when the user navigates to them, keeping initial load times near zero.
---
### Key Features
### The "No-Build" Philosophy
In an ecosystem obsessed with compilers, SigPro bets on **standardization**. Write code today that will still run 10 years from now, without depending on build tools that will eventually become obsolete.
#### ⚡️ Fine-Grained Reactivity
Unlike frameworks that diff complex trees (V-DOM), SigPro binds your signals directly to real DOM text nodes and attributes. If the data changes, the node changes. Period.
#### 🔌 Polymorphic Plugin System
Extend core capabilities in a single line. Add global UI helpers, routing, or state persistence seamlessly.
```javascript
import { UI, Router } from 'sigpro/plugins';
$.plugin([UI, Router]);
```
#### 📂 File-Based Routing
With our dedicated Vite plugin, manage your routes simply by creating files in `src/pages/`. It supports native **Lazy Loading** out of the box for lightning-fast initial loads.
> "The best way to optimize code is to not have to process it at all."
---
### Quick Install
## Community & Vision
SigPro is an open-source project focused on simplicity and extreme speed. Designed for developers who love the web platform and hate unnecessary "bloatware".
::: code-group
```bash [npm]
npm install sigpro
```
```bash [pnpm]
pnpm add sigpro
```
```bash [yarn]
yarn add sigpro
```
```bash [bun]
bun add sigpro
```
:::
---
## Community & Support
SigPro is an open-source project. Whether you want to contribute, report a bug, or just talk about reactivity, join us on our official repository.
```
Built with ❤️ by NatxoCC
```
```text
Built with ❤️ by NatxoCC for the Modern Web.

110
src/docs/install.md Normal file
View File

@@ -0,0 +1,110 @@
# Installation & Setup
SigPro is designed to be drop-in ready. Whether you are building a complex application with a bundler or a simple reactive widget in a single HTML file, SigPro scales with your needs.
## 1. Installation
Choose the method that best fits your workflow:
::: code-group
```bash [npm]
npm install sigpro
````
```bash [pnpm]
pnpm add sigpro
```
```bash [yarn]
yarn add sigpro
```
```bash [bun]
bun add sigpro
```
```html [CDN (ESM)]
<script type="module">
import { $ } from 'https://cdn.jsdelivr.net/npm/sigpro@latest/+esm';
</script>
```
:::
-----
## 2\. Quick Start Examples
Depending on your installation method, here is how you can get SigPro running in seconds.
::: code-group
```javascript [Mainstream (Bundlers)]
// File: App.js
import { $ } from 'sigpro';
export const App = () => {
// $ is global, but we import it for better IDE intellisense
const $count = $(0);
// Tags like div, h1, button are available globally
return div({ class: 'card' }, [
h1(["Count is: ", $count]),
button({ onclick: () => $count(c => c + 1) }, "Increment")
]);
};
// File: main.js
import { $ } from 'sigpro';
import { App } from './App.js';
$.mount(App, '#app');
```
```html [Classic (Direct CDN)]
<!DOCTYPE html>
<html lang="en">
<body>
<div id="app"></div>
<script type="module">
// Import directly from CDN
import { $ } from 'https://cdn.jsdelivr.net/npm/sigpro@latest/+esm';
const $name = $("Developer");
// No need to import div, section, h2, input... they are global!
const App = () => section([
h2(["Welcome, ", $name]),
input({
type: 'text',
$value: $name, // Automatic two-way binding
placeholder: 'Type your name...'
})
]);
$.mount(App, '#app');
</script>
</body>
</html>
```
:::
-----
## 3\. Global by Design
One of SigPro's core strengths is its **Global API**.
* **The `$` Function:** Once loaded, it attaches itself to `window.$`. While you can use `import` for better IDE support and type checking, it is accessible everywhere.
* **HTML Tags:** Common tags (`div`, `span`, `button`, etc.) are automatically registered in the global scope. This eliminates "Import Hell" and keeps your components clean and readable.
## 4\. Why no build step?
Because SigPro uses **native ES Modules** and standard JavaScript functions to generate the DOM, you don't actually *need* a compiler like Babel or a loader for JSX.
* **Development:** Just save and refresh. No complex HMR issues.
* **Production:** Use any bundler (Vite, esbuild, Rollup) to tree-shake and minify your final code for maximum performance.

View File

@@ -1,107 +0,0 @@
# Development Tool: `_debug`
The **Debug Plugin** is a lightweight reactive listener. Once attached to a signal or a computed function, it automatically monitors changes, compares values, and formats the output in the browser console.
## 1. Core Features
* **Reactive Tracking:** Automatically logs whenever the tracked signal updates.
* **Visual Grouping:** Uses styled console groups to keep your dev tools organized.
* **Object Inspection:** Automatically uses `console.table()` when the signal contains an object or array.
* **Efficient Comparison:** Uses `Object.is` to prevent redundant logging if the value hasn't actually changed.
---
## 2. Installation
To use `_debug`, you only need the SigPro core. Register the plugin in your `main.js`. You can conditionally load it so it only runs during development.
```javascript
import { $ } from 'sigpro';
import { Debug } from 'sigpro/plugins';
// Only load Debug in development mode
const plugins = [];
if (import.meta.env.DEV) plugins.push(Debug);
$.plugin(plugins).then(() => {
import('./App.js').then(app => $.mount(app.default));
});
```
::: code-group
```bash [NPM]
npm install sigpro
```
```bash [PNPM]
pnpm add sigpro
```
```bash [Yarn]
yarn add sigpro
```
```bash [Bun]
bun add sigpro
```
:::
---
## 3. Basic Usage
Call `_debug` anywhere in your component. It stays active in the background, watching the signal's lifecycle.
```javascript
export default () => {
const $count = $(0);
const $user = $({ name: "Guest", role: "Viewer" });
// Start tracking
_debug($count, "Main Counter");
_debug($user, "User Session");
return div([
button({ onclick: () => $count(c => c + 1) }, "Increment"),
button({ onclick: () => $user({ name: "Admin", role: "Super" }) }, "Promote")
]);
};
```
---
## 4. Console Output Breakdown
When a signal changes, the console displays a structured block:
1. **Header:** A styled badge with the name (e.g., `SigPro Debug: Main Counter`).
2. **Previous Value:** The value before the update (in red).
3. **Current Value:** The new value (in green).
4. **Table View:** If the value is an object, a formatted table appears automatically.
---
## 5. Debugging Computed Values
You can also debug **computed functions** to see exactly when derived state is recalculated.
```javascript
const $price = $(100);
const $tax = $(0.21);
const $total = $(() => $price() * (1 + $tax()));
// Monitor the result of the calculation
_debug($total, "Final Invoice Total");
```
---
## 6. Why use `_debug`?
1. **Clean Logic:** No need to scatter `console.log` inside your reactive functions.
2. **State History:** Instantly see the "Before" and "After" of any user action.
3. **No-Noise:** It only logs when a real change occurs, keeping the console clean.
4. **Deep Inspection:** The automatic `console.table` makes debugging large API responses much faster.

View File

@@ -1,80 +0,0 @@
# Data Fetching: `_fetch`
The **Fetch Plugin** provides a reactive wrapper around the native browser Fetch API. Instead of managing complex `async/await` flows within your UI, `_fetch` returns a "Reactive Tripod" (Data, Loading, and Error) that your components can listen to automatically.
## 1. Core Concept
When you call `_fetch`, it returns three signals immediately. Your UI declares how to react to these signals as they change from their initial state to the final response.
* **`$data`**: Initialized as `null`. Automatically holds the JSON response on success.
* **`$loading`**: Initialized as `true`. Flips to `false` once the request settles.
* **`$error`**: Initialized as `null`. Holds the error message if the request fails.
---
## 2. Installation
Register the `Fetch` plugin in your `main.js`. By convention, we load it alongside the UI and Router to have the full SigPro ecosystem ready.
```javascript
import { $ } from 'sigpro';
import { Fetch } from 'sigpro/plugins';
$.plugin([Fetch]).then(() => {
// Now _fetch() is available globally
import('./App.js').then(app => $.mount(app.default));
});
```
---
## 3. Basic Usage
Use `_fetch` inside your component to get live updates. The UI updates surgically whenever a signal changes.
```javascript
export default () => {
const { $data, $loading, $error } = _fetch('https://api.github.com/users/octocat');
return div({ class: 'p-6 flex flex-col gap-4' }, [
h1("Profile Details"),
// 1. Loading State (using SigPro UI button)
() => $loading() && _button({ $loading: true }, "Fetching..."),
// 2. Error State
() => $error() && div({ class: 'alert alert-error' }, $error()),
// 3. Success State
() => $data() && div({ class: 'card bg-base-200 p-4' }, [
img({ src: $data().avatar_url, class: 'w-16 rounded-full' }),
h2($data().name),
p($data().bio)
])
]);
};
```
---
## 4. Advanced Configuration
`_fetch` accepts the same `RequestInit` options as the standard `fetch()` (methods, headers, body, etc.).
```javascript
const { $data, $loading } = _fetch('/api/v1/update', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ status: 'active' })
});
```
---
## 5. Why use `_fetch` instead of native Fetch?
1. **Declarative UI**: You define the "Loading", "Error", and "Success" templates once, and they swap automatically.
2. **No `useEffect` required**: Since SigPro is natively reactive, you don't need lifecycle hooks to trigger re-renders; the signals handle it.
3. **Consistency**: It follows the same `_prefix` pattern as the rest of the official plugin ecosystem.
4. **Automatic JSON Parsing**: It assumes JSON by default and handles 404/500 errors by populating the `$error` signal.

View File

@@ -1,110 +0,0 @@
# Navigation Plugin: `Router`
The SigPro Router handles URL changes via hashes (`#`) and maps them to components. It supports dynamic parameters (like `:id`) and asynchronous loading for heavy pages.
## 1. Core Features
* **Hash-based:** Works everywhere without special server configuration.
* **Lazy Loading:** Pages are only downloaded when the user visits the route.
* **Reactive:** The view updates automatically when the hash changes.
* **Dynamic Routes:** Supports paths like `/user/:id`.
---
## 2. Installation
The Router is usually included in the official plugins package.
::: code-group
```bash [NPM]
npm install -D tailwindcss @tailwindcss/vite daisyui@next
```
```bash [PNPM]
pnpm add -D tailwindcss @tailwindcss/vite daisyui@next
```
```bash [Yarn]
yarn add -D tailwindcss @tailwindcss/vite daisyui@next
```
```bash [Bun]
bun add -d tailwindcss @tailwindcss/vite daisyui@next
```
:::
---
## 3. Setting Up Routes
In your `App.js` (or a dedicated routes file), define your navigation map.
```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([
_navbar({ title: "My App" }),
_router(routes) // The router is now a global tag
]);
```
---
## 4. Navigation (`_router.go`)
To move between pages programmatically (e.g., inside an `onclick` event), use the global `_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.
2. **Resolve:** * If it's a standard function, it executes it immediately.
* If it's a **Promise** (via `import()`), it shows a loading state and swaps the content once the module arrives.
3. **Inject:** It replaces the previous DOM node with the new page content surgically.
---
## 6. Integration with UI Components
Since you are using the **UI Plugin**, you can easily create active states in your navigation menus by checking the current hash.
```javascript
// Example of a reactive sidebar menu
_menu({
items: [
{
label: 'Dashboard',
active: () => window.location.hash === '#/',
onclick: () => _router.go('/')
},
{
label: 'Settings',
active: () => window.location.hash === '#/settings',
onclick: () => _router.go('/settings')
}
]
})
```

View File

@@ -1,106 +0,0 @@
# Persistence Tool: `_storage`
The Storage plugin synchronizes a signal with a specific key in your browser's `localStorage`. It handles both the **initial hydration** (loading data when the app starts) and **automatic saving** whenever the signal's value changes.
## 1. Core Concept
When you "attach" a signal to `_storage`, two things happen:
1. **Hydration:** The plugin checks if the key already exists in `localStorage`. If it does, it parses the JSON and updates the signal immediately.
2. **Reactive Sync:** It creates a reactive watcher that stringifies and saves the signal's value to the disk every time it is updated.
---
## 2. Installation
Register the `Storage` plugin in your `main.js`. Since this is a logic-only plugin, it doesn't require any CSS or UI dependencies.
```javascript
import { $ } from 'sigpro';
import { Storage } from 'sigpro/plugins';
$.plugin(Storage).then(() => {
import('./App.js').then(app => $.mount(app.default));
});
```
::: code-group
```bash [NPM]
npm install sigpro
```
```bash [PNPM]
pnpm add sigpro
```
```bash [Yarn]
yarn add sigpro
```
```bash [Bun]
bun add sigpro
```
:::
---
## 3. Basic Usage
You can wrap any signal with `_storage`. It is common practice to do this right after creating the signal.
```javascript
export default () => {
// 1. Create a signal with a default value
const $theme = $( 'light' );
// 2. Persist it. If 'user_theme' exists in localStorage,
// $theme will be updated to that value instantly.
_storage($theme, 'user_theme');
return div({ class: () => `app-${$theme()}` }, [
h1(`Current Theme: ${$theme()}`),
button({
onclick: () => $theme(t => t === 'light' ? 'dark' : 'light')
}, "Toggle Theme")
]);
};
```
---
## 4. Complex Data (Objects & Arrays)
Since the plugin uses `JSON.parse` and `JSON.stringify` internally, it works perfectly with complex state structures.
```javascript
const $settings = $({
notifications: true,
fontSize: 16
});
// Automatically saves the whole object whenever any property changes
_storage($settings, 'app_settings');
```
---
## 5. Why use `_storage`?
1. **Zero Boilerplate:** You don't need to manually write `localStorage.getItem` or `setItem` logic inside your components.
2. **Chaining:** Because `_storage` returns the signal, you can persist it inline.
3. **Error Resilience:** It includes a built-in `try/catch` block to prevent your app from crashing if the stored JSON is corrupted.
4. **Surgical Persistence:** Only the signals you explicitly mark for storage are saved, keeping your `localStorage` clean.
---
## 6. Pro Tip: Combining with Debug
You can chain plugins to create a fully monitored and persistent state:
```javascript
const $score = _storage($(0), 'high_score');
// Now it's saved to disk AND logged to console on every change
_debug($score, "Game Score");
```

View File

@@ -1,956 +0,0 @@
# SigPro UI Plugin - Complete Documentation
## Overview
The **SigPro UI** plugin is a comprehensive, reactive component library built on SigPro's atomic reactivity system. It seamlessly integrates **Tailwind CSS v4** for utility-first styling and **daisyUI v5** for semantic, themeable components. Every component is reactive by nature, automatically responding to signal changes without manual DOM updates.
## Table of Contents
1. [Installation & Setup](#installation--setup)
2. [Core Concepts](#core-concepts)
3. [Form Components](#form-components)
4. [Action Components](#action-components)
5. [Layout Components](#layout-components)
6. [Navigation Components](#navigation-components)
7. [Feedback Components](#feedback-components)
8. [Container Components](#container-components)
9. [Complete Examples](#complete-examples)
10. [Styling Guide](#styling-guide)
11. [Best Practices](#best-practices)
---
## Installation & Setup
### Step 1: Install Dependencies
```bash
npm install -D tailwindcss @tailwindcss/vite daisyui@next
```
### Step 2: Configure Tailwind CSS v4
Create `src/app.css`:
```css
/* src/app.css */
@import "tailwindcss";
@plugin "daisyui";
/* Optional: Custom themes */
@theme {
--color-primary: oklch(0.65 0.2 250);
--color-secondary: oklch(0.7 0.15 150);
}
/* Dark mode support */
@custom-variant dark (&:where(.dark, [data-theme="dark"], [data-theme="dark"] *)));
```
### Step 3: Initialize in Your Entry Point
```javascript
// main.js
import './app.css';
import { $ } from 'sigpro';
import { UI } from 'sigpro/plugins';
// Load the UI plugin - makes all _components globally available
$.plugin(UI).then(() => {
// All UI components are now registered
import('./App.js').then(app => $.mount(app.default));
});
```
---
## Core Concepts
### Reactive Props
All UI components accept reactive props using the `$` prefix. When you pass a signal, the component automatically updates:
```javascript
const $username = $('John');
const $error = $(null);
// Reactive input with two-way binding
_input({
$value: $username, // Auto-updates when signal changes
$error: $error // Shows error message when signal has value
})
```
### The `parseClass` Helper
All components intelligently merge base classes with user-provided classes, supporting both static strings and reactive functions:
```javascript
// Static class merging
_button({ class: 'btn-primary' }, 'Click me')
// Result: class="btn btn-primary"
// Reactive classes
const $theme = $('btn-primary');
_button({ class: () => $theme() }, 'Dynamic Button')
// Updates when $theme changes
```
---
## Form Components
### `_input` - Smart Input Field
A complete input wrapper with label, tooltip, error handling, and two-way binding.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `label` | `string` | Field label text |
| `tip` | `string` | Tooltip text shown on hover of a "?" badge |
| `$value` | `signal` | Two-way bound value signal |
| `$error` | `signal` | Error message signal (shows red border + message) |
| `type` | `string` | Input type: 'text', 'email', 'password', etc. |
| `placeholder` | `string` | Placeholder text |
| `class` | `string\|function` | Additional CSS classes |
**Examples:**
```javascript
// Basic usage
const $email = $('');
_input({
label: 'Email Address',
type: 'email',
placeholder: 'user@example.com',
$value: $email
})
// With validation
const $password = $('');
const $passwordError = $(null);
_input({
label: 'Password',
type: 'password',
$value: $password,
$error: $passwordError,
oninput: (e) => {
if (e.target.value.length < 6) {
$passwordError('Password must be at least 6 characters');
} else {
$passwordError(null);
}
}
})
```
### `_select` - Dropdown Selector
Reactive select component with options array.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `label` | `string` | Field label |
| `options` | `Array<{value: any, label: string}>` | Select options |
| `$value` | `signal` | Two-way bound selected value |
**Example:**
```javascript
const $role = $('user');
const roles = [
{ value: 'admin', label: 'Administrator' },
{ value: 'user', label: 'Standard User' },
{ value: 'guest', label: 'Guest' }
];
_select({
label: 'User Role',
options: roles,
$value: $role
})
// Reactive selection
console.log($role()); // 'user'
```
### `_checkbox` - Toggle Checkbox
Styled checkbox with label support.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `label` | `string` | Checkbox label text |
| `$value` | `signal` | Boolean signal for checked state |
**Example:**
```javascript
const $remember = $(true);
_checkbox({
label: 'Remember me',
$value: $remember
})
```
### `_radio` - Radio Button Group
Radio button with group value binding.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `label` | `string` | Radio option label |
| `value` | `any` | Value for this radio option |
| `$value` | `signal` | Group signal holding selected value |
**Example:**
```javascript
const $paymentMethod = $('credit');
['credit', 'paypal', 'crypto'].forEach(method => {
_radio({
name: 'payment',
label: method.toUpperCase(),
value: method,
$value: $paymentMethod
})
})
// Selected: $paymentMethod() === 'credit'
```
### `_range` - Slider Control
Reactive range slider with optional label.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `label` | `string` | Slider label |
| `min` | `number` | Minimum value |
| `max` | `number` | Maximum value |
| `step` | `number` | Step increment |
| `$value` | `signal` | Current value signal |
**Example:**
```javascript
const $volume = $(50);
_range({
label: 'Volume',
min: 0,
max: 100,
step: 1,
$value: $volume
})
// Display current value
span(() => `Volume: ${$volume()}%`)
```
---
## Action Components
### `_button` - Smart Action Button
Feature-rich button with loading states, icons, and badges.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `$loading` | `signal` | Shows spinner + disables when true |
| `$disabled` | `signal` | Manual disabled state |
| `icon` | `string\|HTMLElement` | Icon element or emoji/unicode |
| `badge` | `string` | Badge text to display |
| `badgeClass` | `string` | Additional badge styling |
| `type` | `string` | Button type: 'button', 'submit', etc. |
| `onclick` | `function` | Click handler |
**Examples:**
```javascript
// Basic button
_button({ onclick: () => alert('Clicked!') }, 'Click Me')
// Loading state
const $saving = $(false);
_button({
$loading: $saving,
icon: '💾',
onclick: async () => {
$saving(true);
await saveData();
$saving(false);
}
}, 'Save Changes')
// With badge notification
_button({
badge: '3',
badgeClass: 'badge-secondary',
icon: '🔔'
}, 'Notifications')
```
---
## Layout Components
### `_fieldset` - Form Section Group
Groups related form fields with a legend.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `legend` | `string` | Fieldset title |
| `class` | `string\|function` | Additional classes |
**Example:**
```javascript
_fieldset({ legend: 'Personal Information' }, [
_input({ label: 'First Name', $value: $firstName }),
_input({ label: 'Last Name', $value: $lastName }),
_input({ label: 'Email', type: 'email', $value: $email })
])
```
### `_accordion` - Collapsible Section
Expandable/collapsible content panel.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `title` | `string` | Accordion header text |
| `name` | `string` | Optional group name (radio behavior) |
| `open` | `boolean` | Initially open state |
**Examples:**
```javascript
// Single accordion (checkbox behavior)
_accordion({ title: 'Frequently Asked Questions' }, [
p('This is the collapsible content...')
])
// Grouped accordions (radio behavior - only one open)
_accordion({ title: 'Section 1', name: 'faq' }, [
p('Content for section 1')
]),
_accordion({ title: 'Section 2', name: 'faq' }, [
p('Content for section 2')
])
```
### `_drawer` - Sidebar Drawer
Responsive drawer component that can be toggled programmatically.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `id` | `string` | Unique ID for checkbox toggle |
| `$open` | `signal` | Boolean signal for drawer state |
| `content` | `HTMLElement` | Main content area |
| `side` | `HTMLElement` | Sidebar content |
**Example:**
```javascript
const $drawerOpen = $(false);
_drawer({
id: 'main-drawer',
$open: $drawerOpen,
content: [
_button({ onclick: () => $drawerOpen(true) }, 'Open Menu'),
div('Main content goes here')
],
side: [
_menu({ items: [
{ label: 'Home', onclick: () => $drawerOpen(false) },
{ label: 'Settings', onclick: () => $drawerOpen(false) }
]})
]
})
```
---
## Navigation Components
### `_navbar` - Application Header
Responsive navigation bar with built-in styling.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `class` | `string\|function` | Additional classes |
**Example:**
```javascript
_navbar([
div({ class: 'flex-1' }, [
a({ class: 'text-xl font-bold' }, 'MyApp')
]),
div({ class: 'flex-none' }, [
_button({ class: 'btn-ghost btn-sm' }, 'Login'),
_button({ class: 'btn-primary btn-sm' }, 'Sign Up')
])
])
```
### `_menu` - Vertical Navigation
Sidebar or dropdown menu with active state support.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `items` | `Array<{label: string, icon?: any, active?: boolean\|function, onclick: function}>` | Menu items |
**Example:**
```javascript
const $currentPage = $('home');
_menu({ items: [
{
label: 'Dashboard',
icon: '📊',
active: () => $currentPage() === 'dashboard',
onclick: () => $currentPage('dashboard')
},
{
label: 'Profile',
icon: '👤',
active: () => $currentPage() === 'profile',
onclick: () => $currentPage('profile')
},
{
label: 'Settings',
icon: '⚙️',
active: () => $currentPage() === 'settings',
onclick: () => $currentPage('settings')
}
]})
```
### `_tabs` - Tab Navigation
Horizontal tabs with lifted styling.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `items` | `Array<{label: string, active: boolean\|function, onclick: function}>` | Tab items |
**Example:**
```javascript
const $activeTab = $('profile');
_tabs({ items: [
{
label: 'Profile',
active: () => $activeTab() === 'profile',
onclick: () => $activeTab('profile')
},
{
label: 'Settings',
active: () => $activeTab() === 'settings',
onclick: () => $activeTab('settings')
}
]})
```
---
## Feedback Components
### `_badge` - Status Indicator
Small badge for counts, statuses, or labels.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `class` | `string\|function` | Badge style (badge-primary, badge-success, etc.) |
**Example:**
```javascript
_badge({ class: 'badge-success' }, 'Active')
_badge({ class: 'badge-error' }, '3 Errors')
_badge({ class: 'badge-warning' }, 'Pending')
```
### `_tooltip` - Hover Information
Wrapper that shows tooltip text on hover.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `tip` | `string` | Tooltip text |
| `position` | `string` | Tooltip position (top, bottom, left, right) |
**Example:**
```javascript
_tooltip({ tip: 'Click to save changes', class: 'tooltip-primary' }, [
_button({}, 'Save')
])
_tooltip({ tip: 'Your email will not be shared', class: 'tooltip-bottom' }, [
span('ⓘ')
])
```
---
## Container Components
### `_modal` - Dialog Window
Programmatically controlled modal dialog.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `$open` | `signal` | Boolean signal controlling visibility |
| `title` | `string` | Modal title |
| `class` | `string\|function` | Additional styling |
**Example:**
```javascript
const $showModal = $(false);
_modal({
$open: $showModal,
title: 'Confirm Action'
}, [
p('Are you sure you want to delete this item?'),
div({ class: 'flex gap-2 justify-end mt-4' }, [
_button({ onclick: () => $showModal(false) }, 'Cancel'),
_button({
class: 'btn-error',
onclick: () => {
deleteItem();
$showModal(false);
}
}, 'Delete')
])
])
// Trigger modal
_button({ onclick: () => $showModal(true) }, 'Delete Item')
```
### `_dropdown` - Context Menu
Dropdown menu that appears on click.
**Properties:**
| Property | Type | Description |
|----------|------|-------------|
| `label` | `string` | Dropdown trigger text |
| `class` | `string\|function` | Additional classes |
**Example:**
```javascript
_dropdown({ label: 'Options' }, [
li([a({ onclick: () => edit() }, 'Edit')]),
li([a({ onclick: () => duplicate() }, 'Duplicate')]),
li([a({ class: 'text-error', onclick: () => delete() }, 'Delete')])
])
```
---
## Complete Examples
### Example 1: User Registration Form
```javascript
// Signals
const $username = $('');
const $email = $('');
const $password = $('');
const $terms = $(false);
const $loading = $(false);
// Validation signals
const $usernameError = $(null);
const $emailError = $(null);
const $passwordError = $(null);
// Form submission
const handleSubmit = async () => {
$loading(true);
// Validate
if ($username().length < 3) $usernameError('Username too short');
if (!$email().includes('@')) $emailError('Invalid email');
if ($password().length < 6) $passwordError('Password too short');
if (!$terms()) alert('Accept terms');
if (!$usernameError() && !$emailError() && !$passwordError()) {
await api.register({
username: $username(),
email: $email(),
password: $password()
});
}
$loading(false);
};
// Component
div({ class: 'max-w-md mx-auto p-6' }, [
_fieldset({ legend: 'Create Account' }, [
_input({
label: 'Username',
$value: $username,
$error: $usernameError,
placeholder: 'johndoe'
}),
_input({
label: 'Email',
type: 'email',
$value: $email,
$error: $emailError,
placeholder: 'john@example.com'
}),
_input({
label: 'Password',
type: 'password',
$value: $password,
$error: $passwordError
}),
_checkbox({
label: 'I agree to the Terms of Service',
$value: $terms
}),
_button({
$loading: $loading,
class: 'btn-primary w-full mt-4',
onclick: handleSubmit
}, 'Sign Up')
])
])
```
### Example 2: Dashboard with Router Integration
```javascript
// App.js
export default () => {
const $activeRoute = $('dashboard');
return div({ class: 'min-h-screen' }, [
_navbar([
div({ class: 'flex-1' }, [
a({ class: 'text-xl font-bold' }, 'Dashboard')
]),
_button({
class: 'btn-ghost btn-circle',
onclick: () => $.router.go('/settings')
}, '⚙️')
]),
div({ class: 'flex' }, [
// Sidebar
div({ class: 'w-64 p-4' }, [
_menu({ items: [
{
label: 'Dashboard',
icon: '📊',
active: () => $activeRoute() === 'dashboard',
onclick: () => {
$activeRoute('dashboard');
$.router.go('/');
}
},
{
label: 'Analytics',
icon: '📈',
active: () => $activeRoute() === 'analytics',
onclick: () => {
$activeRoute('analytics');
$.router.go('/analytics');
}
},
{
label: 'Settings',
icon: '⚙️',
active: () => $activeRoute() === 'settings',
onclick: () => {
$activeRoute('settings');
$.router.go('/settings');
}
}
]})
]),
// Main content
div({ class: 'flex-1 p-6' }, [
$.router([
{ path: '/', component: () => DashboardComponent() },
{ path: '/analytics', component: () => AnalyticsComponent() },
{ path: '/settings', component: () => SettingsComponent() }
])
])
])
]);
};
```
### Example 3: E-commerce Product Card
```javascript
const ProductCard = ({ product }) => {
const $quantity = $(1);
const $inCart = $(false);
return div({ class: 'card bg-base-100 shadow-xl' }, [
figure([img({ src: product.image, alt: product.name })]),
div({ class: 'card-body' }, [
h2({ class: 'card-title' }, product.name),
p(product.description),
div({ class: 'flex justify-between items-center mt-4' }, [
span({ class: 'text-2xl font-bold' }, `$${product.price}`),
div({ class: 'flex gap-2' }, [
_range({
min: 1,
max: 10,
$value: $quantity,
class: 'w-32'
}),
_button({
$loading: $inCart,
class: 'btn-primary',
onclick: async () => {
$inCart(true);
await addToCart(product.id, $quantity());
$inCart(false);
}
}, 'Add to Cart')
])
])
])
]);
};
```
---
## Styling Guide
### Theme Configuration
DaisyUI v5 supports extensive theming. Configure in `tailwind.config.js` or CSS:
```css
/* app.css */
@import "tailwindcss";
@plugin "daisyui";
/* Custom theme */
[data-theme="corporate"] {
--color-primary: oklch(0.6 0.2 250);
--color-secondary: oklch(0.7 0.15 150);
--color-accent: oklch(0.8 0.1 50);
--color-neutral: oklch(0.3 0.01 260);
--color-base-100: oklch(0.98 0.01 260);
--color-info: oklch(0.65 0.2 220);
--color-success: oklch(0.65 0.2 140);
--color-warning: oklch(0.7 0.2 85);
--color-error: oklch(0.65 0.25 25);
}
```
### Component Modifiers
Each component accepts Tailwind/daisyUI classes:
```javascript
// Button variants
_button({ class: 'btn-primary' }, 'Primary')
_button({ class: 'btn-secondary' }, 'Secondary')
_button({ class: 'btn-accent' }, 'Accent')
_button({ class: 'btn-outline' }, 'Outline')
_button({ class: 'btn-ghost' }, 'Ghost')
_button({ class: 'btn-sm' }, 'Small')
_button({ class: 'btn-lg' }, 'Large')
_button({ class: 'btn-block' }, 'Full Width')
```
---
## Best Practices
### 1. Reactive Performance
Always use signals for values that change, not direct variable assignments:
```javascript
// ❌ Bad
let name = 'John';
_input({ $value: () => name }); // Won't update
// ✅ Good
const $name = $('John');
_input({ $value: $name });
```
### 2. Error Handling
Use `$error` signals with validation:
```javascript
const $error = $(null);
_input({
$error: $error,
onchange: (e) => {
if (!validate(e.target.value)) {
$error('Invalid input');
} else {
$error(null);
}
}
})
```
### 3. Modal Management
Keep modals conditionally rendered based on `$open`:
```javascript
// Modal only exists in DOM when open
_modal({ $open: $showModal }, content)
```
### 4. Form Submissions
Combine loading states with error handling:
```javascript
const $loading = $(false);
const $error = $(null);
_button({
$loading: $loading,
onclick: async () => {
$loading(true);
try {
await submit();
$error(null);
} catch (err) {
$error(err.message);
}
$loading(false);
}
}, 'Submit')
```
### 5. Component Composition
Build reusable components by combining UI primitives:
```javascript
const FormField = ({ label, $value, type = 'text' }) => {
return _fieldset({ legend: label }, [
_input({ type, $value, class: 'w-full' })
]);
};
// Usage
FormField({ label: 'Email', $value: $email });
```
---
## API Reference
All components are globally available after plugin initialization:
| Component | Function Signature |
|-----------|-------------------|
| `_button` | `(props, children) => HTMLElement` |
| `_input` | `(props) => HTMLElement` |
| `_select` | `(props) => HTMLElement` |
| `_checkbox` | `(props) => HTMLElement` |
| `_radio` | `(props) => HTMLElement` |
| `_range` | `(props) => HTMLElement` |
| `_fieldset` | `(props, children) => HTMLElement` |
| `_accordion` | `(props, children) => HTMLElement` |
| `_modal` | `(props, children) => HTMLElement` |
| `_drawer` | `(props) => HTMLElement` |
| `_navbar` | `(props, children) => HTMLElement` |
| `_menu` | `(props) => HTMLElement` |
| `_tabs` | `(props) => HTMLElement` |
| `_badge` | `(props, children) => HTMLElement` |
| `_tooltip` | `(props, children) => HTMLElement` |
| `_dropdown` | `(props, children) => HTMLElement` |
---
## Troubleshooting
### Styles Not Applying
Ensure Tailwind CSS is properly configured and imported before your app code:
```javascript
import './app.css'; // Must be first
import { $ } from 'sigpro';
```
### Components Not Found
Verify plugin is loaded before using components:
```javascript
$.plugin(UI).then(() => {
// Components are ready
$.mount(App);
});
```
### Reactive Updates Not Working
Ensure you're using signals, not primitive values:
```javascript
// Wrong
let count = 0;
_button({}, () => count)
// Correct
const $count = $(0);
_button({}, () => $count())
```

View File

@@ -95,29 +95,3 @@ $.plugin(ConfigLoader).then(() => {
| **Encapsulation** | Use the `$` instance passed as an argument rather than importing it again inside the plugin. |
| **Reactivity** | Always use `$(...)` for internal state so the app stays reactive. |
---
## 5. Installation
Custom plugins don't require extra packages, but ensure your build tool (Vite/Bun) is configured to handle the module imports.
::: code-group
```bash [NPM]
npm install sigpro
```
```bash [PNPM]
pnpm add sigpro
```
```bash [Yarn]
yarn add sigpro
```
```bash [Bun]
bun add sigpro
```
:::

View File

@@ -33,11 +33,11 @@ This is the standard way to build apps. It's clean, readable, and supports stand
```javascript
// main.js
import { $ } from 'sigpro';
import { UI } from 'sigpro/plugins';
import { Fetch } from 'sigpro/plugins';
import App from './App.js'; // Static import works perfectly!
// 1. Register plugins
$.plugin(UI);
$.plugin(Fetch);
// 2. Mount your app directly
$.mount(App, '#app');

View File

@@ -114,24 +114,3 @@ export const routes = [
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]
npm install sigpro
```
```bash [PNPM]
pnpm add sigpro
```
```bash [Yarn]
yarn add sigpro
```
```bash [Bun]
bun add sigpro
```
:::