@tooee/toasts 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,2 @@
1
+ export declare function ToastContainer(): import("react").ReactNode;
2
+ //# sourceMappingURL=ToastContainer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToastContainer.d.ts","sourceRoot":"","sources":["../src/ToastContainer.tsx"],"names":[],"mappings":"AAyBA,wBAAgB,cAAc,8BA6B7B"}
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx } from "@opentui/react/jsx-runtime";
2
+ import { useTerminalDimensions } from "@opentui/react";
3
+ import { useTheme } from "@tooee/themes";
4
+ import { useToast } from "./ToastProvider.js";
5
+ const LEVEL_ICONS = {
6
+ info: "ℹ",
7
+ success: "✓",
8
+ warning: "⚠",
9
+ error: "✗",
10
+ };
11
+ function getLevelColor(theme, level) {
12
+ switch (level) {
13
+ case "info":
14
+ return theme.info;
15
+ case "success":
16
+ return theme.success;
17
+ case "warning":
18
+ return theme.warning;
19
+ case "error":
20
+ return theme.error;
21
+ }
22
+ }
23
+ export function ToastContainer() {
24
+ const { currentToast } = useToast();
25
+ const { theme } = useTheme();
26
+ const { width: termWidth } = useTerminalDimensions();
27
+ if (!currentToast)
28
+ return null;
29
+ const maxWidth = Math.min(50, termWidth - 4);
30
+ const borderColor = getLevelColor(theme, currentToast.level);
31
+ const icon = LEVEL_ICONS[currentToast.level];
32
+ return (_jsx("box", { position: "absolute", bottom: 1, right: 1, maxWidth: maxWidth, border: true, borderColor: borderColor, backgroundColor: theme.backgroundPanel, paddingLeft: 1, paddingRight: 1, children: _jsx("text", { content: `${icon} ${currentToast.message}`, fg: theme.text }) }));
33
+ }
34
+ //# sourceMappingURL=ToastContainer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToastContainer.js","sourceRoot":"","sources":["../src/ToastContainer.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAG7C,MAAM,WAAW,GAA+B;IAC9C,IAAI,EAAE,GAAG;IACT,OAAO,EAAE,GAAG;IACZ,OAAO,EAAE,GAAG;IACZ,KAAK,EAAE,GAAG;CACX,CAAA;AAED,SAAS,aAAa,CAAC,KAA2C,EAAE,KAAiB;IACnF,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,MAAM;YACT,OAAO,KAAK,CAAC,IAAI,CAAA;QACnB,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC,OAAO,CAAA;QACtB,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC,OAAO,CAAA;QACtB,KAAK,OAAO;YACV,OAAO,KAAK,CAAC,KAAK,CAAA;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,EAAE,YAAY,EAAE,GAAG,QAAQ,EAAE,CAAA;IACnC,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,CAAA;IAC5B,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,qBAAqB,EAAE,CAAA;IAEpD,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAA;IAE9B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,GAAG,CAAC,CAAC,CAAA;IAC5C,MAAM,WAAW,GAAG,aAAa,CAAC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,CAAA;IAC5D,MAAM,IAAI,GAAG,WAAW,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA;IAE5C,OAAO,CACL,cACE,QAAQ,EAAC,UAAU,EACnB,MAAM,EAAE,CAAC,EACT,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,QAAQ,EAClB,MAAM,QACN,WAAW,EAAE,WAAW,EACxB,eAAe,EAAE,KAAK,CAAC,eAAe,EACtC,WAAW,EAAE,CAAC,EACd,YAAY,EAAE,CAAC,YAEf,eACE,OAAO,EAAE,GAAG,IAAI,IAAI,YAAY,CAAC,OAAO,EAAE,EAC1C,EAAE,EAAE,KAAK,CAAC,IAAI,GACd,GACE,CACP,CAAA;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ToastController } from "./types.js";
3
+ export declare function ToastProvider({ children }: {
4
+ children: ReactNode;
5
+ }): ReactNode;
6
+ export declare function useToast(): ToastController;
7
+ //# sourceMappingURL=ToastProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToastProvider.d.ts","sourceRoot":"","sources":["../src/ToastProvider.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAA4B,eAAe,EAAE,MAAM,YAAY,CAAA;AAa3E,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,aAqDlE;AAED,wBAAgB,QAAQ,IAAI,eAAe,CAM1C"}
@@ -0,0 +1,59 @@
1
+ import { jsx as _jsx } from "@opentui/react/jsx-runtime";
2
+ import { createContext, useContext, useState, useCallback, useRef, useEffect } from "react";
3
+ const DEFAULT_DURATIONS = {
4
+ info: 2000,
5
+ success: 1500,
6
+ warning: 3000,
7
+ error: 5000,
8
+ };
9
+ let nextToastId = 0;
10
+ const ToastContext = createContext(null);
11
+ export function ToastProvider({ children }) {
12
+ const [currentToast, setCurrentToast] = useState(null);
13
+ const timerRef = useRef(null);
14
+ const clearTimer = useCallback(() => {
15
+ if (timerRef.current !== null) {
16
+ clearTimeout(timerRef.current);
17
+ timerRef.current = null;
18
+ }
19
+ }, []);
20
+ const dismiss = useCallback(() => {
21
+ clearTimer();
22
+ setCurrentToast(null);
23
+ }, [clearTimer]);
24
+ const toast = useCallback((options) => {
25
+ clearTimer();
26
+ const level = options.level ?? "info";
27
+ const duration = options.duration ?? DEFAULT_DURATIONS[level] ?? 2000;
28
+ const id = options.id ?? `toast-${nextToastId++}`;
29
+ const entry = {
30
+ id,
31
+ message: options.message,
32
+ level,
33
+ duration,
34
+ };
35
+ setCurrentToast(entry);
36
+ timerRef.current = setTimeout(() => {
37
+ setCurrentToast((current) => (current?.id === id ? null : current));
38
+ timerRef.current = null;
39
+ }, duration);
40
+ }, [clearTimer]);
41
+ // Cleanup on unmount
42
+ useEffect(() => {
43
+ return () => clearTimer();
44
+ }, [clearTimer]);
45
+ const controller = {
46
+ toast,
47
+ dismiss,
48
+ currentToast,
49
+ };
50
+ return _jsx(ToastContext.Provider, { value: controller, children: children });
51
+ }
52
+ export function useToast() {
53
+ const ctx = useContext(ToastContext);
54
+ if (!ctx) {
55
+ throw new Error("useToast must be used within a ToastProvider");
56
+ }
57
+ return ctx;
58
+ }
59
+ //# sourceMappingURL=ToastProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ToastProvider.js","sourceRoot":"","sources":["../src/ToastProvider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAI3F,MAAM,iBAAiB,GAA2B;IAChD,IAAI,EAAE,IAAI;IACV,OAAO,EAAE,IAAI;IACb,OAAO,EAAE,IAAI;IACb,KAAK,EAAE,IAAI;CACZ,CAAA;AAED,IAAI,WAAW,GAAG,CAAC,CAAA;AAEnB,MAAM,YAAY,GAAG,aAAa,CAAyB,IAAI,CAAC,CAAA;AAEhE,MAAM,UAAU,aAAa,CAAC,EAAE,QAAQ,EAA2B;IACjE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAoB,IAAI,CAAC,CAAA;IACzE,MAAM,QAAQ,GAAG,MAAM,CAAuC,IAAI,CAAC,CAAA;IAEnE,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;QAClC,IAAI,QAAQ,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAC9B,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;YAC9B,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAA;QACzB,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE;QAC/B,UAAU,EAAE,CAAA;QACZ,eAAe,CAAC,IAAI,CAAC,CAAA;IACvB,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAA;IAEhB,MAAM,KAAK,GAAG,WAAW,CACvB,CAAC,OAAqB,EAAE,EAAE;QACxB,UAAU,EAAE,CAAA;QAEZ,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAAA;QACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,iBAAiB,CAAC,KAAK,CAAC,IAAI,IAAI,CAAA;QACrE,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,IAAI,SAAS,WAAW,EAAE,EAAE,CAAA;QAEjD,MAAM,KAAK,GAAe;YACxB,EAAE;YACF,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,KAAK;YACL,QAAQ;SACT,CAAA;QAED,eAAe,CAAC,KAAK,CAAC,CAAA;QAEtB,QAAQ,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YACjC,eAAe,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAA;YACnE,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAA;QACzB,CAAC,EAAE,QAAQ,CAAC,CAAA;IACd,CAAC,EACD,CAAC,UAAU,CAAC,CACb,CAAA;IAED,qBAAqB;IACrB,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,GAAG,EAAE,CAAC,UAAU,EAAE,CAAA;IAC3B,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAA;IAEhB,MAAM,UAAU,GAAoB;QAClC,KAAK;QACL,OAAO;QACP,YAAY;KACb,CAAA;IAED,OAAO,KAAC,YAAY,CAAC,QAAQ,IAAC,KAAK,EAAE,UAAU,YAAG,QAAQ,GAAyB,CAAA;AACrF,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,MAAM,GAAG,GAAG,UAAU,CAAC,YAAY,CAAC,CAAA;IACpC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAA;IACjE,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { ToastProvider, useToast } from "./ToastProvider.js";
2
+ export { ToastContainer } from "./ToastContainer.js";
3
+ export type { ToastLevel, ToastOptions, ToastEntry, ToastController } from "./types.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { ToastProvider, useToast } from "./ToastProvider.js";
2
+ export { ToastContainer } from "./ToastContainer.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA"}
@@ -0,0 +1,19 @@
1
+ export type ToastLevel = "info" | "success" | "warning" | "error";
2
+ export interface ToastOptions {
3
+ message: string;
4
+ level?: ToastLevel;
5
+ duration?: number;
6
+ id?: string;
7
+ }
8
+ export interface ToastEntry {
9
+ id: string;
10
+ message: string;
11
+ level: ToastLevel;
12
+ duration: number;
13
+ }
14
+ export interface ToastController {
15
+ toast(options: ToastOptions): void;
16
+ dismiss(): void;
17
+ currentToast: ToastEntry | null;
18
+ }
19
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;AAEjE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,UAAU,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAA;IAClC,OAAO,IAAI,IAAI,CAAA;IACf,YAAY,EAAE,UAAU,GAAG,IAAI,CAAA;CAChC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@tooee/toasts",
3
+ "version": "0.1.0",
4
+ "description": "Toast notification system for Tooee",
5
+ "license": "MIT",
6
+ "author": "Gareth Andrew",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/gingerhendrix/tooee.git",
10
+ "directory": "packages/toasts"
11
+ },
12
+ "homepage": "https://github.com/gingerhendrix/tooee",
13
+ "bugs": "https://github.com/gingerhendrix/tooee/issues",
14
+ "keywords": ["tui", "terminal", "cli", "opentui", "toasts", "notifications"],
15
+ "type": "module",
16
+ "exports": {
17
+ ".": {
18
+ "import": {
19
+ "@tooee/source": "./src/index.ts",
20
+ "default": "./dist/index.js"
21
+ }
22
+ }
23
+ },
24
+ "files": ["dist", "src"],
25
+ "scripts": {
26
+ "typecheck": "tsc --noEmit"
27
+ },
28
+ "dependencies": {
29
+ "@tooee/themes": "workspace:*"
30
+ },
31
+ "devDependencies": {
32
+ "@opentui/core": "^0.1.67",
33
+ "@opentui/react": "^0.1.67",
34
+ "@types/bun": "^1.3.5",
35
+ "@types/react": "^19.1.10",
36
+ "typescript": "^5.8.3"
37
+ },
38
+ "peerDependencies": {
39
+ "@opentui/core": "^0.1.67",
40
+ "@opentui/react": "^0.1.67",
41
+ "react": "^18.0.0 || ^19.0.0"
42
+ }
43
+ }
@@ -0,0 +1,55 @@
1
+ import { useTerminalDimensions } from "@opentui/react"
2
+ import { useTheme } from "@tooee/themes"
3
+ import { useToast } from "./ToastProvider.js"
4
+ import type { ToastLevel } from "./types.js"
5
+
6
+ const LEVEL_ICONS: Record<ToastLevel, string> = {
7
+ info: "ℹ",
8
+ success: "✓",
9
+ warning: "⚠",
10
+ error: "✗",
11
+ }
12
+
13
+ function getLevelColor(theme: ReturnType<typeof useTheme>["theme"], level: ToastLevel): string {
14
+ switch (level) {
15
+ case "info":
16
+ return theme.info
17
+ case "success":
18
+ return theme.success
19
+ case "warning":
20
+ return theme.warning
21
+ case "error":
22
+ return theme.error
23
+ }
24
+ }
25
+
26
+ export function ToastContainer() {
27
+ const { currentToast } = useToast()
28
+ const { theme } = useTheme()
29
+ const { width: termWidth } = useTerminalDimensions()
30
+
31
+ if (!currentToast) return null
32
+
33
+ const maxWidth = Math.min(50, termWidth - 4)
34
+ const borderColor = getLevelColor(theme, currentToast.level)
35
+ const icon = LEVEL_ICONS[currentToast.level]
36
+
37
+ return (
38
+ <box
39
+ position="absolute"
40
+ bottom={1}
41
+ right={1}
42
+ maxWidth={maxWidth}
43
+ border
44
+ borderColor={borderColor}
45
+ backgroundColor={theme.backgroundPanel}
46
+ paddingLeft={1}
47
+ paddingRight={1}
48
+ >
49
+ <text
50
+ content={`${icon} ${currentToast.message}`}
51
+ fg={theme.text}
52
+ />
53
+ </box>
54
+ )
55
+ }
@@ -0,0 +1,77 @@
1
+ import { createContext, useContext, useState, useCallback, useRef, useEffect } from "react"
2
+ import type { ReactNode } from "react"
3
+ import type { ToastOptions, ToastEntry, ToastController } from "./types.js"
4
+
5
+ const DEFAULT_DURATIONS: Record<string, number> = {
6
+ info: 2000,
7
+ success: 1500,
8
+ warning: 3000,
9
+ error: 5000,
10
+ }
11
+
12
+ let nextToastId = 0
13
+
14
+ const ToastContext = createContext<ToastController | null>(null)
15
+
16
+ export function ToastProvider({ children }: { children: ReactNode }) {
17
+ const [currentToast, setCurrentToast] = useState<ToastEntry | null>(null)
18
+ const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
19
+
20
+ const clearTimer = useCallback(() => {
21
+ if (timerRef.current !== null) {
22
+ clearTimeout(timerRef.current)
23
+ timerRef.current = null
24
+ }
25
+ }, [])
26
+
27
+ const dismiss = useCallback(() => {
28
+ clearTimer()
29
+ setCurrentToast(null)
30
+ }, [clearTimer])
31
+
32
+ const toast = useCallback(
33
+ (options: ToastOptions) => {
34
+ clearTimer()
35
+
36
+ const level = options.level ?? "info"
37
+ const duration = options.duration ?? DEFAULT_DURATIONS[level] ?? 2000
38
+ const id = options.id ?? `toast-${nextToastId++}`
39
+
40
+ const entry: ToastEntry = {
41
+ id,
42
+ message: options.message,
43
+ level,
44
+ duration,
45
+ }
46
+
47
+ setCurrentToast(entry)
48
+
49
+ timerRef.current = setTimeout(() => {
50
+ setCurrentToast((current) => (current?.id === id ? null : current))
51
+ timerRef.current = null
52
+ }, duration)
53
+ },
54
+ [clearTimer],
55
+ )
56
+
57
+ // Cleanup on unmount
58
+ useEffect(() => {
59
+ return () => clearTimer()
60
+ }, [clearTimer])
61
+
62
+ const controller: ToastController = {
63
+ toast,
64
+ dismiss,
65
+ currentToast,
66
+ }
67
+
68
+ return <ToastContext.Provider value={controller}>{children}</ToastContext.Provider>
69
+ }
70
+
71
+ export function useToast(): ToastController {
72
+ const ctx = useContext(ToastContext)
73
+ if (!ctx) {
74
+ throw new Error("useToast must be used within a ToastProvider")
75
+ }
76
+ return ctx
77
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { ToastProvider, useToast } from "./ToastProvider.js"
2
+ export { ToastContainer } from "./ToastContainer.js"
3
+ export type { ToastLevel, ToastOptions, ToastEntry, ToastController } from "./types.js"
package/src/types.ts ADDED
@@ -0,0 +1,21 @@
1
+ export type ToastLevel = "info" | "success" | "warning" | "error"
2
+
3
+ export interface ToastOptions {
4
+ message: string
5
+ level?: ToastLevel
6
+ duration?: number
7
+ id?: string
8
+ }
9
+
10
+ export interface ToastEntry {
11
+ id: string
12
+ message: string
13
+ level: ToastLevel
14
+ duration: number
15
+ }
16
+
17
+ export interface ToastController {
18
+ toast(options: ToastOptions): void
19
+ dismiss(): void
20
+ currentToast: ToastEntry | null
21
+ }