sparkbun 0.1.0
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/bin/sparkbun.cjs +18 -0
- package/dist-linux-arm64/bsdiff +0 -0
- package/dist-linux-arm64/bspatch +0 -0
- package/dist-linux-arm64/libElectrobunCore.so +0 -0
- package/dist-linux-arm64/libNativeWrapper.so +0 -0
- package/dist-linux-arm64/libasar.so +0 -0
- package/dist-linux-x64/bsdiff +0 -0
- package/dist-linux-x64/bspatch +0 -0
- package/dist-linux-x64/libElectrobunCore.so +0 -0
- package/dist-linux-x64/libNativeWrapper.so +0 -0
- package/dist-linux-x64/libasar.so +0 -0
- package/dist-macos-arm64/bsdiff +0 -0
- package/dist-macos-arm64/bspatch +0 -0
- package/dist-macos-arm64/libElectrobunCore.dylib +0 -0
- package/dist-macos-arm64/libNativeWrapper.dylib +0 -0
- package/dist-macos-arm64/libasar.dylib +0 -0
- package/dist-macos-arm64/libwebgpu_dawn.dylib +0 -0
- package/dist-macos-arm64/preload-full.js +885 -0
- package/dist-macos-arm64/preload-sandboxed.js +111 -0
- package/dist-macos-arm64/process_helper +0 -0
- package/dist-win-x64/ElectrobunCore.dll +0 -0
- package/dist-win-x64/WebView2Loader.dll +0 -0
- package/dist-win-x64/bsdiff.exe +0 -0
- package/dist-win-x64/bspatch.exe +0 -0
- package/dist-win-x64/libNativeWrapper.dll +0 -0
- package/dist-win-x64/zig-asar/arm64/libasar.dll +0 -0
- package/dist-win-x64/zig-asar/x64/libasar.dll +0 -0
- package/package.json +47 -0
- package/scripts/build-and-upload-artifacts.js +207 -0
- package/scripts/gen-webgpu-ffi.mjs +162 -0
- package/scripts/install-windows-deps.ps1 +80 -0
- package/scripts/package-release.js +237 -0
- package/scripts/push-version.js +84 -0
- package/scripts/update-bun-version.ts +122 -0
- package/scripts/update-cef-version.ts +145 -0
- package/src/browser/builtinrpcSchema.ts +19 -0
- package/src/browser/global.d.ts +36 -0
- package/src/browser/index.ts +234 -0
- package/src/browser/webviewtag.ts +88 -0
- package/src/browser/wgputag.ts +48 -0
- package/src/bun/SparkBunConfig.ts +497 -0
- package/src/bun/__tests__/ffi-contract.test.ts +105 -0
- package/src/bun/core/ApplicationMenu.ts +70 -0
- package/src/bun/core/BrowserView.ts +416 -0
- package/src/bun/core/BrowserWindow.ts +396 -0
- package/src/bun/core/BuildConfig.ts +71 -0
- package/src/bun/core/ContextMenu.ts +75 -0
- package/src/bun/core/GpuWindow.ts +289 -0
- package/src/bun/core/Paths.ts +5 -0
- package/src/bun/core/Socket.ts +22 -0
- package/src/bun/core/Tray.ts +197 -0
- package/src/bun/core/Updater.ts +1131 -0
- package/src/bun/core/Utils.ts +487 -0
- package/src/bun/core/WGPUView.ts +167 -0
- package/src/bun/core/menuRoles.ts +181 -0
- package/src/bun/events/ApplicationEvents.ts +22 -0
- package/src/bun/events/event.ts +27 -0
- package/src/bun/events/eventEmitter.ts +45 -0
- package/src/bun/events/trayEvents.ts +11 -0
- package/src/bun/events/webviewEvents.ts +39 -0
- package/src/bun/events/windowEvents.ts +23 -0
- package/src/bun/index.ts +120 -0
- package/src/bun/preload/.generated/compiled.ts +2 -0
- package/src/bun/preload/build.ts +65 -0
- package/src/bun/preload/dragRegions.ts +41 -0
- package/src/bun/preload/encryption.ts +86 -0
- package/src/bun/preload/events.ts +171 -0
- package/src/bun/preload/globals.d.ts +45 -0
- package/src/bun/preload/index-sandboxed.ts +28 -0
- package/src/bun/preload/index.ts +77 -0
- package/src/bun/preload/internalRpc.ts +80 -0
- package/src/bun/preload/overlaySync.ts +107 -0
- package/src/bun/preload/webviewTag.ts +451 -0
- package/src/bun/preload/wgpuTag.ts +246 -0
- package/src/bun/proc/linux.md +43 -0
- package/src/bun/proc/native.ts +3253 -0
- package/src/bun/webGPU.ts +346 -0
- package/src/bun/webgpuAdapter.ts +3011 -0
- package/src/cli/bun.lockb +0 -0
- package/src/cli/index.ts +4653 -0
- package/src/cli/package-lock.json +81 -0
- package/src/cli/package.json +11 -0
- package/src/cli/templates/embedded.ts +2 -0
- package/src/core/build.zig +16 -0
- package/src/core/main.zig +3378 -0
- package/src/extractor/build.zig +22 -0
- package/src/installer/installer-template.ts +216 -0
- package/src/launcher/main.ts +221 -0
- package/src/native/build/libNativeWrapper.so +0 -0
- package/src/native/linux/build/nativeWrapper.o +0 -0
- package/src/native/linux/cef_loader.cpp +110 -0
- package/src/native/linux/cef_loader.h +28 -0
- package/src/native/linux/cef_process_helper_linux.cpp +160 -0
- package/src/native/linux/nativeWrapper.cpp +11768 -0
- package/src/native/macos/cef_process_helper_mac.cc +160 -0
- package/src/native/macos/nativeWrapper.mm +9172 -0
- package/src/native/shared/accelerator_parser.h +72 -0
- package/src/native/shared/app_paths.h +110 -0
- package/src/native/shared/asar.h +35 -0
- package/src/native/shared/cache_migration.h +244 -0
- package/src/native/shared/callbacks.h +57 -0
- package/src/native/shared/cef_response_filter.h +189 -0
- package/src/native/shared/chromium_flags.h +181 -0
- package/src/native/shared/config.h +66 -0
- package/src/native/shared/download_event.h +197 -0
- package/src/native/shared/ffi_helpers.h +139 -0
- package/src/native/shared/glob_match.h +59 -0
- package/src/native/shared/json_menu_parser.h +223 -0
- package/src/native/shared/mime_types.h +101 -0
- package/src/native/shared/navigation_rules.h +98 -0
- package/src/native/shared/partition_context.h +137 -0
- package/src/native/shared/pending_resize_queue.h +45 -0
- package/src/native/shared/permissions.h +118 -0
- package/src/native/shared/permissions_cef.h +74 -0
- package/src/native/shared/preload_script.h +71 -0
- package/src/native/shared/shutdown_guard.h +134 -0
- package/src/native/shared/thread_safe_map.h +138 -0
- package/src/native/shared/webview_storage.h +91 -0
- package/src/native/win/cef_process_helper_win.cpp +143 -0
- package/src/native/win/dcomp_compositor.h +352 -0
- package/src/native/win/nativeWrapper.cpp +12434 -0
- package/src/npmbin/index.js +34 -0
- package/src/shared/bun-version.ts +3 -0
- package/src/shared/cef-version.ts +5 -0
- package/src/shared/naming.test.ts +327 -0
- package/src/shared/naming.ts +188 -0
- package/src/shared/platform.ts +48 -0
- package/src/shared/rpc.ts +541 -0
- package/src/shared/sparkbun-version.ts +2 -0
- package/src/types/three.d.ts +1 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// ffi_helpers.h - Cross-platform FFI helper utilities
|
|
2
|
+
// Helpers for passing strings and data across FFI boundaries
|
|
3
|
+
// Used across Windows, macOS, and Linux
|
|
4
|
+
//
|
|
5
|
+
// This is a header-only implementation to avoid build complexity.
|
|
6
|
+
|
|
7
|
+
#ifndef ELECTROBUN_FFI_HELPERS_H
|
|
8
|
+
#define ELECTROBUN_FFI_HELPERS_H
|
|
9
|
+
|
|
10
|
+
#include <string>
|
|
11
|
+
#include <cstring>
|
|
12
|
+
#include <cstdlib>
|
|
13
|
+
|
|
14
|
+
namespace electrobun {
|
|
15
|
+
|
|
16
|
+
// Create a copy of a string for FFI callbacks
|
|
17
|
+
// The returned string is allocated with malloc/strdup and must be freed by the caller
|
|
18
|
+
// Returns nullptr if the input is empty
|
|
19
|
+
inline char* createFFIString(const std::string& str) {
|
|
20
|
+
if (str.empty()) {
|
|
21
|
+
return nullptr;
|
|
22
|
+
}
|
|
23
|
+
return strdup(str.c_str());
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Create a copy of a C string for FFI callbacks
|
|
27
|
+
// The returned string is allocated with malloc/strdup and must be freed by the caller
|
|
28
|
+
// Returns nullptr if the input is null or empty
|
|
29
|
+
inline char* createFFIString(const char* str) {
|
|
30
|
+
if (!str || str[0] == '\0') {
|
|
31
|
+
return nullptr;
|
|
32
|
+
}
|
|
33
|
+
return strdup(str);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Free an FFI string that was created with createFFIString
|
|
37
|
+
inline void freeFFIString(char* str) {
|
|
38
|
+
if (str) {
|
|
39
|
+
free(str);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Free an FFI string (const version for convenience)
|
|
44
|
+
inline void freeFFIString(const char* str) {
|
|
45
|
+
if (str) {
|
|
46
|
+
free(const_cast<char*>(str));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// RAII wrapper for FFI strings
|
|
51
|
+
// Automatically frees the string when it goes out of scope
|
|
52
|
+
class FFIString {
|
|
53
|
+
public:
|
|
54
|
+
FFIString() : str_(nullptr) {}
|
|
55
|
+
|
|
56
|
+
explicit FFIString(const std::string& str)
|
|
57
|
+
: str_(createFFIString(str)) {}
|
|
58
|
+
|
|
59
|
+
explicit FFIString(const char* str)
|
|
60
|
+
: str_(createFFIString(str)) {}
|
|
61
|
+
|
|
62
|
+
~FFIString() {
|
|
63
|
+
freeFFIString(str_);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Get the raw pointer (for passing to FFI)
|
|
67
|
+
const char* get() const { return str_; }
|
|
68
|
+
|
|
69
|
+
// Release ownership (caller takes responsibility for freeing)
|
|
70
|
+
char* release() {
|
|
71
|
+
char* temp = str_;
|
|
72
|
+
str_ = nullptr;
|
|
73
|
+
return temp;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check if string is valid
|
|
77
|
+
bool valid() const { return str_ != nullptr; }
|
|
78
|
+
explicit operator bool() const { return valid(); }
|
|
79
|
+
|
|
80
|
+
// Non-copyable
|
|
81
|
+
FFIString(const FFIString&) = delete;
|
|
82
|
+
FFIString& operator=(const FFIString&) = delete;
|
|
83
|
+
|
|
84
|
+
// Movable
|
|
85
|
+
FFIString(FFIString&& other) noexcept : str_(other.str_) {
|
|
86
|
+
other.str_ = nullptr;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
FFIString& operator=(FFIString&& other) noexcept {
|
|
90
|
+
if (this != &other) {
|
|
91
|
+
freeFFIString(str_);
|
|
92
|
+
str_ = other.str_;
|
|
93
|
+
other.str_ = nullptr;
|
|
94
|
+
}
|
|
95
|
+
return *this;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private:
|
|
99
|
+
char* str_;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Helper to create event data strings for callbacks
|
|
103
|
+
// Format: "key1=value1&key2=value2&..."
|
|
104
|
+
class FFIEventBuilder {
|
|
105
|
+
public:
|
|
106
|
+
FFIEventBuilder& add(const std::string& key, const std::string& value) {
|
|
107
|
+
if (!data_.empty()) {
|
|
108
|
+
data_ += "&";
|
|
109
|
+
}
|
|
110
|
+
data_ += key + "=" + value;
|
|
111
|
+
return *this;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
FFIEventBuilder& add(const std::string& key, int value) {
|
|
115
|
+
return add(key, std::to_string(value));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
FFIEventBuilder& add(const std::string& key, double value) {
|
|
119
|
+
return add(key, std::to_string(value));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
FFIEventBuilder& add(const std::string& key, bool value) {
|
|
123
|
+
return add(key, value ? "true" : "false");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
std::string str() const { return data_; }
|
|
127
|
+
|
|
128
|
+
// Create FFI string (caller must free)
|
|
129
|
+
char* createFFIString() const {
|
|
130
|
+
return electrobun::createFFIString(data_);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private:
|
|
134
|
+
std::string data_;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
} // namespace electrobun
|
|
138
|
+
|
|
139
|
+
#endif // ELECTROBUN_FFI_HELPERS_H
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// glob_match.h - Cross-platform glob pattern matching utility
|
|
2
|
+
// Used for navigation rules matching across Windows, macOS, and Linux
|
|
3
|
+
//
|
|
4
|
+
// This is a header-only implementation to avoid build complexity.
|
|
5
|
+
// Supports * wildcard only, case-insensitive matching.
|
|
6
|
+
|
|
7
|
+
#ifndef ELECTROBUN_GLOB_MATCH_H
|
|
8
|
+
#define ELECTROBUN_GLOB_MATCH_H
|
|
9
|
+
|
|
10
|
+
#include <string>
|
|
11
|
+
#include <cctype>
|
|
12
|
+
|
|
13
|
+
namespace electrobun {
|
|
14
|
+
|
|
15
|
+
// Simple case-insensitive glob matcher (supports * wildcard only)
|
|
16
|
+
// Returns true if text matches the glob pattern
|
|
17
|
+
//
|
|
18
|
+
// Examples:
|
|
19
|
+
// globMatch("*.example.com", "www.example.com") -> true
|
|
20
|
+
// globMatch("https://*.wikipedia.org/*", "https://en.wikipedia.org/wiki/Test") -> true
|
|
21
|
+
// globMatch("exact.match.com", "exact.match.com") -> true
|
|
22
|
+
// globMatch("*.google.com", "www.bing.com") -> false
|
|
23
|
+
//
|
|
24
|
+
inline bool globMatch(const std::string& pattern, const std::string& text) {
|
|
25
|
+
size_t p = 0, t = 0;
|
|
26
|
+
size_t starP = std::string::npos, starT = 0;
|
|
27
|
+
|
|
28
|
+
while (t < text.size()) {
|
|
29
|
+
if (p < pattern.size() && (std::tolower(static_cast<unsigned char>(pattern[p])) ==
|
|
30
|
+
std::tolower(static_cast<unsigned char>(text[t])))) {
|
|
31
|
+
// Characters match (case-insensitive)
|
|
32
|
+
p++;
|
|
33
|
+
t++;
|
|
34
|
+
} else if (p < pattern.size() && pattern[p] == '*') {
|
|
35
|
+
// Wildcard: remember position and try matching zero characters
|
|
36
|
+
starP = p++;
|
|
37
|
+
starT = t;
|
|
38
|
+
} else if (starP != std::string::npos) {
|
|
39
|
+
// Mismatch but we have a previous wildcard: backtrack
|
|
40
|
+
p = starP + 1;
|
|
41
|
+
t = ++starT;
|
|
42
|
+
} else {
|
|
43
|
+
// Mismatch and no wildcard to backtrack to
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Skip any trailing wildcards in pattern
|
|
49
|
+
while (p < pattern.size() && pattern[p] == '*') {
|
|
50
|
+
p++;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Match if we consumed the entire pattern
|
|
54
|
+
return p == pattern.size();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
} // namespace electrobun
|
|
58
|
+
|
|
59
|
+
#endif // ELECTROBUN_GLOB_MATCH_H
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// json_menu_parser.h - Cross-platform simple JSON menu parser
|
|
2
|
+
// Lightweight JSON parsing for menu configurations
|
|
3
|
+
// Used across Windows, macOS, and Linux
|
|
4
|
+
//
|
|
5
|
+
// This is a header-only implementation to avoid build complexity.
|
|
6
|
+
// For complex JSON needs, use a proper JSON library instead.
|
|
7
|
+
|
|
8
|
+
#ifndef ELECTROBUN_JSON_MENU_PARSER_H
|
|
9
|
+
#define ELECTROBUN_JSON_MENU_PARSER_H
|
|
10
|
+
|
|
11
|
+
#include <string>
|
|
12
|
+
#include <vector>
|
|
13
|
+
|
|
14
|
+
namespace electrobun {
|
|
15
|
+
|
|
16
|
+
// Represents a parsed menu item from JSON
|
|
17
|
+
struct MenuItemJson {
|
|
18
|
+
std::string type; // "normal", "separator", "checkbox", "submenu"
|
|
19
|
+
std::string label;
|
|
20
|
+
std::string action; // Action identifier for callbacks
|
|
21
|
+
std::string role; // System role (e.g., "quit", "copy", "paste")
|
|
22
|
+
std::string tooltip;
|
|
23
|
+
std::string accelerator; // Keyboard shortcut
|
|
24
|
+
bool enabled = true;
|
|
25
|
+
bool checked = false;
|
|
26
|
+
bool hidden = false;
|
|
27
|
+
std::vector<MenuItemJson> submenu;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Simple JSON string value extractor
|
|
31
|
+
// Finds "key": "value" patterns and extracts the value
|
|
32
|
+
inline std::string extractJsonStringValue(const std::string& json,
|
|
33
|
+
const std::string& key,
|
|
34
|
+
size_t startPos,
|
|
35
|
+
size_t endPos) {
|
|
36
|
+
std::string searchKey = "\"" + key + "\":";
|
|
37
|
+
size_t keyPos = json.find(searchKey, startPos);
|
|
38
|
+
|
|
39
|
+
if (keyPos == std::string::npos || keyPos >= endPos) {
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Skip whitespace after colon
|
|
44
|
+
size_t valueStart = keyPos + searchKey.length();
|
|
45
|
+
while (valueStart < endPos && (json[valueStart] == ' ' || json[valueStart] == '\t')) {
|
|
46
|
+
valueStart++;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (valueStart >= endPos) return "";
|
|
50
|
+
|
|
51
|
+
// Check if it's a string value (starts with quote)
|
|
52
|
+
if (json[valueStart] == '"') {
|
|
53
|
+
valueStart++;
|
|
54
|
+
size_t valueEnd = json.find('"', valueStart);
|
|
55
|
+
if (valueEnd != std::string::npos && valueEnd < endPos) {
|
|
56
|
+
return json.substr(valueStart, valueEnd - valueStart);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return "";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Simple JSON boolean value extractor
|
|
64
|
+
inline bool extractJsonBoolValue(const std::string& json,
|
|
65
|
+
const std::string& key,
|
|
66
|
+
size_t startPos,
|
|
67
|
+
size_t endPos,
|
|
68
|
+
bool defaultValue = false) {
|
|
69
|
+
std::string searchKey = "\"" + key + "\":";
|
|
70
|
+
size_t keyPos = json.find(searchKey, startPos);
|
|
71
|
+
|
|
72
|
+
if (keyPos == std::string::npos || keyPos >= endPos) {
|
|
73
|
+
return defaultValue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
size_t valueStart = keyPos + searchKey.length();
|
|
77
|
+
while (valueStart < endPos && (json[valueStart] == ' ' || json[valueStart] == '\t')) {
|
|
78
|
+
valueStart++;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (valueStart >= endPos) return defaultValue;
|
|
82
|
+
|
|
83
|
+
if (json.substr(valueStart, 4) == "true") {
|
|
84
|
+
return true;
|
|
85
|
+
} else if (json.substr(valueStart, 5) == "false") {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return defaultValue;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Find the end of a JSON object starting at the given position
|
|
93
|
+
// Returns the position of the closing brace
|
|
94
|
+
inline size_t findObjectEnd(const std::string& json, size_t startPos) {
|
|
95
|
+
if (startPos >= json.length() || json[startPos] != '{') {
|
|
96
|
+
return std::string::npos;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
int braceCount = 1;
|
|
100
|
+
size_t pos = startPos + 1;
|
|
101
|
+
|
|
102
|
+
while (pos < json.length() && braceCount > 0) {
|
|
103
|
+
if (json[pos] == '{') braceCount++;
|
|
104
|
+
else if (json[pos] == '}') braceCount--;
|
|
105
|
+
pos++;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return braceCount == 0 ? pos - 1 : std::string::npos;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Find the end of a JSON array starting at the given position
|
|
112
|
+
inline size_t findArrayEnd(const std::string& json, size_t startPos) {
|
|
113
|
+
if (startPos >= json.length() || json[startPos] != '[') {
|
|
114
|
+
return std::string::npos;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
int bracketCount = 1;
|
|
118
|
+
size_t pos = startPos + 1;
|
|
119
|
+
|
|
120
|
+
while (pos < json.length() && bracketCount > 0) {
|
|
121
|
+
if (json[pos] == '[') bracketCount++;
|
|
122
|
+
else if (json[pos] == ']') bracketCount--;
|
|
123
|
+
pos++;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return bracketCount == 0 ? pos - 1 : std::string::npos;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Parse a single menu item from JSON object boundaries
|
|
130
|
+
inline MenuItemJson parseMenuItem(const std::string& json, size_t startPos, size_t endPos) {
|
|
131
|
+
MenuItemJson item;
|
|
132
|
+
|
|
133
|
+
item.label = extractJsonStringValue(json, "label", startPos, endPos);
|
|
134
|
+
item.type = extractJsonStringValue(json, "type", startPos, endPos);
|
|
135
|
+
item.action = extractJsonStringValue(json, "action", startPos, endPos);
|
|
136
|
+
item.role = extractJsonStringValue(json, "role", startPos, endPos);
|
|
137
|
+
item.tooltip = extractJsonStringValue(json, "tooltip", startPos, endPos);
|
|
138
|
+
item.accelerator = extractJsonStringValue(json, "accelerator", startPos, endPos);
|
|
139
|
+
item.enabled = extractJsonBoolValue(json, "enabled", startPos, endPos, true);
|
|
140
|
+
item.checked = extractJsonBoolValue(json, "checked", startPos, endPos, false);
|
|
141
|
+
item.hidden = extractJsonBoolValue(json, "hidden", startPos, endPos, false);
|
|
142
|
+
|
|
143
|
+
// Look for submenu array
|
|
144
|
+
std::string submenuKey = "\"submenu\":";
|
|
145
|
+
size_t submenuPos = json.find(submenuKey, startPos);
|
|
146
|
+
if (submenuPos != std::string::npos && submenuPos < endPos) {
|
|
147
|
+
size_t arrayStart = json.find('[', submenuPos);
|
|
148
|
+
if (arrayStart != std::string::npos && arrayStart < endPos) {
|
|
149
|
+
size_t arrayEnd = findArrayEnd(json, arrayStart);
|
|
150
|
+
if (arrayEnd != std::string::npos) {
|
|
151
|
+
// Recursively parse submenu items
|
|
152
|
+
size_t itemStart = json.find('{', arrayStart);
|
|
153
|
+
while (itemStart != std::string::npos && itemStart < arrayEnd) {
|
|
154
|
+
size_t itemEnd = findObjectEnd(json, itemStart);
|
|
155
|
+
if (itemEnd != std::string::npos && itemEnd <= arrayEnd) {
|
|
156
|
+
item.submenu.push_back(parseMenuItem(json, itemStart, itemEnd));
|
|
157
|
+
itemStart = json.find('{', itemEnd + 1);
|
|
158
|
+
} else {
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Set default type based on content
|
|
167
|
+
if (item.type.empty()) {
|
|
168
|
+
if (item.label == "-" || item.label.empty()) {
|
|
169
|
+
item.type = "separator";
|
|
170
|
+
} else if (!item.submenu.empty()) {
|
|
171
|
+
item.type = "submenu";
|
|
172
|
+
} else {
|
|
173
|
+
item.type = "normal";
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return item;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Parse a JSON array of menu items
|
|
181
|
+
inline std::vector<MenuItemJson> parseMenuJson(const std::string& jsonStr) {
|
|
182
|
+
std::vector<MenuItemJson> items;
|
|
183
|
+
|
|
184
|
+
if (jsonStr.empty()) return items;
|
|
185
|
+
|
|
186
|
+
// Find the start of the array
|
|
187
|
+
size_t arrayStart = jsonStr.find('[');
|
|
188
|
+
if (arrayStart == std::string::npos) {
|
|
189
|
+
// Try parsing as a single object
|
|
190
|
+
size_t objStart = jsonStr.find('{');
|
|
191
|
+
if (objStart != std::string::npos) {
|
|
192
|
+
size_t objEnd = findObjectEnd(jsonStr, objStart);
|
|
193
|
+
if (objEnd != std::string::npos) {
|
|
194
|
+
items.push_back(parseMenuItem(jsonStr, objStart, objEnd));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return items;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
size_t arrayEnd = findArrayEnd(jsonStr, arrayStart);
|
|
201
|
+
if (arrayEnd == std::string::npos) {
|
|
202
|
+
arrayEnd = jsonStr.length();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Find and parse each menu item object
|
|
206
|
+
size_t pos = arrayStart + 1;
|
|
207
|
+
while (pos < arrayEnd) {
|
|
208
|
+
size_t objStart = jsonStr.find('{', pos);
|
|
209
|
+
if (objStart == std::string::npos || objStart >= arrayEnd) break;
|
|
210
|
+
|
|
211
|
+
size_t objEnd = findObjectEnd(jsonStr, objStart);
|
|
212
|
+
if (objEnd == std::string::npos || objEnd > arrayEnd) break;
|
|
213
|
+
|
|
214
|
+
items.push_back(parseMenuItem(jsonStr, objStart, objEnd));
|
|
215
|
+
pos = objEnd + 1;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return items;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
} // namespace electrobun
|
|
222
|
+
|
|
223
|
+
#endif // ELECTROBUN_JSON_MENU_PARSER_H
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// mime_types.h - Cross-platform MIME type detection
|
|
2
|
+
// Based on Bun runtime supported file types and web development standards
|
|
3
|
+
// Used across Windows, macOS, and Linux
|
|
4
|
+
//
|
|
5
|
+
// This is a header-only implementation to avoid build complexity.
|
|
6
|
+
|
|
7
|
+
#ifndef ELECTROBUN_MIME_TYPES_H
|
|
8
|
+
#define ELECTROBUN_MIME_TYPES_H
|
|
9
|
+
|
|
10
|
+
#include <string>
|
|
11
|
+
|
|
12
|
+
namespace electrobun {
|
|
13
|
+
|
|
14
|
+
// Get MIME type from a URL or file path based on extension
|
|
15
|
+
// Returns "application/octet-stream" as default for unknown types
|
|
16
|
+
inline std::string getMimeTypeFromUrl(const std::string& url) {
|
|
17
|
+
// Web/Code Files (Bun native support)
|
|
18
|
+
if (url.find(".html") != std::string::npos || url.find(".htm") != std::string::npos) {
|
|
19
|
+
return "text/html";
|
|
20
|
+
} else if (url.find(".js") != std::string::npos || url.find(".mjs") != std::string::npos || url.find(".cjs") != std::string::npos) {
|
|
21
|
+
return "text/javascript";
|
|
22
|
+
} else if (url.find(".ts") != std::string::npos || url.find(".mts") != std::string::npos || url.find(".cts") != std::string::npos) {
|
|
23
|
+
return "text/typescript";
|
|
24
|
+
} else if (url.find(".jsx") != std::string::npos) {
|
|
25
|
+
return "text/jsx";
|
|
26
|
+
} else if (url.find(".tsx") != std::string::npos) {
|
|
27
|
+
return "text/tsx";
|
|
28
|
+
} else if (url.find(".css") != std::string::npos) {
|
|
29
|
+
return "text/css";
|
|
30
|
+
} else if (url.find(".json") != std::string::npos) {
|
|
31
|
+
return "application/json";
|
|
32
|
+
} else if (url.find(".xml") != std::string::npos) {
|
|
33
|
+
return "application/xml";
|
|
34
|
+
} else if (url.find(".md") != std::string::npos) {
|
|
35
|
+
return "text/markdown";
|
|
36
|
+
} else if (url.find(".txt") != std::string::npos) {
|
|
37
|
+
return "text/plain";
|
|
38
|
+
} else if (url.find(".toml") != std::string::npos) {
|
|
39
|
+
return "application/toml";
|
|
40
|
+
} else if (url.find(".yaml") != std::string::npos || url.find(".yml") != std::string::npos) {
|
|
41
|
+
return "application/x-yaml";
|
|
42
|
+
|
|
43
|
+
// Image Files
|
|
44
|
+
} else if (url.find(".png") != std::string::npos) {
|
|
45
|
+
return "image/png";
|
|
46
|
+
} else if (url.find(".jpg") != std::string::npos || url.find(".jpeg") != std::string::npos) {
|
|
47
|
+
return "image/jpeg";
|
|
48
|
+
} else if (url.find(".gif") != std::string::npos) {
|
|
49
|
+
return "image/gif";
|
|
50
|
+
} else if (url.find(".webp") != std::string::npos) {
|
|
51
|
+
return "image/webp";
|
|
52
|
+
} else if (url.find(".svg") != std::string::npos) {
|
|
53
|
+
return "image/svg+xml";
|
|
54
|
+
} else if (url.find(".ico") != std::string::npos) {
|
|
55
|
+
return "image/x-icon";
|
|
56
|
+
} else if (url.find(".avif") != std::string::npos) {
|
|
57
|
+
return "image/avif";
|
|
58
|
+
|
|
59
|
+
// Font Files
|
|
60
|
+
} else if (url.find(".woff") != std::string::npos) {
|
|
61
|
+
return "font/woff";
|
|
62
|
+
} else if (url.find(".woff2") != std::string::npos) {
|
|
63
|
+
return "font/woff2";
|
|
64
|
+
} else if (url.find(".ttf") != std::string::npos) {
|
|
65
|
+
return "font/ttf";
|
|
66
|
+
} else if (url.find(".otf") != std::string::npos) {
|
|
67
|
+
return "font/otf";
|
|
68
|
+
|
|
69
|
+
// Media Files
|
|
70
|
+
} else if (url.find(".mp3") != std::string::npos) {
|
|
71
|
+
return "audio/mpeg";
|
|
72
|
+
} else if (url.find(".mp4") != std::string::npos) {
|
|
73
|
+
return "video/mp4";
|
|
74
|
+
} else if (url.find(".webm") != std::string::npos) {
|
|
75
|
+
return "video/webm";
|
|
76
|
+
} else if (url.find(".ogg") != std::string::npos) {
|
|
77
|
+
return "audio/ogg";
|
|
78
|
+
} else if (url.find(".wav") != std::string::npos) {
|
|
79
|
+
return "audio/wav";
|
|
80
|
+
|
|
81
|
+
// Document Files
|
|
82
|
+
} else if (url.find(".pdf") != std::string::npos) {
|
|
83
|
+
return "application/pdf";
|
|
84
|
+
|
|
85
|
+
// WebAssembly (Bun support)
|
|
86
|
+
} else if (url.find(".wasm") != std::string::npos) {
|
|
87
|
+
return "application/wasm";
|
|
88
|
+
|
|
89
|
+
// Compressed Files
|
|
90
|
+
} else if (url.find(".zip") != std::string::npos) {
|
|
91
|
+
return "application/zip";
|
|
92
|
+
} else if (url.find(".gz") != std::string::npos) {
|
|
93
|
+
return "application/gzip";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return "application/octet-stream"; // default
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
} // namespace electrobun
|
|
100
|
+
|
|
101
|
+
#endif // ELECTROBUN_MIME_TYPES_H
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// navigation_rules.h - Cross-platform navigation rules checking
|
|
2
|
+
// Handles URL navigation rules with glob pattern matching
|
|
3
|
+
// Used across Windows, macOS, and Linux
|
|
4
|
+
//
|
|
5
|
+
// This is a header-only implementation to avoid build complexity.
|
|
6
|
+
|
|
7
|
+
#ifndef ELECTROBUN_NAVIGATION_RULES_H
|
|
8
|
+
#define ELECTROBUN_NAVIGATION_RULES_H
|
|
9
|
+
|
|
10
|
+
#include <string>
|
|
11
|
+
#include <vector>
|
|
12
|
+
#include "glob_match.h"
|
|
13
|
+
|
|
14
|
+
namespace electrobun {
|
|
15
|
+
|
|
16
|
+
// Represents a single navigation rule
|
|
17
|
+
// Rules starting with "^" are block rules (inverts the match)
|
|
18
|
+
struct NavigationRule {
|
|
19
|
+
std::string pattern;
|
|
20
|
+
bool isBlockRule;
|
|
21
|
+
|
|
22
|
+
NavigationRule(const std::string& ruleString) {
|
|
23
|
+
if (!ruleString.empty() && ruleString[0] == '^') {
|
|
24
|
+
isBlockRule = true;
|
|
25
|
+
pattern = ruleString.substr(1);
|
|
26
|
+
} else {
|
|
27
|
+
isBlockRule = false;
|
|
28
|
+
pattern = ruleString;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Check if a URL should be allowed based on navigation rules
|
|
34
|
+
// Uses "last match wins" semantics
|
|
35
|
+
// Returns true if navigation should be allowed, false if blocked
|
|
36
|
+
//
|
|
37
|
+
// Rules are processed in order:
|
|
38
|
+
// - Normal rules (no ^ prefix): if URL matches, allow navigation
|
|
39
|
+
// - Block rules (^ prefix): if URL matches, block navigation
|
|
40
|
+
// - Last matching rule determines the outcome
|
|
41
|
+
// - If no rules match, returns defaultAllow
|
|
42
|
+
inline bool checkNavigationRulesForUrl(const std::vector<std::string>& rules,
|
|
43
|
+
const std::string& url,
|
|
44
|
+
bool defaultAllow = true) {
|
|
45
|
+
bool shouldAllow = defaultAllow;
|
|
46
|
+
bool anyMatch = false;
|
|
47
|
+
|
|
48
|
+
for (const auto& ruleString : rules) {
|
|
49
|
+
if (ruleString.empty()) continue;
|
|
50
|
+
|
|
51
|
+
NavigationRule rule(ruleString);
|
|
52
|
+
|
|
53
|
+
if (globMatch(rule.pattern, url)) {
|
|
54
|
+
anyMatch = true;
|
|
55
|
+
// If it's a block rule, matching means we should NOT allow
|
|
56
|
+
// If it's a normal rule, matching means we SHOULD allow
|
|
57
|
+
shouldAllow = !rule.isBlockRule;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return shouldAllow;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Overload that takes a comma-separated rules string
|
|
65
|
+
inline bool checkNavigationRulesForUrl(const std::string& rulesString,
|
|
66
|
+
const std::string& url,
|
|
67
|
+
bool defaultAllow = true) {
|
|
68
|
+
std::vector<std::string> rules;
|
|
69
|
+
|
|
70
|
+
size_t start = 0;
|
|
71
|
+
size_t end = rulesString.find(',');
|
|
72
|
+
|
|
73
|
+
while (end != std::string::npos) {
|
|
74
|
+
std::string rule = rulesString.substr(start, end - start);
|
|
75
|
+
// Trim whitespace
|
|
76
|
+
size_t first = rule.find_first_not_of(" \t");
|
|
77
|
+
size_t last = rule.find_last_not_of(" \t");
|
|
78
|
+
if (first != std::string::npos) {
|
|
79
|
+
rules.push_back(rule.substr(first, last - first + 1));
|
|
80
|
+
}
|
|
81
|
+
start = end + 1;
|
|
82
|
+
end = rulesString.find(',', start);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Add the last rule
|
|
86
|
+
std::string rule = rulesString.substr(start);
|
|
87
|
+
size_t first = rule.find_first_not_of(" \t");
|
|
88
|
+
size_t last = rule.find_last_not_of(" \t");
|
|
89
|
+
if (first != std::string::npos) {
|
|
90
|
+
rules.push_back(rule.substr(first, last - first + 1));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return checkNavigationRulesForUrl(rules, url, defaultAllow);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
} // namespace electrobun
|
|
97
|
+
|
|
98
|
+
#endif // ELECTROBUN_NAVIGATION_RULES_H
|