antigravity-rtl-patcher 2.2.1 → 2.3.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.
package/bin/cli.js CHANGED
@@ -5,13 +5,13 @@ const chalk = require('chalk');
5
5
  const { patch, restore, status } = require('../src/patcher');
6
6
 
7
7
  const BANNER = `
8
- ${chalk.cyan('Antigravity Smart RTL Patcher')} ${chalk.gray('v2.2.0')}
8
+ ${chalk.cyan('Antigravity Smart RTL Patcher')} ${chalk.gray('v2.3.1')}
9
9
  `;
10
10
 
11
11
  program
12
12
  .name('agy-rtl')
13
13
  .description('RTL patcher for Antigravity')
14
- .version('2.2.0');
14
+ .version('2.3.1');
15
15
 
16
16
  program
17
17
  .command('patch')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antigravity-rtl-patcher",
3
- "version": "2.2.1",
3
+ "version": "2.3.1",
4
4
  "description": "Smart RTL and Persian/Arabic typography patcher for Antigravity",
5
5
  "main": "src/patcher.js",
6
6
  "bin": {
@@ -1,7 +1,9 @@
1
1
  /**
2
2
  * AGY RTL — Preload Injection Snippet
3
3
  * Appended to Antigravity's preload.js at patch time.
4
- * Injects the RTL engine + toggle panel into every renderer page.
4
+ *
5
+ * Only applies RTL direction to individual text elements containing
6
+ * Persian/Arabic/Hebrew characters. Does NOT change the app layout.
5
7
  *
6
8
  * MARKER: __AGY_RTL_INJECTED__
7
9
  */
@@ -30,51 +32,28 @@ function _agyRtlRendererMain() {
30
32
 
31
33
  var RTL_REGEX = /[\u0591-\u07FF\u200F\u202B\u202E\uFB1D-\uFDFD\uFE70-\uFEFC]/;
32
34
  var RTL_CHAR_REGEX = /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/g;
33
- var MIN_RTL_RATIO = 0.3;
35
+ var MIN_RTL_RATIO = 0.25;
34
36
 
35
37
  // ─── State ───────────────────────────────────────────────
36
38
  var isEnabled = getStoredState();
37
39
  var observer = null;
38
40
  var panelVisible = false;
41
+ var hoverTimeout = null;
39
42
 
40
43
  // ─── CSS ─────────────────────────────────────────────────
41
44
  var CSS = `
42
45
  @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;700&display=swap');
43
46
 
44
- /* === Global RTL Mode === */
45
- html.agy-rtl-active {
46
- direction: rtl !important;
47
- }
48
- html.agy-rtl-active body {
47
+ /* RTL styling applied per-element, NOT globally */
48
+ [${PROCESSED_ATTR}="true"] {
49
49
  direction: rtl !important;
50
50
  text-align: right !important;
51
- }
52
-
53
- /* Keep code and technical elements LTR */
54
- html.agy-rtl-active code,
55
- html.agy-rtl-active pre,
56
- html.agy-rtl-active .monaco-editor,
57
- html.agy-rtl-active .monaco-tokenized-source,
58
- html.agy-rtl-active [class*="CodeMirror"],
59
- html.agy-rtl-active input[type="text"],
60
- html.agy-rtl-active input[type="url"],
61
- html.agy-rtl-active input[type="email"],
62
- html.agy-rtl-active textarea.inputarea,
63
- html.agy-rtl-active .terminal,
64
- html.agy-rtl-active .xterm {
65
- direction: ltr !important;
66
- text-align: left !important;
67
- unicode-bidi: isolate !important;
68
- }
69
-
70
- /* Font for RTL text */
71
- html.agy-rtl-active [${PROCESSED_ATTR}="true"] {
51
+ unicode-bidi: plaintext !important;
72
52
  font-family: 'Vazirmatn', system-ui, -apple-system, 'Segoe UI', sans-serif !important;
73
- line-height: 1.85 !important;
74
- word-spacing: 0.02em;
53
+ line-height: 1.8 !important;
75
54
  }
76
55
 
77
- /* Preserve code inside RTL blocks */
56
+ /* Keep code LTR inside RTL blocks */
78
57
  [${PROCESSED_ATTR}="true"] code,
79
58
  [${PROCESSED_ATTR}="true"] pre {
80
59
  direction: ltr !important;
@@ -95,40 +74,42 @@ function _agyRtlRendererMain() {
95
74
  /* === Status Bar Icon === */
96
75
  .agy-rtl-status-icon {
97
76
  position: fixed;
98
- bottom: 2px;
99
- right: 70px;
77
+ bottom: 6px;
78
+ right: 16px;
100
79
  z-index: 99999;
101
80
  display: flex;
102
81
  align-items: center;
103
82
  justify-content: center;
104
- gap: 4px;
105
- height: 20px;
106
- padding: 0 8px;
83
+ gap: 6px;
84
+ height: 28px;
85
+ padding: 0 12px;
107
86
  cursor: pointer;
108
- border-radius: 3px;
87
+ border-radius: 14px;
109
88
  user-select: none;
110
89
  -webkit-user-select: none;
111
- transition: background .15s, opacity .15s;
112
- opacity: .75;
90
+ transition: background .2s, opacity .2s, box-shadow .2s;
91
+ opacity: .6;
113
92
  font-family: -apple-system, 'Segoe UI', system-ui, sans-serif;
114
- font-size: 11px;
115
- font-weight: 500;
116
- letter-spacing: .02em;
117
- color: #999;
118
- background: transparent;
93
+ font-size: 12px;
94
+ font-weight: 600;
95
+ letter-spacing: .03em;
96
+ color: #888;
97
+ background: rgba(128,128,128,.08);
119
98
  }
120
99
  .agy-rtl-status-icon:hover {
121
100
  opacity: 1;
122
- background: rgba(255,255,255,.08);
123
- color: #ddd;
101
+ background: rgba(128,128,128,.15);
102
+ color: #aaa;
124
103
  }
125
104
  .agy-rtl-status-icon.active {
126
- color: #6cb6ff;
127
- opacity: .9;
105
+ color: #5ba0d6;
106
+ opacity: .85;
107
+ background: rgba(91,160,214,.08);
128
108
  }
129
109
  .agy-rtl-status-icon.active:hover {
130
110
  opacity: 1;
131
- color: #8ecaff;
111
+ background: rgba(91,160,214,.15);
112
+ box-shadow: 0 0 12px rgba(91,160,214,.12);
132
113
  }
133
114
  .agy-rtl-status-icon svg {
134
115
  flex-shrink: 0;
@@ -138,20 +119,22 @@ function _agyRtlRendererMain() {
138
119
  .agy-rtl-panel {
139
120
  position: fixed;
140
121
  z-index: 100000;
141
- width: 280px;
122
+ width: 260px;
142
123
  background: #1e1e1e;
143
- border: 1px solid #383838;
124
+ border: 1px solid #333;
144
125
  border-radius: 10px;
145
126
  box-shadow: 0 12px 40px rgba(0,0,0,.5), 0 0 0 1px rgba(255,255,255,.04);
146
127
  padding: 0;
147
128
  opacity: 0;
148
129
  visibility: hidden;
149
130
  transform: translateY(6px);
150
- transition: opacity .18s ease, transform .18s ease, visibility .18s ease;
131
+ transition: opacity .15s ease, transform .15s ease, visibility .15s ease;
151
132
  font-family: -apple-system, 'Segoe UI', system-ui, sans-serif;
152
133
  font-size: 13px;
153
134
  color: #ccc;
154
135
  overflow: hidden;
136
+ direction: ltr !important;
137
+ text-align: left !important;
155
138
  }
156
139
  .agy-rtl-panel.visible {
157
140
  opacity: 1;
@@ -181,11 +164,6 @@ function _agyRtlRendererMain() {
181
164
  font-size: 13px;
182
165
  color: #b0b0b0;
183
166
  }
184
- .agy-rtl-panel-sep {
185
- height: 1px;
186
- background: #2a2a2a;
187
- margin: 4px 0;
188
- }
189
167
 
190
168
  /* Toggle */
191
169
  .agy-rtl-switch {
@@ -234,7 +212,7 @@ function _agyRtlRendererMain() {
234
212
  display: inline-flex;
235
213
  align-items: center;
236
214
  gap: 5px;
237
- color: #6c7986;
215
+ color: #555;
238
216
  text-decoration: none;
239
217
  font-size: 11px;
240
218
  transition: color .15s;
@@ -245,8 +223,8 @@ function _agyRtlRendererMain() {
245
223
  // ─── Helpers ─────────────────────────────────────────────
246
224
 
247
225
  function getStoredState() {
248
- try { var s = localStorage.getItem(STORAGE_KEY); return s === null ? true : s === 'true'; }
249
- catch (e) { return true; }
226
+ try { var s = localStorage.getItem(STORAGE_KEY); return s === null ? false : s === 'true'; }
227
+ catch (e) { return false; }
250
228
  }
251
229
  function setStoredState(v) {
252
230
  try { localStorage.setItem(STORAGE_KEY, v ? 'true' : 'false'); } catch (e) {}
@@ -261,23 +239,36 @@ function _agyRtlRendererMain() {
261
239
  }
262
240
  function applyRTL(el) {
263
241
  if (el.getAttribute(PROCESSED_ATTR)) return;
264
- if (isRTL(el.textContent || '')) {
242
+ // Skip the RTL panel/icon itself
243
+ if (el.closest && (el.closest('#' + PANEL_ID) || el.closest('#' + STATUS_ICON_ID))) return;
244
+ var directText = Array.from(el.childNodes)
245
+ .filter(n => n.nodeType === Node.TEXT_NODE)
246
+ .map(n => n.textContent)
247
+ .join(' ');
248
+ if (directText.trim().length > 0 && isRTL(directText)) {
265
249
  el.setAttribute(PROCESSED_ATTR, 'true');
266
- el.style.unicodeBidi = 'plaintext';
267
250
  }
268
251
  }
269
252
  function removeRTL(el) {
270
253
  if (el.getAttribute(PROCESSED_ATTR)) {
271
254
  el.removeAttribute(PROCESSED_ATTR);
272
- el.style.unicodeBidi = '';
255
+ el.removeAttribute('dir'); // Ensure dir is cleaned up if it was left over from older versions
273
256
  }
274
257
  }
275
258
 
276
- // Target all text-bearing elements
277
- var SELECTORS = 'p,li,h1,h2,h3,h4,h5,h6,blockquote,td,th,span,div,a,label,button,[class*="title"],[class*="label"],[class*="name"],[class*="text"],[class*="message"],[class*="content"],[class*="description"]';
259
+ // Wide selectors — catch all text-bearing elements
260
+ var SELECTORS = [
261
+ 'p', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
262
+ 'blockquote', 'td', 'th', 'span', 'a', 'label',
263
+ 'button',
264
+ ].join(',');
278
265
 
279
266
  function scanElement(root) {
280
267
  if (!root || root.nodeType !== 1) return;
268
+ // Skip our own UI
269
+ if (root.id === PANEL_ID || root.id === STATUS_ICON_ID) return;
270
+ if (root.closest && (root.closest('#' + PANEL_ID) || root.closest('#' + STATUS_ICON_ID))) return;
271
+
281
272
  try { if (root.matches && root.matches(SELECTORS)) applyRTL(root); } catch (e) {}
282
273
  try { root.querySelectorAll(SELECTORS).forEach(applyRTL); } catch (e) {}
283
274
  }
@@ -289,9 +280,6 @@ function _agyRtlRendererMain() {
289
280
 
290
281
  function startObserver() {
291
282
  if (observer) return;
292
- // Apply global RTL
293
- document.documentElement.classList.add('agy-rtl-active');
294
-
295
283
  observer = new MutationObserver(function (muts) {
296
284
  if (!isEnabled) return;
297
285
  muts.forEach(function (m) {
@@ -305,7 +293,6 @@ function _agyRtlRendererMain() {
305
293
 
306
294
  function stopObserver() {
307
295
  if (observer) { observer.disconnect(); observer = null; }
308
- document.documentElement.classList.remove('agy-rtl-active');
309
296
  clearAllRTL();
310
297
  }
311
298
 
@@ -327,10 +314,10 @@ function _agyRtlRendererMain() {
327
314
  icon.className = 'agy-rtl-status-icon' + (isEnabled ? ' active' : '');
328
315
  icon.setAttribute('title', 'Antigravity Smart RTL');
329
316
 
330
- // Globe + arrow SVG icon
317
+ // Globe SVG
331
318
  var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
332
- svg.setAttribute('width', '14');
333
- svg.setAttribute('height', '14');
319
+ svg.setAttribute('width', '15');
320
+ svg.setAttribute('height', '15');
334
321
  svg.setAttribute('viewBox', '0 0 24 24');
335
322
  svg.setAttribute('fill', 'none');
336
323
  svg.setAttribute('stroke', 'currentColor');
@@ -338,33 +325,41 @@ function _agyRtlRendererMain() {
338
325
  svg.setAttribute('stroke-linecap', 'round');
339
326
  svg.setAttribute('stroke-linejoin', 'round');
340
327
 
341
- // Globe circle
342
328
  var circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
343
- circle.setAttribute('cx', '12');
344
- circle.setAttribute('cy', '12');
345
- circle.setAttribute('r', '10');
329
+ circle.setAttribute('cx', '12'); circle.setAttribute('cy', '12'); circle.setAttribute('r', '10');
346
330
  svg.appendChild(circle);
347
-
348
- // Globe horizontal line
349
331
  var line1 = document.createElementNS('http://www.w3.org/2000/svg', 'line');
350
332
  line1.setAttribute('x1', '2'); line1.setAttribute('y1', '12');
351
333
  line1.setAttribute('x2', '22'); line1.setAttribute('y2', '12');
352
334
  svg.appendChild(line1);
353
-
354
- // Globe vertical ellipse
355
- var ellipse = document.createElementNS('http://www.w3.org/2000/svg', 'path');
356
- ellipse.setAttribute('d', 'M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z');
357
- svg.appendChild(ellipse);
358
-
335
+ var ell = document.createElementNS('http://www.w3.org/2000/svg', 'path');
336
+ ell.setAttribute('d', 'M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z');
337
+ svg.appendChild(ell);
359
338
  icon.appendChild(svg);
360
339
 
361
- // Label text
362
340
  var lbl = document.createElement('span');
363
341
  lbl.textContent = 'RTL';
364
342
  lbl.style.cssText = 'direction:ltr!important;';
365
343
  icon.appendChild(lbl);
366
344
 
367
- icon.addEventListener('click', function (e) { e.stopPropagation(); togglePanel(); });
345
+ // Hover to open panel
346
+ icon.addEventListener('mouseenter', function () {
347
+ clearTimeout(hoverTimeout);
348
+ hoverTimeout = setTimeout(function () { showPanel(); }, 200);
349
+ });
350
+ icon.addEventListener('mouseleave', function () {
351
+ clearTimeout(hoverTimeout);
352
+ hoverTimeout = setTimeout(function () {
353
+ var panel = document.getElementById(PANEL_ID);
354
+ if (panel && !panel.matches(':hover')) hidePanel();
355
+ }, 300);
356
+ });
357
+ // Also toggle on click
358
+ icon.addEventListener('click', function (e) {
359
+ e.stopPropagation();
360
+ if (panelVisible) hidePanel(); else showPanel();
361
+ });
362
+
368
363
  document.body.appendChild(icon);
369
364
  }
370
365
 
@@ -374,7 +369,12 @@ function _agyRtlRendererMain() {
374
369
  var panel = document.createElement('div');
375
370
  panel.id = PANEL_ID;
376
371
  panel.className = 'agy-rtl-panel';
377
- panel.style.cssText = 'direction:ltr!important;text-align:left!important;';
372
+
373
+ // Keep panel open on hover
374
+ panel.addEventListener('mouseenter', function () { clearTimeout(hoverTimeout); });
375
+ panel.addEventListener('mouseleave', function () {
376
+ hoverTimeout = setTimeout(function () { hidePanel(); }, 300);
377
+ });
378
378
 
379
379
  // Header
380
380
  var header = document.createElement('div');
@@ -389,7 +389,6 @@ function _agyRtlRendererMain() {
389
389
  var body = document.createElement('div');
390
390
  body.className = 'agy-rtl-panel-body';
391
391
 
392
- // Toggle row
393
392
  var row = document.createElement('div');
394
393
  row.className = 'agy-rtl-panel-row';
395
394
  var label = document.createElement('span');
@@ -411,7 +410,7 @@ function _agyRtlRendererMain() {
411
410
  body.appendChild(row);
412
411
  panel.appendChild(body);
413
412
 
414
- // Footer with GitHub
413
+ // Footer
415
414
  var footer = document.createElement('div');
416
415
  footer.className = 'agy-rtl-panel-footer';
417
416
  var ghLink = document.createElement('a');
@@ -421,10 +420,8 @@ function _agyRtlRendererMain() {
421
420
  ghLink.rel = 'noopener noreferrer';
422
421
 
423
422
  var ghSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
424
- ghSvg.setAttribute('width', '12');
425
- ghSvg.setAttribute('height', '12');
426
- ghSvg.setAttribute('viewBox', '0 0 16 16');
427
- ghSvg.setAttribute('fill', 'currentColor');
423
+ ghSvg.setAttribute('width', '12'); ghSvg.setAttribute('height', '12');
424
+ ghSvg.setAttribute('viewBox', '0 0 16 16'); ghSvg.setAttribute('fill', 'currentColor');
428
425
  var ghP = document.createElementNS('http://www.w3.org/2000/svg', 'path');
429
426
  ghP.setAttribute('d', 'M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z');
430
427
  ghSvg.appendChild(ghP);
@@ -435,35 +432,35 @@ function _agyRtlRendererMain() {
435
432
  footer.appendChild(ghLink);
436
433
  panel.appendChild(footer);
437
434
 
438
- // Events
435
+ // Toggle event
439
436
  cb.addEventListener('change', function () {
440
437
  isEnabled = cb.checked;
441
438
  setStoredState(isEnabled);
442
439
  updateState();
443
440
  });
444
441
 
442
+ // Close on Escape
443
+ document.addEventListener('keydown', function (e) {
444
+ if (e.key === 'Escape' && panelVisible) hidePanel();
445
+ });
446
+
447
+ // Close on outside click
445
448
  document.addEventListener('click', function (e) {
446
449
  var p = document.getElementById(PANEL_ID);
447
450
  var ic = document.getElementById(STATUS_ICON_ID);
448
451
  if (p && panelVisible && !p.contains(e.target) && !ic.contains(e.target)) hidePanel();
449
452
  });
450
453
 
451
- document.addEventListener('keydown', function (e) {
452
- if (e.key === 'Escape' && panelVisible) hidePanel();
453
- });
454
-
455
454
  document.body.appendChild(panel);
456
455
  }
457
456
 
458
- function togglePanel() { panelVisible ? hidePanel() : showPanel(); }
459
-
460
457
  function showPanel() {
461
458
  var panel = document.getElementById(PANEL_ID);
462
459
  if (!panel) { createPanel(); panel = document.getElementById(PANEL_ID); }
463
460
  var icon = document.getElementById(STATUS_ICON_ID);
464
461
  if (icon) {
465
462
  var r = icon.getBoundingClientRect();
466
- panel.style.bottom = (window.innerHeight - r.top + 6) + 'px';
463
+ panel.style.bottom = (window.innerHeight - r.top + 8) + 'px';
467
464
  panel.style.right = (window.innerWidth - r.right) + 'px';
468
465
  }
469
466
  panel.classList.add('visible');
package/src/patcher.js CHANGED
@@ -75,6 +75,19 @@ async function patchAsar(installation, spinner) {
75
75
  asar.extractAll(asarPath, tmpDir);
76
76
  spinner.succeed('Extracted successfully.');
77
77
 
78
+ // 2.5 Check if it's the IDE
79
+ const pkgJsonPath = path.join(tmpDir, 'package.json');
80
+ if (fs.existsSync(pkgJsonPath)) {
81
+ try {
82
+ const pkg = JSON.parse(await fs.readFile(pkgJsonPath, 'utf-8'));
83
+ if (pkg.name && (pkg.name.toLowerCase().includes('ide') || pkg.name.toLowerCase().includes('antigravity-ide'))) {
84
+ spinner.warn('Antigravity IDE detected. Skipping (this patcher is only for the main app).');
85
+ await fs.remove(tmpDir);
86
+ return;
87
+ }
88
+ } catch (e) {}
89
+ }
90
+
78
91
  // 3. Find preload.js
79
92
  spinner.start('Searching for preload.js...');
80
93
  const preloadPath = findPreloadJs(tmpDir);