i18n_proofreading 1.1.0 → 1.1.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1dba859df20dd5bc929a6c3bbde753b36591b8dfd92fb24547272ab9d1137d72
4
- data.tar.gz: 284954216db75a99209bb72248c2b447ac62dbd3d28b550c996015392422c4ac
3
+ metadata.gz: 9a73bed80d485e35617f3ea214165c0ffcfc296721ac896f09d389cbf3a1b6e8
4
+ data.tar.gz: 4cc2e32bf3420c01ea3af58c33582ffaa37c9fd26cf6e7425db1b554c4652c84
5
5
  SHA512:
6
- metadata.gz: 47cf7fd0e919c0f1be04b1b27804238da759b955339ee316d4fb522bdfd2e34b96aa661739bd9e8dff324d0a61d7127e155a2322f99b48d8d24736c69aac4003
7
- data.tar.gz: ff614c4482c8c9ee666d6a636f46cc84d724e75289925dabf8aa10b27b004d98e31ce539715476faf7f155b6d2cf3d4b26a86899e281d255a449824c05f816fd
6
+ metadata.gz: 1c884b9b47f7e5712ab618fc663f5160ed7d7eabd571b295818efe445f11111f2bbb81e7110336594c7885d79c032479241623a33bb81d170309165ccb522f40
7
+ data.tar.gz: 7c939c1e63d708c6772dcd4e703816967942b1ca2400a05027cbd8fa200ec1c446de390daf2bca70cf5313eccaa4615dc132464ffbe395a3a8cfb19d84ba04f0
data/CHANGELOG.md CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.1.1] - 2026-10-30
6
+
7
+ ### Fixed
8
+
9
+ - **A Turbo visit to a page without the tool now switches the widget off.**
10
+ Before, the widget kept the previous page's config when the new page carried
11
+ none, so the pill survived a sign-out or `enabled` turning false until a hard
12
+ reload, and a visit made while in suggest mode (Back, `Turbo.visit`) kept
13
+ every click on the new page frozen. A host that worked around this with a
14
+ `data-turbo-track="reload"` element can drop it.
15
+ - **The pill comes back after a Turbo render that has no `turbo:load`**, such
16
+ as a form answered with a 422 or a morph refresh. The widget's per-page setup
17
+ now runs on `turbo:render` as well as `turbo:load`; it is idempotent, so a
18
+ normal visit runs it twice at the cost of rebuilding one button.
19
+ - Costs: a page with a malformed config block now gets no widget instead of the
20
+ previous page's; a host that injects the tag in only some layouts sees the
21
+ pill only on those pages. No configuration, migration, or server-side change.
22
+ - Close a suggestion dialog that a custom render preserves after suggest mode
23
+ becomes unavailable or inactive, releasing its scroll and focus locks. Any
24
+ unsent draft in that dialog is discarded when leaving suggest mode.
25
+
26
+ ### Changed
27
+
28
+ - Exercise real Turbo visits, cached history, 422 responses, and morph refreshes
29
+ in browser tests. Turbo is a development/test dependency only; the installed
30
+ gem still has no Turbo dependency or asset build step.
31
+
5
32
  ## [1.1.0] - 2026-10-18
6
33
 
7
34
  - Add Rails edge CI and experimental Ruby 4.0+ Ractor request support. The host
data/README.md CHANGED
@@ -314,6 +314,9 @@ configure Action View during initialization can boot reliably.
314
314
  `script-src` including `strict-dynamic`. Runtime config ships as
315
315
  `<script type="application/json">` (data, not code), so it needs no nonce and
316
316
  survives Turbo visits.
317
+ - **The browser follows the server's gate across Turbo visits.** A page the
318
+ middleware injected nothing into (signed out, `enabled` false) turns the
319
+ widget off: no pill and no suggest mode, even when the previous page had them.
317
320
  - **Rate-limited per IP** on the submission endpoint (30/min by default,
318
321
  Rails 7.2+).
319
322
  - **Host-owned review data.** A row may contain translated text, proposed text,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module I18nProofreading
4
- VERSION = '1.1.0'
4
+ VERSION = '1.1.1'
5
5
  end
@@ -20,6 +20,12 @@
20
20
  if (!config || window.__i18nProofreadingLoaded) return;
21
21
  window.__i18nProofreadingLoaded = true;
22
22
 
