muxmind-ai 2.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.
- package/.env.example +11 -0
- package/README.md +81 -0
- package/assets/favicon.png +0 -0
- package/assets/muxmind-logo-full.png +0 -0
- package/assets/muxmind-logo-icon.png +0 -0
- package/bin/cli.js +98 -0
- package/index.html +293 -0
- package/package.json +47 -0
- package/server.js +265 -0
- package/src/api-manager.js +227 -0
- package/src/auth.js +159 -0
- package/src/config.js +267 -0
- package/src/file-parser.js +122 -0
- package/src/image-engine.js +73 -0
- package/src/router.js +317 -0
- package/src/tts-engine.js +88 -0
- package/src/ui-render.js +91 -0
- package/src-client/app.js +1306 -0
- package/src-client/i18n.js +229 -0
- package/style.css +948 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* MuxMind AI — Client-side i18n (English / Arabic + RTL)
|
|
4
|
+
* Any element with data-i18n="key" gets its textContent (or, for elements
|
|
5
|
+
* with data-i18n-html, its innerHTML) replaced by the translated string.
|
|
6
|
+
* data-i18n-placeholder and data-i18n-title do the same for those attrs.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const I18N_DICT = {
|
|
10
|
+
en: {
|
|
11
|
+
'login.title': 'MuxMind AI',
|
|
12
|
+
'login.subtitle': 'Enterprise Orchestration Engine — sign in to continue',
|
|
13
|
+
'login.password': 'Password',
|
|
14
|
+
'login.unlock': 'Unlock',
|
|
15
|
+
'login.hint': 'First run only: default password is <strong>muxmind2026</strong>. Change it any time from Settings once signed in.',
|
|
16
|
+
'rail.chat': 'Chat',
|
|
17
|
+
'rail.image': 'Image Studio',
|
|
18
|
+
'rail.providers': 'Providers',
|
|
19
|
+
'rail.map': 'Network Map',
|
|
20
|
+
'rail.settings': 'Settings',
|
|
21
|
+
'rail.logout': 'Log out',
|
|
22
|
+
'nav.smartRouting': '🧠 Smart Auto-Routing',
|
|
23
|
+
'nav.vault': 'API Vault',
|
|
24
|
+
'chat.emptyHint': 'Configure your provider keys in the API Vault, then start chatting.<br/>The smart router picks the best model for each reply and fails over automatically if a provider goes down.',
|
|
25
|
+
'chat.jumpLatest': '↓ New messages',
|
|
26
|
+
'chat.placeholder': 'Ask MuxMind anything...',
|
|
27
|
+
'chat.tokenSaver': 'Smart Token Saver',
|
|
28
|
+
'chat.noProvider': '⚠️ No active provider configured. Open the API Vault, add a key, and wait for the smart validation to pass.',
|
|
29
|
+
'chat.attachedFiles': '(attached files)',
|
|
30
|
+
'history.title': 'Chats',
|
|
31
|
+
'history.new': 'New chat',
|
|
32
|
+
'history.search': 'Search chats...',
|
|
33
|
+
'history.untitled': 'New conversation',
|
|
34
|
+
'history.confirmDelete': 'Delete this conversation?',
|
|
35
|
+
'history.confirmClear': 'Delete all saved conversations from this browser?',
|
|
36
|
+
'history.cleared': 'Chat history cleared.',
|
|
37
|
+
'image.title': '🎨 Image Studio',
|
|
38
|
+
'image.subtitle': 'Generate images using an OpenAI or Gemini key from your Vault.',
|
|
39
|
+
'image.promptPlaceholder': 'Describe the image you want...',
|
|
40
|
+
'image.generate': 'Generate',
|
|
41
|
+
'image.generating': 'Generating...',
|
|
42
|
+
'image.empty': 'No images yet. Add an OpenAI or Gemini key in the Vault, then describe an image above.',
|
|
43
|
+
'image.noProvider': 'Add a live OpenAI or Gemini key in the Vault to generate images.',
|
|
44
|
+
'image.download': 'Download',
|
|
45
|
+
'providers.title': '🔌 Providers',
|
|
46
|
+
'providers.subtitle': 'Where to get an API key for each provider MuxMind can orchestrate — free and paid. Add the key in the Vault once you have it.',
|
|
47
|
+
'providers.getKey': 'Get API Key ↗',
|
|
48
|
+
'providers.docs': 'Docs ↗',
|
|
49
|
+
'providers.noKey': 'No key configured yet',
|
|
50
|
+
'map.empty': 'No providers connected yet. Add a key in the API Vault and it will appear here automatically.',
|
|
51
|
+
'map.active': 'Active provider',
|
|
52
|
+
'map.notConfigured': 'NOT CONFIGURED',
|
|
53
|
+
'map.online': 'ONLINE',
|
|
54
|
+
'settings.title': '⚙️ Settings',
|
|
55
|
+
'settings.subtitle': 'Appearance, language, security, and session preferences.',
|
|
56
|
+
'settings.appearance': 'Appearance',
|
|
57
|
+
'settings.theme': 'Theme',
|
|
58
|
+
'settings.themeDesc': 'Switch between dark and light interface.',
|
|
59
|
+
'settings.dark': 'Dark',
|
|
60
|
+
'settings.light': 'Light',
|
|
61
|
+
'settings.language': 'Language',
|
|
62
|
+
'settings.languageDesc': 'Switch the interface language.',
|
|
63
|
+
'settings.security': 'Security',
|
|
64
|
+
'settings.session': 'Session',
|
|
65
|
+
'settings.sessionDesc': "You're currently signed in on this device.",
|
|
66
|
+
'settings.changePassword': 'Change password',
|
|
67
|
+
'settings.changePasswordDesc': 'Update your login password right here — no server restart needed.',
|
|
68
|
+
'settings.currentPassword': 'Current password',
|
|
69
|
+
'settings.newPassword': 'New password (min 6 chars)',
|
|
70
|
+
'settings.confirmPassword': 'Confirm new password',
|
|
71
|
+
'settings.updatePassword': 'Update Password',
|
|
72
|
+
'settings.pwMismatch': 'New passwords do not match.',
|
|
73
|
+
'settings.pwSuccess': 'Password updated. You are still signed in.',
|
|
74
|
+
'settings.clearVault': 'Clear local vault',
|
|
75
|
+
'settings.clearVaultDesc': 'Removes all saved provider keys from this browser.',
|
|
76
|
+
'settings.clearVaultBtn': 'Clear Vault',
|
|
77
|
+
'settings.clearHistory': 'Clear chat history',
|
|
78
|
+
'settings.clearHistoryDesc': 'Deletes all saved conversations from this browser.',
|
|
79
|
+
'settings.clearHistoryBtn': 'Clear History',
|
|
80
|
+
'settings.smartRouter': 'Smart Router',
|
|
81
|
+
'settings.singleModel': 'Single model per reply',
|
|
82
|
+
'settings.singleModelDesc': 'Each answer is generated by one model start-to-finish — never a mix of models in the same reply.',
|
|
83
|
+
'settings.autoTier': 'Auto tier-matching',
|
|
84
|
+
'settings.autoTierDesc': 'Router estimates task complexity and prefers a matching model tier automatically.',
|
|
85
|
+
'settings.liveOnly': 'Live models only',
|
|
86
|
+
'settings.liveOnlyDesc': 'Only keys that pass a real completion probe are ever used to route a request.',
|
|
87
|
+
'settings.adaptiveFallback': 'Adaptive fallback',
|
|
88
|
+
'settings.adaptiveFallbackDesc': 'If a model fails before producing output, MuxMind fails over to the next best live model.',
|
|
89
|
+
'settings.enabled': 'Enabled',
|
|
90
|
+
'vault.title': '🔐 API Key Vault',
|
|
91
|
+
'vault.note': 'Keys are stored <strong>only</strong> in this browser\'s local storage. Each key is validated with a real completion probe (not just a listing check) so only genuinely callable models appear — this eliminates false "404" model entries. Nothing is committed to the repo or sent anywhere except directly to the provider you choose.',
|
|
92
|
+
'vault.provider': 'Provider',
|
|
93
|
+
'vault.label': 'Custom Label',
|
|
94
|
+
'vault.labelPlaceholder': 'e.g. My Groq Key',
|
|
95
|
+
'vault.key': 'API Key',
|
|
96
|
+
'vault.keyPlaceholder': 'Paste your API key',
|
|
97
|
+
'vault.save': '➕ Save & Smart-Validate',
|
|
98
|
+
'vault.empty': 'No providers configured yet. Add one above.',
|
|
99
|
+
'vault.liveCount': 'live',
|
|
100
|
+
'vault.checking': 'checking...',
|
|
101
|
+
'vault.dead': 'dead',
|
|
102
|
+
},
|
|
103
|
+
ar: {
|
|
104
|
+
'login.title': 'ماكس مايند AI',
|
|
105
|
+
'login.subtitle': 'محرك تنسيق النماذج الذكية — سجّل الدخول للمتابعة',
|
|
106
|
+
'login.password': 'كلمة المرور',
|
|
107
|
+
'login.unlock': 'دخول',
|
|
108
|
+
'login.hint': 'أول تشغيل فقط: كلمة المرور الافتراضية <strong>muxmind2026</strong>. يمكنك تغييرها في أي وقت من الإعدادات بعد الدخول.',
|
|
109
|
+
'rail.chat': 'المحادثة',
|
|
110
|
+
'rail.image': 'استوديو الصور',
|
|
111
|
+
'rail.providers': 'المزوّدون',
|
|
112
|
+
'rail.map': 'خريطة الشبكة',
|
|
113
|
+
'rail.settings': 'الإعدادات',
|
|
114
|
+
'rail.logout': 'تسجيل الخروج',
|
|
115
|
+
'nav.smartRouting': '🧠 توجيه ذكي تلقائي',
|
|
116
|
+
'nav.vault': 'خزنة المفاتيح',
|
|
117
|
+
'chat.emptyHint': 'أضف مفاتيح المزودين من خزنة API، ثم ابدأ الدردشة.<br/>الموجّه الذكي يختار أفضل نموذج لكل رد، وينتقل تلقائيًا لنموذج آخر إذا تعطّل المزوّد.',
|
|
118
|
+
'chat.jumpLatest': '↓ رسائل جديدة',
|
|
119
|
+
'chat.placeholder': 'اسأل ماكس مايند أي شيء...',
|
|
120
|
+
'chat.tokenSaver': 'موفّر التوكنز الذكي',
|
|
121
|
+
'chat.noProvider': '⚠️ لا يوجد مزوّد نشط. افتح خزنة API وأضف مفتاحًا وانتظر اكتمال التحقق الذكي.',
|
|
122
|
+
'chat.attachedFiles': '(ملفات مرفقة)',
|
|
123
|
+
'history.title': 'الدردشات',
|
|
124
|
+
'history.new': 'دردشة جديدة',
|
|
125
|
+
'history.search': 'ابحث في الدردشات...',
|
|
126
|
+
'history.untitled': 'محادثة جديدة',
|
|
127
|
+
'history.confirmDelete': 'حذف هذه المحادثة؟',
|
|
128
|
+
'history.confirmClear': 'حذف كل المحادثات المحفوظة من هذا المتصفح؟',
|
|
129
|
+
'history.cleared': 'تم حذف سجل الدردشات.',
|
|
130
|
+
'image.title': '🎨 استوديو الصور',
|
|
131
|
+
'image.subtitle': 'أنشئ صورًا باستخدام مفتاح OpenAI أو Gemini من خزنتك.',
|
|
132
|
+
'image.promptPlaceholder': 'صف الصورة التي تريدها...',
|
|
133
|
+
'image.generate': 'إنشاء',
|
|
134
|
+
'image.generating': 'جارٍ الإنشاء...',
|
|
135
|
+
'image.empty': 'لا توجد صور بعد. أضف مفتاح OpenAI أو Gemini في الخزنة، ثم صف صورة في الأعلى.',
|
|
136
|
+
'image.noProvider': 'أضف مفتاح OpenAI أو Gemini شغّالًا في الخزنة لإنشاء الصور.',
|
|
137
|
+
'image.download': 'تنزيل',
|
|
138
|
+
'providers.title': '🔌 المزوّدون',
|
|
139
|
+
'providers.subtitle': 'من أين تحصل على مفتاح API لكل مزوّد يدعمه ماكس مايند — مجاني ومدفوع. أضف المفتاح في الخزنة بعد الحصول عليه.',
|
|
140
|
+
'providers.getKey': 'احصل على مفتاح ↗',
|
|
141
|
+
'providers.docs': 'التوثيق ↗',
|
|
142
|
+
'providers.noKey': 'لم يُضَف مفتاح بعد',
|
|
143
|
+
'map.empty': 'لا يوجد مزوّدون متصلون بعد. أضف مفتاحًا في خزنة API وسيظهر هنا تلقائيًا.',
|
|
144
|
+
'map.active': 'مزوّد نشط',
|
|
145
|
+
'map.notConfigured': 'غير مُهيّأ',
|
|
146
|
+
'map.online': 'متصل',
|
|
147
|
+
'settings.title': '⚙️ الإعدادات',
|
|
148
|
+
'settings.subtitle': 'المظهر واللغة والأمان وتفضيلات الجلسة.',
|
|
149
|
+
'settings.appearance': 'المظهر',
|
|
150
|
+
'settings.theme': 'السمة',
|
|
151
|
+
'settings.themeDesc': 'التبديل بين الوضع الداكن والفاتح.',
|
|
152
|
+
'settings.dark': 'داكن',
|
|
153
|
+
'settings.light': 'فاتح',
|
|
154
|
+
'settings.language': 'اللغة',
|
|
155
|
+
'settings.languageDesc': 'تغيير لغة الواجهة.',
|
|
156
|
+
'settings.security': 'الأمان',
|
|
157
|
+
'settings.session': 'الجلسة',
|
|
158
|
+
'settings.sessionDesc': 'أنت مسجّل الدخول حاليًا على هذا الجهاز.',
|
|
159
|
+
'settings.changePassword': 'تغيير كلمة المرور',
|
|
160
|
+
'settings.changePasswordDesc': 'حدّث كلمة مرور الدخول من هنا مباشرة — بدون إعادة تشغيل السيرفر.',
|
|
161
|
+
'settings.currentPassword': 'كلمة المرور الحالية',
|
|
162
|
+
'settings.newPassword': 'كلمة المرور الجديدة (٦ أحرف على الأقل)',
|
|
163
|
+
'settings.confirmPassword': 'تأكيد كلمة المرور الجديدة',
|
|
164
|
+
'settings.updatePassword': 'تحديث كلمة المرور',
|
|
165
|
+
'settings.pwMismatch': 'كلمتا المرور الجديدتان غير متطابقتين.',
|
|
166
|
+
'settings.pwSuccess': 'تم تحديث كلمة المرور. أنت ما زلت مسجّل الدخول.',
|
|
167
|
+
'settings.clearVault': 'مسح الخزنة المحلية',
|
|
168
|
+
'settings.clearVaultDesc': 'يحذف جميع مفاتيح المزودين المحفوظة من هذا المتصفح.',
|
|
169
|
+
'settings.clearVaultBtn': 'مسح الخزنة',
|
|
170
|
+
'settings.clearHistory': 'مسح سجل الدردشات',
|
|
171
|
+
'settings.clearHistoryDesc': 'يحذف جميع المحادثات المحفوظة من هذا المتصفح.',
|
|
172
|
+
'settings.clearHistoryBtn': 'مسح السجل',
|
|
173
|
+
'settings.smartRouter': 'الموجّه الذكي',
|
|
174
|
+
'settings.singleModel': 'نموذج واحد لكل رد',
|
|
175
|
+
'settings.singleModelDesc': 'كل رد يُنشئه نموذج واحد من البداية للنهاية — بدون خلط بين النماذج في نفس الرد.',
|
|
176
|
+
'settings.autoTier': 'مطابقة المستوى تلقائيًا',
|
|
177
|
+
'settings.autoTierDesc': 'يقدّر الموجّه تعقيد المهمة ويفضّل مستوى نموذج مناسبًا تلقائيًا.',
|
|
178
|
+
'settings.liveOnly': 'النماذج الشغّالة فقط',
|
|
179
|
+
'settings.liveOnlyDesc': 'لا يُستخدم في التوجيه إلا المفاتيح التي اجتازت اختبار استدعاء حقيقي.',
|
|
180
|
+
'settings.adaptiveFallback': 'تحويل تلقائي عند الفشل',
|
|
181
|
+
'settings.adaptiveFallbackDesc': 'إذا فشل نموذج قبل إنتاج أي رد، ينتقل ماكس مايند تلقائيًا لأفضل نموذج شغّال آخر.',
|
|
182
|
+
'settings.enabled': 'مفعّل',
|
|
183
|
+
'vault.title': '🔐 خزنة مفاتيح API',
|
|
184
|
+
'vault.note': 'تُحفظ المفاتيح <strong>فقط</strong> في تخزين هذا المتصفح المحلي. كل مفتاح يُختبر باستدعاء حقيقي (وليس فقط قائمة) بحيث تظهر النماذج الشغّالة فعليًا فقط — هذا يلغي أخطاء "404" الوهمية. لا شيء يُرسل لأي جهة سوى المزوّد الذي تختاره مباشرة.',
|
|
185
|
+
'vault.provider': 'المزوّد',
|
|
186
|
+
'vault.label': 'تسمية مخصصة',
|
|
187
|
+
'vault.labelPlaceholder': 'مثال: مفتاح Groq الخاص بي',
|
|
188
|
+
'vault.key': 'مفتاح API',
|
|
189
|
+
'vault.keyPlaceholder': 'الصق مفتاح API',
|
|
190
|
+
'vault.save': '➕ حفظ والتحقق الذكي',
|
|
191
|
+
'vault.empty': 'لا يوجد مزوّدون مُهيّأون بعد. أضف واحدًا أعلاه.',
|
|
192
|
+
'vault.liveCount': 'شغّال',
|
|
193
|
+
'vault.checking': 'جارٍ التحقق...',
|
|
194
|
+
'vault.dead': 'معطّل',
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
let currentLang = localStorage.getItem('muxmind_lang') || 'en';
|
|
199
|
+
|
|
200
|
+
function t(key) {
|
|
201
|
+
return (I18N_DICT[currentLang] && I18N_DICT[currentLang][key]) || I18N_DICT.en[key] || key;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function applyI18n(lang) {
|
|
205
|
+
currentLang = lang;
|
|
206
|
+
localStorage.setItem('muxmind_lang', lang);
|
|
207
|
+
document.documentElement.lang = lang;
|
|
208
|
+
document.documentElement.dir = lang === 'ar' ? 'rtl' : 'ltr';
|
|
209
|
+
|
|
210
|
+
document.querySelectorAll('[data-i18n]').forEach((el) => {
|
|
211
|
+
const key = el.getAttribute('data-i18n');
|
|
212
|
+
el.innerHTML = t(key);
|
|
213
|
+
});
|
|
214
|
+
document.querySelectorAll('[data-i18n-placeholder]').forEach((el) => {
|
|
215
|
+
el.setAttribute('placeholder', t(el.getAttribute('data-i18n-placeholder')));
|
|
216
|
+
});
|
|
217
|
+
document.querySelectorAll('[data-i18n-title]').forEach((el) => {
|
|
218
|
+
el.setAttribute('title', t(el.getAttribute('data-i18n-title')));
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const langBtnEn = document.getElementById('lang-btn-en');
|
|
222
|
+
const langBtnAr = document.getElementById('lang-btn-ar');
|
|
223
|
+
if (langBtnEn && langBtnAr) {
|
|
224
|
+
langBtnEn.classList.toggle('active', lang === 'en');
|
|
225
|
+
langBtnAr.classList.toggle('active', lang === 'ar');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (typeof window.onLangChanged === 'function') window.onLangChanged(lang);
|
|
229
|
+
}
|