feat(readwise): allow overriding the Readwise sync base URL (#4196)
* feat(readwise): allow overriding the Readwise sync base URL Add an advanced option to point Readwise sync/export at a custom, Readwise-compatible endpoint instead of the hardcoded official API. When the override is unset or blank, behavior is unchanged. - ReadwiseClient resolves a custom `baseUrl` over `READWISE_API_BASE_URL`, trimming whitespace and trailing slashes. - ReadwiseSettings gains an optional `baseUrl` field; it syncs as plaintext via the settings sync whitelist. - ReadwiseForm exposes the URL under a collapsed "Advanced" disclosure on the connect screen, and surfaces a custom URL read-only once connected. Disconnect preserves the custom URL for easy reconnect. Closes #4114 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * i18n(readwise): rename "Sync Base URL" label to "Custom URL" Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1546,5 +1546,7 @@
|
||||
"Are you sure to clear all {{count}} highlights and notes?_few": "هل أنت متأكد من مسح التمييزات والملاحظات الـ{{count}}؟",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_many": "هل أنت متأكد من مسح التمييزات والملاحظات الـ{{count}}؟",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "هل أنت متأكد من مسح التمييزات والملاحظات الـ{{count}}؟",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "اتركه فارغًا لاستخدام واجهة Readwise الرسمية. عيّن عنوان URL مخصصًا فقط للاتصال بخدمة مستضافة ذاتيًا ومتوافقة مع Readwise.",
|
||||
"Custom URL": "عنوان URL مخصص"
|
||||
}
|
||||
|
||||
@@ -1438,5 +1438,7 @@
|
||||
"Clear Annotations": "টীকা মুছুন",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "আপনি কি নিশ্চিত যে {{count}}টি হাইলাইট ও নোট মুছে ফেলতে চান?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "আপনি কি নিশ্চিত যে সব {{count}}টি হাইলাইট ও নোট মুছে ফেলতে চান?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "অফিসিয়াল Readwise API ব্যবহার করতে ফাঁকা রাখুন। শুধুমাত্র একটি স্ব-হোস্ট করা, Readwise-সামঞ্জস্যপূর্ণ পরিষেবার জন্য একটি কাস্টম URL সেট করুন।",
|
||||
"Custom URL": "কাস্টম URL"
|
||||
}
|
||||
|
||||
@@ -1411,5 +1411,7 @@
|
||||
"Cleared {{count}} highlights and notes._other": "གཙོ་གནད་དང་ཟིན་བྲིས་ {{count}} སུབ་ཟིན།",
|
||||
"Clear Annotations": "ཟིན་བྲིས་སུབ་པ།",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "གཙོ་གནད་དང་ཟིན་བྲིས་ {{count}} ཚང་མ་སུབ་པར་གཏན་འཁེལ་ལམ།",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Readwise ཡི་ངོ་མའི་API སྤྱོད་ན་སྟོང་པར་བཞག Readwise དང་མཐུན་པའི་རང་འཇོག་ཞབས་ཞུའི་ཆེད་ཁོ་ནར་སྒེར་གྱི་URL འགོད་དགོས།",
|
||||
"Custom URL": "སྒེར་གྱི་URL"
|
||||
}
|
||||
|
||||
@@ -1438,5 +1438,7 @@
|
||||
"Clear Annotations": "Anmerkungen löschen",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "Möchten Sie wirklich {{count}} Markierung und Notiz löschen?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Möchten Sie wirklich alle {{count}} Markierungen und Notizen löschen?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Leer lassen, um die offizielle Readwise-API zu verwenden. Eine benutzerdefinierte URL nur festlegen, um einen selbst gehosteten, Readwise-kompatiblen Dienst anzusprechen.",
|
||||
"Custom URL": "Benutzerdefinierte URL"
|
||||
}
|
||||
|
||||
@@ -1438,5 +1438,7 @@
|
||||
"Clear Annotations": "Εκκαθάριση σχολιασμών",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "Είστε σίγουροι ότι θέλετε να διαγράψετε {{count}} επισήμανση και σημείωση;",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Είστε σίγουροι ότι θέλετε να διαγράψετε και τις {{count}} επισημάνσεις και σημειώσεις;",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Αφήστε το κενό για χρήση του επίσημου Readwise API. Ορίστε προσαρμοσμένο URL μόνο για να στοχεύσετε μια αυτο-φιλοξενούμενη υπηρεσία συμβατή με το Readwise.",
|
||||
"Custom URL": "Προσαρμοσμένο URL"
|
||||
}
|
||||
|
||||
@@ -1465,5 +1465,7 @@
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "¿Seguro que quieres borrar {{count}} resaltado y nota?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_many": "¿Seguro que quieres borrar los {{count}} resaltados y notas?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "¿Seguro que quieres borrar los {{count}} resaltados y notas?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Déjalo en blanco para usar la API oficial de Readwise. Establece una URL personalizada solo para apuntar a un servicio autoalojado compatible con Readwise.",
|
||||
"Custom URL": "URL personalizada"
|
||||
}
|
||||
|
||||
@@ -1438,5 +1438,7 @@
|
||||
"Clear Annotations": "پاک کردن حاشیهنویسیها",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "آیا مطمئنید که میخواهید {{count}} برجسته و یادداشت را پاک کنید؟",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "آیا مطمئنید که میخواهید همهٔ {{count}} برجسته و یادداشت را پاک کنید؟",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "برای استفاده از API رسمی Readwise خالی بگذارید. تنها برای اتصال به یک سرویس خودمیزبان سازگار با Readwise یک نشانی سفارشی تنظیم کنید.",
|
||||
"Custom URL": "نشانی سفارشی"
|
||||
}
|
||||
|
||||
@@ -1465,5 +1465,7 @@
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "Voulez-vous vraiment effacer {{count}} surlignage et note ?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_many": "Voulez-vous vraiment effacer les {{count}} surlignages et notes ?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Voulez-vous vraiment effacer les {{count}} surlignages et notes ?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Laissez vide pour utiliser l'API officielle de Readwise. Définissez une URL personnalisée uniquement pour cibler un service auto-hébergé compatible avec Readwise.",
|
||||
"Custom URL": "URL personnalisée"
|
||||
}
|
||||
|
||||
@@ -1465,5 +1465,7 @@
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "האם לנקות {{count}} הדגשה והערה?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_two": "האם לנקות את כל {{count}} ההדגשות וההערות?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "האם לנקות את כל {{count}} ההדגשות וההערות?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "השאירו ריק כדי להשתמש ב-API הרשמי של Readwise. הגדירו כתובת URL מותאמת אישית רק כדי להפנות לשירות בעל אירוח עצמי התואם ל-Readwise.",
|
||||
"Custom URL": "כתובת URL מותאמת אישית"
|
||||
}
|
||||
|
||||
@@ -1438,5 +1438,7 @@
|
||||
"Clear Annotations": "टिप्पणियाँ साफ़ करें",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "क्या आप वाकई {{count}} हाइलाइट और नोट साफ़ करना चाहते हैं?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "क्या आप वाकई सभी {{count}} हाइलाइट और नोट साफ़ करना चाहते हैं?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "आधिकारिक Readwise API का उपयोग करने के लिए खाली छोड़ें। कस्टम URL केवल तभी सेट करें जब किसी स्वयं-होस्ट किए गए, Readwise-संगत सेवा को लक्षित करना हो।",
|
||||
"Custom URL": "कस्टम URL"
|
||||
}
|
||||
|
||||
@@ -1438,5 +1438,7 @@
|
||||
"Clear Annotations": "Jegyzetek törlése",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "Biztosan törli ezt a {{count}} kiemelést és jegyzetet?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Biztosan törli mind a(z) {{count}} kiemelést és jegyzetet?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Hagyja üresen a hivatalos Readwise API használatához. Egyéni URL-t csak akkor adjon meg, ha saját üzemeltetésű, Readwise-kompatibilis szolgáltatást szeretne megcélozni.",
|
||||
"Custom URL": "Egyéni URL"
|
||||
}
|
||||
|
||||
@@ -1411,5 +1411,7 @@
|
||||
"Cleared {{count}} highlights and notes._other": "{{count}} sorotan dan catatan dihapus.",
|
||||
"Clear Annotations": "Hapus Anotasi",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Yakin ingin menghapus semua {{count}} sorotan dan catatan?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Kosongkan untuk menggunakan API Readwise resmi. Setel URL khusus hanya untuk menargetkan layanan yang dihosting sendiri dan kompatibel dengan Readwise.",
|
||||
"Custom URL": "URL Khusus"
|
||||
}
|
||||
|
||||
@@ -1465,5 +1465,7 @@
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "Vuoi davvero cancellare {{count}} evidenziazione e nota?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_many": "Vuoi davvero cancellare tutte le {{count}} evidenziazioni e note?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Vuoi davvero cancellare tutte le {{count}} evidenziazioni e note?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Lascia vuoto per usare l'API ufficiale di Readwise. Imposta un URL personalizzato solo per puntare a un servizio self-hosted compatibile con Readwise.",
|
||||
"Custom URL": "URL personalizzato"
|
||||
}
|
||||
|
||||
@@ -1411,5 +1411,7 @@
|
||||
"Cleared {{count}} highlights and notes._other": "{{count}} 件のハイライトとメモをクリアしました。",
|
||||
"Clear Annotations": "注釈をクリア",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "{{count}} 件のハイライトとメモをすべてクリアしてもよろしいですか?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "公式のReadwise APIを使用する場合は空白のままにします。カスタムURLは、セルフホストのReadwise互換サービスに接続する場合のみ設定してください。",
|
||||
"Custom URL": "カスタムURL"
|
||||
}
|
||||
|
||||
@@ -1411,5 +1411,7 @@
|
||||
"Cleared {{count}} highlights and notes._other": "하이라이트 및 메모 {{count}}개를 지웠습니다.",
|
||||
"Clear Annotations": "주석 지우기",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "하이라이트 및 메모 {{count}}개를 모두 지우시겠습니까?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "공식 Readwise API를 사용하려면 비워 두세요. 사용자 지정 URL은 자체 호스팅된 Readwise 호환 서비스를 대상으로 할 때만 설정하세요.",
|
||||
"Custom URL": "사용자 지정 URL"
|
||||
}
|
||||
|
||||
@@ -1411,5 +1411,7 @@
|
||||
"Cleared {{count}} highlights and notes._other": "{{count}} serlahan dan nota telah dikosongkan.",
|
||||
"Clear Annotations": "Kosongkan Anotasi",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Adakah anda pasti mahu mengosongkan semua {{count}} serlahan dan nota?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Biarkan kosong untuk menggunakan API Readwise rasmi. Tetapkan URL tersuai hanya untuk menyasarkan perkhidmatan layan-diri yang serasi dengan Readwise.",
|
||||
"Custom URL": "URL Tersuai"
|
||||
}
|
||||
|
||||
@@ -1438,5 +1438,7 @@
|
||||
"Clear Annotations": "Annotaties wissen",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "Weet u zeker dat u {{count}} markering en notitie wilt wissen?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Weet u zeker dat u alle {{count}} markeringen en notities wilt wissen?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Laat leeg om de officiële Readwise-API te gebruiken. Stel alleen een aangepaste URL in om een zelf-gehoste, Readwise-compatibele service te benaderen.",
|
||||
"Custom URL": "Aangepaste URL"
|
||||
}
|
||||
|
||||
@@ -1492,5 +1492,7 @@
|
||||
"Are you sure to clear all {{count}} highlights and notes?_few": "Czy na pewno chcesz wyczyścić wszystkie {{count}} zaznaczenia i notatki?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_many": "Czy na pewno chcesz wyczyścić wszystkie {{count}} zaznaczeń i notatek?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Czy na pewno chcesz wyczyścić wszystkie {{count}} zaznaczeń i notatek?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Pozostaw puste, aby używać oficjalnego API Readwise. Ustaw niestandardowy adres URL tylko po to, aby kierować ruch do samodzielnie hostowanej usługi zgodnej z Readwise.",
|
||||
"Custom URL": "Niestandardowy adres URL"
|
||||
}
|
||||
|
||||
@@ -1465,5 +1465,7 @@
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "Tem certeza de que deseja limpar {{count}} destaque e nota?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_many": "Tem certeza de que deseja limpar todos os {{count}} destaques e notas?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Tem certeza de que deseja limpar todos os {{count}} destaques e notas?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Deixe em branco para usar a API oficial do Readwise. Defina um URL personalizado apenas para direcionar a um serviço auto-hospedado compatível com o Readwise.",
|
||||
"Custom URL": "URL personalizado"
|
||||
}
|
||||
|
||||
@@ -1465,5 +1465,7 @@
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "Tem certeza de que deseja limpar {{count}} destaque e nota?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_many": "Tem certeza de que deseja limpar todos os {{count}} destaques e notas?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Tem certeza de que deseja limpar todos os {{count}} destaques e notas?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Deixe em branco para utilizar a API oficial do Readwise. Defina um URL personalizado apenas para direcionar para um serviço auto-alojado compatível com o Readwise.",
|
||||
"Custom URL": "URL personalizado"
|
||||
}
|
||||
|
||||
@@ -1465,5 +1465,7 @@
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "Sigur ștergeți {{count}} evidențiere și notă?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_few": "Sigur ștergeți toate cele {{count}} evidențieri și note?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Sigur ștergeți toate cele {{count}} de evidențieri și note?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Lăsați necompletat pentru a folosi API-ul oficial Readwise. Setați un URL personalizat doar pentru a viza un serviciu găzduit local, compatibil cu Readwise.",
|
||||
"Custom URL": "URL personalizat"
|
||||
}
|
||||
|
||||
@@ -1492,5 +1492,7 @@
|
||||
"Are you sure to clear all {{count}} highlights and notes?_few": "Очистить все {{count}} выделения и заметки?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_many": "Очистить все {{count}} выделений и заметок?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Очистить все {{count}} выделений и заметок?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Оставьте пустым, чтобы использовать официальный API Readwise. Указывайте собственный URL только для подключения к самостоятельно размещённому сервису, совместимому с Readwise.",
|
||||
"Custom URL": "Пользовательский URL"
|
||||
}
|
||||
|
||||
@@ -1438,5 +1438,7 @@
|
||||
"Clear Annotations": "විවරණ හිස් කරන්න",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "ඉස්මතු කිරීම් සහ සටහන් {{count}}ක් හිස් කිරීමට අවශ්ය බව විශ්වාසද?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "ඉස්මතු කිරීම් සහ සටහන් {{count}}ම හිස් කිරීමට අවශ්ය බව විශ්වාසද?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "නිල Readwise API භාවිතා කිරීමට හිස්ව තබන්න. ස්වයං-සත්කාරක, Readwise-අනුකූල සේවාවක් ඉලක්ක කිරීමට පමණක් අභිරුචි URL එකක් සකසන්න.",
|
||||
"Custom URL": "අභිරුචි URL"
|
||||
}
|
||||
|
||||
@@ -1492,5 +1492,7 @@
|
||||
"Are you sure to clear all {{count}} highlights and notes?_two": "Ali res želite počistiti {{count}} označbi in opombi?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_few": "Ali res želite počistiti vse {{count}} označbe in opombe?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Ali res želite počistiti vseh {{count}} označb in opomb?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Pustite prazno za uporabo uradnega API-ja Readwise. URL po meri nastavite samo za ciljanje na samostojno gostovano storitev, združljivo z Readwise.",
|
||||
"Custom URL": "URL po meri"
|
||||
}
|
||||
|
||||
@@ -1438,5 +1438,7 @@
|
||||
"Clear Annotations": "Rensa anteckningar",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "Är du säker på att du vill rensa {{count}} markering och anteckning?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Är du säker på att du vill rensa alla {{count}} markeringar och anteckningar?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Lämna tomt för att använda det officiella Readwise-API:et. Ange en anpassad URL endast för att rikta mot en självhostad, Readwise-kompatibel tjänst.",
|
||||
"Custom URL": "Anpassad URL"
|
||||
}
|
||||
|
||||
@@ -1438,5 +1438,7 @@
|
||||
"Clear Annotations": "குறிப்புகளை அழி",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "{{count}} சிறப்பம்சம் மற்றும் குறிப்பை அழிக்க வேண்டும் என்பது உறுதியா?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "அனைத்து {{count}} சிறப்பம்சங்கள் மற்றும் குறிப்புகளையும் அழிக்க வேண்டும் என்பது உறுதியா?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "அதிகாரப்பூர்வ Readwise API ஐப் பயன்படுத்த காலியாக விடவும். சுய-ஹோஸ்ட் செய்யப்பட்ட, Readwise-இணக்கமான சேவையை இலக்காகக் கொள்ள மட்டுமே தனிப்பயன் URL ஐ அமைக்கவும்.",
|
||||
"Custom URL": "தனிப்பயன் URL"
|
||||
}
|
||||
|
||||
@@ -1411,5 +1411,7 @@
|
||||
"Cleared {{count}} highlights and notes._other": "ล้างไฮไลต์และบันทึก {{count}} รายการแล้ว",
|
||||
"Clear Annotations": "ล้างบันทึก",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "แน่ใจหรือไม่ว่าต้องการล้างไฮไลต์และบันทึกทั้งหมด {{count}} รายการ?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "เว้นว่างไว้เพื่อใช้ API อย่างเป็นทางการของ Readwise ตั้งค่า URL ที่กำหนดเองเฉพาะเมื่อต้องการเชื่อมต่อกับบริการที่โฮสต์เองซึ่งเข้ากันได้กับ Readwise",
|
||||
"Custom URL": "URL ที่กำหนดเอง"
|
||||
}
|
||||
|
||||
@@ -1438,5 +1438,7 @@
|
||||
"Clear Annotations": "Notları Temizle",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "{{count}} vurgu ve notu temizlemek istediğinizden emin misiniz?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Tüm {{count}} vurgu ve notu temizlemek istediğinizden emin misiniz?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Resmi Readwise API'sini kullanmak için boş bırakın. Özel bir URL'yi yalnızca kendi barındırdığınız, Readwise uyumlu bir hizmeti hedeflemek için ayarlayın.",
|
||||
"Custom URL": "Özel URL"
|
||||
}
|
||||
|
||||
@@ -1492,5 +1492,7 @@
|
||||
"Are you sure to clear all {{count}} highlights and notes?_few": "Очистити всі {{count}} виділення та нотатки?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_many": "Очистити всі {{count}} виділень та нотаток?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Очистити всі {{count}} виділень та нотаток?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Залиште порожнім, щоб використовувати офіційний API Readwise. Указуйте власну URL-адресу лише для підключення до самостійно розміщеної служби, сумісної з Readwise.",
|
||||
"Custom URL": "Власний URL"
|
||||
}
|
||||
|
||||
@@ -1438,5 +1438,7 @@
|
||||
"Clear Annotations": "Izohlarni tozalash",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_one": "{{count}} ta belgilash va eslatmani tozalamoqchimisiz?",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Barcha {{count}} ta belgilash va eslatmani tozalamoqchimisiz?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Rasmiy Readwise API'sidan foydalanish uchun bo'sh qoldiring. Maxsus URL manzilini faqat o'zingiz joylashtirgan, Readwise bilan mos xizmatga ulanish uchun belgilang.",
|
||||
"Custom URL": "Maxsus URL"
|
||||
}
|
||||
|
||||
@@ -1411,5 +1411,7 @@
|
||||
"Cleared {{count}} highlights and notes._other": "Đã xóa {{count}} đánh dấu và ghi chú.",
|
||||
"Clear Annotations": "Xóa chú thích",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "Bạn có chắc muốn xóa tất cả {{count}} đánh dấu và ghi chú không?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "Để trống để dùng API Readwise chính thức. Chỉ đặt URL tùy chỉnh để trỏ đến dịch vụ tự lưu trữ tương thích với Readwise.",
|
||||
"Custom URL": "URL tùy chỉnh"
|
||||
}
|
||||
|
||||
@@ -1411,5 +1411,7 @@
|
||||
"Cleared {{count}} highlights and notes._other": "已清除 {{count}} 个高亮和笔记。",
|
||||
"Clear Annotations": "清除笔记",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "确定要清除全部 {{count}} 个高亮和笔记吗?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "留空以使用官方 Readwise API。仅在需要连接自托管的 Readwise 兼容服务时才设置自定义 URL。",
|
||||
"Custom URL": "自定义 URL"
|
||||
}
|
||||
|
||||
@@ -1411,5 +1411,7 @@
|
||||
"Cleared {{count}} highlights and notes._other": "已清除 {{count}} 個標記和筆記。",
|
||||
"Clear Annotations": "清除筆記",
|
||||
"Are you sure to clear all {{count}} highlights and notes?_other": "確定要清除全部 {{count}} 個標記和筆記嗎?",
|
||||
"Discord": "Discord"
|
||||
"Discord": "Discord",
|
||||
"Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.": "留空以使用官方 Readwise API。僅在需要連接自架的 Readwise 相容服務時才設定自訂 URL。",
|
||||
"Custom URL": "自訂 URL"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { ReadwiseClient } from '@/services/readwise/ReadwiseClient';
|
||||
import { READWISE_API_BASE_URL } from '@/services/constants';
|
||||
import type { ReadwiseSettings } from '@/types/settings';
|
||||
import type { Book, BookNote } from '@/types/book';
|
||||
|
||||
const makeSettings = (overrides: Partial<ReadwiseSettings> = {}): ReadwiseSettings => ({
|
||||
enabled: true,
|
||||
accessToken: 'test-token',
|
||||
lastSyncedAt: 0,
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const makeBook = (): Book =>
|
||||
({
|
||||
hash: 'bookhash',
|
||||
title: 'Test Book',
|
||||
author: 'Test Author',
|
||||
}) as Book;
|
||||
|
||||
const makeNote = (): BookNote =>
|
||||
({
|
||||
id: 'note-1',
|
||||
type: 'annotation',
|
||||
cfi: 'epubcfi(/6/2!/4)',
|
||||
text: 'highlighted text',
|
||||
note: '',
|
||||
color: 'yellow',
|
||||
page: 1,
|
||||
createdAt: 1700000000000,
|
||||
updatedAt: 1700000000000,
|
||||
}) as BookNote;
|
||||
|
||||
describe('ReadwiseClient base URL', () => {
|
||||
let fetchMock: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(() => {
|
||||
fetchMock = vi.fn().mockResolvedValue(new Response(null, { status: 204 }));
|
||||
vi.stubGlobal('fetch', fetchMock);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
const requestedUrl = () => String(fetchMock.mock.calls[0]![0]);
|
||||
|
||||
test('validateToken uses the official base URL when no custom URL is set', async () => {
|
||||
const client = new ReadwiseClient(makeSettings());
|
||||
await client.validateToken();
|
||||
expect(requestedUrl()).toBe(`${READWISE_API_BASE_URL}/auth/`);
|
||||
});
|
||||
|
||||
test('validateToken uses a custom base URL when configured', async () => {
|
||||
const client = new ReadwiseClient(makeSettings({ baseUrl: 'https://example.com/api/v2' }));
|
||||
await client.validateToken();
|
||||
expect(requestedUrl()).toBe('https://example.com/api/v2/auth/');
|
||||
});
|
||||
|
||||
test('a trailing slash on the custom base URL is normalized away', async () => {
|
||||
const client = new ReadwiseClient(makeSettings({ baseUrl: 'https://example.com/api/v2/' }));
|
||||
await client.validateToken();
|
||||
expect(requestedUrl()).toBe('https://example.com/api/v2/auth/');
|
||||
});
|
||||
|
||||
test('a blank custom base URL falls back to the official base URL', async () => {
|
||||
const client = new ReadwiseClient(makeSettings({ baseUrl: ' ' }));
|
||||
await client.validateToken();
|
||||
expect(requestedUrl()).toBe(`${READWISE_API_BASE_URL}/auth/`);
|
||||
});
|
||||
|
||||
test('pushHighlights posts to the custom base URL with the auth header', async () => {
|
||||
fetchMock.mockResolvedValue(new Response(null, { status: 200 }));
|
||||
const client = new ReadwiseClient(makeSettings({ baseUrl: 'https://example.com/api/v2' }));
|
||||
const result = await client.pushHighlights([makeNote()], makeBook());
|
||||
expect(result.success).toBe(true);
|
||||
expect(requestedUrl()).toBe('https://example.com/api/v2/highlights/');
|
||||
const init = fetchMock.mock.calls[0]![1] as RequestInit;
|
||||
expect(init.method).toBe('POST');
|
||||
expect((init.headers as Record<string, string>)['Authorization']).toBe('Token test-token');
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,14 @@
|
||||
import clsx from 'clsx';
|
||||
import React, { useState } from 'react';
|
||||
import { MdExpandMore } from 'react-icons/md';
|
||||
import { useEnv } from '@/context/EnvContext';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
import { useSettingsStore } from '@/store/settingsStore';
|
||||
import { eventDispatcher } from '@/utils/event';
|
||||
import { ReadwiseClient } from '@/services/readwise';
|
||||
import { READWISE_API_BASE_URL } from '@/services/constants';
|
||||
import SubPageHeader from '../SubPageHeader';
|
||||
import { SectionTitle, SettingLabel } from '../primitives';
|
||||
import { SectionTitle, SettingLabel, Tips } from '../primitives';
|
||||
|
||||
interface ReadwiseFormProps {
|
||||
onBack: () => void;
|
||||
@@ -18,14 +20,23 @@ const ReadwiseForm: React.FC<ReadwiseFormProps> = ({ onBack }) => {
|
||||
const { settings, setSettings, saveSettings } = useSettingsStore();
|
||||
|
||||
const [accessToken, setAccessToken] = useState('');
|
||||
const [baseUrl, setBaseUrl] = useState(settings.readwise?.baseUrl ?? '');
|
||||
const [isConnecting, setIsConnecting] = useState(false);
|
||||
|
||||
const isConfigured = !!settings.readwise?.accessToken;
|
||||
// Persisted custom URL; empty means the official Readwise endpoint is used.
|
||||
const configuredBaseUrl = settings.readwise?.baseUrl?.trim() ?? '';
|
||||
|
||||
const handleConnect = async () => {
|
||||
setIsConnecting(true);
|
||||
try {
|
||||
const client = new ReadwiseClient({ enabled: true, accessToken, lastSyncedAt: 0 });
|
||||
const trimmedBaseUrl = baseUrl.trim();
|
||||
const client = new ReadwiseClient({
|
||||
enabled: true,
|
||||
accessToken,
|
||||
lastSyncedAt: 0,
|
||||
baseUrl: trimmedBaseUrl || undefined,
|
||||
});
|
||||
const { valid, isNetworkError } = await client.validateToken();
|
||||
if (valid) {
|
||||
const newSettings = {
|
||||
@@ -34,6 +45,7 @@ const ReadwiseForm: React.FC<ReadwiseFormProps> = ({ onBack }) => {
|
||||
enabled: true,
|
||||
accessToken,
|
||||
lastSyncedAt: settings.readwise?.lastSyncedAt ?? 0,
|
||||
baseUrl: trimmedBaseUrl || undefined,
|
||||
},
|
||||
};
|
||||
setSettings(newSettings);
|
||||
@@ -56,9 +68,16 @@ const ReadwiseForm: React.FC<ReadwiseFormProps> = ({ onBack }) => {
|
||||
};
|
||||
|
||||
const handleDisconnect = async () => {
|
||||
// Keep the custom base URL so an advanced user can reconnect without
|
||||
// re-entering it; only the credential and sync state are cleared.
|
||||
const newSettings = {
|
||||
...settings,
|
||||
readwise: { enabled: false, accessToken: '', lastSyncedAt: 0 },
|
||||
readwise: {
|
||||
enabled: false,
|
||||
accessToken: '',
|
||||
lastSyncedAt: 0,
|
||||
baseUrl: configuredBaseUrl || undefined,
|
||||
},
|
||||
};
|
||||
setSettings(newSettings);
|
||||
await saveSettings(envConfig, newSettings);
|
||||
@@ -116,6 +135,17 @@ const ReadwiseForm: React.FC<ReadwiseFormProps> = ({ onBack }) => {
|
||||
onChange={handleToggleEnabled}
|
||||
/>
|
||||
</label>
|
||||
{configuredBaseUrl && (
|
||||
<div className='flex min-h-14 items-center justify-between gap-3 px-4'>
|
||||
<SettingLabel>{_('Custom URL')}</SettingLabel>
|
||||
<span
|
||||
className='text-base-content/60 min-w-0 truncate text-end text-sm'
|
||||
title={configuredBaseUrl}
|
||||
>
|
||||
{configuredBaseUrl}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -152,6 +182,46 @@ const ReadwiseForm: React.FC<ReadwiseFormProps> = ({ onBack }) => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Advanced: a self-hosted, Readwise-compatible receiver. Hidden in
|
||||
a disclosure so it doesn't confuse users on the default flow. */}
|
||||
<details className='group'>
|
||||
<summary
|
||||
className={clsx(
|
||||
'flex cursor-pointer list-none items-center gap-1',
|
||||
'text-base-content/70 hover:text-base-content text-sm font-medium',
|
||||
'transition-colors duration-150',
|
||||
)}
|
||||
>
|
||||
<MdExpandMore className='h-4 w-4 transition-transform duration-150 group-open:rotate-180' />
|
||||
{_('Advanced')}
|
||||
</summary>
|
||||
<div className='space-y-3 pt-3'>
|
||||
<div className='space-y-1.5'>
|
||||
<SectionTitle as='label' htmlFor='readwise-base-url' className='block'>
|
||||
{_('Custom URL')}
|
||||
</SectionTitle>
|
||||
<input
|
||||
id='readwise-base-url'
|
||||
type='url'
|
||||
inputMode='url'
|
||||
placeholder={READWISE_API_BASE_URL}
|
||||
className='input input-bordered eink-bordered h-11 w-full text-sm focus:outline-none'
|
||||
spellCheck='false'
|
||||
autoCapitalize='off'
|
||||
value={baseUrl}
|
||||
onChange={(e) => setBaseUrl(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Tips>
|
||||
<li>
|
||||
{_(
|
||||
'Leave blank to use the official Readwise API. Set a custom URL only to target a self-hosted, Readwise-compatible service.',
|
||||
)}
|
||||
</li>
|
||||
</Tips>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div className='flex justify-end'>
|
||||
<button
|
||||
type='button'
|
||||
|
||||
@@ -18,12 +18,23 @@ export class ReadwiseClient {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the API base URL. An advanced custom override (self-hosted,
|
||||
* Readwise-compatible receiver) wins when set; trailing slashes are
|
||||
* trimmed so `${baseUrl}${endpoint}` joins cleanly. Falls back to the
|
||||
* official endpoint when unset or blank.
|
||||
*/
|
||||
private get baseUrl(): string {
|
||||
const custom = this.config.baseUrl?.trim();
|
||||
return (custom || READWISE_API_BASE_URL).replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
private async request(
|
||||
endpoint: string,
|
||||
options: { method?: 'GET' | 'POST'; body?: string } = {},
|
||||
): Promise<Response> {
|
||||
const { method = 'GET', body } = options;
|
||||
return fetch(`${READWISE_API_BASE_URL}${endpoint}`, {
|
||||
return fetch(`${this.baseUrl}${endpoint}`, {
|
||||
method,
|
||||
headers: {
|
||||
Authorization: `Token ${this.config.accessToken}`,
|
||||
|
||||
@@ -52,6 +52,7 @@ export const SETTINGS_WHITELIST = [
|
||||
'kosync.username',
|
||||
'kosync.userkey',
|
||||
'kosync.password',
|
||||
'readwise.baseUrl',
|
||||
'readwise.accessToken',
|
||||
'hardcover.accessToken',
|
||||
] as const;
|
||||
|
||||
@@ -71,6 +71,12 @@ export interface ReadwiseSettings {
|
||||
enabled: boolean;
|
||||
accessToken: string;
|
||||
lastSyncedAt: number;
|
||||
/**
|
||||
* Advanced: override the Readwise API base URL (e.g. for a self-hosted,
|
||||
* Readwise-compatible receiver). When unset or blank, the official
|
||||
* `READWISE_API_BASE_URL` is used.
|
||||
*/
|
||||
baseUrl?: string;
|
||||
}
|
||||
|
||||
export interface HardcoverSettings {
|
||||
|
||||
+1
-1
Submodule packages/foliate-js updated: 3c597a6dc5...4361f29b5a
Reference in New Issue
Block a user