@stacksjs/desktop 0.2.196 → 0.2.198

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/index.js CHANGED
@@ -1,332 +1,124 @@
1
1
  // @bun
2
- var __require = import.meta.require;
3
-
4
- // src/alerts.ts
5
- function hasNativeNotificationSupport() {
6
- if (typeof Notification !== "undefined" && Notification.permission === "granted") {
7
- return true;
8
- }
9
- return false;
10
- }
11
- function isBrowser() {
12
- return typeof window !== "undefined" && typeof document !== "undefined";
13
- }
14
- async function requestNotificationPermission() {
15
- if (typeof Notification === "undefined") {
16
- return false;
17
- }
18
- if (Notification.permission === "granted") {
19
- return true;
20
- }
21
- if (Notification.permission === "denied") {
22
- return false;
23
- }
24
- const permission = await Notification.requestPermission();
25
- return permission === "granted";
26
- }
27
- var activeAlerts = new Map;
28
- function generateAlertId() {
29
- return `alert-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
30
- }
31
- function getAlertIcon(type) {
32
- switch (type) {
33
- case "info":
34
- return "ℹ";
35
- case "success":
36
- return "✔";
37
- case "warning":
38
- return "⚠";
39
- case "error":
40
- return "✖";
41
- default:
42
- return "ℹ";
43
- }
44
- }
45
- function getToastContainer(position = "top-right") {
46
- if (!isBrowser()) {
47
- throw new Error("Toast container requires browser environment");
48
- }
49
- try {
50
- const existingContainer = document.querySelector(`.stx-toast-container[data-position="${position}"]`);
51
- if (existingContainer) {
52
- return existingContainer;
53
- }
54
- } catch {}
55
- const container = document.createElement("div");
56
- container.className = `stx-toast-container ${position}`;
57
- container.dataset.position = position;
58
- try {
59
- if (document.body) {
60
- document.body.appendChild(container);
61
- }
62
- } catch {}
63
- return container;
64
- }
65
- function createToastHTML(state) {
66
- const { options } = state;
67
- const icon = getAlertIcon(options.type);
68
- const typeClass = options.type || "info";
69
- const hasClose = options.duration !== 0;
70
- return `
71
- <div class="stx-toast ${typeClass}" data-alert-id="${state.id}" role="alert" aria-live="polite">
72
- <div class="stx-toast-icon">${icon}</div>
73
- <div class="stx-toast-content">
74
- ${options.title ? `<div class="stx-toast-title">${escapeHtml(options.title)}</div>` : ""}
75
- <div class="stx-toast-message">${escapeHtml(options.message)}</div>
76
- </div>
77
- ${hasClose ? '<button class="stx-toast-close" aria-label="Close">&times;</button>' : ""}
78
- </div>
79
- `;
80
- }
81
- function escapeHtml(str) {
82
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
83
- }
84
- function dismissAlert(state) {
85
- if (state.timeout) {
86
- clearTimeout(state.timeout);
87
- }
88
- activeAlerts.delete(state.id);
89
- if (state.element && isBrowser()) {
90
- state.element.classList.add("dismissing");
91
- setTimeout(() => {
92
- state.element?.remove();
93
- }, 200);
94
- }
95
- }
96
- async function showAlert(options) {
97
- const id = generateAlertId();
98
- const hasNative = hasNativeNotificationSupport();
99
- const duration = options.duration ?? 5000;
100
- const state = {
101
- id,
102
- options
103
- };
104
- activeAlerts.set(id, state);
105
- if (hasNative && options.type !== "error") {
106
- try {
107
- const notification = new Notification(options.title || "Notification", {
108
- body: options.message,
109
- icon: options.type === "success" ? "\u2713" : options.type === "warning" ? "\u26A0" : "\u2139",
110
- tag: id
111
- });
112
- notification.onclick = () => {
113
- if (options.onClick) {
114
- options.onClick();
115
- }
116
- notification.close();
117
- };
118
- if (duration > 0) {
119
- state.timeout = setTimeout(() => {
120
- notification.close();
121
- activeAlerts.delete(id);
122
- }, duration);
123
- }
124
- return;
125
- } catch {}
126
- }
127
- if (isBrowser()) {
128
- try {
129
- const container = getToastContainer(options.position);
130
- const wrapper = document.createElement("div");
131
- wrapper.innerHTML = createToastHTML(state);
132
- const toast = wrapper.firstElementChild;
133
- if (!toast) {
134
- const typeLabel = (options.type || "info").toUpperCase();
135
- console.log(`[stx-alert] ${typeLabel}: ${options.title || ""}`);
136
- console.log(`[stx-alert] ${options.message}`);
137
- return;
138
- }
139
- state.element = toast;
140
- try {
141
- container.appendChild(toast);
142
- } catch {
143
- const typeLabel = (options.type || "info").toUpperCase();
144
- console.log(`[stx-alert] ${typeLabel}: ${options.title || ""}`);
145
- console.log(`[stx-alert] ${options.message}`);
146
- return;
147
- }
148
- if (typeof requestAnimationFrame === "function") {
149
- requestAnimationFrame(() => {
150
- try {
151
- toast.classList.add("visible");
152
- } catch {}
153
- });
154
- }
155
- try {
156
- toast.addEventListener("click", (e) => {
157
- if (e.target.classList?.contains("stx-toast-close")) {
158
- dismissAlert(state);
159
- return;
160
- }
161
- if (options.onClick) {
162
- options.onClick();
163
- }
164
- });
165
- } catch {}
166
- if (duration > 0) {
167
- state.timeout = setTimeout(() => {
168
- dismissAlert(state);
169
- }, duration);
170
- }
171
- } catch {
172
- const typeLabel = (options.type || "info").toUpperCase();
173
- console.log(`[stx-alert] ${typeLabel}: ${options.title || ""}`);
174
- console.log(`[stx-alert] ${options.message}`);
175
- }
176
- } else {
177
- const typeLabel = (options.type || "info").toUpperCase();
178
- console.log(`[stx-alert] ${typeLabel}: ${options.title || ""}`);
179
- console.log(`[stx-alert] ${options.message}`);
180
- if (duration > 0) {
181
- state.timeout = setTimeout(() => {
182
- activeAlerts.delete(id);
183
- }, duration);
184
- }
185
- }
186
- }
187
- async function showToast(options) {
188
- return showAlert({
189
- ...options,
190
- position: options.position || "top-right"
191
- });
192
- }
193
- async function showInfoToast(message, duration = 3000) {
194
- return showToast({ message, type: "info", duration });
195
- }
196
- async function showSuccessToast(message, duration = 3000) {
197
- return showToast({ message, type: "success", duration });
198
- }
199
- async function showWarningToast(message, duration = 3000) {
200
- return showToast({ message, type: "warning", duration });
201
- }
202
- async function showErrorToast(message, duration = 5000) {
203
- return showToast({ message, type: "error", duration });
204
- }
205
- async function notify(title, message, type = "info") {
206
- return showAlert({ title, message, type });
207
- }
208
- function dismissAlertById(id) {
209
- const state = activeAlerts.get(id);
210
- if (state) {
211
- dismissAlert(state);
212
- }
213
- }
214
- function dismissAllAlerts() {
215
- for (const state of activeAlerts.values()) {
216
- dismissAlert(state);
217
- }
218
- }
219
- function getActiveAlertCount() {
220
- return activeAlerts.size;
221
- }
222
- var TOAST_STYLES = `
223
- .stx-toast-container {
224
- position: fixed;
225
- z-index: 10001;
226
- display: flex;
227
- flex-direction: column;
228
- gap: 8px;
229
- max-width: 400px;
230
- pointer-events: none;
231
- }
232
-
233
- .stx-toast-container.top-left { top: 16px; left: 16px; }
234
- .stx-toast-container.top-center { top: 16px; left: 50%; transform: translateX(-50%); }
235
- .stx-toast-container.top-right { top: 16px; right: 16px; }
236
- .stx-toast-container.bottom-left { bottom: 16px; left: 16px; }
237
- .stx-toast-container.bottom-center { bottom: 16px; left: 50%; transform: translateX(-50%); }
238
- .stx-toast-container.bottom-right { bottom: 16px; right: 16px; }
239
-
240
- .stx-toast {
241
- display: flex;
242
- align-items: flex-start;
243
- gap: 12px;
244
- padding: 12px 16px;
245
- background: #fff;
246
- border-radius: 8px;
247
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
248
- pointer-events: auto;
249
- cursor: pointer;
250
- opacity: 0;
251
- transform: translateY(-10px);
252
- transition: opacity 0.2s, transform 0.2s;
253
- }
254
-
255
- .stx-toast.visible {
256
- opacity: 1;
257
- transform: translateY(0);
258
- }
259
-
260
- .stx-toast.dismissing {
261
- opacity: 0;
262
- transform: translateX(100%);
263
- }
264
-
265
- @media (prefers-color-scheme: dark) {
266
- .stx-toast {
267
- background: #2d2d2d;
268
- color: #fff;
269
- }
270
- }
271
-
272
- .stx-toast-icon {
273
- font-size: 20px;
274
- flex-shrink: 0;
275
- margin-top: 2px;
276
- }
277
-
278
- .stx-toast.info .stx-toast-icon { color: #3498db; }
279
- .stx-toast.success .stx-toast-icon { color: #27ae60; }
280
- .stx-toast.warning .stx-toast-icon { color: #f39c12; }
281
- .stx-toast.error .stx-toast-icon { color: #e74c3c; }
282
-
283
- .stx-toast-content {
284
- flex: 1;
285
- min-width: 0;
286
- }
287
-
288
- .stx-toast-title {
289
- font-weight: 600;
290
- margin-bottom: 4px;
291
- }
292
-
293
- .stx-toast-message {
294
- color: #666;
295
- font-size: 14px;
296
- line-height: 1.4;
297
- }
298
-
299
- @media (prefers-color-scheme: dark) {
300
- .stx-toast-message { color: #aaa; }
301
- }
302
-
303
- .stx-toast-close {
304
- background: none;
305
- border: none;
306
- font-size: 20px;
307
- cursor: pointer;
308
- opacity: 0.5;
309
- padding: 0;
310
- line-height: 1;
311
- color: inherit;
312
- transition: opacity 0.15s;
313
- }
314
-
315
- .stx-toast-close:hover {
316
- opacity: 1;
317
- }
318
-
319
- /* Border accent for different types */
320
- .stx-toast.info { border-left: 4px solid #3498db; }
321
- .stx-toast.success { border-left: 4px solid #27ae60; }
322
- .stx-toast.warning { border-left: 4px solid #f39c12; }
323
- .stx-toast.error { border-left: 4px solid #e74c3c; }
324
- `;
2
+ import {
3
+ DEFAULT_WATCH_INTERVAL_MS,
4
+ MAX_WATCH_INTERVAL_MS,
5
+ MIN_WATCH_INTERVAL_MS,
6
+ MODAL_STYLES,
7
+ TOAST_STYLES,
8
+ alert,
9
+ app,
10
+ appleScript,
11
+ audio,
12
+ battery,
13
+ biometric,
14
+ bluetooth,
15
+ bonjour,
16
+ caffeinate,
17
+ clipboard,
18
+ closeAllModals,
19
+ confirm,
20
+ continuityCamera,
21
+ coreml,
22
+ crashReporter,
23
+ createInterval,
24
+ createTimer,
25
+ decaffeinate,
26
+ deepLinks,
27
+ delay,
28
+ dismissAlertById,
29
+ dismissAllAlerts,
30
+ dragOut,
31
+ fileAssociations,
32
+ focus,
33
+ focusShortcutsReady,
34
+ formatCompact,
35
+ formatDuration,
36
+ formatRemainingTime,
37
+ formatShortcut,
38
+ formatTime,
39
+ fs,
40
+ getActiveAlertCount,
41
+ getActiveModalCount,
42
+ getCaffeinateStatus,
43
+ getCapabilities,
44
+ getCapability,
45
+ getDialogBridgeScript,
46
+ getRegisteredHotkeys,
47
+ globalShortcuts,
48
+ handoff,
49
+ hasBridge,
50
+ hasFocusShortcuts,
51
+ iap,
52
+ isAvailable,
53
+ isCaffeinated,
54
+ isDragOutAvailable,
55
+ keychain,
56
+ liveActivities,
57
+ localServer,
58
+ location,
59
+ log,
60
+ menu,
61
+ midi,
62
+ nativeAutoLaunch,
63
+ network,
64
+ notifications,
65
+ notify,
66
+ parseShortcut,
67
+ pdf,
68
+ permissions,
69
+ printing,
70
+ prompt,
71
+ redactPII,
72
+ registerHotkey,
73
+ requestNotificationPermission,
74
+ screen,
75
+ screenCapture,
76
+ screenSharing,
77
+ serial,
78
+ serviceMenu,
79
+ shell,
80
+ showAlert,
81
+ showAlertDialog,
82
+ showColorPicker,
83
+ showConfirmDialog,
84
+ showErrorDialog,
85
+ showErrorModal,
86
+ showErrorToast,
87
+ showInfoModal,
88
+ showInfoToast,
89
+ showMessageBox,
90
+ showModal,
91
+ showOpenDialog,
92
+ showQuestionModal,
93
+ showSaveDialog,
94
+ showSuccessModal,
95
+ showSuccessToast,
96
+ showToast,
97
+ showWarningDialog,
98
+ showWarningModal,
99
+ showWarningToast,
100
+ signPayload,
101
+ speech,
102
+ speechRecognition,
103
+ spotlight,
104
+ tags,
105
+ theme,
106
+ touchbar,
107
+ unregisterAllHotkeys,
108
+ unregisterHotkey,
109
+ updater,
110
+ vision,
111
+ watchScreenSharing,
112
+ windowEvents
113
+ } from "./chunk-ys4frvmj.js";
114
+ import {
115
+ __require
116
+ } from "./chunk-2mx7fq49.js";
325
117
  // src/components.ts
326
118
  function generateId(prefix = "stx") {
327
119
  return `${prefix}-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
328
120
  }
329
- function escapeHtml2(str) {
121
+ function escapeHtml(str) {
330
122
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
331
123
  }
332
124
  function buildClasses(...classes) {
@@ -354,7 +146,7 @@ function createButton(props) {
354
146
  const styleStr = buildStyles(style);
355
147
  const iconHtml = icon ? `<span class="stx-button-icon">${icon}</span>` : "";
356
148
  const loadingHtml = loading ? '<span class="stx-button-spinner"></span>' : "";
357
- const content = iconPosition === "left" ? `${iconHtml}${loadingHtml}<span class="stx-button-text">${escapeHtml2(text)}</span>` : `<span class="stx-button-text">${escapeHtml2(text)}</span>${iconHtml}${loadingHtml}`;
149
+ const content = iconPosition === "left" ? `${iconHtml}${loadingHtml}<span class="stx-button-text">${escapeHtml(text)}</span>` : `<span class="stx-button-text">${escapeHtml(text)}</span>${iconHtml}${loadingHtml}`;
358
150
  return `<button id="${id}" class="${classes}" ${disabled || loading ? "disabled" : ""} ${styleStr ? `style='${styleStr}'` : ""}>${content}</button>`;
359
151
  }
360
152
  function createTextInput(props) {
@@ -382,26 +174,26 @@ function createTextInput(props) {
382
174
  `type="${type}"`,
383
175
  `id="${id}"`,
384
176
  `class="${classes}"`,
385
- value && `value="${escapeHtml2(value)}"`,
386
- placeholder && `placeholder="${escapeHtml2(placeholder)}"`,
177
+ value && `value="${escapeHtml(value)}"`,
178
+ placeholder && `placeholder="${escapeHtml(placeholder)}"`,
387
179
  disabled && "disabled",
388
180
  readonly && "readonly",
389
181
  required && "required",
390
182
  maxLength && `maxlength="${maxLength}"`,
391
183
  minLength && `minlength="${minLength}"`,
392
- pattern && `pattern="${escapeHtml2(pattern)}"`,
184
+ pattern && `pattern="${escapeHtml(pattern)}"`,
393
185
  autocomplete && `autocomplete="${autocomplete}"`,
394
186
  styleStr && `style="${styleStr}"`
395
187
  ].filter(Boolean).join(" ");
396
188
  let html = "";
397
189
  if (label) {
398
- html += `<label class="stx-input-label" for="${id}">${escapeHtml2(label)}</label>`;
190
+ html += `<label class="stx-input-label" for="${id}">${escapeHtml(label)}</label>`;
399
191
  }
400
192
  html += `<input ${attrs} />`;
401
193
  if (error) {
402
- html += `<div class="stx-input-error">${escapeHtml2(error)}</div>`;
194
+ html += `<div class="stx-input-error">${escapeHtml(error)}</div>`;
403
195
  } else if (hint) {
404
- html += `<div class="stx-input-hint">${escapeHtml2(hint)}</div>`;
196
+ html += `<div class="stx-input-hint">${escapeHtml(hint)}</div>`;
405
197
  }
406
198
  return `<div class="stx-input-wrapper">${html}</div>`;
407
199
  }
@@ -428,7 +220,7 @@ function createCheckbox(props) {
428
220
  <label class="${classes}" ${styleStr ? `style="${styleStr}"` : ""}>
429
221
  <input ${inputAttrs} />
430
222
  <span class="stx-checkbox-box"></span>
431
- ${label ? `<span class="stx-checkbox-label">${escapeHtml2(label)}</span>` : ""}
223
+ ${label ? `<span class="stx-checkbox-label">${escapeHtml(label)}</span>` : ""}
432
224
  </label>
433
225
  `;
434
226
  }
@@ -458,7 +250,7 @@ function createSlider(props) {
458
250
  ].filter(Boolean).join(" ");
459
251
  let html = "";
460
252
  if (label) {
461
- html += `<label class="stx-slider-label" for="${id}">${escapeHtml2(label)}</label>`;
253
+ html += `<label class="stx-slider-label" for="${id}">${escapeHtml(label)}</label>`;
462
254
  }
463
255
  html += `<div class="stx-slider-track"><input ${inputAttrs} /></div>`;
464
256
  if (showValue) {
@@ -499,7 +291,7 @@ function createBadge(props) {
499
291
  } = props;
500
292
  const classes = buildClasses("stx-badge", `stx-badge--${variant}`, `stx-badge--${size}`, className);
501
293
  const styleStr = buildStyles(style);
502
- return `<span id="${id}" class="${classes}" ${styleStr ? `style='${styleStr}'` : ""}>${escapeHtml2(text)}</span>`;
294
+ return `<span id="${id}" class="${classes}" ${styleStr ? `style='${styleStr}'` : ""}>${escapeHtml(text)}</span>`;
503
295
  }
504
296
  function createAvatar(props) {
505
297
  const {
@@ -519,7 +311,7 @@ function createAvatar(props) {
519
311
  const initials = name ? name.split(" ").map((n) => n[0]).join("").toUpperCase().slice(0, 2) : "";
520
312
  if (src) {
521
313
  return `<div id="${id}" class="${classes}" ${combinedStyle ? `style="${combinedStyle}"` : ""}>
522
- <img src="${escapeHtml2(src)}" alt="${escapeHtml2(alt)}" class="stx-avatar-img" />
314
+ <img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}" class="stx-avatar-img" />
523
315
  </div>`;
524
316
  }
525
317
  return `<div id="${id}" class="${classes}" ${combinedStyle ? `style="${combinedStyle}"` : ""}>
@@ -542,21 +334,21 @@ function createCard(props) {
542
334
  const styleStr = buildStyles(style);
543
335
  let html = "";
544
336
  if (image) {
545
- html += `<div class="stx-card-image"><img src="${escapeHtml2(image)}" alt="" /></div>`;
337
+ html += `<div class="stx-card-image"><img src="${escapeHtml(image)}" alt="" /></div>`;
546
338
  }
547
339
  html += '<div class="stx-card-body">';
548
340
  if (title) {
549
- html += `<h3 class="stx-card-title">${escapeHtml2(title)}</h3>`;
341
+ html += `<h3 class="stx-card-title">${escapeHtml(title)}</h3>`;
550
342
  }
551
343
  if (subtitle) {
552
- html += `<p class="stx-card-subtitle">${escapeHtml2(subtitle)}</p>`;
344
+ html += `<p class="stx-card-subtitle">${escapeHtml(subtitle)}</p>`;
553
345
  }
554
346
  if (content) {
555
- html += `<div class="stx-card-content">${escapeHtml2(content)}</div>`;
347
+ html += `<div class="stx-card-content">${escapeHtml(content)}</div>`;
556
348
  }
557
349
  html += "</div>";
558
350
  if (footer) {
559
- html += `<div class="stx-card-footer">${escapeHtml2(footer)}</div>`;
351
+ html += `<div class="stx-card-footer">${escapeHtml(footer)}</div>`;
560
352
  }
561
353
  return `<div id="${id}" class="${classes}" ${styleStr ? `style='${styleStr}'` : ""}>${html}</div>`;
562
354
  }
@@ -574,7 +366,7 @@ function createTabs(props) {
574
366
  const tabsHtml = tabs.map((tab, index) => {
575
367
  const isActive = activeTab ? tab.value === activeTab : index === 0;
576
368
  const tabClasses = buildClasses("stx-tab", isActive && "stx-tab--active", tab.disabled && "stx-tab--disabled");
577
- return `<button class="${tabClasses}" data-value="${escapeHtml2(tab.value)}" ${tab.disabled ? "disabled" : ""} role="tab" aria-selected="${isActive}">${escapeHtml2(tab.label)}</button>`;
369
+ return `<button class="${tabClasses}" data-value="${escapeHtml(tab.value)}" ${tab.disabled ? "disabled" : ""} role="tab" aria-selected="${isActive}">${escapeHtml(tab.label)}</button>`;
578
370
  }).join("");
579
371
  return `<div id="${id}" class="${classes}" role="tablist" ${styleStr ? `style='${styleStr}'` : ""}>${tabsHtml}</div>`;
580
372
  }
@@ -593,9 +385,9 @@ function createDropdown(props) {
593
385
  const optionsHtml = options.map((opt) => {
594
386
  const selected = opt.value === value ? "selected" : "";
595
387
  const disabledAttr = opt.disabled ? "disabled" : "";
596
- return `<option value="${escapeHtml2(opt.value)}" ${selected} ${disabledAttr}>${escapeHtml2(opt.label)}</option>`;
388
+ return `<option value="${escapeHtml(opt.value)}" ${selected} ${disabledAttr}>${escapeHtml(opt.label)}</option>`;
597
389
  }).join("");
598
- const placeholderOption = value ? "" : `<option value="" disabled selected>${escapeHtml2(placeholder)}</option>`;
390
+ const placeholderOption = value ? "" : `<option value="" disabled selected>${escapeHtml(placeholder)}</option>`;
599
391
  return `<select id="${id}" class="${classes}" ${disabled ? "disabled" : ""} ${styleStr ? `style='${styleStr}'` : ""}>${placeholderOption}${optionsHtml}</select>`;
600
392
  }
601
393
  function createRating(props) {
@@ -636,8 +428,8 @@ function createRadioButton(props) {
636
428
  const inputAttrs = [
637
429
  'type="radio"',
638
430
  `id="${id}"`,
639
- `name="${escapeHtml2(name)}"`,
640
- `value="${escapeHtml2(value)}"`,
431
+ `name="${escapeHtml(name)}"`,
432
+ `value="${escapeHtml(value)}"`,
641
433
  checked && "checked",
642
434
  disabled && "disabled"
643
435
  ].filter(Boolean).join(" ");
@@ -645,7 +437,7 @@ function createRadioButton(props) {
645
437
  <label class="${classes}" ${styleStr ? `style="${styleStr}"` : ""}>
646
438
  <input ${inputAttrs} />
647
439
  <span class="stx-radio-circle"></span>
648
- ${label ? `<span class="stx-radio-label">${escapeHtml2(label)}</span>` : ""}
440
+ ${label ? `<span class="stx-radio-label">${escapeHtml(label)}</span>` : ""}
649
441
  </label>
650
442
  `;
651
443
  }
@@ -663,16 +455,16 @@ function createColorPicker(props) {
663
455
  const styleStr = buildStyles(style);
664
456
  let html = "";
665
457
  if (label) {
666
- html += `<label class="stx-color-picker-label" for="${id}">${escapeHtml2(label)}</label>`;
458
+ html += `<label class="stx-color-picker-label" for="${id}">${escapeHtml(label)}</label>`;
667
459
  }
668
460
  html += `<div class="stx-color-picker-input">
669
- <input type="color" id="${id}" value="${escapeHtml2(value)}" ${disabled ? "disabled" : ""} />
670
- <span class="stx-color-picker-value">${escapeHtml2(value)}</span>
461
+ <input type="color" id="${id}" value="${escapeHtml(value)}" ${disabled ? "disabled" : ""} />
462
+ <span class="stx-color-picker-value">${escapeHtml(value)}</span>
671
463
  </div>`;
672
464
  if (presetColors.length > 0) {
673
465
  html += '<div class="stx-color-picker-presets">';
674
466
  for (const color of presetColors) {
675
- html += `<button class="stx-color-preset" style="background: ${escapeHtml2(color)}" data-color="${escapeHtml2(color)}" ${disabled ? "disabled" : ""}></button>`;
467
+ html += `<button class="stx-color-preset" style="background: ${escapeHtml(color)}" data-color="${escapeHtml(color)}" ${disabled ? "disabled" : ""}></button>`;
676
468
  }
677
469
  html += "</div>";
678
470
  }
@@ -703,11 +495,11 @@ function createDatePicker(props) {
703
495
  minValue && `min="${minValue}"`,
704
496
  maxValue && `max="${maxValue}"`,
705
497
  disabled && "disabled",
706
- `placeholder="${escapeHtml2(placeholder)}"`
498
+ `placeholder="${escapeHtml(placeholder)}"`
707
499
  ].filter(Boolean).join(" ");
708
500
  let html = "";
709
501
  if (label) {
710
- html += `<label class="stx-date-picker-label" for="${id}">${escapeHtml2(label)}</label>`;
502
+ html += `<label class="stx-date-picker-label" for="${id}">${escapeHtml(label)}</label>`;
711
503
  }
712
504
  html += `<div class="stx-date-picker-input">
713
505
  <input ${inputAttrs} />
@@ -732,15 +524,15 @@ function createTimePicker(props) {
732
524
  const inputAttrs = [
733
525
  'type="time"',
734
526
  `id="${id}"`,
735
- value && `value="${escapeHtml2(value)}"`,
527
+ value && `value="${escapeHtml(value)}"`,
736
528
  `step="${step}"`,
737
- min && `min="${escapeHtml2(min)}"`,
738
- max && `max="${escapeHtml2(max)}"`,
529
+ min && `min="${escapeHtml(min)}"`,
530
+ max && `max="${escapeHtml(max)}"`,
739
531
  disabled && "disabled"
740
532
  ].filter(Boolean).join(" ");
741
533
  let html = "";
742
534
  if (label) {
743
- html += `<label class="stx-time-picker-label" for="${id}">${escapeHtml2(label)}</label>`;
535
+ html += `<label class="stx-time-picker-label" for="${id}">${escapeHtml(label)}</label>`;
744
536
  }
745
537
  html += `<input ${inputAttrs} class="stx-time-picker-input" />`;
746
538
  return `<div class="${classes}" ${styleStr ? `style='${styleStr}'` : ""}>${html}</div>`;
@@ -763,15 +555,15 @@ function createAutocomplete(props) {
763
555
  const displayValue = selectedOption ? selectedOption.label : value;
764
556
  let html = "";
765
557
  if (label) {
766
- html += `<label class="stx-autocomplete-label" for="${id}">${escapeHtml2(label)}</label>`;
558
+ html += `<label class="stx-autocomplete-label" for="${id}">${escapeHtml(label)}</label>`;
767
559
  }
768
560
  html += `<div class="stx-autocomplete-input-wrapper">
769
- <input type="text" id="${id}" value="${escapeHtml2(displayValue)}" placeholder="${escapeHtml2(placeholder)}" ${disabled ? "disabled" : ""} autocomplete="off" class="stx-autocomplete-input" />
561
+ <input type="text" id="${id}" value="${escapeHtml(displayValue)}" placeholder="${escapeHtml(placeholder)}" ${disabled ? "disabled" : ""} autocomplete="off" class="stx-autocomplete-input" />
770
562
  ${loading ? '<span class="stx-autocomplete-spinner"></span>' : ""}
771
563
  </div>`;
772
564
  html += '<ul class="stx-autocomplete-list">';
773
565
  for (const opt of options) {
774
- html += `<li class="stx-autocomplete-item ${opt.value === value ? "stx-autocomplete-item--selected" : ""}" data-value="${escapeHtml2(opt.value)}">${escapeHtml2(opt.label)}</li>`;
566
+ html += `<li class="stx-autocomplete-item ${opt.value === value ? "stx-autocomplete-item--selected" : ""}" data-value="${escapeHtml(opt.value)}">${escapeHtml(opt.label)}</li>`;
775
567
  }
776
568
  html += "</ul>";
777
569
  return `<div class="${classes}" ${styleStr ? `style='${styleStr}'` : ""}>${html}</div>`;
@@ -788,7 +580,7 @@ function createLabel(props) {
788
580
  } = props;
789
581
  const classes = buildClasses("stx-label", `stx-label--${size}`, className);
790
582
  const styleStr = buildStyles(style);
791
- return `<label id="${id}" class="${classes}" ${htmlFor ? `for='${escapeHtml2(htmlFor)}'` : ""} ${styleStr ? `style='${styleStr}'` : ""}>${escapeHtml2(text)}${required ? '<span class="stx-label-required">*</span>' : ""}</label>`;
583
+ return `<label id="${id}" class="${classes}" ${htmlFor ? `for='${escapeHtml(htmlFor)}'` : ""} ${styleStr ? `style='${styleStr}'` : ""}>${escapeHtml(text)}${required ? '<span class="stx-label-required">*</span>' : ""}</label>`;
792
584
  }
793
585
  function createImageView(props) {
794
586
  const {
@@ -810,8 +602,8 @@ function createImageView(props) {
810
602
  if (height)
811
603
  imageStyles.height = typeof height === "number" ? `${height}px` : height;
812
604
  const combinedStyle = [buildStyles(imageStyles), buildStyles(style)].filter(Boolean).join("; ");
813
- const fallbackAttr = fallback ? `onerror="this.src='${escapeHtml2(fallback)}'"` : "";
814
- return `<img id="${id}" class="${classes}" src="${escapeHtml2(src)}" alt="${escapeHtml2(alt)}" loading="${loading}" ${combinedStyle ? `style='${combinedStyle}'` : ""} ${fallbackAttr} />`;
605
+ const fallbackAttr = fallback ? `onerror="this.src='${escapeHtml(fallback)}'"` : "";
606
+ return `<img id="${id}" class="${classes}" src="${escapeHtml(src)}" alt="${escapeHtml(alt)}" loading="${loading}" ${combinedStyle ? `style='${combinedStyle}'` : ""} ${fallbackAttr} />`;
815
607
  }
816
608
  function createChip(props) {
817
609
  const {
@@ -830,7 +622,7 @@ function createChip(props) {
830
622
  if (icon) {
831
623
  html += `<span class="stx-chip-icon">${icon}</span>`;
832
624
  }
833
- html += `<span class="stx-chip-text">${escapeHtml2(text)}</span>`;
625
+ html += `<span class="stx-chip-text">${escapeHtml(text)}</span>`;
834
626
  if (removable) {
835
627
  html += '<button class="stx-chip-remove" type="button">&times;</button>';
836
628
  }
@@ -840,7 +632,7 @@ function createTooltip(props) {
840
632
  const {
841
633
  text,
842
634
  position = "top",
843
- delay = 200,
635
+ delay: delay2 = 200,
844
636
  children,
845
637
  id = generateId("tooltip"),
846
638
  className,
@@ -848,9 +640,9 @@ function createTooltip(props) {
848
640
  } = props;
849
641
  const classes = buildClasses("stx-tooltip-wrapper", className);
850
642
  const styleStr = buildStyles(style);
851
- return `<div id="${id}" class="${classes}" ${styleStr ? `style="${styleStr}"` : ""} data-tooltip="${escapeHtml2(text)}" data-position="${position}" data-delay="${delay}">
643
+ return `<div id="${id}" class="${classes}" ${styleStr ? `style="${styleStr}"` : ""} data-tooltip="${escapeHtml(text)}" data-position="${position}" data-delay="${delay2}">
852
644
  ${children}
853
- <span class="stx-tooltip stx-tooltip--${position}">${escapeHtml2(text)}</span>
645
+ <span class="stx-tooltip stx-tooltip--${position}">${escapeHtml(text)}</span>
854
646
  </div>`;
855
647
  }
856
648
  function createScrollView(props) {
@@ -910,11 +702,11 @@ function createAccordion(props) {
910
702
  const itemClasses = buildClasses("stx-accordion-item", isOpen && "stx-accordion-item--open", item.disabled && "stx-accordion-item--disabled");
911
703
  html += `<div class="${itemClasses}" data-index="${index}">
912
704
  <button class="stx-accordion-header" ${item.disabled ? "disabled" : ""} aria-expanded="${isOpen}" data-multiple="${multiple}">
913
- <span class="stx-accordion-title">${escapeHtml2(item.title)}</span>
705
+ <span class="stx-accordion-title">${escapeHtml(item.title)}</span>
914
706
  <span class="stx-accordion-icon">\u25BC</span>
915
707
  </button>
916
708
  <div class="stx-accordion-content" ${isOpen ? "" : "hidden"}>
917
- ${escapeHtml2(item.content)}
709
+ ${escapeHtml(item.content)}
918
710
  </div>
919
711
  </div>`;
920
712
  });
@@ -939,8 +731,8 @@ function createStepper(props) {
939
731
  ${index < currentStep ? "\u2713" : index + 1}
940
732
  </div>
941
733
  <div class="stx-step-content">
942
- <div class="stx-step-label">${escapeHtml2(step.label)}</div>
943
- ${step.description ? `<div class="stx-step-description">${escapeHtml2(step.description)}</div>` : ""}
734
+ <div class="stx-step-label">${escapeHtml(step.label)}</div>
735
+ ${step.description ? `<div class="stx-step-description">${escapeHtml(step.description)}</div>` : ""}
944
736
  </div>
945
737
  </div>`;
946
738
  if (index < steps.length - 1) {
@@ -971,7 +763,7 @@ function createModalComponent(props) {
971
763
  if (title || closable) {
972
764
  html += '<div class="stx-modal-component-header">';
973
765
  if (title) {
974
- html += `<h3 class="stx-modal-component-title">${escapeHtml2(title)}</h3>`;
766
+ html += `<h3 class="stx-modal-component-title">${escapeHtml(title)}</h3>`;
975
767
  }
976
768
  if (closable) {
977
769
  html += '<button class="stx-modal-component-close">&times;</button>';
@@ -999,13 +791,13 @@ function createListView(props) {
999
791
  const styleStr = buildStyles(style);
1000
792
  if (items.length === 0) {
1001
793
  return `<div id="${id}" class="${classes}" ${styleStr ? `style="${styleStr}"` : ""}>
1002
- <div class="stx-list-view-empty">${escapeHtml2(emptyMessage)}</div>
794
+ <div class="stx-list-view-empty">${escapeHtml(emptyMessage)}</div>
1003
795
  </div>`;
1004
796
  }
1005
797
  let html = '<ul class="stx-list-view-items">';
1006
798
  for (const item of items) {
1007
799
  const itemClasses = buildClasses("stx-list-view-item", item.selected && "stx-list-view-item--selected");
1008
- html += `<li class="${itemClasses}" data-id="${escapeHtml2(item.id)}" ${selectable ? 'tabindex="0"' : ""}>${item.content}</li>`;
800
+ html += `<li class="${itemClasses}" data-id="${escapeHtml(item.id)}" ${selectable ? 'tabindex="0"' : ""}>${item.content}</li>`;
1009
801
  }
1010
802
  html += "</ul>";
1011
803
  return `<div id="${id}" class="${classes}" ${styleStr ? `style='${styleStr}'` : ""}>${html}</div>`;
@@ -1032,7 +824,7 @@ function createTable(props) {
1032
824
  const sortClass = isSorted ? `stx-table-sorted stx-table-sorted--${sortOrder}` : "";
1033
825
  const sortable = col.sortable ? 'data-sortable="true"' : "";
1034
826
  const widthStyle = col.width ? `style="width: ${col.width}"` : "";
1035
- html += `<th class="${sortClass}" ${sortable} ${widthStyle} data-key="${escapeHtml2(col.key)}">${escapeHtml2(col.label)}${col.sortable ? '<span class="stx-table-sort-icon"></span>' : ""}</th>`;
827
+ html += `<th class="${sortClass}" ${sortable} ${widthStyle} data-key="${escapeHtml(col.key)}">${escapeHtml(col.label)}${col.sortable ? '<span class="stx-table-sort-icon"></span>' : ""}</th>`;
1036
828
  }
1037
829
  html += "</tr></thead>";
1038
830
  html += "<tbody>";
@@ -1040,7 +832,7 @@ function createTable(props) {
1040
832
  html += "<tr>";
1041
833
  for (const col of columns) {
1042
834
  const cellValue = row[col.key];
1043
- html += `<td>${cellValue != null ? escapeHtml2(String(cellValue)) : ""}</td>`;
835
+ html += `<td>${cellValue != null ? escapeHtml(String(cellValue)) : ""}</td>`;
1044
836
  }
1045
837
  html += "</tr>";
1046
838
  }
@@ -1052,7 +844,7 @@ function renderTreeNode(node, expandedKeys, selectedKeys, checkable) {
1052
844
  const isSelected = selectedKeys.includes(node.key);
1053
845
  const hasChildren = node.children && node.children.length > 0;
1054
846
  const nodeClasses = buildClasses("stx-tree-node", isSelected && "stx-tree-node--selected", node.disabled && "stx-tree-node--disabled");
1055
- let html = `<div class="${nodeClasses}" data-key="${escapeHtml2(node.key)}">`;
847
+ let html = `<div class="${nodeClasses}" data-key="${escapeHtml(node.key)}">`;
1056
848
  html += '<div class="stx-tree-node-content">';
1057
849
  if (hasChildren) {
1058
850
  html += `<span class="stx-tree-toggle ${isExpanded ? "stx-tree-toggle--expanded" : ""}">\u25B6</span>`;
@@ -1065,7 +857,7 @@ function renderTreeNode(node, expandedKeys, selectedKeys, checkable) {
1065
857
  if (node.icon) {
1066
858
  html += `<span class="stx-tree-icon">${node.icon}</span>`;
1067
859
  }
1068
- html += `<span class="stx-tree-label">${escapeHtml2(node.label)}</span>`;
860
+ html += `<span class="stx-tree-label">${escapeHtml(node.label)}</span>`;
1069
861
  html += "</div>";
1070
862
  if (hasChildren && isExpanded) {
1071
863
  html += '<div class="stx-tree-children">';
@@ -1117,7 +909,7 @@ function createDataGrid(props) {
1117
909
  const widthStyle = col.width ? `style="width: ${col.width}"` : "";
1118
910
  html += `<th ${widthStyle}>
1119
911
  <div class="stx-data-grid-header-cell">
1120
- <span>${escapeHtml2(col.label)}</span>
912
+ <span>${escapeHtml(col.label)}</span>
1121
913
  ${col.sortable ? '<button class="stx-data-grid-sort">\u21C5</button>' : ""}
1122
914
  ${col.filterable ? '<button class="stx-data-grid-filter">\u22BB</button>' : ""}
1123
915
  </div>
@@ -1132,7 +924,7 @@ function createDataGrid(props) {
1132
924
  }
1133
925
  for (const col of columns) {
1134
926
  const cellValue = row[col.key];
1135
- html += `<td>${cellValue != null ? escapeHtml2(String(cellValue)) : ""}</td>`;
927
+ html += `<td>${cellValue != null ? escapeHtml(String(cellValue)) : ""}</td>`;
1136
928
  }
1137
929
  html += "</tr>";
1138
930
  }
@@ -1167,9 +959,9 @@ function createChart(props) {
1167
959
  };
1168
960
  const combinedStyle = [buildStyles(chartStyles), buildStyles(style)].filter(Boolean).join("; ");
1169
961
  const chartData = JSON.stringify({ type, data, options });
1170
- return `<div id="${id}" class="${classes}" style="${combinedStyle}" data-chart='${escapeHtml2(chartData)}'>
962
+ return `<div id="${id}" class="${classes}" style="${combinedStyle}" data-chart='${escapeHtml(chartData)}'>
1171
963
  <div class="stx-chart-placeholder">
1172
- ${options.title ? `<div class="stx-chart-title">${escapeHtml2(options.title)}</div>` : ""}
964
+ ${options.title ? `<div class="stx-chart-title">${escapeHtml(options.title)}</div>` : ""}
1173
965
  <div class="stx-chart-type">${type.charAt(0).toUpperCase() + type.slice(1)} Chart</div>
1174
966
  <div class="stx-chart-info">${data.datasets.length} dataset(s), ${data.labels.length} labels</div>
1175
967
  </div>
@@ -1179,7 +971,7 @@ function createCodeEditor(props) {
1179
971
  const {
1180
972
  value = "",
1181
973
  language = "plaintext",
1182
- theme = "dark",
974
+ theme: theme2 = "dark",
1183
975
  lineNumbers = true,
1184
976
  readOnly = false,
1185
977
  height = 300,
@@ -1189,7 +981,7 @@ function createCodeEditor(props) {
1189
981
  className,
1190
982
  style
1191
983
  } = props;
1192
- const classes = buildClasses("stx-code-editor", `stx-code-editor--${theme}`, lineNumbers && "stx-code-editor--line-numbers", readOnly && "stx-code-editor--readonly", wordWrap && "stx-code-editor--wrap", className);
984
+ const classes = buildClasses("stx-code-editor", `stx-code-editor--${theme2}`, lineNumbers && "stx-code-editor--line-numbers", readOnly && "stx-code-editor--readonly", wordWrap && "stx-code-editor--wrap", className);
1193
985
  const editorStyles = {
1194
986
  height: typeof height === "number" ? `${height}px` : height
1195
987
  };
@@ -1204,8 +996,8 @@ function createCodeEditor(props) {
1204
996
  }
1205
997
  codeHtml += "</div>";
1206
998
  }
1207
- codeHtml += `<pre class="stx-code-editor-content"><code class="language-${escapeHtml2(language)}">${escapeHtml2(value)}</code></pre>`;
1208
- return `<div id="${id}" class="${classes}" style="${combinedStyle}" data-language="${escapeHtml2(language)}" data-tab-size="${tabSize}">${codeHtml}</div>`;
999
+ codeHtml += `<pre class="stx-code-editor-content"><code class="language-${escapeHtml(language)}">${escapeHtml(value)}</code></pre>`;
1000
+ return `<div id="${id}" class="${classes}" style="${combinedStyle}" data-language="${escapeHtml(language)}" data-tab-size="${tabSize}">${codeHtml}</div>`;
1209
1001
  }
1210
1002
  function createMediaPlayer(props) {
1211
1003
  const {
@@ -1231,12 +1023,12 @@ function createMediaPlayer(props) {
1231
1023
  const combinedStyle = [buildStyles(mediaStyles), buildStyles(style)].filter(Boolean).join("; ");
1232
1024
  const mediaAttrs = [
1233
1025
  `id="${id}-media"`,
1234
- `src="${escapeHtml2(src)}"`,
1026
+ `src="${escapeHtml(src)}"`,
1235
1027
  controls && "controls",
1236
1028
  autoplay && "autoplay",
1237
1029
  loop && "loop",
1238
1030
  muted && "muted",
1239
- type === "video" && poster && `poster="${escapeHtml2(poster)}"`
1031
+ type === "video" && poster && `poster="${escapeHtml(poster)}"`
1240
1032
  ].filter(Boolean).join(" ");
1241
1033
  const mediaElement = type === "video" ? `<video ${mediaAttrs}>Your browser does not support the video tag.</video>` : `<audio ${mediaAttrs}>Your browser does not support the audio tag.</audio>`;
1242
1034
  return `<div id="${id}" class="${classes}" ${combinedStyle ? `style='${combinedStyle}'` : ""}>${mediaElement}</div>`;
@@ -1273,8 +1065,8 @@ function createFileExplorer(props) {
1273
1065
  const icon = file.icon || (file.type === "folder" ? "\uD83D\uDCC1" : "\uD83D\uDCC4");
1274
1066
  const size = file.type === "file" ? formatFileSize(file.size) : "";
1275
1067
  const modified = file.modified ? file.modified.toLocaleDateString() : "";
1276
- html += `<tr class="stx-file-explorer-item ${selectable ? "stx-file-explorer-item--selectable" : ""}" data-path="${escapeHtml2(file.path)}" data-type="${file.type}">
1277
- <td><span class="stx-file-explorer-icon">${icon}</span>${escapeHtml2(file.name)}</td>
1068
+ html += `<tr class="stx-file-explorer-item ${selectable ? "stx-file-explorer-item--selectable" : ""}" data-path="${escapeHtml(file.path)}" data-type="${file.type}">
1069
+ <td><span class="stx-file-explorer-icon">${icon}</span>${escapeHtml(file.name)}</td>
1278
1070
  <td>${size}</td>
1279
1071
  <td>${modified}</td>
1280
1072
  </tr>`;
@@ -1284,9 +1076,9 @@ function createFileExplorer(props) {
1284
1076
  html = '<div class="stx-file-explorer-grid">';
1285
1077
  for (const file of filteredFiles) {
1286
1078
  const icon = file.icon || (file.type === "folder" ? "\uD83D\uDCC1" : "\uD83D\uDCC4");
1287
- html += `<div class="stx-file-explorer-item ${selectable ? "stx-file-explorer-item--selectable" : ""}" data-path="${escapeHtml2(file.path)}" data-type="${file.type}">
1079
+ html += `<div class="stx-file-explorer-item ${selectable ? "stx-file-explorer-item--selectable" : ""}" data-path="${escapeHtml(file.path)}" data-type="${file.type}">
1288
1080
  <div class="stx-file-explorer-icon">${icon}</div>
1289
- <div class="stx-file-explorer-name">${escapeHtml2(file.name)}</div>
1081
+ <div class="stx-file-explorer-name">${escapeHtml(file.name)}</div>
1290
1082
  </div>`;
1291
1083
  }
1292
1084
  html += "</div>";
@@ -1318,7 +1110,7 @@ function createWebView(props) {
1318
1110
  }
1319
1111
  }
1320
1112
  const sandboxAttr = sandbox ? `sandbox="${sandboxAttrs.join(" ")}"` : "";
1321
- return `<iframe id="${id}" class="${classes}" src="${escapeHtml2(url)}" style="${combinedStyle}" ${sandboxAttr} loading="lazy" frameborder="0"></iframe>`;
1113
+ return `<iframe id="${id}" class="${classes}" src="${escapeHtml(url)}" style="${combinedStyle}" ${sandboxAttr} loading="lazy" frameborder="0"></iframe>`;
1322
1114
  }
1323
1115
  var AVAILABLE_COMPONENTS = [
1324
1116
  "Button",
@@ -1715,385 +1507,26 @@ var COMPONENT_STYLES = `
1715
1507
  .stx-chip--error { background: #3a1a1a; }
1716
1508
  }
1717
1509
  `;
1718
- // src/modals.ts
1719
- function hasNativeDialogSupport() {
1720
- return false;
1721
- }
1722
- function isBrowser2() {
1723
- return typeof window !== "undefined" && typeof document !== "undefined";
1724
- }
1725
- var activeModals = [];
1726
- function generateModalId() {
1727
- return `modal-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
1728
- }
1729
- function getModalIcon(type) {
1730
- switch (type) {
1731
- case "info":
1732
- return "&#x2139;";
1733
- case "warning":
1734
- return "&#x26A0;";
1735
- case "error":
1736
- return "&#x2716;";
1737
- case "success":
1738
- return "&#x2714;";
1739
- case "question":
1740
- return "&#x2753;";
1741
- default:
1742
- return "&#x2139;";
1743
- }
1744
- }
1745
- function getDefaultButtons(type) {
1746
- if (type === "question") {
1747
- return [
1748
- { label: "No", style: "default" },
1749
- { label: "Yes", style: "primary" }
1750
- ];
1510
+ // src/system-tray.ts
1511
+ import process2 from "process";
1512
+ function getPlatform() {
1513
+ if (process2.platform) {
1514
+ return process2.platform;
1751
1515
  }
1752
- return [{ label: "OK", style: "primary" }];
1753
- }
1754
- function createModalHTML(state) {
1755
- const { options } = state;
1756
- const icon = getModalIcon(options.type);
1757
- const buttons = options.buttons || getDefaultButtons(options.type);
1758
- const typeClass = options.type || "info";
1759
- let buttonsHtml = "";
1760
- buttons.forEach((btn, index) => {
1761
- const styleClass = btn.style === "destructive" ? "destructive" : btn.style === "primary" ? "primary" : "default";
1762
- const autoFocus = index === (options.defaultButton ?? buttons.length - 1) ? "autofocus" : "";
1763
- buttonsHtml += `<button class="stx-modal-btn ${styleClass}" data-index="${index}" ${autoFocus}>${btn.label}</button>`;
1764
- });
1765
- return `
1766
- <div class="stx-modal-overlay" data-modal-id="${state.id}">
1767
- <div class="stx-modal ${typeClass}" role="dialog" aria-modal="true" aria-labelledby="${state.id}-title">
1768
- <div class="stx-modal-icon">${icon}</div>
1769
- <div class="stx-modal-content">
1770
- ${options.title ? `<h2 id="${state.id}-title" class="stx-modal-title">${escapeHtml3(options.title)}</h2>` : ""}
1771
- <p class="stx-modal-message">${escapeHtml3(options.message)}</p>
1772
- </div>
1773
- <div class="stx-modal-buttons">${buttonsHtml}</div>
1774
- </div>
1775
- </div>
1776
- `;
1777
- }
1778
- function escapeHtml3(str) {
1779
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
1516
+ return "unknown";
1780
1517
  }
1781
- function closeModal(state, buttonIndex, cancelled = false) {
1782
- const index = activeModals.indexOf(state);
1783
- if (index > -1) {
1784
- activeModals.splice(index, 1);
1785
- }
1786
- if (state.element && isBrowser2()) {
1787
- state.element.remove();
1788
- }
1789
- const buttons = state.options.buttons || getDefaultButtons(state.options.type);
1790
- const button = buttons[buttonIndex];
1791
- if (button?.action) {
1792
- button.action();
1518
+ function isInCraftWindow() {
1519
+ if (typeof window !== "undefined" && window.craft?.tray) {
1520
+ return true;
1793
1521
  }
1794
- state.resolve({ buttonIndex, cancelled });
1522
+ return false;
1795
1523
  }
1796
- async function showModal(options) {
1797
- const hasNative = hasNativeDialogSupport();
1798
- const id = generateModalId();
1799
- return new Promise((resolve) => {
1800
- const state = {
1801
- id,
1802
- options,
1803
- resolve
1804
- };
1805
- activeModals.push(state);
1806
- if (hasNative) {
1807
- console.log(`[stx-modal] Showing native modal: ${options.title || "Modal"}`);
1808
- setTimeout(() => {
1809
- closeModal(state, options.defaultButton ?? 0, false);
1810
- }, 0);
1811
- } else if (isBrowser2()) {
1812
- try {
1813
- const container = document.createElement("div");
1814
- container.innerHTML = createModalHTML(state);
1815
- const overlay = container.firstElementChild;
1816
- if (!overlay) {
1817
- console.log(`[stx-modal] ${options.type?.toUpperCase() || "INFO"}: ${options.title || "Modal"}`);
1818
- console.log(`[stx-modal] ${options.message}`);
1819
- setTimeout(() => {
1820
- closeModal(state, options.defaultButton ?? 0, false);
1821
- }, 0);
1822
- return;
1823
- }
1824
- state.element = overlay;
1825
- try {
1826
- document.body.appendChild(overlay);
1827
- } catch {
1828
- console.log(`[stx-modal] ${options.type?.toUpperCase() || "INFO"}: ${options.title || "Modal"}`);
1829
- console.log(`[stx-modal] ${options.message}`);
1830
- setTimeout(() => {
1831
- closeModal(state, options.defaultButton ?? 0, false);
1832
- }, 0);
1833
- return;
1834
- }
1835
- try {
1836
- overlay.querySelectorAll(".stx-modal-btn").forEach((btn) => {
1837
- btn.addEventListener("click", () => {
1838
- const index = Number.parseInt(btn.dataset.index || "0", 10);
1839
- closeModal(state, index, false);
1840
- });
1841
- });
1842
- } catch {}
1843
- try {
1844
- overlay.addEventListener("click", (e) => {
1845
- if (e.target === overlay) {
1846
- const cancelIndex = options.cancelButton ?? 0;
1847
- closeModal(state, cancelIndex, true);
1848
- }
1849
- });
1850
- } catch {}
1851
- try {
1852
- const handleKeydown = (e) => {
1853
- if (e.key === "Escape") {
1854
- const cancelIndex = options.cancelButton ?? 0;
1855
- closeModal(state, cancelIndex, true);
1856
- document.removeEventListener("keydown", handleKeydown);
1857
- } else if (e.key === "Enter") {
1858
- const defaultIndex = options.defaultButton ?? (options.buttons || getDefaultButtons(options.type)).length - 1;
1859
- closeModal(state, defaultIndex, false);
1860
- document.removeEventListener("keydown", handleKeydown);
1861
- }
1862
- };
1863
- document.addEventListener("keydown", handleKeydown);
1864
- } catch {}
1865
- try {
1866
- const firstButton = overlay.querySelector(".stx-modal-btn[autofocus]");
1867
- if (firstButton) {
1868
- firstButton.focus();
1869
- }
1870
- } catch {}
1871
- if (typeof process !== "undefined" && process.env.BUN_TEST) {
1872
- setTimeout(() => {
1873
- closeModal(state, options.defaultButton ?? 0, false);
1874
- }, 0);
1875
- }
1876
- } catch {
1877
- console.log(`[stx-modal] ${options.type?.toUpperCase() || "INFO"}: ${options.title || "Modal"}`);
1878
- console.log(`[stx-modal] ${options.message}`);
1879
- setTimeout(() => {
1880
- closeModal(state, options.defaultButton ?? 0, false);
1881
- }, 0);
1882
- }
1883
- } else {
1884
- console.log(`[stx-modal] ${options.type?.toUpperCase() || "INFO"}: ${options.title || "Modal"}`);
1885
- console.log(`[stx-modal] ${options.message}`);
1886
- const buttons = options.buttons || getDefaultButtons(options.type);
1887
- console.log(`[stx-modal] Buttons: ${buttons.map((b) => b.label).join(", ")}`);
1888
- setTimeout(() => {
1889
- closeModal(state, options.defaultButton ?? 0, false);
1890
- }, 0);
1891
- }
1892
- });
1893
- }
1894
- async function showInfoModal(title, message) {
1895
- return showModal({ title, message, type: "info" });
1896
- }
1897
- async function showWarningModal(title, message) {
1898
- return showModal({ title, message, type: "warning" });
1899
- }
1900
- async function showErrorModal(title, message) {
1901
- return showModal({ title, message, type: "error" });
1902
- }
1903
- async function showSuccessModal(title, message) {
1904
- return showModal({ title, message, type: "success" });
1905
- }
1906
- async function showQuestionModal(title, message) {
1907
- return showModal({
1908
- title,
1909
- message,
1910
- type: "question",
1911
- buttons: [
1912
- { label: "No", style: "default" },
1913
- { label: "Yes", style: "primary" }
1914
- ],
1915
- defaultButton: 1,
1916
- cancelButton: 0
1917
- });
1918
- }
1919
- async function confirm2(message, title = "Confirm") {
1920
- const result = await showQuestionModal(title, message);
1921
- return result.buttonIndex === 1;
1922
- }
1923
- async function alert2(message, title = "Alert") {
1924
- await showInfoModal(title, message);
1925
- }
1926
- async function prompt2(message, defaultValue = "", title = "Input") {
1927
- if (isBrowser2() && typeof window.prompt === "function") {
1928
- return window.prompt(message, defaultValue);
1929
- }
1930
- console.log(`[stx-modal] PROMPT: ${title}`);
1931
- console.log(`[stx-modal] ${message}`);
1932
- console.log(`[stx-modal] Default: ${defaultValue}`);
1933
- return defaultValue;
1934
- }
1935
- function getActiveModalCount() {
1936
- return activeModals.length;
1937
- }
1938
- function closeAllModals() {
1939
- while (activeModals.length > 0) {
1940
- const state = activeModals[activeModals.length - 1];
1941
- closeModal(state, 0, true);
1942
- }
1943
- }
1944
- var MODAL_STYLES = `
1945
- .stx-modal-overlay {
1946
- position: fixed;
1947
- inset: 0;
1948
- background: rgba(0, 0, 0, 0.5);
1949
- display: flex;
1950
- align-items: center;
1951
- justify-content: center;
1952
- z-index: 10000;
1953
- animation: stx-modal-fade-in 0.15s ease-out;
1954
- }
1955
-
1956
- @keyframes stx-modal-fade-in {
1957
- from { opacity: 0; }
1958
- to { opacity: 1; }
1959
- }
1960
-
1961
- .stx-modal {
1962
- background: #fff;
1963
- border-radius: 12px;
1964
- padding: 24px;
1965
- max-width: 400px;
1966
- width: 90%;
1967
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
1968
- animation: stx-modal-slide-up 0.2s ease-out;
1969
- }
1970
-
1971
- @keyframes stx-modal-slide-up {
1972
- from { transform: translateY(20px); opacity: 0; }
1973
- to { transform: translateY(0); opacity: 1; }
1974
- }
1975
-
1976
- @media (prefers-color-scheme: dark) {
1977
- .stx-modal {
1978
- background: #2d2d2d;
1979
- color: #fff;
1980
- }
1981
- }
1982
-
1983
- .stx-modal-icon {
1984
- font-size: 48px;
1985
- text-align: center;
1986
- margin-bottom: 16px;
1987
- }
1988
-
1989
- .stx-modal.info .stx-modal-icon { color: #3498db; }
1990
- .stx-modal.warning .stx-modal-icon { color: #f39c12; }
1991
- .stx-modal.error .stx-modal-icon { color: #e74c3c; }
1992
- .stx-modal.success .stx-modal-icon { color: #27ae60; }
1993
- .stx-modal.question .stx-modal-icon { color: #9b59b6; }
1994
-
1995
- .stx-modal-content {
1996
- text-align: center;
1997
- margin-bottom: 24px;
1998
- }
1999
-
2000
- .stx-modal-title {
2001
- margin: 0 0 8px;
2002
- font-size: 20px;
2003
- font-weight: 600;
2004
- }
2005
-
2006
- .stx-modal-message {
2007
- margin: 0;
2008
- color: #666;
2009
- line-height: 1.5;
2010
- }
2011
-
2012
- @media (prefers-color-scheme: dark) {
2013
- .stx-modal-message { color: #aaa; }
2014
- }
2015
-
2016
- .stx-modal-buttons {
2017
- display: flex;
2018
- gap: 8px;
2019
- justify-content: center;
2020
- }
2021
-
2022
- .stx-modal-btn {
2023
- padding: 10px 24px;
2024
- border-radius: 6px;
2025
- font-size: 14px;
2026
- font-weight: 500;
2027
- cursor: pointer;
2028
- border: none;
2029
- transition: background 0.15s, transform 0.1s;
2030
- }
2031
-
2032
- .stx-modal-btn:hover {
2033
- transform: translateY(-1px);
2034
- }
2035
-
2036
- .stx-modal-btn:active {
2037
- transform: translateY(0);
2038
- }
2039
-
2040
- .stx-modal-btn.default {
2041
- background: #e0e0e0;
2042
- color: #333;
2043
- }
2044
-
2045
- .stx-modal-btn.default:hover {
2046
- background: #d0d0d0;
2047
- }
2048
-
2049
- .stx-modal-btn.primary {
2050
- background: #3498db;
2051
- color: #fff;
2052
- }
2053
-
2054
- .stx-modal-btn.primary:hover {
2055
- background: #2980b9;
2056
- }
2057
-
2058
- .stx-modal-btn.destructive {
2059
- background: #e74c3c;
2060
- color: #fff;
2061
- }
2062
-
2063
- .stx-modal-btn.destructive:hover {
2064
- background: #c0392b;
2065
- }
2066
-
2067
- @media (prefers-color-scheme: dark) {
2068
- .stx-modal-btn.default {
2069
- background: #444;
2070
- color: #fff;
2071
- }
2072
- .stx-modal-btn.default:hover {
2073
- background: #555;
2074
- }
2075
- }
2076
- `;
2077
- // src/system-tray.ts
2078
- import process2 from "process";
2079
- function getPlatform() {
2080
- if (process2.platform) {
2081
- return process2.platform;
2082
- }
2083
- return "unknown";
2084
- }
2085
- function isInCraftWindow() {
2086
- if (typeof window !== "undefined" && window.craft?.tray) {
2087
- return true;
2088
- }
2089
- return false;
2090
- }
2091
- function convertMenuItem(item, index) {
2092
- if (item.type === "separator") {
2093
- return {
2094
- id: `sep-${index}`,
2095
- label: "",
2096
- type: "separator"
1524
+ function convertMenuItem(item, index) {
1525
+ if (item.type === "separator") {
1526
+ return {
1527
+ id: `sep-${index}`,
1528
+ label: "",
1529
+ type: "separator"
2097
1530
  };
2098
1531
  }
2099
1532
  const menuItem = {
@@ -2137,9 +1570,9 @@ function renderMenuItem(item, level = 0) {
2137
1570
  html += "</div>";
2138
1571
  return html;
2139
1572
  }
2140
- function renderTrayMenu(menu) {
1573
+ function renderTrayMenu(menu2) {
2141
1574
  let html = '<div class="stx-tray-menu">';
2142
- for (const item of menu) {
1575
+ for (const item of menu2) {
2143
1576
  html += renderMenuItem(item);
2144
1577
  }
2145
1578
  html += "</div>";
@@ -2234,10 +1667,10 @@ async function createSystemTray(options = {}) {
2234
1667
  }
2235
1668
  console.log(`[stx-tray:${id}] Tooltip: ${tooltip}`);
2236
1669
  },
2237
- setMenu: (menu) => {
2238
- state.menu = menu;
1670
+ setMenu: (menu2) => {
1671
+ state.menu = menu2;
2239
1672
  handlers.clear();
2240
- for (const item of menu) {
1673
+ for (const item of menu2) {
2241
1674
  if (item.type !== "separator" && item.onClick) {
2242
1675
  handlers.set(item.label, item.onClick);
2243
1676
  }
@@ -2251,12 +1684,12 @@ async function createSystemTray(options = {}) {
2251
1684
  }
2252
1685
  if (inCraft) {
2253
1686
  const craftWindow = window;
2254
- const craftMenu = convertMenuItems(menu);
1687
+ const craftMenu = convertMenuItems(menu2);
2255
1688
  craftWindow.craft.tray.setMenu({ items: craftMenu }).catch((e) => {
2256
1689
  console.warn("[stx-tray] Failed to set menu:", e);
2257
1690
  });
2258
1691
  }
2259
- console.log(`[stx-tray:${id}] Menu updated (${menu.length} items)`);
1692
+ console.log(`[stx-tray:${id}] Menu updated (${menu2.length} items)`);
2260
1693
  },
2261
1694
  destroy: () => {
2262
1695
  state.visible = false;
@@ -2297,11 +1730,11 @@ function getTrayInstance(id) {
2297
1730
  craftWindow.craft.tray.setTooltip({ tooltip }).catch(() => {});
2298
1731
  }
2299
1732
  },
2300
- setMenu: (menu) => {
2301
- instance.state.menu = menu;
1733
+ setMenu: (menu2) => {
1734
+ instance.state.menu = menu2;
2302
1735
  if (inCraft) {
2303
1736
  const craftWindow = window;
2304
- const craftMenu = convertMenuItems(menu);
1737
+ const craftMenu = convertMenuItems(menu2);
2305
1738
  craftWindow.craft.tray.setMenu({ items: craftMenu }).catch(() => {});
2306
1739
  }
2307
1740
  },
@@ -2414,222 +1847,6 @@ var TRAY_MENU_STYLES = `
2414
1847
  display: block;
2415
1848
  }
2416
1849
  `;
2417
- // src/dialogs.ts
2418
- function isInCraftWindow2() {
2419
- if (typeof window !== "undefined" && window.craft?.dialog) {
2420
- return true;
2421
- }
2422
- return false;
2423
- }
2424
- async function showOpenDialog(options = {}) {
2425
- if (isInCraftWindow2()) {
2426
- const craftWindow = window;
2427
- try {
2428
- return await craftWindow.craft.dialog.showOpenDialog(options);
2429
- } catch (error) {
2430
- console.warn("[stx-dialog] Failed to show native open dialog:", error);
2431
- }
2432
- }
2433
- return new Promise((resolve) => {
2434
- if (typeof document === "undefined") {
2435
- resolve({ canceled: true, filePaths: [] });
2436
- return;
2437
- }
2438
- const input = document.createElement("input");
2439
- input.type = "file";
2440
- input.multiple = options.multiSelections ?? false;
2441
- if (options.filters?.length) {
2442
- const extensions = options.filters.flatMap((f) => f.extensions.map((e) => `.${e}`));
2443
- input.accept = extensions.join(",");
2444
- }
2445
- if (options.canChooseDirectories && !options.canChooseFiles) {
2446
- input.webkitdirectory = true;
2447
- }
2448
- input.onchange = () => {
2449
- const files = Array.from(input.files || []);
2450
- if (files.length === 0) {
2451
- resolve({ canceled: true, filePaths: [] });
2452
- } else {
2453
- const filePaths = files.map((f) => f.name);
2454
- resolve({ canceled: false, filePaths });
2455
- }
2456
- };
2457
- input.oncancel = () => {
2458
- resolve({ canceled: true, filePaths: [] });
2459
- };
2460
- input.click();
2461
- });
2462
- }
2463
- async function showSaveDialog(options = {}) {
2464
- if (isInCraftWindow2()) {
2465
- const craftWindow = window;
2466
- try {
2467
- return await craftWindow.craft.dialog.showSaveDialog(options);
2468
- } catch (error) {
2469
- console.warn("[stx-dialog] Failed to show native save dialog:", error);
2470
- }
2471
- }
2472
- if (typeof window !== "undefined" && "showSaveFilePicker" in window) {
2473
- try {
2474
- const fileTypes = options.filters?.map((f) => ({
2475
- description: f.name,
2476
- accept: {
2477
- "*/*": f.extensions.map((e) => `.${e}`)
2478
- }
2479
- }));
2480
- const handle = await window.showSaveFilePicker({
2481
- suggestedName: options.defaultPath,
2482
- types: fileTypes
2483
- });
2484
- return { canceled: false, filePath: handle.name };
2485
- } catch (error) {
2486
- return { canceled: true };
2487
- }
2488
- }
2489
- console.warn("[stx-dialog] Save dialog not available, using prompt fallback");
2490
- const filename = prompt("Enter filename:", options.defaultPath || "file.txt");
2491
- if (filename) {
2492
- return { canceled: false, filePath: filename };
2493
- }
2494
- return { canceled: true };
2495
- }
2496
- async function showMessageBox(options) {
2497
- if (isInCraftWindow2()) {
2498
- const craftWindow = window;
2499
- try {
2500
- return await craftWindow.craft.dialog.showMessageBox(options);
2501
- } catch (error) {
2502
- console.warn("[stx-dialog] Failed to show native message box:", error);
2503
- }
2504
- }
2505
- const buttons = options.buttons || ["OK"];
2506
- if (buttons.length === 1) {
2507
- alert(options.message);
2508
- return { response: 0 };
2509
- }
2510
- if (buttons.length === 2 && options.type === "question") {
2511
- const confirmed2 = confirm(options.message);
2512
- return { response: confirmed2 ? 1 : 0 };
2513
- }
2514
- console.warn("[stx-dialog] Complex message box not fully supported in browser, using confirm");
2515
- const confirmed = confirm(`${options.message}
2516
-
2517
- ${buttons.join(" / ")}`);
2518
- return { response: confirmed ? buttons.length - 1 : 0 };
2519
- }
2520
- async function showColorPicker(options = {}) {
2521
- if (isInCraftWindow2()) {
2522
- const craftWindow = window;
2523
- try {
2524
- return await craftWindow.craft.dialog.showColorPicker(options);
2525
- } catch (error) {
2526
- console.warn("[stx-dialog] Failed to show native color picker:", error);
2527
- }
2528
- }
2529
- return new Promise((resolve) => {
2530
- if (typeof document === "undefined") {
2531
- resolve({ canceled: true });
2532
- return;
2533
- }
2534
- const input = document.createElement("input");
2535
- input.type = "color";
2536
- input.value = options.color || "#000000";
2537
- input.onchange = () => {
2538
- resolve({ canceled: false, color: input.value });
2539
- };
2540
- input.oncancel = () => {
2541
- resolve({ canceled: true });
2542
- };
2543
- input.click();
2544
- });
2545
- }
2546
- async function showAlertDialog(message, title) {
2547
- await showMessageBox({
2548
- type: "info",
2549
- title: title || "Alert",
2550
- message,
2551
- buttons: ["OK"]
2552
- });
2553
- }
2554
- async function showConfirmDialog(message, title) {
2555
- const result = await showMessageBox({
2556
- type: "question",
2557
- title: title || "Confirm",
2558
- message,
2559
- buttons: ["Cancel", "OK"],
2560
- defaultButton: 1,
2561
- cancelButton: 0
2562
- });
2563
- return result.response === 1;
2564
- }
2565
- async function showErrorDialog(message, title) {
2566
- await showMessageBox({
2567
- type: "error",
2568
- title: title || "Error",
2569
- message,
2570
- buttons: ["OK"]
2571
- });
2572
- }
2573
- async function showWarningDialog(message, title) {
2574
- await showMessageBox({
2575
- type: "warning",
2576
- title: title || "Warning",
2577
- message,
2578
- buttons: ["OK"]
2579
- });
2580
- }
2581
- function getDialogBridgeScript() {
2582
- return `
2583
- // STX Desktop Dialog Bridge
2584
- // Provides convenient wrappers around window.craft.dialog APIs
2585
- window.stxDialog = {
2586
- // File dialogs
2587
- showOpenDialog: (options) => window.craft?.dialog?.showOpenDialog(options),
2588
- showSaveDialog: (options) => window.craft?.dialog?.showSaveDialog(options),
2589
-
2590
- // Message dialogs
2591
- showMessageBox: (options) => window.craft?.dialog?.showMessageBox(options),
2592
-
2593
- // Color picker
2594
- showColorPicker: (options) => window.craft?.dialog?.showColorPicker(options),
2595
-
2596
- // Font picker
2597
- showFontPicker: (options) => window.craft?.dialog?.showFontPicker(options),
2598
-
2599
- // Convenience functions
2600
- alert: async (message, title) => {
2601
- return window.craft?.dialog?.showMessageBox({
2602
- type: 'info',
2603
- title: title || 'Alert',
2604
- message,
2605
- buttons: ['OK'],
2606
- });
2607
- },
2608
-
2609
- confirm: async (message, title) => {
2610
- const result = await window.craft?.dialog?.showMessageBox({
2611
- type: 'question',
2612
- title: title || 'Confirm',
2613
- message,
2614
- buttons: ['Cancel', 'OK'],
2615
- });
2616
- return result?.response === 1;
2617
- },
2618
-
2619
- error: async (message, title) => {
2620
- return window.craft?.dialog?.showMessageBox({
2621
- type: 'error',
2622
- title: title || 'Error',
2623
- message,
2624
- buttons: ['OK'],
2625
- });
2626
- },
2627
-
2628
- // Check if dialog is available
2629
- isAvailable: () => typeof window.craft?.dialog !== 'undefined',
2630
- };
2631
- `;
2632
- }
2633
1850
  // src/window.ts
2634
1851
  import process3 from "process";
2635
1852
  import { existsSync } from "fs";
@@ -2689,7 +1906,7 @@ function closeAllWindows() {
2689
1906
  }
2690
1907
  activeWindows.clear();
2691
1908
  }
2692
- function createWindowInstance(id, app) {
1909
+ function createWindowInstance(id, app2) {
2693
1910
  return {
2694
1911
  id,
2695
1912
  show: () => {
@@ -2772,7 +1989,7 @@ async function createWindow(url, options = {}) {
2772
1989
  const craftPath = getCraftBinaryPath();
2773
1990
  try {
2774
1991
  const { createApp } = await import("craft-native");
2775
- const app = createApp({
1992
+ const app2 = createApp({
2776
1993
  url,
2777
1994
  craftPath,
2778
1995
  window: {
@@ -2793,9 +2010,9 @@ async function createWindow(url, options = {}) {
2793
2010
  sidebarConfig: prepareSidebarConfig(options)
2794
2011
  }
2795
2012
  });
2796
- activeWindows.set(id, { app, url, options });
2797
- await app.show();
2798
- return createWindowInstance(id, app);
2013
+ activeWindows.set(id, { app: app2, url, options });
2014
+ await app2.show();
2015
+ return createWindowInstance(id, app2);
2799
2016
  } catch {
2800
2017
  try {
2801
2018
  const { spawn } = await import("child_process");
@@ -2803,9 +2020,9 @@ async function createWindow(url, options = {}) {
2803
2020
  child.on("error", (err) => {
2804
2021
  console.error(err.code === "ENOENT" ? craftBinaryNotFoundMessage(craftPath) : `craft child error: ${err.message}`);
2805
2022
  });
2806
- const app = { close: () => child.kill() };
2807
- activeWindows.set(id, { app, url, options });
2808
- return createWindowInstance(id, app);
2023
+ const app2 = { close: () => child.kill() };
2024
+ activeWindows.set(id, { app: app2, url, options });
2025
+ return createWindowInstance(id, app2);
2809
2026
  } catch (binaryError) {
2810
2027
  console.error("Failed to spawn craft binary:", binaryError.message);
2811
2028
  return null;
@@ -2825,7 +2042,7 @@ async function openDevWindow(port, options = {}) {
2825
2042
  console.log("\u26A1 Opening native window via craft-native\u2026");
2826
2043
  const useSystemTray = !options.nativeSidebar;
2827
2044
  const sidebarConfig = prepareSidebarConfig(options);
2828
- const app = createApp({
2045
+ const app2 = createApp({
2829
2046
  url,
2830
2047
  craftPath: getCraftBinaryPath(),
2831
2048
  window: {
@@ -2844,8 +2061,8 @@ async function openDevWindow(port, options = {}) {
2844
2061
  }
2845
2062
  });
2846
2063
  const id = `dev-window-${port}`;
2847
- activeWindows.set(id, { app, url, options });
2848
- await app.show();
2064
+ activeWindows.set(id, { app: app2, url, options });
2065
+ await app2.show();
2849
2066
  console.log(`\u2713 Native window opened at ${url}`);
2850
2067
  return true;
2851
2068
  } catch {
@@ -2905,7 +2122,7 @@ async function createWindowWithHTML(html, options = {}) {
2905
2122
  try {
2906
2123
  const { createApp } = await import("craft-native");
2907
2124
  const craftPath = getCraftBinaryPath();
2908
- const app = createApp({
2125
+ const app2 = createApp({
2909
2126
  html,
2910
2127
  craftPath,
2911
2128
  window: {
@@ -2920,9 +2137,9 @@ async function createWindowWithHTML(html, options = {}) {
2920
2137
  }
2921
2138
  });
2922
2139
  const id = `craft-window-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
2923
- activeWindows.set(id, { app, url: "html-content", options });
2924
- await app.show();
2925
- return createWindowInstance(id, app);
2140
+ activeWindows.set(id, { app: app2, url: "html-content", options });
2141
+ await app2.show();
2142
+ return createWindowInstance(id, app2);
2926
2143
  } catch (error) {
2927
2144
  console.error("Failed to create window with HTML:", error);
2928
2145
  return null;
@@ -3016,179 +2233,6 @@ function createGitHubUpdateManifest(options) {
3016
2233
  platforms
3017
2234
  });
3018
2235
  }
3019
- // src/power.ts
3020
- var currentProcess = null;
3021
- var currentInstance = null;
3022
-
3023
- class CaffeinateInstanceImpl {
3024
- _process;
3025
- _startedAt;
3026
- _endsAt;
3027
- _options;
3028
- _expireHandlers = [];
3029
- _expireTimer = null;
3030
- _stopped = false;
3031
- constructor(process4, options) {
3032
- this._process = process4;
3033
- this._options = options;
3034
- this._startedAt = new Date;
3035
- const duration = options.duration;
3036
- if (duration && duration > 0) {
3037
- const durationMs = duration * 60 * 1000;
3038
- this._endsAt = new Date(this._startedAt.getTime() + durationMs);
3039
- this._expireTimer = setTimeout(() => {
3040
- this._stopped = true;
3041
- for (const handler of this._expireHandlers) {
3042
- try {
3043
- handler();
3044
- } catch {}
3045
- }
3046
- }, durationMs);
3047
- } else {
3048
- this._endsAt = null;
3049
- }
3050
- }
3051
- get pid() {
3052
- return this._process.pid;
3053
- }
3054
- get startedAt() {
3055
- return this._startedAt;
3056
- }
3057
- get endsAt() {
3058
- return this._endsAt;
3059
- }
3060
- get options() {
3061
- return { ...this._options };
3062
- }
3063
- get isActive() {
3064
- if (this._stopped)
3065
- return false;
3066
- return this._process.exitCode === null;
3067
- }
3068
- get remainingMs() {
3069
- if (!this._endsAt)
3070
- return null;
3071
- const remaining = this._endsAt.getTime() - Date.now();
3072
- return Math.max(0, remaining);
3073
- }
3074
- get elapsedMs() {
3075
- return Date.now() - this._startedAt.getTime();
3076
- }
3077
- stop() {
3078
- if (this._stopped)
3079
- return;
3080
- this._stopped = true;
3081
- if (this._expireTimer) {
3082
- clearTimeout(this._expireTimer);
3083
- this._expireTimer = null;
3084
- }
3085
- try {
3086
- this._process.kill();
3087
- } catch {}
3088
- }
3089
- onExpire(handler) {
3090
- this._expireHandlers.push(handler);
3091
- }
3092
- }
3093
- function caffeinate(options = {}) {
3094
- decaffeinate();
3095
- const {
3096
- duration,
3097
- preventDisplaySleep = true,
3098
- preventIdleSleep = true,
3099
- preventSystemSleep = true,
3100
- preventDiskSleep = false,
3101
- assertUserActivity = true
3102
- } = options;
3103
- const flags = [];
3104
- if (preventDisplaySleep)
3105
- flags.push("-d");
3106
- if (preventIdleSleep)
3107
- flags.push("-i");
3108
- if (preventSystemSleep)
3109
- flags.push("-s");
3110
- if (preventDiskSleep)
3111
- flags.push("-m");
3112
- if (assertUserActivity)
3113
- flags.push("-u");
3114
- const args = [...flags];
3115
- if (duration && duration > 0) {
3116
- args.push("-t", String(duration * 60));
3117
- }
3118
- const proc = Bun.spawn(["/usr/bin/caffeinate", ...args], {
3119
- stdio: ["ignore", "ignore", "ignore"]
3120
- });
3121
- const instance = new CaffeinateInstanceImpl(proc, options);
3122
- currentProcess = proc;
3123
- currentInstance = instance;
3124
- return instance;
3125
- }
3126
- function decaffeinate(instance) {
3127
- if (instance) {
3128
- instance.stop();
3129
- if (currentInstance === instance) {
3130
- currentProcess = null;
3131
- currentInstance = null;
3132
- }
3133
- return;
3134
- }
3135
- if (currentInstance) {
3136
- currentInstance.stop();
3137
- }
3138
- currentProcess = null;
3139
- currentInstance = null;
3140
- }
3141
- function isCaffeinated() {
3142
- return currentInstance !== null && currentInstance.isActive;
3143
- }
3144
- function getCaffeinateStatus() {
3145
- if (!currentInstance || !currentInstance.isActive) {
3146
- return {
3147
- active: false,
3148
- instance: null,
3149
- startedAt: null,
3150
- endsAt: null,
3151
- durationMinutes: null
3152
- };
3153
- }
3154
- const opts = currentInstance.options;
3155
- const duration = opts.duration;
3156
- return {
3157
- active: true,
3158
- instance: currentInstance,
3159
- startedAt: currentInstance.startedAt,
3160
- endsAt: currentInstance.endsAt,
3161
- durationMinutes: duration && duration > 0 ? duration : -1
3162
- };
3163
- }
3164
- function formatRemainingTime(instance) {
3165
- const inst = instance || currentInstance;
3166
- if (!inst || !inst.isActive)
3167
- return "0:00";
3168
- const remaining = inst.remainingMs;
3169
- if (remaining === null)
3170
- return "\u221E";
3171
- const totalSeconds = Math.ceil(remaining / 1000);
3172
- const hours = Math.floor(totalSeconds / 3600);
3173
- const minutes = Math.floor(totalSeconds % 3600 / 60);
3174
- const seconds = totalSeconds % 60;
3175
- if (hours > 0)
3176
- return `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
3177
- return `${minutes}:${String(seconds).padStart(2, "0")}`;
3178
- }
3179
- function formatDuration(minutes) {
3180
- if (minutes <= 0 || minutes === -1)
3181
- return "Indefinitely";
3182
- if (minutes < 60)
3183
- return `${minutes} minutes`;
3184
- if (minutes === 60)
3185
- return "1 hour";
3186
- if (minutes % 60 === 0)
3187
- return `${minutes / 60} hours`;
3188
- const h = Math.floor(minutes / 60);
3189
- const m = minutes % 60;
3190
- return `${h}h ${m}m`;
3191
- }
3192
2236
  // src/preferences.ts
3193
2237
  import { existsSync as existsSync2, mkdirSync, readFileSync, watch, writeFileSync } from "fs";
3194
2238
  import { homedir, platform } from "os";
@@ -3486,1421 +2530,25 @@ async function isAutoLaunchEnabled(appName) {
3486
2530
  return false;
3487
2531
  }
3488
2532
  }
3489
- // src/hotkeys.ts
3490
- var registrations = new Map;
3491
- var nextId = 0;
3492
- var documentListenerAttached = false;
3493
- function parseShortcut(shortcut) {
3494
- const parts = shortcut.split("+").map((p) => p.trim());
3495
- const result = {
3496
- key: "",
3497
- meta: false,
3498
- ctrl: false,
3499
- shift: false,
3500
- alt: false
3501
- };
3502
- for (const part of parts) {
3503
- const lower = part.toLowerCase();
3504
- switch (lower) {
3505
- case "cmd":
3506
- case "command":
3507
- case "meta":
3508
- case "\u2318":
3509
- result.meta = true;
3510
- break;
3511
- case "ctrl":
3512
- case "control":
3513
- case "\u2303":
3514
- result.ctrl = true;
3515
- break;
3516
- case "shift":
3517
- case "\u21E7":
3518
- result.shift = true;
3519
- break;
3520
- case "alt":
3521
- case "option":
3522
- case "opt":
3523
- case "\u2325":
3524
- result.alt = true;
3525
- break;
3526
- case "cmdorctrl":
3527
- case "commandorcontrol":
3528
- if (typeof process !== "undefined" && process.platform === "darwin") {
3529
- result.meta = true;
3530
- } else {
3531
- result.ctrl = true;
3532
- }
3533
- break;
3534
- default:
3535
- result.key = lower;
3536
- }
3537
- }
3538
- return result;
3539
- }
3540
- function formatShortcut(parsed) {
3541
- const parts = [];
3542
- if (parsed.ctrl)
3543
- parts.push("\u2303");
3544
- if (parsed.alt)
3545
- parts.push("\u2325");
3546
- if (parsed.shift)
3547
- parts.push("\u21E7");
3548
- if (parsed.meta)
3549
- parts.push("\u2318");
3550
- parts.push(parsed.key.toUpperCase());
3551
- return parts.join("");
3552
- }
3553
- function matchesEvent(event, parsed) {
3554
- if (parsed.meta !== event.metaKey)
3555
- return false;
3556
- if (parsed.ctrl !== event.ctrlKey)
3557
- return false;
3558
- if (parsed.shift !== event.shiftKey)
3559
- return false;
3560
- if (parsed.alt !== event.altKey)
3561
- return false;
3562
- const eventKey = event.key.toLowerCase();
3563
- return eventKey === parsed.key || event.code.toLowerCase() === `key${parsed.key}`;
3564
- }
3565
- function handleKeyDown(event) {
3566
- for (const [, reg] of registrations) {
3567
- if (matchesEvent(event, reg.parsed)) {
3568
- event.preventDefault();
3569
- event.stopPropagation();
3570
- try {
3571
- reg.handler();
3572
- } catch {}
3573
- break;
3574
- }
3575
- }
3576
- }
3577
- function ensureDocumentListener() {
3578
- if (documentListenerAttached)
3579
- return;
3580
- if (typeof document === "undefined")
3581
- return;
3582
- document.addEventListener("keydown", handleKeyDown, true);
3583
- documentListenerAttached = true;
3584
- }
3585
- function removeDocumentListener() {
3586
- if (!documentListenerAttached)
3587
- return;
3588
- if (typeof document === "undefined")
3589
- return;
3590
- document.removeEventListener("keydown", handleKeyDown, true);
3591
- documentListenerAttached = false;
3592
- }
3593
- async function registerWithCraft(id, shortcut) {
3594
- if (typeof window === "undefined")
3595
- return false;
3596
- const craft = window.craft;
3597
- if (!craft?.hotkeys?.register)
3598
- return false;
3599
- try {
3600
- await craft.hotkeys.register(id, shortcut);
3601
- return true;
3602
- } catch {
3603
- return false;
3604
- }
3605
- }
3606
- async function unregisterWithCraft(id) {
3607
- if (typeof window === "undefined")
3608
- return;
3609
- const craft = window.craft;
3610
- if (!craft?.hotkeys?.unregister)
3611
- return;
3612
- try {
3613
- await craft.hotkeys.unregister(id);
3614
- } catch {}
3615
- }
3616
- function registerHotkey(shortcut, handler) {
3617
- const id = `hotkey_${++nextId}_${Date.now()}`;
3618
- const parsed = parseShortcut(shortcut);
3619
- registrations.set(id, { shortcut, handler, parsed });
3620
- registerWithCraft(id, shortcut);
3621
- ensureDocumentListener();
3622
- const registration = {
3623
- shortcut,
3624
- id,
3625
- unregister() {
3626
- unregisterHotkey(registration);
3627
- }
3628
- };
3629
- return registration;
3630
- }
3631
- function unregisterHotkey(registration) {
3632
- registrations.delete(registration.id);
3633
- unregisterWithCraft(registration.id);
3634
- if (registrations.size === 0) {
3635
- removeDocumentListener();
3636
- }
3637
- }
3638
- function unregisterAllHotkeys() {
3639
- for (const [id] of registrations) {
3640
- unregisterWithCraft(id);
3641
- }
3642
- registrations.clear();
3643
- removeDocumentListener();
3644
- }
3645
- function getRegisteredHotkeys() {
3646
- return Array.from(registrations.entries()).map(([id, reg]) => ({
3647
- shortcut: reg.shortcut,
3648
- id,
3649
- unregister() {
3650
- registrations.delete(id);
3651
- unregisterWithCraft(id);
3652
- if (registrations.size === 0) {
3653
- removeDocumentListener();
3654
- }
3655
- }
3656
- }));
3657
- }
3658
- // src/timer.ts
3659
- class TimerImpl {
3660
- _duration;
3661
- _tickInterval;
3662
- _remaining;
3663
- _running = false;
3664
- _paused = false;
3665
- _complete = false;
3666
- _intervalId = null;
3667
- _lastTick = 0;
3668
- _completionHandlers = new Set;
3669
- _tickHandlers = new Set;
3670
- constructor(options) {
3671
- this._duration = options.duration;
3672
- this._tickInterval = options.tickInterval || 1000;
3673
- this._remaining = options.duration;
3674
- if (options.onComplete)
3675
- this._completionHandlers.add(options.onComplete);
3676
- if (options.onTick)
3677
- this._tickHandlers.add(options.onTick);
3678
- if (options.autoStart)
3679
- this.start();
3680
- }
3681
- get isRunning() {
3682
- return this._running && !this._paused;
3683
- }
3684
- get isPaused() {
3685
- return this._paused;
3686
- }
3687
- get isComplete() {
3688
- return this._complete;
3689
- }
3690
- get remaining() {
3691
- if (this._running && !this._paused) {
3692
- const elapsed = Date.now() - this._lastTick;
3693
- return Math.max(0, this._remaining - elapsed);
3694
- }
3695
- return Math.max(0, this._remaining);
3696
- }
3697
- get elapsed() {
3698
- return this._duration - this.remaining;
3699
- }
3700
- get duration() {
3701
- return this._duration;
3702
- }
3703
- get progress() {
3704
- if (this._duration === 0)
3705
- return 1;
3706
- return Math.min(1, this.elapsed / this._duration);
3707
- }
3708
- start() {
3709
- if (this._running)
3710
- return;
3711
- this._running = true;
3712
- this._paused = false;
3713
- this._complete = false;
3714
- this._lastTick = Date.now();
3715
- this._startInterval();
3716
- }
3717
- stop() {
3718
- this._clearInterval();
3719
- this._running = false;
3720
- this._paused = false;
3721
- this._remaining = this._duration;
3722
- }
3723
- pause() {
3724
- if (!this._running || this._paused)
3725
- return;
3726
- const elapsed = Date.now() - this._lastTick;
3727
- this._remaining = Math.max(0, this._remaining - elapsed);
3728
- this._clearInterval();
3729
- this._paused = true;
3730
- }
3731
- resume() {
3732
- if (!this._paused)
3733
- return;
3734
- this._paused = false;
3735
- this._lastTick = Date.now();
3736
- this._startInterval();
3737
- }
3738
- reset() {
3739
- this._clearInterval();
3740
- this._running = false;
3741
- this._paused = false;
3742
- this._complete = false;
3743
- this._remaining = this._duration;
3744
- }
3745
- onComplete(handler) {
3746
- this._completionHandlers.add(handler);
3747
- return () => {
3748
- this._completionHandlers.delete(handler);
3749
- };
3750
- }
3751
- onTick(handler) {
3752
- this._tickHandlers.add(handler);
3753
- return () => {
3754
- this._tickHandlers.delete(handler);
3755
- };
3756
- }
3757
- _startInterval() {
3758
- this._clearInterval();
3759
- this._intervalId = setInterval(() => {
3760
- const now = Date.now();
3761
- const elapsed = now - this._lastTick;
3762
- this._lastTick = now;
3763
- this._remaining = Math.max(0, this._remaining - elapsed);
3764
- for (const handler of this._tickHandlers) {
3765
- try {
3766
- handler(this._remaining);
3767
- } catch {}
3768
- }
3769
- if (this._remaining <= 0) {
3770
- this._clearInterval();
3771
- this._running = false;
3772
- this._complete = true;
3773
- for (const handler of this._completionHandlers) {
3774
- try {
3775
- handler();
3776
- } catch {}
3777
- }
3778
- }
3779
- }, this._tickInterval);
3780
- }
3781
- _clearInterval() {
3782
- if (this._intervalId !== null) {
3783
- clearInterval(this._intervalId);
3784
- this._intervalId = null;
3785
- }
3786
- }
3787
- }
3788
-
3789
- class IntervalImpl {
3790
- _interval;
3791
- _handler;
3792
- _immediate;
3793
- _running = false;
3794
- _intervalId = null;
3795
- constructor(options) {
3796
- this._interval = options.interval;
3797
- this._handler = options.handler;
3798
- this._immediate = options.immediate !== false;
3799
- if (this._immediate)
3800
- this.start();
3801
- }
3802
- get isRunning() {
3803
- return this._running;
3804
- }
3805
- start() {
3806
- if (this._running)
3807
- return;
3808
- this._running = true;
3809
- this._intervalId = setInterval(() => {
3810
- try {
3811
- this._handler();
3812
- } catch {}
3813
- }, this._interval);
3814
- }
3815
- stop() {
3816
- if (this._intervalId !== null) {
3817
- clearInterval(this._intervalId);
3818
- this._intervalId = null;
3819
- }
3820
- this._running = false;
3821
- }
3822
- }
3823
- function createTimer(options) {
3824
- return new TimerImpl(options);
3825
- }
3826
- function createInterval(options) {
3827
- return new IntervalImpl(options);
3828
- }
3829
- function delay(ms) {
3830
- return new Promise((resolve) => setTimeout(resolve, ms));
3831
- }
3832
- function formatTime(ms) {
3833
- const totalSeconds = Math.ceil(ms / 1000);
3834
- const hours = Math.floor(totalSeconds / 3600);
3835
- const minutes = Math.floor(totalSeconds % 3600 / 60);
3836
- const seconds = totalSeconds % 60;
3837
- if (hours > 0)
3838
- return `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
3839
- return `${minutes}:${String(seconds).padStart(2, "0")}`;
3840
- }
3841
- function formatCompact(ms) {
3842
- const totalSeconds = Math.ceil(ms / 1000);
3843
- const hours = Math.floor(totalSeconds / 3600);
3844
- const minutes = Math.floor(totalSeconds % 3600 / 60);
3845
- const seconds = totalSeconds % 60;
3846
- if (hours > 0) {
3847
- if (minutes > 0)
3848
- return `${hours}h ${minutes}m`;
3849
- return `${hours}h`;
3850
- }
3851
- if (minutes > 0) {
3852
- if (seconds > 0)
3853
- return `${minutes}m ${seconds}s`;
3854
- return `${minutes}m`;
3855
- }
3856
- return `${seconds}s`;
3857
- }
3858
- // src/_bridge.ts
3859
- function hasBridge(ns) {
3860
- if (typeof window === "undefined")
3861
- return false;
3862
- const c = window.craft;
3863
- return !!(c && c[ns]);
3864
- }
3865
- function requireBridge(ns) {
3866
- if (!hasBridge(ns)) {
3867
- throw new Error(`craft.${ns} is not available \u2014 this API requires a Craft native window`);
3868
- }
3869
- return window.craft[ns];
3870
- }
3871
- function onCraftEvent(name, cb) {
3872
- if (typeof window === "undefined")
3873
- return () => {};
3874
- const h = (e) => cb(e.detail ?? {});
3875
- window.addEventListener(name, h);
3876
- return () => window.removeEventListener(name, h);
3877
- }
3878
-
3879
- // src/clipboard.ts
3880
- var clipboard = {
3881
- async writeText(text) {
3882
- if (hasBridge("clipboard")) {
3883
- await window.craft.clipboard.writeText(text);
3884
- return;
3885
- }
3886
- if (typeof navigator !== "undefined" && navigator.clipboard) {
3887
- await navigator.clipboard.writeText(text);
3888
- }
3889
- },
3890
- async readText() {
3891
- if (hasBridge("clipboard")) {
3892
- const v = await window.craft.clipboard.readText();
3893
- return typeof v === "string" ? v : "";
3894
- }
3895
- if (typeof navigator !== "undefined" && navigator.clipboard?.readText) {
3896
- try {
3897
- return await navigator.clipboard.readText();
3898
- } catch {
3899
- return "";
3900
- }
3901
- }
3902
- return "";
3903
- },
3904
- async writeHTML(html) {
3905
- if (hasBridge("clipboard")) {
3906
- await window.craft.clipboard.writeHTML(html);
3907
- return;
3908
- }
3909
- if (typeof navigator !== "undefined" && navigator.clipboard?.write) {
3910
- try {
3911
- const item = new window.ClipboardItem({
3912
- "text/html": new Blob([html], { type: "text/html" }),
3913
- "text/plain": new Blob([stripHtml(html)], { type: "text/plain" })
3914
- });
3915
- await navigator.clipboard.write([item]);
3916
- } catch {}
3917
- }
3918
- },
3919
- async readHTML() {
3920
- if (hasBridge("clipboard")) {
3921
- const v = await window.craft.clipboard.readHTML();
3922
- return typeof v === "string" ? v : "";
3923
- }
3924
- if (typeof navigator !== "undefined" && navigator.clipboard?.read) {
3925
- try {
3926
- const items = await navigator.clipboard.read();
3927
- for (const item of items) {
3928
- if (item.types.includes("text/html")) {
3929
- const blob = await item.getType("text/html");
3930
- return await blob.text();
3931
- }
3932
- }
3933
- } catch {}
3934
- }
3935
- return "";
3936
- },
3937
- async clear() {
3938
- if (hasBridge("clipboard")) {
3939
- await window.craft.clipboard.clear();
3940
- return;
3941
- }
3942
- if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
3943
- try {
3944
- await navigator.clipboard.writeText("");
3945
- } catch {}
3946
- }
3947
- },
3948
- async hasText() {
3949
- if (hasBridge("clipboard")) {
3950
- return await window.craft.clipboard.hasText();
3951
- }
3952
- return (await this.readText()).length > 0;
3953
- },
3954
- async hasHTML() {
3955
- if (hasBridge("clipboard")) {
3956
- return await window.craft.clipboard.hasHTML();
3957
- }
3958
- return (await this.readHTML()).length > 0;
3959
- },
3960
- async hasImage() {
3961
- if (hasBridge("clipboard")) {
3962
- return await window.craft.clipboard.hasImage();
3963
- }
3964
- return false;
3965
- }
3966
- };
3967
- function stripHtml(html) {
3968
- return html.replace(/<(script|style)[^>]*>[\s\S]*?<\/\1>/gi, "").replace(/<\/?(br|p|div|li|h[1-6])\b[^>]*>/gi, `
3969
- `).replace(/<[^>]+>/g, "").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/\n{3,}/g, `
3970
-
3971
- `).trim();
3972
- }
3973
- // src/notifications.ts
3974
- var notifications = {
3975
- async show(options) {
3976
- if (!options.title)
3977
- throw new Error("notification title is required");
3978
- if (hasBridge("notifications")) {
3979
- await window.craft.notifications.show(options);
3980
- return;
3981
- }
3982
- if (typeof window !== "undefined" && "Notification" in window) {
3983
- const N = window.Notification;
3984
- if (N.permission === "granted") {
3985
- new N(options.title, { body: options.body, icon: options.icon });
3986
- } else if (N.permission === "default") {
3987
- const granted = await N.requestPermission() === "granted";
3988
- if (granted)
3989
- new N(options.title, { body: options.body, icon: options.icon });
3990
- }
3991
- }
3992
- },
3993
- async schedule(options) {
3994
- if (hasBridge("notifications")) {
3995
- const o = { ...options };
3996
- if (o.triggerAt instanceof Date)
3997
- o.triggerAt = o.triggerAt.toISOString();
3998
- await window.craft.notifications.schedule(o);
3999
- return;
4000
- }
4001
- const fireAt = toEpochMs(options.triggerAt);
4002
- const delay2 = Math.max(0, fireAt - Date.now());
4003
- setTimeout(() => {
4004
- this.show(options).catch(() => {});
4005
- }, delay2);
4006
- },
4007
- async cancel(id) {
4008
- if (hasBridge("notifications")) {
4009
- await window.craft.notifications.cancel(id);
4010
- }
4011
- },
4012
- async cancelAll() {
4013
- if (hasBridge("notifications")) {
4014
- await window.craft.notifications.cancelAll();
4015
- }
4016
- },
4017
- async setBadge(n) {
4018
- const safe = Math.max(0, Math.round(Number.isFinite(n) ? n : 0));
4019
- if (hasBridge("notifications")) {
4020
- await window.craft.notifications.setBadge(safe);
4021
- return;
4022
- }
4023
- if (typeof navigator !== "undefined" && navigator.setAppBadge) {
4024
- try {
4025
- await navigator.setAppBadge(safe);
4026
- } catch {}
4027
- }
4028
- },
4029
- async clearBadge() {
4030
- if (hasBridge("notifications")) {
4031
- await window.craft.notifications.clearBadge();
4032
- return;
4033
- }
4034
- if (typeof navigator !== "undefined" && navigator.clearAppBadge) {
4035
- try {
4036
- await navigator.clearAppBadge();
4037
- } catch {}
4038
- }
4039
- },
4040
- async requestPermission() {
4041
- if (hasBridge("notifications")) {
4042
- return await window.craft.notifications.requestPermission();
4043
- }
4044
- if (typeof window !== "undefined" && "Notification" in window) {
4045
- const N = window.Notification;
4046
- if (N.permission === "granted")
4047
- return true;
4048
- if (N.permission === "denied")
4049
- return false;
4050
- const result = await N.requestPermission();
4051
- return result === "granted";
4052
- }
4053
- return false;
4054
- },
4055
- async registerCategories(categories) {
4056
- if (!Array.isArray(categories) || categories.length === 0)
4057
- return;
4058
- if (hasBridge("notifications")) {
4059
- const fn = window.craft.notifications.registerCategories;
4060
- if (typeof fn === "function")
4061
- await fn(categories);
4062
- }
4063
- },
4064
- onActionClicked(cb) {
4065
- return onCraftEvent("craft:notification:actionClicked", cb);
4066
- },
4067
- onReply(cb) {
4068
- return onCraftEvent("craft:notification:reply", cb);
4069
- }
4070
- };
4071
- function toEpochMs(t) {
4072
- if (t == null)
4073
- return Date.now();
4074
- if (t instanceof Date)
4075
- return t.getTime();
4076
- if (typeof t === "number")
4077
- return t;
4078
- const parsed = Date.parse(t);
4079
- return Number.isNaN(parsed) ? Date.now() : parsed;
4080
- }
4081
- // src/fs.ts
4082
- var fs = {
4083
- async readFile(path) {
4084
- const r = await requireBridge("fs").readFile(path);
4085
- return r && r.data || "";
4086
- },
4087
- async readBuffer(path) {
4088
- const r = await requireBridge("fs").readFile(path);
4089
- const text = r && r.data || "";
4090
- if (r && r.base64 === true) {
4091
- return base64ToBytes(text);
4092
- }
4093
- return new TextEncoder().encode(text);
4094
- },
4095
- async writeFile(path, data) {
4096
- await requireBridge("fs").writeFile(path, data);
4097
- },
4098
- async writeBuffer(path, data) {
4099
- const b64 = bytesToBase64(data);
4100
- const bridge = requireBridge("fs");
4101
- if (typeof bridge.writeFileBytes === "function") {
4102
- await bridge.writeFileBytes(path, b64);
4103
- } else {
4104
- await bridge.writeFile(path, b64);
4105
- }
4106
- },
4107
- async copy(from, to) {
4108
- if (from === to)
4109
- throw new Error("fs.copy: source and destination must differ");
4110
- await requireBridge("fs").copy(from, to);
4111
- },
4112
- async move(from, to) {
4113
- if (from === to)
4114
- throw new Error("fs.move: source and destination must differ");
4115
- await requireBridge("fs").move(from, to);
4116
- },
4117
- async appendFile(path, data) {
4118
- await requireBridge("fs").appendFile(path, data);
4119
- },
4120
- async deleteFile(path) {
4121
- await requireBridge("fs").deleteFile(path);
4122
- },
4123
- async exists(path) {
4124
- return await requireBridge("fs").exists(path);
4125
- },
4126
- async stat(path) {
4127
- return normalizeStat(await requireBridge("fs").stat(path));
4128
- },
4129
- async readDir(path) {
4130
- const r = await requireBridge("fs").readDir(path);
4131
- const raw = r && r.entries || [];
4132
- const base = path.endsWith("/") ? path.slice(0, -1) : path;
4133
- return raw.map((e) => ({
4134
- name: e.name,
4135
- path: `${base}/${e.name}`,
4136
- isDirectory: !!e.isDirectory
4137
- }));
4138
- },
4139
- async mkdir(path, opts) {
4140
- await requireBridge("fs").mkdir(path, opts);
4141
- },
4142
- async rmdir(path, opts) {
4143
- await requireBridge("fs").rmdir(path, opts);
4144
- },
4145
- async watch(path, id) {
4146
- await requireBridge("fs").watch(path, id);
4147
- },
4148
- async unwatch(id) {
4149
- await requireBridge("fs").unwatch(id);
4150
- },
4151
- onChange(cb) {
4152
- return onCraftEvent("craft:fs:change", cb);
4153
- },
4154
- async watchTree(path, options, cb) {
4155
- const id = `watch-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
4156
- const bridge = requireBridge("fs");
4157
- await bridge.watch(path, id, { recursive: !!options.recursive });
4158
- const allowed = options.kinds && options.kinds.length > 0 ? new Set(options.kinds) : null;
4159
- const coalesceMs = Math.max(0, Math.floor(options.coalesceMs ?? 0));
4160
- let buffer = [];
4161
- let flushTimer = null;
4162
- let stopped = false;
4163
- const flush = () => {
4164
- flushTimer = null;
4165
- if (buffer.length === 0)
4166
- return;
4167
- const batch = buffer;
4168
- buffer = [];
4169
- try {
4170
- cb(batch);
4171
- } catch {}
4172
- };
4173
- const off = onCraftEvent("craft:fs:change", (e) => {
4174
- if (stopped)
4175
- return;
4176
- if (e.id !== id)
4177
- return;
4178
- if (allowed && e.kind && !allowed.has(e.kind))
4179
- return;
4180
- buffer.push(e);
4181
- if (coalesceMs === 0) {
4182
- flush();
4183
- return;
4184
- }
4185
- if (flushTimer == null) {
4186
- flushTimer = setTimeout(flush, coalesceMs);
4187
- }
4188
- });
4189
- return {
4190
- id,
4191
- async stop() {
4192
- if (stopped)
4193
- return;
4194
- stopped = true;
4195
- off();
4196
- if (flushTimer) {
4197
- clearTimeout(flushTimer);
4198
- flushTimer = null;
4199
- }
4200
- if (buffer.length > 0)
4201
- flush();
4202
- try {
4203
- await bridge.unwatch(id);
4204
- } catch {}
4205
- }
4206
- };
4207
- },
4208
- async homeDir() {
4209
- return await requireBridge("fs").homeDir();
4210
- },
4211
- async tempDir() {
4212
- return await requireBridge("fs").tempDir();
4213
- },
4214
- async appDataDir() {
4215
- return await requireBridge("fs").appDataDir();
4216
- }
4217
- };
4218
- function bytesToBase64(bytes) {
4219
- let s = "";
4220
- for (let i = 0;i < bytes.length; i++)
4221
- s += String.fromCharCode(bytes[i]);
4222
- return typeof btoa === "function" ? btoa(s) : Buffer.from(bytes).toString("base64");
4223
- }
4224
- function base64ToBytes(b64) {
4225
- const bin = typeof atob === "function" ? atob(b64) : Buffer.from(b64, "base64").toString("binary");
4226
- const out = new Uint8Array(bin.length);
4227
- for (let i = 0;i < bin.length; i++)
4228
- out[i] = bin.charCodeAt(i);
4229
- return out;
4230
- }
4231
- function normalizeStat(raw) {
4232
- return {
4233
- isFile: !!raw.isFile,
4234
- isDirectory: !!raw.isDirectory,
4235
- isSymlink: !!raw.isSymlink,
4236
- size: Number(raw.size) || 0,
4237
- modifiedAt: raw.modifiedAt != null ? raw.modifiedAt < 1000000000000 ? raw.modifiedAt * 1000 : raw.modifiedAt : 0
4238
- };
4239
- }
4240
- // src/shell.ts
4241
- var activeSpawnIds = new Set;
4242
- var exitListenerAttached = false;
4243
- function ensureExitListener() {
4244
- if (exitListenerAttached)
4245
- return;
4246
- if (typeof window === "undefined" || typeof window.addEventListener !== "function")
4247
- return;
4248
- window.addEventListener("craft:shell:exit", (e) => {
4249
- const detail = e.detail;
4250
- if (detail?.id)
4251
- activeSpawnIds.delete(detail.id);
4252
- });
4253
- exitListenerAttached = true;
4254
- }
4255
- var BLOCKED_SCHEMES = new Set(["javascript:", "data:", "file:", "vbscript:"]);
4256
- var shell = {
4257
- async openExternal(url) {
4258
- const lc = url.trim().toLowerCase();
4259
- for (const s of BLOCKED_SCHEMES) {
4260
- if (lc.startsWith(s)) {
4261
- throw new Error(`shell.openExternal: ${s} URLs are blocked for safety`);
4262
- }
4263
- }
4264
- if (hasBridge("shell")) {
4265
- await window.craft.shell.openExternal(url);
4266
- return;
4267
- }
4268
- if (typeof window !== "undefined" && typeof window.open === "function") {
4269
- window.open(url, "_blank", "noopener,noreferrer");
4270
- }
4271
- },
4272
- async openPath(path) {
4273
- await requireBridge("shell").openPath(path);
4274
- },
4275
- async showInFinder(path) {
4276
- await requireBridge("shell").showInFinder(path);
4277
- },
4278
- async spawn(id, command, args = [], opts = {}) {
4279
- ensureExitListener();
4280
- if (!id || typeof id !== "string")
4281
- throw new Error("shell.spawn: id must be a non-empty string");
4282
- if (!command || typeof command !== "string")
4283
- throw new Error("shell.spawn: command must be a non-empty string");
4284
- if (!Array.isArray(args))
4285
- throw new Error("shell.spawn: args must be an array");
4286
- for (const a of args) {
4287
- if (typeof a !== "string")
4288
- throw new Error("shell.spawn: args entries must be strings");
4289
- }
4290
- if (activeSpawnIds.has(id)) {
4291
- throw new Error(`shell.spawn: id "${id}" is already in use \u2014 call kill(id) first or pick a different id`);
4292
- }
4293
- activeSpawnIds.add(id);
4294
- try {
4295
- await requireBridge("shell").spawn(id, command, args, opts);
4296
- } catch (e) {
4297
- activeSpawnIds.delete(id);
4298
- throw e;
4299
- }
4300
- },
4301
- async kill(id) {
4302
- await requireBridge("shell").kill(id);
4303
- activeSpawnIds.delete(id);
4304
- },
4305
- async getEnv(name) {
4306
- if (hasBridge("shell")) {
4307
- const v = await window.craft.shell.getEnv(name);
4308
- return v == null ? undefined : String(v);
4309
- }
4310
- return;
4311
- },
4312
- async setEnv(name, value) {
4313
- await requireBridge("shell").setEnv(name, value);
4314
- },
4315
- onStdout(cb) {
4316
- return onCraftEvent("craft:shell:stdout", cb);
4317
- },
4318
- onStderr(cb) {
4319
- return onCraftEvent("craft:shell:stderr", cb);
4320
- },
4321
- onExit(cb) {
4322
- return onCraftEvent("craft:shell:exit", cb);
4323
- }
4324
- };
4325
- // src/global-shortcuts.ts
4326
- var globalShortcuts = {
4327
- async register(id, accelerator, opts) {
4328
- if (!hasBridge("shortcuts"))
4329
- return;
4330
- await window.craft.shortcuts.register(id, accelerator, opts);
4331
- },
4332
- async unregister(id) {
4333
- if (!hasBridge("shortcuts"))
4334
- return;
4335
- await window.craft.shortcuts.unregister(id);
4336
- },
4337
- async unregisterAll() {
4338
- if (!hasBridge("shortcuts"))
4339
- return;
4340
- await window.craft.shortcuts.unregisterAll();
4341
- },
4342
- async enable(id) {
4343
- if (!hasBridge("shortcuts"))
4344
- return;
4345
- await window.craft.shortcuts.enable(id);
4346
- },
4347
- async disable(id) {
4348
- if (!hasBridge("shortcuts"))
4349
- return;
4350
- await window.craft.shortcuts.disable(id);
4351
- },
4352
- async isRegistered(id) {
4353
- if (!hasBridge("shortcuts"))
4354
- return false;
4355
- return await window.craft.shortcuts.isRegistered(id);
4356
- },
4357
- async list() {
4358
- if (!hasBridge("shortcuts"))
4359
- return [];
4360
- return await window.craft.shortcuts.list();
4361
- },
4362
- on(cb) {
4363
- return onCraftEvent("craft:shortcut", cb);
4364
- }
4365
- };
4366
- // src/theme.ts
4367
- var theme = {
4368
- get() {
4369
- if (hasBridge("theme")) {
4370
- try {
4371
- return window.craft.theme.get();
4372
- } catch {}
4373
- }
4374
- return { appearance: detectWebAppearance() };
4375
- },
4376
- onChange(cb) {
4377
- cb(this.get());
4378
- if (hasBridge("theme")) {
4379
- return onCraftEvent("craft:theme", cb);
4380
- }
4381
- if (typeof window !== "undefined" && window.matchMedia) {
4382
- const mq = window.matchMedia("(prefers-color-scheme: dark)");
4383
- const handler = () => cb({ appearance: mq.matches ? "dark" : "light" });
4384
- mq.addEventListener("change", handler);
4385
- return () => mq.removeEventListener("change", handler);
4386
- }
4387
- return () => {};
4388
- },
4389
- async current() {
4390
- return this.get();
4391
- }
4392
- };
4393
- function detectWebAppearance() {
4394
- if (typeof window === "undefined" || !window.matchMedia)
4395
- return "light";
4396
- return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
4397
- }
4398
- // src/drag-out.ts
4399
- async function dragOut(paths, options = {}) {
4400
- if (!hasBridge("dragOut")) {
4401
- throw new Error("dragOut requires a Craft native window");
4402
- }
4403
- const arr = Array.isArray(paths) ? paths : [paths];
4404
- if (arr.length === 0)
4405
- throw new Error("dragOut: at least one path required");
4406
- await window.craft.dragOut.start(arr, options);
4407
- }
4408
- function isDragOutAvailable() {
4409
- return hasBridge("dragOut");
4410
- }
4411
- // src/deep-link.ts
4412
- var deepLinks = {
4413
- onUrl(cb) {
4414
- if (!hasBridge("deepLink"))
4415
- return () => {};
4416
- return onCraftEvent("craft:deepLink", cb);
4417
- },
4418
- getInitialUrl() {
4419
- if (typeof window === "undefined")
4420
- return null;
4421
- if (hasBridge("deepLink")) {
4422
- try {
4423
- return window.craft.deepLink.getInitialUrl();
4424
- } catch {
4425
- return null;
4426
- }
4427
- }
4428
- return window.__craftPendingDeepLink || null;
4429
- },
4430
- consumeInitialUrl() {
4431
- const url = this.getInitialUrl();
4432
- if (typeof window !== "undefined")
4433
- window.__craftPendingDeepLink = undefined;
4434
- return url;
4435
- },
4436
- isAvailable() {
4437
- return hasBridge("deepLink");
4438
- }
4439
- };
4440
- // src/battery.ts
4441
- var battery = {
4442
- async isCharging() {
4443
- if (hasBridge("power"))
4444
- return await window.craft.power.isCharging();
4445
- const b = await getWebBatteryManager();
4446
- return b ? !!b.charging : false;
4447
- },
4448
- async isPluggedIn() {
4449
- if (hasBridge("power"))
4450
- return await window.craft.power.isPluggedIn();
4451
- const b = await getWebBatteryManager();
4452
- return b ? !!b.charging || b.level >= 0.999 : false;
4453
- },
4454
- async isLowPowerMode() {
4455
- if (hasBridge("power"))
4456
- return await window.craft.power.isLowPowerMode();
4457
- return false;
4458
- },
4459
- async level() {
4460
- if (hasBridge("power")) {
4461
- const v = await window.craft.power.batteryLevel();
4462
- return typeof v === "number" ? v : null;
4463
- }
4464
- const b = await getWebBatteryManager();
4465
- return b ? b.level : null;
4466
- },
4467
- async timeRemaining() {
4468
- if (hasBridge("power")) {
4469
- const r = await window.craft.power.timeRemaining();
4470
- return typeof r === "number" ? r : null;
4471
- }
4472
- const b = await getWebBatteryManager();
4473
- if (!b)
4474
- return null;
4475
- const sec = b.charging ? b.chargingTime : b.dischargingTime;
4476
- return Number.isFinite(sec) ? Math.round(sec / 60) : null;
4477
- },
4478
- async thermalState() {
4479
- if (hasBridge("power")) {
4480
- const s = await window.craft.power.thermalState();
4481
- return s || "unknown";
4482
- }
4483
- return "unknown";
4484
- },
4485
- async uptimeSeconds() {
4486
- if (hasBridge("power"))
4487
- return await window.craft.power.uptimeSeconds();
4488
- if (typeof performance !== "undefined" && typeof performance.now === "function") {
4489
- return Math.round(performance.now() / 1000);
4490
- }
4491
- return 0;
4492
- },
4493
- async preventSleep(reason = "app is busy") {
4494
- if (hasBridge("power")) {
4495
- await window.craft.power.preventSleep(reason);
4496
- return;
4497
- }
4498
- if (typeof navigator === "undefined" || !navigator.wakeLock)
4499
- return;
4500
- const w = window;
4501
- if (w.__craftWebWakeLock?.release) {
4502
- try {
4503
- await w.__craftWebWakeLock.release();
4504
- } catch {}
4505
- w.__craftWebWakeLock = null;
4506
- }
4507
- try {
4508
- const sentinel = await navigator.wakeLock.request("screen");
4509
- w.__craftWebWakeLock = sentinel;
4510
- } catch {}
4511
- },
4512
- async allowSleep() {
4513
- if (hasBridge("power")) {
4514
- await window.craft.power.allowSleep();
4515
- return;
4516
- }
4517
- const s = window.__craftWebWakeLock;
4518
- if (s && typeof s.release === "function") {
4519
- try {
4520
- await s.release();
4521
- } catch {}
4522
- window.__craftWebWakeLock = null;
4523
- }
4524
- },
4525
- onSleep(cb) {
4526
- return onCraftEvent("craft:powerSleep", cb);
4527
- },
4528
- onWake(cb) {
4529
- return onCraftEvent("craft:powerWake", cb);
4530
- }
4531
- };
4532
- async function getWebBatteryManager() {
4533
- if (typeof navigator === "undefined")
4534
- return null;
4535
- const nav = navigator;
4536
- if (typeof nav.getBattery !== "function")
4537
- return null;
4538
- try {
4539
- return await nav.getBattery();
4540
- } catch {
4541
- return null;
4542
- }
4543
- }
4544
- // src/network.ts
4545
- var network = {
4546
- async connectionType() {
4547
- if (hasBridge("network"))
4548
- return await window.craft.network.connectionType();
4549
- return webConnectionType();
4550
- },
4551
- async wifiSSID() {
4552
- if (hasBridge("network")) {
4553
- const v = await window.craft.network.wifiSSID();
4554
- return v || undefined;
4555
- }
4556
- return;
4557
- },
4558
- async wifiSignalStrength() {
4559
- if (hasBridge("network")) {
4560
- const v = await window.craft.network.wifiSignalStrength();
4561
- return typeof v === "number" ? v : undefined;
4562
- }
4563
- return;
4564
- },
4565
- async ipAddress() {
4566
- if (hasBridge("network"))
4567
- return await window.craft.network.ipAddress();
4568
- return "";
4569
- },
4570
- async macAddress() {
4571
- if (hasBridge("network"))
4572
- return await window.craft.network.macAddress();
4573
- return "";
4574
- },
4575
- async interfaces() {
4576
- if (hasBridge("network"))
4577
- return await window.craft.network.interfaces();
4578
- return [];
4579
- },
4580
- async isVPNConnected() {
4581
- if (hasBridge("network"))
4582
- return await window.craft.network.isVPNConnected();
4583
- return false;
4584
- },
4585
- async proxySettings() {
4586
- if (hasBridge("network")) {
4587
- const r = await window.craft.network.proxySettings();
4588
- return r || {};
4589
- }
4590
- return {};
4591
- },
4592
- async openPreferences() {
4593
- if (hasBridge("network"))
4594
- await window.craft.network.openPreferences();
4595
- },
4596
- onChange(cb) {
4597
- if (hasBridge("network")) {
4598
- return onCraftEvent("craft:networkChange", cb);
4599
- }
4600
- if (typeof window === "undefined")
4601
- return () => {};
4602
- const onlineH = () => cb({ type: webConnectionType(), online: true });
4603
- const offlineH = () => cb({ type: "none", online: false });
4604
- window.addEventListener("online", onlineH);
4605
- window.addEventListener("offline", offlineH);
4606
- return () => {
4607
- window.removeEventListener("online", onlineH);
4608
- window.removeEventListener("offline", offlineH);
4609
- };
4610
- }
4611
- };
4612
- function webConnectionType() {
4613
- if (typeof navigator === "undefined")
4614
- return "unknown";
4615
- if (navigator.onLine === false)
4616
- return "none";
4617
- const conn = navigator.connection;
4618
- if (!conn)
4619
- return "unknown";
4620
- const t = String(conn.type || conn.effectiveType || "unknown").toLowerCase();
4621
- if (t === "wifi" || t === "cellular" || t === "ethernet" || t === "bluetooth" || t === "none")
4622
- return t;
4623
- return "unknown";
4624
- }
4625
- // src/updater.ts
4626
- var updater = {
4627
- async checkForUpdates() {
4628
- if (!hasBridge("updater"))
4629
- return;
4630
- await window.craft.updater.checkForUpdates();
4631
- },
4632
- async checkInBackground() {
4633
- if (!hasBridge("updater"))
4634
- return;
4635
- await window.craft.updater.checkInBackground();
4636
- },
4637
- async setAutomaticChecks(on) {
4638
- if (!hasBridge("updater"))
4639
- return;
4640
- await window.craft.updater.setAutomaticChecks(on);
4641
- },
4642
- async setCheckInterval(seconds) {
4643
- if (!hasBridge("updater"))
4644
- return;
4645
- if (!Number.isFinite(seconds)) {
4646
- throw new Error("setCheckInterval: must be a finite number");
4647
- }
4648
- const safe = seconds <= 0 ? 0 : Math.max(60, Math.round(seconds));
4649
- await window.craft.updater.setCheckInterval(safe);
4650
- },
4651
- async setFeedURL(url) {
4652
- if (!hasBridge("updater"))
4653
- return;
4654
- await window.craft.updater.setFeedURL(url);
4655
- },
4656
- async getLastUpdateCheckDate() {
4657
- if (!hasBridge("updater"))
4658
- return null;
4659
- const v = await window.craft.updater.getLastUpdateCheckDate();
4660
- return v || null;
4661
- },
4662
- async getUpdateInfo() {
4663
- if (!hasBridge("updater"))
4664
- return null;
4665
- const v = await window.craft.updater.getUpdateInfo();
4666
- if (!v || typeof v.version !== "string" || v.version.length === 0)
4667
- return null;
4668
- return v;
4669
- },
4670
- onAvailable(cb) {
4671
- return onCraftEvent("craft:updateAvailable", cb);
4672
- },
4673
- onDownloaded(cb) {
4674
- return onCraftEvent("craft:updateDownloaded", cb);
4675
- },
4676
- async verifySignature({ payload, signatureB64, publicKeyB64 }) {
4677
- const data = toArrayBufferBytes(payload);
4678
- let publicKey;
4679
- try {
4680
- publicKey = await crypto.subtle.importKey("raw", base64ToBytes2(publicKeyB64), { name: "Ed25519" }, false, ["verify"]);
4681
- } catch {
4682
- return false;
4683
- }
4684
- try {
4685
- return await crypto.subtle.verify("Ed25519", publicKey, base64ToBytes2(signatureB64), data);
4686
- } catch {
4687
- return false;
4688
- }
4689
- },
4690
- async verifyDownload({ url, signatureB64, publicKeyB64, fetchInit }) {
4691
- let response;
4692
- try {
4693
- response = await fetch(url, fetchInit);
4694
- } catch {
4695
- return { ok: false, reason: "fetch-failed" };
4696
- }
4697
- if (!response.ok) {
4698
- return { ok: false, reason: "http-error", status: response.status };
4699
- }
4700
- const buffer = new Uint8Array(await response.arrayBuffer());
4701
- let publicKey;
4702
- try {
4703
- publicKey = await crypto.subtle.importKey("raw", base64ToBytes2(publicKeyB64), { name: "Ed25519" }, false, ["verify"]);
4704
- } catch {
4705
- return { ok: false, reason: "bad-key" };
4706
- }
4707
- let valid = false;
4708
- try {
4709
- valid = await crypto.subtle.verify("Ed25519", publicKey, base64ToBytes2(signatureB64), toArrayBufferBytes(buffer));
4710
- } catch {
4711
- valid = false;
4712
- }
4713
- if (!valid)
4714
- return { ok: false, reason: "bad-signature" };
4715
- return { ok: true, payload: buffer };
4716
- }
4717
- };
4718
- function toArrayBufferBytes(input) {
4719
- if (input instanceof ArrayBuffer)
4720
- return new Uint8Array(input);
4721
- const out = new ArrayBuffer(input.byteLength);
4722
- const view = new Uint8Array(out);
4723
- view.set(input);
4724
- return view;
4725
- }
4726
- function base64ToBytes2(b64) {
4727
- if (typeof atob === "function") {
4728
- const bin = atob(b64);
4729
- const buffer = new ArrayBuffer(bin.length);
4730
- const out = new Uint8Array(buffer);
4731
- for (let i = 0;i < bin.length; i++)
4732
- out[i] = bin.charCodeAt(i);
4733
- return out;
4734
- }
4735
- const node = Buffer.from(b64, "base64");
4736
- const buf = new ArrayBuffer(node.length);
4737
- new Uint8Array(buf).set(node);
4738
- return new Uint8Array(buf);
4739
- }
4740
- // src/window-events.ts
4741
- var windowEvents = {
4742
- onFocus(cb) {
4743
- if (hasBridge("window"))
4744
- return onCraftEvent("craft:window:focus", () => cb());
4745
- return webEvent("focus", cb);
4746
- },
4747
- onBlur(cb) {
4748
- if (hasBridge("window"))
4749
- return onCraftEvent("craft:window:blur", () => cb());
4750
- return webEvent("blur", cb);
4751
- },
4752
- onResize(cb) {
4753
- if (hasBridge("window"))
4754
- return onCraftEvent("craft:window:resize", cb);
4755
- if (typeof window === "undefined")
4756
- return () => {};
4757
- const h = () => cb({ width: window.innerWidth, height: window.innerHeight });
4758
- window.addEventListener("resize", h);
4759
- return () => window.removeEventListener("resize", h);
4760
- },
4761
- onMove(cb) {
4762
- if (hasBridge("window"))
4763
- return onCraftEvent("craft:window:move", cb);
4764
- return () => {};
4765
- },
4766
- onClose(cb) {
4767
- if (hasBridge("window"))
4768
- return onCraftEvent("craft:window:close", () => cb());
4769
- return webEvent("beforeunload", cb);
4770
- },
4771
- onMinimize(cb) {
4772
- if (hasBridge("window"))
4773
- return onCraftEvent("craft:window:minimize", () => cb());
4774
- if (typeof document === "undefined")
4775
- return () => {};
4776
- const h = () => {
4777
- if (document.visibilityState === "hidden")
4778
- cb();
4779
- };
4780
- document.addEventListener("visibilitychange", h);
4781
- return () => document.removeEventListener("visibilitychange", h);
4782
- },
4783
- onRestore(cb) {
4784
- if (hasBridge("window"))
4785
- return onCraftEvent("craft:window:restore", () => cb());
4786
- if (typeof document === "undefined")
4787
- return () => {};
4788
- const h = () => {
4789
- if (document.visibilityState === "visible")
4790
- cb();
4791
- };
4792
- document.addEventListener("visibilitychange", h);
4793
- return () => document.removeEventListener("visibilitychange", h);
4794
- }
4795
- };
4796
- function webEvent(name, cb) {
4797
- if (typeof window === "undefined")
4798
- return () => {};
4799
- const h = () => cb();
4800
- window.addEventListener(name, h);
4801
- return () => window.removeEventListener(name, h);
4802
- }
4803
- // src/app-info.ts
4804
- var DEFAULT_INFO = { name: "", version: "0.0.0" };
4805
- var app = {
4806
- async hideDockIcon() {
4807
- if (hasBridge("app"))
4808
- await window.craft.app.hideDockIcon();
4809
- },
4810
- async showDockIcon() {
4811
- if (hasBridge("app"))
4812
- await window.craft.app.showDockIcon();
4813
- },
4814
- async quit() {
4815
- if (hasBridge("app"))
4816
- await window.craft.app.quit();
4817
- },
4818
- async getInfo() {
4819
- if (!hasBridge("app"))
4820
- return DEFAULT_INFO;
4821
- const r = await window.craft.app.getInfo();
4822
- return { ...DEFAULT_INFO, ...r || {} };
4823
- },
4824
- async notify(options) {
4825
- if (!options.title)
4826
- throw new Error("notify: title is required");
4827
- if (hasBridge("app"))
4828
- await window.craft.app.notify(options);
4829
- },
4830
- async setBadge(count) {
4831
- if (hasBridge("app"))
4832
- await window.craft.app.setBadge(count);
4833
- },
4834
- async bounce(type = "informational") {
4835
- if (hasBridge("app"))
4836
- await window.craft.app.bounce(type);
4837
- }
4838
- };
4839
- // src/menu.ts
4840
- var menu = {
4841
- async set(items) {
4842
- if (hasBridge("menu"))
4843
- await window.craft.menu.set(items);
4844
- },
4845
- async setDock(items) {
4846
- if (hasBridge("menu"))
4847
- await window.craft.menu.setDock(items);
4848
- },
4849
- async addItem(parent, item) {
4850
- if (hasBridge("menu"))
4851
- await window.craft.menu.addItem(parent, item);
4852
- },
4853
- async removeItem(id) {
4854
- if (hasBridge("menu"))
4855
- await window.craft.menu.removeItem(id);
4856
- },
4857
- async enableItem(id) {
4858
- if (hasBridge("menu"))
4859
- await window.craft.menu.enableItem(id);
4860
- },
4861
- async disableItem(id) {
4862
- if (hasBridge("menu"))
4863
- await window.craft.menu.disableItem(id);
4864
- },
4865
- async checkItem(id) {
4866
- if (hasBridge("menu"))
4867
- await window.craft.menu.checkItem(id);
4868
- },
4869
- async uncheckItem(id) {
4870
- if (hasBridge("menu"))
4871
- await window.craft.menu.uncheckItem(id);
4872
- },
4873
- async setItemLabel(id, lbl) {
4874
- if (hasBridge("menu"))
4875
- await window.craft.menu.setItemLabel(id, lbl);
4876
- },
4877
- async clearDock() {
4878
- if (hasBridge("menu"))
4879
- await window.craft.menu.clearDock();
4880
- },
4881
- onAction(cb) {
4882
- return onCraftEvent("craft:menu:action", cb);
4883
- }
4884
- };
4885
- // src/system.ts
4886
- var system = {
4887
- accentColor: () => bridgeOr("system", "accentColor", () => ""),
4888
- highlightColor: () => bridgeOr("system", "highlightColor", () => ""),
4889
- language: () => bridgeOr("system", "language", () => {
4890
- if (typeof navigator === "undefined")
4891
- return "";
4892
- return navigator.language?.split("-")[0] || "";
4893
- }),
4894
- locale: () => bridgeOr("system", "locale", () => {
4895
- if (typeof navigator === "undefined")
4896
- return "";
4897
- return navigator.language || "";
4898
- }),
4899
- timezone: () => bridgeOr("system", "timezone", () => {
4900
- try {
4901
- return Intl.DateTimeFormat().resolvedOptions().timeZone || "";
4902
- } catch {
4903
- return "";
2533
+ // src/system.ts
2534
+ var system = {
2535
+ accentColor: () => bridgeOr("system", "accentColor", () => ""),
2536
+ highlightColor: () => bridgeOr("system", "highlightColor", () => ""),
2537
+ language: () => bridgeOr("system", "language", () => {
2538
+ if (typeof navigator === "undefined")
2539
+ return "";
2540
+ return navigator.language?.split("-")[0] || "";
2541
+ }),
2542
+ locale: () => bridgeOr("system", "locale", () => {
2543
+ if (typeof navigator === "undefined")
2544
+ return "";
2545
+ return navigator.language || "";
2546
+ }),
2547
+ timezone: () => bridgeOr("system", "timezone", () => {
2548
+ try {
2549
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "";
2550
+ } catch {
2551
+ return "";
4904
2552
  }
4905
2553
  }),
4906
2554
  is24HourTime: () => bridgeOr("system", "is24HourTime", () => {
@@ -4932,1445 +2580,6 @@ function mediaMatches(query) {
4932
2580
  return false;
4933
2581
  return window.matchMedia(query).matches;
4934
2582
  }
4935
- // src/screen.ts
4936
- var screen = {
4937
- async getDisplays() {
4938
- if (hasBridge("screen"))
4939
- return await window.craft.screen.getDisplays();
4940
- return webDisplays();
4941
- },
4942
- async getPrimary() {
4943
- if (hasBridge("screen")) {
4944
- const r = await window.craft.screen.getPrimary();
4945
- return r && typeof r.width === "number" ? r : null;
4946
- }
4947
- return webDisplays()[0] ?? null;
4948
- },
4949
- onChange(cb) {
4950
- if (hasBridge("screen"))
4951
- return onCraftEvent("craft:screen:change", () => cb());
4952
- if (typeof window === "undefined")
4953
- return () => {};
4954
- const h = () => cb();
4955
- window.addEventListener("resize", h);
4956
- return () => window.removeEventListener("resize", h);
4957
- }
4958
- };
4959
- function webDisplays() {
4960
- if (typeof window === "undefined" || !window.screen)
4961
- return [];
4962
- const s = window.screen;
4963
- return [{
4964
- id: 0,
4965
- x: s.left ?? 0,
4966
- y: s.top ?? 0,
4967
- width: s.width || 0,
4968
- height: s.height || 0,
4969
- workX: s.availLeft ?? 0,
4970
- workY: s.availTop ?? 0,
4971
- workWidth: s.availWidth ?? s.width ?? 0,
4972
- workHeight: s.availHeight ?? s.height ?? 0,
4973
- scaleFactor: window.devicePixelRatio || 1
4974
- }];
4975
- }
4976
- // src/keychain.ts
4977
- var keychain = {
4978
- async set(service, account, password) {
4979
- if (!service)
4980
- throw new Error("keychain.set: service is required");
4981
- if (!account)
4982
- throw new Error("keychain.set: account is required");
4983
- await requireBridge("keychain").set(service, account, password);
4984
- },
4985
- async get(service, account) {
4986
- if (!service)
4987
- throw new Error("keychain.get: service is required");
4988
- if (!account)
4989
- throw new Error("keychain.get: account is required");
4990
- const v = await requireBridge("keychain").get(service, account);
4991
- return typeof v === "string" ? v : null;
4992
- },
4993
- async delete(service, account) {
4994
- if (!service)
4995
- throw new Error("keychain.delete: service is required");
4996
- if (!account)
4997
- throw new Error("keychain.delete: account is required");
4998
- await requireBridge("keychain").delete(service, account);
4999
- },
5000
- async has(service, account) {
5001
- if (!service)
5002
- throw new Error("keychain.has: service is required");
5003
- if (!account)
5004
- throw new Error("keychain.has: account is required");
5005
- return await requireBridge("keychain").has(service, account);
5006
- }
5007
- };
5008
- // src/permissions.ts
5009
- var permissions = {
5010
- async check(name) {
5011
- if (hasBridge("permissions"))
5012
- return await window.craft.permissions.check(name);
5013
- return await webCheck(name);
5014
- },
5015
- async request(name) {
5016
- if (hasBridge("permissions"))
5017
- return await window.craft.permissions.request(name);
5018
- return await webRequest(name);
5019
- },
5020
- async openSettings(name) {
5021
- if (hasBridge("permissions"))
5022
- await window.craft.permissions.openSettings(name);
5023
- }
5024
- };
5025
- async function webCheck(name) {
5026
- if (typeof navigator === "undefined" || !navigator.permissions?.query)
5027
- return "not-supported";
5028
- try {
5029
- const result = await navigator.permissions.query({ name });
5030
- return mapWebState(result.state);
5031
- } catch {
5032
- return "not-supported";
5033
- }
5034
- }
5035
- async function webRequest(name) {
5036
- if (name === "notifications" && typeof window !== "undefined" && "Notification" in window) {
5037
- const r = await window.Notification.requestPermission();
5038
- return r === "granted" ? "granted" : r === "denied" ? "denied" : "undetermined";
5039
- }
5040
- return await webCheck(name);
5041
- }
5042
- function mapWebState(s) {
5043
- if (s === "granted")
5044
- return "granted";
5045
- if (s === "denied")
5046
- return "denied";
5047
- if (s === "prompt")
5048
- return "undetermined";
5049
- return "undetermined";
5050
- }
5051
- // src/printing.ts
5052
- var printing = {
5053
- async print() {
5054
- if (hasBridge("printing")) {
5055
- await window.craft.printing.print();
5056
- return;
5057
- }
5058
- if (typeof window !== "undefined" && typeof window.print === "function") {
5059
- window.print();
5060
- }
5061
- },
5062
- async printToPDF(path) {
5063
- if (!path)
5064
- throw new Error("printToPDF: path is required");
5065
- const isPosixAbs = path.startsWith("/");
5066
- const isWinAbs = /^[a-zA-Z]:[\\/]/.test(path) || path.startsWith("\\\\");
5067
- if (!isPosixAbs && !isWinAbs) {
5068
- throw new Error("printToPDF: path must be absolute");
5069
- }
5070
- const r = await requireBridge("printing").printToPDF(path);
5071
- return { ok: !!(r && r.ok), path: r?.path };
5072
- }
5073
- };
5074
- // src/native-autolaunch.ts
5075
- var nativeAutoLaunch = {
5076
- async enable() {
5077
- if (!hasBridge("autoLaunch"))
5078
- return false;
5079
- return await window.craft.autoLaunch.enable();
5080
- },
5081
- async disable() {
5082
- if (!hasBridge("autoLaunch"))
5083
- return false;
5084
- return await window.craft.autoLaunch.disable();
5085
- },
5086
- async isEnabled() {
5087
- if (!hasBridge("autoLaunch"))
5088
- return false;
5089
- return await window.craft.autoLaunch.isEnabled();
5090
- }
5091
- };
5092
- // src/touchbar.ts
5093
- var touchbar = {
5094
- async addItem(item) {
5095
- if (hasBridge("touchbar"))
5096
- await window.craft.touchbar.addItem(item);
5097
- },
5098
- async removeItem(id) {
5099
- if (hasBridge("touchbar"))
5100
- await window.craft.touchbar.removeItem(id);
5101
- },
5102
- async updateItem(id, props) {
5103
- if (hasBridge("touchbar"))
5104
- await window.craft.touchbar.updateItem(id, props);
5105
- },
5106
- async setLabel(id, label) {
5107
- if (hasBridge("touchbar"))
5108
- await window.craft.touchbar.setLabel(id, label);
5109
- },
5110
- async setIcon(id, icon) {
5111
- if (hasBridge("touchbar"))
5112
- await window.craft.touchbar.setIcon(id, icon);
5113
- },
5114
- async setEnabled(id, enabled) {
5115
- if (hasBridge("touchbar"))
5116
- await window.craft.touchbar.setEnabled(id, enabled);
5117
- },
5118
- async setSliderValue(id, value) {
5119
- if (hasBridge("touchbar"))
5120
- await window.craft.touchbar.setSliderValue(id, value);
5121
- },
5122
- async clear() {
5123
- if (hasBridge("touchbar"))
5124
- await window.craft.touchbar.clear();
5125
- },
5126
- async show() {
5127
- if (hasBridge("touchbar"))
5128
- await window.craft.touchbar.show();
5129
- },
5130
- async hide() {
5131
- if (hasBridge("touchbar"))
5132
- await window.craft.touchbar.hide();
5133
- },
5134
- onAction(cb) {
5135
- return onCraftEvent("craft:touchbar:action", cb);
5136
- }
5137
- };
5138
- // src/bluetooth.ts
5139
- var HEX_RE = /^[\da-f]*$/i;
5140
- function assertHex(label, hex) {
5141
- if (typeof hex !== "string" || !HEX_RE.test(hex) || hex.length % 2 !== 0) {
5142
- throw new Error(`${label}: must be a hex string with even length, got ${JSON.stringify(hex)}`);
5143
- }
5144
- }
5145
- var bluetooth = {
5146
- async isEnabled() {
5147
- return hasBridge("bluetooth") ? await window.craft.bluetooth.isEnabled() : false;
5148
- },
5149
- async powerState() {
5150
- return hasBridge("bluetooth") ? await window.craft.bluetooth.powerState() : "unknown";
5151
- },
5152
- async connectedDevices() {
5153
- return hasBridge("bluetooth") ? await window.craft.bluetooth.connectedDevices() : [];
5154
- },
5155
- async pairedDevices() {
5156
- return hasBridge("bluetooth") ? await window.craft.bluetooth.pairedDevices() : [];
5157
- },
5158
- async startDiscovery() {
5159
- if (hasBridge("bluetooth"))
5160
- await window.craft.bluetooth.startDiscovery();
5161
- },
5162
- async stopDiscovery() {
5163
- if (hasBridge("bluetooth"))
5164
- await window.craft.bluetooth.stopDiscovery();
5165
- },
5166
- async isDiscovering() {
5167
- return hasBridge("bluetooth") ? await window.craft.bluetooth.isDiscovering() : false;
5168
- },
5169
- async connect(id) {
5170
- if (hasBridge("bluetooth"))
5171
- await window.craft.bluetooth.connect(id);
5172
- },
5173
- async disconnect(id) {
5174
- if (hasBridge("bluetooth"))
5175
- await window.craft.bluetooth.disconnect(id);
5176
- },
5177
- async openPreferences() {
5178
- if (hasBridge("bluetooth"))
5179
- await window.craft.bluetooth.openPreferences();
5180
- },
5181
- async discoverServices(deviceId) {
5182
- if (!deviceId)
5183
- throw new Error("bluetooth.discoverServices: deviceId is required");
5184
- if (!hasBridge("bluetooth"))
5185
- return [];
5186
- const r = await window.craft.bluetooth.discoverServices(deviceId);
5187
- return Array.isArray(r) ? r : [];
5188
- },
5189
- async discoverCharacteristics(deviceId, serviceUuid) {
5190
- if (!deviceId || !serviceUuid)
5191
- throw new Error("bluetooth.discoverCharacteristics: deviceId and serviceUuid are required");
5192
- if (!hasBridge("bluetooth"))
5193
- return [];
5194
- const r = await window.craft.bluetooth.discoverCharacteristics(deviceId, serviceUuid);
5195
- return Array.isArray(r) ? r : [];
5196
- },
5197
- async readCharacteristic(deviceId, serviceUuid, characteristicUuid) {
5198
- if (!deviceId || !serviceUuid || !characteristicUuid) {
5199
- throw new Error("bluetooth.readCharacteristic: deviceId, serviceUuid, characteristicUuid are required");
5200
- }
5201
- if (!hasBridge("bluetooth"))
5202
- return { ok: false, reason: "bridge unavailable" };
5203
- return await window.craft.bluetooth.readCharacteristic(deviceId, serviceUuid, characteristicUuid);
5204
- },
5205
- async writeCharacteristic(deviceId, serviceUuid, characteristicUuid, valueHex, mode = "with-response") {
5206
- if (!deviceId || !serviceUuid || !characteristicUuid) {
5207
- throw new Error("bluetooth.writeCharacteristic: deviceId, serviceUuid, characteristicUuid are required");
5208
- }
5209
- assertHex("bluetooth.writeCharacteristic.valueHex", valueHex);
5210
- if (!hasBridge("bluetooth"))
5211
- return { ok: false, reason: "bridge unavailable" };
5212
- return await window.craft.bluetooth.writeCharacteristic(deviceId, serviceUuid, characteristicUuid, valueHex, mode);
5213
- },
5214
- async setCharacteristicNotify(deviceId, serviceUuid, characteristicUuid, on) {
5215
- if (!deviceId || !serviceUuid || !characteristicUuid) {
5216
- throw new Error("bluetooth.setCharacteristicNotify: deviceId, serviceUuid, characteristicUuid are required");
5217
- }
5218
- if (!hasBridge("bluetooth"))
5219
- return { ok: false, reason: "bridge unavailable" };
5220
- return await window.craft.bluetooth.setCharacteristicNotify(deviceId, serviceUuid, characteristicUuid, on);
5221
- },
5222
- onDeviceFound(cb) {
5223
- return onCraftEvent("craft:bluetooth:deviceFound", cb);
5224
- },
5225
- onDeviceConnected(cb) {
5226
- return onCraftEvent("craft:bluetooth:deviceConnected", cb);
5227
- },
5228
- onDeviceDisconnected(cb) {
5229
- return onCraftEvent("craft:bluetooth:deviceDisconnected", cb);
5230
- },
5231
- onCharacteristicValue(cb) {
5232
- return onCraftEvent("craft:bluetooth:characteristicValue", cb);
5233
- }
5234
- };
5235
- // src/speech.ts
5236
- var speech = {
5237
- async speak(text, options) {
5238
- if (!text)
5239
- throw new Error("speech.speak: text is required");
5240
- if (hasBridge("speech")) {
5241
- await window.craft.speech.speak(text, options);
5242
- return;
5243
- }
5244
- if (typeof window !== "undefined" && window.speechSynthesis) {
5245
- const u = new window.SpeechSynthesisUtterance(text);
5246
- if (options) {
5247
- if (options.rate != null)
5248
- u.rate = options.rate;
5249
- if (options.pitch != null)
5250
- u.pitch = options.pitch;
5251
- if (options.volume != null)
5252
- u.volume = options.volume;
5253
- if (options.voice) {
5254
- const voices = window.speechSynthesis.getVoices();
5255
- const match = voices.find((v) => v.voiceURI === options.voice || v.name === options.voice || v.lang === options.voice);
5256
- if (match)
5257
- u.voice = match;
5258
- }
5259
- }
5260
- window.speechSynthesis.speak(u);
5261
- }
5262
- },
5263
- async stop() {
5264
- if (hasBridge("speech")) {
5265
- await window.craft.speech.stop();
5266
- return;
5267
- }
5268
- if (typeof window !== "undefined" && window.speechSynthesis) {
5269
- window.speechSynthesis.cancel();
5270
- }
5271
- },
5272
- async pause() {
5273
- if (hasBridge("speech")) {
5274
- await window.craft.speech.pause();
5275
- return;
5276
- }
5277
- if (typeof window !== "undefined" && window.speechSynthesis) {
5278
- window.speechSynthesis.pause();
5279
- }
5280
- },
5281
- async resume() {
5282
- if (hasBridge("speech")) {
5283
- await window.craft.speech.resume();
5284
- return;
5285
- }
5286
- if (typeof window !== "undefined" && window.speechSynthesis) {
5287
- window.speechSynthesis.resume();
5288
- }
5289
- },
5290
- async isSpeaking() {
5291
- if (hasBridge("speech"))
5292
- return await window.craft.speech.isSpeaking();
5293
- if (typeof window !== "undefined" && window.speechSynthesis) {
5294
- return !!window.speechSynthesis.speaking;
5295
- }
5296
- return false;
5297
- },
5298
- async getVoices() {
5299
- if (hasBridge("speech"))
5300
- return await window.craft.speech.getVoices();
5301
- if (typeof window !== "undefined" && window.speechSynthesis) {
5302
- const raw = window.speechSynthesis.getVoices();
5303
- return raw.map((v) => ({
5304
- id: v.voiceURI || v.name,
5305
- name: v.name,
5306
- language: v.lang || "",
5307
- quality: "default"
5308
- }));
5309
- }
5310
- return [];
5311
- }
5312
- };
5313
- // src/crash-reporter.ts
5314
- var jsQueue = [];
5315
- var jsEnabled = true;
5316
- var jsUserId;
5317
- var jsAppVersion;
5318
- var crashReporter = {
5319
- async report(entry) {
5320
- if (hasBridge("crashReporter")) {
5321
- await window.craft.crashReporter.report(entry);
5322
- return;
5323
- }
5324
- if (!jsEnabled)
5325
- return;
5326
- const normalized = entry instanceof Error ? {
5327
- timestamp: Date.now(),
5328
- severity: "error",
5329
- message: entry.message,
5330
- source: "js",
5331
- stack: entry.stack || "",
5332
- userId: jsUserId,
5333
- appVersion: jsAppVersion
5334
- } : {
5335
- timestamp: Date.now(),
5336
- severity: entry.severity || "error",
5337
- message: entry.message || "",
5338
- source: entry.source || "js",
5339
- stack: entry.stack || "",
5340
- userId: jsUserId,
5341
- appVersion: jsAppVersion
5342
- };
5343
- if (jsQueue.length >= 64)
5344
- jsQueue.shift();
5345
- jsQueue.push(normalized);
5346
- },
5347
- async flush() {
5348
- if (hasBridge("crashReporter"))
5349
- return await window.craft.crashReporter.flush();
5350
- return [...jsQueue];
5351
- },
5352
- async clear() {
5353
- if (hasBridge("crashReporter")) {
5354
- await window.craft.crashReporter.clear();
5355
- return;
5356
- }
5357
- jsQueue.length = 0;
5358
- },
5359
- async setEnabled(on) {
5360
- if (hasBridge("crashReporter")) {
5361
- await window.craft.crashReporter.setEnabled(on);
5362
- return;
5363
- }
5364
- jsEnabled = on;
5365
- },
5366
- async isEnabled() {
5367
- if (hasBridge("crashReporter"))
5368
- return await window.craft.crashReporter.isEnabled();
5369
- return jsEnabled;
5370
- },
5371
- async setUser(id) {
5372
- if (hasBridge("crashReporter")) {
5373
- await window.craft.crashReporter.setUser(id);
5374
- return;
5375
- }
5376
- jsUserId = id || undefined;
5377
- },
5378
- async setAppVersion(version) {
5379
- if (hasBridge("crashReporter")) {
5380
- await window.craft.crashReporter.setAppVersion(version);
5381
- return;
5382
- }
5383
- jsAppVersion = version || undefined;
5384
- },
5385
- attachGlobalHandlers() {
5386
- if (hasBridge("crashReporter") && window.craft.crashReporter.attachGlobalHandlers) {
5387
- return window.craft.crashReporter.attachGlobalHandlers();
5388
- }
5389
- if (typeof window === "undefined")
5390
- return () => {};
5391
- const errorH = (e) => {
5392
- crashReporter.report({
5393
- severity: "error",
5394
- message: e.message,
5395
- source: "js",
5396
- stack: e.error?.stack || `${e.message}
5397
- at ${e.filename}:${e.lineno}:${e.colno}`
5398
- }).catch(() => {});
5399
- };
5400
- const rejectH = (e) => {
5401
- const r = e.reason;
5402
- crashReporter.report({
5403
- severity: "error",
5404
- message: r?.message || String(r),
5405
- source: "js",
5406
- stack: r?.stack || ""
5407
- }).catch(() => {});
5408
- };
5409
- window.addEventListener("error", errorH);
5410
- window.addEventListener("unhandledrejection", rejectH);
5411
- return () => {
5412
- window.removeEventListener("error", errorH);
5413
- window.removeEventListener("unhandledrejection", rejectH);
5414
- };
5415
- },
5416
- forwardTo(options) {
5417
- return startForwarder(options);
5418
- }
5419
- };
5420
- var DEFAULT_PERSIST_KEY = "craft:crashReporter:pending";
5421
- var EMAIL_RE = /[\w.+-]+@[\w-]+\.[\w.-]+/g;
5422
- var IPV4_RE = /\b(?:\d{1,3}\.){3}\d{1,3}\b/g;
5423
- var HOME_PATH_RE = /\/(?:Users|home)\/[^\s/'"`]+/g;
5424
- function redactPII(entry) {
5425
- const scrub = (s) => s.replace(EMAIL_RE, "<email>").replace(IPV4_RE, "<ip>").replace(HOME_PATH_RE, "/<home>");
5426
- return {
5427
- ...entry,
5428
- message: scrub(entry.message),
5429
- stack: scrub(entry.stack)
5430
- };
5431
- }
5432
- async function signPayload(secret, body) {
5433
- const enc = new TextEncoder;
5434
- const key = await crypto.subtle.importKey("raw", enc.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
5435
- const sig = await crypto.subtle.sign("HMAC", key, enc.encode(body));
5436
- return [...new Uint8Array(sig)].map((b) => b.toString(16).padStart(2, "0")).join("");
5437
- }
5438
- function loadPersisted(key) {
5439
- if (!key || typeof localStorage === "undefined")
5440
- return [];
5441
- try {
5442
- const raw = localStorage.getItem(key);
5443
- if (!raw)
5444
- return [];
5445
- const parsed = JSON.parse(raw);
5446
- return Array.isArray(parsed) ? parsed : [];
5447
- } catch {
5448
- return [];
5449
- }
5450
- }
5451
- function savePersisted(key, entries) {
5452
- if (!key || typeof localStorage === "undefined")
5453
- return;
5454
- try {
5455
- if (entries.length === 0)
5456
- localStorage.removeItem(key);
5457
- else
5458
- localStorage.setItem(key, JSON.stringify(entries));
5459
- } catch {}
5460
- }
5461
- function startForwarder(options) {
5462
- const {
5463
- endpoint,
5464
- intervalMs = 60000,
5465
- signingSecret,
5466
- redact = true,
5467
- headers = {},
5468
- maxRetries = 5,
5469
- persistKey
5470
- } = options;
5471
- const storageKey = persistKey === null ? null : persistKey ?? DEFAULT_PERSIST_KEY;
5472
- let pending = loadPersisted(storageKey);
5473
- let stopped = false;
5474
- let timer = null;
5475
- let inFlight = null;
5476
- const redactor = typeof redact === "function" ? redact : redact === false ? (e) => e : redactPII;
5477
- async function postBatch(batch) {
5478
- const body = JSON.stringify({ entries: batch });
5479
- const requestHeaders = {
5480
- "Content-Type": "application/json",
5481
- ...headers
5482
- };
5483
- if (signingSecret) {
5484
- requestHeaders["X-Craft-Signature"] = await signPayload(signingSecret, body);
5485
- }
5486
- let attempt = 0;
5487
- let delay2 = 1000;
5488
- while (!stopped) {
5489
- try {
5490
- const res = await fetch(endpoint, { method: "POST", headers: requestHeaders, body });
5491
- if (res.ok)
5492
- return;
5493
- if (res.status >= 400 && res.status < 500)
5494
- return;
5495
- throw new Error(`HTTP ${res.status}`);
5496
- } catch (err) {
5497
- attempt += 1;
5498
- if (attempt > maxRetries) {
5499
- pending = [...batch, ...pending];
5500
- savePersisted(storageKey, pending);
5501
- throw err;
5502
- }
5503
- await new Promise((r) => {
5504
- setTimeout(r, delay2);
5505
- });
5506
- delay2 = Math.min(delay2 * 2, 60000);
5507
- }
5508
- }
5509
- }
5510
- async function drain() {
5511
- if (stopped || inFlight)
5512
- return inFlight ?? undefined;
5513
- const fresh = await crashReporter.flush();
5514
- if (fresh.length > 0)
5515
- await crashReporter.clear();
5516
- pending = [...pending, ...fresh.map((e) => redactor(e))];
5517
- if (pending.length === 0)
5518
- return;
5519
- const batch = pending;
5520
- pending = [];
5521
- savePersisted(storageKey, pending);
5522
- inFlight = postBatch(batch).catch(() => {}).finally(() => {
5523
- inFlight = null;
5524
- });
5525
- return inFlight;
5526
- }
5527
- if (intervalMs > 0) {
5528
- timer = setInterval(() => {
5529
- drain().catch(() => {});
5530
- }, intervalMs);
5531
- }
5532
- return {
5533
- async flushNow() {
5534
- await drain();
5535
- },
5536
- stop() {
5537
- stopped = true;
5538
- if (timer)
5539
- clearInterval(timer);
5540
- timer = null;
5541
- },
5542
- pending() {
5543
- return [...pending];
5544
- }
5545
- };
5546
- }
5547
- // src/iap.ts
5548
- var iap = {
5549
- async isAvailable() {
5550
- if (!hasBridge("iap"))
5551
- return false;
5552
- return await window.craft.iap.isAvailable();
5553
- },
5554
- async getProducts(ids) {
5555
- if (!hasBridge("iap"))
5556
- return [];
5557
- const arr = Array.isArray(ids) ? ids : [String(ids)];
5558
- return await window.craft.iap.getProducts(arr);
5559
- },
5560
- async purchase(productId) {
5561
- if (!hasBridge("iap"))
5562
- return { queued: false, productId, reason: "IAP bridge not available" };
5563
- const r = await window.craft.iap.purchase(productId);
5564
- return { queued: !!(r && r.queued), productId: r?.productId, reason: r?.reason };
5565
- },
5566
- async restorePurchases() {
5567
- if (!hasBridge("iap"))
5568
- return { ok: false };
5569
- const r = await window.craft.iap.restorePurchases();
5570
- return { ok: !!(r && r.ok) };
5571
- },
5572
- async finishTransaction(transactionId) {
5573
- if (!hasBridge("iap"))
5574
- return;
5575
- await window.craft.iap.finishTransaction(transactionId);
5576
- },
5577
- async getReceiptData() {
5578
- if (!hasBridge("iap"))
5579
- return null;
5580
- const r = await window.craft.iap.getReceiptData();
5581
- return r ? String(r) : null;
5582
- },
5583
- onPurchased(cb) {
5584
- return onCraftEvent("craft:iap:purchased", cb);
5585
- },
5586
- onFailed(cb) {
5587
- return onCraftEvent("craft:iap:failed", cb);
5588
- },
5589
- onRestored(cb) {
5590
- return onCraftEvent("craft:iap:restored", cb);
5591
- },
5592
- onProductsLoaded(cb) {
5593
- return onCraftEvent("craft:iap:productsLoaded", (e) => cb(e.products || []));
5594
- },
5595
- onRefunded(cb) {
5596
- return onCraftEvent("craft:iap:refunded", cb);
5597
- },
5598
- onSubscriptionStatusChanged(cb) {
5599
- return onCraftEvent("craft:iap:subscriptionStatusChanged", cb);
5600
- },
5601
- async getActiveSubscriptions() {
5602
- if (!hasBridge("iap"))
5603
- return [];
5604
- const fn = window.craft.iap.getActiveSubscriptions;
5605
- if (typeof fn !== "function")
5606
- return [];
5607
- const r = await fn();
5608
- return Array.isArray(r) ? r : [];
5609
- },
5610
- async isEligibleForIntroOffer(productId) {
5611
- if (!hasBridge("iap"))
5612
- return false;
5613
- const fn = window.craft.iap.isEligibleForIntroOffer;
5614
- if (typeof fn !== "function")
5615
- return false;
5616
- return !!await fn(productId);
5617
- }
5618
- };
5619
- // src/handoff.ts
5620
- var handoff = {
5621
- async startActivity(type, options) {
5622
- if (!type)
5623
- throw new Error("handoff.startActivity: type is required");
5624
- if (!hasBridge("handoff"))
5625
- return false;
5626
- const r = await window.craft.handoff.startActivity(type, options);
5627
- return typeof r === "boolean" ? r : !!(r && r.ok);
5628
- },
5629
- async updateActivity(options) {
5630
- if (!hasBridge("handoff"))
5631
- return false;
5632
- const r = await window.craft.handoff.updateActivity(options);
5633
- return typeof r === "boolean" ? r : !!(r && r.ok);
5634
- },
5635
- async stopActivity() {
5636
- if (!hasBridge("handoff"))
5637
- return;
5638
- await window.craft.handoff.stopActivity();
5639
- },
5640
- async getCurrentActivity() {
5641
- if (!hasBridge("handoff"))
5642
- return null;
5643
- const r = await window.craft.handoff.getCurrentActivity();
5644
- return r && typeof r.type === "string" ? r : null;
5645
- },
5646
- onIncoming(cb) {
5647
- return onCraftEvent("craft:handoff:incoming", cb);
5648
- }
5649
- };
5650
- // src/live-activities.ts
5651
- var liveActivities = {
5652
- async start(type, state) {
5653
- return handoff.startActivity(type, {
5654
- title: state?.title,
5655
- webpageURL: state?.webpageURL,
5656
- userInfo: state?.state
5657
- });
5658
- },
5659
- async update(state) {
5660
- return handoff.updateActivity({
5661
- title: state.title,
5662
- webpageURL: state.webpageURL,
5663
- userInfo: state.state
5664
- });
5665
- },
5666
- async stop() {
5667
- await handoff.stopActivity();
5668
- }
5669
- };
5670
- // src/location.ts
5671
- var location = {
5672
- async requestPermission(mode = "whenInUse") {
5673
- if (hasBridge("location"))
5674
- return await window.craft.location.requestPermission(mode);
5675
- if (typeof navigator !== "undefined" && navigator.geolocation) {
5676
- return "undetermined";
5677
- }
5678
- return "not-supported";
5679
- },
5680
- async getAuthorization() {
5681
- if (hasBridge("location"))
5682
- return await window.craft.location.getAuthorization();
5683
- return "unknown";
5684
- },
5685
- async getCurrentLocation() {
5686
- if (hasBridge("location"))
5687
- return await window.craft.location.getCurrentLocation();
5688
- if (typeof navigator !== "undefined" && navigator.geolocation) {
5689
- navigator.geolocation.getCurrentPosition((pos) => {
5690
- window.dispatchEvent(new CustomEvent("craft:location:update", {
5691
- detail: {
5692
- latitude: pos.coords.latitude,
5693
- longitude: pos.coords.longitude,
5694
- altitude: pos.coords.altitude,
5695
- horizontalAccuracy: pos.coords.accuracy,
5696
- verticalAccuracy: pos.coords.altitudeAccuracy,
5697
- speed: pos.coords.speed
5698
- }
5699
- }));
5700
- }, (err) => {
5701
- window.dispatchEvent(new CustomEvent("craft:location:error", {
5702
- detail: { message: err.message || String(err) }
5703
- }));
5704
- });
5705
- return { requested: true };
5706
- }
5707
- return { requested: false };
5708
- },
5709
- async startWatching(options) {
5710
- if (hasBridge("location"))
5711
- return await window.craft.location.startWatching(options);
5712
- if (typeof navigator !== "undefined" && navigator.geolocation) {
5713
- const watchId = navigator.geolocation.watchPosition((pos) => {
5714
- window.dispatchEvent(new CustomEvent("craft:location:update", {
5715
- detail: {
5716
- latitude: pos.coords.latitude,
5717
- longitude: pos.coords.longitude,
5718
- altitude: pos.coords.altitude,
5719
- horizontalAccuracy: pos.coords.accuracy,
5720
- verticalAccuracy: pos.coords.altitudeAccuracy,
5721
- speed: pos.coords.speed
5722
- }
5723
- }));
5724
- });
5725
- window.__craftWebLocationWatchId = watchId;
5726
- return true;
5727
- }
5728
- return false;
5729
- },
5730
- async stopWatching() {
5731
- if (hasBridge("location")) {
5732
- await window.craft.location.stopWatching();
5733
- return;
5734
- }
5735
- if (typeof navigator !== "undefined" && navigator.geolocation) {
5736
- const id = window.__craftWebLocationWatchId;
5737
- if (id != null) {
5738
- navigator.geolocation.clearWatch(id);
5739
- window.__craftWebLocationWatchId = null;
5740
- }
5741
- }
5742
- },
5743
- onUpdate(cb) {
5744
- return onCraftEvent("craft:location:update", cb);
5745
- },
5746
- onError(cb) {
5747
- return onCraftEvent("craft:location:error", cb);
5748
- },
5749
- onAuthChanged(cb) {
5750
- return onCraftEvent("craft:location:authChanged", cb);
5751
- }
5752
- };
5753
- // src/screen-capture.ts
5754
- var screenCapture = {
5755
- async captureScreen() {
5756
- if (!hasBridge("screenCapture"))
5757
- return null;
5758
- const r = await window.craft.screenCapture.captureScreen();
5759
- return r ? String(r) : null;
5760
- },
5761
- async captureWindow(id) {
5762
- if (!hasBridge("screenCapture"))
5763
- return null;
5764
- if (!Number.isFinite(id) || id <= 0)
5765
- throw new Error("captureWindow: id must be a positive number");
5766
- const r = await window.craft.screenCapture.captureWindow(id);
5767
- return r ? String(r) : null;
5768
- },
5769
- async listWindows() {
5770
- if (!hasBridge("screenCapture"))
5771
- return [];
5772
- return await window.craft.screenCapture.listWindows();
5773
- }
5774
- };
5775
- // src/screen-sharing.ts
5776
- var MIN_WATCH_INTERVAL_MS = 250;
5777
- var MAX_WATCH_INTERVAL_MS = 60000;
5778
- var DEFAULT_WATCH_INTERVAL_MS = 2000;
5779
- var IDLE = {
5780
- sharing: false,
5781
- signals: {
5782
- systemScreenShare: false,
5783
- remoteSession: false,
5784
- conferenceSharing: false,
5785
- screenRecording: false
5786
- },
5787
- sources: []
5788
- };
5789
- function idleState() {
5790
- return { ...IDLE, signals: { ...IDLE.signals }, sources: [] };
5791
- }
5792
- var screenSharing = {
5793
- async getState() {
5794
- if (!hasBridge("screenSharing"))
5795
- return idleState();
5796
- return await window.craft.screenSharing.getState();
5797
- },
5798
- async watch(intervalMs = DEFAULT_WATCH_INTERVAL_MS) {
5799
- const clamped = Math.min(MAX_WATCH_INTERVAL_MS, Math.max(MIN_WATCH_INTERVAL_MS, Math.round(intervalMs)));
5800
- if (!hasBridge("screenSharing"))
5801
- return clamped;
5802
- const r = await window.craft.screenSharing.watch(clamped);
5803
- return r && r.intervalMs || clamped;
5804
- },
5805
- async stop() {
5806
- if (!hasBridge("screenSharing"))
5807
- return;
5808
- await window.craft.screenSharing.unwatch();
5809
- },
5810
- onChange(cb) {
5811
- return onCraftEvent("craft:screenSharing:change", cb);
5812
- }
5813
- };
5814
- async function watchScreenSharing(cb, intervalMs = DEFAULT_WATCH_INTERVAL_MS) {
5815
- const off = screenSharing.onChange(cb);
5816
- await screenSharing.watch(intervalMs);
5817
- return () => {
5818
- off();
5819
- screenSharing.stop();
5820
- };
5821
- }
5822
- // src/focus.ts
5823
- var UNSUPPORTED = { supported: false, isFocused: null, authorization: "unsupported" };
5824
- function unavailable() {
5825
- return { ok: false, error: "Focus control is only available in a Craft window on macOS" };
5826
- }
5827
- var focus = {
5828
- async getStatus() {
5829
- if (!hasBridge("focus"))
5830
- return { ...UNSUPPORTED };
5831
- return await window.craft.focus.getStatus();
5832
- },
5833
- async requestAuthorization() {
5834
- if (!hasBridge("focus"))
5835
- return "unsupported";
5836
- return await window.craft.focus.requestAuthorization();
5837
- },
5838
- async setEnabled(enabled, options = {}) {
5839
- if (!hasBridge("focus"))
5840
- return unavailable();
5841
- const name = enabled ? options.onShortcut : options.offShortcut;
5842
- if (!name) {
5843
- return {
5844
- ok: false,
5845
- error: `focus.setEnabled: no ${enabled ? "onShortcut" : "offShortcut"} configured`
5846
- };
5847
- }
5848
- return await window.craft.focus.setEnabled(enabled, options);
5849
- },
5850
- async runShortcut(name) {
5851
- if (!hasBridge("focus"))
5852
- return unavailable();
5853
- if (!name)
5854
- throw new Error("focus.runShortcut: name is required");
5855
- return await window.craft.focus.runShortcut(name);
5856
- },
5857
- async listShortcuts() {
5858
- if (!hasBridge("focus"))
5859
- return [];
5860
- return await window.craft.focus.listShortcuts();
5861
- },
5862
- async listShortcutsResult() {
5863
- if (!hasBridge("focus"))
5864
- return { canList: false, shortcuts: [] };
5865
- const r = await window.craft.focus.listShortcutsResult();
5866
- return { canList: Boolean(r?.canList), shortcuts: r?.shortcuts || [] };
5867
- }
5868
- };
5869
- async function hasFocusShortcuts(...names) {
5870
- if (names.length === 0)
5871
- return false;
5872
- const installed = new Set(await focus.listShortcuts());
5873
- return names.every((name) => installed.has(name));
5874
- }
5875
- async function focusShortcutsReady(...names) {
5876
- if (names.length === 0)
5877
- return false;
5878
- const { canList, shortcuts } = await focus.listShortcutsResult();
5879
- if (!canList)
5880
- return "unknown";
5881
- const installed = new Set(shortcuts);
5882
- return names.every((name) => installed.has(name));
5883
- }
5884
- // src/local-server.ts
5885
- var localServer = {
5886
- async start(port = 0, host = "127.0.0.1") {
5887
- if (!hasBridge("localServer"))
5888
- return { port: 0, started: false, reason: "bridge unavailable" };
5889
- return await window.craft.localServer.start(port, host);
5890
- },
5891
- async stop() {
5892
- if (!hasBridge("localServer"))
5893
- return;
5894
- await window.craft.localServer.stop();
5895
- },
5896
- async respond(options) {
5897
- if (!hasBridge("localServer"))
5898
- return;
5899
- await window.craft.localServer.respond(options || { status: 200, body: "OK" });
5900
- },
5901
- onRequest(cb) {
5902
- return onCraftEvent("craft:localServer:request", cb);
5903
- },
5904
- async awaitOAuthCallback(options = {}) {
5905
- requireBridge("localServer");
5906
- const { port: requestedPort = 0, host = "127.0.0.1", timeoutMs = 5 * 60 * 1000, successHTML } = options;
5907
- const start = await this.start(requestedPort, host);
5908
- if (!start.started)
5909
- throw new Error(`localServer: start failed${start.reason ? ` \u2014 ${start.reason}` : ""}`);
5910
- return new Promise((resolve, reject) => {
5911
- const timer = setTimeout(() => {
5912
- off();
5913
- this.stop().catch(() => {});
5914
- reject(new Error("localServer: OAuth callback timed out"));
5915
- }, timeoutMs);
5916
- const off = this.onRequest(({ url }) => {
5917
- clearTimeout(timer);
5918
- off();
5919
- const body = successHTML ?? `<!doctype html><meta charset="utf-8"><title>Done</title>
5920
- <style>body{font-family:-apple-system,BlinkMacSystemFont,sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#f5f5f7}</style>
5921
- <div><h1>You can close this tab.</h1><p>Returning to the app\u2026</p></div>
5922
- <script>setTimeout(()=>window.close(),1500)</script>`;
5923
- this.respond({ status: 200, body, contentType: "text/html; charset=utf-8" }).catch(() => {}).finally(() => this.stop().catch(() => {}));
5924
- resolve({ url, port: start.port });
5925
- });
5926
- });
5927
- }
5928
- };
5929
- // src/biometric.ts
5930
- var biometric = {
5931
- async isAvailable() {
5932
- if (!hasBridge("biometric"))
5933
- return false;
5934
- return await window.craft.biometric.isAvailable();
5935
- },
5936
- async getBiometryType() {
5937
- if (!hasBridge("biometric"))
5938
- return "none";
5939
- return await window.craft.biometric.getBiometryType();
5940
- },
5941
- async evaluate(reason, options) {
5942
- if (!reason)
5943
- throw new Error("biometric.evaluate: reason is required");
5944
- if (!hasBridge("biometric"))
5945
- return { success: false, errorCode: -1 };
5946
- return await window.craft.biometric.evaluate(reason, options);
5947
- }
5948
- };
5949
- // src/audio.ts
5950
- var webAudio = null;
5951
- var audio = {
5952
- async play(path, options) {
5953
- if (hasBridge("audio")) {
5954
- return await window.craft.audio.play(path, options);
5955
- }
5956
- if (typeof window === "undefined" || typeof Audio === "undefined")
5957
- return false;
5958
- if (webAudio) {
5959
- webAudio.pause();
5960
- webAudio = null;
5961
- }
5962
- webAudio = new Audio(path);
5963
- if (options?.volume != null)
5964
- webAudio.volume = options.volume;
5965
- if (options?.loops)
5966
- webAudio.loop = true;
5967
- try {
5968
- await webAudio.play();
5969
- return true;
5970
- } catch {
5971
- return false;
5972
- }
5973
- },
5974
- async playSystemSound(name) {
5975
- if (hasBridge("audio"))
5976
- return await window.craft.audio.playSystemSound(name);
5977
- if (typeof window === "undefined" || typeof Audio === "undefined")
5978
- return false;
5979
- try {
5980
- webAudio = new Audio(`/System/Library/Sounds/${name}.aiff`);
5981
- await webAudio.play();
5982
- return true;
5983
- } catch {
5984
- return false;
5985
- }
5986
- },
5987
- async stop() {
5988
- if (hasBridge("audio")) {
5989
- await window.craft.audio.stop();
5990
- return;
5991
- }
5992
- if (webAudio) {
5993
- webAudio.pause();
5994
- webAudio.currentTime = 0;
5995
- webAudio = null;
5996
- }
5997
- },
5998
- async isPlaying() {
5999
- if (hasBridge("audio"))
6000
- return await window.craft.audio.isPlaying();
6001
- return !!(webAudio && !webAudio.paused);
6002
- },
6003
- async startRecording(path, options) {
6004
- if (hasBridge("audio"))
6005
- return await window.craft.audio.startRecording(path, options);
6006
- return false;
6007
- },
6008
- async stopRecording() {
6009
- if (hasBridge("audio"))
6010
- await window.craft.audio.stopRecording();
6011
- },
6012
- async isRecording() {
6013
- if (hasBridge("audio"))
6014
- return await window.craft.audio.isRecording();
6015
- return false;
6016
- }
6017
- };
6018
- // src/apple-script.ts
6019
- var appleScript = {
6020
- async execute(source) {
6021
- if (!source)
6022
- throw new Error("appleScript.execute: source is required");
6023
- if (!hasBridge("appleScript"))
6024
- return { ok: false };
6025
- return await window.craft.appleScript.execute(source);
6026
- }
6027
- };
6028
- // src/file-associations.ts
6029
- var fileAssociations = {
6030
- async getDefault(uti) {
6031
- if (!uti)
6032
- throw new Error("fileAssociations.getDefault: uti is required");
6033
- if (!hasBridge("fileAssociations"))
6034
- return null;
6035
- const v = await window.craft.fileAssociations.getDefault(uti);
6036
- return v ? String(v) : null;
6037
- },
6038
- async setDefault(uti, bundleId) {
6039
- if (!uti || !bundleId)
6040
- throw new Error("fileAssociations.setDefault: uti and bundleId are required");
6041
- if (!hasBridge("fileAssociations"))
6042
- return false;
6043
- return await window.craft.fileAssociations.setDefault(uti, bundleId);
6044
- }
6045
- };
6046
- // src/tags.ts
6047
- var tags = {
6048
- async get(path) {
6049
- if (!path)
6050
- throw new Error("tags.get: path is required");
6051
- if (!hasBridge("tags"))
6052
- return [];
6053
- return await window.craft.tags.get(path);
6054
- },
6055
- async set(path, t) {
6056
- if (!path)
6057
- throw new Error("tags.set: path is required");
6058
- if (!hasBridge("tags"))
6059
- return false;
6060
- const arr = Array.isArray(t) ? t : [String(t)];
6061
- return await window.craft.tags.set(path, arr);
6062
- },
6063
- async clear(path) {
6064
- if (!path)
6065
- throw new Error("tags.clear: path is required");
6066
- if (!hasBridge("tags"))
6067
- return false;
6068
- return await window.craft.tags.clear(path);
6069
- }
6070
- };
6071
- // src/pdf.ts
6072
- var pdf = {
6073
- async countPages(path) {
6074
- if (!path)
6075
- throw new Error("pdf.countPages: path is required");
6076
- if (!hasBridge("pdf"))
6077
- return 0;
6078
- return await window.craft.pdf.countPages(path);
6079
- },
6080
- async extractText(path) {
6081
- if (!path)
6082
- throw new Error("pdf.extractText: path is required");
6083
- if (!hasBridge("pdf"))
6084
- return "";
6085
- return await window.craft.pdf.extractText(path);
6086
- }
6087
- };
6088
- // src/log.ts
6089
- var log = {
6090
- async debug(m) {
6091
- if (hasBridge("log"))
6092
- await window.craft.log.debug(m);
6093
- else
6094
- console.debug(m);
6095
- },
6096
- async info(m) {
6097
- if (hasBridge("log"))
6098
- await window.craft.log.info(m);
6099
- else
6100
- console.info(m);
6101
- },
6102
- async warn(m) {
6103
- if (hasBridge("log"))
6104
- await window.craft.log.warn(m);
6105
- else
6106
- console.warn(m);
6107
- },
6108
- async error(m) {
6109
- if (hasBridge("log"))
6110
- await window.craft.log.error(m);
6111
- else
6112
- console.error(m);
6113
- }
6114
- };
6115
- // src/bonjour.ts
6116
- var bonjour = {
6117
- async browse(serviceType) {
6118
- if (!hasBridge("bonjour"))
6119
- return { started: false, reason: "bridge unavailable" };
6120
- return await window.craft.bonjour.browse(serviceType);
6121
- },
6122
- async stop() {
6123
- if (hasBridge("bonjour"))
6124
- await window.craft.bonjour.stop();
6125
- },
6126
- onFound(cb) {
6127
- return onCraftEvent("craft:bonjour:found", cb);
6128
- },
6129
- onLost(cb) {
6130
- return onCraftEvent("craft:bonjour:lost", cb);
6131
- }
6132
- };
6133
- // src/spotlight.ts
6134
- var spotlight = {
6135
- async index(items) {
6136
- if (!hasBridge("spotlight"))
6137
- return { ok: false, reason: "bridge unavailable" };
6138
- return await window.craft.spotlight.index(items);
6139
- },
6140
- async remove(ids) {
6141
- if (!hasBridge("spotlight"))
6142
- return { ok: false };
6143
- return await window.craft.spotlight.remove(ids);
6144
- },
6145
- async removeAll() {
6146
- if (!hasBridge("spotlight"))
6147
- return { ok: false };
6148
- return await window.craft.spotlight.removeAll();
6149
- }
6150
- };
6151
- // src/speech-recognition.ts
6152
- var speechRecognition = {
6153
- async isAvailable() {
6154
- if (!hasBridge("speechRecognition"))
6155
- return false;
6156
- return await window.craft.speechRecognition.isAvailable();
6157
- },
6158
- async start(opts) {
6159
- if (!hasBridge("speechRecognition"))
6160
- return { started: false, reason: "bridge unavailable" };
6161
- return await window.craft.speechRecognition.start(opts);
6162
- },
6163
- async stop() {
6164
- if (hasBridge("speechRecognition"))
6165
- await window.craft.speechRecognition.stop();
6166
- },
6167
- onPartial(cb) {
6168
- return onCraftEvent("craft:speechRecognition:partial", cb);
6169
- },
6170
- onFinal(cb) {
6171
- return onCraftEvent("craft:speechRecognition:final", cb);
6172
- }
6173
- };
6174
- // src/vision.ts
6175
- var vision = {
6176
- async recognizeText(path) {
6177
- if (!path)
6178
- throw new Error("vision.recognizeText: path is required");
6179
- if (!hasBridge("vision"))
6180
- return [];
6181
- return await window.craft.vision.recognizeText(path);
6182
- },
6183
- async detectFaces(path) {
6184
- if (!path)
6185
- throw new Error("vision.detectFaces: path is required");
6186
- if (!hasBridge("vision"))
6187
- return [];
6188
- return await window.craft.vision.detectFaces(path);
6189
- },
6190
- async detectBarcodes(path) {
6191
- if (!path)
6192
- throw new Error("vision.detectBarcodes: path is required");
6193
- if (!hasBridge("vision"))
6194
- return [];
6195
- return await window.craft.vision.detectBarcodes(path);
6196
- }
6197
- };
6198
- // src/midi.ts
6199
- var midi = {
6200
- async listSources() {
6201
- if (!hasBridge("midi"))
6202
- return [];
6203
- return await window.craft.midi.listSources();
6204
- },
6205
- async listDestinations() {
6206
- if (!hasBridge("midi"))
6207
- return [];
6208
- return await window.craft.midi.listDestinations();
6209
- },
6210
- async send(destinationIndex, data) {
6211
- if (!hasBridge("midi"))
6212
- return { ok: false, reason: "bridge unavailable" };
6213
- return await window.craft.midi.send(destinationIndex, data);
6214
- },
6215
- async subscribe(sourceIndex) {
6216
- if (!hasBridge("midi"))
6217
- return { ok: false, reason: "bridge unavailable" };
6218
- return await window.craft.midi.subscribe(sourceIndex);
6219
- },
6220
- async unsubscribe(sourceIndex) {
6221
- if (!hasBridge("midi"))
6222
- return { ok: false };
6223
- return await window.craft.midi.unsubscribe(sourceIndex);
6224
- },
6225
- onMessage(cb) {
6226
- return onCraftEvent("craft:midi:message", cb);
6227
- }
6228
- };
6229
- // src/coreml.ts
6230
- var coreml = {
6231
- async loadModel(id, path) {
6232
- if (!id || !path)
6233
- throw new Error("coreml.loadModel: id and path are required");
6234
- if (!hasBridge("coreml"))
6235
- return false;
6236
- return await window.craft.coreml.loadModel(id, path);
6237
- },
6238
- async unloadModel(id) {
6239
- if (!hasBridge("coreml"))
6240
- return;
6241
- await window.craft.coreml.unloadModel(id);
6242
- },
6243
- async predict(id, input) {
6244
- if (!id)
6245
- throw new Error("coreml.predict: id is required");
6246
- if (!hasBridge("coreml"))
6247
- return null;
6248
- return await window.craft.coreml.predict(id, input);
6249
- }
6250
- };
6251
- // src/continuity-camera.ts
6252
- var continuityCamera = {
6253
- async listCameras() {
6254
- if (!hasBridge("continuityCamera"))
6255
- return [];
6256
- return await window.craft.continuityCamera.listCameras();
6257
- }
6258
- };
6259
- // src/service-menu.ts
6260
- var serviceMenu = {
6261
- async register(name) {
6262
- if (!name)
6263
- throw new Error("serviceMenu.register: name is required");
6264
- if (!hasBridge("serviceMenu"))
6265
- return { ok: false, reason: "bridge unavailable" };
6266
- return await window.craft.serviceMenu.register(name);
6267
- },
6268
- async unregister(name) {
6269
- if (!name)
6270
- throw new Error("serviceMenu.unregister: name is required");
6271
- if (!hasBridge("serviceMenu"))
6272
- return;
6273
- await window.craft.serviceMenu.unregister(name);
6274
- },
6275
- onInvoked(cb) {
6276
- return onCraftEvent("craft:serviceMenu:invoked", cb);
6277
- }
6278
- };
6279
- // src/serial.ts
6280
- var serial = {
6281
- async list() {
6282
- if (!hasBridge("serial"))
6283
- return [];
6284
- return await window.craft.serial.list();
6285
- },
6286
- async open(path, baud = 9600) {
6287
- if (!path)
6288
- throw new Error("serial.open: path is required");
6289
- if (!hasBridge("serial"))
6290
- return { ok: false, reason: "bridge unavailable" };
6291
- return await window.craft.serial.open(path, baud);
6292
- },
6293
- async write(id, data) {
6294
- if (!id)
6295
- throw new Error("serial.write: id is required");
6296
- if (!hasBridge("serial"))
6297
- return { ok: false, reason: "bridge unavailable" };
6298
- return await window.craft.serial.write(id, data);
6299
- },
6300
- async close(id) {
6301
- if (!hasBridge("serial"))
6302
- return;
6303
- await window.craft.serial.close(id);
6304
- },
6305
- onData(cb) {
6306
- return onCraftEvent("craft:serial:data", cb);
6307
- }
6308
- };
6309
- // src/capabilities.ts
6310
- var BRIDGE_INDEX = [
6311
- { name: "fs", support: "native" },
6312
- { name: "shell", support: "native" },
6313
- { name: "system", support: "all" },
6314
- { name: "clipboard", support: "all" },
6315
- { name: "dialog", support: "all" },
6316
- { name: "window", support: "native" },
6317
- { name: "tray", support: "native" },
6318
- { name: "menu", support: "native" },
6319
- { name: "theme", support: "all" },
6320
- { name: "screen", support: "all" },
6321
- { name: "network", support: "all" },
6322
- { name: "power", support: "all" },
6323
- { name: "battery", support: "all" },
6324
- { name: "notifications", support: "all" },
6325
- { name: "globalShortcuts", support: "native" },
6326
- { name: "autolaunch", support: "native" },
6327
- { name: "appInfo", support: "all" },
6328
- { name: "localServer", support: "native" },
6329
- { name: "bluetooth", support: "native" },
6330
- { name: "crashReporter", support: "all" },
6331
- { name: "updater", support: "native" },
6332
- { name: "iap", support: "macos" },
6333
- { name: "keychain", support: "native" },
6334
- { name: "log", support: "all" },
6335
- { name: "biometric", support: "macos" },
6336
- { name: "location", support: "macos" },
6337
- { name: "audio", support: "macos" },
6338
- { name: "deepLink", support: "native" },
6339
- { name: "handoff", support: "macos" },
6340
- { name: "liveActivities", support: "macos" },
6341
- { name: "touchbar", support: "macos" },
6342
- { name: "dragOut", support: "macos" },
6343
- { name: "appleScript", support: "macos" },
6344
- { name: "fileAssociations", support: "native" },
6345
- { name: "tags", support: "macos" },
6346
- { name: "pdf", support: "macos" },
6347
- { name: "bonjour", support: "macos" },
6348
- { name: "spotlight", support: "macos" },
6349
- { name: "speechRecognition", support: "macos" },
6350
- { name: "vision", support: "macos" },
6351
- { name: "midi", support: "macos" },
6352
- { name: "coreml", support: "macos" },
6353
- { name: "continuityCamera", support: "macos" },
6354
- { name: "serviceMenu", support: "macos" },
6355
- { name: "serial", support: "native" }
6356
- ];
6357
- function getCapabilities() {
6358
- return BRIDGE_INDEX.map(({ name, support }) => ({
6359
- name,
6360
- support,
6361
- available: hasBridge(name)
6362
- }));
6363
- }
6364
- function getCapability(name) {
6365
- const entry = BRIDGE_INDEX.find((b) => b.name === name);
6366
- if (!entry)
6367
- return;
6368
- return { ...entry, available: hasBridge(name) };
6369
- }
6370
- function isAvailable(name) {
6371
- const cap = getCapability(name);
6372
- return !!cap && cap.available;
6373
- }
6374
2583
  export {
6375
2584
  windowEvents,
6376
2585
  watchScreenSharing,
@@ -6420,7 +2629,7 @@ export {
6420
2629
  requestNotificationPermission,
6421
2630
  registerHotkey,
6422
2631
  redactPII,
6423
- prompt2 as prompt,
2632
+ prompt,
6424
2633
  printing,
6425
2634
  permissions,
6426
2635
  pdf,
@@ -6529,7 +2738,7 @@ export {
6529
2738
  craftBinaryNotFoundMessage2 as craftBinaryNotFoundMessage,
6530
2739
  coreml,
6531
2740
  continuityCamera,
6532
- confirm2 as confirm,
2741
+ confirm,
6533
2742
  closeAllWindows,
6534
2743
  closeAllModals,
6535
2744
  clipboard,
@@ -6541,7 +2750,7 @@ export {
6541
2750
  audio,
6542
2751
  appleScript,
6543
2752
  app as appInfo,
6544
- alert2 as alert,
2753
+ alert,
6545
2754
  UPDATE_MANIFEST_ASSET,
6546
2755
  TRAY_MENU_STYLES,
6547
2756
  TOAST_STYLES,
@@ -6553,5 +2762,3 @@ export {
6553
2762
  AutoUpdater,
6554
2763
  AVAILABLE_COMPONENTS
6555
2764
  };
6556
-
6557
- //# debugId=4BACEAAC5AF2924164756E2164756E21