413 lines
19 KiB
HTML
413 lines
19 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>FileBrowser Widget Example</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
|
|
<script src="/uppy.min.js"></script>
|
|
<link href="/uppy.min.css" rel="stylesheet">
|
|
<style>
|
|
body {
|
|
padding: 20px;
|
|
background-color: #f8f9fa;
|
|
}
|
|
.widget-container {
|
|
border: 2px dashed #dee2e6;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
background: white;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
min-height: 400px;
|
|
}
|
|
.widget-container:empty::after {
|
|
content: "Widget will render here...";
|
|
color: #6c757d;
|
|
font-style: italic;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 200px;
|
|
}
|
|
.config-panel {
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
margin-bottom: 20px;
|
|
}
|
|
.status-indicator {
|
|
display: inline-block;
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
margin-right: 8px;
|
|
}
|
|
.status-success { background-color: #28a745; }
|
|
.status-error { background-color: #dc3545; }
|
|
.status-loading { background-color: #ffc107; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<!-- Widget Header -->
|
|
<div class="config-panel mb-4">
|
|
<div class="row align-items-center">
|
|
<div class="col-md-6">
|
|
<h3 class="mb-0">
|
|
<i class="bi bi-hdd-stack text-primary"></i>
|
|
File Browser Widget
|
|
<span class="badge bg-secondary ms-2" id="widget-version">v0.1.0</span>
|
|
</h3>
|
|
<p class="text-muted mb-0 mt-1">Self-contained WASM widget for file management</p>
|
|
</div>
|
|
<div class="col-md-6 text-end">
|
|
<div class="btn-group" role="group">
|
|
<button type="button" class="btn btn-outline-primary btn-sm" data-bs-toggle="modal" data-bs-target="#assetsModal">
|
|
<i class="bi bi-file-earmark-zip"></i>
|
|
Assets
|
|
</button>
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="window.open('https://github.com/herocode/framework/tree/main/widgets/file_browser_widget', '_blank')">
|
|
<i class="bi bi-code-slash"></i>
|
|
Code
|
|
</button>
|
|
<button type="button" class="btn btn-outline-primary btn-sm" onclick="window.open('#documentation', '_blank')">
|
|
<i class="bi bi-book"></i>
|
|
Documentation
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-4">
|
|
<div class="config-panel">
|
|
<h4>
|
|
<i class="bi bi-gear"></i>
|
|
Configuration
|
|
</h4>
|
|
<div class="mb-3">
|
|
<label for="endpoint" class="form-label">Base Endpoint:</label>
|
|
<input type="text" id="endpoint" class="form-control" value="http://localhost:3001/files">
|
|
<div class="form-text">Backend API endpoint for file operations</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="theme" class="form-label">Theme:</label>
|
|
<select id="theme" class="form-select">
|
|
<option value="light">Light</option>
|
|
<option value="dark">Dark</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="max-file-size" class="form-label">Max File Size (MB):</label>
|
|
<input type="number" id="max-file-size" class="form-control" value="100" min="1" max="1000">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label class="form-label">Features:</label>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="show-upload" checked>
|
|
<label class="form-check-label" for="show-upload">Show Upload</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="show-download" checked>
|
|
<label class="form-check-label" for="show-download">Show Download</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="show-delete" checked>
|
|
<label class="form-check-label" for="show-delete">Show Delete</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="initial-path" class="form-label">Initial Path:</label>
|
|
<input type="text" id="initial-path" class="form-control" placeholder="e.g., documents/">
|
|
</div>
|
|
|
|
<button id="recreate-widget" class="btn btn-primary w-100">
|
|
<i class="bi bi-arrow-clockwise"></i>
|
|
Apply Configuration
|
|
</button>
|
|
|
|
<div class="mt-3">
|
|
<div class="d-flex align-items-center justify-content-between">
|
|
<div id="status" class="small">
|
|
<span class="status-indicator status-loading"></span>
|
|
<span id="status-text">Initializing...</span>
|
|
</div>
|
|
<div class="small">
|
|
<span class="badge bg-success" id="browser-compat">Compatible</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
<div class="col-md-8">
|
|
<!-- Widget Rendering Area -->
|
|
<div class="widget-container">
|
|
<div id="file-browser-widget"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script type="module">
|
|
import init, {
|
|
create_file_browser_widget,
|
|
create_default_config,
|
|
check_browser_compatibility,
|
|
get_version
|
|
} from '/file_browser_widget.js';
|
|
|
|
let currentWidget = null;
|
|
let isInitialized = false;
|
|
|
|
function updateStatus(text, type = 'loading') {
|
|
const statusElement = document.getElementById('status-text');
|
|
const indicatorElement = document.querySelector('.status-indicator');
|
|
|
|
statusElement.textContent = text;
|
|
indicatorElement.className = `status-indicator status-${type}`;
|
|
}
|
|
|
|
async function initWidget() {
|
|
try {
|
|
updateStatus('Loading WASM module...', 'loading');
|
|
await init();
|
|
|
|
updateStatus('Checking compatibility...', 'loading');
|
|
const version = get_version();
|
|
const isCompatible = check_browser_compatibility();
|
|
|
|
document.getElementById('widget-version').textContent = version;
|
|
document.getElementById('browser-compat').textContent = isCompatible ? 'Yes ✓' : 'No ✗';
|
|
|
|
if (!isCompatible) {
|
|
updateStatus('Browser not compatible', 'error');
|
|
document.getElementById('file-browser-widget').innerHTML =
|
|
'<div class="alert alert-danger">Your browser is not compatible with this widget</div>';
|
|
return;
|
|
}
|
|
|
|
isInitialized = true;
|
|
updateStatus('Ready', 'success');
|
|
createWidget();
|
|
|
|
} catch (error) {
|
|
console.error('Failed to initialize widget:', error);
|
|
updateStatus(`Initialization failed: ${error.message}`, 'error');
|
|
document.getElementById('file-browser-widget').innerHTML =
|
|
`<div class="alert alert-danger">Failed to initialize: ${error.message}</div>`;
|
|
}
|
|
}
|
|
|
|
function createWidget() {
|
|
if (!isInitialized) {
|
|
updateStatus('Widget not initialized', 'error');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
updateStatus('Creating widget...', 'loading');
|
|
|
|
// Destroy existing widget
|
|
if (currentWidget) {
|
|
currentWidget.destroy();
|
|
currentWidget = null;
|
|
}
|
|
|
|
// Clear container
|
|
const container = document.getElementById('file-browser-widget');
|
|
container.innerHTML = '';
|
|
|
|
// Get configuration from form
|
|
const config = create_default_config(document.getElementById('endpoint').value);
|
|
|
|
// Apply configuration using the corrected method names
|
|
config.setTheme(document.getElementById('theme').value);
|
|
config.setMaxFileSize(parseInt(document.getElementById('max-file-size').value) * 1024 * 1024);
|
|
config.setShowUpload(document.getElementById('show-upload').checked);
|
|
config.setShowDownload(document.getElementById('show-download').checked);
|
|
config.setShowDelete(document.getElementById('show-delete').checked);
|
|
|
|
const initialPath = document.getElementById('initial-path').value.trim();
|
|
if (initialPath) {
|
|
config.setInitialPath(initialPath);
|
|
}
|
|
|
|
// Create widget
|
|
currentWidget = create_file_browser_widget('file-browser-widget', config);
|
|
updateStatus('Widget ready', 'success');
|
|
|
|
} catch (error) {
|
|
console.error('Failed to create widget:', error);
|
|
updateStatus(`Widget creation failed: ${error.message}`, 'error');
|
|
document.getElementById('file-browser-widget').innerHTML =
|
|
`<div class="alert alert-danger">Failed to create widget: ${error.message}</div>`;
|
|
}
|
|
}
|
|
|
|
// Event listeners
|
|
document.getElementById('recreate-widget').addEventListener('click', createWidget);
|
|
|
|
// Auto-recreate on configuration changes
|
|
['endpoint', 'theme', 'max-file-size', 'show-upload', 'show-download', 'show-delete', 'initial-path'].forEach(id => {
|
|
const element = document.getElementById(id);
|
|
if (element.type === 'checkbox') {
|
|
element.addEventListener('change', () => {
|
|
if (isInitialized) createWidget();
|
|
});
|
|
} else {
|
|
element.addEventListener('input', () => {
|
|
if (isInitialized) {
|
|
clearTimeout(element.debounceTimer);
|
|
element.debounceTimer = setTimeout(createWidget, 500);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Initialize when page loads
|
|
initWidget();
|
|
</script>
|
|
|
|
<!-- Assets Modal -->
|
|
<div class="modal fade" id="assetsModal" tabindex="-1" aria-labelledby="assetsModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="assetsModalLabel">
|
|
<i class="bi bi-file-earmark-zip text-primary"></i>
|
|
Widget Assets & Size Optimization
|
|
</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row mb-4">
|
|
<div class="col-md-6">
|
|
<h6 class="text-success">
|
|
<i class="bi bi-check-circle"></i>
|
|
Distribution Files
|
|
</h6>
|
|
<p class="small text-muted mb-3">Self-contained widget distribution with no external dependencies.</p>
|
|
</div>
|
|
<div class="col-md-6 text-end">
|
|
<div class="small">
|
|
<div class="badge bg-success mb-2">67.9% compression ratio</div>
|
|
<div class="text-muted">Optimized for web delivery</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-striped">
|
|
<thead class="table-dark">
|
|
<tr>
|
|
<th>Asset</th>
|
|
<th>Description</th>
|
|
<th class="text-end">Original</th>
|
|
<th class="text-end">Gzipped</th>
|
|
<th class="text-end">Savings</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><code>file_browser_widget_bg.wasm</code></td>
|
|
<td class="small text-muted">WebAssembly binary</td>
|
|
<td class="text-end">331KB</td>
|
|
<td class="text-end text-info">136KB</td>
|
|
<td class="text-end text-success">59%</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>file_browser_widget.js</code></td>
|
|
<td class="small text-muted">JavaScript bindings</td>
|
|
<td class="text-end">39KB</td>
|
|
<td class="text-end text-info">7KB</td>
|
|
<td class="text-end text-success">82%</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>uppy.min.js</code></td>
|
|
<td class="small text-muted">File upload library</td>
|
|
<td class="text-end">564KB</td>
|
|
<td class="text-end text-info">172KB</td>
|
|
<td class="text-end text-success">70%</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>uppy.min.css</code></td>
|
|
<td class="small text-muted">Upload UI styling</td>
|
|
<td class="text-end">90KB</td>
|
|
<td class="text-end text-info">14KB</td>
|
|
<td class="text-end text-success">84%</td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>file_browser_widget.d.ts</code></td>
|
|
<td class="small text-muted">TypeScript definitions</td>
|
|
<td class="text-end">5KB</td>
|
|
<td class="text-end text-muted">-</td>
|
|
<td class="text-end text-muted">-</td>
|
|
</tr>
|
|
<tr class="table-active fw-bold">
|
|
<td>Total Distribution</td>
|
|
<td class="small text-muted">Complete widget package</td>
|
|
<td class="text-end">1.03MB</td>
|
|
<td class="text-end text-success">329KB</td>
|
|
<td class="text-end text-success">68%</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="row mt-4">
|
|
<div class="col-md-6">
|
|
<h6 class="text-info">
|
|
<i class="bi bi-speedometer2"></i>
|
|
Performance Benefits
|
|
</h6>
|
|
<ul class="small">
|
|
<li>Faster initial load times</li>
|
|
<li>Reduced bandwidth usage</li>
|
|
<li>Better mobile experience</li>
|
|
<li>CDN-friendly distribution</li>
|
|
</ul>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6 class="text-warning">
|
|
<i class="bi bi-tools"></i>
|
|
Optimization Techniques
|
|
</h6>
|
|
<ul class="small">
|
|
<li>wasm-opt binary optimization</li>
|
|
<li>Gzip compression (level 9)</li>
|
|
<li>Dead code elimination</li>
|
|
<li>Release build optimizations</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
|
<button type="button" class="btn btn-primary" onclick="window.open('https://github.com/herocode/framework/tree/main/widgets/file_browser_widget', '_blank')">
|
|
<i class="bi bi-download"></i>
|
|
Download Widget
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bootstrap JS -->
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
</body>
|
|
</html>
|