Merge branch 'dev' into docs-pena-team

This commit is contained in:
Arista Indrajaya 2024-03-13 15:59:24 +07:00 committed by GitHub
commit 463a8c791a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 134 additions and 44 deletions

View File

@ -76,31 +76,31 @@ Jan is an open-source ChatGPT alternative that runs 100% offline on your compute
<tr style="text-align:center"> <tr style="text-align:center">
<td style="text-align:center"><b>Experimental (Nightly Build)</b></td> <td style="text-align:center"><b>Experimental (Nightly Build)</b></td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-win-x64-0.4.8-313.exe'> <a href='https://delta.jan.ai/latest/jan-win-x64-0.4.8-315.exe'>
<img src='./docs/static/img/windows.png' style="height:14px; width: 14px" /> <img src='./docs/static/img/windows.png' style="height:14px; width: 14px" />
<b>jan.exe</b> <b>jan.exe</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-mac-x64-0.4.8-313.dmg'> <a href='https://delta.jan.ai/latest/jan-mac-x64-0.4.8-315.dmg'>
<img src='./docs/static/img/mac.png' style="height:15px; width: 15px" /> <img src='./docs/static/img/mac.png' style="height:15px; width: 15px" />
<b>Intel</b> <b>Intel</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-mac-arm64-0.4.8-313.dmg'> <a href='https://delta.jan.ai/latest/jan-mac-arm64-0.4.8-315.dmg'>
<img src='./docs/static/img/mac.png' style="height:15px; width: 15px" /> <img src='./docs/static/img/mac.png' style="height:15px; width: 15px" />
<b>M1/M2</b> <b>M1/M2</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-linux-amd64-0.4.8-313.deb'> <a href='https://delta.jan.ai/latest/jan-linux-amd64-0.4.8-315.deb'>
<img src='./docs/static/img/linux.png' style="height:14px; width: 14px" /> <img src='./docs/static/img/linux.png' style="height:14px; width: 14px" />
<b>jan.deb</b> <b>jan.deb</b>
</a> </a>
</td> </td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://delta.jan.ai/latest/jan-linux-x86_64-0.4.8-313.AppImage'> <a href='https://delta.jan.ai/latest/jan-linux-x86_64-0.4.8-315.AppImage'>
<img src='./docs/static/img/linux.png' style="height:14px; width: 14px" /> <img src='./docs/static/img/linux.png' style="height:14px; width: 14px" />
<b>jan.AppImage</b> <b>jan.AppImage</b>
</a> </a>

View File

