@nikala-ui/hooks 0.10.1 → 0.11.0-nightly.b01fa01
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/package.json +1 -1
- package/src/create-app-updater.ts +272 -0
- package/src/create-chat-scroll.ts +113 -0
- package/src/create-document-tabs.ts +267 -0
- package/src/create-drop-zone.ts +317 -0
- package/src/create-global-shortcut.ts +140 -0
- package/src/create-pagination.ts +208 -0
- package/src/create-tauri-window.ts +280 -0
- package/src/index.ts +8 -1
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { createSignal, onMount, onCleanup, type Accessor } from "solid-js";
|
|
2
|
+
|
|
3
|
+
export type TauriPlatform = "macos" | "windows" | "linux" | "web";
|
|
4
|
+
|
|
5
|
+
export interface CreateTauriWindowOptions {
|
|
6
|
+
/** Initial fallback platform if not detected automatically. */
|
|
7
|
+
defaultPlatform?: TauriPlatform;
|
|
8
|
+
/** Listen to window resize and focus events. Defaults to true. */
|
|
9
|
+
listenEvents?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CreateTauriWindowReturn {
|
|
13
|
+
/** Signal indicating whether the application is running inside a Tauri runtime. */
|
|
14
|
+
isTauri: Accessor<boolean>;
|
|
15
|
+
/** Detected host platform ('macos' | 'windows' | 'linux' | 'web'). */
|
|
16
|
+
platform: Accessor<TauriPlatform>;
|
|
17
|
+
/** Signal indicating whether the window is currently maximized. */
|
|
18
|
+
isMaximized: Accessor<boolean>;
|
|
19
|
+
/** Signal indicating whether the window is currently minimized. */
|
|
20
|
+
isMinimized: Accessor<boolean>;
|
|
21
|
+
/** Signal indicating whether the window is currently in fullscreen mode. */
|
|
22
|
+
isFullscreen: Accessor<boolean>;
|
|
23
|
+
/** Signal indicating whether the window is currently focused. */
|
|
24
|
+
isFocused: Accessor<boolean>;
|
|
25
|
+
/** Minimize the application window. */
|
|
26
|
+
minimize: () => Promise<void>;
|
|
27
|
+
/** Maximize the application window. */
|
|
28
|
+
maximize: () => Promise<void>;
|
|
29
|
+
/** Unmaximize the application window. */
|
|
30
|
+
unmaximize: () => Promise<void>;
|
|
31
|
+
/** Toggle between maximized and normal window states. */
|
|
32
|
+
toggleMaximize: () => Promise<void>;
|
|
33
|
+
/** Close the application window. */
|
|
34
|
+
close: () => Promise<void>;
|
|
35
|
+
/** Toggle fullscreen state. */
|
|
36
|
+
setFullscreen: (fullscreen: boolean) => Promise<void>;
|
|
37
|
+
/** Start dragging the window. Can be bound to mousedown event or custom titlebar headers. */
|
|
38
|
+
startDragging: (e?: MouseEvent) => Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Helper to detect current operating system in browser or Tauri environments.
|
|
43
|
+
*/
|
|
44
|
+
export function detectPlatform(): TauriPlatform {
|
|
45
|
+
if (typeof window === "undefined" || typeof navigator === "undefined") {
|
|
46
|
+
return "web";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
50
|
+
const platform = (navigator as any).userAgentData?.platform?.toLowerCase() || navigator.platform?.toLowerCase() || "";
|
|
51
|
+
|
|
52
|
+
if (platform.includes("mac") || userAgent.includes("macintosh") || userAgent.includes("mac os x")) {
|
|
53
|
+
return "macos";
|
|
54
|
+
}
|
|
55
|
+
if (platform.includes("win") || userAgent.includes("windows")) {
|
|
56
|
+
return "windows";
|
|
57
|
+
}
|
|
58
|
+
if (platform.includes("linux") || userAgent.includes("linux")) {
|
|
59
|
+
return "linux";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return "web";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Checks whether the current environment is running inside Tauri v1 or v2.
|
|
67
|
+
*/
|
|
68
|
+
export function isTauriEnvironment(): boolean {
|
|
69
|
+
if (typeof window === "undefined") return false;
|
|
70
|
+
return (
|
|
71
|
+
"__TAURI_INTERNALS__" in window ||
|
|
72
|
+
"__TAURI__" in window ||
|
|
73
|
+
"__TAURI_PATTERN__" in window ||
|
|
74
|
+
Boolean((window as any).__TAURI_METADATA__)
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* SolidJS reactive primitive for controlling and observing Tauri desktop window states.
|
|
80
|
+
* Provides seamless fallbacks for web environments and interactive previews.
|
|
81
|
+
*/
|
|
82
|
+
export function createTauriWindow(
|
|
83
|
+
options: CreateTauriWindowOptions = {}
|
|
84
|
+
): CreateTauriWindowReturn {
|
|
85
|
+
const [isTauri, setIsTauri] = createSignal<boolean>(false);
|
|
86
|
+
const [platform, setPlatform] = createSignal<TauriPlatform>(options.defaultPlatform || "web");
|
|
87
|
+
const [isMaximized, setIsMaximized] = createSignal<boolean>(false);
|
|
88
|
+
const [isMinimized, setIsMinimized] = createSignal<boolean>(false);
|
|
89
|
+
const [isFullscreen, setIsFullscreen] = createSignal<boolean>(false);
|
|
90
|
+
const [isFocused, setIsFocused] = createSignal<boolean>(true);
|
|
91
|
+
|
|
92
|
+
let unlistenResize: (() => void) | null = null;
|
|
93
|
+
let unlistenFocus: (() => void) | null = null;
|
|
94
|
+
|
|
95
|
+
// Retrieve Tauri Window instance safely
|
|
96
|
+
const getTauriWindow = async () => {
|
|
97
|
+
if (typeof window === "undefined") return null;
|
|
98
|
+
try {
|
|
99
|
+
if ((window as any).__TAURI__?.window?.getCurrentWindow) {
|
|
100
|
+
return (window as any).__TAURI__.window.getCurrentWindow();
|
|
101
|
+
}
|
|
102
|
+
if ((window as any).__TAURI__?.window?.appWindow) {
|
|
103
|
+
return (window as any).__TAURI__.window.appWindow;
|
|
104
|
+
}
|
|
105
|
+
// Variable prevents Vite dev server from static module resolution
|
|
106
|
+
const moduleName = "@tauri-apps/api/window";
|
|
107
|
+
// @ts-ignore - Optional runtime dependency in Tauri apps
|
|
108
|
+
const tauriApi = await import(/* @vite-ignore */ moduleName).catch(() => null);
|
|
109
|
+
if (tauriApi?.getCurrentWindow) {
|
|
110
|
+
return tauriApi.getCurrentWindow();
|
|
111
|
+
}
|
|
112
|
+
} catch {
|
|
113
|
+
// Running in standard web mode
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const updateWindowStates = async () => {
|
|
119
|
+
if (typeof window === "undefined") return;
|
|
120
|
+
|
|
121
|
+
const tauriWin = await getTauriWindow();
|
|
122
|
+
if (tauriWin) {
|
|
123
|
+
setIsTauri(true);
|
|
124
|
+
try {
|
|
125
|
+
const [max, min, full, foc] = await Promise.all([
|
|
126
|
+
tauriWin.isMaximized ? tauriWin.isMaximized() : false,
|
|
127
|
+
tauriWin.isMinimized ? tauriWin.isMinimized() : false,
|
|
128
|
+
tauriWin.isFullscreen ? tauriWin.isFullscreen() : false,
|
|
129
|
+
tauriWin.isFocused ? tauriWin.isFocused() : true,
|
|
130
|
+
]);
|
|
131
|
+
setIsMaximized(Boolean(max));
|
|
132
|
+
setIsMinimized(Boolean(min));
|
|
133
|
+
setIsFullscreen(Boolean(full));
|
|
134
|
+
setIsFocused(Boolean(foc));
|
|
135
|
+
} catch {
|
|
136
|
+
// Fallback gracefully
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
setIsTauri(false);
|
|
140
|
+
setIsFullscreen(Boolean(document.fullscreenElement));
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
onMount(async () => {
|
|
145
|
+
if (typeof window === "undefined") return;
|
|
146
|
+
|
|
147
|
+
setPlatform(detectPlatform());
|
|
148
|
+
setIsTauri(isTauriEnvironment());
|
|
149
|
+
await updateWindowStates();
|
|
150
|
+
|
|
151
|
+
if (options.listenEvents !== false) {
|
|
152
|
+
const handleWindowFocus = () => setIsFocused(true);
|
|
153
|
+
const handleWindowBlur = () => setIsFocused(false);
|
|
154
|
+
const handleFullscreenChange = () => setIsFullscreen(Boolean(document.fullscreenElement));
|
|
155
|
+
|
|
156
|
+
window.addEventListener("focus", handleWindowFocus);
|
|
157
|
+
window.addEventListener("blur", handleWindowBlur);
|
|
158
|
+
document.addEventListener("fullscreenchange", handleFullscreenChange);
|
|
159
|
+
|
|
160
|
+
const tauriWin = await getTauriWindow();
|
|
161
|
+
if (tauriWin?.onResized) {
|
|
162
|
+
try {
|
|
163
|
+
unlistenResize = await tauriWin.onResized(() => updateWindowStates());
|
|
164
|
+
unlistenFocus = await tauriWin.onFocusChanged(({ payload }: { payload: boolean }) => {
|
|
165
|
+
setIsFocused(payload);
|
|
166
|
+
});
|
|
167
|
+
} catch {
|
|
168
|
+
// Ignored in non-tauri contexts
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
onCleanup(() => {
|
|
173
|
+
window.removeEventListener("focus", handleWindowFocus);
|
|
174
|
+
window.removeEventListener("blur", handleWindowBlur);
|
|
175
|
+
document.removeEventListener("fullscreenchange", handleFullscreenChange);
|
|
176
|
+
if (unlistenResize) unlistenResize();
|
|
177
|
+
if (unlistenFocus) unlistenFocus();
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const minimize = async (): Promise<void> => {
|
|
183
|
+
const tauriWin = await getTauriWindow();
|
|
184
|
+
if (tauriWin?.minimize) {
|
|
185
|
+
await tauriWin.minimize();
|
|
186
|
+
setIsMinimized(true);
|
|
187
|
+
} else {
|
|
188
|
+
setIsMinimized((prev) => !prev);
|
|
189
|
+
if (!isMinimized()) {
|
|
190
|
+
setIsMaximized(false);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const maximize = async (): Promise<void> => {
|
|
196
|
+
const tauriWin = await getTauriWindow();
|
|
197
|
+
if (tauriWin?.maximize) {
|
|
198
|
+
await tauriWin.maximize();
|
|
199
|
+
setIsMaximized(true);
|
|
200
|
+
setIsMinimized(false);
|
|
201
|
+
} else {
|
|
202
|
+
setIsMaximized(true);
|
|
203
|
+
setIsMinimized(false);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const unmaximize = async (): Promise<void> => {
|
|
208
|
+
const tauriWin = await getTauriWindow();
|
|
209
|
+
if (tauriWin?.unmaximize) {
|
|
210
|
+
await tauriWin.unmaximize();
|
|
211
|
+
setIsMaximized(false);
|
|
212
|
+
} else {
|
|
213
|
+
setIsMaximized(false);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const toggleMaximize = async (): Promise<void> => {
|
|
218
|
+
const tauriWin = await getTauriWindow();
|
|
219
|
+
if (tauriWin?.toggleMaximize) {
|
|
220
|
+
await tauriWin.toggleMaximize();
|
|
221
|
+
setIsMaximized((prev) => !prev);
|
|
222
|
+
setIsMinimized(false);
|
|
223
|
+
} else {
|
|
224
|
+
setIsMaximized((prev) => !prev);
|
|
225
|
+
setIsMinimized(false);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const close = async (): Promise<void> => {
|
|
230
|
+
const tauriWin = await getTauriWindow();
|
|
231
|
+
if (tauriWin?.close) {
|
|
232
|
+
await tauriWin.close();
|
|
233
|
+
} else if (typeof window !== "undefined") {
|
|
234
|
+
window.close();
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const setFullscreen = async (fullscreen: boolean): Promise<void> => {
|
|
239
|
+
const tauriWin = await getTauriWindow();
|
|
240
|
+
if (tauriWin?.setFullscreen) {
|
|
241
|
+
await tauriWin.setFullscreen(fullscreen);
|
|
242
|
+
setIsFullscreen(fullscreen);
|
|
243
|
+
} else if (typeof document !== "undefined") {
|
|
244
|
+
if (fullscreen && document.documentElement.requestFullscreen) {
|
|
245
|
+
await document.documentElement.requestFullscreen();
|
|
246
|
+
} else if (!fullscreen && document.fullscreenElement && document.exitFullscreen) {
|
|
247
|
+
await document.exitFullscreen();
|
|
248
|
+
}
|
|
249
|
+
setIsFullscreen(fullscreen);
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const startDragging = async (e?: MouseEvent): Promise<void> => {
|
|
254
|
+
if (e && e.button !== 0) return; // Only primary mouse button starts drag
|
|
255
|
+
try {
|
|
256
|
+
const tauriWin = await getTauriWindow();
|
|
257
|
+
if (tauriWin?.startDragging) {
|
|
258
|
+
await tauriWin.startDragging();
|
|
259
|
+
}
|
|
260
|
+
} catch {
|
|
261
|
+
// Silently fall back if Tauri capability is not yet enabled
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
isTauri,
|
|
267
|
+
platform,
|
|
268
|
+
isMaximized,
|
|
269
|
+
isMinimized,
|
|
270
|
+
isFullscreen,
|
|
271
|
+
isFocused,
|
|
272
|
+
minimize,
|
|
273
|
+
maximize,
|
|
274
|
+
unmaximize,
|
|
275
|
+
toggleMaximize,
|
|
276
|
+
close,
|
|
277
|
+
setFullscreen,
|
|
278
|
+
startDragging,
|
|
279
|
+
};
|
|
280
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -37,4 +37,11 @@ export * from "./create-websocket";
|
|
|
37
37
|
export * from "./create-document-title";
|
|
38
38
|
export * from "./create-favicon";
|
|
39
39
|
export * from "./create-event-source";
|
|
40
|
-
export * from "./create-scroll-into-view";
|
|
40
|
+
export * from "./create-scroll-into-view";
|
|
41
|
+
export * from "./create-drop-zone";
|
|
42
|
+
export * from "./create-pagination";
|
|
43
|
+
export * from "./create-chat-scroll";
|
|
44
|
+
export * from "./create-tauri-window";
|
|
45
|
+
export * from "./create-global-shortcut";
|
|
46
|
+
export * from "./create-app-updater";
|
|
47
|
+
export * from "./create-document-tabs";
|