commit a4a8d92fcd9f1ce4ed3e5a11a1d1cf4fc8e42fc2
parent 4ca507394f753cdb2622c35ce55992690040eba0
Author: triesap <triesap@radroots.dev>
Date: Mon, 17 Nov 2025 23:51:11 +0000
apps-lib: add themed ui primitives, app/carousel state, and browser/idb/geo utilities, consolidating exports and ignore rules
Diffstat:
26 files changed, 1142 insertions(+), 145 deletions(-)
diff --git a/apps-lib/.gitignore b/apps-lib/.gitignore
@@ -1,18 +1,10 @@
node_modules
-
-# Output
-.output
-.vercel
-.netlify
-.wrangler
-/.svelte-kit
-/build
dist
.turbo
-# OS
-.DS_Store
-Thumbs.db
+# Logs
+logs/
+*.log
# Env
.env
@@ -20,26 +12,31 @@ Thumbs.db
!.env.example
!.env.test
-# Vite
-vite.config.js.timestamp-*
-vite.config.ts.timestamp-*
-vite.config.dev*
-
-# debug
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
+# OS
+.DS_Store
+Thumbs.db
-# secrets
+# Secrets
*.pem
+*.crt
+*.key
+
+# Testing
+test*.json
-# local
-.tmp*
-.backup*
-.dev*
-.vscode
+# Editors
+.vscode/
+.idea/
+*.iml
+
+# Notes
notes*.txt
notes*.md
notes*.json
-git-diff*.txt
-justfile
+tree*.txt
+diff*.txt
+prompt*.txt
+
+# Dev
+.local*
+justfile
+\ No newline at end of file
diff --git a/apps-lib/README.md b/apps-lib/README.md
@@ -1 +0,0 @@
-# lib-app
diff --git a/apps-lib/package.json b/apps-lib/package.json
@@ -51,6 +51,7 @@
"@radroots/locales": "*",
"@radroots/utils": "*",
"@radroots/utils-nostr": "*",
+ "@radroots/themes": "*",
"@nostr-dev-kit/ndk": "2.14.33",
"@nostr-dev-kit/ndk-cache-dexie": "2.6.34",
"@nostr-dev-kit/ndk-svelte": "2.4.38",
diff --git a/apps-lib/src/lib/components/carousel-item.svelte b/apps-lib/src/lib/components/carousel-item.svelte
@@ -0,0 +1,19 @@
+<script lang="ts">
+ import type { IBasisOpt, IClOpt } from "$lib/types/ui";
+ import { fmt_cl } from "$lib/utils/lib";
+ import type { Snippet } from "svelte";
+
+ let {
+ basis = undefined,
+ children,
+ }: {
+ basis?: IBasisOpt<IClOpt>;
+ children: Snippet;
+ } = $props();
+</script>
+
+<div
+ class={`${fmt_cl(basis?.classes)} flex flex-col flex-shrink-0 w-[100vw] justify-start items-center`}
+>
+ {@render children()}
+</div>
diff --git a/apps-lib/src/lib/components/carousel.svelte b/apps-lib/src/lib/components/carousel.svelte
@@ -0,0 +1,25 @@
+<script lang="ts">
+ import { casl_i } from "$lib/stores/carousel";
+ import type { IBasisOpt, IClOpt } from "$lib/types/ui";
+ import { fmt_cl } from "$lib/utils/lib";
+ import type { Snippet } from "svelte";
+
+ let {
+ basis = undefined,
+ children,
+ }: {
+ basis?: IBasisOpt<IClOpt>;
+ children: Snippet;
+ } = $props();
+</script>
+
+<div
+ class={`${fmt_cl(basis?.classes)} relative flex flex-col w-full overflow-hidden`}
+>
+ <div
+ class={`flex h-full transition-transform duration-500`}
+ style={`transform: translateX(-${Math.max($casl_i, 0) * 100}vw)`}
+ >
+ {@render children()}
+ </div>
+</div>
diff --git a/apps-lib/src/lib/components/fade.svelte b/apps-lib/src/lib/components/fade.svelte
@@ -0,0 +1,34 @@
+<script lang="ts">
+ import { fmt_cl } from "$lib/utils/lib";
+ import type { Snippet } from "svelte";
+ import { cubicIn, cubicOut } from "svelte/easing";
+ import { fade, type FadeParams } from "svelte/transition";
+
+ let {
+ basis,
+ children,
+ }: {
+ basis?: {
+ in?: FadeParams;
+ out?: FadeParams;
+ classes?: string;
+ };
+ children: Snippet;
+ } = $props();
+</script>
+
+<div
+ in:fade={{
+ duration: 200,
+ easing: cubicIn,
+ ...basis?.in,
+ }}
+ out:fade={{
+ duration: 200,
+ easing: cubicOut,
+ ...basis?.out,
+ }}
+ class={`flex w-full overflow-hidden ${fmt_cl(basis?.classes)}`}
+>
+ {@render children()}
+</div>
diff --git a/apps-lib/src/lib/components/glyph.svelte b/apps-lib/src/lib/components/glyph.svelte
@@ -1,20 +1,20 @@
<script lang="ts">
- import type { IGlyphI } from "$lib/types/components";
+ import { glyph_style_map } from "$lib/styles/glyphs";
+ import type { IGlyph } from "$lib/types/components";
import { fmt_cl } from "$lib/utils/lib";
- const styles = {
- xs: `text-[16px]`,
- sm: `text-[20px]`,
- md: `text-[26px]`,
- lg: `text-[32px]`,
- xl: `text-[48px]`,
- "2xl": "text-[64px]",
- };
+ let { basis }: { basis: IGlyph } = $props();
- let { basis }: { basis: IGlyphI } = $props();
+ const styles = $derived(
+ basis?.dim ? glyph_style_map.get(basis.dim) : glyph_style_map.get(`sm`),
+ );
+
+ const weight = $derived(basis.weight ? `-${basis.weight}` : `-bold`);
</script>
-<i
- class={`ph${!basis?.weight || basis?.weight === `regular` ? `` : `-${basis?.weight}`} ph-${basis.key} ${fmt_cl(basis.classes)} ${basis.size ? styles[basis.size] : ""}`}
+<div
+ id={basis.id || null}
+ class={`${fmt_cl(basis.classes)} flex flex-row text-[${styles?.gl_1}px] justify-center items-center`}
>
-</i>
+ <i class={`ph${weight} ph-${basis.key}`}></i>
+</div>
diff --git a/apps-lib/src/lib/components/glyph.svelte b/apps-lib/src/lib/components/glyphi.svelte
diff --git a/apps-lib/src/lib/components/image-blob.svelte b/apps-lib/src/lib/components/image-blob.svelte
@@ -0,0 +1,31 @@
+<script lang="ts">
+ import type { IImageBlob } from "$lib/types/ui";
+ import { fmt_cl, to_arr_buf } from "$lib/utils/lib";
+
+ let { basis }: { basis: IImageBlob } = $props();
+
+ const img_src = $derived(
+ basis.data
+ ? URL.createObjectURL(
+ new Blob(
+ [
+ basis.data instanceof Uint8Array
+ ? to_arr_buf(basis.data)
+ : basis.data,
+ ],
+ { type: "image/jpeg" },
+ ),
+ )
+ : undefined,
+ );
+</script>
+
+{#if img_src}
+ <img
+ id={basis?.id || null}
+ class={`${fmt_cl(basis?.classes)}`}
+ src={img_src}
+ alt={basis?.alt || null}
+ style="height: 100%; width: 100%; object-fit: cover; display: block;"
+ />
+{/if}
diff --git a/apps-lib/src/lib/components/image-path.svelte b/apps-lib/src/lib/components/image-path.svelte
@@ -0,0 +1,23 @@
+<script lang="ts">
+ import type { IImagePath } from "$lib/types/ui";
+ import { get_context } from "$lib/utils/lib";
+ import ImageBlob from "./image-blob.svelte";
+ import ImageSrc from "./image-src.svelte";
+
+ const { lc_img_bin } = get_context(`lib`);
+ let { basis }: { basis: IImagePath } = $props();
+
+ const img_path = $derived(basis.path);
+</script>
+
+{#if img_path}
+ {@const is_bin = img_path.startsWith(`file:`)}
+
+ {#if is_bin}
+ {#await lc_img_bin(img_path) then data}
+ <ImageBlob basis={{ data, ...basis }} />
+ {/await}
+ {:else}
+ <ImageSrc basis={{ src: img_path, ...basis }} />
+ {/if}
+{/if}
diff --git a/apps-lib/src/lib/components/image-src.svelte b/apps-lib/src/lib/components/image-src.svelte
@@ -0,0 +1,18 @@
+<script lang="ts">
+ import type { IImageSource } from "$lib/types/ui";
+ import { fmt_cl } from "$lib/utils/lib";
+
+ let { basis }: { basis: IImageSource } = $props();
+
+ const img_src = $derived(basis.src);
+</script>
+
+{#if img_src}
+ <img
+ id={basis?.id || null}
+ class={`${fmt_cl(basis?.classes)}`}
+ src={img_src || null}
+ alt={basis?.alt || null}
+ style={`height: 100%; width: 100%; object-fit: cover; display: block;`}
+ />
+{/if}
diff --git a/apps-lib/src/lib/index.ts b/apps-lib/src/lib/index.ts
@@ -1,13 +1,27 @@
-export { default as Flex } from "./components/flex.svelte"
-export { default as Glyph } from "./components/glyph.svelte"
-export { default as Input } from "./components/input.svelte"
+export * from "./stores/app.js"
+export * from "./stores/carousel.js"
export * from "./stores/ndk.js"
export * from "./stores/theme.js"
+export * from "./styles/glyphs.js"
export * from "./types/components.js"
export * from "./types/lib.js"
export * from "./types/ndk.js"
+export * from "./types/ui.js"
+export * from "./utils/app/carousel.js"
+export * from "./utils/browser.js"
export * from "./utils/fetch/lib.js"
+export * from "./utils/geo.js"
export * from "./utils/i18n.js"
-export * from "./utils/idb/lib.js"
+export * from "./utils/idb/kv.js"
export * from "./utils/lib.js"
export * from "./utils/nostr/lib.js"
+export { default as Carousel } from "./components/carousel.svelte"
+export { default as CarouselItem } from "./components/carousel-item.svelte"
+export { default as Fade } from "./components/fade.svelte"
+export { default as Flex } from "./components/flex.svelte"
+export { default as Glyph } from "./components/glyph.svelte"
+export { default as Glyphi } from "./components/glyphi.svelte"
+export { default as ImageBlob } from "./components/image-blob.svelte"
+export { default as ImagePath } from "./components/image-path.svelte"
+export { default as ImageSrc } from "./components/image-src.svelte"
+export { default as Input } from "./components/input.svelte"
diff --git a/apps-lib/src/lib/stores/app.ts b/apps-lib/src/lib/stores/app.ts
@@ -0,0 +1,4 @@
+import { writable } from "svelte/store";
+
+export const win_h = writable<number>(0);
+export const win_w = writable<number>(0);
diff --git a/apps-lib/src/lib/stores/carousel.ts b/apps-lib/src/lib/stores/carousel.ts
@@ -0,0 +1,39 @@
+import { get_store } from "$lib/utils/lib";
+import { writable } from "svelte/store";
+
+export const carousel_active = writable<boolean>(false);
+export const casl_i = writable<number>(0);
+export const casl_imax = writable<number>(0);
+
+const fn_carousel_num = (num_i: number, num_min: number) => {
+ const store = writable<number>(num_i);
+ return {
+ subscribe: store.subscribe,
+ set: (num: number) => {
+ store.set(Math.max(num, num_min));
+ },
+ update: (updater: (num: number) => number) => {
+ store.update((num) => Math.max(updater(num), num_min));
+ }
+ };
+}
+export const carousel_num = fn_carousel_num(1, 1);
+
+export const casl_inc = async (opts?: 'noflow'): Promise<void> => {
+ const $casl_i = get_store(casl_i);
+ const $casl_imax = get_store(casl_imax);
+ if (opts === 'noflow' && $casl_i < $casl_imax) casl_i.set($casl_i + 1);
+ else casl_i.set(($casl_i + 1) % ($casl_imax + 1));
+};
+
+export const casl_dec = async (opts?: 'noflow'): Promise<void> => {
+ const $casl_i = get_store(casl_i);
+ const $casl_imax = get_store(casl_imax);
+ if (opts === 'noflow' && $casl_i > 0) casl_i.set($casl_i - 1);
+ else casl_i.set(($casl_i - 1 + ($casl_imax + 1)) % ($casl_imax + 1));
+};
+
+export const casl_init = (index_curr: number, index_max: number): void => {
+ casl_i.set(index_curr);
+ casl_imax.set(index_max);
+};
diff --git a/apps-lib/src/lib/stores/theme.ts b/apps-lib/src/lib/stores/theme.ts
@@ -1,10 +1,10 @@
-import type { ThemeMode } from "$lib/types/lib";
import { get_store } from "$lib/utils/lib";
+import type { ThemeKey, ThemeMode } from "@radroots/themes";
import { type CallbackPromiseGeneric } from "@radroots/utils";
import { writable } from "svelte/store";
export const theme_mode = writable<ThemeMode>();
-export const theme_key = writable<string>();
+export const theme_key = writable<ThemeKey>();
export const theme_reset = writable<boolean>(false);
export const theme_toggle = async (callback: CallbackPromiseGeneric<ThemeMode>): Promise<void> => {
diff --git a/apps-lib/src/lib/styles/glyphs.ts b/apps-lib/src/lib/styles/glyphs.ts
@@ -0,0 +1,18 @@
+import type { GeometryGlyphDimension } from "$lib/types/lib";
+
+export const glyph_style_map: Map<GeometryGlyphDimension, { gl_1: number; dim_1?: number; }> = new Map([
+ ["xs--", { gl_1: 12 }],
+ ["xs-", { gl_1: 12, dim_1: 17 }],
+ ["xs", { gl_1: 15, dim_1: 18 }],
+ ["xs+", { gl_1: 18, dim_1: 20 }],
+ ["sm-", { gl_1: 19, dim_1: 22 }],
+ ["sm", { gl_1: 20, dim_1: 24 }],
+ ["sm+", { gl_1: 21 }],
+ ["md-", { gl_1: 23 }],
+ ["md", { gl_1: 24 }],
+ ["md+", { gl_1: 26 }],
+ ["lg-", { gl_1: 27 }],
+ ["lg", { gl_1: 28 }],
+ ["xl", { gl_1: 30 }],
+ ["xl+", { gl_1: 40 }],
+]);
+\ No newline at end of file
diff --git a/apps-lib/src/lib/types/components.ts b/apps-lib/src/lib/types/components.ts
@@ -1,7 +1,9 @@
-import type { CallbackPromiseArgs } from "$lib";
+import type { ElementCallbackMount, ElementCallbackValue, ElementCallbackValueBlur, ElementCallbackValueKeydown, GeometryGlyphDimension, GlyphKey, GlyphWeight, ICbOpt, IClOpt, IconWeight, IDisabledOpt, IIdGOpt, IIdOpt, ILyOpt } from "$lib";
+import type { CallbackPromiseGeneric, FormField } from "@radroots/utils";
import type { HTMLInputTypeAttribute } from "svelte/elements";
-export type IconWeight = `regular` | `bold` | `fill`;
+
+export type EntryStyle = `guide` | `line`;
export type IGlyphI = {
classes?: string;
@@ -10,6 +12,12 @@ export type IGlyphI = {
size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
};
+export type IGlyph = ICbOpt & IIdOpt & ILyOpt & IClOpt & {
+ weight?: GlyphWeight;
+ key: GlyphKey;
+ dim?: GeometryGlyphDimension;
+};
+
export type ILibInputKeydown = {
key: string;
is_submit: boolean;
@@ -22,6 +30,53 @@ export type ILibInput = {
pattern?: string;
placeholder?: string;
disabled?: boolean;
- callback_keydown?: CallbackPromiseArgs<ILibInputKeydown>;
- callback_input?: CallbackPromiseArgs<HTMLInputElement>;
+ callback_keydown?: CallbackPromiseGeneric<ILibInputKeydown>;
+ callback_input?: CallbackPromiseGeneric<HTMLInputElement>;
+};
+
+export type IInput<T extends string> = IIdGOpt<T> & IClOpt & ILyOpt & IDisabledOpt & {
+ placeholder?: string;
+ label?: string;
+ hidden?: boolean;
+ validate?: RegExp;
+ sync?: boolean;
+ field?: FormField;
+ field_constrain?: boolean;
+ callback?: ElementCallbackValue,
+ callback_keydown?: ElementCallbackValueKeydown<HTMLInputElement>,
+ callback_blur?: ElementCallbackValueBlur<HTMLInputElement>;
+ callback_focus?: ElementCallbackValueBlur<HTMLInputElement>;
+ callback_mount?: ElementCallbackMount<HTMLInputElement>;
+};
+
+export type IInputValue<T extends string> = Omit<IInput<T>, `sync`>;
+
+export type ISelectOption<T extends string> = IDisabledOpt & {
+ value: T;
+ label: string;
+};
+
+export type ISelectCallback = CallbackPromiseGeneric<ISelectOption<string>>
+
+export type ISelect = IIdOpt & IClOpt & ILyOpt & {
+ callback?: ISelectCallback;
+ sync?: boolean;
+ sync_init?: boolean;
+ options: { group?: string | true; entries: ISelectOption<string>[] }[];
+ show_arrows?: 'l' | 'r';
+};
+
+export type ITextArea = IIdOpt & IClOpt & ILyOpt & {
+ placeholder?: string;
+ label?: string;
+ hidden?: boolean;
+ validate?: RegExp;
+ sync?: true;
+ field?: FormField;
+ field_constrain?: boolean;
+ callback?: ElementCallbackValue,
+ callback_keydown?: ElementCallbackValueKeydown<HTMLTextAreaElement>,
+ callback_blur?: ElementCallbackValueBlur<HTMLTextAreaElement>;
+ callback_focus?: ElementCallbackValueBlur<HTMLTextAreaElement>;
+ callback_mount?: ElementCallbackMount<HTMLTextAreaElement>;
};
\ No newline at end of file
diff --git a/apps-lib/src/lib/types/lib.ts b/apps-lib/src/lib/types/lib.ts
@@ -1,7 +1,197 @@
+import type { CallbackPromise, CallbackPromiseFull, CallbackPromiseGeneric, CallbackPromiseResult, GeocoderReverseResult, GeolocationPoint, IClientGeolocationPosition, MediaImageUploadResult } from "@radroots/utils";
import type { Writable } from "svelte/store";
+import type { ISelectOption } from "./components";
-export type ThemeMode = 'light' | 'dark';
export type StoreWritable<S> = S extends Writable<infer T> ? T : never;
-export type CallbackPromise = () => Promise<void>
-export type CallbackPromiseArgs<T> = (args: T) => Promise<void>
+export type INavigationRoute<T extends string> = {
+ route: T | [T, NavigationParamTuple<NavigationRouteParamKey>[]];
+};
+export type CallbackRoute<T extends string> = CallbackPromise | INavigationRoute<T>;
+
+export type IViewOnMount<TypeCallbackParam> = {
+ on_mount: CallbackPromiseGeneric<TypeCallbackParam>;
+};
+
+export type IViewOnDestroy<TypeCallbackParam> = {
+ on_destroy: CallbackPromiseGeneric<TypeCallbackParam>;
+};
+
+
+export type IconWeight = `regular` | `bold` | `fill`;
+
+export type GlyphKey = |
+ `video-camera` |
+ `device-mobile-camera` |
+ `crop` |
+ `map-trifold` |
+ `trash-simple` |
+ `backspace` |
+ `user-circle-check` |
+ `images-square` |
+ `bell` |
+ `columns` |
+ `bold` |
+ `article` |
+ `grid-four` |
+ `link-simple` |
+ `seal-check` |
+ `selection-foreground` |
+ `image-square` |
+ `image-broken` |
+ `funnel` |
+ `users-three` |
+ `note-blank` |
+ `user-circle-plus` |
+ `user-circle` |
+ `receipt` |
+ `invoice` |
+ `note` |
+ `arrow-left` |
+ `arrows-down-up` |
+ `basket` |
+ `arrow-right` |
+ `upload-simple` |
+ `printer` |
+ `download-simple` |
+ `list` |
+ `asterisk` |
+ `asterisk-simple` |
+ `subtitles-slash` |
+ `cardholder` |
+ `globe-x` |
+ `exclamation-mark` |
+ `network-x` |
+ `x-circle` |
+ `address-book-tabs` |
+ `paper-plane-tilt` |
+ `note-pencil` |
+ `share-fat` |
+ `folder` |
+ `trash` |
+ `plus-circle` |
+ `currency-${GlyphKeyCurrency}` |
+ `arrow-down` |
+ `caret-circle-down` |
+ `caret-circle-up` |
+ `shopping-bag-open` |
+ `coffee-bean` |
+ `compass` |
+ `map-pin-simple` |
+ `handbag-simple` |
+ `devices` |
+ `lock-key` |
+ `gear` |
+ `gear-fine` |
+ `bell-simple` |
+ `envelope` |
+ `house-line` |
+ `arrows-left-right` |
+ `list-plus` |
+ `squares-four` |
+ `list-plus` |
+ `app-window` |
+ `circle-notch` |
+ `subtract-square` |
+ `device-tablet-speaker` |
+ `weather-cloud` |
+ `warning` |
+ `circle-notch` |
+ `minus` |
+ `key` |
+ `arrow-u-up-left` |
+ `arrow-counter-clockwise` |
+ `circle` |
+ `check-circle` |
+ `circle-dashed` |
+ `dots-three` |
+ `cards-three` |
+ `lightning` |
+ `cards` |
+ `note-pencil` |
+ `tray` |
+ `calendar-dots` |
+ `notepad` |
+ `network` |
+ `calendar-blank` |
+ `chats-circle` |
+ `plant` |
+ `farm` |
+ `magnifying-glass` |
+ `chat-circle-dots` |
+ `dots-three-outline` |
+ `copy` |
+ `circles-four` |
+ `waveform` |
+ `film-strip` |
+ `arrow-up` |
+ `arrow-circle-up` |
+ `plus` |
+ `funnel-simple` |
+ `user` |
+ `camera` |
+ `check` |
+ `file` |
+ `share-network` |
+ `question` |
+ `minus-circle` |
+ `globe-simple` |
+ `globe` |
+ `warning-circle` |
+ `x` |
+ `info` |
+ `caret-${GeometryCardinalDirection}` |
+ `caret-up-down`;
+
+export type GlyphKeyCurrency = `dollar` | `eur`;
+export type GlyphWeight = `bold` | `fill`
+
+export type GeometryScreenPositionHorizontal = `left` | `center` | `right`;
+export type GeometryScreenPositionVertical = `top` | `center` | `bottom`;
+export type GeometryScreenPosition = `${GeometryScreenPositionVertical}-${GeometryScreenPositionHorizontal}`;
+export type GeometryCardinalDirection = `up` | `down` | `left` | `right`;
+
+export type GeometryDimension =
+ `xs` |
+ `sm` |
+ `md` |
+ `lg` |
+ `xl`;
+export type GeometryGlyphDimension =
+ | `${GeometryDimension}`
+ | `${GeometryDimension}-`
+ | `${GeometryDimension}--`
+ | `${GeometryDimension}+`;
+
+export type LayerGlyphBasisKind = `_a` | `_d` | `_pl`;
+
+export type LoadingBlades = 8 | 12;
+export type LoadingDimension = GeometryDimension | `glyph-send-button`; //@todo remove
+
+export type ElementCallbackValue = CallbackPromiseGeneric<{ value: string; pass: boolean; }>;
+export type ElementCallbackValueKeydown<T extends HTMLElement> = CallbackPromiseGeneric<{ key: string; key_s: boolean; el: T }>;
+export type ElementCallbackValueBlur<T extends HTMLElement> = CallbackPromiseGeneric<{ el: T }>;
+export type ElementCallbackValueFocus<T extends HTMLElement> = CallbackPromiseGeneric<{ el: T }>;
+export type ElementCallbackMount<T extends HTMLElement> = CallbackPromiseGeneric<{ el: T }>;
+
+export type NavigationParamTuple<T extends string> = [T, string];
+
+export type NavigationRouteParamId = `id`;
+export type NavigationRouteParamField = `field`;
+export type NavigationRouteParamRef = `ref`;
+export type NavigationRouteParamLat = `lat`;
+export type NavigationRouteParamLng = `lng`;
+export type NavigationRouteParamNostrPublicKey = `key_nostr`;
+export type NavigationRouteParamKey = NavigationRouteParamId | NavigationRouteParamField | NavigationRouteParamRef | NavigationRouteParamLat | NavigationRouteParamLng | NavigationRouteParamNostrPublicKey;
+export type NavigationRouteParamTuple = NavigationParamTuple<NavigationRouteParamKey>;
+export type NavigationPreviousParam<T extends string> = { route: T, label?: string; params?: NavigationRouteParamTuple[] }
+
+export type LocalCallbackColorMode = CallbackPromiseGeneric<ISelectOption<string>>
+export type LocalCallbackGuiAlert = CallbackPromiseFull<string, boolean>;
+export type LocalCallbackGuiConfirm = CallbackPromiseFull<string | { message: string; ok?: string; cancel?: string }, boolean>;
+export type LocalCallbackGeocode = CallbackPromiseFull<GeolocationPoint, GeocoderReverseResult | undefined>;
+export type LocalCallbackGeocodeCurrent = CallbackPromiseResult<IClientGeolocationPosition>;
+export type LocalCallbackImgBin = CallbackPromiseFull<string, Uint8Array | undefined>;
+export type LocalCallbackPhotosAdd = CallbackPromiseResult<string>;
+export type LocalCallbackPhotosAddMultiple = CallbackPromiseResult<string[]>;
+export type LocalCallbackPhotosUpload = CallbackPromiseFull<{ url: string, path: string }, MediaImageUploadResult | undefined>;
diff --git a/apps-lib/src/lib/types/ui.ts b/apps-lib/src/lib/types/ui.ts
@@ -0,0 +1,230 @@
+import type { ThemeLayer } from "@radroots/themes";
+import type { CallbackPromise, CallbackPromiseGeneric } from "@radroots/utils";
+import type { EntryStyle, IGlyph, IInput, IInputValue, ISelect, ITextArea } from "./components";
+import type { GlyphKey, LayerGlyphBasisKind, LoadingBlades, LoadingDimension } from "./lib";
+
+export type IDisabled = {
+ disabled: boolean | never;
+};
+
+export type IDisabledOpt = Partial<IDisabled>;
+
+export type IBasisOpt<T extends object> = T | undefined;
+
+export type IBasis<T> = {
+ basis: T;
+};
+
+export type ICb = {
+ callback: CallbackPromise | never;
+};
+
+export type ICbOpt = Partial<ICb>;
+
+export type ICbG<T> = {
+ callback: CallbackPromiseGeneric<T> | never;
+};
+
+export type ICbMouseEventOpt = ICbGOpt<
+ MouseEvent & {
+ currentTarget: EventTarget & HTMLImageElement;
+ }
+>;
+
+export type ICbGOpt<T> = Partial<ICbG<T>>;
+
+export type ICl = {
+ classes: string | never;
+};
+
+export type IClOpt = Partial<ICl>;
+
+export type IClWrap = {
+ classes_wr: string | never;
+};
+
+export type IClOptWrap = Partial<IClWrap>;
+
+export type IId = {
+ id: string | never;
+};
+
+export type IIdOpt = Partial<IId>;
+
+export type IGl = {
+ glyph: IGlyph | never;
+};
+
+export type IGlOpt = Partial<IGl>;
+
+export type IGlyphKey = {
+ glyph: GlyphKey
+};
+
+export type ILy = {
+ layer: ThemeLayer | never;
+};
+
+export type ILyOpt = Partial<ILy>;
+
+export type ILableFieldsSwap = {
+ toggle: boolean;
+ on: IClOpt & {
+ value: string;
+ },
+ off: IClOpt & {
+ value: string;
+ },
+};
+
+export type ILabelSwap = {
+ swap: ILableFieldsSwap;
+}
+
+export type ILabelTupFields = {
+ left?: ILableFields[];
+ right?: ILableFields[];
+};
+
+export type ILabelTup = {
+ label: ILabelTupFields;
+};
+
+export type ILableFields = & {
+ classes_wrap?: string
+ classes?: string;
+ kind?: LayerGlyphBasisKind;
+ hide_truncate?: boolean;
+ hide_active?: boolean;
+} & (
+ ({
+ value: string;
+ } | ILabelSwap)
+ | IGl
+ );
+
+export type ILabel = {
+ label: ILableFields;
+};
+
+export type ILabelOpt = Partial<ILabel>;
+
+export type ILoadSymbol = IClOpt & {
+ color?: 'white';
+ blades?: LoadingBlades;
+ dim?: LoadingDimension;
+};
+
+export type IIdG<T extends string> = {
+ id: T | never;
+};
+
+export type IIdGOpt<T extends string> = Partial<IIdG<T>>;
+
+export type IIdWrap = {
+ id_wrap: string | never;
+};
+
+export type IIdWrapOpt = Partial<IIdWrap>;
+
+export type ILabelValue = {
+ label: IClOpt & {
+ value: string;
+ };
+};
+
+export type ILabelDisplay = IIdWrapOpt & IClOpt & ILabelValue & ILyOpt & {
+ style?: EntryStyle;
+};
+
+
+export type ILoading = {
+ loading: boolean | never;
+};
+
+export type ILoadingOpt = Partial<ILoading>;
+
+export type IEntryWrap = IClOpt & IIdOpt & ILyOpt & {
+ style?: EntryStyle;
+ style_a?: true;
+ no_pad?: true;
+ fade?: {
+ in?: SvelteTransitionConfig;
+ out?: SvelteTransitionConfig;
+ };
+}
+
+export type IEntryLine = ILoadingOpt & {
+ wrap?: IEntryWrap;
+ el: IInputValue<string>;
+ notify_inline?: {
+ glyph: GlyphKey | IGlyph;
+ };
+};
+
+export type IEntryLineIdb = ILoadingOpt & {
+ wrap?: IEntryWrap;
+ el: IInput<string>;
+ notify_inline?: {
+ glyph: GlyphKey | IGlyph;
+ };
+};
+
+export type IEntryLineSelectIdb = ILoadingOpt & {
+ wrap?: IEntryWrap;
+ el_input: IInput<string>;
+ el_sel: ISelect;
+ /*notify_inline?: {
+ glyph: GlyphKey | IGlyph;
+ };*/
+};
+
+export type IEntryMultiLine = {
+ wrap?: IEntryWrap;
+ el: ITextArea;
+ notify_inline?: {
+ glyph: GlyphKey | IGlyph;
+ };
+}
+
+export type IEnvelopeLower = {
+ visible: boolean;
+ close: CallbackPromise;
+ full_cover?: boolean;
+ label_close?: string | true;
+};
+
+export type IButtonRound = IClOpt & ILoadingOpt & {
+ label: string;
+ callback: CallbackPromise;
+};
+
+export type INavigationRoutePreventRouteNav = {
+ prevent_route?: {
+ callback: CallbackPromise;
+ };
+};
+
+export type INavigationRoutePreventRoute = {
+ prevent_route: CallbackPromise;
+};
+
+export type IImage = IIdOpt & IClOpt & {
+ src?: string;
+ alt?: string;
+};
+
+export type IImageBlob = IIdOpt & IClOpt & {
+ data: Uint8Array | undefined;
+ alt?: string;
+};
+
+export type IImageSource = IIdOpt & IClOpt & {
+ src?: string;
+ alt?: string;
+};
+
+export type IImagePath = Omit<IImage, 'src'> & {
+ path?: string;
+};
+
diff --git a/apps-lib/src/lib/utils/app/carousel.ts b/apps-lib/src/lib/utils/app/carousel.ts
@@ -0,0 +1,83 @@
+
+import { carousel_active, carousel_num, casl_i, casl_imax } from "$lib/stores/carousel";
+import { exe_iter } from "@radroots/utils";
+import { get_store } from "../lib";
+
+const CAROUSEL_DELAY_MS = 150;
+
+const get_slide_container = <T extends string>(
+ view: T,
+): Element | undefined => {
+ const el = document.querySelector(
+ `[data-carousel-container="${view}"]`,
+ );
+ return el ? el : undefined;
+};
+
+const get_slide_item = <T extends string>(view: T): Element | undefined => {
+ const el = document.querySelector(`[data-carousel-item="${view}"]`);
+ return el ? el : undefined;
+};
+
+const carousel_dec_handler = async <T extends string>(
+ view: T,
+): Promise<void> => {
+ const $carousel_active = get_store(carousel_active);
+ if ($carousel_active) return;
+ carousel_active.set(true);
+ const slide_item = get_slide_item<T>(view);
+ const slide_container = get_slide_container<T>(view);
+ if (slide_container && slide_item) {
+ const slide_w = slide_item?.clientWidth || 0;
+ slide_container.scrollLeft -= slide_w;
+ const $casl_i = get_store(casl_i);
+ casl_i.set(Math.max($casl_i - 1, 0));
+ }
+ carousel_active.set(false);
+};
+
+const carousel_inc_handler = async <T extends string>(
+ view: T,
+): Promise<void> => {
+ const $carousel_active = get_store(carousel_active);
+ if ($carousel_active) return;
+ carousel_active.set(true);
+ const slide_item = get_slide_item<T>(view);
+ const slide_container = get_slide_container<T>(view);
+ if (slide_container && slide_item) {
+ const slide_w = slide_item?.clientWidth || 0;
+ slide_container.scrollLeft += slide_w;
+ const $casl_i = get_store(casl_i);
+ const $casl_imax = get_store(casl_imax);
+ casl_i.set(
+ Math.min($casl_i + 1, $casl_imax),
+ );
+ }
+ carousel_active.set(false);
+};
+
+export const carousel_inc = async <T extends string>(
+ view: T,
+ duration: number = CAROUSEL_DELAY_MS
+): Promise<void> => {
+ const $carousel_num = get_store(carousel_num);
+ carousel_num.set(1);
+ await exe_iter(async () => carousel_inc_handler(view), $carousel_num, duration);
+};
+
+
+export const carousel_dec = async <T extends string>(
+ view: T,
+ duration: number = CAROUSEL_DELAY_MS
+): Promise<void> => {
+ const $carousel_num = get_store(carousel_num);
+ carousel_num.set(1);
+ await exe_iter(async () => carousel_dec_handler(view), $carousel_num, duration);
+};
+
+export const carousel_init = async <T extends string>(view: T, num_max: number): Promise<void> => {
+ await carousel_dec(view);
+ casl_i.set(0);
+ casl_imax.set(num_max);
+ carousel_num.set(1);
+};
diff --git a/apps-lib/src/lib/utils/browser.ts b/apps-lib/src/lib/utils/browser.ts
@@ -0,0 +1,92 @@
+export type BrowserPlatformInfo = {
+ os: string;
+ browser: string;
+ version: string;
+};
+
+const remove_excess_mozilla_and_version = /^mozilla\/\d\.\d\W/;
+const browser_pattern = /(\w+)\/(\d+\.\d+(?:\.\d+)?(?:\.\d+)?)/g;
+const engine_and_version_pattern = /^(ver|cri|gec)/;
+const brand_list = ['chrome', 'opera', 'safari', 'edge', 'firefox'];
+
+const mobiles: Record<string, RegExp> = {
+ iphone: /iphone/,
+ ipad: /ipad|macintosh/,
+ android: /android/
+};
+
+const desktops: Record<string, RegExp> = {
+ windows: /win/,
+ mac: /macintosh/,
+ linux: /linux/
+};
+
+const parse_user_agent_string = (ua_string: string): BrowserPlatformInfo => {
+ const ua = ua_string.toLowerCase().replace(remove_excess_mozilla_and_version, '');
+
+ const mobile_os = Object.keys(mobiles).find(
+ (key) => mobiles[key].test(ua) && navigator.maxTouchPoints >= 1
+ );
+ const desktop_os = Object.keys(desktops).find((key) => desktops[key].test(ua));
+ const os = mobile_os || desktop_os || '';
+
+ const browser_matches = ua.match(browser_pattern);
+ const version_regex = /version\/(\d+(\.\d+)*)/;
+ const safari_version_match = ua.match(version_regex);
+ const safari_version = Array.isArray(safari_version_match) ? safari_version_match[1] : null;
+
+ const browser_offset =
+ browser_matches && browser_matches.length > 2 && !engine_and_version_pattern.test(browser_matches[1])
+ ? 1
+ : 0;
+ const browser_result =
+ browser_matches && browser_matches[browser_matches.length - 1 - browser_offset].split('/');
+ const browser = browser_result ? browser_result[0] : '';
+ const version = safari_version || (browser_result ? browser_result[1] : '');
+
+ return { os, browser, version };
+};
+
+export const browser_platform = (): BrowserPlatformInfo | undefined => {
+ if (typeof navigator !== 'undefined') {
+ if ('userAgentData' in navigator && navigator.userAgentData) {
+ const ua_data = navigator.userAgentData as {
+ platform: string;
+ brands: { brand: string; version: string }[];
+ };
+ const os = ua_data.platform.toLowerCase();
+ let browser = '';
+ let version = '';
+
+ if (Array.isArray(ua_data.brands)) {
+ for (const { brand, version: brand_version } of ua_data.brands) {
+ const lower_brand = brand.toLowerCase();
+ if (brand_list.some((b) => lower_brand.includes(b))) {
+ browser = lower_brand;
+ version = brand_version;
+ break;
+ }
+ }
+ }
+
+ if (!browser && navigator.userAgent) {
+ return parse_user_agent_string(navigator.userAgent);
+ }
+ return { os, browser, version };
+ }
+
+ if (navigator.userAgent) {
+ return parse_user_agent_string(navigator.userAgent);
+ }
+
+ const nav_platform = navigator.platform;
+ if (!nav_platform) return undefined;
+ return {
+ os: nav_platform,
+ browser: '',
+ version: ''
+ };
+ }
+
+ return undefined;
+};
diff --git a/apps-lib/src/lib/utils/geo.ts b/apps-lib/src/lib/utils/geo.ts
@@ -0,0 +1,8 @@
+import type { GeolocationPoint } from "@radroots/utils";
+
+export const geop_is_valid = (point?: GeolocationPoint): boolean => {
+ if (!point) return false;
+ return !(point.lat === 0 && point.lng === 0);
+};
+
+export const geop_init = (): GeolocationPoint => ({ lat: 0, lng: 0 });
diff --git a/apps-lib/src/lib/utils/idb/kv.ts b/apps-lib/src/lib/utils/idb/kv.ts
@@ -0,0 +1,54 @@
+import { browser } from "$app/environment";
+import { fmt_id } from "../lib";
+
+//@ts-ignore
+const idb_name = import.meta.env.VITE_PUBLIC_IDB_NAME;
+if (!idb_name) throw new Error('Error: VITE_PUBLIC_IDB_NAME is required');
+
+export let idb: Keyva;
+if (browser) idb = new Keyva({ name: idb_name });
+
+export const idb_init = async (): Promise<void> => {
+ if (!browser) return;
+ const range = Keyva.prefix(`*`);
+ const idb_list = await idb.each({ range }, `keys`);
+ await Promise.all(idb_list.map((i) => idb.delete(i)));
+};
+
+export const idb_init_page = async (): Promise<void> => {
+ if (!browser) return;
+ const idb_pref = fmt_id();
+ const range = Keyva.prefix(idb_pref);
+ const idb_list = await idb.each({ range }, `keys`);
+ await Promise.all(idb_list.map((i) => idb.delete(i)));
+};
+
+export const idb_sync = async (list: [string, string][]): Promise<void> => {
+ if (!browser) return;
+ for (const [key, val] of list) await idb.set(key, val);
+};
+
+export class IdbLib<T extends string> {
+ private _idb: Keyva;
+
+ constructor(kv: Keyva) {
+ this._idb = kv;
+ }
+ public init = async () => {
+ await idb_init_page();
+ }
+
+ public save = async (key: T, value: string) => {
+ await this._idb.set(fmt_id(key), value);
+ }
+
+ public read = async (key: T): Promise<string | undefined> => {
+ const result = await this._idb.get<string>(fmt_id(key));
+ if (result) return result;
+ return undefined;
+ }
+
+ public del = async (key: T) => {
+ await this._idb.delete(fmt_id(key));
+ }
+}
+\ No newline at end of file
diff --git a/apps-lib/src/lib/utils/idb/lib.ts b/apps-lib/src/lib/utils/idb/lib.ts
@@ -1,86 +0,0 @@
-import { browser } from '$app/environment';
-import { fmt_id } from '$lib';
-
-const IDB_NAME = import.meta.env.VITE_PUBLIC_IDB_NAME;
-if (!IDB_NAME) throw new Error('VITE_PUBLIC_IDB_NAME is required');
-
-let _kv: Keyva | null = null;
-export function get_idb(): Keyva {
- if (!browser) throw new Error('IndexedDB not available on server');
- if (!_kv) _kv = new Keyva({ name: IDB_NAME });
- return _kv;
-}
-
-export class IdbLib<
- CKey extends string,
- CMap extends Record<CKey, any>,
- SKey extends string,
- SMap extends Record<SKey, any>
-> {
- private get kv(): Keyva {
- return get_idb();
- }
-
- public async save_global(key: CKey, value: CMap[CKey]): Promise<void> {
- await this.kv.set(key, value);
- }
-
- public async read_global<K extends CKey>(key: K): Promise<CMap[K] | undefined> {
- const result = await this.kv.get<CMap[K]>(key);
- return result ? result : undefined;
- }
-
- public async del_global(key: CKey): Promise<void> {
- await this.kv.delete(key);
- }
-
- public async list_global(): Promise<Array<[CKey, CMap[CKey]]>> {
- const entries = await this.kv.each<CMap[CKey]>();
- return entries.map(([rk, v]) => [rk as CKey, v]);
- }
-
- private keyof(key: SKey): string {
- return fmt_id(key);
- }
-
- public async save(key: SKey, value: SMap[SKey]): Promise<void> {
- await this.kv.set(this.keyof(key), value);
- }
-
- public async read(key: SKey): Promise<SMap[SKey] | undefined> {
- const result = await this.kv.get<SMap[SKey]>(this.keyof(key));
- return result ? result : undefined;
- }
-
- public async del(key: SKey): Promise<void> {
- await this.kv.delete(this.keyof(key));
- }
-
- public async list(): Promise<Array<[SKey, SMap[SKey]]>> {
- const prefix = fmt_id();
- const range = Keyva.prefix(prefix);
- const entries = await this.kv.each<SMap[SKey]>({ range });
- return entries.map(([rk, v]) => {
- const str = String(rk);
- const suffix = str.startsWith(prefix) ? str.slice(prefix.length) : str;
- return [suffix as SKey, v];
- });
- }
-
- public async clear_all(): Promise<void> {
- const range = Keyva.prefix('*');
- const keys = await this.kv.each({ range }, 'keys');
- if (keys.length) await Promise.all(keys.map((k) => this.kv.delete(k)));
- }
-
- public async clear_scope(): Promise<void> {
- const prefix = fmt_id();
- const range = Keyva.prefix(prefix);
- const keys = await this.kv.each({ range }, 'keys');
- if (keys.length) await Promise.all(keys.map((k) => this.kv.delete(k)));
- }
-
- public async sync_batch(entries: Array<[string, any]>): Promise<void> {
- if (entries.length) await this.kv.set(entries);
- }
-}
diff --git a/apps-lib/src/lib/utils/lib.ts b/apps-lib/src/lib/utils/lib.ts
@@ -1,9 +1,28 @@
import { browser } from '$app/environment';
-import type { ThemeMode } from '$lib/types/lib';
+import { goto } from '$app/navigation';
+import { win_h, win_w } from '$lib/stores/app';
+import type { CallbackRoute, NavigationParamTuple, NavigationRouteParamKey, NavigationRouteParamTuple, } from '$lib/types/lib';
+import type { ThemeLayer, ThemeMode } from '@radroots/themes';
+import type { FilePath } from '@radroots/utils';
+import { getContext, setContext } from "svelte";
import { get } from "svelte/store";
+export const symbols = {
+ bullet: '•',
+ dash: `—`,
+ up: `↑`,
+ down: `↓`,
+ percent: `%`
+};
+
export const get_store = get;
+export const get_context = <M extends Record<string, any>, K extends keyof M>(key: K): M[K] =>
+ getContext(key as string) as M[K];
+
+export const set_context = <M extends Record<string, any>, K extends keyof M>(key: K, value: M[K]): void =>
+ setContext(key as string, value);
+
export const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
export const trim_slashes = (path: string): string =>
@@ -27,6 +46,19 @@ export const fmt_id = (raw_id?: string): string => {
return `*${prefix}${suffix}`;
};
+export const view_effect = <T extends string>(view: T): void => {
+ if (!browser) return;
+ for (const el of document.querySelectorAll(`[data-view]`)) {
+ if (el.getAttribute(`data-view`) !== view) el.classList.add(`hidden`)
+ else el.classList.remove(`hidden`)
+ }
+};
+
+export const el_id = (id: string): HTMLElement | undefined => {
+ const el = document.getElementById(id);
+ return el ? el : undefined;
+};
+
export const build_storage_key = (
raw_id: string,
base_prefix: string
@@ -41,4 +73,118 @@ export const get_system_theme = (): ThemeMode => {
export const theme_set = (theme_key: string, color_mode: ThemeMode): void => {
document.documentElement.setAttribute("data-theme", `${theme_key}_${color_mode}`);
};
-export const fmt_cl = (classes?: string): string => `${classes || ``}`;
-\ No newline at end of file
+export const fmt_cl = (classes?: string): string => `${classes || ``}`;
+
+export const handle_err = async (e: unknown, fcall: string): Promise<void> => {
+ try {
+ console.log(`[handle_err] `, e, fcall)
+ /*return void await catch_err(e, fcall, async (opts) => {
+ console.log(`handle_err e `, e)
+ console.log(JSON.stringify(opts, null, 4), `handle_err opts`)
+ });*/
+ } catch (e) {
+ console.log(`(handle_err) `, e)
+ }
+};
+
+export const window_set = (): void => {
+ if (!browser) return;
+ win_h.set(window.innerHeight);
+ win_w.set(window.innerWidth);
+};
+
+export const parse_layer = (layer?: number, layer_default?: ThemeLayer): ThemeLayer => {
+ switch (layer) {
+ case 0:
+ case 1:
+ case 2:
+ return layer;
+ default:
+ return layer_default ? layer_default : 0;
+ };
+};
+
+export const value_constrain = (regex_charset: RegExp, value: string): string => {
+ return value
+ .split(``)
+ .filter((char) => regex_charset.test(char))
+ .join(``);
+};
+
+
+export const encode_query_params = <T extends string>(params_list: NavigationParamTuple<T>[] = []): string => {
+ let query = "";
+ for (const [k, v] of params_list) {
+ if (k && v) {
+ if (query) query += `&`;
+ query += `${k.trim()}=${encodeURIComponent(v.trim())}`;
+ }
+ }
+ return query ? `?${query}` : ``;
+};
+
+export const encode_route = <TRoute extends string, TParam extends string>(route: TRoute, params_list?: NavigationParamTuple<TParam>[]): string => {
+ const query = encode_query_params(params_list);
+ if (!query) return route;
+ return `${route === `/` ? `/` : route.replace(/\/+$/, ``)}${query}`;
+};
+
+export const debounce = <T extends (...args: any[]) => void>(
+ fn: T,
+ delay: number
+): T => {
+ let timeout: ReturnType<typeof setTimeout>;
+ return ((...args: any[]) => {
+ clearTimeout(timeout);
+ timeout = setTimeout(() => fn(...args), delay);
+ }) as T;
+};
+
+export const create_router = <T extends string>() => {
+ const router = async (nav_route: T, params: NavigationRouteParamTuple[] = []): Promise<void> => {
+ try {
+ if (params.length) await goto(encode_route<T, NavigationRouteParamKey>(nav_route, params));
+ else await goto(nav_route);
+ } catch (e) {
+ handle_err(e, `route`);
+ };
+ };
+ return router;
+};
+
+export const get_locale = (locales: string[]): string => {
+ const { language: navigator_locale } = navigator;
+ let locale = `en`;
+ if (locales.some(i => i === navigator_locale.toLowerCase())) locale = navigator.language;
+ else if (locales.some(i => i === navigator_locale.slice(0, 2).toLowerCase())) locale = navigator_locale.slice(0, 2);
+ return locale.toLowerCase();
+};
+
+export const callback_route = async <T extends string>(callback_route: CallbackRoute<T>): Promise<void> => {
+ if (`route` in callback_route) {
+ if (typeof callback_route.route === `string`) return void await goto(callback_route.route);
+ else return void await goto(
+ encode_route<string, NavigationRouteParamKey>(
+ callback_route.route[0],
+ callback_route.route[1],
+ ),
+ );
+ }
+ return void await callback_route();
+};
+
+export const to_arr_buf = (u8: Uint8Array): ArrayBuffer => {
+ return u8.slice().buffer;
+};
+
+export const parse_file_path = (file_path: string): FilePath | undefined => {
+ const file_path_spl = file_path.split(`/`);
+ const file_path_file = file_path_spl[file_path_spl.length - 1] || ``;
+ const [file_name, mime_type] = file_path_file.split(`.`);
+ if (!file_name || !mime_type) return undefined;
+ return {
+ file_path,
+ file_name,
+ mime_type
+ };
+};
+\ No newline at end of file
diff --git a/apps-lib/vite.config.ts b/apps-lib/vite.config.ts
@@ -2,5 +2,5 @@ import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
- plugins: [sveltekit()]
+ plugins: [sveltekit() as any]
});