@notapublicfigureanymore/relay-sdk 0.0.4 → 0.0.6

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/README.md ADDED
@@ -0,0 +1,313 @@
1
+ # @notapublicfigureanymore/relay-sdk
2
+
3
+ [![npm version](https://badge.fury.io/js/%40notapublicfigureanymore%2Frelay-sdk.svg)](https://badge.fury.io/js/%40notapublicfigureanymore%2Frelay-sdk)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ Complete documentation for building Relay Apps with `@notapublicfigureanymore/relay-sdk`.
7
+
8
+ ## Features
9
+
10
+ - Platform Components: Keyboard avoiding views, safe area support, haptic feedback
11
+ - UI Components: Complete component library with 40+ modern components
12
+ - Modern Hooks: Device sensors, notifications, clipboard, camera access
13
+ - TypeScript: Full TypeScript support with comprehensive type definitions
14
+ - Fast: Built with modern React and optimized for performance
15
+ - Small: Tree-shakable with minimal bundle impact
16
+
17
+ ## Installation
18
+
19
+ Install the SDK in your Relay App project.
20
+
21
+ ```bash
22
+ npm install @notapublicfigureanymore/relay-sdk
23
+ ```
24
+
25
+ Peer dependencies required:
26
+
27
+ ```bash
28
+ npm install react@^19.0.0 react-dom@^19.0.0 tailwindcss@>=3
29
+ ```
30
+
31
+
32
+ ## Platform & UI
33
+
34
+ Core components for handling safe areas and device capabilities.
35
+
36
+ ### SafeAreaProvider
37
+
38
+ Essential provider that handles notch and safe area insets. Wrap your app in this.
39
+
40
+ ```tsx
41
+ import { SafeAreaProvider } from '@notapublicfigureanymore/relay-sdk';
42
+
43
+ export default function App() {
44
+ return (
45
+ <SafeAreaProvider>
46
+ <YourApp />
47
+ </SafeAreaProvider>
48
+ );
49
+ }
50
+ ```
51
+
52
+ ### usePlatform()
53
+
54
+ Get information about the current device environment.
55
+
56
+ ```tsx
57
+ import { usePlatform } from '@notapublicfigureanymore/relay-sdk';
58
+
59
+ const { os, isMobile } = usePlatform();
60
+
61
+ if (isMobile) {
62
+ // Render mobile layout
63
+ }
64
+ ```
65
+
66
+ ## UI Components
67
+
68
+ Complete component library with 40+ modern components including Button, Card, Dialog, Input, Select, Tabs, etc.
69
+
70
+ ```tsx
71
+ import {
72
+ Button,
73
+ Card,
74
+ Dialog,
75
+ Input,
76
+ Select,
77
+ Tabs,
78
+ // ... and 30+ more components
79
+ } from '@notapublicfigureanymore/relay-sdk';
80
+ ```
81
+
82
+ ## Notifications
83
+
84
+ Send system notifications and manage app badges. The SDK automatically uses your app's icon if available.
85
+
86
+ ### useNotifications()
87
+
88
+ ```tsx
89
+ import { useNotifications } from '@notapublicfigureanymore/relay-sdk';
90
+
91
+ export function NotificationDemo() {
92
+ const {
93
+ requestPermission,
94
+ sendNotification,
95
+ setBadge,
96
+ permission
97
+ } = useNotifications();
98
+
99
+ const handleNotify = async () => {
100
+ // 1. Check/Request Permission
101
+ if (permission !== 'granted') {
102
+ await requestPermission();
103
+ }
104
+
105
+ // 2. Send Notification
106
+ // Note: Automatically uses app icon from <link rel="icon">
107
+ sendNotification('New Message', {
108
+ body: 'You have a new message from Relay',
109
+ tag: 'message-1' // Prevents duplicates
110
+ });
111
+
112
+ // 3. Update App Badge
113
+ await setBadge(1);
114
+ };
115
+ }
116
+ ```
117
+
118
+ ## Clipboard
119
+
120
+ Access the system clipboard with built-in history management.
121
+
122
+ ### useClipboard()
123
+
124
+ ```tsx
125
+ import { useClipboard } from '@notapublicfigureanymore/relay-sdk';
126
+
127
+ export function ClipboardDemo() {
128
+ const {
129
+ copyText,
130
+ readText,
131
+ history
132
+ } = useClipboard();
133
+
134
+ const handleCopy = async () => {
135
+ await copyText('Copied from Relay!');
136
+ };
137
+
138
+ const handlePaste = async () => {
139
+ const text = await readText();
140
+ console.log('Clipboard content:', text);
141
+ };
142
+
143
+ // Access history
144
+ // history = [{ type: 'text', content: '...', timestamp: 123 }, ...]
145
+ }
146
+ ```
147
+
148
+ ## Geolocation
149
+
150
+ Access device location and calculate distances.
151
+
152
+ ### useGeolocation()
153
+
154
+ ```tsx
155
+ import { useGeolocation } from '@notapublicfigureanymore/relay-sdk';
156
+
157
+ export function LocationDemo() {
158
+ const {
159
+ latitude,
160
+ longitude,
161
+ loading,
162
+ error,
163
+ getLocation
164
+ } = useGeolocation();
165
+
166
+ useEffect(() => {
167
+ getLocation();
168
+ }, []);
169
+
170
+ if (loading) return <div>Locating...</div>;
171
+ if (error) return <div>Error: {error.message}</div>;
172
+
173
+ return (
174
+ <div>
175
+ Position: {latitude}, {longitude}
176
+ </div>
177
+ );
178
+ }
179
+ ```
180
+
181
+ ### Camera Access
182
+
183
+ ```tsx
184
+ import { useCamera } from '@notapublicfigureanymore/relay-sdk';
185
+
186
+ function CameraExample() {
187
+ const { devices, startCamera, stopCamera, capturePhoto } = useCamera();
188
+
189
+ return (
190
+ <div>
191
+ <select onChange={(e) => startCamera(e.target.value)}>
192
+ <option>Select Camera</option>
193
+ {devices.map(device => (
194
+ <option key={device.deviceId} value={device.deviceId}>
195
+ {device.label}
196
+ </option>
197
+ ))}
198
+ </select>
199
+ <Button onClick={capturePhoto}>Take Photo</Button>
200
+ <Button onClick={stopCamera}>Stop Camera</Button>
201
+ </div>
202
+ );
203
+ }
204
+ ```
205
+
206
+ ### Platform Detection
207
+
208
+ ```tsx
209
+ import {
210
+ useColorScheme,
211
+ useSafeArea,
212
+ useAppState,
213
+ useStatusBar
214
+ } from '@notapublicfigureanymore/relay-sdk';
215
+
216
+ function PlatformExample() {
217
+ const colorScheme = useColorScheme();
218
+ const safeArea = useSafeArea();
219
+ const appState = useAppState();
220
+
221
+ // Set status bar color based on theme
222
+ useStatusBar(colorScheme === 'dark' ? '#000000' : '#ffffff');
223
+
224
+ return (
225
+ <div>
226
+ <p>Theme: {colorScheme}</p>
227
+ <p>App State: {appState}</p>
228
+ <p>Safe Area Top: {safeArea.top}px</p>
229
+ </div>
230
+ );
231
+ }
232
+ ```
233
+
234
+ ## Platform Hooks
235
+
236
+ Additional platform utilities for device interaction.
237
+
238
+ ### useKeyboard()
239
+
240
+ Track virtual keyboard state for mobile devices.
241
+
242
+ ```tsx
243
+ import { useKeyboard } from '@notapublicfigureanymore/relay-sdk';
244
+
245
+ const keyboard = useKeyboard();
246
+
247
+ // Adjust layout based on keyboard state
248
+ <div style={{
249
+ paddingBottom: keyboard.isOpen ? keyboard.height : 0
250
+ }}>
251
+ <Input placeholder="Type here..." />
252
+ </div>
253
+ ```
254
+
255
+ ### useBackHandler()
256
+
257
+ Handle back navigation with custom behavior.
258
+
259
+ ```tsx
260
+ import { useBackHandler } from '@notapublicfigureanymore/relay-sdk';
261
+
262
+ useBackHandler(() => {
263
+ // Return true to prevent default back behavior
264
+ const shouldPrevent = confirm('Are you sure you want to go back?');
265
+ return shouldPrevent;
266
+ });
267
+ ```
268
+
269
+ ## Styling
270
+
271
+ The SDK uses Tailwind CSS for styling. Make sure your `tailwind.config.js` includes:
272
+
273
+ ```js
274
+ /** @type {import('tailwindcss').Config} */
275
+ module.exports = {
276
+ content: [
277
+ './src/**/*.{js,ts,jsx,tsx}',
278
+ './node_modules/@notapublicfigureanymore/relay-sdk/dist/**/*.{js,ts,jsx,tsx}'
279
+ ],
280
+ // ... rest of your config
281
+ }
282
+ ```
283
+
284
+ ## API Reference
285
+
286
+ ### Platform Hooks
287
+ - `useSafeArea()` - Get device safe area insets
288
+ - `useColorScheme()` - Get current color scheme preference
289
+ - `useStatusBar(color)` - Control status bar appearance
290
+ - `useBackHandler(handler)` - Handle back navigation
291
+ - `useAppState()` - Track app foreground/background state
292
+ - `useKeyboard()` - Track virtual keyboard state
293
+ - `usePlatform()` - Get device environment information
294
+
295
+ ### Device Hooks
296
+ - `useNotifications()` - Send and schedule notifications
297
+ - `useClipboard()` - Read/write clipboard content
298
+ - `useGeolocation()` - Access device location
299
+ - `useCamera()` - Camera and media capture
300
+
301
+ ### UI Components
302
+ - 40+ components including Button, Card, Dialog, Input, Select, Tabs, etc.
303
+
304
+ ## Links
305
+
306
+ - [Relay Platform](https://relay.notapublicfigureanymore.com) - Learn more about Relay
307
+ - [Documentation](https://relay.notapublicfigureanymore.com/docs/sdk) - Full API documentation
308
+ - [Examples](https://github.com/Jaseunda/clips/tree/main/open) - Example apps
309
+ - [Issues](https://github.com/Jaseunda/clips/issues) - Report bugs
310
+
311
+ ## License
312
+
313
+ MIT
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _chunk7RGKC52Kcjs = require('./chunk-7RGKC52K.cjs');var _react = require('react');function S(){let[t,e]=_react.useState.call(void 0, {isOpen:!1,height:0});return _react.useEffect.call(void 0, ()=>{if(!/iPad|iPhone|iPod|Android/.test(navigator.userAgent))return;let o=()=>{if(!window.visualViewport)return;let i=window.visualViewport,a=window.innerHeight-i.height,l=a>100;e({isOpen:l,height:l?a:0})};window.visualViewport&&(window.visualViewport.addEventListener("resize",o),window.visualViewport.addEventListener("scroll",o));let n=i=>{let a=i.target;(a.tagName==="INPUT"||a.tagName==="TEXTAREA")&&setTimeout(o,100)},s=()=>{setTimeout(()=>{e({isOpen:!1,height:0})},100)};return document.addEventListener("focusin",n),document.addEventListener("focusout",s),o(),()=>{_optionalChain([window, 'access', _2 => _2.visualViewport, 'optionalAccess', _3 => _3.removeEventListener, 'call', _4 => _4("resize",o)]),_optionalChain([window, 'access', _5 => _5.visualViewport, 'optionalAccess', _6 => _6.removeEventListener, 'call', _7 => _7("scroll",o)]),document.removeEventListener("focusin",n),document.removeEventListener("focusout",s)}},[]),t}function Ss(){return _react.useCallback.call(void 0, ()=>{window.parent!==window&&window.parent.postMessage("relay:close","*"),window.opener&&window.opener.postMessage("relay:close","*"),window.close()},[])}var _jsxruntime = require('react/jsx-runtime');var b={top:0,bottom:0,left:0,right:0},y=_react.createContext.call(void 0, b);function As({children:t}){let[e,r]=_react.useState.call(void 0, b);return _react.useEffect.call(void 0, ()=>{let o=()=>{let s=getComputedStyle(document.documentElement),i=d=>{let u=s.getPropertyValue(`--safe-area-inset-${d}`);return parseInt(u)||0},a=i("top"),l=i("bottom"),h=i("left"),p=i("right");if(a===0&&l===0){let d=document.createElement("div");d.style.cssText=`
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _chunk7RGKC52Kcjs = require('./chunk-7RGKC52K.cjs');var _react = require('react');function L(){let[o,t]=_react.useState.call(void 0, {isOpen:!1,height:0});return _react.useEffect.call(void 0, ()=>{if(!/iPad|iPhone|iPod|Android/.test(navigator.userAgent))return;let n=()=>{if(!window.visualViewport)return;let u=window.visualViewport,l=window.innerHeight-u.height,p=l>100;t({isOpen:p,height:p?l:0})};window.visualViewport&&(window.visualViewport.addEventListener("resize",n),window.visualViewport.addEventListener("scroll",n));let r=u=>{let l=u.target;(l.tagName==="INPUT"||l.tagName==="TEXTAREA")&&setTimeout(n,100)},d=()=>{setTimeout(()=>{t({isOpen:!1,height:0})},100)};return document.addEventListener("focusin",r),document.addEventListener("focusout",d),n(),()=>{_optionalChain([window, 'access', _2 => _2.visualViewport, 'optionalAccess', _3 => _3.removeEventListener, 'call', _4 => _4("resize",n)]),_optionalChain([window, 'access', _5 => _5.visualViewport, 'optionalAccess', _6 => _6.removeEventListener, 'call', _7 => _7("scroll",n)]),document.removeEventListener("focusin",r),document.removeEventListener("focusout",d)}},[]),o}function Ua(){return _react.useCallback.call(void 0, ()=>{window.parent!==window&&window.parent.postMessage("relay:close","*"),window.opener&&window.opener.postMessage("relay:close","*"),window.close()},[])}var _jsxruntime = require('react/jsx-runtime');var B={top:0,bottom:0,left:0,right:0},D=_react.createContext.call(void 0, B);function qa({children:o}){let[t,s]=_react.useState.call(void 0, B);return _react.useEffect.call(void 0, ()=>{let n=()=>{let d=getComputedStyle(document.documentElement),u=e=>{let a=d.getPropertyValue(`--safe-area-inset-${e}`);return parseInt(a)||0},l=u("top"),p=u("bottom"),f=u("left"),i=u("right");if(l===0&&p===0){let e=document.createElement("div");e.style.cssText=`
2
2
  position: fixed;
3
3
  top: env(safe-area-inset-top, 0px);
4
4
  bottom: env(safe-area-inset-bottom, 0px);
@@ -6,5 +6,5 @@
6
6
  right: env(safe-area-inset-right, 0px);
7
7
  pointer-events: none;
8
8
  visibility: hidden;
9
- `,document.body.appendChild(d);let u=d.getBoundingClientRect();a=u.top,l=window.innerHeight-u.bottom,h=u.left,p=window.innerWidth-u.right,document.body.removeChild(d)}r({top:a,bottom:l,left:h,right:p})},n=s=>{_optionalChain([s, 'access', _8 => _8.data, 'optionalAccess', _9 => _9.type])==="relay:safearea"&&r(s.data.insets)};return o(),window.addEventListener("message",n),window.addEventListener("resize",o),()=>{window.removeEventListener("message",n),window.removeEventListener("resize",o)}},[]),_jsxruntime.jsx.call(void 0, y.Provider,{value:e,children:t})}function E(){return _react.useContext.call(void 0, y)}function ks(){let[t,e]=_react.useState.call(void 0, ()=>window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light");return _react.useEffect.call(void 0, ()=>{let r=window.matchMedia("(prefers-color-scheme: dark)"),o=n=>{e(n.matches?"dark":"light")};return r.addEventListener("change",o),()=>r.removeEventListener("change",o)},[]),t}function Ls(t){_react.useEffect.call(void 0, ()=>{let e=document.querySelector('meta[name="theme-color"]');e||(e=document.createElement("meta"),e.setAttribute("name","theme-color"),document.head.appendChild(e)),e.setAttribute("content",t),_optionalChain([window, 'access', _10 => _10.parent, 'optionalAccess', _11 => _11.postMessage, 'call', _12 => _12({type:"relay:statusbar",color:t},"*")])},[t])}function Cs(t){_react.useEffect.call(void 0, ()=>{let e=o=>{o.data==="relay:back"&&(t()||_optionalChain([window, 'access', _13 => _13.parent, 'optionalAccess', _14 => _14.postMessage, 'call', _15 => _15("relay:close","*")]))},r=o=>{t()&&window.history.pushState(null,"",window.location.href)};return window.history.pushState(null,"",window.location.href),window.addEventListener("message",e),window.addEventListener("popstate",r),()=>{window.removeEventListener("message",e),window.removeEventListener("popstate",r)}},[t])}function Ps(){let[t,e]=_react.useState.call(void 0, "active");return _react.useEffect.call(void 0, ()=>{let r=()=>{e(document.hidden?"background":"active")},o=n=>{n.data==="relay:foreground"&&e("active"),n.data==="relay:background"&&e("background")};return document.addEventListener("visibilitychange",r),window.addEventListener("message",o),()=>{document.removeEventListener("visibilitychange",r),window.removeEventListener("message",o)}},[]),t}function Bs({children:t,behavior:e="padding",keyboardVerticalOffset:r=0,style:o,className:n}){let s=S(),i=s.height+r,a={...o,transition:"all 0.2s ease-out"};if(s.isOpen)switch(e){case"padding":a.paddingBottom=i;break;case"height":a.height=`calc(100% - ${i}px)`;break;case"position":a.transform=`translateY(-${i}px)`;break}return _jsxruntime.jsx.call(void 0, "div",{style:a,className:n,children:t})}function Ns({children:t,edges:e=["top","bottom","left","right"],style:r,className:o}){let n=E(),s={...r,paddingTop:e.includes("top")?n.top:_optionalChain([r, 'optionalAccess', _16 => _16.paddingTop]),paddingBottom:e.includes("bottom")?n.bottom:_optionalChain([r, 'optionalAccess', _17 => _17.paddingBottom]),paddingLeft:e.includes("left")?n.left:_optionalChain([r, 'optionalAccess', _18 => _18.paddingLeft]),paddingRight:e.includes("right")?n.right:_optionalChain([r, 'optionalAccess', _19 => _19.paddingRight])};return _jsxruntime.jsx.call(void 0, "div",{style:s,className:o,children:t})}function Hs(){let[t,e]=_react.useState.call(void 0, 1500),[r,o]=_react.useState.call(void 0, 1500),[n,s]=_react.useState.call(void 0, !1),[i,a]=_react.useState.call(void 0, "focus"),[l,h]=_react.useState.call(void 0, ""),[p,d]=_react.useState.call(void 0, "egg"),[u,k]=_react.useState.call(void 0, !1);_react.useEffect.call(void 0, ()=>{let c=null;if(n&&t>0)c=setInterval(()=>{e(f=>f-1)},1e3);else if(t===0&&n&&(s(!1),i==="focus")){let f=Math.random()<.01;d(f?"alien":"chick")}return()=>{c&&clearInterval(c)}},[n,t,i]),_react.useEffect.call(void 0, ()=>{if(!n&&(p==="chick"||p==="alien"||p==="ghost")||i!=="focus")return;let c=1-t/r;c<.5?d("egg"):c>=.5&&c<1&&d("cracked")},[t,r,n,p,i]);let L=()=>s(!n),C=()=>{s(!1),d("ghost")},w=c=>{s(!1),a(c),d("egg");let f=1500;c==="shortBreak"&&(f=300),c==="longBreak"&&(f=900),e(f),o(f)};return{seconds:t,initialTime:r,isActive:n,mode:i,task:l,storyState:p,soundEnabled:u,setSeconds:e,setInitialTime:o,setIsActive:s,setMode:a,setTask:h,setStoryState:d,setSoundEnabled:k,toggleTimer:L,giveUp:C,switchMode:w,resetTimer:()=>{s(!1),w(i)},setCustomTime:c=>{s(!1),e(c*60),o(c*60),d("egg")}}}exports.Accordion = _chunk7RGKC52Kcjs.b; exports.AccordionContent = _chunk7RGKC52Kcjs.e; exports.AccordionItem = _chunk7RGKC52Kcjs.c; exports.AccordionTrigger = _chunk7RGKC52Kcjs.d; exports.Alert = _chunk7RGKC52Kcjs.t; exports.AlertAction = _chunk7RGKC52Kcjs.w; exports.AlertDescription = _chunk7RGKC52Kcjs.v; exports.AlertDialog = _chunk7RGKC52Kcjs.h; exports.AlertDialogAction = _chunk7RGKC52Kcjs.r; exports.AlertDialogCancel = _chunk7RGKC52Kcjs.s; exports.AlertDialogContent = _chunk7RGKC52Kcjs.l; exports.AlertDialogDescription = _chunk7RGKC52Kcjs.q; exports.AlertDialogFooter = _chunk7RGKC52Kcjs.n; exports.AlertDialogHeader = _chunk7RGKC52Kcjs.m; exports.AlertDialogMedia = _chunk7RGKC52Kcjs.o; exports.AlertDialogOverlay = _chunk7RGKC52Kcjs.k; exports.AlertDialogPortal = _chunk7RGKC52Kcjs.j; exports.AlertDialogTitle = _chunk7RGKC52Kcjs.p; exports.AlertDialogTrigger = _chunk7RGKC52Kcjs.i; exports.AlertTitle = _chunk7RGKC52Kcjs.u; exports.AspectRatio = _chunk7RGKC52Kcjs.x; exports.Avatar = _chunk7RGKC52Kcjs.y; exports.AvatarBadge = _chunk7RGKC52Kcjs.B; exports.AvatarFallback = _chunk7RGKC52Kcjs.A; exports.AvatarGroup = _chunk7RGKC52Kcjs.C; exports.AvatarGroupCount = _chunk7RGKC52Kcjs.D; exports.AvatarImage = _chunk7RGKC52Kcjs.z; exports.Badge = _chunk7RGKC52Kcjs.F; exports.Breadcrumb = _chunk7RGKC52Kcjs.G; exports.BreadcrumbEllipsis = _chunk7RGKC52Kcjs.M; exports.BreadcrumbItem = _chunk7RGKC52Kcjs.I; exports.BreadcrumbLink = _chunk7RGKC52Kcjs.J; exports.BreadcrumbList = _chunk7RGKC52Kcjs.H; exports.BreadcrumbPage = _chunk7RGKC52Kcjs.K; exports.BreadcrumbSeparator = _chunk7RGKC52Kcjs.L; exports.Button = _chunk7RGKC52Kcjs.g; exports.ButtonGroup = _chunk7RGKC52Kcjs.P; exports.ButtonGroupSeparator = _chunk7RGKC52Kcjs.R; exports.ButtonGroupText = _chunk7RGKC52Kcjs.Q; exports.Calendar = _chunk7RGKC52Kcjs.S; exports.CalendarDayButton = _chunk7RGKC52Kcjs.T; exports.Card = _chunk7RGKC52Kcjs.U; exports.CardAction = _chunk7RGKC52Kcjs.Y; exports.CardContent = _chunk7RGKC52Kcjs.Z; exports.CardDescription = _chunk7RGKC52Kcjs.X; exports.CardFooter = _chunk7RGKC52Kcjs._; exports.CardHeader = _chunk7RGKC52Kcjs.V; exports.CardTitle = _chunk7RGKC52Kcjs.W; exports.Carousel = _chunk7RGKC52Kcjs.aa; exports.CarouselContent = _chunk7RGKC52Kcjs.ba; exports.CarouselItem = _chunk7RGKC52Kcjs.ca; exports.CarouselNext = _chunk7RGKC52Kcjs.ea; exports.CarouselPrevious = _chunk7RGKC52Kcjs.da; exports.ChartContainer = _chunk7RGKC52Kcjs.fa; exports.ChartLegend = _chunk7RGKC52Kcjs.ja; exports.ChartLegendContent = _chunk7RGKC52Kcjs.ka; exports.ChartStyle = _chunk7RGKC52Kcjs.ga; exports.ChartTooltip = _chunk7RGKC52Kcjs.ha; exports.ChartTooltipContent = _chunk7RGKC52Kcjs.ia; exports.Checkbox = _chunk7RGKC52Kcjs.la; exports.Collapsible = _chunk7RGKC52Kcjs.ma; exports.CollapsibleContent = _chunk7RGKC52Kcjs.oa; exports.CollapsibleTrigger = _chunk7RGKC52Kcjs.na; exports.Combobox = _chunk7RGKC52Kcjs.xa; exports.ComboboxChip = _chunk7RGKC52Kcjs.Ka; exports.ComboboxChips = _chunk7RGKC52Kcjs.Ja; exports.ComboboxChipsInput = _chunk7RGKC52Kcjs.La; exports.ComboboxCollection = _chunk7RGKC52Kcjs.Ga; exports.ComboboxContent = _chunk7RGKC52Kcjs.Ba; exports.ComboboxEmpty = _chunk7RGKC52Kcjs.Ha; exports.ComboboxGroup = _chunk7RGKC52Kcjs.Ea; exports.ComboboxInput = _chunk7RGKC52Kcjs.Aa; exports.ComboboxItem = _chunk7RGKC52Kcjs.Da; exports.ComboboxLabel = _chunk7RGKC52Kcjs.Fa; exports.ComboboxList = _chunk7RGKC52Kcjs.Ca; exports.ComboboxSeparator = _chunk7RGKC52Kcjs.Ia; exports.ComboboxTrigger = _chunk7RGKC52Kcjs.za; exports.ComboboxValue = _chunk7RGKC52Kcjs.ya; exports.Command = _chunk7RGKC52Kcjs.Xa; exports.CommandDialog = _chunk7RGKC52Kcjs.Ya; exports.CommandEmpty = _chunk7RGKC52Kcjs.$a; exports.CommandGroup = _chunk7RGKC52Kcjs.ab; exports.CommandInput = _chunk7RGKC52Kcjs.Za; exports.CommandItem = _chunk7RGKC52Kcjs.cb; exports.CommandList = _chunk7RGKC52Kcjs._a; exports.CommandSeparator = _chunk7RGKC52Kcjs.bb; exports.CommandShortcut = _chunk7RGKC52Kcjs.db; exports.ContextMenu = _chunk7RGKC52Kcjs.eb; exports.ContextMenuCheckboxItem = _chunk7RGKC52Kcjs.ob; exports.ContextMenuContent = _chunk7RGKC52Kcjs.kb; exports.ContextMenuGroup = _chunk7RGKC52Kcjs.gb; exports.ContextMenuItem = _chunk7RGKC52Kcjs.lb; exports.ContextMenuLabel = _chunk7RGKC52Kcjs.qb; exports.ContextMenuPortal = _chunk7RGKC52Kcjs.hb; exports.ContextMenuRadioGroup = _chunk7RGKC52Kcjs.jb; exports.ContextMenuRadioItem = _chunk7RGKC52Kcjs.pb; exports.ContextMenuSeparator = _chunk7RGKC52Kcjs.rb; exports.ContextMenuShortcut = _chunk7RGKC52Kcjs.sb; exports.ContextMenuSub = _chunk7RGKC52Kcjs.ib; exports.ContextMenuSubContent = _chunk7RGKC52Kcjs.nb; exports.ContextMenuSubTrigger = _chunk7RGKC52Kcjs.mb; exports.ContextMenuTrigger = _chunk7RGKC52Kcjs.fb; exports.Dialog = _chunk7RGKC52Kcjs.Na; exports.DialogClose = _chunk7RGKC52Kcjs.Qa; exports.DialogContent = _chunk7RGKC52Kcjs.Sa; exports.DialogDescription = _chunk7RGKC52Kcjs.Wa; exports.DialogFooter = _chunk7RGKC52Kcjs.Ua; exports.DialogHeader = _chunk7RGKC52Kcjs.Ta; exports.DialogOverlay = _chunk7RGKC52Kcjs.Ra; exports.DialogPortal = _chunk7RGKC52Kcjs.Pa; exports.DialogTitle = _chunk7RGKC52Kcjs.Va; exports.DialogTrigger = _chunk7RGKC52Kcjs.Oa; exports.Drawer = _chunk7RGKC52Kcjs.tb; exports.DrawerClose = _chunk7RGKC52Kcjs.wb; exports.DrawerContent = _chunk7RGKC52Kcjs.yb; exports.DrawerDescription = _chunk7RGKC52Kcjs.Cb; exports.DrawerFooter = _chunk7RGKC52Kcjs.Ab; exports.DrawerHeader = _chunk7RGKC52Kcjs.zb; exports.DrawerOverlay = _chunk7RGKC52Kcjs.xb; exports.DrawerPortal = _chunk7RGKC52Kcjs.vb; exports.DrawerTitle = _chunk7RGKC52Kcjs.Bb; exports.DrawerTrigger = _chunk7RGKC52Kcjs.ub; exports.DropdownMenu = _chunk7RGKC52Kcjs.Db; exports.DropdownMenuCheckboxItem = _chunk7RGKC52Kcjs.Jb; exports.DropdownMenuContent = _chunk7RGKC52Kcjs.Gb; exports.DropdownMenuGroup = _chunk7RGKC52Kcjs.Hb; exports.DropdownMenuItem = _chunk7RGKC52Kcjs.Ib; exports.DropdownMenuLabel = _chunk7RGKC52Kcjs.Mb; exports.DropdownMenuPortal = _chunk7RGKC52Kcjs.Eb; exports.DropdownMenuRadioGroup = _chunk7RGKC52Kcjs.Kb; exports.DropdownMenuRadioItem = _chunk7RGKC52Kcjs.Lb; exports.DropdownMenuSeparator = _chunk7RGKC52Kcjs.Nb; exports.DropdownMenuShortcut = _chunk7RGKC52Kcjs.Ob; exports.DropdownMenuSub = _chunk7RGKC52Kcjs.Pb; exports.DropdownMenuSubContent = _chunk7RGKC52Kcjs.Rb; exports.DropdownMenuSubTrigger = _chunk7RGKC52Kcjs.Qb; exports.DropdownMenuTrigger = _chunk7RGKC52Kcjs.Fb; exports.Empty = _chunk7RGKC52Kcjs.Sb; exports.EmptyContent = _chunk7RGKC52Kcjs.Xb; exports.EmptyDescription = _chunk7RGKC52Kcjs.Wb; exports.EmptyHeader = _chunk7RGKC52Kcjs.Tb; exports.EmptyMedia = _chunk7RGKC52Kcjs.Ub; exports.EmptyTitle = _chunk7RGKC52Kcjs.Vb; exports.Field = _chunk7RGKC52Kcjs.ac; exports.FieldContent = _chunk7RGKC52Kcjs.bc; exports.FieldDescription = _chunk7RGKC52Kcjs.ec; exports.FieldError = _chunk7RGKC52Kcjs.gc; exports.FieldGroup = _chunk7RGKC52Kcjs.$b; exports.FieldLabel = _chunk7RGKC52Kcjs.cc; exports.FieldLegend = _chunk7RGKC52Kcjs._b; exports.FieldSeparator = _chunk7RGKC52Kcjs.fc; exports.FieldSet = _chunk7RGKC52Kcjs.Zb; exports.FieldTitle = _chunk7RGKC52Kcjs.dc; exports.HoverCard = _chunk7RGKC52Kcjs.hc; exports.HoverCardContent = _chunk7RGKC52Kcjs.jc; exports.HoverCardTrigger = _chunk7RGKC52Kcjs.ic; exports.Input = _chunk7RGKC52Kcjs.pa; exports.InputGroup = _chunk7RGKC52Kcjs.ra; exports.InputGroupAddon = _chunk7RGKC52Kcjs.sa; exports.InputGroupButton = _chunk7RGKC52Kcjs.ta; exports.InputGroupInput = _chunk7RGKC52Kcjs.va; exports.InputGroupText = _chunk7RGKC52Kcjs.ua; exports.InputGroupTextarea = _chunk7RGKC52Kcjs.wa; exports.InputOTP = _chunk7RGKC52Kcjs.kc; exports.InputOTPGroup = _chunk7RGKC52Kcjs.lc; exports.InputOTPSeparator = _chunk7RGKC52Kcjs.nc; exports.InputOTPSlot = _chunk7RGKC52Kcjs.mc; exports.Item = _chunk7RGKC52Kcjs.qc; exports.ItemActions = _chunk7RGKC52Kcjs.vc; exports.ItemContent = _chunk7RGKC52Kcjs.sc; exports.ItemDescription = _chunk7RGKC52Kcjs.uc; exports.ItemFooter = _chunk7RGKC52Kcjs.xc; exports.ItemGroup = _chunk7RGKC52Kcjs.oc; exports.ItemHeader = _chunk7RGKC52Kcjs.wc; exports.ItemMedia = _chunk7RGKC52Kcjs.rc; exports.ItemSeparator = _chunk7RGKC52Kcjs.pc; exports.ItemTitle = _chunk7RGKC52Kcjs.tc; exports.Kbd = _chunk7RGKC52Kcjs.yc; exports.KbdGroup = _chunk7RGKC52Kcjs.zc; exports.KeyboardAvoidingView = Bs; exports.Label = _chunk7RGKC52Kcjs.Yb; exports.Menubar = _chunk7RGKC52Kcjs.Ac; exports.MenubarCheckboxItem = _chunk7RGKC52Kcjs.Ic; exports.MenubarContent = _chunk7RGKC52Kcjs.Gc; exports.MenubarGroup = _chunk7RGKC52Kcjs.Cc; exports.MenubarItem = _chunk7RGKC52Kcjs.Hc; exports.MenubarLabel = _chunk7RGKC52Kcjs.Kc; exports.MenubarMenu = _chunk7RGKC52Kcjs.Bc; exports.MenubarPortal = _chunk7RGKC52Kcjs.Dc; exports.MenubarRadioGroup = _chunk7RGKC52Kcjs.Ec; exports.MenubarRadioItem = _chunk7RGKC52Kcjs.Jc; exports.MenubarSeparator = _chunk7RGKC52Kcjs.Lc; exports.MenubarShortcut = _chunk7RGKC52Kcjs.Mc; exports.MenubarSub = _chunk7RGKC52Kcjs.Nc; exports.MenubarSubContent = _chunk7RGKC52Kcjs.Pc; exports.MenubarSubTrigger = _chunk7RGKC52Kcjs.Oc; exports.MenubarTrigger = _chunk7RGKC52Kcjs.Fc; exports.NavigationMenu = _chunk7RGKC52Kcjs.Qc; exports.NavigationMenuContent = _chunk7RGKC52Kcjs.Vc; exports.NavigationMenuIndicator = _chunk7RGKC52Kcjs.Yc; exports.NavigationMenuItem = _chunk7RGKC52Kcjs.Sc; exports.NavigationMenuLink = _chunk7RGKC52Kcjs.Xc; exports.NavigationMenuList = _chunk7RGKC52Kcjs.Rc; exports.NavigationMenuTrigger = _chunk7RGKC52Kcjs.Uc; exports.NavigationMenuViewport = _chunk7RGKC52Kcjs.Wc; exports.Pagination = _chunk7RGKC52Kcjs.Zc; exports.PaginationContent = _chunk7RGKC52Kcjs._c; exports.PaginationEllipsis = _chunk7RGKC52Kcjs.dd; exports.PaginationItem = _chunk7RGKC52Kcjs.$c; exports.PaginationLink = _chunk7RGKC52Kcjs.ad; exports.PaginationNext = _chunk7RGKC52Kcjs.cd; exports.PaginationPrevious = _chunk7RGKC52Kcjs.bd; exports.Popover = _chunk7RGKC52Kcjs.ed; exports.PopoverAnchor = _chunk7RGKC52Kcjs.hd; exports.PopoverContent = _chunk7RGKC52Kcjs.gd; exports.PopoverDescription = _chunk7RGKC52Kcjs.kd; exports.PopoverHeader = _chunk7RGKC52Kcjs.id; exports.PopoverTitle = _chunk7RGKC52Kcjs.jd; exports.PopoverTrigger = _chunk7RGKC52Kcjs.fd; exports.Progress = _chunk7RGKC52Kcjs.ld; exports.RadioGroup = _chunk7RGKC52Kcjs.md; exports.RadioGroupItem = _chunk7RGKC52Kcjs.nd; exports.ResizableHandle = _chunk7RGKC52Kcjs.qd; exports.ResizablePanel = _chunk7RGKC52Kcjs.pd; exports.ResizablePanelGroup = _chunk7RGKC52Kcjs.od; exports.SafeAreaProvider = As; exports.SafeAreaView = Ns; exports.ScrollArea = _chunk7RGKC52Kcjs.rd; exports.ScrollBar = _chunk7RGKC52Kcjs.sd; exports.Select = _chunk7RGKC52Kcjs.td; exports.SelectContent = _chunk7RGKC52Kcjs.xd; exports.SelectGroup = _chunk7RGKC52Kcjs.ud; exports.SelectItem = _chunk7RGKC52Kcjs.zd; exports.SelectLabel = _chunk7RGKC52Kcjs.yd; exports.SelectScrollDownButton = _chunk7RGKC52Kcjs.Cd; exports.SelectScrollUpButton = _chunk7RGKC52Kcjs.Bd; exports.SelectSeparator = _chunk7RGKC52Kcjs.Ad; exports.SelectTrigger = _chunk7RGKC52Kcjs.wd; exports.SelectValue = _chunk7RGKC52Kcjs.vd; exports.Separator = _chunk7RGKC52Kcjs.N; exports.Sheet = _chunk7RGKC52Kcjs.Dd; exports.SheetClose = _chunk7RGKC52Kcjs.Fd; exports.SheetContent = _chunk7RGKC52Kcjs.Gd; exports.SheetDescription = _chunk7RGKC52Kcjs.Kd; exports.SheetFooter = _chunk7RGKC52Kcjs.Id; exports.SheetHeader = _chunk7RGKC52Kcjs.Hd; exports.SheetTitle = _chunk7RGKC52Kcjs.Jd; exports.SheetTrigger = _chunk7RGKC52Kcjs.Ed; exports.Sidebar = _chunk7RGKC52Kcjs.Td; exports.SidebarContent = _chunk7RGKC52Kcjs.$d; exports.SidebarFooter = _chunk7RGKC52Kcjs.Zd; exports.SidebarGroup = _chunk7RGKC52Kcjs.ae; exports.SidebarGroupAction = _chunk7RGKC52Kcjs.ce; exports.SidebarGroupContent = _chunk7RGKC52Kcjs.de; exports.SidebarGroupLabel = _chunk7RGKC52Kcjs.be; exports.SidebarHeader = _chunk7RGKC52Kcjs.Yd; exports.SidebarInput = _chunk7RGKC52Kcjs.Xd; exports.SidebarInset = _chunk7RGKC52Kcjs.Wd; exports.SidebarMenu = _chunk7RGKC52Kcjs.ee; exports.SidebarMenuAction = _chunk7RGKC52Kcjs.he; exports.SidebarMenuBadge = _chunk7RGKC52Kcjs.ie; exports.SidebarMenuButton = _chunk7RGKC52Kcjs.ge; exports.SidebarMenuItem = _chunk7RGKC52Kcjs.fe; exports.SidebarMenuSkeleton = _chunk7RGKC52Kcjs.je; exports.SidebarMenuSub = _chunk7RGKC52Kcjs.ke; exports.SidebarMenuSubButton = _chunk7RGKC52Kcjs.me; exports.SidebarMenuSubItem = _chunk7RGKC52Kcjs.le; exports.SidebarProvider = _chunk7RGKC52Kcjs.Sd; exports.SidebarRail = _chunk7RGKC52Kcjs.Vd; exports.SidebarSeparator = _chunk7RGKC52Kcjs._d; exports.SidebarTrigger = _chunk7RGKC52Kcjs.Ud; exports.Skeleton = _chunk7RGKC52Kcjs.Ld; exports.Slider = _chunk7RGKC52Kcjs.ne; exports.Spinner = _chunk7RGKC52Kcjs.pe; exports.Switch = _chunk7RGKC52Kcjs.qe; exports.Table = _chunk7RGKC52Kcjs.re; exports.TableBody = _chunk7RGKC52Kcjs.te; exports.TableCaption = _chunk7RGKC52Kcjs.ye; exports.TableCell = _chunk7RGKC52Kcjs.xe; exports.TableFooter = _chunk7RGKC52Kcjs.ue; exports.TableHead = _chunk7RGKC52Kcjs.we; exports.TableHeader = _chunk7RGKC52Kcjs.se; exports.TableRow = _chunk7RGKC52Kcjs.ve; exports.Tabs = _chunk7RGKC52Kcjs.ze; exports.TabsContent = _chunk7RGKC52Kcjs.De; exports.TabsList = _chunk7RGKC52Kcjs.Be; exports.TabsTrigger = _chunk7RGKC52Kcjs.Ce; exports.Textarea = _chunk7RGKC52Kcjs.qa; exports.Toaster = _chunk7RGKC52Kcjs.oe; exports.Toggle = _chunk7RGKC52Kcjs.Fe; exports.ToggleGroup = _chunk7RGKC52Kcjs.Ge; exports.ToggleGroupItem = _chunk7RGKC52Kcjs.He; exports.Tooltip = _chunk7RGKC52Kcjs.Nd; exports.TooltipContent = _chunk7RGKC52Kcjs.Pd; exports.TooltipProvider = _chunk7RGKC52Kcjs.Md; exports.TooltipTrigger = _chunk7RGKC52Kcjs.Od; exports.badgeVariants = _chunk7RGKC52Kcjs.E; exports.buttonGroupVariants = _chunk7RGKC52Kcjs.O; exports.buttonVariants = _chunk7RGKC52Kcjs.f; exports.cn = _chunk7RGKC52Kcjs.a; exports.navigationMenuTriggerStyle = _chunk7RGKC52Kcjs.Tc; exports.tabsListVariants = _chunk7RGKC52Kcjs.Ae; exports.toggleVariants = _chunk7RGKC52Kcjs.Ee; exports.useAppState = Ps; exports.useBackHandler = Cs; exports.useCarousel = _chunk7RGKC52Kcjs.$; exports.useColorScheme = ks; exports.useComboboxAnchor = _chunk7RGKC52Kcjs.Ma; exports.useFocusTimer = Hs; exports.useIsMobile = _chunk7RGKC52Kcjs.Qd; exports.useKeyboard = S; exports.useRelayClose = Ss; exports.useSafeArea = E; exports.useSidebar = _chunk7RGKC52Kcjs.Rd; exports.useStatusBar = Ls;
9
+ `,document.body.appendChild(e);let a=e.getBoundingClientRect();l=a.top,p=window.innerHeight-a.bottom,f=a.left,i=window.innerWidth-a.right,document.body.removeChild(e)}s({top:l,bottom:p,left:f,right:i})},r=d=>{_optionalChain([d, 'access', _8 => _8.data, 'optionalAccess', _9 => _9.type])==="relay:safearea"&&s(d.data.insets)};return n(),window.addEventListener("message",r),window.addEventListener("resize",n),()=>{window.removeEventListener("message",r),window.removeEventListener("resize",n)}},[]),_jsxruntime.jsx.call(void 0, D.Provider,{value:t,children:o})}function A(){return _react.useContext.call(void 0, D)}function za(){let[o,t]=_react.useState.call(void 0, ()=>window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light");return _react.useEffect.call(void 0, ()=>{let s=window.matchMedia("(prefers-color-scheme: dark)"),n=r=>{t(r.matches?"dark":"light")};return s.addEventListener("change",n),()=>s.removeEventListener("change",n)},[]),o}function $a(o){_react.useEffect.call(void 0, ()=>{let t=document.querySelector('meta[name="theme-color"]');t||(t=document.createElement("meta"),t.setAttribute("name","theme-color"),document.head.appendChild(t)),t.setAttribute("content",o),_optionalChain([window, 'access', _10 => _10.parent, 'optionalAccess', _11 => _11.postMessage, 'call', _12 => _12({type:"relay:statusbar",color:o},"*")])},[o])}function Wa(o){_react.useEffect.call(void 0, ()=>{let t=n=>{n.data==="relay:back"&&(o()||_optionalChain([window, 'access', _13 => _13.parent, 'optionalAccess', _14 => _14.postMessage, 'call', _15 => _15("relay:close","*")]))},s=n=>{o()&&window.history.pushState(null,"",window.location.href)};return window.history.pushState(null,"",window.location.href),window.addEventListener("message",t),window.addEventListener("popstate",s),()=>{window.removeEventListener("message",t),window.removeEventListener("popstate",s)}},[o])}function ja(){let[o,t]=_react.useState.call(void 0, "active");return _react.useEffect.call(void 0, ()=>{let s=()=>{t(document.hidden?"background":"active")},n=r=>{r.data==="relay:foreground"&&t("active"),r.data==="relay:background"&&t("background")};return document.addEventListener("visibilitychange",s),window.addEventListener("message",n),()=>{document.removeEventListener("visibilitychange",s),window.removeEventListener("message",n)}},[]),o}function ei({children:o,behavior:t="padding",keyboardVerticalOffset:s=0,style:n,className:r}){let d=L(),u=d.height+s,l={...n,transition:"all 0.2s ease-out"};if(d.isOpen)switch(t){case"padding":l.paddingBottom=u;break;case"height":l.height=`calc(100% - ${u}px)`;break;case"position":l.transform=`translateY(-${u}px)`;break}return _jsxruntime.jsx.call(void 0, "div",{style:l,className:r,children:o})}function ti({children:o,edges:t=["top","bottom","left","right"],style:s,className:n}){let r=A(),d={...s,paddingTop:t.includes("top")?r.top:_optionalChain([s, 'optionalAccess', _16 => _16.paddingTop]),paddingBottom:t.includes("bottom")?r.bottom:_optionalChain([s, 'optionalAccess', _17 => _17.paddingBottom]),paddingLeft:t.includes("left")?r.left:_optionalChain([s, 'optionalAccess', _18 => _18.paddingLeft]),paddingRight:t.includes("right")?r.right:_optionalChain([s, 'optionalAccess', _19 => _19.paddingRight])};return _jsxruntime.jsx.call(void 0, "div",{style:d,className:n,children:o})}function oi({title:o,showBack:t=!0,onBack:s,className:n,rightElement:r}){let d=A();return _jsxruntime.jsx.call(void 0, "div",{className:_chunk7RGKC52Kcjs.a.call(void 0, "bg-background border-b flex flex-col z-50",n),style:{paddingTop:d.top},children:_jsxruntime.jsxs.call(void 0, "div",{className:"h-14 flex items-center justify-between px-4",children:[_jsxruntime.jsxs.call(void 0, "div",{className:"flex items-center gap-4 flex-1",children:[t&&_jsxruntime.jsx.call(void 0, "button",{onClick:s||(()=>window.history.back()),className:"p-2 -ml-2 hover:bg-muted rounded-full transition-colors",children:_jsxruntime.jsx.call(void 0, "svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:_jsxruntime.jsx.call(void 0, "path",{d:"M19 12H5M12 19l-7-7 7-7"})})}),_jsxruntime.jsx.call(void 0, "h1",{className:"text-lg font-semibold truncate",children:o})]}),r&&_jsxruntime.jsx.call(void 0, "div",{className:"flex-none",children:r})]})})}function ii(){let[o,t]=_react.useState.call(void 0, 1500),[s,n]=_react.useState.call(void 0, 1500),[r,d]=_react.useState.call(void 0, !1),[u,l]=_react.useState.call(void 0, "focus"),[p,f]=_react.useState.call(void 0, ""),[i,e]=_react.useState.call(void 0, "egg"),[a,c]=_react.useState.call(void 0, !1);_react.useEffect.call(void 0, ()=>{let g=null;if(r&&o>0)g=setInterval(()=>{t(v=>v-1)},1e3);else if(o===0&&r&&(d(!1),u==="focus")){let v=Math.random()<.01;e(v?"alien":"chick")}return()=>{g&&clearInterval(g)}},[r,o,u]),_react.useEffect.call(void 0, ()=>{if(!r&&(i==="chick"||i==="alien"||i==="ghost")||u!=="focus")return;let g=1-o/s;g<.5?e("egg"):g>=.5&&g<1&&e("cracked")},[o,s,r,i,u]);let m=()=>d(!r),h=()=>{d(!1),e("ghost")},y=g=>{d(!1),l(g),e("egg");let v=1500;g==="shortBreak"&&(v=300),g==="longBreak"&&(v=900),t(v),n(v)};return{seconds:o,initialTime:s,isActive:r,mode:u,task:p,storyState:i,soundEnabled:a,setSeconds:t,setInitialTime:n,setIsActive:d,setMode:l,setTask:f,setStoryState:e,setSoundEnabled:c,toggleTimer:m,giveUp:h,switchMode:y,resetTimer:()=>{d(!1),y(u)},setCustomTime:g=>{d(!1),t(g*60),n(g*60),e("egg")}}}function li(){let[o,t]=_react.useState.call(void 0, "default");_react.useEffect.call(void 0, ()=>{"Notification"in window&&t(Notification.permission)},[]);let s=_react.useCallback.call(void 0, async()=>{if(!("Notification"in window))return console.warn("Notifications not supported"),"denied";let l=await Notification.requestPermission();return t(l),l},[]),n=_react.useCallback.call(void 0, (l,p)=>{if(!("Notification"in window))return;let f=p;if(!_optionalChain([p, 'optionalAccess', _20 => _20.icon])){let i=document.querySelector('link[rel="apple-touch-icon"]')||document.querySelector('link[rel="icon"]');i&&(f={...p,icon:i.href})}Notification.permission==="granted"?new Notification(l,f):Notification.permission!=="denied"&&Notification.requestPermission().then(i=>{i==="granted"&&new Notification(l,f)})},[]),r=_react.useCallback.call(void 0, (l,p,f)=>{setTimeout(()=>{n(l,f)},p)},[n]),d=_react.useCallback.call(void 0, async l=>{if("setAppBadge"in navigator)try{await navigator.setAppBadge(l)}catch(p){console.error("Error setting badge:",p)}},[]),u=_react.useCallback.call(void 0, async()=>{if("clearAppBadge"in navigator)try{await navigator.clearAppBadge()}catch(l){console.error("Error clearing badge:",l)}},[]);return{permission:o,requestPermission:s,sendNotification:n,scheduleNotification:r,setBadge:d,clearBadge:u}}var R="relay_clipboard_history",Q=10;function mi(){let[o,t]=_react.useState.call(void 0, null),[s,n]=_react.useState.call(void 0, []);_react.useEffect.call(void 0, ()=>{try{let i=localStorage.getItem(R);if(i){let e=JSON.parse(i);n(e)}}catch(i){console.error("Failed to load clipboard history",i)}},[]);let r=_react.useCallback.call(void 0, i=>{n(e=>{let a=[i,...e].slice(0,Q),c=a.filter(m=>m.type==="text");try{localStorage.setItem(R,JSON.stringify(c))}catch(m){console.error("Failed to save clipboard history",m)}return a})},[]),d=_react.useCallback.call(void 0, async i=>{if(!navigator.clipboard){console.warn("Clipboard API not supported");return}try{await navigator.clipboard.writeText(i),t(i),r({type:"text",content:i,timestamp:Date.now()})}catch(e){throw console.error("Failed to copy text:",e),e}},[r]),u=_react.useCallback.call(void 0, async i=>{if(!navigator.clipboard){console.warn("Clipboard API not supported");return}try{await navigator.clipboard.write([new ClipboardItem({[i.type]:i})]),r({type:"image",content:i,timestamp:Date.now()})}catch(e){throw console.error("Failed to copy image:",e),e}},[r]),l=_react.useCallback.call(void 0, async()=>{if(!navigator.clipboard)return console.warn("Clipboard API not supported"),"";try{let i=await navigator.clipboard.readText();return t(i),i}catch(i){throw console.error("Failed to read text:",i),i}},[]),p=_react.useCallback.call(void 0, async()=>{if(!navigator.clipboard)return console.warn("Clipboard API not supported"),[];try{return await navigator.clipboard.read()}catch(i){throw console.error("Failed to read content:",i),i}},[]),f=_react.useCallback.call(void 0, ()=>{n([]),localStorage.removeItem(R)},[]);return _react.useEffect.call(void 0, ()=>{let i=()=>{l().catch(()=>{})};return window.addEventListener("focus",i),()=>window.removeEventListener("focus",i)},[l]),{clipboardContent:o,history:s,copyText:d,copyImage:u,readText:l,readContent:p,clearHistory:f}}function gi(o){let[t,s]=_react.useState.call(void 0, {loading:!0,accuracy:null,altitude:null,altitudeAccuracy:null,heading:null,latitude:null,longitude:null,speed:null,timestamp:null,error:null}),n=_react.useRef.call(void 0, null),r=_react.useCallback.call(void 0, e=>{s({loading:!1,accuracy:e.coords.accuracy,altitude:e.coords.altitude,altitudeAccuracy:e.coords.altitudeAccuracy,heading:e.coords.heading,latitude:e.coords.latitude,longitude:e.coords.longitude,speed:e.coords.speed,timestamp:e.timestamp,error:null})},[]),d=_react.useCallback.call(void 0, e=>{s(a=>({...a,loading:!1,error:e}))},[]),u=_react.useCallback.call(void 0, ()=>{if(!navigator.geolocation){s(e=>({...e,loading:!1,error:{code:0,message:"Geolocation not supported",PERMISSION_DENIED:1,POSITION_UNAVAILABLE:2,TIMEOUT:3}}));return}s(e=>({...e,loading:!0})),navigator.geolocation.getCurrentPosition(r,d,o)},[r,d,o]),l=_react.useCallback.call(void 0, e=>{navigator.geolocation&&(n.current!==null&&navigator.geolocation.clearWatch(n.current),n.current=navigator.geolocation.watchPosition(r,d,e||o))},[r,d,o]),p=_react.useCallback.call(void 0, ()=>{n.current!==null&&(navigator.geolocation.clearWatch(n.current),n.current=null)},[]),f=_react.useCallback.call(void 0, (e,a)=>{if(t.latitude===null||t.longitude===null)return 1/0;let c=6371e3,m=t.latitude*Math.PI/180,h=e*Math.PI/180,y=(e-t.latitude)*Math.PI/180,M=(a-t.longitude)*Math.PI/180,N=Math.sin(y/2)*Math.sin(y/2)+Math.cos(m)*Math.cos(h)*Math.sin(M/2)*Math.sin(M/2),g=2*Math.atan2(Math.sqrt(N),Math.sqrt(1-N));return c*g},[t.latitude,t.longitude]),i=_react.useCallback.call(void 0, e=>f(e.latitude,e.longitude)<=e.radius,[f]);return _react.useEffect.call(void 0, ()=>()=>{n.current!==null&&navigator.geolocation.clearWatch(n.current)},[]),{...t,getLocation:u,watchLocation:l,clearWatch:p,checkGeofence:i,distanceTo:f}}function bi(){let[o,t]=_react.useState.call(void 0, {stream:null,error:null,permission:"unknown",isRecording:!1,devices:[]}),s=_react.useRef.call(void 0, null),n=_react.useRef.call(void 0, []),r=_react.useCallback.call(void 0, async()=>{if(!_optionalChain([navigator, 'access', _21 => _21.mediaDevices, 'optionalAccess', _22 => _22.enumerateDevices]))return[];try{let a=await navigator.mediaDevices.enumerateDevices();return t(c=>({...c,devices:a})),a}catch(a){return console.error("Error enumerating devices:",a),[]}},[]);_react.useEffect.call(void 0, ()=>(r(),_optionalChain([navigator, 'access', _23 => _23.mediaDevices, 'optionalAccess', _24 => _24.addEventListener, 'call', _25 => _25("devicechange",r)]),()=>{_optionalChain([navigator, 'access', _26 => _26.mediaDevices, 'optionalAccess', _27 => _27.removeEventListener, 'call', _28 => _28("devicechange",r)])}),[r]);let d=_react.useCallback.call(void 0, async(a={video:!0,audio:!1})=>{try{let c=await navigator.mediaDevices.getUserMedia(a);return t(m=>({...m,stream:c,error:null,permission:"granted"})),c}catch(c){let m=c;t(h=>({...h,error:m,permission:"denied"})),console.error("Error starting camera:",c)}},[]),u=_react.useCallback.call(void 0, ()=>{o.stream&&(o.stream.getTracks().forEach(a=>a.stop()),t(a=>({...a,stream:null,isRecording:!1})))},[o.stream]),l=_react.useCallback.call(void 0, async(a={video:!0})=>{try{let c=await navigator.mediaDevices.getDisplayMedia(a);return t(m=>({...m,stream:c,error:null,permission:"granted"})),c}catch(c){let m=c;t(h=>({...h,error:m,permission:"denied"})),console.error("Error starting screen share:",c)}},[]),p=_react.useCallback.call(void 0, async()=>{if(!o.stream||!o.stream.getVideoTracks()[0])return;let c=document.createElement("video");c.srcObject=o.stream,await c.play();let m=document.createElement("canvas");m.width=c.videoWidth,m.height=c.videoHeight;let h=m.getContext("2d");if(!h)return;h.drawImage(c,0,0);let y=m.toDataURL("image/png");return c.pause(),c.srcObject=null,y},[o.stream]),f=_react.useCallback.call(void 0, ()=>{if(o.stream)try{let a=new MediaRecorder(o.stream);s.current=a,n.current=[],a.ondataavailable=c=>{c.data.size>0&&n.current.push(c.data)},a.start(),t(c=>({...c,isRecording:!0}))}catch(a){console.error("Error starting recording:",a),t(c=>({...c,error:a}))}},[o.stream]),i=_react.useCallback.call(void 0, async()=>{if(!(!s.current||s.current.state==="inactive"))return new Promise(a=>{s.current&&(s.current.onstop=()=>{let c=new Blob(n.current,{type:"video/webm"});t(m=>({...m,isRecording:!1})),a(c)},s.current.stop())})},[]),e=_react.useCallback.call(void 0, async(a,c)=>{u();let m={[c]:{deviceId:{exact:a}}};await d(m)},[d,u]);return _react.useEffect.call(void 0, ()=>()=>{u()},[]),{...o,startCamera:d,stopCamera:u,takePhoto:p,startRecording:f,stopRecording:i,startScreenShare:l,switchDevice:e,getDevices:r}}function Si(){let[o,t]=_react.useState.call(void 0, !1),[s,n]=_react.useState.call(void 0, !1),[r,d]=_react.useState.call(void 0, []),u="http://localhost:5378",l=_react.useCallback.call(void 0, async()=>{try{let a=await(await fetch(`${u}/api/config`)).json();t(_nullishCoalesce(_optionalChain([a, 'access', _29 => _29.ollama, 'optionalAccess', _30 => _30.available]), () => (!1)))}catch (e2){t(!1)}},[]),p=_react.useCallback.call(void 0, async()=>{n(!0);try{let e=await fetch(`${u}/api/ollama/api/tags`);if(e.ok){let a=await e.json();d(a.models||[])}}catch(e){console.error("Failed to fetch Ollama models:",e)}finally{n(!1)}},[]);_react.useEffect.call(void 0, ()=>{l();let e=setInterval(l,3e4);return()=>clearInterval(e)},[l]),_react.useEffect.call(void 0, ()=>{o&&p()},[o,p]);let f=_react.useCallback.call(void 0, async(e,a,c={})=>{let m=await fetch(`${u}/api/ollama/api/generate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:e,prompt:a,stream:!1,...c})});if(!m.ok)throw new Error(`Ollama generation failed: ${m.statusText}`);return await m.json()},[]),i=_react.useCallback.call(void 0, async(e,a,c={})=>{let m=await fetch(`${u}/api/ollama/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:e,messages:a,stream:!1,...c})});if(!m.ok)throw new Error(`Ollama chat failed: ${m.statusText}`);return await m.json()},[]);return{available:o,loading:s,models:r,refreshModels:p,generate:f,chat:i}}exports.Accordion = _chunk7RGKC52Kcjs.b; exports.AccordionContent = _chunk7RGKC52Kcjs.e; exports.AccordionItem = _chunk7RGKC52Kcjs.c; exports.AccordionTrigger = _chunk7RGKC52Kcjs.d; exports.Alert = _chunk7RGKC52Kcjs.t; exports.AlertAction = _chunk7RGKC52Kcjs.w; exports.AlertDescription = _chunk7RGKC52Kcjs.v; exports.AlertDialog = _chunk7RGKC52Kcjs.h; exports.AlertDialogAction = _chunk7RGKC52Kcjs.r; exports.AlertDialogCancel = _chunk7RGKC52Kcjs.s; exports.AlertDialogContent = _chunk7RGKC52Kcjs.l; exports.AlertDialogDescription = _chunk7RGKC52Kcjs.q; exports.AlertDialogFooter = _chunk7RGKC52Kcjs.n; exports.AlertDialogHeader = _chunk7RGKC52Kcjs.m; exports.AlertDialogMedia = _chunk7RGKC52Kcjs.o; exports.AlertDialogOverlay = _chunk7RGKC52Kcjs.k; exports.AlertDialogPortal = _chunk7RGKC52Kcjs.j; exports.AlertDialogTitle = _chunk7RGKC52Kcjs.p; exports.AlertDialogTrigger = _chunk7RGKC52Kcjs.i; exports.AlertTitle = _chunk7RGKC52Kcjs.u; exports.AspectRatio = _chunk7RGKC52Kcjs.x; exports.Avatar = _chunk7RGKC52Kcjs.y; exports.AvatarBadge = _chunk7RGKC52Kcjs.B; exports.AvatarFallback = _chunk7RGKC52Kcjs.A; exports.AvatarGroup = _chunk7RGKC52Kcjs.C; exports.AvatarGroupCount = _chunk7RGKC52Kcjs.D; exports.AvatarImage = _chunk7RGKC52Kcjs.z; exports.Badge = _chunk7RGKC52Kcjs.F; exports.Breadcrumb = _chunk7RGKC52Kcjs.G; exports.BreadcrumbEllipsis = _chunk7RGKC52Kcjs.M; exports.BreadcrumbItem = _chunk7RGKC52Kcjs.I; exports.BreadcrumbLink = _chunk7RGKC52Kcjs.J; exports.BreadcrumbList = _chunk7RGKC52Kcjs.H; exports.BreadcrumbPage = _chunk7RGKC52Kcjs.K; exports.BreadcrumbSeparator = _chunk7RGKC52Kcjs.L; exports.Button = _chunk7RGKC52Kcjs.g; exports.ButtonGroup = _chunk7RGKC52Kcjs.P; exports.ButtonGroupSeparator = _chunk7RGKC52Kcjs.R; exports.ButtonGroupText = _chunk7RGKC52Kcjs.Q; exports.Calendar = _chunk7RGKC52Kcjs.S; exports.CalendarDayButton = _chunk7RGKC52Kcjs.T; exports.Card = _chunk7RGKC52Kcjs.U; exports.CardAction = _chunk7RGKC52Kcjs.Y; exports.CardContent = _chunk7RGKC52Kcjs.Z; exports.CardDescription = _chunk7RGKC52Kcjs.X; exports.CardFooter = _chunk7RGKC52Kcjs._; exports.CardHeader = _chunk7RGKC52Kcjs.V; exports.CardTitle = _chunk7RGKC52Kcjs.W; exports.Carousel = _chunk7RGKC52Kcjs.aa; exports.CarouselContent = _chunk7RGKC52Kcjs.ba; exports.CarouselItem = _chunk7RGKC52Kcjs.ca; exports.CarouselNext = _chunk7RGKC52Kcjs.ea; exports.CarouselPrevious = _chunk7RGKC52Kcjs.da; exports.ChartContainer = _chunk7RGKC52Kcjs.fa; exports.ChartLegend = _chunk7RGKC52Kcjs.ja; exports.ChartLegendContent = _chunk7RGKC52Kcjs.ka; exports.ChartStyle = _chunk7RGKC52Kcjs.ga; exports.ChartTooltip = _chunk7RGKC52Kcjs.ha; exports.ChartTooltipContent = _chunk7RGKC52Kcjs.ia; exports.Checkbox = _chunk7RGKC52Kcjs.la; exports.Collapsible = _chunk7RGKC52Kcjs.ma; exports.CollapsibleContent = _chunk7RGKC52Kcjs.oa; exports.CollapsibleTrigger = _chunk7RGKC52Kcjs.na; exports.Combobox = _chunk7RGKC52Kcjs.xa; exports.ComboboxChip = _chunk7RGKC52Kcjs.Ka; exports.ComboboxChips = _chunk7RGKC52Kcjs.Ja; exports.ComboboxChipsInput = _chunk7RGKC52Kcjs.La; exports.ComboboxCollection = _chunk7RGKC52Kcjs.Ga; exports.ComboboxContent = _chunk7RGKC52Kcjs.Ba; exports.ComboboxEmpty = _chunk7RGKC52Kcjs.Ha; exports.ComboboxGroup = _chunk7RGKC52Kcjs.Ea; exports.ComboboxInput = _chunk7RGKC52Kcjs.Aa; exports.ComboboxItem = _chunk7RGKC52Kcjs.Da; exports.ComboboxLabel = _chunk7RGKC52Kcjs.Fa; exports.ComboboxList = _chunk7RGKC52Kcjs.Ca; exports.ComboboxSeparator = _chunk7RGKC52Kcjs.Ia; exports.ComboboxTrigger = _chunk7RGKC52Kcjs.za; exports.ComboboxValue = _chunk7RGKC52Kcjs.ya; exports.Command = _chunk7RGKC52Kcjs.Xa; exports.CommandDialog = _chunk7RGKC52Kcjs.Ya; exports.CommandEmpty = _chunk7RGKC52Kcjs.$a; exports.CommandGroup = _chunk7RGKC52Kcjs.ab; exports.CommandInput = _chunk7RGKC52Kcjs.Za; exports.CommandItem = _chunk7RGKC52Kcjs.cb; exports.CommandList = _chunk7RGKC52Kcjs._a; exports.CommandSeparator = _chunk7RGKC52Kcjs.bb; exports.CommandShortcut = _chunk7RGKC52Kcjs.db; exports.ContextMenu = _chunk7RGKC52Kcjs.eb; exports.ContextMenuCheckboxItem = _chunk7RGKC52Kcjs.ob; exports.ContextMenuContent = _chunk7RGKC52Kcjs.kb; exports.ContextMenuGroup = _chunk7RGKC52Kcjs.gb; exports.ContextMenuItem = _chunk7RGKC52Kcjs.lb; exports.ContextMenuLabel = _chunk7RGKC52Kcjs.qb; exports.ContextMenuPortal = _chunk7RGKC52Kcjs.hb; exports.ContextMenuRadioGroup = _chunk7RGKC52Kcjs.jb; exports.ContextMenuRadioItem = _chunk7RGKC52Kcjs.pb; exports.ContextMenuSeparator = _chunk7RGKC52Kcjs.rb; exports.ContextMenuShortcut = _chunk7RGKC52Kcjs.sb; exports.ContextMenuSub = _chunk7RGKC52Kcjs.ib; exports.ContextMenuSubContent = _chunk7RGKC52Kcjs.nb; exports.ContextMenuSubTrigger = _chunk7RGKC52Kcjs.mb; exports.ContextMenuTrigger = _chunk7RGKC52Kcjs.fb; exports.Dialog = _chunk7RGKC52Kcjs.Na; exports.DialogClose = _chunk7RGKC52Kcjs.Qa; exports.DialogContent = _chunk7RGKC52Kcjs.Sa; exports.DialogDescription = _chunk7RGKC52Kcjs.Wa; exports.DialogFooter = _chunk7RGKC52Kcjs.Ua; exports.DialogHeader = _chunk7RGKC52Kcjs.Ta; exports.DialogOverlay = _chunk7RGKC52Kcjs.Ra; exports.DialogPortal = _chunk7RGKC52Kcjs.Pa; exports.DialogTitle = _chunk7RGKC52Kcjs.Va; exports.DialogTrigger = _chunk7RGKC52Kcjs.Oa; exports.Drawer = _chunk7RGKC52Kcjs.tb; exports.DrawerClose = _chunk7RGKC52Kcjs.wb; exports.DrawerContent = _chunk7RGKC52Kcjs.yb; exports.DrawerDescription = _chunk7RGKC52Kcjs.Cb; exports.DrawerFooter = _chunk7RGKC52Kcjs.Ab; exports.DrawerHeader = _chunk7RGKC52Kcjs.zb; exports.DrawerOverlay = _chunk7RGKC52Kcjs.xb; exports.DrawerPortal = _chunk7RGKC52Kcjs.vb; exports.DrawerTitle = _chunk7RGKC52Kcjs.Bb; exports.DrawerTrigger = _chunk7RGKC52Kcjs.ub; exports.DropdownMenu = _chunk7RGKC52Kcjs.Db; exports.DropdownMenuCheckboxItem = _chunk7RGKC52Kcjs.Jb; exports.DropdownMenuContent = _chunk7RGKC52Kcjs.Gb; exports.DropdownMenuGroup = _chunk7RGKC52Kcjs.Hb; exports.DropdownMenuItem = _chunk7RGKC52Kcjs.Ib; exports.DropdownMenuLabel = _chunk7RGKC52Kcjs.Mb; exports.DropdownMenuPortal = _chunk7RGKC52Kcjs.Eb; exports.DropdownMenuRadioGroup = _chunk7RGKC52Kcjs.Kb; exports.DropdownMenuRadioItem = _chunk7RGKC52Kcjs.Lb; exports.DropdownMenuSeparator = _chunk7RGKC52Kcjs.Nb; exports.DropdownMenuShortcut = _chunk7RGKC52Kcjs.Ob; exports.DropdownMenuSub = _chunk7RGKC52Kcjs.Pb; exports.DropdownMenuSubContent = _chunk7RGKC52Kcjs.Rb; exports.DropdownMenuSubTrigger = _chunk7RGKC52Kcjs.Qb; exports.DropdownMenuTrigger = _chunk7RGKC52Kcjs.Fb; exports.Empty = _chunk7RGKC52Kcjs.Sb; exports.EmptyContent = _chunk7RGKC52Kcjs.Xb; exports.EmptyDescription = _chunk7RGKC52Kcjs.Wb; exports.EmptyHeader = _chunk7RGKC52Kcjs.Tb; exports.EmptyMedia = _chunk7RGKC52Kcjs.Ub; exports.EmptyTitle = _chunk7RGKC52Kcjs.Vb; exports.Field = _chunk7RGKC52Kcjs.ac; exports.FieldContent = _chunk7RGKC52Kcjs.bc; exports.FieldDescription = _chunk7RGKC52Kcjs.ec; exports.FieldError = _chunk7RGKC52Kcjs.gc; exports.FieldGroup = _chunk7RGKC52Kcjs.$b; exports.FieldLabel = _chunk7RGKC52Kcjs.cc; exports.FieldLegend = _chunk7RGKC52Kcjs._b; exports.FieldSeparator = _chunk7RGKC52Kcjs.fc; exports.FieldSet = _chunk7RGKC52Kcjs.Zb; exports.FieldTitle = _chunk7RGKC52Kcjs.dc; exports.HoverCard = _chunk7RGKC52Kcjs.hc; exports.HoverCardContent = _chunk7RGKC52Kcjs.jc; exports.HoverCardTrigger = _chunk7RGKC52Kcjs.ic; exports.Input = _chunk7RGKC52Kcjs.pa; exports.InputGroup = _chunk7RGKC52Kcjs.ra; exports.InputGroupAddon = _chunk7RGKC52Kcjs.sa; exports.InputGroupButton = _chunk7RGKC52Kcjs.ta; exports.InputGroupInput = _chunk7RGKC52Kcjs.va; exports.InputGroupText = _chunk7RGKC52Kcjs.ua; exports.InputGroupTextarea = _chunk7RGKC52Kcjs.wa; exports.InputOTP = _chunk7RGKC52Kcjs.kc; exports.InputOTPGroup = _chunk7RGKC52Kcjs.lc; exports.InputOTPSeparator = _chunk7RGKC52Kcjs.nc; exports.InputOTPSlot = _chunk7RGKC52Kcjs.mc; exports.Item = _chunk7RGKC52Kcjs.qc; exports.ItemActions = _chunk7RGKC52Kcjs.vc; exports.ItemContent = _chunk7RGKC52Kcjs.sc; exports.ItemDescription = _chunk7RGKC52Kcjs.uc; exports.ItemFooter = _chunk7RGKC52Kcjs.xc; exports.ItemGroup = _chunk7RGKC52Kcjs.oc; exports.ItemHeader = _chunk7RGKC52Kcjs.wc; exports.ItemMedia = _chunk7RGKC52Kcjs.rc; exports.ItemSeparator = _chunk7RGKC52Kcjs.pc; exports.ItemTitle = _chunk7RGKC52Kcjs.tc; exports.Kbd = _chunk7RGKC52Kcjs.yc; exports.KbdGroup = _chunk7RGKC52Kcjs.zc; exports.KeyboardAvoidingView = ei; exports.Label = _chunk7RGKC52Kcjs.Yb; exports.Menubar = _chunk7RGKC52Kcjs.Ac; exports.MenubarCheckboxItem = _chunk7RGKC52Kcjs.Ic; exports.MenubarContent = _chunk7RGKC52Kcjs.Gc; exports.MenubarGroup = _chunk7RGKC52Kcjs.Cc; exports.MenubarItem = _chunk7RGKC52Kcjs.Hc; exports.MenubarLabel = _chunk7RGKC52Kcjs.Kc; exports.MenubarMenu = _chunk7RGKC52Kcjs.Bc; exports.MenubarPortal = _chunk7RGKC52Kcjs.Dc; exports.MenubarRadioGroup = _chunk7RGKC52Kcjs.Ec; exports.MenubarRadioItem = _chunk7RGKC52Kcjs.Jc; exports.MenubarSeparator = _chunk7RGKC52Kcjs.Lc; exports.MenubarShortcut = _chunk7RGKC52Kcjs.Mc; exports.MenubarSub = _chunk7RGKC52Kcjs.Nc; exports.MenubarSubContent = _chunk7RGKC52Kcjs.Pc; exports.MenubarSubTrigger = _chunk7RGKC52Kcjs.Oc; exports.MenubarTrigger = _chunk7RGKC52Kcjs.Fc; exports.NavigationMenu = _chunk7RGKC52Kcjs.Qc; exports.NavigationMenuContent = _chunk7RGKC52Kcjs.Vc; exports.NavigationMenuIndicator = _chunk7RGKC52Kcjs.Yc; exports.NavigationMenuItem = _chunk7RGKC52Kcjs.Sc; exports.NavigationMenuLink = _chunk7RGKC52Kcjs.Xc; exports.NavigationMenuList = _chunk7RGKC52Kcjs.Rc; exports.NavigationMenuTrigger = _chunk7RGKC52Kcjs.Uc; exports.NavigationMenuViewport = _chunk7RGKC52Kcjs.Wc; exports.Pagination = _chunk7RGKC52Kcjs.Zc; exports.PaginationContent = _chunk7RGKC52Kcjs._c; exports.PaginationEllipsis = _chunk7RGKC52Kcjs.dd; exports.PaginationItem = _chunk7RGKC52Kcjs.$c; exports.PaginationLink = _chunk7RGKC52Kcjs.ad; exports.PaginationNext = _chunk7RGKC52Kcjs.cd; exports.PaginationPrevious = _chunk7RGKC52Kcjs.bd; exports.PlatformHeader = oi; exports.Popover = _chunk7RGKC52Kcjs.ed; exports.PopoverAnchor = _chunk7RGKC52Kcjs.hd; exports.PopoverContent = _chunk7RGKC52Kcjs.gd; exports.PopoverDescription = _chunk7RGKC52Kcjs.kd; exports.PopoverHeader = _chunk7RGKC52Kcjs.id; exports.PopoverTitle = _chunk7RGKC52Kcjs.jd; exports.PopoverTrigger = _chunk7RGKC52Kcjs.fd; exports.Progress = _chunk7RGKC52Kcjs.ld; exports.RadioGroup = _chunk7RGKC52Kcjs.md; exports.RadioGroupItem = _chunk7RGKC52Kcjs.nd; exports.ResizableHandle = _chunk7RGKC52Kcjs.qd; exports.ResizablePanel = _chunk7RGKC52Kcjs.pd; exports.ResizablePanelGroup = _chunk7RGKC52Kcjs.od; exports.SafeAreaProvider = qa; exports.SafeAreaView = ti; exports.ScrollArea = _chunk7RGKC52Kcjs.rd; exports.ScrollBar = _chunk7RGKC52Kcjs.sd; exports.Select = _chunk7RGKC52Kcjs.td; exports.SelectContent = _chunk7RGKC52Kcjs.xd; exports.SelectGroup = _chunk7RGKC52Kcjs.ud; exports.SelectItem = _chunk7RGKC52Kcjs.zd; exports.SelectLabel = _chunk7RGKC52Kcjs.yd; exports.SelectScrollDownButton = _chunk7RGKC52Kcjs.Cd; exports.SelectScrollUpButton = _chunk7RGKC52Kcjs.Bd; exports.SelectSeparator = _chunk7RGKC52Kcjs.Ad; exports.SelectTrigger = _chunk7RGKC52Kcjs.wd; exports.SelectValue = _chunk7RGKC52Kcjs.vd; exports.Separator = _chunk7RGKC52Kcjs.N; exports.Sheet = _chunk7RGKC52Kcjs.Dd; exports.SheetClose = _chunk7RGKC52Kcjs.Fd; exports.SheetContent = _chunk7RGKC52Kcjs.Gd; exports.SheetDescription = _chunk7RGKC52Kcjs.Kd; exports.SheetFooter = _chunk7RGKC52Kcjs.Id; exports.SheetHeader = _chunk7RGKC52Kcjs.Hd; exports.SheetTitle = _chunk7RGKC52Kcjs.Jd; exports.SheetTrigger = _chunk7RGKC52Kcjs.Ed; exports.Sidebar = _chunk7RGKC52Kcjs.Td; exports.SidebarContent = _chunk7RGKC52Kcjs.$d; exports.SidebarFooter = _chunk7RGKC52Kcjs.Zd; exports.SidebarGroup = _chunk7RGKC52Kcjs.ae; exports.SidebarGroupAction = _chunk7RGKC52Kcjs.ce; exports.SidebarGroupContent = _chunk7RGKC52Kcjs.de; exports.SidebarGroupLabel = _chunk7RGKC52Kcjs.be; exports.SidebarHeader = _chunk7RGKC52Kcjs.Yd; exports.SidebarInput = _chunk7RGKC52Kcjs.Xd; exports.SidebarInset = _chunk7RGKC52Kcjs.Wd; exports.SidebarMenu = _chunk7RGKC52Kcjs.ee; exports.SidebarMenuAction = _chunk7RGKC52Kcjs.he; exports.SidebarMenuBadge = _chunk7RGKC52Kcjs.ie; exports.SidebarMenuButton = _chunk7RGKC52Kcjs.ge; exports.SidebarMenuItem = _chunk7RGKC52Kcjs.fe; exports.SidebarMenuSkeleton = _chunk7RGKC52Kcjs.je; exports.SidebarMenuSub = _chunk7RGKC52Kcjs.ke; exports.SidebarMenuSubButton = _chunk7RGKC52Kcjs.me; exports.SidebarMenuSubItem = _chunk7RGKC52Kcjs.le; exports.SidebarProvider = _chunk7RGKC52Kcjs.Sd; exports.SidebarRail = _chunk7RGKC52Kcjs.Vd; exports.SidebarSeparator = _chunk7RGKC52Kcjs._d; exports.SidebarTrigger = _chunk7RGKC52Kcjs.Ud; exports.Skeleton = _chunk7RGKC52Kcjs.Ld; exports.Slider = _chunk7RGKC52Kcjs.ne; exports.Spinner = _chunk7RGKC52Kcjs.pe; exports.Switch = _chunk7RGKC52Kcjs.qe; exports.Table = _chunk7RGKC52Kcjs.re; exports.TableBody = _chunk7RGKC52Kcjs.te; exports.TableCaption = _chunk7RGKC52Kcjs.ye; exports.TableCell = _chunk7RGKC52Kcjs.xe; exports.TableFooter = _chunk7RGKC52Kcjs.ue; exports.TableHead = _chunk7RGKC52Kcjs.we; exports.TableHeader = _chunk7RGKC52Kcjs.se; exports.TableRow = _chunk7RGKC52Kcjs.ve; exports.Tabs = _chunk7RGKC52Kcjs.ze; exports.TabsContent = _chunk7RGKC52Kcjs.De; exports.TabsList = _chunk7RGKC52Kcjs.Be; exports.TabsTrigger = _chunk7RGKC52Kcjs.Ce; exports.Textarea = _chunk7RGKC52Kcjs.qa; exports.Toaster = _chunk7RGKC52Kcjs.oe; exports.Toggle = _chunk7RGKC52Kcjs.Fe; exports.ToggleGroup = _chunk7RGKC52Kcjs.Ge; exports.ToggleGroupItem = _chunk7RGKC52Kcjs.He; exports.Tooltip = _chunk7RGKC52Kcjs.Nd; exports.TooltipContent = _chunk7RGKC52Kcjs.Pd; exports.TooltipProvider = _chunk7RGKC52Kcjs.Md; exports.TooltipTrigger = _chunk7RGKC52Kcjs.Od; exports.badgeVariants = _chunk7RGKC52Kcjs.E; exports.buttonGroupVariants = _chunk7RGKC52Kcjs.O; exports.buttonVariants = _chunk7RGKC52Kcjs.f; exports.cn = _chunk7RGKC52Kcjs.a; exports.navigationMenuTriggerStyle = _chunk7RGKC52Kcjs.Tc; exports.tabsListVariants = _chunk7RGKC52Kcjs.Ae; exports.toggleVariants = _chunk7RGKC52Kcjs.Ee; exports.useAppState = ja; exports.useBackHandler = Wa; exports.useCamera = bi; exports.useCarousel = _chunk7RGKC52Kcjs.$; exports.useClipboard = mi; exports.useColorScheme = za; exports.useComboboxAnchor = _chunk7RGKC52Kcjs.Ma; exports.useFocusTimer = ii; exports.useGeolocation = gi; exports.useIsMobile = _chunk7RGKC52Kcjs.Qd; exports.useKeyboard = L; exports.useNotifications = li; exports.useOllama = Si; exports.useRelayClose = Ua; exports.useSafeArea = A; exports.useSidebar = _chunk7RGKC52Kcjs.Rd; exports.useStatusBar = $a;
10
10
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/jas/Desktop/relay/sdk/dist/index.cjs","../src/hooks/use-keyboard.ts","../src/hooks/use-platform.tsx","../src/hooks/use-focus-timer.ts"],"names":["useKeyboard","state","setState","useState","useEffect","updateKeyboardState","viewport","keyboardHeight","isOpen","handleFocusIn","e","target","handleFocusOut","useRelayClose","useCallback","defaultInsets","SafeAreaContext","createContext","SafeAreaProvider","children","insets","setInsets","computeInsets","style","getEnv","name","value"],"mappings":"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","file":"/Users/jas/Desktop/relay/sdk/dist/index.cjs","sourcesContent":[null,"import { useState, useEffect, useCallback } from 'react';\n\ninterface KeyboardState {\n isOpen: boolean;\n height: number;\n}\n\n/**\n * useKeyboard - Track virtual keyboard state on mobile devices\n * \n * Uses visualViewport API to detect keyboard height and provides\n * a safe area offset for positioning UI elements above the keyboard.\n * \n * @returns {KeyboardState} - { isOpen: boolean, height: number }\n */\nexport function useKeyboard(): KeyboardState {\n const [state, setState] = useState<KeyboardState>({\n isOpen: false,\n height: 0\n });\n\n useEffect(() => {\n // Check if we're on a mobile device\n const isMobile = /iPad|iPhone|iPod|Android/.test(navigator.userAgent);\n if (!isMobile) return;\n\n const updateKeyboardState = () => {\n if (!window.visualViewport) return;\n\n const viewport = window.visualViewport;\n // Calculate keyboard height: difference between window and viewport height\n const keyboardHeight = window.innerHeight - viewport.height;\n // Consider keyboard open if height is more than 100px (threshold for accessory bars)\n const isOpen = keyboardHeight > 100;\n\n setState({\n isOpen,\n height: isOpen ? keyboardHeight : 0\n });\n };\n\n // Listen to visualViewport changes\n if (window.visualViewport) {\n window.visualViewport.addEventListener('resize', updateKeyboardState);\n window.visualViewport.addEventListener('scroll', updateKeyboardState);\n }\n\n // Also listen for focus/blur on inputs as fallback\n const handleFocusIn = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {\n // Small delay to let keyboard animate\n setTimeout(updateKeyboardState, 100);\n }\n };\n\n const handleFocusOut = () => {\n // Small delay to let keyboard animate closed\n setTimeout(() => {\n setState({ isOpen: false, height: 0 });\n }, 100);\n };\n\n document.addEventListener('focusin', handleFocusIn);\n document.addEventListener('focusout', handleFocusOut);\n\n // Initial check\n updateKeyboardState();\n\n return () => {\n window.visualViewport?.removeEventListener('resize', updateKeyboardState);\n window.visualViewport?.removeEventListener('scroll', updateKeyboardState);\n document.removeEventListener('focusin', handleFocusIn);\n document.removeEventListener('focusout', handleFocusOut);\n };\n }, []);\n\n return state;\n}\n\n/**\n * useRelayClose - Get the close function for closing the current app\n * \n * Works with both iframe and blob URL contexts.\n * \n * @returns {() => void} - Function to close the current app\n */\nexport function useRelayClose(): () => void {\n const close = useCallback(() => {\n // Try multiple methods to close\n // 1. PostMessage to parent (works in iframe)\n if (window.parent !== window) {\n window.parent.postMessage('relay:close', '*');\n }\n // 2. PostMessage with opener (works if opened as popup)\n if (window.opener) {\n window.opener.postMessage('relay:close', '*');\n }\n // 3. Try self-closing (may not work in all contexts)\n window.close();\n }, []);\n\n return close;\n}\n","import { useState, useEffect, useCallback, createContext, useContext, ReactNode } from 'react';\n\n// ============================================\n// SAFE AREA TYPES & CONTEXT\n// ============================================\n\nexport interface SafeAreaInsets {\n top: number;\n bottom: number;\n left: number;\n right: number;\n}\n\nconst defaultInsets: SafeAreaInsets = {\n top: 0,\n bottom: 0,\n left: 0,\n right: 0\n};\n\nconst SafeAreaContext = createContext<SafeAreaInsets>(defaultInsets);\n\n// ============================================\n// SAFE AREA PROVIDER\n// ============================================\n\ninterface SafeAreaProviderProps {\n children: ReactNode;\n}\n\n/**\n * SafeAreaProvider - Provides safe area insets to child components\n * \n * Automatically detects safe areas from:\n * 1. CSS env() values (iOS Safari)\n * 2. Parent Shell postMessage (when running in Relay Shell)\n */\nexport function SafeAreaProvider({ children }: SafeAreaProviderProps) {\n const [insets, setInsets] = useState<SafeAreaInsets>(defaultInsets);\n\n useEffect(() => {\n // Method 1: Read from CSS env() values\n const computeInsets = () => {\n const style = getComputedStyle(document.documentElement);\n const getEnv = (name: string) => {\n const value = style.getPropertyValue(`--safe-area-inset-${name}`);\n return parseInt(value) || 0;\n };\n\n // Try to get from CSS custom properties first\n let top = getEnv('top');\n let bottom = getEnv('bottom');\n let left = getEnv('left');\n let right = getEnv('right');\n\n // If CSS props not set, try to read from env() directly\n if (top === 0 && bottom === 0) {\n // Create a temp element to measure env() values\n const temp = document.createElement('div');\n temp.style.cssText = `\n position: fixed;\n top: env(safe-area-inset-top, 0px);\n bottom: env(safe-area-inset-bottom, 0px);\n left: env(safe-area-inset-left, 0px);\n right: env(safe-area-inset-right, 0px);\n pointer-events: none;\n visibility: hidden;\n `;\n document.body.appendChild(temp);\n const rect = temp.getBoundingClientRect();\n top = rect.top;\n bottom = window.innerHeight - rect.bottom;\n left = rect.left;\n right = window.innerWidth - rect.right;\n document.body.removeChild(temp);\n }\n\n setInsets({ top, bottom, left, right });\n };\n\n // Method 2: Listen for Shell messages\n const handleMessage = (event: MessageEvent) => {\n if (event.data?.type === 'relay:safearea') {\n setInsets(event.data.insets);\n }\n };\n\n computeInsets();\n window.addEventListener('message', handleMessage);\n window.addEventListener('resize', computeInsets);\n\n return () => {\n window.removeEventListener('message', handleMessage);\n window.removeEventListener('resize', computeInsets);\n };\n }, []);\n\n return (\n <SafeAreaContext.Provider value={insets}>\n {children}\n </SafeAreaContext.Provider>\n );\n}\n\n// ============================================\n// SAFE AREA HOOKS\n// ============================================\n\n/**\n * useSafeArea - Get safe area insets\n * \n * @returns SafeAreaInsets with top, bottom, left, right values in pixels\n */\nexport function useSafeArea(): SafeAreaInsets {\n return useContext(SafeAreaContext);\n}\n\n// ============================================\n// COLOR SCHEME\n// ============================================\n\nexport type ColorScheme = 'light' | 'dark';\n\n/**\n * useColorScheme - Get current system color scheme\n * \n * @returns 'light' | 'dark'\n */\nexport function useColorScheme(): ColorScheme {\n const [scheme, setScheme] = useState<ColorScheme>(() =>\n window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n );\n\n useEffect(() => {\n const media = window.matchMedia('(prefers-color-scheme: dark)');\n const handler = (e: MediaQueryListEvent) => {\n setScheme(e.matches ? 'dark' : 'light');\n };\n media.addEventListener('change', handler);\n return () => media.removeEventListener('change', handler);\n }, []);\n\n return scheme;\n}\n\n// ============================================\n// STATUS BAR\n// ============================================\n\n/**\n * useStatusBar - Control the status bar appearance\n * \n * @param color - Hex color for the status bar\n */\nexport function useStatusBar(color: string): void {\n useEffect(() => {\n // Update meta theme-color\n let meta = document.querySelector('meta[name=\"theme-color\"]');\n if (!meta) {\n meta = document.createElement('meta');\n meta.setAttribute('name', 'theme-color');\n document.head.appendChild(meta);\n }\n meta.setAttribute('content', color);\n\n // Notify parent Shell\n window.parent?.postMessage({ type: 'relay:statusbar', color }, '*');\n }, [color]);\n}\n\n// ============================================\n// BACK HANDLER\n// ============================================\n\n/**\n * useBackHandler - Handle back navigation\n * \n * @param handler - Return true to prevent default back behavior\n */\nexport function useBackHandler(handler: () => boolean): void {\n useEffect(() => {\n const handleMessage = (event: MessageEvent) => {\n if (event.data === 'relay:back') {\n const handled = handler();\n if (!handled) {\n // Allow default close behavior\n window.parent?.postMessage('relay:close', '*');\n }\n }\n };\n\n // Also handle browser back button\n const handlePopState = (event: PopStateEvent) => {\n const handled = handler();\n if (handled) {\n // Push a dummy state to prevent navigation\n window.history.pushState(null, '', window.location.href);\n }\n };\n\n // Push initial state for popstate handling\n window.history.pushState(null, '', window.location.href);\n\n window.addEventListener('message', handleMessage);\n window.addEventListener('popstate', handlePopState);\n\n return () => {\n window.removeEventListener('message', handleMessage);\n window.removeEventListener('popstate', handlePopState);\n };\n }, [handler]);\n}\n\n// ============================================\n// APP STATE\n// ============================================\n\nexport type AppState = 'active' | 'background' | 'inactive';\n\n/**\n * useAppState - Track app foreground/background state\n * \n * @returns Current app state\n */\nexport function useAppState(): AppState {\n const [state, setState] = useState<AppState>('active');\n\n useEffect(() => {\n const handleVisibility = () => {\n setState(document.hidden ? 'background' : 'active');\n };\n\n const handleMessage = (event: MessageEvent) => {\n if (event.data === 'relay:foreground') setState('active');\n if (event.data === 'relay:background') setState('background');\n };\n\n document.addEventListener('visibilitychange', handleVisibility);\n window.addEventListener('message', handleMessage);\n\n return () => {\n document.removeEventListener('visibilitychange', handleVisibility);\n window.removeEventListener('message', handleMessage);\n };\n }, []);\n\n return state;\n}\n","import { useState, useEffect } from 'react';\n\nexport type TimerMode = 'focus' | 'shortBreak' | 'longBreak';\nexport type StoryState = 'egg' | 'cracked' | 'chick' | 'alien' | 'ghost';\n\nexport function useFocusTimer() {\n const [seconds, setSeconds] = useState(25 * 60);\n const [initialTime, setInitialTime] = useState(25 * 60);\n const [isActive, setIsActive] = useState(false);\n const [mode, setMode] = useState<TimerMode>('focus');\n const [task, setTask] = useState('');\n const [storyState, setStoryState] = useState<StoryState>('egg');\n const [soundEnabled, setSoundEnabled] = useState(false);\n\n useEffect(() => {\n let interval: any = null;\n\n if (isActive && seconds > 0) {\n interval = setInterval(() => {\n setSeconds((s) => s - 1);\n }, 1000);\n } else if (seconds === 0 && isActive) {\n // Timer Finished\n setIsActive(false);\n if (mode === 'focus') {\n const isAbducted = Math.random() < 0.01;\n setStoryState(isAbducted ? 'alien' : 'chick');\n }\n }\n\n return () => {\n if (interval) clearInterval(interval);\n };\n }, [isActive, seconds, mode]);\n\n // Update Visuals based on progress\n useEffect(() => {\n if (!isActive && (storyState === 'chick' || storyState === 'alien' || storyState === 'ghost')) return;\n\n // Don't change chick state during break\n if (mode !== 'focus') return;\n\n const progress = 1 - (seconds / initialTime);\n\n if (progress < 0.5) {\n setStoryState('egg');\n } else if (progress >= 0.5 && progress < 1) {\n setStoryState('cracked');\n }\n }, [seconds, initialTime, isActive, storyState, mode]);\n\n const toggleTimer = () => setIsActive(!isActive);\n\n const giveUp = () => {\n setIsActive(false);\n setStoryState('ghost');\n };\n\n const switchMode = (newMode: TimerMode) => {\n setIsActive(false);\n setMode(newMode);\n setStoryState('egg'); // Reset chick for new session\n\n // Set Times\n let newTime = 25 * 60;\n if (newMode === 'shortBreak') newTime = 5 * 60;\n if (newMode === 'longBreak') newTime = 15 * 60;\n\n setSeconds(newTime);\n setInitialTime(newTime);\n };\n\n const resetTimer = () => {\n setIsActive(false);\n switchMode(mode); // Re-init current mode\n };\n\n const setCustomTime = (mins: number) => {\n setIsActive(false);\n setSeconds(mins * 60);\n setInitialTime(mins * 60);\n setStoryState('egg');\n };\n\n return {\n seconds,\n initialTime,\n isActive,\n mode,\n task,\n storyState,\n soundEnabled,\n setSeconds,\n setInitialTime,\n setIsActive,\n setMode,\n setTask,\n setStoryState,\n setSoundEnabled,\n toggleTimer,\n giveUp,\n switchMode,\n resetTimer,\n setCustomTime,\n };\n}\n"]}
1
+ {"version":3,"sources":["/Users/jas/Desktop/relay/sdk/dist/index.cjs","../src/hooks/use-keyboard.ts","../src/hooks/use-platform.tsx","../src/hooks/use-focus-timer.ts"],"names":["useKeyboard","state","setState","useState","useEffect","updateKeyboardState","viewport","keyboardHeight","isOpen","handleFocusIn","e","target","handleFocusOut","useRelayClose","useCallback","defaultInsets","SafeAreaContext","createContext","SafeAreaProvider","children","insets","setInsets","computeInsets","style","getEnv","name","value","setSeconds"],"mappings":"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","file":"/Users/jas/Desktop/relay/sdk/dist/index.cjs","sourcesContent":[null,"import { useState, useEffect, useCallback } from 'react';\n\ninterface KeyboardState {\n isOpen: boolean;\n height: number;\n}\n\n/**\n * useKeyboard - Track virtual keyboard state on mobile devices\n * \n * Uses visualViewport API to detect keyboard height and provides\n * a safe area offset for positioning UI elements above the keyboard.\n * \n * @returns {KeyboardState} - { isOpen: boolean, height: number }\n */\nexport function useKeyboard(): KeyboardState {\n const [state, setState] = useState<KeyboardState>({\n isOpen: false,\n height: 0\n });\n\n useEffect(() => {\n // Check if we're on a mobile device\n const isMobile = /iPad|iPhone|iPod|Android/.test(navigator.userAgent);\n if (!isMobile) return;\n\n const updateKeyboardState = () => {\n if (!window.visualViewport) return;\n\n const viewport = window.visualViewport;\n // Calculate keyboard height: difference between window and viewport height\n const keyboardHeight = window.innerHeight - viewport.height;\n // Consider keyboard open if height is more than 100px (threshold for accessory bars)\n const isOpen = keyboardHeight > 100;\n\n setState({\n isOpen,\n height: isOpen ? keyboardHeight : 0\n });\n };\n\n // Listen to visualViewport changes\n if (window.visualViewport) {\n window.visualViewport.addEventListener('resize', updateKeyboardState);\n window.visualViewport.addEventListener('scroll', updateKeyboardState);\n }\n\n // Also listen for focus/blur on inputs as fallback\n const handleFocusIn = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {\n // Small delay to let keyboard animate\n setTimeout(updateKeyboardState, 100);\n }\n };\n\n const handleFocusOut = () => {\n // Small delay to let keyboard animate closed\n setTimeout(() => {\n setState({ isOpen: false, height: 0 });\n }, 100);\n };\n\n document.addEventListener('focusin', handleFocusIn);\n document.addEventListener('focusout', handleFocusOut);\n\n // Initial check\n updateKeyboardState();\n\n return () => {\n window.visualViewport?.removeEventListener('resize', updateKeyboardState);\n window.visualViewport?.removeEventListener('scroll', updateKeyboardState);\n document.removeEventListener('focusin', handleFocusIn);\n document.removeEventListener('focusout', handleFocusOut);\n };\n }, []);\n\n return state;\n}\n\n/**\n * useRelayClose - Get the close function for closing the current app\n * \n * Works with both iframe and blob URL contexts.\n * \n * @returns {() => void} - Function to close the current app\n */\nexport function useRelayClose(): () => void {\n const close = useCallback(() => {\n // Try multiple methods to close\n // 1. PostMessage to parent (works in iframe)\n if (window.parent !== window) {\n window.parent.postMessage('relay:close', '*');\n }\n // 2. PostMessage with opener (works if opened as popup)\n if (window.opener) {\n window.opener.postMessage('relay:close', '*');\n }\n // 3. Try self-closing (may not work in all contexts)\n window.close();\n }, []);\n\n return close;\n}\n","import { useState, useEffect, useCallback, createContext, useContext, ReactNode } from 'react';\n\n// ============================================\n// SAFE AREA TYPES & CONTEXT\n// ============================================\n\nexport interface SafeAreaInsets {\n top: number;\n bottom: number;\n left: number;\n right: number;\n}\n\nconst defaultInsets: SafeAreaInsets = {\n top: 0,\n bottom: 0,\n left: 0,\n right: 0\n};\n\nconst SafeAreaContext = createContext<SafeAreaInsets>(defaultInsets);\n\n// ============================================\n// SAFE AREA PROVIDER\n// ============================================\n\ninterface SafeAreaProviderProps {\n children: ReactNode;\n}\n\n/**\n * SafeAreaProvider - Provides safe area insets to child components\n * \n * Automatically detects safe areas from:\n * 1. CSS env() values (iOS Safari)\n * 2. Parent Shell postMessage (when running in Relay Shell)\n */\nexport function SafeAreaProvider({ children }: SafeAreaProviderProps) {\n const [insets, setInsets] = useState<SafeAreaInsets>(defaultInsets);\n\n useEffect(() => {\n // Method 1: Read from CSS env() values\n const computeInsets = () => {\n const style = getComputedStyle(document.documentElement);\n const getEnv = (name: string) => {\n const value = style.getPropertyValue(`--safe-area-inset-${name}`);\n return parseInt(value) || 0;\n };\n\n // Try to get from CSS custom properties first\n let top = getEnv('top');\n let bottom = getEnv('bottom');\n let left = getEnv('left');\n let right = getEnv('right');\n\n // If CSS props not set, try to read from env() directly\n if (top === 0 && bottom === 0) {\n // Create a temp element to measure env() values\n const temp = document.createElement('div');\n temp.style.cssText = `\n position: fixed;\n top: env(safe-area-inset-top, 0px);\n bottom: env(safe-area-inset-bottom, 0px);\n left: env(safe-area-inset-left, 0px);\n right: env(safe-area-inset-right, 0px);\n pointer-events: none;\n visibility: hidden;\n `;\n document.body.appendChild(temp);\n const rect = temp.getBoundingClientRect();\n top = rect.top;\n bottom = window.innerHeight - rect.bottom;\n left = rect.left;\n right = window.innerWidth - rect.right;\n document.body.removeChild(temp);\n }\n\n setInsets({ top, bottom, left, right });\n };\n\n // Method 2: Listen for Shell messages\n const handleMessage = (event: MessageEvent) => {\n if (event.data?.type === 'relay:safearea') {\n setInsets(event.data.insets);\n }\n };\n\n computeInsets();\n window.addEventListener('message', handleMessage);\n window.addEventListener('resize', computeInsets);\n\n return () => {\n window.removeEventListener('message', handleMessage);\n window.removeEventListener('resize', computeInsets);\n };\n }, []);\n\n return (\n <SafeAreaContext.Provider value={insets}>\n {children}\n </SafeAreaContext.Provider>\n );\n}\n\n// ============================================\n// SAFE AREA HOOKS\n// ============================================\n\n/**\n * useSafeArea - Get safe area insets\n * \n * @returns SafeAreaInsets with top, bottom, left, right values in pixels\n */\nexport function useSafeArea(): SafeAreaInsets {\n return useContext(SafeAreaContext);\n}\n\n// ============================================\n// COLOR SCHEME\n// ============================================\n\nexport type ColorScheme = 'light' | 'dark';\n\n/**\n * useColorScheme - Get current system color scheme\n * \n * @returns 'light' | 'dark'\n */\nexport function useColorScheme(): ColorScheme {\n const [scheme, setScheme] = useState<ColorScheme>(() =>\n window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n );\n\n useEffect(() => {\n const media = window.matchMedia('(prefers-color-scheme: dark)');\n const handler = (e: MediaQueryListEvent) => {\n setScheme(e.matches ? 'dark' : 'light');\n };\n media.addEventListener('change', handler);\n return () => media.removeEventListener('change', handler);\n }, []);\n\n return scheme;\n}\n\n// ============================================\n// STATUS BAR\n// ============================================\n\n/**\n * useStatusBar - Control the status bar appearance\n * \n * @param color - Hex color for the status bar\n */\nexport function useStatusBar(color: string): void {\n useEffect(() => {\n // Update meta theme-color\n let meta = document.querySelector('meta[name=\"theme-color\"]');\n if (!meta) {\n meta = document.createElement('meta');\n meta.setAttribute('name', 'theme-color');\n document.head.appendChild(meta);\n }\n meta.setAttribute('content', color);\n\n // Notify parent Shell\n window.parent?.postMessage({ type: 'relay:statusbar', color }, '*');\n }, [color]);\n}\n\n// ============================================\n// BACK HANDLER\n// ============================================\n\n/**\n * useBackHandler - Handle back navigation\n * \n * @param handler - Return true to prevent default back behavior\n */\nexport function useBackHandler(handler: () => boolean): void {\n useEffect(() => {\n const handleMessage = (event: MessageEvent) => {\n if (event.data === 'relay:back') {\n const handled = handler();\n if (!handled) {\n // Allow default close behavior\n window.parent?.postMessage('relay:close', '*');\n }\n }\n };\n\n // Also handle browser back button\n const handlePopState = (event: PopStateEvent) => {\n const handled = handler();\n if (handled) {\n // Push a dummy state to prevent navigation\n window.history.pushState(null, '', window.location.href);\n }\n };\n\n // Push initial state for popstate handling\n window.history.pushState(null, '', window.location.href);\n\n window.addEventListener('message', handleMessage);\n window.addEventListener('popstate', handlePopState);\n\n return () => {\n window.removeEventListener('message', handleMessage);\n window.removeEventListener('popstate', handlePopState);\n };\n }, [handler]);\n}\n\n// ============================================\n// APP STATE\n// ============================================\n\nexport type AppState = 'active' | 'background' | 'inactive';\n\n/**\n * useAppState - Track app foreground/background state\n * \n * @returns Current app state\n */\nexport function useAppState(): AppState {\n const [state, setState] = useState<AppState>('active');\n\n useEffect(() => {\n const handleVisibility = () => {\n setState(document.hidden ? 'background' : 'active');\n };\n\n const handleMessage = (event: MessageEvent) => {\n if (event.data === 'relay:foreground') setState('active');\n if (event.data === 'relay:background') setState('background');\n };\n\n document.addEventListener('visibilitychange', handleVisibility);\n window.addEventListener('message', handleMessage);\n\n return () => {\n document.removeEventListener('visibilitychange', handleVisibility);\n window.removeEventListener('message', handleMessage);\n };\n }, []);\n\n return state;\n}\n","import { useState, useEffect } from 'react';\n\nexport type TimerMode = 'focus' | 'shortBreak' | 'longBreak';\nexport type StoryState = 'egg' | 'cracked' | 'chick' | 'alien' | 'ghost';\n\nexport function useFocusTimer() {\n const [seconds, setSeconds] = useState(25 * 60);\n const [initialTime, setInitialTime] = useState(25 * 60);\n const [isActive, setIsActive] = useState(false);\n const [mode, setMode] = useState<TimerMode>('focus');\n const [task, setTask] = useState('');\n const [storyState, setStoryState] = useState<StoryState>('egg');\n const [soundEnabled, setSoundEnabled] = useState(false);\n\n useEffect(() => {\n let interval: any = null;\n\n if (isActive && seconds > 0) {\n interval = setInterval(() => {\n setSeconds((s) => s - 1);\n }, 1000);\n } else if (seconds === 0 && isActive) {\n // Timer Finished\n setIsActive(false);\n if (mode === 'focus') {\n const isAbducted = Math.random() < 0.01;\n setStoryState(isAbducted ? 'alien' : 'chick');\n }\n }\n\n return () => {\n if (interval) clearInterval(interval);\n };\n }, [isActive, seconds, mode]);\n\n // Update Visuals based on progress\n useEffect(() => {\n if (!isActive && (storyState === 'chick' || storyState === 'alien' || storyState === 'ghost')) return;\n\n // Don't change chick state during break\n if (mode !== 'focus') return;\n\n const progress = 1 - (seconds / initialTime);\n\n if (progress < 0.5) {\n setStoryState('egg');\n } else if (progress >= 0.5 && progress < 1) {\n setStoryState('cracked');\n }\n }, [seconds, initialTime, isActive, storyState, mode]);\n\n const toggleTimer = () => setIsActive(!isActive);\n\n const giveUp = () => {\n setIsActive(false);\n setStoryState('ghost');\n };\n\n const switchMode = (newMode: TimerMode) => {\n setIsActive(false);\n setMode(newMode);\n setStoryState('egg'); // Reset chick for new session\n\n // Set Times\n let newTime = 25 * 60;\n if (newMode === 'shortBreak') newTime = 5 * 60;\n if (newMode === 'longBreak') newTime = 15 * 60;\n\n setSeconds(newTime);\n setInitialTime(newTime);\n };\n\n const resetTimer = () => {\n setIsActive(false);\n switchMode(mode); // Re-init current mode\n };\n\n const setCustomTime = (mins: number) => {\n setIsActive(false);\n setSeconds(mins * 60);\n setInitialTime(mins * 60);\n setStoryState('egg');\n };\n\n return {\n seconds,\n initialTime,\n isActive,\n mode,\n task,\n storyState,\n soundEnabled,\n setSeconds,\n setInitialTime,\n setIsActive,\n setMode,\n setTask,\n setStoryState,\n setSoundEnabled,\n toggleTimer,\n giveUp,\n switchMode,\n resetTimer,\n setCustomTime,\n };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -45,6 +45,14 @@ interface SafeAreaViewProps {
45
45
  * @param edges - Which edges to apply safe area to (default: all)
46
46
  */
47
47
  declare function SafeAreaView({ children, edges, style, className }: SafeAreaViewProps): react_jsx_runtime.JSX.Element;
48
+ interface PlatformHeaderProps {
49
+ title: string;
50
+ showBack?: boolean;
51
+ onBack?: () => void;
52
+ className?: string;
53
+ rightElement?: ReactNode;
54
+ }
55
+ declare function PlatformHeader({ title, showBack, onBack, className, rightElement }: PlatformHeaderProps): react_jsx_runtime.JSX.Element;
48
56
 
49
57
  type TimerMode = 'focus' | 'shortBreak' | 'longBreak';
50
58
  type StoryState = 'egg' | 'cracked' | 'chick' | 'alien' | 'ghost';
@@ -144,4 +152,139 @@ type AppState = 'active' | 'background' | 'inactive';
144
152
  */
145
153
  declare function useAppState(): AppState;
146
154
 
147
- export { type AppState, type ColorScheme, KeyboardAvoidingView, type SafeAreaInsets, SafeAreaProvider, SafeAreaView, type StoryState, type TimerMode, cn, useAppState, useBackHandler, useColorScheme, useFocusTimer, useIsMobile, useKeyboard, useRelayClose, useSafeArea, useStatusBar };
155
+ interface NotificationOptions {
156
+ body?: string;
157
+ icon?: string;
158
+ image?: string;
159
+ badge?: string;
160
+ tag?: string;
161
+ data?: any;
162
+ vibrate?: number[];
163
+ timestamp?: number;
164
+ renotify?: boolean;
165
+ silent?: boolean;
166
+ requireInteraction?: boolean;
167
+ actions?: NotificationAction[];
168
+ }
169
+ interface NotificationAction {
170
+ action: string;
171
+ title: string;
172
+ icon?: string;
173
+ }
174
+ interface UseNotificationsReturn {
175
+ permission: NotificationPermission;
176
+ requestPermission: () => Promise<NotificationPermission>;
177
+ sendNotification: (title: string, options?: NotificationOptions) => void;
178
+ scheduleNotification: (title: string, delayMs: number, options?: NotificationOptions) => void;
179
+ clearBadge: () => Promise<void>;
180
+ setBadge: (count: number) => Promise<void>;
181
+ }
182
+ declare function useNotifications(): UseNotificationsReturn;
183
+
184
+ interface ClipboardItem {
185
+ type: 'text' | 'image' | 'html';
186
+ content: string | Blob;
187
+ timestamp: number;
188
+ }
189
+ interface UseClipboardReturn {
190
+ clipboardContent: string | null;
191
+ history: ClipboardItem[];
192
+ copyText: (text: string) => Promise<void>;
193
+ copyImage: (blob: Blob) => Promise<void>;
194
+ readText: () => Promise<string>;
195
+ readContent: () => Promise<ClipboardItems>;
196
+ clearHistory: () => void;
197
+ }
198
+ declare function useClipboard(): UseClipboardReturn;
199
+
200
+ interface LocationState {
201
+ loading: boolean;
202
+ accuracy: number | null;
203
+ altitude: number | null;
204
+ altitudeAccuracy: number | null;
205
+ heading: number | null;
206
+ latitude: number | null;
207
+ longitude: number | null;
208
+ speed: number | null;
209
+ timestamp: number | null;
210
+ error: GeolocationPositionError | null;
211
+ }
212
+ interface Geofence {
213
+ id: string;
214
+ latitude: number;
215
+ longitude: number;
216
+ radius: number;
217
+ }
218
+ interface UseGeolocationReturn extends LocationState {
219
+ getLocation: () => void;
220
+ watchLocation: (options?: PositionOptions) => void;
221
+ clearWatch: () => void;
222
+ checkGeofence: (fence: Geofence) => boolean;
223
+ distanceTo: (lat: number, lng: number) => number;
224
+ }
225
+ declare function useGeolocation(options?: PositionOptions): UseGeolocationReturn;
226
+
227
+ interface CameraState {
228
+ stream: MediaStream | null;
229
+ error: Error | null;
230
+ permission: PermissionState | 'unknown';
231
+ isRecording: boolean;
232
+ devices: MediaDeviceInfo[];
233
+ }
234
+ interface CameraOptions {
235
+ video?: boolean | MediaTrackConstraints;
236
+ audio?: boolean | MediaTrackConstraints;
237
+ }
238
+ interface UseCameraReturn extends CameraState {
239
+ startCamera: (options?: CameraOptions) => Promise<MediaStream | undefined>;
240
+ stopCamera: () => void;
241
+ takePhoto: () => Promise<string | undefined>;
242
+ startRecording: () => void;
243
+ stopRecording: () => Promise<Blob | undefined>;
244
+ startScreenShare: (options?: DisplayMediaStreamOptions) => Promise<MediaStream | undefined>;
245
+ switchDevice: (deviceId: string, kind: 'video' | 'audio') => Promise<void>;
246
+ getDevices: () => Promise<MediaDeviceInfo[]>;
247
+ }
248
+ declare function useCamera(): UseCameraReturn;
249
+
250
+ interface OllamaModel {
251
+ name: string;
252
+ modified_at: string;
253
+ size: number;
254
+ digest: string;
255
+ details: {
256
+ parent_model: string;
257
+ format: string;
258
+ family: string;
259
+ families: string[];
260
+ parameter_size: string;
261
+ quantization_level: string;
262
+ };
263
+ }
264
+ interface OllamaResponse {
265
+ model: string;
266
+ created_at: string;
267
+ response: string;
268
+ done: boolean;
269
+ context?: number[];
270
+ total_duration?: number;
271
+ load_duration?: number;
272
+ prompt_eval_count?: number;
273
+ prompt_eval_duration?: number;
274
+ eval_count?: number;
275
+ eval_duration?: number;
276
+ }
277
+ interface UseOllamaReturn {
278
+ available: boolean;
279
+ loading: boolean;
280
+ models: OllamaModel[];
281
+ refreshModels: () => Promise<void>;
282
+ generate: (model: string, prompt: string, options?: any) => Promise<OllamaResponse>;
283
+ chat: (model: string, messages: {
284
+ role: string;
285
+ content: string;
286
+ }[], options?: any) => Promise<OllamaResponse>;
287
+ }
288
+ declare function useOllama(): UseOllamaReturn;
289
+
290
+ export { type AppState, type CameraOptions, type CameraState, type ClipboardItem, type ColorScheme, type Geofence, KeyboardAvoidingView, type LocationState, type NotificationAction, type NotificationOptions, type OllamaModel, type OllamaResponse, PlatformHeader, type SafeAreaInsets, SafeAreaProvider, SafeAreaView, type StoryState, type TimerMode, type UseCameraReturn, type UseClipboardReturn, type UseGeolocationReturn, type UseNotificationsReturn, type UseOllamaReturn, cn, useAppState, useBackHandler, useCamera, useClipboard, useColorScheme, useFocusTimer, useGeolocation, useIsMobile, useKeyboard, useNotifications, useOllama, useRelayClose, useSafeArea, useStatusBar };
package/dist/index.d.ts CHANGED
@@ -45,6 +45,14 @@ interface SafeAreaViewProps {
45
45
  * @param edges - Which edges to apply safe area to (default: all)
46
46
  */
47
47
  declare function SafeAreaView({ children, edges, style, className }: SafeAreaViewProps): react_jsx_runtime.JSX.Element;
48
+ interface PlatformHeaderProps {
49
+ title: string;
50
+ showBack?: boolean;
51
+ onBack?: () => void;
52
+ className?: string;
53
+ rightElement?: ReactNode;
54
+ }
55
+ declare function PlatformHeader({ title, showBack, onBack, className, rightElement }: PlatformHeaderProps): react_jsx_runtime.JSX.Element;
48
56
 
49
57
  type TimerMode = 'focus' | 'shortBreak' | 'longBreak';
50
58
  type StoryState = 'egg' | 'cracked' | 'chick' | 'alien' | 'ghost';
@@ -144,4 +152,139 @@ type AppState = 'active' | 'background' | 'inactive';
144
152
  */
145
153
  declare function useAppState(): AppState;
146
154
 
147
- export { type AppState, type ColorScheme, KeyboardAvoidingView, type SafeAreaInsets, SafeAreaProvider, SafeAreaView, type StoryState, type TimerMode, cn, useAppState, useBackHandler, useColorScheme, useFocusTimer, useIsMobile, useKeyboard, useRelayClose, useSafeArea, useStatusBar };
155
+ interface NotificationOptions {
156
+ body?: string;
157
+ icon?: string;
158
+ image?: string;
159
+ badge?: string;
160
+ tag?: string;
161
+ data?: any;
162
+ vibrate?: number[];
163
+ timestamp?: number;
164
+ renotify?: boolean;
165
+ silent?: boolean;
166
+ requireInteraction?: boolean;
167
+ actions?: NotificationAction[];
168
+ }
169
+ interface NotificationAction {
170
+ action: string;
171
+ title: string;
172
+ icon?: string;
173
+ }
174
+ interface UseNotificationsReturn {
175
+ permission: NotificationPermission;
176
+ requestPermission: () => Promise<NotificationPermission>;
177
+ sendNotification: (title: string, options?: NotificationOptions) => void;
178
+ scheduleNotification: (title: string, delayMs: number, options?: NotificationOptions) => void;
179
+ clearBadge: () => Promise<void>;
180
+ setBadge: (count: number) => Promise<void>;
181
+ }
182
+ declare function useNotifications(): UseNotificationsReturn;
183
+
184
+ interface ClipboardItem {
185
+ type: 'text' | 'image' | 'html';
186
+ content: string | Blob;
187
+ timestamp: number;
188
+ }
189
+ interface UseClipboardReturn {
190
+ clipboardContent: string | null;
191
+ history: ClipboardItem[];
192
+ copyText: (text: string) => Promise<void>;
193
+ copyImage: (blob: Blob) => Promise<void>;
194
+ readText: () => Promise<string>;
195
+ readContent: () => Promise<ClipboardItems>;
196
+ clearHistory: () => void;
197
+ }
198
+ declare function useClipboard(): UseClipboardReturn;
199
+
200
+ interface LocationState {
201
+ loading: boolean;
202
+ accuracy: number | null;
203
+ altitude: number | null;
204
+ altitudeAccuracy: number | null;
205
+ heading: number | null;
206
+ latitude: number | null;
207
+ longitude: number | null;
208
+ speed: number | null;
209
+ timestamp: number | null;
210
+ error: GeolocationPositionError | null;
211
+ }
212
+ interface Geofence {
213
+ id: string;
214
+ latitude: number;
215
+ longitude: number;
216
+ radius: number;
217
+ }
218
+ interface UseGeolocationReturn extends LocationState {
219
+ getLocation: () => void;
220
+ watchLocation: (options?: PositionOptions) => void;
221
+ clearWatch: () => void;
222
+ checkGeofence: (fence: Geofence) => boolean;
223
+ distanceTo: (lat: number, lng: number) => number;
224
+ }
225
+ declare function useGeolocation(options?: PositionOptions): UseGeolocationReturn;
226
+
227
+ interface CameraState {
228
+ stream: MediaStream | null;
229
+ error: Error | null;
230
+ permission: PermissionState | 'unknown';
231
+ isRecording: boolean;
232
+ devices: MediaDeviceInfo[];
233
+ }
234
+ interface CameraOptions {
235
+ video?: boolean | MediaTrackConstraints;
236
+ audio?: boolean | MediaTrackConstraints;
237
+ }
238
+ interface UseCameraReturn extends CameraState {
239
+ startCamera: (options?: CameraOptions) => Promise<MediaStream | undefined>;
240
+ stopCamera: () => void;
241
+ takePhoto: () => Promise<string | undefined>;
242
+ startRecording: () => void;
243
+ stopRecording: () => Promise<Blob | undefined>;
244
+ startScreenShare: (options?: DisplayMediaStreamOptions) => Promise<MediaStream | undefined>;
245
+ switchDevice: (deviceId: string, kind: 'video' | 'audio') => Promise<void>;
246
+ getDevices: () => Promise<MediaDeviceInfo[]>;
247
+ }
248
+ declare function useCamera(): UseCameraReturn;
249
+
250
+ interface OllamaModel {
251
+ name: string;
252
+ modified_at: string;
253
+ size: number;
254
+ digest: string;
255
+ details: {
256
+ parent_model: string;
257
+ format: string;
258
+ family: string;
259
+ families: string[];
260
+ parameter_size: string;
261
+ quantization_level: string;
262
+ };
263
+ }
264
+ interface OllamaResponse {
265
+ model: string;
266
+ created_at: string;
267
+ response: string;
268
+ done: boolean;
269
+ context?: number[];
270
+ total_duration?: number;
271
+ load_duration?: number;
272
+ prompt_eval_count?: number;
273
+ prompt_eval_duration?: number;
274
+ eval_count?: number;
275
+ eval_duration?: number;
276
+ }
277
+ interface UseOllamaReturn {
278
+ available: boolean;
279
+ loading: boolean;
280
+ models: OllamaModel[];
281
+ refreshModels: () => Promise<void>;
282
+ generate: (model: string, prompt: string, options?: any) => Promise<OllamaResponse>;
283
+ chat: (model: string, messages: {
284
+ role: string;
285
+ content: string;
286
+ }[], options?: any) => Promise<OllamaResponse>;
287
+ }
288
+ declare function useOllama(): UseOllamaReturn;
289
+
290
+ export { type AppState, type CameraOptions, type CameraState, type ClipboardItem, type ColorScheme, type Geofence, KeyboardAvoidingView, type LocationState, type NotificationAction, type NotificationOptions, type OllamaModel, type OllamaResponse, PlatformHeader, type SafeAreaInsets, SafeAreaProvider, SafeAreaView, type StoryState, type TimerMode, type UseCameraReturn, type UseClipboardReturn, type UseGeolocationReturn, type UseNotificationsReturn, type UseOllamaReturn, cn, useAppState, useBackHandler, useCamera, useClipboard, useColorScheme, useFocusTimer, useGeolocation, useIsMobile, useKeyboard, useNotifications, useOllama, useRelayClose, useSafeArea, useStatusBar };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import{$ as Ke,$a as Kt,$b as Ro,$c as On,$d as Or,A as de,Aa as dt,Ab as co,Ac as ln,Ad as lr,Ae as ls,B as ce,Ba as ct,Bb as lo,Bc as pn,Bd as pr,Be as ps,C as le,Ca as lt,Cb as po,Cc as un,Cd as ur,Ce as us,D as pe,Da as pt,Db as uo,Dc as fn,Dd as fr,De as fs,E as ue,Ea as ut,Eb as fo,Ec as mn,Ed as mr,Ee as ms,F as fe,Fa as ft,Fb as mo,Fc as gn,Fd as gr,Fe as gs,G as me,Ga as mt,Gb as go,Gc as hn,Gd as hr,Ge as hs,H as ge,Ha as gt,Hb as ho,Hc as vn,Hd as vr,He as vs,I as he,Ia as ht,Ib as vo,Ic as wn,Id as wr,J as ve,Ja as vt,Jb as wo,Jc as Sn,Jd as Sr,K as we,Ka as wt,Kb as So,Kc as bn,Kd as br,L as Se,La as St,Lb as bo,Lc as yn,Ld as yr,M as be,Ma as bt,Mb as yo,Mc as En,Md as Er,N as ye,Na as yt,Nb as Eo,Nc as xn,Nd as xr,O as Ee,Oa as Et,Ob as xo,Oc as An,Od as Ar,P as xe,Pa as xt,Pb as Ao,Pc as kn,Pd as kr,Q as Ae,Qa as At,Qb as ko,Qc as Ln,Qd as Lr,R as ke,Ra as kt,Rb as Lo,Rc as Cn,Rd as Cr,S as Le,Sa as Lt,Sb as Co,Sc as Pn,Sd as Pr,T as Ce,Ta as Ct,Tb as Po,Tc as Tn,Td as Tr,U as Pe,Ua as Pt,Ub as To,Uc as Mn,Ud as Mr,V as Te,Va as Tt,Vb as Mo,Vc as Vn,Vd as Vr,W as Me,Wa as Mt,Wb as Vo,Wc as In,Wd as Ir,X as Ve,Xa as Vt,Xb as Io,Xc as Bn,Xd as Br,Y as Ie,Ya as It,Yb as Bo,Yc as Nn,Yd as Nr,Z as Be,Za as Bt,Zb as No,Zc as Kn,Zd as Kr,_ as Ne,_a as Nt,_b as Ko,_c as Rn,_d as Rr,a as R,aa as Re,ab as Rt,ac as Oo,ad as Hn,ae as Hr,b as O,ba as Oe,bb as Ot,bc as Ho,bd as zn,be as zr,c as H,ca as He,cb as Ht,cc as zo,cd as Fn,ce as Fr,d as z,da as ze,db as zt,dc as Fo,dd as $n,de as $r,e as F,ea as Fe,eb as Ft,ec as $o,ed as Un,ee as Ur,f as $,fa as $e,fb as $t,fc as Uo,fd as qn,fe as qr,g as U,ga as Ue,gb as Ut,gc as qo,gd as Qn,ge as Qr,h as q,ha as qe,hb as qt,hc as Qo,hd as Wn,he as Wr,i as Q,ia as Qe,ib as Qt,ic as Wo,id as Xn,ie as Xr,j as W,ja as We,jb as Wt,jc as Xo,jd as Yn,je as Yr,k as X,ka as Xe,kb as Xt,kc as Yo,kd as Dn,ke as Dr,l as Y,la as Ye,lb as Yt,lc as Do,ld as Gn,le as Gr,m as D,ma as De,mb as Dt,mc as Go,md as Jn,me as Jr,n as G,na as Ge,nb as Gt,nc as Jo,nd as Zn,ne as Zr,o as J,oa as Je,ob as Jt,oc as Zo,od as _n,oe as _r,p as Z,pa as Ze,pb as Zt,pc as _o,pd as jn,pe as jr,q as _,qa as _e,qb as _t,qc as jo,qd as er,qe as es,r as j,ra as je,rb as jt,rc as en,rd as tr,re as ts,s as ee,sa as et,sb as eo,sc as tn,sd as or,se as os,t as te,ta as tt,tb as to,tc as on,td as nr,te as ns,u as oe,ua as ot,ub as oo,uc as nn,ud as rr,ue as rs,v as ne,va as nt,vb as no,vc as rn,vd as sr,ve as ss,w as re,wa as rt,wb as ro,wc as sn,wd as ir,we as is,x as se,xa as st,xb as so,xc as an,xd as ar,xe as as,y as ie,ya as it,yb as io,yc as dn,yd as dr,ye as ds,z as ae,za as at,zb as ao,zc as cn,zd as cr,ze as cs}from"./chunk-6VSTRIAD.js";import{useState as P,useEffect as T,useCallback as M}from"react";function S(){let[t,e]=P({isOpen:!1,height:0});return T(()=>{if(!/iPad|iPhone|iPod|Android/.test(navigator.userAgent))return;let o=()=>{if(!window.visualViewport)return;let i=window.visualViewport,a=window.innerHeight-i.height,l=a>100;e({isOpen:l,height:l?a:0})};window.visualViewport&&(window.visualViewport.addEventListener("resize",o),window.visualViewport.addEventListener("scroll",o));let n=i=>{let a=i.target;(a.tagName==="INPUT"||a.tagName==="TEXTAREA")&&setTimeout(o,100)},s=()=>{setTimeout(()=>{e({isOpen:!1,height:0})},100)};return document.addEventListener("focusin",n),document.addEventListener("focusout",s),o(),()=>{window.visualViewport?.removeEventListener("resize",o),window.visualViewport?.removeEventListener("scroll",o),document.removeEventListener("focusin",n),document.removeEventListener("focusout",s)}},[]),t}function Ss(){return M(()=>{window.parent!==window&&window.parent.postMessage("relay:close","*"),window.opener&&window.opener.postMessage("relay:close","*"),window.close()},[])}import{useState as v,useEffect as g,createContext as V,useContext as I}from"react";import{jsx as B}from"react/jsx-runtime";var b={top:0,bottom:0,left:0,right:0},y=V(b);function As({children:t}){let[e,r]=v(b);return g(()=>{let o=()=>{let s=getComputedStyle(document.documentElement),i=d=>{let u=s.getPropertyValue(`--safe-area-inset-${d}`);return parseInt(u)||0},a=i("top"),l=i("bottom"),h=i("left"),p=i("right");if(a===0&&l===0){let d=document.createElement("div");d.style.cssText=`
1
+ import{$ as tt,$a as to,$b as on,$c as nr,$d as na,A as Ne,Aa as Nt,Ab as Io,Ac as An,Ad as Ar,Ae as Aa,B as Ie,Ba as It,Bb as Ao,Bc as Rn,Bd as Rr,Be as Ra,C as Ae,Ca as At,Cb as Ro,Cc as Tn,Cd as Tr,Ce as Ta,D as Re,Da as Rt,Db as To,Dc as On,Dd as Or,De as Oa,E as Te,Ea as Tt,Eb as Oo,Ec as Ln,Ed as Lr,Ee as La,F as Oe,Fa as Ot,Fb as Lo,Fc as Bn,Fd as Br,Fe as Ba,G as Le,Ga as Lt,Gb as Bo,Gc as Dn,Gd as Dr,Ge as Da,H as Be,Ha as Bt,Hb as Do,Hc as Hn,Hd as Hr,He as Ha,I as De,Ia as Dt,Ib as Ho,Ic as _n,Id as _r,J as He,Ja as Ht,Jb as _o,Jc as Un,Jd as Ur,K as _e,Ka as _t,Kb as Uo,Kc as Vn,Kd as Vr,L as Ue,La as Ut,Lb as Vo,Lc as Gn,Ld as Gr,M as Ve,Ma as Vt,Mb as Go,Mc as Fn,Md as Fr,N as Ge,Na as Gt,Nb as Fo,Nc as Kn,Nd as Kr,O as Fe,Oa as Ft,Ob as Ko,Oc as qn,Od as qr,P as Ke,Pa as Kt,Pb as qo,Pc as zn,Pd as zr,Q as qe,Qa as qt,Qb as zo,Qc as $n,Qd as $r,R as ze,Ra as zt,Rb as $o,Rc as Wn,Rd as Wr,S as $e,Sa as $t,Sb as Wo,Sc as jn,Sd as jr,T as We,Ta as Wt,Tb as jo,Tc as Jn,Td as Jr,U as je,Ua as jt,Ub as Jo,Uc as Yn,Ud as Yr,V as Je,Va as Jt,Vb as Yo,Vc as Xn,Vd as Xr,W as Ye,Wa as Yt,Wb as Xo,Wc as Qn,Wd as Qr,X as Xe,Xa as Xt,Xb as Qo,Xc as Zn,Xd as Zr,Y as Qe,Ya as Qt,Yb as Zo,Yc as er,Yd as ea,Z as Ze,Za as Zt,Zb as en,Zc as tr,Zd as ta,_ as et,_a as eo,_b as tn,_c as or,_d as oa,a as O,aa as ot,ab as oo,ac as nn,ad as rr,ae as ra,b as ne,ba as nt,bb as no,bc as rn,bd as ar,be as aa,c as re,ca as rt,cb as ro,cc as an,cd as ir,ce as ia,d as ae,da as at,db as ao,dc as sn,dd as sr,de as sa,e as ie,ea as it,eb as io,ec as cn,ed as cr,ee as ca,f as se,fa as st,fb as so,fc as ln,fd as lr,fe as la,g as ce,ga as ct,gb as co,gc as dn,gd as dr,ge as da,h as le,ha as lt,hb as lo,hc as un,hd as ur,he as ua,i as de,ia as dt,ib as uo,ic as mn,id as mr,ie as ma,j as ue,ja as ut,jb as mo,jc as pn,jd as pr,je as pa,k as me,ka as mt,kb as po,kc as fn,kd as fr,ke as fa,l as pe,la as pt,lb as fo,lc as gn,ld as gr,le as ga,m as fe,ma as ft,mb as go,mc as hn,md as hr,me as ha,n as ge,na as gt,nb as ho,nc as vn,nd as vr,ne as va,o as he,oa as ht,ob as vo,oc as bn,od as br,oe as ba,p as ve,pa as vt,pb as bo,pc as wn,pd as wr,pe as wa,q as be,qa as bt,qb as wo,qc as yn,qd as yr,qe as ya,r as we,ra as wt,rb as yo,rc as Sn,rd as Sr,re as Sa,s as ye,sa as yt,sb as So,sc as xn,sd as xr,se as xa,t as Se,ta as St,tb as xo,tc as En,td as Er,te as Ea,u as xe,ua as xt,ub as Eo,uc as Pn,ud as Pr,ue as Pa,v as Ee,va as Et,vb as Po,vc as Cn,vd as Cr,ve as Ca,w as Pe,wa as Pt,wb as Co,wc as kn,wd as kr,we as ka,x as Ce,xa as Ct,xb as ko,xc as Mn,xd as Mr,xe as Ma,y as ke,ya as kt,yb as Mo,yc as Nn,yd as Nr,ye as Na,z as Me,za as Mt,zb as No,zc as In,zd as Ir,ze as Ia}from"./chunk-6VSTRIAD.js";import{useState as q,useEffect as z,useCallback as $}from"react";function L(){let[o,t]=q({isOpen:!1,height:0});return z(()=>{if(!/iPad|iPhone|iPod|Android/.test(navigator.userAgent))return;let n=()=>{if(!window.visualViewport)return;let u=window.visualViewport,l=window.innerHeight-u.height,p=l>100;t({isOpen:p,height:p?l:0})};window.visualViewport&&(window.visualViewport.addEventListener("resize",n),window.visualViewport.addEventListener("scroll",n));let r=u=>{let l=u.target;(l.tagName==="INPUT"||l.tagName==="TEXTAREA")&&setTimeout(n,100)},d=()=>{setTimeout(()=>{t({isOpen:!1,height:0})},100)};return document.addEventListener("focusin",r),document.addEventListener("focusout",d),n(),()=>{window.visualViewport?.removeEventListener("resize",n),window.visualViewport?.removeEventListener("scroll",n),document.removeEventListener("focusin",r),document.removeEventListener("focusout",d)}},[]),o}function Ua(){return $(()=>{window.parent!==window&&window.parent.postMessage("relay:close","*"),window.opener&&window.opener.postMessage("relay:close","*"),window.close()},[])}import{useState as I,useEffect as P,createContext as W,useContext as j}from"react";import{jsx as J}from"react/jsx-runtime";var B={top:0,bottom:0,left:0,right:0},D=W(B);function qa({children:o}){let[t,s]=I(B);return P(()=>{let n=()=>{let d=getComputedStyle(document.documentElement),u=e=>{let a=d.getPropertyValue(`--safe-area-inset-${e}`);return parseInt(a)||0},l=u("top"),p=u("bottom"),f=u("left"),i=u("right");if(l===0&&p===0){let e=document.createElement("div");e.style.cssText=`
2
2
  position: fixed;
3
3
  top: env(safe-area-inset-top, 0px);
4
4
  bottom: env(safe-area-inset-bottom, 0px);
@@ -6,5 +6,5 @@ import{$ as Ke,$a as Kt,$b as Ro,$c as On,$d as Or,A as de,Aa as dt,Ab as co,Ac
6
6
  right: env(safe-area-inset-right, 0px);
7
7
  pointer-events: none;
8
8
  visibility: hidden;
9
- `,document.body.appendChild(d);let u=d.getBoundingClientRect();a=u.top,l=window.innerHeight-u.bottom,h=u.left,p=window.innerWidth-u.right,document.body.removeChild(d)}r({top:a,bottom:l,left:h,right:p})},n=s=>{s.data?.type==="relay:safearea"&&r(s.data.insets)};return o(),window.addEventListener("message",n),window.addEventListener("resize",o),()=>{window.removeEventListener("message",n),window.removeEventListener("resize",o)}},[]),B(y.Provider,{value:e,children:t})}function E(){return I(y)}function ks(){let[t,e]=v(()=>window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light");return g(()=>{let r=window.matchMedia("(prefers-color-scheme: dark)"),o=n=>{e(n.matches?"dark":"light")};return r.addEventListener("change",o),()=>r.removeEventListener("change",o)},[]),t}function Ls(t){g(()=>{let e=document.querySelector('meta[name="theme-color"]');e||(e=document.createElement("meta"),e.setAttribute("name","theme-color"),document.head.appendChild(e)),e.setAttribute("content",t),window.parent?.postMessage({type:"relay:statusbar",color:t},"*")},[t])}function Cs(t){g(()=>{let e=o=>{o.data==="relay:back"&&(t()||window.parent?.postMessage("relay:close","*"))},r=o=>{t()&&window.history.pushState(null,"",window.location.href)};return window.history.pushState(null,"",window.location.href),window.addEventListener("message",e),window.addEventListener("popstate",r),()=>{window.removeEventListener("message",e),window.removeEventListener("popstate",r)}},[t])}function Ps(){let[t,e]=v("active");return g(()=>{let r=()=>{e(document.hidden?"background":"active")},o=n=>{n.data==="relay:foreground"&&e("active"),n.data==="relay:background"&&e("background")};return document.addEventListener("visibilitychange",r),window.addEventListener("message",o),()=>{document.removeEventListener("visibilitychange",r),window.removeEventListener("message",o)}},[]),t}import{jsx as x}from"react/jsx-runtime";function Bs({children:t,behavior:e="padding",keyboardVerticalOffset:r=0,style:o,className:n}){let s=S(),i=s.height+r,a={...o,transition:"all 0.2s ease-out"};if(s.isOpen)switch(e){case"padding":a.paddingBottom=i;break;case"height":a.height=`calc(100% - ${i}px)`;break;case"position":a.transform=`translateY(-${i}px)`;break}return x("div",{style:a,className:n,children:t})}function Ns({children:t,edges:e=["top","bottom","left","right"],style:r,className:o}){let n=E(),s={...r,paddingTop:e.includes("top")?n.top:r?.paddingTop,paddingBottom:e.includes("bottom")?n.bottom:r?.paddingBottom,paddingLeft:e.includes("left")?n.left:r?.paddingLeft,paddingRight:e.includes("right")?n.right:r?.paddingRight};return x("div",{style:s,className:o,children:t})}import{useState as m,useEffect as A}from"react";function Hs(){let[t,e]=m(1500),[r,o]=m(1500),[n,s]=m(!1),[i,a]=m("focus"),[l,h]=m(""),[p,d]=m("egg"),[u,k]=m(!1);A(()=>{let c=null;if(n&&t>0)c=setInterval(()=>{e(f=>f-1)},1e3);else if(t===0&&n&&(s(!1),i==="focus")){let f=Math.random()<.01;d(f?"alien":"chick")}return()=>{c&&clearInterval(c)}},[n,t,i]),A(()=>{if(!n&&(p==="chick"||p==="alien"||p==="ghost")||i!=="focus")return;let c=1-t/r;c<.5?d("egg"):c>=.5&&c<1&&d("cracked")},[t,r,n,p,i]);let L=()=>s(!n),C=()=>{s(!1),d("ghost")},w=c=>{s(!1),a(c),d("egg");let f=1500;c==="shortBreak"&&(f=300),c==="longBreak"&&(f=900),e(f),o(f)};return{seconds:t,initialTime:r,isActive:n,mode:i,task:l,storyState:p,soundEnabled:u,setSeconds:e,setInitialTime:o,setIsActive:s,setMode:a,setTask:h,setStoryState:d,setSoundEnabled:k,toggleTimer:L,giveUp:C,switchMode:w,resetTimer:()=>{s(!1),w(i)},setCustomTime:c=>{s(!1),e(c*60),o(c*60),d("egg")}}}export{O as Accordion,F as AccordionContent,H as AccordionItem,z as AccordionTrigger,te as Alert,re as AlertAction,ne as AlertDescription,q as AlertDialog,j as AlertDialogAction,ee as AlertDialogCancel,Y as AlertDialogContent,_ as AlertDialogDescription,G as AlertDialogFooter,D as AlertDialogHeader,J as AlertDialogMedia,X as AlertDialogOverlay,W as AlertDialogPortal,Z as AlertDialogTitle,Q as AlertDialogTrigger,oe as AlertTitle,se as AspectRatio,ie as Avatar,ce as AvatarBadge,de as AvatarFallback,le as AvatarGroup,pe as AvatarGroupCount,ae as AvatarImage,fe as Badge,me as Breadcrumb,be as BreadcrumbEllipsis,he as BreadcrumbItem,ve as BreadcrumbLink,ge as BreadcrumbList,we as BreadcrumbPage,Se as BreadcrumbSeparator,U as Button,xe as ButtonGroup,ke as ButtonGroupSeparator,Ae as ButtonGroupText,Le as Calendar,Ce as CalendarDayButton,Pe as Card,Ie as CardAction,Be as CardContent,Ve as CardDescription,Ne as CardFooter,Te as CardHeader,Me as CardTitle,Re as Carousel,Oe as CarouselContent,He as CarouselItem,Fe as CarouselNext,ze as CarouselPrevious,$e as ChartContainer,We as ChartLegend,Xe as ChartLegendContent,Ue as ChartStyle,qe as ChartTooltip,Qe as ChartTooltipContent,Ye as Checkbox,De as Collapsible,Je as CollapsibleContent,Ge as CollapsibleTrigger,st as Combobox,wt as ComboboxChip,vt as ComboboxChips,St as ComboboxChipsInput,mt as ComboboxCollection,ct as ComboboxContent,gt as ComboboxEmpty,ut as ComboboxGroup,dt as ComboboxInput,pt as ComboboxItem,ft as ComboboxLabel,lt as ComboboxList,ht as ComboboxSeparator,at as ComboboxTrigger,it as ComboboxValue,Vt as Command,It as CommandDialog,Kt as CommandEmpty,Rt as CommandGroup,Bt as CommandInput,Ht as CommandItem,Nt as CommandList,Ot as CommandSeparator,zt as CommandShortcut,Ft as ContextMenu,Jt as ContextMenuCheckboxItem,Xt as ContextMenuContent,Ut as ContextMenuGroup,Yt as ContextMenuItem,_t as ContextMenuLabel,qt as ContextMenuPortal,Wt as ContextMenuRadioGroup,Zt as ContextMenuRadioItem,jt as ContextMenuSeparator,eo as ContextMenuShortcut,Qt as ContextMenuSub,Gt as ContextMenuSubContent,Dt as ContextMenuSubTrigger,$t as ContextMenuTrigger,yt as Dialog,At as DialogClose,Lt as DialogContent,Mt as DialogDescription,Pt as DialogFooter,Ct as DialogHeader,kt as DialogOverlay,xt as DialogPortal,Tt as DialogTitle,Et as DialogTrigger,to as Drawer,ro as DrawerClose,io as DrawerContent,po as DrawerDescription,co as DrawerFooter,ao as DrawerHeader,so as DrawerOverlay,no as DrawerPortal,lo as DrawerTitle,oo as DrawerTrigger,uo as DropdownMenu,wo as DropdownMenuCheckboxItem,go as DropdownMenuContent,ho as DropdownMenuGroup,vo as DropdownMenuItem,yo as DropdownMenuLabel,fo as DropdownMenuPortal,So as DropdownMenuRadioGroup,bo as DropdownMenuRadioItem,Eo as DropdownMenuSeparator,xo as DropdownMenuShortcut,Ao as DropdownMenuSub,Lo as DropdownMenuSubContent,ko as DropdownMenuSubTrigger,mo as DropdownMenuTrigger,Co as Empty,Io as EmptyContent,Vo as EmptyDescription,Po as EmptyHeader,To as EmptyMedia,Mo as EmptyTitle,Oo as Field,Ho as FieldContent,$o as FieldDescription,qo as FieldError,Ro as FieldGroup,zo as FieldLabel,Ko as FieldLegend,Uo as FieldSeparator,No as FieldSet,Fo as FieldTitle,Qo as HoverCard,Xo as HoverCardContent,Wo as HoverCardTrigger,Ze as Input,je as InputGroup,et as InputGroupAddon,tt as InputGroupButton,nt as InputGroupInput,ot as InputGroupText,rt as InputGroupTextarea,Yo as InputOTP,Do as InputOTPGroup,Jo as InputOTPSeparator,Go as InputOTPSlot,jo as Item,rn as ItemActions,tn as ItemContent,nn as ItemDescription,an as ItemFooter,Zo as ItemGroup,sn as ItemHeader,en as ItemMedia,_o as ItemSeparator,on as ItemTitle,dn as Kbd,cn as KbdGroup,Bs as KeyboardAvoidingView,Bo as Label,ln as Menubar,wn as MenubarCheckboxItem,hn as MenubarContent,un as MenubarGroup,vn as MenubarItem,bn as MenubarLabel,pn as MenubarMenu,fn as MenubarPortal,mn as MenubarRadioGroup,Sn as MenubarRadioItem,yn as MenubarSeparator,En as MenubarShortcut,xn as MenubarSub,kn as MenubarSubContent,An as MenubarSubTrigger,gn as MenubarTrigger,Ln as NavigationMenu,Vn as NavigationMenuContent,Nn as NavigationMenuIndicator,Pn as NavigationMenuItem,Bn as NavigationMenuLink,Cn as NavigationMenuList,Mn as NavigationMenuTrigger,In as NavigationMenuViewport,Kn as Pagination,Rn as PaginationContent,$n as PaginationEllipsis,On as PaginationItem,Hn as PaginationLink,Fn as PaginationNext,zn as PaginationPrevious,Un as Popover,Wn as PopoverAnchor,Qn as PopoverContent,Dn as PopoverDescription,Xn as PopoverHeader,Yn as PopoverTitle,qn as PopoverTrigger,Gn as Progress,Jn as RadioGroup,Zn as RadioGroupItem,er as ResizableHandle,jn as ResizablePanel,_n as ResizablePanelGroup,As as SafeAreaProvider,Ns as SafeAreaView,tr as ScrollArea,or as ScrollBar,nr as Select,ar as SelectContent,rr as SelectGroup,cr as SelectItem,dr as SelectLabel,ur as SelectScrollDownButton,pr as SelectScrollUpButton,lr as SelectSeparator,ir as SelectTrigger,sr as SelectValue,ye as Separator,fr as Sheet,gr as SheetClose,hr as SheetContent,br as SheetDescription,wr as SheetFooter,vr as SheetHeader,Sr as SheetTitle,mr as SheetTrigger,Tr as Sidebar,Or as SidebarContent,Kr as SidebarFooter,Hr as SidebarGroup,Fr as SidebarGroupAction,$r as SidebarGroupContent,zr as SidebarGroupLabel,Nr as SidebarHeader,Br as SidebarInput,Ir as SidebarInset,Ur as SidebarMenu,Wr as SidebarMenuAction,Xr as SidebarMenuBadge,Qr as SidebarMenuButton,qr as SidebarMenuItem,Yr as SidebarMenuSkeleton,Dr as SidebarMenuSub,Jr as SidebarMenuSubButton,Gr as SidebarMenuSubItem,Pr as SidebarProvider,Vr as SidebarRail,Rr as SidebarSeparator,Mr as SidebarTrigger,yr as Skeleton,Zr as Slider,jr as Spinner,es as Switch,ts as Table,ns as TableBody,ds as TableCaption,as as TableCell,rs as TableFooter,is as TableHead,os as TableHeader,ss as TableRow,cs as Tabs,fs as TabsContent,ps as TabsList,us as TabsTrigger,_e as Textarea,_r as Toaster,gs as Toggle,hs as ToggleGroup,vs as ToggleGroupItem,xr as Tooltip,kr as TooltipContent,Er as TooltipProvider,Ar as TooltipTrigger,ue as badgeVariants,Ee as buttonGroupVariants,$ as buttonVariants,R as cn,Tn as navigationMenuTriggerStyle,ls as tabsListVariants,ms as toggleVariants,Ps as useAppState,Cs as useBackHandler,Ke as useCarousel,ks as useColorScheme,bt as useComboboxAnchor,Hs as useFocusTimer,Lr as useIsMobile,S as useKeyboard,Ss as useRelayClose,E as useSafeArea,Cr as useSidebar,Ls as useStatusBar};
9
+ `,document.body.appendChild(e);let a=e.getBoundingClientRect();l=a.top,p=window.innerHeight-a.bottom,f=a.left,i=window.innerWidth-a.right,document.body.removeChild(e)}s({top:l,bottom:p,left:f,right:i})},r=d=>{d.data?.type==="relay:safearea"&&s(d.data.insets)};return n(),window.addEventListener("message",r),window.addEventListener("resize",n),()=>{window.removeEventListener("message",r),window.removeEventListener("resize",n)}},[]),J(D.Provider,{value:t,children:o})}function A(){return j(D)}function za(){let[o,t]=I(()=>window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light");return P(()=>{let s=window.matchMedia("(prefers-color-scheme: dark)"),n=r=>{t(r.matches?"dark":"light")};return s.addEventListener("change",n),()=>s.removeEventListener("change",n)},[]),o}function $a(o){P(()=>{let t=document.querySelector('meta[name="theme-color"]');t||(t=document.createElement("meta"),t.setAttribute("name","theme-color"),document.head.appendChild(t)),t.setAttribute("content",o),window.parent?.postMessage({type:"relay:statusbar",color:o},"*")},[o])}function Wa(o){P(()=>{let t=n=>{n.data==="relay:back"&&(o()||window.parent?.postMessage("relay:close","*"))},s=n=>{o()&&window.history.pushState(null,"",window.location.href)};return window.history.pushState(null,"",window.location.href),window.addEventListener("message",t),window.addEventListener("popstate",s),()=>{window.removeEventListener("message",t),window.removeEventListener("popstate",s)}},[o])}function ja(){let[o,t]=I("active");return P(()=>{let s=()=>{t(document.hidden?"background":"active")},n=r=>{r.data==="relay:foreground"&&t("active"),r.data==="relay:background"&&t("background")};return document.addEventListener("visibilitychange",s),window.addEventListener("message",n),()=>{document.removeEventListener("visibilitychange",s),window.removeEventListener("message",n)}},[]),o}import{jsx as b,jsxs as H}from"react/jsx-runtime";function ei({children:o,behavior:t="padding",keyboardVerticalOffset:s=0,style:n,className:r}){let d=L(),u=d.height+s,l={...n,transition:"all 0.2s ease-out"};if(d.isOpen)switch(t){case"padding":l.paddingBottom=u;break;case"height":l.height=`calc(100% - ${u}px)`;break;case"position":l.transform=`translateY(-${u}px)`;break}return b("div",{style:l,className:r,children:o})}function ti({children:o,edges:t=["top","bottom","left","right"],style:s,className:n}){let r=A(),d={...s,paddingTop:t.includes("top")?r.top:s?.paddingTop,paddingBottom:t.includes("bottom")?r.bottom:s?.paddingBottom,paddingLeft:t.includes("left")?r.left:s?.paddingLeft,paddingRight:t.includes("right")?r.right:s?.paddingRight};return b("div",{style:d,className:n,children:o})}function oi({title:o,showBack:t=!0,onBack:s,className:n,rightElement:r}){let d=A();return b("div",{className:O("bg-background border-b flex flex-col z-50",n),style:{paddingTop:d.top},children:H("div",{className:"h-14 flex items-center justify-between px-4",children:[H("div",{className:"flex items-center gap-4 flex-1",children:[t&&b("button",{onClick:s||(()=>window.history.back()),className:"p-2 -ml-2 hover:bg-muted rounded-full transition-colors",children:b("svg",{width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:b("path",{d:"M19 12H5M12 19l-7-7 7-7"})})}),b("h1",{className:"text-lg font-semibold truncate",children:o})]}),r&&b("div",{className:"flex-none",children:r})]})})}import{useState as S,useEffect as _}from"react";function ii(){let[o,t]=S(1500),[s,n]=S(1500),[r,d]=S(!1),[u,l]=S("focus"),[p,f]=S(""),[i,e]=S("egg"),[a,c]=S(!1);_(()=>{let g=null;if(r&&o>0)g=setInterval(()=>{t(v=>v-1)},1e3);else if(o===0&&r&&(d(!1),u==="focus")){let v=Math.random()<.01;e(v?"alien":"chick")}return()=>{g&&clearInterval(g)}},[r,o,u]),_(()=>{if(!r&&(i==="chick"||i==="alien"||i==="ghost")||u!=="focus")return;let g=1-o/s;g<.5?e("egg"):g>=.5&&g<1&&e("cracked")},[o,s,r,i,u]);let m=()=>d(!r),h=()=>{d(!1),e("ghost")},y=g=>{d(!1),l(g),e("egg");let v=1500;g==="shortBreak"&&(v=300),g==="longBreak"&&(v=900),t(v),n(v)};return{seconds:o,initialTime:s,isActive:r,mode:u,task:p,storyState:i,soundEnabled:a,setSeconds:t,setInitialTime:n,setIsActive:d,setMode:l,setTask:f,setStoryState:e,setSoundEnabled:c,toggleTimer:m,giveUp:h,switchMode:y,resetTimer:()=>{d(!1),y(u)},setCustomTime:g=>{d(!1),t(g*60),n(g*60),e("egg")}}}import{useState as Y,useCallback as C,useEffect as X}from"react";function li(){let[o,t]=Y("default");X(()=>{"Notification"in window&&t(Notification.permission)},[]);let s=C(async()=>{if(!("Notification"in window))return console.warn("Notifications not supported"),"denied";let l=await Notification.requestPermission();return t(l),l},[]),n=C((l,p)=>{if(!("Notification"in window))return;let f=p;if(!p?.icon){let i=document.querySelector('link[rel="apple-touch-icon"]')||document.querySelector('link[rel="icon"]');i&&(f={...p,icon:i.href})}Notification.permission==="granted"?new Notification(l,f):Notification.permission!=="denied"&&Notification.requestPermission().then(i=>{i==="granted"&&new Notification(l,f)})},[]),r=C((l,p,f)=>{setTimeout(()=>{n(l,f)},p)},[n]),d=C(async l=>{if("setAppBadge"in navigator)try{await navigator.setAppBadge(l)}catch(p){console.error("Error setting badge:",p)}},[]),u=C(async()=>{if("clearAppBadge"in navigator)try{await navigator.clearAppBadge()}catch(l){console.error("Error clearing badge:",l)}},[]);return{permission:o,requestPermission:s,sendNotification:n,scheduleNotification:r,setBadge:d,clearBadge:u}}import{useState as U,useCallback as E,useEffect as V}from"react";var R="relay_clipboard_history",Q=10;function mi(){let[o,t]=U(null),[s,n]=U([]);V(()=>{try{let i=localStorage.getItem(R);if(i){let e=JSON.parse(i);n(e)}}catch(i){console.error("Failed to load clipboard history",i)}},[]);let r=E(i=>{n(e=>{let a=[i,...e].slice(0,Q),c=a.filter(m=>m.type==="text");try{localStorage.setItem(R,JSON.stringify(c))}catch(m){console.error("Failed to save clipboard history",m)}return a})},[]),d=E(async i=>{if(!navigator.clipboard){console.warn("Clipboard API not supported");return}try{await navigator.clipboard.writeText(i),t(i),r({type:"text",content:i,timestamp:Date.now()})}catch(e){throw console.error("Failed to copy text:",e),e}},[r]),u=E(async i=>{if(!navigator.clipboard){console.warn("Clipboard API not supported");return}try{await navigator.clipboard.write([new ClipboardItem({[i.type]:i})]),r({type:"image",content:i,timestamp:Date.now()})}catch(e){throw console.error("Failed to copy image:",e),e}},[r]),l=E(async()=>{if(!navigator.clipboard)return console.warn("Clipboard API not supported"),"";try{let i=await navigator.clipboard.readText();return t(i),i}catch(i){throw console.error("Failed to read text:",i),i}},[]),p=E(async()=>{if(!navigator.clipboard)return console.warn("Clipboard API not supported"),[];try{return await navigator.clipboard.read()}catch(i){throw console.error("Failed to read content:",i),i}},[]),f=E(()=>{n([]),localStorage.removeItem(R)},[]);return V(()=>{let i=()=>{l().catch(()=>{})};return window.addEventListener("focus",i),()=>window.removeEventListener("focus",i)},[l]),{clipboardContent:o,history:s,copyText:d,copyImage:u,readText:l,readContent:p,clearHistory:f}}import{useState as Z,useCallback as x,useEffect as ee,useRef as te}from"react";function gi(o){let[t,s]=Z({loading:!0,accuracy:null,altitude:null,altitudeAccuracy:null,heading:null,latitude:null,longitude:null,speed:null,timestamp:null,error:null}),n=te(null),r=x(e=>{s({loading:!1,accuracy:e.coords.accuracy,altitude:e.coords.altitude,altitudeAccuracy:e.coords.altitudeAccuracy,heading:e.coords.heading,latitude:e.coords.latitude,longitude:e.coords.longitude,speed:e.coords.speed,timestamp:e.timestamp,error:null})},[]),d=x(e=>{s(a=>({...a,loading:!1,error:e}))},[]),u=x(()=>{if(!navigator.geolocation){s(e=>({...e,loading:!1,error:{code:0,message:"Geolocation not supported",PERMISSION_DENIED:1,POSITION_UNAVAILABLE:2,TIMEOUT:3}}));return}s(e=>({...e,loading:!0})),navigator.geolocation.getCurrentPosition(r,d,o)},[r,d,o]),l=x(e=>{navigator.geolocation&&(n.current!==null&&navigator.geolocation.clearWatch(n.current),n.current=navigator.geolocation.watchPosition(r,d,e||o))},[r,d,o]),p=x(()=>{n.current!==null&&(navigator.geolocation.clearWatch(n.current),n.current=null)},[]),f=x((e,a)=>{if(t.latitude===null||t.longitude===null)return 1/0;let c=6371e3,m=t.latitude*Math.PI/180,h=e*Math.PI/180,y=(e-t.latitude)*Math.PI/180,M=(a-t.longitude)*Math.PI/180,N=Math.sin(y/2)*Math.sin(y/2)+Math.cos(m)*Math.cos(h)*Math.sin(M/2)*Math.sin(M/2),g=2*Math.atan2(Math.sqrt(N),Math.sqrt(1-N));return c*g},[t.latitude,t.longitude]),i=x(e=>f(e.latitude,e.longitude)<=e.radius,[f]);return ee(()=>()=>{n.current!==null&&navigator.geolocation.clearWatch(n.current)},[]),{...t,getLocation:u,watchLocation:l,clearWatch:p,checkGeofence:i,distanceTo:f}}import{useState as oe,useCallback as w,useRef as G,useEffect as F}from"react";function bi(){let[o,t]=oe({stream:null,error:null,permission:"unknown",isRecording:!1,devices:[]}),s=G(null),n=G([]),r=w(async()=>{if(!navigator.mediaDevices?.enumerateDevices)return[];try{let a=await navigator.mediaDevices.enumerateDevices();return t(c=>({...c,devices:a})),a}catch(a){return console.error("Error enumerating devices:",a),[]}},[]);F(()=>(r(),navigator.mediaDevices?.addEventListener("devicechange",r),()=>{navigator.mediaDevices?.removeEventListener("devicechange",r)}),[r]);let d=w(async(a={video:!0,audio:!1})=>{try{let c=await navigator.mediaDevices.getUserMedia(a);return t(m=>({...m,stream:c,error:null,permission:"granted"})),c}catch(c){let m=c;t(h=>({...h,error:m,permission:"denied"})),console.error("Error starting camera:",c)}},[]),u=w(()=>{o.stream&&(o.stream.getTracks().forEach(a=>a.stop()),t(a=>({...a,stream:null,isRecording:!1})))},[o.stream]),l=w(async(a={video:!0})=>{try{let c=await navigator.mediaDevices.getDisplayMedia(a);return t(m=>({...m,stream:c,error:null,permission:"granted"})),c}catch(c){let m=c;t(h=>({...h,error:m,permission:"denied"})),console.error("Error starting screen share:",c)}},[]),p=w(async()=>{if(!o.stream||!o.stream.getVideoTracks()[0])return;let c=document.createElement("video");c.srcObject=o.stream,await c.play();let m=document.createElement("canvas");m.width=c.videoWidth,m.height=c.videoHeight;let h=m.getContext("2d");if(!h)return;h.drawImage(c,0,0);let y=m.toDataURL("image/png");return c.pause(),c.srcObject=null,y},[o.stream]),f=w(()=>{if(o.stream)try{let a=new MediaRecorder(o.stream);s.current=a,n.current=[],a.ondataavailable=c=>{c.data.size>0&&n.current.push(c.data)},a.start(),t(c=>({...c,isRecording:!0}))}catch(a){console.error("Error starting recording:",a),t(c=>({...c,error:a}))}},[o.stream]),i=w(async()=>{if(!(!s.current||s.current.state==="inactive"))return new Promise(a=>{s.current&&(s.current.onstop=()=>{let c=new Blob(n.current,{type:"video/webm"});t(m=>({...m,isRecording:!1})),a(c)},s.current.stop())})},[]),e=w(async(a,c)=>{u();let m={[c]:{deviceId:{exact:a}}};await d(m)},[d,u]);return F(()=>()=>{u()},[]),{...o,startCamera:d,stopCamera:u,takePhoto:p,startRecording:f,stopRecording:i,startScreenShare:l,switchDevice:e,getDevices:r}}import{useState as T,useCallback as k,useEffect as K}from"react";function Si(){let[o,t]=T(!1),[s,n]=T(!1),[r,d]=T([]),u="http://localhost:5378",l=k(async()=>{try{let a=await(await fetch(`${u}/api/config`)).json();t(a.ollama?.available??!1)}catch{t(!1)}},[]),p=k(async()=>{n(!0);try{let e=await fetch(`${u}/api/ollama/api/tags`);if(e.ok){let a=await e.json();d(a.models||[])}}catch(e){console.error("Failed to fetch Ollama models:",e)}finally{n(!1)}},[]);K(()=>{l();let e=setInterval(l,3e4);return()=>clearInterval(e)},[l]),K(()=>{o&&p()},[o,p]);let f=k(async(e,a,c={})=>{let m=await fetch(`${u}/api/ollama/api/generate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:e,prompt:a,stream:!1,...c})});if(!m.ok)throw new Error(`Ollama generation failed: ${m.statusText}`);return await m.json()},[]),i=k(async(e,a,c={})=>{let m=await fetch(`${u}/api/ollama/api/chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({model:e,messages:a,stream:!1,...c})});if(!m.ok)throw new Error(`Ollama chat failed: ${m.statusText}`);return await m.json()},[]);return{available:o,loading:s,models:r,refreshModels:p,generate:f,chat:i}}export{ne as Accordion,ie as AccordionContent,re as AccordionItem,ae as AccordionTrigger,Se as Alert,Pe as AlertAction,Ee as AlertDescription,le as AlertDialog,we as AlertDialogAction,ye as AlertDialogCancel,pe as AlertDialogContent,be as AlertDialogDescription,ge as AlertDialogFooter,fe as AlertDialogHeader,he as AlertDialogMedia,me as AlertDialogOverlay,ue as AlertDialogPortal,ve as AlertDialogTitle,de as AlertDialogTrigger,xe as AlertTitle,Ce as AspectRatio,ke as Avatar,Ie as AvatarBadge,Ne as AvatarFallback,Ae as AvatarGroup,Re as AvatarGroupCount,Me as AvatarImage,Oe as Badge,Le as Breadcrumb,Ve as BreadcrumbEllipsis,De as BreadcrumbItem,He as BreadcrumbLink,Be as BreadcrumbList,_e as BreadcrumbPage,Ue as BreadcrumbSeparator,ce as Button,Ke as ButtonGroup,ze as ButtonGroupSeparator,qe as ButtonGroupText,$e as Calendar,We as CalendarDayButton,je as Card,Qe as CardAction,Ze as CardContent,Xe as CardDescription,et as CardFooter,Je as CardHeader,Ye as CardTitle,ot as Carousel,nt as CarouselContent,rt as CarouselItem,it as CarouselNext,at as CarouselPrevious,st as ChartContainer,ut as ChartLegend,mt as ChartLegendContent,ct as ChartStyle,lt as ChartTooltip,dt as ChartTooltipContent,pt as Checkbox,ft as Collapsible,ht as CollapsibleContent,gt as CollapsibleTrigger,Ct as Combobox,_t as ComboboxChip,Ht as ComboboxChips,Ut as ComboboxChipsInput,Lt as ComboboxCollection,It as ComboboxContent,Bt as ComboboxEmpty,Tt as ComboboxGroup,Nt as ComboboxInput,Rt as ComboboxItem,Ot as ComboboxLabel,At as ComboboxList,Dt as ComboboxSeparator,Mt as ComboboxTrigger,kt as ComboboxValue,Xt as Command,Qt as CommandDialog,to as CommandEmpty,oo as CommandGroup,Zt as CommandInput,ro as CommandItem,eo as CommandList,no as CommandSeparator,ao as CommandShortcut,io as ContextMenu,vo as ContextMenuCheckboxItem,po as ContextMenuContent,co as ContextMenuGroup,fo as ContextMenuItem,wo as ContextMenuLabel,lo as ContextMenuPortal,mo as ContextMenuRadioGroup,bo as ContextMenuRadioItem,yo as ContextMenuSeparator,So as ContextMenuShortcut,uo as ContextMenuSub,ho as ContextMenuSubContent,go as ContextMenuSubTrigger,so as ContextMenuTrigger,Gt as Dialog,qt as DialogClose,$t as DialogContent,Yt as DialogDescription,jt as DialogFooter,Wt as DialogHeader,zt as DialogOverlay,Kt as DialogPortal,Jt as DialogTitle,Ft as DialogTrigger,xo as Drawer,Co as DrawerClose,Mo as DrawerContent,Ro as DrawerDescription,Io as DrawerFooter,No as DrawerHeader,ko as DrawerOverlay,Po as DrawerPortal,Ao as DrawerTitle,Eo as DrawerTrigger,To as DropdownMenu,_o as DropdownMenuCheckboxItem,Bo as DropdownMenuContent,Do as DropdownMenuGroup,Ho as DropdownMenuItem,Go as DropdownMenuLabel,Oo as DropdownMenuPortal,Uo as DropdownMenuRadioGroup,Vo as DropdownMenuRadioItem,Fo as DropdownMenuSeparator,Ko as DropdownMenuShortcut,qo as DropdownMenuSub,$o as DropdownMenuSubContent,zo as DropdownMenuSubTrigger,Lo as DropdownMenuTrigger,Wo as Empty,Qo as EmptyContent,Xo as EmptyDescription,jo as EmptyHeader,Jo as EmptyMedia,Yo as EmptyTitle,nn as Field,rn as FieldContent,cn as FieldDescription,dn as FieldError,on as FieldGroup,an as FieldLabel,tn as FieldLegend,ln as FieldSeparator,en as FieldSet,sn as FieldTitle,un as HoverCard,pn as HoverCardContent,mn as HoverCardTrigger,vt as Input,wt as InputGroup,yt as InputGroupAddon,St as InputGroupButton,Et as InputGroupInput,xt as InputGroupText,Pt as InputGroupTextarea,fn as InputOTP,gn as InputOTPGroup,vn as InputOTPSeparator,hn as InputOTPSlot,yn as Item,Cn as ItemActions,xn as ItemContent,Pn as ItemDescription,Mn as ItemFooter,bn as ItemGroup,kn as ItemHeader,Sn as ItemMedia,wn as ItemSeparator,En as ItemTitle,Nn as Kbd,In as KbdGroup,ei as KeyboardAvoidingView,Zo as Label,An as Menubar,_n as MenubarCheckboxItem,Dn as MenubarContent,Tn as MenubarGroup,Hn as MenubarItem,Vn as MenubarLabel,Rn as MenubarMenu,On as MenubarPortal,Ln as MenubarRadioGroup,Un as MenubarRadioItem,Gn as MenubarSeparator,Fn as MenubarShortcut,Kn as MenubarSub,zn as MenubarSubContent,qn as MenubarSubTrigger,Bn as MenubarTrigger,$n as NavigationMenu,Xn as NavigationMenuContent,er as NavigationMenuIndicator,jn as NavigationMenuItem,Zn as NavigationMenuLink,Wn as NavigationMenuList,Yn as NavigationMenuTrigger,Qn as NavigationMenuViewport,tr as Pagination,or as PaginationContent,sr as PaginationEllipsis,nr as PaginationItem,rr as PaginationLink,ir as PaginationNext,ar as PaginationPrevious,oi as PlatformHeader,cr as Popover,ur as PopoverAnchor,dr as PopoverContent,fr as PopoverDescription,mr as PopoverHeader,pr as PopoverTitle,lr as PopoverTrigger,gr as Progress,hr as RadioGroup,vr as RadioGroupItem,yr as ResizableHandle,wr as ResizablePanel,br as ResizablePanelGroup,qa as SafeAreaProvider,ti as SafeAreaView,Sr as ScrollArea,xr as ScrollBar,Er as Select,Mr as SelectContent,Pr as SelectGroup,Ir as SelectItem,Nr as SelectLabel,Tr as SelectScrollDownButton,Rr as SelectScrollUpButton,Ar as SelectSeparator,kr as SelectTrigger,Cr as SelectValue,Ge as Separator,Or as Sheet,Br as SheetClose,Dr as SheetContent,Vr as SheetDescription,_r as SheetFooter,Hr as SheetHeader,Ur as SheetTitle,Lr as SheetTrigger,Jr as Sidebar,na as SidebarContent,ta as SidebarFooter,ra as SidebarGroup,ia as SidebarGroupAction,sa as SidebarGroupContent,aa as SidebarGroupLabel,ea as SidebarHeader,Zr as SidebarInput,Qr as SidebarInset,ca as SidebarMenu,ua as SidebarMenuAction,ma as SidebarMenuBadge,da as SidebarMenuButton,la as SidebarMenuItem,pa as SidebarMenuSkeleton,fa as SidebarMenuSub,ha as SidebarMenuSubButton,ga as SidebarMenuSubItem,jr as SidebarProvider,Xr as SidebarRail,oa as SidebarSeparator,Yr as SidebarTrigger,Gr as Skeleton,va as Slider,wa as Spinner,ya as Switch,Sa as Table,Ea as TableBody,Na as TableCaption,Ma as TableCell,Pa as TableFooter,ka as TableHead,xa as TableHeader,Ca as TableRow,Ia as Tabs,Oa as TabsContent,Ra as TabsList,Ta as TabsTrigger,bt as Textarea,ba as Toaster,Ba as Toggle,Da as ToggleGroup,Ha as ToggleGroupItem,Kr as Tooltip,zr as TooltipContent,Fr as TooltipProvider,qr as TooltipTrigger,Te as badgeVariants,Fe as buttonGroupVariants,se as buttonVariants,O as cn,Jn as navigationMenuTriggerStyle,Aa as tabsListVariants,La as toggleVariants,ja as useAppState,Wa as useBackHandler,bi as useCamera,tt as useCarousel,mi as useClipboard,za as useColorScheme,Vt as useComboboxAnchor,ii as useFocusTimer,gi as useGeolocation,$r as useIsMobile,L as useKeyboard,li as useNotifications,Si as useOllama,Ua as useRelayClose,A as useSafeArea,Wr as useSidebar,$a as useStatusBar};
10
10
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/use-keyboard.ts","../src/hooks/use-platform.tsx","../src/components/platform.tsx","../src/hooks/use-focus-timer.ts"],"sourcesContent":["import { useState, useEffect, useCallback } from 'react';\n\ninterface KeyboardState {\n isOpen: boolean;\n height: number;\n}\n\n/**\n * useKeyboard - Track virtual keyboard state on mobile devices\n * \n * Uses visualViewport API to detect keyboard height and provides\n * a safe area offset for positioning UI elements above the keyboard.\n * \n * @returns {KeyboardState} - { isOpen: boolean, height: number }\n */\nexport function useKeyboard(): KeyboardState {\n const [state, setState] = useState<KeyboardState>({\n isOpen: false,\n height: 0\n });\n\n useEffect(() => {\n // Check if we're on a mobile device\n const isMobile = /iPad|iPhone|iPod|Android/.test(navigator.userAgent);\n if (!isMobile) return;\n\n const updateKeyboardState = () => {\n if (!window.visualViewport) return;\n\n const viewport = window.visualViewport;\n // Calculate keyboard height: difference between window and viewport height\n const keyboardHeight = window.innerHeight - viewport.height;\n // Consider keyboard open if height is more than 100px (threshold for accessory bars)\n const isOpen = keyboardHeight > 100;\n\n setState({\n isOpen,\n height: isOpen ? keyboardHeight : 0\n });\n };\n\n // Listen to visualViewport changes\n if (window.visualViewport) {\n window.visualViewport.addEventListener('resize', updateKeyboardState);\n window.visualViewport.addEventListener('scroll', updateKeyboardState);\n }\n\n // Also listen for focus/blur on inputs as fallback\n const handleFocusIn = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {\n // Small delay to let keyboard animate\n setTimeout(updateKeyboardState, 100);\n }\n };\n\n const handleFocusOut = () => {\n // Small delay to let keyboard animate closed\n setTimeout(() => {\n setState({ isOpen: false, height: 0 });\n }, 100);\n };\n\n document.addEventListener('focusin', handleFocusIn);\n document.addEventListener('focusout', handleFocusOut);\n\n // Initial check\n updateKeyboardState();\n\n return () => {\n window.visualViewport?.removeEventListener('resize', updateKeyboardState);\n window.visualViewport?.removeEventListener('scroll', updateKeyboardState);\n document.removeEventListener('focusin', handleFocusIn);\n document.removeEventListener('focusout', handleFocusOut);\n };\n }, []);\n\n return state;\n}\n\n/**\n * useRelayClose - Get the close function for closing the current app\n * \n * Works with both iframe and blob URL contexts.\n * \n * @returns {() => void} - Function to close the current app\n */\nexport function useRelayClose(): () => void {\n const close = useCallback(() => {\n // Try multiple methods to close\n // 1. PostMessage to parent (works in iframe)\n if (window.parent !== window) {\n window.parent.postMessage('relay:close', '*');\n }\n // 2. PostMessage with opener (works if opened as popup)\n if (window.opener) {\n window.opener.postMessage('relay:close', '*');\n }\n // 3. Try self-closing (may not work in all contexts)\n window.close();\n }, []);\n\n return close;\n}\n","import { useState, useEffect, useCallback, createContext, useContext, ReactNode } from 'react';\n\n// ============================================\n// SAFE AREA TYPES & CONTEXT\n// ============================================\n\nexport interface SafeAreaInsets {\n top: number;\n bottom: number;\n left: number;\n right: number;\n}\n\nconst defaultInsets: SafeAreaInsets = {\n top: 0,\n bottom: 0,\n left: 0,\n right: 0\n};\n\nconst SafeAreaContext = createContext<SafeAreaInsets>(defaultInsets);\n\n// ============================================\n// SAFE AREA PROVIDER\n// ============================================\n\ninterface SafeAreaProviderProps {\n children: ReactNode;\n}\n\n/**\n * SafeAreaProvider - Provides safe area insets to child components\n * \n * Automatically detects safe areas from:\n * 1. CSS env() values (iOS Safari)\n * 2. Parent Shell postMessage (when running in Relay Shell)\n */\nexport function SafeAreaProvider({ children }: SafeAreaProviderProps) {\n const [insets, setInsets] = useState<SafeAreaInsets>(defaultInsets);\n\n useEffect(() => {\n // Method 1: Read from CSS env() values\n const computeInsets = () => {\n const style = getComputedStyle(document.documentElement);\n const getEnv = (name: string) => {\n const value = style.getPropertyValue(`--safe-area-inset-${name}`);\n return parseInt(value) || 0;\n };\n\n // Try to get from CSS custom properties first\n let top = getEnv('top');\n let bottom = getEnv('bottom');\n let left = getEnv('left');\n let right = getEnv('right');\n\n // If CSS props not set, try to read from env() directly\n if (top === 0 && bottom === 0) {\n // Create a temp element to measure env() values\n const temp = document.createElement('div');\n temp.style.cssText = `\n position: fixed;\n top: env(safe-area-inset-top, 0px);\n bottom: env(safe-area-inset-bottom, 0px);\n left: env(safe-area-inset-left, 0px);\n right: env(safe-area-inset-right, 0px);\n pointer-events: none;\n visibility: hidden;\n `;\n document.body.appendChild(temp);\n const rect = temp.getBoundingClientRect();\n top = rect.top;\n bottom = window.innerHeight - rect.bottom;\n left = rect.left;\n right = window.innerWidth - rect.right;\n document.body.removeChild(temp);\n }\n\n setInsets({ top, bottom, left, right });\n };\n\n // Method 2: Listen for Shell messages\n const handleMessage = (event: MessageEvent) => {\n if (event.data?.type === 'relay:safearea') {\n setInsets(event.data.insets);\n }\n };\n\n computeInsets();\n window.addEventListener('message', handleMessage);\n window.addEventListener('resize', computeInsets);\n\n return () => {\n window.removeEventListener('message', handleMessage);\n window.removeEventListener('resize', computeInsets);\n };\n }, []);\n\n return (\n <SafeAreaContext.Provider value={insets}>\n {children}\n </SafeAreaContext.Provider>\n );\n}\n\n// ============================================\n// SAFE AREA HOOKS\n// ============================================\n\n/**\n * useSafeArea - Get safe area insets\n * \n * @returns SafeAreaInsets with top, bottom, left, right values in pixels\n */\nexport function useSafeArea(): SafeAreaInsets {\n return useContext(SafeAreaContext);\n}\n\n// ============================================\n// COLOR SCHEME\n// ============================================\n\nexport type ColorScheme = 'light' | 'dark';\n\n/**\n * useColorScheme - Get current system color scheme\n * \n * @returns 'light' | 'dark'\n */\nexport function useColorScheme(): ColorScheme {\n const [scheme, setScheme] = useState<ColorScheme>(() =>\n window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n );\n\n useEffect(() => {\n const media = window.matchMedia('(prefers-color-scheme: dark)');\n const handler = (e: MediaQueryListEvent) => {\n setScheme(e.matches ? 'dark' : 'light');\n };\n media.addEventListener('change', handler);\n return () => media.removeEventListener('change', handler);\n }, []);\n\n return scheme;\n}\n\n// ============================================\n// STATUS BAR\n// ============================================\n\n/**\n * useStatusBar - Control the status bar appearance\n * \n * @param color - Hex color for the status bar\n */\nexport function useStatusBar(color: string): void {\n useEffect(() => {\n // Update meta theme-color\n let meta = document.querySelector('meta[name=\"theme-color\"]');\n if (!meta) {\n meta = document.createElement('meta');\n meta.setAttribute('name', 'theme-color');\n document.head.appendChild(meta);\n }\n meta.setAttribute('content', color);\n\n // Notify parent Shell\n window.parent?.postMessage({ type: 'relay:statusbar', color }, '*');\n }, [color]);\n}\n\n// ============================================\n// BACK HANDLER\n// ============================================\n\n/**\n * useBackHandler - Handle back navigation\n * \n * @param handler - Return true to prevent default back behavior\n */\nexport function useBackHandler(handler: () => boolean): void {\n useEffect(() => {\n const handleMessage = (event: MessageEvent) => {\n if (event.data === 'relay:back') {\n const handled = handler();\n if (!handled) {\n // Allow default close behavior\n window.parent?.postMessage('relay:close', '*');\n }\n }\n };\n\n // Also handle browser back button\n const handlePopState = (event: PopStateEvent) => {\n const handled = handler();\n if (handled) {\n // Push a dummy state to prevent navigation\n window.history.pushState(null, '', window.location.href);\n }\n };\n\n // Push initial state for popstate handling\n window.history.pushState(null, '', window.location.href);\n\n window.addEventListener('message', handleMessage);\n window.addEventListener('popstate', handlePopState);\n\n return () => {\n window.removeEventListener('message', handleMessage);\n window.removeEventListener('popstate', handlePopState);\n };\n }, [handler]);\n}\n\n// ============================================\n// APP STATE\n// ============================================\n\nexport type AppState = 'active' | 'background' | 'inactive';\n\n/**\n * useAppState - Track app foreground/background state\n * \n * @returns Current app state\n */\nexport function useAppState(): AppState {\n const [state, setState] = useState<AppState>('active');\n\n useEffect(() => {\n const handleVisibility = () => {\n setState(document.hidden ? 'background' : 'active');\n };\n\n const handleMessage = (event: MessageEvent) => {\n if (event.data === 'relay:foreground') setState('active');\n if (event.data === 'relay:background') setState('background');\n };\n\n document.addEventListener('visibilitychange', handleVisibility);\n window.addEventListener('message', handleMessage);\n\n return () => {\n document.removeEventListener('visibilitychange', handleVisibility);\n window.removeEventListener('message', handleMessage);\n };\n }, []);\n\n return state;\n}\n","import React, { ReactNode, CSSProperties } from 'react';\nimport { useKeyboard } from '../hooks/use-keyboard';\nimport { useSafeArea } from '../hooks/use-platform';\n\n// ============================================\n// KEYBOARD AVOIDING VIEW\n// ============================================\n\ntype KeyboardBehavior = 'padding' | 'height' | 'position';\n\ninterface KeyboardAvoidingViewProps {\n children: ReactNode;\n behavior?: KeyboardBehavior;\n keyboardVerticalOffset?: number;\n style?: CSSProperties;\n className?: string;\n}\n\n/**\n * KeyboardAvoidingView - Automatically adjusts content when keyboard opens\n * \n * @param behavior - How to adjust: 'padding' | 'height' | 'position'\n * @param keyboardVerticalOffset - Additional offset to add\n */\nexport function KeyboardAvoidingView({\n children,\n behavior = 'padding',\n keyboardVerticalOffset = 0,\n style,\n className\n}: KeyboardAvoidingViewProps) {\n const keyboard = useKeyboard();\n const offset = keyboard.height + keyboardVerticalOffset;\n\n const computedStyle: CSSProperties = {\n ...style,\n transition: 'all 0.2s ease-out',\n };\n\n if (keyboard.isOpen) {\n switch (behavior) {\n case 'padding':\n computedStyle.paddingBottom = offset;\n break;\n case 'height':\n computedStyle.height = `calc(100% - ${offset}px)`;\n break;\n case 'position':\n computedStyle.transform = `translateY(-${offset}px)`;\n break;\n }\n }\n\n return (\n <div style={computedStyle} className={className}>\n {children}\n </div>\n );\n}\n\n// ============================================\n// SAFE AREA VIEW\n// ============================================\n\ntype SafeAreaEdge = 'top' | 'bottom' | 'left' | 'right';\n\ninterface SafeAreaViewProps {\n children: ReactNode;\n edges?: SafeAreaEdge[];\n style?: CSSProperties;\n className?: string;\n}\n\n/**\n * SafeAreaView - Container that respects safe area insets\n * \n * @param edges - Which edges to apply safe area to (default: all)\n */\nexport function SafeAreaView({\n children,\n edges = ['top', 'bottom', 'left', 'right'],\n style,\n className\n}: SafeAreaViewProps) {\n const insets = useSafeArea();\n\n const computedStyle: CSSProperties = {\n ...style,\n paddingTop: edges.includes('top') ? insets.top : style?.paddingTop,\n paddingBottom: edges.includes('bottom') ? insets.bottom : style?.paddingBottom,\n paddingLeft: edges.includes('left') ? insets.left : style?.paddingLeft,\n paddingRight: edges.includes('right') ? insets.right : style?.paddingRight,\n };\n\n return (\n <div style={computedStyle} className={className}>\n {children}\n </div>\n );\n}\n","import { useState, useEffect } from 'react';\n\nexport type TimerMode = 'focus' | 'shortBreak' | 'longBreak';\nexport type StoryState = 'egg' | 'cracked' | 'chick' | 'alien' | 'ghost';\n\nexport function useFocusTimer() {\n const [seconds, setSeconds] = useState(25 * 60);\n const [initialTime, setInitialTime] = useState(25 * 60);\n const [isActive, setIsActive] = useState(false);\n const [mode, setMode] = useState<TimerMode>('focus');\n const [task, setTask] = useState('');\n const [storyState, setStoryState] = useState<StoryState>('egg');\n const [soundEnabled, setSoundEnabled] = useState(false);\n\n useEffect(() => {\n let interval: any = null;\n\n if (isActive && seconds > 0) {\n interval = setInterval(() => {\n setSeconds((s) => s - 1);\n }, 1000);\n } else if (seconds === 0 && isActive) {\n // Timer Finished\n setIsActive(false);\n if (mode === 'focus') {\n const isAbducted = Math.random() < 0.01;\n setStoryState(isAbducted ? 'alien' : 'chick');\n }\n }\n\n return () => {\n if (interval) clearInterval(interval);\n };\n }, [isActive, seconds, mode]);\n\n // Update Visuals based on progress\n useEffect(() => {\n if (!isActive && (storyState === 'chick' || storyState === 'alien' || storyState === 'ghost')) return;\n\n // Don't change chick state during break\n if (mode !== 'focus') return;\n\n const progress = 1 - (seconds / initialTime);\n\n if (progress < 0.5) {\n setStoryState('egg');\n } else if (progress >= 0.5 && progress < 1) {\n setStoryState('cracked');\n }\n }, [seconds, initialTime, isActive, storyState, mode]);\n\n const toggleTimer = () => setIsActive(!isActive);\n\n const giveUp = () => {\n setIsActive(false);\n setStoryState('ghost');\n };\n\n const switchMode = (newMode: TimerMode) => {\n setIsActive(false);\n setMode(newMode);\n setStoryState('egg'); // Reset chick for new session\n\n // Set Times\n let newTime = 25 * 60;\n if (newMode === 'shortBreak') newTime = 5 * 60;\n if (newMode === 'longBreak') newTime = 15 * 60;\n\n setSeconds(newTime);\n setInitialTime(newTime);\n };\n\n const resetTimer = () => {\n setIsActive(false);\n switchMode(mode); // Re-init current mode\n };\n\n const setCustomTime = (mins: number) => {\n setIsActive(false);\n setSeconds(mins * 60);\n setInitialTime(mins * 60);\n setStoryState('egg');\n };\n\n return {\n seconds,\n initialTime,\n isActive,\n mode,\n task,\n storyState,\n soundEnabled,\n setSeconds,\n setInitialTime,\n setIsActive,\n setMode,\n setTask,\n setStoryState,\n setSoundEnabled,\n toggleTimer,\n giveUp,\n switchMode,\n resetTimer,\n setCustomTime,\n };\n}\n"],"mappings":"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","names":["useState","useEffect","useCallback","useKeyboard","state","setState","updateKeyboardState","viewport","keyboardHeight","isOpen","handleFocusIn","e","target","handleFocusOut","useRelayClose","useState","useEffect","createContext","useContext","jsx","defaultInsets","SafeAreaContext","SafeAreaProvider","children","insets","setInsets","computeInsets","style","getEnv","name","value","top","bottom","left","right","temp","rect","handleMessage","event","useSafeArea","useColorScheme","scheme","setScheme","media","handler","e","useStatusBar","color","meta","useBackHandler","handlePopState","useAppState","state","setState","handleVisibility","jsx","KeyboardAvoidingView","children","behavior","keyboardVerticalOffset","style","className","keyboard","useKeyboard","offset","computedStyle","SafeAreaView","edges","insets","useSafeArea","useState","useEffect","useFocusTimer","seconds","setSeconds","initialTime","setInitialTime","isActive","setIsActive","mode","setMode","task","setTask","storyState","setStoryState","soundEnabled","setSoundEnabled","interval","s","isAbducted","progress","toggleTimer","giveUp","switchMode","newMode","newTime","mins"]}
1
+ {"version":3,"sources":["../src/hooks/use-keyboard.ts","../src/hooks/use-platform.tsx","../src/components/platform.tsx","../src/hooks/use-focus-timer.ts","../src/hooks/use-notifications.ts","../src/hooks/use-clipboard.ts","../src/hooks/use-geolocation.ts","../src/hooks/use-camera.ts","../src/hooks/use-ollama.ts"],"sourcesContent":["import { useState, useEffect, useCallback } from 'react';\n\ninterface KeyboardState {\n isOpen: boolean;\n height: number;\n}\n\n/**\n * useKeyboard - Track virtual keyboard state on mobile devices\n * \n * Uses visualViewport API to detect keyboard height and provides\n * a safe area offset for positioning UI elements above the keyboard.\n * \n * @returns {KeyboardState} - { isOpen: boolean, height: number }\n */\nexport function useKeyboard(): KeyboardState {\n const [state, setState] = useState<KeyboardState>({\n isOpen: false,\n height: 0\n });\n\n useEffect(() => {\n // Check if we're on a mobile device\n const isMobile = /iPad|iPhone|iPod|Android/.test(navigator.userAgent);\n if (!isMobile) return;\n\n const updateKeyboardState = () => {\n if (!window.visualViewport) return;\n\n const viewport = window.visualViewport;\n // Calculate keyboard height: difference between window and viewport height\n const keyboardHeight = window.innerHeight - viewport.height;\n // Consider keyboard open if height is more than 100px (threshold for accessory bars)\n const isOpen = keyboardHeight > 100;\n\n setState({\n isOpen,\n height: isOpen ? keyboardHeight : 0\n });\n };\n\n // Listen to visualViewport changes\n if (window.visualViewport) {\n window.visualViewport.addEventListener('resize', updateKeyboardState);\n window.visualViewport.addEventListener('scroll', updateKeyboardState);\n }\n\n // Also listen for focus/blur on inputs as fallback\n const handleFocusIn = (e: FocusEvent) => {\n const target = e.target as HTMLElement;\n if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA') {\n // Small delay to let keyboard animate\n setTimeout(updateKeyboardState, 100);\n }\n };\n\n const handleFocusOut = () => {\n // Small delay to let keyboard animate closed\n setTimeout(() => {\n setState({ isOpen: false, height: 0 });\n }, 100);\n };\n\n document.addEventListener('focusin', handleFocusIn);\n document.addEventListener('focusout', handleFocusOut);\n\n // Initial check\n updateKeyboardState();\n\n return () => {\n window.visualViewport?.removeEventListener('resize', updateKeyboardState);\n window.visualViewport?.removeEventListener('scroll', updateKeyboardState);\n document.removeEventListener('focusin', handleFocusIn);\n document.removeEventListener('focusout', handleFocusOut);\n };\n }, []);\n\n return state;\n}\n\n/**\n * useRelayClose - Get the close function for closing the current app\n * \n * Works with both iframe and blob URL contexts.\n * \n * @returns {() => void} - Function to close the current app\n */\nexport function useRelayClose(): () => void {\n const close = useCallback(() => {\n // Try multiple methods to close\n // 1. PostMessage to parent (works in iframe)\n if (window.parent !== window) {\n window.parent.postMessage('relay:close', '*');\n }\n // 2. PostMessage with opener (works if opened as popup)\n if (window.opener) {\n window.opener.postMessage('relay:close', '*');\n }\n // 3. Try self-closing (may not work in all contexts)\n window.close();\n }, []);\n\n return close;\n}\n","import { useState, useEffect, useCallback, createContext, useContext, ReactNode } from 'react';\n\n// ============================================\n// SAFE AREA TYPES & CONTEXT\n// ============================================\n\nexport interface SafeAreaInsets {\n top: number;\n bottom: number;\n left: number;\n right: number;\n}\n\nconst defaultInsets: SafeAreaInsets = {\n top: 0,\n bottom: 0,\n left: 0,\n right: 0\n};\n\nconst SafeAreaContext = createContext<SafeAreaInsets>(defaultInsets);\n\n// ============================================\n// SAFE AREA PROVIDER\n// ============================================\n\ninterface SafeAreaProviderProps {\n children: ReactNode;\n}\n\n/**\n * SafeAreaProvider - Provides safe area insets to child components\n * \n * Automatically detects safe areas from:\n * 1. CSS env() values (iOS Safari)\n * 2. Parent Shell postMessage (when running in Relay Shell)\n */\nexport function SafeAreaProvider({ children }: SafeAreaProviderProps) {\n const [insets, setInsets] = useState<SafeAreaInsets>(defaultInsets);\n\n useEffect(() => {\n // Method 1: Read from CSS env() values\n const computeInsets = () => {\n const style = getComputedStyle(document.documentElement);\n const getEnv = (name: string) => {\n const value = style.getPropertyValue(`--safe-area-inset-${name}`);\n return parseInt(value) || 0;\n };\n\n // Try to get from CSS custom properties first\n let top = getEnv('top');\n let bottom = getEnv('bottom');\n let left = getEnv('left');\n let right = getEnv('right');\n\n // If CSS props not set, try to read from env() directly\n if (top === 0 && bottom === 0) {\n // Create a temp element to measure env() values\n const temp = document.createElement('div');\n temp.style.cssText = `\n position: fixed;\n top: env(safe-area-inset-top, 0px);\n bottom: env(safe-area-inset-bottom, 0px);\n left: env(safe-area-inset-left, 0px);\n right: env(safe-area-inset-right, 0px);\n pointer-events: none;\n visibility: hidden;\n `;\n document.body.appendChild(temp);\n const rect = temp.getBoundingClientRect();\n top = rect.top;\n bottom = window.innerHeight - rect.bottom;\n left = rect.left;\n right = window.innerWidth - rect.right;\n document.body.removeChild(temp);\n }\n\n setInsets({ top, bottom, left, right });\n };\n\n // Method 2: Listen for Shell messages\n const handleMessage = (event: MessageEvent) => {\n if (event.data?.type === 'relay:safearea') {\n setInsets(event.data.insets);\n }\n };\n\n computeInsets();\n window.addEventListener('message', handleMessage);\n window.addEventListener('resize', computeInsets);\n\n return () => {\n window.removeEventListener('message', handleMessage);\n window.removeEventListener('resize', computeInsets);\n };\n }, []);\n\n return (\n <SafeAreaContext.Provider value={insets}>\n {children}\n </SafeAreaContext.Provider>\n );\n}\n\n// ============================================\n// SAFE AREA HOOKS\n// ============================================\n\n/**\n * useSafeArea - Get safe area insets\n * \n * @returns SafeAreaInsets with top, bottom, left, right values in pixels\n */\nexport function useSafeArea(): SafeAreaInsets {\n return useContext(SafeAreaContext);\n}\n\n// ============================================\n// COLOR SCHEME\n// ============================================\n\nexport type ColorScheme = 'light' | 'dark';\n\n/**\n * useColorScheme - Get current system color scheme\n * \n * @returns 'light' | 'dark'\n */\nexport function useColorScheme(): ColorScheme {\n const [scheme, setScheme] = useState<ColorScheme>(() =>\n window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n );\n\n useEffect(() => {\n const media = window.matchMedia('(prefers-color-scheme: dark)');\n const handler = (e: MediaQueryListEvent) => {\n setScheme(e.matches ? 'dark' : 'light');\n };\n media.addEventListener('change', handler);\n return () => media.removeEventListener('change', handler);\n }, []);\n\n return scheme;\n}\n\n// ============================================\n// STATUS BAR\n// ============================================\n\n/**\n * useStatusBar - Control the status bar appearance\n * \n * @param color - Hex color for the status bar\n */\nexport function useStatusBar(color: string): void {\n useEffect(() => {\n // Update meta theme-color\n let meta = document.querySelector('meta[name=\"theme-color\"]');\n if (!meta) {\n meta = document.createElement('meta');\n meta.setAttribute('name', 'theme-color');\n document.head.appendChild(meta);\n }\n meta.setAttribute('content', color);\n\n // Notify parent Shell\n window.parent?.postMessage({ type: 'relay:statusbar', color }, '*');\n }, [color]);\n}\n\n// ============================================\n// BACK HANDLER\n// ============================================\n\n/**\n * useBackHandler - Handle back navigation\n * \n * @param handler - Return true to prevent default back behavior\n */\nexport function useBackHandler(handler: () => boolean): void {\n useEffect(() => {\n const handleMessage = (event: MessageEvent) => {\n if (event.data === 'relay:back') {\n const handled = handler();\n if (!handled) {\n // Allow default close behavior\n window.parent?.postMessage('relay:close', '*');\n }\n }\n };\n\n // Also handle browser back button\n const handlePopState = (event: PopStateEvent) => {\n const handled = handler();\n if (handled) {\n // Push a dummy state to prevent navigation\n window.history.pushState(null, '', window.location.href);\n }\n };\n\n // Push initial state for popstate handling\n window.history.pushState(null, '', window.location.href);\n\n window.addEventListener('message', handleMessage);\n window.addEventListener('popstate', handlePopState);\n\n return () => {\n window.removeEventListener('message', handleMessage);\n window.removeEventListener('popstate', handlePopState);\n };\n }, [handler]);\n}\n\n// ============================================\n// APP STATE\n// ============================================\n\nexport type AppState = 'active' | 'background' | 'inactive';\n\n/**\n * useAppState - Track app foreground/background state\n * \n * @returns Current app state\n */\nexport function useAppState(): AppState {\n const [state, setState] = useState<AppState>('active');\n\n useEffect(() => {\n const handleVisibility = () => {\n setState(document.hidden ? 'background' : 'active');\n };\n\n const handleMessage = (event: MessageEvent) => {\n if (event.data === 'relay:foreground') setState('active');\n if (event.data === 'relay:background') setState('background');\n };\n\n document.addEventListener('visibilitychange', handleVisibility);\n window.addEventListener('message', handleMessage);\n\n return () => {\n document.removeEventListener('visibilitychange', handleVisibility);\n window.removeEventListener('message', handleMessage);\n };\n }, []);\n\n return state;\n}\n","import React, { ReactNode, CSSProperties } from 'react';\nimport { useKeyboard } from '../hooks/use-keyboard';\nimport { useSafeArea } from '../hooks/use-platform';\nimport { cn } from '../lib/utils';\n\n// ============================================\n// KEYBOARD AVOIDING VIEW\n// ============================================\n\ntype KeyboardBehavior = 'padding' | 'height' | 'position';\n\ninterface KeyboardAvoidingViewProps {\n children: ReactNode;\n behavior?: KeyboardBehavior;\n keyboardVerticalOffset?: number;\n style?: CSSProperties;\n className?: string;\n}\n\n/**\n * KeyboardAvoidingView - Automatically adjusts content when keyboard opens\n * \n * @param behavior - How to adjust: 'padding' | 'height' | 'position'\n * @param keyboardVerticalOffset - Additional offset to add\n */\nexport function KeyboardAvoidingView({\n children,\n behavior = 'padding',\n keyboardVerticalOffset = 0,\n style,\n className\n}: KeyboardAvoidingViewProps) {\n const keyboard = useKeyboard();\n const offset = keyboard.height + keyboardVerticalOffset;\n\n const computedStyle: CSSProperties = {\n ...style,\n transition: 'all 0.2s ease-out',\n };\n\n if (keyboard.isOpen) {\n switch (behavior) {\n case 'padding':\n computedStyle.paddingBottom = offset;\n break;\n case 'height':\n computedStyle.height = `calc(100% - ${offset}px)`;\n break;\n case 'position':\n computedStyle.transform = `translateY(-${offset}px)`;\n break;\n }\n }\n\n return (\n <div style={computedStyle} className={className}>\n {children}\n </div>\n );\n}\n\n// ============================================\n// SAFE AREA VIEW\n// ============================================\n\ntype SafeAreaEdge = 'top' | 'bottom' | 'left' | 'right';\n\ninterface SafeAreaViewProps {\n children: ReactNode;\n edges?: SafeAreaEdge[];\n style?: CSSProperties;\n className?: string;\n}\n\n/**\n * SafeAreaView - Container that respects safe area insets\n * \n * @param edges - Which edges to apply safe area to (default: all)\n */\nexport function SafeAreaView({\n children,\n edges = ['top', 'bottom', 'left', 'right'],\n style,\n className\n}: SafeAreaViewProps) {\n const insets = useSafeArea();\n\n const computedStyle: CSSProperties = {\n ...style,\n paddingTop: edges.includes('top') ? insets.top : style?.paddingTop,\n paddingBottom: edges.includes('bottom') ? insets.bottom : style?.paddingBottom,\n paddingLeft: edges.includes('left') ? insets.left : style?.paddingLeft,\n paddingRight: edges.includes('right') ? insets.right : style?.paddingRight,\n };\n\n return (\n <div style={computedStyle} className={className}>\n {children}\n </div>\n );\n}\n\n// ============================================\n// PLATFORM HEADER\n// ============================================\n\ninterface PlatformHeaderProps {\n title: string;\n showBack?: boolean;\n onBack?: () => void;\n className?: string;\n rightElement?: ReactNode;\n}\n\nexport function PlatformHeader({\n title,\n showBack = true,\n onBack,\n className,\n rightElement\n}: PlatformHeaderProps) {\n const insets = useSafeArea();\n\n return (\n <div \n className={cn(\"bg-background border-b flex flex-col z-50\", className)}\n style={{ paddingTop: insets.top }}\n >\n <div className=\"h-14 flex items-center justify-between px-4\">\n <div className=\"flex items-center gap-4 flex-1\">\n {showBack && (\n <button \n onClick={onBack || (() => window.history.back())}\n className=\"p-2 -ml-2 hover:bg-muted rounded-full transition-colors\"\n >\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\">\n <path d=\"M19 12H5M12 19l-7-7 7-7\" />\n </svg>\n </button>\n )}\n <h1 className=\"text-lg font-semibold truncate\">{title}</h1>\n </div>\n {rightElement && (\n <div className=\"flex-none\">\n {rightElement}\n </div>\n )}\n </div>\n </div>\n );\n}\n","import { useState, useEffect } from 'react';\n\nexport type TimerMode = 'focus' | 'shortBreak' | 'longBreak';\nexport type StoryState = 'egg' | 'cracked' | 'chick' | 'alien' | 'ghost';\n\nexport function useFocusTimer() {\n const [seconds, setSeconds] = useState(25 * 60);\n const [initialTime, setInitialTime] = useState(25 * 60);\n const [isActive, setIsActive] = useState(false);\n const [mode, setMode] = useState<TimerMode>('focus');\n const [task, setTask] = useState('');\n const [storyState, setStoryState] = useState<StoryState>('egg');\n const [soundEnabled, setSoundEnabled] = useState(false);\n\n useEffect(() => {\n let interval: any = null;\n\n if (isActive && seconds > 0) {\n interval = setInterval(() => {\n setSeconds((s) => s - 1);\n }, 1000);\n } else if (seconds === 0 && isActive) {\n // Timer Finished\n setIsActive(false);\n if (mode === 'focus') {\n const isAbducted = Math.random() < 0.01;\n setStoryState(isAbducted ? 'alien' : 'chick');\n }\n }\n\n return () => {\n if (interval) clearInterval(interval);\n };\n }, [isActive, seconds, mode]);\n\n // Update Visuals based on progress\n useEffect(() => {\n if (!isActive && (storyState === 'chick' || storyState === 'alien' || storyState === 'ghost')) return;\n\n // Don't change chick state during break\n if (mode !== 'focus') return;\n\n const progress = 1 - (seconds / initialTime);\n\n if (progress < 0.5) {\n setStoryState('egg');\n } else if (progress >= 0.5 && progress < 1) {\n setStoryState('cracked');\n }\n }, [seconds, initialTime, isActive, storyState, mode]);\n\n const toggleTimer = () => setIsActive(!isActive);\n\n const giveUp = () => {\n setIsActive(false);\n setStoryState('ghost');\n };\n\n const switchMode = (newMode: TimerMode) => {\n setIsActive(false);\n setMode(newMode);\n setStoryState('egg'); // Reset chick for new session\n\n // Set Times\n let newTime = 25 * 60;\n if (newMode === 'shortBreak') newTime = 5 * 60;\n if (newMode === 'longBreak') newTime = 15 * 60;\n\n setSeconds(newTime);\n setInitialTime(newTime);\n };\n\n const resetTimer = () => {\n setIsActive(false);\n switchMode(mode); // Re-init current mode\n };\n\n const setCustomTime = (mins: number) => {\n setIsActive(false);\n setSeconds(mins * 60);\n setInitialTime(mins * 60);\n setStoryState('egg');\n };\n\n return {\n seconds,\n initialTime,\n isActive,\n mode,\n task,\n storyState,\n soundEnabled,\n setSeconds,\n setInitialTime,\n setIsActive,\n setMode,\n setTask,\n setStoryState,\n setSoundEnabled,\n toggleTimer,\n giveUp,\n switchMode,\n resetTimer,\n setCustomTime,\n };\n}\n","import { useState, useCallback, useEffect } from 'react';\n\nexport interface NotificationOptions {\n body?: string;\n icon?: string;\n image?: string;\n badge?: string;\n tag?: string;\n data?: any;\n vibrate?: number[];\n timestamp?: number;\n renotify?: boolean;\n silent?: boolean;\n requireInteraction?: boolean;\n actions?: NotificationAction[];\n}\n\nexport interface NotificationAction {\n action: string;\n title: string;\n icon?: string;\n}\n\nexport interface UseNotificationsReturn {\n permission: NotificationPermission;\n requestPermission: () => Promise<NotificationPermission>;\n sendNotification: (title: string, options?: NotificationOptions) => void;\n scheduleNotification: (title: string, delayMs: number, options?: NotificationOptions) => void;\n clearBadge: () => Promise<void>;\n setBadge: (count: number) => Promise<void>;\n}\n\nexport function useNotifications(): UseNotificationsReturn {\n const [permission, setPermission] = useState<NotificationPermission>('default');\n\n useEffect(() => {\n if ('Notification' in window) {\n setPermission(Notification.permission);\n }\n }, []);\n\n const requestPermission = useCallback(async () => {\n if (!('Notification' in window)) {\n console.warn('Notifications not supported');\n return 'denied';\n }\n const perm = await Notification.requestPermission();\n setPermission(perm);\n return perm;\n }, []);\n\n const sendNotification = useCallback((title: string, options?: NotificationOptions) => {\n if (!('Notification' in window)) return;\n \n // Auto-detect icon if not provided\n let notificationOptions = options;\n if (!options?.icon) {\n // Try to find apple-touch-icon or shortcut icon\n const iconLink = document.querySelector('link[rel=\"apple-touch-icon\"]') || \n document.querySelector('link[rel=\"icon\"]');\n if (iconLink) {\n notificationOptions = {\n ...options,\n icon: (iconLink as HTMLLinkElement).href\n };\n }\n }\n\n if (Notification.permission === 'granted') {\n new Notification(title, notificationOptions);\n } else if (Notification.permission !== 'denied') {\n Notification.requestPermission().then((perm) => {\n if (perm === 'granted') {\n new Notification(title, notificationOptions);\n }\n });\n }\n }, []);\n\n const scheduleNotification = useCallback((title: string, delayMs: number, options?: NotificationOptions) => {\n setTimeout(() => {\n sendNotification(title, options);\n }, delayMs);\n }, [sendNotification]);\n\n const setBadge = useCallback(async (count: number) => {\n if ('setAppBadge' in navigator) {\n try {\n await (navigator as any).setAppBadge(count);\n } catch (e) {\n console.error('Error setting badge:', e);\n }\n }\n }, []);\n\n const clearBadge = useCallback(async () => {\n if ('clearAppBadge' in navigator) {\n try {\n await (navigator as any).clearAppBadge();\n } catch (e) {\n console.error('Error clearing badge:', e);\n }\n }\n }, []);\n\n return {\n permission,\n requestPermission,\n sendNotification,\n scheduleNotification,\n setBadge,\n clearBadge\n };\n}\n","import { useState, useCallback, useEffect } from 'react';\n\nexport interface ClipboardItem {\n type: 'text' | 'image' | 'html';\n content: string | Blob;\n timestamp: number;\n}\n\nexport interface UseClipboardReturn {\n clipboardContent: string | null;\n history: ClipboardItem[];\n copyText: (text: string) => Promise<void>;\n copyImage: (blob: Blob) => Promise<void>;\n readText: () => Promise<string>;\n readContent: () => Promise<ClipboardItems>;\n clearHistory: () => void;\n}\n\nconst HISTORY_KEY = 'relay_clipboard_history';\nconst MAX_HISTORY = 10;\n\nexport function useClipboard(): UseClipboardReturn {\n const [clipboardContent, setClipboardContent] = useState<string | null>(null);\n const [history, setHistory] = useState<ClipboardItem[]>([]);\n\n // Load history from local storage on mount\n useEffect(() => {\n try {\n const savedHistory = localStorage.getItem(HISTORY_KEY);\n if (savedHistory) {\n // Note: We can only restore text items easily from localStorage\n const parsed = JSON.parse(savedHistory);\n setHistory(parsed);\n }\n } catch (e) {\n console.error('Failed to load clipboard history', e);\n }\n }, []);\n\n const addToHistory = useCallback((item: ClipboardItem) => {\n setHistory(prev => {\n const newHistory = [item, ...prev].slice(0, MAX_HISTORY);\n // Only save text items to localStorage to avoid quota issues\n const textOnlyHistory = newHistory.filter(i => i.type === 'text');\n try {\n localStorage.setItem(HISTORY_KEY, JSON.stringify(textOnlyHistory));\n } catch (e) {\n console.error('Failed to save clipboard history', e);\n }\n return newHistory;\n });\n }, []);\n\n const copyText = useCallback(async (text: string) => {\n if (!navigator.clipboard) {\n console.warn('Clipboard API not supported');\n return;\n }\n try {\n await navigator.clipboard.writeText(text);\n setClipboardContent(text);\n addToHistory({\n type: 'text',\n content: text,\n timestamp: Date.now()\n });\n } catch (e) {\n console.error('Failed to copy text:', e);\n throw e;\n }\n }, [addToHistory]);\n\n const copyImage = useCallback(async (blob: Blob) => {\n if (!navigator.clipboard) {\n console.warn('Clipboard API not supported');\n return;\n }\n try {\n await navigator.clipboard.write([\n new ClipboardItem({\n [blob.type]: blob\n })\n ]);\n addToHistory({\n type: 'image',\n content: blob, // Note: This won't be persisted to localStorage\n timestamp: Date.now()\n });\n } catch (e) {\n console.error('Failed to copy image:', e);\n throw e;\n }\n }, [addToHistory]);\n\n const readText = useCallback(async () => {\n if (!navigator.clipboard) {\n console.warn('Clipboard API not supported');\n return '';\n }\n try {\n const text = await navigator.clipboard.readText();\n setClipboardContent(text);\n return text;\n } catch (e) {\n console.error('Failed to read text:', e);\n throw e;\n }\n }, []);\n\n const readContent = useCallback(async () => {\n if (!navigator.clipboard) {\n console.warn('Clipboard API not supported');\n return [] as unknown as ClipboardItems;\n }\n try {\n return await navigator.clipboard.read();\n } catch (e) {\n console.error('Failed to read content:', e);\n throw e;\n }\n }, []);\n\n const clearHistory = useCallback(() => {\n setHistory([]);\n localStorage.removeItem(HISTORY_KEY);\n }, []);\n\n // Listen for clipboard changes (focus based)\n useEffect(() => {\n const handleFocus = () => {\n readText().catch(() => {});\n };\n window.addEventListener('focus', handleFocus);\n return () => window.removeEventListener('focus', handleFocus);\n }, [readText]);\n\n return {\n clipboardContent,\n history,\n copyText,\n copyImage,\n readText,\n readContent,\n clearHistory\n };\n}\n","import { useState, useCallback, useEffect, useRef } from 'react';\n\nexport interface LocationState {\n loading: boolean;\n accuracy: number | null;\n altitude: number | null;\n altitudeAccuracy: number | null;\n heading: number | null;\n latitude: number | null;\n longitude: number | null;\n speed: number | null;\n timestamp: number | null;\n error: GeolocationPositionError | null;\n}\n\nexport interface Geofence {\n id: string;\n latitude: number;\n longitude: number;\n radius: number; // meters\n}\n\nexport interface UseGeolocationReturn extends LocationState {\n getLocation: () => void;\n watchLocation: (options?: PositionOptions) => void;\n clearWatch: () => void;\n checkGeofence: (fence: Geofence) => boolean;\n distanceTo: (lat: number, lng: number) => number;\n}\n\nexport function useGeolocation(options?: PositionOptions): UseGeolocationReturn {\n const [state, setState] = useState<LocationState>({\n loading: true,\n accuracy: null,\n altitude: null,\n altitudeAccuracy: null,\n heading: null,\n latitude: null,\n longitude: null,\n speed: null,\n timestamp: null,\n error: null,\n });\n\n const watchId = useRef<number | null>(null);\n\n const onEvent = useCallback((position: GeolocationPosition) => {\n setState({\n loading: false,\n accuracy: position.coords.accuracy,\n altitude: position.coords.altitude,\n altitudeAccuracy: position.coords.altitudeAccuracy,\n heading: position.coords.heading,\n latitude: position.coords.latitude,\n longitude: position.coords.longitude,\n speed: position.coords.speed,\n timestamp: position.timestamp,\n error: null,\n });\n }, []);\n\n const onError = useCallback((error: GeolocationPositionError) => {\n setState((s) => ({\n ...s,\n loading: false,\n error,\n }));\n }, []);\n\n const getLocation = useCallback(() => {\n if (!navigator.geolocation) {\n setState((s) => ({\n ...s,\n loading: false,\n error: {\n code: 0,\n message: 'Geolocation not supported',\n PERMISSION_DENIED: 1,\n POSITION_UNAVAILABLE: 2,\n TIMEOUT: 3,\n } as GeolocationPositionError,\n }));\n return;\n }\n\n setState((s) => ({ ...s, loading: true }));\n navigator.geolocation.getCurrentPosition(onEvent, onError, options);\n }, [onEvent, onError, options]);\n\n const watchLocation = useCallback((watchOptions?: PositionOptions) => {\n if (!navigator.geolocation) return;\n \n if (watchId.current !== null) {\n navigator.geolocation.clearWatch(watchId.current);\n }\n\n watchId.current = navigator.geolocation.watchPosition(\n onEvent,\n onError,\n watchOptions || options\n );\n }, [onEvent, onError, options]);\n\n const clearWatch = useCallback(() => {\n if (watchId.current !== null) {\n navigator.geolocation.clearWatch(watchId.current);\n watchId.current = null;\n }\n }, []);\n\n // Helper to calculate distance in meters (Haversine formula)\n const distanceTo = useCallback((lat: number, lng: number) => {\n if (state.latitude === null || state.longitude === null) return Infinity;\n\n const R = 6371e3; // metres\n const φ1 = state.latitude * Math.PI / 180;\n const φ2 = lat * Math.PI / 180;\n const Δφ = (lat - state.latitude) * Math.PI / 180;\n const Δλ = (lng - state.longitude) * Math.PI / 180;\n\n const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +\n Math.cos(φ1) * Math.cos(φ2) *\n Math.sin(Δλ / 2) * Math.sin(Δλ / 2);\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n\n return R * c;\n }, [state.latitude, state.longitude]);\n\n const checkGeofence = useCallback((fence: Geofence) => {\n const distance = distanceTo(fence.latitude, fence.longitude);\n return distance <= fence.radius;\n }, [distanceTo]);\n\n useEffect(() => {\n return () => {\n if (watchId.current !== null) {\n navigator.geolocation.clearWatch(watchId.current);\n }\n };\n }, []);\n\n return {\n ...state,\n getLocation,\n watchLocation,\n clearWatch,\n checkGeofence,\n distanceTo,\n };\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\n\nexport interface CameraState {\n stream: MediaStream | null;\n error: Error | null;\n permission: PermissionState | 'unknown';\n isRecording: boolean;\n devices: MediaDeviceInfo[];\n}\n\nexport interface CameraOptions {\n video?: boolean | MediaTrackConstraints;\n audio?: boolean | MediaTrackConstraints;\n}\n\nexport interface UseCameraReturn extends CameraState {\n startCamera: (options?: CameraOptions) => Promise<MediaStream | undefined>;\n stopCamera: () => void;\n takePhoto: () => Promise<string | undefined>;\n startRecording: () => void;\n stopRecording: () => Promise<Blob | undefined>;\n startScreenShare: (options?: DisplayMediaStreamOptions) => Promise<MediaStream | undefined>;\n switchDevice: (deviceId: string, kind: 'video' | 'audio') => Promise<void>;\n getDevices: () => Promise<MediaDeviceInfo[]>;\n}\n\nexport function useCamera(): UseCameraReturn {\n const [state, setState] = useState<CameraState>({\n stream: null,\n error: null,\n permission: 'unknown',\n isRecording: false,\n devices: []\n });\n\n const mediaRecorderRef = useRef<MediaRecorder | null>(null);\n const chunksRef = useRef<Blob[]>([]);\n\n const getDevices = useCallback(async () => {\n if (!navigator.mediaDevices?.enumerateDevices) return [];\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n setState(s => ({ ...s, devices }));\n return devices;\n } catch (e) {\n console.error('Error enumerating devices:', e);\n return [];\n }\n }, []);\n\n // Initial device list\n useEffect(() => {\n getDevices();\n navigator.mediaDevices?.addEventListener('devicechange', getDevices);\n return () => {\n navigator.mediaDevices?.removeEventListener('devicechange', getDevices);\n };\n }, [getDevices]);\n\n const startCamera = useCallback(async (options: CameraOptions = { video: true, audio: false }) => {\n try {\n const stream = await navigator.mediaDevices.getUserMedia(options);\n setState(s => ({ ...s, stream, error: null, permission: 'granted' }));\n return stream;\n } catch (e) {\n const error = e as Error;\n setState(s => ({ ...s, error, permission: 'denied' }));\n console.error('Error starting camera:', e);\n }\n }, []);\n\n const stopCamera = useCallback(() => {\n if (state.stream) {\n state.stream.getTracks().forEach(track => track.stop());\n setState(s => ({ ...s, stream: null, isRecording: false }));\n }\n }, [state.stream]);\n\n const startScreenShare = useCallback(async (options: DisplayMediaStreamOptions = { video: true }) => {\n try {\n const stream = await navigator.mediaDevices.getDisplayMedia(options);\n setState(s => ({ ...s, stream, error: null, permission: 'granted' }));\n return stream;\n } catch (e) {\n const error = e as Error;\n setState(s => ({ ...s, error, permission: 'denied' }));\n console.error('Error starting screen share:', e);\n }\n }, []);\n\n const takePhoto = useCallback(async () => {\n if (!state.stream) return;\n\n const videoTrack = state.stream.getVideoTracks()[0];\n if (!videoTrack) return;\n\n // Create a video element to capture the frame\n const video = document.createElement('video');\n video.srcObject = state.stream;\n await video.play();\n\n const canvas = document.createElement('canvas');\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n \n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n \n ctx.drawImage(video, 0, 0);\n const dataUrl = canvas.toDataURL('image/png');\n \n // Cleanup\n video.pause();\n video.srcObject = null;\n \n return dataUrl;\n }, [state.stream]);\n\n const startRecording = useCallback(() => {\n if (!state.stream) return;\n\n try {\n const mediaRecorder = new MediaRecorder(state.stream);\n mediaRecorderRef.current = mediaRecorder;\n chunksRef.current = [];\n\n mediaRecorder.ondataavailable = (e) => {\n if (e.data.size > 0) {\n chunksRef.current.push(e.data);\n }\n };\n\n mediaRecorder.start();\n setState(s => ({ ...s, isRecording: true }));\n } catch (e) {\n console.error('Error starting recording:', e);\n setState(s => ({ ...s, error: e as Error }));\n }\n }, [state.stream]);\n\n const stopRecording = useCallback(async () => {\n if (!mediaRecorderRef.current || mediaRecorderRef.current.state === 'inactive') return;\n\n return new Promise<Blob>((resolve) => {\n if (!mediaRecorderRef.current) return;\n \n mediaRecorderRef.current.onstop = () => {\n const blob = new Blob(chunksRef.current, { type: 'video/webm' });\n setState(s => ({ ...s, isRecording: false }));\n resolve(blob);\n };\n\n mediaRecorderRef.current.stop();\n });\n }, []);\n\n const switchDevice = useCallback(async (deviceId: string, kind: 'video' | 'audio') => {\n stopCamera();\n const constraints = {\n [kind]: { deviceId: { exact: deviceId } }\n };\n await startCamera(constraints);\n }, [startCamera, stopCamera]);\n\n useEffect(() => {\n return () => {\n stopCamera();\n };\n }, []);\n\n return {\n ...state,\n startCamera,\n stopCamera,\n takePhoto,\n startRecording,\n stopRecording,\n startScreenShare,\n switchDevice,\n getDevices\n };\n}\n","import { useState, useCallback, useEffect } from 'react';\n\nexport interface OllamaModel {\n name: string;\n modified_at: string;\n size: number;\n digest: string;\n details: {\n parent_model: string;\n format: string;\n family: string;\n families: string[];\n parameter_size: string;\n quantization_level: string;\n };\n}\n\nexport interface OllamaResponse {\n model: string;\n created_at: string;\n response: string;\n done: boolean;\n context?: number[];\n total_duration?: number;\n load_duration?: number;\n prompt_eval_count?: number;\n prompt_eval_duration?: number;\n eval_count?: number;\n eval_duration?: number;\n}\n\nexport interface UseOllamaReturn {\n available: boolean;\n loading: boolean;\n models: OllamaModel[];\n refreshModels: () => Promise<void>;\n generate: (model: string, prompt: string, options?: any) => Promise<OllamaResponse>;\n chat: (model: string, messages: { role: string; content: string }[], options?: any) => Promise<OllamaResponse>;\n}\n\nexport function useOllama(): UseOllamaReturn {\n const [available, setAvailable] = useState(false);\n const [loading, setLoading] = useState(false);\n const [models, setModels] = useState<OllamaModel[]>([]);\n\n const HELPER_URL = 'http://localhost:5378';\n\n const checkStatus = useCallback(async () => {\n try {\n const res = await fetch(`${HELPER_URL}/api/config`);\n const data = await res.json();\n setAvailable(data.ollama?.available ?? false);\n } catch (e) {\n setAvailable(false);\n }\n }, []);\n\n const refreshModels = useCallback(async () => {\n setLoading(true);\n try {\n const res = await fetch(`${HELPER_URL}/api/ollama/api/tags`);\n if (res.ok) {\n const data = await res.json();\n setModels(data.models || []);\n }\n } catch (e) {\n console.error('Failed to fetch Ollama models:', e);\n } finally {\n setLoading(false);\n }\n }, []);\n\n useEffect(() => {\n checkStatus();\n const interval = setInterval(checkStatus, 30000);\n return () => clearInterval(interval);\n }, [checkStatus]);\n\n useEffect(() => {\n if (available) {\n refreshModels();\n }\n }, [available, refreshModels]);\n\n const generate = useCallback(async (model: string, prompt: string, options = {}): Promise<OllamaResponse> => {\n const res = await fetch(`${HELPER_URL}/api/ollama/api/generate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ model, prompt, stream: false, ...options })\n });\n\n if (!res.ok) throw new Error(`Ollama generation failed: ${res.statusText}`);\n return await res.json();\n }, []);\n\n const chat = useCallback(async (model: string, messages: { role: string; content: string }[], options = {}): Promise<OllamaResponse> => {\n const res = await fetch(`${HELPER_URL}/api/ollama/api/chat`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ model, messages, stream: false, ...options })\n });\n\n if (!res.ok) throw new Error(`Ollama chat failed: ${res.statusText}`);\n return await res.json();\n }, []);\n\n return {\n available,\n loading,\n models,\n refreshModels,\n generate,\n chat\n };\n}\n"],"mappings":"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","names":["useState","useEffect","useCallback","useKeyboard","state","setState","updateKeyboardState","viewport","keyboardHeight","isOpen","handleFocusIn","e","target","handleFocusOut","useRelayClose","useState","useEffect","createContext","useContext","jsx","defaultInsets","SafeAreaContext","SafeAreaProvider","children","insets","setInsets","computeInsets","style","getEnv","name","value","top","bottom","left","right","temp","rect","handleMessage","event","useSafeArea","useColorScheme","scheme","setScheme","media","handler","e","useStatusBar","color","meta","useBackHandler","handlePopState","useAppState","state","setState","handleVisibility","jsx","jsxs","KeyboardAvoidingView","children","behavior","keyboardVerticalOffset","style","className","keyboard","useKeyboard","offset","computedStyle","SafeAreaView","edges","insets","useSafeArea","PlatformHeader","title","showBack","onBack","rightElement","cn","useState","useEffect","useFocusTimer","seconds","setSeconds","initialTime","setInitialTime","isActive","setIsActive","mode","setMode","task","setTask","storyState","setStoryState","soundEnabled","setSoundEnabled","interval","s","isAbducted","progress","toggleTimer","giveUp","switchMode","newMode","newTime","mins","useState","useCallback","useEffect","useNotifications","permission","setPermission","requestPermission","perm","sendNotification","title","options","notificationOptions","iconLink","scheduleNotification","delayMs","setBadge","count","e","clearBadge","useState","useCallback","useEffect","HISTORY_KEY","MAX_HISTORY","useClipboard","clipboardContent","setClipboardContent","history","setHistory","savedHistory","parsed","e","addToHistory","item","prev","newHistory","textOnlyHistory","i","copyText","text","copyImage","blob","readText","readContent","clearHistory","handleFocus","useState","useCallback","useEffect","useRef","useGeolocation","options","state","setState","watchId","onEvent","position","onError","error","s","getLocation","watchLocation","watchOptions","clearWatch","distanceTo","lat","lng","R","φ1","φ2","Δφ","Δλ","a","c","checkGeofence","fence","useState","useCallback","useRef","useEffect","useCamera","state","setState","mediaRecorderRef","chunksRef","getDevices","devices","s","e","startCamera","options","stream","error","stopCamera","track","startScreenShare","takePhoto","video","canvas","ctx","dataUrl","startRecording","mediaRecorder","stopRecording","resolve","blob","switchDevice","deviceId","kind","constraints","useState","useCallback","useEffect","useOllama","available","setAvailable","loading","setLoading","models","setModels","HELPER_URL","checkStatus","data","refreshModels","res","interval","generate","model","prompt","options","chat","messages"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notapublicfigureanymore/relay-sdk",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "The official SDK for building Relay apps.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",