@stacksjs/desktop 0.2.6 → 0.2.9

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
@@ -2070,10 +2070,10 @@ var MODAL_STYLES = `
2070
2070
  }
2071
2071
  `;
2072
2072
  // src/system-tray.ts
2073
- import process from "process";
2073
+ import process2 from "process";
2074
2074
  function getPlatform() {
2075
- if (process.platform) {
2076
- return process.platform;
2075
+ if (process2.platform) {
2076
+ return process2.platform;
2077
2077
  }
2078
2078
  return "unknown";
2079
2079
  }
@@ -2628,7 +2628,7 @@ window.stxDialog = {
2628
2628
  // src/window.ts
2629
2629
  import { existsSync } from "fs";
2630
2630
  import { join } from "path";
2631
- import process2 from "process";
2631
+ import process3 from "process";
2632
2632
  var currentConfig = {};
2633
2633
  function setDesktopConfig(config) {
2634
2634
  currentConfig = { ...currentConfig, ...config };
@@ -2640,19 +2640,19 @@ function resetDesktopConfig() {
2640
2640
  currentConfig = {};
2641
2641
  }
2642
2642
  var DEFAULT_SEARCH_PATHS = [
2643
- join(process2.env.HOME || "", ".bun/bin/craft"),
2644
- join(process2.env.HOME || "", "Code/Tools/craft/packages/zig/zig-out/bin/craft"),
2645
- join(process2.env.HOME || "", "Code/craft/packages/zig/zig-out/bin/craft"),
2646
- join(process2.cwd(), "../../craft/packages/zig/zig-out/bin/craft"),
2647
- join(process2.cwd(), "../../craft/packages/zig/zig-out/bin/craft-minimal"),
2648
- join(process2.cwd(), "../../../craft/packages/zig/zig-out/bin/craft"),
2649
- join(process2.cwd(), "../../../craft/packages/zig/zig-out/bin/craft-minimal"),
2650
- join(process2.cwd(), "../craft/packages/zig/zig-out/bin/craft"),
2651
- join(process2.cwd(), "../craft/packages/zig/zig-out/bin/craft-minimal"),
2652
- join(process2.cwd(), "node_modules/.bin/craft")
2643
+ join(process3.env.HOME || "", ".bun/bin/craft"),
2644
+ join(process3.env.HOME || "", "Code/Tools/craft/packages/zig/zig-out/bin/craft"),
2645
+ join(process3.env.HOME || "", "Code/craft/packages/zig/zig-out/bin/craft"),
2646
+ join(process3.cwd(), "../../craft/packages/zig/zig-out/bin/craft"),
2647
+ join(process3.cwd(), "../../craft/packages/zig/zig-out/bin/craft-minimal"),
2648
+ join(process3.cwd(), "../../../craft/packages/zig/zig-out/bin/craft"),
2649
+ join(process3.cwd(), "../../../craft/packages/zig/zig-out/bin/craft-minimal"),
2650
+ join(process3.cwd(), "../craft/packages/zig/zig-out/bin/craft"),
2651
+ join(process3.cwd(), "../craft/packages/zig/zig-out/bin/craft-minimal"),
2652
+ join(process3.cwd(), "node_modules/.bin/craft")
2653
2653
  ];
2654
2654
  function getCraftBinaryPath() {
2655
- const envPath = process2.env.CRAFT_BINARY_PATH;
2655
+ const envPath = process3.env.CRAFT_BINARY_PATH;
2656
2656
  if (envPath && existsSync(envPath)) {
2657
2657
  return envPath;
2658
2658
  }
@@ -2802,14 +2802,14 @@ async function openDevWindow(port, options = {}) {
2802
2802
  return true;
2803
2803
  } catch (error) {
2804
2804
  console.warn("\u26A0 Could not open native window:", error.message);
2805
- if (process2.env.NODE_ENV === "test" || process2.env.BUN_TEST) {
2805
+ if (process3.env.NODE_ENV === "test" || process3.env.BUN_TEST) {
2806
2806
  console.log("(Skipping browser fallback in test environment)");
2807
2807
  return false;
2808
2808
  }
2809
2809
  console.log("\uD83D\uDCF1 Opening in browser instead...");
2810
2810
  try {
2811
2811
  const { spawn } = await import("child_process");
2812
- const platform = process2.platform;
2812
+ const platform = process3.platform;
2813
2813
  let command;
2814
2814
  let args;
2815
2815
  if (platform === "darwin") {
@@ -2921,7 +2921,848 @@ window.stxWindow = {
2921
2921
  window.desktop = window.stxWindow;
2922
2922
  `;
2923
2923
  }
2924
+ // src/power.ts
2925
+ var currentProcess = null;
2926
+ var currentInstance = null;
2927
+
2928
+ class CaffeinateInstanceImpl {
2929
+ _process;
2930
+ _startedAt;
2931
+ _endsAt;
2932
+ _options;
2933
+ _expireHandlers = [];
2934
+ _expireTimer = null;
2935
+ _stopped = false;
2936
+ constructor(process4, options) {
2937
+ this._process = process4;
2938
+ this._options = options;
2939
+ this._startedAt = new Date;
2940
+ const duration = options.duration;
2941
+ if (duration && duration > 0) {
2942
+ const durationMs = duration * 60 * 1000;
2943
+ this._endsAt = new Date(this._startedAt.getTime() + durationMs);
2944
+ this._expireTimer = setTimeout(() => {
2945
+ this._stopped = true;
2946
+ for (const handler of this._expireHandlers) {
2947
+ try {
2948
+ handler();
2949
+ } catch {}
2950
+ }
2951
+ }, durationMs);
2952
+ } else {
2953
+ this._endsAt = null;
2954
+ }
2955
+ }
2956
+ get pid() {
2957
+ return this._process.pid;
2958
+ }
2959
+ get startedAt() {
2960
+ return this._startedAt;
2961
+ }
2962
+ get endsAt() {
2963
+ return this._endsAt;
2964
+ }
2965
+ get options() {
2966
+ return { ...this._options };
2967
+ }
2968
+ get isActive() {
2969
+ if (this._stopped)
2970
+ return false;
2971
+ return this._process.exitCode === null;
2972
+ }
2973
+ get remainingMs() {
2974
+ if (!this._endsAt)
2975
+ return null;
2976
+ const remaining = this._endsAt.getTime() - Date.now();
2977
+ return Math.max(0, remaining);
2978
+ }
2979
+ get elapsedMs() {
2980
+ return Date.now() - this._startedAt.getTime();
2981
+ }
2982
+ stop() {
2983
+ if (this._stopped)
2984
+ return;
2985
+ this._stopped = true;
2986
+ if (this._expireTimer) {
2987
+ clearTimeout(this._expireTimer);
2988
+ this._expireTimer = null;
2989
+ }
2990
+ try {
2991
+ this._process.kill();
2992
+ } catch {}
2993
+ }
2994
+ onExpire(handler) {
2995
+ this._expireHandlers.push(handler);
2996
+ }
2997
+ }
2998
+ function caffeinate(options = {}) {
2999
+ decaffeinate();
3000
+ const {
3001
+ duration,
3002
+ preventDisplaySleep = true,
3003
+ preventIdleSleep = true,
3004
+ preventSystemSleep = true,
3005
+ preventDiskSleep = false,
3006
+ assertUserActivity = true
3007
+ } = options;
3008
+ const flags = [];
3009
+ if (preventDisplaySleep)
3010
+ flags.push("-d");
3011
+ if (preventIdleSleep)
3012
+ flags.push("-i");
3013
+ if (preventSystemSleep)
3014
+ flags.push("-s");
3015
+ if (preventDiskSleep)
3016
+ flags.push("-m");
3017
+ if (assertUserActivity)
3018
+ flags.push("-u");
3019
+ const args = [...flags];
3020
+ if (duration && duration > 0) {
3021
+ args.push("-t", String(duration * 60));
3022
+ }
3023
+ const proc = Bun.spawn(["/usr/bin/caffeinate", ...args], {
3024
+ stdio: ["ignore", "ignore", "ignore"]
3025
+ });
3026
+ const instance = new CaffeinateInstanceImpl(proc, options);
3027
+ currentProcess = proc;
3028
+ currentInstance = instance;
3029
+ return instance;
3030
+ }
3031
+ function decaffeinate(instance) {
3032
+ if (instance) {
3033
+ instance.stop();
3034
+ if (currentInstance === instance) {
3035
+ currentProcess = null;
3036
+ currentInstance = null;
3037
+ }
3038
+ return;
3039
+ }
3040
+ if (currentInstance) {
3041
+ currentInstance.stop();
3042
+ }
3043
+ currentProcess = null;
3044
+ currentInstance = null;
3045
+ }
3046
+ function isCaffeinated() {
3047
+ return currentInstance !== null && currentInstance.isActive;
3048
+ }
3049
+ function getCaffeinateStatus() {
3050
+ if (!currentInstance || !currentInstance.isActive) {
3051
+ return {
3052
+ active: false,
3053
+ instance: null,
3054
+ startedAt: null,
3055
+ endsAt: null,
3056
+ durationMinutes: null
3057
+ };
3058
+ }
3059
+ const opts = currentInstance.options;
3060
+ const duration = opts.duration;
3061
+ return {
3062
+ active: true,
3063
+ instance: currentInstance,
3064
+ startedAt: currentInstance.startedAt,
3065
+ endsAt: currentInstance.endsAt,
3066
+ durationMinutes: duration && duration > 0 ? duration : -1
3067
+ };
3068
+ }
3069
+ function formatRemainingTime(instance) {
3070
+ const inst = instance || currentInstance;
3071
+ if (!inst || !inst.isActive)
3072
+ return "0:00";
3073
+ const remaining = inst.remainingMs;
3074
+ if (remaining === null)
3075
+ return "\u221E";
3076
+ const totalSeconds = Math.ceil(remaining / 1000);
3077
+ const hours = Math.floor(totalSeconds / 3600);
3078
+ const minutes = Math.floor(totalSeconds % 3600 / 60);
3079
+ const seconds = totalSeconds % 60;
3080
+ if (hours > 0)
3081
+ return `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
3082
+ return `${minutes}:${String(seconds).padStart(2, "0")}`;
3083
+ }
3084
+ function formatDuration(minutes) {
3085
+ if (minutes <= 0 || minutes === -1)
3086
+ return "Indefinitely";
3087
+ if (minutes < 60)
3088
+ return `${minutes} minutes`;
3089
+ if (minutes === 60)
3090
+ return "1 hour";
3091
+ if (minutes % 60 === 0)
3092
+ return `${minutes / 60} hours`;
3093
+ const h = Math.floor(minutes / 60);
3094
+ const m = minutes % 60;
3095
+ return `${h}h ${m}m`;
3096
+ }
3097
+ // src/preferences.ts
3098
+ import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync, watch } from "fs";
3099
+ import { dirname, join as join2 } from "path";
3100
+ import { homedir, platform } from "os";
3101
+ function getDefaultPrefsDir(appName) {
3102
+ const os = platform();
3103
+ const home = homedir();
3104
+ switch (os) {
3105
+ case "darwin":
3106
+ return join2(home, "Library", "Application Support", appName);
3107
+ case "win32":
3108
+ return join2(process.env.APPDATA || join2(home, "AppData", "Roaming"), appName);
3109
+ default:
3110
+ return join2(home, ".config", appName);
3111
+ }
3112
+ }
3113
+
3114
+ class PreferencesImpl {
3115
+ _defaults;
3116
+ _data;
3117
+ _path;
3118
+ _changeHandlers = new Map;
3119
+ _anyChangeHandlers = new Set;
3120
+ _watcher = null;
3121
+ _writeTimer = null;
3122
+ _writing = false;
3123
+ constructor(options) {
3124
+ this._defaults = { ...options.defaults };
3125
+ const dir = options.path ? dirname(options.path) : getDefaultPrefsDir(options.name);
3126
+ this._path = options.path || join2(dir, "preferences.json");
3127
+ if (!existsSync2(dir)) {
3128
+ mkdirSync(dir, { recursive: true });
3129
+ }
3130
+ this._data = this._load();
3131
+ if (options.watch !== false) {
3132
+ this._setupWatcher();
3133
+ }
3134
+ }
3135
+ get path() {
3136
+ return this._path;
3137
+ }
3138
+ get(key) {
3139
+ if (key in this._data) {
3140
+ return this._data[key];
3141
+ }
3142
+ return this._defaults[key];
3143
+ }
3144
+ set(key, value) {
3145
+ const oldValue = this.get(key);
3146
+ if (JSON.stringify(oldValue) === JSON.stringify(value))
3147
+ return;
3148
+ this._data[key] = value;
3149
+ this._scheduleSave();
3150
+ this._notifyChange(key, value, oldValue);
3151
+ }
3152
+ getAll() {
3153
+ return { ...this._defaults, ...this._data };
3154
+ }
3155
+ has(key) {
3156
+ return key in this._data;
3157
+ }
3158
+ delete(key) {
3159
+ if (!(key in this._data))
3160
+ return;
3161
+ const oldValue = this._data[key];
3162
+ delete this._data[key];
3163
+ this._scheduleSave();
3164
+ this._notifyChange(key, this._defaults[key], oldValue);
3165
+ }
3166
+ reset() {
3167
+ const oldData = { ...this._data };
3168
+ this._data = {};
3169
+ this._save();
3170
+ for (const key of Object.keys(oldData)) {
3171
+ const defaultVal = this._defaults[key];
3172
+ this._notifyChange(key, defaultVal, oldData[key]);
3173
+ }
3174
+ }
3175
+ onChange(key, handler) {
3176
+ const keyStr = key;
3177
+ if (!this._changeHandlers.has(keyStr)) {
3178
+ this._changeHandlers.set(keyStr, new Set);
3179
+ }
3180
+ this._changeHandlers.get(keyStr).add(handler);
3181
+ return () => {
3182
+ this._changeHandlers.get(keyStr)?.delete(handler);
3183
+ };
3184
+ }
3185
+ onAnyChange(handler) {
3186
+ this._anyChangeHandlers.add(handler);
3187
+ return () => {
3188
+ this._anyChangeHandlers.delete(handler);
3189
+ };
3190
+ }
3191
+ close() {
3192
+ if (this._watcher) {
3193
+ this._watcher.close();
3194
+ this._watcher = null;
3195
+ }
3196
+ if (this._writeTimer) {
3197
+ clearTimeout(this._writeTimer);
3198
+ this._writeTimer = null;
3199
+ this._save();
3200
+ }
3201
+ }
3202
+ _load() {
3203
+ try {
3204
+ if (existsSync2(this._path)) {
3205
+ const content = readFileSync(this._path, "utf-8");
3206
+ return JSON.parse(content);
3207
+ }
3208
+ } catch {}
3209
+ return {};
3210
+ }
3211
+ _save() {
3212
+ this._writing = true;
3213
+ try {
3214
+ const json = JSON.stringify({ ...this._defaults, ...this._data }, null, 2);
3215
+ writeFileSync(this._path, json, "utf-8");
3216
+ } catch (err) {
3217
+ console.error(`Failed to save preferences to ${this._path}:`, err);
3218
+ } finally {
3219
+ setTimeout(() => {
3220
+ this._writing = false;
3221
+ }, 100);
3222
+ }
3223
+ }
3224
+ _scheduleSave() {
3225
+ if (this._writeTimer) {
3226
+ clearTimeout(this._writeTimer);
3227
+ }
3228
+ this._writeTimer = setTimeout(() => {
3229
+ this._writeTimer = null;
3230
+ this._save();
3231
+ }, 50);
3232
+ }
3233
+ _setupWatcher() {
3234
+ try {
3235
+ if (!existsSync2(this._path)) {
3236
+ this._save();
3237
+ }
3238
+ this._watcher = watch(this._path, () => {
3239
+ if (this._writing)
3240
+ return;
3241
+ const newData = this._load();
3242
+ const oldData = { ...this._data };
3243
+ this._data = newData;
3244
+ const allKeys = new Set([...Object.keys(oldData), ...Object.keys(newData)]);
3245
+ for (const key of allKeys) {
3246
+ const oldVal = key in oldData ? oldData[key] : this._defaults[key];
3247
+ const newVal = key in newData ? newData[key] : this._defaults[key];
3248
+ if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
3249
+ this._notifyChange(key, newVal, oldVal);
3250
+ }
3251
+ }
3252
+ });
3253
+ } catch {}
3254
+ }
3255
+ _notifyChange(key, value, oldValue) {
3256
+ const handlers = this._changeHandlers.get(key);
3257
+ if (handlers) {
3258
+ for (const handler of handlers) {
3259
+ try {
3260
+ handler(value, oldValue);
3261
+ } catch {}
3262
+ }
3263
+ }
3264
+ for (const handler of this._anyChangeHandlers) {
3265
+ try {
3266
+ handler(key, value, oldValue);
3267
+ } catch {}
3268
+ }
3269
+ }
3270
+ }
3271
+ function createPreferences(options) {
3272
+ return new PreferencesImpl(options);
3273
+ }
3274
+ // src/autolaunch.ts
3275
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
3276
+ import { join as join3 } from "path";
3277
+ import { homedir as homedir2, platform as platform2 } from "os";
3278
+ function getLaunchAgentPath(appName) {
3279
+ const identifier = appName.toLowerCase().replace(/[^a-z0-9]/g, "-");
3280
+ return join3(homedir2(), "Library", "LaunchAgents", `com.${identifier}.plist`);
3281
+ }
3282
+ function getLinuxAutostartPath(appName) {
3283
+ const configDir = process.env.XDG_CONFIG_HOME || join3(homedir2(), ".config");
3284
+ return join3(configDir, "autostart", `${appName.toLowerCase()}.desktop`);
3285
+ }
3286
+ async function setAutoLaunchMacOS(enabled, options) {
3287
+ const appName = options.appName || "StxApp";
3288
+ const plistPath = getLaunchAgentPath(appName);
3289
+ const launchAgentsDir = join3(homedir2(), "Library", "LaunchAgents");
3290
+ if (enabled) {
3291
+ const appPath = options.appPath || process.execPath;
3292
+ const args = options.args || [];
3293
+ const programArgs = [appPath, ...args];
3294
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
3295
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3296
+ <plist version="1.0">
3297
+ <dict>
3298
+ <key>Label</key>
3299
+ <string>com.${appName.toLowerCase().replace(/[^a-z0-9]/g, "-")}</string>
3300
+ <key>ProgramArguments</key>
3301
+ <array>
3302
+ ${programArgs.map((arg) => ` <string>${escapeXml(arg)}</string>`).join(`
3303
+ `)}
3304
+ </array>
3305
+ <key>RunAtLoad</key>
3306
+ <true/>
3307
+ <key>KeepAlive</key>
3308
+ <false/>
3309
+ ${options.isHidden ? ` <key>ProcessType</key>
3310
+ <string>Background</string>` : ""}
3311
+ </dict>
3312
+ </plist>`;
3313
+ if (!existsSync3(launchAgentsDir)) {
3314
+ mkdirSync2(launchAgentsDir, { recursive: true });
3315
+ }
3316
+ writeFileSync2(plistPath, plist, "utf-8");
3317
+ } else {
3318
+ if (existsSync3(plistPath)) {
3319
+ unlinkSync(plistPath);
3320
+ }
3321
+ }
3322
+ }
3323
+ async function setAutoLaunchLinux(enabled, options) {
3324
+ const appName = options.appName || "StxApp";
3325
+ const desktopPath = getLinuxAutostartPath(appName);
3326
+ const autostartDir = join3(process.env.XDG_CONFIG_HOME || join3(homedir2(), ".config"), "autostart");
3327
+ if (enabled) {
3328
+ const appPath = options.appPath || process.execPath;
3329
+ const args = options.args || [];
3330
+ const exec = [appPath, ...args].join(" ");
3331
+ const desktopEntry = `[Desktop Entry]
3332
+ Type=Application
3333
+ Name=${appName}
3334
+ Exec=${exec}
3335
+ Terminal=false
3336
+ StartupNotify=false
3337
+ ${options.isHidden ? `X-GNOME-Autostart-enabled=true
3338
+ NoDisplay=true` : ""}
3339
+ `;
3340
+ if (!existsSync3(autostartDir)) {
3341
+ mkdirSync2(autostartDir, { recursive: true });
3342
+ }
3343
+ writeFileSync2(desktopPath, desktopEntry, "utf-8");
3344
+ } else {
3345
+ if (existsSync3(desktopPath)) {
3346
+ unlinkSync(desktopPath);
3347
+ }
3348
+ }
3349
+ }
3350
+ async function isAutoLaunchEnabledMacOS(appName) {
3351
+ return existsSync3(getLaunchAgentPath(appName));
3352
+ }
3353
+ async function isAutoLaunchEnabledLinux(appName) {
3354
+ const path = getLinuxAutostartPath(appName);
3355
+ if (!existsSync3(path))
3356
+ return false;
3357
+ try {
3358
+ const content = readFileSync2(path, "utf-8");
3359
+ if (content.includes("X-GNOME-Autostart-enabled=false"))
3360
+ return false;
3361
+ if (content.includes("Hidden=true"))
3362
+ return false;
3363
+ return true;
3364
+ } catch {
3365
+ return false;
3366
+ }
3367
+ }
3368
+ function escapeXml(str) {
3369
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
3370
+ }
3371
+ async function setAutoLaunch(enabled, options = {}) {
3372
+ const os = platform2();
3373
+ switch (os) {
3374
+ case "darwin":
3375
+ return setAutoLaunchMacOS(enabled, options);
3376
+ case "linux":
3377
+ return setAutoLaunchLinux(enabled, options);
3378
+ default:
3379
+ throw new Error(`Auto-launch is not yet supported on ${os}`);
3380
+ }
3381
+ }
3382
+ async function isAutoLaunchEnabled(appName) {
3383
+ const name = appName || "StxApp";
3384
+ const os = platform2();
3385
+ switch (os) {
3386
+ case "darwin":
3387
+ return isAutoLaunchEnabledMacOS(name);
3388
+ case "linux":
3389
+ return isAutoLaunchEnabledLinux(name);
3390
+ default:
3391
+ return false;
3392
+ }
3393
+ }
3394
+ // src/hotkeys.ts
3395
+ var registrations = new Map;
3396
+ var nextId = 0;
3397
+ var documentListenerAttached = false;
3398
+ function parseShortcut(shortcut) {
3399
+ const parts = shortcut.split("+").map((p) => p.trim());
3400
+ const result = {
3401
+ key: "",
3402
+ meta: false,
3403
+ ctrl: false,
3404
+ shift: false,
3405
+ alt: false
3406
+ };
3407
+ for (const part of parts) {
3408
+ const lower = part.toLowerCase();
3409
+ switch (lower) {
3410
+ case "cmd":
3411
+ case "command":
3412
+ case "meta":
3413
+ case "\u2318":
3414
+ result.meta = true;
3415
+ break;
3416
+ case "ctrl":
3417
+ case "control":
3418
+ case "\u2303":
3419
+ result.ctrl = true;
3420
+ break;
3421
+ case "shift":
3422
+ case "\u21E7":
3423
+ result.shift = true;
3424
+ break;
3425
+ case "alt":
3426
+ case "option":
3427
+ case "opt":
3428
+ case "\u2325":
3429
+ result.alt = true;
3430
+ break;
3431
+ case "cmdorctrl":
3432
+ case "commandorcontrol":
3433
+ if (typeof process !== "undefined" && process.platform === "darwin") {
3434
+ result.meta = true;
3435
+ } else {
3436
+ result.ctrl = true;
3437
+ }
3438
+ break;
3439
+ default:
3440
+ result.key = lower;
3441
+ }
3442
+ }
3443
+ return result;
3444
+ }
3445
+ function formatShortcut(parsed) {
3446
+ const parts = [];
3447
+ if (parsed.ctrl)
3448
+ parts.push("\u2303");
3449
+ if (parsed.alt)
3450
+ parts.push("\u2325");
3451
+ if (parsed.shift)
3452
+ parts.push("\u21E7");
3453
+ if (parsed.meta)
3454
+ parts.push("\u2318");
3455
+ parts.push(parsed.key.toUpperCase());
3456
+ return parts.join("");
3457
+ }
3458
+ function matchesEvent(event, parsed) {
3459
+ if (parsed.meta !== event.metaKey)
3460
+ return false;
3461
+ if (parsed.ctrl !== event.ctrlKey)
3462
+ return false;
3463
+ if (parsed.shift !== event.shiftKey)
3464
+ return false;
3465
+ if (parsed.alt !== event.altKey)
3466
+ return false;
3467
+ const eventKey = event.key.toLowerCase();
3468
+ return eventKey === parsed.key || event.code.toLowerCase() === `key${parsed.key}`;
3469
+ }
3470
+ function handleKeyDown(event) {
3471
+ for (const [, reg] of registrations) {
3472
+ if (matchesEvent(event, reg.parsed)) {
3473
+ event.preventDefault();
3474
+ event.stopPropagation();
3475
+ try {
3476
+ reg.handler();
3477
+ } catch {}
3478
+ break;
3479
+ }
3480
+ }
3481
+ }
3482
+ function ensureDocumentListener() {
3483
+ if (documentListenerAttached)
3484
+ return;
3485
+ if (typeof document === "undefined")
3486
+ return;
3487
+ document.addEventListener("keydown", handleKeyDown, true);
3488
+ documentListenerAttached = true;
3489
+ }
3490
+ function removeDocumentListener() {
3491
+ if (!documentListenerAttached)
3492
+ return;
3493
+ if (typeof document === "undefined")
3494
+ return;
3495
+ document.removeEventListener("keydown", handleKeyDown, true);
3496
+ documentListenerAttached = false;
3497
+ }
3498
+ async function registerWithCraft(id, shortcut) {
3499
+ if (typeof window === "undefined")
3500
+ return false;
3501
+ const craft = window.craft;
3502
+ if (!craft?.hotkeys?.register)
3503
+ return false;
3504
+ try {
3505
+ await craft.hotkeys.register(id, shortcut);
3506
+ return true;
3507
+ } catch {
3508
+ return false;
3509
+ }
3510
+ }
3511
+ async function unregisterWithCraft(id) {
3512
+ if (typeof window === "undefined")
3513
+ return;
3514
+ const craft = window.craft;
3515
+ if (!craft?.hotkeys?.unregister)
3516
+ return;
3517
+ try {
3518
+ await craft.hotkeys.unregister(id);
3519
+ } catch {}
3520
+ }
3521
+ function registerHotkey(shortcut, handler) {
3522
+ const id = `hotkey_${++nextId}_${Date.now()}`;
3523
+ const parsed = parseShortcut(shortcut);
3524
+ registrations.set(id, { shortcut, handler, parsed });
3525
+ registerWithCraft(id, shortcut);
3526
+ ensureDocumentListener();
3527
+ const registration = {
3528
+ shortcut,
3529
+ id,
3530
+ unregister() {
3531
+ unregisterHotkey(registration);
3532
+ }
3533
+ };
3534
+ return registration;
3535
+ }
3536
+ function unregisterHotkey(registration) {
3537
+ registrations.delete(registration.id);
3538
+ unregisterWithCraft(registration.id);
3539
+ if (registrations.size === 0) {
3540
+ removeDocumentListener();
3541
+ }
3542
+ }
3543
+ function unregisterAllHotkeys() {
3544
+ for (const [id] of registrations) {
3545
+ unregisterWithCraft(id);
3546
+ }
3547
+ registrations.clear();
3548
+ removeDocumentListener();
3549
+ }
3550
+ function getRegisteredHotkeys() {
3551
+ return Array.from(registrations.entries()).map(([id, reg]) => ({
3552
+ shortcut: reg.shortcut,
3553
+ id,
3554
+ unregister() {
3555
+ registrations.delete(id);
3556
+ unregisterWithCraft(id);
3557
+ if (registrations.size === 0) {
3558
+ removeDocumentListener();
3559
+ }
3560
+ }
3561
+ }));
3562
+ }
3563
+ // src/timer.ts
3564
+ class TimerImpl {
3565
+ _duration;
3566
+ _tickInterval;
3567
+ _remaining;
3568
+ _running = false;
3569
+ _paused = false;
3570
+ _complete = false;
3571
+ _intervalId = null;
3572
+ _lastTick = 0;
3573
+ _completionHandlers = new Set;
3574
+ _tickHandlers = new Set;
3575
+ constructor(options) {
3576
+ this._duration = options.duration;
3577
+ this._tickInterval = options.tickInterval || 1000;
3578
+ this._remaining = options.duration;
3579
+ if (options.onComplete)
3580
+ this._completionHandlers.add(options.onComplete);
3581
+ if (options.onTick)
3582
+ this._tickHandlers.add(options.onTick);
3583
+ if (options.autoStart)
3584
+ this.start();
3585
+ }
3586
+ get isRunning() {
3587
+ return this._running && !this._paused;
3588
+ }
3589
+ get isPaused() {
3590
+ return this._paused;
3591
+ }
3592
+ get isComplete() {
3593
+ return this._complete;
3594
+ }
3595
+ get remaining() {
3596
+ if (this._running && !this._paused) {
3597
+ const elapsed = Date.now() - this._lastTick;
3598
+ return Math.max(0, this._remaining - elapsed);
3599
+ }
3600
+ return Math.max(0, this._remaining);
3601
+ }
3602
+ get elapsed() {
3603
+ return this._duration - this.remaining;
3604
+ }
3605
+ get duration() {
3606
+ return this._duration;
3607
+ }
3608
+ get progress() {
3609
+ if (this._duration === 0)
3610
+ return 1;
3611
+ return Math.min(1, this.elapsed / this._duration);
3612
+ }
3613
+ start() {
3614
+ if (this._running)
3615
+ return;
3616
+ this._running = true;
3617
+ this._paused = false;
3618
+ this._complete = false;
3619
+ this._lastTick = Date.now();
3620
+ this._startInterval();
3621
+ }
3622
+ stop() {
3623
+ this._clearInterval();
3624
+ this._running = false;
3625
+ this._paused = false;
3626
+ this._remaining = this._duration;
3627
+ }
3628
+ pause() {
3629
+ if (!this._running || this._paused)
3630
+ return;
3631
+ const elapsed = Date.now() - this._lastTick;
3632
+ this._remaining = Math.max(0, this._remaining - elapsed);
3633
+ this._clearInterval();
3634
+ this._paused = true;
3635
+ }
3636
+ resume() {
3637
+ if (!this._paused)
3638
+ return;
3639
+ this._paused = false;
3640
+ this._lastTick = Date.now();
3641
+ this._startInterval();
3642
+ }
3643
+ reset() {
3644
+ this._clearInterval();
3645
+ this._running = false;
3646
+ this._paused = false;
3647
+ this._complete = false;
3648
+ this._remaining = this._duration;
3649
+ }
3650
+ onComplete(handler) {
3651
+ this._completionHandlers.add(handler);
3652
+ return () => {
3653
+ this._completionHandlers.delete(handler);
3654
+ };
3655
+ }
3656
+ onTick(handler) {
3657
+ this._tickHandlers.add(handler);
3658
+ return () => {
3659
+ this._tickHandlers.delete(handler);
3660
+ };
3661
+ }
3662
+ _startInterval() {
3663
+ this._clearInterval();
3664
+ this._intervalId = setInterval(() => {
3665
+ const now = Date.now();
3666
+ const elapsed = now - this._lastTick;
3667
+ this._lastTick = now;
3668
+ this._remaining = Math.max(0, this._remaining - elapsed);
3669
+ for (const handler of this._tickHandlers) {
3670
+ try {
3671
+ handler(this._remaining);
3672
+ } catch {}
3673
+ }
3674
+ if (this._remaining <= 0) {
3675
+ this._clearInterval();
3676
+ this._running = false;
3677
+ this._complete = true;
3678
+ for (const handler of this._completionHandlers) {
3679
+ try {
3680
+ handler();
3681
+ } catch {}
3682
+ }
3683
+ }
3684
+ }, this._tickInterval);
3685
+ }
3686
+ _clearInterval() {
3687
+ if (this._intervalId !== null) {
3688
+ clearInterval(this._intervalId);
3689
+ this._intervalId = null;
3690
+ }
3691
+ }
3692
+ }
3693
+
3694
+ class IntervalImpl {
3695
+ _interval;
3696
+ _handler;
3697
+ _immediate;
3698
+ _running = false;
3699
+ _intervalId = null;
3700
+ constructor(options) {
3701
+ this._interval = options.interval;
3702
+ this._handler = options.handler;
3703
+ this._immediate = options.immediate !== false;
3704
+ if (this._immediate)
3705
+ this.start();
3706
+ }
3707
+ get isRunning() {
3708
+ return this._running;
3709
+ }
3710
+ start() {
3711
+ if (this._running)
3712
+ return;
3713
+ this._running = true;
3714
+ this._intervalId = setInterval(() => {
3715
+ try {
3716
+ this._handler();
3717
+ } catch {}
3718
+ }, this._interval);
3719
+ }
3720
+ stop() {
3721
+ if (this._intervalId !== null) {
3722
+ clearInterval(this._intervalId);
3723
+ this._intervalId = null;
3724
+ }
3725
+ this._running = false;
3726
+ }
3727
+ }
3728
+ function createTimer(options) {
3729
+ return new TimerImpl(options);
3730
+ }
3731
+ function createInterval(options) {
3732
+ return new IntervalImpl(options);
3733
+ }
3734
+ function delay(ms) {
3735
+ return new Promise((resolve) => setTimeout(resolve, ms));
3736
+ }
3737
+ function formatTime(ms) {
3738
+ const totalSeconds = Math.ceil(ms / 1000);
3739
+ const hours = Math.floor(totalSeconds / 3600);
3740
+ const minutes = Math.floor(totalSeconds % 3600 / 60);
3741
+ const seconds = totalSeconds % 60;
3742
+ if (hours > 0)
3743
+ return `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
3744
+ return `${minutes}:${String(seconds).padStart(2, "0")}`;
3745
+ }
3746
+ function formatCompact(ms) {
3747
+ const totalSeconds = Math.ceil(ms / 1000);
3748
+ const hours = Math.floor(totalSeconds / 3600);
3749
+ const minutes = Math.floor(totalSeconds % 3600 / 60);
3750
+ const seconds = totalSeconds % 60;
3751
+ if (hours > 0) {
3752
+ if (minutes > 0)
3753
+ return `${hours}h ${minutes}m`;
3754
+ return `${hours}h`;
3755
+ }
3756
+ if (minutes > 0) {
3757
+ if (seconds > 0)
3758
+ return `${minutes}m ${seconds}s`;
3759
+ return `${minutes}m`;
3760
+ }
3761
+ return `${seconds}s`;
3762
+ }
2924
3763
  export {
3764
+ unregisterHotkey,
3765
+ unregisterAllHotkeys,
2925
3766
  triggerTrayAction,
2926
3767
  showWarningToast,
2927
3768
  showWarningModal,
@@ -2944,30 +3785,45 @@ export {
2944
3785
  showAlertDialog,
2945
3786
  showAlert,
2946
3787
  setDesktopConfig,
3788
+ setAutoLaunch,
2947
3789
  resetDesktopConfig,
2948
3790
  requestNotificationPermission,
3791
+ registerHotkey,
2949
3792
  prompt2 as prompt,
3793
+ parseShortcut,
2950
3794
  openDevWindow,
2951
3795
  notify,
2952
3796
  isWebviewAvailable,
3797
+ isCaffeinated,
3798
+ isAutoLaunchEnabled,
2953
3799
  getWindowBridgeScript,
2954
3800
  getWindow,
2955
3801
  getTrayInstance,
2956
3802
  getTrayBridgeScript,
2957
3803
  getSimulatedTrayHTML,
3804
+ getRegisteredHotkeys,
2958
3805
  getDialogBridgeScript,
2959
3806
  getDesktopConfig,
3807
+ getCaffeinateStatus,
2960
3808
  getActiveWindowIds,
2961
3809
  getActiveTrayInstances,
2962
3810
  getActiveModalCount,
2963
3811
  getActiveAlertCount,
3812
+ formatTime,
3813
+ formatShortcut,
3814
+ formatRemainingTime,
3815
+ formatDuration,
3816
+ formatCompact,
2964
3817
  dismissAllAlerts,
2965
3818
  dismissAlertById,
3819
+ delay,
3820
+ decaffeinate,
2966
3821
  createWindowWithHTML,
2967
3822
  createWindow,
2968
3823
  createWebView,
2969
3824
  createTreeView,
2970
3825
  createTooltip,
3826
+ createTimer,
2971
3827
  createTimePicker,
2972
3828
  createTextInput,
2973
3829
  createTabs,
@@ -2980,11 +3836,13 @@ export {
2980
3836
  createRating,
2981
3837
  createRadioButton,
2982
3838
  createProgressBar,
3839
+ createPreferences,
2983
3840
  createModalComponent,
2984
3841
  createMenubar,
2985
3842
  createMediaPlayer,
2986
3843
  createListView,
2987
3844
  createLabel,
3845
+ createInterval,
2988
3846
  createImageView,
2989
3847
  createFileExplorer,
2990
3848
  createDropdown,
@@ -3004,6 +3862,7 @@ export {
3004
3862
  confirm2 as confirm,
3005
3863
  closeAllWindows,
3006
3864
  closeAllModals,
3865
+ caffeinate,
3007
3866
  alert2 as alert,
3008
3867
  TRAY_MENU_STYLES,
3009
3868
  TOAST_STYLES,
@@ -3012,4 +3871,4 @@ export {
3012
3871
  AVAILABLE_COMPONENTS
3013
3872
  };
3014
3873
 
3015
- //# debugId=574CB389B0B4B44F64756E2164756E21
3874
+ //# debugId=20FC12EC72514C4464756E2164756E21