This commit is contained in:
2026-03-22 00:44:55 +01:00
parent 443315aae4
commit 7c400ad227
113 changed files with 31773 additions and 998 deletions

View File

@@ -0,0 +1,275 @@
import {
useMediaQuery
} from "./chunk-RLEUDPPB.js";
import {
computed,
ref,
shallowRef,
watch
} from "./chunk-3S55Y3P7.js";
// node_modules/vitepress/dist/client/theme-default/index.js
import "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/styles/fonts.css";
// node_modules/vitepress/dist/client/theme-default/without-fonts.js
import "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/styles/vars.css";
import "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/styles/base.css";
import "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/styles/icons.css";
import "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/styles/utils.css";
import "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/styles/components/custom-block.css";
import "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/styles/components/vp-code.css";
import "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/styles/components/vp-code-group.css";
import "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/styles/components/vp-doc.css";
import "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/styles/components/vp-sponsor.css";
import VPBadge from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPBadge.vue";
import Layout from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/Layout.vue";
import { default as default2 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPBadge.vue";
import { default as default3 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPButton.vue";
import { default as default4 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPDocAsideSponsors.vue";
import { default as default5 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPFeatures.vue";
import { default as default6 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPHomeContent.vue";
import { default as default7 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPHomeFeatures.vue";
import { default as default8 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPHomeHero.vue";
import { default as default9 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPHomeSponsors.vue";
import { default as default10 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPImage.vue";
import { default as default11 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPLink.vue";
import { default as default12 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPNavBarSearch.vue";
import { default as default13 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPSocialLink.vue";
import { default as default14 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPSocialLinks.vue";
import { default as default15 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPSponsors.vue";
import { default as default16 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPTeamMembers.vue";
import { default as default17 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPTeamPage.vue";
import { default as default18 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPTeamPageSection.vue";
import { default as default19 } from "/config/workspace/sigpro/node_modules/vitepress/dist/client/theme-default/components/VPTeamPageTitle.vue";
// node_modules/vitepress/dist/client/theme-default/composables/local-nav.js
import { onContentUpdated } from "vitepress";
// node_modules/vitepress/dist/client/theme-default/composables/outline.js
import { getScrollOffset } from "vitepress";
// node_modules/vitepress/dist/client/theme-default/support/utils.js
import { withBase } from "vitepress";
// node_modules/vitepress/dist/client/theme-default/composables/data.js
import { useData as useData$ } from "vitepress";
var useData = useData$;
// node_modules/vitepress/dist/client/theme-default/support/utils.js
function ensureStartingSlash(path) {
return path.startsWith("/") ? path : `/${path}`;
}
// node_modules/vitepress/dist/client/theme-default/support/sidebar.js
function getSidebar(_sidebar, path) {
if (Array.isArray(_sidebar))
return addBase(_sidebar);
if (_sidebar == null)
return [];
path = ensureStartingSlash(path);
const dir = Object.keys(_sidebar).sort((a, b) => {
return b.split("/").length - a.split("/").length;
}).find((dir2) => {
return path.startsWith(ensureStartingSlash(dir2));
});
const sidebar = dir ? _sidebar[dir] : [];
return Array.isArray(sidebar) ? addBase(sidebar) : addBase(sidebar.items, sidebar.base);
}
function getSidebarGroups(sidebar) {
const groups = [];
let lastGroupIndex = 0;
for (const index in sidebar) {
const item = sidebar[index];
if (item.items) {
lastGroupIndex = groups.push(item);
continue;
}
if (!groups[lastGroupIndex]) {
groups.push({ items: [] });
}
groups[lastGroupIndex].items.push(item);
}
return groups;
}
function addBase(items, _base) {
return [...items].map((_item) => {
const item = { ..._item };
const base = item.base || _base;
if (base && item.link)
item.link = base + item.link;
if (item.items)
item.items = addBase(item.items, base);
return item;
});
}
// node_modules/vitepress/dist/client/theme-default/composables/sidebar.js
function useSidebar() {
const { frontmatter, page, theme: theme2 } = useData();
const is960 = useMediaQuery("(min-width: 960px)");
const isOpen = ref(false);
const _sidebar = computed(() => {
const sidebarConfig = theme2.value.sidebar;
const relativePath = page.value.relativePath;
return sidebarConfig ? getSidebar(sidebarConfig, relativePath) : [];
});
const sidebar = ref(_sidebar.value);
watch(_sidebar, (next, prev) => {
if (JSON.stringify(next) !== JSON.stringify(prev))
sidebar.value = _sidebar.value;
});
const hasSidebar = computed(() => {
return frontmatter.value.sidebar !== false && sidebar.value.length > 0 && frontmatter.value.layout !== "home";
});
const leftAside = computed(() => {
if (hasAside)
return frontmatter.value.aside == null ? theme2.value.aside === "left" : frontmatter.value.aside === "left";
return false;
});
const hasAside = computed(() => {
if (frontmatter.value.layout === "home")
return false;
if (frontmatter.value.aside != null)
return !!frontmatter.value.aside;
return theme2.value.aside !== false;
});
const isSidebarEnabled = computed(() => hasSidebar.value && is960.value);
const sidebarGroups = computed(() => {
return hasSidebar.value ? getSidebarGroups(sidebar.value) : [];
});
function open() {
isOpen.value = true;
}
function close() {
isOpen.value = false;
}
function toggle() {
isOpen.value ? close() : open();
}
return {
isOpen,
sidebar,
sidebarGroups,
hasSidebar,
hasAside,
leftAside,
isSidebarEnabled,
open,
close,
toggle
};
}
// node_modules/vitepress/dist/client/theme-default/composables/outline.js
var ignoreRE = /\b(?:VPBadge|header-anchor|footnote-ref|ignore-header)\b/;
var resolvedHeaders = [];
function getHeaders(range) {
const headers = [
...document.querySelectorAll(".VPDoc :where(h1,h2,h3,h4,h5,h6)")
].filter((el) => el.id && el.hasChildNodes()).map((el) => {
const level = Number(el.tagName[1]);
return {
element: el,
title: serializeHeader(el),
link: "#" + el.id,
level
};
});
return resolveHeaders(headers, range);
}
function serializeHeader(h) {
let ret = "";
for (const node of h.childNodes) {
if (node.nodeType === 1) {
if (ignoreRE.test(node.className))
continue;
ret += node.textContent;
} else if (node.nodeType === 3) {
ret += node.textContent;
}
}
return ret.trim();
}
function resolveHeaders(headers, range) {
if (range === false) {
return [];
}
const levelsRange = (typeof range === "object" && !Array.isArray(range) ? range.level : range) || 2;
const [high, low] = typeof levelsRange === "number" ? [levelsRange, levelsRange] : levelsRange === "deep" ? [2, 6] : levelsRange;
return buildTree(headers, high, low);
}
function buildTree(data, min, max) {
resolvedHeaders.length = 0;
const result = [];
const stack = [];
data.forEach((item) => {
const node = { ...item, children: [] };
let parent = stack[stack.length - 1];
while (parent && parent.level >= node.level) {
stack.pop();
parent = stack[stack.length - 1];
}
if (node.element.classList.contains("ignore-header") || parent && "shouldIgnore" in parent) {
stack.push({ level: node.level, shouldIgnore: true });
return;
}
if (node.level > max || node.level < min)
return;
resolvedHeaders.push({ element: node.element, link: node.link });
if (parent)
parent.children.push(node);
else
result.push(node);
stack.push(node);
});
return result;
}
// node_modules/vitepress/dist/client/theme-default/composables/local-nav.js
function useLocalNav() {
const { theme: theme2, frontmatter } = useData();
const headers = shallowRef([]);
const hasLocalNav = computed(() => {
return headers.value.length > 0;
});
onContentUpdated(() => {
headers.value = getHeaders(frontmatter.value.outline ?? theme2.value.outline);
});
return {
headers,
hasLocalNav
};
}
// node_modules/vitepress/dist/client/theme-default/without-fonts.js
var theme = {
Layout,
enhanceApp: ({ app }) => {
app.component("Badge", VPBadge);
}
};
var without_fonts_default = theme;
export {
default2 as VPBadge,
default3 as VPButton,
default4 as VPDocAsideSponsors,
default5 as VPFeatures,
default6 as VPHomeContent,
default7 as VPHomeFeatures,
default8 as VPHomeHero,
default9 as VPHomeSponsors,
default10 as VPImage,
default11 as VPLink,
default12 as VPNavBarSearch,
default13 as VPSocialLink,
default14 as VPSocialLinks,
default15 as VPSponsors,
default16 as VPTeamMembers,
default17 as VPTeamPage,
default18 as VPTeamPageSection,
default19 as VPTeamPageTitle,
without_fonts_default as default,
useLocalNav,
useSidebar
};
//# sourceMappingURL=@theme_index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,40 @@
{
"hash": "0412844f",
"configHash": "5a0057cf",
"lockfileHash": "e3b0c442",
"browserHash": "d3515516",
"optimized": {
"vue": {
"src": "../../../../../node_modules/vue/dist/vue.runtime.esm-bundler.js",
"file": "vue.js",
"fileHash": "5e2bcecf",
"needsInterop": false
},
"vitepress > @vue/devtools-api": {
"src": "../../../../../node_modules/@vue/devtools-api/dist/index.js",
"file": "vitepress___@vue_devtools-api.js",
"fileHash": "604942f7",
"needsInterop": false
},
"vitepress > @vueuse/core": {
"src": "../../../../../node_modules/@vueuse/core/index.mjs",
"file": "vitepress___@vueuse_core.js",
"fileHash": "f08e5a15",
"needsInterop": false
},
"@theme/index": {
"src": "../../../../../node_modules/vitepress/dist/client/theme-default/index.js",
"file": "@theme_index.js",
"fileHash": "442c9e5b",
"needsInterop": false
}
},
"chunks": {
"chunk-RLEUDPPB": {
"file": "chunk-RLEUDPPB.js"
},
"chunk-3S55Y3P7": {
"file": "chunk-3S55Y3P7.js"
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,583 @@
import {
DefaultMagicKeysAliasMap,
StorageSerializers,
TransitionPresets,
assert,
breakpointsAntDesign,
breakpointsBootstrapV5,
breakpointsElement,
breakpointsMasterCss,
breakpointsPrimeFlex,
breakpointsQuasar,
breakpointsSematic,
breakpointsTailwind,
breakpointsVuetify,
breakpointsVuetifyV2,
breakpointsVuetifyV3,
bypassFilter,
camelize,
clamp,
cloneFnJSON,
computedAsync,
computedEager,
computedInject,
computedWithControl,
containsProp,
controlledRef,
createEventHook,
createFetch,
createFilterWrapper,
createGlobalState,
createInjectionState,
createRef,
createReusableTemplate,
createSharedComposable,
createSingletonPromise,
createTemplatePromise,
createUnrefFn,
customStorageEventName,
debounceFilter,
defaultDocument,
defaultLocation,
defaultNavigator,
defaultWindow,
executeTransition,
extendRef,
formatDate,
formatTimeAgo,
get,
getLifeCycleTarget,
getSSRHandler,
hasOwn,
hyphenate,
identity,
increaseWithUnit,
injectLocal,
invoke,
isClient,
isDef,
isDefined,
isIOS,
isObject,
isWorker,
makeDestructurable,
mapGamepadToXbox360Controller,
noop,
normalizeDate,
notNullish,
now,
objectEntries,
objectOmit,
objectPick,
onClickOutside,
onElementRemoval,
onKeyDown,
onKeyPressed,
onKeyStroke,
onKeyUp,
onLongPress,
onStartTyping,
pausableFilter,
promiseTimeout,
provideLocal,
provideSSRWidth,
pxValue,
rand,
reactify,
reactifyObject,
reactiveComputed,
reactiveOmit,
reactivePick,
refAutoReset,
refDebounced,
refDefault,
refThrottled,
refWithControl,
resolveRef,
resolveUnref,
set,
setSSRHandler,
syncRef,
syncRefs,
templateRef,
throttleFilter,
timestamp,
toArray,
toReactive,
toRef,
toRefs,
toValue,
tryOnBeforeMount,
tryOnBeforeUnmount,
tryOnMounted,
tryOnScopeDispose,
tryOnUnmounted,
unrefElement,
until,
useActiveElement,
useAnimate,
useArrayDifference,
useArrayEvery,
useArrayFilter,
useArrayFind,
useArrayFindIndex,
useArrayFindLast,
useArrayIncludes,
useArrayJoin,
useArrayMap,
useArrayReduce,
useArraySome,
useArrayUnique,
useAsyncQueue,
useAsyncState,
useBase64,
useBattery,
useBluetooth,
useBreakpoints,
useBroadcastChannel,
useBrowserLocation,
useCached,
useClipboard,
useClipboardItems,
useCloned,
useColorMode,
useConfirmDialog,
useCountdown,
useCounter,
useCssVar,
useCurrentElement,
useCycleList,
useDark,
useDateFormat,
useDebounceFn,
useDebouncedRefHistory,
useDeviceMotion,
useDeviceOrientation,
useDevicePixelRatio,
useDevicesList,
useDisplayMedia,
useDocumentVisibility,
useDraggable,
useDropZone,
useElementBounding,
useElementByPoint,
useElementHover,
useElementSize,
useElementVisibility,
useEventBus,
useEventListener,
useEventSource,
useEyeDropper,
useFavicon,
useFetch,
useFileDialog,
useFileSystemAccess,
useFocus,
useFocusWithin,
useFps,
useFullscreen,
useGamepad,
useGeolocation,
useIdle,
useImage,
useInfiniteScroll,
useIntersectionObserver,
useInterval,
useIntervalFn,
useKeyModifier,
useLastChanged,
useLocalStorage,
useMagicKeys,
useManualRefHistory,
useMediaControls,
useMediaQuery,
useMemoize,
useMemory,
useMounted,
useMouse,
useMouseInElement,
useMousePressed,
useMutationObserver,
useNavigatorLanguage,
useNetwork,
useNow,
useObjectUrl,
useOffsetPagination,
useOnline,
usePageLeave,
useParallax,
useParentElement,
usePerformanceObserver,
usePermission,
usePointer,
usePointerLock,
usePointerSwipe,
usePreferredColorScheme,
usePreferredContrast,
usePreferredDark,
usePreferredLanguages,
usePreferredReducedMotion,
usePreferredReducedTransparency,
usePrevious,
useRafFn,
useRefHistory,
useResizeObserver,
useSSRWidth,
useScreenOrientation,
useScreenSafeArea,
useScriptTag,
useScroll,
useScrollLock,
useSessionStorage,
useShare,
useSorted,
useSpeechRecognition,
useSpeechSynthesis,
useStepper,
useStorage,
useStorageAsync,
useStyleTag,
useSupported,
useSwipe,
useTemplateRefsList,
useTextDirection,
useTextSelection,
useTextareaAutosize,
useThrottleFn,
useThrottledRefHistory,
useTimeAgo,
useTimeout,
useTimeoutFn,
useTimeoutPoll,
useTimestamp,
useTitle,
useToNumber,
useToString,
useToggle,
useTransition,
useUrlSearchParams,
useUserMedia,
useVModel,
useVModels,
useVibrate,
useVirtualList,
useWakeLock,
useWebNotification,
useWebSocket,
useWebWorker,
useWebWorkerFn,
useWindowFocus,
useWindowScroll,
useWindowSize,
watchArray,
watchAtMost,
watchDebounced,
watchDeep,
watchIgnorable,
watchImmediate,
watchOnce,
watchPausable,
watchThrottled,
watchTriggerable,
watchWithFilter,
whenever
} from "./chunk-RLEUDPPB.js";
import "./chunk-3S55Y3P7.js";
export {
DefaultMagicKeysAliasMap,
StorageSerializers,
TransitionPresets,
assert,
computedAsync as asyncComputed,
refAutoReset as autoResetRef,
breakpointsAntDesign,
breakpointsBootstrapV5,
breakpointsElement,
breakpointsMasterCss,
breakpointsPrimeFlex,
breakpointsQuasar,
breakpointsSematic,
breakpointsTailwind,
breakpointsVuetify,
breakpointsVuetifyV2,
breakpointsVuetifyV3,
bypassFilter,
camelize,
clamp,
cloneFnJSON,
computedAsync,
computedEager,
computedInject,
computedWithControl,
containsProp,
computedWithControl as controlledComputed,
controlledRef,
createEventHook,
createFetch,
createFilterWrapper,
createGlobalState,
createInjectionState,
reactify as createReactiveFn,
createRef,
createReusableTemplate,
createSharedComposable,
createSingletonPromise,
createTemplatePromise,
createUnrefFn,
customStorageEventName,
debounceFilter,
refDebounced as debouncedRef,
watchDebounced as debouncedWatch,
defaultDocument,
defaultLocation,
defaultNavigator,
defaultWindow,
computedEager as eagerComputed,
executeTransition,
extendRef,
formatDate,
formatTimeAgo,
get,
getLifeCycleTarget,
getSSRHandler,
hasOwn,
hyphenate,
identity,
watchIgnorable as ignorableWatch,
increaseWithUnit,
injectLocal,
invoke,
isClient,
isDef,
isDefined,
isIOS,
isObject,
isWorker,
makeDestructurable,
mapGamepadToXbox360Controller,
noop,
normalizeDate,
notNullish,
now,
objectEntries,
objectOmit,
objectPick,
onClickOutside,
onElementRemoval,
onKeyDown,
onKeyPressed,
onKeyStroke,
onKeyUp,
onLongPress,
onStartTyping,
pausableFilter,
watchPausable as pausableWatch,
promiseTimeout,
provideLocal,
provideSSRWidth,
pxValue,
rand,
reactify,
reactifyObject,
reactiveComputed,
reactiveOmit,
reactivePick,
refAutoReset,
refDebounced,
refDefault,
refThrottled,
refWithControl,
resolveRef,
resolveUnref,
set,
setSSRHandler,
syncRef,
syncRefs,
templateRef,
throttleFilter,
refThrottled as throttledRef,
watchThrottled as throttledWatch,
timestamp,
toArray,
toReactive,
toRef,
toRefs,
toValue,
tryOnBeforeMount,
tryOnBeforeUnmount,
tryOnMounted,
tryOnScopeDispose,
tryOnUnmounted,
unrefElement,
until,
useActiveElement,
useAnimate,
useArrayDifference,
useArrayEvery,
useArrayFilter,
useArrayFind,
useArrayFindIndex,
useArrayFindLast,
useArrayIncludes,
useArrayJoin,
useArrayMap,
useArrayReduce,
useArraySome,
useArrayUnique,
useAsyncQueue,
useAsyncState,
useBase64,
useBattery,
useBluetooth,
useBreakpoints,
useBroadcastChannel,
useBrowserLocation,
useCached,
useClipboard,
useClipboardItems,
useCloned,
useColorMode,
useConfirmDialog,
useCountdown,
useCounter,
useCssVar,
useCurrentElement,
useCycleList,
useDark,
useDateFormat,
refDebounced as useDebounce,
useDebounceFn,
useDebouncedRefHistory,
useDeviceMotion,
useDeviceOrientation,
useDevicePixelRatio,
useDevicesList,
useDisplayMedia,
useDocumentVisibility,
useDraggable,
useDropZone,
useElementBounding,
useElementByPoint,
useElementHover,
useElementSize,
useElementVisibility,
useEventBus,
useEventListener,
useEventSource,
useEyeDropper,
useFavicon,
useFetch,
useFileDialog,
useFileSystemAccess,
useFocus,
useFocusWithin,
useFps,
useFullscreen,
useGamepad,
useGeolocation,
useIdle,
useImage,
useInfiniteScroll,
useIntersectionObserver,
useInterval,
useIntervalFn,
useKeyModifier,
useLastChanged,
useLocalStorage,
useMagicKeys,
useManualRefHistory,
useMediaControls,
useMediaQuery,
useMemoize,
useMemory,
useMounted,
useMouse,
useMouseInElement,
useMousePressed,
useMutationObserver,
useNavigatorLanguage,
useNetwork,
useNow,
useObjectUrl,
useOffsetPagination,
useOnline,
usePageLeave,
useParallax,
useParentElement,
usePerformanceObserver,
usePermission,
usePointer,
usePointerLock,
usePointerSwipe,
usePreferredColorScheme,
usePreferredContrast,
usePreferredDark,
usePreferredLanguages,
usePreferredReducedMotion,
usePreferredReducedTransparency,
usePrevious,
useRafFn,
useRefHistory,
useResizeObserver,
useSSRWidth,
useScreenOrientation,
useScreenSafeArea,
useScriptTag,
useScroll,
useScrollLock,
useSessionStorage,
useShare,
useSorted,
useSpeechRecognition,
useSpeechSynthesis,
useStepper,
useStorage,
useStorageAsync,
useStyleTag,
useSupported,
useSwipe,
useTemplateRefsList,
useTextDirection,
useTextSelection,
useTextareaAutosize,
refThrottled as useThrottle,
useThrottleFn,
useThrottledRefHistory,
useTimeAgo,
useTimeout,
useTimeoutFn,
useTimeoutPoll,
useTimestamp,
useTitle,
useToNumber,
useToString,
useToggle,
useTransition,
useUrlSearchParams,
useUserMedia,
useVModel,
useVModels,
useVibrate,
useVirtualList,
useWakeLock,
useWebNotification,
useWebSocket,
useWebWorker,
useWebWorkerFn,
useWindowFocus,
useWindowScroll,
useWindowSize,
watchArray,
watchAtMost,
watchDebounced,
watchDeep,
watchIgnorable,
watchImmediate,
watchOnce,
watchPausable,
watchThrottled,
watchTriggerable,
watchWithFilter,
whenever
};
//# sourceMappingURL=vitepress___@vueuse_core.js.map

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}

347
src/docs/.vitepress/cache/deps/vue.js vendored Normal file
View File

@@ -0,0 +1,347 @@
import {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBaseVNode,
createBlock,
createCommentVNode,
createElementBlock,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
nodeOps,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
patchProp,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
} from "./chunk-3S55Y3P7.js";
export {
BaseTransition,
BaseTransitionPropsValidators,
Comment,
DeprecationTypes,
EffectScope,
ErrorCodes,
ErrorTypeStrings,
Fragment,
KeepAlive,
ReactiveEffect,
Static,
Suspense,
Teleport,
Text,
TrackOpTypes,
Transition,
TransitionGroup,
TriggerOpTypes,
VueElement,
assertNumber,
callWithAsyncErrorHandling,
callWithErrorHandling,
camelize,
capitalize,
cloneVNode,
compatUtils,
compile,
computed,
createApp,
createBlock,
createCommentVNode,
createElementBlock,
createBaseVNode as createElementVNode,
createHydrationRenderer,
createPropsRestProxy,
createRenderer,
createSSRApp,
createSlots,
createStaticVNode,
createTextVNode,
createVNode,
customRef,
defineAsyncComponent,
defineComponent,
defineCustomElement,
defineEmits,
defineExpose,
defineModel,
defineOptions,
defineProps,
defineSSRCustomElement,
defineSlots,
devtools,
effect,
effectScope,
getCurrentInstance,
getCurrentScope,
getCurrentWatcher,
getTransitionRawChildren,
guardReactiveProps,
h,
handleError,
hasInjectionContext,
hydrate,
hydrateOnIdle,
hydrateOnInteraction,
hydrateOnMediaQuery,
hydrateOnVisible,
initCustomFormatter,
initDirectivesForSSR,
inject,
isMemoSame,
isProxy,
isReactive,
isReadonly,
isRef,
isRuntimeOnly,
isShallow,
isVNode,
markRaw,
mergeDefaults,
mergeModels,
mergeProps,
nextTick,
nodeOps,
normalizeClass,
normalizeProps,
normalizeStyle,
onActivated,
onBeforeMount,
onBeforeUnmount,
onBeforeUpdate,
onDeactivated,
onErrorCaptured,
onMounted,
onRenderTracked,
onRenderTriggered,
onScopeDispose,
onServerPrefetch,
onUnmounted,
onUpdated,
onWatcherCleanup,
openBlock,
patchProp,
popScopeId,
provide,
proxyRefs,
pushScopeId,
queuePostFlushCb,
reactive,
readonly,
ref,
registerRuntimeCompiler,
render,
renderList,
renderSlot,
resolveComponent,
resolveDirective,
resolveDynamicComponent,
resolveFilter,
resolveTransitionHooks,
setBlockTracking,
setDevtoolsHook,
setTransitionHooks,
shallowReactive,
shallowReadonly,
shallowRef,
ssrContextKey,
ssrUtils,
stop,
toDisplayString,
toHandlerKey,
toHandlers,
toRaw,
toRef,
toRefs,
toValue,
transformVNodeArgs,
triggerRef,
unref,
useAttrs,
useCssModule,
useCssVars,
useHost,
useId,
useModel,
useSSRContext,
useShadowRoot,
useSlots,
useTemplateRef,
useTransitionState,
vModelCheckbox,
vModelDynamic,
vModelRadio,
vModelSelect,
vModelText,
vShow,
version,
warn,
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
withAsyncContext,
withCtx,
withDefaults,
withDirectives,
withKeys,
withMemo,
withModifiers,
withScopeId
};
//# sourceMappingURL=vue.js.map

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": [],
"sourcesContent": [],
"mappings": "",
"names": []
}

View File

@@ -38,13 +38,22 @@ export default defineConfig({
text: 'API Reference',
items: [
{ text: 'Quick Start', link: '/api/quick' },
{ text: 'Signals', link: '/api/signals' },
{ text: 'Effects', link: '/api/effects' },
{ text: 'Storage', link: '/api/storage' },
{ text: 'Fetch', link: '/api/fetch' },
{ text: 'Pages', link: '/api/pages' },
{ text: 'Components', link: '/api/components' },
{ text: 'Routing', link: '/api/routing' },
{ text: '$', link: '/api/$' },
{ text: '$.html', link: '/api/html' },
{ text: '$.mount', link: '/api/mount' },
{ text: 'Tags', link: '/api/tags' },
]
},
{
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: 'Custom', link: '/plugins/custom' },
]
},
{

101
src/docs/api/$.md Normal file
View File

@@ -0,0 +1,101 @@
# 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.
## 1. The Constructor: `$( input )`
Depending on what you pass into `$( )`, SigPro creates a different type of reactive primitive:
| 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. |
---
## 2. Signal (State)
A **Signal** is a "box" that holds a value. It provides a getter/setter function to interact with that value.
* **When to use:** For data that changes over time (counters, user input, toggle states, API data).
* **Syntax:** `const $state = $(initialValue);`
### Example:
```javascript
const $name = $("Alice");
// Read the value (Getter)
console.log($name()); // "Alice"
// Update the value (Setter)
$name("Bob");
// Update based on previous value
$name(current => current + " Smith");
```
---
## 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 to use:** For values that depend on other signals (totals, filtered lists, formatted strings).
* **Syntax:** `const $derived = $(() => logic);`
### Example:
```javascript
const $price = $(100);
const $qty = $(2);
// Automatically tracks $price and $qty
const $total = $(() => $price() * $qty());
console.log($total()); // 200
$qty(3); // $total updates to 300 automatically
```
---
## 4. Effects (Side Effects)
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.
* **When to use:** For DOM manipulations, logging, or syncing with external APIs (LocalStorage, Fetch).
* **Syntax:** `$(() => { action });`
### Example:
```javascript
const $theme = $("light");
// This effect runs every time $theme changes
$(() => {
document.body.className = $theme();
console.log("Theme updated to:", $theme());
});
$theme("dark"); // Logs: Theme updated to: dark
```
---
## 5. Summary Table: Usage Guide
| Primitive | Logic Type | Returns Value? | 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()))` |
---
## 💡 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:
```javascript
let count = 0; // Static
const $count = $(0); // Reactive (Function)
```

103
src/docs/api/html.md Normal file
View File

@@ -0,0 +1,103 @@
# Rendering Engine: `$.html`
The `$.html` function is the architect of your UI. It creates standard HTML elements and wires them directly to your signals without the need for a Virtual DOM.
## 1. Syntax: `$.html(tag, [props], [content])`
| Parameter | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **tag** | `string` | **Yes** | Any valid HTML5 tag (e.g., `'div'`, `'button'`, `'input'`). |
| **props** | `Object` | No | Attributes, event listeners, and reactive bindings. |
| **content** | `any` | No | Text, Nodes, Arrays, or Reactive Functions. |
### Example:
```javascript
const myButton = $.html('button', { class: 'btn-primary' }, 'Click me');
```
---
## 2. Global Tag Helpers
To avoid repetitive `$.html` calls, SigPro automatically exposes common tags to the global `window` object. This allows for a clean, declarative syntax.
```javascript
// Instead of $.html('div', ...), just use:
div({ id: 'wrapper' }, [
h1("Welcome"),
p("This is SigPro.")
]);
```
---
## 3. Handling Properties & Attributes
SigPro distinguishes between static attributes and reactive bindings using the **`$` prefix**.
### Static vs. Reactive Attributes
* **Static:** Applied once during creation.
* **Reactive (`$`):** Automatically updates the DOM when the signal changes.
| Property | Syntax | Result |
| :--- | :--- | :--- |
| **Attribute** | `{ id: 'main' }` | `id="main"` |
| **Event** | `{ onclick: fn }` | Adds an event listener. |
| **Reactive Attr** | `{ $class: $theme }` | Updates `class` whenever `$theme()` changes. |
| **Boolean Attr** | `{ $disabled: $isBusy }` | Toggles the `disabled` attribute automatically. |
---
## 4. Two-Way Data Binding
For form inputs, SigPro provides a powerful shortcut using `$value` or `$checked`. It automatically handles the event listening and the value synchronization.
```javascript
const $text = $("Type here...");
input({
type: 'text',
$value: $text // Syncs input -> signal and signal -> input
});
p(["You typed: ", $text]);
```
---
## 5. Reactive Content (Dynamic Children)
The `content` argument is incredibly flexible. If you pass a **function**, SigPro treats it as a reactive "portal" that re-renders only that specific part of the DOM.
### Text & Nodes
```javascript
const $count = $(0);
// Text node updates surgically
div(["Count: ", $count]);
// Conditional rendering with a function
div(() => {
return $count() > 10
? h1("High Score!")
: p("Keep going...");
});
```
### The "Guillotine" (Performance Tip)
When a reactive function in the content returns a **new Node**, SigPro uses `replaceWith()` to swap the old node for the new one. This ensures that:
1. The update is nearly instantaneous.
2. The old node is correctly garbage-collected.
---
## 6. Summary: Content Types
| Input | Behavior |
| :--- | :--- |
| **String / Number** | Appended as a TextNode. |
| **HTMLElement** | Appended directly to the parent. |
| **Array** | Each item is processed and appended in order. |
| **Function `() => ...`** | Creates a **live reactive zone** that updates automatically. |

108
src/docs/api/mount.md Normal file
View File

@@ -0,0 +1,108 @@
# 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.
## 1. Syntax: `$.mount(node, [target])`
| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| **node** | `HTMLElement` or `Function` | **Required** | The component or element to render. |
| **target** | `string` or `HTMLElement` | `document.body` | Where to mount the app (CSS selector or Element). |
---
## 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.
```javascript
// src/main.js
import { $ } from 'SigPro';
import App from './App.js';
$.mount(App); // Mounts to <body> by default
```
### 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.
```html
<div id="sidebar"></div>
<div id="app-root"></div>
```
```javascript
// Local mount to a specific ID
$.mount(MyComponent, '#app-root');
// Or using a direct DOM reference
const sidebar = document.getElementById('sidebar');
$.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.
```javascript
// A small reactive widget in a static .js file
const CounterWidget = () => {
const $c = $(0);
return button({ onclick: () => $c(v => v + 1) }, [
"Clicks: ", $c
]);
};
// Mount it into an existing div in your 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.
---
## 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.
```javascript
// main.js - Global Initialization
import 'SigPro';
// Now any other file can just 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.
```javascript
// widget.js - Local usage
import { $ } from 'SigPro';
const myNode = $.html('div', 'Local Widget');
$.mount(myNode, '#widget-target');
```
---
### 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')` |

99
src/docs/api/quick.md Normal file
View File

@@ -0,0 +1,99 @@
# 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.
## 1. Core Reactivity: `$( )`
The `$` function is a polymorphic constructor. It creates **Signals** (state) or **Computed Effects** (logic) based on the input type.
| 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:**
```javascript
const $count = $(0); // Signal
const $double = $(() => $count() * 2); // Computed
```
---
## 2. Rendering Engine: `$.html`
SigPro uses a hyperscript-style engine to create live DOM nodes.
| 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. |
**Example:**
```javascript
$.html('button', { onclick: () => alert('Hi!') }, 'Click Me');
```
---
## 3. Global Helpers (Tag Proxies)
To keep your code clean, SigPro automatically exposes common HTML tags to the global scope.
| 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:**
```javascript
// No imports needed!
div([
h1("Title"),
button("Ok")
]);
```
---
## 4. Mounting & Plugins
Methods to initialize your application and extend the engine.
| 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. |
**Example:**
```javascript
$.plugin([UI, Router]);
$.mount(App, '#root');
```
---
## 5. Reactive Syntax Cheat Sheet
| 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. |
---
## Summary Table
| 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. |

160
src/docs/api/tags.md Normal file
View File

@@ -0,0 +1,160 @@
# 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.
## 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.
* **Traditional:** `$.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
The following tags are available globally by default:
| Category | Available 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` |
---
## 3. Usage Patterns
The tag functions are highly flexible and accept arguments in different orders to suit your coding style.
### A. Attributes + Content
The most common pattern.
```javascript
div({ class: 'card' }, [
h1("Title"),
p("Description")
]);
```
### B. Content Only
If you don't need attributes, you can skip the object entirely.
```javascript
div([
h1("Just Content"),
p("No attributes object needed here.")
]);
```
### C. Simple Text
For elements that only contain a string.
```javascript
button("Submit"); // Equivalent to <button>Submit</button>
```
---
## 4. Reactive Tags
Since these helpers are just wrappers around `$.html`, they support full reactivity out of the box.
```javascript
const $loading = $(true);
div([
$loading() ? span("Loading...") : h1("Data Ready!"),
button({
$disabled: $loading, // Reactive attribute
onclick: () => $loading(false)
}, "Stop Loading")
]);
```
---
## 5. Under the Hood
If you are curious about how this happens without a compiler, here is the logic inside the SigPro core:
```javascript
const tags = ['div', 'span', 'p', 'button', ...];
tags.forEach(tag => {
window[tag] = (props, content) => $.html(tag, props, content);
});
```
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.")
]);
```
---
## 6. Full Comparison: SigPro vs. Standard HTML
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.
::: 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")
])
);
```
```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>
```
:::
### 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', { ... })`.

View File

@@ -1,308 +1,76 @@
# Getting Started with SigPro 🚀
# Getting Started
Welcome to SigPro! This guide will help you get up and running with the library in minutes. SigPro is a minimalist reactive library that embraces the web platform - no compilation, no virtual DOM, just pure JavaScript and intelligent reactivity.
**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.
## 📦 Installation
## 1. Installation
Choose your preferred installation method:
You can install SigPro via your favorite package manager:
```bash
# Using npm
npm install sigpro
::: code-group
```bash [npm]
npm install SigPro
````
# Using bun
bun add sigpro
# Or simply copy sigpro.js to your project
# (yes, it's that simple!)
```bash [pnpm]
pnpm add SigPro
```
## 🎯 Core Imports
```javascript
import { $, html } from 'sigpro';
```bash [yarn]
yarn add SigPro
```
That's it! Just two imports to unlock the entire reactive system:
- **`$`** - Creates reactive signals (the heart of reactivity)
- **`html`** - Template literal tag for reactive DOM rendering
## 🧠 Understanding the Basics
### Signals - The Reactive Heart
Signals are reactive values that automatically track dependencies and update when changed:
```javascript
// Create a signal with initial value
const count = $(0);
// Read value (with auto dependency tracking)
console.log(count()); // 0
// Set new value
count(5);
// Update using previous value
count(prev => prev + 1); // 6
// Create computed signals (auto-updating)
const firstName = $('John');
const lastName = $('Doe');
const fullName = $(() => `${firstName()} ${lastName()}`);
console.log(fullName()); // "John Doe"
firstName('Jane'); // fullName() now returns "Jane Doe"
```bash [bun]
bun add SigPro
```
:::
### Effects - Automatic Reactions
## 2\. Basic Usage
Effects automatically run and re-run when their signal dependencies change:
The core of SigPro is the `$` function, which creates reactive state (Signals) and computed effects.
Create a `main.js` file and try this:
```javascript
const count = $(0);
import { $ } from 'SigPro';
$.effect(() => {
console.log(`Count is: ${count()}`);
});
// Logs: "Count is: 0"
// 1. Create a reactive signal
const $name = $("World");
count(1);
// Logs: "Count is: 1"
// Effects can return cleanup functions
$.effect(() => {
const id = count();
const timer = setInterval(() => {
console.log(`Polling with count: ${id}`);
}, 1000);
// 2. Define a reactive component
const App = () => div({ class: 'container' }, [
h1(["Hello, ", $name, "!"]),
// Cleanup runs before next effect execution
return () => clearInterval(timer);
});
```
### Rendering with `html`
The `html` tag creates reactive DOM fragments:
```javascript
const count = $(0);
const isActive = $(true);
const fragment = html`
<div class="counter">
<h2>Count: ${count}</h2>
<!-- Event binding -->
<button @click=${() => count(c => c + 1)}>
Increment
</button>
<!-- Boolean attributes -->
<button ?disabled=${() => !isActive()}>
Submit
</button>
</div>
`;
document.body.appendChild(fragment);
```
## 🎨 Your First Reactive App
Let's build a simple todo app to see SigPro in action:
```javascript
import { $, html } from 'sigpro';
// Create a simple todo app
function TodoApp() {
// Reactive state
const todos = $(['Learn SigPro', 'Build something awesome']);
const newTodo = $('');
input({
type: 'text',
$value: $name, // Two-way binding
placeholder: 'Enter your name...'
}),
// Computed value
const todoCount = $(() => todos().length);
// Add todo function
const addTodo = () => {
if (newTodo().trim()) {
todos([...todos(), newTodo()]);
newTodo('');
}
};
// Remove todo function
const removeTodo = (index) => {
todos(todos().filter((_, i) => i !== index));
};
// Return reactive template
return html`
<div style="max-width: 400px; margin: 2rem auto; font-family: system-ui;">
<h1>📝 Todo App</h1>
<!-- Input form -->
<div style="display: flex; gap: 8px; margin-bottom: 16px;">
<input
type="text"
:value=${newTodo}
placeholder="Add a new todo..."
style="flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px;"
@keydown.enter=${addTodo}
/>
<button
@click=${addTodo}
style="padding: 8px 16px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer;"
>
Add
</button>
</div>
<!-- Todo count -->
<p>Total todos: ${todoCount}</p>
<!-- Todo list -->
<ul style="list-style: none; padding: 0;">
${() => todos().map((todo, index) => html`
<li style="display: flex; justify-content: space-between; align-items: center; padding: 8px; margin: 4px 0; background: #f5f5f5; border-radius: 4px;">
<span>${todo}</span>
<button
@click=${() => removeTodo(index)}
style="padding: 4px 8px; background: #ff4444; color: white; border: none; border-radius: 4px; cursor: pointer;"
>
</button>
</li>
`)}
</ul>
</div>
`;
}
button({
onclick: () => $name("SigPro")
}, "Set to SigPro")
]);
// Mount the app
document.body.appendChild(TodoApp());
// 3. Mount the application
$.mount(App, '#app');
```
## 🎯 Key Concepts
## 3\. How it Works
### 1. **Signal Patterns**
SigPro doesn't use a Virtual DOM. Instead, it creates real DOM nodes and binds them directly to your data:
| Pattern | Example | Use Case |
|---------|---------|----------|
| Basic signal | `const count = $(0)` | Simple values |
| Computed | `$( () => first() + last() )` | Derived values |
| Signal update | `count(5)` | Direct set |
| Functional update | `count(prev => prev + 1)` | Based on previous |
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.
### 2. **Effect Patterns**
## 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
// Basic effect
$.effect(() => console.log(count()));
// Effect with cleanup
$.effect(() => {
const timer = setInterval(() => {}, 1000);
return () => clearInterval(timer);
});
// Stopping an effect
const stop = $.effect(() => {});
stop(); // Effect won't run again
// Instead of $.html('div', ...), just use:
div([
h1("Clean Syntax"),
p("No more boilerplate.")
]);
```
### 3. **HTML Directives**
| Directive | Example | Description |
|-----------|---------|-------------|
| `@event` | `@click=${handler}` | Event listeners |
| `:property` | `:value=${signal}` | Two-way binding |
| `?attribute` | `?disabled=${signal}` | Boolean attributes |
| `.property` | `.scrollTop=${value}` | DOM properties |
## 💡 Pro Tips for Beginners
### 1. **Start Simple**
```javascript
// Begin with basic signals
const name = $('World');
html`<h1>Hello, ${name}!</h1>`;
```
### 2. **Use Computed Signals for Derived State**
```javascript
// ❌ Don't compute in template
html`<p>Total: ${items().length * price()}</p>`;
// ✅ Compute with signals
const total = $(() => items().length * price());
html`<p>Total: ${total}</p>`;
```
### 3. **Leverage Effects for Side Effects**
```javascript
// Auto-save to localStorage
$.effect(() => {
localStorage.setItem('draft', JSON.stringify(draft()));
});
```
## 🔧 VS Code Setup
For the best development experience, install these VS Code extensions:
- **lit-html** - Adds syntax highlighting for `html` tagged templates
- **Prettier** - Automatically formats your template literals
```javascript
// With lit-html extension, you get full syntax highlighting!
html`
<div style="color: #ff4444; background: linear-gradient(45deg, blue, green)">
<h1>Beautiful highlighted template</h1>
</div>
`
```
## 📁 Project Structure
Here's a recommended structure for larger apps:
```
my-sigpro-app/
├── index.html
├── main.js
├── components/
│ ├── Button.js
│ ├── TodoList.js
│ └── TodoItem.js
├── pages/
│ ├── HomePage.js
│ └── AboutPage.js
└── utils/
└── helpers.js
```
Example `main.js`:
```javascript
import { $, html } from 'sigpro';
import HomePage from './pages/HomePage.js';
// Mount your app
document.body.appendChild(HomePage());
```
## 🎓 Summary
You've learned:
- ✅ How to install SigPro
- ✅ Core concepts: signals, effects, and reactive rendering
- ✅ Built a complete todo app
- ✅ Key patterns and best practices
- ✅ How to structure larger applications
**Remember:** SigPro embraces the web platform. You're writing vanilla JavaScript with superpowers—no compilation, no lock-in, just clean, maintainable code that will work for years to come.
> "Stop fighting the platform. Start building with it."
Happy coding! 🎉

View File

@@ -1,135 +1,78 @@
# Why SigPro?
# Why SigPro?
After years of building applications with React, Vue, and Svelte—investing countless hours mastering their unique mental models, build tools, and update cycles—I kept circling back to the same realization: no matter how sophisticated the framework, it all eventually compiles down to HTML, CSS, and vanilla JavaScript. The web platform has evolved tremendously, yet many libraries continue to reinvent the wheel, creating parallel universes with their own rules, their own syntaxes, and their own steep learning curves.
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 my answer to a simple question:** Why fight the platform when we can embrace it?
**SigPro** is the answer to a simple question: **Why fight the platform when we can embrace it?**
## 🌐 The Web Platform Is Finally Ready
## The Modern Web is Ready
Modern browsers now offer powerful primitives that make true reactivity possible without virtual DOM diffing, without compilers, and without lock-in:
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 |
|-------------------|-----------------|
| **Custom Elements** | Create reusable components with native browser APIs |
| **Shadow DOM** | Encapsulate styles and markup without preprocessors |
| **CSS Custom Properties** | Dynamic theming without CSS-in-JS |
| **Microtask Queues** | Efficient update batching without complex scheduling |
| :--- | :--- |
| **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 familiar but stays remarkably close to vanilla JS:
## The SigPro Philosophy
- **No JSX transformations** - Just template literals
- **No template compilers** - The browser parses your HTML
- **No proprietary syntax to learn** - Just functions and signals
- **No build step required** - Works directly in the browser
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
// Just vanilla JavaScript with signals
import { $, html } from 'sigpro';
// Pure, Atomic, Reactive.
const $count = $(0);
const count = $(0);
document.body.appendChild(html`
<div>
<p>Count: ${count}</p>
<button @click=${() => count(c => c + 1)}>
Increment
</button>
</div>
`);
const Counter = () => div([
p(["Count: ", $count]),
button({ onclick: () => $count(c => c + 1) }, "Increment")
]);
```
## 📊 Comparative
---
| Metric | SigPro | Solid | Svelte | Vue | React |
|--------|--------|-------|--------|-----|-------|
| **Bundle Size** (gzip) | 🥇 **5.2KB** | 🥈 15KB | 🥉 16.6KB | 20.4KB | 43.9KB |
| **Time to Interactive** | 🥇 **0.8s** | 🥈 1.3s | 🥉 1.4s | 1.6s | 2.3s |
| **Initial Render** (ms) | 🥇 **124ms** | 🥈 198ms | 🥉 287ms | 298ms | 452ms |
| **Update Performance** (ms) | 🥇 **4ms** | 🥈 5ms | 🥈 5ms | 🥉 7ms | 18ms |
| **Dependencies** | 🥇 **0** | 🥇 **0** | 🥇 **0** | 🥈 2 | 🥉 5 |
| **Compilation Required** | 🥇 **No** | 🥇 **No** | 🥈 Yes | 🥇 **No** | 🥇 **No** |
| **Browser Native** | 🥇 **Yes** | 🥈 Partial | 🥉 Partial | 🥉 Partial | No |
| **Framework Lock-in** | 🥇 **None** | 🥈 Medium | 🥉 High | 🥈 Medium | 🥉 High |
| **Longevity** (standards-based) | 🥇 **10+ years** | 🥈 5 years | 🥉 3 years | 🥈 5 years | 🥈 5 years |
## 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 principles:
SigPro is built on four fundamental pillars:
### 📡 **True Reactivity**
Automatic dependency tracking with no manual subscriptions. When a signal changes, only the exact DOM nodes that depend on it update—surgically, efficiently, instantly.
### 📡 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 Updates**
No virtual DOM diffing. No tree reconciliation. Just direct DOM updates where and when needed. The result is predictable performance that scales with your content, not your component count.
### ⚡ 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.
### 🧩 **Web Standards**
Built on Custom Elements, not a custom rendering engine. Your components are real web components that work in any framework—or none at all.
### 🧩 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**
No magic, just signals and effects. What you see is what you get. The debugging experience is straightforward because there's no framework layer between your code and the browser.
### 🔬 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.
## 🎨 The Development Experience
---
```javascript
// With VS Code + lit-html extension, you get:
// ✅ Syntax highlighting
// ✅ Color previews
// ✅ Auto-formatting
// ✅ IntelliSense
> "SigPro returns the joy of web development by making the browser the hero again."
html`
<div style="color: #ff4444; background: linear-gradient(45deg, blue, green)">
<h1>Beautiful highlighted template</h1>
</div>
`
```
## ⏱️ Built for the Long Term
What emerged is a library that proves we've reached a turning point: the web is finally mature enough that we don't need to abstract it anymore. We can build reactive, component-based applications using virtually pure JavaScript, leveraging the platform's latest advances instead of working against them.
**The result isn't just smaller bundles or faster rendering—it's code that will still run 10 years from now, in any browser, without maintenance.**
## 📈 The Verdict
While other frameworks build parallel universes with proprietary syntax and compilation steps, SigPro embraces the web platform. SigPro isn't just another framework—it's a return to fundamentals, showing that the dream of simple, powerful reactivity is now achievable with the tools browsers give us out of the box.
> *"Stop fighting the platform. Start building with it."*
## 🚀 Ready to Start?
[Get Started with SigPro](/guide/getting-started) • [View on GitHub](https://github.com/natxocc/sigpro) • [npm Package](https://www.npmjs.com/package/sigpro)
<style>
table {
width: 100%;
border-collapse: collapse;
margin: 2rem 0;
}
th {
background-color: var(--vp-c-bg-soft);
padding: 0.75rem;
text-align: left;
}
td {
padding: 0.75rem;
border-bottom: 1px solid var(--vp-c-divider);
}
tr:hover {
background-color: var(--vp-c-bg-soft);
}
blockquote {
margin: 2rem 0;
padding: 1.5rem;
background: linear-gradient(135deg, var(--vp-c-brand-soft) 0%, transparent 100%);
border-radius: 12px;
font-size: 1.2rem;
font-style: italic;
}
</style>

View File

@@ -1,84 +1,96 @@
---
# https://vitepress.dev/reference/default-theme-home-page
layout: home
hero:
name: "SigPro"
text: "Reactivity for the Web Platform"
tagline: A minimalist reactive library for building web interfaces with signals, effects, and native web components. No compilation, no virtual DOM, just pure JavaScript and intelligent reactivity.
name: SigPro
text: Atomic Unified Reactive Engine
tagline: Fine-grained reactivity, built-in routing, and modular plugins. All under 2KB.
image:
src: /logo.svg
alt: SigPro
src: /logo.png
alt: SigPro Logo
actions:
- theme: brand
text: Get Started
link: /guide/getting-started
- theme: alt
text: View on GitHub
link: https://git.natxocc.com/sigpro/
features:
- title: ⚡ 3KB gzipped
details: Minimal footprint with maximum impact. No heavy dependencies, just pure reactivity.
- title: 🎯 Native Web Components
details: Built on Custom Elements and Shadow DOM. Leverage the platform, don't fight it.
- title: 🔄 Signal-based Reactivity
details: Fine-grained updates without virtual DOM diffing. Just intelligent, automatic reactivity.
- 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.
---
<div class="custom-container">
<p class="npm-stats">
<img src="https://badge.fury.io/js/sigpro.svg" alt="npm version">
<img src="https://img.shields.io/bundlephobia/minzip/sigpro" alt="bundle size">
<img src="https://img.shields.io/npm/l/sigpro" alt="license">
</p>
</div>
## Why SigPro?
<div class="verdict-quote">
<p><strong>"Stop fighting the platform. Start building with it."</strong></p>
</div>
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.
<style>
.npm-stats {
text-align: center;
margin: 2rem 0;
}
### The Core in Action
.npm-stats img {
margin: 0 0.5rem;
display: inline-block;
}
```javascript
import { $ } from 'sigpro2';
.custom-container {
max-width: 1152px;
margin: 0 auto;
padding: 0 24px;
}
// A reactive state Signal
const $count = $(0);
.verdict-quote {
text-align: center;
font-size: 1.5rem;
margin: 3rem 0;
padding: 2rem;
background: linear-gradient(135deg, var(--vp-c-brand-soft) 0%, transparent 100%);
border-radius: 12px;
}
// A Computed signal that updates automatically
const $double = $(() => $count() * 2);
table {
width: 100%;
border-collapse: collapse;
margin: 2rem 0;
}
// UI that breathes with your data
const Counter = () => div([
h1(["Count: ", $count]),
p(["Double: ", $double]),
button({ onclick: () => $count(c => c + 1) }, "Increment")
]);
th {
background-color: var(--vp-c-bg-soft);
padding: 0.75rem;
text-align: left;
}
$.mount(Counter);
```
td {
padding: 0.75rem;
border-bottom: 1px solid var(--vp-c-divider);
}
---
tr:hover {
background-color: var(--vp-c-bg-soft);
}
</style>
### Key Features
#### ⚡️ 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.
---
### Quick Install
::: 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
```

View File

@@ -0,0 +1,107 @@
# 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

@@ -0,0 +1,80 @@
# 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

@@ -0,0 +1,110 @@
# 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

@@ -0,0 +1,106 @@
# 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");
```

147
src/docs/plugins/core.ui.md Normal file
View File

@@ -0,0 +1,147 @@
# Official UI Plugin: `UI`
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 components.
## 1. Prerequisites & Installation
To use these components, you must install the styling engine. SigPro UI provides the logic, but Tailwind and daisyUI provide the visuals.
::: 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
```
:::
Would you like to continue with the **Router.md** documentation now?
### CSS Configuration (`app.css`)
In Tailwind v4, configuration is handled directly in your CSS. Create a `src/app.css` file:
```css
/* src/app.css */
@import "tailwindcss";
/* Import daisyUI v5 as a Tailwind v4 plugin */
@plugin "daisyui";
/* Optional: Configure themes */
@custom-variant dark (&:where(.dark, [data-theme="dark"], [data-theme="dark"] *)));
```
---
## 2. Initialization
You must import your CSS and register the `UI` plugin in your entry point. This populates the global scope with reactive component helpers (prefixed with `_`).
```javascript
// main.js
import './app.css';
import { $ } from 'sigpro';
import { UI } from 'sigpro/plugins';
$.plugin(UI).then(() => {
// Global components like _button and _input are now ready
import('./App.js').then(app => $.mount(app.default));
});
```
---
## 3. Core Component Tags (`_tags`)
SigPro UI components are more than just HTML; they are **Reactive Functional Components** that manage complex states (loading, errors, accessibility) automatically.
### A. Action Components (`_button`)
The `_button` automatically handles spinners and disabled states based on signals.
| Property | Type | Description |
| :--- | :--- | :--- |
| **`$loading`** | `signal` | If true, shows a spinner and disables the button. |
| **`$disabled`**| `signal` | Manually disables the button (logic-bound). |
| **`icon`** | `node/str`| Prepends an icon to the text. |
| **`badge`** | `string` | Appends a small badge to the button. |
```javascript
_button({
$loading: $isSaving,
icon: '💾',
class: 'btn-primary'
}, "Save Data")
```
### B. High-Density Forms (`_input`, `_select`, `_checkbox`)
These components wrap the raw input in a `fieldset` with integrated labels and tooltips.
* **`label`**: Field title displayed above the input.
* **`tip`**: Displays a `?` badge that shows a tooltip on hover.
* **`$error`**: A signal that, when populated, turns the input red and displays the message.
* **`$value`**: **Two-way binding**. Updates the signal on input and the input on signal change.
```javascript
_input({
label: "Username",
tip: "Choose a unique name",
$value: $name,
$error: $nameError
})
```
---
## 4. Complex UI Patterns
### Reactive Modals (`_modal`)
The `_modal` is surgically mounted. If the `$open` signal is `false`, the component is completely removed from the DOM, optimizing performance.
```javascript
const $showModal = $(false);
_modal({ $open: $showModal, title: "Alert" }, [
p("Are you sure you want to proceed?"),
_button({ onclick: () => doAction() }, "Confirm")
])
```
### Navigation & Layout (`_tabs`, `_drawer`, `_navbar`)
Designed to work seamlessly with the **Router**.
| Component | Key Logic |
| :--- | :--- |
| **`_tabs`** | Accepts an `active` property (signal or function) to highlight the current tab. |
| **`_drawer`** | A responsive sidebar that toggles via an ID or an `$open` signal. |
| **`_navbar`** | Standard top bar with shadow and glass effect support. |
| **`_menu`** | Vertical navigation list with active state support. |
---
## 5. Summary Table: UI Globals
Once `$.plugin(UI)` is active, these tags are available project-wide:
| Tag | Category | Use Case |
| :--- | :--- | :--- |
| `_fieldset` | Layout | Grouping related inputs with a `legend`. |
| `_accordion`| Content | Collapsible sections (FAQs). |
| `_badge` | Feedback | Status indicators (Success, Warning). |
| `_tooltip` | Feedback | Descriptive text on hover. |
| `_range` | Input | Reactive slider for numerical values. |
---
### What's next?
With the UI ready and styled via **Tailwind v4**, we can move to the **Router.md**. We will explain how to link `_tabs` and `_menu` to different URL paths for a full SPA experience.
**Would you like to start with the Router configuration?**

123
src/docs/plugins/custom.md Normal file
View File

@@ -0,0 +1,123 @@
# Creating Custom Plugins
There are two main ways to expose a plugin's functionality: **Static/Manual Imports** (cleaner for large projects) or **Global/Automatic Window Injection** (easier for quick scripts and global helpers).
## 1. The Anatomy of a Plugin
A plugin is a standard JavaScript function. By convention, if a plugin adds a global helper or component, it should be prefixed with an underscore (`_`).
```javascript
// plugins/my-utils.js
export const MyUtils = ($) => {
// 1. Attach to the SigPro instance
$.capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
// 2. Attach to the Window (Global access)
window._hello = (name) => div(`Hello, ${$.capitalize(name)}!`);
// 3. You can also return values if needed
return { version: '1.0.0' };
};
```
---
## 2. Integration Strategies
### Option A: Manual Import (Recommended)
This approach keeps your global namespace clean. You import the logic only where you need it, but the plugin still initializes the core `$` extensions.
```javascript
// main.js
import { $ } from 'sigpro';
import { MyUtils } from './plugins/my-utils.js';
$.plugin(MyUtils);
// App.js
export default () => {
const name = "sigpro";
// $.capitalize was added by the plugin
return h1($.capitalize(name));
};
```
### Option B: Automatic Window Injection
If your plugin defines global tags (like `_button` or `_hello`), you should attach them to the `window` object inside the plugin function. This makes them available everywhere without imports.
```javascript
// plugins/theme.js
export const Theme = ($) => {
const $dark = $(false);
window._themeToggle = () => button({
onclick: () => $dark(v => !v),
class: () => $dark() ? 'bg-black text-white' : 'bg-white text-black'
}, "Toggle Mode");
};
// main.js
$.plugin(Theme).then(() => {
// _themeToggle is now a global function
$.mount(App);
});
```
---
## 3. Asynchronous Plugins
If your plugin needs to load external data or scripts before the app starts, make it `async`. SigPro will wait for it.
```javascript
export const ConfigLoader = async ($) => {
const res = await fetch('/config.json');
const config = await res.json();
$.config = config; // Attach loaded config to SigPro
};
// Usage
$.plugin(ConfigLoader).then(() => {
console.log("Config loaded:", $.config);
$.mount(App);
});
```
---
## 4. Best Practices for Plugin Authors
| Rule | Description |
| :--- | :--- |
| **Prefixing** | Use `_` for UI components (`_modal`) and `$.` for logic (`$.fetch`). |
| **Idempotency** | Ensure calling `$.plugin(MyPlugin)` twice doesn't break the app. |
| **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
```
:::

101
src/docs/plugins/quick.md Normal file
View File

@@ -0,0 +1,101 @@
# 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.
## 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.).
### Functional Plugin Example
```javascript
// A plugin that adds a simple logger to any signal
const Logger = ($) => {
$.watch = (target, label = "Log") => {
$(() => console.log(`[${label}]:`, target()));
};
};
// Activation
$.plugin(Logger);
const $count = $(0);
$.watch($count, "Counter"); // Now available globally via $
```
---
## 2. Initialization Patterns
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:
### 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.
```javascript
// main.js
import { $ } from 'sigpro';
import { UI } from 'sigpro/plugins';
import MyApp from './App.js'; // Static import works here
$.plugin(UI);
$.mount(MyApp, '#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.
```javascript
// Loading external libraries as plugins
await $.plugin([
'https://cdn.jsdelivr.net/npm/chart.js',
'https://cdn.example.com/custom-ui-lib.js'
]);
console.log("External resources are ready to use!");
```
---
## 4. Polymorphic Loading Reference
The `$.plugin` method adapts to whatever you throw at it:
| 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 |
---
## 💡 Pro Tip: Why the `.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.

View File

@@ -1,287 +1,33 @@
# Vite Plugin: Automatic File-based Routing 🚦
# Vite Plugin: File-based Routing
SigPro provides an optional Vite plugin that automatically generates routes based on your file structure. No configuration needed - just create pages and they're instantly available with the correct paths.
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.
## Why Use This Plugin?
## 1. Project Structure
While SigPro's router works perfectly with manually defined routes, this plugin:
- **Eliminates boilerplate** - No need to write route configurations
- **Enforces conventions** - Consistent URL structure across your app
- **Supports dynamic routes** - Use `[param]` syntax for parameters
- **Automatic code-splitting** - Each page becomes a separate chunk
- **Type-safe** (with JSDoc) - Routes follow your file structure
To use the plugin, organize your files within the `src/pages` directory. The folder hierarchy directly determines your application's URL structure.
## Installation
The plugin is included with SigPro, but you need to add it to your Vite config:
```javascript
// vite.config.js
import { defineConfig } from 'vite';
import { sigproRouter } from 'sigpro';
export default defineConfig({
plugins: [sigproRouter()]
});
```text
my-sigpro-app/
├── src/
│ ├── pages/
│ │ ├── index.js → #/
│ │ ├── about.js → #/about
│ │ ├── users/
│ │ │ └── [id].js → #/users/:id
│ │ └── blog/
│ │ ├── index.js → #/blog
│ │ └── [slug].js → #/blog/:slug
│ ├── App.js (Optional App Shell)
│ └── main.js (Entry Point)
├── vite.config.js
└── package.json
```
## How It Works
---
The plugin scans your `src/pages` directory and automatically generates routes based on the file structure:
## 2. Setup & Configuration
```
src/pages/
├── index.js → '/'
├── about.js → '/about'
├── blog/
│ ├── index.js → '/blog'
│ └── [slug].js → '/blog/:slug'
└── users/
├── [id].js → '/users/:id'
└── [id]/edit.js → '/users/:id/edit'
```
## Usage
### 1. Enable the Plugin
Add the plugin to your Vite config as shown above.
### 2. Import the Generated Routes
Once you have the generated routes, using them with the router is straightforward:
```javascript
// main.js
import { $, html } from 'sigpro';
import { routes } from 'virtual:sigpro-routes';
// Simple usage
const router = $.router(routes);
document.body.appendChild(router);
```
Or directly in your template:
```javascript
// app.js
import { $, html } from 'sigpro';
import { routes } from 'virtual:sigpro-routes';
const App = () => html`
<div class="app">
<header>
<h1>My Application</h1>
</header>
<main class="p-4 flex flex-col gap-4 mx-auto w-full">
<div class="p-4 bg-base-100 rounded-box shadow-sm">
${$.router(routes)}
</div>
</main>
</div>
`;
document.body.appendChild(App());
```
This approach keeps your template clean and lets the router handle all the page rendering automatically.
### 3. Create Pages
```javascript
// src/pages/index.js
import { $, html } from 'sigpro';
export default () => {
return html`
<div>
<h1>Home Page</h1>
<a href="#/about">About</a>
</div>
`;
};
```
```javascript
// src/pages/users/[id].js
import { $, html } from 'sigpro';
export default (params) => {
const userId = params.id;
return html`
<div>
<h1>User Profile: ${userId}</h1>
<a href="#/users/${userId}/edit">Edit</a>
</div>
`;
};
```
## 📋 File-to-Route Mapping
### Static Routes
| File Path | Generated Route |
|-----------|-----------------|
| `src/pages/index.js` | `/` |
| `src/pages/about.js` | `/about` |
| `src/pages/contact/index.js` | `/contact` |
| `src/pages/blog/post.js` | `/blog/post` |
### Dynamic Routes
| File Path | Generated Route | Example URL |
|-----------|-----------------|-------------|
| `src/pages/users/[id].js` | `/users/:id` | `/users/42` |
| `src/pages/blog/[slug].js` | `/blog/:slug` | `/blog/hello-world` |
| `src/pages/users/[id]/posts/[pid].js` | `/users/:id/posts/:pid` | `/users/42/posts/123` |
### Nested Routes
| File Path | Generated Route | Notes |
|-----------|-----------------|-------|
| `src/pages/settings/index.js` | `/settings` | Index page |
| `src/pages/settings/profile.js` | `/settings/profile` | Sub-page |
| `src/pages/settings/security.js` | `/settings/security` | Sub-page |
| `src/pages/settings/[section].js` | `/settings/:section` | Dynamic section |
## 🎯 Advanced Examples
### Blog with Posts
```javascript
// src/pages/blog/index.js - Lists all posts
export default () => {
const posts = $([]);
$.effect(() => {
fetch('/api/posts')
.then(res => res.json())
.then(data => posts(data));
});
return html`
<div>
<h1>Blog</h1>
${posts().map(post => html`
<article>
<h2><a href="#/blog/${post.slug}">${post.title}</a></h2>
<p>${post.excerpt}</p>
</article>
`)}
</div>
`;
};
```
```javascript
// src/pages/blog/[slug].js - Single post
export default (params) => {
const post = $(null);
const slug = params.slug;
$.effect(() => {
fetch(`/api/posts/${slug}`)
.then(res => res.json())
.then(data => post(data));
});
return html`
<div>
<a href="#/blog">← Back to blog</a>
${() => post() ? html`
<article>
<h1>${post().title}</h1>
<div>${post().content}</div>
</article>
` : html`<div>Loading...</div>`}
</div>
`;
};
```
### Dashboard with Nested Sections
```javascript
// src/pages/dashboard/index.js
export default () => {
return html`
<div class="dashboard">
<nav>
<a href="#/dashboard">Overview</a>
<a href="#/dashboard/analytics">Analytics</a>
<a href="#/dashboard/settings">Settings</a>
</nav>
<main>
<h1>Dashboard Overview</h1>
<!-- Overview content -->
</main>
</div>
`;
};
```
```javascript
// src/pages/dashboard/analytics.js
export default () => {
return html`
<div class="dashboard">
<nav>
<a href="#/dashboard">Overview</a>
<a href="#/dashboard/analytics">Analytics</a>
<a href="#/dashboard/settings">Settings</a>
</nav>
<main>
<h1>Analytics</h1>
<!-- Analytics content -->
</main>
</div>
`;
};
```
### E-commerce Product Routes
```javascript
// src/pages/products/[category]/[id].js
export default (params) => {
const { category, id } = params;
const product = $(null);
$.effect(() => {
fetch(`/api/products/${category}/${id}`)
.then(res => res.json())
.then(data => product(data));
});
return html`
<div class="product-page">
<nav class="breadcrumbs">
<a href="#/products">Products</a> &gt;
<a href="#/products/${category}">${category}</a> &gt;
<span>${id}</span>
</nav>
${() => product() ? html`
<div class="product">
<h1>${product().name}</h1>
<p class="price">$${product().price}</p>
<p>${product().description}</p>
<button @click=${() => addToCart(product())}>
Add to Cart
</button>
</div>
` : html`<div>Loading...</div>`}
</div>
`;
};
```
## 🔧 Configuration Options
The plugin accepts an optional configuration object:
Add the plugin to your `vite.config.js`.
```javascript
// vite.config.js
@@ -289,135 +35,90 @@ import { defineConfig } from 'vite';
import { sigproRouter } from 'sigpro/vite';
export default defineConfig({
plugins: [
sigproRouter({
pagesDir: 'src/pages', // Default: 'src/pages'
extensions: ['.js', '.jsx'], // Default: ['.js', '.jsx']
exclude: ['**/_*', '**/components/**'] // Glob patterns to exclude
})
]
plugins: [sigproRouter()]
});
```
### Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `pagesDir` | `string` | `'src/pages'` | Directory containing your pages |
| `extensions` | `string[]` | `['.js', '.jsx']` | File extensions to include |
| `exclude` | `string[]` | `[]` | Glob patterns to exclude |
## 🎯 Route Priority
The plugin automatically sorts routes to ensure correct matching:
1. **Static routes** take precedence over dynamic ones
2. **More specific routes** (deeper paths) come first
3. **Alphabetical order** for routes at the same level
Example sorting:
```
/users/new (static, specific)
/users/[id]/edit (dynamic, deeper)
/users/[id] (dynamic, shallower)
/users/profile (static, shallower)
```
## 📦 Output Example
When you import `virtual:sigpro-routes`, you get:
```javascript
// Generated module
import Page_0 from '/src/pages/index.js';
import Page_1 from '/src/pages/about.js';
import Page_2 from '/src/pages/blog/index.js';
import Page_3 from '/src/pages/blog/[slug].js';
import Page_4 from '/src/pages/users/[id].js';
import Page_5 from '/src/pages/users/[id]/edit.js';
export const routes = [
{ path: '/', component: Page_0 },
{ path: '/about', component: Page_1 },
{ path: '/blog', component: Page_2 },
{ path: '/blog/:slug', component: Page_3 },
{ path: '/users/:id', component: Page_4 },
{ path: '/users/:id/edit', component: Page_5 },
];
```
## 🚀 Performance Benefits
- **Automatic code splitting** - Each page becomes a separate chunk
- **Lazy loading ready** - Import pages dynamically
- **Tree shaking** - Only used routes are included
```javascript
// With dynamic imports (automatic with Vite)
const routes = [
{ path: '/', component: () => import('./pages/index.js') },
{ path: '/about', component: () => import('./pages/about.js') },
// ...
];
```
## 💡 Pro Tips
### 1. Group Related Pages
```
src/pages/
├── dashboard/
│ ├── index.js
│ ├── analytics.js
│ └── settings.js
└── dashboard.js # ❌ Don't mix with folder
```
### 2. Use Index Files for Clean URLs
```
✅ Good:
pages/blog/index.js → /blog
pages/blog/post.js → /blog/post
❌ Avoid:
pages/blog.js → /blog (conflicts with folder)
```
### 3. Private Components
Prefix with underscore to exclude from routing:
```
src/pages/
├── index.js
├── about.js
└── _components/ # ❌ Not scanned
└── Header.js
```
### 4. Layout Components
Create a layout wrapper in your main entry:
```javascript
// main.js
import { $, html } from 'sigpro';
import { routes } from 'virtual:sigpro-routes';
// Wrap all routes with layout
const routesWithLayout = routes.map(route => ({
...route,
component: (params) => Layout(route.component(params))
}));
const router = $.router(routesWithLayout);
document.body.appendChild(router);
```
---
> **Note:** This plugin is completely optional. You can always define routes manually if you prefer. The plugin just saves you from writing boilerplate route configurations.
## 3. Implementation
> **Pro Tip:** The plugin works great with hot module replacement (HMR) - add a new page and it's instantly available in your dev server without restarting!
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).
### Option A: Direct in `main.js`
Best for simple apps where the router occupies 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');
});
```
### Option B: Inside `App.js` (With Layout)
Recommended for apps with a fixed Sidebar or Navbar.
```javascript
// src/main.js
import { $ } from 'sigpro';
import { Router } from 'sigpro/plugins';
$.plugin(Router).then(() => {
import('./App.js').then(app => $.mount(app.default, '#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))
]);
};
```
---
## 4. Route Mapping Reference
| File Path | Generated Route | Logic |
| :--- | :--- | :--- |
| `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 |
---
## 5. Installation
::: code-group
```bash [NPM]
npm install sigpro
```
```bash [PNPM]
pnpm add sigpro
```
```bash [Yarn]
yarn add sigpro
```
```bash [Bun]
bun add sigpro
```
:::