diff --git a/electron/main.cjs b/electron/main.cjs index b2060edb..b80febdf 100644 --- a/electron/main.cjs +++ b/electron/main.cjs @@ -190,9 +190,19 @@ function loadRenderer(win) { /** * Packaged (and dev) renderer runs on http://127.0.0.1; hls.js and other fetches hit third-party - * streams without CORS. Chromium still enforces CORS, so inject a permissive ACAO on subresources only. + * streams without CORS. Chromium still enforces CORS, so inject permissive CORS on subresources only. + * LanguageTool / LibreTranslate use POST + non-simple Content-Type → preflight must see Allow-Methods / + * Allow-Headers, not only ACAO. */ function relaxCorsForRendererSubresources() { + const stripCors = new Set([ + 'access-control-allow-origin', + 'access-control-allow-credentials', + 'access-control-allow-methods', + 'access-control-allow-headers', + 'access-control-expose-headers', + 'access-control-max-age' + ]) session.defaultSession.webRequest.onHeadersReceived((details, callback) => { if (details.resourceType === 'mainFrame' || details.resourceType === 'subFrame') { callback({ cancel: false, responseHeaders: details.responseHeaders }) @@ -205,15 +215,15 @@ function relaxCorsForRendererSubresources() { } const responseHeaders = { ...raw } for (const key of Object.keys(responseHeaders)) { - const lower = key.toLowerCase() - if ( - lower === 'access-control-allow-origin' || - lower === 'access-control-allow-credentials' - ) { + if (stripCors.has(key.toLowerCase())) { delete responseHeaders[key] } } responseHeaders['Access-Control-Allow-Origin'] = ['*'] + responseHeaders['Access-Control-Allow-Methods'] = ['GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS'] + responseHeaders['Access-Control-Allow-Headers'] = [ + 'Authorization,Content-Type,Accept,Accept-Language,Origin,X-Requested-With' + ] callback({ cancel: false, responseHeaders }) }) } diff --git a/package-lock.json b/package-lock.json index fed44b3c..860efc8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "imwald", - "version": "23.0.5", + "version": "23.0.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "imwald", - "version": "23.0.5", + "version": "23.0.8", "license": "MIT", "dependencies": { "@asciidoctor/core": "^3.0.4", diff --git a/package.json b/package.json index 5a5e8c5a..a50892e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "imwald", - "version": "23.0.7", + "version": "23.0.8", "description": "Imwald — a user-friendly Nostr client focused on relay feed browsing, publications, and relay discovery", "private": true, "type": "module", diff --git a/src/components/AdvancedEventLab/AdvancedEventLabDialog.tsx b/src/components/AdvancedEventLab/AdvancedEventLabDialog.tsx index 9453cee6..0a971d3a 100644 --- a/src/components/AdvancedEventLab/AdvancedEventLabDialog.tsx +++ b/src/components/AdvancedEventLab/AdvancedEventLabDialog.tsx @@ -21,6 +21,7 @@ import { languageToolLintExtension, requestAdvancedLabGrammarLint } from '@/lib/ import { pickLanguageToolCodeForTranslateTarget } from '@/lib/languagetool-language-order' import { filterTranslateLanguagesWithGrammarCatalog, + TRANSLATE_GRAMMAR_LANGUAGE_OPTIONS, translateLanguageOptionMatchesQuery } from '@/lib/language-display-meta' import { LanguageSelectOptionLines } from '@/lib/language-select-option-lines' @@ -536,21 +537,29 @@ export default function AdvancedEventLabDialog({ void fetchTranslateLanguages() .then((list) => { if (cancelled) return - const filtered = filterTranslateLanguagesWithGrammarCatalog(list) - if (!filtered.length) { + const base = list.length > 0 ? list : TRANSLATE_GRAMMAR_LANGUAGE_OPTIONS + const filtered = filterTranslateLanguagesWithGrammarCatalog(base) + const resolved = filtered.length > 0 ? filtered : TRANSLATE_GRAMMAR_LANGUAGE_OPTIONS + if (!resolved.length) { setTranslateLangs([]) setTranslateLoad('empty') return } - setTranslateLangs(filtered) + setTranslateLangs([...resolved]) setTranslateSource('auto') - const codes = filtered.map((l) => l.code) + const codes = resolved.map((l) => l.code) const tgt = codes.includes('en') ? 'en' : codes[0]! setTranslateTarget(tgt) setTranslateLoad('ready') }) .catch(() => { - if (!cancelled) setTranslateLoad('error') + if (cancelled) return + const resolved = TRANSLATE_GRAMMAR_LANGUAGE_OPTIONS + setTranslateLangs([...resolved]) + setTranslateSource('auto') + const codes = resolved.map((l) => l.code) + setTranslateTarget(codes.includes('en') ? 'en' : codes[0]!) + setTranslateLoad('ready') }) return () => { cancelled = true diff --git a/src/components/NoteOptions/useMenuActions.tsx b/src/components/NoteOptions/useMenuActions.tsx index 769ddcbc..768d7d91 100644 --- a/src/components/NoteOptions/useMenuActions.tsx +++ b/src/components/NoteOptions/useMenuActions.tsx @@ -70,6 +70,7 @@ import { import { filterTranslateLanguagesWithGrammarCatalog, languageSelectSingleLine, + TRANSLATE_GRAMMAR_LANGUAGE_OPTIONS, TRANSLATE_LANGUAGE_MENU_ITEM_CLASS } from '@/lib/language-display-meta' @@ -187,10 +188,18 @@ export function useMenuActions({ return } let cancelled = false - void fetchTranslateLanguages().then((list) => { - if (cancelled) return - setTranslateMenuOptions(filterTranslateLanguagesWithGrammarCatalog(list)) - }) + void fetchTranslateLanguages() + .then((list) => { + if (cancelled) return + const base = list.length > 0 ? list : TRANSLATE_GRAMMAR_LANGUAGE_OPTIONS + const filtered = filterTranslateLanguagesWithGrammarCatalog(base) + setTranslateMenuOptions( + filtered.length > 0 ? filtered : TRANSLATE_GRAMMAR_LANGUAGE_OPTIONS + ) + }) + .catch(() => { + if (!cancelled) setTranslateMenuOptions(TRANSLATE_GRAMMAR_LANGUAGE_OPTIONS) + }) return () => { cancelled = true }