ywana-core8 0.2.20 → 0.2.21

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.
@@ -1,12 +1,15 @@
1
1
  import React, { useState, useEffect, useRef } from 'react'
2
2
  import { useWindows } from './WindowContext'
3
3
  import { useAppManager } from './desktop'
4
- import './ApplicationMenu.css'
4
+ import './LaunchPad.css'
5
5
 
6
6
  /**
7
- * ApplicationMenu - Full-screen overlay menu for launching applications
7
+ * LaunchPad - Full-screen application launcher inspired by macOS
8
+ *
9
+ * Displays all available applications organized by categories,
10
+ * with search functionality and multiple view modes.
8
11
  */
9
- export const ApplicationMenu = ({ isOpen, onClose }) => {
12
+ export const LaunchPad = ({ isOpen, onClose }) => {
10
13
  const appManager = useAppManager()
11
14
  const [searchTerm, setSearchTerm] = useState('')
12
15
  const [selectedCategory, setSelectedCategory] = useState('all')
@@ -109,34 +112,33 @@ export const ApplicationMenu = ({ isOpen, onClose }) => {
109
112
  if (!isOpen) return null
110
113
 
111
114
  return (
112
- <div className="application-menu-overlay" onClick={onClose}>
113
- <div className={`application-menu ${isCondensed ? 'application-menu--condensed' : ''}`} onClick={(e) => e.stopPropagation()}>
115
+ <div className={`launchpad ${isCondensed ? 'launchpad--condensed' : ''}`}>
114
116
  {/* Header */}
115
- <div className="application-menu__header">
116
- <div className="application-menu__header-controls">
117
+ <div className="launchpad__header">
118
+ <div className="launchpad__header-controls">
117
119
  <button
118
- className={`application-menu__view-toggle ${isCondensed ? 'active' : ''}`}
120
+ className={`launchpad__view-toggle ${isCondensed ? 'active' : ''}`}
119
121
  onClick={() => setIsCondensed(!isCondensed)}
120
122
  title={isCondensed ? "Normal view" : "Condensed view"}
121
123
  >
122
124
 
123
125
  </button>
124
126
  <button
125
- className={`application-menu__view-toggle ${viewMode === 'grid' ? 'active' : ''}`}
127
+ className={`launchpad__view-toggle ${viewMode === 'grid' ? 'active' : ''}`}
126
128
  onClick={() => setViewMode('grid')}
127
129
  title="Grid view"
128
130
  >
129
131
 
130
132
  </button>
131
133
  <button
132
- className={`application-menu__view-toggle ${viewMode === 'list' ? 'active' : ''}`}
134
+ className={`launchpad__view-toggle ${viewMode === 'list' ? 'active' : ''}`}
133
135
  onClick={() => setViewMode('list')}
134
136
  title="List view"
135
137
  >
136
138
 
137
139
  </button>
138
140
  <button
139
- className="application-menu__close"
141
+ className="launchpad__close"
140
142
  onClick={onClose}
141
143
  title="Close menu"
142
144
  >
@@ -146,24 +148,24 @@ export const ApplicationMenu = ({ isOpen, onClose }) => {
146
148
  </div>
147
149
 
148
150
  {/* Search */}
149
- <div className="application-menu__search">
151
+ <div className="launchpad__search">
150
152
  <input
151
153
  ref={searchInputRef}
152
154
  type="text"
153
155
  placeholder="Search applications..."
154
156
  value={searchTerm}
155
157
  onChange={(e) => setSearchTerm(e.target.value)}
156
- className="application-menu__search-input"
158
+ className="launchpad__search-input"
157
159
  />
158
160
  </div>
159
161
 
160
162
  {/* Main Grid Layout */}
161
- <div className="application-menu__main">
163
+ <div className="launchpad__main">
162
164
  {/* Categories Sidebar */}
163
- <div className="application-menu__sidebar">
164
- <div className="application-menu__categories">
165
+ <div className="launchpad__sidebar">
166
+ <div className="launchpad__categories">
165
167
  <button
166
- className={`application-menu__category ${selectedCategory === 'all' ? 'active' : ''}`}
168
+ className={`launchpad__category ${selectedCategory === 'all' ? 'active' : ''}`}
167
169
  onClick={() => handleCategorySelect('all')}
168
170
  title="All Apps"
169
171
  >
@@ -173,7 +175,7 @@ export const ApplicationMenu = ({ isOpen, onClose }) => {
173
175
  {categories.map(category => (
174
176
  <button
175
177
  key={category.id}
176
- className={`application-menu__category ${selectedCategory === category.id ? 'active' : ''}`}
178
+ className={`launchpad__category ${selectedCategory === category.id ? 'active' : ''}`}
177
179
  onClick={() => handleCategorySelect(category.id)}
178
180
  title={category.name}
179
181
  >
@@ -185,15 +187,15 @@ export const ApplicationMenu = ({ isOpen, onClose }) => {
185
187
  </div>
186
188
 
187
189
  {/* Applications Content */}
188
- <div className="application-menu__content">
190
+ <div className="launchpad__content">
189
191
  {searchTerm && (
190
- <div className="application-menu__search-results">
192
+ <div className="launchpad__search-results">
191
193
  <h3>Search Results ({filteredApps.length})</h3>
192
- <div className={`application-menu__apps-${viewMode}`}>
194
+ <div className={`launchpad__apps-${viewMode}`}>
193
195
  {filteredApps.map(app => (
194
196
  <div
195
197
  key={app.id}
196
- className={`application-menu__app--${viewMode}`}
198
+ className={`launchpad__app--${viewMode}`}
197
199
  onClick={() => handleLaunchApp(app)}
198
200
  title={app.description}
199
201
  >
@@ -213,15 +215,15 @@ export const ApplicationMenu = ({ isOpen, onClose }) => {
213
215
  )}
214
216
 
215
217
  {!searchTerm && (
216
- <div className="application-menu__categories-content">
218
+ <div className="launchpad__categories-content">
217
219
  {Object.entries(groupedApps).map(([categoryName, categoryApps]) => (
218
- <div key={categoryName} className="application-menu__category-section">
220
+ <div key={categoryName} className="launchpad__category-section">
219
221
  <h3 className="category-title">{categoryName}</h3>
220
- <div className={`application-menu__apps-${viewMode}`}>
222
+ <div className={`launchpad__apps-${viewMode}`}>
221
223
  {categoryApps.map(app => (
222
224
  <div
223
225
  key={app.id}
224
- className={`application-menu__app--${viewMode}`}
226
+ className={`launchpad__app--${viewMode}`}
225
227
  onClick={() => handleLaunchApp(app)}
226
228
  title={app.description}
227
229
  >
@@ -243,7 +245,7 @@ export const ApplicationMenu = ({ isOpen, onClose }) => {
243
245
  )}
244
246
 
245
247
  {filteredApps.length === 0 && (
246
- <div className="application-menu__no-results">
248
+ <div className="launchpad__no-results">
247
249
  <div style={{ fontSize: '48px', marginBottom: '16px' }}>🔍</div>
248
250
  <h3>No applications found</h3>
249
251
  <p>Try adjusting your search or category filter.</p>
@@ -252,8 +254,7 @@ export const ApplicationMenu = ({ isOpen, onClose }) => {
252
254
  </div>
253
255
  </div>
254
256
  </div>
255
- </div>
256
257
  )
257
258
  }
258
259
 
259
- export default ApplicationMenu
260
+ export default LaunchPad
@@ -76,6 +76,8 @@ export const WindowProvider = ({ children, desktopSize }) => {
76
76
  // Layout functions
77
77
  cascadeWindows: () => windowManagerRef.current.cascadeWindows(),
78
78
  tileWindows: () => windowManagerRef.current.tileWindows(),
79
+ arrangeWindowsInColumns: () => windowManagerRef.current.arrangeWindowsInColumns(),
80
+ arrangeWindowsInRows: () => windowManagerRef.current.arrangeWindowsInRows(),
79
81
  centerWindow: (id) => windowManagerRef.current.centerWindow(id),
80
82
  centerAllWindows: () => windowManagerRef.current.centerAllWindows(),
81
83
 
@@ -366,6 +366,60 @@ export class WindowManager {
366
366
  this.notifyListeners()
367
367
  }
368
368
 
369
+ /**
370
+ * Arrange windows in columns
371
+ */
372
+ arrangeWindowsInColumns() {
373
+ const visibleWindows = this.getVisibleWindows().filter(w => !w.maximized)
374
+ if (visibleWindows.length === 0) return
375
+
376
+ const cols = visibleWindows.length
377
+ const windowWidth = Math.floor(this.desktopSize.width / cols)
378
+ const windowHeight = this.desktopSize.height
379
+ const padding = 5
380
+
381
+ visibleWindows.forEach((window, index) => {
382
+ window.position = {
383
+ x: index * windowWidth + padding,
384
+ y: padding
385
+ }
386
+ window.size = {
387
+ width: windowWidth - (padding * 2),
388
+ height: windowHeight - (padding * 2)
389
+ }
390
+ window.maximized = false
391
+ })
392
+
393
+ this.notifyListeners()
394
+ }
395
+
396
+ /**
397
+ * Arrange windows in rows
398
+ */
399
+ arrangeWindowsInRows() {
400
+ const visibleWindows = this.getVisibleWindows().filter(w => !w.maximized)
401
+ if (visibleWindows.length === 0) return
402
+
403
+ const rows = visibleWindows.length
404
+ const windowWidth = this.desktopSize.width
405
+ const windowHeight = Math.floor(this.desktopSize.height / rows)
406
+ const padding = 5
407
+
408
+ visibleWindows.forEach((window, index) => {
409
+ window.position = {
410
+ x: padding,
411
+ y: index * windowHeight + padding
412
+ }
413
+ window.size = {
414
+ width: windowWidth - (padding * 2),
415
+ height: windowHeight - (padding * 2)
416
+ }
417
+ window.maximized = false
418
+ })
419
+
420
+ this.notifyListeners()
421
+ }
422
+
369
423
  /**
370
424
  * Get statistics
371
425
  */
@@ -1,19 +1,19 @@
1
- /* Linux Desktop Theme (GNOME/Ubuntu inspired) */
1
+ /* GNOME Desktop Theme (Ubuntu/Fedora inspired) */
2
2
 
3
- .desktop--linux {
3
+ .desktop--gnome {
4
4
  background: linear-gradient(135deg, #2c001e 0%, #4c2c92 50%, #0e4b99 100%);
5
5
  font-family: 'Ubuntu', 'Cantarell', 'DejaVu Sans', sans-serif;
6
6
  }
7
7
 
8
- .desktop--linux .desktop__background {
9
- background-image:
8
+ .desktop--gnome .desktop__background {
9
+ background-image:
10
10
  radial-gradient(circle at 30% 70%, rgba(233, 84, 32, 0.2) 0%, transparent 50%),
11
11
  radial-gradient(circle at 70% 30%, rgba(119, 41, 83, 0.3) 0%, transparent 50%),
12
12
  radial-gradient(circle at 50% 50%, rgba(14, 75, 153, 0.2) 0%, transparent 50%);
13
13
  }
14
14
 
15
- /* Linux-style windows (GNOME) */
16
- .desktop--linux .window {
15
+ /* GNOME-style windows */
16
+ .desktop--gnome .window {
17
17
  border: 1px solid #2d2d2d;
18
18
  border-radius: 12px 12px 0 0;
19
19
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
@@ -21,7 +21,7 @@
21
21
  overflow: hidden;
22
22
  }
23
23
 
24
- .desktop--linux .window__header {
24
+ .desktop--gnome .window__header {
25
25
  background: linear-gradient(180deg, #f6f5f4 0%, #e6e5e4 100%);
26
26
  border-bottom: 1px solid #d0cfce;
27
27
  border-radius: 12px 12px 0 0;
@@ -29,13 +29,13 @@
29
29
  padding: 0 16px;
30
30
  }
31
31
 
32
- .desktop--linux .window__title {
32
+ .desktop--gnome .window__title {
33
33
  font-size: 14px;
34
34
  font-weight: 500;
35
35
  color: #2e3436;
36
36
  }
37
37
 
38
- .desktop--linux .window__control {
38
+ .desktop--gnome .window__control {
39
39
  width: 24px;
40
40
  height: 24px;
41
41
  border: none;
@@ -49,50 +49,59 @@
49
49
  margin-left: 8px;
50
50
  }
51
51
 
52
- .desktop--linux .window__control--minimize {
52
+ .desktop--gnome .window__control--minimize {
53
53
  background: #f6d32d;
54
54
  border: 1px solid #f5c211;
55
55
  }
56
56
 
57
- .desktop--linux .window__control--maximize {
57
+ .desktop--gnome .window__control--maximize {
58
58
  background: #33d17a;
59
59
  border: 1px solid #2ec27e;
60
60
  }
61
61
 
62
- .desktop--linux .window__control--close {
62
+ .desktop--gnome .window__control--close {
63
63
  background: #e01b24;
64
64
  border: 1px solid #c01c28;
65
65
  color: white;
66
66
  }
67
67
 
68
- .desktop--linux .window__control:hover {
68
+ .desktop--gnome .window__control:hover {
69
69
  transform: scale(1.1);
70
70
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
71
71
  }
72
72
 
73
- .desktop--linux .window__control--minimize::before {
73
+ .desktop--gnome .window__control--minimize::before {
74
74
  content: '−';
75
75
  font-weight: bold;
76
76
  }
77
77
 
78
- .desktop--linux .window__control--maximize::before {
78
+ .desktop--gnome .window__control--maximize::before {
79
79
  content: '□';
80
80
  font-weight: bold;
81
81
  }
82
82
 
83
- .desktop--linux .window__control--close::before {
83
+ .desktop--gnome .window__control--close::before {
84
84
  content: '×';
85
85
  font-weight: bold;
86
86
  }
87
87
 
88
- /* Linux taskbar styling (GNOME Activities) */
89
- .desktop--linux .desktop-taskbar {
88
+ /* GNOME taskbar styling (GNOME Activities) */
89
+ .desktop--gnome .desktop-taskbar {
90
90
  background: rgba(0, 0, 0, 0.85);
91
91
  backdrop-filter: blur(10px);
92
92
  border-top: 1px solid rgba(255, 255, 255, 0.1);
93
93
  }
94
94
 
95
- .desktop--linux .taskbar-button {
95
+ .desktop--gnome .desktop-taskbar .icon {
96
+ color: rgba(255, 255, 255, 0.9);
97
+ }
98
+
99
+ .desktop--gnome .desktop-taskbar .icon:hover {
100
+ color: #ffffff;
101
+ background-color: rgba(255, 255, 255, 0.12);
102
+ }
103
+
104
+ .desktop--gnome .taskbar-button {
96
105
  background: rgba(255, 255, 255, 0.08);
97
106
  border: 1px solid rgba(255, 255, 255, 0.15);
98
107
  border-radius: 8px;
@@ -103,19 +112,19 @@
103
112
  transition: all 0.3s ease;
104
113
  }
105
114
 
106
- .desktop--linux .taskbar-button:hover {
115
+ .desktop--gnome .taskbar-button:hover {
107
116
  background: rgba(255, 255, 255, 0.15);
108
117
  border-color: rgba(255, 255, 255, 0.25);
109
118
  transform: translateY(-1px);
110
119
  }
111
120
 
112
- .desktop--linux .taskbar-button--active {
121
+ .desktop--gnome .taskbar-button--active {
113
122
  background: rgba(53, 132, 228, 0.3);
114
123
  border-color: rgba(53, 132, 228, 0.5);
115
124
  }
116
125
 
117
126
  /* Linux Start Button Styling (GNOME Activities) */
118
- .desktop--linux .toggle-button {
127
+ .desktop--gnome .toggle-button {
119
128
  /* Reset selector styles and apply Linux button styling */
120
129
  position: relative;
121
130
  padding: 8px 16px;
@@ -141,26 +150,26 @@
141
150
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
142
151
  }
143
152
 
144
- .desktop--linux .toggle-button:hover:not(.toggle-button--disabled) {
153
+ .desktop--gnome .toggle-button:hover:not(.toggle-button--disabled) {
145
154
  background: linear-gradient(180deg, rgba(53, 132, 228, 1) 0%, rgba(26, 95, 180, 1) 100%);
146
155
  border-color: rgba(255, 255, 255, 0.25);
147
156
  transform: translateY(-1px);
148
157
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25);
149
158
  }
150
159
 
151
- .desktop--linux .toggle-button.selected {
160
+ .desktop--gnome .toggle-button.selected {
152
161
  background: linear-gradient(180deg, rgba(26, 95, 180, 0.9) 0%, rgba(15, 70, 140, 0.9) 100%);
153
162
  color: #ffffff;
154
163
  border-color: rgba(255, 255, 255, 0.3);
155
164
  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(53, 132, 228, 0.4);
156
165
  }
157
166
 
158
- .desktop--linux .toggle-button:focus {
167
+ .desktop--gnome .toggle-button:focus {
159
168
  outline: 2px solid rgba(53, 132, 228, 0.6);
160
169
  outline-offset: 2px;
161
170
  }
162
171
 
163
- .desktop--linux .toggle-button--disabled {
172
+ .desktop--gnome .toggle-button--disabled {
164
173
  background: rgba(255, 255, 255, 0.1) !important;
165
174
  color: rgba(255, 255, 255, 0.4) !important;
166
175
  border-color: rgba(255, 255, 255, 0.05) !important;
@@ -169,8 +178,8 @@
169
178
  box-shadow: none;
170
179
  }
171
180
 
172
- /* Linux Application Menu (Activities Overview) */
173
- .desktop--linux .application-menu {
181
+ /* Linux LaunchPad (Activities Overview) */
182
+ .desktop--gnome .launchpad {
174
183
  background: rgba(36, 31, 49, 0.95);
175
184
  backdrop-filter: blur(20px);
176
185
  border: 1px solid rgba(255, 255, 255, 0.1);
@@ -179,29 +188,29 @@
179
188
  color: #ffffff;
180
189
  }
181
190
 
182
- .desktop--linux .application-menu__header {
191
+ .desktop--gnome .launchpad__header {
183
192
  background: linear-gradient(180deg, rgba(46, 52, 54, 0.8) 0%, rgba(36, 31, 49, 0.8) 100%);
184
193
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
185
194
  color: #ffffff;
186
195
  }
187
196
 
188
- .desktop--linux .application-menu__header h2 {
197
+ .desktop--gnome .launchpad__header h2 {
189
198
  color: #ffffff;
190
199
  font-family: 'Ubuntu', sans-serif;
191
200
  font-weight: 500;
192
201
  }
193
202
 
194
- .desktop--linux .application-menu__close {
203
+ .desktop--gnome .launchpad__close {
195
204
  color: #ffffff;
196
205
  background: rgba(255, 255, 255, 0.1);
197
206
  border-radius: 6px;
198
207
  }
199
208
 
200
- .desktop--linux .application-menu__close:hover {
209
+ .desktop--gnome .launchpad__close:hover {
201
210
  background: rgba(224, 27, 36, 0.8);
202
211
  }
203
212
 
204
- .desktop--linux .application-menu__search-input {
213
+ .desktop--gnome .launchpad__search-input {
205
214
  background: rgba(255, 255, 255, 0.1);
206
215
  border: 2px solid rgba(255, 255, 255, 0.2);
207
216
  border-radius: 8px;
@@ -209,17 +218,26 @@
209
218
  font-family: 'Ubuntu', sans-serif;
210
219
  }
211
220
 
212
- .desktop--linux .application-menu__search-input::placeholder {
221
+ .desktop--gnome .launchpad__search-input::placeholder {
213
222
  color: rgba(255, 255, 255, 0.6);
214
223
  }
215
224
 
216
- .desktop--linux .application-menu__search-input:focus {
225
+ .desktop--gnome .launchpad__search-input:focus {
217
226
  border-color: rgba(53, 132, 228, 0.8);
218
227
  background: rgba(255, 255, 255, 0.15);
219
228
  outline: none;
220
229
  }
221
230
 
222
- .desktop--linux .application-menu__category {
231
+ .desktop--gnome .launchpad__sidebar {
232
+ background: rgba(46, 52, 54, 0.8);
233
+ border-right: 1px solid rgba(255, 255, 255, 0.1);
234
+ }
235
+
236
+ .desktop--gnome .launchpad__categories {
237
+ background: rgba(46, 52, 54, 0.8);
238
+ }
239
+
240
+ .desktop--gnome .launchpad__category {
223
241
  background: rgba(255, 255, 255, 0.1);
224
242
  border: 1px solid rgba(255, 255, 255, 0.2);
225
243
  border-radius: 20px;
@@ -229,59 +247,59 @@
229
247
  font-weight: 400;
230
248
  }
231
249
 
232
- .desktop--linux .application-menu__category:hover {
250
+ .desktop--gnome .launchpad__category:hover {
233
251
  background: rgba(255, 255, 255, 0.2);
234
252
  border-color: rgba(255, 255, 255, 0.3);
235
253
  }
236
254
 
237
- .desktop--linux .application-menu__category.active {
255
+ .desktop--gnome .launchpad__category.active {
238
256
  background: rgba(53, 132, 228, 0.8);
239
257
  border-color: rgba(53, 132, 228, 1);
240
258
  color: #ffffff;
241
259
  }
242
260
 
243
- .desktop--linux .application-menu__app {
261
+ .desktop--gnome .launchpad__app {
244
262
  background: rgba(255, 255, 255, 0.05);
245
263
  border: 1px solid transparent;
246
264
  border-radius: 12px;
247
265
  transition: all 0.2s ease;
248
266
  }
249
267
 
250
- .desktop--linux .application-menu__app:hover {
268
+ .desktop--gnome .launchpad__app:hover {
251
269
  background: rgba(255, 255, 255, 0.15);
252
270
  border-color: rgba(255, 255, 255, 0.2);
253
271
  transform: translateY(-2px);
254
272
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
255
273
  }
256
274
 
257
- .desktop--linux .app-name {
275
+ .desktop--gnome .app-name {
258
276
  font-family: 'Ubuntu', sans-serif;
259
277
  font-size: 13px;
260
278
  font-weight: 400;
261
279
  color: #ffffff;
262
280
  }
263
281
 
264
- .desktop--linux .category-title {
282
+ .desktop--gnome .category-title {
265
283
  color: #ffffff;
266
284
  font-family: 'Ubuntu', sans-serif;
267
285
  font-weight: 500;
268
286
  }
269
287
 
270
288
  /* Linux scrollbar */
271
- .desktop--linux .application-menu__content::-webkit-scrollbar {
289
+ .desktop--gnome .launchpad__content::-webkit-scrollbar {
272
290
  width: 8px;
273
291
  }
274
292
 
275
- .desktop--linux .application-menu__content::-webkit-scrollbar-track {
293
+ .desktop--gnome .launchpad__content::-webkit-scrollbar-track {
276
294
  background: rgba(255, 255, 255, 0.1);
277
295
  border-radius: 4px;
278
296
  }
279
297
 
280
- .desktop--linux .application-menu__content::-webkit-scrollbar-thumb {
298
+ .desktop--gnome .launchpad__content::-webkit-scrollbar-thumb {
281
299
  background: rgba(255, 255, 255, 0.3);
282
300
  border-radius: 4px;
283
301
  }
284
302
 
285
- .desktop--linux .application-menu__content::-webkit-scrollbar-thumb:hover {
303
+ .desktop--gnome .launchpad__content::-webkit-scrollbar-thumb:hover {
286
304
  background: rgba(255, 255, 255, 0.5);
287
305
  }