stilura 0.6.2 → 0.6.3

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
@@ -6,8 +6,11 @@ Stilura (renamed from Litura) follows [semantic versioning](https://semver.org).
6
6
 
7
7
  - Fixed cross-document Undo and saves from concurrent tabs. File writes now keep revision checks and history; workspace APIs reject symbolic links.
8
8
  - Desktop state survives server port changes. Closing, changing folders, and installing updates wait for confirmed saves. Partial brief answers survive reloads, and Save as falls back to a download where the system file-picker API is unavailable.
9
+ - Fixed the desktop app crash on macOS ("The server stopped before it was ready"): ad-hoc code signing now carries the JIT entitlements that V8 needs under the hardened runtime. The smoke test verifies the sidecar signature.
10
+ - Desktop releases are gated behind a draft: the updater never sees a half-built release. All platforms must build and pass their smoke test before the release goes live.
11
+ - Removed the Discuss button from finding cards in the chat. When rewrite options appear they show one at a time with Yes / No instead of three cards at once.
9
12
 
10
- - Renamed Litura → Stilura (*stilus*, the pen, + *litura*, the correction): npm package `stilura`, commands `stilura` / `stilura-mcp`, desktop app, skill (`skills/stilura`), and all user-facing strings. `STILURA_*` environment variables replace `LITURA_*` (old spellings still honored). The GitHub repo, release URLs, and the Tauri bundle identifier move with the name — reinstall the desktop app from the new releases page; its saved folder choice does not carry over. Browser drafts, `style.md`, and disk history (`.litura-history`, `litura:` storage keys, `<litura-edits>` tags) are untouched and keep working.
13
+ - Renamed Litura → Stilura (*stilus*, the pen, + *litura*, the correction): npm package `stilura`, commands `stilura` / `stilura-mcp`, desktop app, skill (`skills/stilura`), and all user-facing strings. `STILURA_*` environment variables replace `LITURA_*` (old spellings still honored). The GitHub repo, release URLs, and the Tauri bundle identifier move with the name — reinstall the desktop app from the new releases page; desktop state migrates automatically. Browser drafts, `style.md`, and disk history (`.litura-history`, `litura:` storage keys, `<litura-edits>` tags) are untouched and keep working.
11
14
 
12
15
  ## 0.6.0 — 2026-10-09
13
16
 
package/README.md CHANGED
@@ -8,13 +8,12 @@ Local AI writing enhancer that writes with you. It highlights generic, AI-soundi
8
8
  npx stilura
9
9
  ```
10
10
 
11
- ![Stilura highlighting sales language, a "not X but Y" construction and a forced triple, with a finding card open](docs/screenshot.png)
11
+ ![Stilura marking stock wording in a short story, with a finding card explaining why "It is important to note" weakens the sentence](docs/screenshot.png)
12
12
  ## How it works
13
13
 
14
14
  Stilura checks your draft against 20 named patterns in `style.md`: sales language, vague attribution, forced triples, filler openers, stacked qualifiers, and more. A second pass catches structural issues like buried points and broken paragraph promises.
15
15
 
16
- - **Options** — three rewrites for any finding. Hover to preview, click to apply.
17
- - **Discuss** — chat about a passage or the whole draft.
16
+ - **Options** — rewrites for any finding, shown one at a time. Yes to apply, No to see the next.
18
17
  - **Brief** (`/brief`) — tell it who reads this and what it should do. Rewrites aim at that reader; review stays unbiased.
19
18
  - **Continuations** — short next-line suggestions appear when you pause. `Tab` to accept, `Escape` to dismiss.
20
19
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stilura",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "A local AI-assisted editor for sharper, more human writing",
5
5
  "keywords": [
6
6
  "writing",
package/public/app.js CHANGED
@@ -22457,14 +22457,6 @@ ${lines.join("\n")}` : "";
22457
22457
  });
22458
22458
  const actions = chatEl("div", "finding-actions");
22459
22459
  actions.append(offer, dismiss);
22460
- const discuss = chatEl("button", "chat-action", "Discuss");
22461
- discuss.type = "button";
22462
- discuss.addEventListener("click", () => {
22463
- const range = findingRange(finding.id);
22464
- if (range) attachLive(finding, range);
22465
- chatInput.focus();
22466
- });
22467
- actions.append(discuss);
22468
22460
  if (finding.id === -1) dismiss.textContent = "Keep here";
22469
22461
  const variantsBox = chatEl("div", "finding-variants");
22470
22462
  card.append(actions, variantsBox);
