ywana-core8 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,214 @@
1
+ import React, { useState, useEffect, useRef } from 'react'
2
+ import { useWindows } from './WindowContext'
3
+ import { useAppManager } from './desktop'
4
+ import './ApplicationMenu.css'
5
+
6
+ /**
7
+ * ApplicationMenu - Full-screen overlay menu for launching applications
8
+ */
9
+ export const ApplicationMenu = ({ isOpen, onClose }) => {
10
+ const appManager = useAppManager()
11
+ const [searchTerm, setSearchTerm] = useState('')
12
+ const [selectedCategory, setSelectedCategory] = useState('all')
13
+ const [apps, setApps] = useState([])
14
+ const [categories, setCategories] = useState([])
15
+ const searchInputRef = useRef(null)
16
+ const { createWindow } = useWindows()
17
+
18
+ // Load apps and categories
19
+ useEffect(() => {
20
+ const loadData = () => {
21
+ setApps(appManager.getAllApps())
22
+ setCategories(appManager.getAllCategories())
23
+ }
24
+
25
+ loadData()
26
+ appManager.addListener(loadData)
27
+
28
+ return () => {
29
+ appManager.removeListener(loadData)
30
+ }
31
+ }, [appManager])
32
+
33
+ // Focus search input when menu opens
34
+ useEffect(() => {
35
+ if (isOpen && searchInputRef.current) {
36
+ setTimeout(() => {
37
+ searchInputRef.current.focus()
38
+ }, 100)
39
+ }
40
+ }, [isOpen])
41
+
42
+ // Handle escape key to close menu
43
+ useEffect(() => {
44
+ const handleKeyDown = (e) => {
45
+ if (e.key === 'Escape' && isOpen) {
46
+ onClose()
47
+ }
48
+ }
49
+
50
+ if (isOpen) {
51
+ document.addEventListener('keydown', handleKeyDown)
52
+ return () => document.removeEventListener('keydown', handleKeyDown)
53
+ }
54
+ }, [isOpen, onClose])
55
+
56
+ // Filter apps based on search and category
57
+ const filteredApps = apps.filter(app => {
58
+ const matchesSearch = searchTerm === '' ||
59
+ app.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
60
+ app.description.toLowerCase().includes(searchTerm.toLowerCase())
61
+
62
+ const matchesCategory = selectedCategory === 'all' ||
63
+ app.category.toLowerCase() === selectedCategory.toLowerCase()
64
+
65
+ return matchesSearch && matchesCategory
66
+ })
67
+
68
+ // Group filtered apps by category
69
+ const groupedApps = filteredApps.reduce((groups, app) => {
70
+ const category = app.category
71
+ if (!groups[category]) {
72
+ groups[category] = []
73
+ }
74
+ groups[category].push(app)
75
+ return groups
76
+ }, {})
77
+
78
+ // Handle app launch
79
+ const handleLaunchApp = (app) => {
80
+ createWindow({
81
+ id: `${app.id}-${Date.now()}`,
82
+ title: app.name,
83
+ icon: app.icon,
84
+ size: app.size,
85
+ toolbar: app.toolbar,
86
+ statusBar: app.statusBar,
87
+ content: app.component || (
88
+ <div style={{ padding: '20px', textAlign: 'center' }}>
89
+ <div style={{ fontSize: '48px', marginBottom: '16px' }}>{app.icon}</div>
90
+ <h2>{app.name}</h2>
91
+ <p style={{ color: '#666', marginBottom: '20px' }}>{app.description}</p>
92
+ <p style={{ fontSize: '14px', color: '#999' }}>
93
+ This is a placeholder for the {app.name} application.
94
+ </p>
95
+ </div>
96
+ )
97
+ })
98
+ onClose()
99
+ }
100
+
101
+ // Handle category selection
102
+ const handleCategorySelect = (categoryId) => {
103
+ setSelectedCategory(categoryId)
104
+ setSearchTerm('')
105
+ }
106
+
107
+ if (!isOpen) return null
108
+
109
+ return (
110
+ <div className="application-menu-overlay" onClick={onClose}>
111
+ <div className="application-menu" onClick={(e) => e.stopPropagation()}>
112
+ {/* Header */}
113
+ <div className="application-menu__header">
114
+ <h2>Applications</h2>
115
+ <button
116
+ className="application-menu__close"
117
+ onClick={onClose}
118
+ title="Close menu"
119
+ >
120
+ ×
121
+ </button>
122
+ </div>
123
+
124
+ {/* Search */}
125
+ <div className="application-menu__search">
126
+ <input
127
+ ref={searchInputRef}
128
+ type="text"
129
+ placeholder="Search applications..."
130
+ value={searchTerm}
131
+ onChange={(e) => setSearchTerm(e.target.value)}
132
+ className="application-menu__search-input"
133
+ />
134
+ </div>
135
+
136
+ {/* Categories */}
137
+ <div className="application-menu__categories">
138
+ <button
139
+ className={`application-menu__category ${selectedCategory === 'all' ? 'active' : ''}`}
140
+ onClick={() => handleCategorySelect('all')}
141
+ >
142
+ <span className="category-icon">📱</span>
143
+ All Apps
144
+ </button>
145
+ {categories.map(category => (
146
+ <button
147
+ key={category.id}
148
+ className={`application-menu__category ${selectedCategory === category.id ? 'active' : ''}`}
149
+ onClick={() => handleCategorySelect(category.id)}
150
+ >
151
+ <span className="category-icon">{category.icon}</span>
152
+ {category.name}
153
+ </button>
154
+ ))}
155
+ </div>
156
+
157
+ {/* Applications Grid */}
158
+ <div className="application-menu__content">
159
+ {searchTerm && (
160
+ <div className="application-menu__search-results">
161
+ <h3>Search Results ({filteredApps.length})</h3>
162
+ <div className="application-menu__apps-grid">
163
+ {filteredApps.map(app => (
164
+ <div
165
+ key={app.id}
166
+ className="application-menu__app"
167
+ onClick={() => handleLaunchApp(app)}
168
+ title={app.description}
169
+ >
170
+ <div className="app-icon">{app.icon}</div>
171
+ <div className="app-name">{app.name}</div>
172
+ </div>
173
+ ))}
174
+ </div>
175
+ </div>
176
+ )}
177
+
178
+ {!searchTerm && (
179
+ <div className="application-menu__categories-content">
180
+ {Object.entries(groupedApps).map(([categoryName, categoryApps]) => (
181
+ <div key={categoryName} className="application-menu__category-section">
182
+ <h3 className="category-title">{categoryName}</h3>
183
+ <div className="application-menu__apps-grid">
184
+ {categoryApps.map(app => (
185
+ <div
186
+ key={app.id}
187
+ className="application-menu__app"
188
+ onClick={() => handleLaunchApp(app)}
189
+ title={app.description}
190
+ >
191
+ <div className="app-icon">{app.icon}</div>
192
+ <div className="app-name">{app.name}</div>
193
+ </div>
194
+ ))}
195
+ </div>
196
+ </div>
197
+ ))}
198
+ </div>
199
+ )}
200
+
201
+ {filteredApps.length === 0 && (
202
+ <div className="application-menu__no-results">
203
+ <div style={{ fontSize: '48px', marginBottom: '16px' }}>🔍</div>
204
+ <h3>No applications found</h3>
205
+ <p>Try adjusting your search or category filter.</p>
206
+ </div>
207
+ )}
208
+ </div>
209
+ </div>
210
+ </div>
211
+ )
212
+ }
213
+
214
+ export default ApplicationMenu
@@ -1,5 +1,6 @@
1
1
  import React from 'react'
