From b9b421a495876a58465710da872425a8e045dd57 Mon Sep 17 00:00:00 2001 From: Louis Date: Tue, 12 Mar 2024 17:25:50 +0700 Subject: [PATCH 1/7] fix: quick app bugs (#2327) --- electron/main.ts | 20 +++++++------ electron/managers/mainWindowConfig.ts | 2 +- electron/managers/tray.ts | 36 +++++++++++++++--------- electron/managers/window.ts | 4 --- web/containers/Providers/KeyListener.tsx | 4 --- 5 files changed, 35 insertions(+), 31 deletions(-) diff --git a/electron/main.ts b/electron/main.ts index 78577ac68..b7ac0a74c 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -43,6 +43,12 @@ const gotTheLock = app.requestSingleInstanceLock() app .whenReady() + .then(() => { + if (!gotTheLock) { + app.quit() + throw new Error('Another instance of the app is already running') + } + }) .then(setupReactDevTool) .then(setupCore) .then(createUserSpace) @@ -63,22 +69,20 @@ app log(`Version: ${app.getVersion()}`) }) .then(() => { - if (!gotTheLock) { - app.quit() - } else { - app.on('second-instance', (_event, _commandLine, _workingDirectory) => { - // Someone tried to run a second instance, we should focus our window. - windowManager.showMainWindow() - }) - } app.on('activate', () => { if (!BrowserWindow.getAllWindows().length) { createMainWindow() + } else { + windowManager.showMainWindow() } }) }) .then(() => cleanLogs()) +app.on('second-instance', (_event, _commandLine, _workingDirectory) => { + windowManager.showMainWindow() +}) + app.on('ready', () => { registerGlobalShortcuts() }) diff --git a/electron/managers/mainWindowConfig.ts b/electron/managers/mainWindowConfig.ts index 184fb1c86..4f1715a94 100644 --- a/electron/managers/mainWindowConfig.ts +++ b/electron/managers/mainWindowConfig.ts @@ -5,7 +5,7 @@ export const mainWindowConfig: Electron.BrowserWindowConstructorOptions = { width: DEFAULT_WIDTH, minWidth: DEFAULT_WIDTH, height: DEFAULT_HEIGHT, - skipTaskbar: true, + skipTaskbar: false, show: true, trafficLightPosition: { x: 10, diff --git a/electron/managers/tray.ts b/electron/managers/tray.ts index 4e1c1a4ff..e7e07a0a1 100644 --- a/electron/managers/tray.ts +++ b/electron/managers/tray.ts @@ -13,20 +13,28 @@ class TrayManager { const tray = new Tray(iconPath) tray.setToolTip(app.getName()) - const contextMenu = Menu.buildFromTemplate([ - { - label: 'Open Jan', - type: 'normal', - click: () => windowManager.showMainWindow(), - }, - { - label: 'Open Quick Ask', - type: 'normal', - click: () => windowManager.showQuickAskWindow(), - }, - { label: 'Quit', type: 'normal', click: () => app.quit() }, - ]) - tray.setContextMenu(contextMenu) + tray.on('click', () => { + windowManager.showQuickAskWindow() + }) + + // Add context menu for windows only + if (process.platform === 'win32') { + const contextMenu = Menu.buildFromTemplate([ + { + label: 'Open Jan', + type: 'normal', + click: () => windowManager.showMainWindow(), + }, + { + label: 'Open Quick Ask', + type: 'normal', + click: () => windowManager.showQuickAskWindow(), + }, + { label: 'Quit', type: 'normal', click: () => app.quit() }, + ]) + + tray.setContextMenu(contextMenu) + } this.currentTray = tray } diff --git a/electron/managers/window.ts b/electron/managers/window.ts index eed80c37c..7b97f8349 100644 --- a/electron/managers/window.ts +++ b/electron/managers/window.ts @@ -73,15 +73,11 @@ class WindowManager { hideMainWindow(): void { this.mainWindow?.hide() this._mainWindowVisible = false - // Only macos - if (process.platform === 'darwin') app.dock.hide() } showMainWindow(): void { this.mainWindow?.show() this._mainWindowVisible = true - // Only macos - if (process.platform === 'darwin') app.dock.show() } hideQuickAskWindow(): void { diff --git a/web/containers/Providers/KeyListener.tsx b/web/containers/Providers/KeyListener.tsx index d832059c2..a4702783c 100644 --- a/web/containers/Providers/KeyListener.tsx +++ b/web/containers/Providers/KeyListener.tsx @@ -24,10 +24,6 @@ export default function KeyListener({ children }: Props) { useEffect(() => { const onKeyDown = (e: KeyboardEvent) => { - if (e.key === 'Escape') { - window.core?.api?.hideMainWindow() - } - const prefixKey = isMac ? e.metaKey : e.ctrlKey if (e.key === 'b' && prefixKey) { From 32b76499d973e67586b1aad460b4577a0a9c50fb Mon Sep 17 00:00:00 2001 From: hieu-jan <150573299+hieu-jan@users.noreply.github.com> Date: Tue, 12 Mar 2024 20:54:59 +0900 Subject: [PATCH 2/7] fix: existing-changelog --- docs/plugins/changelog-plugin/fetchData.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/plugins/changelog-plugin/fetchData.js b/docs/plugins/changelog-plugin/fetchData.js index 351ab3932..a9b970b3a 100644 --- a/docs/plugins/changelog-plugin/fetchData.js +++ b/docs/plugins/changelog-plugin/fetchData.js @@ -1,5 +1,6 @@ const fs = require('fs'); const path = require('path'); +const fetch = require('node-fetch'); async function fetchData(siteConfig) { const owner = siteConfig.organizationName; @@ -70,6 +71,14 @@ async function fetchData(siteConfig) { // Process the GitHub releases data here for (const release of releases) { const version = release.tag_name; + + // Check if the changelog file already exists for the current version + const existingChangelogPath = path.join(outputDirectory, `changelog-${version}.mdx`); + if (fs.existsSync(existingChangelogPath)) { + console.log(`Changelog for version ${version} already exists. Skipping...`); + continue; + } + const releaseUrl = release.html_url; const issueNumberMatch = release.body.match(/#(\d+)/); const issueNumber = issueNumberMatch ? parseInt(issueNumberMatch[1], 10) : null; @@ -94,4 +103,4 @@ async function fetchData(siteConfig) { } } -module.exports = fetchData; \ No newline at end of file +module.exports = fetchData; From af5bcea773e0f0e574663fcf1f399f3e961f6a63 Mon Sep 17 00:00:00 2001 From: Louis Date: Tue, 12 Mar 2024 20:10:59 +0700 Subject: [PATCH 3/7] fix: gate quick ask with feature toggle (#2331) --- core/src/node/helper/config.ts | 2 +- core/src/types/config/appConfigEntity.ts | 1 + electron/main.ts | 13 ++++++- electron/managers/tray.ts | 4 +++ electron/managers/window.ts | 4 +++ web/containers/Providers/DataLoader.tsx | 9 +++-- web/helpers/atoms/AppConfig.atom.ts | 2 ++ web/hooks/useFactoryReset.ts | 1 + web/screens/Settings/Advanced/index.tsx | 45 ++++++++++++++++++++++-- 9 files changed, 74 insertions(+), 7 deletions(-) diff --git a/core/src/node/helper/config.ts b/core/src/node/helper/config.ts index 71e721578..06f2b03cd 100644 --- a/core/src/node/helper/config.ts +++ b/core/src/node/helper/config.ts @@ -4,13 +4,13 @@ import fs from 'fs' import os from 'os' import childProcess from 'child_process' -// TODO: move this to core const configurationFileName = 'settings.json' // TODO: do no specify app name in framework module const defaultJanDataFolder = join(os.homedir(), 'jan') const defaultAppConfig: AppConfiguration = { data_folder: defaultJanDataFolder, + quick_ask: false, } /** diff --git a/core/src/types/config/appConfigEntity.ts b/core/src/types/config/appConfigEntity.ts index 81ea0b30f..1402aeca1 100644 --- a/core/src/types/config/appConfigEntity.ts +++ b/core/src/types/config/appConfigEntity.ts @@ -1,3 +1,4 @@ export type AppConfiguration = { data_folder: string + quick_ask: boolean } diff --git a/electron/main.ts b/electron/main.ts index b7ac0a74c..70314b169 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -5,7 +5,7 @@ import { join } from 'path' * Managers **/ import { windowManager } from './managers/window' -import { log } from '@janhq/core/node' +import { getAppConfigurations, log } from '@janhq/core/node' /** * IPC Handlers @@ -95,7 +95,15 @@ app.once('quit', () => { cleanUpAndQuit() }) +app.once('window-all-closed', () => { + // Feature Toggle for Quick Ask + if (getAppConfigurations().quick_ask) return + cleanUpAndQuit() +}) + function createQuickAskWindow() { + // Feature Toggle for Quick Ask + if (!getAppConfigurations().quick_ask) return const startUrl = app.isPackaged ? `file://${quickAskPath}` : quickAskUrl windowManager.createQuickAskWindow(preloadPath, startUrl) } @@ -107,6 +115,9 @@ function createMainWindow() { function registerGlobalShortcuts() { const ret = registerShortcut(quickAskHotKey, (selectedText: string) => { + // Feature Toggle for Quick Ask + if (!getAppConfigurations().quick_ask) return + if (!windowManager.isQuickAskWindowVisible()) { windowManager.showQuickAskWindow() windowManager.sendQuickAskSelectedText(selectedText) diff --git a/electron/managers/tray.ts b/electron/managers/tray.ts index e7e07a0a1..b81b1e556 100644 --- a/electron/managers/tray.ts +++ b/electron/managers/tray.ts @@ -1,11 +1,15 @@ import { join } from 'path' import { Tray, app, Menu } from 'electron' import { windowManager } from '../managers/window' +import { getAppConfigurations } from '@janhq/core/node' class TrayManager { currentTray: Tray | undefined createSystemTray = () => { + // Feature Toggle for Quick Ask + if (!getAppConfigurations().quick_ask) return + if (this.currentTray) { return } diff --git a/electron/managers/window.ts b/electron/managers/window.ts index 7b97f8349..da8dd4b17 100644 --- a/electron/managers/window.ts +++ b/electron/managers/window.ts @@ -2,6 +2,7 @@ import { BrowserWindow, app, shell } from 'electron' import { quickAskWindowConfig } from './quickAskWindowConfig' import { AppEvent } from '@janhq/core' import { mainWindowConfig } from './mainWindowConfig' +import { getAppConfigurations } from '@janhq/core/node' /** * Manages the current window instance. @@ -43,6 +44,9 @@ class WindowManager { }) windowManager.mainWindow?.on('close', function (evt) { + // Feature Toggle for Quick Ask + if (!getAppConfigurations().quick_ask) return + if (!isAppQuitting) { evt.preventDefault() windowManager.hideMainWindow() diff --git a/web/containers/Providers/DataLoader.tsx b/web/containers/Providers/DataLoader.tsx index bc1461d5b..7d38a29d6 100644 --- a/web/containers/Providers/DataLoader.tsx +++ b/web/containers/Providers/DataLoader.tsx @@ -10,7 +10,10 @@ import useGetSystemResources from '@/hooks/useGetSystemResources' import useModels from '@/hooks/useModels' import useThreads from '@/hooks/useThreads' -import { janDataFolderPathAtom } from '@/helpers/atoms/AppConfig.atom' +import { + janDataFolderPathAtom, + quickAskEnabledAtom, +} from '@/helpers/atoms/AppConfig.atom' type Props = { children: ReactNode @@ -18,6 +21,7 @@ type Props = { const DataLoader: React.FC = ({ children }) => { const setJanDataFolderPath = useSetAtom(janDataFolderPathAtom) + const setQuickAskEnabled = useSetAtom(quickAskEnabledAtom) useModels() useThreads() @@ -29,8 +33,9 @@ const DataLoader: React.FC = ({ children }) => { ?.getAppConfigurations() ?.then((appConfig: AppConfiguration) => { setJanDataFolderPath(appConfig.data_folder) + setQuickAskEnabled(appConfig.quick_ask) }) - }, [setJanDataFolderPath]) + }, [setJanDataFolderPath, setQuickAskEnabled]) console.debug('Load Data...') diff --git a/web/helpers/atoms/AppConfig.atom.ts b/web/helpers/atoms/AppConfig.atom.ts index 75343d722..2c678b77d 100644 --- a/web/helpers/atoms/AppConfig.atom.ts +++ b/web/helpers/atoms/AppConfig.atom.ts @@ -6,6 +6,7 @@ const PROXY_FEATURE_ENABLED = 'proxyFeatureEnabled' const VULKAN_ENABLED = 'vulkanEnabled' const IGNORE_SSL = 'ignoreSSLFeature' const HTTPS_PROXY_FEATURE = 'httpsProxyFeature' +const QUICK_ASK_ENABLED = 'quickAskEnabled' export const janDataFolderPathAtom = atom('') @@ -19,3 +20,4 @@ export const proxyAtom = atomWithStorage(HTTPS_PROXY_FEATURE, '') export const ignoreSslAtom = atomWithStorage(IGNORE_SSL, false) export const vulkanEnabledAtom = atomWithStorage(VULKAN_ENABLED, false) +export const quickAskEnabledAtom = atomWithStorage(QUICK_ASK_ENABLED, false) diff --git a/web/hooks/useFactoryReset.ts b/web/hooks/useFactoryReset.ts index 06a637572..da0813060 100644 --- a/web/hooks/useFactoryReset.ts +++ b/web/hooks/useFactoryReset.ts @@ -30,6 +30,7 @@ export default function useFactoryReset() { // set the default jan data folder to user's home directory const configuration: AppConfiguration = { data_folder: defaultJanDataFolder, + quick_ask: appConfiguration?.quick_ask ?? false, } await window.core?.api?.updateAppConfiguration(configuration) } diff --git a/web/screens/Settings/Advanced/index.tsx b/web/screens/Settings/Advanced/index.tsx index 70cf335c2..0433ba75c 100644 --- a/web/screens/Settings/Advanced/index.tsx +++ b/web/screens/Settings/Advanced/index.tsx @@ -2,7 +2,7 @@ import { useEffect, useState, useCallback, ChangeEvent } from 'react' -import { openExternalUrl, fs } from '@janhq/core' +import { openExternalUrl, fs, AppConfiguration } from '@janhq/core' import { Switch, @@ -23,7 +23,7 @@ import { ScrollArea, } from '@janhq/uikit' -import { useAtom } from 'jotai' +import { useAtom, useAtomValue } from 'jotai' import { AlertTriangleIcon, AlertCircleIcon } from 'lucide-react' import ShortcutModal from '@/containers/ShortcutModal' @@ -42,6 +42,7 @@ import { proxyAtom, proxyEnabledAtom, vulkanEnabledAtom, + quickAskEnabledAtom, } from '@/helpers/atoms/AppConfig.atom' type GPU = { @@ -56,6 +57,8 @@ const Advanced = () => { ) const [vulkanEnabled, setVulkanEnabled] = useAtom(vulkanEnabledAtom) const [proxyEnabled, setProxyEnabled] = useAtom(proxyEnabledAtom) + const quickAskEnabled = useAtomValue(quickAskEnabledAtom) + const [proxy, setProxy] = useAtom(proxyAtom) const [ignoreSSL, setIgnoreSSL] = useAtom(ignoreSslAtom) @@ -87,6 +90,14 @@ const Advanced = () => { [setPartialProxy, setProxy] ) + const updateQuickAskEnabled = async (e: boolean) => { + const appConfiguration: AppConfiguration = + await window.core?.api?.getAppConfigurations() + appConfiguration.quick_ask = e + await window.core?.api?.updateAppConfiguration(appConfiguration) + window.core?.api?.relaunch() + } + useEffect(() => { const setUseGpuIfPossible = async () => { const settings = await readSettings() @@ -361,7 +372,7 @@ const Advanced = () => { Vulkan Support -

+

Enable Vulkan with AMD GPU/APU and Intel Arc GPU for better model performance (reload needed).

@@ -426,6 +437,34 @@ const Advanced = () => { /> +
+
+
+
+ Jan Quick Ask +
+
+

+ Enable Quick Ask to be triggered via the default hotkey{' '} +

+ {isMac ? '⌘' : 'Ctrl'} + J +
{' '} + (reload needed). +

+
+ { + toaster({ + title: 'Reload', + description: + 'Quick Ask settings updated. Reload now to apply the changes.', + }) + updateQuickAskEnabled(!quickAskEnabled) + }} + /> +
+ {/* Clear log */}
From 2082b81d64835edc714e0552f0c928950bc9cfbf Mon Sep 17 00:00:00 2001 From: Service Account Date: Tue, 12 Mar 2024 13:40:59 +0000 Subject: [PATCH 4/7] janhq/jan: Update README.md with nightly build artifact URL --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 107a35d29..9fc133a8b 100644 --- a/README.md +++ b/README.md @@ -76,31 +76,31 @@ Jan is an open-source ChatGPT alternative that runs 100% offline on your compute Experimental (Nightly Build) - + jan.exe - + Intel - + M1/M2 - + jan.deb - + jan.AppImage From e5369ed151238acec7c5105360e1282f45c20a72 Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 13 Mar 2024 01:11:21 +0700 Subject: [PATCH 5/7] fix: do not migrate extensions from quick ask window (#2336) --- web/containers/Providers/index.tsx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/web/containers/Providers/index.tsx b/web/containers/Providers/index.tsx index 73445f10a..e70a56ca8 100644 --- a/web/containers/Providers/index.tsx +++ b/web/containers/Providers/index.tsx @@ -4,6 +4,8 @@ import { PropsWithChildren, useEffect, useState } from 'react' import { Toaster } from 'react-hot-toast' +import { usePathname } from 'next/navigation' + import { TooltipProvider } from '@janhq/uikit' import GPUDriverPrompt from '@/containers/GPUDriverPromptModal' @@ -29,6 +31,7 @@ import { extensionManager } from '@/extension' const Providers = (props: PropsWithChildren) => { const { children } = props + const pathname = usePathname() const [setupCore, setSetupCore] = useState(false) const [activated, setActivated] = useState(false) @@ -40,6 +43,11 @@ const Providers = (props: PropsWithChildren) => { setTimeout(async () => { if (!isCoreExtensionInstalled()) { + // TODO: Proper window handle + // Do not migrate extension from quick ask window + if (pathname === '/search') { + return + } setSettingUp(true) await setupBaseExtensions() return From fb893b89f3d2f1b04b54ccbafd6482d1ca2282f9 Mon Sep 17 00:00:00 2001 From: Service Account Date: Tue, 12 Mar 2024 20:19:19 +0000 Subject: [PATCH 6/7] janhq/jan: Update README.md with nightly build artifact URL --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9fc133a8b..a50691950 100644 --- a/README.md +++ b/README.md @@ -76,31 +76,31 @@ Jan is an open-source ChatGPT alternative that runs 100% offline on your compute Experimental (Nightly Build) - + jan.exe - + Intel - + M1/M2 - + jan.deb - + jan.AppImage From bff20ab87bdc3a4c5270226aa9bae470228a807f Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 13 Mar 2024 10:18:20 +0700 Subject: [PATCH 7/7] fix: put quick ask feature toggle under experimental feature (#2338) --- web/screens/Settings/Advanced/index.tsx | 52 +++++++++++++------------ 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/web/screens/Settings/Advanced/index.tsx b/web/screens/Settings/Advanced/index.tsx index 0433ba75c..6853a5104 100644 --- a/web/screens/Settings/Advanced/index.tsx +++ b/web/screens/Settings/Advanced/index.tsx @@ -437,33 +437,35 @@ const Advanced = () => { />
-
-
-
-
- Jan Quick Ask -
+ {experimentalEnabled && ( +
+
+
+
+ Jan Quick Ask +
+
+

+ Enable Quick Ask to be triggered via the default hotkey{' '} +

+ {isMac ? '⌘' : 'Ctrl'} + J +
{' '} + (reload needed). +

-

- Enable Quick Ask to be triggered via the default hotkey{' '} -

- {isMac ? '⌘' : 'Ctrl'} + J -
{' '} - (reload needed). -

+ { + toaster({ + title: 'Reload', + description: + 'Quick Ask settings updated. Reload now to apply the changes.', + }) + updateQuickAskEnabled(!quickAskEnabled) + }} + />
- { - toaster({ - title: 'Reload', - description: - 'Quick Ask settings updated. Reload now to apply the changes.', - }) - updateQuickAskEnabled(!quickAskEnabled) - }} - /> -
+ )} {/* Clear log */}