mirror of
https://github.com/khoj-ai/khoj.git
synced 2024-11-23 23:48:56 +01:00
Remove PySide dependency and deprecate desktop builds (#475)
* Remove PySide, gui option from code * Remove pyside 6 dependency from code * Remove workflows which build desktop applications * Update unit tests and update line in documentation * Remove additional references to pyinstaller, gui * Add uninstall steps to normal uninstall instructions
This commit is contained in:
parent
76562f4250
commit
dccfae3853
14 changed files with 13 additions and 568 deletions
113
.github/workflows/build_desktop.yml
vendored
113
.github/workflows/build_desktop.yml
vendored
|
@ -1,113 +0,0 @@
|
||||||
name: desktop_dev_build
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
paths:
|
|
||||||
- src/khoj/**
|
|
||||||
- pyproject.toml
|
|
||||||
- Khoj.spec
|
|
||||||
- .github/workflows/build_desktop.yml
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish_desktop_apps:
|
|
||||||
name: 🖥️ Publish Desktop Apps
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-20.04
|
|
||||||
extension: deb
|
|
||||||
- os: macos-latest
|
|
||||||
extension: dmg
|
|
||||||
- os: windows-latest
|
|
||||||
extension: exe
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Python 3.9
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: '3.9'
|
|
||||||
|
|
||||||
- name: ⏬️ Install Dependencies
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
|
||||||
sudo apt update && sudo apt install libegl1 libxcb-xinerama0 python3-tk -y
|
|
||||||
fi
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install pyinstaller
|
|
||||||
|
|
||||||
- name: ⬇️ Install Khoj App
|
|
||||||
run: |
|
|
||||||
pip install --upgrade .
|
|
||||||
|
|
||||||
- name: 📦 Package Khoj App
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
# Setup Environment for Reproducible Builds
|
|
||||||
export PYTHONHASHSEED=42
|
|
||||||
export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
|
|
||||||
|
|
||||||
pyinstaller --noconfirm Khoj.spec
|
|
||||||
if [ "$RUNNER_OS" == "Windows" ]; then
|
|
||||||
mv dist/Khoj.exe dist/khoj_dev_amd64.exe
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: 💻 Create Mac App DMG
|
|
||||||
if: matrix.os == 'macos-latest'
|
|
||||||
run: |
|
|
||||||
# Install Mac DMG Creator
|
|
||||||
brew install create-dmg
|
|
||||||
# Copy app to separate dmg folder
|
|
||||||
mkdir -p dist/dmg && cp -r dist/Khoj.app dist/dmg
|
|
||||||
# Create disk image with the app
|
|
||||||
create-dmg \
|
|
||||||
--volname "Khoj" \
|
|
||||||
--volicon "src/khoj/interface/web/assets/icons/favicon.icns" \
|
|
||||||
--window-pos 200 120 \
|
|
||||||
--window-size 600 300 \
|
|
||||||
--icon-size 100 \
|
|
||||||
--icon "Khoj.app" 175 120 \
|
|
||||||
--hide-extension "Khoj.app" \
|
|
||||||
--app-drop-link 425 120 \
|
|
||||||
"dist/khoj_dev_amd64.dmg" \
|
|
||||||
"dist/dmg/"
|
|
||||||
|
|
||||||
- uses: ruby/setup-ruby@v1
|
|
||||||
if: matrix.os == 'ubuntu-20.04'
|
|
||||||
with:
|
|
||||||
ruby-version: '3.0'
|
|
||||||
|
|
||||||
- name: 🐧 Create Debian Package
|
|
||||||
if: matrix.os == 'ubuntu-20.04'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
# Install Debian Packager
|
|
||||||
gem install fpm
|
|
||||||
|
|
||||||
# Copy app files into expected output directory structure
|
|
||||||
mkdir -p package/opt package/usr/share/applications package/usr/share/icons/hicolor/128x128/apps
|
|
||||||
cp -r dist/Khoj package/opt/Khoj
|
|
||||||
cp src/khoj/interface/web/assets/icons/favicon-128x128.png package/usr/share/icons/hicolor/128x128/apps/Khoj.png
|
|
||||||
cp Khoj.desktop package/usr/share/applications
|
|
||||||
|
|
||||||
# Fix permissions to be usable by non-root users
|
|
||||||
find package/usr/share -type f -exec chmod 644 -- {} +
|
|
||||||
chmod 755 package/opt/Khoj
|
|
||||||
|
|
||||||
# Package the app
|
|
||||||
fpm -C package -s dir -t deb -n Khoj -p dist/khoj_dev_amd64.deb
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: khoj_dev_amd64.${{matrix.extension}}
|
|
||||||
path: dist/khoj_dev_amd64.${{matrix.extension}}
|
|
||||||
retention-days: 3
|
|
108
.github/workflows/release.yml
vendored
108
.github/workflows/release.yml
vendored
|
@ -64,111 +64,3 @@ jobs:
|
||||||
src/interface/obsidian/main.js
|
src/interface/obsidian/main.js
|
||||||
src/interface/obsidian/manifest.json
|
src/interface/obsidian/manifest.json
|
||||||
src/interface/obsidian/styles.css
|
src/interface/obsidian/styles.css
|
||||||
|
|
||||||
publish_desktop_apps:
|
|
||||||
name: 🖥️ Publish Desktop Apps
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-20.04
|
|
||||||
extension: deb
|
|
||||||
- os: macos-latest
|
|
||||||
extension: dmg
|
|
||||||
- os: windows-latest
|
|
||||||
extension: exe
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Set up Python 3.9
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
|
||||||
python-version: '3.9'
|
|
||||||
|
|
||||||
- name: ⏬️ Install Dependencies
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
if [ "$RUNNER_OS" == "Linux" ]; then
|
|
||||||
sudo apt update && sudo apt install libegl1 libxcb-xinerama0 python3-tk -y
|
|
||||||
fi
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install pyinstaller
|
|
||||||
|
|
||||||
- name: ⬇️ Install Khoj App
|
|
||||||
run: |
|
|
||||||
pip install --upgrade .
|
|
||||||
|
|
||||||
- name: 📦 Package Khoj App
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
# Setup Environment for Reproducible Builds
|
|
||||||
export PYTHONHASHSEED=42
|
|
||||||
export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
|
|
||||||
|
|
||||||
pyinstaller --noconfirm Khoj.spec
|
|
||||||
if [ "$RUNNER_OS" == "Windows" ]; then
|
|
||||||
mv dist/Khoj.exe dist/khoj_"$GITHUB_REF_NAME"_amd64.exe
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: 💻 Create Mac App DMG
|
|
||||||
if: matrix.os == 'macos-latest'
|
|
||||||
run: |
|
|
||||||
# Install Mac DMG Creator
|
|
||||||
brew install create-dmg
|
|
||||||
# Copy app to separate dmg folder
|
|
||||||
mkdir -p dist/dmg && cp -r dist/Khoj.app dist/dmg
|
|
||||||
# Create disk image with the app
|
|
||||||
create-dmg \
|
|
||||||
--volname "Khoj" \
|
|
||||||
--volicon "src/khoj/interface/web/assets/icons/favicon.icns" \
|
|
||||||
--window-pos 200 120 \
|
|
||||||
--window-size 600 300 \
|
|
||||||
--icon-size 100 \
|
|
||||||
--icon "Khoj.app" 175 120 \
|
|
||||||
--hide-extension "Khoj.app" \
|
|
||||||
--app-drop-link 425 120 \
|
|
||||||
"dist/khoj_"$GITHUB_REF_NAME"_amd64.dmg" \
|
|
||||||
"dist/dmg/"
|
|
||||||
|
|
||||||
- uses: ruby/setup-ruby@v1
|
|
||||||
if: matrix.os == 'ubuntu-20.04'
|
|
||||||
with:
|
|
||||||
ruby-version: '3.0'
|
|
||||||
- name: 🐧 Create Debian Package
|
|
||||||
if: matrix.os == 'ubuntu-20.04'
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
DEBIAN_PACKAGE_VERSION: ${{ inputs.version }}
|
|
||||||
run: |
|
|
||||||
# Install Debian Packager
|
|
||||||
gem install fpm
|
|
||||||
|
|
||||||
# Copy app files into expected output directory structure
|
|
||||||
mkdir -p package/opt package/usr/share/applications package/usr/share/icons/hicolor/128x128/apps
|
|
||||||
cp -r dist/Khoj package/opt/Khoj
|
|
||||||
cp src/khoj/interface/web/assets/icons/favicon-128x128.png package/usr/share/icons/hicolor/128x128/apps/Khoj.png
|
|
||||||
cp Khoj.desktop package/usr/share/applications
|
|
||||||
|
|
||||||
# Fix permissions to be usable by non-root users
|
|
||||||
find package/usr/share -type f -exec chmod 644 -- {} +
|
|
||||||
chmod 755 package/opt/Khoj
|
|
||||||
|
|
||||||
# Package the app
|
|
||||||
if [ -z "$DEBIAN_PACKAGE_VERSION" ]; then
|
|
||||||
DEBIAN_PACKAGE_VERSION=$(echo $GITHUB_REF_NAME | sed -E 's/v(.*)/\1/g')
|
|
||||||
fi
|
|
||||||
fpm -C package -s dir -t deb -n Khoj --version $DEBIAN_PACKAGE_VERSION -p dist/khoj_"$GITHUB_REF_NAME"_amd64.deb
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: khoj_${{github.ref_name}}_amd64.${{matrix.extension}}
|
|
||||||
path: dist/khoj_${{github.ref_name}}_amd64.${{matrix.extension}}
|
|
||||||
|
|
||||||
- name: 🌈 Release
|
|
||||||
uses: softprops/action-gh-release@v1
|
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
|
||||||
with:
|
|
||||||
generate_release_notes: true
|
|
||||||
files: dist/khoj_${{github.ref_name}}_amd64.${{matrix.extension}}
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
[Desktop Entry]
|
|
||||||
Type=Application
|
|
||||||
Name=Khoj
|
|
||||||
Comment=An AI personal assistant for your Digital Brain
|
|
||||||
Path=/opt
|
|
||||||
Exec=/opt/Khoj
|
|
||||||
Icon=Khoj
|
|
123
Khoj.spec
123
Khoj.spec
|
@ -1,123 +0,0 @@
|
||||||
# -*- mode: python ; coding: utf-8 -*-
|
|
||||||
from os.path import join
|
|
||||||
from platform import system
|
|
||||||
from PyInstaller.utils.hooks import copy_metadata
|
|
||||||
import sysconfig
|
|
||||||
|
|
||||||
datas = [
|
|
||||||
('src/khoj/interface/web', 'khoj/interface/web'),
|
|
||||||
(f'{sysconfig.get_paths()["purelib"]}/transformers', 'transformers'),
|
|
||||||
(f'{sysconfig.get_paths()["purelib"]}/langchain', 'langchain'),
|
|
||||||
(f'{sysconfig.get_paths()["purelib"]}/PIL', 'PIL'),
|
|
||||||
(f'{sysconfig.get_paths()["purelib"]}/gpt4all', 'gpt4all'),
|
|
||||||
]
|
|
||||||
datas += copy_metadata('torch')
|
|
||||||
datas += copy_metadata('tqdm')
|
|
||||||
datas += copy_metadata('regex')
|
|
||||||
datas += copy_metadata('requests')
|
|
||||||
datas += copy_metadata('packaging')
|
|
||||||
datas += copy_metadata('filelock')
|
|
||||||
datas += copy_metadata('numpy')
|
|
||||||
datas += copy_metadata('tokenizers')
|
|
||||||
datas += copy_metadata('pillow')
|
|
||||||
datas += copy_metadata('huggingface_hub')
|
|
||||||
datas += copy_metadata('safetensors')
|
|
||||||
datas += copy_metadata('pyyaml')
|
|
||||||
|
|
||||||
block_cipher = None
|
|
||||||
|
|
||||||
a = Analysis(
|
|
||||||
['src/khoj/main.py'],
|
|
||||||
pathex=[],
|
|
||||||
binaries=[],
|
|
||||||
datas=datas,
|
|
||||||
hiddenimports=['huggingface_hub.repository', 'PIL', 'PIL._tkinter_finder', 'tiktoken_ext', 'tiktoken_ext.openai_public'],
|
|
||||||
hookspath=[],
|
|
||||||
hooksconfig={},
|
|
||||||
runtime_hooks=[],
|
|
||||||
excludes=[],
|
|
||||||
win_no_prefer_redirects=False,
|
|
||||||
win_private_assemblies=False,
|
|
||||||
cipher=block_cipher,
|
|
||||||
noarchive=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Filter out unused and/or duplicate shared libs
|
|
||||||
torch_lib_paths = {
|
|
||||||
join('torch', 'lib', 'libtorch_cuda.so'),
|
|
||||||
join('torch', 'lib', 'libtorch_cpu.so'),
|
|
||||||
}
|
|
||||||
a.datas = [entry for entry in a.datas if not entry[0] in torch_lib_paths]
|
|
||||||
|
|
||||||
os_path_separator = '\\' if system() == 'Windows' else '/'
|
|
||||||
a.datas = [entry for entry in a.datas if not f'torch{os_path_separator}_C.cp' in entry[0]]
|
|
||||||
a.datas = [entry for entry in a.datas if not f'torch{os_path_separator}_dl.cp' in entry[0]]
|
|
||||||
|
|
||||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
|
||||||
|
|
||||||
if system() != 'Darwin':
|
|
||||||
# Add Splash screen to show on app launch
|
|
||||||
splash = Splash(
|
|
||||||
'src/khoj/interface/web/assets/icons/favicon-128x128.png',
|
|
||||||
binaries=a.binaries,
|
|
||||||
datas=a.datas,
|
|
||||||
text_pos=(10, 160),
|
|
||||||
text_size=12,
|
|
||||||
text_color='black',
|
|
||||||
minify_script=True,
|
|
||||||
always_on_top=True
|
|
||||||
)
|
|
||||||
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
a.binaries,
|
|
||||||
a.zipfiles,
|
|
||||||
a.datas,
|
|
||||||
splash,
|
|
||||||
splash.binaries,
|
|
||||||
[],
|
|
||||||
name='Khoj',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
|
||||||
runtime_tmpdir=None,
|
|
||||||
console=False,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch='x86_64',
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
icon='src/khoj/interface/web/assets/icons/favicon-128x128.ico',
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
exe = EXE(
|
|
||||||
pyz,
|
|
||||||
a.scripts,
|
|
||||||
a.binaries,
|
|
||||||
a.zipfiles,
|
|
||||||
a.datas,
|
|
||||||
[],
|
|
||||||
name='Khoj',
|
|
||||||
debug=False,
|
|
||||||
bootloader_ignore_signals=False,
|
|
||||||
strip=False,
|
|
||||||
upx=True,
|
|
||||||
upx_exclude=[],
|
|
||||||
runtime_tmpdir=None,
|
|
||||||
console=False,
|
|
||||||
disable_windowed_traceback=False,
|
|
||||||
argv_emulation=False,
|
|
||||||
target_arch='x86_64',
|
|
||||||
codesign_identity=None,
|
|
||||||
entitlements_file=None,
|
|
||||||
icon='src/khoj/interface/web/assets/icons/favicon.icns',
|
|
||||||
)
|
|
||||||
app = BUNDLE(
|
|
||||||
exe,
|
|
||||||
name='Khoj.app',
|
|
||||||
icon='src/khoj/interface/web/assets/icons/favicon.icns',
|
|
||||||
bundle_identifier=None,
|
|
||||||
)
|
|
|
@ -27,7 +27,7 @@ For more detailed Windows installation and troubleshooting, see [Windows Install
|
||||||
Run the following command from your terminal to start the Khoj backend and open Khoj in your browser.
|
Run the following command from your terminal to start the Khoj backend and open Khoj in your browser.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
khoj --gui
|
khoj
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: To start Khoj automatically in the background use [Task scheduler](https://www.windowscentral.com/how-create-automated-task-using-task-scheduler-windows-10) on Windows or [Cron](https://en.wikipedia.org/wiki/Cron) on Mac, Linux (e.g with `@reboot khoj`)
|
Note: To start Khoj automatically in the background use [Task scheduler](https://www.windowscentral.com/how-create-automated-task-using-task-scheduler-windows-10) on Windows or [Cron](https://en.wikipedia.org/wiki/Cron) on Mac, Linux (e.g with `@reboot khoj`)
|
||||||
|
@ -73,6 +73,9 @@ pip install --upgrade --pre khoj-assistant
|
||||||
## Uninstall
|
## Uninstall
|
||||||
1. (Optional) Hit `Ctrl-C` in the terminal running the khoj server to stop it
|
1. (Optional) Hit `Ctrl-C` in the terminal running the khoj server to stop it
|
||||||
2. Delete the khoj directory in your home folder (i.e `~/.khoj` on Linux, Mac or `C:\Users\<your-username>\.khoj` on Windows)
|
2. Delete the khoj directory in your home folder (i.e `~/.khoj` on Linux, Mac or `C:\Users\<your-username>\.khoj` on Windows)
|
||||||
|
5. You might want to `rm -rf` the following directories:
|
||||||
|
- `~/.khoj`
|
||||||
|
- `~/.cache/gpt4all`
|
||||||
3. Uninstall the khoj server with `pip uninstall khoj-assistant`
|
3. Uninstall the khoj server with `pip uninstall khoj-assistant`
|
||||||
4. (Optional) Uninstall khoj.el or the khoj obsidian plugin in the standard way on Emacs, Obsidian
|
4. (Optional) Uninstall khoj.el or the khoj obsidian plugin in the standard way on Emacs, Obsidian
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"version": "0.11.4",
|
"version": "0.11.4",
|
||||||
"minAppVersion": "0.15.0",
|
"minAppVersion": "0.15.0",
|
||||||
"description": "An AI Personal Assistant for your Digital Brain",
|
"description": "An AI Personal Assistant for your Digital Brain",
|
||||||
"author": "Debanjum Singh Solanky",
|
"author": "Khoj",
|
||||||
"authorUrl": "https://github.com/debanjum",
|
"authorUrl": "https://github.com/khoj-ai/",
|
||||||
"isDesktopOnly": true
|
"isDesktopOnly": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,6 @@ dependencies = [
|
||||||
"tenacity >= 8.2.2",
|
"tenacity >= 8.2.2",
|
||||||
"pillow == 9.3.0",
|
"pillow == 9.3.0",
|
||||||
"pydantic >= 1.10.10",
|
"pydantic >= 1.10.10",
|
||||||
"pyside6 >= 6.5.1",
|
|
||||||
"pyyaml == 6.0",
|
"pyyaml == 6.0",
|
||||||
"rich >= 13.3.1",
|
"rich >= 13.3.1",
|
||||||
"schedule == 1.1.0",
|
"schedule == 1.1.0",
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
# Standard Packages
|
|
||||||
import webbrowser
|
|
||||||
import os
|
|
||||||
import signal
|
|
||||||
|
|
||||||
# External Packages
|
|
||||||
from PySide6 import QtGui, QtWidgets
|
|
||||||
from PySide6.QtCore import Qt
|
|
||||||
|
|
||||||
# Internal Packages
|
|
||||||
from khoj.utils import constants
|
|
||||||
from PySide6.QtCore import QThread
|
|
||||||
|
|
||||||
|
|
||||||
class ServerThread(QThread):
|
|
||||||
def __init__(self, start_server_func, parent=None):
|
|
||||||
super(ServerThread, self).__init__(parent)
|
|
||||||
self.start_server_func = start_server_func
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.wait()
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.start_server_func()
|
|
||||||
|
|
||||||
def exit(self):
|
|
||||||
os.kill(os.getpid(), signal.SIGTERM)
|
|
||||||
super(ServerThread, self).exit()
|
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QtWidgets.QMainWindow):
|
|
||||||
"""Create Window to Navigate users to the web UI"""
|
|
||||||
|
|
||||||
def __init__(self, host: str, port: int):
|
|
||||||
super(MainWindow, self).__init__()
|
|
||||||
|
|
||||||
# Initialize Configure Window
|
|
||||||
self.setWindowTitle("Khoj")
|
|
||||||
|
|
||||||
# Set Window Icon
|
|
||||||
icon_path = constants.web_directory / "assets/icons/favicon-128x128.png"
|
|
||||||
self.setWindowIcon(QtGui.QIcon(f"{icon_path.absolute()}"))
|
|
||||||
|
|
||||||
# Initialize Configure Window Layout
|
|
||||||
self.wlayout = QtWidgets.QVBoxLayout()
|
|
||||||
|
|
||||||
# Add a Label that says "Khoj Configuration" to the Window
|
|
||||||
self.wlayout.addWidget(QtWidgets.QLabel("Welcome to Khoj"))
|
|
||||||
|
|
||||||
# Add a Button to open the Web UI at http://host:port/config
|
|
||||||
self.open_web_ui_button = QtWidgets.QPushButton("Open Web UI")
|
|
||||||
self.open_web_ui_button.clicked.connect(lambda: webbrowser.open(f"http://{host}:{port}/config"))
|
|
||||||
|
|
||||||
self.wlayout.addWidget(self.open_web_ui_button)
|
|
||||||
|
|
||||||
# Set the central widget of the Window. Widget will expand
|
|
||||||
# to take up all the space in the window by default.
|
|
||||||
self.config_window = QtWidgets.QWidget()
|
|
||||||
self.config_window.setLayout(self.wlayout)
|
|
||||||
self.setCentralWidget(self.config_window)
|
|
||||||
self.position_window()
|
|
||||||
|
|
||||||
def position_window(self):
|
|
||||||
"Position the window at center of X axis and near top on Y axis"
|
|
||||||
window_rectangle = self.geometry()
|
|
||||||
screen_center = self.screen().availableGeometry().center()
|
|
||||||
window_rectangle.moveCenter(screen_center)
|
|
||||||
self.move(window_rectangle.topLeft().x(), 25)
|
|
||||||
|
|
||||||
def show_on_top(self):
|
|
||||||
"Bring Window on Top"
|
|
||||||
self.show()
|
|
||||||
self.setWindowState(Qt.WindowState.WindowActive)
|
|
||||||
self.activateWindow() # For Bringing to Top on Windows
|
|
||||||
self.raise_() # For Bringing to Top from Minimized State on OSX
|
|
|
@ -1,43 +0,0 @@
|
||||||
# Standard Packages
|
|
||||||
import webbrowser
|
|
||||||
|
|
||||||
# External Packages
|
|
||||||
from PySide6 import QtGui, QtWidgets
|
|
||||||
|
|
||||||
# Internal Packages
|
|
||||||
from khoj.utils import constants, state
|
|
||||||
from khoj.interface.desktop.main_window import MainWindow
|
|
||||||
|
|
||||||
|
|
||||||
def create_system_tray(gui: QtWidgets.QApplication, main_window: MainWindow):
|
|
||||||
"""Create System Tray with Menu. Menu contain options to
|
|
||||||
1. Open Search Page on the Web Interface
|
|
||||||
2. Open App Configuration Screen
|
|
||||||
3. Quit Application
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Create the system tray with icon
|
|
||||||
icon_path = constants.web_directory / "assets/icons/favicon-128x128.png"
|
|
||||||
icon = QtGui.QIcon(f"{icon_path.absolute()}")
|
|
||||||
tray = QtWidgets.QSystemTrayIcon(icon)
|
|
||||||
tray.setVisible(True)
|
|
||||||
|
|
||||||
# Create the menu and menu actions
|
|
||||||
menu = QtWidgets.QMenu()
|
|
||||||
menu_actions = [
|
|
||||||
("Search", lambda: webbrowser.open(f"http://{state.host}:{state.port}/")),
|
|
||||||
("Configure", lambda: webbrowser.open(f"http://{state.host}:{state.port}/config")),
|
|
||||||
("App", main_window.show),
|
|
||||||
("Quit", gui.quit),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Add the menu actions to the menu
|
|
||||||
for action_text, action_function in menu_actions:
|
|
||||||
menu_action = QtGui.QAction(action_text, menu)
|
|
||||||
menu_action.triggered.connect(action_function) # type: ignore[attr-defined]
|
|
||||||
menu.addAction(menu_action)
|
|
||||||
|
|
||||||
# Add the menu to the system tray
|
|
||||||
tray.setContextMenu(menu)
|
|
||||||
|
|
||||||
return tray
|
|
|
@ -1,6 +1,5 @@
|
||||||
# Standard Packages
|
# Standard Packages
|
||||||
import os
|
import os
|
||||||
import signal
|
|
||||||
import sys
|
import sys
|
||||||
import locale
|
import locale
|
||||||
|
|
||||||
|
@ -12,8 +11,6 @@ if sys.stderr is None:
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
import warnings
|
import warnings
|
||||||
from platform import system
|
|
||||||
import webbrowser
|
|
||||||
from importlib.metadata import version
|
from importlib.metadata import version
|
||||||
|
|
||||||
# Ignore non-actionable warnings
|
# Ignore non-actionable warnings
|
||||||
|
@ -70,81 +67,13 @@ def run():
|
||||||
|
|
||||||
logger.info("🌘 Starting Khoj")
|
logger.info("🌘 Starting Khoj")
|
||||||
|
|
||||||
if not args.gui:
|
# Setup task scheduler
|
||||||
# Setup task scheduler
|
poll_task_scheduler()
|
||||||
poll_task_scheduler()
|
|
||||||
|
|
||||||
# Start Server
|
# Start Server
|
||||||
configure_routes(app)
|
configure_routes(app)
|
||||||
initialize_server(args.config, required=False)
|
initialize_server(args.config, required=False)
|
||||||
start_server(app, host=args.host, port=args.port, socket=args.socket)
|
start_server(app, host=args.host, port=args.port, socket=args.socket)
|
||||||
else:
|
|
||||||
from PySide6 import QtWidgets
|
|
||||||
from PySide6.QtCore import QTimer
|
|
||||||
|
|
||||||
from khoj.interface.desktop.main_window import MainWindow, ServerThread
|
|
||||||
from khoj.interface.desktop.system_tray import create_system_tray
|
|
||||||
|
|
||||||
# Setup GUI
|
|
||||||
gui = QtWidgets.QApplication([])
|
|
||||||
main_window = MainWindow(args.host, args.port)
|
|
||||||
|
|
||||||
# System tray is only available on Windows, MacOS.
|
|
||||||
# On Linux (Gnome) the System tray is not supported.
|
|
||||||
# Since only the Main Window is available
|
|
||||||
# Quitting it should quit the application
|
|
||||||
if system() in ["Windows", "Darwin"]:
|
|
||||||
gui.setQuitOnLastWindowClosed(False)
|
|
||||||
tray = create_system_tray(gui, main_window)
|
|
||||||
tray.show()
|
|
||||||
|
|
||||||
# Setup Server
|
|
||||||
initialize_server(args.config, required=False)
|
|
||||||
configure_routes(app)
|
|
||||||
server = ServerThread(start_server_func=lambda: start_server(app, host=args.host, port=args.port), parent=gui)
|
|
||||||
|
|
||||||
url = f"http://{args.host}:{args.port}"
|
|
||||||
logger.info(f"🌗 Khoj is running at {url}")
|
|
||||||
try:
|
|
||||||
startup_url = url if args.config else f"{url}/config"
|
|
||||||
webbrowser.open(startup_url)
|
|
||||||
except:
|
|
||||||
logger.warning(f"🚧 Unable to open browser. Please open {url} manually to configure or use Khoj.")
|
|
||||||
|
|
||||||
# Show Main Window on First Run Experience or if on Linux
|
|
||||||
if args.config is None or system() not in ["Windows", "Darwin"]:
|
|
||||||
main_window.show()
|
|
||||||
|
|
||||||
# Setup Signal Handlers
|
|
||||||
signal.signal(signal.SIGINT, sigint_handler)
|
|
||||||
# Invoke Python interpreter every 500ms to handle signals, run scheduled tasks
|
|
||||||
timer = QTimer()
|
|
||||||
timer.start(500)
|
|
||||||
timer.timeout.connect(schedule.run_pending)
|
|
||||||
|
|
||||||
# Start Application
|
|
||||||
server.start()
|
|
||||||
gui.aboutToQuit.connect(server.exit)
|
|
||||||
|
|
||||||
# Close Splash Screen if still open
|
|
||||||
if system() != "Darwin":
|
|
||||||
try:
|
|
||||||
import pyi_splash
|
|
||||||
|
|
||||||
# Update the text on the splash screen
|
|
||||||
pyi_splash.update_text("Khoj setup complete")
|
|
||||||
# Close Splash Screen
|
|
||||||
pyi_splash.close()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
gui.exec()
|
|
||||||
|
|
||||||
|
|
||||||
def sigint_handler(*args):
|
|
||||||
from PySide6 import QtWidgets
|
|
||||||
|
|
||||||
QtWidgets.QApplication.quit()
|
|
||||||
|
|
||||||
|
|
||||||
def set_state(args):
|
def set_state(args):
|
||||||
|
@ -171,12 +100,3 @@ def poll_task_scheduler():
|
||||||
timer_thread.daemon = True
|
timer_thread.daemon = True
|
||||||
timer_thread.start()
|
timer_thread.start()
|
||||||
schedule.run_pending()
|
schedule.run_pending()
|
||||||
|
|
||||||
|
|
||||||
def run_gui():
|
|
||||||
sys.argv += ["--gui"]
|
|
||||||
run()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
run_gui()
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ def cli(args=None):
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--config-file", "-c", default="~/.khoj/khoj.yml", type=pathlib.Path, help="YAML file to configure Khoj"
|
"--config-file", "-c", default="~/.khoj/khoj.yml", type=pathlib.Path, help="YAML file to configure Khoj"
|
||||||
)
|
)
|
||||||
parser.add_argument("--gui", action="store_true", default=False, help="Show native desktop GUI. Default: false")
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--regenerate",
|
"--regenerate",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
|
|
|
@ -106,11 +106,6 @@ def load_model(
|
||||||
return model
|
return model
|
||||||
|
|
||||||
|
|
||||||
def is_pyinstaller_app():
|
|
||||||
"Returns true if the app is running from Native GUI created by PyInstaller"
|
|
||||||
return getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS")
|
|
||||||
|
|
||||||
|
|
||||||
def get_class_by_name(name: str) -> object:
|
def get_class_by_name(name: str) -> object:
|
||||||
"Returns the class object from name string"
|
"Returns the class object from name string"
|
||||||
module_name, class_name = name.rsplit(".", 1)
|
module_name, class_name = name.rsplit(".", 1)
|
||||||
|
|
|
@ -16,7 +16,6 @@ def test_cli_minimal_default():
|
||||||
# Assert
|
# Assert
|
||||||
assert actual_args.config_file == resolve_absolute_path(Path("~/.khoj/khoj.yml"))
|
assert actual_args.config_file == resolve_absolute_path(Path("~/.khoj/khoj.yml"))
|
||||||
assert actual_args.regenerate == False
|
assert actual_args.regenerate == False
|
||||||
assert actual_args.gui == False
|
|
||||||
assert actual_args.verbose == 0
|
assert actual_args.verbose == 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,11 +35,10 @@ def test_cli_invalid_config_file_path():
|
||||||
# ----------------------------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------------------------
|
||||||
def test_cli_config_from_file():
|
def test_cli_config_from_file():
|
||||||
# Act
|
# Act
|
||||||
actual_args = cli(["-c=tests/data/config.yml", "--regenerate", "--gui", "-vvv"])
|
actual_args = cli(["-c=tests/data/config.yml", "--regenerate", "-vvv"])
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
assert actual_args.config_file == resolve_absolute_path(Path("tests/data/config.yml"))
|
assert actual_args.config_file == resolve_absolute_path(Path("tests/data/config.yml"))
|
||||||
assert actual_args.gui == True
|
|
||||||
assert actual_args.regenerate == True
|
assert actual_args.regenerate == True
|
||||||
assert actual_args.config is not None
|
assert actual_args.config is not None
|
||||||
assert actual_args.verbose == 3
|
assert actual_args.verbose == 3
|
||||||
|
|
Loading…
Reference in a new issue