2
- import { Desktop } from './desktop.js'
2
+ import { Desktop, useApplicationMenu, useAppManager } from './desktop.js'
3
+ import { defaultAppManager } from './AppManager.js'
3
4
 
4
5
  /**
5
6
  * Basic Desktop with WindowManager
@@ -88,10 +89,10 @@ export const DarkDesktop = () => (
88
89
  export const LightDesktop = () => (
89
90
  <div style={{ height: '600px', width: '100%', position: 'relative', overflow: 'hidden' }}>
90
91
  <Desktop className="desktop--light">
91
- <div style={{
92
- position: 'absolute',
93
- top: '50%',
94
- left: '50%',
92
+ <div style={{
93
+ position: 'absolute',
94
+ top: '50%',
95
+ left: '50%',
95
96
  transform: 'translate(-50%, -50%)',
96
97
  background: 'rgba(255,255,255,0.9)',
97
98
  color: '#333',
@@ -108,3 +109,259 @@ export const LightDesktop = () => (
108
109
  </Desktop>
109
110
  </div>
110
111
  )
112
+
113
+ // Component that demonstrates using both AppProvider hooks
114
+ const DemoControls = () => {
115
+ const { isOpen, open, close, toggle } = useApplicationMenu()
116
+ const appManager = useAppManager()
117
+ const [appCount, setAppCount] = React.useState(0)
118
+
119
+ React.useEffect(() => {
120
+ const updateCount = () => setAppCount(appManager.getAllApps().length)
121
+ updateCount()
122
+ appManager.addListener(updateCount)
123
+ return () => appManager.removeListener(updateCount)
124
+ }, [appManager])
125
+
126
+ const addCustomApp = () => {
127
+ const randomId = `custom-app-${Date.now()}`
128
+ appManager.registerApp({
129
+ id: randomId,
130
+ name: `Custom App ${Math.floor(Math.random() * 100)}`,
131
+ description: 'Dynamically added application',
132
+ icon: ['🎨', '🎯', '🎪', '🎭', '🎨'][Math.floor(Math.random() * 5)],
133
+ category: 'Custom',
134
+ component: React.createElement('div', {
135
+ style: { padding: '20px', textAlign: 'center' }
136
+ }, [
137
+ React.createElement('h2', { key: 'title' }, 'Dynamic App'),
138
+ React.createElement('p', { key: 'desc' }, 'This app was added dynamically using AppManager!'),
139
+ React.createElement('p', { key: 'id' }, `ID: ${randomId}`)
140
+ ]),
141
+ size: { width: 400, height: 300 }
142
+ })
143
+ }
144
+
145
+ return (
146
+ <div style={{
147
+ position: 'absolute',
148
+ top: '20px',
149
+ right: '20px',
150
+ background: 'rgba(255,255,255,0.9)',
151
+ padding: '20px',
152
+ borderRadius: '8px',
153
+ boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
154
+ maxWidth: '300px'
155
+ }}>
156
+ <h4 style={{ margin: '0 0 15px 0' }}>🎮 App Controls</h4>
157
+ <p style={{ margin: '0 0 10px 0', fontSize: '14px', color: '#666' }}>
158
+ Menu: <strong>{isOpen ? 'Open' : 'Closed'}</strong><br/>
159
+ Apps: <strong>{appCount}</strong>
160
+ </p>
161
+
162
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
163
+ <button
164
+ onClick={open}
165
+ style={{
166
+ padding: '8px 12px',
167
+ background: '#4caf50',
168
+ color: 'white',
169
+ border: 'none',
170
+ borderRadius: '4px',
171
+ cursor: 'pointer',
172
+ fontSize: '12px'
173
+ }}
174
+ >
175
+ Open Menu
176
+ </button>
177
+ <button
178
+ onClick={close}
179
+ style={{
180
+ padding: '8px 12px',
181
+ background: '#f44336',
182
+ color: 'white',
183
+ border: 'none',
184
+ borderRadius: '4px',
185
+ cursor: 'pointer',
186
+ fontSize: '12px'
187
+ }}
188
+ >
189
+ Close Menu
190
+ </button>
191
+ <button
192
+ onClick={toggle}
193
+ style={{
194
+ padding: '8px 12px',
195
+ background: '#2196f3',
196
+ color: 'white',
197
+ border: 'none',
198
+ borderRadius: '4px',
199
+ cursor: 'pointer',
200
+ fontSize: '12px'
201
+ }}
202
+ >
203
+ Toggle Menu
204
+ </button>
205
+ <hr style={{ margin: '10px 0', border: 'none', borderTop: '1px solid #eee' }} />
206
+ <button
207
+ onClick={addCustomApp}
208
+ style={{
209
+ padding: '8px 12px',
210
+ background: '#ff9800',
211
+ color: 'white',
212
+ border: 'none',
213
+ borderRadius: '4px',
214
+ cursor: 'pointer',
215
+ fontSize: '12px'
216
+ }}
217
+ >
218
+ Add Custom App
219
+ </button>
220
+ </div>
221
+ </div>
222
+ )
223
+ }
224
+
225
+ /**
226
+ * Desktop with ApplicationMenu - Basic functionality
227
+ */
228
+ export const ApplicationMenuBasic = () => (
229
+ <div style={{ height: '600px', width: '100%', position: 'relative', overflow: 'hidden' }}>
230
+ <Desktop desktopSize={{ width: 1200, height: 600 }}>
231
+ <div style={{
232
+ position: 'absolute',
233
+ top: '20px',
234
+ left: '20px',
235
+ background: 'rgba(255,255,255,0.9)',
236
+ padding: '20px',
237
+ borderRadius: '8px',
238
+ boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
239
+ maxWidth: '400px'
240
+ }}>
241
+ <h3 style={{ margin: '0 0 15px 0' }}>🚀 Application Menu Demo</h3>
242
+ <p style={{ margin: '0 0 15px 0', lineHeight: '1.5' }}>
243
+ Click the <strong>"Start"</strong> button in the taskbar to open the Application Menu.
244
+ Browse applications by category and launch them to create windows.
245
+ </p>
246
+ <div style={{ fontSize: '14px', color: '#666' }}>
247
+ <p><strong>Features:</strong></p>
248
+ <ul style={{ margin: '5px 0', paddingLeft: '20px' }}>
249
+ <li>Browse apps by category</li>
250
+ <li>Search functionality</li>
251
+ <li>Launch applications as windows</li>
252
+ <li>Full-screen overlay menu</li>
253
+ </ul>
254
+ </div>
255
+ </div>
256
+ </Desktop>
257
+ </div>
258
+ )
259
+
260
+ /**
261
+ * Desktop with ApplicationMenu - Advanced with controls
262
+ */
263
+ export const ApplicationMenuAdvanced = () => {
264
+ // Setup custom apps
265
+ React.useEffect(() => {
266
+ // Register some custom applications
267
+ defaultAppManager.registerApp({
268
+ id: 'advanced-calculator',
269
+ name: 'Advanced Calculator',
270
+ description: 'Scientific calculator with advanced functions',
271
+ icon: '🧮',
272
+ category: 'Utilities',
273
+ component: React.createElement('div', {
274
+ style: { padding: '20px', textAlign: 'center' }
275
+ }, [
276
+ React.createElement('h2', { key: 'title' }, '🧮 Advanced Calculator'),
277
+ React.createElement('p', { key: 'desc' }, 'A powerful calculator with scientific functions.'),
278
+ React.createElement('div', {
279
+ key: 'buttons',
280
+ style: { display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '8px', marginTop: '20px' }
281
+ }, [
282
+ ...['7', '8', '9', '/', '4', '5', '6', '*', '1', '2', '3', '-', '0', '.', '=', '+'].map((btn, i) =>
283
+ React.createElement('button', {
284
+ key: i,
285
+ style: {
286
+ padding: '12px',
287
+ background: '#f0f0f0',
288
+ border: '1px solid #ccc',
289
+ borderRadius: '4px',
290
+ cursor: 'pointer'
291
+ }
292
+ }, btn)
293
+ )
294
+ ])
295
+ ]),
296
+ size: { width: 300, height: 400 }
297
+ })
298
+
299
+ defaultAppManager.registerApp({
300
+ id: 'text-editor-pro',
301
+ name: 'Text Editor Pro',
302
+ description: 'Professional text editor with syntax highlighting',
303
+ icon: '📝',
304
+ category: 'Productivity',
305
+ component: React.createElement('div', {
306
+ style: { padding: '20px', height: '100%', display: 'flex', flexDirection: 'column' }
307
+ }, [
308
+ React.createElement('div', {
309
+ key: 'toolbar',
310
+ style: { marginBottom: '10px', display: 'flex', gap: '10px' }
311
+ }, [
312
+ React.createElement('button', { key: 'new', style: { padding: '5px 10px', fontSize: '12px' } }, 'New'),
313
+ React.createElement('button', { key: 'open', style: { padding: '5px 10px', fontSize: '12px' } }, 'Open'),
314
+ React.createElement('button', { key: 'save', style: { padding: '5px 10px', fontSize: '12px' } }, 'Save')
315
+ ]),
316
+ React.createElement('textarea', {
317
+ key: 'editor',
318
+ style: {
319
+ flex: 1,
320
+ border: '1px solid #ccc',
321
+ padding: '10px',
322
+ fontFamily: 'monospace',
323
+ fontSize: '14px',
324
+ resize: 'none'
325
+ },
326
+ placeholder: 'Start typing your code here...',
327
+ defaultValue: '// Welcome to Text Editor Pro!\nfunction hello() {\n console.log("Hello, World!");\n}'
328
+ })
329
+ ]),
330
+ size: { width: 600, height: 400 }
331
+ })
332
+ }, [])
333
+
334
+ return (
335
+ <div style={{ height: '600px', width: '100%', position: 'relative', overflow: 'hidden' }}>
336
+ <Desktop desktopSize={{ width: 1200, height: 600 }}>
337
+ <div style={{
338
+ position: 'absolute',
339
+ top: '20px',
340
+ left: '20px',
341
+ background: 'rgba(255,255,255,0.9)',
342
+ padding: '20px',
343
+ borderRadius: '8px',
344
+ boxShadow: '0 2px 10px rgba(0,0,0,0.1)',
345
+ maxWidth: '400px'
346
+ }}>
347
+ <h3 style={{ margin: '0 0 15px 0' }}>🎮 AppProvider + AppManager</h3>
348
+ <p style={{ margin: '0 0 15px 0', lineHeight: '1.5' }}>
349
+ This demo shows AppProvider and AppManager collaboration. Use the controls
350
+ on the right to manage the menu and add custom applications dynamically.
351
+ </p>
352
+ <div style={{ fontSize: '14px', color: '#666' }}>
353
+ <p><strong>Advanced Features:</strong></p>
354
+ <ul style={{ margin: '5px 0', paddingLeft: '20px' }}>
355
+ <li>Dynamic app registration</li>
356
+ <li>Real-time app count updates</li>
357
+ <li>Custom app components</li>
358
+ <li>Context-based state management</li>
359
+ </ul>
360
+ </div>
361
+ </div>
362
+
363
+ <DemoControls />
364
+ </Desktop>
365
+ </div>
366
+ )
367
+ }
@@ -1,8 +1,56 @@
1
- import React, { useRef, useEffect } from 'react'
1
+ import React, { useRef, useEffect, useState, createContext, useContext } from 'react'
2
2
  import { WindowProvider, useWindows } from './WindowContext'
