Merge branch 'docs-pena-team' of github.com:janhq/jan into docs-pena-team

This commit is contained in:
Arista Indrajaya 2024-03-12 16:55:12 +07:00
commit 8dd925f978
32 changed files with 1268 additions and 1142 deletions

View File

@ -7,6 +7,6 @@ if [[ -z "$APP_PATH" ]] || [[ -z "$DEVELOPER_ID" ]]; then
fi fi
# If both variables are set, execute the following commands # If both variables are set, execute the following commands
find "$APP_PATH" \( -type f -perm +111 -o -name "*.node" \) -exec codesign -s "$DEVELOPER_ID" --options=runtime {} \; find "$APP_PATH" \( -type f -perm +111 -o -name "*.node" \) -exec codesign --force -s "$DEVELOPER_ID" --options=runtime {} \;
find "$APP_PATH" -type f -name "*.o" -exec codesign -s "$DEVELOPER_ID" --options=runtime {} \; find "$APP_PATH" -type f -name "*.o" -exec codesign --force -s "$DEVELOPER_ID" --options=runtime {} \;

View File

@ -43,31 +43,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>Stable (Recommended)</b></td> <td style="text-align:center"><b>Stable (Recommended)</b></td>
<td style="text-align:center"> <td style="text-align:center">
<a href='https://github.com/janhq/jan/releases/download/v0.4.7/jan-win-x64-0.4.7.exe'> <a href='https://github.com/janhq/jan/releases/download/v0.4.8/jan-win-x64-0.4.8.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://github.com/janhq/jan/releases/download/v0.4.7/jan-mac-x64-0.4.7.dmg'> <a href='https://github.com/janhq/jan/releases/download/v0.4.8/jan-mac-x64-0.4.8.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://github.com/janhq/jan/releases/download/v0.4.7/jan-mac-arm64-0.4.7.dmg'> <a href='https://github.com/janhq/jan/releases/download/v0.4.8/jan-mac-arm64-0.4.8.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://github.com/janhq/jan/releases/download/v0.4.7/jan-linux-amd64-0.4.7.deb'> <a href='https://github.com/janhq/jan/releases/download/v0.4.8/jan-linux-amd64-0.4.8.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://github.com/janhq/jan/releases/download/v0.4.7/jan-linux-x86_64-0.4.7.AppImage'> <a href='https://github.com/janhq/jan/releases/download/v0.4.8/jan-linux-x86_64-0.4.8.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>
@ -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.7-304.exe'> <a href='https://delta.jan.ai/latest/jan-win-x64-0.4.8-313.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.7-304.dmg'> <a href='https://delta.jan.ai/latest/jan-mac-x64-0.4.8-313.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.7-304.dmg'> <a href='https://delta.jan.ai/latest/jan-mac-arm64-0.4.8-313.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.7-304.deb'> <a href='https://delta.jan.ai/latest/jan-linux-amd64-0.4.8-313.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.7-304.AppImage'> <a href='https://delta.jan.ai/latest/jan-linux-x86_64-0.4.8-313.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

