From 92013f3bfcb17bba886c297c2fb6c6b9f48f6d87 Mon Sep 17 00:00:00 2001 From: n4ze3m Date: Sun, 17 Nov 2024 12:26:14 +0530 Subject: [PATCH] feat(settings): add Ollama connection status check setting - Add new setting to enable/disable Ollama connection status check - Update translations for the new setting across all supported languages --- src/assets/locale/da/playground.json | 3 +- src/assets/locale/da/settings.json | 3 ++ src/assets/locale/de/playground.json | 3 +- src/assets/locale/de/settings.json | 3 ++ src/assets/locale/en/playground.json | 3 +- src/assets/locale/en/settings.json | 3 ++ src/assets/locale/es/playground.json | 3 +- src/assets/locale/es/settings.json | 3 ++ src/assets/locale/fa/playground.json | 3 +- src/assets/locale/fa/settings.json | 3 ++ src/assets/locale/fr/playground.json | 3 +- src/assets/locale/fr/settings.json | 3 ++ src/assets/locale/it/playground.json | 3 +- src/assets/locale/it/settings.json | 3 ++ src/assets/locale/ja-JP/playground.json | 3 +- src/assets/locale/ja-JP/settings.json | 3 ++ src/assets/locale/ko/playground.json | 3 +- src/assets/locale/ko/settings.json | 4 +- src/assets/locale/ml/playground.json | 3 +- src/assets/locale/ml/settings.json | 3 ++ src/assets/locale/no/playground.json | 3 +- src/assets/locale/no/settings.json | 3 ++ src/assets/locale/pt-BR/playground.json | 3 +- src/assets/locale/pt-BR/settings.json | 3 ++ src/assets/locale/ru/playground.json | 3 +- src/assets/locale/ru/settings.json | 3 ++ src/assets/locale/sv/playground.json | 3 +- src/assets/locale/sv/settings.json | 3 ++ src/assets/locale/zh/playground.json | 3 +- src/assets/locale/zh/settings.json | 3 ++ src/components/Layouts/Header.tsx | 1 + .../Option/Playground/PlaygroundEmpty.tsx | 31 +++++++++++---- .../Option/Settings/general-settings.tsx | 29 +++++++++++--- src/components/Select/index.tsx | 8 ++-- src/components/Sidepanel/Chat/empty.tsx | 9 +++-- src/services/ollama.ts | 38 +++++++------------ 36 files changed, 148 insertions(+), 59 deletions(-) diff --git a/src/assets/locale/da/playground.json b/src/assets/locale/da/playground.json index f8df2b2..8fdcd54 100644 --- a/src/assets/locale/da/playground.json +++ b/src/assets/locale/da/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "Stop Streaming", "knowledge": "Viden" }, - "sendWhenEnter": "Søg, når Indtast trykkes" + "sendWhenEnter": "Søg, når Indtast trykkes", + "welcome": "Hej! Hvordan kan jeg hjælpe dig i dag?" } \ No newline at end of file diff --git a/src/assets/locale/da/settings.json b/src/assets/locale/da/settings.json index 1666d9c..1d088d1 100644 --- a/src/assets/locale/da/settings.json +++ b/src/assets/locale/da/settings.json @@ -32,6 +32,9 @@ }, "generateTitle": { "label": "Generer titel med AI" + }, + "ollamaStatus": { + "label": "Aktivér eller deaktivér Ollama forbindelsesstatus kontrol" } }, "sidepanelRag": { diff --git a/src/assets/locale/de/playground.json b/src/assets/locale/de/playground.json index 119d87f..43eafb9 100644 --- a/src/assets/locale/de/playground.json +++ b/src/assets/locale/de/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "Streaming stoppen", "knowledge": "Wissen" }, - "sendWhenEnter": "Senden bei Drücken der Eingabetaste" + "sendWhenEnter": "Senden bei Drücken der Eingabetaste", + "welcome": "Hallo! Wie kann ich Ihnen heute helfen?" } \ No newline at end of file diff --git a/src/assets/locale/de/settings.json b/src/assets/locale/de/settings.json index 30a5da3..c6b134b 100644 --- a/src/assets/locale/de/settings.json +++ b/src/assets/locale/de/settings.json @@ -32,6 +32,9 @@ }, "generateTitle": { "label": "Titel mit KI generieren" + }, + "ollamaStatus": { + "label": "Ollama-Verbindungsstatus-Überprüfung aktivieren oder deaktivieren" } }, "sidepanelRag": { diff --git a/src/assets/locale/en/playground.json b/src/assets/locale/en/playground.json index 05da9ad..2fc5041 100644 --- a/src/assets/locale/en/playground.json +++ b/src/assets/locale/en/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "Stop Streaming", "knowledge": "Knowledge" }, - "sendWhenEnter": "Send when Enter pressed" + "sendWhenEnter": "Send when Enter pressed", + "welcome": "Hello! How can I help you today?" } \ No newline at end of file diff --git a/src/assets/locale/en/settings.json b/src/assets/locale/en/settings.json index 8f19b6a..eb98bf4 100644 --- a/src/assets/locale/en/settings.json +++ b/src/assets/locale/en/settings.json @@ -32,6 +32,9 @@ }, "generateTitle": { "label": "Generate Title using AI" + }, + "ollamaStatus" :{ + "label":"Enable or disable Ollama connection status check" } }, "sidepanelRag": { diff --git a/src/assets/locale/es/playground.json b/src/assets/locale/es/playground.json index afc3771..2e16639 100644 --- a/src/assets/locale/es/playground.json +++ b/src/assets/locale/es/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "Parar Transmisión", "knowledge": "Conocimiento" }, - "sendWhenEnter": "Enviar cuando presione Enter" + "sendWhenEnter": "Enviar cuando presione Enter", + "welcome": "¡Hola! ¿Cómo puedo ayudarte hoy?" } diff --git a/src/assets/locale/es/settings.json b/src/assets/locale/es/settings.json index 241df94..4ed17d8 100644 --- a/src/assets/locale/es/settings.json +++ b/src/assets/locale/es/settings.json @@ -32,6 +32,9 @@ }, "generateTitle": { "label": "Generar título usando IA" + }, + "ollamaStatus": { + "label": "Habilitar o deshabilitar la verificación del estado de conexión de Ollama" } }, "sidepanelRag": { diff --git a/src/assets/locale/fa/playground.json b/src/assets/locale/fa/playground.json index e9161a7..3732e7f 100644 --- a/src/assets/locale/fa/playground.json +++ b/src/assets/locale/fa/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "توقف Streaming", "knowledge": "دانش" }, - "sendWhenEnter": "با فشار دادن Enter ارسال شود" + "sendWhenEnter": "با فشار دادن Enter ارسال شود", + "welcome": "سلام! امروز چطور می‌توانم به شما کمک کنم؟" } diff --git a/src/assets/locale/fa/settings.json b/src/assets/locale/fa/settings.json index 039f222..0a0cde8 100644 --- a/src/assets/locale/fa/settings.json +++ b/src/assets/locale/fa/settings.json @@ -32,6 +32,9 @@ }, "generateTitle": { "label": "تولید عنوان با استفاده از هوش مصنوعی" + }, + "ollamaStatus": { + "label": "فعال یا غیرفعال کردن بررسی وضعیت اتصال Ollama" } }, "sidepanelRag": { diff --git a/src/assets/locale/fr/playground.json b/src/assets/locale/fr/playground.json index fddf9c0..e81c542 100644 --- a/src/assets/locale/fr/playground.json +++ b/src/assets/locale/fr/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "Arrêtez la diffusion", "knowledge": "Connaissance" }, - "sendWhenEnter": "Envoyer en appuyant sur Entrée" + "sendWhenEnter": "Envoyer en appuyant sur Entrée", + "welcome": "Bonjour ! Comment puis-je vous aider aujourd'hui ?" } \ No newline at end of file diff --git a/src/assets/locale/fr/settings.json b/src/assets/locale/fr/settings.json index 027c1b3..7d3b191 100644 --- a/src/assets/locale/fr/settings.json +++ b/src/assets/locale/fr/settings.json @@ -32,6 +32,9 @@ }, "generateTitle": { "label": "Générer le titre en utilisant l'IA" + }, + "ollamaStatus": { + "label": "Activer ou désactiver la vérification de l'état de la connexion Ollama" } }, "sidepanelRag": { diff --git a/src/assets/locale/it/playground.json b/src/assets/locale/it/playground.json index 766fe20..c43cf87 100644 --- a/src/assets/locale/it/playground.json +++ b/src/assets/locale/it/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "Ferma lo Streaming", "knowledge": "Conoscenza" }, - "sendWhenEnter": "Invia subito dopo Enter" + "sendWhenEnter": "Invia subito dopo Enter", + "welcome": "Ciao! Come posso aiutarti oggi?" } \ No newline at end of file diff --git a/src/assets/locale/it/settings.json b/src/assets/locale/it/settings.json index af15a71..e227d22 100644 --- a/src/assets/locale/it/settings.json +++ b/src/assets/locale/it/settings.json @@ -32,6 +32,9 @@ }, "generateTitle": { "label": "Genera titolo utilizzando l'IA" + }, + "ollamaStatus": { + "label": "Abilita o disabilita il controllo dello stato della connessione Ollama" } }, "sidepanelRag": { diff --git a/src/assets/locale/ja-JP/playground.json b/src/assets/locale/ja-JP/playground.json index 96c4d1a..f89911c 100644 --- a/src/assets/locale/ja-JP/playground.json +++ b/src/assets/locale/ja-JP/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "ストリーミングを停止", "knowledge": "知識" }, - "sendWhenEnter": "Enterキーを押すと送信" + "sendWhenEnter": "Enterキーを押すと送信", + "welcome": "こんにちは!本日はどのようなお手伝いができますか?" } \ No newline at end of file diff --git a/src/assets/locale/ja-JP/settings.json b/src/assets/locale/ja-JP/settings.json index af5a1f5..0058062 100644 --- a/src/assets/locale/ja-JP/settings.json +++ b/src/assets/locale/ja-JP/settings.json @@ -35,6 +35,9 @@ }, "generateTitle": { "label": "AIを使用してタイトルを生成" + }, + "ollamaStatus": { + "label": "Ollamaの接続状態チェックを有効または無効にする" } }, "sidepanelRag": { diff --git a/src/assets/locale/ko/playground.json b/src/assets/locale/ko/playground.json index 082ad5d..d60b102 100644 --- a/src/assets/locale/ko/playground.json +++ b/src/assets/locale/ko/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "스트리밍 중지", "knowledge": "지식" }, - "sendWhenEnter": "Enter 키를 누르면 전송" + "sendWhenEnter": "Enter 키를 누르면 전송", + "welcome": "안녕하세요! 오늘 어떻게 도와드릴까요?" } diff --git a/src/assets/locale/ko/settings.json b/src/assets/locale/ko/settings.json index 072c7f6..f3c7109 100644 --- a/src/assets/locale/ko/settings.json +++ b/src/assets/locale/ko/settings.json @@ -35,6 +35,9 @@ }, "generateTitle": { "label": "AI로 제목 생성" + }, + "ollamaStatus": { + "label": "Ollama 연결 상태 확인 활성화 또는 비활성화" } }, "sidepanelRag": { @@ -342,4 +345,3 @@ "title": "Chrome AI 설정" } } - diff --git a/src/assets/locale/ml/playground.json b/src/assets/locale/ml/playground.json index 8297aa4..e789350 100644 --- a/src/assets/locale/ml/playground.json +++ b/src/assets/locale/ml/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "സ്ട്രീമിംഗ് നിർത്തുക", "knowledge": "അറിവ്" }, - "sendWhenEnter": "എന്റര്‍ അമര്‍ത്തുമ്പോള്‍ അയയ്ക്കുക" + "sendWhenEnter": "എന്റര്‍ അമര്‍ത്തുമ്പോള്‍ അയയ്ക്കുക", + "welcome": "നമസ്കാരം! ഇന്ന് എനിക്ക് നിങ്ങളെ എങ്ങനെ സഹായിക്കാൻ കഴിയും?" } \ No newline at end of file diff --git a/src/assets/locale/ml/settings.json b/src/assets/locale/ml/settings.json index b6ebd30..03d67f1 100644 --- a/src/assets/locale/ml/settings.json +++ b/src/assets/locale/ml/settings.json @@ -35,6 +35,9 @@ }, "generateTitle": { "label": "എഐ ഉപയോഗിച്ച് ശീർഷകം സൃഷ്ടിക്കുക" + }, + "ollamaStatus": { + "label": "ഒല്ലാമ കണക്ഷൻ സ്റ്റാറ്റസ് പരിശോധന പ്രവർത്തനക്ഷമമാക്കുകയോ പ്രവർത്തനരഹിതമാക്കുകയോ ചെയ്യുക" } }, "sidepanelRag": { diff --git a/src/assets/locale/no/playground.json b/src/assets/locale/no/playground.json index b812649..43e2fa3 100644 --- a/src/assets/locale/no/playground.json +++ b/src/assets/locale/no/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "Stopp Streaming", "knowledge": "Kunnskap" }, - "sendWhenEnter": "Søk når Enter trykkes" + "sendWhenEnter": "Søk når Enter trykkes", + "welcome": "Hei! Hvordan kan jeg hjelpe deg i dag?" } diff --git a/src/assets/locale/no/settings.json b/src/assets/locale/no/settings.json index a09437f..8085e3e 100644 --- a/src/assets/locale/no/settings.json +++ b/src/assets/locale/no/settings.json @@ -32,6 +32,9 @@ }, "generateTitle": { "label": "Generer tittel med AI" + }, + "ollamaStatus": { + "label": "Aktiver eller deaktiver Ollama tilkoblingsstatussjekk" } }, "sidepanelRag": { diff --git a/src/assets/locale/pt-BR/playground.json b/src/assets/locale/pt-BR/playground.json index 6e91e97..ea11ffa 100644 --- a/src/assets/locale/pt-BR/playground.json +++ b/src/assets/locale/pt-BR/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "Parar Streaming", "knowledge": "Conhecimento" }, - "sendWhenEnter": "Enviar ao pressionar Enter" + "sendWhenEnter": "Enviar ao pressionar Enter", + "welcome": "Olá! Como posso ajudar você hoje?" } \ No newline at end of file diff --git a/src/assets/locale/pt-BR/settings.json b/src/assets/locale/pt-BR/settings.json index ac7e70b..fad3b11 100644 --- a/src/assets/locale/pt-BR/settings.json +++ b/src/assets/locale/pt-BR/settings.json @@ -32,6 +32,9 @@ }, "generateTitle": { "label": "Gerar título usando IA" + }, + "ollamaStatus": { + "label": "Ativar ou desativar verificação de status da conexão Ollama" } }, "sidepanelRag": { diff --git a/src/assets/locale/ru/playground.json b/src/assets/locale/ru/playground.json index fdc2933..39ba724 100644 --- a/src/assets/locale/ru/playground.json +++ b/src/assets/locale/ru/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "Остановить поток", "knowledge": "Знание" }, - "sendWhenEnter": "Отправить при нажатии клавиши Enter" + "sendWhenEnter": "Отправить при нажатии клавиши Enter", + "welcome": "Здравствуйте! Как я могу помочь вам сегодня?" } diff --git a/src/assets/locale/ru/settings.json b/src/assets/locale/ru/settings.json index 0c3c183..adf0008 100644 --- a/src/assets/locale/ru/settings.json +++ b/src/assets/locale/ru/settings.json @@ -32,6 +32,9 @@ }, "generateTitle": { "label": "Сгенерировать заголовок с помощью ИИ" + }, + "ollamaStatus": { + "label": "Включить или отключить проверку состояния подключения Ollama" } }, "sidepanelRag": { diff --git a/src/assets/locale/sv/playground.json b/src/assets/locale/sv/playground.json index d7195c5..16c8e80 100644 --- a/src/assets/locale/sv/playground.json +++ b/src/assets/locale/sv/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "Stoppa strömning", "knowledge": "Kunskap" }, - "sendWhenEnter": "Skicka när Enter trycks" + "sendWhenEnter": "Skicka när Enter trycks", + "welcome": "Hej! Hur kan jag hjälpa dig idag?" } diff --git a/src/assets/locale/sv/settings.json b/src/assets/locale/sv/settings.json index cb28f1e..d1e11d0 100644 --- a/src/assets/locale/sv/settings.json +++ b/src/assets/locale/sv/settings.json @@ -32,6 +32,9 @@ }, "generateTitle": { "label": "Generera titel med AI" + }, + "ollamaStatus": { + "label": "Aktivera eller inaktivera Ollama anslutningsstatuskontroll" } }, "sidepanelRag": { diff --git a/src/assets/locale/zh/playground.json b/src/assets/locale/zh/playground.json index 8f7da46..2b99e39 100644 --- a/src/assets/locale/zh/playground.json +++ b/src/assets/locale/zh/playground.json @@ -25,5 +25,6 @@ "stopStreaming": "停止流媒体", "knowledge": "知识" }, - "sendWhenEnter": "按Enter发送" + "sendWhenEnter": "按Enter发送", + "welcome": "你好!今天我能帮你什么?" } \ No newline at end of file diff --git a/src/assets/locale/zh/settings.json b/src/assets/locale/zh/settings.json index 4c86040..2f834e9 100644 --- a/src/assets/locale/zh/settings.json +++ b/src/assets/locale/zh/settings.json @@ -35,6 +35,9 @@ }, "generateTitle": { "label": "使用人工智能生成标题" + }, + "ollamaStatus": { + "label": "启用或禁用Ollama连接状态检查" } }, "sidepanelRag": { diff --git a/src/components/Layouts/Header.tsx b/src/components/Layouts/Header.tsx index 3e4a1f6..72f31da 100644 --- a/src/components/Layouts/Header.tsx +++ b/src/components/Layouts/Header.tsx @@ -113,6 +113,7 @@ export const Header: React.FC = ({ { setSelectedModel(e.value) diff --git a/src/components/Option/Playground/PlaygroundEmpty.tsx b/src/components/Option/Playground/PlaygroundEmpty.tsx index 7fb94ab..4e23d7d 100644 --- a/src/components/Option/Playground/PlaygroundEmpty.tsx +++ b/src/components/Option/Playground/PlaygroundEmpty.tsx @@ -1,4 +1,5 @@ import { cleanUrl } from "@/libs/clean-url" +import { useStorage } from "@plasmohq/storage/hook" import { useQuery } from "@tanstack/react-query" import { RotateCcw } from "lucide-react" import { useEffect, useState } from "react" @@ -12,6 +13,9 @@ import { export const PlaygroundEmpty = () => { const [ollamaURL, setOllamaURL] = useState("") const { t } = useTranslation(["playground", "common"]) + + const [checkOllamaStatus] = useStorage("checkOllamaStatus", true) + const { data: ollamaInfo, status: ollamaStatus, @@ -23,19 +27,32 @@ export const PlaygroundEmpty = () => { const ollamaURL = await getOllamaURL() const isOk = await isOllamaRunning() + if (ollamaURL) { + saveOllamaURL(ollamaURL) + } + return { isOk, ollamaURL } - } + }, + enabled: checkOllamaStatus }) - useEffect(() => { - if (ollamaInfo?.ollamaURL) { - setOllamaURL(ollamaInfo.ollamaURL) - } - }, [ollamaInfo]) - + if (!checkOllamaStatus) { + return ( +
+
+

+ 👋 + + {t("welcome")} + +

+
+
+ ) + } return (
diff --git a/src/components/Option/Settings/general-settings.tsx b/src/components/Option/Settings/general-settings.tsx index 7ceba76..f615236 100644 --- a/src/components/Option/Settings/general-settings.tsx +++ b/src/components/Option/Settings/general-settings.tsx @@ -16,13 +16,12 @@ import { import { useStorage } from "@plasmohq/storage/hook" export const GeneralSettings = () => { - const { clearChat } = - useMessageOption() + const { clearChat } = useMessageOption() - const [ speechToTextLanguage, setSpeechToTextLanguage ] = useStorage( - "speechToTextLanguage", - "en-US" - ) + const [speechToTextLanguage, setSpeechToTextLanguage] = useStorage( + "speechToTextLanguage", + "en-US" + ) const [copilotResumeLastChat, setCopilotResumeLastChat] = useStorage( "copilotResumeLastChat", false @@ -41,6 +40,11 @@ export const GeneralSettings = () => { const [sendNotificationAfterIndexing, setSendNotificationAfterIndexing] = useStorage("sendNotificationAfterIndexing", false) + const [checkOllamaStatus, setCheckOllamaStatus] = useStorage( + "checkOllamaStatus", + true + ) + const queryClient = useQueryClient() const { mode, toggleDarkMode } = useDarkMode() @@ -160,6 +164,19 @@ export const GeneralSettings = () => { />
+
+
+ + {t("generalSettings.settings.ollamaStatus.label")} + +
+ + setCheckOllamaStatus(checked)} + /> +
+
{t("generalSettings.settings.darkMode.label")} diff --git a/src/components/Select/index.tsx b/src/components/Select/index.tsx index e3da503..34d205f 100644 --- a/src/components/Select/index.tsx +++ b/src/components/Select/index.tsx @@ -235,14 +235,16 @@ export const PageAssistSelect: React.FC = ({ onClick={() => !disabled && !isLoading && setIsOpen(!isOpen)} onKeyDown={handleKeyDown} className={`${defaultSelectClass} ${className}`}> - - {isLoading && } + + {isLoading && } {isLoading ? ( loadingText ) : selectedOption ? ( selectedOption.label ) : ( - {placeholder} + + {placeholder} + )} { const [ollamaURL, setOllamaURL] = useState("") const { t } = useTranslation(["playground", "common"]) const queryClient = useQueryClient() + const [checkOllamaStatus] = useStorage("checkOllamaStatus", true) + const { data: ollamaInfo, status: ollamaStatus, refetch, isRefetching } = useQuery({ - queryKey: ["ollamaStatus"], + queryKey: ["ollamaStatus", checkOllamaStatus], queryFn: async () => { const ollamaURL = await getOllamaURL() const isOk = await isOllamaRunning() @@ -32,7 +35,7 @@ export const EmptySidePanel = () => { queryKey: ["getAllModelsForSelect"] }) return { - isOk, + isOk: checkOllamaStatus ? isOk : true, models, ollamaURL } @@ -59,7 +62,7 @@ export const EmptySidePanel = () => {

)} - {!isRefetching && ollamaStatus === "success" ? ( + {!isRefetching && ollamaStatus === "success" && checkOllamaStatus ? ( ollamaInfo.isOk ? (
diff --git a/src/services/ollama.ts b/src/services/ollama.ts index e8689da..42046ff 100644 --- a/src/services/ollama.ts +++ b/src/services/ollama.ts @@ -102,6 +102,7 @@ export const getAllModels = async ({ returnEmpty?: boolean }) => { try { + const baseUrl = await getOllamaURL() const response = await fetcher(`${cleanUrl(baseUrl)}/api/tags`) if (!response.ok) { @@ -178,30 +179,9 @@ export const fetchChatModels = async ({ returnEmpty?: boolean }) => { try { - const baseUrl = await getOllamaURL() - const response = await fetcher(`${cleanUrl(baseUrl)}/api/tags`) - if (!response.ok) { - if (returnEmpty) { - return [] - } - throw new Error(response.statusText) - } - const json = await response.json() - const models = json.models as { - name: string - model: string - modified_at: string - size: number - digest: string - details?: { - parent_model?: string - format: string - family: string - families: string[] - parameter_size: string - quantization_level: string - } - }[] + + const models = await getAllModels({ returnEmpty }) + const chatModels = models ?.filter((model) => { return ( @@ -408,3 +388,13 @@ export const getPageShareUrl = async () => { export const setPageShareUrl = async (pageShareUrl: string) => { await storage.set("pageShareUrl", pageShareUrl) } + + +export const isOllamaEnabled = async () => { + const ollamaStatus = await storage.get("checkOllamaStatus") + // if data is empty or null then return true + if (typeof ollamaStatus === "undefined" || ollamaStatus === null) { + return true + } + return ollamaStatus +} \ No newline at end of file