ywana-core8 0.2.6 → 0.2.8

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.
@@ -6,6 +6,7 @@ export class WindowManager {
6
6
  this.windows = new Map()
7
7
  this.activeWindowId = null
8
8
  this.nextZIndex = 1000
9
+ this.maxZIndex = 9999 // Maximum z-index for windows (ApplicationMenu is 10000)
9
10
  this.listeners = new Set()
10
11
  this.desktopSize = desktopSize
11
12
  }
@@ -77,7 +78,7 @@ export class WindowManager {
77
78
  size: size,
78
79
  minimized: false,
79
80
  maximized: false,
80
- zIndex: this.nextZIndex++,
81
+ zIndex: Math.min(this.nextZIndex++, this.maxZIndex),
81
82
  ...config
82
83
  }
83
84
 
@@ -101,9 +102,9 @@ export class WindowManager {
101
102
  let x = baseX + (existingWindows.length * offsetStep)
102
103
  let y = baseY + (existingWindows.length * offsetStep)
103
104
 
104
- // Check if it goes out of desktop bounds
105
+ // Check if it goes out of workspace bounds
105
106
  const maxX = this.desktopSize.width - windowSize.width - 20
106
- const maxY = this.desktopSize.height - windowSize.height - 100 // Space for taskbar
107
+ const maxY = this.desktopSize.height - windowSize.height - 20 // Workspace boundaries
107
108
 
108
109
  // If out of bounds, restart cascade
109
110
  if (x > maxX || y > maxY) {
@@ -152,8 +153,11 @@ export class WindowManager {
152
153
  window.minimized = false
153
154
  }
154
155
 
155
- // Bring to front
156
- window.zIndex = this.nextZIndex++
156
+ // Bring to front (but never above ApplicationMenu)
157
+ if (this.nextZIndex >= this.maxZIndex) {
158
+ this.resetZIndexes()
159
+ }
160
+ window.zIndex = Math.min(this.nextZIndex++, this.maxZIndex)
157
161
  this.activeWindowId = windowId
158
162
 
159
163
  this.notifyListeners()
@@ -221,7 +225,7 @@ export class WindowManager {
221
225
  const minWidth = 200
222
226
  const minHeight = 150
223
227
  const maxWidth = this.desktopSize.width
224
- const maxHeight = this.desktopSize.height - 50 // Space for taskbar
228
+ const maxHeight = this.desktopSize.height // Workspace boundaries
225
229
 
226
230
  const constrainedSize = {
227
231
  width: Math.max(minWidth, Math.min(size.width || window.size.width, maxWidth)),
@@ -264,7 +268,7 @@ export class WindowManager {
264
268
  if (window.maximized) return // Maximized windows don't need repositioning
265
269
 
266
270
  const maxX = this.desktopSize.width - window.size.width
267
- const maxY = this.desktopSize.height - window.size.height - 50 // Space for taskbar
271
+ const maxY = this.desktopSize.height - window.size.height // Workspace boundaries
268
272
 
269
273
  // Adjust position if out of bounds
270
274
  if (window.position.x > maxX) {
@@ -306,7 +310,7 @@ export class WindowManager {
306
310
  const cols = Math.ceil(Math.sqrt(visibleWindows.length))
307
311
  const rows = Math.ceil(visibleWindows.length / cols)
308
312
  const windowWidth = Math.floor(this.desktopSize.width / cols)
309
- const windowHeight = Math.floor((this.desktopSize.height - 80) / rows) // Space for taskbar
313
+ const windowHeight = Math.floor(this.desktopSize.height / rows) // Workspace boundaries
310
314
 
311
315
  visibleWindows.forEach((window, index) => {
312
316
  const col = index % cols
@@ -335,7 +339,7 @@ export class WindowManager {
335
339
 
336
340
  window.position = {
337
341
  x: (this.desktopSize.width - window.size.width) / 2,
338
- y: (this.desktopSize.height - window.size.height - 50) / 2 // Space for taskbar
342
+ y: (this.desktopSize.height - window.size.height) / 2 // Workspace center
339
343
  }
340
344
 
341
345
  this.notifyListeners()
@@ -351,7 +355,7 @@ export class WindowManager {
351
355
 
352
356
  visibleWindows.forEach((window, index) => {
353
357
  const baseX = (this.desktopSize.width - window.size.width) / 2
354
- const baseY = (this.desktopSize.height - window.size.height - 50) / 2
358
+ const baseY = (this.desktopSize.height - window.size.height) / 2
355
359
 
356
360
  window.position = {
357
361
  x: baseX + (index * offsetStep),
@@ -375,4 +379,22 @@ export class WindowManager {
375
379
  desktopSize: this.desktopSize
376
380
  }
377
381
  }
382
+
383
+ /**
384
+ * Reset z-indexes when they reach the maximum
385
+ */
386
+ resetZIndexes() {
387
+ const windows = Array.from(this.windows.values())
388
+ // Sort windows by current z-index
389
+ windows.sort((a, b) => a.zIndex - b.zIndex)
390
+
391
+ // Reassign z-indexes starting from 1000
392
+ this.nextZIndex = 1000
393
+ windows.forEach((window, index) => {
394
+ window.zIndex = this.nextZIndex + index
395
+ })
396
+ this.nextZIndex = 1000 + windows.length
397
+
398
+ this.notifyListeners()
399
+ }
378
400
  }
@@ -114,6 +114,61 @@
114
114
  border-color: rgba(53, 132, 228, 0.5);
115
115
  }
116
116
 
117
+ /* Linux Start Button Styling (GNOME Activities) */
118
+ .desktop--linux .toggle-button {
119
+ /* Reset selector styles and apply Linux button styling */
120
+ position: relative;
121
+ padding: 8px 16px;
122
+ display: flex;
123
+ align-items: center;
124
+ justify-content: center;
125
+ gap: 8px;
126
+ overflow: hidden;
127
+ border: 1px solid rgba(255, 255, 255, 0.15);
128
+ outline: none;
129
+ transition: all 0.3s ease;
130
+ color: #ffffff;
131
+ background: linear-gradient(180deg, rgba(53, 132, 228, 0.9) 0%, rgba(26, 95, 180, 0.9) 100%);
132
+ min-height: 40px;
133
+ border-radius: 8px;
134
+ cursor: pointer;
135
+ font-family: 'Ubuntu', 'Cantarell', sans-serif;
136
+ font-weight: 500;
137
+ text-decoration: none;
138
+ white-space: nowrap;
139
+ list-style: none;
140
+ margin: 0;
141
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
142
+ }
143
+
144
+ .desktop--linux .toggle-button:hover:not(.toggle-button--disabled) {
145
+ background: linear-gradient(180deg, rgba(53, 132, 228, 1) 0%, rgba(26, 95, 180, 1) 100%);
146
+ border-color: rgba(255, 255, 255, 0.25);
147
+ transform: translateY(-1px);
148
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25);
149
+ }
150
+
151
+ .desktop--linux .toggle-button.selected {
152
+ background: linear-gradient(180deg, rgba(26, 95, 180, 0.9) 0%, rgba(15, 70, 140, 0.9) 100%);
153
+ color: #ffffff;
154
+ border-color: rgba(255, 255, 255, 0.3);
155
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(53, 132, 228, 0.4);
156
+ }
157
+
158
+ .desktop--linux .toggle-button:focus {
159
+ outline: 2px solid rgba(53, 132, 228, 0.6);
160
+ outline-offset: 2px;
161
+ }
162
+
163
+ .desktop--linux .toggle-button--disabled {
164
+ background: rgba(255, 255, 255, 0.1) !important;
165
+ color: rgba(255, 255, 255, 0.4) !important;
166
+ border-color: rgba(255, 255, 255, 0.05) !important;
167
+ cursor: not-allowed;
168
+ opacity: 1;
169
+ box-shadow: none;
170
+ }
171
+
117
172
  /* Linux Application Menu (Activities Overview) */
118
173
  .desktop--linux .application-menu {
119
174
  background: rgba(36, 31, 49, 0.95);
@@ -137,6 +137,63 @@
137
137
  color: #007aff;
138
138
  }
139
139
 
140
+ /* macOS Start Button Styling (Dock-inspired) */
141
+ .desktop--macos .toggle-button {
142
+ /* Reset selector styles and apply macOS button styling */
143
+ position: relative;
144
+ padding: 8px 16px;
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ gap: 8px;
149
+ overflow: hidden;
150
+ border: 1px solid rgba(0, 0, 0, 0.1);
151
+ outline: none;
152
+ transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
153
+ color: #1d1d1f;
154
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.9) 0%, rgba(248, 248, 248, 0.9) 100%);
155
+ min-height: 40px;
156
+ border-radius: 12px;
157
+ cursor: pointer;
158
+ font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', sans-serif;
159
+ font-weight: 500;
160
+ text-decoration: none;
161
+ white-space: nowrap;
162
+ list-style: none;
163
+ margin: 0;
164
+ backdrop-filter: blur(20px);
165
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
166
+ }
167
+
168
+ .desktop--macos .toggle-button:hover:not(.toggle-button--disabled) {
169
+ background: linear-gradient(180deg, rgba(255, 255, 255, 0.95) 0%, rgba(245, 245, 245, 0.95) 100%);
170
+ border-color: rgba(0, 0, 0, 0.15);
171
+ transform: translateY(-1px);
172
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
173
+ }
174
+
175
+ .desktop--macos .toggle-button.selected {
176
+ background: linear-gradient(180deg, rgba(0, 122, 255, 0.9) 0%, rgba(0, 100, 210, 0.9) 100%);
177
+ color: white;
178
+ border-color: rgba(0, 122, 255, 0.3);
179
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2), 0 2px 8px rgba(0, 122, 255, 0.3);
180
+ }
181
+
182
+ .desktop--macos .toggle-button:focus {
183
+ outline: 2px solid rgba(0, 122, 255, 0.5);
184
+ outline-offset: 2px;
185
+ }
186
+
187
+ .desktop--macos .toggle-button--disabled {
188
+ background: rgba(248, 248, 248, 0.5) !important;
189
+ color: rgba(60, 60, 67, 0.3) !important;
190
+ border-color: rgba(0, 0, 0, 0.05) !important;
191
+ cursor: not-allowed;
192
+ opacity: 1;
193
+ box-shadow: none;
194
+ backdrop-filter: none;
195
+ }
196
+
140
197
  /* macOS Application Menu (Launchpad-inspired) */
141
198
  .desktop--macos .application-menu {
142
199
  background: rgba(255, 255, 255, 0.9);
@@ -104,6 +104,60 @@
104
104
  border-color: rgba(255, 255, 255, 0.4);
105
105
  }
106
106
 
107
+ /* Windows Start Button Styling */
108
+ .desktop--windows .toggle-button {
109
+ /* Reset selector styles and apply Windows button styling */
110
+ position: relative;
111
+ padding: 8px 16px;
112
+ display: flex;
113
+ align-items: center;
114
+ justify-content: center;
115
+ gap: 8px;
116
+ overflow: hidden;
117
+ border: 1px solid rgba(255, 255, 255, 0.2);
118
+ outline: none;
119
+ transition: all 0.2s ease;
120
+ color: white;
121
+ background: linear-gradient(180deg, #0078d4 0%, #106ebe 100%);
122
+ min-height: 40px;
123
+ border-radius: 4px;
124
+ cursor: pointer;
125
+ font-family: 'Segoe UI', sans-serif;
126
+ font-weight: 500;
127
+ text-decoration: none;
128
+ white-space: nowrap;
129
+ list-style: none;
130
+ margin: 0;
131
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
132
+ }
133
+
134
+ .desktop--windows .toggle-button:hover:not(.toggle-button--disabled) {
135
+ background: linear-gradient(180deg, #106ebe 0%, #005a9e 100%);
136
+ border-color: rgba(255, 255, 255, 0.3);
137
+ transform: none;
138
+ }
139
+
140
+ .desktop--windows .toggle-button.selected {
141
+ background: linear-gradient(180deg, #005a9e 0%, #004578 100%);
142
+ color: white;
143
+ border-color: rgba(255, 255, 255, 0.4);
144
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
145
+ }
146
+
147
+ .desktop--windows .toggle-button:focus {
148
+ outline: 2px solid rgba(255, 255, 255, 0.5);
149
+ outline-offset: 2px;
150
+ }
151
+
152
+ .desktop--windows .toggle-button--disabled {
153
+ background: rgba(200, 200, 200, 0.3) !important;
154
+ color: rgba(100, 100, 100, 0.5) !important;
155
+ border-color: rgba(255, 255, 255, 0.1) !important;
156
+ cursor: not-allowed;
157
+ opacity: 1;
158
+ box-shadow: none;
159
+ }
160
+
107
161
  /* Windows Application Menu */
108
162
  .desktop--windows .application-menu {
109
163
  background: rgba(248, 248, 248, 0.95);
@@ -24,8 +24,38 @@
24
24
  width: 100%;
25
25
  height: 100%;
26
26
  z-index: 1;
27
+ display: flex;
28
+ flex-direction: column;
27
29
  }
28
30
 
31
+ /* Workspace container - takes available space above taskbar */
32
+ .desktop__workspace-container {
33
+ position: relative;
34
+ flex: 1;
35
+ overflow: hidden;
36
+ z-index: 1;
37
+ }
38
+
39
+ /* Workspace - container for windows with boundaries */
40
+ .workspace {
41
+ position: relative;
42
+ width: 100%;
43
+ height: 100%;
44
+ overflow: hidden;
45
+ }
46
+
47
+ .desktop-taskbar {
48
+
49
+ & .icon {
50
+ color: white;
51
+ & :hover {
52
+ color: #fff;
53
+ background-color: rgba(255, 255, 255, 0.1);
54
+ }
55
+ }
56
+ }
57
+
58
+
29
59
  /* Desktop variants */
30
60
  .desktop--dark {
31
61
  color: white;
@@ -3,6 +3,8 @@ import { WindowProvider, useWindows } from './WindowContext'
3
3
  import { Window } from './window'
4
4
  import { ApplicationMenu } from './ApplicationMenu'
5
5
  import { defaultAppManager } from './AppManager'
6
+ import { ToggleButton } from '../html/selector'
7
+ import { Icon } from '../html/icon'
6
8
  import './desktop.css'
7
9
  import './desktop-windows.css'
8
10
  import './desktop-linux.css'
@@ -57,17 +59,17 @@ export const AppProvider = ({ children, appManager = defaultAppManager }) => {
57
59
  /**
58
60
  * Desktop layout component - manages overall desktop structure and sizing
59
61
  */
60
- const DesktopLayout = ({ children, className = '', theme = 'windows', ...props }) => {
62
+ const DesktopLayout = ({ children, className = '', theme = 'windows', workspaceRef, ...props }) => {
61
63
  const desktopRef = useRef(null)
62
64
  const { windowManager } = useWindows()
63
65
 
64
- // Measure desktop size on mount and resize
66
+ // Measure workspace size on mount and resize
65
67
  useEffect(() => {
66
- let currentSize = { width: 1200, height: 800 }
68
+ let currentSize = { width: 1200, height: 750 } // Default workspace size (desktop - taskbar)
67
69
 
68
- const measureDesktop = () => {
69
- if (desktopRef.current) {
70
- const rect = desktopRef.current.getBoundingClientRect()
70
+ const measureWorkspace = () => {
71
+ if (workspaceRef.current) {
72
+ const rect = workspaceRef.current.getBoundingClientRect()
71
73
  const newSize = {
72
74
  width: rect.width,
73
75
  height: rect.height
@@ -82,12 +84,12 @@ const DesktopLayout = ({ children, className = '', theme = 'windows', ...props }
82
84
  }
83
85
 
84
86
  // Initial measurement
85
- measureDesktop()
87
+ measureWorkspace()
86
88
 
87
89
  // Listen for resize
88
- const resizeObserver = new ResizeObserver(measureDesktop)
89
- if (desktopRef.current) {
90
- resizeObserver.observe(desktopRef.current)
90
+ const resizeObserver = new ResizeObserver(measureWorkspace)
91
+ if (workspaceRef.current) {
92
+ resizeObserver.observe(workspaceRef.current)
91
93
  }
92
94
 
93
95
  return () => {
@@ -124,13 +126,13 @@ const DesktopLayout = ({ children, className = '', theme = 'windows', ...props }
124
126
  }
125
127
 
126
128
  /**
127
- * Workspace - renders windows only
129
+ * Workspace - container for windows with proper boundaries
128
130
  */
129
131
  export const Workspace = ({ children }) => {
130
132
  const { windows } = useWindows()
131
133
 
132
134
  return (
133
- <>
135
+ <div className="workspace">
134
136
  {children}
135
137
 
136
138
  {/* Render windows using Window component */}
@@ -148,7 +150,7 @@ export const Workspace = ({ children }) => {
148
150
  </div>
149
151
  </Window>
150
152
  ))}
151
- </>
153
+ </div>
152
154
  )
153
155
  }
154
156
 
@@ -156,6 +158,11 @@ export const Workspace = ({ children }) => {
156
158
  * DesktopTaskbar - handles window creation and management
157
159
  */
158
160
  export const DesktopTaskbar = () => {
161
+ console.log('DesktopTaskbar render')
162
+
163
+ const windowsContext = useWindows()
164
+ console.log('useWindows result:', windowsContext)
165
+
159
166
  const {
160
167
  createWindow,
161
168
  windows,
@@ -163,12 +170,20 @@ export const DesktopTaskbar = () => {
163
170
  activeWindowId,
164
171
  focusWindow,
165
172
  minimizeWindow,
166
- closeWindow
167
- } = useWindows()
173
+ closeWindow,
174
+ cascadeWindows,
175
+ tileWindows,
176
+ centerAllWindows,
177
+ clearAllWindows
178
+ } = windowsContext
168
179
 
169
- const { open: openApplicationMenu } = useApplicationMenu()
180
+ const { isOpen, toggle } = useApplicationMenu()
181
+ console.log('DesktopTaskbar - isOpen:', isOpen, 'toggle:', toggle)
170
182
 
171
183
  const handleCreateWindow = () => {
184
+ console.log('handleCreateWindow called!')
185
+ console.log('createWindow function:', createWindow)
186
+
172
187
  const windowTypes = [
173
188
  { title: 'File Explorer', icon: '📁', size: { width: 600, height: 400 } },
174
189
  { title: 'Text Editor', icon: '📝', size: { width: 500, height: 350 } },
@@ -178,6 +193,7 @@ export const DesktopTaskbar = () => {
178
193
  ]
179
194
 
180
195
  const randomType = windowTypes[Math.floor(Math.random() * windowTypes.length)]
196
+ console.log('Creating window:', randomType)
181
197
 
182
198
  createWindow({
183
199
  title: randomType.title,
@@ -226,11 +242,7 @@ export const DesktopTaskbar = () => {
226
242
  }
227
243
 
228
244
  return (
229
- <div style={{
230
- position: 'absolute',
231
- bottom: 0,
232
- left: 0,
233
- right: 0,
245
+ <div className='desktop-taskbar' style={{
234
246
  height: '50px',
235
247
  background: 'rgba(0,0,0,0.8)',
236
248
  display: 'flex',
@@ -239,45 +251,16 @@ export const DesktopTaskbar = () => {
239
251
  gap: '8px'
240
252
  }}>
241
253
  {/* Start button */}
242
- <button
243
- onClick={openApplicationMenu}
244
- style={{
245
- padding: '8px 16px',
246
- background: '#1976d2',
247
- color: 'white',
248
- border: 'none',
249
- borderRadius: '4px',
250
- cursor: 'pointer',
251
- fontSize: '14px',
252
- fontWeight: 'bold',
253
- flexShrink: 0,
254
- display: 'flex',
255
- alignItems: 'center',
256
- gap: '6px'
254
+ <ToggleButton
255
+ checked={isOpen}
256
+ onToggle={() => {
257
+ console.log('Start button toggled, current state:', isOpen)
258
+ toggle()
257
259
  }}
258
- title="Open application menu"
260
+ icon="start"
261
+ label="Start"
259
262
  >
260
- <span style={{ fontSize: '16px' }}>🚀</span>
261
- Start
262
- </button>
263
-
264
- {/* Create window button (for testing) */}
265
- <button
266
- onClick={handleCreateWindow}
267
- style={{
268
- padding: '8px 12px',
269
- background: '#666',
270
- color: 'white',
271
- border: 'none',
272
- borderRadius: '4px',
273
- cursor: 'pointer',
274
- fontSize: '12px',
275
- flexShrink: 0
276
- }}
277
- title="Create random window (for testing)"
278
- >
279
- +
280
- </button>
263
+ </ToggleButton>
281
264
 
282
265
  {/* Separator */}
283
266
  <div style={{
@@ -356,6 +339,49 @@ export const DesktopTaskbar = () => {
356
339
  ))}
357
340
  </div>
358
341
 
342
+ {/* Window Layout Controls */}
343
+ <div style={{
344
+ display: 'flex',
345
+ gap: '4px',
346
+ alignItems: 'center',
347
+ marginLeft: 'auto',
348
+ marginRight: '8px'
349
+ }}>
350
+
351
+ <Icon clickable size="small"
352
+ action={() => {
353
+ console.log('Cascade windows clicked!')
354
+ cascadeWindows()
355
+ }}
356
+ icon="view_stream"
357
+ label="Cascade"
358
+ />
359
+ <Icon clickable size="small"
360
+ action={() => {
361
+ console.log('Tile windows clicked!')
362
+ tileWindows()
363
+ }}
364
+ icon="view_module"
365
+ label="Tile"
366
+ />
367
+ <Icon clickable size="small"
368
+ action={() => {
369
+ console.log('Center all windows clicked!')
370
+ centerAllWindows()
371
+ }}
372
+ icon="center_focus_strong"
373
+ label="Center"
374
+ />
375
+ <Icon clickable size="small"
376
+ action={() => {
377
+ console.log('Clear all windows clicked!')
378
+ clearAllWindows()
379
+ }}
380
+ icon="clear_all"
381
+ label="Clear"
382
+ />
383
+ </div>
384
+
359
385
  {/* Debug info */}
360
386
  <div style={{
361
387
  color: 'rgba(255,255,255,0.7)',
@@ -375,18 +401,26 @@ export const DesktopTaskbar = () => {
375
401
  */
376
402
  const DesktopInternal = ({ desktopSize, children, ...props }) => {
377
403
  const { isOpen, close } = useApplicationMenu()
404
+ const workspaceRef = useRef(null)
378
405
 
379
406
  return (
380
407
  <WindowProvider desktopSize={desktopSize}>
381
- <DesktopLayout {...props}>
382
- <Workspace>
383
- {children}
384
- </Workspace>
408
+ <DesktopLayout workspaceRef={workspaceRef} {...props}>
409
+ {/* Workspace container - has its own boundaries */}
410
+ <div ref={workspaceRef} className="desktop__workspace-container">
411
+ <Workspace>
412
+ {children}
413
+ </Workspace>
414
+
415
+ {/* ApplicationMenu positioned over workspace */}
416
+ <ApplicationMenu
417
+ isOpen={isOpen}
418
+ onClose={close}
419
+ />
420
+ </div>
421
+
422
+ {/* Taskbar at bottom - separate from workspace */}
385
423
  <DesktopTaskbar />
386
- <ApplicationMenu
387
- isOpen={isOpen}
388
- onClose={close}
389
- />
390
424
  </DesktopLayout>
391
425
  </WindowProvider>
392
426
  )