windowpp 0.1.1

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.
Files changed (88) hide show
  1. package/bin/windowpp.js +86 -0
  2. package/cmake/embed_assets.py +144 -0
  3. package/framework/CMakeLists.txt +176 -0
  4. package/framework/include/windowpp/windowpp.h +704 -0
  5. package/framework/src/AppData/API/AppData.ts +137 -0
  6. package/framework/src/AppData/appdata_bridge.h +138 -0
  7. package/framework/src/AppData/appdata_manager.cpp +126 -0
  8. package/framework/src/AppData/appdata_manager.h +3 -0
  9. package/framework/src/FileSystem/API/FileSystem.ts +389 -0
  10. package/framework/src/FileSystem/Linux/filesearch.cpp +148 -0
  11. package/framework/src/FileSystem/Linux/readfile.cpp +79 -0
  12. package/framework/src/FileSystem/Linux/savefile.cpp +333 -0
  13. package/framework/src/FileSystem/MacOS/filesearch.cpp +149 -0
  14. package/framework/src/FileSystem/MacOS/readfile.cpp +80 -0
  15. package/framework/src/FileSystem/MacOS/savefile.cpp +264 -0
  16. package/framework/src/FileSystem/Windows/filesearch.cpp +195 -0
  17. package/framework/src/FileSystem/Windows/readfile.cpp +122 -0
  18. package/framework/src/FileSystem/Windows/savefile.cpp +290 -0
  19. package/framework/src/FileSystem/file_index_service.cpp +262 -0
  20. package/framework/src/FileSystem/file_index_service.h +55 -0
  21. package/framework/src/FileSystem/filesystem_bridge.h +243 -0
  22. package/framework/src/FileSystem/filesystem_handler.h +93 -0
  23. package/framework/src/FileSystem/filesystem_json.h +241 -0
  24. package/framework/src/FileSystem/filesystem_search_service.cpp +414 -0
  25. package/framework/src/FileSystem/filesystem_search_service.h +94 -0
  26. package/framework/src/Input/API/Input.ts +161 -0
  27. package/framework/src/Input/Linux/linux_key_utils.h +135 -0
  28. package/framework/src/Input/MacOS/macos_key_utils.h +137 -0
  29. package/framework/src/Input/Windows/win32_key_utils.h +199 -0
  30. package/framework/src/Input/input_bridge.h +192 -0
  31. package/framework/src/Input/input_service.cpp +584 -0
  32. package/framework/src/Input/input_service.h +21 -0
  33. package/framework/src/application.cpp +29 -0
  34. package/framework/src/common/hit_test.cpp +40 -0
  35. package/framework/src/common/image_loader.cpp +24 -0
  36. package/framework/src/common/paths.cpp +75 -0
  37. package/framework/src/filedrop/filedrop.cpp +316 -0
  38. package/framework/src/filedrop/filedrop.css +421 -0
  39. package/framework/src/filedrop/filedrop.hpp +92 -0
  40. package/framework/src/filedrop/filedrop.ts +183 -0
  41. package/framework/src/platform/API/App.ts +156 -0
  42. package/framework/src/platform/API/Window.ts +249 -0
  43. package/framework/src/platform/linux/app_linux.cpp +256 -0
  44. package/framework/src/platform/linux/app_linux.h +64 -0
  45. package/framework/src/platform/linux/linux_helpers.cpp +26 -0
  46. package/framework/src/platform/linux/linux_helpers.h +19 -0
  47. package/framework/src/platform/linux/tray_linux.cpp +21 -0
  48. package/framework/src/platform/linux/tray_linux.h +26 -0
  49. package/framework/src/platform/linux/window_linux.cpp +256 -0
  50. package/framework/src/platform/linux/window_linux.h +70 -0
  51. package/framework/src/platform/macos/app_macos.h +59 -0
  52. package/framework/src/platform/macos/app_macos.mm +223 -0
  53. package/framework/src/platform/macos/macos_helpers.h +21 -0
  54. package/framework/src/platform/macos/tray_macos.h +22 -0
  55. package/framework/src/platform/macos/tray_macos.mm +53 -0
  56. package/framework/src/platform/macos/window_macos.h +74 -0
  57. package/framework/src/platform/macos/window_macos.mm +318 -0
  58. package/framework/src/platform/platform_bridge.h +514 -0
  59. package/framework/src/platform/platform_factory.cpp +33 -0
  60. package/framework/src/platform/platform_factory.h +19 -0
  61. package/framework/src/platform/win32/app_win32.cpp +572 -0
  62. package/framework/src/platform/win32/app_win32.h +83 -0
  63. package/framework/src/platform/win32/tray_win32.cpp +57 -0
  64. package/framework/src/platform/win32/tray_win32.h +30 -0
  65. package/framework/src/platform/win32/win32_helpers.h +61 -0
  66. package/framework/src/platform/win32/window_win32.cpp +267 -0
  67. package/framework/src/platform/win32/window_win32.h +79 -0
  68. package/framework/src/renderer/webgpu.h +128 -0
  69. package/framework/src/renderer/webview/include/WebView2.h +48014 -0
  70. package/framework/src/renderer/webview/include/WebView2EnvironmentOptions.h +342 -0
  71. package/framework/src/renderer/webview/webview.h +13 -0
  72. package/framework/src/renderer/webview/webview_linux.cpp +392 -0
  73. package/framework/src/renderer/webview/webview_macos.mm +388 -0
  74. package/framework/src/renderer/webview/webview_win32.cpp +688 -0
  75. package/framework/src/renderer/webview/x64/WebView2Loader.dll +0 -0
  76. package/framework/src/renderer/webview/x64/WebView2Loader.lib +0 -0
  77. package/framework/src/renderer/webview/x64/WebView2LoaderStatic.lib +0 -0
  78. package/lib/build.js +112 -0
  79. package/lib/create.js +283 -0
  80. package/lib/dev.js +155 -0
  81. package/package.json +24 -0
  82. package/scripts/publish.js +67 -0
  83. package/scripts/sync-framework.js +73 -0
  84. package/templates/solid/CMakeLists.txt +56 -0
  85. package/templates/solid/frontend/package.json +22 -0
  86. package/templates/solid/frontend/vite.config.ts +25 -0
  87. package/templates/solid/main.cpp +72 -0
  88. package/templates/solid/package.json +12 -0
