codex-rtl 0.0.2 → 1.0.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 +2 -0
- package/bin/Vazirmatn-Variable.woff2 +0 -0
- package/bin/index.js +252 -1
- package/bin/payload.js +454 -0
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ This CLI tool automatically injects a sophisticated RTL engine into Codex, addin
|
|
|
11
11
|
- **Persian Keyboard Fix**: Maps `Shift + 2` to type `@` instead of `٬` on Persian keyboards.
|
|
12
12
|
- **Beautiful Settings Panel**: A floating, non-intrusive UI widget at the bottom right corner.
|
|
13
13
|
- **Vazirmatn Built-in**: Comes with the beautiful Vazirmatn variable font by default.
|
|
14
|
+
- **Theme Compatibility**: Seamlessly adapts colors based on Codex's active theme, using native color variables.
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -74,6 +75,7 @@ Feel free to open issues or submit pull requests. Let's make Codex accessible an
|
|
|
74
75
|
- **حل مشکل کیبورد فارسی**: این ابزار کلید ترکیبی `Shift + 2` روی کیبورد فارسی را اصلاح میکند تا به جای «٬» علامت `@` تایپ شود.
|
|
75
76
|
- **پنل تنظیمات زیبا**: تمام این تنظیمات در یک ویجتِ کوچک، مدرن و شناور در پایینِ صفحه قرار گرفتهاند.
|
|
76
77
|
- **فونت وزیرمتن**: فونت زیبای Vazirmatn Variable به صورت پیشفرض در این افزونه گنجانده شده است.
|
|
78
|
+
- **همگامسازی خودکار با تم (Theme Compatibility)**: هماهنگی و تغییر پویای رنگ سوییچهای پنل با تغییر تم رنگی Codex به صورت کاملاً بومی.
|
|
77
79
|
|
|
78
80
|
## آموزش نصب
|
|
79
81
|
|
|
Binary file
|
package/bin/index.js
CHANGED
|
@@ -1,3 +1,254 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import picocolors from 'picocolors';
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
import prompts from 'prompts';
|
|
9
|
+
import * as asar from '@electron/asar';
|
|
2
10
|
|
|
3
|
-
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = path.dirname(__filename);
|
|
13
|
+
const { blue, cyan, green, red, yellow, bold } = picocolors;
|
|
14
|
+
|
|
15
|
+
const pkgPath = path.join(__dirname, '..', 'package.json');
|
|
16
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
17
|
+
console.log(bold(cyan(`\n✨ Codex Smart RTL Patcher v${pkg.version}\n`)));
|
|
18
|
+
|
|
19
|
+
function getDefaultPath() {
|
|
20
|
+
if (os.platform() === 'darwin') {
|
|
21
|
+
return '/Applications/Codex.app/Contents/Resources/app.asar';
|
|
22
|
+
} else if (os.platform() === 'win32') {
|
|
23
|
+
return path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Codex', 'resources', 'app.asar');
|
|
24
|
+
} else {
|
|
25
|
+
return '/opt/Codex/resources/app.asar';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function getAsarPath() {
|
|
30
|
+
let asarPath = getDefaultPath();
|
|
31
|
+
if (fs.existsSync(asarPath)) {
|
|
32
|
+
console.log(blue(`ℹ Found Codex installation at:`));
|
|
33
|
+
console.log(` ${asarPath}\n`);
|
|
34
|
+
return asarPath;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log(yellow(`⚠ Could not find Codex at default location.`));
|
|
38
|
+
const response = await prompts({
|
|
39
|
+
type: 'text',
|
|
40
|
+
name: 'customPath',
|
|
41
|
+
message: 'Please enter the full path to app.asar:'
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (!response.customPath || !fs.existsSync(response.customPath)) {
|
|
45
|
+
console.error(red('\n✖ Invalid path. Aborting.\n'));
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
return response.customPath;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const args = process.argv.slice(2);
|
|
52
|
+
const isRestore = args.includes('--restore');
|
|
53
|
+
|
|
54
|
+
async function main() {
|
|
55
|
+
const asarPath = await getAsarPath();
|
|
56
|
+
const backupPath = asarPath + '.bak';
|
|
57
|
+
|
|
58
|
+
if (isRestore) {
|
|
59
|
+
if (!fs.existsSync(backupPath)) {
|
|
60
|
+
console.error(red('✖ No backup found to restore.\n'));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const spinner = ora('Restoring original app.asar...').start();
|
|
64
|
+
try {
|
|
65
|
+
fs.copyFileSync(backupPath, asarPath);
|
|
66
|
+
spinner.succeed('Successfully restored original Codex!\n');
|
|
67
|
+
process.exit(0);
|
|
68
|
+
} catch (e) {
|
|
69
|
+
spinner.fail('Failed to restore.');
|
|
70
|
+
console.error(red(e.message));
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const spinner = ora('Checking permissions and backing up...').start();
|
|
76
|
+
try {
|
|
77
|
+
fs.accessSync(path.dirname(asarPath), fs.constants.W_OK);
|
|
78
|
+
if (!fs.existsSync(backupPath)) {
|
|
79
|
+
fs.copyFileSync(asarPath, backupPath);
|
|
80
|
+
}
|
|
81
|
+
} catch (e) {
|
|
82
|
+
spinner.fail('Permission Denied.');
|
|
83
|
+
console.error(red('\nSystem Error: ' + e.message));
|
|
84
|
+
if (os.platform() === 'win32') {
|
|
85
|
+
console.error(yellow('\nPlease run your terminal (PowerShell/CMD) as Administrator and try again.\n'));
|
|
86
|
+
} else if (os.platform() === 'darwin') {
|
|
87
|
+
console.error(yellow('\nPlease ensure you run this command with sudo (if required).'));
|
|
88
|
+
console.error(yellow('On macOS, you might also need to give your terminal "App Management" permission.'));
|
|
89
|
+
console.error(yellow('Go to: System Settings > Privacy & Security > App Management'));
|
|
90
|
+
console.error(yellow('And enable the toggle for your terminal, then try again.\n'));
|
|
91
|
+
} else {
|
|
92
|
+
console.error(yellow('\nPlease run this command with sudo.\n'));
|
|
93
|
+
}
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const extractDir = path.join(path.dirname(asarPath), 'app-extracted-codex-rtl-temp');
|
|
98
|
+
spinner.text = 'Extracting app.asar (this may take a few seconds)...';
|
|
99
|
+
try {
|
|
100
|
+
if (fs.existsSync(extractDir)) {
|
|
101
|
+
fs.rmSync(extractDir, { recursive: true, force: true });
|
|
102
|
+
}
|
|
103
|
+
asar.extractAll(asarPath, extractDir);
|
|
104
|
+
} catch (e) {
|
|
105
|
+
spinner.fail('Failed to extract ASAR.');
|
|
106
|
+
console.error(red(e.message));
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
spinner.text = 'Injecting RTL features and enabling DevTools...';
|
|
111
|
+
try {
|
|
112
|
+
const buildDir = path.join(extractDir, '.vite', 'build');
|
|
113
|
+
if (!fs.existsSync(buildDir)) {
|
|
114
|
+
throw new Error('.vite/build not found in ASAR. Unsupported Codex version.');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 1. Find main-*.js
|
|
118
|
+
const files = fs.readdirSync(buildDir);
|
|
119
|
+
const mainFiles = files.filter(f => f.startsWith('main-') && f.endsWith('.js'));
|
|
120
|
+
|
|
121
|
+
for (const mainFile of mainFiles) {
|
|
122
|
+
const mainJsPath = path.join(buildDir, mainFile);
|
|
123
|
+
let mainCode = fs.readFileSync(mainJsPath, 'utf8');
|
|
124
|
+
|
|
125
|
+
// Force devTools: true
|
|
126
|
+
const devtoolsRegex = /devTools:\s*this\.options\.allowDevtools/g;
|
|
127
|
+
if (devtoolsRegex.test(mainCode)) {
|
|
128
|
+
mainCode = mainCode.replace(devtoolsRegex, 'devTools:true');
|
|
129
|
+
fs.writeFileSync(mainJsPath, mainCode, 'utf8');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 2. Inject RTL Loader into bootstrap.js
|
|
134
|
+
const bootstrapPath = path.join(buildDir, 'bootstrap.js');
|
|
135
|
+
if (!fs.existsSync(bootstrapPath)) {
|
|
136
|
+
throw new Error('.vite/build/bootstrap.js not found in ASAR. Unsupported Codex version.');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let bootstrapCode = fs.readFileSync(bootstrapPath, 'utf8');
|
|
140
|
+
|
|
141
|
+
if (bootstrapCode.includes('/* CODEX RTL PATCH START */')) {
|
|
142
|
+
spinner.succeed('Codex is already patched!');
|
|
143
|
+
fs.rmSync(extractDir, { recursive: true, force: true });
|
|
144
|
+
console.log(green('\n✨ Enjoy your RTL experience!\n'));
|
|
145
|
+
process.exit(0);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const loaderCode = `
|
|
149
|
+
/* CODEX RTL PATCH START */
|
|
150
|
+
try {
|
|
151
|
+
const { app } = require('electron');
|
|
152
|
+
app.on('browser-window-created', (event, win) => {
|
|
153
|
+
// DevTools Shortcut Handler (F12, Cmd+Option+I, Ctrl+Shift+I)
|
|
154
|
+
win.webContents.on('before-input-event', (ev, input) => {
|
|
155
|
+
const isShortcut = input.key === 'F12' ||
|
|
156
|
+
(input.control && input.shift && input.key.toLowerCase() === 'i') ||
|
|
157
|
+
(input.meta && input.alt && input.key.toLowerCase() === 'i');
|
|
158
|
+
if (isShortcut && input.type === 'keyDown') {
|
|
159
|
+
try {
|
|
160
|
+
win.webContents.toggleDevTools();
|
|
161
|
+
ev.preventDefault();
|
|
162
|
+
} catch (e) {}
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
win.webContents.on('console-message', (ev, level, message) => {
|
|
167
|
+
if (typeof message === 'string' && message.startsWith('SAVE_RTL_CONFIG|')) {
|
|
168
|
+
try {
|
|
169
|
+
const data = message.substring(16);
|
|
170
|
+
const configPath = require('path').join(require('os').homedir(), '.codex-rtl.json');
|
|
171
|
+
require('fs').writeFileSync(configPath, data);
|
|
172
|
+
} catch (e) {}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
win.webContents.on('dom-ready', () => {
|
|
177
|
+
try {
|
|
178
|
+
const path = require('path');
|
|
179
|
+
const fs = require('fs');
|
|
180
|
+
const fontPath = path.join(__dirname, 'Vazirmatn-Variable.woff2');
|
|
181
|
+
const payloadPath = path.join(__dirname, 'payload.js');
|
|
182
|
+
if (!fs.existsSync(fontPath) || !fs.existsSync(payloadPath)) return;
|
|
183
|
+
|
|
184
|
+
const fontBase64 = fs.readFileSync(fontPath).toString('base64');
|
|
185
|
+
let payload = fs.readFileSync(payloadPath, 'utf8');
|
|
186
|
+
|
|
187
|
+
// Read config
|
|
188
|
+
let rtlConfig = { faFont: '', enFont: '', codeFont: '', lh: '1.6', isRTL: true, forceRTL: false, fixAtSign: true };
|
|
189
|
+
try {
|
|
190
|
+
const configPath = path.join(require('os').homedir(), '.codex-rtl.json');
|
|
191
|
+
if (fs.existsSync(configPath)) {
|
|
192
|
+
const cfg = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
193
|
+
rtlConfig = { ...rtlConfig, ...cfg };
|
|
194
|
+
}
|
|
195
|
+
} catch (e) {}
|
|
196
|
+
|
|
197
|
+
// Replace placeholders in payload
|
|
198
|
+
payload = payload.replace('__FONT_BASE64__', fontBase64);
|
|
199
|
+
payload = payload.replace('__RTL_CONFIG__', JSON.stringify(rtlConfig));
|
|
200
|
+
|
|
201
|
+
win.webContents.executeJavaScript(payload).catch(err => console.error("Failed to inject RTL:", err));
|
|
202
|
+
} catch (e) {
|
|
203
|
+
console.error("Failed to read RTL patch assets:", e);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
} catch(e) {
|
|
208
|
+
console.error("RTL patch initialization failed:", e);
|
|
209
|
+
}
|
|
210
|
+
/* CODEX RTL PATCH END */
|
|
211
|
+
`;
|
|
212
|
+
|
|
213
|
+
// Append loader to bootstrap.js
|
|
214
|
+
bootstrapCode += '\n' + loaderCode;
|
|
215
|
+
fs.writeFileSync(bootstrapPath, bootstrapCode, 'utf8');
|
|
216
|
+
|
|
217
|
+
// 3. Copy font file
|
|
218
|
+
const fontSource = path.join(__dirname, 'Vazirmatn-Variable.woff2');
|
|
219
|
+
const fontDest = path.join(buildDir, 'Vazirmatn-Variable.woff2');
|
|
220
|
+
if (fs.existsSync(fontSource)) {
|
|
221
|
+
fs.copyFileSync(fontSource, fontDest);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// 4. Copy payload file
|
|
225
|
+
const payloadSource = path.join(__dirname, 'payload.js');
|
|
226
|
+
const payloadDest = path.join(buildDir, 'payload.js');
|
|
227
|
+
if (fs.existsSync(payloadSource)) {
|
|
228
|
+
fs.copyFileSync(payloadSource, payloadDest);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
} catch (e) {
|
|
232
|
+
spinner.fail('Injection failed.');
|
|
233
|
+
console.error(red(e.message));
|
|
234
|
+
if (fs.existsSync(extractDir)) fs.rmSync(extractDir, { recursive: true, force: true });
|
|
235
|
+
process.exit(1);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
spinner.text = 'Repacking app.asar (almost done)...';
|
|
239
|
+
try {
|
|
240
|
+
await asar.createPackage(extractDir, asarPath);
|
|
241
|
+
fs.rmSync(extractDir, { recursive: true, force: true });
|
|
242
|
+
spinner.succeed('Successfully patched Codex!');
|
|
243
|
+
console.log(green('\n✨ RTL Features and DevTools have been enabled. Please restart Codex to see the changes.\n'));
|
|
244
|
+
} catch (e) {
|
|
245
|
+
spinner.fail('Failed to repack ASAR.');
|
|
246
|
+
console.error(red(e.message));
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
main().catch(e => {
|
|
252
|
+
console.error(red('\n✖ An unexpected error occurred:'), e.message);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
});
|
package/bin/payload.js
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
const fontBase64 = '__FONT_BASE64__';
|
|
2
|
+
const rtlConfig = __RTL_CONFIG__;
|
|
3
|
+
|
|
4
|
+
let isRTL = rtlConfig.isRTL !== false;
|
|
5
|
+
let forceRTL = rtlConfig.forceRTL === true;
|
|
6
|
+
let fixAtSign = rtlConfig.fixAtSign !== false;
|
|
7
|
+
let savedFaFont = rtlConfig.faFont || '';
|
|
8
|
+
let savedEnFont = rtlConfig.enFont || '';
|
|
9
|
+
let savedCodeFont = rtlConfig.codeFont || '';
|
|
10
|
+
let savedLH = rtlConfig.lh || '1.6';
|
|
11
|
+
|
|
12
|
+
if (!document.getElementById('rtl-widget-style')) {
|
|
13
|
+
let widgetStyle = document.createElement('style');
|
|
14
|
+
widgetStyle.id = 'rtl-widget-style';
|
|
15
|
+
widgetStyle.innerHTML = `
|
|
16
|
+
.rtl-widget-container {
|
|
17
|
+
position: fixed;
|
|
18
|
+
bottom: 16px;
|
|
19
|
+
right: 16px;
|
|
20
|
+
z-index: 99999;
|
|
21
|
+
direction: ltr;
|
|
22
|
+
font-family: inherit;
|
|
23
|
+
}
|
|
24
|
+
.rtl-widget-trigger {
|
|
25
|
+
width: 40px;
|
|
26
|
+
height: 40px;
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: center;
|
|
30
|
+
border-radius: 9999px;
|
|
31
|
+
background-color: var(--scrollbar-thumb, #555);
|
|
32
|
+
color: #fff;
|
|
33
|
+
cursor: pointer;
|
|
34
|
+
opacity: 0.8;
|
|
35
|
+
transition: all 0.3s ease-in-out;
|
|
36
|
+
}
|
|
37
|
+
.rtl-widget-trigger:hover {
|
|
38
|
+
transform: scale(1.1);
|
|
39
|
+
opacity: 1;
|
|
40
|
+
}
|
|
41
|
+
.rtl-widget-container:hover .rtl-widget-trigger {
|
|
42
|
+
opacity: 0;
|
|
43
|
+
transform: scale(0.5);
|
|
44
|
+
pointer-events: none;
|
|
45
|
+
}
|
|
46
|
+
.rtl-widget-panel {
|
|
47
|
+
position: absolute;
|
|
48
|
+
bottom: 0;
|
|
49
|
+
right: 0;
|
|
50
|
+
width: 270px;
|
|
51
|
+
transform: scale(0);
|
|
52
|
+
transform-origin: bottom right;
|
|
53
|
+
opacity: 0;
|
|
54
|
+
pointer-events: none;
|
|
55
|
+
transition: all 0.3s ease-in-out;
|
|
56
|
+
}
|
|
57
|
+
.rtl-widget-container:hover .rtl-widget-panel {
|
|
58
|
+
transform: scale(1);
|
|
59
|
+
opacity: 1;
|
|
60
|
+
pointer-events: auto;
|
|
61
|
+
}
|
|
62
|
+
.rtl-tooltip {
|
|
63
|
+
visibility: hidden;
|
|
64
|
+
opacity: 0;
|
|
65
|
+
transition: opacity 0.15s ease-in-out;
|
|
66
|
+
pointer-events: none;
|
|
67
|
+
position: absolute;
|
|
68
|
+
bottom: 100%;
|
|
69
|
+
left: 50%;
|
|
70
|
+
transform: translateX(-50%);
|
|
71
|
+
margin-bottom: 8px;
|
|
72
|
+
width: 200px;
|
|
73
|
+
padding: 8px;
|
|
74
|
+
border-radius: 8px;
|
|
75
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.5);
|
|
76
|
+
z-index: 999999 !important;
|
|
77
|
+
white-space: normal;
|
|
78
|
+
text-align: center;
|
|
79
|
+
background-color: #202123 !important;
|
|
80
|
+
background-color: light-dark(#ffffff, #202123) !important;
|
|
81
|
+
border: 1px solid #4d4d4d !important;
|
|
82
|
+
border: 1px solid light-dark(#e5e7eb, #4d4d4d) !important;
|
|
83
|
+
color: var(--color-token-foreground, #fff) !important;
|
|
84
|
+
font-size: 11px;
|
|
85
|
+
line-height: 1.4;
|
|
86
|
+
}
|
|
87
|
+
.rtl-tooltip::after {
|
|
88
|
+
content: "";
|
|
89
|
+
position: absolute;
|
|
90
|
+
top: 100%;
|
|
91
|
+
left: 50%;
|
|
92
|
+
transform: translateX(-50%);
|
|
93
|
+
border-width: 5px;
|
|
94
|
+
border-style: solid;
|
|
95
|
+
border-color: #4d4d4d transparent transparent transparent !important;
|
|
96
|
+
border-color: light-dark(#e5e7eb, #4d4d4d) transparent transparent transparent !important;
|
|
97
|
+
}
|
|
98
|
+
.rtl-info-icon {
|
|
99
|
+
position: relative;
|
|
100
|
+
display: inline-flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
justify-content: center;
|
|
103
|
+
z-index: 50;
|
|
104
|
+
opacity: 0.5;
|
|
105
|
+
transition: opacity 0.15s ease-in-out;
|
|
106
|
+
}
|
|
107
|
+
.rtl-info-icon:hover {
|
|
108
|
+
opacity: 1;
|
|
109
|
+
}
|
|
110
|
+
.rtl-info-icon:hover .rtl-tooltip {
|
|
111
|
+
visibility: visible;
|
|
112
|
+
opacity: 1;
|
|
113
|
+
}
|
|
114
|
+
.rtl-github-link {
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
justify-content: center;
|
|
118
|
+
gap: 8px;
|
|
119
|
+
font-size: 12px;
|
|
120
|
+
font-weight: 600;
|
|
121
|
+
opacity: 0.7;
|
|
122
|
+
text-decoration: none;
|
|
123
|
+
padding-top: 4px;
|
|
124
|
+
padding-bottom: 2px;
|
|
125
|
+
transition: all 0.15s ease-in-out;
|
|
126
|
+
color: var(--color-token-foreground, #fff) !important;
|
|
127
|
+
}
|
|
128
|
+
.rtl-github-link:hover {
|
|
129
|
+
opacity: 1;
|
|
130
|
+
color: #ffd700 !important; /* Gold color */
|
|
131
|
+
}
|
|
132
|
+
`;
|
|
133
|
+
document.head.appendChild(widgetStyle);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const rtlStyle = document.createElement('style');
|
|
137
|
+
rtlStyle.id = 'codex-rtl-style';
|
|
138
|
+
|
|
139
|
+
const updateDynamicCSS = () => {
|
|
140
|
+
if (!isRTL) {
|
|
141
|
+
if (rtlStyle.parentNode) rtlStyle.parentNode.removeChild(rtlStyle);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let faFontRule = '';
|
|
146
|
+
let faFontName = "'PersianOnlyFont'";
|
|
147
|
+
|
|
148
|
+
if (savedFaFont) {
|
|
149
|
+
faFontName = "'UserPersianFont', 'PersianOnlyFont'";
|
|
150
|
+
let baseFaFont = savedFaFont.replace(/[-\s]?Regular$/i, '');
|
|
151
|
+
faFontRule = `
|
|
152
|
+
@font-face {
|
|
153
|
+
font-family: 'UserPersianFont';
|
|
154
|
+
src: local('${savedFaFont}'), local('${baseFaFont}');
|
|
155
|
+
font-weight: 400;
|
|
156
|
+
unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF;
|
|
157
|
+
}
|
|
158
|
+
@font-face {
|
|
159
|
+
font-family: 'UserPersianFont';
|
|
160
|
+
src: local('${baseFaFont} Bold'), local('${baseFaFont}-Bold'), local('${baseFaFont}Bold');
|
|
161
|
+
font-weight: 700;
|
|
162
|
+
unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF;
|
|
163
|
+
}
|
|
164
|
+
`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let enFontStr = savedEnFont ? `'${savedEnFont}', ui-sans-serif, system-ui, sans-serif` : 'ui-sans-serif, system-ui, sans-serif';
|
|
168
|
+
let codeFontStr = savedCodeFont ? `'${savedCodeFont}', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace` : 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace';
|
|
169
|
+
|
|
170
|
+
let forceRtlStyle = forceRTL ? `
|
|
171
|
+
/* Force RTL layout for all text components excluding the widget panel */
|
|
172
|
+
p:not(.rtl-widget-container *),
|
|
173
|
+
li:not(.rtl-widget-container *),
|
|
174
|
+
h1:not(.rtl-widget-container *),
|
|
175
|
+
h2:not(.rtl-widget-container *),
|
|
176
|
+
h3:not(.rtl-widget-container *),
|
|
177
|
+
h4:not(.rtl-widget-container *),
|
|
178
|
+
h5:not(.rtl-widget-container *),
|
|
179
|
+
h6:not(.rtl-widget-container *),
|
|
180
|
+
textarea:not(.rtl-widget-container *),
|
|
181
|
+
[contenteditable="true"]:not(.rtl-widget-container *),
|
|
182
|
+
[contenteditable="true"] p:not(.rtl-widget-container *),
|
|
183
|
+
[data-lexical-text="true"]:not(.rtl-widget-container *) {
|
|
184
|
+
direction: rtl !important;
|
|
185
|
+
text-align: right !important;
|
|
186
|
+
unicode-bidi: isolate !important;
|
|
187
|
+
}
|
|
188
|
+
` : '';
|
|
189
|
+
|
|
190
|
+
rtlStyle.textContent = `
|
|
191
|
+
${faFontRule}
|
|
192
|
+
@font-face {
|
|
193
|
+
font-family: 'PersianOnlyFont';
|
|
194
|
+
src: url('data:font/woff2;base64,${fontBase64}') format('woff2');
|
|
195
|
+
font-weight: 100 900;
|
|
196
|
+
unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
:root, :host, html, body {
|
|
200
|
+
font-family: ${faFontName}, ${enFontStr}, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
|
|
201
|
+
--diffs-font-family: ${codeFontStr} !important;
|
|
202
|
+
--diffs-font-fallback: ${codeFontStr} !important;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* Smart RTL using CSS unicode-bidi plaintext */
|
|
206
|
+
p, h1, h2, h3, h4, h5, h6, li, span, div, [role="presentation"] {
|
|
207
|
+
unicode-bidi: plaintext !important;
|
|
208
|
+
text-align: start !important;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* Explicitly keep our widget panel LTR and left-aligned */
|
|
212
|
+
.rtl-widget-container, .rtl-widget-container * {
|
|
213
|
+
direction: ltr !important;
|
|
214
|
+
text-align: left !important;
|
|
215
|
+
unicode-bidi: isolate !important;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* Force RTL rules if enabled */
|
|
219
|
+
${forceRtlStyle}
|
|
220
|
+
|
|
221
|
+
/* Ensure code blocks are kept LTR and get the code font applied */
|
|
222
|
+
pre:not(.rtl-widget-container *),
|
|
223
|
+
code:not(.rtl-widget-container *),
|
|
224
|
+
pre:not(.rtl-widget-container *) *,
|
|
225
|
+
code:not(.rtl-widget-container *) *,
|
|
226
|
+
pre:not(.rtl-widget-container *) span,
|
|
227
|
+
code:not(.rtl-widget-container *) span,
|
|
228
|
+
[data-line] span {
|
|
229
|
+
unicode-bidi: isolate !important;
|
|
230
|
+
direction: ltr !important;
|
|
231
|
+
text-align: left !important;
|
|
232
|
+
font-family: ${codeFontStr} !important;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/* Text input fields and lexical text editors */
|
|
236
|
+
textarea:not(.rtl-widget-container *),
|
|
237
|
+
[contenteditable="true"]:not(.rtl-widget-container *),
|
|
238
|
+
[contenteditable="true"] p:not(.rtl-widget-container *),
|
|
239
|
+
[data-lexical-text="true"]:not(.rtl-widget-container *) {
|
|
240
|
+
unicode-bidi: plaintext !important;
|
|
241
|
+
text-align: start !important;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* Adjust list padding when elements render as RTL */
|
|
245
|
+
ul:not(#_)[dir="rtl"], ol:not(#_)[dir="rtl"],
|
|
246
|
+
[dir="rtl"] ul:not(#_), [dir="rtl"] ol:not(#_) {
|
|
247
|
+
padding-left: 0 !important;
|
|
248
|
+
padding-right: 1.25rem !important;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/* Custom Line Height */
|
|
252
|
+
p:not(.rtl-widget-container *),
|
|
253
|
+
li:not(.rtl-widget-container *),
|
|
254
|
+
h1:not(.rtl-widget-container *),
|
|
255
|
+
h2:not(.rtl-widget-container *),
|
|
256
|
+
h3:not(.rtl-widget-container *),
|
|
257
|
+
[contenteditable="true"] p:not(.rtl-widget-container *),
|
|
258
|
+
[data-lexical-text="true"]:not(.rtl-widget-container *) {
|
|
259
|
+
line-height: ${savedLH} !important;
|
|
260
|
+
}
|
|
261
|
+
`;
|
|
262
|
+
|
|
263
|
+
if (!rtlStyle.parentNode) {
|
|
264
|
+
document.head.appendChild(rtlStyle);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
// Initial apply
|
|
269
|
+
updateDynamicCSS();
|
|
270
|
+
|
|
271
|
+
// Global Shift+2 keyboard handler for @
|
|
272
|
+
document.addEventListener('keydown', (e) => {
|
|
273
|
+
if (!fixAtSign) return;
|
|
274
|
+
if (e.code === 'Digit2' && e.shiftKey) {
|
|
275
|
+
if (e.key === '٬' || e.key === '،') {
|
|
276
|
+
e.preventDefault();
|
|
277
|
+
document.execCommand('insertText', false, '@');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}, { capture: true });
|
|
281
|
+
|
|
282
|
+
// Floating widget for Codex RTL
|
|
283
|
+
const widgetWrapper = document.createElement('div');
|
|
284
|
+
widgetWrapper.className = 'rtl-widget-container';
|
|
285
|
+
widgetWrapper.innerHTML = `
|
|
286
|
+
<div class="rtl-widget-trigger">
|
|
287
|
+
<svg height="20" width="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><path d="M2 12h20"></path><path 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"></path></svg>
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
<div class="rtl-widget-panel">
|
|
291
|
+
<div class="relative flex max-h-full min-h-0 flex-col rounded-3xl bg-token-dropdown-background pt-3 border border-token-border-default shadow-md">
|
|
292
|
+
<div class="flex flex-col gap-2 px-3 pb-3 pt-0 w-full h-full" style="display: flex; flex-direction: column; gap: 8px;">
|
|
293
|
+
|
|
294
|
+
<div class="border-b border-token-border-default pb-2 mb-1 text-center" style="text-align: center !important;">
|
|
295
|
+
<div class="electron:heading-lg heading-base truncate text-center" style="color: var(--color-token-foreground); display: flex !important; justify-content: center !important; text-align: center; width: 100%;">Codex Smart RTL</div>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<div class="flex items-center justify-between gap-4 px-1" style="display: flex; justify-content: space-between; align-items: center;">
|
|
299
|
+
<span id="rtl-toggle-label" class="font-medium text-xs" style="font-size: 12px; color: var(--color-token-foreground);">${isRTL ? 'Enabled' : 'Disabled'}</span>
|
|
300
|
+
<button id="rtl-toggle-btn" type="button" class="relative inline-flex items-center rounded-full transition-colors duration-200 h-6 w-11" style="background-color: ${isRTL ? 'var(--color-token-charts-blue, #339cff)' : '#555'}; border: none; cursor: pointer; height: 24px; width: 44px; border-radius: 9999px; position: relative;">
|
|
301
|
+
<span id="rtl-toggle-knob" class="inline-block transform rounded-full bg-white transition-transform h-4 w-4" style="margin-left: 4px; transform: ${isRTL ? 'translateX(20px)' : 'translateX(0)'}; transition: transform 0.2s; height: 16px; width: 16px; border-radius: 9999px; background: #fff; display: block;"></span>
|
|
302
|
+
</button>
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
<div id="rtl-settings-wrapper" class="flex flex-col gap-2 transition-all duration-300" style="position: relative; z-index: 10; opacity: ${isRTL ? '1' : '0.4'}; pointer-events: ${isRTL ? 'auto' : 'none'}; display: flex; flex-direction: column; gap: 8px; transition: opacity 0.3s;">
|
|
306
|
+
<div class="flex items-center justify-between gap-2 px-1 mt-1" style="display: flex; justify-content: space-between; align-items: center;">
|
|
307
|
+
<div style="display: flex; align-items: center; gap: 4px;">
|
|
308
|
+
<span class="font-medium text-xs" style="font-size: 12px; color: var(--color-token-foreground);">Force RTL</span>
|
|
309
|
+
<div class="rtl-info-icon" style="color: var(--color-token-foreground); cursor: pointer; margin-left: 2px;">
|
|
310
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 -960 960 960" fill="currentColor"><path d="M450-290h60V-520H450v230Zm52.92-307.75q9.38-9.29 9.38-23.02t-9.29-23.02T480-653.07t-23.02,9.29t-9.29,23.02t9.38,23.02T480-588.46t22.92-9.29ZM480.07-100q-78.84,0-148.2-29.92T211.18-211.13T129.93-331.76T100-479.93t29.92-148.2t81.21-120.68t120.63-81.25T479.93-860t148.2,29.92t120.68,81.21t81.25,120.63T860-480.07t-29.92,148.2T748.87-211.18T628.24-129.93T480.07-100ZM480-160q134,0 227-93t93-227T707-707T480-800T253-707T160-480t93,227t227,93Zm0-320Z"></path></svg>
|
|
311
|
+
<div class="rtl-tooltip">Forces RTL layout on all elements, even if the text starts with English characters.</div>
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
<button id="rtl-force-btn" type="button" class="relative inline-flex items-center rounded-full transition-colors duration-200 h-6 w-11" style="background-color: ${forceRTL ? 'var(--color-token-charts-blue, #339cff)' : '#555'}; border: none; cursor: pointer; height: 24px; width: 44px; border-radius: 9999px; position: relative;">
|
|
315
|
+
<span id="rtl-force-knob" class="inline-block transform rounded-full bg-white transition-transform h-4 w-4" style="margin-left: 4px; transform: ${forceRTL ? 'translateX(20px)' : 'translateX(0)'}; transition: transform 0.2s; height: 16px; width: 16px; border-radius: 9999px; background: #fff; display: block;"></span>
|
|
316
|
+
</button>
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
<div class="h-px bg-token-border-default w-full"></div>
|
|
320
|
+
|
|
321
|
+
<div style="display: flex; justify-content: space-between; align-items: center; gap: 8px;">
|
|
322
|
+
<span class="font-medium text-xs" style="font-size: 12px; color: var(--color-token-foreground);">FA/AR Font</span>
|
|
323
|
+
<input id="rtl-fafont-input" type="text" placeholder="Default: Vazirmatn" value="${savedFaFont}" class="focus-visible:ring-token-focus h-7 w-full max-w-[8.5rem] rounded-lg border border-token-border bg-token-input-background px-2 text-xs text-token-text-primary shadow-sm outline-none focus-visible:ring-2 max-sm:max-w-none" spellcheck="false">
|
|
324
|
+
</div>
|
|
325
|
+
|
|
326
|
+
<div style="display: flex; justify-content: space-between; align-items: center; gap: 8px;">
|
|
327
|
+
<span class="font-medium text-xs" style="font-size: 12px; color: var(--color-token-foreground);">EN Font</span>
|
|
328
|
+
<input id="rtl-enfont-input" type="text" placeholder="Default: System" value="${savedEnFont}" class="focus-visible:ring-token-focus h-7 w-full max-w-[8.5rem] rounded-lg border border-token-border bg-token-input-background px-2 text-xs text-token-text-primary shadow-sm outline-none focus-visible:ring-2 max-sm:max-w-none" spellcheck="false">
|
|
329
|
+
</div>
|
|
330
|
+
|
|
331
|
+
<div style="display: flex; justify-content: space-between; align-items: center; gap: 8px;">
|
|
332
|
+
<span class="font-medium text-xs" style="font-size: 12px; color: var(--color-token-foreground);">Code Font</span>
|
|
333
|
+
<input id="rtl-codefont-input" type="text" placeholder="Default: System" value="${savedCodeFont}" class="focus-visible:ring-token-focus h-7 w-full max-w-[8.5rem] rounded-lg border border-token-border bg-token-input-background px-2 text-xs text-token-text-primary shadow-sm outline-none focus-visible:ring-2 max-sm:max-w-none" spellcheck="false">
|
|
334
|
+
</div>
|
|
335
|
+
|
|
336
|
+
<div style="display: flex; justify-content: space-between; align-items: center; gap: 8px; height: 28px;">
|
|
337
|
+
<span class="font-medium text-xs" style="font-size: 12px; color: var(--color-token-foreground);">Line Height</span>
|
|
338
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
339
|
+
<input id="rtl-lh-input" type="range" min="1.2" max="2.5" step="0.1" value="${savedLH}" style="width: 80px; cursor: pointer; accent-color: var(--color-token-charts-blue, #339cff);">
|
|
340
|
+
<button id="rtl-lh-reset" type="button" class="text-token-text-primary opacity-50 hover:opacity-100 transition-opacity cursor-pointer text-sm" style="background: none; border: none; padding: 0;">↺</button>
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
|
|
344
|
+
<div class="h-px bg-token-border-default w-full"></div>
|
|
345
|
+
|
|
346
|
+
<div style="display: flex; justify-content: space-between; align-items: center; gap: 8px;">
|
|
347
|
+
<div style="display: flex; align-items: center; gap: 4px;">
|
|
348
|
+
<span class="font-medium text-xs" style="font-size: 12px; color: var(--color-token-foreground);">Type @ with Shift+2</span>
|
|
349
|
+
<div class="rtl-info-icon" style="color: var(--color-token-foreground); cursor: pointer; margin-left: 2px;">
|
|
350
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 -960 960 960" fill="currentColor"><path d="M450-290h60V-520H450v230Zm52.92-307.75q9.38-9.29 9.38-23.02t-9.29-23.02T480-653.07t-23.02,9.29t-9.29,23.02t9.38,23.02T480-588.46t22.92-9.29ZM480.07-100q-78.84,0-148.2-29.92T211.18-211.13T129.93-331.76T100-479.93t29.92-148.2t81.21-120.68t120.63-81.25T479.93-860t148.2,29.92t120.68,81.21t81.25,120.63T860-480.07t-29.92,148.2T748.87-211.18T628.24-129.93T480.07-100ZM480-160q134,0 227-93t93-227T707-707T480-800T253-707T160-480t93,227t227,93Zm0-320Z"></path></svg>
|
|
351
|
+
<div class="rtl-tooltip">Automatically converts '٬' to '@' when you press Shift+2 on a Persian keyboard layout.</div>
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
<button id="rtl-at-btn" type="button" class="relative inline-flex items-center rounded-full transition-colors duration-200 h-6 w-11" style="background-color: ${fixAtSign ? 'var(--color-token-charts-blue, #339cff)' : '#555'}; border: none; cursor: pointer; height: 24px; width: 44px; border-radius: 9999px; position: relative;">
|
|
355
|
+
<span id="rtl-at-knob" class="inline-block transform rounded-full bg-white transition-transform h-4 w-4" style="margin-left: 4px; transform: ${fixAtSign ? 'translateX(20px)' : 'translateX(0)'}; transition: transform 0.2s; height: 16px; width: 16px; border-radius: 9999px; background: #fff; display: block;"></span>
|
|
356
|
+
</button>
|
|
357
|
+
</div>
|
|
358
|
+
|
|
359
|
+
<div class="h-px bg-token-border-default w-full"></div>
|
|
360
|
+
|
|
361
|
+
<!-- GitHub -->
|
|
362
|
+
<a href="https://github.com/mmnaderi/codex-rtl" target="_blank" class="rtl-github-link">
|
|
363
|
+
<svg height="14" width="14" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"></path></svg>
|
|
364
|
+
Star on GitHub
|
|
365
|
+
</a>
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
`;
|
|
371
|
+
document.body.appendChild(widgetWrapper);
|
|
372
|
+
|
|
373
|
+
const toggleBtn = document.getElementById('rtl-toggle-btn');
|
|
374
|
+
const toggleKnob = document.getElementById('rtl-toggle-knob');
|
|
375
|
+
const toggleLabel = document.getElementById('rtl-toggle-label');
|
|
376
|
+
const settingsWrapper = document.getElementById('rtl-settings-wrapper');
|
|
377
|
+
const forceBtn = document.getElementById('rtl-force-btn');
|
|
378
|
+
const forceKnob = document.getElementById('rtl-force-knob');
|
|
379
|
+
const atBtn = document.getElementById('rtl-at-btn');
|
|
380
|
+
const atKnob = document.getElementById('rtl-at-knob');
|
|
381
|
+
const faFontInput = document.getElementById('rtl-fafont-input');
|
|
382
|
+
const enFontInput = document.getElementById('rtl-enfont-input');
|
|
383
|
+
const codeFontInput = document.getElementById('rtl-codefont-input');
|
|
384
|
+
const lhInput = document.getElementById('rtl-lh-input');
|
|
385
|
+
const lhResetBtn = document.getElementById('rtl-lh-reset');
|
|
386
|
+
|
|
387
|
+
const saveConfig = () => {
|
|
388
|
+
console.log("SAVE_RTL_CONFIG|" + JSON.stringify({
|
|
389
|
+
isRTL: isRTL,
|
|
390
|
+
forceRTL: forceRTL,
|
|
391
|
+
fixAtSign: fixAtSign,
|
|
392
|
+
faFont: faFontInput.value.trim(),
|
|
393
|
+
enFont: enFontInput.value.trim(),
|
|
394
|
+
codeFont: codeFontInput.value.trim(),
|
|
395
|
+
lh: lhInput.value
|
|
396
|
+
}));
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
toggleBtn.addEventListener('click', () => {
|
|
400
|
+
isRTL = !isRTL;
|
|
401
|
+
saveConfig();
|
|
402
|
+
toggleLabel.innerText = isRTL ? 'Enabled' : 'Disabled';
|
|
403
|
+
settingsWrapper.style.opacity = isRTL ? '1' : '0.4';
|
|
404
|
+
settingsWrapper.style.pointerEvents = isRTL ? 'auto' : 'none';
|
|
405
|
+
toggleKnob.style.transform = isRTL ? 'translateX(20px)' : 'translateX(0)';
|
|
406
|
+
toggleBtn.style.backgroundColor = isRTL ? 'var(--color-token-charts-blue, #339cff)' : '#555';
|
|
407
|
+
updateDynamicCSS();
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
forceBtn.addEventListener('click', () => {
|
|
411
|
+
forceRTL = !forceRTL;
|
|
412
|
+
saveConfig();
|
|
413
|
+
forceKnob.style.transform = forceRTL ? 'translateX(20px)' : 'translateX(0)';
|
|
414
|
+
forceBtn.style.backgroundColor = forceRTL ? 'var(--color-token-charts-blue, #339cff)' : '#555';
|
|
415
|
+
updateDynamicCSS();
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
atBtn.addEventListener('click', () => {
|
|
419
|
+
fixAtSign = !fixAtSign;
|
|
420
|
+
saveConfig();
|
|
421
|
+
atKnob.style.transform = fixAtSign ? 'translateX(20px)' : 'translateX(0)';
|
|
422
|
+
atBtn.style.backgroundColor = fixAtSign ? 'var(--color-token-charts-blue, #339cff)' : '#555';
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
faFontInput.addEventListener('input', () => {
|
|
426
|
+
savedFaFont = faFontInput.value.trim();
|
|
427
|
+
saveConfig();
|
|
428
|
+
updateDynamicCSS();
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
enFontInput.addEventListener('input', () => {
|
|
432
|
+
savedEnFont = enFontInput.value.trim();
|
|
433
|
+
saveConfig();
|
|
434
|
+
updateDynamicCSS();
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
codeFontInput.addEventListener('input', () => {
|
|
438
|
+
savedCodeFont = codeFontInput.value.trim();
|
|
439
|
+
saveConfig();
|
|
440
|
+
updateDynamicCSS();
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
lhInput.addEventListener('input', () => {
|
|
444
|
+
savedLH = lhInput.value;
|
|
445
|
+
saveConfig();
|
|
446
|
+
updateDynamicCSS();
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
lhResetBtn.addEventListener('click', () => {
|
|
450
|
+
lhInput.value = '1.6';
|
|
451
|
+
savedLH = '1.6';
|
|
452
|
+
saveConfig();
|
|
453
|
+
updateDynamicCSS();
|
|
454
|
+
});
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-rtl",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "A smart RTL (Right-to-Left) patcher with UI settings for the Codex application.",
|
|
5
|
-
"main": "index.js",
|
|
5
|
+
"main": "./bin/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"codex-rtl": "./bin/index.js"
|
|
8
8
|
},
|
|
@@ -32,5 +32,12 @@
|
|
|
32
32
|
"bugs": {
|
|
33
33
|
"url": "https://github.com/mmnaderi/codex-rtl/issues"
|
|
34
34
|
},
|
|
35
|
-
"homepage": "https://github.com/mmnaderi/codex-rtl#readme"
|
|
35
|
+
"homepage": "https://github.com/mmnaderi/codex-rtl#readme",
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@electron/asar": "^4.2.0",
|
|
38
|
+
"ora": "^9.4.1",
|
|
39
|
+
"picocolors": "^1.1.1",
|
|
40
|
+
"prompts": "^2.4.2"
|
|
41
|
+
},
|
|
42
|
+
"type": "module"
|
|
36
43
|
}
|