3
3
  import { Window } from './window'
4
+ import { ApplicationMenu } from './ApplicationMenu'
5
+ import { defaultAppManager } from './AppManager'
4
6
  import './desktop.css'
5
7
 
8
+ /**
9
+ * Context for Application state
10
+ */
11
+ const AppContext = createContext()
12
+
13
+ export const useApplicationMenu = () => {
14
+ const context = useContext(AppContext)
15
+ if (!context) {
16
+ throw new Error('useApplicationMenu must be used within AppProvider')
17
+ }
18
+ return context.applicationMenu
19
+ }
20
+
21
+ export const useAppManager = () => {
22
+ const context = useContext(AppContext)
23
+ if (!context) {
24
+ throw new Error('useAppManager must be used within AppProvider')
25
+ }
26
+ return context.appManager
27
+ }
28
+
29
+ /**
30
+ * AppProvider - Provides Application state and AppManager to React components
31
+ */
32
+ export const AppProvider = ({ children, appManager = defaultAppManager }) => {
33
+ const [isApplicationMenuOpen, setIsApplicationMenuOpen] = useState(false)
34
+
35
+ const value = {
36
+ // Application Menu state
37
+ applicationMenu: {
38
+ isOpen: isApplicationMenuOpen,
39
+ open: () => setIsApplicationMenuOpen(true),
40
+ close: () => setIsApplicationMenuOpen(false),
41
+ toggle: () => setIsApplicationMenuOpen(!isApplicationMenuOpen)
42
+ },
43
+ // App Manager instance
44
+ appManager: appManager
45
+ }
46
+
47
+ return (
48
+ <AppContext.Provider value={value}>
49
+ {children}
50
+ </AppContext.Provider>
51
+ )
52
+ }
53
+
6
54
  /**
7
55
  * Desktop layout component - manages overall desktop structure and sizing
8
56
  */
