PageSourceSearch

https://kindlingwriter.com/features/

html kindlingwriter.com collected 2026-09-25 22:03:10 UTC 57,514 bytes, 566 lines download raw bytes

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 &amp; Review</title><meta name="description" content="Writing software for plotters and pantsers. Rolling outlines, scene beats, editorial review, writing goals, and character references. Free &#38; open source."><link rel="canonical" href="https://kindlingwriter.com/features/"><!-- Open Graph --><meta property="og:title" content="kindling Writer Features — Outlining, Drafting &#38; Review"><meta property="og:description" content="Writing software for plotters and pantsers. Rolling outlines, scene beats, editorial review, writing goals, and character references. Free &#38; 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 &#38; Review"><meta name="twitter:description" content="Writing software for plotters and pantsers. Rolling outlines, scene beats, editorial review, writing goals, and character references. Free &#38; 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 &amp; 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 &amp; 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 &rarr;
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 &rarr;
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 &rarr;
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 &rarr;
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 &rarr;
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 &rarr;
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 &rarr;
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 &rarr;
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 &rarr;
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 &rarr;
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 &rarr;
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 &amp; 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 &amp; 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&#39;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 &amp; 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 &amp; dark themes</dt> <dd class="astro-mpbnit47">
381System preference detection or manual override. Switch from
382                    Settings → Appearance &amp; 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">&copy; 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 &rarr;</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.