@ -1,18 +1,20 @@
# Website # Website & Docs
This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator. This website is built using [Docusaurus 3.0](https://docusaurus.io/), a modern static website generator.
## Information Architecture ### Information Architecture
We try to **keep routes consistent** to maintain SEO. We try to **keep routes consistent** to maintain SEO.
- `/guides`: Guides on how to use the Jan application, with GIFs. For end users who are directly using Jan. Always assume users are not technical. - **`/guides/`**: Guides on how to use the Jan application. For end users who are directly using Jan.
- `/developer`: Developer docs on how to extend Jan. These pages are about what people can build with our software. We must hide the complexity of HOW the app is built, but explain just enough of the high level architecture so devs know enough to build on top of it. - **`/developer/`**: Developer docs on how to extend Jan. These pages are about what people can build with our software.
- `/api-reference`: Reference documentation, written in Swagger/OpenAPI format. - **`/api-reference/`**: Reference documentation for the Jan API server, written in Swagger/OpenAPI format.
- `/docs`: Engineering specs and product specs, i.e. HOW the app is built. Mostly for internal reference and for our core contributors who are building the SDK itself. - **`/changelog/`**: A list of changes made to the Jan application with each release.
- **`/blog/`**: A blog for the Jan application.
### Sidebar Autogeneration ### Sidebar Autogeneration
@ -20,34 +22,36 @@ The order of each page is either explicitly defined in `sidebar.js` or follows t
Important slugs are hardcoded at the document level (and shouldn't be rerouted): Important slugs are hardcoded at the document level (and shouldn't be rerouted):
```md ```
--- ---
title: Overview title: Overview
slug: /docs slug: /docs
--- ---
``` ```
## Contributing ## How to Contribute
### Installation Refer to the [Contributing Guide](https://github.com/janhq/jan/blob/dev/CONTRIBUTING.md) for more comprehensive information on how to contribute to the Jan project.
``` ### Pre-requisites and Installation
$ yarn
```
### Local Development - [Node.js](https://nodejs.org/en/) (version 20.0.0 or higher)
- [yarn](https://yarnpkg.com/) (version 1.22.0 or higher)
``` #### Installation
$ cp .env.example .env
$ yarn start ```bash
cd jan/docs
yarn install
yarn start
``` ```
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
### Build #### Build
``` ```bash
$ yarn build yarn build
``` ```
This command generates static content into the `build` directory and can be served using any static contents hosting service. This command generates static content into the `build` directory and can be served using any static contents hosting service.
@ -56,25 +60,27 @@ This command generates static content into the `build` directory and can be serv
Using SSH: Using SSH:
``` ```bash
$ USE_SSH=true yarn deploy USE_SSH=true yarn deploy
``` ```
Not using SSH: Not using SSH:
``` ```bash
$ GIT_USER=<Your GitHub username> yarn deploy GIT_USER=<Your GitHub username> yarn deploy
``` ```
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
### Preview URL, Pre-release and Publishing Documentation ### Preview URL, Pre-release and Publishing Documentation
When a PR is created, the preview URL will be automatically commented on the PR. - When a pull request is created, the preview URL will be automatically commented on the pull request.
The documentation will then be published to [https://jan.ai/](https://jan.ai/) when the PR is merged to `main`. - The documentation will then be published to [https://dev.jan.ai/](https://dev.jan.ai/) when the pull request is merged to `dev`.
- Our open-source maintainers will sync the updated content from `dev` to `docs` branch, which will then be published to [https://jan.ai/](https://jan.ai/).
### Additional Plugins ### Additional Plugins
- @docusaurus/theme-live-codeblock - @docusaurus/theme-live-codeblock
- [Redocusaurus](https://redocusaurus.vercel.app/): manually upload swagger files at `/openapi/OpenAPISpec.json` - [Redocusaurus](https://redocusaurus.vercel.app/): manually upload swagger files at `/openapi/jan.yaml` to update the API reference documentation.

View File

@ -18,7 +18,7 @@ keywords:
] ]
--- ---
Jan turns computers into a thinking machine to change how you use computers. Jan turns computers into thinking machines to change how we use them.
Jan is created and maintained by Jan Labs, a robotics company. Jan is created and maintained by Jan Labs, a robotics company.
With Jan, you can: With Jan, you can:

View File

@ -1,3 +1,88 @@
--- ---
title: Website & Docs title: Website & Docs
--- ---
This website is built using [Docusaurus 3.0](https://docusaurus.io/), a modern static website generator.
### Information Architecture
We try to **keep routes consistent** to maintain SEO.
- **`/guides/`**: Guides on how to use the Jan application. For end users who are directly using Jan.
- **`/developer/`**: Developer docs on how to extend Jan. These pages are about what people can build with our software.
- **`/api-reference/`**: Reference documentation for the Jan API server, written in Swagger/OpenAPI format.
- **`/changelog/`**: A list of changes made to the Jan application with each release.
- **`/blog/`**: A blog for the Jan application.
### Sidebar Autogeneration
The order of each page is either explicitly defined in `sidebar.js` or follows the [Docusaurus autogenerated](https://docusaurus.io/docs/next/sidebar/autogenerated) naming format, `##-path-name.md`.
Important slugs are hardcoded at the document level (and shouldn't be rerouted):
```
---
title: Overview
slug: /docs
---
```
## How to Contribute
Refer to the [Contributing Guide](https://github.com/janhq/jan/blob/dev/CONTRIBUTING.md) for more comprehensive information on how to contribute to the Jan project.
### Pre-requisites and Installation
- [Node.js](https://nodejs.org/en/) (version 20.0.0 or higher)
- [yarn](https://yarnpkg.com/) (version 1.22.0 or higher)
#### Installation
```bash
cd jan/docs
yarn install
yarn start
```
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
#### Build
```bash
yarn build
```
This command generates static content into the `build` directory and can be served using any static contents hosting service.
### Deployment
Using SSH:
```bash
USE_SSH=true yarn deploy
```
Not using SSH:
```bash
GIT_USER=<Your GitHub username> yarn deploy
```
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
### Preview URL, Pre-release and Publishing Documentation
- When a pull request is created, the preview URL will be automatically commented on the pull request.
- The documentation will then be published to [https://dev.jan.ai/](https://dev.jan.ai/) when the pull request is merged to `dev`.
- Our open-source maintainers will sync the updated content from `dev` to `docs` branch, which will then be published to [https://jan.ai/](https://jan.ai/).
### Additional Plugins
- @docusaurus/theme-live-codeblock
- [Redocusaurus](https://redocusaurus.vercel.app/): manually upload swagger files at `/openapi/jan.yaml` to update the API reference documentation.

View File

@ -6,14 +6,13 @@ async function fetchData(siteConfig) {
const repo = siteConfig.projectName; const repo = siteConfig.projectName;
const apiUrl = `https://api.github.com/repos/${owner}/${repo}/releases`; const apiUrl = `https://api.github.com/repos/${owner}/${repo}/releases`;
const outputDirectory = path.join(__dirname, '../../docs/guides/changelogs'); const outputDirectory = path.join(__dirname, '../../docs/releases/changelog');
if (!fs.existsSync(outputDirectory)) { if (!fs.existsSync(outputDirectory)) {
fs.mkdirSync(outputDirectory); fs.mkdirSync(outputDirectory);
} }
let counter = 1; let counter = 1;
const categoryFilePath = path.join(outputDirectory, '_category_.json');
const cacheFilePath = path.join(outputDirectory, 'cache.json'); const cacheFilePath = path.join(outputDirectory, 'cache.json');
let cachedData = {}; let cachedData = {};
@ -83,7 +82,7 @@ async function fetchData(siteConfig) {
const changes = release.body; const changes = release.body;
let markdownContent = `---\nsidebar_position: ${counter}\n---\n# ${version}\n\nFor more details, [GitHub Issues](${releaseUrl})\n\nHighlighted Issue: ${issueLink}\n\n${changes}\n`; let markdownContent = `---\nsidebar_position: ${counter}\nslug: /changelog/changelog-${version}\n---\n# ${version}\n\nFor more details, [GitHub Issues](${releaseUrl})\n\nHighlighted Issue: ${issueLink}\n\n${changes}\n`;
// Write to a separate markdown file for each version // Write to a separate markdown file for each version
const outputFilePath = path.join(outputDirectory, `changelog-${version}.mdx`); const outputFilePath = path.join(outputDirectory, `changelog-${version}.mdx`);
@ -93,20 +92,6 @@ async function fetchData(siteConfig) {
counter++; counter++;
} }
// Create _category_.json file
const categoryContent = {
label: 'Changelogs',
position: 5,
link: {
type: 'generated-index',
description: 'Changelog for Jan',
},
};
fs.writeFileSync(categoryFilePath, JSON.stringify(categoryContent, null, 2), 'utf-8');
console.log(`_category_.json has been created at: ${categoryFilePath}`);
} }
module.exports = fetchData; module.exports = fetchData;

View File

@ -7,6 +7,7 @@ import {
autoUpdater, autoUpdater,
} from 'electron-updater' } from 'electron-updater'
import { AppEvent } from '@janhq/core' import { AppEvent } from '@janhq/core'
import { trayManager } from '../managers/tray'
export let waitingToInstallVersion: string | undefined = undefined export let waitingToInstallVersion: string | undefined = undefined
@ -22,6 +23,7 @@ export function handleAppUpdates() {
message: 'Would you like to download and install it now?', message: 'Would you like to download and install it now?',
buttons: ['Download', 'Later'], buttons: ['Download', 'Later'],
}) })
trayManager.destroyCurrentTray()
if (action.response === 0) await autoUpdater.downloadUpdate() if (action.response === 0) await autoUpdater.downloadUpdate()
}) })

View File

@ -1,4 +1,4 @@
import { app, BrowserWindow, Menu, Tray } from 'electron' import { app, BrowserWindow, Tray } from 'electron'
import { join } from 'path' import { join } from 'path'
/** /**
@ -27,6 +27,7 @@ import { setupReactDevTool } from './utils/dev'
import { cleanLogs } from './utils/log' import { cleanLogs } from './utils/log'
import { registerShortcut } from './utils/selectedText' import { registerShortcut } from './utils/selectedText'
import { trayManager } from './managers/tray'
const preloadPath = join(__dirname, 'preload.js') const preloadPath = join(__dirname, 'preload.js')
const rendererPath = join(__dirname, '..', 'renderer') const rendererPath = join(__dirname, '..', 'renderer')
@ -38,6 +39,8 @@ const quickAskUrl = `${mainUrl}/search`
const quickAskHotKey = 'CommandOrControl+J' const quickAskHotKey = 'CommandOrControl+J'
const gotTheLock = app.requestSingleInstanceLock()
app app
.whenReady() .whenReady()
.then(setupReactDevTool) .then(setupReactDevTool)
@ -48,37 +51,26 @@ app
.then(setupMenu) .then(setupMenu)
.then(handleIPCs) .then(handleIPCs)
.then(handleAppUpdates) .then(handleAppUpdates)
.then(createQuickAskWindow) .then(() => process.env.CI !== 'e2e' && createQuickAskWindow())
.then(createMainWindow) .then(createMainWindow)
.then(() => { .then(() => {
if (!app.isPackaged) { if (!app.isPackaged) {
windowManager.mainWindow?.webContents.openDevTools() windowManager.mainWindow?.webContents.openDevTools()
} }
}) })
.then(() => { .then(() => process.env.CI !== 'e2e' && trayManager.createSystemTray())
const iconPath = join(app.getAppPath(), 'icons', 'icon-tray.png')
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)
})
.then(() => { .then(() => {
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()
@ -91,6 +83,10 @@ app.on('ready', () => {
registerGlobalShortcuts() registerGlobalShortcuts()
}) })
app.on('before-quit', function (evt) {
trayManager.destroyCurrentTray()
})
app.once('quit', () => { app.once('quit', () => {
cleanUpAndQuit() cleanUpAndQuit()
}) })

39
electron/managers/tray.ts Normal file
View File

@ -0,0 +1,39 @@
import { join } from 'path'
import { Tray, app, Menu } from 'electron'
import { windowManager } from '../managers/window'
class TrayManager {
currentTray: Tray | undefined
createSystemTray = () => {
if (this.currentTray) {
return
}
const iconPath = join(app.getAppPath(), 'icons', 'icon-tray.png')
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)
this.currentTray = tray
}
destroyCurrentTray() {
this.currentTray?.destroy()
this.currentTray = undefined
}
}
export const trayManager = new TrayManager()

View File

@ -101,6 +101,7 @@ class WindowManager {
expandQuickAskWindow(heightOffset: number): void { expandQuickAskWindow(heightOffset: number): void {
const width = quickAskWindowConfig.width! const width = quickAskWindowConfig.width!
const height = quickAskWindowConfig.height! + heightOffset const height = quickAskWindowConfig.height! + heightOffset
this._quickAskWindow?.setMinimumSize(width, height)
this._quickAskWindow?.setSize(width, height, true) this._quickAskWindow?.setSize(width, height, true)
} }

View File

@ -41,7 +41,6 @@
"notarize": { "notarize": {
"teamId": "F8AH6NHVY5" "teamId": "F8AH6NHVY5"
}, },
"icon": "icons/icon.png" "icon": "icons/icon.png"
}, },
"linux": { "linux": {
@ -92,7 +91,7 @@
"request": "^2.88.2", "request": "^2.88.2",
"request-progress": "^3.0.0", "request-progress": "^3.0.0",
"ulid": "^2.3.0", "ulid": "^2.3.0",
"@hurdlegroup/robotjs": "^0.11.4" "@nut-tree/nut-js": "^4.0.0"
}, },
"devDependencies": { "devDependencies": {
"@electron/notarize": "^2.1.0", "@electron/notarize": "^2.1.0",

View File

@ -1,19 +1,24 @@
import { clipboard, globalShortcut } from "electron"; import { clipboard, globalShortcut } from 'electron'
import { keyTap, keys } from "@hurdlegroup/robotjs"; import { keyboard, Key } from '@nut-tree/nut-js'
/** /**
* Gets selected text by synthesizing the keyboard shortcut * Gets selected text by synthesizing the keyboard shortcut
* "CommandOrControl+c" then reading text from the clipboard * "CommandOrControl+c" then reading text from the clipboard
*/ */
export const getSelectedText = async () => { export const getSelectedText = async () => {
const currentClipboardContent = clipboard.readText(); // preserve clipboard content const currentClipboardContent = clipboard.readText() // preserve clipboard content
clipboard.clear(); clipboard.clear()
keyTap("c" as keys, process.platform === "darwin" ? "command" : "control"); const hotkeys: Key[] = [
await new Promise((resolve) => setTimeout(resolve, 200)); // add a delay before checking clipboard process.platform === 'darwin' ? Key.LeftCmd : Key.LeftControl,
const selectedText = clipboard.readText(); Key.C,
clipboard.writeText(currentClipboardContent); ]
return selectedText; await keyboard.pressKey(...hotkeys)
}; await keyboard.releaseKey(...hotkeys)
await new Promise((resolve) => setTimeout(resolve, 200)) // add a delay before checking clipboard
const selectedText = clipboard.readText()
clipboard.writeText(currentClipboardContent)
return selectedText
}
/** /**
* Registers a global shortcut of `accelerator`. The `callback` is called * Registers a global shortcut of `accelerator`. The `callback` is called
@ -26,14 +31,14 @@ export const registerShortcut = (
callback: (selectedText: string) => void callback: (selectedText: string) => void
) => { ) => {
return globalShortcut.register(accelerator, async () => { return globalShortcut.register(accelerator, async () => {
callback(await getSelectedText()); callback(await getSelectedText())
}); })
}; }
/** /**
* Unregisters a global shortcut of `accelerator` and * Unregisters a global shortcut of `accelerator` and
* is equivalent to electron.globalShortcut.unregister * is equivalent to electron.globalShortcut.unregister
*/ */
export const unregisterShortcut = (accelerator: Electron.Accelerator) => { export const unregisterShortcut = (accelerator: Electron.Accelerator) => {
globalShortcut.unregister(accelerator); globalShortcut.unregister(accelerator)
}; }

View File

@ -29,15 +29,15 @@ const SelectedText = ({ onCleared }: { onCleared?: () => void }) => {
return shouldShowSelectedText ? ( return shouldShowSelectedText ? (
<div <div
ref={containerRef} ref={containerRef}
className="relative rounded-lg border-[1px] border-[#0000000F] bg-[#0000000A] p-[10px]" className="relative rounded-lg border border-border bg-secondary p-[10px]"
> >
<div <div
className="absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full border-[1px] border-[#0000000F] bg-white drop-shadow" className="absolute right-2 top-2 flex h-6 w-6 cursor-pointer items-center justify-center rounded-full border border-border bg-white shadow dark:bg-black/80"
onClick={onClearClicked} onClick={onClearClicked}
> >
<X size={16} /> <X size={14} className="text-muted-foreground" />
</div> </div>
<p className="font-semibold text-[#00000099]">{text}</p> <p className="pr-8 font-medium text-muted-foreground">{text}</p>
</div> </div>
) : ( ) : (
<div /> <div />

View File

@ -56,7 +56,7 @@ const UserInput: React.FC = () => {
} }
return ( return (
<div className="flex flex-col space-y-3 p-3"> <div className="flex flex-col space-y-3 bg-white p-3 dark:bg-background">
<form <form
ref={formRef} ref={formRef}
className="flex h-full w-full items-center justify-center" className="flex h-full w-full items-center justify-center"
@ -66,7 +66,7 @@ const UserInput: React.FC = () => {
<LogoMark width={28} height={28} className="mx-auto" /> <LogoMark width={28} height={28} className="mx-auto" />
<input <input
ref={inputRef} ref={inputRef}
className="flex-1 bg-transparent font-bold text-black focus:outline-none" className="flex-1 bg-transparent font-bold focus:outline-none"
type="text" type="text"
value={inputValue} value={inputValue}
onChange={handleChange} onChange={handleChange}
@ -77,7 +77,6 @@ const UserInput: React.FC = () => {
</Button> </Button>
</div> </div>
</form> </form>
<SelectedText onCleared={() => inputRef?.current?.focus()} /> <SelectedText onCleared={() => inputRef?.current?.focus()} />
</div> </div>
) )

View File

@ -4,7 +4,7 @@ import UserInput from './UserInput'
const Search: React.FC = () => { const Search: React.FC = () => {
return ( return (
<div className="h-screen w-screen overflow-hidden bg-white"> <div className="h-screen w-screen overflow-hidden bg-white dark:bg-background">
<UserInput /> <UserInput />
</div> </div>
) )

View File

@ -79,6 +79,8 @@ export default function useSendChatMessage() {
const setIsGeneratingResponse = useSetAtom(isGeneratingResponseAtom) const setIsGeneratingResponse = useSetAtom(isGeneratingResponseAtom)
const activeThreadRef = useRef<Thread | undefined>() const activeThreadRef = useRef<Thread | undefined>()
const selectedModelRef = useRef<Model | undefined>()
useEffect(() => { useEffect(() => {
modelRef.current = activeModel modelRef.current = activeModel
}, [activeModel]) }, [activeModel])
@ -91,6 +93,10 @@ export default function useSendChatMessage() {
activeThreadRef.current = activeThread activeThreadRef.current = activeThread
}, [activeThread]) }, [activeThread])
useEffect(() => {
selectedModelRef.current = selectedModel
}, [selectedModel])
const resendChatMessage = async (currentMessage: ThreadMessage) => { const resendChatMessage = async (currentMessage: ThreadMessage) => {
if (!activeThreadRef.current) { if (!activeThreadRef.current) {
console.error('No active thread') console.error('No active thread')
@ -128,11 +134,13 @@ export default function useSendChatMessage() {
type: MessageRequestType.Thread, type: MessageRequestType.Thread,
messages: messages, messages: messages,
threadId: activeThreadRef.current.id, threadId: activeThreadRef.current.id,
model: activeThreadRef.current.assistants[0].model ?? selectedModel, model:
activeThreadRef.current.assistants[0].model ?? selectedModelRef.current,
} }
const modelId = const modelId =
selectedModel?.id ?? activeThreadRef.current.assistants[0].model.id selectedModelRef.current?.id ??
activeThreadRef.current.assistants[0].model.id
if (modelRef.current?.id !== modelId) { if (modelRef.current?.id !== modelId) {
setQueuedMessage(true) setQueuedMessage(true)
@ -213,7 +221,7 @@ export default function useSendChatMessage() {
{ {
role: ChatCompletionRole.User, role: ChatCompletionRole.User,
content: content:
selectedModel && base64Blob selectedModelRef.current && base64Blob
? [ ? [
{ {
type: ChatCompletionMessageContentType.Text, type: ChatCompletionMessageContentType.Text,
@ -242,7 +250,7 @@ export default function useSendChatMessage() {
) )
let modelRequest = let modelRequest =
selectedModel ?? activeThreadRef.current.assistants[0].model selectedModelRef?.current ?? activeThreadRef.current.assistants[0].model
if (runtimeParams.stream == null) { if (runtimeParams.stream == null) {
runtimeParams.stream = true runtimeParams.stream = true
} }
@ -344,7 +352,8 @@ export default function useSendChatMessage() {
?.addNewMessage(threadMessage) ?.addNewMessage(threadMessage)
const modelId = const modelId =
selectedModel?.id ?? activeThreadRef.current.assistants[0].model.id selectedModelRef.current?.id ??
activeThreadRef.current.assistants[0].model.id
if (modelRef.current?.id !== modelId) { if (modelRef.current?.id !== modelId) {
setQueuedMessage(true) setQueuedMessage(true)