@@ -22476,71 +22468,48 @@ ${lines.join("\n")}` : "";
22476
22468
  const wrap = chatEl("div", "chat-variants");
22477
22469
  wrap.setAttribute("role", "list");
22478
22470
  wrap.dataset.targetFrom = String(target.from);
22479
- ordered.forEach((text, index) => {
22471
+ let current = 0;
22472
+ const items = ordered.map((text, index) => {
22480
22473
  const item = chatEl("div", "variant-card");
22481
22474
  item.setAttribute("role", "listitem");
22482
- item.tabIndex = 0;
22483
- item.setAttribute("role", "button");
22484
- item.setAttribute("aria-label", `Apply option ${index + 1}`);
22485
- const body = chatEl("div", "variant-text", text);
22486
- item.append(body);
22487
- const previewIt = () => {
22488
- if (wrap.classList.contains("is-spent")) return;
22489
- if (!replacementTarget(workView.state.doc.toString(), target)) return;
22490
- clearTimeout(unpreviewTimer);
22491
- showPlainPreview(text, target);
22492
- };
22493
- const unpreview = () => {
22494
- clearTimeout(unpreviewTimer);
22495
- unpreviewTimer = setTimeout(clearPlainPreview, UNPREVIEW_DELAY);
22496
- };
22497
- item.addEventListener("mouseenter", previewIt);
22498
- item.addEventListener("mouseleave", unpreview);
22499
- item.addEventListener("focus", previewIt);
22500
- item.addEventListener("blur", unpreview);
22501
- const apply = () => {
22502
- if (!wrap.classList.contains("is-spent")) applyText(text, target, wrap, item);
22503
- };
22504
- item.addEventListener("click", apply);
22505
- item.addEventListener("keydown", (event) => {
22506
- if (event.key === "Enter" || event.key === " ") {
22507
- event.preventDefault();
22508
- apply();
22509
- } else if (event.key === "Escape") {
22510
- event.preventDefault();
22511
- unpreview();
22512
- item.blur();
22513
- workView.focus();
22514
- }
22515
- });
22475
+ item.append(chatEl("div", "variant-text", text));
22476
+ if (index > 0) item.hidden = true;
22516
22477
  wrap.append(item);
22478
+ return { element: item, text };
22517
22479
  });
22518
- const hintSeen = (() => {
22519
- try {
22520
- return localStorage.getItem("wa-variant-hint") === "seen";
22521
- } catch {
22522
- return false;
22480
+ const previewCurrent = () => {
22481
+ if (wrap.classList.contains("is-spent")) return;
22482
+ if (!replacementTarget(workView.state.doc.toString(), target)) return;
22483
+ showPlainPreview(items[current].text, target);
22484
+ };
22485
+ const foot = chatEl("div", "variant-foot");
22486
+ const yes = chatEl("button", "chat-action is-primary", "Yes");
22487
+ yes.type = "button";
22488
+ yes.addEventListener("click", () => {
22489
+ if (wrap.classList.contains("is-spent")) return;
22490
+ clearPlainPreview();
22491
+ applyText(items[current].text, target, wrap, items[current].element);
22492
+ });
22493
+ const no = chatEl("button", "chat-dismiss", "No");
22494
+ no.type = "button";
22495
+ no.addEventListener("click", () => {
22496
+ if (wrap.classList.contains("is-spent")) return;
22497
+ clearPlainPreview();
22498
+ if (current < items.length - 1) {
22499
+ items[current].element.hidden = true;
22500
+ current++;
22501
+ items[current].element.hidden = false;
22502
+ previewCurrent();
22503
+ } else {
22504
+ const host = wrap.closest(".chat-entry.is-finding");
22505
+ if (host) restoreFindingCard(host);
22506
+ fadeRemove(wrap);
22507
+ markVariantTarget(null);
22523
22508
  }
22524
- })();
22525
- if (instruction !== null) {
22526
- const foot = chatEl("div", "variant-foot");
22527
- const again = chatEl("button", "chat-action is-icon");
22528
- again.type = "button";
22529
- again.setAttribute("aria-label", "Try again \u2014 ask for three more");
22530
- again.title = "Try again \u2014 ask for three more";
22531
- again.innerHTML = '<svg viewBox="0 0 20 20" width="15" height="15" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><polyline points="19.17 3.33 19.17 8.33 14.17 8.33"/><path d="M17.07 12.5a7.5 7.5 0 1 1-1.77-7.8l3.87 3.63"/></svg>';
22532
- again.addEventListener("click", () => {
22533
- if (wrap.classList.contains("is-spent")) return;
22534
- clearPlainPreview();
22535
- wrap.remove();
22536
- requestVariants(instruction, target, card);
22537
- });
22538
- if (!hintSeen) foot.append(chatEl("span", "variant-hint", "Hover to preview \xB7 Click to apply \xB7 Esc to dismiss"));
22539
- foot.append(again);
22540
- wrap.append(foot);
22541
- } else if (!hintSeen) {
22542
- wrap.append(chatEl("div", "variant-hint", "Hover to preview \xB7 Click to apply \xB7 Esc to dismiss"));
22543
- }
22509
+ });
22510
+ foot.append(yes, no);
22511
+ wrap.append(foot);
22512
+ requestAnimationFrame(previewCurrent);
22544
22513
  return wrap;
22545
22514
  }
22546
22515
  function skeletonCards(count = 3) {