mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-13 11:39:41 +03:00
feat(desktop): spellcheck toggle and system-language dictionaries (#59129)
Co-authored-by: Brendonovich <14191578+Brendonovich@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
a88d3ca0bc
commit
bf94de965d
@@ -1,3 +1,5 @@
|
||||
import { flow, Option, Schema } from "effect"
|
||||
|
||||
export const DESKTOP_NATIVE_LOCALES = [
|
||||
"en",
|
||||
"zh",
|
||||
@@ -302,6 +304,9 @@ export const DESKTOP_NATIVE_ENGLISH = {
|
||||
|
||||
"desktop.picker.error.notSelected": "File was not selected by the picker",
|
||||
"desktop.picker.error.sizeLimit": "Selected attachments exceed the {{limit}} MB limit",
|
||||
|
||||
"desktop.contextMenu.spellcheck": "Spellcheck",
|
||||
"desktop.contextMenu.spellcheckLanguages": "Spellcheck Languages",
|
||||
} as const
|
||||
|
||||
export type DesktopNativeKey = keyof typeof DESKTOP_NATIVE_ENGLISH
|
||||
@@ -310,6 +315,7 @@ export type DesktopNativeMessages = Record<DesktopNativeKey, string>
|
||||
|
||||
export type DesktopNativeBundle = { locale: DesktopNativeLocale; messages: DesktopNativeMessages }
|
||||
|
||||
// SAFETY: DESKTOP_NATIVE_ENGLISH is a const literal, so its own keys are exactly DesktopNativeKey.
|
||||
export const DESKTOP_NATIVE_KEYS = Object.keys(DESKTOP_NATIVE_ENGLISH) as DesktopNativeKey[]
|
||||
|
||||
export const DESKTOP_NATIVE_MAX_PAYLOAD_BYTES = 64 * 1024
|
||||
@@ -320,35 +326,24 @@ export function createDesktopNativeBundle(
|
||||
): DesktopNativeBundle {
|
||||
return {
|
||||
locale,
|
||||
// SAFETY: the entries map every DesktopNativeKey to a string, which is exactly DesktopNativeMessages.
|
||||
messages: Object.fromEntries(DESKTOP_NATIVE_KEYS.map((key) => [key, translate(key)])) as DesktopNativeMessages,
|
||||
}
|
||||
}
|
||||
|
||||
export function parseDesktopNativeBundle(value: unknown): DesktopNativeBundle | undefined {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined
|
||||
const DesktopNativeBundleSchema = Schema.Struct({
|
||||
locale: Schema.Literals(DESKTOP_NATIVE_LOCALES),
|
||||
messages: Schema.Record(Schema.Literals(DESKTOP_NATIVE_KEYS), Schema.String),
|
||||
}).check(
|
||||
Schema.makeFilter(
|
||||
(bundle) => new TextEncoder().encode(JSON.stringify(bundle)).byteLength <= DESKTOP_NATIVE_MAX_PAYLOAD_BYTES,
|
||||
),
|
||||
)
|
||||
|
||||
try {
|
||||
if (new TextEncoder().encode(JSON.stringify(value)).byteLength > DESKTOP_NATIVE_MAX_PAYLOAD_BYTES) return undefined
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
const decodeDesktopNativeBundle = Schema.decodeUnknownOption(DesktopNativeBundleSchema, { onExcessProperty: "error" })
|
||||
|
||||
const bundle = value as { locale?: unknown; messages?: unknown }
|
||||
|
||||
if (!DESKTOP_NATIVE_LOCALES.some((locale) => locale === bundle.locale)) return undefined
|
||||
|
||||
if (!bundle.messages || typeof bundle.messages !== "object" || Array.isArray(bundle.messages)) return undefined
|
||||
const messages = bundle.messages as Record<string, unknown>
|
||||
const keys = Object.keys(messages)
|
||||
|
||||
if (keys.length !== DESKTOP_NATIVE_KEYS.length) return undefined
|
||||
|
||||
if (!DESKTOP_NATIVE_KEYS.every((key) => typeof messages[key] === "string")) return undefined
|
||||
|
||||
if (!keys.every((key) => key in DESKTOP_NATIVE_ENGLISH)) return undefined
|
||||
|
||||
return bundle as DesktopNativeBundle
|
||||
}
|
||||
/** Validates a bundle the renderer sent over IPC: a supported locale, exactly the native keys, and a bounded size. */
|
||||
export const parseDesktopNativeBundle = flow(decodeDesktopNativeBundle, Option.getOrUndefined)
|
||||
|
||||
export function formatDesktopNativeMessage(message: string, params?: Record<string, string | number>) {
|
||||
if (!params) return message
|
||||
|
||||
@@ -1005,6 +1005,8 @@ export const dict = {
|
||||
"settings.general.row.followUpBehavior.steer": "Steer",
|
||||
"settings.general.row.pinchZoom.title": "Pinch to zoom",
|
||||
"settings.general.row.pinchZoom.description": "Allow trackpad pinch and Ctrl-scroll gestures to zoom",
|
||||
"settings.general.row.spellcheck.title": "Spellcheck",
|
||||
"settings.general.row.spellcheck.description": "Underline misspelled words while typing",
|
||||
|
||||
"sound.option.none": "None",
|
||||
"sound.option.alert01": "Alert 01",
|
||||
|
||||
@@ -72,7 +72,7 @@ const desktop = [
|
||||
]
|
||||
|
||||
export const dict = {
|
||||
...Object.fromEntries(DESKTOP_NATIVE_KEYS.map((key, index) => [key, desktop[index]])),
|
||||
...Object.fromEntries(desktop.map((value, index) => [DESKTOP_NATIVE_KEYS[index], value])),
|
||||
"command.category.suggested": "Predloženo",
|
||||
"command.category.view": "Prikaz",
|
||||
"command.category.project": "Projekt",
|
||||
|
||||
@@ -72,7 +72,7 @@ const desktop = [
|
||||
]
|
||||
|
||||
export const dict = {
|
||||
...Object.fromEntries(DESKTOP_NATIVE_KEYS.map((key, index) => [key, desktop[index]])),
|
||||
...Object.fromEntries(desktop.map((value, index) => [DESKTOP_NATIVE_KEYS[index], value])),
|
||||
"command.category.suggested": "Javasolt",
|
||||
"command.category.view": "Nézet",
|
||||
"command.category.project": "Projekt",
|
||||
|
||||
@@ -72,7 +72,7 @@ const desktop = [
|
||||
]
|
||||
|
||||
export const dict = {
|
||||
...Object.fromEntries(DESKTOP_NATIVE_KEYS.map((key, index) => [key, desktop[index]])),
|
||||
...Object.fromEntries(desktop.map((value, index) => [DESKTOP_NATIVE_KEYS[index], value])),
|
||||
"command.category.suggested": "Tillögur",
|
||||
"command.category.view": "Skoða",
|
||||
"command.category.project": "Verkefni",
|
||||
|
||||
@@ -72,7 +72,7 @@ const desktop = [
|
||||
]
|
||||
|
||||
export const dict = {
|
||||
...Object.fromEntries(DESKTOP_NATIVE_KEYS.map((key, index) => [key, desktop[index]])),
|
||||
...Object.fromEntries(desktop.map((value, index) => [DESKTOP_NATIVE_KEYS[index], value])),
|
||||
"command.category.suggested": "Siūloma",
|
||||
"command.category.view": "Rodinys",
|
||||
"command.category.project": "Projektas",
|
||||
|
||||
@@ -108,6 +108,12 @@ type PlatformBase = {
|
||||
/** Allow native pinch/Ctrl-scroll zoom gestures (desktop only) */
|
||||
setPinchZoomEnabled?(enabled: boolean): Promise<void> | void
|
||||
|
||||
/** Get whether the native spellchecker underlines misspelled words (desktop only) */
|
||||
getSpellcheckEnabled?(): Promise<boolean>
|
||||
|
||||
/** Turn the native spellchecker on or off (desktop only) */
|
||||
setSpellcheckEnabled?(enabled: boolean): Promise<void>
|
||||
|
||||
/** Run a desktop-only menu action from the app chrome */
|
||||
runDesktopMenuAction?(action: DesktopMenuAction): Promise<void> | void
|
||||
|
||||
|
||||
@@ -249,6 +249,17 @@ export const SettingsGeneral: Component = () => {
|
||||
void update.catch(() => setPinchZoom(!checked))
|
||||
}
|
||||
|
||||
const [spellcheck, { mutate: setSpellcheck }] = createResource(
|
||||
() => desktop() && "getSpellcheckEnabled" in platform,
|
||||
() => platform.getSpellcheckEnabled?.().catch(() => true) ?? true,
|
||||
{ initialValue: true },
|
||||
)
|
||||
|
||||
const onSpellcheckChange = (checked: boolean) => {
|
||||
setSpellcheck(checked)
|
||||
void platform.setSpellcheckEnabled?.(checked).catch(() => setSpellcheck(!checked))
|
||||
}
|
||||
|
||||
const GeneralSection = () => (
|
||||
<div class="settings-section">
|
||||
<h3 class="settings-section-title">{language.t("settings.general.section.general")}</h3>
|
||||
@@ -282,6 +293,14 @@ export const SettingsGeneral: Component = () => {
|
||||
<Switch checked={pinchZoom.latest} onChange={onPinchZoomChange} />
|
||||
</div>
|
||||
</SettingsRow>
|
||||
<SettingsRow
|
||||
title={language.t("settings.general.row.spellcheck.title")}
|
||||
description={language.t("settings.general.row.spellcheck.description")}
|
||||
>
|
||||
<div data-action="settings-spellcheck">
|
||||
<Switch checked={spellcheck.latest} onChange={onSpellcheckChange} />
|
||||
</div>
|
||||
</SettingsRow>
|
||||
</Show>
|
||||
|
||||
<PluginSettingsSections page="general" section="general" />
|
||||
|
||||
@@ -55,6 +55,13 @@ export const clientSettings: Entry<SettingsRootTab>[] = [
|
||||
target: "settings-pinch-zoom",
|
||||
available: "desktop",
|
||||
},
|
||||
{
|
||||
tab: "general",
|
||||
label: "settings.general.row.spellcheck.title",
|
||||
target: "settings-spellcheck",
|
||||
keywords: "spelling spell check dictionary",
|
||||
available: "desktop",
|
||||
},
|
||||
{
|
||||
tab: "general",
|
||||
label: "settings.general.row.mobileTitlebarBottom.title",
|
||||
|
||||
Reference in New Issue
Block a user