litura-app 0.3.7 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ number carries breaking changes.
5
5
 
6
6
  ## Unreleased
7
7
 
8
+ ## 0.4.2 — 2026-09-13
9
+
10
+ - Clicking at the end of a paragraph now offers a Continue bubble, asking for the same continuation Cmd/Ctrl+Enter would. An explicit ask is allowed to open the next sentence and run longer than a background suggestion, instead of stopping at a finished thought.
11
+ - Fixed the field popovers flashing at their previous position before snapping to the trigger.
12
+ - Removed the unused standalone Rewrite button from the chat chip.
13
+
14
+ ## 0.4.0 — 2026-09-13
15
+
16
+ - Added spell checking. Misspelled words are marked in the editor using a vendored en_US Hunspell dictionary, checked client-side alongside the existing style-tell markers.
17
+
8
18
  ## 0.3.7 — 2026-09-13
9
19
 
10
20
  - Litura is now also a desktop app. A Tauri shell picks the writing folder, starts the same server on a free port, and shows it in its own window; it carries its own Node runtime, so nothing has to be installed first.
package/index.js CHANGED
@@ -183,15 +183,18 @@ const MIME = {
183
183
  '.svg': 'image/svg+xml',
184
184
  '.woff2': 'font/woff2',
185
185
  '.woff': 'font/woff',
186
+ '.aff': 'text/plain; charset=utf-8',
187
+ '.dic': 'text/plain; charset=utf-8',
186
188
  };
187
189
 
188
190
  function serveStatic(res, filePath) {
189
191
  const ext = path.extname(filePath);
190
192
  const mime = MIME[ext] || 'text/plain';
191
- // Fonts never change under a given name; the bundle changes with every
192
- // update, and a tab holding a stale app.js against a new server is the
193
- // classic post-upgrade bug. Revalidate it on each load.
194
- const cache = ext === '.woff2' ? 'public, max-age=31536000, immutable' : 'no-cache';
193
+ // Fonts and the spelling dictionary never change under a given name; the
194
+ // bundle changes with every update, and a tab holding a stale app.js
195
+ // against a new server is the classic post-upgrade bug. Revalidate it on
196
+ // each load.
197
+ const cache = ['.woff2', '.aff', '.dic'].includes(ext) ? 'public, max-age=31536000, immutable' : 'no-cache';
195
198
  try {
196
199
  const data = fs.readFileSync(filePath);
197
200
  res.writeHead(200, { 'Content-Type': mime, 'Cache-Control': cache });
@@ -327,6 +330,10 @@ async function handleRequest(req, res) {
327
330
  const fontFile = path.basename(url.pathname);
328
331
  serveStatic(res, path.join(PUBLIC, 'fonts', fontFile));
329
332
  }
333
+ else if (url.pathname.startsWith('/dictionary/')) {
334
+ const dictFile = path.basename(url.pathname);
335
+ serveStatic(res, path.join(PUBLIC, 'dictionary', dictFile));
336
+ }
330
337
  else { res.writeHead(404); res.end('Not found'); }
331
338
  return;
332
339
  }
@@ -535,20 +542,21 @@ async function handleRequest(req, res) {
535
542
 
536
543
  // POST /suggest — inline ghost-text suggestion (VS Code style)
537
544
  //
538
- // Request: { context, document, cursor }
545
+ // Request: { context, document, cursor, manual }
539
546
  // cursor is the character offset where the ghost text will appear.
547
+ // manual marks a continuation the writer asked for, not one offered.
540
548
  //
541
549
  // Returns: { suggestion: string } — a short natural continuation
542
550
  //
543
551
  if (url.pathname === '/suggest') {
544
- const { context, document: doc, cursor } = body;
552
+ const { context, document: doc, cursor, manual } = body;
545
553
 
546
554
  const at = cursor ?? (doc ?? '').length;
547
555
  const prefix = (doc ?? '').slice(0, at);
548
556
 
549
557
  try {
550
558
  const suggestion = await completeText({
551
- ...suggestionPrompts({ document: doc, cursor: at, context, style: readStyle() }),
559
+ ...suggestionPrompts({ document: doc, cursor: at, context, style: readStyle(), manual: !!manual }),
552
560
  selection: body.agent,
553
561
  continuation: true,
554
562
  signal: AbortSignal.any([signal, AbortSignal.timeout(30_000)]),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "litura-app",
3
- "version": "0.3.7",
3
+ "version": "0.4.2",
4
4
  "description": "A local AI-assisted editor for sharper, more human writing",
5
5
  "keywords": [
6
6
  "writing",
@@ -48,6 +48,7 @@
48
48
  "desktop:prepare": "npm run build && npm run build:server && node scripts/fetch-node.mjs",
49
49
  "desktop": "tauri dev",
50
50
  "desktop:build": "tauri build",
51
+ "tauri": "tauri",
51
52
  "smoke:desktop": "node scripts/smoke-desktop.mjs",
52
53
  "check": "node --check index.js && node --check pi.js && node --check review-model.js && node --check deepcheck.js && npm run build && node selfcheck.js && node tests/suggestions.mjs && node safetycheck.js",
53
54
  "test:browser": "node tests/browser-fixture.mjs",
@@ -63,6 +64,8 @@
63
64
  "devDependencies": {
64
65
  "@tauri-apps/cli": "^2.9.1",
65
66
  "codemirror": "^6.0.1",
66
- "esbuild": "^0.25.0"
67
+ "dictionary-en": "^4.0.0",
68
+ "esbuild": "^0.25.0",
69
+ "nspell": "^2.1.5"
67
70
  }
68
71
  }