@ -4,13 +4,13 @@ import fs from 'fs'
import os from 'os' import os from 'os'
import childProcess from 'child_process' import childProcess from 'child_process'
// TODO: move this to core
const configurationFileName = 'settings.json' const configurationFileName = 'settings.json'
// TODO: do no specify app name in framework module // TODO: do no specify app name in framework module
const defaultJanDataFolder = join(os.homedir(), 'jan') const defaultJanDataFolder = join(os.homedir(), 'jan')
const defaultAppConfig: AppConfiguration = { const defaultAppConfig: AppConfiguration = {
data_folder: defaultJanDataFolder, data_folder: defaultJanDataFolder,
quick_ask: false,
} }
/** /**

View File

@ -1,3 +1,4 @@
export type AppConfiguration = { export type AppConfiguration = {
data_folder: string data_folder: string
quick_ask: boolean
} }

View File

@ -1,5 +1,6 @@
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const fetch = require('node-fetch');
async function fetchData(siteConfig) { async function fetchData(siteConfig) {
const owner = siteConfig.organizationName; const owner = siteConfig.organizationName;
@ -70,6 +71,14 @@ async function fetchData(siteConfig) {
// Process the GitHub releases data here // Process the GitHub releases data here
for (const release of releases) { for (const release of releases) {
const version = release.tag_name; 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 releaseUrl = release.html_url;
const issueNumberMatch = release.body.match(/#(\d+)/); const issueNumberMatch = release.body.match(/#(\d+)/);
const issueNumber = issueNumberMatch ? parseInt(issueNumberMatch[1], 10) : null; const issueNumber = issueNumberMatch ? parseInt(issueNumberMatch[1], 10) : null;

View File

@ -5,7 +5,7 @@ import { join } from 'path'
* Managers * Managers
**/ **/
import { windowManager } from './managers/window' import { windowManager } from './managers/window'
import { log } from '@janhq/core/node' import { getAppConfigurations, log } from '@janhq/core/node'
/** /**
* IPC Handlers * IPC Handlers
@ -43,6 +43,12 @@ const gotTheLock = app.requestSingleInstanceLock()
app app
.whenReady() .whenReady()
.then(() => {
if (!gotTheLock) {
app.quit()
throw new Error('Another instance of the app is already running')
}
})
.then(setupReactDevTool) .then(setupReactDevTool)
.then(setupCore) .then(setupCore)
.then(createUserSpace) .then(createUserSpace)
@ -63,22 +69,20 @@ app
log(`Version: ${app.getVersion()}`) log(`Version: ${app.getVersion()}`)
}) })
.then(() => { .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', () => { app.on('activate', () => {
if (!BrowserWindow.getAllWindows().length) { if (!BrowserWindow.getAllWindows().length) {
createMainWindow() createMainWindow()
} else {
windowManager.showMainWindow()
} }
}) })
}) })
.then(() => cleanLogs()) .then(() => cleanLogs())
app.on('second-instance', (_event, _commandLine, _workingDirectory) => {
windowManager.showMainWindow()
})
app.on('ready', () => { app.on('ready', () => {
registerGlobalShortcuts() registerGlobalShortcuts()
}) })
@ -91,7 +95,15 @@ app.once('quit', () => {
cleanUpAndQuit() cleanUpAndQuit()
}) })
app.once('window-all-closed', () => {
// Feature Toggle for Quick Ask
if (getAppConfigurations().quick_ask) return
cleanUpAndQuit()
})
function createQuickAskWindow() { function createQuickAskWindow() {
// Feature Toggle for Quick Ask
if (!getAppConfigurations().quick_ask) return
const startUrl = app.isPackaged ? `file://${quickAskPath}` : quickAskUrl const startUrl = app.isPackaged ? `file://${quickAskPath}` : quickAskUrl
windowManager.createQuickAskWindow(preloadPath, startUrl) windowManager.createQuickAskWindow(preloadPath, startUrl)
} }
@ -103,6 +115,9 @@ function createMainWindow() {
function registerGlobalShortcuts() { function registerGlobalShortcuts() {
const ret = registerShortcut(quickAskHotKey, (selectedText: string) => { const ret = registerShortcut(quickAskHotKey, (selectedText: string) => {
// Feature Toggle for Quick Ask
if (!getAppConfigurations().quick_ask) return
if (!windowManager.isQuickAskWindowVisible()) { if (!windowManager.isQuickAskWindowVisible()) {
windowManager.showQuickAskWindow() windowManager.showQuickAskWindow()
windowManager.sendQuickAskSelectedText(selectedText) windowManager.sendQuickAskSelectedText(selectedText)

View File

@ -5,7 +5,7 @@ export const mainWindowConfig: Electron.BrowserWindowConstructorOptions = {
width: DEFAULT_WIDTH, width: DEFAULT_WIDTH,
minWidth: DEFAULT_WIDTH, minWidth: DEFAULT_WIDTH,
height: DEFAULT_HEIGHT, height: DEFAULT_HEIGHT,
skipTaskbar: true, skipTaskbar: false,
show: true, show: true,
trafficLightPosition: { trafficLightPosition: {
x: 10, x: 10,

View File

@ -1,11 +1,15 @@
import { join } from 'path' import { join } from 'path'
import { Tray, app, Menu } from 'electron' import { Tray, app, Menu } from 'electron'
import { windowManager } from '../managers/window' import { windowManager } from '../managers/window'
import { getAppConfigurations } from '@janhq/core/node'
class TrayManager { class TrayManager {
currentTray: Tray | undefined currentTray: Tray | undefined
createSystemTray = () => { createSystemTray = () => {
// Feature Toggle for Quick Ask
if (!getAppConfigurations().quick_ask) return
if (this.currentTray) { if (this.currentTray) {
return return
} }
@ -13,6 +17,12 @@ class TrayManager {
const tray = new Tray(iconPath) const tray = new Tray(iconPath)
tray.setToolTip(app.getName()) tray.setToolTip(app.getName())
tray.on('click', () => {
windowManager.showQuickAskWindow()
})
// Add context menu for windows only
if (process.platform === 'win32') {
const contextMenu = Menu.buildFromTemplate([ const contextMenu = Menu.buildFromTemplate([
{ {
label: 'Open Jan', label: 'Open Jan',
@ -26,7 +36,9 @@ class TrayManager {
}, },
{ label: 'Quit', type: 'normal', click: () => app.quit() }, { label: 'Quit', type: 'normal', click: () => app.quit() },
]) ])
tray.setContextMenu(contextMenu) tray.setContextMenu(contextMenu)
}
this.currentTray = tray this.currentTray = tray
} }

View File

@ -2,6 +2,7 @@ import { BrowserWindow, app, shell } from 'electron'
import { quickAskWindowConfig } from './quickAskWindowConfig' import { quickAskWindowConfig } from './quickAskWindowConfig'
import { AppEvent } from '@janhq/core' import { AppEvent } from '@janhq/core'
import { mainWindowConfig } from './mainWindowConfig' import { mainWindowConfig } from './mainWindowConfig'
import { getAppConfigurations } from '@janhq/core/node'
/** /**
* Manages the current window instance. * Manages the current window instance.
@ -43,6 +44,9 @@ class WindowManager {
}) })
windowManager.mainWindow?.on('close', function (evt) { windowManager.mainWindow?.on('close', function (evt) {
// Feature Toggle for Quick Ask
if (!getAppConfigurations().quick_ask) return
if (!isAppQuitting) { if (!isAppQuitting) {
evt.preventDefault() evt.preventDefault()
windowManager.hideMainWindow() windowManager.hideMainWindow()
@ -73,15 +77,11 @@ class WindowManager {
hideMainWindow(): void { hideMainWindow(): void {
this.mainWindow?.hide() this.mainWindow?.hide()
this._mainWindowVisible = false this._mainWindowVisible = false
// Only macos
if (process.platform === 'darwin') app.dock.hide()
} }
showMainWindow(): void { showMainWindow(): void {
this.mainWindow?.show() this.mainWindow?.show()
this._mainWindowVisible = true this._mainWindowVisible = true
// Only macos
if (process.platform === 'darwin') app.dock.show()
} }
hideQuickAskWindow(): void { hideQuickAskWindow(): void {

View File

@ -10,7 +10,10 @@ import useGetSystemResources from '@/hooks/useGetSystemResources'
import useModels from '@/hooks/useModels' import useModels from '@/hooks/useModels'
import useThreads from '@/hooks/useThreads' import useThreads from '@/hooks/useThreads'
import { janDataFolderPathAtom } from '@/helpers/atoms/AppConfig.atom' import {
janDataFolderPathAtom,
quickAskEnabledAtom,
} from '@/helpers/atoms/AppConfig.atom'
type Props = { type Props = {
children: ReactNode children: ReactNode
@ -18,6 +21,7 @@ type Props = {
const DataLoader: React.FC<Props> = ({ children }) => { const DataLoader: React.FC<Props> = ({ children }) => {
const setJanDataFolderPath = useSetAtom(janDataFolderPathAtom) const setJanDataFolderPath = useSetAtom(janDataFolderPathAtom)
const setQuickAskEnabled = useSetAtom(quickAskEnabledAtom)
useModels() useModels()
useThreads() useThreads()
@ -29,8 +33,9 @@ const DataLoader: React.FC<Props> = ({ children }) => {
?.getAppConfigurations() ?.getAppConfigurations()
?.then((appConfig: AppConfiguration) => { ?.then((appConfig: AppConfiguration) => {
setJanDataFolderPath(appConfig.data_folder) setJanDataFolderPath(appConfig.data_folder)
setQuickAskEnabled(appConfig.quick_ask)
}) })
}, [setJanDataFolderPath]) }, [setJanDataFolderPath, setQuickAskEnabled])
console.debug('Load Data...') console.debug('Load Data...')

View File

@ -24,10 +24,6 @@ export default function KeyListener({ children }: Props) {
useEffect(() => { useEffect(() => {
const onKeyDown = (e: KeyboardEvent) => { const onKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
window.core?.api?.hideMainWindow()
}
const prefixKey = isMac ? e.metaKey : e.ctrlKey const prefixKey = isMac ? e.metaKey : e.ctrlKey
if (e.key === 'b' && prefixKey) { if (e.key === 'b' && prefixKey) {

View File

@ -4,6 +4,8 @@ import { PropsWithChildren, useEffect, useState } from 'react'
import { Toaster } from 'react-hot-toast' import { Toaster } from 'react-hot-toast'
import { usePathname } from 'next/navigation'
import { TooltipProvider } from '@janhq/uikit' import { TooltipProvider } from '@janhq/uikit'
import GPUDriverPrompt from '@/containers/GPUDriverPromptModal' import GPUDriverPrompt from '@/containers/GPUDriverPromptModal'
@ -29,6 +31,7 @@ import { extensionManager } from '@/extension'
const Providers = (props: PropsWithChildren) => { const Providers = (props: PropsWithChildren) => {
const { children } = props const { children } = props
const pathname = usePathname()
const [setupCore, setSetupCore] = useState(false) const [setupCore, setSetupCore] = useState(false)
const [activated, setActivated] = useState(false) const [activated, setActivated] = useState(false)
@ -40,6 +43,11 @@ const Providers = (props: PropsWithChildren) => {
setTimeout(async () => { setTimeout(async () => {
if (!isCoreExtensionInstalled()) { if (!isCoreExtensionInstalled()) {
// TODO: Proper window handle
// Do not migrate extension from quick ask window
if (pathname === '/search') {
return
}
setSettingUp(true) setSettingUp(true)
await setupBaseExtensions() await setupBaseExtensions()
return return

View File

@ -6,6 +6,7 @@ const PROXY_FEATURE_ENABLED = 'proxyFeatureEnabled'
const VULKAN_ENABLED = 'vulkanEnabled' const VULKAN_ENABLED = 'vulkanEnabled'
const IGNORE_SSL = 'ignoreSSLFeature' const IGNORE_SSL = 'ignoreSSLFeature'
const HTTPS_PROXY_FEATURE = 'httpsProxyFeature' const HTTPS_PROXY_FEATURE = 'httpsProxyFeature'
const QUICK_ASK_ENABLED = 'quickAskEnabled'
export const janDataFolderPathAtom = atom('') export const janDataFolderPathAtom = atom('')
@ -19,3 +20,4 @@ export const proxyAtom = atomWithStorage(HTTPS_PROXY_FEATURE, '')
export const ignoreSslAtom = atomWithStorage(IGNORE_SSL, false) export const ignoreSslAtom = atomWithStorage(IGNORE_SSL, false)
export const vulkanEnabledAtom = atomWithStorage(VULKAN_ENABLED, false) export const vulkanEnabledAtom = atomWithStorage(VULKAN_ENABLED, false)
export const quickAskEnabledAtom = atomWithStorage(QUICK_ASK_ENABLED, false)

View File

@ -30,6 +30,7 @@ export default function useFactoryReset() {
// set the default jan data folder to user's home directory // set the default jan data folder to user's home directory
const configuration: AppConfiguration = { const configuration: AppConfiguration = {
data_folder: defaultJanDataFolder, data_folder: defaultJanDataFolder,
quick_ask: appConfiguration?.quick_ask ?? false,
} }
await window.core?.api?.updateAppConfiguration(configuration) await window.core?.api?.updateAppConfiguration(configuration)
} }

View File

@ -2,7 +2,7 @@
import { useEffect, useState, useCallback, ChangeEvent } from 'react' import { useEffect, useState, useCallback, ChangeEvent } from 'react'
import { openExternalUrl, fs } from '@janhq/core' import { openExternalUrl, fs, AppConfiguration } from '@janhq/core'
import { import {
Switch, Switch,
@ -23,7 +23,7 @@ import {
ScrollArea, ScrollArea,
} from '@janhq/uikit' } from '@janhq/uikit'
import { useAtom } from 'jotai' import { useAtom, useAtomValue } from 'jotai'
import { AlertTriangleIcon, AlertCircleIcon } from 'lucide-react' import { AlertTriangleIcon, AlertCircleIcon } from 'lucide-react'
import ShortcutModal from '@/containers/ShortcutModal' import ShortcutModal from '@/containers/ShortcutModal'
@ -42,6 +42,7 @@ import {
proxyAtom, proxyAtom,
proxyEnabledAtom, proxyEnabledAtom,
vulkanEnabledAtom, vulkanEnabledAtom,
quickAskEnabledAtom,
} from '@/helpers/atoms/AppConfig.atom' } from '@/helpers/atoms/AppConfig.atom'
type GPU = { type GPU = {
@ -56,6 +57,8 @@ const Advanced = () => {
) )
const [vulkanEnabled, setVulkanEnabled] = useAtom(vulkanEnabledAtom) const [vulkanEnabled, setVulkanEnabled] = useAtom(vulkanEnabledAtom)
const [proxyEnabled, setProxyEnabled] = useAtom(proxyEnabledAtom) const [proxyEnabled, setProxyEnabled] = useAtom(proxyEnabledAtom)
const quickAskEnabled = useAtomValue(quickAskEnabledAtom)
const [proxy, setProxy] = useAtom(proxyAtom) const [proxy, setProxy] = useAtom(proxyAtom)
const [ignoreSSL, setIgnoreSSL] = useAtom(ignoreSslAtom) const [ignoreSSL, setIgnoreSSL] = useAtom(ignoreSslAtom)
@ -87,6 +90,14 @@ const Advanced = () => {
[setPartialProxy, setProxy] [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(() => { useEffect(() => {
const setUseGpuIfPossible = async () => { const setUseGpuIfPossible = async () => {
const settings = await readSettings() const settings = await readSettings()
@ -361,7 +372,7 @@ const Advanced = () => {
Vulkan Support Vulkan Support
</h6> </h6>
</div> </div>
<p className="text-xs leading-relaxed"> <p className="leading-relaxed">
Enable Vulkan with AMD GPU/APU and Intel Arc GPU for better Enable Vulkan with AMD GPU/APU and Intel Arc GPU for better
model performance (reload needed). model performance (reload needed).
</p> </p>
@ -426,6 +437,36 @@ const Advanced = () => {
/> />
</div> </div>
{experimentalEnabled && (
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
<div className="flex-shrink-0 space-y-1.5">
<div className="flex gap-x-2">
<h6 className="text-sm font-semibold capitalize">
Jan Quick Ask
</h6>
</div>
<p className="leading-relaxed">
Enable Quick Ask to be triggered via the default hotkey{' '}
<div className="inline-flex items-center justify-center rounded-full bg-secondary px-1 py-0.5 text-xs font-bold text-muted-foreground">
<span className="font-bold">{isMac ? '⌘' : 'Ctrl'} + J</span>
</div>{' '}
(reload needed).
</p>
</div>
<Switch
checked={quickAskEnabled}
onCheckedChange={() => {
toaster({
title: 'Reload',
description:
'Quick Ask settings updated. Reload now to apply the changes.',
})
updateQuickAskEnabled(!quickAskEnabled)
}}
/>
</div>
)}
{/* Clear log */} {/* Clear log */}
<div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none"> <div className="flex w-full items-start justify-between border-b border-border py-4 first:pt-0 last:border-none">
<div className="flex-shrink-0 space-y-1.5"> <div className="flex-shrink-0 space-y-1.5">