rspace-online/modules/rnotes/landing.ts

434 lines
19 KiB
TypeScript

/**
* Notes module landing page — static HTML, no React.
*/
export function renderLanding(): string {
return `
<!-- Hero -->
<div class="rl-hero">
<span class="rl-tagline">rNotes</span>
<h1 class="rl-heading" style="background:linear-gradient(135deg,#f59e0b,#f97316);-webkit-background-clip:text;background-clip:text">(You)rNotes, your thoughts unbound.</h1>
<p class="rl-subtitle" style="background:linear-gradient(135deg,#f59e0b,#f97316);-webkit-background-clip:text;background-clip:text">Capture Everything, Find Anything, and Share your Insights</p>
<p class="rl-subtext">
Notes, clips, voice recordings, and live transcription &mdash; all in one place.
Speak and watch your words appear in real time, or drop in audio and video files to transcribe offline.
</p>
<div class="rl-cta-row">
<a href="https://demo.rspace.online/rnotes" class="rl-cta-primary" id="ml-primary">Open Notebook</a>
<a href="#transcription-demo" class="rl-cta-secondary">Transcribe</a>
<a href="#extension-download" class="rl-cta-secondary">Get Extension</a>
</div>
<p style="font-size:0.82rem;margin-top:0.5rem">
<a href="#" onclick="document.querySelector('folk-notes-app')?.startTour?.();window.__rspaceHideInfo?.();return false" style="color:var(--rs-primary,#06b6d4);text-decoration:none">
Start Guided Tour &rarr;
</a>
</p>
</div>
<!-- Live Transcription Demo -->
<section class="rl-section" id="transcription-demo">
<div class="rl-container">
<h2 class="rl-heading" style="text-align:center">Live Transcription Demo</h2>
<p class="rl-subtext" style="text-align:center">Try it right here &mdash; click the mic and start speaking.</p>
<div style="max-width:640px;margin:2rem auto;background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.08);border-radius:1rem;padding:1.5rem;position:relative">
<!-- Unsupported fallback (hidden by default, shown via JS) -->
<div id="transcription-unsupported" style="display:none;text-align:center;padding:2rem 1rem;color:#94a3b8">
<div style="font-size:2rem;margin-bottom:0.75rem">&#9888;&#65039;</div>
<p style="margin:0 0 0.5rem">Live transcription requires <strong>Chrome</strong> or <strong>Edge</strong> (Web Speech API).</p>
<p style="margin:0;font-size:0.85rem;color:#64748b">Try opening this page in a Chromium-based browser to test the demo.</p>
</div>
<!-- Demo UI (hidden if unsupported) -->
<div id="transcription-ui">
<!-- Controls -->
<div style="display:flex;align-items:center;justify-content:center;gap:1rem;margin-bottom:1.25rem">
<button id="mic-btn" style="width:56px;height:56px;border-radius:50%;border:2px solid rgba(245,158,11,0.4);background:rgba(245,158,11,0.1);color:#f59e0b;font-size:1.5rem;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all 0.2s" title="Start transcription">
&#127908;
</button>
<div style="text-align:left">
<div id="mic-status" style="font-size:0.9rem;color:#94a3b8">Click mic to start</div>
<div id="mic-timer" style="font-size:0.75rem;color:#64748b;font-variant-numeric:tabular-nums">00:00</div>
</div>
<div id="live-indicator" style="display:none;background:rgba(239,68,68,0.15);color:#ef4444;font-size:0.7rem;font-weight:600;padding:0.2rem 0.6rem;border-radius:9999px;text-transform:uppercase;letter-spacing:0.05em">
&#9679; Live
</div>
</div>
<!-- Transcript area -->
<div id="transcript-area" style="min-height:120px;max-height:240px;overflow-y:auto;background:rgba(0,0,0,0.2);border-radius:0.5rem;padding:1rem;font-size:0.9rem;line-height:1.6;color:#e2e8f0">
<span style="color:#64748b;font-style:italic">Your transcript will appear here&hellip;</span>
</div>
</div>
<!-- Capability badges -->
<div style="display:flex;flex-wrap:wrap;gap:0.5rem;justify-content:center;margin-top:1.25rem">
<span class="rl-badge" style="background:rgba(34,197,94,0.15);color:#22c55e">&#9679; Live streaming</span>
<span class="rl-badge" style="background:rgba(59,130,246,0.15);color:#3b82f6">&#127925; Audio file upload</span>
<span class="rl-badge" style="background:rgba(168,85,247,0.15);color:#a855f7">&#127909; Video transcription</span>
<span class="rl-badge" style="background:rgba(245,158,11,0.15);color:#f59e0b">&#128268; Offline (Parakeet.js)</span>
</div>
</div>
</div>
</section>
<!-- Transcription Demo Script -->
<script>
(function() {
var SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
var ui = document.getElementById('transcription-ui');
var unsupported = document.getElementById('transcription-unsupported');
if (!SpeechRecognition) {
if (ui) ui.style.display = 'none';
if (unsupported) unsupported.style.display = 'block';
return;
}
var recognition = new SpeechRecognition();
recognition.continuous = true;
recognition.interimResults = true;
recognition.lang = 'en-US';
var micBtn = document.getElementById('mic-btn');
var micStatus = document.getElementById('mic-status');
var micTimer = document.getElementById('mic-timer');
var liveIndicator = document.getElementById('live-indicator');
var transcriptArea = document.getElementById('transcript-area');
var isListening = false;
var timerInterval = null;
var seconds = 0;
var finalTranscript = '';
function formatTime(s) {
var m = Math.floor(s / 60);
var sec = s % 60;
return (m < 10 ? '0' : '') + m + ':' + (sec < 10 ? '0' : '') + sec;
}
function startTimer() {
seconds = 0;
micTimer.textContent = '00:00';
timerInterval = setInterval(function() {
seconds++;
micTimer.textContent = formatTime(seconds);
}, 1000);
}
function stopTimer() {
if (timerInterval) { clearInterval(timerInterval); timerInterval = null; }
}
micBtn.addEventListener('click', function() {
if (!isListening) {
finalTranscript = '';
transcriptArea.innerHTML = '';
recognition.start();
} else {
recognition.stop();
}
});
recognition.onstart = function() {
isListening = true;
micBtn.style.background = 'rgba(239,68,68,0.2)';
micBtn.style.borderColor = '#ef4444';
micBtn.style.color = '#ef4444';
micBtn.title = 'Stop transcription';
micStatus.textContent = 'Listening...';
micStatus.style.color = '#ef4444';
liveIndicator.style.display = 'block';
startTimer();
};
recognition.onend = function() {
isListening = false;
micBtn.style.background = 'rgba(245,158,11,0.1)';
micBtn.style.borderColor = 'rgba(245,158,11,0.4)';
micBtn.style.color = '#f59e0b';
micBtn.title = 'Start transcription';
micStatus.textContent = 'Click mic to start';
micStatus.style.color = '#94a3b8';
liveIndicator.style.display = 'none';
stopTimer();
};
recognition.onresult = function(event) {
var interim = '';
for (var i = event.resultIndex; i < event.results.length; i++) {
var transcript = event.results[i][0].transcript;
if (event.results[i].isFinal) {
finalTranscript += transcript + ' ';
} else {
interim += transcript;
}
}
transcriptArea.innerHTML = finalTranscript +
(interim ? '<span style="color:#94a3b8">' + interim + '</span>' : '');
transcriptArea.scrollTop = transcriptArea.scrollHeight;
};
recognition.onerror = function(event) {
if (event.error === 'not-allowed') {
micStatus.textContent = 'Microphone access denied';
micStatus.style.color = '#ef4444';
}
};
})();
</script>
<!-- Features -->
<section class="rl-section">
<div class="rl-container">
<h2 class="rl-heading" style="text-align:center">What rNotes Handles</h2>
<div class="rl-grid-3">
<div class="rl-card rl-card--center">
<div class="rl-icon-box">&#128221;</div>
<h3>Rich Text Notes</h3>
<p>Write with a full TipTap editor &mdash; formatting, code blocks, checklists, and embeds. Dual-format storage keeps Markdown portable.</p>
</div>
<div class="rl-card rl-card--center">
<div class="rl-icon-box">&#127908;</div>
<h3>Voice &amp; Transcription</h3>
<p>Record voice notes with live transcription via Web Speech API. Drop in audio or video files and get full transcripts &mdash; all in the browser.</p>
</div>
<div class="rl-card rl-card--center">
<div class="rl-icon-box">&#127991;</div>
<h3>Tagging &amp; Organization</h3>
<p>Tag freely, organize into notebooks, and search everything. Filtered views surface the right cards at the right time.</p>
</div>
</div>
</div>
</section>
<!-- Chrome Extension -->
<section class="rl-section rl-section--alt" id="extension-download">
<div class="rl-container">
<h2 class="rl-heading" style="text-align:center">Chrome Extension</h2>
<p class="rl-subtext" style="text-align:center">Clip pages, record voice notes, and transcribe &mdash; right from the toolbar.</p>
<div class="rl-grid-2" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:1.5rem;max-width:860px;margin:2rem auto">
<div class="rl-card rl-card--center">
<div class="rl-icon-box">&#128203;</div>
<h3>Web Clipper</h3>
<p>Save any page as a note with one click &mdash; article text, selection, or full HTML.</p>
</div>
<div class="rl-card rl-card--center">
<div class="rl-icon-box">&#127908;</div>
<h3>Voice Recording</h3>
<p>Press <kbd style="background:rgba(255,255,255,0.1);padding:0.1rem 0.4rem;border-radius:4px;font-size:0.8rem">Ctrl+Shift+V</kbd> to start recording and transcribing from any tab.</p>
</div>
<div class="rl-card rl-card--center">
<div class="rl-icon-box">&#128275;</div>
<h3>Article Unlock</h3>
<p>Bypass soft paywalls by fetching archived versions &mdash; read the article, then save it to your notebook.</p>
</div>
<div class="rl-card rl-card--center">
<div class="rl-icon-box">&#128268;</div>
<h3>Offline Transcription</h3>
<p>Parakeet.js runs entirely in-browser &mdash; your audio never leaves the device.</p>
</div>
</div>
<div style="text-align:center;margin-top:1.5rem">
<a href="/rnotes/extension/download" class="rl-cta-primary" style="display:inline-flex;align-items:center;gap:0.5rem">
&#11015; Download Extension
</a>
<p style="margin-top:0.75rem;font-size:0.8rem;color:#64748b">
Unzip, then load unpacked at <code style="font-size:0.75rem;color:#94a3b8">chrome://extensions</code>
</p>
</div>
</div>
</section>
<!-- How It Works -->
<section class="rl-section">
<div class="rl-container">
<h2 class="rl-heading" style="text-align:center">How It Works</h2>
<div class="rl-grid-4" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:1.5rem">
<div class="rl-step">
<div class="rl-step__num">1</div>
<h3>Live Transcribe</h3>
<p>Speak and watch words appear in real time via the Web Speech API. No uploads, no waiting.</p>
</div>
<div class="rl-step">
<div class="rl-step__num">2</div>
<h3>Audio &amp; Video</h3>
<p>Drop files and get full transcripts via Parakeet, running entirely in-browser. Supports MP3, WAV, MP4, and more.</p>
</div>
<div class="rl-step">
<div class="rl-step__num">3</div>
<h3>Notebooks &amp; Tags</h3>
<p>Organize transcripts alongside notes, clips, code, and files. Tag, search, and filter across everything.</p>
</div>
<div class="rl-step">
<div class="rl-step__num">4</div>
<h3>Private &amp; Offline</h3>
<p>Parakeet.js runs in-browser &mdash; audio never leaves your device. Works offline once the model is cached.</p>
</div>
</div>
</div>
</section>
<!-- Memory Cards -->
<section class="rl-section rl-section--alt">
<div class="rl-container">
<h2 class="rl-heading" style="text-align:center">Memory Cards</h2>
<p class="rl-subtext" style="text-align:center">
Every note is a Memory Card &mdash; a typed, structured unit of knowledge with hierarchy,
properties, and attachments. Designed for round-trip interoperability with Logseq.
</p>
<div class="rl-grid-3">
<!-- 7 Card Types -->
<div class="rl-card">
<div class="rl-icon-box">&#127991;</div>
<h3>7 Card Types</h3>
<div style="display:flex;flex-wrap:wrap;gap:0.35rem;margin-bottom:0.75rem">
<span class="rl-badge" style="background:rgba(245,158,11,0.2);color:#f59e0b">note</span>
<span class="rl-badge" style="background:rgba(59,130,246,0.2);color:#3b82f6">link</span>
<span class="rl-badge" style="background:rgba(34,197,94,0.2);color:#22c55e">task</span>
<span class="rl-badge" style="background:rgba(234,179,8,0.2);color:#eab308">idea</span>
<span class="rl-badge" style="background:rgba(168,85,247,0.2);color:#a855f7">person</span>
<span class="rl-badge" style="background:rgba(236,72,153,0.2);color:#ec4899">reference</span>
<span class="rl-badge" style="background:rgba(100,116,139,0.2);color:#94a3b8">file</span>
</div>
<p>Each card type has distinct styling and behavior. Typed notes surface in filtered views and canvas visualizations.</p>
</div>
<!-- Hierarchy & Properties -->
<div class="rl-card">
<div class="rl-icon-box">&#128194;</div>
<h3>Hierarchy &amp; Properties</h3>
<p>
Nest cards under parents to build knowledge trees. Add structured
<code style="font-size:0.8rem;color:rgba(245,158,11,0.8)">key:: value</code>
properties &mdash; compatible with Logseq's property syntax.
</p>
<div style="margin-top:0.5rem;font-family:monospace;font-size:0.75rem;color:#64748b;line-height:1.7">
<div><span style="color:#94a3b8">type::</span> idea</div>
<div><span style="color:#94a3b8">status::</span> doing</div>
<div><span style="color:#94a3b8">tags::</span> #research, #web3</div>
</div>
</div>
<!-- Data Source Integrations -->
<div class="rl-card">
<div class="rl-icon-box">&#128260;</div>
<h3>Import &amp; Export</h3>
<p>
Bring your notes from <strong>Logseq</strong>, <strong>Obsidian</strong>,
<strong>Notion</strong>, and <strong>Google Docs</strong>.
Export back to any format anytime &mdash; your data, your choice.
</p>
<div style="display:flex;flex-wrap:wrap;gap:0.35rem;margin-top:0.5rem">
<span class="rl-badge" style="background:rgba(34,197,94,0.2);color:#22c55e">Logseq</span>
<span class="rl-badge" style="background:rgba(139,92,246,0.2);color:#8b5cf6">Obsidian</span>
<span class="rl-badge" style="background:rgba(59,130,246,0.2);color:#3b82f6">Notion</span>
<span class="rl-badge" style="background:rgba(245,158,11,0.2);color:#f59e0b">Google Docs</span>
</div>
</div>
<!-- Dual Format Storage -->
<div class="rl-card">
<div class="rl-icon-box">&#128196;</div>
<h3>Dual Format Storage</h3>
<p>
Every card stores rich TipTap JSON for editing and portable Markdown for search, export, and interoperability.
Write once, read anywhere.
</p>
</div>
<!-- Structured Attachments -->
<div class="rl-card">
<div class="rl-icon-box">&#128206;</div>
<h3>Structured Attachments</h3>
<p>
Attach images, PDFs, audio, and files to any card with roles (primary, preview, supporting) and captions.
Thumbnails render inline.
</p>
</div>
<!-- FUN Model -->
<div class="rl-card">
<div class="rl-icon-box">&#10084;</div>
<h3>FUN, Not CRUD</h3>
<p>
<span style="color:#f59e0b;font-weight:600">F</span>orget,
<span style="color:#f59e0b;font-weight:600">U</span>pdate,
<span style="color:#f59e0b;font-weight:600">N</span>ew &mdash;
nothing is permanently destroyed. Forgotten cards are archived and can be remembered at any time.
</p>
</div>
</div>
</div>
</section>
<!-- Built on Open Source -->
<section class="rl-section">
<div class="rl-container">
<h2 class="rl-heading" style="text-align:center">Built on Open Source</h2>
<p class="rl-subtext" style="text-align:center">The libraries and tools that power rNotes.</p>
<div class="rl-grid-4" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:1.5rem">
<div class="rl-card rl-card--center">
<h3>Automerge</h3>
<p>Local-first CRDT for conflict-free real-time collaboration. Your notes sync across devices without a central server.</p>
</div>
<div class="rl-card rl-card--center">
<h3>Web Speech API</h3>
<p>Browser-native live transcription &mdash; speak and watch your words appear in real time.</p>
</div>
<div class="rl-card rl-card--center">
<h3>Parakeet.js</h3>
<p>NVIDIA&rsquo;s in-browser speech recognition. Transcribe audio and video files offline &mdash; nothing leaves your device.</p>
</div>
<div class="rl-card rl-card--center">
<h3>Hono</h3>
<p>Ultra-fast, lightweight API framework powering the rNotes backend.</p>
</div>
</div>
</div>
</section>
<!-- Your Data, Protected -->
<section class="rl-section rl-section--alt">
<div class="rl-container" style="text-align:center">
<h2 class="rl-heading">Your Data, Protected</h2>
<p class="rl-subtext">How rNotes keeps your information safe.</p>
<div class="rl-grid-3">
<div class="rl-card rl-card--center">
<div class="rl-icon-box">&#128274;</div>
<h3>End-to-End Encryption</h3>
<span class="rl-badge">Coming Soon</span>
<p>All content encrypted before it leaves your device. Not even the server can read it.</p>
</div>
<div class="rl-card rl-card--center">
<div class="rl-icon-box">&#128373;</div>
<h3>Zero-Knowledge Architecture</h3>
<span class="rl-badge">Coming Soon</span>
<p>The server processes your requests without ever seeing your data in the clear.</p>
</div>
<div class="rl-card rl-card--center">
<div class="rl-icon-box">&#127968;</div>
<h3>Self-Hosted</h3>
<p>Run on your own infrastructure. Your server, your rules, your data.</p>
</div>
</div>
</div>
</section>
<!-- CTA -->
<section class="rl-section">
<div class="rl-container" style="text-align:center">
<h2 class="rl-heading">(You)rNotes, your thoughts unbound.</h2>
<p class="rl-subtext">Try the demo or create a space to get started.</p>
<div class="rl-cta-row">
<a href="https://demo.rspace.online/rnotes" class="rl-cta-primary">Open Notebook</a>
<a href="#transcription-demo" class="rl-cta-secondary">Transcribe</a>
<a href="/create-space" class="rl-cta-secondary">Create a Space</a>
</div>
</div>
</section>
<div class="rl-back">
<a href="/">&larr; Back to rSpace</a>
</div>
`;
}