@effect-tui/react 0.9.0 → 0.9.1
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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-tui/react",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "React bindings for @effect-tui/core",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"prepublishOnly": "bun run typecheck && bun run build"
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@effect-tui/core": "^0.9.
|
|
86
|
+
"@effect-tui/core": "^0.9.1",
|
|
87
87
|
"@effect/platform": "^0.94.0",
|
|
88
88
|
"@effect/platform-bun": "^0.87.0",
|
|
89
89
|
"@effect/rpc": "^0.73.0",
|
package/src/dev/Toast.tsx
CHANGED
|
@@ -2,18 +2,20 @@
|
|
|
2
2
|
// Beautiful, minimal notifications that appear at the top of the screen
|
|
3
3
|
|
|
4
4
|
import { Colors } from "@effect-tui/core"
|
|
5
|
-
import { createContext, type ReactNode, useCallback, useContext, useState } from "react"
|
|
5
|
+
import { createContext, type ReactNode, useCallback, useContext, useEffect, useState } from "react"
|
|
6
6
|
|
|
7
7
|
// ─────────────────────────────────────────────────────────────
|
|
8
8
|
// Types
|
|
9
9
|
// ─────────────────────────────────────────────────────────────
|
|
10
10
|
|
|
11
|
-
export type ToastType = "success" | "info" | "warning" | "error"
|
|
11
|
+
export type ToastType = "success" | "info" | "warning" | "error" | "screenshot"
|
|
12
12
|
|
|
13
13
|
export interface Toast {
|
|
14
14
|
id: number
|
|
15
15
|
message: string
|
|
16
16
|
type: ToastType
|
|
17
|
+
/** Timestamp when the toast was created (for animations) */
|
|
18
|
+
createdAt: number
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export interface ToastContextValue {
|
|
@@ -48,6 +50,7 @@ const TOAST_STYLES: Record<
|
|
|
48
50
|
info: { bg: Colors.rgb(30, 50, 80), fg: Colors.rgb(140, 180, 230), icon: "ℹ" },
|
|
49
51
|
warning: { bg: Colors.rgb(80, 60, 20), fg: Colors.rgb(230, 200, 100), icon: "⚠" },
|
|
50
52
|
error: { bg: Colors.rgb(80, 30, 30), fg: Colors.rgb(230, 140, 140), icon: "✗" },
|
|
53
|
+
screenshot: { bg: Colors.rgb(30, 70, 40), fg: Colors.rgb(140, 230, 140), icon: "📷" },
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -61,7 +64,7 @@ export function ToastProvider({ children }: { children: ReactNode }) {
|
|
|
61
64
|
|
|
62
65
|
const show = useCallback((message: string, type: ToastType = "info", durationMs = 2000) => {
|
|
63
66
|
const id = ++toastId
|
|
64
|
-
setToasts((prev) => [...prev, { id, message, type }])
|
|
67
|
+
setToasts((prev) => [...prev, { id, message, type, createdAt: Date.now() }])
|
|
65
68
|
|
|
66
69
|
setTimeout(() => {
|
|
67
70
|
setToasts((prev) => prev.filter((t) => t.id !== id))
|
|
@@ -75,6 +78,65 @@ export function ToastProvider({ children }: { children: ReactNode }) {
|
|
|
75
78
|
return <ToastContext.Provider value={{ toasts, show, dismiss }}>{children}</ToastContext.Provider>
|
|
76
79
|
}
|
|
77
80
|
|
|
81
|
+
// ─────────────────────────────────────────────────────────────
|
|
82
|
+
// Screenshot Toast Animation
|
|
83
|
+
// ─────────────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
// Animation phases: camera → flash → success
|
|
86
|
+
type ScreenshotPhase = "camera" | "flash" | "success"
|
|
87
|
+
|
|
88
|
+
const SCREENSHOT_PHASES: Record<
|
|
89
|
+
ScreenshotPhase,
|
|
90
|
+
{ icon: string; bg: ReturnType<typeof Colors.rgb>; fg: ReturnType<typeof Colors.rgb> }
|
|
91
|
+
> = {
|
|
92
|
+
camera: {
|
|
93
|
+
icon: "📷",
|
|
94
|
+
bg: Colors.rgb(40, 50, 60),
|
|
95
|
+
fg: Colors.rgb(180, 200, 220),
|
|
96
|
+
},
|
|
97
|
+
flash: {
|
|
98
|
+
icon: "⚡",
|
|
99
|
+
bg: Colors.rgb(255, 255, 200), // Bright flash!
|
|
100
|
+
fg: Colors.rgb(60, 60, 40),
|
|
101
|
+
},
|
|
102
|
+
success: {
|
|
103
|
+
icon: "✓",
|
|
104
|
+
bg: Colors.rgb(30, 70, 40),
|
|
105
|
+
fg: Colors.rgb(140, 230, 140),
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function ScreenshotToast({ message, createdAt }: { message: string; createdAt: number }) {
|
|
110
|
+
const [phase, setPhase] = useState<ScreenshotPhase>("camera")
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
// Phase timing: camera (0-120ms) → flash (120-280ms) → success (280ms+)
|
|
114
|
+
const elapsed = Date.now() - createdAt
|
|
115
|
+
const cameraDelay = Math.max(0, 120 - elapsed)
|
|
116
|
+
const flashDelay = Math.max(0, 280 - elapsed)
|
|
117
|
+
|
|
118
|
+
const flashTimer = setTimeout(() => setPhase("flash"), cameraDelay)
|
|
119
|
+
const successTimer = setTimeout(() => setPhase("success"), flashDelay)
|
|
120
|
+
|
|
121
|
+
return () => {
|
|
122
|
+
clearTimeout(flashTimer)
|
|
123
|
+
clearTimeout(successTimer)
|
|
124
|
+
}
|
|
125
|
+
}, [createdAt])
|
|
126
|
+
|
|
127
|
+
const style = SCREENSHOT_PHASES[phase]
|
|
128
|
+
const content = ` ${style.icon} ${message} `
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<hstack>
|
|
132
|
+
<spacer />
|
|
133
|
+
<box bg={style.bg} padding={{ x: 1 }}>
|
|
134
|
+
<text fg={style.fg}>{content}</text>
|
|
135
|
+
</box>
|
|
136
|
+
</hstack>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
78
140
|
// ─────────────────────────────────────────────────────────────
|
|
79
141
|
// Toast Display Component
|
|
80
142
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -86,6 +148,12 @@ export function ToastContainer() {
|
|
|
86
148
|
|
|
87
149
|
// Show only the most recent toast
|
|
88
150
|
const toast = toasts[toasts.length - 1]
|
|
151
|
+
|
|
152
|
+
// Special animated toast for screenshots
|
|
153
|
+
if (toast.type === "screenshot") {
|
|
154
|
+
return <ScreenshotToast message={toast.message} createdAt={toast.createdAt} />
|
|
155
|
+
}
|
|
156
|
+
|
|
89
157
|
const style = TOAST_STYLES[toast.type]
|
|
90
158
|
|
|
91
159
|
// Compact pill in upper right
|