JADE:// Custom Protocol: Loading Local Web Resources
JadeView registers a custom protocol handler via set_protocol_service_path, mapping JADE://{app_signature} (the domain is the lowercased app_signature) to a local directory or JAPK file to enable secure and efficient loading of local resources.
JADE://{app_signature} is JadeView's internal custom protocol route, not a real HTTP server. It does not listen on any port and runs entirely within the application.
Prerequisites
- Download
JadeView_x64.dllandJadeView.hfrom GitHub Releases - Basic understanding of the C language
Step 1: Initialize and Set Up the Protocol Service
#include <stdio.h>
#include <string.h>
#include "JadeView.h"
// Window control IPC callbacks
const char* minimize_callback(uint32_t window_id, const char* event_data) {
minimize_window(window_id);
return jade_text_create("{\"ok\":true}");
}
const char* maximize_callback(uint32_t window_id, const char* event_data) {
toggle_maximize_window(window_id);
return jade_text_create("{\"ok\":true}");
}
const char* close_callback(uint32_t window_id, const char* event_data) {
close_window(window_id);
return jade_text_create("{\"ok\":true}");
}
const char* app_ready_callback(uint32_t window_id, const char* event_data) {
if (window_id == 1) { // window_id==1 means success; event_data is JSON, and on failure (window_id==0) it is a plain-text error code
printf("JadeView is ready\n");
// Register window control commands
register_ipc_handler("minimize-window", minimize_callback);
register_ipc_handler("toggle-maximize", maximize_callback);
register_ipc_handler("close-window", close_callback);
// Set up the local protocol service —— map the ./web directory to JADE://com.example.myapp
char url_buffer[256];
int result = set_protocol_service_path(
"./web", // local resource directory
url_buffer, // output protocol URL buffer
sizeof(url_buffer), // buffer size
0 // hot_reload: 0 = disabled, 1 = enabled
);
if (result == 1) {
printf("Protocol URL: %s\n", url_buffer);
// url_buffer content looks like: JADE://com.example.myapp
WebViewWindowOptions options = {
.title = "Local Resource Example",
.width = 800,
.height = 600,
.resizable = 1,
.frame_style = "normal"
};
create_webview_window(url_buffer, 0, &options, NULL);
} else {
printf("Failed to set up protocol service\n");
}
} else {
printf("Initialization failed: %s\n", event_data ? event_data : "unknown");
}
return NULL;
}
int main() {
// app-ready must be registered before JadeView_init
jade_on("app-ready", app_ready_callback);
int result = JadeView_init(
1, // enable_devmod
NULL, // log_path
NULL, // data_directory
"My App", // app_name (app display name)
"com.example.myapp", // app_signature (lowercased, used as the protocol domain)
0 // single_instance
);
if (result == 0) {
printf("Initialization failed\n");
return 1;
}
printf("JadeView has started\n");
return 0;
}Step 2: Create the Frontend Page
Create a web folder and an index.html in your project directory:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Local Resource Example</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
}
/* Custom title bar */
.titlebar {
height: 32px;
background: #333;
color: #fff;
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 12px;
-webkit-app-region: drag;
user-select: none;
}
.titlebar-controls {
display: flex;
height: 100%;
}
.titlebar-btn {
-webkit-app-region: no-drag;
width: 46px;
height: 100%;
border: none;
background: transparent;
color: #fff;
cursor: pointer;
font-size: 14px;
}
.titlebar-btn:hover { background: #555; }
.titlebar-btn.close:hover { background: #e81123; }
.content {
flex: 1;
padding: 20px;
background: #f5f5f5;
}
.btn {
padding: 8px 16px;
background: #333;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn:hover { background: #555; }
</style>
</head>
<body>
<div class="titlebar">
<span>Local Resource Example</span>
<div class="titlebar-controls">
<button class="titlebar-btn" id="minBtn">─</button>
<button class="titlebar-btn" id="maxBtn">□</button>
<button class="titlebar-btn close" id="closeBtn">✕</button>
</div>
</div>
<div class="content">
<h1>JadeView Local Resources</h1>
<p>This page is loaded from the local file system via the custom protocol.</p>
<button class="btn" id="testBtn">Send Test Message</button>
<pre id="result"></pre>
</div>
<script>
// Window control —— send commands to the host process via jade.invoke
document.getElementById('minBtn').onclick = () => {
jade.invoke('minimize-window');
};
document.getElementById('maxBtn').onclick = () => {
jade.invoke('toggle-maximize');
};
document.getElementById('closeBtn').onclick = () => {
jade.invoke('close-window');
};
// Test IPC communication
document.getElementById('testBtn').onclick = async () => {
try {
const result = await jade.invoke('test-message',
{ text: 'Hello from page!', time: Date.now() });
document.getElementById('result').textContent =
JSON.stringify(result, null, 2);
} catch (e) {
document.getElementById('result').textContent =
'Error: ' + e.message;
}
};
// Listen for pushes from the host process
jade.on('host-notify', (data) => {
console.log('Host process notification:', data);
});
</script>
</body>
</html>Custom Protocol in Detail
How It Works
set_protocol_service_path("./web", url_buffer, size, 0)registers the local directory with the protocol route- The returned URL has the format
JADE://{app_signature}(lowercasedapp_signature) - When the WebView accesses
JADE://com.example.myapp/index.html, JadeView reads directly from./web/index.html - The entire process involves no network communication and no port listening, and is completed entirely within the application
Supported File Types
JadeView automatically sets the correct MIME type based on the file extension, with no manual configuration required. Supported types include:
- HTML, CSS, JavaScript
- Images (PNG, JPG, GIF, SVG, WebP, etc.)
- Fonts (WOFF, WOFF2, TTF, etc.)
- Data formats such as JSON and XML
SPA Routing Support
The protocol service has a built-in SPA fallback: when the requested path does not match any file, it automatically returns index.html, providing seamless support for History-mode routing in frontend frameworks.
Combining with JAPK
In addition to local directories, set_protocol_service_path also supports JAPK files:
// Load a JAPK file
char url_buffer[256];
set_protocol_service_path("app.japk", url_buffer, sizeof(url_buffer), 0);
// url_buffer → JADE://com.example.myapp
create_webview_window(url_buffer, 0, &options, NULL);Communication inside a JAPK also uses jade.invoke / jade.on, with no additional configuration required.
More information: JAPK Resource Bundle | Loading from Memory
Notes
- Not an HTTP server:
JADE://{app_signature}does not listen on any port and cannot be accessed by external browsers jade.invokerequires a CORS whitelist (v2.1+): if the page is not loaded through the protocol service, you must set the allowed origins inWebViewSettings.cors_whitelistapp_signaturedetermines the domain: the protocol URL domain is taken from theapp_signatureparameter ofJadeView_init(lowercased), not fromapp_name- Page path mapping:
JADE://com.example.myapp/css/style.css→./web/css/style.css
Detailed API: Core API - Local Protocol Service | Frontend Communication API