Merge branch 'docs-pena-team' of github.com:janhq/jan into docs-pena-team
This commit is contained in:
commit
8dd925f978
4
.github/scripts/auto-sign.sh
vendored
4
.github/scripts/auto-sign.sh
vendored
@ -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 {} \;
|
||||||
|
|||||||
20
README.md
20
README.md
@ -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>
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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;
|
||||||
@ -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()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -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
39
electron/managers/tray.ts
Normal 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()
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
@ -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)
|
||||||
};
|
}
|
||||||
|
|||||||
@ -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 />
|
||||||
|
|||||||
@ -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>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user