antigravity-rtl-patcher 2.0.0 → 2.1.0

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/README.md CHANGED
@@ -7,9 +7,9 @@
7
7
  ![License](https://img.shields.io/badge/LICENSE-MIT-brightgreen?style=for-the-badge)
8
8
  ![Node](https://img.shields.io/badge/NODE-%3E%3D16-339933?style=for-the-badge&logo=node.js&logoColor=white)
9
9
 
10
- **Intelligent RTL & Persian Typography Engine for Antigravity IDE**
10
+ **Intelligent RTL & Persian Typography Engine for Antigravity**
11
11
 
12
- *One command. Full right-to-left support. Zero config.*
12
+ *One command. Full right-to-left support. Toggle on/off anytime.*
13
13
 
14
14
  </div>
15
15
 
@@ -17,13 +17,14 @@
17
17
 
18
18
  ## 🧬 How It Works
19
19
 
20
- Antigravity IDE is built on Electron. This CLI tool directly patches the IDE's core workbench files to inject a smart **MutationObserver** engine that:
20
+ Antigravity is built on Electron. This CLI tool directly patches the app's core workbench files to inject a smart **MutationObserver** engine that:
21
21
 
22
22
  1. šŸ” **Watches** the DOM for new chat messages and AI responses in real-time
23
23
  2. 🧠 **Detects** Persian/Arabic/Hebrew text using Unicode analysis with ratio-based scoring
24
24
  3. šŸŽØ **Applies** `direction: rtl` and `unicode-bidi: plaintext` only where needed
25
25
  4. āœļø **Injects** the beautiful **Vazirmatn** font for perfect Persian/Arabic readability
26
26
  5. šŸ”’ **Isolates** English code blocks inside RTL text so they don't break
27
+ 6. šŸŽ›ļø **Toggle Panel** — Enable/disable RTL support anytime via the in-app panel
27
28
 
28
29
  ---
29
30
 
@@ -42,7 +43,17 @@ sudo npx antigravity-rtl-patcher patch
42
43
  npx antigravity-rtl-patcher patch
43
44
  ```
44
45
 
45
- That's it. Restart Antigravity IDE and enjoy native RTL support. ✨
46
+ That's it. Restart Antigravity and enjoy native RTL support. ✨
47
+
48
+ ---
49
+
50
+ ## šŸŽ›ļø In-App Toggle
51
+
52
+ After patching, a small **RTL icon** appears at the bottom of Antigravity's status bar. Click it to open the settings panel:
53
+
54
+ - **Enabled** — Toggle RTL support on/off instantly
55
+ - Settings are saved automatically and persist across restarts
56
+ - Works on every page — enable/disable applies everywhere
46
57
 
47
58
  ---
48
59
 
@@ -50,16 +61,16 @@ That's it. Restart Antigravity IDE and enjoy native RTL support. ✨
50
61
 
51
62
  | Command | Description |
52
63
  |---------|-------------|
53
- | `agy-rtl patch` | Inject RTL support into Antigravity IDE |
64
+ | `agy-rtl patch` | Inject RTL support into Antigravity |
54
65
  | `agy-rtl restore` | Remove patch and restore original files |
55
- | `agy-rtl status` | Check if IDE is currently patched |
66
+ | `agy-rtl status` | Check if Antigravity is currently patched |
56
67
  | `agy-rtl patch --path /custom/path` | Patch a custom installation directory |
57
68
 
58
69
  ---
59
70
 
60
- ## šŸ”„ After IDE Updates
71
+ ## šŸ”„ After Updates
61
72
 
62
- When Antigravity IDE updates, the patch may be overwritten. Simply run the patch command again:
73
+ When Antigravity updates, the patch may be overwritten. Simply run the patch command again:
63
74
 
64
75
  ```bash
65
76
  npx antigravity-rtl-patcher patch
@@ -78,8 +89,8 @@ antigravity-rtl-patcher/
78
89
  │ ā”œā”€ā”€ paths.js # OS-aware installation path detection
79
90
  │ └── utils.js # Permission checks & helpers
80
91
  ā”œā”€ā”€ payload/
81
- │ ā”œā”€ā”€ rtl-engine.js # MutationObserver RTL detection engine
82
- │ └── styles.css # Vazirmatn font + RTL typography fixes
92
+ │ ā”œā”€ā”€ rtl-engine.js # MutationObserver RTL engine + Toggle UI
93
+ │ └── styles.css # Vazirmatn font + RTL + Panel styles
83
94
  ā”œā”€ā”€ package.json
84
95
  └── README.md
85
96
  ```
@@ -88,7 +99,7 @@ antigravity-rtl-patcher/
88
99
 
89
100
  | Type | Description | Method |
90
101
  |------|-------------|--------|
91
- | **Unpacked** | IDE with `resources/app/` directory | Direct file modification |
102
+ | **Unpacked** | App with `resources/app/` directory | Direct file modification |
92
103
  | **ASAR** | App bundled as `app.asar` | Extract → Patch → Repack |
93
104
 
94
105
  ---
package/bin/cli.js CHANGED
@@ -6,20 +6,20 @@ const { patch, restore, status } = require('../src/patcher');
6
6
 
7
7
  const BANNER = `
8
8
  ${chalk.cyan('╔══════════════════════════════════════════════╗')}
9
- ${chalk.cyan('ā•‘')} ${chalk.bold.white('🌌 Antigravity RTL Patcher')} ${chalk.gray('v2.0.0')} ${chalk.cyan('ā•‘')}
10
- ${chalk.cyan('ā•‘')} ${chalk.gray('Intelligent RTL support for Antigravity IDE')} ${chalk.cyan('ā•‘')}
9
+ ${chalk.cyan('ā•‘')} ${chalk.bold.white('🌌 Antigravity RTL Patcher')} ${chalk.gray('v2.1.0')} ${chalk.cyan('ā•‘')}
10
+ ${chalk.cyan('ā•‘')} ${chalk.gray('Intelligent RTL support for Antigravity')} ${chalk.cyan('ā•‘')}
11
11
  ${chalk.cyan('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•')}
12
12
  `;
13
13
 
14
14
  program
15
15
  .name('agy-rtl')
16
- .description('RTL & Typography patcher for Antigravity IDE')
17
- .version('2.0.0');
16
+ .description('RTL & Typography patcher for Antigravity')
17
+ .version('2.1.0');
18
18
 
19
19
  program
20
20
  .command('patch')
21
- .description('Inject RTL support into Antigravity IDE')
22
- .option('-p, --path <path>', 'Custom path to Antigravity IDE installation')
21
+ .description('Inject RTL support into Antigravity')
22
+ .option('-p, --path <path>', 'Custom path to Antigravity installation')
23
23
  .action(async (options) => {
24
24
  console.log(BANNER);
25
25
  try {
@@ -32,8 +32,8 @@ program
32
32
 
33
33
  program
34
34
  .command('restore')
35
- .description('Restore Antigravity IDE to its original state')
36
- .option('-p, --path <path>', 'Custom path to Antigravity IDE installation')
35
+ .description('Restore Antigravity to its original state')
36
+ .option('-p, --path <path>', 'Custom path to Antigravity installation')
37
37
  .action(async (options) => {
38
38
  console.log(BANNER);
39
39
  try {
@@ -46,8 +46,8 @@ program
46
46
 
47
47
  program
48
48
  .command('status')
49
- .description('Check if Antigravity IDE is currently patched')
50
- .option('-p, --path <path>', 'Custom path to Antigravity IDE installation')
49
+ .description('Check if Antigravity is currently patched')
50
+ .option('-p, --path <path>', 'Custom path to Antigravity installation')
51
51
  .action(async (options) => {
52
52
  console.log(BANNER);
53
53
  try {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "antigravity-rtl-patcher",
3
- "version": "2.0.0",
4
- "description": "🌌 Intelligent RTL & Persian/Arabic typography patcher for Antigravity IDE",
3
+ "version": "2.1.0",
4
+ "description": "🌌 Intelligent RTL & Persian/Arabic typography patcher for Antigravity",
5
5
  "main": "src/patcher.js",
6
6
  "bin": {
7
7
  "antigravity-rtl-patcher": "./bin/cli.js",
@@ -22,8 +22,6 @@
22
22
  "patcher",
23
23
  "electron",
24
24
  "asar",
25
- "ide",
26
- "vscode",
27
25
  "right-to-left",
28
26
  "bidirectional"
29
27
  ],
@@ -1,20 +1,30 @@
1
1
  /**
2
- * AGY RTL Engine v2.0.0
3
- * Intelligent RTL detection & styling for Antigravity IDE
4
- *
2
+ * AGY RTL Engine v2.1.0
3
+ * Intelligent RTL detection & styling for Antigravity
4
+ *
5
5
  * This script runs inside the Electron renderer process.
6
6
  * It uses MutationObserver to watch for DOM changes and
7
7
  * automatically applies RTL direction to Persian/Arabic/Hebrew text.
8
- *
8
+ *
9
+ * Features:
10
+ * - Toggle panel UI with Enable/Disable switch
11
+ * - Persistent state via localStorage
12
+ * - Real-time apply/remove across all pages
13
+ *
9
14
  * CSP Compatible: No use of innerHTML, eval, or inline event handlers.
10
15
  */
11
16
  (function () {
12
17
  'use strict';
13
18
 
19
+ // ─── Configuration ───────────────────────────────────────
20
+ const STORAGE_KEY = 'agy-rtl-enabled';
21
+ const PANEL_ID = 'agy-rtl-panel';
22
+ const TOGGLE_BTN_ID = 'agy-rtl-toggle-btn';
23
+ const STATUS_ICON_ID = 'agy-rtl-status-icon';
24
+ const GITHUB_URL = 'https://github.com/VaFa1726/antigravity-rtl-patcher';
25
+
14
26
  // Unicode ranges for RTL scripts
15
27
  const RTL_REGEX = /[\u0591-\u07FF\u200F\u202B\u202E\uFB1D-\uFDFD\uFE70-\uFEFC]/;
16
-
17
- // Minimum ratio of RTL characters to consider text as RTL
18
28
  const RTL_CHAR_REGEX = /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/g;
19
29
  const MIN_RTL_RATIO = 0.3;
20
30
 
@@ -54,20 +64,42 @@
54
64
  'p', 'li', 'h1', 'h2', 'h3', 'h4', 'blockquote',
55
65
  ].join(', ');
56
66
 
67
+ // ─── State ───────────────────────────────────────────────
68
+ var isEnabled = getStoredState();
69
+ var observer = null;
70
+ var panelVisible = false;
71
+
72
+ // ─── Helpers ─────────────────────────────────────────────
73
+
74
+ function getStoredState() {
75
+ try {
76
+ var stored = localStorage.getItem(STORAGE_KEY);
77
+ if (stored === null) return true; // Default: enabled
78
+ return stored === 'true';
79
+ } catch (e) {
80
+ return true;
81
+ }
82
+ }
83
+
84
+ function setStoredState(val) {
85
+ try {
86
+ localStorage.setItem(STORAGE_KEY, val ? 'true' : 'false');
87
+ } catch (e) {
88
+ // Ignore storage errors
89
+ }
90
+ }
91
+
57
92
  /**
58
93
  * Determine if a text string is primarily RTL.
59
94
  */
60
95
  function isRTL(text) {
61
96
  if (!text || text.trim().length === 0) return false;
62
-
63
- // Quick check: does it contain any RTL character at all?
64
97
  if (!RTL_REGEX.test(text)) return false;
65
98
 
66
- // Count RTL characters vs total alphabetic characters
67
- const rtlMatches = text.match(RTL_CHAR_REGEX);
99
+ var rtlMatches = text.match(RTL_CHAR_REGEX);
68
100
  if (!rtlMatches) return false;
69
101
 
70
- const alphaChars = text.replace(/[\s\d\W]/g, '').length;
102
+ var alphaChars = text.replace(/[\s\d\W]/g, '').length;
71
103
  if (alphaChars === 0) return false;
72
104
 
73
105
  return (rtlMatches.length / alphaChars) >= MIN_RTL_RATIO;
@@ -79,7 +111,7 @@
79
111
  function applyRTL(element) {
80
112
  if (element.getAttribute(PROCESSED_ATTR)) return;
81
113
 
82
- const text = element.textContent || '';
114
+ var text = element.textContent || '';
83
115
  if (isRTL(text)) {
84
116
  element.setAttribute(PROCESSED_ATTR, 'true');
85
117
  element.style.direction = 'rtl';
@@ -88,46 +120,65 @@
88
120
  }
89
121
  }
90
122
 
123
+ /**
124
+ * Remove RTL styling from an element.
125
+ */
126
+ function removeRTL(element) {
127
+ if (element.getAttribute(PROCESSED_ATTR)) {
128
+ element.removeAttribute(PROCESSED_ATTR);
129
+ element.style.direction = '';
130
+ element.style.textAlign = '';
131
+ element.style.unicodeBidi = '';
132
+ }
133
+ }
134
+
91
135
  /**
92
136
  * Scan a subtree for elements that need RTL.
93
137
  */
94
138
  function scanElement(root) {
95
139
  if (!root || root.nodeType !== Node.ELEMENT_NODE) return;
96
140
 
97
- // Check if the root itself matches
98
141
  try {
99
142
  if (root.matches && root.matches(TARGET_SELECTORS)) {
100
143
  applyRTL(root);
101
144
  }
102
- } catch (e) {
103
- // Ignore selector errors
104
- }
145
+ } catch (e) { /* Ignore */ }
105
146
 
106
- // Scan children
107
147
  try {
108
- const elements = root.querySelectorAll(TARGET_SELECTORS);
148
+ var elements = root.querySelectorAll(TARGET_SELECTORS);
109
149
  elements.forEach(function (el) {
110
150
  applyRTL(el);
111
151
  });
112
- } catch (e) {
113
- // Ignore errors
114
- }
152
+ } catch (e) { /* Ignore */ }
115
153
  }
116
154
 
117
155
  /**
118
- * Initialize the MutationObserver.
156
+ * Remove RTL from all processed elements.
119
157
  */
120
- function initObserver() {
121
- var observer = new MutationObserver(function (mutations) {
158
+ function clearAllRTL() {
159
+ try {
160
+ var elements = document.querySelectorAll('[' + PROCESSED_ATTR + ']');
161
+ elements.forEach(function (el) {
162
+ removeRTL(el);
163
+ });
164
+ } catch (e) { /* Ignore */ }
165
+ }
166
+
167
+ // ─── MutationObserver ────────────────────────────────────
168
+
169
+ function startObserver() {
170
+ if (observer) return;
171
+
172
+ observer = new MutationObserver(function (mutations) {
173
+ if (!isEnabled) return;
174
+
122
175
  mutations.forEach(function (mutation) {
123
- // Handle added nodes
124
176
  mutation.addedNodes.forEach(function (node) {
125
177
  if (node.nodeType === Node.ELEMENT_NODE) {
126
178
  scanElement(node);
127
179
  }
128
180
  });
129
181
 
130
- // Handle text content changes
131
182
  if (mutation.type === 'characterData' && mutation.target.parentElement) {
132
183
  applyRTL(mutation.target.parentElement);
133
184
  }
@@ -144,13 +195,267 @@
144
195
  scanElement(document.body);
145
196
  }
146
197
 
198
+ function stopObserver() {
199
+ if (observer) {
200
+ observer.disconnect();
201
+ observer = null;
202
+ }
203
+ clearAllRTL();
204
+ }
205
+
206
+ // ─── Toggle Panel UI ────────────────────────────────────
207
+
208
+ function createStatusIcon() {
209
+ // Check if icon already exists
210
+ if (document.getElementById(STATUS_ICON_ID)) return;
211
+
212
+ var icon = document.createElement('div');
213
+ icon.id = STATUS_ICON_ID;
214
+ icon.className = 'agy-rtl-status-icon';
215
+ icon.setAttribute('role', 'button');
216
+ icon.setAttribute('tabindex', '0');
217
+ icon.setAttribute('title', 'Antigravity Smart RTL');
218
+
219
+ // RTL icon SVG
220
+ var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
221
+ svg.setAttribute('width', '16');
222
+ svg.setAttribute('height', '16');
223
+ svg.setAttribute('viewBox', '0 0 24 24');
224
+ svg.setAttribute('fill', 'none');
225
+ svg.setAttribute('stroke', 'currentColor');
226
+ svg.setAttribute('stroke-width', '2');
227
+ svg.setAttribute('stroke-linecap', 'round');
228
+ svg.setAttribute('stroke-linejoin', 'round');
229
+
230
+ // Pilcrow/RTL icon path
231
+ var path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
232
+ path1.setAttribute('d', 'M16 4H9.5a3.5 3.5 0 1 0 0 7H12');
233
+ svg.appendChild(path1);
234
+
235
+ var path2 = document.createElementNS('http://www.w3.org/2000/svg', 'line');
236
+ path2.setAttribute('x1', '12');
237
+ path2.setAttribute('y1', '4');
238
+ path2.setAttribute('x2', '12');
239
+ path2.setAttribute('y2', '20');
240
+ svg.appendChild(path2);
241
+
242
+ var path3 = document.createElementNS('http://www.w3.org/2000/svg', 'line');
243
+ path3.setAttribute('x1', '16');
244
+ path3.setAttribute('y1', '4');
245
+ path3.setAttribute('x2', '16');
246
+ path3.setAttribute('y2', '20');
247
+ svg.appendChild(path3);
248
+
249
+ icon.appendChild(svg);
250
+
251
+ // Status dot
252
+ var dot = document.createElement('span');
253
+ dot.className = 'agy-rtl-status-dot';
254
+ if (isEnabled) dot.classList.add('active');
255
+ icon.appendChild(dot);
256
+
257
+ icon.addEventListener('click', function (e) {
258
+ e.stopPropagation();
259
+ togglePanel();
260
+ });
261
+
262
+ icon.addEventListener('keydown', function (e) {
263
+ if (e.key === 'Enter' || e.key === ' ') {
264
+ e.preventDefault();
265
+ e.stopPropagation();
266
+ togglePanel();
267
+ }
268
+ });
269
+
270
+ // Try to insert into the status bar, fallback to body
271
+ var statusBar = document.querySelector('.statusbar-item.right') ||
272
+ document.querySelector('.statusbar') ||
273
+ document.querySelector('[id*="statusbar"]') ||
274
+ document.querySelector('.footer') ||
275
+ document.body;
276
+
277
+ if (statusBar === document.body) {
278
+ // Position fixed at bottom-right
279
+ icon.classList.add('agy-rtl-fixed');
280
+ document.body.appendChild(icon);
281
+ } else {
282
+ statusBar.appendChild(icon);
283
+ }
284
+ }
285
+
286
+ function createPanel() {
287
+ if (document.getElementById(PANEL_ID)) return;
288
+
289
+ var panel = document.createElement('div');
290
+ panel.id = PANEL_ID;
291
+ panel.className = 'agy-rtl-panel';
292
+
293
+ // ── Header ──
294
+ var header = document.createElement('div');
295
+ header.className = 'agy-rtl-panel-header';
296
+
297
+ var title = document.createElement('span');
298
+ title.className = 'agy-rtl-panel-title';
299
+ title.textContent = 'Antigravity Smart RTL';
300
+ header.appendChild(title);
301
+ panel.appendChild(header);
302
+
303
+ // ── Toggle Row ──
304
+ var row = document.createElement('div');
305
+ row.className = 'agy-rtl-panel-row';
306
+
307
+ var label = document.createElement('span');
308
+ label.className = 'agy-rtl-panel-label';
309
+ label.textContent = 'Enabled';
310
+ row.appendChild(label);
311
+
312
+ var toggleWrapper = document.createElement('label');
313
+ toggleWrapper.className = 'agy-rtl-switch';
314
+
315
+ var checkbox = document.createElement('input');
316
+ checkbox.type = 'checkbox';
317
+ checkbox.id = TOGGLE_BTN_ID;
318
+ checkbox.checked = isEnabled;
319
+
320
+ var slider = document.createElement('span');
321
+ slider.className = 'agy-rtl-slider';
322
+
323
+ toggleWrapper.appendChild(checkbox);
324
+ toggleWrapper.appendChild(slider);
325
+ row.appendChild(toggleWrapper);
326
+ panel.appendChild(row);
327
+
328
+ // ── Separator ──
329
+ var sep = document.createElement('div');
330
+ sep.className = 'agy-rtl-panel-sep';
331
+ panel.appendChild(sep);
332
+
333
+ // ── GitHub Link ──
334
+ var ghRow = document.createElement('div');
335
+ ghRow.className = 'agy-rtl-panel-row agy-rtl-panel-gh';
336
+
337
+ var ghLink = document.createElement('a');
338
+ ghLink.className = 'agy-rtl-gh-link';
339
+ ghLink.href = GITHUB_URL;
340
+ ghLink.target = '_blank';
341
+ ghLink.rel = 'noopener noreferrer';
342
+
343
+ // GitHub icon SVG
344
+ var ghSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
345
+ ghSvg.setAttribute('width', '14');
346
+ ghSvg.setAttribute('height', '14');
347
+ ghSvg.setAttribute('viewBox', '0 0 16 16');
348
+ ghSvg.setAttribute('fill', 'currentColor');
349
+
350
+ var ghPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
351
+ ghPath.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');
352
+ ghSvg.appendChild(ghPath);
353
+ ghLink.appendChild(ghSvg);
354
+
355
+ var ghText = document.createElement('span');
356
+ ghText.textContent = ' Star on GitHub';
357
+ ghLink.appendChild(ghText);
358
+
359
+ ghRow.appendChild(ghLink);
360
+ panel.appendChild(ghRow);
361
+
362
+ // ── Event Handlers ──
363
+ checkbox.addEventListener('change', function () {
364
+ isEnabled = checkbox.checked;
365
+ setStoredState(isEnabled);
366
+ updateState();
367
+ });
368
+
369
+ // Close panel when clicking outside
370
+ document.addEventListener('click', function (e) {
371
+ var panelEl = document.getElementById(PANEL_ID);
372
+ var iconEl = document.getElementById(STATUS_ICON_ID);
373
+ if (panelEl && panelVisible && !panelEl.contains(e.target) && !iconEl.contains(e.target)) {
374
+ hidePanel();
375
+ }
376
+ });
377
+
378
+ document.body.appendChild(panel);
379
+ }
380
+
381
+ function togglePanel() {
382
+ if (panelVisible) {
383
+ hidePanel();
384
+ } else {
385
+ showPanel();
386
+ }
387
+ }
388
+
389
+ function showPanel() {
390
+ var panel = document.getElementById(PANEL_ID);
391
+ if (!panel) {
392
+ createPanel();
393
+ panel = document.getElementById(PANEL_ID);
394
+ }
395
+
396
+ // Position the panel above the icon
397
+ var icon = document.getElementById(STATUS_ICON_ID);
398
+ if (icon) {
399
+ var iconRect = icon.getBoundingClientRect();
400
+ panel.style.bottom = (window.innerHeight - iconRect.top + 8) + 'px';
401
+ panel.style.right = (window.innerWidth - iconRect.right) + 'px';
402
+ }
403
+
404
+ panel.classList.add('visible');
405
+ panelVisible = true;
406
+ }
407
+
408
+ function hidePanel() {
409
+ var panel = document.getElementById(PANEL_ID);
410
+ if (panel) {
411
+ panel.classList.remove('visible');
412
+ }
413
+ panelVisible = false;
414
+ }
415
+
416
+ /**
417
+ * Update the UI and RTL state based on isEnabled.
418
+ */
419
+ function updateState() {
420
+ // Update status dot
421
+ var dot = document.querySelector('.agy-rtl-status-dot');
422
+ if (dot) {
423
+ if (isEnabled) {
424
+ dot.classList.add('active');
425
+ } else {
426
+ dot.classList.remove('active');
427
+ }
428
+ }
429
+
430
+ // Update toggle checkbox
431
+ var checkbox = document.getElementById(TOGGLE_BTN_ID);
432
+ if (checkbox) {
433
+ checkbox.checked = isEnabled;
434
+ }
435
+
436
+ // Apply or remove RTL
437
+ if (isEnabled) {
438
+ startObserver();
439
+ } else {
440
+ stopObserver();
441
+ }
442
+ }
443
+
444
+ // ─── Initialization ──────────────────────────────────────
445
+
446
+ function init() {
447
+ createStatusIcon();
448
+ createPanel();
449
+ updateState();
450
+ }
451
+
147
452
  // Start when DOM is ready
148
453
  if (document.readyState === 'loading') {
149
- document.addEventListener('DOMContentLoaded', initObserver);
454
+ document.addEventListener('DOMContentLoaded', init);
150
455
  } else {
151
456
  // DOM already loaded, wait a moment for dynamic content
152
- setTimeout(initObserver, 500);
457
+ setTimeout(init, 500);
153
458
  }
154
459
 
155
- console.log('%c🌌 AGY RTL Engine v2.0.0 loaded', 'color: cyan; font-weight: bold;');
460
+ console.log('%c🌌 AGY RTL Engine v2.1.0 loaded', 'color: cyan; font-weight: bold;');
156
461
  })();
@@ -1,12 +1,13 @@
1
1
  /*
2
- * AGY RTL Styles v2.0.0
3
- * Typography & RTL fixes for Antigravity IDE
2
+ * AGY RTL Styles v2.1.0
3
+ * Typography & RTL fixes + Toggle Panel for Antigravity
4
4
  */
5
5
 
6
6
  /* Vazirmatn — Modern Persian/Arabic font from Google Fonts */
7
7
  @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;700&display=swap');
8
8
 
9
- /* RTL text styling */
9
+ /* ─── RTL Text Styling ───────────────────────────────────── */
10
+
10
11
  [data-agy-rtl="true"] {
11
12
  font-family: 'Vazirmatn', system-ui, -apple-system, 'Segoe UI', sans-serif !important;
12
13
  line-height: 1.85 !important;
@@ -35,3 +36,199 @@
35
36
  [data-agy-rtl="true"] li {
36
37
  list-style-position: inside;
37
38
  }
39
+
40
+ /* ─── Status Bar Icon ────────────────────────────────────── */
41
+
42
+ .agy-rtl-status-icon {
43
+ display: inline-flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ position: relative;
47
+ width: 28px;
48
+ height: 22px;
49
+ cursor: pointer;
50
+ color: var(--vscode-statusBar-foreground, #ccc);
51
+ opacity: 0.85;
52
+ transition: opacity 0.2s ease, color 0.2s ease;
53
+ vertical-align: middle;
54
+ border-radius: 3px;
55
+ user-select: none;
56
+ -webkit-user-select: none;
57
+ }
58
+
59
+ .agy-rtl-status-icon:hover {
60
+ opacity: 1;
61
+ color: var(--vscode-statusBarItem-hoverForeground, #fff);
62
+ background: var(--vscode-statusBarItem-hoverBackground, rgba(255,255,255,0.12));
63
+ }
64
+
65
+ /* Fixed position fallback when status bar not found */
66
+ .agy-rtl-status-icon.agy-rtl-fixed {
67
+ position: fixed;
68
+ bottom: 0;
69
+ right: 100px;
70
+ z-index: 99999;
71
+ width: 32px;
72
+ height: 22px;
73
+ background: var(--vscode-statusBar-background, #007acc);
74
+ }
75
+
76
+ /* Status dot indicator */
77
+ .agy-rtl-status-dot {
78
+ position: absolute;
79
+ top: 2px;
80
+ right: 2px;
81
+ width: 6px;
82
+ height: 6px;
83
+ border-radius: 50%;
84
+ background: #666;
85
+ transition: background 0.3s ease, box-shadow 0.3s ease;
86
+ }
87
+
88
+ .agy-rtl-status-dot.active {
89
+ background: #4caf50;
90
+ box-shadow: 0 0 4px rgba(76, 175, 80, 0.6);
91
+ }
92
+
93
+ /* ─── Toggle Panel ───────────────────────────────────────── */
94
+
95
+ .agy-rtl-panel {
96
+ position: fixed;
97
+ z-index: 100000;
98
+ min-width: 260px;
99
+ max-width: 300px;
100
+ background: var(--vscode-editorWidget-background, #252526);
101
+ border: 1px solid var(--vscode-editorWidget-border, #454545);
102
+ border-radius: 8px;
103
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45), 0 2px 8px rgba(0, 0, 0, 0.2);
104
+ padding: 16px;
105
+ opacity: 0;
106
+ visibility: hidden;
107
+ transform: translateY(8px) scale(0.96);
108
+ transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease;
109
+ font-family: -apple-system, 'Segoe UI', system-ui, sans-serif;
110
+ font-size: 13px;
111
+ color: var(--vscode-foreground, #ccc);
112
+ }
113
+
114
+ .agy-rtl-panel.visible {
115
+ opacity: 1;
116
+ visibility: visible;
117
+ transform: translateY(0) scale(1);
118
+ }
119
+
120
+ /* ── Panel Header ── */
121
+
122
+ .agy-rtl-panel-header {
123
+ margin-bottom: 14px;
124
+ padding-bottom: 10px;
125
+ border-bottom: 1px solid var(--vscode-editorWidget-border, #3a3a3a);
126
+ }
127
+
128
+ .agy-rtl-panel-title {
129
+ font-size: 14px;
130
+ font-weight: 600;
131
+ color: var(--vscode-foreground, #e0e0e0);
132
+ letter-spacing: -0.01em;
133
+ }
134
+
135
+ /* ── Panel Row ── */
136
+
137
+ .agy-rtl-panel-row {
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: space-between;
141
+ padding: 6px 0;
142
+ }
143
+
144
+ .agy-rtl-panel-label {
145
+ font-size: 13px;
146
+ color: var(--vscode-foreground, #ccc);
147
+ }
148
+
149
+ /* ── Separator ── */
150
+
151
+ .agy-rtl-panel-sep {
152
+ height: 1px;
153
+ background: var(--vscode-editorWidget-border, #3a3a3a);
154
+ margin: 8px 0;
155
+ }
156
+
157
+ /* ── Toggle Switch ── */
158
+
159
+ .agy-rtl-switch {
160
+ position: relative;
161
+ display: inline-block;
162
+ width: 40px;
163
+ height: 22px;
164
+ cursor: pointer;
165
+ flex-shrink: 0;
166
+ }
167
+
168
+ .agy-rtl-switch input {
169
+ opacity: 0;
170
+ width: 0;
171
+ height: 0;
172
+ position: absolute;
173
+ }
174
+
175
+ .agy-rtl-slider {
176
+ position: absolute;
177
+ top: 0;
178
+ left: 0;
179
+ right: 0;
180
+ bottom: 0;
181
+ background: #555;
182
+ border-radius: 22px;
183
+ transition: background 0.25s ease, box-shadow 0.25s ease;
184
+ }
185
+
186
+ .agy-rtl-slider::before {
187
+ content: '';
188
+ position: absolute;
189
+ width: 16px;
190
+ height: 16px;
191
+ left: 3px;
192
+ bottom: 3px;
193
+ background: #fff;
194
+ border-radius: 50%;
195
+ transition: transform 0.25s ease;
196
+ box-shadow: 0 1px 3px rgba(0,0,0,0.3);
197
+ }
198
+
199
+ .agy-rtl-switch input:checked + .agy-rtl-slider {
200
+ background: #4b9cf5;
201
+ box-shadow: 0 0 8px rgba(75, 156, 245, 0.3);
202
+ }
203
+
204
+ .agy-rtl-switch input:checked + .agy-rtl-slider::before {
205
+ transform: translateX(18px);
206
+ }
207
+
208
+ .agy-rtl-switch input:focus-visible + .agy-rtl-slider {
209
+ outline: 2px solid var(--vscode-focusBorder, #007fd4);
210
+ outline-offset: 2px;
211
+ }
212
+
213
+ /* ── GitHub Link ── */
214
+
215
+ .agy-rtl-panel-gh {
216
+ justify-content: center;
217
+ padding-top: 4px;
218
+ }
219
+
220
+ .agy-rtl-gh-link {
221
+ display: inline-flex;
222
+ align-items: center;
223
+ gap: 6px;
224
+ color: var(--vscode-textLink-foreground, #9cdcfe);
225
+ text-decoration: none;
226
+ font-size: 12px;
227
+ opacity: 0.8;
228
+ transition: opacity 0.2s ease;
229
+ }
230
+
231
+ .agy-rtl-gh-link:hover {
232
+ opacity: 1;
233
+ text-decoration: underline;
234
+ }
package/src/patcher.js CHANGED
@@ -13,7 +13,7 @@ const ENGINE_FILENAME = 'agy-rtl-engine.js';
13
13
  const STYLES_FILENAME = 'agy-rtl-styles.css';
14
14
 
15
15
  /**
16
- * Patch an unpacked Antigravity IDE installation.
16
+ * Patch an unpacked Antigravity installation.
17
17
  * Copies payload files and modifies workbench.html.
18
18
  */
19
19
  async function patchUnpacked(installation, spinner) {
@@ -166,14 +166,14 @@ async function patchAsar(installation, spinner) {
166
166
  * Main patch function.
167
167
  */
168
168
  async function patch(customPath) {
169
- const spinner = ora('Searching for Antigravity IDE...').start();
169
+ const spinner = ora('Searching for Antigravity...').start();
170
170
 
171
171
  const installations = findInstallations(customPath);
172
172
 
173
173
  if (installations.length === 0) {
174
- spinner.fail('Antigravity IDE installation not found.');
174
+ spinner.fail('Antigravity installation not found.');
175
175
  console.error(chalk.yellow('\nTry specifying the path manually:'));
176
- console.error(chalk.cyan(' agy-rtl patch --path /path/to/Antigravity-IDE\n'));
176
+ console.error(chalk.cyan(' agy-rtl patch --path /path/to/Antigravity\n'));
177
177
  throw new Error('Installation not found.');
178
178
  }
179
179
 
@@ -192,8 +192,8 @@ async function patch(customPath) {
192
192
  }
193
193
  }
194
194
 
195
- console.log(chalk.green.bold('\n✨ Antigravity IDE successfully patched with RTL support!'));
196
- console.log(chalk.cyan(' Please restart Antigravity IDE for changes to take effect.\n'));
195
+ console.log(chalk.green.bold('\n✨ Antigravity successfully patched with RTL support!'));
196
+ console.log(chalk.cyan(' Please restart Antigravity for changes to take effect.\n'));
197
197
  }
198
198
 
199
199
  /**
@@ -242,11 +242,11 @@ async function restoreAsar(installation, spinner) {
242
242
  * Main restore function.
243
243
  */
244
244
  async function restore(customPath) {
245
- const spinner = ora('Searching for Antigravity IDE...').start();
245
+ const spinner = ora('Searching for Antigravity...').start();
246
246
  const installations = findInstallations(customPath);
247
247
 
248
248
  if (installations.length === 0) {
249
- spinner.fail('Antigravity IDE installation not found.');
249
+ spinner.fail('Antigravity installation not found.');
250
250
  throw new Error('Installation not found.');
251
251
  }
252
252
 
@@ -264,19 +264,19 @@ async function restore(customPath) {
264
264
  }
265
265
  }
266
266
 
267
- console.log(chalk.green.bold('\n✨ Antigravity IDE restored to original state!'));
268
- console.log(chalk.cyan(' Please restart Antigravity IDE for changes to take effect.\n'));
267
+ console.log(chalk.green.bold('\n✨ Antigravity restored to original state!'));
268
+ console.log(chalk.cyan(' Please restart Antigravity for changes to take effect.\n'));
269
269
  }
270
270
 
271
271
  /**
272
272
  * Check patch status.
273
273
  */
274
274
  async function status(customPath) {
275
- const spinner = ora('Searching for Antigravity IDE...').start();
275
+ const spinner = ora('Searching for Antigravity...').start();
276
276
  const installations = findInstallations(customPath);
277
277
 
278
278
  if (installations.length === 0) {
279
- spinner.fail('Antigravity IDE installation not found.');
279
+ spinner.fail('Antigravity installation not found.');
280
280
  return;
281
281
  }
282
282
 
package/src/paths.js CHANGED
@@ -3,7 +3,7 @@ const path = require('path');
3
3
  const os = require('os');
4
4
 
5
5
  /**
6
- * Known installation paths for Antigravity IDE across all platforms.
6
+ * Known installation paths for Antigravity across all platforms.
7
7
  * Both the IDE (unpacked) and the standalone app (asar) are supported.
8
8
  */
9
9
  function getSearchPaths() {
@@ -11,37 +11,52 @@ function getSearchPaths() {
11
11
  const platform = os.platform();
12
12
 
13
13
  const common = [
14
- // Downloads (common for manual installs)
15
- path.join(home, 'Downloads', 'Antigravity-IDE'),
16
- path.join(home, 'Downloads', 'Antigravity-x64'),
14
+ // Antigravity (primary target)
17
15
  path.join(home, 'Downloads', 'Antigravity'),
18
- // Desktop
19
- path.join(home, 'Desktop', 'Antigravity-IDE'),
16
+ path.join(home, 'Downloads', 'Antigravity-x64'),
17
+ path.join(home, 'Desktop', 'Antigravity'),
20
18
  path.join(home, 'Desktop', 'Antigravity-x64'),
19
+ // Antigravity IDE (backward compatibility)
20
+ path.join(home, 'Downloads', 'Antigravity-IDE'),
21
+ path.join(home, 'Desktop', 'Antigravity-IDE'),
21
22
  ];
22
23
 
23
24
  const platformPaths = {
24
25
  linux: [
25
- ...common,
26
+ // Antigravity (primary)
27
+ '/opt/antigravity',
28
+ '/opt/Antigravity',
29
+ '/usr/lib/antigravity',
30
+ '/usr/share/antigravity',
31
+ path.join(home, '.local', 'share', 'antigravity'),
32
+ path.join(home, '.local', 'lib', 'antigravity'),
33
+ // Antigravity IDE (fallback)
26
34
  '/opt/antigravity-ide',
27
35
  '/opt/Antigravity-IDE',
28
- '/opt/antigravity',
29
36
  '/usr/lib/antigravity-ide',
30
37
  '/usr/share/antigravity-ide',
31
38
  path.join(home, '.local', 'share', 'antigravity-ide'),
32
39
  path.join(home, '.local', 'lib', 'antigravity-ide'),
40
+ ...common,
33
41
  ],
34
42
  darwin: [
43
+ // Antigravity (primary)
44
+ '/Applications/Antigravity.app/Contents',
45
+ path.join(home, 'Applications', 'Antigravity.app', 'Contents'),
46
+ // Antigravity IDE (fallback)
35
47
  '/Applications/Antigravity IDE.app/Contents',
36
48
  path.join(home, 'Applications', 'Antigravity IDE.app', 'Contents'),
37
- '/Applications/Antigravity.app/Contents',
38
49
  ...common,
39
50
  ],
40
51
  win32: [
52
+ // Antigravity (primary)
53
+ path.join(process.env.LOCALAPPDATA || '', 'Programs', 'antigravity'),
54
+ path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Antigravity'),
55
+ path.join(process.env.PROGRAMFILES || '', 'Antigravity'),
56
+ // Antigravity IDE (fallback)
41
57
  path.join(process.env.LOCALAPPDATA || '', 'Programs', 'antigravity-ide'),
42
58
  path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Antigravity IDE'),
43
59
  path.join(process.env.PROGRAMFILES || '', 'Antigravity IDE'),
44
- path.join(process.env.LOCALAPPDATA || '', 'Programs', 'antigravity'),
45
60
  ...common,
46
61
  ],
47
62
  };
@@ -85,7 +100,7 @@ function detectInstallation(basePath) {
85
100
  }
86
101
 
87
102
  /**
88
- * Find all Antigravity IDE installations on the system.
103
+ * Find all Antigravity installations on the system.
89
104
  */
90
105
  function findInstallations(customPath) {
91
106
  const results = [];