200 lines
9.0 KiB
HTML
200 lines
9.0 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Thread Ripper - Stitch Threads into Single Posts</title>
|
|
<link rel="stylesheet" href="style.css">
|
|
<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">
|
|
<!-- Markdown parser for preview -->
|
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
</head>
|
|
<body>
|
|
<div class="app-layout">
|
|
<!-- Main Content Area -->
|
|
<main class="main-content">
|
|
<header class="app-header">
|
|
<div class="logo-group">
|
|
<div class="logo-icon">
|
|
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.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 d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<h1>Thread Ripper</h1>
|
|
<p class="subtitle">Extract and stitch multi-part Threads into a single cohesive post</p>
|
|
</div>
|
|
</div>
|
|
<div class="header-badges">
|
|
<span class="badge docker-badge">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M4.5 10.5h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-6-3h2v2h-2zm3 0h2v2h-2zm3 0h2v2h-2zm-3-3h2v2h-2zm12 5.5c-.3-.2-1.5-.7-2.3-.2-.2-.8-.7-1.4-1.4-1.8l-.5-.3-.4.4c-.6.7-.8 1.6-.7 2.4-.9.2-1.8.8-2.2 1.6H2.5c-.8 0-1.5.7-1.5 1.5 0 3.3 2.1 6.3 5.4 7.4 3.7 1.3 7.8.4 10.7-2.2 2.3-2.1 3.5-5.2 3.4-8.3-.1-.3-.3-.5-.5-.5z"/>
|
|
</svg>
|
|
Docker Container
|
|
</span>
|
|
<button id="toggle-history-btn" class="btn btn-ghost" title="Toggle Saved Library">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M12 8v4l3 3"/>
|
|
<circle cx="12" cy="12" r="9"/>
|
|
</svg>
|
|
Library (<span id="history-count">0</span>)
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Input Form Card -->
|
|
<section class="card input-card">
|
|
<div class="url-input-container">
|
|
<div class="input-prefix">
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<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 d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/>
|
|
</svg>
|
|
</div>
|
|
<input
|
|
type="url"
|
|
id="thread-url"
|
|
placeholder="Paste any Threads link (e.g. https://www.threads.com/share/BASy90mpUT/ or https://www.threads.net/@user/post/...)"
|
|
autocomplete="off"
|
|
spellcheck="false"
|
|
/>
|
|
<button id="paste-btn" class="btn btn-secondary btn-sm" title="Paste from clipboard">Paste</button>
|
|
<button id="rip-btn" class="btn btn-primary">
|
|
<span class="btn-text">Rip Thread</span>
|
|
<span class="btn-spinner hidden"></span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="input-controls">
|
|
<div class="sample-links">
|
|
<span class="sample-label">Try example:</span>
|
|
<button class="pill-btn" id="sample-mall-btn" data-url="https://www.threads.com/share/BASy90mpUT/">
|
|
GenX Mall Retirement Community (@bethmcmilliandesigns)
|
|
</button>
|
|
</div>
|
|
|
|
<div class="mode-selector">
|
|
<label class="radio-label">
|
|
<input type="radio" name="rip-mode" value="author_only" checked />
|
|
<span>Author Thread Only</span>
|
|
</label>
|
|
<label class="radio-label">
|
|
<input type="radio" name="rip-mode" value="all_replies" />
|
|
<span>Include Community Replies</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Status Banner -->
|
|
<div id="status-banner" class="status-banner hidden">
|
|
<div class="status-spinner"></div>
|
|
<div class="status-content">
|
|
<p class="status-title" id="status-title">Ripping thread...</p>
|
|
<p class="status-desc" id="status-desc">Launching headless browser, following redirect, and collecting chained posts...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Result Card -->
|
|
<section id="result-section" class="result-section hidden">
|
|
<!-- Thread Info Header -->
|
|
<div class="card meta-card">
|
|
<div class="author-profile">
|
|
<img id="res-avatar" class="author-avatar" src="" alt="Avatar" />
|
|
<div class="author-meta">
|
|
<h2 id="res-author-name">Author Name</h2>
|
|
<a id="res-author-link" href="#" target="_blank" class="author-handle">@username</a>
|
|
</div>
|
|
</div>
|
|
<div class="thread-stats">
|
|
<div class="stat-box">
|
|
<span class="stat-number" id="res-segment-count">0</span>
|
|
<span class="stat-label">Segments Merged</span>
|
|
</div>
|
|
<div class="stat-box">
|
|
<span class="stat-number" id="res-word-count">0</span>
|
|
<span class="stat-label">Total Words</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Action Bar -->
|
|
<div class="action-bar">
|
|
<div class="tab-group">
|
|
<button class="tab-btn active" data-tab="reader">Reader View</button>
|
|
<button class="tab-btn" data-tab="markdown">Markdown</button>
|
|
<button class="tab-btn" data-tab="segments">Segments (<span id="tab-segment-count">0</span>)</button>
|
|
</div>
|
|
|
|
<div class="export-group">
|
|
<button id="copy-md-btn" class="btn btn-secondary btn-sm" title="Copy as Markdown">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
|
Copy MD
|
|
</button>
|
|
<button id="copy-txt-btn" class="btn btn-secondary btn-sm" title="Copy Plain Text">
|
|
Copy Text
|
|
</button>
|
|
<div class="dropdown">
|
|
<button id="download-dropdown-btn" class="btn btn-primary btn-sm">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
|
Download ▼
|
|
</button>
|
|
<div class="dropdown-menu" id="download-menu">
|
|
<a href="#" id="dl-md" class="dropdown-item">Download .md (Markdown)</a>
|
|
<a href="#" id="dl-txt" class="dropdown-item">Download .txt (Plain Text)</a>
|
|
<a href="#" id="dl-html" class="dropdown-item">Download .html (Standalone Page)</a>
|
|
<a href="#" id="dl-json" class="dropdown-item">Download .json (Raw Data)</a>
|
|
</div>
|
|
</div>
|
|
<button id="print-btn" class="btn btn-ghost btn-sm" title="Print or save as PDF">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 6 2 18 2 18 9"/><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"/><rect x="6" y="14" width="12" height="8"/></svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tab Views -->
|
|
<div class="card view-card">
|
|
<!-- Reader View -->
|
|
<div id="view-reader" class="tab-view active">
|
|
<div id="reader-content" class="reader-typography"></div>
|
|
</div>
|
|
|
|
<!-- Markdown View -->
|
|
<div id="view-markdown" class="tab-view hidden">
|
|
<pre><code id="markdown-content"></code></pre>
|
|
</div>
|
|
|
|
<!-- Segments View -->
|
|
<div id="view-segments" class="tab-view hidden">
|
|
<div id="segments-container" class="segments-list"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<!-- Slide-out Library Sidebar -->
|
|
<aside id="library-sidebar" class="library-sidebar">
|
|
<div class="sidebar-header">
|
|
<h3>Saved Threads</h3>
|
|
<button id="close-library-btn" class="btn btn-ghost btn-icon">✕</button>
|
|
</div>
|
|
<div class="sidebar-search">
|
|
<input type="text" id="library-search" placeholder="Search saved threads..." />
|
|
</div>
|
|
<div class="sidebar-list" id="library-list">
|
|
<!-- Rendered dynamically -->
|
|
<p class="empty-state">No threads ripped yet.</p>
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
|
|
<!-- Toast Notification -->
|
|
<div id="toast" class="toast hidden"></div>
|
|
|
|
<script src="app.js"></script>
|
|
</body>
|
|
</html>
|