ywana-core8 0.2.19 → 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,13 +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 }) => {
10
- console.log('ApplicationMenu render - isOpen:', isOpen, 'onClose:', onClose)
12
+ export const LaunchPad = ({ isOpen, onClose }) => {
11
13
  const appManager = useAppManager()
12
14
  const [searchTerm, setSearchTerm] = useState('')
13
15
  const [selectedCategory, setSelectedCategory] = useState('all')
@@ -110,34 +112,33 @@ export const ApplicationMenu = ({ isOpen, onClose }) => {
110
112
  if (!isOpen) return null
111
113
 
112
114
  return (
113
- <div className="application-menu-overlay" onClick={onClose}>
114
- <div className={`application-menu ${isCondensed ? 'application-menu--condensed' : ''}`} onClick={(e) => e.stopPropagation()}>
115
+ <div className={`launchpad ${isCondensed ? 'launchpad--condensed' : ''}`}>
115
116
  {/* Header */}
116
- <div className="application-menu__header">
117
- <div className="application-menu__header-controls">
117
+ <div className="launchpad__header">
118
+ <div className="launchpad__header-controls">
118
119
  <button
119
- className={`application-menu__view-toggle ${isCondensed ? 'active' : ''}`}
120
+ className={`launchpad__view-toggle ${isCondensed ? 'active' : ''}`}
120
121
  onClick={() => setIsCondensed(!isCondensed)}
121
122
  title={isCondensed ? "Normal view" : "Condensed view"}
122
123
  >
123
124
 
124
125
  </button>
125
126
  <button
126
- className={`application-menu__view-toggle ${viewMode === 'grid' ? 'active' : ''}`}
127
+ className={`launchpad__view-toggle ${viewMode === 'grid' ? 'active' : ''}`}
127
128
  onClick={() => setViewMode('grid')}
128
129
  title="Grid view"
129
130
  >
130
131
 
131
132
  </button>
132
133
  <button
133
- className={`application-menu__view-toggle ${viewMode === 'list' ? 'active' : ''}`}
134
+ className={`launchpad__view-toggle ${viewMode === 'list' ? 'active' : ''}`}
134
135
  onClick={() => setViewMode('list')}
135
136
  title="List view"
136
137
  >
137
138
 
138
139
  </button>
139
140
  <button
140
- className="application-menu__close"
141
+ className="launchpad__close"
141
142
  onClick={onClose}
142
143
  title="Close menu"
143
144
  >
@@ -147,24 +148,24 @@ export const ApplicationMenu = ({ isOpen, onClose }) => {
147
148
  </div>
148
149
 
149
150
  {/* Search */}
150
- <div className="application-menu__search">
151
+ <div className="launchpad__search">
151
152
  <input
152
153
  ref={searchInputRef}
153
154
  type="text"
154
155
  placeholder="Search applications..."
155
156
  value={searchTerm}
156
157
  onChange={(e) => setSearchTerm(e.target.value)}
157
- className="application-menu__search-input"
158
+ className="launchpad__search-input"
158
159
  />
159
160
  </div>
160
161
 
161
162
  {/* Main Grid Layout */}
162
- <div className="application-menu__main">
163
+ <div className="launchpad__main">
163
164
  {/* Categories Sidebar */}
164
- <div className="application-menu__sidebar">
165
- <div className="application-menu__categories">
165
+ <div className="launchpad__sidebar">
166
+ <div className="launchpad__categories">
166
167
  <button
167
- className={`application-menu__category ${selectedCategory === 'all' ? 'active' : ''}`}
168
+ className={`launchpad__category ${selectedCategory === 'all' ? 'active' : ''}`}
168
169
  onClick={() => handleCategorySelect('all')}
169
170
  title="All Apps"
170
171
  >
@@ -174,7 +175,7 @@ export const ApplicationMenu = ({ isOpen, onClose }) => {
174
175
  {categories.map(category => (
175
176
  <button
176
177
  key={category.id}
177
- className={`application-menu__category ${selectedCategory === category.id ? 'active' : ''}`}
178
+ className={`launchpad__category ${selectedCategory === category.id ? 'active' : ''}`}
178
179
  onClick={() => handleCategorySelect(category.id)}
179
180
  title={category.name}
180
181
  >
@@ -186,15 +187,15 @@ export const ApplicationMenu = ({ isOpen, onClose }) => {
186
187
  </div>
187
188
 
188
189
  {/* Applications Content */}
189
- <div className="application-menu__content">
190
+ <div className="launchpad__content">
190
191
  {searchTerm && (
191
- <div className="application-menu__search-results">
192
+ <div className="launchpad__search-results">
192
193
  <h3>Search Results ({filteredApps.length})</h3>
193
- <div className={`application-menu__apps-${viewMode}`}>
194
+ <div className={`launchpad__apps-${viewMode}`}>
194
195
  {filteredApps.map(app => (
195
196
  <div
196
197
  key={app.id}
197
- className={`application-menu__app--${viewMode}`}
198
+ className={`launchpad__app--${viewMode}`}
198
199
  onClick={() => handleLaunchApp(app)}
199
200
  title={app.description}
200
201
  >
@@ -214,15 +215,15 @@ export const ApplicationMenu = ({ isOpen, onClose }) => {
214
215
  )}
215
216
 
216
217
  {!searchTerm && (
217
- <div className="application-menu__categories-content">
218
+ <div className="launchpad__categories-content">
218
219
  {Object.entries(groupedApps).map(([categoryName, categoryApps]) => (
219
- <div key={categoryName} className="application-menu__category-section">
220
+ <div key={categoryName} className="launchpad__category-section">
220
221
  <h3 className="category-title">{categoryName}</h3>
221
- <div className={`application-menu__apps-${viewMode}`}>
222
+ <div className={`launchpad__apps-${viewMode}`}>
222
223
  {categoryApps.map(app => (
223
224
  <div
224
225
  key={app.id}
225
- className={`application-menu__app--${viewMode}`}
226
+ className={`launchpad__app--${viewMode}`}
226
227
  onClick={() => handleLaunchApp(app)}
227
228
  title={app.description}
228
229
  >
@@ -244,7 +245,7 @@ export const ApplicationMenu = ({ isOpen, onClose }) => {
244
245
  )}
245
246
 
246
247
  {filteredApps.length === 0 && (
247
- <div className="application-menu__no-results">
248
+ <div className="launchpad__no-results">
248
249
  <div style={{ fontSize: '48px', marginBottom: '16px' }}>🔍</div>
249
250
  <h3>No applications found</h3>
250
251
  <p>Try adjusting your search or category filter.</p>
@@ -253,8 +254,7 @@ export const ApplicationMenu = ({ isOpen, onClose }) => {
253
254
  </div>
254
255
  </div>
255
256
  </div>
256
- </div>
257
257
  )
258
258
  }
259
259
 
260
- 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
  }