From 3c0858e73f9a5319264efef2fc5e0dc778fc73df Mon Sep 17 00:00:00 2001 From: Coki Date: Thu, 23 Jan 2025 10:54:04 +0800 Subject: [PATCH 1/6] refactor: remove unused imports and refactor components for consistency --- .../editor/common/FileUploadButton.tsx | 11 ++--- app/components/editor/core/EngineSelector.tsx | 31 +++++++----- app/components/editor/index.tsx | 26 +++++----- .../editor/panels/PolicyToolbar.tsx | 47 +++++++++++++++++++ 4 files changed, 82 insertions(+), 33 deletions(-) create mode 100644 app/components/editor/panels/PolicyToolbar.tsx diff --git a/app/components/editor/common/FileUploadButton.tsx b/app/components/editor/common/FileUploadButton.tsx index 422a712..8a1931f 100644 --- a/app/components/editor/common/FileUploadButton.tsx +++ b/app/components/editor/common/FileUploadButton.tsx @@ -1,6 +1,5 @@ -import React, { useRef } from 'react'; +import { useRef } from 'react'; import clsx from 'clsx'; -import { useLang } from '@/app/context/LangContext'; interface FileUploadButtonProps { onFileContent: (content: string) => void; @@ -10,7 +9,6 @@ interface FileUploadButtonProps { export const FileUploadButton: React.FC = ({ onFileContent, accept = '.txt,.conf,.csv', className }) => { const fileInputRef = useRef(null); - const { t } = useLang(); const handleFileChange = async (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; @@ -43,6 +41,7 @@ export const FileUploadButton: React.FC = ({ onFileConten 'flex items-center gap-1', 'rounded', 'px-2', + 'py-1', 'border border-[#453d7d]', 'text-[#453d7a]', 'bg-[#efefef]', @@ -50,10 +49,10 @@ export const FileUploadButton: React.FC = ({ onFileConten 'transition-colors duration-500', )} > - - + + {/* eslint-disable-next-line max-len */} + - {t('Load')} ); diff --git a/app/components/editor/core/EngineSelector.tsx b/app/components/editor/core/EngineSelector.tsx index 47ff57a..7bb0889 100644 --- a/app/components/editor/core/EngineSelector.tsx +++ b/app/components/editor/core/EngineSelector.tsx @@ -1,6 +1,4 @@ import React, { useState, useRef, useEffect } from 'react'; -import { useLang } from '@/app/context/LangContext'; - interface EngineVersion { libVersion: string; @@ -53,7 +51,6 @@ export const EngineSelector: React.FC = ({ engineGithubLinks, }) => { const [isOpen, setIsOpen] = useState(false); - const { t } = useLang(); const dropdownRef = useRef(null); const engines = AVAILABLE_ENGINES(casbinVersion, javaVersion, goVersion); @@ -88,15 +85,22 @@ export const EngineSelector: React.FC = ({ {isOpen && ( diff --git a/app/components/editor/index.tsx b/app/components/editor/index.tsx index 45196b4..98eaac7 100755 --- a/app/components/editor/index.tsx +++ b/app/components/editor/index.tsx @@ -14,9 +14,9 @@ import { CasbinConfSupport } from '@/app/components/editor/casbin-mode/casbin-co import { CasbinPolicySupport } from '@/app/components/editor/casbin-mode/casbin-csv'; import SidePanelChat from '@/app/components/editor/panels/SidePanelChat'; import { CustomConfigPanel } from '@/app/components/editor/panels/CustomConfigPanel'; +import { PolicyToolbar } from '@/app/components/editor/panels/PolicyToolbar'; import { MessageWithTooltip } from '@/app/components/editor/common/MessageWithTooltip'; import { FileUploadButton } from '@/app/components/editor/common/FileUploadButton'; -import { EngineSelector } from '@/app/components/editor/core/EngineSelector'; import { e, m, p, r } from '@/app/components/hooks/useSetupEnforceContext'; import useRunTest from '@/app/components/hooks/useRunTest'; import useShareInfo from '@/app/components/hooks/useShareInfo'; @@ -274,20 +274,16 @@ export const EditorScreen = () => {
{t('Policy')}
-
-
- -
- -
+
diff --git a/app/components/editor/panels/PolicyToolbar.tsx b/app/components/editor/panels/PolicyToolbar.tsx new file mode 100644 index 0000000..62d30be --- /dev/null +++ b/app/components/editor/panels/PolicyToolbar.tsx @@ -0,0 +1,47 @@ +import { FileUploadButton } from '@/app/components/editor/common/FileUploadButton'; +import { EngineSelector } from '@/app/components/editor/core/EngineSelector'; + +interface PolicyToolbarProps { + setPolicyPersistent: (content: string) => void; + selectedEngine: string; + comparisonEngines: string[]; + handleEngineChange: (newPrimary: string, newComparison: string[]) => void; + casbinVersion?: string; + javaVersion?: { + libVersion: string; + engineVersion: string; + }; + goVersion?: { + libVersion: string; + engineVersion: string; + }; + engineGithubLinks: Record; +} + +export const PolicyToolbar: React.FC = ({ + setPolicyPersistent, + selectedEngine, + comparisonEngines, + handleEngineChange, + casbinVersion, + javaVersion, + goVersion, + engineGithubLinks, +}) => { + return ( +
+
+ +
+ +
+ ); +}; From 3f0cafa9b569b2f140b38495a33f94521fc5cfd6 Mon Sep 17 00:00:00 2001 From: Coki Date: Thu, 23 Jan 2025 13:34:42 +0800 Subject: [PATCH 2/6] feat: add EndpointSelector component and integrate with PolicyToolbar --- .../editor/common/EndpointSelector.tsx | 119 ++++++++++++++++++ .../editor/panels/PolicyToolbar.tsx | 2 + app/components/hooks/useRemoteEnforcer.ts | 6 +- 3 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 app/components/editor/common/EndpointSelector.tsx diff --git a/app/components/editor/common/EndpointSelector.tsx b/app/components/editor/common/EndpointSelector.tsx new file mode 100644 index 0000000..c588a76 --- /dev/null +++ b/app/components/editor/common/EndpointSelector.tsx @@ -0,0 +1,119 @@ +import React from 'react'; +import { clsx } from 'clsx'; +import { DEFAULT_ENDPOINT } from '@/app/components/hooks/useRemoteEnforcer'; + +const ENDPOINTS = [ + DEFAULT_ENDPOINT, + 'demo.casdoor.com' +]; + +export const EndpointSelector: React.FC = () => { + const [isOpen, setIsOpen] = React.useState(false); + const [selectedEndpoint, setSelectedEndpoint] = React.useState( + window.localStorage.getItem('casbinEndpoint') || DEFAULT_ENDPOINT + ); + const [customEndpoint, setCustomEndpoint] = React.useState(''); + const dropdownRef = React.useRef(null); + + React.useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + setIsOpen(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + return document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + const handleEndpointSelect = (value: string) => { + setSelectedEndpoint(value); + window.localStorage.setItem('casbinEndpoint', value); + setIsOpen(false); + }; + + const handleCustomEndpointChange = (e: React.ChangeEvent) => { + const value = e.target.value; + if (value) { + setCustomEndpoint(value); + setSelectedEndpoint(value); + window.localStorage.setItem('casbinEndpoint', value); + } + }; + + return ( +
+ + + {isOpen && ( +
+
+ {ENDPOINTS.map((endpoint) => { + const isSelected = endpoint === selectedEndpoint; + return ( + + ); + })} +
+
+
+ { + return e === selectedEndpoint; + }) + } + onChange={() => {}} + className="form-checkbox h-4 w-4 text-[#e13c3c] rounded border-gray-300 focus:ring-[#e13c3c] accent-[#e13c3c]" + /> +
+ +
+
+
+
+ )} +
+ ); +}; diff --git a/app/components/editor/panels/PolicyToolbar.tsx b/app/components/editor/panels/PolicyToolbar.tsx index 62d30be..fcf811b 100644 --- a/app/components/editor/panels/PolicyToolbar.tsx +++ b/app/components/editor/panels/PolicyToolbar.tsx @@ -1,5 +1,6 @@ import { FileUploadButton } from '@/app/components/editor/common/FileUploadButton'; import { EngineSelector } from '@/app/components/editor/core/EngineSelector'; +import { EndpointSelector } from '@/app/components/editor/common/EndpointSelector'; interface PolicyToolbarProps { setPolicyPersistent: (content: string) => void; @@ -33,6 +34,7 @@ export const PolicyToolbar: React.FC = ({
+ { try { - const baseUrl = 'https://door.casdoor.com/api/run-casbin-command'; + const baseUrl = `https://${window.localStorage.getItem('casbinEndpoint') || DEFAULT_ENDPOINT}/api/run-casbin-command`; const url = new URL(baseUrl); url.searchParams.set('language', language); url.searchParams.set('args', JSON.stringify(['-v'])); From f9b79d970e589c3aa9e31ce8c87e7203fc143b69 Mon Sep 17 00:00:00 2001 From: Coki Date: Thu, 23 Jan 2025 13:53:21 +0800 Subject: [PATCH 3/6] refactor: improve endpoint selection logic and state management --- app/components/editor/common/EndpointSelector.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/components/editor/common/EndpointSelector.tsx b/app/components/editor/common/EndpointSelector.tsx index c588a76..575ceb8 100644 --- a/app/components/editor/common/EndpointSelector.tsx +++ b/app/components/editor/common/EndpointSelector.tsx @@ -9,10 +9,11 @@ const ENDPOINTS = [ export const EndpointSelector: React.FC = () => { const [isOpen, setIsOpen] = React.useState(false); - const [selectedEndpoint, setSelectedEndpoint] = React.useState( - window.localStorage.getItem('casbinEndpoint') || DEFAULT_ENDPOINT + const storedEndpoint = window.localStorage.getItem('casbinEndpoint') || DEFAULT_ENDPOINT; + const [selectedEndpoint, setSelectedEndpoint] = React.useState(storedEndpoint); + const [customEndpoint, setCustomEndpoint] = React.useState( + ENDPOINTS.includes(storedEndpoint) ? '' : storedEndpoint ); - const [customEndpoint, setCustomEndpoint] = React.useState(''); const dropdownRef = React.useRef(null); React.useEffect(() => { @@ -36,10 +37,14 @@ export const EndpointSelector: React.FC = () => { const handleCustomEndpointChange = (e: React.ChangeEvent) => { const value = e.target.value; + setCustomEndpoint(value); + if (value) { - setCustomEndpoint(value); setSelectedEndpoint(value); window.localStorage.setItem('casbinEndpoint', value); + } else { + setSelectedEndpoint(DEFAULT_ENDPOINT); + window.localStorage.setItem('casbinEndpoint', DEFAULT_ENDPOINT); } }; From ca8c51cfc2d72d37d7028d223abd424de96b9985 Mon Sep 17 00:00:00 2001 From: Coki Date: Thu, 23 Jan 2025 14:00:04 +0800 Subject: [PATCH 4/6] feat: add i18n support for custom endpoint placeholder in EndpointSelector --- app/components/editor/common/EndpointSelector.tsx | 4 +++- messages/ar.json | 5 ++--- messages/de.json | 5 ++--- messages/en.json | 5 ++--- messages/es.json | 5 ++--- messages/fr.json | 5 ++--- messages/id.json | 5 ++--- messages/it.json | 5 ++--- messages/ja.json | 5 ++--- messages/ko.json | 5 ++--- messages/ms.json | 5 ++--- messages/pt.json | 5 ++--- messages/ru.json | 5 ++--- messages/tr.json | 5 ++--- messages/vi.json | 5 ++--- messages/zh-Hant.json | 5 ++--- messages/zh.json | 5 ++--- 17 files changed, 35 insertions(+), 49 deletions(-) diff --git a/app/components/editor/common/EndpointSelector.tsx b/app/components/editor/common/EndpointSelector.tsx index 575ceb8..d30399f 100644 --- a/app/components/editor/common/EndpointSelector.tsx +++ b/app/components/editor/common/EndpointSelector.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { clsx } from 'clsx'; import { DEFAULT_ENDPOINT } from '@/app/components/hooks/useRemoteEnforcer'; +import { useLang } from '@/app/context/LangContext'; const ENDPOINTS = [ DEFAULT_ENDPOINT, @@ -8,6 +9,7 @@ const ENDPOINTS = [ ]; export const EndpointSelector: React.FC = () => { + const { t } = useLang(); const [isOpen, setIsOpen] = React.useState(false); const storedEndpoint = window.localStorage.getItem('casbinEndpoint') || DEFAULT_ENDPOINT; const [selectedEndpoint, setSelectedEndpoint] = React.useState(storedEndpoint); @@ -105,7 +107,7 @@ export const EndpointSelector: React.FC = () => { type="text" value={customEndpoint} onChange={handleCustomEndpointChange} - placeholder="Enter custom endpoint" + placeholder={t('Enter custom endpoint')} className={clsx( 'border border-gray-300 rounded', 'px-2 py-1', diff --git a/messages/ar.json b/messages/ar.json index 3f27dd6..46fb90b 100644 --- a/messages/ar.json +++ b/messages/ar.json @@ -7,9 +7,7 @@ "Select your model": "حدد النموذج الخاص بك", "RESET": "إعادة ضبط", "Policy": "سياسة", - "Compare": "مقارنة", "Request": "طلب", - "Load": "تحميل", "Enforcement Result": "نتيجة التنفيذ", "Why this result": "لماذا هذه النتيجة", "RUN THE TEST": "تشغيل الاختبار", @@ -18,5 +16,6 @@ "Share failed": "مشاركة فشلت", "No content to share": "لا يوجد محتوى للمشاركة", "Link copied to clipboard": "رابط منسوخ إلى الملف المطلق", - "Test completed successfully": "اختبار منجز بنجاح" + "Test completed successfully": "اختبار منجز بنجاح", + "Enter custom endpoint": "أدخل نهاية مخصصة" } diff --git a/messages/de.json b/messages/de.json index 54a4197..b022107 100644 --- a/messages/de.json +++ b/messages/de.json @@ -7,9 +7,7 @@ "Select your model": "Wählen Sie Ihr Modell", "RESET": "Zurücksetzen", "Policy": "Richtlinie", - "Compare": "Vergleichen", "Request": "Anfrage", - "Load": "Laden", "Enforcement Result": "Durchsetzungsergebnis", "Why this result": "Warum dieses Ergebnis?", "RUN THE TEST": "Test ausführen", @@ -18,5 +16,6 @@ "Share failed": "Teilen fehlgeschlagen", "No content to share": "Kein Inhalt zum Teilen", "Link copied to clipboard": "Link in die Zwischenablage kopiert", - "Test completed successfully": "Test erfolgreich abgeschlossen" + "Test completed successfully": "Test erfolgreich abgeschlossen", + "Enter custom endpoint": "Benutzerdefinierte Endpunkt eingeben" } diff --git a/messages/en.json b/messages/en.json index 6f0f775..5017a48 100644 --- a/messages/en.json +++ b/messages/en.json @@ -7,9 +7,7 @@ "Select your model": "Select your model", "RESET": "RESET", "Policy": "Policy", - "Compare": "Compare", "Request": "Request", - "Load": "Load", "Enforcement Result": "Enforcement Result", "Why this result": "Why this result", "RUN THE TEST": "RUN THE TEST", @@ -18,5 +16,6 @@ "Share failed": "Share failed", "No content to share": "No content to share", "Link copied to clipboard": "Link copied to clipboard", - "Test completed successfully": "Test completed successfully" + "Test completed successfully": "Test completed successfully", + "Enter custom endpoint": "Enter custom endpoint" } diff --git a/messages/es.json b/messages/es.json index a147ae4..486c798 100644 --- a/messages/es.json +++ b/messages/es.json @@ -7,9 +7,7 @@ "Select your model": "Seleccione su modelo", "RESET": "REINICIAR", "Policy": "Política", - "Compare": "Comparar", "Request": "Solicitud", - "Load": "Cargar", "Enforcement Result": "Resultado de ejecución", "Why this result": "Por qué este resultado", "RUN THE TEST": "EJECUTAR PRUEBA", @@ -18,5 +16,6 @@ "Share failed": "Compartir falló", "No content to share": "No hay contenido para compartir", "Link copied to clipboard": "Enlace copiado en el portapapeles", - "Test completed successfully": "Prueba completada con éxito" + "Test completed successfully": "Prueba completada con éxito", + "Enter custom endpoint": "Ingrese el endpoint personalizado" } diff --git a/messages/fr.json b/messages/fr.json index 44fc586..a61bbdf 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -7,9 +7,7 @@ "Select your model": "Sélectionnez votre modèle", "RESET": "Réinitialiser", "Policy": "Politique", - "Compare": "Comparer", "Request": "Requête", - "Load": "Charger", "Enforcement Result": "Résultat d'application", "Why this result": "Pourquoi ce résultat ?", "RUN THE TEST": "Exécuter le test", @@ -18,5 +16,6 @@ "Share failed": "Partage échoué", "No content to share": "Aucun contenu à partager", "Link copied to clipboard": "Lien copié dans le presse-papiers", - "Test completed successfully": "Test terminé avec succès" + "Test completed successfully": "Test terminé avec succès", + "Enter custom endpoint": "Entrez l'endpoint personnalisé" } diff --git a/messages/id.json b/messages/id.json index dc55e08..e8ad83e 100644 --- a/messages/id.json +++ b/messages/id.json @@ -7,9 +7,7 @@ "Select your model": "Pilih model Anda", "RESET": "RESET", "Policy": "Kebijakan", - "Compare": "Membandingkan", "Request": "Permintaan", - "Load": "Muat", "Enforcement Result": "Hasil penegakan", "Why this result": "Mengapa hasil ini", "RUN THE TEST": "JALANKAN UJI", @@ -18,5 +16,6 @@ "Share failed": "Membagikan gagal", "No content to share": "Tidak ada konten untuk dibagikan", "Link copied to clipboard": "Tautan disalin ke clipboard", - "Test completed successfully": "Uji berhasil" + "Test completed successfully": "Uji berhasil", + "Enter custom endpoint": "Masukkan endpoint kustom" } diff --git a/messages/it.json b/messages/it.json index 32c2261..a79b443 100644 --- a/messages/it.json +++ b/messages/it.json @@ -7,9 +7,7 @@ "Select your model": "Seleziona il tuo modello", "RESET": "REIMPOSTA", "Policy": "Politica", - "Compare": "Confronta", "Request": "Richiesta", - "Load": "Carica", "Enforcement Result": "Risultato dell'applicazione", "Why this result": "Perché questo risultato", "RUN THE TEST": "ESEGUI IL TEST", @@ -18,5 +16,6 @@ "Share failed": "Condivisione fallita", "No content to share": "Nessun contenuto da condividere", "Link copied to clipboard": "Link copiato nel portapapele", - "Test completed successfully": "Test completato con successo" + "Test completed successfully": "Test completato con successo", + "Enter custom endpoint": "Inserisci l'endpoint personalizzato" } diff --git a/messages/ja.json b/messages/ja.json index fb8e1e8..6bc102c 100644 --- a/messages/ja.json +++ b/messages/ja.json @@ -7,9 +7,7 @@ "Select your model": "モデルを選択", "RESET": "リセット", "Policy": "ポリシー", - "Compare": "比較", "Request": "リクエスト", - "Load": "ロード", "Enforcement Result": "実行結果", "Why this result": "なぜこの結果?", "RUN THE TEST": "テスト実行", @@ -18,5 +16,6 @@ "Share failed": "共有失敗", "No content to share": "共有する内容がありません", "Link copied to clipboard": "リンクがクリップボードにコピーされました", - "Test completed successfully": "テストが完了しました" + "Test completed successfully": "テストが完了しました", + "Enter custom endpoint": "カスタムエンドポイントを入力" } diff --git a/messages/ko.json b/messages/ko.json index 15db6a2..60b9209 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -7,9 +7,7 @@ "Select your model": "모델 선택", "RESET": "재설정", "Policy": "정책", - "Compare": "비교", "Request": "요청", - "Load": "로드", "Enforcement Result": "집행 결과", "Why this result": "이 결과의 이유", "RUN THE TEST": "테스트 실행", @@ -18,5 +16,6 @@ "Share failed": "공유 실패", "No content to share": "공유할 내용이 없습니다", "Link copied to clipboard": "링크가 클립보드에 복사되었습니다", - "Test completed successfully": "테스트가 완료되었습니다" + "Test completed successfully": "테스트가 완료되었습니다", + "Enter custom endpoint": "사용자 정의 엔드포인트 입력" } diff --git a/messages/ms.json b/messages/ms.json index f79d079..3ed261d 100644 --- a/messages/ms.json +++ b/messages/ms.json @@ -7,9 +7,7 @@ "Select your model": "Pilih model anda", "RESET": "TETAPKAN SEMULA", "Policy": "Polisi", - "Compare": "Membandingkan", "Request": "Permintaan", - "Load": "Muat", "Enforcement Result": "Keputusan penguatkuasaan", "Why this result": "Mengapa keputusan ini", "RUN THE TEST": "JALANKAN UJIAN", @@ -18,5 +16,6 @@ "Share failed": "Mengongsikan gagal", "No content to share": "Tidak ada konten untuk dibagikan", "Link copied to clipboard": "Tautan disalin ke clipboard", - "Test completed successfully": "Uji berhasil" + "Test completed successfully": "Uji berhasil", + "Enter custom endpoint": "Masukkan endpoint kustom" } diff --git a/messages/pt.json b/messages/pt.json index 112ee97..1274444 100644 --- a/messages/pt.json +++ b/messages/pt.json @@ -7,9 +7,7 @@ "Select your model": "Selecione seu modelo", "RESET": "REINICIAR", "Policy": "Política", - "Compare": "Comparar", "Request": "Solicitação", - "Load": "Carregar", "Enforcement Result": "Resultado da aplicação", "Why this result": "Por que esse resultado", "RUN THE TEST": "EXECUTAR TESTE", @@ -18,5 +16,6 @@ "Share failed": "Compartilhamento falhou", "No content to share": "Não há conteúdo para compartilhar", "Link copied to clipboard": "Link copiado para o clipboard", - "Test completed successfully": "Teste concluído com sucesso" + "Test completed successfully": "Teste concluído com sucesso", + "Enter custom endpoint": "Insira o endpoint personalizado" } diff --git a/messages/ru.json b/messages/ru.json index 9eceba6..045dda0 100644 --- a/messages/ru.json +++ b/messages/ru.json @@ -7,9 +7,7 @@ "Select your model": "Выберите вашу модель", "RESET": "СБРОС", "Policy": "Политика", - "Compare": "Сравнить", "Request": "Запрос", - "Load": "Загрузить", "Enforcement Result": "Результат исполнения", "Why this result": "Почему этот результат", "RUN THE TEST": "ЗАПУСТИТЬ ТЕСТ", @@ -18,5 +16,6 @@ "Share failed": "Поделиться не удалось", "No content to share": "Нет содержимого для разделения", "Link copied to clipboard": "Ссылка скопирована в буфер обмена", - "Test completed successfully": "Тест завершен успешно" + "Test completed successfully": "Тест завершен успешно", + "Enter custom endpoint": "Введите пользовательский конечный пункт" } diff --git a/messages/tr.json b/messages/tr.json index 50b9a38..332daf2 100644 --- a/messages/tr.json +++ b/messages/tr.json @@ -7,9 +7,7 @@ "Select your model": "Modelinizi seçin", "RESET": "SIFIRLA", "Policy": "Politika", - "Compare": "Karşılaştır", "Request": "İstek", - "Load": "Yükle", "Enforcement Result": "Uygulama Sonucu", "Why this result": "Neden bu sonuç", "RUN THE TEST": "TESTİ ÇALIŞTIR", @@ -18,5 +16,6 @@ "Share failed": "Paylaşım başarısız", "No content to share": "Paylaşılacak içerik yok", "Link copied to clipboard": "Link kopyalandı", - "Test completed successfully": "Test başarıyla tamamlandı" + "Test completed successfully": "Test başarıyla tamamlandı", + "Enter custom endpoint": "Özel endpoint girin" } diff --git a/messages/vi.json b/messages/vi.json index ec3ac66..711280a 100644 --- a/messages/vi.json +++ b/messages/vi.json @@ -7,9 +7,7 @@ "Select your model": "Chọn mô hình của bạn", "RESET": "CÀI LẠI", "Policy": "Chính sách", - "Compare": "So sánh", "Request": "Yêu cầu", - "Load": "Tải", "Enforcement Result": "Kết quả thi hành", "Why this result": "Tại sao kết quả này", "RUN THE TEST": "CHẠY THỬ", @@ -18,5 +16,6 @@ "Share failed": "Chia sẻ thất bại", "No content to share": "Không có nội dung để chia sẻ", "Link copied to clipboard": "Link đã được sao chép vào clipboard", - "Test completed successfully": "Test hoàn tất thành công" + "Test completed successfully": "Test hoàn tất thành công", + "Enter custom endpoint": "Nhập điểm cuối tùy chỉnh" } diff --git a/messages/zh-Hant.json b/messages/zh-Hant.json index b1c2c6f..59098c3 100644 --- a/messages/zh-Hant.json +++ b/messages/zh-Hant.json @@ -7,9 +7,7 @@ "Select your model": "選擇模型", "RESET": "重置", "Policy": "策略", - "Compare": "比較", "Request": "請求", - "Load": "上傳", "Enforcement Result": "執行結果", "Why this result": "為何此結果?", "RUN THE TEST": "運行", @@ -18,5 +16,6 @@ "Share failed": "分享失敗", "No content to share": "沒有內容可以分享", "Link copied to clipboard": "連結已複製到剪貼簿", - "Test completed successfully": "測試成功" + "Test completed successfully": "測試成功", + "Enter custom endpoint": "輸入自定義端點" } diff --git a/messages/zh.json b/messages/zh.json index 66604b7..7afb1b5 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -7,9 +7,7 @@ "Select your model": "选择模型", "RESET": "重置", "Policy": "策略", - "Compare": "比较", "Request": "请求", - "Load": "上传", "Enforcement Result": "执行结果", "Why this result": "为何此结果?", "RUN THE TEST": "运行", @@ -18,5 +16,6 @@ "Share failed": "分享失败", "No content to share": "没有内容可以分享", "Link copied to clipboard": "链接已复制到剪贴板", - "Test completed successfully": "测试成功" + "Test completed successfully": "测试成功", + "Enter custom endpoint": "输入自定义端点" } From f789586e3d47a4621ed41a1418501dad553b839a Mon Sep 17 00:00:00 2001 From: Coki Date: Thu, 23 Jan 2025 15:08:51 +0800 Subject: [PATCH 5/6] refactor: extract endpoint retrieval logic into `getEndpoint` function --- app/components/hooks/useRemoteEnforcer.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/components/hooks/useRemoteEnforcer.ts b/app/components/hooks/useRemoteEnforcer.ts index 634b835..b799048 100644 --- a/app/components/hooks/useRemoteEnforcer.ts +++ b/app/components/hooks/useRemoteEnforcer.ts @@ -26,9 +26,17 @@ export interface VersionInfo { export const DEFAULT_ENDPOINT = 'door.casdoor.com'; +export const getEndpoint = () => { + try { + return window?.localStorage?.getItem('casbinEndpoint') || DEFAULT_ENDPOINT; + } catch { + return DEFAULT_ENDPOINT; + } +}; + export async function remoteEnforcer(props: RemoteEnforcerProps) { try { - const baseUrl = `https://${window.localStorage.getItem('casbinEndpoint') || DEFAULT_ENDPOINT}/api/run-casbin-command`; + const baseUrl = `https://${getEndpoint()}/api/run-casbin-command`; const args = [ 'enforceEx', '-m', @@ -86,7 +94,7 @@ export async function remoteEnforcer(props: RemoteEnforcerProps) { export async function getRemoteVersion(language: 'java' | 'go'): Promise { try { - const baseUrl = `https://${window.localStorage.getItem('casbinEndpoint') || DEFAULT_ENDPOINT}/api/run-casbin-command`; + const baseUrl = `https://${getEndpoint()}/api/run-casbin-command`; const url = new URL(baseUrl); url.searchParams.set('language', language); url.searchParams.set('args', JSON.stringify(['-v'])); From 0cc8f60d0043a11e7892865b8c3380b6b4375d12 Mon Sep 17 00:00:00 2001 From: Coki Date: Thu, 23 Jan 2025 15:51:54 +0800 Subject: [PATCH 6/6] feat: add tooltip components and integrate with existing UI elements --- .../editor/common/EndpointSelector.tsx | 64 +++++---- .../{core => common}/EngineSelector.tsx | 57 +++++--- .../editor/common/FileUploadButton.tsx | 50 ++++--- .../editor/panels/PolicyToolbar.tsx | 2 +- app/components/ui/tooltip.tsx | 35 +++++ app/globals.css | 66 ++++++++- app/utils/lib/utils.ts | 6 + components.json | 21 +++ messages/ar.json | 6 +- messages/de.json | 6 +- messages/en.json | 6 +- messages/es.json | 6 +- messages/fr.json | 6 +- messages/id.json | 6 +- messages/it.json | 6 +- messages/ja.json | 6 +- messages/ko.json | 6 +- messages/ms.json | 6 +- messages/pt.json | 6 +- messages/ru.json | 6 +- messages/tr.json | 6 +- messages/vi.json | 6 +- messages/zh-Hant.json | 6 +- messages/zh.json | 6 +- package.json | 9 +- tailwind.config.ts | 68 ++++++++-- yarn.lock | 128 +++++++++++++++++- 27 files changed, 506 insertions(+), 96 deletions(-) rename app/components/editor/{core => common}/EngineSelector.tsx (58%) create mode 100644 app/components/ui/tooltip.tsx create mode 100644 app/utils/lib/utils.ts create mode 100644 components.json diff --git a/app/components/editor/common/EndpointSelector.tsx b/app/components/editor/common/EndpointSelector.tsx index d30399f..4d2bae6 100644 --- a/app/components/editor/common/EndpointSelector.tsx +++ b/app/components/editor/common/EndpointSelector.tsx @@ -2,20 +2,16 @@ import React from 'react'; import { clsx } from 'clsx'; import { DEFAULT_ENDPOINT } from '@/app/components/hooks/useRemoteEnforcer'; import { useLang } from '@/app/context/LangContext'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/app/components/ui/tooltip'; -const ENDPOINTS = [ - DEFAULT_ENDPOINT, - 'demo.casdoor.com' -]; +const ENDPOINTS = [DEFAULT_ENDPOINT, 'demo.casdoor.com']; export const EndpointSelector: React.FC = () => { const { t } = useLang(); const [isOpen, setIsOpen] = React.useState(false); const storedEndpoint = window.localStorage.getItem('casbinEndpoint') || DEFAULT_ENDPOINT; const [selectedEndpoint, setSelectedEndpoint] = React.useState(storedEndpoint); - const [customEndpoint, setCustomEndpoint] = React.useState( - ENDPOINTS.includes(storedEndpoint) ? '' : storedEndpoint - ); + const [customEndpoint, setCustomEndpoint] = React.useState(ENDPOINTS.includes(storedEndpoint) ? '' : storedEndpoint); const dropdownRef = React.useRef(null); React.useEffect(() => { @@ -40,7 +36,7 @@ export const EndpointSelector: React.FC = () => { const handleCustomEndpointChange = (e: React.ChangeEvent) => { const value = e.target.value; setCustomEndpoint(value); - + if (value) { setSelectedEndpoint(value); window.localStorage.setItem('casbinEndpoint', value); @@ -52,26 +48,38 @@ export const EndpointSelector: React.FC = () => { return (
- + + + + + + +

{t('Select Endpoint')}

+
+
+
{isOpen && ( -
+
{ENDPOINTS.map((endpoint) => { const isSelected = endpoint === selectedEndpoint; @@ -81,7 +89,9 @@ export const EndpointSelector: React.FC = () => { {return handleEndpointSelect(endpoint)}} + onChange={() => { + return handleEndpointSelect(endpoint); + }} className="form-checkbox h-4 w-4 text-[#e13c3c] rounded border-gray-300 focus:ring-[#e13c3c] accent-[#e13c3c]" />
diff --git a/app/components/editor/core/EngineSelector.tsx b/app/components/editor/common/EngineSelector.tsx similarity index 58% rename from app/components/editor/core/EngineSelector.tsx rename to app/components/editor/common/EngineSelector.tsx index 7bb0889..accd637 100644 --- a/app/components/editor/core/EngineSelector.tsx +++ b/app/components/editor/common/EngineSelector.tsx @@ -1,4 +1,6 @@ import React, { useState, useRef, useEffect } from 'react'; +import { useLang } from '@/app/context/LangContext'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/app/components/ui/tooltip'; interface EngineVersion { libVersion: string; @@ -50,6 +52,7 @@ export const EngineSelector: React.FC = ({ goVersion, engineGithubLinks, }) => { + const { t } = useLang(); const [isOpen, setIsOpen] = useState(false); const dropdownRef = useRef(null); const engines = AVAILABLE_ENGINES(casbinVersion, javaVersion, goVersion); @@ -103,17 +106,42 @@ export const EngineSelector: React.FC = ({ })} - + + + + + + +

{t('Compare Engines')}

+
+
+
+ + + + + + + {/* eslint-disable-next-line max-len */} + + + + + +

{t('View Source Code')}

+
+
+
{isOpen && (
@@ -140,13 +168,6 @@ export const EngineSelector: React.FC = ({
)} - - - - {/* eslint-disable-next-line max-len */} - - -
); }; diff --git a/app/components/editor/common/FileUploadButton.tsx b/app/components/editor/common/FileUploadButton.tsx index 8a1931f..bc4ea8e 100644 --- a/app/components/editor/common/FileUploadButton.tsx +++ b/app/components/editor/common/FileUploadButton.tsx @@ -1,5 +1,7 @@ import { useRef } from 'react'; import clsx from 'clsx'; +import { useLang } from '@/app/context/LangContext'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/app/components/ui/tooltip'; interface FileUploadButtonProps { onFileContent: (content: string) => void; @@ -9,6 +11,7 @@ interface FileUploadButtonProps { export const FileUploadButton: React.FC = ({ onFileContent, accept = '.txt,.conf,.csv', className }) => { const fileInputRef = useRef(null); + const { t } = useLang(); const handleFileChange = async (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) return; @@ -35,25 +38,34 @@ export const FileUploadButton: React.FC = ({ onFileConten return (
- + + + + + + +

{t('Upload File')}

+
+
+
); }; diff --git a/app/components/editor/panels/PolicyToolbar.tsx b/app/components/editor/panels/PolicyToolbar.tsx index fcf811b..6ec248e 100644 --- a/app/components/editor/panels/PolicyToolbar.tsx +++ b/app/components/editor/panels/PolicyToolbar.tsx @@ -1,5 +1,5 @@ import { FileUploadButton } from '@/app/components/editor/common/FileUploadButton'; -import { EngineSelector } from '@/app/components/editor/core/EngineSelector'; +import { EngineSelector } from '@/app/components/editor/common/EngineSelector'; import { EndpointSelector } from '@/app/components/editor/common/EndpointSelector'; interface PolicyToolbarProps { diff --git a/app/components/ui/tooltip.tsx b/app/components/ui/tooltip.tsx new file mode 100644 index 0000000..aa7711d --- /dev/null +++ b/app/components/ui/tooltip.tsx @@ -0,0 +1,35 @@ +'use client'; + +import * as React from 'react'; +import * as TooltipPrimitive from '@radix-ui/react-tooltip'; + +import { cn } from '@/app/utils/lib/utils'; + +const TooltipProvider = TooltipPrimitive.Provider; + +const Tooltip = TooltipPrimitive.Root; + +const TooltipTrigger = TooltipPrimitive.Trigger; + +const TooltipContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => { + return ( + + + + ); +}); +TooltipContent.displayName = TooltipPrimitive.Content.displayName; + +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; diff --git a/app/globals.css b/app/globals.css index a264cea..779598e 100644 --- a/app/globals.css +++ b/app/globals.css @@ -40,4 +40,68 @@ button:hover img { .btn-disabled { @apply border-gray-300 text-gray-300 bg-gray-100 cursor-not-allowed; } -/* ------------------------------ */ \ No newline at end of file +/* ------------------------------ */ + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 0 0% 3.9%; + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + --primary: 0 0% 9%; + --primary-foreground: 0 0% 98%; + --secondary: 0 0% 96.1%; + --secondary-foreground: 0 0% 9%; + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 89.8%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --radius: 0.5rem; + } + .dark { + --background: 0 0% 3.9%; + --foreground: 0 0% 98%; + --card: 0 0% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 0 0% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 0 0% 9%; + --secondary: 0 0% 14.9%; + --secondary-foreground: 0 0% 98%; + --muted: 0 0% 14.9%; + --muted-foreground: 0 0% 63.9%; + --accent: 0 0% 14.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 14.9%; + --input: 0 0% 14.9%; + --ring: 0 0% 83.1%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } +} +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/app/utils/lib/utils.ts b/app/utils/lib/utils.ts new file mode 100644 index 0000000..bd0c391 --- /dev/null +++ b/app/utils/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/components.json b/components.json new file mode 100644 index 0000000..cbbf848 --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/utils/lib/utils", + "ui": "@/components/ui", + "lib": "@/utils", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/messages/ar.json b/messages/ar.json index 46fb90b..614724b 100644 --- a/messages/ar.json +++ b/messages/ar.json @@ -17,5 +17,9 @@ "No content to share": "لا يوجد محتوى للمشاركة", "Link copied to clipboard": "رابط منسوخ إلى الملف المطلق", "Test completed successfully": "اختبار منجز بنجاح", - "Enter custom endpoint": "أدخل نهاية مخصصة" + "Enter custom endpoint": "أدخل نهاية مخصصة", + "Upload File": "رفع ملف", + "Select Endpoint": "اختيار نهاية", + "Compare Engines": "مقارنة المحركات", + "View Source Code": "عرض الكود المصدري" } diff --git a/messages/de.json b/messages/de.json index b022107..8480f3e 100644 --- a/messages/de.json +++ b/messages/de.json @@ -17,5 +17,9 @@ "No content to share": "Kein Inhalt zum Teilen", "Link copied to clipboard": "Link in die Zwischenablage kopiert", "Test completed successfully": "Test erfolgreich abgeschlossen", - "Enter custom endpoint": "Benutzerdefinierte Endpunkt eingeben" + "Enter custom endpoint": "Benutzerdefinierte Endpunkt eingeben", + "Upload File": "Datei hochladen", + "Select Endpoint": "Endpunkt auswählen", + "Compare Engines": "Engines vergleichen", + "View Source Code": "Quellcode anzeigen" } diff --git a/messages/en.json b/messages/en.json index 5017a48..ba53036 100644 --- a/messages/en.json +++ b/messages/en.json @@ -17,5 +17,9 @@ "No content to share": "No content to share", "Link copied to clipboard": "Link copied to clipboard", "Test completed successfully": "Test completed successfully", - "Enter custom endpoint": "Enter custom endpoint" + "Enter custom endpoint": "Enter custom endpoint", + "Upload File": "Upload File", + "Select Endpoint": "Select Endpoint", + "Compare Engines": "Compare Engines", + "View Source Code": "View Source Code" } diff --git a/messages/es.json b/messages/es.json index 486c798..0d63b6a 100644 --- a/messages/es.json +++ b/messages/es.json @@ -17,5 +17,9 @@ "No content to share": "No hay contenido para compartir", "Link copied to clipboard": "Enlace copiado en el portapapeles", "Test completed successfully": "Prueba completada con éxito", - "Enter custom endpoint": "Ingrese el endpoint personalizado" + "Enter custom endpoint": "Ingrese el endpoint personalizado", + "Upload File": "Cargar archivo", + "Select Endpoint": "Seleccionar endpoint", + "Compare Engines": "Comparar motores", + "View Source Code": "Ver código fuente" } diff --git a/messages/fr.json b/messages/fr.json index a61bbdf..f630a0a 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -17,5 +17,9 @@ "No content to share": "Aucun contenu à partager", "Link copied to clipboard": "Lien copié dans le presse-papiers", "Test completed successfully": "Test terminé avec succès", - "Enter custom endpoint": "Entrez l'endpoint personnalisé" + "Enter custom endpoint": "Entrez l'endpoint personnalisé", + "Upload File": "Charger un fichier", + "Select Endpoint": "Sélectionner l'endpoint", + "Compare Engines": "Comparer les moteurs", + "View Source Code": "Voir le code source" } diff --git a/messages/id.json b/messages/id.json index e8ad83e..33df3ce 100644 --- a/messages/id.json +++ b/messages/id.json @@ -17,5 +17,9 @@ "No content to share": "Tidak ada konten untuk dibagikan", "Link copied to clipboard": "Tautan disalin ke clipboard", "Test completed successfully": "Uji berhasil", - "Enter custom endpoint": "Masukkan endpoint kustom" + "Enter custom endpoint": "Masukkan endpoint kustom", + "Upload File": "Unggah file", + "Select Endpoint": "Pilih endpoint", + "Compare Engines": "Bandingkan mesin", + "View Source Code": "Lihat kode sumber" } diff --git a/messages/it.json b/messages/it.json index a79b443..a2ef05d 100644 --- a/messages/it.json +++ b/messages/it.json @@ -17,5 +17,9 @@ "No content to share": "Nessun contenuto da condividere", "Link copied to clipboard": "Link copiato nel portapapele", "Test completed successfully": "Test completato con successo", - "Enter custom endpoint": "Inserisci l'endpoint personalizzato" + "Enter custom endpoint": "Inserisci l'endpoint personalizzato", + "Upload File": "Carica file", + "Select Endpoint": "Seleziona endpoint", + "Compare Engines": "Confronta motori", + "View Source Code": "Visualizza codice sorgente" } diff --git a/messages/ja.json b/messages/ja.json index 6bc102c..a8b3527 100644 --- a/messages/ja.json +++ b/messages/ja.json @@ -17,5 +17,9 @@ "No content to share": "共有する内容がありません", "Link copied to clipboard": "リンクがクリップボードにコピーされました", "Test completed successfully": "テストが完了しました", - "Enter custom endpoint": "カスタムエンドポイントを入力" + "Enter custom endpoint": "カスタムエンドポイントを入力", + "Upload File": "ファイルをアップロード", + "Select Endpoint": "エンドポイントを選択", + "Compare Engines": "エンジンを比較", + "View Source Code": "ソースコードを表示" } diff --git a/messages/ko.json b/messages/ko.json index 60b9209..d01fa04 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -17,5 +17,9 @@ "No content to share": "공유할 내용이 없습니다", "Link copied to clipboard": "링크가 클립보드에 복사되었습니다", "Test completed successfully": "테스트가 완료되었습니다", - "Enter custom endpoint": "사용자 정의 엔드포인트 입력" + "Enter custom endpoint": "사용자 정의 엔드포인트 입력", + "Upload File": "파일 업로드", + "Select Endpoint": "엔드포인트 선택", + "Compare Engines": "엔진 비교", + "View Source Code": "소스 코드 보기" } diff --git a/messages/ms.json b/messages/ms.json index 3ed261d..e095fb7 100644 --- a/messages/ms.json +++ b/messages/ms.json @@ -17,5 +17,9 @@ "No content to share": "Tidak ada konten untuk dibagikan", "Link copied to clipboard": "Tautan disalin ke clipboard", "Test completed successfully": "Uji berhasil", - "Enter custom endpoint": "Masukkan endpoint kustom" + "Enter custom endpoint": "Masukkan endpoint kustom", + "Upload File": "Unggah file", + "Select Endpoint": "Pilih endpoint", + "Compare Engines": "Bandingkan mesin", + "View Source Code": "Lihat kode sumber" } diff --git a/messages/pt.json b/messages/pt.json index 1274444..a1941bb 100644 --- a/messages/pt.json +++ b/messages/pt.json @@ -17,5 +17,9 @@ "No content to share": "Não há conteúdo para compartilhar", "Link copied to clipboard": "Link copiado para o clipboard", "Test completed successfully": "Teste concluído com sucesso", - "Enter custom endpoint": "Insira o endpoint personalizado" + "Enter custom endpoint": "Insira o endpoint personalizado", + "Upload File": "Carregar arquivo", + "Select Endpoint": "Selecionar endpoint", + "Compare Engines": "Comparar motores", + "View Source Code": "Visualizar código fonte" } diff --git a/messages/ru.json b/messages/ru.json index 045dda0..bdcaeea 100644 --- a/messages/ru.json +++ b/messages/ru.json @@ -17,5 +17,9 @@ "No content to share": "Нет содержимого для разделения", "Link copied to clipboard": "Ссылка скопирована в буфер обмена", "Test completed successfully": "Тест завершен успешно", - "Enter custom endpoint": "Введите пользовательский конечный пункт" + "Enter custom endpoint": "Введите пользовательский конечный пункт", + "Upload File": "Загрузить файл", + "Select Endpoint": "Выберите конечный пункт", + "Compare Engines": "Сравнить движки", + "View Source Code": "Просмотреть исходный код" } diff --git a/messages/tr.json b/messages/tr.json index 332daf2..b9ac5ae 100644 --- a/messages/tr.json +++ b/messages/tr.json @@ -17,5 +17,9 @@ "No content to share": "Paylaşılacak içerik yok", "Link copied to clipboard": "Link kopyalandı", "Test completed successfully": "Test başarıyla tamamlandı", - "Enter custom endpoint": "Özel endpoint girin" + "Enter custom endpoint": "Özel endpoint girin", + "Upload File": "Dosya yükle", + "Select Endpoint": "Endpoint seçin", + "Compare Engines": "Motorları karşılaştır", + "View Source Code": "Kaynak kodu görüntüle" } diff --git a/messages/vi.json b/messages/vi.json index 711280a..f757fa1 100644 --- a/messages/vi.json +++ b/messages/vi.json @@ -17,5 +17,9 @@ "No content to share": "Không có nội dung để chia sẻ", "Link copied to clipboard": "Link đã được sao chép vào clipboard", "Test completed successfully": "Test hoàn tất thành công", - "Enter custom endpoint": "Nhập điểm cuối tùy chỉnh" + "Enter custom endpoint": "Nhập điểm cuối tùy chỉnh", + "Upload File": "Tải lên tệp", + "Select Endpoint": "Chọn điểm cuối", + "Compare Engines": "So sánh máy", + "View Source Code": "Xem mã nguồn" } diff --git a/messages/zh-Hant.json b/messages/zh-Hant.json index 59098c3..eaa4c96 100644 --- a/messages/zh-Hant.json +++ b/messages/zh-Hant.json @@ -17,5 +17,9 @@ "No content to share": "沒有內容可以分享", "Link copied to clipboard": "連結已複製到剪貼簿", "Test completed successfully": "測試成功", - "Enter custom endpoint": "輸入自定義端點" + "Enter custom endpoint": "輸入自定義端點", + "Upload File": "上傳檔案", + "Select Endpoint": "選擇端點", + "Compare Engines": "比較引擎", + "View Source Code": "查看原始碼" } diff --git a/messages/zh.json b/messages/zh.json index 7afb1b5..ccf00eb 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -17,5 +17,9 @@ "No content to share": "没有内容可以分享", "Link copied to clipboard": "链接已复制到剪贴板", "Test completed successfully": "测试成功", - "Enter custom endpoint": "输入自定义端点" + "Enter custom endpoint": "输入自定义端点", + "Upload File": "上传文件", + "Select Endpoint": "选择端点", + "Compare Engines": "比较引擎", + "View Source Code": "查看源码" } diff --git a/package.json b/package.json index 193fe6b..85e1b29 100644 --- a/package.json +++ b/package.json @@ -86,15 +86,20 @@ "@codemirror/view": "^6.24.1", "@lezer/highlight": "^1.2.0", "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-tooltip": "^1.1.7", "@uiw/codemirror-theme-monokai": "^4.21.22", "@uiw/react-codemirror": "^4.21.22", "casbin": "^5.37.0", - "clsx": "^2.1.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", "codemirror": "^6.0.1", + "lucide-react": "^0.473.0", "next": "14.1.0", "react": "^18", "react-dom": "^18", - "react-hot-toast": "^2.4.1" + "react-hot-toast": "^2.4.1", + "tailwind-merge": "^2.6.0", + "tailwindcss-animate": "^1.0.7" }, "devDependencies": { "@types/jest": "^29.5.14", diff --git a/tailwind.config.ts b/tailwind.config.ts index d412ef5..d191dac 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,18 +1,64 @@ import type { Config } from 'tailwindcss'; const config: Config = { - content: ['./pages/**/*.{js,ts,jsx,tsx,mdx}', './components/**/*.{js,ts,jsx,tsx,mdx}', './app/**/*.{js,ts,jsx,tsx,mdx}'], + darkMode: ['class'], + content: ['./pages/**/*.{js,ts,jsx,tsx,mdx}', './components/**/*.{js,ts,jsx,tsx,mdx}', './app/**/*.{js,ts,jsx,tsx,mdx}'], theme: { - extend: { - backgroundImage: { - 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', - 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', - }, - colors: { - customDark: 'rgb(36, 37, 38)', - }, - }, + extend: { + backgroundImage: { + 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', + 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))' + }, + colors: { + customDark: 'rgb(36, 37, 38)', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))' + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))' + }, + primary: { + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))' + }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))' + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))' + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))' + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))' + }, + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + chart: { + '1': 'hsl(var(--chart-1))', + '2': 'hsl(var(--chart-2))', + '3': 'hsl(var(--chart-3))', + '4': 'hsl(var(--chart-4))', + '5': 'hsl(var(--chart-5))' + } + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)' + } + } }, - plugins: [], + plugins: [require("tailwindcss-animate")], }; export default config; diff --git a/yarn.lock b/yarn.lock index 5b52666..ba6dc7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -938,6 +938,11 @@ resolved "https://registry.npmmirror.com/@radix-ui/primitive/-/primitive-1.1.0.tgz#42ef83b3b56dccad5d703ae8c42919a68798bbe2" integrity sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA== +"@radix-ui/primitive@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.1.tgz#fc169732d755c7fbad33ba8d0cd7fd10c90dc8e3" + integrity sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA== + "@radix-ui/react-arrow@1.1.0": version "1.1.0" resolved "https://registry.npmmirror.com/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz#744f388182d360b86285217e43b6c63633f39e7a" @@ -945,6 +950,13 @@ dependencies: "@radix-ui/react-primitive" "2.0.0" +"@radix-ui/react-arrow@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz#2103721933a8bfc6e53bbfbdc1aaad5fc8ba0dd7" + integrity sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w== + dependencies: + "@radix-ui/react-primitive" "2.0.1" + "@radix-ui/react-collection@1.1.0": version "1.1.0" resolved "https://registry.npmmirror.com/@radix-ui/react-collection/-/react-collection-1.1.0.tgz#f18af78e46454a2360d103c2251773028b7724ed" @@ -960,11 +972,21 @@ resolved "https://registry.npmmirror.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74" integrity sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw== +"@radix-ui/react-compose-refs@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz#6f766faa975f8738269ebb8a23bad4f5a8d2faec" + integrity sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw== + "@radix-ui/react-context@1.1.0": version "1.1.0" resolved "https://registry.npmmirror.com/@radix-ui/react-context/-/react-context-1.1.0.tgz#6df8d983546cfd1999c8512f3a8ad85a6e7fcee8" integrity sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A== +"@radix-ui/react-context@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.1.tgz#82074aa83a472353bb22e86f11bcbd1c61c4c71a" + integrity sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q== + "@radix-ui/react-direction@1.1.0": version "1.1.0" resolved "https://registry.npmmirror.com/@radix-ui/react-direction/-/react-direction-1.1.0.tgz#a7d39855f4d077adc2a1922f9c353c5977a09cdc" @@ -981,6 +1003,17 @@ "@radix-ui/react-use-callback-ref" "1.1.0" "@radix-ui/react-use-escape-keydown" "1.1.0" +"@radix-ui/react-dismissable-layer@1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.4.tgz#6e31ad92e7d9e77548001fd8c04f8561300c02a9" + integrity sha512-XDUI0IVYVSwjMXxM6P4Dfti7AH+Y4oS/TB+sglZ/EXc7cqLwGAmp1NlMrcUjj7ks6R5WTZuWKv44FBbLpwU3sA== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-primitive" "2.0.1" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-escape-keydown" "1.1.0" + "@radix-ui/react-dropdown-menu@^2.1.1": version "2.1.1" resolved "https://registry.npmmirror.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.1.tgz#3dc578488688250dbbe109d9ff2ca28a9bca27ec" @@ -1055,6 +1088,22 @@ "@radix-ui/react-use-size" "1.1.0" "@radix-ui/rect" "1.1.0" +"@radix-ui/react-popper@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.1.tgz#2fc66cfc34f95f00d858924e3bee54beae2dff0a" + integrity sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw== + dependencies: + "@floating-ui/react-dom" "^2.0.0" + "@radix-ui/react-arrow" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-primitive" "2.0.1" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-use-rect" "1.1.0" + "@radix-ui/react-use-size" "1.1.0" + "@radix-ui/rect" "1.1.0" + "@radix-ui/react-portal@1.1.1": version "1.1.1" resolved "https://registry.npmmirror.com/@radix-ui/react-portal/-/react-portal-1.1.1.tgz#1957f1eb2e1aedfb4a5475bd6867d67b50b1d15f" @@ -1063,6 +1112,14 @@ "@radix-ui/react-primitive" "2.0.0" "@radix-ui/react-use-layout-effect" "1.1.0" +"@radix-ui/react-portal@1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.3.tgz#b0ea5141103a1671b715481b13440763d2ac4440" + integrity sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw== + dependencies: + "@radix-ui/react-primitive" "2.0.1" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-presence@1.1.0": version "1.1.0" resolved "https://registry.npmmirror.com/@radix-ui/react-presence/-/react-presence-1.1.0.tgz#227d84d20ca6bfe7da97104b1a8b48a833bfb478" @@ -1071,6 +1128,14 @@ "@radix-ui/react-compose-refs" "1.1.0" "@radix-ui/react-use-layout-effect" "1.1.0" +"@radix-ui/react-presence@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.2.tgz#bb764ed8a9118b7ec4512da5ece306ded8703cdc" + integrity sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg== + dependencies: + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-primitive@2.0.0": version "2.0.0" resolved "https://registry.npmmirror.com/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz#fe05715faa9203a223ccc0be15dc44b9f9822884" @@ -1078,6 +1143,13 @@ dependencies: "@radix-ui/react-slot" "1.1.0" +"@radix-ui/react-primitive@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz#6d9efc550f7520135366f333d1e820cf225fad9e" + integrity sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg== + dependencies: + "@radix-ui/react-slot" "1.1.1" + "@radix-ui/react-roving-focus@1.1.0": version "1.1.0" resolved "https://registry.npmmirror.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz#b30c59daf7e714c748805bfe11c76f96caaac35e" @@ -1100,6 +1172,31 @@ dependencies: "@radix-ui/react-compose-refs" "1.1.0" +"@radix-ui/react-slot@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.1.tgz#ab9a0ffae4027db7dc2af503c223c978706affc3" + integrity sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g== + dependencies: + "@radix-ui/react-compose-refs" "1.1.1" + +"@radix-ui/react-tooltip@^1.1.7": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.1.7.tgz#2984dc0374874029b7ea8a1987f23247b3334b2a" + integrity sha512-ss0s80BC0+g0+Zc53MvilcnTYSOi4mSuFWBPYPuTOFGjx+pUU+ZrmamMNwS56t8MTFlniA5ocjd4jYm/CdhbOg== + dependencies: + "@radix-ui/primitive" "1.1.1" + "@radix-ui/react-compose-refs" "1.1.1" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-dismissable-layer" "1.1.4" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-popper" "1.2.1" + "@radix-ui/react-portal" "1.1.3" + "@radix-ui/react-presence" "1.1.2" + "@radix-ui/react-primitive" "2.0.1" + "@radix-ui/react-slot" "1.1.1" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-visually-hidden" "1.1.1" + "@radix-ui/react-use-callback-ref@1.1.0": version "1.1.0" resolved "https://registry.npmmirror.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1" @@ -1138,6 +1235,13 @@ dependencies: "@radix-ui/react-use-layout-effect" "1.1.0" +"@radix-ui/react-visually-hidden@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.1.tgz#f7b48c1af50dfdc366e92726aee6d591996c5752" + integrity sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg== + dependencies: + "@radix-ui/react-primitive" "2.0.1" + "@radix-ui/rect@1.1.0": version "1.1.0" resolved "https://registry.npmmirror.com/@radix-ui/rect/-/rect-1.1.0.tgz#f817d1d3265ac5415dadc67edab30ae196696438" @@ -2132,6 +2236,13 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170" integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA== +class-variance-authority@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz#4008a798a0e4553a781a57ac5177c9fb5d043787" + integrity sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg== + dependencies: + clsx "^2.1.1" + cli-truncate@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" @@ -2161,7 +2272,7 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" -clsx@^2.1.0: +clsx@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== @@ -4443,6 +4554,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lucide-react@^0.473.0: + version "0.473.0" + resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.473.0.tgz#f01dcde458e55bce766a282f4f5894970a9f24ac" + integrity sha512-KW6u5AKeIjkvrxXZ6WuCu9zHE/gEYSXCay+Gre2ZoInD0Je/e3RBtP4OHpJVJ40nDklSvjVKjgH7VU8/e2dzRw== + make-dir@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" @@ -5665,6 +5781,16 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +tailwind-merge@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-2.6.0.tgz#ac5fb7e227910c038d458f396b7400d93a3142d5" + integrity sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA== + +tailwindcss-animate@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz#318b692c4c42676cc9e67b19b78775742388bef4" + integrity sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA== + tailwindcss@^3.4.1: version "3.4.3" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.3.tgz#be48f5283df77dfced705451319a5dffb8621519"