1<!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
1<script>// Platform detection â the single source of truth for "what is this visitor on". 2// 3// Inlined in MarketingLayout's <head> (a classic script, like analytics.js), so 4// it runs before the first paint and records its answer on <html>: 5// 6// data-device="mobile" | "desktop" phones and tablets can't run a desktop binary 7// data-os="mac" | "windows" | "linux" set only for a desktop OS it can identify 8// 9// CSS can then lay a page out for the visitor's device in the first frame. On 10// /download/ the "kindling is a desktop app" notice used to be revealed by a 11// module script after paint, which pushed the platform picker down on every 12// phone â a layout shift PageSpeed measured at up to 0.15 once first paint got 13// faster. Scripts read the answer through detectPlatform() in platform.ts. 14// 15// Detection used to exist twice (SmartDownloadButton and an inline script on 16// /download/); they disagreed, so every fix had to be made twice. Keep it here. 17(function () { 18 var root = document.documentElement; 19 var ua = navigator.userAgent; 20 var legacyPlatform = (navigator.platform || '').toLowerCase(); 21 22 function record(os, mobile) { 23 root.dataset.device = mobile ? 'mobile' : 'desktop'; 24 if (os) root.dataset.os = os; 25 } 26 27 // iPadOS 13+ browses "desktop-class": its userAgent says Macintosh and 28 // navigator.platform is MacIntel, so a naive check calls it a Mac and 29 // cheerfully offers a .dmg. maxTouchPoints separates them â a real Mac 30 // reports 0 (or 1 with a touch bar), an iPad reports 5. 31 var isIPadOS = legacyPlatform.indexOf('mac') !== -1 && (navigator.maxTouchPoints || 0) > 1; 32 var isMobileUA = /Android|iPhone|iPad|iPod|webOS|BlackBerry|IEMobile|Opera Mini/i.test(ua); 33 if (isMobileUA || isIPadOS) return record(null, true); 34 35 // Chromium User-Agent Client Hints, where available â more reliable than 36 // sniffing the UA string, and it reports mobile directly. 37 var uad = navigator.userAgentData; 38 if (uad) { 39 if (uad.mobile) return record(null, true); 40 var p = (uad.platform || '').toLowerCase(); 41 if (p.indexOf('mac') !== -1) return record('mac', false); 42 if (p.indexOf('win') !== -1) return record('windows', false); 43 if (p.indexOf('linux') !== -1 || p.indexOf('chromeos') !== -1) return record('linux', false); 44 } 45 46 // Fallback: classic userAgent string. 47 if (/Macintosh|Mac OS X/i.test(ua)) return record('mac', false); 48 if (/Windows/i.test(ua)) return record('windows', false); 49 if (/Linux/i.test(ua)) return record('linux', false); 50 return record(null, false); 51})(); 52</script>
52<script>/* kindling Press website early behaviors, v0.1.0. 53 Inline this in <head>, before any stylesheet paints. It is optional; without 54 it website.js behaves exactly as before. 55 56 Why: at container widths at or below 820px the navigation collapses behind 57 its menu button, but website.css only collapses it once website.js has bound 58 the button and set data-pw-ready. When that script arrives after first paint 59 â a slow phone, a large page bundle â the page paints with every link 60 expanded and then snaps shut, moving everything below the header. 61 kindlingwriter.com measured that layout shift at 0.165 on /download/. 62 63 What it does: 64 1. Marks <html> with data-pw-js before paint. website.css collapses the nav 65 on that flag as well as on data-pw-ready, so the first frame is already 66 collapsed. Without JavaScript the flag is never set and every link stays 67 visible, as the contract requires. 68 2. Makes the menu button work until website.js binds it: a delegated click 69 toggles data-open and aria-expanded. Once website.js sets data-pw-ready 70 on the nav, this handler steps aside and the full behaviour (focus, 71 Escape, outside click) takes over. If website.js never loads, the menu 72 still opens and closes. 73 74 No requests, analytics or storage. */ 75(function () { 76 var root = document.documentElement; 77 root.setAttribute('data-pw-js', ''); 78 document.addEventListener('click', function (event) { 79 var toggle = event.target instanceof Element ? event.target.closest('[data-pw-menu]') : null; 80 var nav = toggle ? toggle.closest('[data-pw-nav]') : null; 81 if (!nav || nav.hasAttribute('data-pw-ready')) return; 82 var open = !nav.hasAttribute('data-open'); 83 if (open) nav.setAttribute('data-open', ''); else nav.removeAttribute('data-open'); 84 toggle.setAttribute('aria-expanded', String(open)); 85 }); 86})(); 87</script>
87<title>kindling Writer Features â Outlining, Drafting & Review</title><meta name="description" content="Writing software for plotters and pantsers. Rolling outlines, scene beats, editorial review, writing goals, and character references. Free & open source."><link rel="canonical" href="https://kindlingwriter.com/features/"><!-- Open Graph --><meta property="og:title" content="kindling Writer Features â Outlining, Drafting & Review"><meta property="og:description" content="Writing software for plotters and pantsers. Rolling outlines, scene beats, editorial review, writing goals, and character references. Free & open source."><meta property="og:type" content="website"><meta property="og:url" content="https://kindlingwriter.com/features/"><meta property="og:image" content="https://kindlingwriter.com/og-image.png"><meta property="og:image:width" content="1200"><meta property="og:image:height" content="630"><meta property="og:site_name" content="kindling"><!-- Twitter Card. @KindlingWriter is the account handle, not the brand name: 88 it is case-sensitive and stays as registered. --><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content="@KindlingWriter"><meta name="twitter:title" content="kindling Writer Features â Outlining, Drafting & Review"><meta name="twitter:description" content="Writing software for plotters and pantsers. Rolling outlines, scene beats, editorial review, writing goals, and character references. Free & open source."><meta name="twitter:image" content="https://kindlingwriter.com/og-image.png"><!-- Favicon / brand marks. Synced from Press; see src/vendor/press/MANIFEST.json. --><link rel="icon" type="image/svg+xml" href="/favicon.svg"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16.png"><link rel="icon" href="/favicon.ico" sizes="any"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="manifest" href="/site.webmanifest"><link rel="alternate" type="application/rss+xml" title="kindling Blog" href="/blog/rss.xml"><meta name="theme-color" content="#B5532E"><link rel="preload" href="/_astro/Fraunces-Latin.BUWZdjub.woff2" as="font" type="font/woff2" crossorigin><link rel="preload" href="/_astro/Inter-Latin.CkIvHIc3.woff2" as="font" type="font/woff2" crossorigin><!-- GA4 -->
88<script>// Shared by marketing pages and Starlight. Collection is production-only. 89(() => { 90 if (window.location.hostname !== 'kindlingwriter.com' || window.location.protocol !== 'https:') return; 91 if (window.kindlingAnalyticsLoaded) return; 92 window.kindlingAnalyticsLoaded = true; 93 94 window.dataLayer = window.dataLayer || []; 95 window.gtag = function () { window.dataLayer.push(arguments); }; 96 window.gtag('js', new Date()); 97 window.gtag('config', 'G-VJQ72G87FE', { 98 allow_google_signals: false, 99 allow_ad_personalization_signals: false, 100 }); 101 const tag = document.createElement('script'); 102 tag.async = true; 103 tag.src = 'https://www.googletagmanager.com/gtag/js?id=G-VJQ72G87FE'; 104 document.head.appendChild(tag); 105 106 // Delegation includes Markdown links, docs navigation, and hydrated islands. 107 // Direct installer actions are handled separately by the download helper. 108 document.addEventListener('click', (event) => { 109 const link = event.target instanceof Element ? event.target.closest('a[href]') : null; 110 if (!link) return; 111 const url = new URL(link.href, window.location.href); 112 if (url.origin !== window.location.origin || url.pathname.replace(/\/$/, '') !== '/download') return; 113 const location = link.dataset.ctaLocation || 114 (window.location.pathname.startsWith('/docs/') ? 'docs' : 115 link.closest('nav') ? 'navbar' : link.closest('footer') ? 'footer' : 116 link.closest('.article-content') ? 'blog_inline' : 'page_inline'); 117 window.gtag('event', 'download_cta_click', { cta_location: location }); 118 }); 119 120 // The home-page demo's beats are native <details>. Record a visitor opening 121 // one, from the click on its summary: `toggle` does not bubble, and it also 122 // fires for a beat that starts open, which would count the initial state. 123 // A click on a closed beat's summary is always the visitor opening it 124 // (Enter and Space on a summary dispatch the same click). 125 document.addEventListener('click', (event) => { 126 const summary = event.target instanceof Element ? event.target.closest('summary') : null; 127 const beat = summary?.parentElement; 128 if (!(beat instanceof HTMLDetailsElement) || !/^sample-beat-\d+$/.test(beat.id) || beat.open) return; 129 window.gtag('event', 'demo_interaction', { 130 demo_id: 'outline_to_draft', 131 interaction_type: 'open_beat', 132 beat_id: beat.id, 133 }); 134 }); 135 136 // The demo's scenes are one native radio group in its outline. `change` 137 // fires only when the visitor picks a different scene, never for the 138 // scene checked on load. 139 document.addEventListener('change', (event) => { 140 const choice = event.target; 141 if (!(choice instanceof HTMLInputElement) || choice.name !== 'sample-scene' || !choice.checked) return; 142 window.gtag('event', 'demo_interaction', { 143 demo_id: 'outline_to_draft', 144 interaction_type: 'select_scene', 145 scene_id: choice.value, 146 }); 147 }); 148 149 document.addEventListener('submit', (event) => { 150 const form = event.target; 151 if (!(form instanceof HTMLFormElement) || !form.matches('[data-newsletter-form]')) return; 152 // A submission is not provider-verified confirmation. Never send form values. 153 window.gtag('event', 'newsletter_submit', { form_id: form.dataset.newsletterForm }); 154 }); 155})(); 156</script>
156<!-- Structured Data -->
156<script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"SoftwareApplication","@id":"https://kindlingwriter.com/#software","name":"kindling Writer","description":"Free, open-source fiction writing software that keeps your outline visible while you draft. Built for plotters, pantsers, and every kind of fiction writer.","url":"https://kindlingwriter.com/","applicationCategory":"DesktopApplication","operatingSystem":"Windows, macOS, Linux","offers":{"@type":"Offer","price":"0","priceCurrency":"USD"},"softwareVersion":"1.3.0","license":"https://opensource.org/licenses/MIT","downloadUrl":"https://kindlingwriter.com/download/","screenshot":{"@type":"ImageObject","url":"https://kindlingwriter.com/_astro/beat-with-prose.eSJ8Cqfd.png","width":1408,"height":1126,"caption":"The kindling editor: a scene's beats beside its prose"},"author":{"@type":"Organization","@id":"https://kindlingwriter.com/#organization","name":"kindling","url":"https://kindlingwriter.com/","logo":{"@type":"ImageObject","url":"https://kindlingwriter.com/icon-512.png","width":512,"height":512}},"publisher":{"@type":"Organization","@id":"https://kindlingwriter.com/#organization","name":"kindling","url":"https://kindlingwriter.com/","logo":{"@type":"ImageObject","url":"https://kindlingwriter.com/icon-512.png","width":512,"height":512}},"featureList":["Rolling Outline Mode â progressive structure for plotters and plantsers","Blank projects â start from scratch without importing","Import outlines from Plottr, Scrivener, yWriter, Obsidian Longform, novelWriter, and Markdown","Expandable scene beats as writing prompts","Beat management â split, merge, reorder, and delete beats","Inline discovery notes to capture ideas mid-writing","Context-aware character and location references","Command palette for quick access to all actions","Export to DOCX, EPUB, HTML, plain text, Markdown, Scrivener, Obsidian Longform, and novelWriter","Custom export workspace with saved profiles, manuscript previews, chapter selection, and typography controls","Local-first â all data stays on your computer","Distraction-free writing interface","Automatic updates â never download manually again","Built with Rust and Tauri for fast performance","Session persistence â pick up where you left off","Press â an editorial design with warm paper surfaces and light and dark themes","Editorial review â local files, tracked suggestions, and threaded comments","Daily writing goals, session totals, streaks, and manuscript statistics","Previous scene context and project-wide find and replace","Copy references, custom fields, and tags between projects","Free and open source under MIT license"]}]}</script>
156<style>.product-figure:where(.astro-f5rbpj5f){min-width:0} 157</style> 158<link rel="stylesheet" href="/_astro/MarketingLayout.CCkBzMsW.css"> 159<style>.page-lede:where(.astro-mpbnit47){padding-top:var(--space-4xl);padding-bottom:var(--space-2xl)}.page-lede:where(.astro-mpbnit47) h1:where(.astro-mpbnit47){font-family:var(--font-display);font-size:var(--text-hero);font-weight:600;line-height:var(--leading-tight);letter-spacing:var(--tracking-tight);max-width:18ch;margin-bottom:var(--space-m)}.lede-standfirst:where(.astro-mpbnit47){font-family:var(--font-body);font-size:var(--text-body-lg);color:var(--color-text);line-height:var(--leading-relaxed);max-width:var(--measure);margin-bottom:var(--space-l)}.features-seq-section:where(.astro-mpbnit47){padding-block:var(--space-3xl)}.features-seq-section:where(.astro-mpbnit47) .pw-section-head:where(.astro-mpbnit47){margin-bottom:var(--space-l)}.feature-coda:where(.astro-mpbnit47){display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--space-2xl);padding-top:var(--space-3xl);margin-top:var(--space-3xl);border-top:1px solid var(--color-border)}.feature-coda:where(.astro-mpbnit47)>:where(.astro-mpbnit47){min-width:0}.feature-coda:where(.astro-mpbnit47) .pw-label:where(.astro-mpbnit47){margin-bottom:var(--space-2xs)}.feature-coda:where(.astro-mpbnit47) h3:where(.astro-mpbnit47){margin-bottom:var(--space-2xs)}.feature-coda:where(.astro-mpbnit47) .pw-problem:where(.astro-mpbnit47){font-weight:600}.feature-coda:where(.astro-mpbnit47) p:where(.astro-mpbnit47)+p:where(.astro-mpbnit47){margin-top:var(--space-s)}@media(max-width:768px){.feature-coda:where(.astro-mpbnit47){grid-template-columns:minmax(0,1fr);gap:var(--space-l);padding-top:var(--space-2xl);margin-top:var(--space-2xl)}}.release-link:where(.astro-mpbnit47){margin-top:var(--space-xl);max-width:var(--measure)}.flow-diagram:where(.astro-mpbnit47){display:flex;flex-direction:column;align-items:stretch;gap:4px;margin-top:var(--space-m)}.flow-node:where(.astro-mpbnit47){background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-l);padding:10px 16px;text-align:center}.flow-node-center:where(.astro-mpbnit47){border-color:var(--color-accent)}.flow-label:where(.astro-mpbnit47){font-family:var(--font-ui);font-weight:600;font-size:var(--text-small);display:block}.flow-formats:where(.astro-mpbnit47){font-family:var(--font-ui);font-size:var(--text-eyebrow);color:var(--color-text-muted)}.flow-arrow:where(.astro-mpbnit47){font-size:var(--text-body-lg);color:var(--color-text-muted);line-height:1;text-align:center}.spec-list:where(.astro-mpbnit47){display:grid;grid-template-columns:1fr 1fr;column-gap:var(--space-2xl)}.spec-item:where(.astro-mpbnit47){padding:var(--space-m) 0;border-top:1px solid var(--color-border)}.spec-item:where(.astro-mpbnit47) dt:where(.astro-mpbnit47){font-family:var(--font-display);font-size:var(--text-h3);font-weight:600;margin-bottom:var(--space-3xs)}.spec-item:where(.astro-mpbnit47) dd:where(.astro-mpbnit47){font-family:var(--font-body);font-size:var(--text-body);color:var(--color-text-muted);line-height:var(--leading);margin:0}.spec-item:where(.astro-mpbnit47) dd:where(.astro-mpbnit47) a:where(.astro-mpbnit47){color:var(--color-accent-text);text-decoration:none}.spec-item:where(.astro-mpbnit47) dd:where(.astro-mpbnit47) a:where(.astro-mpbnit47):hover{text-decoration:underline}@media(max-width:768px){.spec-list:where(.astro-mpbnit47){grid-template-columns:1fr}} 160</style>
160<script type="module" src="/_astro/page.BIhdncjw.js"></script>
160</head> <body> <a href="#main" class="skip-link">Skip to main content</a> <!-- 161 The Press website boundary. It establishes the light theme, the Inter 162 product-copy variant and the named inline-size container every `pw-*` 163 responsive rule queries, so the header, main content and footer all sit 164 inside it. It is not on <body>: `container-type: inline-size` applies 165 layout containment, which would make this element the containing block for 166 fixed-position descendants and trap the paper grain that body::before 167 paints behind the page. FeedbackWidget is deliberately outside it for the 168 same reason â it is floating chrome, and it draws its dialog, fields and 169 buttons from application.css, which is not scoped to this boundary. 170 --> <div class="press-web"> <header class="pw-header"> <nav class="pw-nav pw-frame" data-pw-nav aria-label="Primary"> <a href="/" class="pw-brand" aria-label="kindling â home"> <img src="/brand/kindling-wordmark.svg" width="6445" height="1972" alt="kindling"> </a> <button class="pw-menu" type="button" aria-expanded="false" aria-controls="primary-nav-links" data-pw-menu> 171Menu 172</button> <div class="pw-nav-links" id="primary-nav-links" data-pw-links> <a href="/features/" aria-current="page"> Features </a><a href="/compare/"> Compare </a><a href="/open-source/"> Open source </a><a href="/blog/"> Blog </a><a href="/docs/getting-started/"> Docs </a><a href="/feedback/"> Contact </a> <a href="/download/" class="pw-button pw-button--secondary navbar-cta"> 173Download 174</a> </div> </nav> </header>
174<script type="module">(function(){if(typeof window>"u"||typeof document>"u"||window.KindlingWebsite)return;const l=new WeakMap,p="[data-pw-nav], [data-pw-beats], [data-pw-platform], [data-pw-signup]";function m(o){const t=[...o.querySelectorAll(p)];return o.matches&&o.matches(p)&&t.unshift(o),t.filter(u=>u.closest(".press-web"))}function f(o=document){m(o).forEach(t=>{if(l.has(t))return;const u=new AbortController,i=(a,n,r)=>a.addEventListener(n,r,{signal:u.signal});let d=()=>{};if(t.matches("[data-pw-nav]")){const a=t.querySelector("[data-pw-menu]"),n=t.querySelector("[data-pw-links]");if(!a||!n)return;const r=(e=!1)=>{t.removeAttribute("data-open"),a.setAttribute("aria-expanded","false"),e&&a.focus()};i(a,"click",()=>{t.hasAttribute("data-open")?r(!0):(t.setAttribute("data-open",""),a.setAttribute("aria-expanded","true"),n.querySelector("a[href]")?.focus())}),i(t,"keydown",e=>{e.key==="Escape"&&t.hasAttribute("data-open")&&(e.preventDefault(),r(!0))}),i(n,"click",e=>{e.target.closest("a[href]")&&r()}),i(document,"pointerdown",e=>{t.contains(e.target)||r()}),i(t,"focusout",e=>{e.relatedTarget&&!t.contains(e.relatedTarget)&&r()}),d=()=>{r(),t.removeAttribute("data-pw-ready")},t.setAttribute("data-pw-ready","")}if(t.matches("[data-pw-beats]")){const a=[...t.querySelectorAll('input[type="radio"][data-pw-beat]')],n=[...t.querySelectorAll("[data-pw-draft]")],r=()=>{const e=a.find(s=>s.checked);n.forEach(s=>{s.hidden=s.dataset.pwDraft!==e?.value})};a.forEach(e=>i(e,"change",r)),r(),d=()=>n.forEach(e=>{e.hidden=!1})}if(t.matches("[data-pw-platform]")){const a=t.querySelector("[data-pw-platform-result]"),n=[...t.querySelectorAll('input[type="radio"][data-pw-platform-choice]')],r=()=>{const e=n.find(s=>s.checked);a&&e&&(a.textContent=e.dataset.pwDescription||e.value)};n.forEach(e=>i(e,"change",r)),r()}if(t.matches("[data-pw-signup]")){const a=t.querySelector('input[type="email"]'),n=t.querySelector("[data-pw-error]"),r=t.querySelector("[data-pw-status]"),e=t.querySelector('button[type="submit"]');if(!a||!n||!r||!e)return;let s;const w=e.textContent,h=()=>{let c="";return a.value.trim()?a.validity.valid||(c="Use an email address like [email protected]."):c="Enter your email address to continue.",n.textContent=c,n.hidden=!c,a.setAttribute("aria-invalid",String(!!c)),!c};i(a,"blur",h),i(t,"submit",c=>{if(c.preventDefault(),r.textContent="",!h()){a.focus();return}e.disabled=!0,e.textContent="Checkingâ¦",a.readOnly=!0,r.textContent="Checking this example locallyâ¦",s=window.setTimeout(()=>{r.textContent="Example complete. Your address was not sent or stored.",e.disabled=!1,e.textContent=w,a.readOnly=!1},450)}),d=()=>{clearTimeout(s),e.disabled=!1,e.textContent=w,a.readOnly=!1}}l.set(t,()=>{u.abort(),d(),l.delete(t)})})}function y(o=document){m(o).forEach(t=>l.get(t)?.())}window.KindlingWebsite=Object.freeze({init:f,destroy:y}),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>f(),{once:!0}):f()})();</script>
174 <main id="main">
174<script type="application/ld+json">{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"What file formats does kindling support?","acceptedAnswer":{"@type":"Answer","text":"kindling imports from Plottr (.pltr), Scrivener (.scriv), yWriter (.yw7), Obsidian Longform, novelWriter projects, and Markdown outlines. It exports to DOCX, EPUB, HTML, plain text, Markdown, Scrivener, Obsidian Longform, and novelWriter. Choose a simple export or use Custom for saved profiles and manuscript previews; HTML and plain-text manuscripts are available through Custom."}},{"@type":"Question","name":"Is kindling open source?","acceptedAnswer":{"@type":"Answer","text":"Yes. kindling is fully open source under the MIT license. The complete source code is available on GitHub at https://github.com/smith-and-web/kindling. Anyone can inspect, modify, and redistribute the code."}},{"@type":"Question","name":"Does kindling send data to the cloud?","acceptedAnswer":{"@type":"Answer","text":"kindling stores your projects locally and does not upload your manuscripts. Writing and editorial review work offline, with no account, analytics, or telemetry. You choose how to transfer editorial review files. Automatic updates use the internet, and submitting the optional Send Feedback form sends your feedback to the kindling team."}},{"@type":"Question","name":"What is kindling built with?","acceptedA
174nswer":{"@type":"Answer","text":"kindling is built with Rust and Tauri 2.x for the backend, providing fast performance in a small native app: a ~8 MB installer on Windows, ~21 MB on macOS, and under 100 MB installed. The user interface is built with Svelte 5. Project data is stored in SQLite databases. It launches in under a second and uses a fraction of the memory of Electron-based writing tools."}}]}</script>
174 <section class="page-lede editorial astro-mpbnit47"> <h1 class="astro-mpbnit47">From your first outline to editorial review</h1> <p class="lede-standfirst astro-mpbnit47"> 175Whether you outline every beat or discover your story as you write, 176 kindling gives you a scaffolded writing space â scene beats as 177 expandable prompts, character details at your fingertips, and the 178 flexibility to add structure at your own pace. When the draft is 179 ready for another reader, exchange comments and tracked suggestions 180 through local review files. 181</p> <a href="/download/" class="pw-button astro-mpbnit47" data-cta-location="features_hero"> 182Download kindling â Free 183</a> </section> <section class="features-seq-section pw-frame astro-mpbnit47"> <div class="pw-feature-grid astro-mpbnit47"> <article class="pw-feature fade-in-on-scroll astro-mpbnit47"> <figure class="pw-figure product-figure astro-mpbnit47 astro-f5rbpj5f"> <div class="pw-image-frame astro-f5rbpj5f" style="--pw-reference-ratio: 1408 / 1126"> <img src="/_astro/beat-with-prose.eSJ8Cqfd_2vhTRe.webp" alt="kindling editor showing scene beats alongside prose" loading="eager" decoding="async" width="560" height="448" class="astro-f5rbpj5f"> </div> </figure> <div class="astro-mpbnit47"> <p class="pw-label astro-mpbnit47">Core Feature</p> <h3 class="astro-mpbnit47">Outline-Aware Drafting</h3> <p class="pw-problem astro-mpbnit47"> 184Other apps make you flip between your outline and a 185 blank page. 186</p> <p class="astro-mpbnit47"> 187Scene beats appear as expandable prompts in your 188 drafting view. Click a beat, write into it. Your outline 189 is the scaffolding for your first draft. 190</p> </div> </article> <article class="pw-feature pw-feature--reverse fade-in-on-scroll astro-mpbnit47"> <div class="astro-mpbnit47"> <p class="pw-label astro-mpbnit47">Stay in Flow</p> <h3 class="astro-mpbnit47">References Panel</h3> <p class="pw-problem astro-mpbnit47"> 191Was it green eyes or blue? What was the tavern called? 192</p> <p class="astro-mpbnit47"> 193Characters and locations tagged in your scene 194 auto-surface in a side panel. Physical descriptions, key 195 notes, and custom fields â all visible while you write. 196</p> </div> <figure class="pw-figure product-figure astro-mpbnit47 astro-f5rbpj5f"> <div class="pw-image-frame astro-f5rbpj5f" style="--pw-reference-ratio: 1280 / 1024"> <img src="/_astro/references-panel.DrG68UqQ_2lgvXi.webp" alt="kindling references panel showing character details" loading="lazy" decoding="async" width="560" height="448" class="astro-f5rbpj5f"> </div> </figure> </article> <article class="pw-feature fade-in-on-scroll astro-mpbnit47"> <figure class="pw-figure product-figure astro-mpbnit47 astro-f5rbpj5f"> <div class="pw-image-frame astro-f5rbpj5f" style="--pw-reference-ratio: 1456 / 1090"> <img src="/_astro/import-formats.Bjy8SjUF_MDdFQ.webp" alt="kindling's import options, including novelWriter, Plottr, yWriter, Markdown, Longform and Scrivener" loading="lazy" decoding="async" width="560" height="419" class="astro-f5rbpj5f"> </div> </figure> <div class="astro-mpbnit47"> <p class="pw-label astro-mpbnit47">Your Tools</p> <h3 class="astro-mpbnit47">Import & Export</h3> <p class="pw-problem astro-mpbnit47"> 197Your outline lives in one app, your manuscript in 198 another. 199</p> <p class="astro-mpbnit47"> 200Import from Plottr, Scrivener, yWriter, Obsidian 201 Longform, novelWriter, or Markdown. Choose a simple 202 export or save a Custom profile with manuscript previews 203 and layout controls. Custom also adds HTML and 204 plain-text manuscript output. See our <a href="/free-scrivener-alternative/" class="astro-mpbnit47"> 205Scrivener alternative guide 206</a> 207and <a href="/docs/importing-projects/" class="astro-mpbnit47"> 208import instructions 209</a> 210before moving an existing project. 211</p> <div class="flow-diagram astro-mpbnit47"> <div class="flow-node astro-mpbnit47"> <span class="flow-label astro-mpbnit47">
211Import</span> <span class="flow-formats astro-mpbnit47"> 212Plottr · Scrivener · yWriter · Obsidian · 213 novelWriter · Markdown 214</span> </div> <span class="flow-arrow astro-mpbnit47" aria-hidden="true"> 215â 216</span> <div class="flow-node flow-node-center astro-mpbnit47"> <span class="flow-label astro-mpbnit47">kindling</span> <span class="flow-formats astro-mpbnit47">Write & edit</span> </div> <span class="flow-arrow astro-mpbnit47" aria-hidden="true"> 217â 218</span> <div class="flow-node astro-mpbnit47"> <span class="flow-label astro-mpbnit47">Export</span> <span class="flow-formats astro-mpbnit47"> 219DOCX · EPUB · HTML · Plain text · Markdown · 220 Scrivener · Longform · novelWriter 221</span> </div> </div> </div> </article> </div> <!-- The three features with nothing to photograph, gathered on one line 222 under the sequence. They are short and text-only, so pairing each with 223 an empty half-spread left a void beside every one of them; three 224 columns of equal weight read as a coda to the sequence rather than 225 three more rows of it. No folio: it is not another numbered spread. --> <div class="feature-coda fade-in-on-scroll astro-mpbnit47"> <div class="astro-mpbnit47"> <p class="pw-label astro-mpbnit47">Your Pace</p> <h3 class="pw-feature-title astro-mpbnit47">Rolling Outline</h3> <p class="pw-problem astro-mpbnit47"> 226Not every scene needs the same level of planning. 227</p> <p class="astro-mpbnit47"> 228Three planning states â <strong class="astro-mpbnit47">Fixed</strong>, <strong class="astro-mpbnit47">Flexible</strong>, <strong class="astro-mpbnit47">Undefined</strong> â let 229 you add structure progressively. Start vague, define as you 230 go. The sidebar adapts automatically. 231</p> </div> <div class="astro-mpbnit47"> <p class="pw-label astro-mpbnit47">Full Control</p> <h3 class="pw-feature-title astro-mpbnit47">Beat Management</h3> <p class="pw-problem astro-mpbnit47"> 232Your outline needs to evolve as you write. 233</p> <p class="astro-mpbnit47"> 234Delete, reorder, split, and merge beats with drag-and-drop. 235 A beat density indicator shows how detailed each scene is at 236 a glance. 237</p> </div> <div class="astro-mpbnit47"> <p class="pw-label astro-mpbnit47">Capture Ideas</p> <h3 class="pw-feature-title astro-mpbnit47">Discovery Notes</h3> <p class="pw-problem astro-mpbnit47"> 238Inspiration doesn't wait for your outline. 239</p> <p class="astro-mpbnit47"> 240Press <strong class="astro-mpbnit47">Cmd/Ctrl+D</strong> to jot a note mid-scene â 241 a subplot idea, a character insight, a thread to weave in 242 later. Promote notes to full beats when you're ready. 243</p> </div> </div> </section> <section class="features-seq-section pw-frame astro-mpbnit47" id="new-in-v1-3"> <div class="pw-section-head astro-mpbnit47"> <p class="pw-label">02 / This release</p> <div> <h2 class="pw-heading"> New in v1.3 </h2> <p class="pw-prose">A new editorial design, and tools for your next draft, writing session, and round of feedback.</p> </div> </div> <div class="pw-release-grid astro-mpbnit47"> <article class="pw-release-item astro-mpbnit47"> <h3 class="astro-mpbnit47">Meet Press</h3> <p class="astro-mpbnit47"> 244kindlingâs new design brings warm paper surfaces, bookish 245 typography, and quiet controls to the app and website. Your 246 prose has room to breathe, with fine rules to separate the 247 tools around it. The appâs light and dark themes share the 248 same care for reading. 249</p> <a class="pw-link astro-mpbnit47" href="/blog/kindling-v1-3-writing-and-review/#meet-press"> 250Take a first look at Press → 251</a> </article> <article class="pw-release-item astro-mpbnit47"> <h3 class="astro-mpbnit47">Editorial review without accounts</h3> <p class="astro-mpbnit47"> 252Send your whole manuscript or selected chapters as a <code class="astro-mpbnit47">.kindling-review</code> file. Your editor reads and 253 edits in kindling, with tracked suggestions, threaded 254 comments, and simple or full markup. Import their feedback 255 and accept or reject changes beside your manuscript. Keep 256 writing while the review is out; if a passage has changed, 257 compare it before applying the suggestion. 258</p> <a class="pw-link astro-mpbnit47" href="/docs/editorial-review/"> 259See the review workflow → 260</a> </article> <article class="pw-release-item astro-mpbnit47"> <h3 class="astro-mpbnit47">Custom exports for your next reader</h3> <p class="astro-mpbnit47"> 261Prepare a submission, a reading copy, or chapters for your 262 website. Save profiles with chapter selections, typography, 263 and book details, and check the manuscript preview before 264 exporting. Start with Agent submission, Writing group, or
265 Website chapters. The simple format choices remain available 266 whenever you just need a file. 267</p> <a class="pw-link astro-mpbnit47" href="/docs/export-workspace/"> 268Explore the export workspace → 269</a> </article> <article class="pw-release-item astro-mpbnit47"> <h3 class="astro-mpbnit47">Writing goals, sessions, and statistics</h3> <p class="astro-mpbnit47"> 270Set a daily goal for each book and follow your writing 271 streak. Scene, chapter, project, and session totals stay 272 visible in the status bar. Open Writing statistics for 273 chapter counts and a breakdown of scenes with prose. Daily 274 totals count net words added through saved edits, so cutting 275 words can lower the number. 276</p> <a class="pw-link astro-mpbnit47" href="/docs/writing-progress/"> 277Understand your progress → 278</a> </article> <article class="pw-release-item astro-mpbnit47"> <h3 class="astro-mpbnit47">Return to the story</h3> <p class="astro-mpbnit47"> 279Previously shows the preceding sceneâs title, synopsis, and 280 closing three sentences, even across chapter boundaries. 281 kindling also restores your last scene, expanded beat, 282 cursor, and scroll position when you reopen a project. 283</p> <a class="pw-link astro-mpbnit47" href="/docs/scene-workflow/#previous-scene-context"> 284Pick up where you left off → 285</a> </article> <article class="pw-release-item astro-mpbnit47"> <h3 class="astro-mpbnit47">Find and replace across the manuscript</h3> <p class="astro-mpbnit47"> 286Search the current scene or the whole project, match case or 287 whole words, and jump directly to a passage. Replace one 288 match or all editable matches, with Undo replacement while 289 the dialog stays open. Locked scenes and chapters are 290 protected from replacement. 291</p> <a class="pw-link astro-mpbnit47" href="/docs/scene-workflow/#find-and-replace"> 292See search scope and shortcuts → 293</a> </article> <article class="pw-release-item astro-mpbnit47"> <h3 class="astro-mpbnit47">References that travel between books</h3> <p class="astro-mpbnit47"> 294Copy characters, locations, and other references into 295 another project, including their notes, custom fields, and 296 tags. Review possible duplicates before copying. Existing 297 references are never overwritten, and the copies evolve 298 independently with each book. 299</p> <a class="pw-link astro-mpbnit47" href="/docs/references/#copying-references-between-projects"> 300Bring your story world along → 301</a> </article> <article class="pw-release-item astro-mpbnit47"> <h3 class="astro-mpbnit47">novelWriter import, export, and prose sync</h3> <p class="astro-mpbnit47"> 302Bring in a novelWriter project folder with chapters, scenes, 303 prose, beat comments, and reference notes. Export for 304 novelWriter 26.2 or newer. For a linked project, Sync lets 305 you compare incoming prose and select the replacements you 306 want; no prose changes are selected by default. 307</p> <a class="pw-link astro-mpbnit47" href="/docs/importing-projects/#novelwriter-project-folder"> 308Follow the novelWriter workflow → 309</a> </article> <article class="pw-release-item astro-mpbnit47"> <h3 class="astro-mpbnit47">One Settings window</h3> <p class="astro-mpbnit47"> 310Appearance, shortcuts, author details, project preferences, 311 reference types, tags, and custom fields now share one 312 window. Choose another project inside Settings without 313 moving away from the manuscript you have open. 314</p> <a class="pw-link astro-mpbnit47" href="/docs/settings/"> 315Explore Settings → 316</a> </article> <article class="pw-release-item astro-mpbnit47"> <h3 class="astro-mpbnit47">Make the shortcuts yours</h3> <p class="astro-mpbnit47"> 317Remap commands and prose formatting, clear a binding, or 318 restore the defaults. Changes apply across projects and 319 launches, with menus and shortcut hints updating 320 immediately. Conflicts name the command already using your 321 chosen keys. 322</p> <a class="pw-link astro-mpbnit47" href="/docs/settings/#keyboard-shortcuts"> 323Customize your shortcuts → 324</a> </article> </div> <p class="release-link astro-mpbnit47"> <a href="/blog/kindling-v1-3-writing-and-review/" class="astro-mpbnit47"> 325Read the full v1.3 release story → 326</a> </p> </section> <section class="features-seq-section pw-frame astro-mpbnit47"> <div class="pw-section-head astro-mpbnit47"> <p class="pw-label">03 / Drafting tools</p> <div> <h2 class="pw-heading"> More ways to shape your draft </h2> </div> </div> <div class="pw-feature-grid astro-mpbnit47"> <article class="pw-feature fade-in-on-scroll astro-mpbnit47"> <figure class="pw-figure product-figure astro-mpbnit47 astro-f5rbpj5f"> <div class="pw-image-frame astro-f5rbpj5f" style="--pw-reference-ratio: 2176 / 1740"> <img src="/_astro/page-view.dhhhCDZG_Z1ptChP.webp" alt="A scene in kindling's full-page prose editor, with the Page view active" loading="lazy" decoding="async" width="560" height="448" class="astro-f5rbpj5f"> </div> </figure> <div class="astro-mpbnit47"> <p class="pw-label astro-mpbnit47">Write Your Way</p> <h3 class="astro-mpbnit47">Full-Page Prose Editing</h3> <p class="pw-problem astro-mpbnit47"> 327Beat-by-beat is great for structure. But sometimes you 328 just want to write. 329</p> <p class="astro-mpbnit47"> 330Toggle between beat view and a full-page prose editor
331 per scene. Your beats stay available for reference â you 332 get an uninterrupted writing surface without leaving 333 your structure behind. 334</p> </div> </article> <article class="pw-feature pw-feature--reverse fade-in-on-scroll astro-mpbnit47"> <div class="astro-mpbnit47"> <p class="pw-label astro-mpbnit47">Screenwriters</p> <h3 class="astro-mpbnit47">Screenplay Projects & Templates</h3> <p class="pw-problem astro-mpbnit47"> 335Novel tools don't speak the language of screenwriting. 336</p> <p class="astro-mpbnit47"> 337Create Screenplay projects with slugline input 338 (INT./EXT.), page count estimation (250 words/page), and 339 treatment export (1-page or 5-page DOCX). Pre-built 340 templates: Save the Cat, Hero's Journey, Three-Act, 341 Seven-Point, Snowflake, Story Grid. 342</p> </div> <figure class="pw-figure product-figure astro-mpbnit47 astro-f5rbpj5f"> <div class="pw-image-frame astro-f5rbpj5f" style="--pw-reference-ratio: 2288 / 1830">
342 <img src="/_astro/screenplay.ZldBRlpW_vCdtm.webp" alt="A screenplay project with an INT./EXT. slugline, act sequences and a running page estimate" loading="lazy" decoding="async" width="560" height="448" class="astro-f5rbpj5f"> </div> </figure> </article> <article class="pw-feature fade-in-on-scroll astro-mpbnit47"> <div class="astro-mpbnit47"> <p class="pw-label astro-mpbnit47">Smart Context</p> <h3 class="astro-mpbnit47">Reference Auto-Detection</h3> <p class="pw-problem astro-mpbnit47"> 343You mentioned a character three paragraphs ago but 344 forgot to link them. 345</p> <p class="astro-mpbnit47"> 346A non-AI engine scans your prose for character, 347 location, and item names. Suggestions surface in the 348 References panel â link with one click, or dismiss. No 349 cloud, no magic, just pattern matching. 350</p> </div> <div class="astro-mpbnit47"> <p class="pw-label astro-mpbnit47">Flexible Data</p> <h3 class="astro-mpbnit47">Custom Fields & Tags</h3> <p class="pw-problem astro-mpbnit47"> 351Plain text notes don't cut it for complex characters and 352 worlds. 353</p> <p class="astro-mpbnit47"> 354Define typed fields (text, number, date, select, 355 multi-select) on any reference type. Organise with 356 hierarchical tags up to three levels deep, with colours 357 and a Tag Manager. Use saved filters to find exactly 358 what you need. 359</p> </div> </article> </div> </section> <section class="features-seq-section pw-frame astro-mpbnit47"> <div class="pw-section-head astro-mpbnit47"> <p class="pw-label">04 / Specifications</p> <div> <h2 class="pw-heading"> And everything else you'd expect </h2> </div> </div> <dl class="spec-list astro-mpbnit47"> <div class="spec-item astro-mpbnit47"> <dt class="astro-mpbnit47">Command palette</dt> <dd class="astro-mpbnit47"> 360Press Cmd+K to fuzzy-search every action â import, export, 361 settings, navigation. 362</dd> </div> <div class="spec-item astro-mpbnit47"> <dt class="astro-mpbnit47">Blank projects</dt> <dd class="astro-mpbnit47"> 363Start from scratch with File â New Project. No import 364 required. 365</dd> </div> <div class="spec-item astro-mpbnit47"> <dt class="astro-mpbnit47">Distraction-free writing</dt> <dd class="astro-mpbnit47"> 366Clean interface. Beats expand only when you need them. No 367 feature overload. 368</dd> </div> <div class="spec-item astro-mpbnit47"> <dt class="astro-mpbnit47">Local-first & private</dt> <dd class="astro-mpbnit47"> 369Your manuscripts stay on your computer. No account, desktop 370 analytics, or telemetry. You choose how to share review 371 files. <a href="/open-source/" class="astro-mpbnit47">Open source.</a> </dd> </div> <div class="spec-item astro-mpbnit47"> <dt class="astro-mpbnit47">Built with Rust</dt> <dd class="astro-mpbnit47"> 372Sub-second launch. Fraction of the memory of Electron apps. 373 Under 100 MB installed. 374</dd> </div> <div class="spec-item astro-mpbnit47"> <dt class="astro-mpbnit47">Session persistence</dt> <dd class="astro-mpbnit47"> 375Cursor position, scroll state, and active scene â all 376 restored when you reopen. 377</dd> </div> <div class="spec-item astro-mpbnit47"> <dt class="astro-mpbnit47">Automatic updates</dt> <dd class="astro-mpbnit47"> 378New versions download silently. A banner appears when ready 379 â click to restart. 380</dd> </div> <div class="spec-item astro-mpbnit47"> <dt class="astro-mpbnit47">Light & dark themes</dt> <dd class="astro-mpbnit47"> 381System preference detection or manual override. Switch from 382 Settings â Appearance & Guidance. 383</dd> </div> </dl> </section> <section class="cta-section astro-mpbnit47"> <h2 class="astro-mpbnit47">Ready to spark your draft?</h2> <p class="astro-mpbnit47"> 384Free, open source, MIT licensed. <a href="/compare/" class="astro-mpbnit47">See how kindling compares</a> to Scrivener, 385 Plottr, and other writing tools. 386</p> <div class="download-buttons single astro-mpbnit47"> <a href="/download/" class="pw-button astro-mpbnit47" data-cta-location="features_bottom"> 387Download kindling â Free 388</a> </div> </section> </main> <footer class="pw-footer astro-sz7xmlte"> <div class="pw-frame astro-sz7xmlte"> <div class="pw-footer-top astro-sz7xmlte"> <div class="footer-signature astro-sz7xmlte"> <a href="/" class="pw-brand astro-sz7xmlte" aria-label="kindling â home"> <img src="/brand/kindling-wordmark.svg" width="6445" height="1972" alt="kindling" class="astro-sz7xmlte"> </a> <p class="footer-tagline astro-sz7xmlte">Your words, your data, forever.</p> </div> <ul class="pw-footer-links footer-primary astro-sz7xmlte"> <li class="astro-sz7xmlte"><a href="/features/" class="astro-sz7xmlte">Features</a></li><li class="astro-sz7xmlte"><a href="/download/" class="astro-sz7xmlte">Download</a></li><li class="astro-sz7xmlte"><a href="/compare/" class="astro-sz7xmlte">Compare</a></li><li class="astro-sz7xmlte"><a href="/open-source/" class="astro-sz7xmlte">Open source</a></li><li class="astro-sz7xmlte"><a href="/docs/getting-started/" class="astro-sz7xmlte">Docs</a></li><li class="astro-sz7xmlte"><a href="/blog/" class="astro-sz7xmlte">Blog</a></li><li class="astro-sz7xmlte"><a href="/faq/" class="astro-sz7xmlte">FAQ</a></li><li class="astro-sz7xmlte"><a href="/feedback/" class="astro-sz7xmlte">Send feedback</a></li> </ul> </div> <div class="footer-guides astro-sz7xmlte"> <p class="pw-label astro-sz7xmlte" id="footer-guides-label">Guides</p> <ul class="pw-footer-links footer-guides-links astro-sz7xmlte" aria-labelledby="footer-guides-label"> <li class="astro-sz7xmlte"><a href="/plottr-vs-scrivener/" class="astro-sz7xmlte">Plottr vs Scrivener</a></li><li class="astro-sz7xmlte"><a href="/free-scrivener-alternative/" class="astro-sz7xmlte">Free Scrivener alternative</a></li><li class="astro-sz7xmlte"><a href="/story-outlining-software/" class="astro-sz7xmlte">Story outlining software</a></li><li class="astro-sz7xmlte"><a href="/blog/best-plottr-alternatives-for-fiction-writers/" class="astro-sz7xmlte">Best Plottr alternatives</a></li><li class="astro-sz7xmlte"><a href="/blog/best-scrivener-alternatives-for-plotters/" class="astro-sz7xmlte">Best Scrivener alternatives</a></li><li class="astro-sz7xmlte"><a href="/blog/best-story-outlining-software-2026/" class="astro-sz7xmlte">Best story outlining software</a></li> </ul> </div> <div class="pw-footer-bottom astro-sz7xmlte"> <p class="astro-sz7xmlte">© 2026 kindling. Free and open source.</p> <ul class="pw-footer-links footer-secondary astro-sz7xmlte"> <li class="astro-sz7xmlte"> <a href="https://github.com/smith-and-web/kindling" rel="noopener" target="_blank" class="astro-sz7xmlte">GitHub</a> </li><li class="astro-sz7xmlte"> <a href="https://discord.gg/g7bkj4kY8w" rel="noopener" target="_blank" class="astro-sz7xmlte">Discord</a> </li><li class="astro-sz7xmlte"> <a href="https://opensource.org/licenses/MIT" rel="noopener" target="_blank" class="astro-sz7xmlte">MIT License</a> </li><li class="astro-sz7xmlte"> <a href="/privacy/" class="astro-sz7xmlte">Privacy policy</a> </li><li class="astro-sz7xmlte"> <a href="/terms/" class="astro-sz7xmlte">Terms of service</a> </li><li class="astro-sz7xmlte"> <a href="/code-signing-policy/" class="astro-sz7xmlte">Code signing policy</a> </li> </ul> </div> </div> </footer> </div> <div class="fw astro-m57zai7y"><button type="button" class="ka-button ka-icon-button fw-launcher astro-m57zai7y" id="fw-launcher" aria-haspopup="dialog" aria-label="Send feedback">
388<svg class="ka-icon astro-m57zai7y" viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 15a2 2 0 0 1-2 2H8l-4 4V5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2z" class="astro-m57zai7y"></path></svg></button><dialog class="ka-dialog fw-dialog astro-m57zai7y" id="fw-dialog" aria-labelledby="fw-title"><div class="ka-dialog-header fw-header astro-m57zai7y"><h2 id="fw-title" class="astro-m57zai7y">Send feedback</h2><form method="dialog" class="fw-close-form astro-m57zai7y"><button class="ka-button ka-button--ghost ka-icon-button fw-close astro-m57zai7y" value="cancel" aria-label="Close feedback form"><svg class="ka-icon astro-m57zai7y" viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18" class="astro-m57zai7y"></path></svg></button></form></div><div class="ka-dialog-body od-stack fw-body astro-m57zai7y"><p class="fw-intro astro-m57zai7y">Found a bug, or thought of something kindling should do? Tell us here. It's anonymous unless you leave an email.</p><!-- Toggle buttons with aria-pressed rather than a radiogroup: radiogroup 389 semantics promise arrow-key navigation, and two Tab-reachable buttons 390 are simpler than implementing a roving tabindex for two options. The 391 track and selected treatment are the application's segmented control. --><div class="ka-segment-track fw-types astro-m57zai7y" role="group" aria-label="Feedback type"><button type="button" class="ka-segment fw-type ka-selected astro-m57zai7y" data-type="bug" aria-pressed="true">Bug</button><button type="button" class="ka-segment fw-type astro-m57zai7y" data-type="feature" aria-pressed="false">Idea</button></div><div class="ka-field fw-field astro-m57zai7y"><label for="fw-summary" class="astro-m57zai7y">Summary <span class="fw-req astro-m57zai7y" aria-hidden="true">*</span></label><input type="text" id="fw-summary" maxlength="120" placeholder="One line" class="astro-m57zai7y"></div><div class="ka-field fw-field astro-m57zai7y"><label for="fw-message" class="astro-m57zai7y">Details <span class="fw-req astro-m57zai7y" aria-hidden="true">*</span></label><textarea id="fw-message" maxlength="2000" rows="4" placeholder="What happened, or what should it do?" class="astro-m57zai7y"></textarea></div><fieldset class="ka-segments fw-field astro-m57zai7y" id="fw-severity-field"><legend class="ka-label astro-m57zai7y">How severe is it? <span class="fw-req astro-m57zai7y" aria-hidden="true">*</span></legend><div class="fw-severity astro-m57zai7y"><div class="ka-check od-row astro-m57zai7y"><input type="radio" id="fw-sev-minor" name="fw-severity" value="minor" class="astro-m57zai7y"><label for="fw-sev-minor" class="astro-m57zai7y">Minor</label></div><div class="ka-check od-row astro-m57zai7y"><input type="radio" id="fw-sev-broken" name="fw-severity" value="broken" class="astro-m57zai7y"><label for="fw-sev-broken" class="astro-m57zai7y">Broken</label></div><div class="ka-check od-row astro-m57zai7y"><input type="radio" id="fw-sev-crash" name="fw-severity" value="crash" class="astro-m57zai7y"><label for="fw-sev-crash" class="astro-m57zai7y">Crash or data loss</label></div></div></fieldset><div class="ka-field fw-field astro-m57zai7y"><label for="fw-email" class="astro-m57zai7y">Email <span class="fw-optional astro-m57zai7y">optional, for follow-up</span></label><input type="email" id="fw-email" placeholder="[email protected]" class="astro-m57zai7y"></div><div class="fw-honeypot astro-m57zai7y" aria-hidden="true"><input type="text" id="fw-honeypot" tabindex="-1" autocomplete="off" class="astro-m57zai7y"></div><div class="fw-turnstile astro-m57zai7y" id="fw-turnstile"></div><p class="ka-help fw-privacy astro-m57zai7y"> 392We store your message and OS, nothing else. Please don't include personal 393 information. <a href="/privacy/" class="astro-m57zai7y">Privacy policy →</a></p></div><div class="ka-dialog-footer fw-footer astro-m57zai7y"><div class="ka-notice fw-status astro-m57zai7y" id="fw-status" role="status" aria-l
393ive="polite"></div><button type="button" class="ka-button fw-submit astro-m57zai7y" id="fw-submit">Send feedback</button></div></dialog></div>
393<script> 394 (function () { 395 const launcher = document.getElementById('fw-launcher'); 396 if (!launcher) return; 397 398 const API_URL = 'https://2gcszmyn325n5yaey2poh72qbe0tkmre.lambda-url.ca-central-1.on.aws/'; 399 const SITEKEY = '0x4AAAAAACkqRWHDozaF1dmj'; 400 401 const dialog = document.getElementById('fw-dialog'); 402 const statusEl = document.getElementById('fw-status'); 403 const submitBtn = document.getElementById('fw-submit'); 404 const severityField = document.getElementById('fw-severity-field'); 405 406 let currentType = 'bug'; 407 let formOpenedAt = new Date().toISOString(); 408 let turnstileWidgetId = null; 409 let turnstileRequested = false; 410 411 // Detected once; the modal has no OS picker, unlike the full /feedback/ form. 412 const ua = navigator.userAgent.toLowerCase(); 413 const os = ua.includes('mac') ? 'macos' 414 : ua.includes('win') ? 'windows' 415 : ua.includes('linux') ? 'linux' 416 : 'unknown'; 417 418 function showStatus(message, kind) { 419 statusEl.textContent = message; 420 // ka-notice--error / ka-notice--success are the application's own status 421 // treatments; `visible` is local, because an empty notice must not draw 422 // its surface and border. 423 statusEl.className = 'ka-notice fw-status visible ka-notice--' + kind; 424 } 425 426 function clearStatus() { 427 statusEl.textContent = ''; 428 statusEl.className = 'ka-notice fw-status'; 429 } 430 431 /* Turnstile is fetched on first open, not on page load. Explicit rendering 432 (render=explicit) so we own the widget id and can reset just ours. */ 433 function loadTurnstile() { 434 if (turnstileRequested) return; 435 turnstileRequested = true; 436 window.__kindlingTurnstileReady = function () { 437 turnstileWidgetId = window.turnstile.render('#fw-turnstile', { 438 sitekey: SITEKEY, 439 theme: 'light', 440 }); 441 }; 442 const s = document.createElement('script'); 443 s.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit&onload=__kindlingTurnstileReady'; 444 s.async = true; 445 s.defer = true; 446 document.head.appendChild(s); 447 } 448 449 launcher.addEventListener('click', () => { 450 clearStatus(); 451 formOpenedAt = new Date().toISOString(); 452 loadTurnstile(); 453 dialog.showModal(); 454 document.documentElement.style.overflow = 'hidden'; 455 if (typeof gtag === 'function') { 456 gtag('event', 'feedback_widget_open', { page_path: location.pathname }); 457 } 458 }); 459 460 // Covers the close button, Esc, and backdrop dismissal in one place. 461 dialog.addEventListener('close', () => { 462 document.documentElement.style.overflow = '';
463 launcher.focus(); 464 }); 465 466 // Native <dialog> treats the whole viewport as the element, so a click is 467 // "on the backdrop" when it lands outside the dialog's own box. 468 dialog.addEventListener('click', (e) => { 469 if (e.target !== dialog) return; 470 const r = dialog.getBoundingClientRect(); 471 const outside = e.clientX < r.left || e.clientX > r.right || 472 e.clientY < r.top || e.clientY > r.bottom; 473 if (outside) dialog.close(); 474 }); 475 476 document.querySelectorAll('.fw-type').forEach((btn) => { 477 btn.addEventListener('click', () => { 478 document.querySelectorAll('.fw-type').forEach((b) => { 479 b.classList.remove('ka-selected'); 480 b.setAttribute('aria-pressed', 'false'); 481 }); 482 btn.classList.add('ka-selected'); 483 btn.setAttribute('aria-pressed', 'true'); 484 currentType = btn.dataset.type; 485 severityField.hidden = currentType !== 'bug'; 486 clearStatus(); 487 }); 488 }); 489 490 submitBtn.addEventListener('click', async () => { 491 const summary = document.getElementById('fw-summary').value.trim(); 492 const message = document.getElementById('fw-message').value.trim(); 493 const email = document.getElementById('fw-email').value.trim(); 494 const severity = document.querySelector('input[name="fw-severity"]:checked')?.value; 495 496 if (!summary || !message) { 497 showStatus('Please fill in all required fields.', 'error'); 498 return; 499 } 500 if (currentType === 'bug' && !severity) { 501 showStatus('Please choose how severe it is.', 'error'); 502 return; 503 } 504 505 const payload = { 506 type: currentType, 507 source: 'website', 508 os: os, 509 appVersion: 'website', 510 locale: navigator.language, 511 website: document.getElementById('fw-honeypot').value, 512 formOpenedAt: formOpenedAt, 513 summary: summary, 514 message: message, 515 }; 516 if (email) payload.email = email; 517 if (currentType === 'bug') payload.severity = severity; 518 519 const token = document.querySelector('#fw-turnstile [name="cf-turnstile-response"]')?.value; 520 if (token) payload.turnstileToken = token; 521 522 submitBtn.disabled = true; 523 submitBtn.textContent = 'Sending...'; 524 525 try { 526 const response = await fetch(API_URL, { 527 method: 'POST', 528 headers: { 'Content-Type': 'application/json' }, 529 body: JSON.stringify(payload), 530 }); 531 532 if (response.ok) { 533 showStatus('Thank you! Your feedback has been received.', 'success'); 534 resetForm(); 535 if (typeof gtag === 'function') { 536 gtag('event', 'feedback_widget_submit', { feedback_type: currentType }); 537 } 538 } else if (response.status === 429) { 539 showStatus('Too many submissions. Please try again later.', 'error'); 540 } else { 541 const data = await response.json().catch(() => ({})); 542 showStatus(data.error || 'Something went wrong. Please try again.', 'error'); 543 } 544 } catch (err) { 545 showStatus('Network error. Please check your connection and try again.', 'error'); 546 } 547 548 submitBtn.disabled = false; 549 submitBtn.textContent = 'Send feedback'; 550 551 if (window.turnstile && turnstileWidgetId !== null) { 552 window.turnstile.reset(turnstileWidgetId); 553 } 554 }); 555 556 function resetForm() { 557 document.getElementById('fw-summary').value = ''; 558 document.getElementById('fw-message').value = ''; 559 document.getElementById('fw-email').value = ''; 560 document.querySelectorAll('input[name="fw-severity"]').forEach((el) => { 561 el.checked = false; 562 }); 563 formOpenedAt = new Date().toISOString(); 564 } 565 })(); 566</script>
566 </body></html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.