@@ -0,0 +1,389 @@
1
+ // ============================================================================
2
+ // FileSystem.ts — WindowPP FileSystem API
3
+ //
4
+ // Frontend (TypeScript) side of the cross-platform file-system bridge.
5
+ //
6
+ // All calls are async and resolve/reject against the same __response__
7
+ // callback used by every other WindowPP bridge module.
8
+ //
9
+ // Methods surfaced:
10
+ // fs.readFile(path) → string (UTF-8 text)
11
+ // fs.readBinaryFile(path) → Uint8Array
12
+ // fs.writeFile(path, text) → void
13
+ // fs.writeBinaryFile(path, data) → void
14
+ // fs.appendFile(path, text) → void
15
+ // fs.copyFile(src, dest) → void
16
+ // fs.moveFile(src, dest) → void
17
+ // fs.deleteFile(path) → void
18
+ // fs.openPath(path) → void
19
+ // fs.revealPath(path) → void
20
+ // fs.listApplications() → ApplicationEntry[]
21
+ // fs.launchApplication(target) → void
22
+ // fs.exists(path) → boolean
23
+ // fs.stat(path) → FileStat
24
+ // fs.readDir(path) → DirEntry[]
25
+ // fs.createDir(path, recursive?) → void
26
+ // fs.removeDir(path, recursive?) → void
27
+ // fs.searchFiles(dir, pattern, opts?) → string[]
28
+ // ============================================================================
29
+
30
+ // ─── Types ───────────────────────────────────────────────────────────────────
31
+
32
+ export interface FileStat {
33
+ /** Full path */
34
+ path: string;
35
+ /** File name with extension */
36
+ name: string;
37
+ /** Size in bytes */
38
+ size: number;
39
+ /** True when this entry is a directory */
40
+ isDir: boolean;
41
+ /** True when this entry is a symlink */
42
+ isSymlink: boolean;
43
+ /** Last-modified timestamp (ms since Unix epoch) */
44
+ modifiedMs: number;
45
+ /** Created-at timestamp (ms since Unix epoch); −1 if unsupported */
46
+ createdMs: number;
47
+ /** POSIX permission bits, e.g. 0o644; −1 on Windows */
48
+ mode: number;
49
+ }
50
+
51
+ export interface DirEntry {
52
+ name: string;
53
+ path: string;
54
+ isDir: boolean;
55
+ isSymlink: boolean;
56
+ }
57
+
58
+ export interface ApplicationEntry {
59
+ id: string;
60
+ name: string;
61
+ path: string;
62
+ launchTarget: string;
63
+ }
64
+
65
+ export interface SearchOptions {
66
+ /** Match filenames with glob patterns, e.g. "*.ts". Default: "*" */
67
+ pattern?: string;
68
+ /** Recurse into sub-directories. Default: false */
69
+ recursive?: boolean;
70
+ /** Include hidden files/dirs (name starts with "."). Default: false */
71
+ includeHidden?: boolean;
72
+ /** Include matching directories in results. Default: false */
73
+ includeDirectories?: boolean;
74
+ /** Maximum results to return. 0 = unlimited. Default: 0 */
75
+ maxResults?: number;
76
+ }
77
+
78
+ export interface SearchStreamSummary {
79
+ scannedEntries: number;
80
+ matchedEntries: number;
81
+ cancelled: boolean;
82
+ truncated: boolean;
83
+ error: string;
84
+ }
85
+
86
+ export interface SearchStreamCallbacks {
87
+ onBatch?: (results: string[]) => void;
88
+ onDone?: (summary: SearchStreamSummary) => void;
89
+ }
90
+
91
+ export interface SearchStreamHandle {
92
+ id: string;
93
+ cancel: () => Promise<void>;
94
+ }
95
+
96
+ // ─── Internal bridge helpers ─────────────────────────────────────────────────
97
+
98
+ interface PendingCall {
99
+ resolve: (value: any) => void;
100
+ reject: (reason: Error) => void;
101
+ }
102
+
103
+ let _callId = 0;
104
+ let _searchStreamId = 0;
105
+ const _pending = new Map<string, PendingCall>();
106
+
107
+ function _getGlobal(): any {
108
+ if (typeof window !== "undefined") return window;
109
+ if (typeof globalThis !== "undefined") return globalThis;
110
+ return {};
111
+ }
112
+
113
+ const _g = _getGlobal();
114
+ const _streams = new Map<string, SearchStreamCallbacks>();
115
+
116
+ // Hook into the shared __response__ dispatcher (same one used by filedrop, etc.)
117
+ const _prevResponse = _g.__response__;
118
+ _g.__response__ = function (id: string, result: any, error?: string) {
119
+ const p = _pending.get(id);
120
+ if (p) {
121
+ _pending.delete(id);
122
+ if (error) {
123
+ p.reject(new Error(error));
124
+ } else {
125
+ p.resolve(result);
126
+ }
127
+ return;
128
+ }
129
+ // Fall through to any previously registered handler.
130
+ if (_prevResponse) _prevResponse(id, result, error);
131
+ };
132
+
133
+ function _invoke<T>(method: string, params: unknown[] = []): Promise<T> {
134
+ return new Promise<T>((resolve, reject) => {
135
+ const id = String(++_callId);
136
+ _pending.set(id, { resolve, reject });
137
+ const payload = JSON.stringify({ id, method, params });
138
+
139
+ if (_g.__native_invoke__) {
140
+ _g.__native_invoke__(payload);
141
+ } else if (_g.chrome?.webview?.postMessage) {
142
+ _g.chrome.webview.postMessage(payload);
143
+ } else {
144
+ _pending.delete(id);
145
+ console.warn("[WindowPP/fs] Native bridge not available");
146
+ reject(new Error("Native bridge not available"));
147
+ }
148
+ });
149
+ }
150
+
151
+ _g.__wpp_fs_stream__ = function (searchId: string, eventName: string, payload: any) {
152
+ const stream = _streams.get(searchId);
153
+ if (!stream) return;
154
+
155
+ if (eventName === 'batch') {
156
+ stream.onBatch?.(Array.isArray(payload) ? payload : []);
157
+ return;
158
+ }
159
+
160
+ if (eventName === 'done') {
161
+ _streams.delete(searchId);
162
+ stream.onDone?.(payload as SearchStreamSummary);
163
+ }
164
+ };
165
+
166
+ // ─── Public API ──────────────────────────────────────────────────────────────
167
+
168
+ /**
169
+ * Read a file as UTF-8 text.
170
+ * @param path Absolute path to the file.
171
+ */
172
+ export function readFile(path: string): Promise<string> {
173
+ return _invoke<string>("fs:readFile", [path]);
174
+ }
175
+
176
+ /**
177
+ * Read a file as raw bytes.
178
+ * The native side returns a Base64 string which is decoded here.
179
+ * @param path Absolute path to the file.
180
+ */
181
+ export async function readBinaryFile(path: string): Promise<Uint8Array> {
182
+ const b64 = await _invoke<string>("fs:readBinaryFile", [path]);
183
+ return _b64ToBytes(b64);
184
+ }
185
+
186
+ /**
187
+ * Write UTF-8 text to a file, creating or overwriting it.
188
+ * @param path Absolute destination path.
189
+ * @param content Text to write.
190
+ */
191
+ export function writeFile(path: string, content: string): Promise<void> {
192
+ return _invoke<void>("fs:writeFile", [path, content]);
193
+ }
194
+
195
+ /**
196
+ * Write raw bytes to a file, creating or overwriting it.
197
+ * @param path Absolute destination path.
198
+ * @param data Bytes to write.
199
+ */
200
+ export function writeBinaryFile(path: string, data: Uint8Array): Promise<void> {
201
+ return _invoke<void>("fs:writeBinaryFile", [path, _bytesToB64(data)]);
202
+ }
203
+
204
+ /**
205
+ * Append UTF-8 text to a file (creates the file if it does not exist).
206
+ * @param path Absolute destination path.
207
+ * @param content Text to append.
208
+ */
209
+ export function appendFile(path: string, content: string): Promise<void> {
210
+ return _invoke<void>("fs:appendFile", [path, content]);
211
+ }
212
+
213
+ /**
214
+ * Copy a file.
215
+ * @param src Source path.
216
+ * @param dest Destination path. Overwrites if it already exists.
217
+ */
218
+ export function copyFile(src: string, dest: string): Promise<void> {
219
+ return _invoke<void>("fs:copyFile", [src, dest]);
220
+ }
221
+
222
+ /**
223
+ * Move (rename) a file or directory.
224
+ * @param src Source path.
225
+ * @param dest Destination path.
226
+ */
227
+ export function moveFile(src: string, dest: string): Promise<void> {
228
+ return _invoke<void>("fs:moveFile", [src, dest]);
229
+ }
230
+
231
+ /**
232
+ * Delete a file.
233
+ * @param path Absolute path to the file.
234
+ */
235
+ export function deleteFile(path: string): Promise<void> {
236
+ return _invoke<void>("fs:deleteFile", [path]);
237
+ }
238
+
239
+ /**
240
+ * Open a file with the default app, or open a directory in the OS file manager.
241
+ * @param path Absolute path to a file or directory.
242
+ */
243
+ export function openPath(path: string): Promise<void> {
244
+ return _invoke<void>("fs:openPath", [path]);
245
+ }
246
+
247
+ /**
248
+ * Reveal a file or directory in the OS file manager.
249
+ * @param path Absolute path to a file or directory.
250
+ */
251
+ export function revealPath(path: string): Promise<void> {
252
+ return _invoke<void>("fs:revealPath", [path]);
253
+ }
254
+
255
+ /**
256
+ * List installed applications discoverable on the current OS.
257
+ */
258
+ export function listApplications(): Promise<ApplicationEntry[]> {
259
+ return _invoke<ApplicationEntry[]>("fs:listApplications");
260
+ }
261
+
262
+ /**
263
+ * Launch an installed application using its platform-specific target.
264
+ */
265
+ export function launchApplication(target: string): Promise<void> {
266
+ return _invoke<void>("fs:launchApplication", [target]);
267
+ }
268
+
269
+ /**
270
+ * Check whether a path exists (file or directory).
271
+ * @param path Absolute path to test.
272
+ */
273
+ export function exists(path: string): Promise<boolean> {
274
+ return _invoke<boolean>("fs:exists", [path]);
275
+ }
276
+
277
+ /**
278
+ * Retrieve metadata for a file or directory.
279
+ * @param path Absolute path.
280
+ */
281
+ export function stat(path: string): Promise<FileStat> {
282
+ return _invoke<FileStat>("fs:stat", [path]);
283
+ }
284
+
285
+ /**
286
+ * List the entries in a directory (non-recursive).
287
+ * @param path Absolute path to the directory.
288
+ */
289
+ export function readDir(path: string): Promise<DirEntry[]> {
290
+ return _invoke<DirEntry[]>("fs:readDir", [path]);
291
+ }
292
+
293
+ /**
294
+ * Create a directory.
295
+ * @param path Absolute path of the directory to create.
296
+ * @param recursive Create parent directories as needed. Default: false.
297
+ */
298
+ export function createDir(path: string, recursive = false): Promise<void> {
299
+ return _invoke<void>("fs:createDir", [path, recursive]);
300
+ }
301
+
302
+ /**
303
+ * Remove a directory.
304
+ * @param path Absolute path of the directory to remove.
305
+ * @param recursive Remove contents recursively. Default: false.
306
+ */
307
+ export function removeDir(path: string, recursive = false): Promise<void> {
308
+ return _invoke<void>("fs:removeDir", [path, recursive]);
309
+ }
310
+
311
+ /**
312
+ * Search for files under a directory.
313
+ * @param dir Root directory to search in.
314
+ * @param options Search options (pattern, recursive, etc.).
315
+ */
316
+ export function searchFiles(
317
+ dir: string,
318
+ options: SearchOptions = {}
319
+ ): Promise<string[]> {
320
+ return _invoke<string[]>("fs:searchFiles", [dir, options]);
321
+ }
322
+
323
+ export function listRoots(): Promise<string[]> {
324
+ return _invoke<string[]>("fs:listRoots");
325
+ }
326
+
327
+ export async function searchFilesStream(
328
+ roots: string | string[],
329
+ options: SearchOptions = {},
330
+ callbacks: SearchStreamCallbacks = {}
331
+ ): Promise<SearchStreamHandle> {
332
+ const normalizedRoots = Array.isArray(roots) ? roots : [roots];
333
+ const id = `fs-stream-${++_searchStreamId}`;
334
+ _streams.set(id, callbacks);
335
+ await _invoke<string>("fs:searchFilesStreamStart", [normalizedRoots, options, id]);
336
+ return {
337
+ id,
338
+ cancel: async () => {
339
+ _streams.delete(id);
340
+ await _invoke<void>("fs:searchFilesStreamCancel", [id]);
341
+ },
342
+ };
343
+ }
344
+
345
+ // ─── Default export ───────────────────────────────────────────────────────────
346
+
347
+ const fs = {
348
+ readFile,
349
+ readBinaryFile,
350
+ writeFile,
351
+ writeBinaryFile,
352
+ appendFile,
353
+ copyFile,
354
+ moveFile,
355
+ deleteFile,
356
+ openPath,
357
+ revealPath,
358
+ listApplications,
359
+ launchApplication,
360
+ exists,
361
+ stat,
362
+ readDir,
363
+ createDir,
364
+ removeDir,
365
+ listRoots,
366
+ searchFiles,
367
+ searchFilesStream,
368
+ };
369
+
370
+ export default fs;
371
+
372
+ // ─── Base64 helpers (no dependencies) ────────────────────────────────────────
373
+
374
+ function _bytesToB64(bytes: Uint8Array): string {
375
+ let binary = "";
376
+ for (let i = 0; i < bytes.byteLength; i++) {
377
+ binary += String.fromCharCode(bytes[i]);
378
+ }
379
+ return btoa(binary);
380
+ }
381
+
382
+ function _b64ToBytes(b64: string): Uint8Array {
383
+ const binary = atob(b64);
384
+ const bytes = new Uint8Array(binary.length);
385
+ for (let i = 0; i < binary.length; i++) {
386
+ bytes[i] = binary.charCodeAt(i);
387
+ }
388
+ return bytes;
389
+ }
@@ -0,0 +1,148 @@
1
+ // ============================================================================
2
+ // FileSystem/Linux/filesearch.cpp
3
+ // Handles: fs:exists, fs:stat, fs:readDir, fs:searchFiles
4
+ // ============================================================================
5
+
6
+ #include <dirent.h>
7
+ #include <fnmatch.h>
8
+ #include <sys/stat.h>
9
+ #include <unistd.h>
10
+
11
+ #include <cerrno>
12
+ #include <cstring>
13
+ #include <stdexcept>
14
+ #include <string>
15
+ #include <vector>
16
+
17
+ #include "../filesystem_handler.h"
18
+
19
+ namespace wpp::fs {
20
+
21
+ // ─── fs:exists ───────────────────────────────────────────────────────────────
22
+
23
+ bool handleExists(const std::string& path) {
24
+ struct stat st{};
25
+ return stat(path.c_str(), &st) == 0;
26
+ }
27
+
28
+ // ─── fs:stat ─────────────────────────────────────────────────────────────────
29
+
30
+ FileStat handleStat(const std::string& path) {
31
+ struct stat st{};
32
+ if (::stat(path.c_str(), &st) != 0)
33
+ throw std::runtime_error("stat failed for '" + path + "': " +
34
+ std::string(strerror(errno)));
35
+
36
+ std::string name = path;
37
+ auto slash = path.rfind('/');
38
+ if (slash != std::string::npos) name = path.substr(slash + 1);
39
+
40
+ bool isDir = S_ISDIR(st.st_mode);
41
+ bool isLink = S_ISLNK(st.st_mode);
42
+
43
+ // Linux: use st_mtim / st_ctim (POSIX.1-2008)
44
+ int64_t modifiedMs = static_cast<int64_t>(st.st_mtim.tv_sec) * 1000 +
45
+ st.st_mtim.tv_nsec / 1000000;
46
+ // Linux does not expose birthtime in struct stat; return -1
47
+ int64_t createdMs = -1;
48
+
49
+ return FileStat{
50
+ path,
51
+ name,
52
+ static_cast<int64_t>(st.st_size),
53
+ isDir,
54
+ isLink,
55
+ modifiedMs,
56
+ createdMs,
57
+ static_cast<int>(st.st_mode & 0777)
58
+ };
59
+ }
60
+
61
+ // ─── fs:readDir ──────────────────────────────────────────────────────────────
62
+
63
+ std::vector<DirEntry> handleReadDir(const std::string& path) {
64
+ DIR* dir = opendir(path.c_str());
65
+ if (!dir)
66
+ throw std::runtime_error("readDir failed for '" + path + "': " +
67
+ std::string(strerror(errno)));
68
+
69
+ std::vector<DirEntry> entries;
70
+ struct dirent* ent;
71
+ while ((ent = readdir(dir)) != nullptr) {
72
+ std::string name = ent->d_name;
73
+ if (name == "." || name == "..") continue;
74
+ std::string full = path + "/" + name;
75
+ bool isDir = ent->d_type == DT_DIR;
76
+ bool isLink = ent->d_type == DT_LNK;
77
+ if (ent->d_type == DT_UNKNOWN) {
78
+ struct stat st{};
79
+ ::stat(full.c_str(), &st);
80
+ isDir = S_ISDIR(st.st_mode);
81
+ isLink = S_ISLNK(st.st_mode);
82
+ }
83
+ entries.push_back(DirEntry{name, full, isDir, isLink});
84
+ }
85
+ closedir(dir);
86
+ return entries;
87
+ }
88
+
89
+ std::vector<std::string> handleListRoots() {
90
+ return {"/"};
91
+ }
92
+
93
+ // ─── fs:searchFiles ──────────────────────────────────────────────────────────
94
+
95
+ static void searchRecursive(const std::string& dir,
96
+ const std::string& pattern,
97
+ bool recursive,
98
+ bool includeHidden,
99
+ bool includeDirectories,
100
+ size_t maxResults,
101
+ std::vector<std::string>& results) {
102
+ if (maxResults > 0 && results.size() >= maxResults) return;
103
+
104
+ DIR* d = opendir(dir.c_str());
105
+ if (!d) return;
106
+
107
+ struct dirent* ent;
108
+ while ((ent = readdir(d)) != nullptr) {
109
+ if (maxResults > 0 && results.size() >= maxResults) break;
110
+ std::string name = ent->d_name;
111
+ if (name == "." || name == "..") continue;
112
+ bool hidden = (!name.empty() && name[0] == '.');
113
+ if (hidden && !includeHidden) continue;
114
+
115
+ std::string full = dir + "/" + name;
116
+ bool isDir = ent->d_type == DT_DIR;
117
+ if (ent->d_type == DT_UNKNOWN) {
118
+ struct stat st{};
119
+ ::stat(full.c_str(), &st);
120
+ isDir = S_ISDIR(st.st_mode);
121
+ }
122
+
123
+ if (isDir) {
124
+ if (includeDirectories && fnmatch(pattern.c_str(), name.c_str(), 0) == 0)
125
+ results.push_back(full);
126
+ if (recursive) {
127
+ searchRecursive(full, pattern, recursive,
128
+ includeHidden, includeDirectories, maxResults, results);
129
+ }
130
+ } else {
131
+ if (fnmatch(pattern.c_str(), name.c_str(), 0) == 0)
132
+ results.push_back(full);
133
+ }
134
+ }
135
+ closedir(d);
136
+ }
137
+
138
+ std::vector<std::string> handleSearchFiles(const std::string& dir,
139
+ const SearchOptions& opts) {
140
+ std::string pat = opts.pattern.empty() ? "*" : opts.pattern;
141
+ std::vector<std::string> results;
142
+ searchRecursive(dir, pat, opts.recursive, opts.includeHidden,
143
+ opts.includeDirectories,
144
+ opts.maxResults, results);
145
+ return results;
146
+ }
147
+
148
+ } // namespace wpp::fs
@@ -0,0 +1,79 @@
1
+ // ============================================================================
2
+ // FileSystem/Linux/readfile.cpp
3
+ // Handles: fs:readFile, fs:readBinaryFile
4
+ // ============================================================================
5
+
6
+ #include <fcntl.h>
7
+ #include <sys/stat.h>
8
+ #include <unistd.h>
9
+
10
+ #include <cerrno>
11
+ #include <cstring>
12
+ #include <stdexcept>
13
+ #include <string>
14
+ #include <vector>
15
+
16
+ #include "../filesystem_handler.h"
17
+
18
+ namespace wpp::fs {
19
+
20
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
21
+
22
+ static const std::string kB64Chars =
23
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
24
+
25
+ static std::string base64Encode(const std::vector<uint8_t>& bytes) {
26
+ std::string out;
27
+ out.reserve(((bytes.size() + 2) / 3) * 4);
28
+ size_t i = 0;
29
+ for (; i + 2 < bytes.size(); i += 3) {
30
+ uint32_t v = (bytes[i] << 16) | (bytes[i+1] << 8) | bytes[i+2];
31
+ out += kB64Chars[(v >> 18) & 0x3F];
32
+ out += kB64Chars[(v >> 12) & 0x3F];
33
+ out += kB64Chars[(v >> 6) & 0x3F];
34
+ out += kB64Chars[(v ) & 0x3F];
35
+ }
36
+ if (i < bytes.size()) {
37
+ uint32_t v = bytes[i] << 16;
38
+ if (i + 1 < bytes.size()) v |= bytes[i+1] << 8;
39
+ out += kB64Chars[(v >> 18) & 0x3F];
40
+ out += kB64Chars[(v >> 12) & 0x3F];
41
+ out += (i + 1 < bytes.size()) ? kB64Chars[(v >> 6) & 0x3F] : '=';
42
+ out += '=';
43
+ }
44
+ return out;
45
+ }
46
+
47
+ static std::vector<uint8_t> readAllBytes(const std::string& path) {
48
+ int fd = open(path.c_str(), O_RDONLY);
49
+ if (fd < 0)
50
+ throw std::runtime_error("Cannot open '" + path + "': " +
51
+ std::string(strerror(errno)));
52
+ struct stat st{};
53
+ if (fstat(fd, &st) < 0) {
54
+ close(fd);
55
+ throw std::runtime_error("stat failed for '" + path + "'");
56
+ }
57
+ std::vector<uint8_t> buf(static_cast<size_t>(st.st_size));
58
+ ssize_t n = read(fd, buf.data(), buf.size());
59
+ close(fd);
60
+ if (n < 0)
61
+ throw std::runtime_error("Read failed for '" + path + "'");
62
+ buf.resize(static_cast<size_t>(n));
63
+ return buf;
64
+ }
65
+
66
+ // ─── fs:readFile ─────────────────────────────────────────────────────────────
67
+
68
+ std::string handleReadFile(const std::string& path) {
69
+ auto bytes = readAllBytes(path);
70
+ return std::string(bytes.begin(), bytes.end());
71
+ }
72
+
73
+ // ─── fs:readBinaryFile ───────────────────────────────────────────────────────
74
+
75
+ std::string handleReadBinaryFile(const std::string& path) {
76
+ return base64Encode(readAllBytes(path));
77
+ }
78
+
79
+ } // namespace wpp::fs