23
+ // The state of a page without a config block: the middleware injected
24
+ // nothing, so the tool is not available there (signed out, `enabled` false,
25
+ // a layout without the tag). Handlers read `config.active`, so this keeps
26
+ // them inert instead of acting on the last page's config.
27
+ var UNAVAILABLE = { active: false, showPill: false };
28
+
23
29
  var LEFT = "⟦"; // ⟦
24
30
  var RIGHT = "⟧"; // ⟧
25
31
  var TOKEN = new RegExp(LEFT + "([^" + RIGHT + "]+)" + RIGHT);
@@ -56,9 +62,13 @@
56
62
 
57
63
  // Everything else lives in <body>, which Turbo replaces on every visit —
58
64
  // taking the pill and the active-mode highlighting with it. Re-run the
59
- // per-page setup on each visit so the widget keeps working without a hard
60
- // reload. render() also runs now for the initial (or non-Turbo) load.
65
+ // per-page setup after every body swap so the widget keeps working without
66
+ // a hard reload. render() also runs now for the initial (or non-Turbo) load.
67
+ // turbo:render covers swaps with no turbo:load after them (a form answered
68
+ // with a 422 or 500, a morph refresh); turbo:load covers the rest. render()
69
+ // is idempotent, so a visit that fires both only rebuilds one button.
61
70
  render();
71
+ document.addEventListener("turbo:render", render);
62
72
  document.addEventListener("turbo:load", render);
63
73
  document.addEventListener("turbo:frame-load", function () { strip(); });
64
74
 
@@ -88,18 +98,23 @@
88
98
 
89
99
  function render() {
90
100
  // Re-read on each visit: the injected config is data (not an executed
91
- // script), so it reflects the page Turbo just rendered.
92
- config = readConfig() || config;
93
- // A Turbo body swap can drop the overlay without close(); documentElement
94
- // survives the swap, so release the scroll lock or the new page is frozen.
95
- if (overlay && !document.body.contains(overlay)) {
96
- overlay = null;
97
- unwatchViewport();
98
- unlockScroll();
99
- }
101
+ // script), so it reflects the page Turbo just rendered. No config means the
102
+ // tool is not available on this page, so the widget switches off rather
103
+ // than keep the previous page's pill or suggest mode across the visit.
104
+ config = readConfig() || UNAVAILABLE;
105
+ // A body swap can detach the dialog; a custom render can preserve it even
106
+ // after suggest mode is disabled. Both must release its scroll/focus lock.
107
+ if (overlay && (!config.active || !document.body.contains(overlay))) close();
108
+ // A cached snapshot or a morph can carry the previous page's pill; the
109
+ // current config alone decides whether one is shown.
110
+ removePill();
111
+ // <html> survives a Turbo visit, so the suggest-mode class must be cleared
112
+ // as well as set.
113
+ document.documentElement.classList.toggle("i18np-active", !!config.active);
114
+ if (config === UNAVAILABLE) return;
115
+
100
116
  injectStyles();
101
117
  if (config.showPill !== false) buildPill();
102
- document.documentElement.classList.toggle("i18np-active", !!config.active);
103
118
  if (config.active) strip();
104
119
  }
105
120
 
@@ -199,10 +214,13 @@
199
214
 
200
215
  // --- pill -----------------------------------------------------------------
201
216
 
202
- function buildPill() {
203
- var existing = document.querySelector(".i18np-pill");
204
- if (existing) existing.remove();
217
+ function removePill() {
218
+ document.querySelectorAll(".i18np-pill").forEach(function (pill) {
219
+ pill.remove();
220
+ });
221
+ }
205
222
 
223
+ function buildPill() {
206
224
  var pill = document.createElement("button");
207
225
  pill.type = "button";
208
226
  pill.className = "i18np-pill" + (config.active ? " i18np-pill-on" : "");
@@ -46,8 +46,10 @@ module I18nProofreading
46
46
  # The config rides in a `type="application/json"` block: it is *data*, not
47
47
  # code, so the browser never executes it and Turbo never tries to re-run it
48
48
  # on a soft visit — which means it needs no CSP nonce and, crucially, the
49
- # widget can re-read the *current* page's config on every `turbo:load`
50
- # instead of being stuck with whatever the last full load evaluated.
49
+ # widget can re-read the *current* page's config after every Turbo render
50
+ # instead of being stuck with whatever the last full load evaluated. A
51
+ # page without the block is a page the tool is off for, and the widget
52
+ # switches itself off there.
51
53
  #
52
54
  # The code is a same-origin `src` script served by the engine — NOT
53
55
  # inlined. Under a nonce-based CSP, Turbo Drive body swaps re-run body
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n_proofreading
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaroslav Shmarov