@signal9/era-ui 1.16.0 → 1.17.0
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/dist/os/window.svelte +26 -16
- package/dist/os/wm.svelte.d.ts +6 -2
- package/dist/os/wm.svelte.js +23 -1
- package/package.json +1 -1
package/dist/os/window.svelte
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import * as Pane from '../ui/pane';
|
|
3
3
|
import { Button } from '../ui/button';
|
|
4
4
|
import Minus from '@lucide/svelte/icons/minus';
|
|
5
|
+
import Square from '@lucide/svelte/icons/square';
|
|
5
6
|
import X from '@lucide/svelte/icons/x';
|
|
6
7
|
import { cn } from '../utils/index.js';
|
|
7
8
|
import { getWindowManager, type AppWindow } from './wm.svelte.js';
|
|
@@ -11,14 +12,14 @@
|
|
|
11
12
|
const wm = getWindowManager();
|
|
12
13
|
const focused = $derived(wm.focusedId === win.id);
|
|
13
14
|
|
|
14
|
-
// The window's single control
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
// keeps the
|
|
18
|
-
// Like a native click,
|
|
19
|
-
//
|
|
20
|
-
// the button fires it,
|
|
21
|
-
let
|
|
15
|
+
// The window's single control: left-click CLOSES, right-click MINIMIZES (the
|
|
16
|
+
// taskbar restores it), middle-click toggles MAXIMIZE — no dedicated buttons.
|
|
17
|
+
// While a non-primary button is held the glyph previews its action (minus /
|
|
18
|
+
// square); a left press keeps the ×, so holding never implies another action
|
|
19
|
+
// when the release will close. Like a native click, each action COMMITS on
|
|
20
|
+
// release, not press: the press only arms it (contextmenu / autoscroll are
|
|
21
|
+
// suppressed), pointerup over the button fires it, dragging off first cancels.
|
|
22
|
+
let armed = $state<'minimize' | 'maximize' | null>(null);
|
|
22
23
|
|
|
23
24
|
// z-index is set imperatively, NOT via a style="" prop: Pane positions itself
|
|
24
25
|
// by writing element.style.translate, and a whole-attribute style binding would
|
|
@@ -49,25 +50,34 @@
|
|
|
49
50
|
{/if}
|
|
50
51
|
<span class={cn('truncate text-body', focused ? 'text-bright' : 'text-muted')}>{win.title}</span
|
|
51
52
|
>
|
|
52
|
-
<!-- Close / minimize. data-pane-control so it clicks instead of
|
|
53
|
+
<!-- Close / minimize / maximize. data-pane-control so it clicks instead of
|
|
54
|
+
dragging the pane. -->
|
|
53
55
|
<div class="ml-auto shrink-0" data-pane-control>
|
|
54
56
|
<Button
|
|
55
57
|
icon
|
|
56
58
|
size="icon"
|
|
57
59
|
tone="destructive"
|
|
58
|
-
aria-label="Close (right-click
|
|
60
|
+
aria-label="Close (right-click minimizes, middle-click maximizes)"
|
|
59
61
|
onclick={() => wm.close(win.id)}
|
|
60
62
|
oncontextmenu={(e: MouseEvent) => e.preventDefault()}
|
|
61
|
-
onpointerdown={(e: PointerEvent) =>
|
|
63
|
+
onpointerdown={(e: PointerEvent) => {
|
|
64
|
+
// Cancelling the middle pointerdown suppresses its compatibility
|
|
65
|
+
// mouse events, so the browser's autoscroll never engages.
|
|
66
|
+
if (e.button === 1) e.preventDefault();
|
|
67
|
+
armed = e.button === 2 ? 'minimize' : e.button === 1 ? 'maximize' : null;
|
|
68
|
+
}}
|
|
62
69
|
onpointerup={(e: PointerEvent) => {
|
|
63
|
-
if (e.button === 2 &&
|
|
64
|
-
|
|
70
|
+
if (e.button === 2 && armed === 'minimize') wm.minimize(win.id);
|
|
71
|
+
else if (e.button === 1 && armed === 'maximize') wm.toggleMaximize(win.id);
|
|
72
|
+
armed = null;
|
|
65
73
|
}}
|
|
66
|
-
onpointerleave={() => (
|
|
67
|
-
onpointercancel={() => (
|
|
74
|
+
onpointerleave={() => (armed = null)}
|
|
75
|
+
onpointercancel={() => (armed = null)}
|
|
68
76
|
>
|
|
69
|
-
{#if
|
|
77
|
+
{#if armed === 'minimize'}
|
|
70
78
|
<Minus class="size-(--era-h-xs)" />
|
|
79
|
+
{:else if armed === 'maximize'}
|
|
80
|
+
<Square class="size-(--era-h-xs)" />
|
|
71
81
|
{:else}
|
|
72
82
|
<X class="size-(--era-h-xs)" />
|
|
73
83
|
{/if}
|
package/dist/os/wm.svelte.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Component } from 'svelte';
|
|
2
2
|
import type { IconProps } from '@lucide/svelte';
|
|
3
|
-
export type WindowState = 'normal' | 'minimized';
|
|
3
|
+
export type WindowState = 'normal' | 'minimized' | 'maximized';
|
|
4
4
|
/** Snap targets: the four edges (halves) or four corners (quadrants). */
|
|
5
5
|
export type SnapZone = 'left' | 'right' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
6
6
|
/** Props every app body receives — its own window record and the manager, so it
|
|
@@ -88,7 +88,11 @@ export declare class WindowManager {
|
|
|
88
88
|
minimize(id: string): void;
|
|
89
89
|
/** Taskbar behaviour: focus if not focused, else minimize; restore if minimized. */
|
|
90
90
|
toggleMinimize(id: string): void;
|
|
91
|
-
/**
|
|
91
|
+
/** Fill the desktop bounds (middle-click on the window's ×). Keeps the first
|
|
92
|
+
* pre-maximize/pre-snap snapshot so restore returns to the true origin. */
|
|
93
|
+
maximize(id: string): void;
|
|
94
|
+
toggleMaximize(id: string): void;
|
|
95
|
+
/** Un-maximize / un-snap back to the pre-change geometry (or just un-minimize). */
|
|
92
96
|
restore(id: string): void;
|
|
93
97
|
/** Half / quadrant tiling against the desktop bounds. */
|
|
94
98
|
snap(id: string, zone: SnapZone): void;
|
package/dist/os/wm.svelte.js
CHANGED
|
@@ -95,7 +95,29 @@ export class WindowManager {
|
|
|
95
95
|
else
|
|
96
96
|
this.focus(id);
|
|
97
97
|
}
|
|
98
|
-
/**
|
|
98
|
+
/** Fill the desktop bounds (middle-click on the window's ×). Keeps the first
|
|
99
|
+
* pre-maximize/pre-snap snapshot so restore returns to the true origin. */
|
|
100
|
+
maximize(id) {
|
|
101
|
+
const w = this.#find(id);
|
|
102
|
+
if (!w)
|
|
103
|
+
return;
|
|
104
|
+
if (!w.restore)
|
|
105
|
+
w.restore = { pos: { ...w.pos }, size: w.size ? { ...w.size } : null };
|
|
106
|
+
w.pos = { x: 0, y: 0 };
|
|
107
|
+
w.size = { width: this.bounds.width, height: this.bounds.height };
|
|
108
|
+
w.state = 'maximized';
|
|
109
|
+
this.focus(id);
|
|
110
|
+
}
|
|
111
|
+
toggleMaximize(id) {
|
|
112
|
+
const w = this.#find(id);
|
|
113
|
+
if (!w)
|
|
114
|
+
return;
|
|
115
|
+
if (w.state === 'maximized')
|
|
116
|
+
this.restore(id);
|
|
117
|
+
else
|
|
118
|
+
this.maximize(id);
|
|
119
|
+
}
|
|
120
|
+
/** Un-maximize / un-snap back to the pre-change geometry (or just un-minimize). */
|
|
99
121
|
restore(id) {
|
|
100
122
|
const w = this.#find(id);
|
|
101
123
|
if (!w)
|