pulpie-webgpu / src /index.template.html
sizzlebop's picture
feat: add edge proxy routing and update URL fetcher
8286b6d
Raw History Blame Contribute Delete
13.5 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0">
<meta name="theme-color" content="#0d0e11">
<meta name="description" content="In-browser WebGPU HTML-to-Markdown main content extraction powered by Pulpie EuroBERT ONNX FP16.">
<title>Pulpie WebGPU — Content Extractor</title>
<!-- Preconnect to Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/src/style.css">
</head>
<body>
<div class="app-layout">
<!-- Header -->
<header class="app-header">
<div class="header-content">
<div class="brand">
<div class="brand-logo" aria-hidden="true">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"></path>
<path d="M2 12h20"></path>
</svg>
</div>
<div class="brand-info">
<div class="brand-title-row">
<h1 class="brand-name">Pulpie</h1>
<span class="badge badge-model">orange-small</span>
<span class="badge badge-gpu" id="webgpuBadge">WebGPU</span>
</div>
<p class="brand-tagline">EuroBERT ONNX FP32 content extraction</p>
</div>
</div>
</div>
</header>
<!-- Main Content -->
<main class="main-content">
<p class="app-intro">Paste a URL or raw HTML to extract the page and get the main content returned in clean Markdown.</p>
<!-- Model Loading Banner (visible during initial load or download) -->
<section class="card model-banner" id="modelBanner">
<div class="progress-bar-container" id="progressBarContainer">
<div class="progress-bar-track">
<div class="progress-bar-fill" id="progressBarFill" style="width: 0%;"></div>
</div>
<div class="progress-info">
<span id="progressStatusText">Initializing model...</span>
<span id="progressSpeedText">0%</span>
</div>
</div>
</section>
<!-- Input Card -->
<section class="card input-card" aria-labelledby="inputSectionTitle">
<h2 id="inputSectionTitle" class="sr-only">Input Content</h2>
<div class="input-header-row">
<!-- Input Mode Tabs -->
<div class="mode-tabs" role="tablist" aria-label="Input mode selection">
<button type="button" role="tab" aria-selected="true" id="modeUrlTab" class="mode-tab active" data-mode="url">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
</svg>
<span>URL</span>
</button>
<button type="button" role="tab" aria-selected="false" id="modeHtmlTab" class="mode-tab" data-mode="html">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="16 18 22 12 16 6"></polyline>
<polyline points="8 6 2 12 8 18"></polyline>
</svg>
<span>Raw HTML</span>
</button>
</div>
<!-- Presets -->
<div class="sample-presets">
<span class="preset-label">Samples:</span>
<button type="button" class="preset-btn" data-preset="article">Article</button>
<button type="button" class="preset-btn" data-preset="docs">Docs</button>
<button type="button" class="preset-btn" data-preset="news">News</button>
</div>
</div>
<!-- URL View -->
<div id="urlView" class="input-view-panel">
<form id="urlForm" class="url-input-form" onsubmit="return false;">
<div class="input-group">
<div class="input-prefix" aria-hidden="true">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<line x1="2" y1="12" x2="22" y2="12"></line>
<path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path>
</svg>
</div>
<input
type="url"
id="urlInput"
name="url"
placeholder="https://example.com/article..."
autocomplete="off"
spellcheck="false"
>
<div class="input-actions">
<button type="button" id="clearUrlBtn" class="btn-input-icon" title="Clear URL" style="display: none;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
<button type="button" id="pasteUrlBtn" class="btn-input-icon" title="Paste from clipboard">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path>
<rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect>
</svg>
</button>
</div>
</div>
<button type="submit" id="extractBtn" class="btn btn-primary btn-submit">
<span class="btn-text">Extract</span>
<span class="kbd-hint">Ctrl+Enter</span>
</button>
</form>
</div>
<!-- Raw HTML View -->
<div id="htmlView" class="input-view-panel" style="display: none;">
<div class="html-input-area" id="htmlDropZone">
<textarea
id="htmlInput"
placeholder="Paste raw HTML here or drop an .html file..."
spellcheck="false"
></textarea>
<div class="drop-overlay" id="dropOverlay">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="17 8 12 3 7 8"></polyline>
<line x1="12" y1="3" x2="12" y2="15"></line>
</svg>
<span>Drop HTML file to load</span>
</div>
</div>
<div class="html-secondary-row">
<div class="base-url-wrapper">
<label for="baseUrlInput">Base URL (optional, for resolving images):</label>
<input type="url" id="baseUrlInput" placeholder="https://example.com" autocomplete="off">
</div>
<div class="html-action-group">
<button type="button" id="clearHtmlBtn" class="btn btn-secondary btn-sm">Clear</button>
<button type="button" id="extractHtmlBtn" class="btn btn-primary btn-submit">
<span class="btn-text">Extract</span>
<span class="kbd-hint">Ctrl+Enter</span>
</button>
</div>
</div>
</div>
</section>
<!-- Status Toast / Alert -->
<div id="statusAlert" class="status-alert" role="status" style="display: none;">
<span class="status-alert-icon" id="statusAlertIcon"></span>
<span class="status-alert-msg" id="statusAlertMsg"></span>
</div>
<!-- Results Card -->
<section class="card results-card" id="resultsCard" style="display: none;" aria-labelledby="resultsSectionTitle">
<h2 id="resultsSectionTitle" class="sr-only">Extraction Results</h2>
<div class="results-header">
<div class="results-meta">
<h3 class="results-title" id="pageTitleDisplay">Extracted Content</h3>
<span class="results-source" id="sourceUrlDisplay"></span>
</div>
<!-- Stats Bar -->
<div class="stats-row">
<div class="stat-item" title="Blocks classified as main content vs total blocks">
<span class="stat-label">Kept Blocks</span>
<span class="stat-value" id="statKeptBlocks">0 / 0</span>
</div>
<div class="stat-item" title="Percentage of content blocks retained">
<span class="stat-label">Retention</span>
<span class="stat-value" id="statRetention">0%</span>
</div>
<div class="stat-item" title="Total sequence tokens evaluated in WebGPU">
<span class="stat-label">Tokens</span>
<span class="stat-value" id="statTokens">0</span>
</div>
<div class="stat-item" title="Neural forward pass latency">
<span class="stat-label" id="statTimeLabel">WebGPU Time</span>
<span class="stat-value stat-highlight" id="statGpuTime">0 ms</span>
</div>
</div>
</div>
<!-- Output Tabs & Action Bar -->
<div class="output-toolbar">
<div class="output-tabs" role="tablist">
<button type="button" role="tab" aria-selected="true" class="output-tab active" data-tab="preview">
<span>Reader Preview</span>
</button>
<button type="button" role="tab" aria-selected="false" class="output-tab" data-tab="markdown">
<span>Markdown</span>
</button>
<button type="button" role="tab" aria-selected="false" class="output-tab" data-tab="html">
<span>Clean HTML</span>
</button>
<button type="button" role="tab" aria-selected="false" class="output-tab" data-tab="blocks">
<span>Block Inspector</span>
</button>
</div>
<div class="output-actions">
<button type="button" class="btn btn-secondary btn-sm" id="copyBtn" title="Copy to clipboard">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
<span id="copyBtnText">Copy</span>
</button>
<button type="button" class="btn btn-secondary btn-sm" id="downloadMdBtn" title="Download clean .md file">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
<span>Export .md</span>
</button>
</div>
</div>
<!-- Output Views -->
<div class="output-content">
<!-- Reader View -->
<div id="tabViewPreview" class="output-view active">
<article class="reader-view prose" id="readerContent"></article>
</div>
<!-- Markdown View -->
<div id="tabViewMarkdown" class="output-view" style="display: none;">
<pre class="code-view"><code id="markdownContent"></code></pre>
</div>
<!-- Clean HTML View -->
<div id="tabViewHtml" class="output-view" style="display: none;">
<pre class="code-view"><code id="htmlContent"></code></pre>
</div>
<!-- Block Inspector View -->
<div id="tabViewBlocks" class="output-view" style="display: none;">
<div class="block-inspector-list" id="blockInspectorList"></div>
</div>
</div>
</section>
</main>
<!-- Footer -->
<footer class="app-footer">
<div class="footer-content">
<p>Pulpie WebGPU · Powered by <a href="https://github.com/feyninc/pulpie" target="_blank" rel="noopener noreferrer">Feyn Pulpie</a> & <a href="https://huggingface.co/sizzlebop/pulpie-orange-small-onnx" target="_blank" rel="noopener noreferrer">sizzlebop/pulpie-orange-small-onnx</a></p>
<p class="footer-sub">Running 100% in your browser on WebGPU. No server GPU required.</p>
</div>
</footer>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>