@samline/notify 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,49 @@
1
+ :root{
2
+ --sileo-bg: #0f1724;
3
+ --sileo-foreground: #ffffff;
4
+ --sileo-success: #16a34a;
5
+ --sileo-error: #ef4444;
6
+ --sileo-info: #2563eb;
7
+ --sileo-warning: #f59e0b;
8
+ --sileo-gap: 12px;
9
+ }
10
+
11
+ .sileo-toaster{
12
+ position: fixed;
13
+ z-index: 9999;
14
+ display: flex;
15
+ flex-direction: column;
16
+ gap: var(--sileo-gap);
17
+ pointer-events: none;
18
+ }
19
+
20
+ .sileo-toaster[data-position="top-right"]{ top: 16px; right: 16px; align-items:flex-end; }
21
+ .sileo-toaster[data-position="top-left"]{ top: 16px; left: 16px; align-items:flex-start; }
22
+ .sileo-toaster[data-position="bottom-right"]{ bottom: 16px; right: 16px; align-items:flex-end; }
23
+ .sileo-toaster[data-position="bottom-left"]{ bottom: 16px; left: 16px; align-items:flex-start; }
24
+
25
+ .sileo-toast{
26
+ pointer-events: auto;
27
+ min-width: 220px;
28
+ max-width: 360px;
29
+ background: var(--sileo-bg);
30
+ color: var(--sileo-foreground);
31
+ padding: 12px 14px;
32
+ border-radius: 10px;
33
+ box-shadow: 0 6px 18px rgba(2,6,23,0.6);
34
+ display: flex;
35
+ gap: 8px;
36
+ align-items: center;
37
+ position: relative;
38
+ overflow: hidden;
39
+ }
40
+
41
+ .sileo-toast[data-type="success"]{ border-left: 4px solid var(--sileo-success); }
42
+ .sileo-toast[data-type="error"]{ border-left: 4px solid var(--sileo-error); }
43
+ .sileo-toast[data-type="info"]{ border-left: 4px solid var(--sileo-info); }
44
+ .sileo-toast[data-type="warning"]{ border-left: 4px solid var(--sileo-warning); }
45
+
46
+ .sileo-toast-header{ font-weight: 600; font-size: 14px; }
47
+ .sileo-toast-desc{ font-size: 13px; opacity: 0.85; margin-top: 4px; }
48
+ .sileo-toast-btn{ margin-left: 8px; background: transparent; color: inherit; border: 1px solid rgba(255,255,255,0.08); padding: 6px 8px; border-radius: 6px; cursor: pointer; }
49
+ .sileo-toast-close{ position: absolute; right: 8px; top: 6px; background: transparent; border: none; color: inherit; font-size: 16px; cursor: pointer; }
@@ -0,0 +1,3 @@
1
+ import { ToastItem } from '../core/index';
2
+ export declare const toasts: import("svelte/store").Writable<ToastItem[]>;
3
+ export declare function initSileoStore(): () => boolean;
@@ -0,0 +1,13 @@
1
+ import { notify as coreNotify, sileo as coreSileo } from '../core/index';
2
+ import { initToasters, POSITIONS } from './toasterManager';
3
+ export { initToasters, POSITIONS };
4
+ export declare const notify: (opts: import("../core/index").ToastOptions) => string;
5
+ type VanillaAPI = {
6
+ sileo: typeof coreSileo;
7
+ initToasters: typeof initToasters;
8
+ notify: typeof notify;
9
+ controller?: typeof coreNotify;
10
+ notifications?: any;
11
+ };
12
+ declare const defaultExport: VanillaAPI;
13
+ export default defaultExport;
@@ -0,0 +1,6 @@
1
+ declare const POSITIONS: readonly ["top-left", "top-center", "top-right", "bottom-left", "bottom-center", "bottom-right"];
2
+ export type Position = typeof POSITIONS[number];
3
+ export declare function initToasters(root?: HTMLElement, positions?: Position[]): {
4
+ destroy(): void;
5
+ };
6
+ export { POSITIONS };
@@ -0,0 +1,20 @@
1
+ import { App } from 'vue';
2
+ export declare const Toaster: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
3
+ position: {
4
+ type: StringConstructor;
5
+ default: string;
6
+ };
7
+ }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
8
+ [key: string]: any;
9
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
10
+ position: {
11
+ type: StringConstructor;
12
+ default: string;
13
+ };
14
+ }>> & Readonly<{}>, {
15
+ position: string;
16
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
17
+ declare const _default: {
18
+ install(app: App): void;
19
+ };
20
+ export default _default;
@@ -0,0 +1,44 @@
1
+ # Browser (UMD / no-bundler)
2
+
3
+ Quick start
4
+
5
+ Include the UMD bundle and stylesheet in a static page:
6
+
7
+ ```html
8
+ <link rel="stylesheet" href="/path/to/dist/styles.css">
9
+ <script src="/path/to/dist/notify.umd.js"></script>
10
+ <script>
11
+ const api = window.notify || window.notifications;
12
+ api.initToasters(document.body, ['top-right']);
13
+ // convenience: api.notify
14
+ api.notify({ title: 'Hello', description: 'No-bundler usage', type: 'info' });
15
+ </script>
16
+ ```
17
+
18
+ Notes
19
+
20
+ - The UMD bundle exposes `window.notify` (preferred). For compatibility it also exposes `window.notifications` with the previous API shape.
21
+ - Make sure to load `dist/styles.css` for styles and animations.
22
+
23
+ ## CDN / Browser
24
+
25
+ Use the browser build when your project loads scripts directly in the page and cannot compile npm modules (Shopify, WordPress, plain HTML templates).
26
+
27
+ Example using the UMD build (replace path/version as needed):
28
+
29
+ ```html
30
+ <link rel="stylesheet" href="/path/to/dist/styles.css">
31
+ <script src="/path/to/dist/notify.umd.js"></script>
32
+ <script>
33
+ document.addEventListener('DOMContentLoaded', () => {
34
+ const api = window.notify || window.notifications;
35
+ api.initToasters(document.body, ['top-right']);
36
+ api.notify({ title: 'Hello', description: 'No-bundler usage', type: 'info' });
37
+ });
38
+ </script>
39
+ ```
40
+
41
+ ### Notes
42
+
43
+ The browser bundle exposes `window.notify` (preferred) and for compatibility also exposes `window.notifications`; if these globals conflict with other scripts use the module builds instead.
44
+ Include `dist/styles.css` for styles and animations when using the UMD/browser bundle.
package/docs/react.md ADDED
@@ -0,0 +1,64 @@
1
+ # React
2
+
3
+ Installation
4
+
5
+ ```bash
6
+ npm install @samline/notify react react-dom
7
+ ```
8
+
9
+ Basic usage
10
+
11
+ ```tsx
12
+ import React from 'react';
13
+ import { notify, sileo } from '@samline/notify';
14
+ import { Toaster } from '@samline/notify/react';
15
+
16
+ function App(){
17
+ return (
18
+ <>
19
+ <Toaster />
20
+ <button onClick={() => notify.show({ title: 'Done', type: 'success' })}>Show</button>
21
+ </>
22
+ )
23
+ }
24
+ ```
25
+
26
+ Notes
27
+
28
+ - The `Toaster` component subscribes to the core controller and renders toasts.
29
+ - You can customize positions and styles by importing `dist/styles.css`.
30
+
31
+ ## Quick start
32
+
33
+ Install:
34
+
35
+ ```bash
36
+ npm install @samline/notify react react-dom
37
+ ```
38
+
39
+ Import and render the React `Toaster` in your app:
40
+
41
+ ```tsx
42
+ import React from 'react';
43
+ import { Toaster } from '@samline/notify/react';
44
+ import { notify, sileo } from '@samline/notify';
45
+
46
+ export function App(){
47
+ return (
48
+ <div>
49
+ <Toaster />
50
+ <button onClick={() => notify.show({ title: 'Done', type: 'success' })}>Show</button>
51
+ </div>
52
+ );
53
+ }
54
+ ```
55
+
56
+ ## API
57
+
58
+ - `Toaster` component: mounts a toast container and subscribes to the core controller.
59
+ - Primary API: use `notify` (e.g. `notify.show(...)`). A backward-compatible `sileo` alias is also available: `sileo.show(...)`.
60
+
61
+ ## Notes
62
+
63
+ - The React adapter renders toasts in a React-friendly way and is SSR-safe when used with client-only mounting.
64
+ - To customize styles, import `dist/styles.css` or override the CSS variables defined in the stylesheet.
package/docs/svelte.md ADDED
@@ -0,0 +1,50 @@
1
+ # Svelte
2
+
3
+ Quick start
4
+ # Svelte
5
+
6
+ Quick start
7
+
8
+ ```bash
9
+ npm install @samline/notify svelte
10
+ ```
11
+
12
+ Usage
13
+
14
+ ```svelte
15
+ <script>
16
+ import Toaster, { initSileoStore } from '@samline/notify/svelte';
17
+ initSileoStore();
18
+ </script>
19
+
20
+ <Toaster />
21
+
22
+ <button on:click={() => import('@samline/notify').then(m => m.notify.show({ title: 'Svelte' }))}>Show</button>
23
+ ```
24
+
25
+ TypeScript
26
+
27
+ If you use TypeScript, run `npm run typecheck` and `npx svelte-check` during development.
28
+
29
+ ## API
30
+
31
+ - `Toaster.svelte` — component that renders toasts and subscribes to the core controller.
32
+ - `initSileoStore()` — helper to wire the core `notify`/`sileo` controller to a Svelte store.
33
+
34
+ ## Examples
35
+
36
+ ```svelte
37
+ <script>
38
+ import Toaster, { initSileoStore } from '@samline/notify/svelte';
39
+ initSileoStore();
40
+ function show(){ import('@samline/notify').then(m => m.notify.show({ title: 'From Svelte' })); }
41
+ </script>
42
+
43
+ <Toaster />
44
+ <button on:click={show}>Show</button>
45
+ ```
46
+
47
+ ## Notes
48
+
49
+ - Use `npx svelte-check` and `npm run typecheck` when developing with TypeScript.
50
+ - The Svelte adapter is lightweight and subscribes to the shared core controller; use `initSileoStore()` in your app root to wire the store.
@@ -0,0 +1,79 @@
1
+ # Vanilla
2
+
3
+ ## Quick Start
4
+
5
+ Import and initialize the vanilla (DOM) renderer:
6
+
7
+ ```ts
8
+ import { notify, sileo, initToasters } from '@samline/notify/vanilla';
9
+
10
+ // Mount the containers (defaults to document.body)
11
+ initToasters(document.body, ['top-right']);
12
+
13
+ // Show a notification (use `notify(...)` as the recommended API)
14
+ notify({ title: 'Saved', description: 'Your changes have been saved', type: 'success' });
15
+ ```
16
+
17
+ ## API
18
+
19
+ ```ts
20
+ // Core controller (primary: `notify`, compatibility alias: `sileo`)
21
+ notify.show(options)
22
+ notify.success(options)
23
+ notify.error(options)
24
+ notify.info(options)
25
+ notify.warning(options)
26
+ notify.action(options)
27
+ notify.promise(promise, { loading, success, error })
28
+ notify.dismiss(id)
29
+ notify.clear()
30
+ ```
31
+
32
+ ### Parameters
33
+
34
+ | Parameter | Type | Description |
35
+ | --- | --- | --- |
36
+ | `options` | object | Toast creation options (see Options below) |
37
+
38
+ ### Returns
39
+
40
+ `string | void` — `show` returns a toast id when created.
41
+
42
+ ## Options
43
+
44
+ | Property | Type | Default | Description |
45
+ | --- | --- | --- | --- |
46
+ | `title` | `string` | — | Toast title |
47
+ | `description` | `string` | — | Optional body text |
48
+ | `type` | `info\|success\|error\|warning` | `info` | Visual intent |
49
+ | `position` | `string` | `top-right` | Container position |
50
+ | `duration` | `number` | `4000` | Auto-dismiss timeout in ms (`null` = sticky) |
51
+ | `button` | `{ title, onClick }` | — | Optional action button |
52
+
53
+ ## Examples
54
+
55
+ ### Basic
56
+
57
+ ```ts
58
+ import { notify, initToasters } from '@samline/notify/vanilla';
59
+ initToasters();
60
+ notify.success({ title: 'Saved' });
61
+ ```
62
+
63
+ ### Promise flow
64
+
65
+ ```ts
66
+ notify.promise(fetch('/api/save'), {
67
+ loading: { title: 'Saving...' },
68
+ success: { title: 'Done', type: 'success' },
69
+ error: { title: 'Failed', type: 'error' }
70
+ });
71
+ ```
72
+
73
+ ## When to use
74
+
75
+ Use the vanilla entry when you interact directly with DOM nodes or need a framework-agnostic API.
76
+
77
+ ## Accessibility
78
+
79
+ Toaster containers use `role="status"` and `aria-live="polite"` by default. Respect `prefers-reduced-motion` in your UI.
package/docs/vue.md ADDED
@@ -0,0 +1,41 @@
1
+ # Vue 3
2
+
3
+ Quick start
4
+
5
+ ```js
6
+ import { createApp } from 'vue';
7
+ import App from './App.vue';
8
+ import Notifications from '@samline/notify/vue';
9
+
10
+ const app = createApp(App);
11
+ app.use(Notifications);
12
+ app.mount('#app');
13
+ ```
14
+
15
+ Usage
16
+
17
+ - The plugin registers a `Toaster` component and provides a `useSileo()` composable to access the controller.
18
+
19
+ Example in a single-file component:
20
+
21
+ ```vue
22
+ <template>
23
+ <Toaster />
24
+ <button @click="show">Show</button>
25
+ </template>
26
+
27
+ <script setup>
28
+ import { notify, sileo } from '@samline/notify';
29
+ function show(){ notify.show({ title: 'Hello from Vue' }); }
30
+ </script>
31
+
32
+ ## API
33
+
34
+ - `Notifications` plugin: registers a global component `SileoToaster` and exposes `app.config.globalProperties.$sileo`.
35
+ - `useSileo()` (composable): returns the `sileo` controller instance for programmatic use.
36
+
37
+ ## Notes
38
+
39
+ - The Vue adapter integrates with the Vue lifecycle and works with Vue 3's Composition API.
40
+ - To customize appearance, import `dist/styles.css` or override the CSS variables.
41
+ ```
@@ -0,0 +1,25 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
6
+ <title>Samline Notifications - No Bundler Example</title>
7
+ <link rel="stylesheet" href="../../dist/styles.css">
8
+ </head>
9
+ <body>
10
+ <h1>Samline Notifications - No Bundler</h1>
11
+ <button id="ok">Show toast</button>
12
+
13
+ <script src="../../dist/notify.umd.js"></script>
14
+ <script>
15
+ // UMD global: window.notify (preferred). For backward compatibility the API
16
+ // object also exposes a `notifications` key when necessary.
17
+ const api = window.notify || window.notifications;
18
+ const manager = api.initToasters(document.body, ['top-right','bottom-left']);
19
+
20
+ document.getElementById('ok').addEventListener('click', () => {
21
+ api.notify({ title: 'Hola', description: 'Toast desde UMD', duration: 4000 });
22
+ });
23
+ </script>
24
+ </body>
25
+ </html>
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@samline/notify",
3
+ "version": "0.1.0",
4
+ "description": "Notifications engine inspired by Sileo, with adapters for vanilla, React, Vue and Svelte.",
5
+ "main": "dist/index.cjs.js",
6
+ "module": "dist/index.esm.js",
7
+ "unpkg": "dist/notify.umd.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ "./styles.css": "./dist/styles.css",
11
+ "./package.json": "./package.json"
12
+ },
13
+ "scripts": {
14
+ "build": "rollup -c",
15
+ "test": "vitest",
16
+ "typecheck": "tsc --noEmit",
17
+ "prepare": "npm run build"
18
+ },
19
+ "keywords": [
20
+ "notifications",
21
+ "toasts",
22
+ "sileo",
23
+ "samline"
24
+ ],
25
+ "license": "MIT",
26
+ "peerDependencies": {
27
+ "react": ">=18",
28
+ "react-dom": ">=18",
29
+ "vue": "^3.5.31",
30
+ "svelte": "^5.55.0"
31
+ },
32
+ "devDependencies": {
33
+ "@rollup/plugin-commonjs": "^25.0.0",
34
+ "@rollup/plugin-node-resolve": "^15.0.0",
35
+ "@rollup/plugin-typescript": "^11.0.0",
36
+ "rollup": "^3.0.0",
37
+ "tslib": "^2.8.1",
38
+ "rollup-plugin-copy": "^3.4.0",
39
+ "svelte-check": "^3.3.0",
40
+ "typescript": "^5.0.0",
41
+ "vitest": "^1.0.0",
42
+ "@types/node": "^20.0.0",
43
+ "@types/react": "^18.2.0",
44
+ "@types/react-dom": "^18.2.0"
45
+ }
46
+ ,
47
+ "devDependenciesMeta": {
48
+ "@types/react": { "optional": true },
49
+ "@types/react-dom": { "optional": true }
50
+ }
51
+ ,
52
+ "dependencies": {
53
+ "motion": "^10.0.0"
54
+ }
55
+ }
@@ -0,0 +1,39 @@
1
+ import resolve from '@rollup/plugin-node-resolve';
2
+ import commonjs from '@rollup/plugin-commonjs';
3
+ import typescript from '@rollup/plugin-typescript';
4
+ import copy from 'rollup-plugin-copy';
5
+
6
+ export default [
7
+ // ESM + CJS
8
+ {
9
+ input: 'src/vanilla/index.ts',
10
+ output: [
11
+ { file: 'dist/index.esm.js', format: 'es', exports: 'named' },
12
+ { file: 'dist/index.cjs.js', format: 'cjs', exports: 'named' }
13
+ ],
14
+ plugins: [resolve(), commonjs(), typescript({ tsconfig: './tsconfig.json' }),
15
+ copy({
16
+ targets: [
17
+ { src: 'src/styles/sileo.css', dest: 'dist', rename: 'styles.css' }
18
+ ],
19
+ verbose: true
20
+ })]
21
+ },
22
+ // UMD build for browser (vanilla DOM use)
23
+ {
24
+ input: 'src/vanilla/index.ts',
25
+ output: {
26
+ file: 'dist/notify.umd.js',
27
+ format: 'umd',
28
+ exports: 'named',
29
+ name: 'notify'
30
+ },
31
+ plugins: [resolve(), commonjs(), typescript({ tsconfig: './tsconfig.json' }),
32
+ copy({
33
+ targets: [
34
+ { src: 'src/styles/sileo.css', dest: 'dist', rename: 'styles.css' }
35
+ ],
36
+ verbose: true
37
+ })]
38
+ }
39
+ ];
@@ -0,0 +1,26 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { sileo } from './index';
3
+
4
+ describe('sileo core', () => {
5
+ it('shows and dismisses a toast', () => {
6
+ const id = sileo.success({ title: 't1' });
7
+ const items = sileo.getToasts();
8
+ expect(items.find((i) => i.id === id)).toBeDefined();
9
+ sileo.dismiss(id);
10
+ const after = sileo.getToasts();
11
+ expect(after.find((i) => i.id === id)).toBeUndefined();
12
+ });
13
+
14
+ it('promise flow shows loading then result', async () => {
15
+ const p = new Promise((resolve) => setTimeout(() => resolve('ok'), 50));
16
+ const promise = sileo.promise(p as any, {
17
+ loading: { title: 'loading' },
18
+ success: { title: 'done' },
19
+ error: { title: 'err' }
20
+ });
21
+ await promise;
22
+ const items = sileo.getToasts();
23
+ // success toast should be present
24
+ expect(items.some((t) => t.options.title === 'done')).toBe(true);
25
+ });
26
+ });
@@ -0,0 +1,122 @@
1
+ export type Position =
2
+ | 'top-left'
3
+ | 'top-center'
4
+ | 'top-right'
5
+ | 'bottom-left'
6
+ | 'bottom-center'
7
+ | 'bottom-right';
8
+
9
+ export type ToastType = 'success' | 'error' | 'info' | 'warning' | 'loading' | 'action';
10
+
11
+ export interface ToastButton {
12
+ title: string;
13
+ onClick: () => void;
14
+ }
15
+
16
+ export interface ToastOptions {
17
+ title?: string;
18
+ description?: string;
19
+ position?: Position;
20
+ duration?: number | null; // ms, null = sticky
21
+ type?: ToastType;
22
+ button?: ToastButton;
23
+ [key: string]: any;
24
+ }
25
+
26
+ export interface ToastItem {
27
+ id: string;
28
+ options: ToastOptions;
29
+ createdAt: number;
30
+ }
31
+
32
+ type Listener = (items: ToastItem[]) => void;
33
+
34
+ class NotifyController {
35
+ private toasts: ToastItem[] = [];
36
+ private listeners = new Set<Listener>();
37
+ private idCounter = 1;
38
+
39
+ private nextId() {
40
+ return `notify_${Date.now()}_${this.idCounter++}`;
41
+ }
42
+
43
+ subscribe(fn: Listener) {
44
+ this.listeners.add(fn);
45
+ fn(this.toasts.slice());
46
+ return () => this.listeners.delete(fn);
47
+ }
48
+
49
+ private notify() {
50
+ const snapshot = this.toasts.slice();
51
+ this.listeners.forEach((l) => l(snapshot));
52
+ }
53
+
54
+ getToasts() {
55
+ return this.toasts.slice();
56
+ }
57
+
58
+ show(opts: ToastOptions): string {
59
+ const id = this.nextId();
60
+ const item: ToastItem = { id, options: { ...opts }, createdAt: Date.now() };
61
+ this.toasts.push(item);
62
+ this.notify();
63
+ return id;
64
+ }
65
+
66
+ success(opts: ToastOptions) {
67
+ return this.show({ ...opts, type: 'success' });
68
+ }
69
+
70
+ error(opts: ToastOptions) {
71
+ return this.show({ ...opts, type: 'error' });
72
+ }
73
+
74
+ info(opts: ToastOptions) {
75
+ return this.show({ ...opts, type: 'info' });
76
+ }
77
+
78
+ warning(opts: ToastOptions) {
79
+ return this.show({ ...opts, type: 'warning' });
80
+ }
81
+
82
+ action(opts: ToastOptions) {
83
+ return this.show({ ...opts, type: 'action' });
84
+ }
85
+
86
+ dismiss(id: string) {
87
+ const idx = this.toasts.findIndex((t) => t.id === id);
88
+ if (idx >= 0) {
89
+ this.toasts.splice(idx, 1);
90
+ this.notify();
91
+ }
92
+ }
93
+
94
+ clear(position?: Position) {
95
+ if (position) {
96
+ this.toasts = this.toasts.filter((t) => t.options.position !== position);
97
+ } else {
98
+ this.toasts = [];
99
+ }
100
+ this.notify();
101
+ }
102
+
103
+ promise<T = any>(p: Promise<T>, opts: { loading: ToastOptions; success?: ToastOptions | ((r: T) => ToastOptions); error?: ToastOptions | ((e: any) => ToastOptions); position?: Position; }) {
104
+ const loadingId = this.show({ ...(opts.loading || {}), type: 'loading', position: opts.position });
105
+ p.then((res) => {
106
+ this.dismiss(loadingId);
107
+ const successOpt = typeof opts.success === 'function' ? (opts.success as any)(res) : opts.success;
108
+ if (successOpt) this.show({ ...(successOpt || {}), type: 'success', position: opts.position });
109
+ }).catch((err) => {
110
+ this.dismiss(loadingId);
111
+ const errorOpt = typeof opts.error === 'function' ? (opts.error as any)(err) : opts.error;
112
+ if (errorOpt) this.show({ ...(errorOpt || {}), type: 'error', position: opts.position });
113
+ });
114
+ return p;
115
+ }
116
+ }
117
+
118
+ export const notify = new NotifyController();
119
+ // backward compatibility alias
120
+ export const sileo = notify as unknown as NotifyController;
121
+
122
+ export default notify;