@@ -112,6 +160,8 @@ export const DesktopTaskbar = () => {
112
160
  closeWindow
113
161
  } = useWindows()
114
162
 
163
+ const { open: openApplicationMenu } = useApplicationMenu()
164
+
115
165
  const handleCreateWindow = () => {
116
166
  const windowTypes = [
117
167
  { title: 'File Explorer', icon: '📁', size: { width: 600, height: 400 } },
@@ -171,34 +221,57 @@ export const DesktopTaskbar = () => {
171
221
 
172
222
  return (
173
223
  <div style={{
174
- position: 'absolute',
175
- bottom: 0,
176
- left: 0,
177
- right: 0,
178
- height: '50px',
179
- background: 'rgba(0,0,0,0.8)',
180
- display: 'flex',
181
- alignItems: 'center',
182
- padding: '0 16px',
183
- gap: '8px'
184
- }}>
185
- {/* Create window button */}
186
- <button
187
- onClick={handleCreateWindow}
188
- style={{
189
- padding: '8px 12px',
190
- background: '#1976d2',
191
- color: 'white',
192
- border: 'none',
193
- borderRadius: '4px',
194
- cursor: 'pointer',
195
- fontSize: '12px',
196
- flexShrink: 0
197
- }}
198
- title="Create new window"
199
- >
200
- +
201
- </button>
224
+ position: 'absolute',
225
+ bottom: 0,
226
+ left: 0,
227
+ right: 0,
228
+ height: '50px',
229
+ background: 'rgba(0,0,0,0.8)',
230
+ display: 'flex',
231
+ alignItems: 'center',
232
+ padding: '0 16px',
233
+ gap: '8px'
234
+ }}>
235
+ {/* Start button */}
236
+ <button
237
+ onClick={openApplicationMenu}
238
+ style={{
239
+ padding: '8px 16px',
240
+ background: '#1976d2',
241
+ color: 'white',
242
+ border: 'none',
243
+ borderRadius: '4px',
244
+ cursor: 'pointer',
245
+ fontSize: '14px',
246
+ fontWeight: 'bold',
247
+ flexShrink: 0,
248
+ display: 'flex',
249
+ alignItems: 'center',
250
+ gap: '6px'
251
+ }}
252
+ title="Open application menu"
253
+ >
254
+ <span style={{ fontSize: '16px' }}>🚀</span>
255
+ Start
256
+ </button>
257
+
258
+ {/* Create window button (for testing) */}
259
+ <button
260
+ onClick={handleCreateWindow}
261
+ style={{
262
+ padding: '8px 12px',
263
+ background: '#666',
264
+ color: 'white',
265
+ border: 'none',
266
+ borderRadius: '4px',
267
+ cursor: 'pointer',
268
+ fontSize: '12px',
269
+ flexShrink: 0
270
+ }}
271
+ title="Create random window (for testing)"
272
+ >
273
+ +
274
+ </button>
202
275
 
203
276
  {/* Separator */}
204
277
  <div style={{
@@ -292,9 +365,11 @@ export const DesktopTaskbar = () => {
292
365
  }
293
366
 
294
367
  /**
295
- * Main Desktop component with WindowProvider
368
+ * Internal Desktop component that uses the contexts
296
369
  */
297
- export const Desktop = ({ desktopSize, children, ...props }) => {
370
+ const DesktopInternal = ({ desktopSize, children, ...props }) => {
371
+ const { isOpen, close } = useApplicationMenu()
372
+
298
373
  return (
299
374
  <WindowProvider desktopSize={desktopSize}>
300
375
  <DesktopLayout {...props}>
@@ -302,7 +377,24 @@ export const Desktop = ({ desktopSize, children, ...props }) => {
302
377
  {children}
303
378
  </Workspace>
304
379
  <DesktopTaskbar />
380
+ <ApplicationMenu
381
+ isOpen={isOpen}
382
+ onClose={close}
383
+ />
305
384
  </DesktopLayout>
306
385
  </WindowProvider>
307
386
  )
308
387
  }
388
+
389
+ /**
390
+ * Main Desktop component with AppProvider wrapper
391
+ */
392
+ export const Desktop = ({ desktopSize, children, ...props }) => {
393
+ return (
394
+ <AppProvider>
395
+ <DesktopInternal desktopSize={desktopSize} {...props}>
396
+ {children}
397
+ </DesktopInternal>
398
+ </AppProvider>
399
+ )
400
+ }