Chandra
view release on metacpan or search on metacpan
examples/assets_mount_example.pl view on Meta::CPAN
#!/usr/bin/env perl
#
# Example: Serving assets via protocol mount
#
# Demonstrates mounting an assets directory so that CSS, JS, and images
# are served via a custom protocol (e.g. app://css/style.css).
# The mount() call registers a protocol handler, and the injected JS
# transparently intercepts <link>, <script>, <img>, and fetch() calls.
#
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/../blib/lib", "$FindBin::Bin/../blib/arch";
use File::Path qw(mkpath rmtree);
use Chandra::App;
# ---- Setup: create a sample assets directory ----
my $asset_dir = '/tmp/chandra_mount_example';
rmtree($asset_dir) if -d $asset_dir;
mkpath("$asset_dir/css");
mkpath("$asset_dir/js");
_write("$asset_dir/css/style.css", <<'CSS');
body {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
margin: 0; padding: 2em;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff;
min-height: 100vh;
}
h1 { margin-bottom: 0.5em; }
.card {
background: rgba(255,255,255,0.15);
backdrop-filter: blur(10px);
border-radius: 12px;
padding: 1.5em;
margin: 1em 0;
border: 1px solid rgba(255,255,255,0.2);
}
button {
background: #fff; color: #764ba2;
border: none; border-radius: 6px;
padding: 0.6em 1.2em; cursor: pointer;
font-weight: bold; font-size: 1em;
}
button:hover { opacity: 0.9; }
#status { color: #a5d6a7; font-weight: bold; }
CSS
_write("$asset_dir/js/main.js", <<'JS');
(function init() {
// Protocol-loaded scripts arrive after DOMContentLoaded,
// so run immediately when the DOM is already ready.
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', run);
} else {
run();
}
function run() {
document.getElementById('status').textContent = 'Assets loaded via protocol!';
document.getElementById('greet-btn').addEventListener('click', async function() {
var name = document.getElementById('name-input').value || 'World';
var result = await window.chandra.invoke('greet', [name]);
document.getElementById('greeting').textContent = result;
});
}
})();
JS
# ---- Create the app ----
my $app = Chandra::App->new(
title => 'Assets Mount Example',
width => 500,
height => 400,
debug => 1,
);
# ---- Mount assets ----
# This registers a custom protocol handler so the webview can load
# files from $asset_dir using the "app" scheme: app://css/style.css
# The injected JS transparently intercepts <link>, <script>, <img>,
# fetch(), and <a> clicks for the "app://" scheme.
my $assets = $app->assets($asset_dir, prefix => 'app');
$assets->mount($app);
# ---- Bind a Perl function ----
$app->bind('greet', sub {
my ($name) = @_;
return "Hello, $name! Greetings from Perl.";
});
# ---- Set content referencing mounted assets ----
$app->set_content(<<'HTML');
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" data-href="app://css/style.css">
</head>
<body>
<h1>Assets Mount Example</h1>
<p>Status: <span id="status">Loading...</span></p>
<div class="card">
<p>CSS and JS are served from disk via the <code>app://</code> protocol.</p>
<p>No inlining needed — the webview fetches them like a real browser.</p>
</div>
<div class="card">
<input id="name-input" type="text" placeholder="Enter your name">
<button id="greet-btn">Greet</button>
<p id="greeting"></p>
</div>
<script data-src="app://js/main.js"></script>
</body>
</html>
HTML
$app->run;
# Cleanup
rmtree($asset_dir);
sub _write {
my ($path, $content) = @_;
open my $fh, '>', $path or die "Cannot write $path: $!";
print $fh $content;
close $fh;
}
( run in 0.831 second using v1.01-cache-2.11-cpan-39bf76dae61 )