feat: add OpenCode update notification setting
Adds a setting to show or hide OpenCode update notifications Dismisses the active update toast when notifications are disabled Adds localized settings labels
This commit is contained in:
@@ -1,11 +1,13 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { Icon } from "@/components/icon/Icon";
|
import { Icon } from "@/components/icon/Icon";
|
||||||
import { isDesktopShell, isTauriShell } from '@/lib/desktop';
|
import { isDesktopShell, isTauriShell } from '@/lib/desktop';
|
||||||
import { updateDesktopSettings } from '@/lib/persistence';
|
import { updateDesktopSettings } from '@/lib/persistence';
|
||||||
import { reloadOpenCodeConfiguration } from '@/stores/useAgentsStore';
|
import { reloadOpenCodeConfiguration } from '@/stores/useAgentsStore';
|
||||||
|
import { useUIStore } from '@/stores/useUIStore';
|
||||||
import { useI18n } from '@/lib/i18n';
|
import { useI18n } from '@/lib/i18n';
|
||||||
|
|
||||||
export const OpenCodeCliSettings: React.FC = () => {
|
export const OpenCodeCliSettings: React.FC = () => {
|
||||||
@@ -13,6 +15,8 @@ export const OpenCodeCliSettings: React.FC = () => {
|
|||||||
const [value, setValue] = React.useState('');
|
const [value, setValue] = React.useState('');
|
||||||
const [isLoading, setIsLoading] = React.useState(true);
|
const [isLoading, setIsLoading] = React.useState(true);
|
||||||
const [isSaving, setIsSaving] = React.useState(false);
|
const [isSaving, setIsSaving] = React.useState(false);
|
||||||
|
const showOpenCodeUpdateNotifications = useUIStore((state) => state.showOpenCodeUpdateNotifications);
|
||||||
|
const setShowOpenCodeUpdateNotifications = useUIStore((state) => state.setShowOpenCodeUpdateNotifications);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
@@ -148,6 +152,17 @@ export const OpenCodeCliSettings: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<label className="flex cursor-pointer items-center gap-2 py-1.5">
|
||||||
|
<Checkbox
|
||||||
|
checked={showOpenCodeUpdateNotifications}
|
||||||
|
onChange={setShowOpenCodeUpdateNotifications}
|
||||||
|
ariaLabel={t('settings.openchamber.opencodeCli.field.showUpdateNotificationsAria')}
|
||||||
|
/>
|
||||||
|
<span className="typography-ui-label text-foreground">
|
||||||
|
{t('settings.openchamber.opencodeCli.field.showUpdateNotifications')}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
<div className="flex justify-start py-1.5">
|
<div className="flex justify-start py-1.5">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import * as React from 'react';
|
|||||||
import { Icon } from '@/components/icon/Icon';
|
import { Icon } from '@/components/icon/Icon';
|
||||||
import { toast } from '@/components/ui/toast';
|
import { toast } from '@/components/ui/toast';
|
||||||
import { reloadOpenCodeConfiguration } from '@/stores/useAgentsStore';
|
import { reloadOpenCodeConfiguration } from '@/stores/useAgentsStore';
|
||||||
|
import { useUIStore } from '@/stores/useUIStore';
|
||||||
import { useI18n } from '@/lib/i18n';
|
import { useI18n } from '@/lib/i18n';
|
||||||
|
|
||||||
type OpenCodeUpdateAvailableEvent = CustomEvent<{ version?: unknown }>;
|
type OpenCodeUpdateAvailableEvent = CustomEvent<{ version?: unknown }>;
|
||||||
@@ -17,9 +18,16 @@ const CHECK_RETRY_DELAYS_MS = [10_000, 60_000];
|
|||||||
|
|
||||||
export const OpenCodeUpdateToast: React.FC = () => {
|
export const OpenCodeUpdateToast: React.FC = () => {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const showOpenCodeUpdateNotifications = useUIStore((state) => state.showOpenCodeUpdateNotifications);
|
||||||
const seenVersionsRef = React.useRef(new Set<string>());
|
const seenVersionsRef = React.useRef(new Set<string>());
|
||||||
const upgradingRef = React.useRef(false);
|
const upgradingRef = React.useRef(false);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!showOpenCodeUpdateNotifications) {
|
||||||
|
toast.dismiss(UPDATE_TOAST_ID);
|
||||||
|
}
|
||||||
|
}, [showOpenCodeUpdateNotifications]);
|
||||||
|
|
||||||
const reloadOpenCode = React.useCallback(() => {
|
const reloadOpenCode = React.useCallback(() => {
|
||||||
toast.dismiss(UPGRADE_TOAST_ID);
|
toast.dismiss(UPGRADE_TOAST_ID);
|
||||||
void reloadOpenCodeConfiguration({
|
void reloadOpenCodeConfiguration({
|
||||||
@@ -79,6 +87,10 @@ export const OpenCodeUpdateToast: React.FC = () => {
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const showUpdateAvailableToast = (version: string) => {
|
const showUpdateAvailableToast = (version: string) => {
|
||||||
|
if (!useUIStore.getState().showOpenCodeUpdateNotifications) {
|
||||||
|
toast.dismiss(UPDATE_TOAST_ID);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!version) {
|
if (!version) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -125,7 +137,9 @@ export const OpenCodeUpdateToast: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
timeoutIds.push(setTimeout(() => { void checkForUpdate(0); }, INITIAL_CHECK_DELAY_MS));
|
if (showOpenCodeUpdateNotifications) {
|
||||||
|
timeoutIds.push(setTimeout(() => { void checkForUpdate(0); }, INITIAL_CHECK_DELAY_MS));
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener('openchamber:opencode-update-available', onUpdateAvailable);
|
window.addEventListener('openchamber:opencode-update-available', onUpdateAvailable);
|
||||||
return () => {
|
return () => {
|
||||||
@@ -133,7 +147,7 @@ export const OpenCodeUpdateToast: React.FC = () => {
|
|||||||
for (const timeoutId of timeoutIds) clearTimeout(timeoutId);
|
for (const timeoutId of timeoutIds) clearTimeout(timeoutId);
|
||||||
window.removeEventListener('openchamber:opencode-update-available', onUpdateAvailable);
|
window.removeEventListener('openchamber:opencode-update-available', onUpdateAvailable);
|
||||||
};
|
};
|
||||||
}, [runUpgrade, t]);
|
}, [runUpgrade, showOpenCodeUpdateNotifications, t]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -709,6 +709,8 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.opencodeCli.tooltipSuffix': 'binary.',
|
'settings.openchamber.opencodeCli.tooltipSuffix': 'binary.',
|
||||||
'settings.openchamber.opencodeCli.field.binaryPath': 'OpenCode Binary Path',
|
'settings.openchamber.opencodeCli.field.binaryPath': 'OpenCode Binary Path',
|
||||||
'settings.openchamber.opencodeCli.field.binaryPathPlaceholder': '/Users/you/.bun/bin/opencode',
|
'settings.openchamber.opencodeCli.field.binaryPathPlaceholder': '/Users/you/.bun/bin/opencode',
|
||||||
|
'settings.openchamber.opencodeCli.field.showUpdateNotifications': 'Show OpenCode update notifications',
|
||||||
|
'settings.openchamber.opencodeCli.field.showUpdateNotificationsAria': 'Show OpenCode update notifications',
|
||||||
'settings.openchamber.opencodeCli.actions.browseAria': 'Browse for OpenCode binary path',
|
'settings.openchamber.opencodeCli.actions.browseAria': 'Browse for OpenCode binary path',
|
||||||
'settings.openchamber.opencodeCli.actions.browse': 'Browse',
|
'settings.openchamber.opencodeCli.actions.browse': 'Browse',
|
||||||
'settings.openchamber.opencodeCli.actions.saveAndReload': 'Save + Reload',
|
'settings.openchamber.opencodeCli.actions.saveAndReload': 'Save + Reload',
|
||||||
|
|||||||
@@ -709,6 +709,8 @@ export const settingsDict = {
|
|||||||
"settings.openchamber.opencodeCli.tooltipSuffix": "ejecutable.",
|
"settings.openchamber.opencodeCli.tooltipSuffix": "ejecutable.",
|
||||||
"settings.openchamber.opencodeCli.field.binaryPath": "Ruta del ejecutable de OpenCode",
|
"settings.openchamber.opencodeCli.field.binaryPath": "Ruta del ejecutable de OpenCode",
|
||||||
"settings.openchamber.opencodeCli.field.binaryPathPlaceholder": "/Users/you/.bun/bin/opencode",
|
"settings.openchamber.opencodeCli.field.binaryPathPlaceholder": "/Users/you/.bun/bin/opencode",
|
||||||
|
"settings.openchamber.opencodeCli.field.showUpdateNotifications": "Mostrar notificaciones de actualización de OpenCode",
|
||||||
|
"settings.openchamber.opencodeCli.field.showUpdateNotificationsAria": "Mostrar notificaciones de actualización de OpenCode",
|
||||||
"settings.openchamber.opencodeCli.actions.browseAria": "Buscar ruta del ejecutable de OpenCode",
|
"settings.openchamber.opencodeCli.actions.browseAria": "Buscar ruta del ejecutable de OpenCode",
|
||||||
"settings.openchamber.opencodeCli.actions.browse": "Buscar",
|
"settings.openchamber.opencodeCli.actions.browse": "Buscar",
|
||||||
"settings.openchamber.opencodeCli.actions.saveAndReload": "Guardar + Recargar",
|
"settings.openchamber.opencodeCli.actions.saveAndReload": "Guardar + Recargar",
|
||||||
|
|||||||
@@ -709,6 +709,8 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.opencodeCli.tooltipSuffix': 'binary.',
|
'settings.openchamber.opencodeCli.tooltipSuffix': 'binary.',
|
||||||
'settings.openchamber.opencodeCli.field.binaryPath': 'OpenCode binary 경로',
|
'settings.openchamber.opencodeCli.field.binaryPath': 'OpenCode binary 경로',
|
||||||
'settings.openchamber.opencodeCli.field.binaryPathPlaceholder': '/Users/you/.bun/bin/opencode',
|
'settings.openchamber.opencodeCli.field.binaryPathPlaceholder': '/Users/you/.bun/bin/opencode',
|
||||||
|
'settings.openchamber.opencodeCli.field.showUpdateNotifications': 'OpenCode 업데이트 알림 표시',
|
||||||
|
'settings.openchamber.opencodeCli.field.showUpdateNotificationsAria': 'OpenCode 업데이트 알림 표시',
|
||||||
'settings.openchamber.opencodeCli.actions.browseAria': 'OpenCode binary 경로 찾아보기',
|
'settings.openchamber.opencodeCli.actions.browseAria': 'OpenCode binary 경로 찾아보기',
|
||||||
'settings.openchamber.opencodeCli.actions.browse': '찾아보기',
|
'settings.openchamber.opencodeCli.actions.browse': '찾아보기',
|
||||||
'settings.openchamber.opencodeCli.actions.saveAndReload': '저장 + 다시 로드',
|
'settings.openchamber.opencodeCli.actions.saveAndReload': '저장 + 다시 로드',
|
||||||
|
|||||||
@@ -634,6 +634,8 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.opencodeCli.dialog.selectBinaryTitle': 'Wybierz plik binarny opencode',
|
'settings.openchamber.opencodeCli.dialog.selectBinaryTitle': 'Wybierz plik binarny opencode',
|
||||||
'settings.openchamber.opencodeCli.field.binaryPath': 'Ścieżka do pliku binarnego OpenCode',
|
'settings.openchamber.opencodeCli.field.binaryPath': 'Ścieżka do pliku binarnego OpenCode',
|
||||||
'settings.openchamber.opencodeCli.field.binaryPathPlaceholder': '/Users/you/.bun/bin/opencode',
|
'settings.openchamber.opencodeCli.field.binaryPathPlaceholder': '/Users/you/.bun/bin/opencode',
|
||||||
|
'settings.openchamber.opencodeCli.field.showUpdateNotifications': 'Pokazuj powiadomienia o aktualizacjach OpenCode',
|
||||||
|
'settings.openchamber.opencodeCli.field.showUpdateNotificationsAria': 'Pokazuj powiadomienia o aktualizacjach OpenCode',
|
||||||
'settings.openchamber.opencodeCli.tipMiddle': 'zmienna środowiskowa, ale to ustawienie jest zapisywane w',
|
'settings.openchamber.opencodeCli.tipMiddle': 'zmienna środowiskowa, ale to ustawienie jest zapisywane w',
|
||||||
'settings.openchamber.opencodeCli.tipPrefix': 'Wskazówka: możesz również użyć',
|
'settings.openchamber.opencodeCli.tipPrefix': 'Wskazówka: możesz również użyć',
|
||||||
'settings.openchamber.opencodeCli.title': 'OpenCode CLI',
|
'settings.openchamber.opencodeCli.title': 'OpenCode CLI',
|
||||||
|
|||||||
@@ -709,6 +709,8 @@ export const settingsDict = {
|
|||||||
"settings.openchamber.opencodeCli.tooltipSuffix": "executável.",
|
"settings.openchamber.opencodeCli.tooltipSuffix": "executável.",
|
||||||
"settings.openchamber.opencodeCli.field.binaryPath": "Caminho do executável do OpenCode",
|
"settings.openchamber.opencodeCli.field.binaryPath": "Caminho do executável do OpenCode",
|
||||||
"settings.openchamber.opencodeCli.field.binaryPathPlaceholder": "/Users/you/.bun/bin/opencode",
|
"settings.openchamber.opencodeCli.field.binaryPathPlaceholder": "/Users/you/.bun/bin/opencode",
|
||||||
|
"settings.openchamber.opencodeCli.field.showUpdateNotifications": "Mostrar notificações de atualização do OpenCode",
|
||||||
|
"settings.openchamber.opencodeCli.field.showUpdateNotificationsAria": "Mostrar notificações de atualização do OpenCode",
|
||||||
"settings.openchamber.opencodeCli.actions.browseAria": "Buscar caminho do executável do OpenCode",
|
"settings.openchamber.opencodeCli.actions.browseAria": "Buscar caminho do executável do OpenCode",
|
||||||
"settings.openchamber.opencodeCli.actions.browse": "Buscar",
|
"settings.openchamber.opencodeCli.actions.browse": "Buscar",
|
||||||
"settings.openchamber.opencodeCli.actions.saveAndReload": "Salvar + Recarregar",
|
"settings.openchamber.opencodeCli.actions.saveAndReload": "Salvar + Recarregar",
|
||||||
|
|||||||
@@ -709,6 +709,8 @@ export const settingsDict = {
|
|||||||
"settings.openchamber.opencodeCli.tooltipSuffix": "бінарного файлу.",
|
"settings.openchamber.opencodeCli.tooltipSuffix": "бінарного файлу.",
|
||||||
"settings.openchamber.opencodeCli.field.binaryPath": "Шлях до бінарного файлу OpenCode",
|
"settings.openchamber.opencodeCli.field.binaryPath": "Шлях до бінарного файлу OpenCode",
|
||||||
"settings.openchamber.opencodeCli.field.binaryPathPlaceholder": "/Users/you/.bun/bin/opencode",
|
"settings.openchamber.opencodeCli.field.binaryPathPlaceholder": "/Users/you/.bun/bin/opencode",
|
||||||
|
"settings.openchamber.opencodeCli.field.showUpdateNotifications": "Показувати сповіщення про оновлення OpenCode",
|
||||||
|
"settings.openchamber.opencodeCli.field.showUpdateNotificationsAria": "Показувати сповіщення про оновлення OpenCode",
|
||||||
"settings.openchamber.opencodeCli.actions.browseAria": "Вибрати шлях до виконуваного файла OpenCode",
|
"settings.openchamber.opencodeCli.actions.browseAria": "Вибрати шлях до виконуваного файла OpenCode",
|
||||||
"settings.openchamber.opencodeCli.actions.browse": "Огляд",
|
"settings.openchamber.opencodeCli.actions.browse": "Огляд",
|
||||||
"settings.openchamber.opencodeCli.actions.saveAndReload": "Зберегти й перезавантажити",
|
"settings.openchamber.opencodeCli.actions.saveAndReload": "Зберегти й перезавантажити",
|
||||||
|
|||||||
@@ -709,6 +709,8 @@ export const settingsDict = {
|
|||||||
'settings.openchamber.opencodeCli.tooltipSuffix': '二进制绝对路径。',
|
'settings.openchamber.opencodeCli.tooltipSuffix': '二进制绝对路径。',
|
||||||
'settings.openchamber.opencodeCli.field.binaryPath': 'OpenCode 可执行文件路径',
|
'settings.openchamber.opencodeCli.field.binaryPath': 'OpenCode 可执行文件路径',
|
||||||
'settings.openchamber.opencodeCli.field.binaryPathPlaceholder': '/Users/you/.bun/bin/opencode',
|
'settings.openchamber.opencodeCli.field.binaryPathPlaceholder': '/Users/you/.bun/bin/opencode',
|
||||||
|
'settings.openchamber.opencodeCli.field.showUpdateNotifications': '显示 OpenCode 更新通知',
|
||||||
|
'settings.openchamber.opencodeCli.field.showUpdateNotificationsAria': '显示 OpenCode 更新通知',
|
||||||
'settings.openchamber.opencodeCli.actions.browseAria': '浏览 OpenCode 可执行文件路径',
|
'settings.openchamber.opencodeCli.actions.browseAria': '浏览 OpenCode 可执行文件路径',
|
||||||
'settings.openchamber.opencodeCli.actions.browse': '浏览',
|
'settings.openchamber.opencodeCli.actions.browse': '浏览',
|
||||||
'settings.openchamber.opencodeCli.actions.saveAndReload': '保存并重载',
|
'settings.openchamber.opencodeCli.actions.saveAndReload': '保存并重载',
|
||||||
|
|||||||
@@ -575,6 +575,7 @@ interface UIStore {
|
|||||||
|
|
||||||
showTerminalQuickKeysOnDesktop: boolean;
|
showTerminalQuickKeysOnDesktop: boolean;
|
||||||
persistChatDraft: boolean;
|
persistChatDraft: boolean;
|
||||||
|
showOpenCodeUpdateNotifications: boolean;
|
||||||
inputSpellcheckEnabled: boolean;
|
inputSpellcheckEnabled: boolean;
|
||||||
wideChatLayoutEnabled: boolean;
|
wideChatLayoutEnabled: boolean;
|
||||||
showToolFileIcons: boolean;
|
showToolFileIcons: boolean;
|
||||||
@@ -704,6 +705,7 @@ interface UIStore {
|
|||||||
setSummaryLength: (value: number) => void;
|
setSummaryLength: (value: number) => void;
|
||||||
setMaxLastMessageLength: (value: number) => void;
|
setMaxLastMessageLength: (value: number) => void;
|
||||||
setPersistChatDraft: (value: boolean) => void;
|
setPersistChatDraft: (value: boolean) => void;
|
||||||
|
setShowOpenCodeUpdateNotifications: (value: boolean) => void;
|
||||||
setInputSpellcheckEnabled: (value: boolean) => void;
|
setInputSpellcheckEnabled: (value: boolean) => void;
|
||||||
setWideChatLayoutEnabled: (value: boolean) => void;
|
setWideChatLayoutEnabled: (value: boolean) => void;
|
||||||
setShowToolFileIcons: (value: boolean) => void;
|
setShowToolFileIcons: (value: boolean) => void;
|
||||||
@@ -828,6 +830,7 @@ export const useUIStore = create<UIStore>()(
|
|||||||
|
|
||||||
showTerminalQuickKeysOnDesktop: false,
|
showTerminalQuickKeysOnDesktop: false,
|
||||||
persistChatDraft: true,
|
persistChatDraft: true,
|
||||||
|
showOpenCodeUpdateNotifications: true,
|
||||||
inputSpellcheckEnabled: false,
|
inputSpellcheckEnabled: false,
|
||||||
wideChatLayoutEnabled: false,
|
wideChatLayoutEnabled: false,
|
||||||
showToolFileIcons: true,
|
showToolFileIcons: true,
|
||||||
@@ -1847,6 +1850,9 @@ export const useUIStore = create<UIStore>()(
|
|||||||
setPersistChatDraft: (value) => {
|
setPersistChatDraft: (value) => {
|
||||||
set({ persistChatDraft: value });
|
set({ persistChatDraft: value });
|
||||||
},
|
},
|
||||||
|
setShowOpenCodeUpdateNotifications: (value) => {
|
||||||
|
set({ showOpenCodeUpdateNotifications: value });
|
||||||
|
},
|
||||||
setInputSpellcheckEnabled: (value) => {
|
setInputSpellcheckEnabled: (value) => {
|
||||||
set({ inputSpellcheckEnabled: value });
|
set({ inputSpellcheckEnabled: value });
|
||||||
},
|
},
|
||||||
@@ -2073,6 +2079,7 @@ export const useUIStore = create<UIStore>()(
|
|||||||
summaryLength: state.summaryLength,
|
summaryLength: state.summaryLength,
|
||||||
maxLastMessageLength: state.maxLastMessageLength,
|
maxLastMessageLength: state.maxLastMessageLength,
|
||||||
persistChatDraft: state.persistChatDraft,
|
persistChatDraft: state.persistChatDraft,
|
||||||
|
showOpenCodeUpdateNotifications: state.showOpenCodeUpdateNotifications,
|
||||||
inputSpellcheckEnabled: state.inputSpellcheckEnabled,
|
inputSpellcheckEnabled: state.inputSpellcheckEnabled,
|
||||||
wideChatLayoutEnabled: state.wideChatLayoutEnabled,
|
wideChatLayoutEnabled: state.wideChatLayoutEnabled,
|
||||||
showToolFileIcons: state.showToolFileIcons,
|
showToolFileIcons: state.showToolFileIcons,
|
||||||
|
|||||||
Reference in New Issue
Block a user