@stacksjs/desktop 0.2.3

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/README.md ADDED
@@ -0,0 +1,563 @@
1
+ # @stacksjs/desktop
2
+
3
+ Native desktop application framework for stx powered by craft.
4
+
5
+ ## Overview
6
+
7
+ `@stacksjs/desktop` provides a TypeScript API for creating native desktop applications with stx. It uses [Craft](https://github.com/stacksjs/craft) to deliver lightweight, fast native apps using web technologies and native WebKit views.
8
+
9
+ ## Features
10
+
11
+ - 🪟 **Native Windows** - Create true native windows (not Electron)
12
+ - 🎯 **Tiny Binary** - Just 1.4MB vs 100MB+ for Electron
13
+ - ⚡ **Fast Startup** - <100ms startup time
14
+ - 🔧 **System Tray Apps** - Build menubar/system tray applications
15
+ - 🎨 **Rich Components** - 35 native UI components
16
+ - 🌍 **Cross-Platform** - macOS, Linux, Windows support
17
+ - 🔥 **Hot Reload** - Development mode with instant updates
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ bun add @stacksjs/desktop
23
+ ```
24
+
25
+ ## Requirements
26
+
27
+ - **craft** - For native window support (linked locally or via npm)
28
+ - **macOS** - WebKit framework (built-in)
29
+ - **Linux** - `libgtk-3-dev` and `libwebkit2gtk-4.0-dev`
30
+ - **Windows** - WebView2 Runtime
31
+
32
+ ### Setup craft Integration
33
+
34
+ For local development with craft:
35
+
36
+ ```bash
37
+ # Clone craft repository
38
+ cd /path/to/your/repos
39
+ git clone https://github.com/stacksjs/craft
40
+
41
+ # Build craft
42
+ cd craft
43
+ bun install
44
+ zig build
45
+
46
+ # Link ts-craft package
47
+ cd packages/typescript
48
+ bun link
49
+
50
+ # Link to desktop package
51
+ cd /path/to/stx/packages/desktop
52
+ bun link ts-craft
53
+ ```
54
+
55
+ ## Usage
56
+
57
+ ### Quick Start with stx Dev Server
58
+
59
+ The easiest way to test native windows:
60
+
61
+ ```bash
62
+ # Start dev server with native window
63
+ stx dev examples/homepage.stx --native
64
+ ```
65
+
66
+ This opens a menubar application. Look for "stx Development" in your macOS menubar and click it to show the window.
67
+
68
+ ### Basic Window with craft
69
+
70
+ ```typescript
71
+ import { createApp } from 'ts-craft'
72
+
73
+ const app = createApp({
74
+ url: 'http://localhost:3000',
75
+ window: {
76
+ title: 'My App',
77
+ width: 1200,
78
+ height: 800,
79
+ systemTray: true, // Required for craft
80
+ darkMode: true,
81
+ hotReload: true,
82
+ devTools: true,
83
+ },
84
+ })
85
+
86
+ await app.show()
87
+ console.log('Window created! Look for "My App" in your menubar')
88
+ ```
89
+
90
+ ### Display HTML Content
91
+
92
+ ```typescript
93
+ import { show } from 'ts-craft'
94
+
95
+ const html = `
96
+ <!DOCTYPE html>
97
+ <html>
98
+ <head>
99
+ <style>
100
+ body {
101
+ margin: 0;
102
+ display: flex;
103
+ justify-content: center;
104
+ align-items: center;
105
+ height: 100vh;
106
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
107
+ font-family: system-ui;
108
+ }
109
+ h1 {
110
+ color: white;
111
+ font-size: 48px;
112
+ }
113
+ </style>
114
+ </head>
115
+ <body>
116
+ <h1>Hello from craft!</h1>
117
+ </body>
118
+ </html>
119
+ `
120
+
121
+ await show(html, {
122
+ title: 'HTML Demo',
123
+ width: 800,
124
+ height: 600,
125
+ })
126
+ ```
127
+
128
+ ### Using with stx Desktop Package
129
+
130
+ The `@stacksjs/desktop` package provides a clean wrapper:
131
+
132
+ ```typescript
133
+ import { openDevWindow } from '@stacksjs/desktop'
134
+
135
+ // Open dev server in native window
136
+ const success = await openDevWindow(3000, {
137
+ title: 'My Dev Server',
138
+ width: 1400,
139
+ height: 900,
140
+ hotReload: true,
141
+ })
142
+
143
+ if (success) {
144
+ console.log('Native window opened!')
145
+ } else {
146
+ console.log('Fell back to browser')
147
+ }
148
+ ```
149
+
150
+ ### Advanced Window Options
151
+
152
+ ```typescript
153
+ import { createApp } from 'ts-craft'
154
+
155
+ const app = createApp({
156
+ url: 'http://localhost:3000',
157
+ window: {
158
+ title: 'Advanced Window',
159
+ width: 1000,
160
+ height: 700,
161
+
162
+ // Window style
163
+ frameless: false, // No title bar (default: false)
164
+ transparent: false, // Transparent window (default: false)
165
+ resizable: true, // Allow resizing (default: true)
166
+ alwaysOnTop: false, // Stay on top (default: false)
167
+ fullscreen: false, // Start fullscreen (default: false)
168
+
169
+ // Position (optional)
170
+ x: 100, // X coordinate
171
+ y: 100, // Y coordinate
172
+
173
+ // Appearance
174
+ darkMode: true, // Force dark mode
175
+
176
+ // Development
177
+ hotReload: true, // Enable hot reload
178
+ devTools: true, // Enable DevTools (right-click > Inspect)
179
+
180
+ // System integration
181
+ systemTray: true, // Show menubar icon (required for craft)
182
+ hideDockIcon: false, // Hide from dock (default: false)
183
+ },
184
+ })
185
+
186
+ await app.show()
187
+ ```
188
+
189
+ ### Controlling the Window from Web Content
190
+
191
+ craft provides a bridge API accessible from your web content:
192
+
193
+ ```html
194
+ <!DOCTYPE html>
195
+ <html>
196
+ <head>
197
+ <title>craft Bridge Demo</title>
198
+ </head>
199
+ <body>
200
+ <h1>craft Bridge API</h1>
201
+
202
+ <button onclick="hideWindow()">Hide Window</button>
203
+ <button onclick="showWindow()">Show Window</button>
204
+ <button onclick="quitApp()">Quit App</button>
205
+ <button onclick="updateTray()">Update Tray Title</button>
206
+
207
+ <script>
208
+ // Access craft bridge API
209
+ const craft = window.craft
210
+
211
+ function hideWindow() {
212
+ craft.window.hide()
213
+ }
214
+
215
+ function showWindow() {
216
+ craft.window.show()
217
+ }
218
+
219
+ function quitApp() {
220
+ craft.app.quit()
221
+ }
222
+
223
+ function updateTray() {
224
+ craft.tray.setTitle('Updated!')
225
+ }
226
+
227
+ // Listen for tray icon clicks
228
+ craft.tray.onClick(() => {
229
+ console.log('Tray icon clicked!')
230
+ craft.window.toggle()
231
+ })
232
+ </script>
233
+ </body>
234
+ </html>
235
+ ```
236
+
237
+ ### Menubar App Pattern
238
+
239
+ craft is designed for menubar applications. Here's a complete example:
240
+
241
+ ```typescript
242
+ import { createApp } from 'ts-craft'
243
+
244
+ const app = createApp({
245
+ url: 'http://localhost:3000',
246
+ window: {
247
+ title: 'My Menubar App',
248
+ width: 400,
249
+ height: 500,
250
+ resizable: false,
251
+ systemTray: true, // Creates menubar icon
252
+ darkMode: true,
253
+ },
254
+ })
255
+
256
+ await app.show()
257
+
258
+ // The window is hidden by default
259
+ // Click the menubar icon to show/hide the window
260
+ // This is the standard macOS menubar app behavior
261
+ ```
262
+
263
+ ### Integration with stx CLI
264
+
265
+ When you use the `--native` flag with stx dev server:
266
+
267
+ ```bash
268
+ stx dev examples/homepage.stx --native
269
+ ```
270
+
271
+ It internally calls:
272
+
273
+ ```typescript
274
+ import { openDevWindow } from '@stacksjs/desktop'
275
+
276
+ const success = await openDevWindow(3000, {
277
+ title: 'stx Development',
278
+ width: 1400,
279
+ height: 900,
280
+ hotReload: true,
281
+ devTools: true,
282
+ })
283
+ ```
284
+
285
+ ### Modals
286
+
287
+ ```typescript
288
+ import { showInfoModal, showQuestionModal } from '@stacksjs/desktop'
289
+
290
+ // Show an info modal
291
+ await showInfoModal('Welcome', 'Welcome to my app!')
292
+
293
+ // Show a question modal
294
+ const result = await showQuestionModal(
295
+ 'Confirm',
296
+ 'Are you sure you want to continue?'
297
+ )
298
+
299
+ if (!result.cancelled) {
300
+ console.log('User clicked button:', result.buttonIndex)
301
+ }
302
+ ```
303
+
304
+ ### Alerts & Toast Notifications
305
+
306
+ ```typescript
307
+ import { showSuccessToast, showErrorToast } from '@stacksjs/desktop'
308
+
309
+ // Show success toast
310
+ await showSuccessToast('File saved successfully!')
311
+
312
+ // Show error toast
313
+ await showErrorToast('Failed to save file', 5000)
314
+ ```
315
+
316
+ ## API Reference
317
+
318
+ ### Window Management
319
+
320
+ #### `createWindow(url, options?)`
321
+
322
+ Create a native window.
323
+
324
+ ```typescript
325
+ const window = await createWindow('http://localhost:3000', {
326
+ title: 'My App',
327
+ width: 1200,
328
+ height: 800,
329
+ darkMode: true,
330
+ hotReload: true,
331
+ resizable: true,
332
+ minimizable: true,
333
+ maximizable: true
334
+ })
335
+ ```
336
+
337
+ **Returns:** `WindowInstance | null`
338
+
339
+ #### `openDevWindow(port, options?)`
340
+
341
+ Open a development server window (used by `stx dev --native`).
342
+
343
+ ```typescript
344
+ await openDevWindow(3000, {
345
+ title: 'stx Development',
346
+ width: 1400,
347
+ height: 900
348
+ })
349
+ ```
350
+
351
+ **Returns:** `boolean`
352
+
353
+ #### `isWebviewAvailable()`
354
+
355
+ Check if craft is available for native windows.
356
+
357
+ ```typescript
358
+ if (!isWebviewAvailable()) {
359
+ console.log('craft not available')
360
+ console.log('Ensure ts-craft is installed and craft binary is built')
361
+ }
362
+ ```
363
+
364
+ **Returns:** `boolean`
365
+
366
+ ### System Tray
367
+
368
+ #### `createSystemTray(options)`
369
+
370
+ Create a system tray/menubar application.
371
+
372
+ ```typescript
373
+ const tray = await createSystemTray({
374
+ icon: './icon.png',
375
+ tooltip: 'My App',
376
+ menu: [
377
+ { label: 'Item 1', onClick: () => {} },
378
+ { type: 'separator' },
379
+ { label: 'Quit', onClick: () => process.exit(0) }
380
+ ]
381
+ })
382
+ ```
383
+
384
+ **Returns:** `SystemTrayInstance | null`
385
+
386
+ ### Modals
387
+
388
+ #### `showModal(options)`
389
+
390
+ Show a custom modal dialog.
391
+
392
+ ```typescript
393
+ const result = await showModal({
394
+ title: 'Confirm Action',
395
+ message: 'Are you sure?',
396
+ type: 'question',
397
+ buttons: [
398
+ { label: 'Cancel', style: 'default' },
399
+ { label: 'OK', style: 'primary' }
400
+ ]
401
+ })
402
+ ```
403
+
404
+ **Returns:** `Promise<ModalResult>`
405
+
406
+ #### Helper Functions
407
+
408
+ - `showInfoModal(title, message)` - Show info modal
409
+ - `showWarningModal(title, message)` - Show warning modal
410
+ - `showErrorModal(title, message)` - Show error modal
411
+ - `showSuccessModal(title, message)` - Show success modal
412
+ - `showQuestionModal(title, message)` - Show question modal
413
+
414
+ ### Alerts & Toasts
415
+
416
+ #### `showToast(options)`
417
+
418
+ Show a toast notification.
419
+
420
+ ```typescript
421
+ await showToast({
422
+ message: 'Operation complete!',
423
+ type: 'success',
424
+ duration: 3000,
425
+ position: 'top-right',
426
+ theme: 'dark'
427
+ })
428
+ ```
429
+
430
+ **Returns:** `Promise<void>`
431
+
432
+ #### Helper Functions
433
+
434
+ - `showInfoToast(message, duration?)` - Show info toast
435
+ - `showSuccessToast(message, duration?)` - Show success toast
436
+ - `showWarningToast(message, duration?)` - Show warning toast
437
+ - `showErrorToast(message, duration?)` - Show error toast
438
+
439
+ ### Components
440
+
441
+ 35 native UI components are available (documentation in progress):
442
+
443
+ **Input:** Button, TextInput, Checkbox, RadioButton, Slider, ColorPicker, DatePicker, TimePicker, Autocomplete
444
+
445
+ **Display:** Label, ImageView, ProgressBar, Avatar, Badge, Chip, Card, Tooltip, Toast
446
+
447
+ **Layout:** ScrollView, SplitView, Accordion, Stepper, Modal, Tabs, Dropdown
448
+
449
+ **Data:** ListView, Table, TreeView, DataGrid, Chart
450
+
451
+ **Advanced:** Rating, CodeEditor, MediaPlayer, FileExplorer, WebView
452
+
453
+ **Currently Implemented:**
454
+ - `createButton(props)` - Button component
455
+ - `createTextInput(props)` - Text input component
456
+ - `createCheckbox(props)` - Checkbox component
457
+
458
+ ## Platform Support
459
+
460
+ | Platform | Status | Notes |
461
+ |----------|--------|-------|
462
+ | macOS | ✅ Working | Uses WebKit (built-in) |
463
+ | Linux | 🚧 Ready | Requires GTK3 + WebKit2GTK |
464
+ | Windows | 🚧 Ready | Requires WebView2 Runtime |
465
+
466
+ ## Development
467
+
468
+ ```bash
469
+ # Install dependencies
470
+ bun install
471
+
472
+ # Build the package
473
+ bun run build
474
+
475
+ # Run tests
476
+ bun test
477
+
478
+ # Run tests with coverage
479
+ bun test --coverage
480
+
481
+ # Type check
482
+ bun run typecheck
483
+
484
+ # Lint
485
+ bun run lint
486
+ ```
487
+
488
+ ## Examples
489
+
490
+ See the `examples/` directory for working examples:
491
+
492
+ - `basic-window.ts` - Simple window example
493
+ - `system-tray.ts` - System tray application
494
+ - `modal-demo.ts` - Modal dialogs
495
+ - `alerts-demo.ts` - Alerts and toasts
496
+ - `all-components.ts` - All 35 components showcase
497
+ - `dev-server-integration.ts` - Dev server integration pattern
498
+
499
+ Run any example:
500
+ ```bash
501
+ cd packages/desktop
502
+ bun run examples/basic-window.ts
503
+ ```
504
+
505
+ ## Testing
506
+
507
+ The desktop package has comprehensive test coverage:
508
+
509
+ **Coverage Stats:**
510
+ - 132 tests passing
511
+ - 100% function coverage
512
+ - 96.77% line coverage
513
+ - 185 expect() assertions
514
+
515
+ **Test Files:**
516
+ - `test/window.test.ts` - Window management (30+ tests)
517
+ - `test/system-tray.test.ts` - System tray (15+ tests)
518
+ - `test/modals.test.ts` - Modal dialogs (30+ tests)
519
+ - `test/alerts.test.ts` - Alerts and toasts (40+ tests)
520
+ - `test/components.test.ts` - UI components (20+ tests)
521
+
522
+ All business logic is fully tested with proper mocking and error handling coverage.
523
+
524
+ ## Comparison with Electron
525
+
526
+ | Feature | @stacksjs/desktop | Electron |
527
+ |---------|------------------|----------|
528
+ | **Binary Size** | 1.4MB | 100MB+ |
529
+ | **Startup Time** | <100ms | 1-3s |
530
+ | **Memory Usage** | ~90MB | 200MB+ |
531
+ | **WebView** | System (WebKit) | Chromium (bundled) |
532
+ | **Distribution** | Tiny | Large |
533
+
534
+ ## Contributing
535
+
536
+ Contributions are welcome! Please see the [Contributing Guide](../../CONTRIBUTING.md).
537
+
538
+ ## License
539
+
540
+ MIT License - see [LICENSE.md](../../LICENSE.md)
541
+
542
+ ## Implementation Status
543
+
544
+ ### ✅ Working
545
+ - Native window creation with craft/ts-craft
546
+ - URL loading in native windows
547
+ - HTML content rendering
548
+ - `stx dev --native` flag integration
549
+ - Browser fallback when craft unavailable
550
+ - Hot reload and dev tools support
551
+
552
+ ### 🚧 Placeholder (Coming Soon)
553
+ - System tray applications
554
+ - Modal dialogs
555
+ - Alerts and toasts
556
+ - Native UI components (35 total)
557
+
558
+ The package is fully functional for basic native windows using craft. System tray and other features are planned for future releases.
559
+
560
+ ## Credits
561
+
562
+ - Powered by [Craft](https://github.com/stacksjs/craft)
563
+ - Part of the [stx](https://github.com/stacksjs/stx) ecosystem
@@ -0,0 +1,164 @@
1
+ import type { AlertOptions, ToastOptions } from './types';
2
+ /**
3
+ * Request notification permission (browser)
4
+ */
5
+ export declare function requestNotificationPermission(): Promise<boolean>;
6
+ /**
7
+ * Show an alert/notification
8
+ *
9
+ * @param options - Alert configuration options
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * await showAlert({
14
+ * title: 'Success',
15
+ * message: 'Your changes have been saved',
16
+ * type: 'success',
17
+ * duration: 3000,
18
+ * position: 'top-right',
19
+ * })
20
+ * ```
21
+ */
22
+ export declare function showAlert(options: AlertOptions): Promise<void>;
23
+ /**
24
+ * Show a toast notification (alias for showAlert with toast-friendly defaults)
25
+ */
26
+ export declare function showToast(options: ToastOptions): Promise<void>;
27
+ /**
28
+ * Show an info toast
29
+ */
30
+ export declare function showInfoToast(message: string, duration?: any): Promise<void>;
31
+ /**
32
+ * Show a success toast
33
+ */
34
+ export declare function showSuccessToast(message: string, duration?: any): Promise<void>;
35
+ /**
36
+ * Show a warning toast
37
+ */
38
+ export declare function showWarningToast(message: string, duration?: any): Promise<void>;
39
+ /**
40
+ * Show an error toast
41
+ */
42
+ export declare function showErrorToast(message: string, duration?: any): Promise<void>;
43
+ /**
44
+ * Show a notification with title
45
+ */
46
+ export declare function notify(title: string, message: string, type?: AlertOptions['type']): Promise<void>;
47
+ /**
48
+ * Dismiss a specific alert by ID
49
+ */
50
+ export declare function dismissAlertById(id: string): void;
51
+ /**
52
+ * Dismiss all active alerts
53
+ */
54
+ export declare function dismissAllAlerts(): void;
55
+ /**
56
+ * Get count of active alerts
57
+ */
58
+ export declare function getActiveAlertCount(): number;
59
+ /**
60
+ * CSS styles for web-based toasts
61
+ */
62
+ export declare const TOAST_STYLES: `
63
+ .stx-toast-container {
64
+ position: fixed;
65
+ z-index: 10001;
66
+ display: flex;
67
+ flex-direction: column;
68
+ gap: 8px;
69
+ max-width: 400px;
70
+ pointer-events: none;
71
+ }
72
+
73
+ .stx-toast-container.top-left { top: 16px; left: 16px; }
74
+ .stx-toast-container.top-center { top: 16px; left: 50%; transform: translateX(-50%); }
75
+ .stx-toast-container.top-right { top: 16px; right: 16px; }
76
+ .stx-toast-container.bottom-left { bottom: 16px; left: 16px; }
77
+ .stx-toast-container.bottom-center { bottom: 16px; left: 50%; transform: translateX(-50%); }
78
+ .stx-toast-container.bottom-right { bottom: 16px; right: 16px; }
79
+
80
+ .stx-toast {
81
+ display: flex;
82
+ align-items: flex-start;
83
+ gap: 12px;
84
+ padding: 12px 16px;
85
+ background: #fff;
86
+ border-radius: 8px;
87
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
88
+ pointer-events: auto;
89
+ cursor: pointer;
90
+ opacity: 0;
91
+ transform: translateY(-10px);
92
+ transition: opacity 0.2s, transform 0.2s;
93
+ }
94
+
95
+ .stx-toast.visible {
96
+ opacity: 1;
97
+ transform: translateY(0);
98
+ }
99
+
100
+ .stx-toast.dismissing {
101
+ opacity: 0;
102
+ transform: translateX(100%);
103
+ }
104
+
105
+ @media (prefers-color-scheme: dark) {
106
+ .stx-toast {
107
+ background: #2d2d2d;
108
+ color: #fff;
109
+ }
110
+ }
111
+
112
+ .stx-toast-icon {
113
+ font-size: 20px;
114
+ flex-shrink: 0;
115
+ margin-top: 2px;
116
+ }
117
+
118
+ .stx-toast.info .stx-toast-icon { color: #3498db; }
119
+ .stx-toast.success .stx-toast-icon { color: #27ae60; }
120
+ .stx-toast.warning .stx-toast-icon { color: #f39c12; }
121
+ .stx-toast.error .stx-toast-icon { color: #e74c3c; }
122
+
123
+ .stx-toast-content {
124
+ flex: 1;
125
+ min-width: 0;
126
+ }
127
+
128
+ .stx-toast-title {
129
+ font-weight: 600;
130
+ margin-bottom: 4px;
131
+ }
132
+
133
+ .stx-toast-message {
134
+ color: #666;
135
+ font-size: 14px;
136
+ line-height: 1.4;
137
+ }
138
+
139
+ @media (prefers-color-scheme: dark) {
140
+ .stx-toast-message { color: #aaa; }
141
+ }
142
+
143
+ .stx-toast-close {
144
+ background: none;
145
+ border: none;
146
+ font-size: 20px;
147
+ cursor: pointer;
148
+ opacity: 0.5;
149
+ padding: 0;
150
+ line-height: 1;
151
+ color: inherit;
152
+ transition: opacity 0.15s;
153
+ }
154
+
155
+ .stx-toast-close:hover {
156
+ opacity: 1;
157
+ }
158
+
159
+ /* Border accent for different types */
160
+ .stx-toast.info { border-left: 4px solid #3498db; }
161
+ .stx-toast.success { border-left: 4px solid #27ae60; }
162
+ .stx-toast.warning { border-left: 4px solid #f39c12; }
163
+ .stx-toast.error { border-left: 4px solid #e74c3c; }
164
+ `;