Merge branch 'master' of github.com:khoj-ai/khoj into features/new-sign-in-page
|
@ -1,10 +1,11 @@
|
|||
.git/
|
||||
.pytest_cache/
|
||||
.vscode/
|
||||
.venv/
|
||||
docs/
|
||||
.*
|
||||
**/__pycache__/
|
||||
*.egg-info/
|
||||
documentation/
|
||||
tests/
|
||||
build/
|
||||
dist/
|
||||
scripts/
|
||||
*.egg-info/
|
||||
src/interface/
|
||||
src/telemetry/
|
||||
!src/interface/web
|
||||
|
|
74
.github/workflows/dockerize.yml
vendored
|
@ -38,13 +38,23 @@ env:
|
|||
jobs:
|
||||
build:
|
||||
name: Publish Khoj Docker Images
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
image:
|
||||
- 'local'
|
||||
- 'cloud'
|
||||
include:
|
||||
- image: 'local'
|
||||
platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
- image: 'local'
|
||||
platform: linux/arm64
|
||||
runner: ubuntu-linux-arm64
|
||||
- image: 'cloud'
|
||||
platform: linux/amd64
|
||||
runner: ubuntu-latest
|
||||
- image: 'cloud'
|
||||
platform: linux/arm64
|
||||
runner: ubuntu-linux-arm64
|
||||
runs-on: ${{ matrix.runner }}
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v3
|
||||
|
@ -52,9 +62,6 @@ jobs:
|
|||
# Get all history to correctly infer Khoj version using hatch
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 🧹 Delete huge unnecessary tools folder
|
||||
run: rm -rf /opt/hostedtoolcache
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
|
@ -73,31 +80,68 @@ jobs:
|
|||
run: rm -rf /opt/hostedtoolcache
|
||||
|
||||
- name: 📦 Build and Push Docker Image
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v4
|
||||
if: (matrix.image == 'local' && github.event_name == 'workflow_dispatch') && github.event.inputs.khoj == 'true' || (matrix.image == 'local' && github.event_name == 'push')
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile
|
||||
platforms: linux/amd64, linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository }}:${{ env.DOCKER_IMAGE_TAG }}
|
||||
${{ github.ref_type == 'tag' && format('ghcr.io/{0}:latest', github.repository) || '' }}
|
||||
ghcr.io/${{ github.repository }}:${{ env.DOCKER_IMAGE_TAG }}-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}
|
||||
build-args: |
|
||||
VERSION=${{ steps.hatch.outputs.version }}
|
||||
PORT=42110
|
||||
cache-from: type=gha,scope=${{ matrix.image }}-${{ matrix.platform }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.image }}-${{ matrix.platform}}
|
||||
labels: |
|
||||
org.opencontainers.image.description=Khoj AI - Your second brain powered by LLMs and Neural Search
|
||||
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
||||
|
||||
- name: 📦️⛅️ Build and Push Cloud Docker Image
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v4
|
||||
if: (matrix.image == 'cloud' && github.event_name == 'workflow_dispatch') && github.event.inputs.khoj-cloud == 'true' || (matrix.image == 'cloud' && github.event_name == 'push')
|
||||
with:
|
||||
context: .
|
||||
file: prod.Dockerfile
|
||||
platforms: linux/amd64, linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository }}-cloud:${{ env.DOCKER_IMAGE_TAG }}
|
||||
${{ github.ref_type == 'tag' && format('ghcr.io/{0}-cloud:latest', github.repository) || '' }}
|
||||
ghcr.io/${{ github.repository }}-cloud:${{ env.DOCKER_IMAGE_TAG }}-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}
|
||||
build-args: |
|
||||
VERSION=${{ steps.hatch.outputs.version }}
|
||||
PORT=42110
|
||||
cache-from: type=gha,scope=${{ matrix.image }}-${{ matrix.platform }}
|
||||
cache-to: type=gha,mode=max,scope=${{ matrix.image }}-${{ matrix.platform}}
|
||||
labels: |
|
||||
org.opencontainers.image.description=Khoj AI Cloud - Your second brain powered by LLMs and Neural Search
|
||||
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
||||
|
||||
manifest:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name != 'pull_request'
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.PAT }}
|
||||
|
||||
- name: Create and Push Local Manifest
|
||||
if: github.event.inputs.khoj == 'true' || github.event_name == 'push'
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t ghcr.io/${{ github.repository }}:${{ env.DOCKER_IMAGE_TAG }} \
|
||||
ghcr.io/${{ github.repository }}:${{ env.DOCKER_IMAGE_TAG }}-amd64 \
|
||||
ghcr.io/${{ github.repository }}:${{ env.DOCKER_IMAGE_TAG }}-arm64
|
||||
|
||||
- name: Create and Push Cloud Manifest
|
||||
if: github.event.inputs.khoj-cloud == 'true' || github.event_name == 'push'
|
||||
run: |
|
||||
docker buildx imagetools create \
|
||||
-t ghcr.io/${{ github.repository }}-cloud:${{ env.DOCKER_IMAGE_TAG }} \
|
||||
ghcr.io/${{ github.repository }}-cloud:${{ env.DOCKER_IMAGE_TAG }}-amd64 \
|
||||
ghcr.io/${{ github.repository }}-cloud:${{ env.DOCKER_IMAGE_TAG }}-arm64
|
||||
|
|
17
.github/workflows/run_evals.yml
vendored
|
@ -25,6 +25,8 @@ on:
|
|||
options:
|
||||
- frames
|
||||
- simpleqa
|
||||
- gpqa
|
||||
- math500
|
||||
sample_size:
|
||||
description: 'Number of samples to evaluate'
|
||||
required: false
|
||||
|
@ -74,12 +76,12 @@ jobs:
|
|||
DEBIAN_FRONTEND: noninteractive
|
||||
run: |
|
||||
# install postgres and other dependencies
|
||||
apt update && apt install -y git python3-pip libegl1 sqlite3 libsqlite3-dev libsqlite3-0 ffmpeg libsm6 libxext6
|
||||
apt install -y postgresql postgresql-client && apt install -y postgresql-server-dev-14
|
||||
sudo apt update && sudo apt install -y git python3-pip libegl1 sqlite3 libsqlite3-dev libsqlite3-0 ffmpeg libsm6 libxext6
|
||||
sudo apt install -y postgresql postgresql-client && sudo apt install -y postgresql-server-dev-14
|
||||
# upgrade pip
|
||||
python -m ensurepip --upgrade && python -m pip install --upgrade pip
|
||||
# install terrarium for code sandbox
|
||||
git clone https://github.com/cohere-ai/cohere-terrarium.git && cd cohere-terrarium && npm install && mkdir pyodide_cache
|
||||
git clone https://github.com/khoj-ai/terrarium.git && cd terrarium && npm install --legacy-peer-deps && mkdir pyodide_cache
|
||||
|
||||
- name: ⬇️ Install Application
|
||||
run: |
|
||||
|
@ -89,14 +91,15 @@ jobs:
|
|||
- name: 📝 Run Eval
|
||||
env:
|
||||
KHOJ_MODE: ${{ matrix.khoj_mode }}
|
||||
SAMPLE_SIZE: ${{ inputs.sample_size }}
|
||||
SAMPLE_SIZE: ${{ github.event_name == 'workflow_dispatch' && inputs.sample_size || 200 }}
|
||||
BATCH_SIZE: "20"
|
||||
RANDOMIZE: "True"
|
||||
KHOJ_URL: "http://localhost:42110"
|
||||
KHOJ_LLM_SEED: "42"
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
SERPER_DEV_API_KEY: ${{ secrets.SERPER_DEV_API_KEY }}
|
||||
OLOSTEP_API_KEY: ${{ secrets.OLOSTEP_API_KEY }}
|
||||
SERPER_DEV_API_KEY: ${{ matrix.dataset != 'math500' && secrets.SERPER_DEV_API_KEY }}
|
||||
OLOSTEP_API_KEY: ${{ matrix.dataset != 'math500' && secrets.OLOSTEP_API_KEY }}
|
||||
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
||||
KHOJ_ADMIN_EMAIL: khoj
|
||||
KHOJ_ADMIN_PASSWORD: khoj
|
||||
POSTGRES_HOST: localhost
|
||||
|
@ -110,7 +113,7 @@ jobs:
|
|||
khoj --anonymous-mode --non-interactive &
|
||||
|
||||
# Start code sandbox
|
||||
npm run dev --prefix cohere-terrarium &
|
||||
npm run dev --prefix terrarium &
|
||||
|
||||
# Wait for server to be ready
|
||||
timeout=120
|
||||
|
|
11
.github/workflows/test.yml
vendored
|
@ -55,15 +55,13 @@ jobs:
|
|||
with:
|
||||
python-version: ${{ matrix.python_version }}
|
||||
|
||||
- name: Install Git
|
||||
run: |
|
||||
apt update && apt install -y git
|
||||
|
||||
- name: ⏬️ Install Dependencies
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
run: |
|
||||
apt update && apt install -y libegl1 sqlite3 libsqlite3-dev libsqlite3-0 ffmpeg libsm6 libxext6
|
||||
apt update && apt install -y git libegl1 sqlite3 libsqlite3-dev libsqlite3-0 ffmpeg libsm6 libxext6
|
||||
# required by llama-cpp-python prebuilt wheels
|
||||
apt install -y musl-dev && ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1
|
||||
|
||||
- name: ⬇️ Install Postgres
|
||||
env:
|
||||
|
@ -78,6 +76,9 @@ jobs:
|
|||
python -m pip install --upgrade pip
|
||||
|
||||
- name: ⬇️ Install Application
|
||||
env:
|
||||
PIP_EXTRA_INDEX_URL: "https://download.pytorch.org/whl/cpu https://abetlen.github.io/llama-cpp-python/whl/cpu"
|
||||
CUDA_VISIBLE_DEVICES: ""
|
||||
run: sed -i 's/dynamic = \["version"\]/version = "0.0.0"/' pyproject.toml && pip install --upgrade .[dev]
|
||||
|
||||
- name: 🧪 Test Application
|
||||
|
|
10
.gitignore
vendored
|
@ -42,3 +42,13 @@ src/interface/obsidian/main.js
|
|||
|
||||
# obsidian
|
||||
data.json
|
||||
|
||||
# Android
|
||||
src/interface/android/.gradle
|
||||
src/interface/android/app/build
|
||||
src/interface/android/build
|
||||
src/interface/android/*.aab
|
||||
src/interface/android/*.apk
|
||||
src/interface/android/*.apk.idsig
|
||||
src/interface/android/*.keystore
|
||||
src/interface/android/local.properties
|
||||
|
|
|
@ -26,7 +26,7 @@ repos:
|
|||
rev: v1.0.0
|
||||
hooks:
|
||||
- id: mypy
|
||||
stages: [push, manual]
|
||||
stages: [pre-push, manual]
|
||||
pass_filenames: false
|
||||
args:
|
||||
- --config-file=pyproject.toml
|
||||
|
|
49
Dockerfile
|
@ -1,5 +1,5 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
FROM ubuntu:jammy
|
||||
FROM ubuntu:jammy AS base
|
||||
LABEL homepage="https://khoj.dev"
|
||||
LABEL repository="https://github.com/khoj-ai/khoj"
|
||||
LABEL org.opencontainers.image.source="https://github.com/khoj-ai/khoj"
|
||||
|
@ -13,36 +13,51 @@ RUN apt update -y && apt -y install \
|
|||
# Required by RapidOCR
|
||||
libgl1 \
|
||||
libglx-mesa0 \
|
||||
libglib2.0-0 && \
|
||||
# Required by Next.js Web app
|
||||
curl -sL https://deb.nodesource.com/setup_20.x | bash - && \
|
||||
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
|
||||
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
|
||||
apt update -y && apt -y --no-install-recommends install nodejs yarn && \
|
||||
libglib2.0-0 \
|
||||
# Required by llama-cpp-python pre-built wheels. See #1628
|
||||
musl-dev && \
|
||||
ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 && \
|
||||
# Clean up
|
||||
apt clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Application
|
||||
# Build Server
|
||||
FROM base AS server-deps
|
||||
WORKDIR /app
|
||||
COPY pyproject.toml .
|
||||
COPY README.md .
|
||||
ARG VERSION=0.0.0
|
||||
# use the pre-built llama-cpp-python, torch cpu wheel
|
||||
ENV PIP_EXTRA_INDEX_URL="https://download.pytorch.org/whl/cpu https://abetlen.github.io/llama-cpp-python/whl/cpu"
|
||||
# avoid downloading unused cuda specific python packages
|
||||
ENV CUDA_VISIBLE_DEVICES=""
|
||||
RUN sed -i "s/dynamic = \\[\"version\"\\]/version = \"$VERSION\"/" pyproject.toml && \
|
||||
pip install --no-cache-dir .
|
||||
|
||||
# Copy Source Code
|
||||
COPY . .
|
||||
|
||||
# Set the PYTHONPATH environment variable in order for it to find the Django app.
|
||||
ENV PYTHONPATH=/app/src:$PYTHONPATH
|
||||
|
||||
# Go to the directory src/interface/web and export the built Next.js assets
|
||||
# Build Web App
|
||||
FROM node:20-alpine AS web-app
|
||||
# Set build optimization env vars
|
||||
ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
WORKDIR /app/src/interface/web
|
||||
RUN bash -c "yarn install --frozen-lockfile && yarn ciexport && yarn cache clean"
|
||||
# Install dependencies first (cache layer)
|
||||
COPY src/interface/web/package.json src/interface/web/yarn.lock ./
|
||||
RUN yarn install --frozen-lockfile
|
||||
# Copy source and build
|
||||
COPY src/interface/web/. ./
|
||||
RUN yarn build
|
||||
|
||||
# Merge the Server and Web App into a Single Image
|
||||
FROM base
|
||||
ENV PYTHONPATH=/app/src
|
||||
WORKDIR /app
|
||||
COPY --from=server-deps /usr/local/lib/python3.10/dist-packages /usr/local/lib/python3.10/dist-packages
|
||||
COPY --from=web-app /app/src/interface/web/out ./src/khoj/interface/built
|
||||
COPY . .
|
||||
RUN cd src && python3 khoj/manage.py collectstatic --noinput
|
||||
|
||||
# Run the Application
|
||||
# There are more arguments required for the application to run,
|
||||
# but these should be passed in through the docker-compose.yml file.
|
||||
# but those should be passed in through the docker-compose.yml file.
|
||||
ARG PORT
|
||||
EXPOSE ${PORT}
|
||||
ENTRYPOINT ["python3", "src/khoj/main.py"]
|
||||
|
|
11
README.md
|
@ -29,10 +29,17 @@
|
|||
|
||||
</div>
|
||||
|
||||
<div align="left">
|
||||
***
|
||||
|
||||
### 🎁 New
|
||||
* Start any message with `/research` to try out the experimental research mode with Khoj.
|
||||
* Anyone can now [create custom agents](https://blog.khoj.dev/posts/create-agents-on-khoj/) with tunable personality, tools and knowledge bases.
|
||||
* [Read](https://blog.khoj.dev/posts/evaluate-khoj-quality/) about Khoj's excellent performance on modern retrieval and reasoning benchmarks.
|
||||
|
||||
***
|
||||
|
||||
## Overview
|
||||
|
||||
[Khoj](https://khoj.dev) is a personal AI app to extend your capabilities. It smoothly scales up from an on-device personal AI to a cloud-scale enterprise AI.
|
||||
|
||||
- Chat with any local or online LLM (e.g llama3, qwen, gemma, mistral, gpt, claude, gemini).
|
||||
|
@ -47,8 +54,6 @@
|
|||
|
||||
***
|
||||
|
||||
</div>
|
||||
|
||||
## See it in action
|
||||
|
||||
![demo_chat](https://github.com/khoj-ai/khoj/blob/master/documentation/assets/img/quadratic_equation_khoj_web.gif?raw=true)
|
||||
|
|
|
@ -22,9 +22,9 @@ services:
|
|||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
# Use the following line to use the latest version of khoj. Otherwise, it will build from source.
|
||||
# Use the following line to use the latest version of khoj. Otherwise, it will build from source. Set this to ghcr.io/khoj-ai/khoj-cloud if you want to use the prod image.
|
||||
image: ghcr.io/khoj-ai/khoj:latest
|
||||
# Uncomment the following line to build from source. This will take a few minutes. Comment the next two lines out if you want to use the offiicial image.
|
||||
# Uncomment the following line to build from source. This will take a few minutes. Comment the next two lines out if you want to use the official image.
|
||||
# build:
|
||||
# context: .
|
||||
ports:
|
||||
|
@ -49,6 +49,8 @@ services:
|
|||
- KHOJ_DEBUG=False
|
||||
- KHOJ_ADMIN_EMAIL=username@example.com
|
||||
- KHOJ_ADMIN_PASSWORD=password
|
||||
# Default URL of Terrarium, the Python sandbox used by Khoj to run code. Its container is specified above
|
||||
- KHOJ_TERRARIUM_URL=http://host.docker.internal:8080
|
||||
# Uncomment line below to use with Ollama running on your local machine at localhost:11434.
|
||||
# Change URL to use with other OpenAI API compatible providers like VLLM, LMStudio etc.
|
||||
# - OPENAI_API_BASE=http://host.docker.internal:11434/v1/
|
||||
|
@ -64,7 +66,7 @@ services:
|
|||
# Ensure you set your provider specific API keys.
|
||||
# ---
|
||||
# Free, Slower API. Does both web search and webpage read. Get API key from https://jina.ai/
|
||||
# - JINA_API_KEY=you_jina_api_key
|
||||
# - JINA_API_KEY=your_jina_api_key
|
||||
# Paid, Fast API. Only does web search. Get API key from https://serper.dev/
|
||||
# - SERPER_DEV_API_KEY=your_serper_dev_api_key
|
||||
# Paid, Fast, Open API. Only does webpage read. Get API key from https://firecrawl.dev/
|
||||
|
@ -83,6 +85,7 @@ services:
|
|||
# Telemetry helps us prioritize feature development and understand how people are using Khoj
|
||||
# Read more at https://docs.khoj.dev/miscellaneous/telemetry
|
||||
# - KHOJ_TELEMETRY_DISABLE=True
|
||||
# Comment out this line when you're using the official ghcr.io/khoj-ai/khoj-cloud prod image.
|
||||
command: --host="0.0.0.0" --port=42110 -vv --anonymous-mode --non-interactive
|
||||
|
||||
|
||||
|
|
BIN
documentation/assets/img/chrome_pwa_alt.png
Normal file
After Width: | Height: | Size: 500 KiB |
Before Width: | Height: | Size: 298 KiB |
Before Width: | Height: | Size: 333 KiB |
Before Width: | Height: | Size: 8.4 KiB |
BIN
documentation/assets/img/pwa_install_desktop.png
Normal file
After Width: | Height: | Size: 15 KiB |
|
@ -38,14 +38,13 @@ To add a server chat setting:
|
|||
- The `Advanced` field doesn't need to be set when self-hosting. When unset, the `Default` chat model is used for all users and the intermediate steps.
|
||||
|
||||
|
||||
### OpenAI Processor Conversation Configs
|
||||
These settings configure chat model providers to be accessed over API.
|
||||
The name of this setting is kind of a misnomer, we know, it'll hopefully be changed at some point.
|
||||
For each chat model provider you [add](http://localhost:42110/server/admin/database/openaiprocessorconversationconfig/add):
|
||||
### AI Model API
|
||||
These settings configure APIs to interact with AI models.
|
||||
For each AI Model API you [add](http://localhost:42110/server/admin/database/aimodelapi/add):
|
||||
- `Api key`: Set to your [OpenAI](https://platform.openai.com/api-keys), [Anthropic](https://console.anthropic.com/account/keys) or [Gemini](https://aistudio.google.com/app/apikey) API keys.
|
||||
- `Name`: Give the configuration any friendly name like `OpenAI`, `Gemini`, `Anthropic`.
|
||||
- `Api base url`: Set the API base URL. This is only relevant to set if you're using another OpenAI-compatible proxy server like [Ollama](/advanced/ollama) or [LMStudio](/advanced/lmstudio).
|
||||
![example configuration for openai processor](/img/example_openai_processor_config.png)
|
||||
![example configuration for ai model api](/img/example_openai_processor_config.png)
|
||||
|
||||
### Search Model Configs
|
||||
Search models are used to generate vector embeddings of your documents for natural language search and chat. You can choose any [embeddings models on HuggingFace](https://huggingface.co/models?pipeline_tag=sentence-similarity) to try, use for your to create vector embeddings of your documents for natural language search and chat.
|
||||
|
|
|
@ -21,7 +21,7 @@ Using LiteLLM with Khoj makes it possible to turn any LLM behind an API into you
|
|||
export MISTRAL_API_KEY=<MISTRAL_API_KEY>
|
||||
litellm --model mistral/mistral-tiny --drop_params
|
||||
```
|
||||
3. Create a new [OpenAI Processor Conversation Config](http://localhost:42110/server/admin/database/openaiprocessorconversationconfig/add) on your Khoj admin panel
|
||||
3. Create a new [API Model API](http://localhost:42110/server/admin/database/aimodelapi/add) on your Khoj admin panel
|
||||
- Name: `proxy-name`
|
||||
- Api Key: `any string`
|
||||
- Api Base Url: **URL of your Openai Proxy API**
|
||||
|
|
|
@ -60,7 +60,7 @@ Restart your Khoj server after first run or update to the settings below to ensu
|
|||
```bash
|
||||
ollama pull llama3.1
|
||||
```
|
||||
3. Create a new [OpenAI Processor Conversation Config](http://localhost:42110/server/admin/database/openaiprocessorconversationconfig/add) on your Khoj admin panel
|
||||
3. Create a new [AI Model API](http://localhost:42110/server/admin/database/aimodelapi/add) on your Khoj admin panel
|
||||
- Name: `ollama`
|
||||
- Api Key: `any string`
|
||||
- Api Base Url: `http://localhost:11434/v1/` (default for Ollama)
|
||||
|
|
|
@ -11,7 +11,7 @@ This is only helpful for self-hosted users. If you're using [Khoj Cloud](https:/
|
|||
Khoj natively supports local LLMs [available on HuggingFace in GGUF format](https://huggingface.co/models?library=gguf). Using an OpenAI API proxy with Khoj maybe useful for ease of setup, trying new models or using commercial LLMs via API.
|
||||
:::
|
||||
|
||||
Khoj can use any OpenAI API compatible server including [Ollama](/advanced/ollama), [LMStudio](/advanced/lmstudio) and [LiteLLM](/advanced/litellm).
|
||||
Khoj can use any OpenAI API compatible server including local providers like [Ollama](/advanced/ollama), [LMStudio](/advanced/lmstudio) and [LiteLLM](/advanced/litellm) and commercial providers like [HuggingFace](https://huggingface.co/docs/api-inference/tasks/chat-completion#using-the-api), [OpenRouter](https://openrouter.ai/docs/quick-start) etc.
|
||||
Configuring this allows you to use non-standard, open or commercial, local or hosted LLM models for Khoj
|
||||
|
||||
Combine them with Khoj can turn your favorite LLM into an AI agent. Allowing you to chat with your docs, find answers from the internet, build custom agents and run automations.
|
||||
|
@ -20,8 +20,8 @@ For specific integrations, see our [Ollama](/advanced/ollama), [LMStudio](/advan
|
|||
|
||||
## General Setup
|
||||
|
||||
1. Start your preferred OpenAI API compatible app
|
||||
2. Create a new [OpenAI Processor Conversation Config](http://localhost:42110/server/admin/database/openaiprocessorconversationconfig/add) on your Khoj admin panel
|
||||
1. Start your preferred OpenAI API compatible app locally or get API keys from commercial AI model providers.
|
||||
3. Create a new [API Model API](http://localhost:42110/server/admin/database/aimodelapi/add) on your Khoj admin panel
|
||||
- Name: `any name`
|
||||
- Api Key: `any string`
|
||||
- Api Base Url: **URL of your Openai Proxy API**
|
||||
|
|
|
@ -4,22 +4,13 @@ sidebar_position: 1
|
|||
|
||||
# Desktop
|
||||
|
||||
> Query your Second Brain from your machine
|
||||
> Upload your knowledge base to Khoj and chat with your whole corpus
|
||||
|
||||
Use the Desktop app to chat and search with Khoj.
|
||||
You can also share your files, folders with Khoj using the app.
|
||||
## Companion App
|
||||
|
||||
Share your files, folders with Khoj using the app.
|
||||
Khoj will keep these files in sync to provide contextual responses when you search or chat.
|
||||
|
||||
## Features
|
||||
- **Chat**
|
||||
- **Faster answers**: Find answers quickly, from your private notes or the public internet
|
||||
- **Assisted creativity**: Smoothly weave across retrieving answers and generating content
|
||||
- **Iterative discovery**: Iteratively explore and re-discover your notes
|
||||
- **Quick access**: Use [Khoj Mini](/features/khoj_mini) on the desktop to quickly pull up a mini chat module for quicker answers
|
||||
- **Search**
|
||||
- **Natural**: Advanced natural language understanding using Transformer based ML Models
|
||||
- **Incremental**: Incremental search for a fast, search-as-you-type experience
|
||||
|
||||
## Setup
|
||||
:::info[Self Hosting]
|
||||
If you are self-hosting the Khoj server, update the *Settings* page on the Khoj Desktop app to:
|
||||
|
@ -34,7 +25,20 @@ If you are self-hosting the Khoj server, update the *Settings* page on the Khoj
|
|||
4. [Optional] Add any files, folders you'd like Khoj to be aware of on the *Settings* page and Click *Save*.
|
||||
These files and folders will be automatically kept in sync for you
|
||||
|
||||
## Interface
|
||||
| Chat | Search |
|
||||
|:----:|:------:|
|
||||
| ![](/img/khoj_chat_on_desktop.png) | ![](/img/khoj_search_on_desktop.png) |
|
||||
# Main App
|
||||
|
||||
You can also install the Khoj application on your desktop as a progressive web app.
|
||||
|
||||
1. Open the [Khoj Web App](https://app.khoj.dev) in Chrome.
|
||||
2. Click on the install button in the address bar to install the app. You must be logged into your Chrome browser for this to work.
|
||||
|
||||
![progressive web app install icon](/img/pwa_install_desktop.png)
|
||||
|
||||
Alternatively, you can also install using this route:
|
||||
|
||||
1. Open the three-dot menu in the top right corner of the browser.
|
||||
2. Go to 'Cast, Save, and Share' option.
|
||||
3. Click on the "Open in Khoj" option.
|
||||
|
||||
|
||||
![progressive web app install route](/img/chrome_pwa_alt.png)
|
||||
|
|
|
@ -114,7 +114,7 @@ This feature finds entries similar to the one you are currently on.
|
|||
2. Hit `C-c s f` (or `M-x khoj RET f`) to find similar entries
|
||||
|
||||
### Advanced Usage
|
||||
- Add [query filters](https://github.com/khoj-ai/khoj/#query-filters) during search to narrow down results further
|
||||
- Add [query filters](/miscellaneous/advanced#query-filters) during search to narrow down results further
|
||||
e.g. `What is the meaning of life? -"god" +"none" dt>"last week"`
|
||||
|
||||
- Use `C-c C-o 2` to open the current result at cursor in its source org file
|
||||
|
|
|
@ -302,11 +302,11 @@ Setup which chat model you'd want to use. Khoj supports local and online chat mo
|
|||
Using Ollama? See the [Ollama Integration](/advanced/ollama) section for more custom setup instructions.
|
||||
:::
|
||||
|
||||
1. Create a new [OpenAI processor conversation config](http://localhost:42110/server/admin/database/openaiprocessorconversationconfig/add) in the server admin settings. This is kind of a misnomer, we know.
|
||||
1. Create a new [AI Model Api](http://localhost:42110/server/admin/database/aimodelapi/add) in the server admin settings.
|
||||
- Add your [OpenAI API key](https://platform.openai.com/api-keys)
|
||||
- Give the configuration a friendly name like `OpenAI`
|
||||
- (Optional) Set the API base URL. It is only relevant if you're using another OpenAI-compatible proxy server like [Ollama](/advanced/ollama) or [LMStudio](/advanced/lmstudio).<br />
|
||||
![example configuration for openai processor](/img/example_openai_processor_config.png)
|
||||
![example configuration for ai model api](/img/example_openai_processor_config.png)
|
||||
2. Create a new [chat model options](http://localhost:42110/server/admin/database/chatmodeloptions/add)
|
||||
- Set the `chat-model` field to an [OpenAI chat model](https://platform.openai.com/docs/models). Example: `gpt-4o`.
|
||||
- Make sure to set the `model-type` field to `OpenAI`.
|
||||
|
@ -315,22 +315,22 @@ Using Ollama? See the [Ollama Integration](/advanced/ollama) section for more cu
|
|||
![example configuration for chat model options](/img/example_chatmodel_option.png)
|
||||
</TabItem>
|
||||
<TabItem value="anthropic" label="Anthropic">
|
||||
1. Create a new [OpenAI processor conversation config](http://localhost:42110/server/admin/database/openaiprocessorconversationconfig/add) in the server admin settings. This is kind of a misnomer, we know.
|
||||
1. Create a new [AI Model API](http://localhost:42110/server/admin/database/aimodelapi/add) in the server admin settings.
|
||||
- Add your [Anthropic API key](https://console.anthropic.com/account/keys)
|
||||
- Give the configuration a friendly name like `Anthropic`. Do not configure the API base url.
|
||||
2. Create a new [chat model options](http://localhost:42110/server/admin/database/chatmodeloptions/add)
|
||||
- Set the `chat-model` field to an [Anthropic chat model](https://docs.anthropic.com/en/docs/about-claude/models#model-names). Example: `claude-3-5-sonnet-20240620`.
|
||||
- Set the `model-type` field to `Anthropic`.
|
||||
- Set the `Openai config` field to the OpenAI processor conversation config for Anthropic you created in step 1.
|
||||
- Set the `ai model api` field to the Anthropic AI Model API you created in step 1.
|
||||
</TabItem>
|
||||
<TabItem value="gemini" label="Gemini">
|
||||
1. Create a new [OpenAI processor conversation config](http://localhost:42110/server/admin/database/openaiprocessorconversationconfig/add) in the server admin settings. This is kind of a misnomer, we know.
|
||||
1. Create a new [AI Model API](http://localhost:42110/server/admin/database/aimodelapi/add) in the server admin settings.
|
||||
- Add your [Gemini API key](https://aistudio.google.com/app/apikey)
|
||||
- Give the configuration a friendly name like `Gemini`. Do not configure the API base url.
|
||||
2. Create a new [chat model options](http://localhost:42110/server/admin/database/chatmodeloptions/add)
|
||||
- Set the `chat-model` field to a [Google Gemini chat model](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models#gemini-models). Example: `gemini-1.5-flash`.
|
||||
- Set the `model-type` field to `Gemini`.
|
||||
- Set the `Openai config` field to the OpenAI processor conversation config for Gemini you created in step 1.
|
||||
- Set the `ai model api` field to the Gemini AI Model API you created in step 1.
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="offline" label="Offline">
|
||||
|
|
|
@ -37,6 +37,9 @@ const config = {
|
|||
locales: ['en'],
|
||||
},
|
||||
|
||||
// Add a widget for Chatwoot for live chat if users need help
|
||||
clientModules: [require.resolve('./src/components/ChatwootWidget.js')],
|
||||
|
||||
presets: [
|
||||
[
|
||||
'classic',
|
||||
|
@ -69,7 +72,6 @@ const config = {
|
|||
}),
|
||||
],
|
||||
],
|
||||
|
||||
themeConfig:
|
||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||
({
|
||||
|
|
19
documentation/src/components/ChatwootWidget.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
|
||||
|
||||
// Only execute on client-side
|
||||
if (ExecutionEnvironment.canUseDOM) {
|
||||
(function (d, t) {
|
||||
var BASE_URL = "https://app.chatwoot.com";
|
||||
var g = d.createElement(t), s = d.getElementsByTagName(t)[0];
|
||||
g.src = BASE_URL + "/packs/js/sdk.js";
|
||||
g.defer = true;
|
||||
g.async = true;
|
||||
s.parentNode.insertBefore(g, s);
|
||||
g.onload = function () {
|
||||
window.chatwootSDK.run({
|
||||
websiteToken: 'cFxvnLSjfE2UF4UUiPCA5NsF',
|
||||
baseUrl: BASE_URL
|
||||
})
|
||||
}
|
||||
})(document, 'script');
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "khoj",
|
||||
"name": "Khoj",
|
||||
"version": "1.30.1",
|
||||
"version": "1.31.0",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "Your Second Brain",
|
||||
"author": "Khoj Inc.",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
FROM ubuntu:jammy
|
||||
FROM ubuntu:jammy AS base
|
||||
LABEL homepage="https://khoj.dev"
|
||||
LABEL repository="https://github.com/khoj-ai/khoj"
|
||||
LABEL org.opencontainers.image.source="https://github.com/khoj-ai/khoj"
|
||||
|
@ -13,36 +13,52 @@ RUN apt update -y && apt -y install \
|
|||
libsm6 \
|
||||
libxext6 \
|
||||
swig \
|
||||
curl && \
|
||||
# Required by Next.js Web app
|
||||
curl -sL https://deb.nodesource.com/setup_20.x | bash - && \
|
||||
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
|
||||
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
|
||||
apt update -y && apt -y --no-install-recommends install nodejs yarn && \
|
||||
curl \
|
||||
# Required by llama-cpp-python pre-built wheels. See #1628
|
||||
musl-dev && \
|
||||
ln -s /usr/lib/x86_64-linux-musl/libc.so /lib/libc.musl-x86_64.so.1 && \
|
||||
# Clean up
|
||||
apt clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Application
|
||||
# Build Server
|
||||
FROM base AS server-deps
|
||||
WORKDIR /app
|
||||
COPY pyproject.toml .
|
||||
COPY README.md .
|
||||
ARG VERSION=0.0.0
|
||||
# use the pre-built llama-cpp-python, torch cpu wheel
|
||||
ENV PIP_EXTRA_INDEX_URL="https://download.pytorch.org/whl/cpu https://abetlen.github.io/llama-cpp-python/whl/cpu"
|
||||
# avoid downloading unused cuda specific python packages
|
||||
ENV CUDA_VISIBLE_DEVICES=""
|
||||
RUN sed -i "s/dynamic = \\[\"version\"\\]/version = \"$VERSION\"/" pyproject.toml && \
|
||||
pip install --no-cache-dir -e .[prod]
|
||||
|
||||
# Copy Source Code
|
||||
COPY . .
|
||||
|
||||
# Set the PYTHONPATH environment variable in order for it to find the Django app.
|
||||
ENV PYTHONPATH=/app/src:$PYTHONPATH
|
||||
|
||||
# Go to the directory src/interface/web and export the built Next.js assets
|
||||
# Build Web App
|
||||
FROM node:20-alpine AS web-app
|
||||
# Set build optimization env vars
|
||||
ENV NODE_ENV=production
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
WORKDIR /app/src/interface/web
|
||||
RUN bash -c "yarn install --frozen-lockfile --verbose && yarn ciexport && yarn cache clean"
|
||||
# Install dependencies first (cache layer)
|
||||
COPY src/interface/web/package.json src/interface/web/yarn.lock ./
|
||||
RUN yarn install --frozen-lockfile
|
||||
# Copy source and build
|
||||
COPY src/interface/web/. ./
|
||||
RUN yarn build
|
||||
|
||||
# Merge the Server and Web App into a Single Image
|
||||
FROM base
|
||||
ENV PYTHONPATH=/app/src:$PYTHONPATH
|
||||
WORKDIR /app
|
||||
COPY --from=server-deps /usr/local/lib/python3.10/dist-packages /usr/local/lib/python3.10/dist-packages
|
||||
COPY --from=server-deps /usr/local/bin /usr/local/bin
|
||||
COPY --from=web-app /app/src/interface/web/out ./src/khoj/interface/built
|
||||
COPY . .
|
||||
RUN cd src && python3 khoj/manage.py collectstatic --noinput
|
||||
|
||||
# Run the Application
|
||||
# There are more arguments required for the application to run,
|
||||
# but these should be passed in through the docker-compose.yml file.
|
||||
# but those should be passed in through the docker-compose.yml file.
|
||||
ARG PORT
|
||||
EXPOSE ${PORT}
|
||||
ENTRYPOINT ["gunicorn", "-c", "gunicorn-config.py", "src.khoj.main:app"]
|
||||
|
|
|
@ -63,7 +63,8 @@ dependencies = [
|
|||
"tenacity == 8.3.0",
|
||||
"anyio == 3.7.1",
|
||||
"pymupdf == 1.24.11",
|
||||
"django == 5.0.9",
|
||||
"django == 5.0.10",
|
||||
"django-unfold == 0.42.0",
|
||||
"authlib == 1.2.1",
|
||||
"llama-cpp-python == 0.2.88",
|
||||
"itsdangerous == 2.1.2",
|
||||
|
@ -88,6 +89,7 @@ dependencies = [
|
|||
"anthropic == 0.26.1",
|
||||
"docx2txt == 0.8",
|
||||
"google-generativeai == 0.8.3",
|
||||
"pyjson5 == 1.6.7",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
|
|
211
src/interface/android/app/build.gradle
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright 2019 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import groovy.xml.MarkupBuilder
|
||||
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
}
|
||||
|
||||
def twaManifest = [
|
||||
applicationId: 'dev.khoj.app',
|
||||
hostName: 'app.khoj.dev', // The domain being opened in the TWA.
|
||||
launchUrl: '/', // The start path for the TWA. Must be relative to the domain.
|
||||
name: 'Khoj AI', // The application name.
|
||||
launcherName: 'Khoj', // The name shown on the Android Launcher.
|
||||
themeColor: '#FFFFFF', // The color used for the status bar.
|
||||
themeColorDark: '#000000', // The color used for the dark status bar.
|
||||
navigationColor: '#000000', // The color used for the navigation bar.
|
||||
navigationColorDark: '#000000', // The color used for the dark navbar.
|
||||
navigationDividerColor: '#000000', // The navbar divider color.
|
||||
navigationDividerColorDark: '#000000', // The dark navbar divider color.
|
||||
backgroundColor: '#FFFFFF', // The color used for the splash screen background.
|
||||
enableNotifications: true, // Set to true to enable notification delegation.
|
||||
// Every shortcut must include the following fields:
|
||||
// - name: String that will show up in the shortcut.
|
||||
// - short_name: Shorter string used if |name| is too long.
|
||||
// - url: Absolute path of the URL to launch the app with (e.g '/create').
|
||||
// - icon: Name of the resource in the drawable folder to use as an icon.
|
||||
shortcuts: [],
|
||||
// The duration of fade out animation in milliseconds to be played when removing splash screen.
|
||||
splashScreenFadeOutDuration: 300,
|
||||
generatorApp: 'bubblewrap-cli', // Application that generated the Android Project
|
||||
// The fallback strategy for when Trusted Web Activity is not available. Possible values are
|
||||
// 'customtabs' and 'webview'.
|
||||
fallbackType: 'customtabs',
|
||||
enableSiteSettingsShortcut: 'true',
|
||||
orientation: 'natural',
|
||||
]
|
||||
|
||||
android {
|
||||
compileSdkVersion 35
|
||||
namespace "dev.khoj.app"
|
||||
defaultConfig {
|
||||
applicationId "dev.khoj.app"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 35
|
||||
versionCode 4
|
||||
versionName "4"
|
||||
|
||||
// The name for the application
|
||||
resValue "string", "appName", twaManifest.name
|
||||
|
||||
// The name for the application on the Android Launcher
|
||||
resValue "string", "launcherName", twaManifest.launcherName
|
||||
|
||||
// The URL that will be used when launching the TWA from the Android Launcher
|
||||
def launchUrl = "https://" + twaManifest.hostName + twaManifest.launchUrl
|
||||
resValue "string", "launchUrl", launchUrl
|
||||
|
||||
|
||||
// The URL the Web Manifest for the Progressive Web App that the TWA points to. This
|
||||
// is used by Chrome OS and Meta Quest to open the Web version of the PWA instead of
|
||||
// the TWA, as it will probably give a better user experience for non-mobile devices.
|
||||
resValue "string", "webManifestUrl", 'https://app.khoj.dev/static/khoj.webmanifest'
|
||||
|
||||
|
||||
|
||||
// This is used by Meta Quest.
|
||||
resValue "string", "fullScopeUrl", 'https://app.khoj.dev/'
|
||||
|
||||
|
||||
|
||||
|
||||
// The hostname is used when building the intent-filter, so the TWA is able to
|
||||
// handle Intents to open host url of the application.
|
||||
resValue "string", "hostName", twaManifest.hostName
|
||||
|
||||
// This attribute sets the status bar color for the TWA. It can be either set here or in
|
||||
// `res/values/colors.xml`. Setting in both places is an error and the app will not
|
||||
// compile. If not set, the status bar color defaults to #FFFFFF - white.
|
||||
resValue "color", "colorPrimary", twaManifest.themeColor
|
||||
|
||||
// This attribute sets the dark status bar color for the TWA. It can be either set here or in
|
||||
// `res/values/colors.xml`. Setting in both places is an error and the app will not
|
||||
// compile. If not set, the status bar color defaults to #000000 - white.
|
||||
resValue "color", "colorPrimaryDark", twaManifest.themeColorDark
|
||||
|
||||
// This attribute sets the navigation bar color for the TWA. It can be either set here or
|
||||
// in `res/values/colors.xml`. Setting in both places is an error and the app will not
|
||||
// compile. If not set, the navigation bar color defaults to #FFFFFF - white.
|
||||
resValue "color", "navigationColor", twaManifest.navigationColor
|
||||
|
||||
// This attribute sets the dark navigation bar color for the TWA. It can be either set here
|
||||
// or in `res/values/colors.xml`. Setting in both places is an error and the app will not
|
||||
// compile. If not set, the navigation bar color defaults to #000000 - black.
|
||||
resValue "color", "navigationColorDark", twaManifest.navigationColorDark
|
||||
|
||||
// This attribute sets the navbar divider color for the TWA. It can be either
|
||||
// set here or in `res/values/colors.xml`. Setting in both places is an error and the app
|
||||
// will not compile. If not set, the divider color defaults to #00000000 - transparent.
|
||||
resValue "color", "navigationDividerColor", twaManifest.navigationDividerColor
|
||||
|
||||
// This attribute sets the dark navbar divider color for the TWA. It can be either
|
||||
// set here or in `res/values/colors.xml`. Setting in both places is an error and the
|
||||
//app will not compile. If not set, the divider color defaults to #000000 - black.
|
||||
resValue "color", "navigationDividerColorDark", twaManifest.navigationDividerColorDark
|
||||
|
||||
// Sets the color for the background used for the splash screen when launching the
|
||||
// Trusted Web Activity.
|
||||
resValue "color", "backgroundColor", twaManifest.backgroundColor
|
||||
|
||||
// Defines a provider authority for the Splash Screen
|
||||
resValue "string", "providerAuthority", twaManifest.applicationId + '.fileprovider'
|
||||
|
||||
// The enableNotification resource is used to enable or disable the
|
||||
// TrustedWebActivityService, by changing the android:enabled and android:exported
|
||||
// attributes
|
||||
resValue "bool", "enableNotification", twaManifest.enableNotifications.toString()
|
||||
|
||||
twaManifest.shortcuts.eachWithIndex { shortcut, index ->
|
||||
resValue "string", "shortcut_name_$index", "$shortcut.name"
|
||||
resValue "string", "shortcut_short_name_$index", "$shortcut.short_name"
|
||||
}
|
||||
|
||||
// The splashScreenFadeOutDuration resource is used to set the duration of fade out animation in milliseconds
|
||||
// to be played when removing splash screen. The default is 0 (no animation).
|
||||
resValue "integer", "splashScreenFadeOutDuration", twaManifest.splashScreenFadeOutDuration.toString()
|
||||
|
||||
resValue "string", "generatorApp", twaManifest.generatorApp
|
||||
|
||||
resValue "string", "fallbackType", twaManifest.fallbackType
|
||||
|
||||
resValue "bool", "enableSiteSettingsShortcut", twaManifest.enableSiteSettingsShortcut
|
||||
resValue "string", "orientation", twaManifest.orientation
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
lintOptions {
|
||||
checkReleaseBuilds false
|
||||
}
|
||||
}
|
||||
|
||||
task generateShorcutsFile {
|
||||
assert twaManifest.shortcuts.size() < 5, "You can have at most 4 shortcuts."
|
||||
twaManifest.shortcuts.eachWithIndex { s, i ->
|
||||
assert s.name != null, 'Missing `name` in shortcut #' + i
|
||||
assert s.short_name != null, 'Missing `short_name` in shortcut #' + i
|
||||
assert s.url != null, 'Missing `icon` in shortcut #' + i
|
||||
assert s.icon != null, 'Missing `url` in shortcut #' + i
|
||||
}
|
||||
|
||||
def shortcutsFile = new File("$projectDir/src/main/res/xml", "shortcuts.xml")
|
||||
|
||||
def xmlWriter = new StringWriter()
|
||||
def xmlMarkup = new MarkupBuilder(new IndentPrinter(xmlWriter, " ", true))
|
||||
|
||||
xmlMarkup
|
||||
.'shortcuts'('xmlns:android': 'http://schemas.android.com/apk/res/android') {
|
||||
twaManifest.shortcuts.eachWithIndex { s, i ->
|
||||
'shortcut'(
|
||||
'android:shortcutId': 'shortcut' + i,
|
||||
'android:enabled': 'true',
|
||||
'android:icon': '@drawable/' + s.icon,
|
||||
'android:shortcutShortLabel': '@string/shortcut_short_name_' + i,
|
||||
'android:shortcutLongLabel': '@string/shortcut_name_' + i) {
|
||||
'intent'(
|
||||
'android:action': 'android.intent.action.MAIN',
|
||||
'android:targetPackage': twaManifest.applicationId,
|
||||
'android:targetClass': twaManifest.applicationId + '.LauncherActivity',
|
||||
'android:data': s.url)
|
||||
'categories'('android:name': 'android.intent.category.LAUNCHER')
|
||||
}
|
||||
}
|
||||
}
|
||||
shortcutsFile.text = xmlWriter.toString() + '\n'
|
||||
}
|
||||
|
||||
preBuild.dependsOn(generateShorcutsFile)
|
||||
|
||||
repositories {
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
|
||||
implementation 'com.google.androidbrowserhelper:locationdelegation:1.1.1'
|
||||
|
||||
implementation 'com.google.androidbrowserhelper:androidbrowserhelper:2.5.0'
|
||||
|
||||
}
|
188
src/interface/android/app/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,188 @@
|
|||
<!--
|
||||
Copyright 2019 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!-- The "package" attribute is rewritten by the Gradle build with the value of applicationId.
|
||||
It is still required here, as it is used to derive paths, for instance when referring
|
||||
to an Activity by ".MyActivity" instead of the full name. If more Activities are added to the
|
||||
application, the package attribute will need to reflect the correct path in order to use
|
||||
the abbreviated format. -->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="dev.khoj.app">
|
||||
|
||||
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<application
|
||||
android:name="Application"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/appName"
|
||||
|
||||
android:manageSpaceActivity="com.google.androidbrowserhelper.trusted.ManageDataLauncherActivity"
|
||||
|
||||
android:supportsRtl="true"
|
||||
android:theme="@android:style/Theme.Translucent.NoTitleBar">
|
||||
|
||||
<meta-data
|
||||
android:name="asset_statements"
|
||||
android:resource="@string/assetStatements" />
|
||||
|
||||
|
||||
<meta-data
|
||||
android:name="web_manifest_url"
|
||||
android:value="@string/webManifestUrl" />
|
||||
|
||||
|
||||
<meta-data
|
||||
android:name="twa_generator"
|
||||
android:value="@string/generatorApp" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<activity android:name="com.google.androidbrowserhelper.trusted.ManageDataLauncherActivity">
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.MANAGE_SPACE_URL"
|
||||
android:value="@string/launchUrl" />
|
||||
</activity>
|
||||
|
||||
|
||||
<activity android:name="LauncherActivity"
|
||||
android:alwaysRetainTaskState="true"
|
||||
android:label="@string/launcherName"
|
||||
android:exported="true">
|
||||
<meta-data android:name="android.support.customtabs.trusted.DEFAULT_URL"
|
||||
android:value="@string/launchUrl" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.STATUS_BAR_COLOR"
|
||||
android:resource="@color/colorPrimary" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.STATUS_BAR_COLOR_DARK"
|
||||
android:resource="@color/colorPrimaryDark" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.NAVIGATION_BAR_COLOR"
|
||||
android:resource="@color/navigationColor" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.NAVIGATION_BAR_COLOR_DARK"
|
||||
android:resource="@color/navigationColorDark" />
|
||||
|
||||
<meta-data
|
||||
android:name="androix.browser.trusted.NAVIGATION_BAR_DIVIDER_COLOR"
|
||||
android:resource="@color/navigationDividerColor" />
|
||||
|
||||
<meta-data
|
||||
android:name="androix.browser.trusted.NAVIGATION_BAR_DIVIDER_COLOR_DARK"
|
||||
android:resource="@color/navigationDividerColorDark" />
|
||||
|
||||
<meta-data android:name="android.support.customtabs.trusted.SPLASH_IMAGE_DRAWABLE"
|
||||
android:resource="@drawable/splash"/>
|
||||
|
||||
<meta-data android:name="android.support.customtabs.trusted.SPLASH_SCREEN_BACKGROUND_COLOR"
|
||||
android:resource="@color/backgroundColor"/>
|
||||
|
||||
<meta-data android:name="android.support.customtabs.trusted.SPLASH_SCREEN_FADE_OUT_DURATION"
|
||||
android:value="@integer/splashScreenFadeOutDuration"/>
|
||||
|
||||
<meta-data android:name="android.support.customtabs.trusted.FILE_PROVIDER_AUTHORITY"
|
||||
android:value="@string/providerAuthority"/>
|
||||
|
||||
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" />
|
||||
|
||||
<meta-data android:name="android.support.customtabs.trusted.FALLBACK_STRATEGY"
|
||||
android:value="@string/fallbackType" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<meta-data android:name="android.support.customtabs.trusted.SCREEN_ORIENTATION"
|
||||
android:value="@string/orientation"/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="https"
|
||||
android:host="@string/hostName"/>
|
||||
</intent-filter>
|
||||
|
||||
|
||||
</activity>
|
||||
|
||||
<activity android:name="com.google.androidbrowserhelper.trusted.FocusActivity" />
|
||||
|
||||
<activity android:name="com.google.androidbrowserhelper.trusted.WebViewFallbackActivity"
|
||||
android:configChanges="orientation|screenSize" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="@string/providerAuthority"
|
||||
android:grantUriPermissions="true"
|
||||
android:exported="false">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/filepaths" />
|
||||
</provider>
|
||||
|
||||
<service
|
||||
android:name=".DelegationService"
|
||||
android:enabled="@bool/enableNotification"
|
||||
android:exported="@bool/enableNotification">
|
||||
|
||||
|
||||
<meta-data
|
||||
android:name="android.support.customtabs.trusted.SMALL_ICON"
|
||||
android:resource="@drawable/ic_notification_icon" />
|
||||
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.support.customtabs.trusted.TRUSTED_WEB_ACTIVITY_SERVICE"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
|
||||
<activity android:name="com.google.androidbrowserhelper.trusted.NotificationPermissionRequestActivity" />
|
||||
|
||||
|
||||
|
||||
<activity android:name=
|
||||
"com.google.androidbrowserhelper.locationdelegation.PermissionRequestActivity"/>
|
||||
|
||||
</application>
|
||||
</manifest>
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2020 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package dev.khoj.app;
|
||||
|
||||
|
||||
|
||||
public class Application extends android.app.Application {
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package dev.khoj.app;
|
||||
|
||||
|
||||
import com.google.androidbrowserhelper.locationdelegation.LocationDelegationExtraCommandHandler;
|
||||
|
||||
|
||||
public class DelegationService extends
|
||||
com.google.androidbrowserhelper.trusted.DelegationService {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
|
||||
registerExtraCommandHandler(new LocationDelegationExtraCommandHandler());
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2020 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package dev.khoj.app;
|
||||
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
|
||||
|
||||
public class LauncherActivity
|
||||
extends com.google.androidbrowserhelper.trusted.LauncherActivity {
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
// Setting an orientation crashes the app due to the transparent background on Android 8.0
|
||||
// Oreo and below. We only set the orientation on Oreo and above. This only affects the
|
||||
// splash screen and Chrome will still respect the orientation.
|
||||
// See https://github.com/GoogleChromeLabs/bubblewrap/issues/496 for details.
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
||||
} else {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Uri getLaunchingUrl() {
|
||||
// Get the original launch Url.
|
||||
Uri uri = super.getLaunchingUrl();
|
||||
|
||||
|
||||
|
||||
return uri;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<!--
|
||||
Copyright 2020 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:inset="2dp">
|
||||
<aapt:attr name="android:drawable">
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="@color/shortcut_background" />
|
||||
<size android:width="44dp" android:height="44dp" />
|
||||
</shape>
|
||||
</aapt:attr>
|
||||
</inset>
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/interface/android/app/src/main/res/drawable-hdpi/splash.png
Normal file
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 678 B |
BIN
src/interface/android/app/src/main/res/drawable-mdpi/splash.png
Normal file
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 1.5 KiB |
BIN
src/interface/android/app/src/main/res/drawable-xhdpi/splash.png
Normal file
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 3 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 8 KiB |
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1 @@
|
|||
{"name":"Khoj","short_name":"Khoj","display":"standalone","start_url":"/","description":"The open, personal AI for your digital brain. You can ask Khoj to draft a message, paint your imagination, find information on the internet and even answer questions from your documents.","theme_color":"#ffffff","background_color":"#ffffff","icons":[{"src":"/static/assets/icons/khoj_lantern_128x128.png","sizes":"128x128","type":"image/png"},{"src":"/static/assets/icons/khoj_lantern_256x256.png","sizes":"256x256","type":"image/png"}],"screenshots":[{"src":"/static/assets/samples/phone-remember-plan-sample.png","sizes":"419x900","type":"image/png","form_factor":"narrow","label":"Remember and Plan"},{"src":"/static/assets/samples/phone-browse-draw-sample.png","sizes":"419x900","type":"image/png","form_factor":"narrow","label":"Browse and Draw"},{"src":"/static/assets/samples/desktop-remember-plan-sample.png","sizes":"1260x742","type":"image/png","form_factor":"wide","label":"Remember and Plan"},{"src":"/static/assets/samples/desktop-browse-draw-sample.png","sizes":"1260x742","type":"image/png","form_factor":"wide","label":"Browse and Draw"}]}
|
18
src/interface/android/app/src/main/res/values/colors.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<!--
|
||||
Copyright 2020 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
<color name="shortcut_background">#F5F5F5</color>
|
||||
</resources>
|
36
src/interface/android/app/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2021 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<resources>
|
||||
|
||||
|
||||
<!--
|
||||
This variable below expresses the relationship between the app and the site,
|
||||
as documented in the TWA documentation at
|
||||
https://developers.google.com/web/updates/2017/10/using-twa#set_up_digital_asset_links_in_an_android_app
|
||||
and is injected into the AndroidManifest.xml
|
||||
-->
|
||||
<string name="assetStatements">
|
||||
[{
|
||||
\"relation\": [\"delegate_permission/common.handle_all_urls\"],
|
||||
\"target\": {
|
||||
\"namespace\": \"web\",
|
||||
\"site\": \"https://app.khoj.dev\"
|
||||
}
|
||||
}]
|
||||
|
||||
</string>
|
||||
</resources>
|
18
src/interface/android/app/src/main/res/xml/filepaths.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<!--
|
||||
Copyright 2019 Google Inc. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<paths>
|
||||
<files-path path="twa_splash/" name="twa_splash" />
|
||||
</paths>
|
1
src/interface/android/app/src/main/res/xml/shortcuts.xml
Normal file
|
@ -0,0 +1 @@
|
|||
<shortcuts xmlns:android='http://schemas.android.com/apk/res/android' />
|
42
src/interface/android/build.gradle
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2019 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.7.2'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('clean', Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
14
src/interface/android/gradle.properties
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
android.useAndroidX=true
|
BIN
src/interface/android/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
7
src/interface/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
249
src/interface/android/gradlew
vendored
Executable file
|
@ -0,0 +1,249 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
92
src/interface/android/gradlew.bat
vendored
Normal file
|
@ -0,0 +1,92 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
1
src/interface/android/manifest-checksum.txt
Normal file
|
@ -0,0 +1 @@
|
|||
6ee1711cf4f745dafc80c1cc13c3025342a0f5da
|
1
src/interface/android/settings.gradle
Normal file
|
@ -0,0 +1 @@
|
|||
include ':app'
|
BIN
src/interface/android/store_icon.png
Normal file
After Width: | Height: | Size: 25 KiB |
55
src/interface/android/twa-manifest.json
Normal file
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"packageId": "dev.khoj.app",
|
||||
"host": "app.khoj.dev",
|
||||
"name": "Khoj AI",
|
||||
"launcherName": "Khoj",
|
||||
"display": "standalone",
|
||||
"themeColor": "#FFFFFF",
|
||||
"themeColorDark": "#000000",
|
||||
"navigationColor": "#000000",
|
||||
"navigationColorDark": "#000000",
|
||||
"navigationDividerColor": "#000000",
|
||||
"navigationDividerColorDark": "#000000",
|
||||
"backgroundColor": "#FFFFFF",
|
||||
"enableNotifications": true,
|
||||
"startUrl": "/",
|
||||
"iconUrl": "https://assets.khoj.dev/khoj_lantern_1200x1200.png",
|
||||
"splashScreenFadeOutDuration": 300,
|
||||
"signingKey": {
|
||||
"path": "android.keystore",
|
||||
"alias": "android"
|
||||
},
|
||||
"appVersionName": "4",
|
||||
"appVersionCode": 4,
|
||||
"shortcuts": [],
|
||||
"generatorApp": "bubblewrap-cli",
|
||||
"webManifestUrl": "https://app.khoj.dev/static/khoj.webmanifest",
|
||||
"fallbackType": "customtabs",
|
||||
"features": {
|
||||
"locationDelegation": {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"alphaDependencies": {
|
||||
"enabled": false
|
||||
},
|
||||
"enableSiteSettingsShortcut": true,
|
||||
"isChromeOSOnly": false,
|
||||
"isMetaQuest": false,
|
||||
"fullScopeUrl": "https://app.khoj.dev/",
|
||||
"minSdkVersion": 19,
|
||||
"orientation": "natural",
|
||||
"fingerprints": [
|
||||
{
|
||||
"name": "signing",
|
||||
"value": "CC:98:4A:0A:F1:CC:84:26:AC:02:86:49:AA:69:64:B9:5E:63:A3:EF:18:56:EA:CA:13:C1:3A:15:CA:49:77:46"
|
||||
},
|
||||
{
|
||||
"name": "upload",
|
||||
"value": "D4:5A:6F:6C:18:28:D2:1C:78:27:92:C6:AC:DB:4C:12:C4:52:A1:88:9B:A1:F5:67:D1:22:FE:A0:0F:B1:AE:92"
|
||||
}
|
||||
],
|
||||
"additionalTrustedOrigins": [],
|
||||
"retainedBundles": [],
|
||||
"appVersion": "4"
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
version="1.1">
|
||||
<path
|
||||
d="m 14.024348,9.8497703 0.04627,1.9750167"
|
||||
stroke="#1c274c"
|
||||
stroke-width="1.77073"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
d="m 9.6453624,9.7953624 0.046275,1.9750166"
|
||||
stroke="#1c274c"
|
||||
stroke-width="1.77072"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
d="m 11.90538,2.3619994 c -5.4939109,0 -9.6890976,4.0608185 -9.6890976,9.8578926 0,1.477202 0.2658016,2.542848 0.6989332,3.331408 0.433559,0.789293 1.0740097,1.372483 1.9230615,1.798517 1.7362861,0.87132 4.1946007,1.018626 7.0671029,1.018626 0.317997,0 0.593711,0.167879 0.784844,0.458501 0.166463,0.253124 0.238617,0.552748 0.275566,0.787233 0.07263,0.460801 0.05871,1.030165 0.04785,1.474824 v 4.8e-5 l -2.26e-4,0.0091 c -0.0085,0.348246 -0.01538,0.634247 -0.0085,0.861186 0.105589,-0.07971 0.227925,-0.185287 0.36735,-0.31735 0.348613,-0.330307 0.743513,-0.767362 1.176607,-1.246635 l 0.07837,-0.08673 c 0.452675,-0.500762 0.941688,-1.037938 1.41216,-1.473209 0.453774,-0.419787 0.969948,-0.822472 1.476003,-0.953853 1.323661,-0.343655 2.330132,-0.904027 3.005749,-1.76381 0.658957,-0.838568 1.073167,-2.051868 1.073167,-3.898667 0,-5.7970748 -4.195186,-9.8578946 -9.689097,-9.8578946 z M 0.92440678,12.219892 c 0,-7.0067939 5.05909412,-11.47090892 10.98097322,-11.47090892 5.921878,0 10.980972,4.46411502 10.980972,11.47090892 0,2.172259 -0.497596,3.825405 -1.442862,5.028357 -0.928601,1.181693 -2.218843,1.837914 -3.664937,2.213334 -0.211641,0.05502 -0.53529,0.268579 -0.969874,0.670658 -0.417861,0.386604 -0.865628,0.876836 -1.324566,1.384504 l -0.09131,0.101202 c -0.419252,0.464136 -0.849637,0.94059 -1.239338,1.309807 -0.210187,0.199169 -0.425281,0.383422 -0.635348,0.523424 -0.200911,0.133819 -0.449635,0.263369 -0.716376,0.281474 -0.327812,0.02226 -0.61539,-0.149209 -0.804998,-0.457293 -0.157614,-0.255993 -0.217622,-0.557143 -0.246564,-0.778198 -0.0542,-0.414027 -0.04101,-0.933065 -0.03027,-1.355183 l 0.0024,-0.0922 c 0.01099,-0.463865 0.01489,-0.820507 -0.01611,-1.06842 C 8.9434608,19.975238 6.3139711,19.828758 4.356743,18.84659 3.3355029,18.334136 2.4624526,17.578678 1.8500164,16.463713 1.2372016,15.348029 0.92459928,13.943803 0.92459928,12.219967 Z"
|
||||
clip-rule="evenodd"
|
||||
stroke-width="0.360886"
|
||||
fill="#1c274c"
|
||||
fill-rule="evenodd"
|
||||
fill-opacity="1" />
|
||||
</svg>
|
Before Width: | Height: | Size: 2.4 KiB |
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 12L12 12M12 12L9 12M12 12L12 9M12 12L12 15" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<path d="M7 3.33782C8.47087 2.48697 10.1786 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 10.1786 2.48697 8.47087 3.33782 7" stroke="#1C274C" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 580 B |
1
src/interface/desktop/assets/icons/clock.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><polyline points="128 80 128 128 168 152" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><polyline points="184 104 224 104 224 64" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M188.4,192a88,88,0,1,1,1.83-126.23C202,77.69,211.72,88.93,224,104" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></svg>
|
After Width: | Height: | Size: 573 B |
|
@ -1 +0,0 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 122.88"><defs><style>.cls-1{fill:#00a912;}.cls-1,.cls-2{fill-rule:evenodd;}.cls-2{fill:#fff;}</style></defs><title>confirm</title><path class="cls-1" d="M61.44,0A61.44,61.44,0,1,1,0,61.44,61.44,61.44,0,0,1,61.44,0Z"/><path class="cls-2" d="M42.37,51.68,53.26,62,79,35.87c2.13-2.16,3.47-3.9,6.1-1.19l8.53,8.74c2.8,2.77,2.66,4.4,0,7L58.14,85.34c-5.58,5.46-4.61,5.79-10.26.19L28,65.77c-1.18-1.28-1.05-2.57.24-3.84l9.9-10.27c1.5-1.58,2.7-1.44,4.22,0Z"/></svg>
|
Before Width: | Height: | Size: 549 B |
|
@ -1,9 +1 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="-2.4 -2.4 28.80 28.80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0">
|
||||
<rect x="-2.4" y="-2.4" width="28.80" height="28.80" rx="14.4" fill="#ffad9f" strokewidth="0"/>
|
||||
</g>
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g id="SVGRepo_iconCarrier"> <path d="M22 12.3529C22 15.0599 20.0726 17.3221 17.5 17.8722M6.28571 18C3.91878 18 2 16.1038 2 13.7647C2 11.4256 3.91878 9.52941 6.28571 9.52941C6.56983 9.52941 6.8475 9.55673 7.11616 9.60887M14.381 7.02721C14.9767 6.81911 15.6178 6.70588 16.2857 6.70588C16.9404 6.70588 17.5693 6.81468 18.1551 7.01498M7.11616 9.60887C6.88706 8.9978 6.7619 8.33687 6.7619 7.64706C6.7619 4.52827 9.32028 2 12.4762 2C15.4159 2 17.8371 4.19371 18.1551 7.01498M7.11616 9.60887C7.68059 9.71839 8.20528 9.9374 8.66667 10.2426M18.1551 7.01498C18.8381 7.24853 19.4623 7.60648 20 8.06141" stroke="#d42400" stroke-width="1.5" stroke-linecap="round"/> <path d="M8.5 17C8.5 15.5858 8.5 14.8787 8.93934 14.4393C9.37868 14 10.0858 14 11.5 14H12.5C13.9142 14 14.6213 14 15.0607 14.4393C15.5 14.8787 15.5 15.5858 15.5 17V19C15.5 20.4142 15.5 21.1213 15.0607 21.5607C14.6213 22 13.9142 22 12.5 22H11.5C10.0858 22 9.37868 22 8.93934 21.5607C8.5 21.1213 8.5 20.4142 8.5 19V17Z" stroke="#d42400" stroke-width="1.5"/> <path d="M11 18H13" stroke="#d42400" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </g>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><line x1="48" y1="40" x2="208" y2="216" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M193.89,200.49A79.66,79.66,0,0,1,160,208H72A56,56,0,1,1,85.92,97.74" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M112.63,63.52A80,80,0,0,1,219.68,181.28" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M80,128A79.68,79.68,0,0,1,91.07,87.37" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></svg>
|
||||
|
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 729 B |
|
@ -1,9 +1 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="-2.4 -2.4 28.80 28.80" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="#16ba00">
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0">
|
||||
<rect x="-2.4" y="-2.4" width="28.80" height="28.80" rx="14.4" fill="#7aff00" strokewidth="0"/>
|
||||
</g>
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g id="SVGRepo_iconCarrier"> <path d="M22 13.3529C22 16.0599 20.0726 18.3221 17.5 18.8722M6.28571 19C3.91878 19 2 17.1038 2 14.7647C2 12.4256 3.91878 10.5294 6.28571 10.5294C6.56983 10.5294 6.8475 10.5567 7.11616 10.6089M14.381 8.02721C14.9767 7.81911 15.6178 7.70588 16.2857 7.70588C16.9404 7.70588 17.5693 7.81468 18.1551 8.01498M7.11616 10.6089C6.88706 9.9978 6.7619 9.33687 6.7619 8.64706C6.7619 5.52827 9.32028 3 12.4762 3C15.4159 3 17.8371 5.19371 18.1551 8.01498M7.11616 10.6089C7.68059 10.7184 8.20528 10.9374 8.66667 11.2426M18.1551 8.01498C18.8381 8.24853 19.4623 8.60648 20 9.06141" stroke="#16ba00" stroke-width="1.5" stroke-linecap="round"/> <path d="M10 19.8L11.1429 21L14 18" stroke="#16ba00" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </g>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><path d="M80,128a80,80,0,1,1,80,80H72A56,56,0,1,1,85.92,97.74" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><polyline points="120 136 144 160 192 112" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></svg>
|
||||
|
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 419 B |
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512 512" xml:space="preserve">
|
||||
<path id="SVGCleanerId_0" style="fill:#FFC36E;" d="M183.295,123.586H55.05c-6.687,0-12.801-3.778-15.791-9.76l-12.776-25.55
|
||||
l12.776-25.55c2.99-5.982,9.103-9.76,15.791-9.76h128.246c6.687,0,12.801,3.778,15.791,9.76l12.775,25.55l-12.776,25.55
|
||||
C196.096,119.808,189.983,123.586,183.295,123.586z"/>
|
||||
<g>
|
||||
<path id="SVGCleanerId_0_1_" style="fill:#FFC36E;" d="M183.295,123.586H55.05c-6.687,0-12.801-3.778-15.791-9.76l-12.776-25.55
|
||||
l12.776-25.55c2.99-5.982,9.103-9.76,15.791-9.76h128.246c6.687,0,12.801,3.778,15.791,9.76l12.775,25.55l-12.776,25.55
|
||||
C196.096,119.808,189.983,123.586,183.295,123.586z"/>
|
||||
</g>
|
||||
<path style="fill:#EFF2FA;" d="M485.517,70.621H26.483c-4.875,0-8.828,3.953-8.828,8.828v44.138h476.69V79.448
|
||||
C494.345,74.573,490.392,70.621,485.517,70.621z"/>
|
||||
<rect x="17.655" y="105.931" style="fill:#E1E6F2;" width="476.69" height="17.655"/>
|
||||
<path style="fill:#FFD782;" d="M494.345,88.276H217.318c-3.343,0-6.4,1.889-7.895,4.879l-10.336,20.671
|
||||
c-2.99,5.982-9.105,9.76-15.791,9.76H55.05c-6.687,0-12.801-3.778-15.791-9.76L28.922,93.155c-1.495-2.99-4.552-4.879-7.895-4.879
|
||||
h-3.372C7.904,88.276,0,96.18,0,105.931v335.448c0,9.751,7.904,17.655,17.655,17.655h476.69c9.751,0,17.655-7.904,17.655-17.655
|
||||
V105.931C512,96.18,504.096,88.276,494.345,88.276z"/>
|
||||
<path style="fill:#FFC36E;" d="M485.517,441.379H26.483c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828
|
||||
h459.034c4.875,0,8.828,3.953,8.828,8.828l0,0C494.345,437.427,490.392,441.379,485.517,441.379z"/>
|
||||
<path style="fill:#EFF2FA;" d="M326.621,220.69h132.414c4.875,0,8.828-3.953,8.828-8.828v-70.621c0-4.875-3.953-8.828-8.828-8.828
|
||||
H326.621c-4.875,0-8.828,3.953-8.828,8.828v70.621C317.793,216.737,321.746,220.69,326.621,220.69z"/>
|
||||
<path style="fill:#C7CFE2;" d="M441.379,167.724h-97.103c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828
|
||||
h97.103c4.875,0,8.828,3.953,8.828,8.828l0,0C450.207,163.772,446.254,167.724,441.379,167.724z"/>
|
||||
<path style="fill:#D7DEED;" d="M441.379,203.034h-97.103c-4.875,0-8.828-3.953-8.828-8.828l0,0c0-4.875,3.953-8.828,8.828-8.828
|
||||
h97.103c4.875,0,8.828,3.953,8.828,8.828l0,0C450.207,199.082,446.254,203.034,441.379,203.034z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.4 KiB |
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M22 8.29344C22 11.7692 19.1708 14.5869 15.6807 14.5869C15.0439 14.5869 13.5939 14.4405 12.8885 13.8551L12.0067 14.7333C11.4883 15.2496 11.6283 15.4016 11.8589 15.652C11.9551 15.7565 12.0672 15.8781 12.1537 16.0505C12.1537 16.0505 12.8885 17.075 12.1537 18.0995C11.7128 18.6849 10.4783 19.5045 9.06754 18.0995L8.77362 18.3922C8.77362 18.3922 9.65538 19.4167 8.92058 20.4412C8.4797 21.0267 7.30403 21.6121 6.27531 20.5876L5.2466 21.6121C4.54119 22.3146 3.67905 21.9048 3.33616 21.6121L2.45441 20.7339C1.63143 19.9143 2.1115 19.0264 2.45441 18.6849L10.0963 11.0743C10.0963 11.0743 9.3615 9.90338 9.3615 8.29344C9.3615 4.81767 12.1907 2 15.6807 2C19.1708 2 22 4.81767 22 8.29344ZM15.681 10.4889C16.8984 10.4889 17.8853 9.50601 17.8853 8.29353C17.8853 7.08105 16.8984 6.09814 15.681 6.09814C14.4635 6.09814 13.4766 7.08105 13.4766 8.29353C13.4766 9.50601 14.4635 10.4889 15.681 10.4889Z" fill="#1C274C"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 29 KiB |
BIN
src/interface/desktop/assets/icons/khoj_logo.png
Normal file
After Width: | Height: | Size: 10 KiB |
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M9.16488 17.6505C8.92513 17.8743 8.73958 18.0241 8.54996 18.1336C7.62175 18.6695 6.47816 18.6695 5.54996 18.1336C5.20791 17.9361 4.87912 17.6073 4.22153 16.9498C3.56394 16.2922 3.23514 15.9634 3.03767 15.6213C2.50177 14.6931 2.50177 13.5495 3.03767 12.6213C3.23514 12.2793 3.56394 11.9505 4.22153 11.2929L7.04996 8.46448C7.70755 7.80689 8.03634 7.47809 8.37838 7.28062C9.30659 6.74472 10.4502 6.74472 11.3784 7.28061C11.7204 7.47809 12.0492 7.80689 12.7068 8.46448C13.3644 9.12207 13.6932 9.45086 13.8907 9.7929C14.4266 10.7211 14.4266 11.8647 13.8907 12.7929C13.7812 12.9825 13.6314 13.1681 13.4075 13.4078M10.5919 10.5922C10.368 10.8319 10.2182 11.0175 10.1087 11.2071C9.57284 12.1353 9.57284 13.2789 10.1087 14.2071C10.3062 14.5492 10.635 14.878 11.2926 15.5355C11.9502 16.1931 12.279 16.5219 12.621 16.7194C13.5492 17.2553 14.6928 17.2553 15.621 16.7194C15.9631 16.5219 16.2919 16.1931 16.9495 15.5355L19.7779 12.7071C20.4355 12.0495 20.7643 11.7207 20.9617 11.3787C21.4976 10.4505 21.4976 9.30689 20.9617 8.37869C20.7643 8.03665 20.4355 7.70785 19.7779 7.05026C19.1203 6.39267 18.7915 6.06388 18.4495 5.8664C17.5212 5.3305 16.3777 5.3305 15.4495 5.8664C15.2598 5.97588 15.0743 6.12571 14.8345 6.34955" stroke="#000000" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1.4 KiB |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M192 0C139 0 96 43 96 96V256c0 53 43 96 96 96s96-43 96-96V96c0-53-43-96-96-96zM64 216c0-13.3-10.7-24-24-24s-24 10.7-24 24v40c0 89.1 66.2 162.7 152 174.4V464H120c-13.3 0-24 10.7-24 24s10.7 24 24 24h72 72c13.3 0 24-10.7 24-24s-10.7-24-24-24H216V430.4c85.8-11.7 152-85.3 152-174.4V216c0-13.3-10.7-24-24-24s-24 10.7-24 24v40c0 70.7-57.3 128-128 128s-128-57.3-128-128V216z"/></svg>
|
Before Width: | Height: | Size: 616 B |
1
src/interface/desktop/assets/icons/open-link.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><polyline points="216 104 215.99 40.01 152 40" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="136" y1="120" x2="216" y2="40" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M184,136v72a8,8,0,0,1-8,8H48a8,8,0,0,1-8-8V80a8,8,0,0,1,8-8h72" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></svg>
|
After Width: | Height: | Size: 574 B |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100px" height="100px"><path fill="#fefdef" d="M29.614,12.307h-1.268c-4.803,0-8.732,3.93-8.732,8.732v61.535c0,4.803,3.93,8.732,8.732,8.732h43.535c4.803,0,8.732-3.93,8.732-8.732v-50.02C72.74,24.68,68.241,20.182,60.367,12.307H41.614"/><path fill="#1f212b" d="M71.882,92.307H28.347c-5.367,0-9.732-4.366-9.732-9.732V21.04c0-5.367,4.366-9.732,9.732-9.732h1.268c0.552,0,1,0.448,1,1s-0.448,1-1,1h-1.268c-4.264,0-7.732,3.469-7.732,7.732v61.535c0,4.264,3.469,7.732,7.732,7.732h43.535c4.264,0,7.732-3.469,7.732-7.732V32.969L59.953,13.307H41.614c-0.552,0-1-0.448-1-1s0.448-1,1-1h18.752c0.265,0,0.52,0.105,0.707,0.293l20.248,20.248c0.188,0.188,0.293,0.442,0.293,0.707v50.02C81.614,87.941,77.248,92.307,71.882,92.307z"/><path fill="#fef6aa" d="M60.114,12.807v10.986c0,4.958,4.057,9.014,9.014,9.014h11.986"/><path fill="#1f212b" d="M81.114 33.307H69.129c-5.247 0-9.515-4.268-9.515-9.515V12.807c0-.276.224-.5.5-.5s.5.224.5.5v10.985c0 4.695 3.82 8.515 8.515 8.515h11.985c.276 0 .5.224.5.5S81.391 33.307 81.114 33.307zM75.114 51.307c-.276 0-.5-.224-.5-.5v-3c0-.276.224-.5.5-.5s.5.224.5.5v3C75.614 51.083 75.391 51.307 75.114 51.307zM75.114 59.307c-.276 0-.5-.224-.5-.5v-6c0-.276.224-.5.5-.5s.5.224.5.5v6C75.614 59.083 75.391 59.307 75.114 59.307zM67.956 86.307H32.272c-4.223 0-7.658-3.45-7.658-7.689V25.955c0-2.549 1.264-4.931 3.382-6.371.228-.156.54-.095.695.132.155.229.096.54-.132.695-1.844 1.254-2.944 3.326-2.944 5.544v52.663c0 3.688 2.987 6.689 6.658 6.689h35.685c3.671 0 6.658-3.001 6.658-6.689V60.807c0-.276.224-.5.5-.5s.5.224.5.5v17.811C75.614 82.857 72.179 86.307 67.956 86.307z"/><path fill="#1f212b" d="M39.802 14.307l-.117 11.834c0 2.21-2.085 3.666-4.036 3.666-1.951 0-4.217-1.439-4.217-3.649l.037-12.58c0-1.307 1.607-2.451 2.801-2.451 1.194 0 2.345 1.149 2.345 2.456l.021 10.829c0 0-.083.667-1.005.645-.507-.012-1.145-.356-1.016-.906v-9.843h-.813l-.021 9.708c0 1.38.54 1.948 1.875 1.948s1.959-.714 1.959-2.094V13.665c0-2.271-1.36-3.5-3.436-3.5s-3.564 1.261-3.564 3.532l.032 12.11c0 3.04 2.123 4.906 4.968 4.906 2.845 0 5-1.71 5-4.75V14.307H39.802zM53.114 52.307h-23c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h23c.276 0 .5.224.5.5S53.391 52.307 53.114 52.307zM44.114 59.307h-14c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h14c.276 0 .5.224.5.5S44.391 59.307 44.114 59.307zM70.114 59.307h-24c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h24c.276 0 .5.224.5.5S70.391 59.307 70.114 59.307zM61.114 66.307h-11c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h11c.276 0 .5.224.5.5S61.391 66.307 61.114 66.307zM71.114 66.307h-8c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h8c.276 0 .5.224.5.5S71.391 66.307 71.114 66.307zM48.114 66.307h-18c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h18c.276 0 .5.224.5.5S48.391 66.307 48.114 66.307zM70.114 73.307h-13c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h13c.276 0 .5.224.5.5S70.391 73.307 70.114 73.307zM54.114 73.307h-24c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h24c.276 0 .5.224.5.5S54.391 73.307 54.114 73.307z"/></svg>
|
Before Width: | Height: | Size: 2.9 KiB |
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 512 512"><path fill-rule="nonzero" d="M256 0c70.69 0 134.7 28.66 181.02 74.98C483.34 121.31 512 185.31 512 256c0 70.69-28.66 134.7-74.98 181.02C390.7 483.34 326.69 512 256 512c-70.69 0-134.69-28.66-181.02-74.98C28.66 390.7 0 326.69 0 256c0-70.69 28.66-134.69 74.98-181.02C121.31 28.66 185.31 0 256 0zm-21.49 301.51v-2.03c.16-13.46 1.48-24.12 4.07-32.05 2.54-7.92 6.19-14.37 10.97-19.25 4.77-4.92 10.51-9.39 17.22-13.46 4.31-2.74 8.22-5.78 11.68-9.18 3.45-3.36 6.19-7.27 8.23-11.69 2.02-4.37 3.04-9.24 3.04-14.62 0-6.4-1.52-11.94-4.57-16.66-3-4.68-7.06-8.28-12.04-10.87-5.03-2.54-10.61-3.81-16.76-3.81-5.53 0-10.81 1.11-15.89 3.45-5.03 2.29-9.25 5.89-12.55 10.77-3.3 4.87-5.23 11.12-5.74 18.74h-32.91c.51-12.95 3.81-23.92 9.85-32.91 6.1-8.99 14.13-15.8 24.08-20.42 10.01-4.62 21.08-6.9 33.16-6.9 13.31 0 24.89 2.43 34.84 7.41 9.96 4.93 17.73 11.83 23.27 20.67 5.48 8.84 8.28 19.1 8.28 30.88 0 8.08-1.27 15.34-3.81 21.79-2.54 6.45-6.1 12.24-10.77 17.27-4.68 5.08-10.21 9.54-16.71 13.41-6.15 3.86-11.12 7.82-14.88 11.93-3.81 4.11-6.56 8.99-8.28 14.58-1.73 5.63-2.69 12.59-2.84 20.92v2.03h-30.94zm16.36 65.82c-5.94-.04-11.02-2.13-15.29-6.35-4.26-4.21-6.35-9.34-6.35-15.33 0-5.89 2.09-10.97 6.35-15.19 4.27-4.21 9.35-6.35 15.29-6.35 5.84 0 10.92 2.14 15.18 6.35 4.32 4.22 6.45 9.3 6.45 15.19 0 3.96-1.01 7.62-2.99 10.87-1.98 3.3-4.57 5.94-7.82 7.87-3.25 1.93-6.86 2.9-10.82 2.94zM417.71 94.29C376.33 52.92 319.15 27.32 256 27.32c-63.15 0-120.32 25.6-161.71 66.97C52.92 135.68 27.32 192.85 27.32 256c0 63.15 25.6 120.33 66.97 161.71 41.39 41.37 98.56 66.97 161.71 66.97 63.15 0 120.33-25.6 161.71-66.97 41.37-41.38 66.97-98.56 66.97-161.71 0-63.15-25.6-120.32-66.97-161.71z"/></svg>
|
Before Width: | Height: | Size: 1.8 KiB |
|
@ -1,25 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="800px"
|
||||
height="800px"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
version="1.1">
|
||||
<path
|
||||
d="m 18.562765,17.147843 c 1.380497,-1.679442 2.307667,-4.013099 2.307667,-6.330999 C 20.870432,5.3951476 16.353958,1 10.782674,1 5.2113555,1 0.69491525,5.3951476 0.69491525,10.816844 c 0,5.421663 4.51644025,9.816844 10.08775875,9.816844 2.381867,0 4.570922,-0.803307 6.296712,-2.14673 0.508475,-0.508475 4.514633,4.192839 4.514633,4.192839 1.036377,1.008544 2.113087,-0.02559 1.07671,-1.034139 z m -7.780091,1.925408 c -4.3394583,0 -8.6708434,-4.033489 -8.6708434,-8.256407 0,-4.2229187 4.3313851,-8.2564401 8.6708434,-8.2564401 4.339458,0 8.670809,4.2369112 8.670809,8.4598301 0,4.222918 -4.331351,8.053017 -8.670809,8.053017 z"
|
||||
fill="#1c274c"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
fill-opacity="1"
|
||||
stroke-width="1.10519"
|
||||
stroke-dasharray="none" />
|
||||
<path
|
||||
d="m 13.337351,9.3402647 0.05184,2.1532893"
|
||||
stroke="#1c274c"
|
||||
stroke-width="1.95702"
|
||||
stroke-linecap="round" />
|
||||
<path
|
||||
d="M 8.431347,9.2809457 8.483191,11.434235"
|
||||
stroke="#1c274c"
|
||||
stroke-width="1.95701"
|
||||
stroke-linecap="round" />
|
||||
</svg>
|
Before Width: | Height: | Size: 1.2 KiB |
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 384 512"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
sodipodi:docname="stop-solid.svg"
|
||||
inkscape:version="1.3 (0e150ed, 2023-07-21)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="0.4609375"
|
||||
inkscape:cx="192"
|
||||
inkscape:cy="256"
|
||||
inkscape:window-width="1312"
|
||||
inkscape:window-height="449"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="88"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg1" />
|
||||
<!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
||||
<path
|
||||
d="M0 128C0 92.7 28.7 64 64 64H320c35.3 0 64 28.7 64 64V384c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128z"
|
||||
id="path1"
|
||||
style="fill:#aa0000" />
|
||||
</svg>
|
Before Width: | Height: | Size: 1.3 KiB |
|
@ -1 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><line x1="216" y1="56" x2="40" y2="56" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="88" y1="24" x2="168" y2="24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><path d="M200,56V208a8,8,0,0,1-8,8H64a8,8,0,0,1-8-8V56" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></svg>
|
||||
|
|
Before Width: | Height: | Size: 503 B After Width: | Height: | Size: 547 B |
1
src/interface/desktop/assets/icons/upload.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><path d="M176,128h48a8,8,0,0,1,8,8v64a8,8,0,0,1-8,8H32a8,8,0,0,1-8-8V136a8,8,0,0,1,8-8H80" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="128" y1="128" x2="128" y2="24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><polyline points="80 72 128 24 176 72" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><circle cx="188" cy="168" r="12"/></svg>
|
After Width: | Height: | Size: 618 B |
|
@ -8,13 +8,12 @@
|
|||
--primary-hover: #fee285;
|
||||
--primary-focus: rgba(255, 179, 0, 0.125);
|
||||
--primary-inverse: rgba(0, 0, 0, 0.75);
|
||||
--background-color: #f5f4f3;
|
||||
--background-color: #fff;
|
||||
--main-text-color: #475569;
|
||||
--summer-sun: #fcc50b;
|
||||
--water: #44b9da;
|
||||
--leaf: #7b990a;
|
||||
--flower: #d1684e;
|
||||
--font-family: "Noto Sans", "Noto Sans Arabic", sans-serif !important;
|
||||
}
|
||||
|
||||
/* Amber Dark scheme (Auto) */
|
||||
|
@ -25,13 +24,12 @@
|
|||
--primary-hover: #fee285;
|
||||
--primary-focus: rgba(255, 179, 0, 0.25);
|
||||
--primary-inverse: rgba(0, 0, 0, 0.75);
|
||||
--background-color: #f5f4f3;
|
||||
--background-color: #fff;
|
||||
--main-text-color: #475569;
|
||||
--summer-sun: #fcc50b;
|
||||
--water: #44b9da;
|
||||
--leaf: #7b990a;
|
||||
--flower: #d1684e;
|
||||
--font-family: "Noto Sans", "Noto Sans Arabic", sans-serif !important;
|
||||
}
|
||||
}
|
||||
/* Amber Dark scheme (Forced) */
|
||||
|
@ -41,13 +39,12 @@
|
|||
--primary-hover: #fcc50b;
|
||||
--primary-focus: rgba(255, 179, 0, 0.25);
|
||||
--primary-inverse: rgba(0, 0, 0, 0.75);
|
||||
--background-color: #f5f4f3;
|
||||
--background-color: #fff;
|
||||
--main-text-color: #475569;
|
||||
--summer-sun: #fcc50b;
|
||||
--water: #44b9da;
|
||||
--leaf: #7b990a;
|
||||
--flower: #d1684e;
|
||||
--font-family: "Noto Sans", "Noto Sans Arabic", sans-serif !important;
|
||||
}
|
||||
/* Amber (Common styles) */
|
||||
:root {
|
||||
|
@ -90,36 +87,46 @@ nav.khoj-nav {
|
|||
grid-gap: 32px;
|
||||
justify-self: right;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.khoj-status-box {
|
||||
.khoj-status-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 52px;
|
||||
gap: 4px;
|
||||
-webkit-app-region: no-drag;
|
||||
padding: 8px 12px;
|
||||
border-radius: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
background-color: #f5f5f5; /* Neutral background */
|
||||
color: #333; /* Neutral text color */
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); /* Subtle shadow */
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.khoj-status-box .khoj-status-connected {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
.khoj-status-connected {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background-color: rgb(90, 235, 90);
|
||||
}
|
||||
.khoj-status-box .khoj-status-not-connected {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
border-radius: 50%;
|
||||
background-color: rgb(235, 90, 90);
|
||||
}
|
||||
|
||||
.khoj-status-box .khoj-status-text {
|
||||
display: none;
|
||||
background-color: #4CAF50; /* Green for connected */
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.khoj-status-not-connected {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background-color: #F44336; /* Red for not connected */
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.khoj-status-text {
|
||||
color: #333; /* Neutral text color */
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.khoj-status-box:hover {
|
||||
background-color: #e0e0e0; /* Slightly darker background on hover */
|
||||
color: #000; /* Darker text on hover */
|
||||
}
|
||||
.khoj-status-box:hover .khoj-status-text {
|
||||
display: block;
|
||||
}
|
||||
|
||||
a.khoj-nav {
|
||||
display: flex;
|
||||
|
@ -188,18 +195,18 @@ img.khoj-logo {
|
|||
.khoj-nav-dropdown-content.show {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
border-radius: 20px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.khoj-nav-dropdown-content a {
|
||||
color: black;
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
border-radius: 20px;
|
||||
}
|
||||
.khoj-nav-dropdown-content a:hover {
|
||||
background-color: var(--primary-hover);
|
||||
background-color: hsla(24.6 95% 53.1% / 0.125);
|
||||
}
|
||||
|
||||
.khoj-nav-username {
|
||||
padding: 12px 16px;
|
||||
text-decoration: none;
|
||||
|
@ -233,6 +240,17 @@ img.khoj-logo {
|
|||
border: 3px solid var(--primary-hover);
|
||||
}
|
||||
|
||||
.khoj-nav-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
a.khoj-nav-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.khoj-nav-dropdown-content {
|
||||
display: block;
|
||||
|
|
|
@ -440,7 +440,7 @@ let titleBarStyle = process.platform === 'win32' ? 'default' : 'hidden';
|
|||
const {globalShortcut, clipboard} = require('electron'); // global shortcut and clipboard dependencies for shortcut window
|
||||
const openShortcutWindowKeyBind = 'CommandOrControl+Shift+K'
|
||||
|
||||
const createWindow = (tab = 'chat.html') => {
|
||||
const createWindow = (tab = 'settings.html') => {
|
||||
win = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 800,
|
||||
|
@ -602,6 +602,14 @@ app.whenReady().then(() => {
|
|||
});
|
||||
ipcMain.handle('deleteAllFiles', deleteAllFiles);
|
||||
|
||||
ipcMain.handle('openFile', async (_, path) => {
|
||||
try {
|
||||
await shell.openPath(path);
|
||||
} catch (error) {
|
||||
console.error('Error opening file:', error);
|
||||
}
|
||||
});
|
||||
|
||||
const mainWindow = createWindow();
|
||||
|
||||
app.setAboutPanelOptions({
|
||||
|
@ -652,7 +660,7 @@ app.whenReady().then(() => {
|
|||
globalShortcut.unregister('Escape');
|
||||
});
|
||||
ipcMain.on('continue-conversation-button-clicked', () => {
|
||||
openWindow('chat.html');
|
||||
openWindow('settings.html');
|
||||
if (shortcutWin && !shortcutWin.isDestroyed()) {
|
||||
shortcutWin.close();
|
||||
}
|
||||
|
@ -727,8 +735,6 @@ app.whenReady().then(() => {
|
|||
tray = new Tray(icon)
|
||||
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{ label: 'Chat', type: 'normal', click: () => { openWindow('chat.html'); }},
|
||||
{ label: 'Search', type: 'normal', click: () => { openWindow('search.html') }},
|
||||
{ label: 'Configure', type: 'normal', click: () => { openWindow('settings.html') }},
|
||||
{ type: 'separator' },
|
||||
{ label: 'About Khoj', type: 'normal', click: () => { openAboutWindow(); } },
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Khoj",
|
||||
"version": "1.30.1",
|
||||
"version": "1.31.0",
|
||||
"description": "Your Second Brain",
|
||||
"author": "Khoj Inc. <team@khoj.dev>",
|
||||
"license": "GPL-3.0-or-later",
|
||||
|
@ -16,7 +16,7 @@
|
|||
"start": "yarn electron ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@todesktop/runtime": "^1.6.4",
|
||||
"@todesktop/runtime": "^2.0.0",
|
||||
"axios": "^1.7.4",
|
||||
"cron": "^2.4.3",
|
||||
"electron-store": "^8.1.0"
|
||||
|
|
|
@ -83,4 +83,9 @@ contextBridge.exposeInMainWorld('appInfoAPI', {
|
|||
contextBridge.exposeInMainWorld('navigateAPI', {
|
||||
navigateToSettings: () => ipcRenderer.send('navigate', 'settings.html'),
|
||||
navigateToWebSettings: () => ipcRenderer.send('navigateToWebApp', 'settings'),
|
||||
navigateToWebHome: () => ipcRenderer.send('navigateToWebApp', ''),
|
||||
})
|
||||
|
||||
contextBridge.exposeInMainWorld('openFileAPI', {
|
||||
openFile: (path) => ipcRenderer.invoke('openFile', path)
|
||||
});
|
||||
|
|
|
@ -26,37 +26,10 @@ async function removeFolder(folderPath) {
|
|||
}
|
||||
}
|
||||
|
||||
const toggleFilesButton = document.getElementById('toggle-files');
|
||||
const currentFiles = document.getElementById('current-files');
|
||||
|
||||
const toggleFilesSVG = document.getElementById('toggle-files-svg');
|
||||
|
||||
toggleFilesButton.addEventListener('click', () => {
|
||||
if (currentFiles.style.display === 'none') {
|
||||
currentFiles.style.display = 'block';
|
||||
toggleFilesSVG.style.transform = 'rotate(0deg)';
|
||||
} else {
|
||||
currentFiles.style.display = 'none';
|
||||
toggleFilesSVG.style.transform = 'rotate(180deg)';
|
||||
}
|
||||
});
|
||||
|
||||
const toggleFoldersButton = document.getElementById('toggle-folders');
|
||||
const currentFolders = document.getElementById('current-folders');
|
||||
|
||||
const toggleFoldersSVG = document.getElementById('toggle-folders-svg');
|
||||
|
||||
|
||||
toggleFoldersButton.addEventListener('click', () => {
|
||||
if (currentFolders.style.display === 'none') {
|
||||
currentFolders.style.display = 'block';
|
||||
toggleFoldersSVG.style.transform = 'rotate(0deg)';
|
||||
} else {
|
||||
currentFolders.style.display = 'none';
|
||||
toggleFoldersSVG.style.transform = 'rotate(180deg)';
|
||||
}
|
||||
});
|
||||
|
||||
function makeFileElement(file) {
|
||||
let fileElement = document.createElement("div");
|
||||
fileElement.classList.add("file-element");
|
||||
|
@ -64,20 +37,34 @@ function makeFileElement(file) {
|
|||
let fileNameElement = document.createElement("div");
|
||||
fileNameElement.classList.add("content-name");
|
||||
fileNameElement.innerHTML = file.path;
|
||||
fileNameElement.style.cursor = "pointer";
|
||||
|
||||
fileNameElement.addEventListener("click", () => {
|
||||
window.openFileAPI.openFile(file.path);
|
||||
});
|
||||
|
||||
fileElement.appendChild(fileNameElement);
|
||||
|
||||
let buttonContainer = document.createElement("div");
|
||||
buttonContainer.classList.add("remove-button-container");
|
||||
let removeFileButton = document.createElement("button");
|
||||
let fileSyncedImage = document.createElement("img")
|
||||
let fileSyncedImage = document.createElement("img");
|
||||
fileSyncedImage.classList.add("file-synced-image");
|
||||
fileSyncedImage.src = "./assets/icons/file-synced.svg";
|
||||
|
||||
// Create trash icon image
|
||||
let trashIcon = document.createElement("img");
|
||||
trashIcon.src = "./assets/icons/trash-solid.svg";
|
||||
trashIcon.classList.add("trash-icon");
|
||||
|
||||
removeFileButton.classList.add("remove-file-button");
|
||||
removeFileButton.innerHTML = "🗑️";
|
||||
removeFileButton.appendChild(trashIcon);
|
||||
removeFileButton.addEventListener("click", () => {
|
||||
removeFile(file.path);
|
||||
});
|
||||
|
||||
buttonContainer.appendChild(removeFileButton);
|
||||
buttonContainer.insertAdjacentElement("afterbegin",fileSyncedImage);
|
||||
buttonContainer.insertAdjacentElement("afterbegin", fileSyncedImage);
|
||||
fileElement.appendChild(buttonContainer);
|
||||
return fileElement;
|
||||
}
|
||||
|
@ -89,13 +76,26 @@ function makeFolderElement(folder) {
|
|||
let folderNameElement = document.createElement("div");
|
||||
folderNameElement.classList.add("content-name");
|
||||
folderNameElement.innerHTML = folder.path;
|
||||
folderNameElement.style.cursor = "pointer";
|
||||
|
||||
folderNameElement.addEventListener("click", () => {
|
||||
window.openFileAPI.openFile(folder.path);
|
||||
});
|
||||
|
||||
folderElement.appendChild(folderNameElement);
|
||||
|
||||
let buttonContainer = document.createElement("div");
|
||||
buttonContainer.classList.add("remove-button-container");
|
||||
let removeFolderButton = document.createElement("button");
|
||||
removeFolderButton.classList.add("remove-folder-button");
|
||||
removeFolderButton.innerHTML = "🗑️";
|
||||
|
||||
// Create trash icon image
|
||||
let trashIcon = document.createElement("img");
|
||||
trashIcon.src = "./assets/icons/trash-solid.svg";
|
||||
trashIcon.classList.add("trash-icon");
|
||||
|
||||
removeFolderButton.appendChild(trashIcon);
|
||||
|
||||
removeFolderButton.addEventListener("click", () => {
|
||||
removeFolder(folder.path);
|
||||
});
|
||||
|
@ -104,7 +104,7 @@ function makeFolderElement(folder) {
|
|||
return folderElement;
|
||||
}
|
||||
|
||||
(async function() {
|
||||
(async function () {
|
||||
const files = await window.getFilesAPI.getFiles();
|
||||
let currentFilesElement = document.getElementById("current-files");
|
||||
for (const file of files) {
|
||||
|
@ -157,26 +157,35 @@ window.updateStateAPI.onUpdateState((event, state) => {
|
|||
console.log("state was updated", state);
|
||||
loadingBar.style.display = 'none';
|
||||
let syncStatusElement = document.getElementById("sync-status");
|
||||
syncStatusElement.innerHTML = '';
|
||||
const currentTime = new Date();
|
||||
nextSyncTime = new Date();
|
||||
nextSyncTime.setMinutes(Math.ceil((nextSyncTime.getMinutes() + 1) / 10) * 10);
|
||||
if (state.completed == false) {
|
||||
|
||||
fileSyncedImage.forEach((image)=> {
|
||||
fileSyncedImage.forEach((image) => {
|
||||
image.style.display = "block"
|
||||
image.src = "./assets/icons/file-not-synced.svg"
|
||||
})
|
||||
if (state.error) syncStatusElement.innerHTML = state.error;
|
||||
return;
|
||||
} else {
|
||||
fileSyncedImage.forEach((image)=> {
|
||||
fileSyncedImage.forEach((image) => {
|
||||
image.style.display = "block"
|
||||
image.src = "./assets/icons/file-synced.svg"
|
||||
})
|
||||
|
||||
}
|
||||
const options = { hour: '2-digit', minute: '2-digit' };
|
||||
syncStatusElement.innerHTML = `⏱️ Synced at ${currentTime.toLocaleTimeString(undefined, options)}. Next sync at ${nextSyncTime.toLocaleTimeString(undefined, options)}.`;
|
||||
|
||||
const clockElement = document.createElement("div");
|
||||
const clockIcon = document.createElement("img");
|
||||
clockIcon.src = "./assets/icons/clock.svg";
|
||||
clockIcon.classList.add("clock-icon");
|
||||
|
||||
clockElement.appendChild(clockIcon);
|
||||
syncStatusElement.appendChild(clockElement);
|
||||
syncStatusElement.innerHTML += ` Synced at ${currentTime.toLocaleTimeString(undefined, options)}. Next sync at ${nextSyncTime.toLocaleTimeString(undefined, options)}.`;
|
||||
});
|
||||
|
||||
window.needsSubscriptionAPI.onNeedsSubscription((event, needsSubscription) => {
|
||||
|
@ -188,7 +197,7 @@ window.needsSubscriptionAPI.onNeedsSubscription((event, needsSubscription) => {
|
|||
});
|
||||
|
||||
const urlInput = document.getElementById('khoj-host-url');
|
||||
(async function() {
|
||||
(async function () {
|
||||
const url = await window.hostURLAPI.getURL();
|
||||
urlInput.value = url;
|
||||
})();
|
||||
|
@ -210,7 +219,7 @@ urlInput.addEventListener('blur', async () => {
|
|||
});
|
||||
|
||||
const khojKeyInput = document.getElementById('khoj-access-key');
|
||||
(async function() {
|
||||
(async function () {
|
||||
const token = await window.tokenAPI.getToken();
|
||||
khojKeyInput.value = token;
|
||||
})();
|
||||
|
|
|
@ -1,458 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0 maximum-scale=1.0">
|
||||
<meta property="og:image" content="https://assets.khoj.dev/khoj_hero.png">
|
||||
<title>Khoj - Search</title>
|
||||
|
||||
<link rel="icon" type="image/png" sizes="128x128" href="./assets/icons/favicon-128x128.png">
|
||||
<link rel="manifest" href="./khoj.webmanifest">
|
||||
<link rel="stylesheet" href="./assets/khoj.css">
|
||||
</head>
|
||||
<script type="text/javascript" src="./assets/org.min.js"></script>
|
||||
<script type="text/javascript" src="./assets/markdown-it.min.js"></script>
|
||||
<script src="./utils.js"></script>
|
||||
|
||||
<script>
|
||||
function render_image(item) {
|
||||
return `
|
||||
<div class="results-image">
|
||||
<a href="${item.entry}" class="image-link">
|
||||
<img id=${item.score} src="${item.entry}?${Math.random()}"
|
||||
title="Effective Score: ${item.score}, Meta: ${item.additional.metadata_score}, Image: ${item.additional.image_score}"
|
||||
class="image">
|
||||
</a>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function render_org(query, data, classPrefix="") {
|
||||
return data.map(function (item) {
|
||||
var orgParser = new Org.Parser();
|
||||
var orgDocument = orgParser.parse(item.entry);
|
||||
var orgHTMLDocument = orgDocument.convert(Org.ConverterHTML, { htmlClassPrefix: classPrefix, suppressNewLines: true });
|
||||
return `<div class="results-org">` + orgHTMLDocument.toString() + `</div>`;
|
||||
}).join("\n");
|
||||
}
|
||||
|
||||
function render_markdown(query, data) {
|
||||
var md = window.markdownit();
|
||||
return data.map(function (item) {
|
||||
let rendered = "";
|
||||
if (item.additional.file.startsWith("http")) {
|
||||
lines = item.entry.split("\n");
|
||||
rendered = md.render(`${lines[0]}\t[*](${item.additional.file})\n${lines.slice(1).join("\n")}`);
|
||||
}
|
||||
else {
|
||||
rendered = md.render(`${item.entry}`);
|
||||
}
|
||||
return `<div class="results-markdown">` + rendered + `</div>`;
|
||||
}).join("\n");
|
||||
}
|
||||
|
||||
function render_pdf(query, data) {
|
||||
return data.map(function (item) {
|
||||
let compiled_lines = item.additional.compiled.split("\n");
|
||||
let filename = compiled_lines.shift();
|
||||
let text_match = compiled_lines.join("\n")
|
||||
return `<div class="results-pdf">` + `<h2>${filename}</h2>\n<p>${text_match}</p>` + `</div>`;
|
||||
}).join("\n");
|
||||
}
|
||||
|
||||
function render_html(query, data) {
|
||||
return data.map(function (item) {
|
||||
let document = new DOMParser().parseFromString(item.entry, "text/html");
|
||||
// Scrub the HTML to remove any script tags and associated content
|
||||
let script_tags = document.querySelectorAll("script");
|
||||
for (let i = 0; i < script_tags.length; i++) {
|
||||
script_tags[i].remove();
|
||||
}
|
||||
// Scrub the HTML to remove any style tags and associated content
|
||||
let style_tags = document.querySelectorAll("style");
|
||||
for (let i = 0; i < style_tags.length; i++) {
|
||||
style_tags[i].remove();
|
||||
}
|
||||
// Scrub the HTML to remove any noscript tags and associated content
|
||||
let noscript_tags = document.querySelectorAll("noscript");
|
||||
for (let i = 0; i < noscript_tags.length; i++) {
|
||||
noscript_tags[i].remove();
|
||||
}
|
||||
// Scrub the HTML to remove any iframe tags and associated content
|
||||
let iframe_tags = document.querySelectorAll("iframe");
|
||||
for (let i = 0; i < iframe_tags.length; i++) {
|
||||
iframe_tags[i].remove();
|
||||
}
|
||||
// Scrub the HTML to remove any object tags and associated content
|
||||
let object_tags = document.querySelectorAll("object");
|
||||
for (let i = 0; i < object_tags.length; i++) {
|
||||
object_tags[i].remove();
|
||||
}
|
||||
// Scrub the HTML to remove any embed tags and associated content
|
||||
let embed_tags = document.querySelectorAll("embed");
|
||||
for (let i = 0; i < embed_tags.length; i++) {
|
||||
embed_tags[i].remove();
|
||||
}
|
||||
let scrubbedHTML = document.body.outerHTML;
|
||||
return `<div class="results-html">` + scrubbedHTML + `</div>`;
|
||||
}).join("\n");
|
||||
}
|
||||
|
||||
function render_xml(query, data) {
|
||||
return data.map(function (item) {
|
||||
return `<div class="results-xml">` +
|
||||
`<b><a href="${item.additional.file}">${item.additional.heading}</a></b>` +
|
||||
`<xml>${item.entry}</xml>` +
|
||||
`</div>`
|
||||
}).join("\n");
|
||||
}
|
||||
|
||||
function render_multiple(query, data, type) {
|
||||
let html = "";
|
||||
data.forEach(item => {
|
||||
if (item.additional.file.endsWith(".org")) {
|
||||
html += render_org(query, [item], "org-");
|
||||
} else if (
|
||||
item.additional.file.endsWith(".md") ||
|
||||
item.additional.file.endsWith(".markdown") ||
|
||||
(item.additional.file.includes("issues") && item.additional.source === "github") ||
|
||||
(item.additional.file.includes("commit") && item.additional.source === "github")
|
||||
)
|
||||
{
|
||||
html += render_markdown(query, [item]);
|
||||
} else if (item.additional.file.endsWith(".pdf")) {
|
||||
html += render_pdf(query, [item]);
|
||||
} else if (item.additional.source == "notion") {
|
||||
html += `<div class="results-notion">` + `<b><a href="${item.additional.file}">${item.additional.heading}</a></b>` + `<p>${item.entry}</p>` + `</div>`;
|
||||
} else if (item.additional.file.endsWith(".html")) {
|
||||
html += render_html(query, [item]);
|
||||
} else if (item.additional.file.endsWith(".xml")) {
|
||||
html += render_xml(query, [item])
|
||||
} else {
|
||||
html += `<div class="results-plugin">` + `<b><a href="${item.additional.file}">${item.additional.heading}</a></b>` + `<p>${item.entry}</p>` + `</div>`;
|
||||
}
|
||||
});
|
||||
return html;
|
||||
}
|
||||
|
||||
function render_results(data, query, type) {
|
||||
let results = "";
|
||||
if (type === "markdown") {
|
||||
results = render_markdown(query, data);
|
||||
} else if (type === "org") {
|
||||
results = render_org(query, data, "org-");
|
||||
} else if (type === "image") {
|
||||
results = data.map(render_image).join('');
|
||||
} else if (type === "pdf") {
|
||||
results = render_pdf(query, data);
|
||||
} else if (type === "github" || type === "all" || type === "notion") {
|
||||
results = render_multiple(query, data, type);
|
||||
} else {
|
||||
results = data.map((item) => `<div class="results-plugin">` + `<p>${item.entry}</p>` + `</div>`).join("\n")
|
||||
}
|
||||
|
||||
// Any POST rendering goes here.
|
||||
|
||||
let renderedResults = document.createElement("div");
|
||||
renderedResults.id = `results-${type}`;
|
||||
renderedResults.innerHTML = results;
|
||||
|
||||
// For all elements that are of type img in the results html and have a src with 'avatar' in the URL, add the class 'avatar'
|
||||
// This is used to make the avatar images round
|
||||
let images = renderedResults.querySelectorAll("img[src*='avatar']");
|
||||
for (let i = 0; i < images.length; i++) {
|
||||
images[i].classList.add("avatar");
|
||||
}
|
||||
|
||||
return renderedResults.outerHTML;
|
||||
}
|
||||
|
||||
async function search(rerank=false) {
|
||||
// Extract required fields for search from form
|
||||
query = document.getElementById("query").value.trim();
|
||||
type = 'all';
|
||||
results_count = localStorage.getItem("khojResultsCount") || 5;
|
||||
console.log(`Query: ${query}, Type: ${type}, Results Count: ${results_count}`);
|
||||
|
||||
// Short circuit on empty query
|
||||
if (query.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If set query field in url query param on rerank
|
||||
if (rerank)
|
||||
setQueryFieldInUrl(query);
|
||||
|
||||
// Execute Search and Render Results
|
||||
url = await createRequestUrl(query, type, results_count || 5, rerank);
|
||||
const khojToken = await window.tokenAPI.getToken();
|
||||
const headers = { 'Authorization': `Bearer ${khojToken}` };
|
||||
|
||||
fetch(url, { headers })
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
document.getElementById("results").innerHTML = render_results(data, query, type);
|
||||
});
|
||||
}
|
||||
|
||||
let debounceTimeout;
|
||||
function incrementalSearch(event) {
|
||||
// Run incremental search only after waitTime passed since the last key press
|
||||
let waitTime = 300;
|
||||
clearTimeout(debounceTimeout);
|
||||
debounceTimeout = setTimeout(() => {
|
||||
type = 'all';
|
||||
// Search with reranking on 'Enter'
|
||||
let should_rerank = event.key === 'Enter';
|
||||
search(rerank=should_rerank);
|
||||
}, waitTime);
|
||||
}
|
||||
|
||||
async function populate_type_dropdown() {
|
||||
const hostURL = await window.hostURLAPI.getURL();
|
||||
const khojToken = await window.tokenAPI.getToken();
|
||||
const headers = { 'Authorization': `Bearer ${khojToken}` };
|
||||
|
||||
// Populate type dropdown field with enabled content types only
|
||||
fetch(`${hostURL}/api/content/types`, { headers })
|
||||
.then(response => response.json())
|
||||
.then(enabled_types => {
|
||||
// Show warning if no content types are enabled
|
||||
if (enabled_types.detail) {
|
||||
document.getElementById("results").innerHTML = "<div id='results-error'>To use Khoj search, setup your content plugins on the Khoj <a class='inline-chat-link' href='/settings'>settings page</a>.</div>";
|
||||
document.getElementById("query").setAttribute("disabled", "disabled");
|
||||
document.getElementById("query").setAttribute("placeholder", "Configure Khoj to enable search");
|
||||
return [];
|
||||
}
|
||||
|
||||
return enabled_types;
|
||||
});
|
||||
}
|
||||
|
||||
async function createRequestUrl(query, type, results_count, rerank) {
|
||||
// Generate Backend API URL to execute Search
|
||||
const hostURL = await window.hostURLAPI.getURL();
|
||||
|
||||
let url = `${hostURL}/api/search?q=${encodeURIComponent(query)}&n=${results_count}&client=web`;
|
||||
// If type is not 'all', append type to URL
|
||||
if (type !== 'all')
|
||||
url += `&t=${type}`;
|
||||
// Rerank is only supported by text types
|
||||
if (type !== "image")
|
||||
url += `&r=${rerank}`;
|
||||
return url;
|
||||
}
|
||||
|
||||
function setQueryFieldInUrl(query) {
|
||||
var url = new URL(window.location.href);
|
||||
url.searchParams.set("q", query);
|
||||
window.history.pushState({}, "", url.href);
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", async() => {
|
||||
// Setup the header pane
|
||||
document.getElementById("khoj-header").innerHTML = await populateHeaderPane();
|
||||
// Setup the nav menu
|
||||
document.getElementById("profile-picture").addEventListener("click", toggleNavMenu);
|
||||
// Set the active nav pane
|
||||
document.getElementById("search-nav")?.classList.add("khoj-nav-selected");
|
||||
})
|
||||
|
||||
|
||||
window.addEventListener("load", async function() {
|
||||
// Dynamically populate type dropdown based on enabled content types and type passed as URL query parameter
|
||||
await populate_type_dropdown();
|
||||
|
||||
// Fill query field with value passed in URL query parameters, if any.
|
||||
var query_via_url = new URLSearchParams(window.location.search).get("q");
|
||||
if (query_via_url)
|
||||
document.getElementById("query").value = query_via_url;
|
||||
});
|
||||
</script>
|
||||
|
||||
<body>
|
||||
<!--Add Header Logo and Nav Pane-->
|
||||
<div id="khoj-header" class="khoj-header"></div>
|
||||
|
||||
<!--Add Text Box To Enter Query, Trigger Incremental Search OnChange -->
|
||||
<input type="text" id="query" class="option" onkeyup=incrementalSearch(event) autofocus="autofocus" placeholder="Search your knowledge base using natural language">
|
||||
|
||||
<!-- Section to Render Results -->
|
||||
<div id="results"></div>
|
||||
</body>
|
||||
|
||||
<style>
|
||||
@media only screen and (max-width: 600px) {
|
||||
body {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr auto auto auto minmax(80px, 100%);
|
||||
font-size: small!important;
|
||||
}
|
||||
body > * {
|
||||
grid-column: 1;
|
||||
}
|
||||
}
|
||||
@media only screen and (min-width: 600px) {
|
||||
body {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr min(70vw, 100%) 1fr;
|
||||
grid-template-rows: 1fr auto auto auto minmax(80px, 100%);
|
||||
padding-top: 60vw;
|
||||
}
|
||||
body > * {
|
||||
grid-column: 2;
|
||||
}
|
||||
}
|
||||
body {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
background: var(--background-color);
|
||||
color: var(--main-text-color);
|
||||
font-family: var(--font-family);
|
||||
font-size: small;
|
||||
font-weight: 300;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
body > * {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
#options {
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
#options > * {
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #475569;
|
||||
background: #f9fafc
|
||||
}
|
||||
.option:hover {
|
||||
box-shadow: 0 0 11px #aaa;
|
||||
}
|
||||
#options > button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#query {
|
||||
font-size: small;
|
||||
}
|
||||
#results {
|
||||
font-size: small;
|
||||
margin: 0px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.results-image {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
.image-link {
|
||||
place-self: center;
|
||||
}
|
||||
.image {
|
||||
width: 20vw;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #475569;
|
||||
}
|
||||
#json {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.results-pdf,
|
||||
.results-notion,
|
||||
.results-html,
|
||||
.results-plugin {
|
||||
text-align: left;
|
||||
white-space: pre-line;
|
||||
}
|
||||
.results-markdown,
|
||||
.results-github {
|
||||
text-align: left;
|
||||
}
|
||||
.results-org {
|
||||
text-align: left;
|
||||
/* white-space: pre-line; */
|
||||
}
|
||||
.results-org h3 {
|
||||
margin: 20px 0 0 0;
|
||||
font-size: small;
|
||||
}
|
||||
span.org-task-status {
|
||||
color: white;
|
||||
padding: 3.5px 3.5px 0;
|
||||
margin-right: 5px;
|
||||
border-radius: 5px;
|
||||
background-color: #eab308;
|
||||
font-size: small;
|
||||
}
|
||||
span.org-task-status.todo {
|
||||
background-color: #3b82f6
|
||||
}
|
||||
span.org-task-status.done {
|
||||
background-color: #22c55e;
|
||||
}
|
||||
span.org-task-tag {
|
||||
color: white;
|
||||
padding: 3.5px 3.5px 0;
|
||||
margin-right: 5px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #475569;
|
||||
background-color: #ef4444;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
pre {
|
||||
max-width: 100;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3b82f6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
img.avatar {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
div#results-error,
|
||||
div.results-markdown,
|
||||
div.results-notion,
|
||||
div.results-org,
|
||||
div.results-plugin,
|
||||
div.results-html,
|
||||
div.results-pdf {
|
||||
text-align: left;
|
||||
box-shadow: 2px 2px 2px var(--primary-hover);
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border: 4px solid rgb(229, 229, 229);
|
||||
}
|
||||
|
||||
div#results-error {
|
||||
box-shadow: 2px 2px 2px #FF5722;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
@keyframes gradient {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
a.khoj-logo {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
</html>
|
|
@ -1,16 +1,264 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0 maximum-scale=1.0">
|
||||
<title>Khoj - Settings</title>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<link rel="icon" type="image/png" sizes="128x128" href="./assets/icons/favicon-128x128.png">
|
||||
<link rel="manifest" href="./khoj.webmanifest">
|
||||
<link rel="stylesheet" href="./assets/khoj.css">
|
||||
</head>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Khoj - Settings</title>
|
||||
<link rel="icon" type="image/png" sizes="128x128" href="./assets/icons/favicon-128x128.png">
|
||||
<link rel="stylesheet" href="./assets/khoj.css">
|
||||
<style>
|
||||
:root {
|
||||
--background-color: #f9fafb;
|
||||
--primary-color: hsla(24.6 95% 53.1%);
|
||||
--secondary-color: #f3f4f6;
|
||||
--text-color: #111827;
|
||||
--card-bg: #ffffff;
|
||||
--card-border: #e5e7eb;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: var(--font-family);
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
/* Main Content */
|
||||
.main-content {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.section-cards {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: var(--card-bg);
|
||||
border: 1px solid var(--card-border);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.card-input {
|
||||
width: -webkit-fill-available;
|
||||
padding: 10px;
|
||||
border: 1px solid var(--card-border);
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
font-family: var(--font-family);
|
||||
}
|
||||
|
||||
.card-button {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.card-button:hover {
|
||||
background-color: hsla(24.6 95% 53.1% / 0.5);
|
||||
}
|
||||
|
||||
.secondary-button {
|
||||
background-color: var(--secondary-color);
|
||||
color: var(--text-color);
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.secondary-button:hover {
|
||||
background-color: #e5e7eb;
|
||||
}
|
||||
|
||||
.sync-data {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
align-items: baseline;
|
||||
gap: 10px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
div.folder-element,
|
||||
div.file-element {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
img.file-synced-image {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
div.remove-button-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: right;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
button.remove-folder-button,
|
||||
button.remove-file-button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
button.remove-folder-button:hover,
|
||||
button.remove-file-button:hover {
|
||||
background-color: #f3f4f6;
|
||||
}
|
||||
|
||||
div#sync-status {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
div#big-actions {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
div#big-actions button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.content-name:hover {
|
||||
text-decoration: underline;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.sync-icon,
|
||||
.clock-icon,
|
||||
.trash-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
#loading-bar {
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: linear-gradient(90deg,
|
||||
hsla(24.6 95% 53.1% / 0.2) 0%,
|
||||
var(--primary-color) 50%,
|
||||
hsla(24.6 95% 53.1% / 0.2) 100%);
|
||||
border-radius: 2px;
|
||||
background-size: 200% 100%;
|
||||
animation: loading 1.5s infinite;
|
||||
margin: 10px 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
#loading-bar[style*="display: none"] {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#loading-bar {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
details.collapsible {
|
||||
background-color: var(--card-bg);
|
||||
border: 1px solid var(--card-border);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
details.collapsible:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
details.collapsible summary {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
list-style: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
details.collapsible summary::after {
|
||||
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>');
|
||||
font-size: 0.8em;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
details.collapsible[open] summary::after {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
details.collapsible .content {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
<script src="./utils.js"></script>
|
||||
<script>
|
||||
window.addEventListener("DOMContentLoaded", async() => {
|
||||
window.addEventListener("DOMContentLoaded", async () => {
|
||||
// Setup the header pane
|
||||
document.getElementById("khoj-header").innerHTML = await populateHeaderPane();
|
||||
// Setup the nav menu
|
||||
|
@ -19,358 +267,54 @@
|
|||
document.getElementById("settings-nav")?.classList.add("khoj-nav-selected");
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--Add Header Logo and Nav Pane-->
|
||||
<body>
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<div id="khoj-header" class="khoj-header"></div>
|
||||
<div class="section-cards">
|
||||
<div class="card-description-row">
|
||||
<div class="card configuration">
|
||||
<div class="card-title-row">
|
||||
<img class="card-icon" src="./assets/icons/link.svg" alt="Khoj Server URL">
|
||||
<h3 class="card-title">
|
||||
Server URL
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-description-row">
|
||||
<input id="khoj-host-url" class="card-input" type="text">
|
||||
</div>
|
||||
<div class="card-title-row">
|
||||
<img class="card-icon" src="./assets/icons/key.svg" alt="Khoj Access Key">
|
||||
<h3 class="card-title">
|
||||
API Key
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-description-row">
|
||||
<input id="khoj-access-key" class="card-input" type="text" placeholder="Enter API key to access your Khoj">
|
||||
</div>
|
||||
<!-- Replace the server URL and API key cards with: -->
|
||||
<details class="collapsible">
|
||||
<summary>Server URL</summary>
|
||||
<div class="content">
|
||||
<input type="text" class="card-input" id="khoj-host-url" placeholder="Enter server URL">
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="collapsible">
|
||||
<summary>API Key</summary>
|
||||
<div class="content">
|
||||
<input type="text" class="card-input" id="khoj-access-key" placeholder="Enter API key">
|
||||
</div>
|
||||
</details>
|
||||
<div class="card">
|
||||
<div class="card-title">Files</div>
|
||||
<div id="current-files"></div>
|
||||
<button class="secondary-button" id="update-file" title="Add a file to be indexed to your Khoj knowledge base. Will be synced automatically.">Add File</button>
|
||||
</div>
|
||||
<div class="card-description-row">
|
||||
<div class="card configuration">
|
||||
<div class="card-title-row">
|
||||
<img class="card-icon" src="./assets/icons/plaintext.svg" alt="File">
|
||||
<h3 class="card-title">
|
||||
Files
|
||||
<button id="toggle-files" class="card-button">
|
||||
<svg id="toggle-files-svg" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12l7 7 7-7"></path></svg>
|
||||
</button>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-description-row">
|
||||
<div id="current-files"></div>
|
||||
</div>
|
||||
<div class="card-action-row">
|
||||
<button id="update-file" class="card-button">
|
||||
Add
|
||||
<img class="add-files-icon" src="./assets/icons/circular-add.svg" alt="Add">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-title">Folders</div>
|
||||
<div id="current-folders"></div>
|
||||
<button class="secondary-button" id="update-folder" title="Add a folder to be indexed to your Khoj knowledge base. All valid files will be synced automatically.">Add Folder</button>
|
||||
</div>
|
||||
<div class="card-description-row">
|
||||
<div class="card configuration">
|
||||
<div class="card-title-row">
|
||||
<img class="card-icon" src="./assets/icons/folder.svg" alt="Folder">
|
||||
<h3 class="card-title">
|
||||
Folders
|
||||
<button id="toggle-folders" class="card-button">
|
||||
<svg id="toggle-folders-svg" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14M5 12l7 7 7-7"></path></svg>
|
||||
</button>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="card-description-row">
|
||||
<div id="current-folders"></div>
|
||||
</div>
|
||||
<div class="card-action-row">
|
||||
<button id="update-folder" class="card-button">
|
||||
Add
|
||||
<img class="add-files-icon" src="./assets/icons/circular-add.svg" alt="Add">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section-action-row">
|
||||
<div class="card-description-row">
|
||||
<button id="sync-force" class="sync-data">💾 Save</button>
|
||||
</div>
|
||||
<div class="card-description-row">
|
||||
<button id="delete-all" class="sync-data">🗑️ Delete All</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="loading-bar" style="display: none;"></div>
|
||||
<div class="card-description-row">
|
||||
<div class="sync-data">
|
||||
<div id="loading-bar" style="display: none;"></div>
|
||||
<div id="sync-status"></div>
|
||||
<div id="big-actions">
|
||||
<button class="card-button" id="sync-force" title="Delete and re-index all configured files and folders">
|
||||
<img src="./assets/icons/upload.svg" class="sync-icon" alt="Sync">
|
||||
Force Sync
|
||||
</button>
|
||||
<button class="card-button" id="delete-all" title="Remove all indexed content from Khoj">
|
||||
<img src="./assets/icons/trash-solid.svg" class="trash-icon" alt="Delete All">
|
||||
Delete All
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<style>
|
||||
@media only screen and (max-width: 600px) {
|
||||
body {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr auto;
|
||||
font-size: small!important;
|
||||
}
|
||||
body > * {
|
||||
grid-column: 1;
|
||||
}
|
||||
}
|
||||
@media only screen and (min-width: 600px) {
|
||||
body {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr min(70vw, 100%) 1fr;
|
||||
grid-template-rows: 80px auto;
|
||||
}
|
||||
body > * {
|
||||
grid-column: 2;
|
||||
}
|
||||
}
|
||||
body, input {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
background: var(--background-color);
|
||||
color: #475569;
|
||||
font-family: var(--font-family);
|
||||
font-size: small;
|
||||
font-weight: 300;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
body > * {
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
svg {
|
||||
transition: transform 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
a.khoj-logo {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#loading-bar {
|
||||
height: 10px;
|
||||
width: 100%;
|
||||
background-color: #ddd;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#loading-bar:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: -200px;
|
||||
width: 200px;
|
||||
height: 100%;
|
||||
background-color: #2980b9;
|
||||
animation: loading-bar 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes loading-bar {
|
||||
0% {
|
||||
left: -200px;
|
||||
}
|
||||
100% {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.card-input {
|
||||
padding: 4px;
|
||||
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.3);
|
||||
border: none;
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
.card {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
padding: 24px 16px;
|
||||
width: 450px;
|
||||
background: var(--background-color);
|
||||
border: 1px solid rgb(229, 229, 229);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.1),0px 1px 2px -1px rgba(0,0,0,0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.section-cards {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
justify-items: center;
|
||||
margin: 0;
|
||||
}
|
||||
.section-action-row {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
gap: 16px;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.card-title-row {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
padding: 0;
|
||||
gap: 12px;
|
||||
}
|
||||
.card-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.add-files-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.card-title {
|
||||
font-size: medium;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
align-self: center;
|
||||
}
|
||||
.card-title-text {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.card-description {
|
||||
margin: 0;
|
||||
color: grey;
|
||||
font-size: small;
|
||||
}
|
||||
.card-button-row {
|
||||
display: grid;
|
||||
grid-template-columns: auto;
|
||||
text-align: right;
|
||||
}
|
||||
.card-button {
|
||||
border: none;
|
||||
font-weight: bold;
|
||||
color: rgb(64,64,64);
|
||||
background: transparent;
|
||||
font-size: small;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 32px;
|
||||
text-align: right;
|
||||
}
|
||||
.primary-button {
|
||||
border: none;
|
||||
color: var(--background-color);
|
||||
padding: 15px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
button.card-button.disabled {
|
||||
color: var(--flower);
|
||||
background: transparent;
|
||||
font-size: small;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 32px;
|
||||
text-align: right;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
button.card-button.happy {
|
||||
color: var(--leaf);
|
||||
}
|
||||
|
||||
img.configured-icon {
|
||||
max-width: 16px;
|
||||
}
|
||||
|
||||
div.card-action-row.enabled{
|
||||
display: block;
|
||||
}
|
||||
|
||||
img.configured-icon.enabled {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.card-action-row.disabled,
|
||||
img.configured-icon.disabled {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.file-element,
|
||||
div.folder-element {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
border: 1px solid rgb(229, 229, 229);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.1),0px 1px 2px -1px rgba(0,0,0,0.8);
|
||||
padding: 4px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
div.content-name {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
div.remove-button-container {
|
||||
text-align: right;
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.file-synced-image{
|
||||
width: 20px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
button.remove-folder-button,
|
||||
button.remove-file-button {
|
||||
background-color: rgb(253 214 214);
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
color: var(--flower);
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
button.remove-folder-button:hover,
|
||||
button.remove-file-button:hover {
|
||||
background-color: rgb(255 235 235);
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
color: var(--flower);
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
button.sync-data {
|
||||
background-color: var(--primary-hover);
|
||||
border: none;
|
||||
color: var(--main-text-color);
|
||||
padding: 12px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
box-shadow: 0px 5px 0px var(--background-color);
|
||||
}
|
||||
|
||||
button.sync-data:hover {
|
||||
background-color: var(--summer-sun);
|
||||
box-shadow: 0px 3px 0px var(--background-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
.sync-force-toggle {
|
||||
align-content: center;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
gap: 4px;
|
||||
}
|
||||
</style>
|
||||
<script src="./renderer.js"></script>
|
||||
</div>
|
||||
</body>
|
||||
<script src="./renderer.js"></script>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -31,7 +31,7 @@ function toggleNavMenu() {
|
|||
}
|
||||
|
||||
// Close the dropdown menu if the user clicks outside of it
|
||||
document.addEventListener('click', function(event) {
|
||||
document.addEventListener('click', function (event) {
|
||||
let menu = document.getElementById("khoj-nav-menu");
|
||||
let menuContainer = document.getElementById("khoj-nav-menu-container");
|
||||
let isClickOnMenu = menuContainer?.contains(event.target) || menuContainer === event.target;
|
||||
|
@ -56,25 +56,19 @@ async function populateHeaderPane() {
|
|||
// Populate the header element with the navigation pane
|
||||
return `
|
||||
<a class="khoj-logo" href="/">
|
||||
<img class="khoj-logo" src="./assets/icons/khoj-logo-sideways-500.png" alt="Khoj"></img>
|
||||
<img class="khoj-logo" src="./assets/icons/khoj_logo.png" alt="Khoj"></img>
|
||||
</a>
|
||||
<nav class="khoj-nav">
|
||||
${
|
||||
userInfo && userInfo.email
|
||||
? `<div class="khoj-status-box">
|
||||
${userInfo && userInfo.email
|
||||
? `<div class="khoj-status-box">
|
||||
<span class="khoj-status-connected"></span>
|
||||
<span class="khoj-status-text">Connected to server</span>
|
||||
</div>`
|
||||
: `<div class="khoj-status-box">
|
||||
: `<div class="khoj-status-box">
|
||||
<span class="khoj-status-not-connected"></span>
|
||||
<span class="khoj-status-text">Not connected to server</span>
|
||||
</div>`
|
||||
}
|
||||
<a id="chat-nav" class="khoj-nav" href="./chat.html">
|
||||
<img class="nav-icon" src="./assets/icons/chat.svg" alt="Chat">
|
||||
<span class="khoj-nav-item-text">Chat</span>
|
||||
</a>
|
||||
${has_documents ? '<a id="search-nav" class="khoj-nav" href="./search.html"><img class="nav-icon" src="./assets/icons/search.svg" alt="Search"> <span class="khoj-nav-item-text">Search</span></a>' : ''}
|
||||
}
|
||||
${username ? `
|
||||
<div id="khoj-nav-menu-container" class="khoj-nav dropdown">
|
||||
${user_photo && user_photo != "None" ? `
|
||||
|
@ -84,7 +78,10 @@ async function populateHeaderPane() {
|
|||
`}
|
||||
<div id="khoj-nav-menu" class="khoj-nav-dropdown-content">
|
||||
<div class="khoj-nav-username"> ${username} </div>
|
||||
<a id="settings-nav" class="khoj-nav" href="./settings.html">⚙️ Settings</a>
|
||||
<a onclick="window.navigateAPI.navigateToWebHome()" class="khoj-nav-link">
|
||||
<img class="khoj-nav-icon" src="./assets/icons/open-link.svg" alt="Open Host Url"></img>
|
||||
Open App
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
` : ''}
|
||||
|
|
|
@ -50,17 +50,17 @@
|
|||
dependencies:
|
||||
defer-to-connect "^2.0.0"
|
||||
|
||||
"@todesktop/runtime@^1.6.4":
|
||||
version "1.6.4"
|
||||
resolved "https://registry.yarnpkg.com/@todesktop/runtime/-/runtime-1.6.4.tgz#a9d62a021cf2647c51371c892bfb1d4c5a29ed7e"
|
||||
integrity sha512-n6dOxhrKKsXMM+i2u9iRvoJSR2KCWw0orYK+FT9RbWNPykhuFIYd0yy8dYgYy/OuClKGyGl4SJFi2757FLhWDA==
|
||||
"@todesktop/runtime@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@todesktop/runtime/-/runtime-2.0.0.tgz#dfd409186ae664f5e28186a03b99e620ec7b7f82"
|
||||
integrity sha512-0a2tmWpIc/HJE/873xRMZKQNggfrYhoKYIchfN+k8RqKdzTPwTWa5ztur7GdCHLHBUaiMBPNRzF3h4kwHd1NCw==
|
||||
dependencies:
|
||||
del "^6.0.0"
|
||||
electron-updater "^4.6.1"
|
||||
eventemitter2 "^6.4.5"
|
||||
del "^6.1.1"
|
||||
electron-updater "^6.3.9"
|
||||
eventemitter2 "^6.4.9"
|
||||
execa "^5.0.0"
|
||||
lodash.once "^4.1.1"
|
||||
semver "^7.3.2"
|
||||
semver "^7.6.3"
|
||||
|
||||
"@types/cacheable-request@^6.0.1":
|
||||
version "6.0.3"
|
||||
|
@ -90,16 +90,16 @@
|
|||
integrity sha512-jYvz8UMLDgy3a5SkGJne8H7VA7zPV2Lwohjx0V8V31+SqAjNmurWMkk9cQhfvlcnXWudBpK9xPM1n4rljOcHYQ==
|
||||
|
||||
"@types/node@*":
|
||||
version "22.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.1.tgz#bdf91c36e0e7ecfb7257b2d75bf1b206b308ca71"
|
||||
integrity sha512-p8Yy/8sw1caA8CdRIQBG5tiLHmxtQKObCijiAa9Ez+d4+PRffM4054xbju0msf+cvhJpnFEeNjxmVT/0ipktrg==
|
||||
version "22.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.1.tgz#41ffeee127b8975a05f8c4f83fb89bcb2987d766"
|
||||
integrity sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==
|
||||
dependencies:
|
||||
undici-types "~6.19.8"
|
||||
undici-types "~6.20.0"
|
||||
|
||||
"@types/node@^18.11.18":
|
||||
version "18.19.64"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.64.tgz#122897fb79f2a9ec9c979bded01c11461b2b1478"
|
||||
integrity sha512-955mDqvO2vFf/oL7V3WiUtiz+BugyX8uVbaT2H8oj3+8dRyH2FLiNdowe7eNqRM7IOIZvzDH76EoAT+gwm6aIQ==
|
||||
version "18.19.67"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.67.tgz#77c4b01641a1e3e1509aff7e10d39e4afd5ae06d"
|
||||
integrity sha512-wI8uHusga+0ZugNp0Ol/3BqQfEcCCNfojtO6Oou9iVNGPTL6QNSdnUdqq85fRgIorLhLMuPIKpsN98QE9Nh+KQ==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
|
||||
|
@ -110,11 +110,6 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/semver@^7.3.6":
|
||||
version "7.5.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
|
||||
integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
|
||||
|
||||
"@types/yauzl@^2.9.1":
|
||||
version "2.10.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999"
|
||||
|
@ -168,9 +163,9 @@ atomically@^1.7.0:
|
|||
integrity sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==
|
||||
|
||||
axios@^1.7.4:
|
||||
version "1.7.7"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f"
|
||||
integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==
|
||||
version "1.7.8"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.8.tgz#1997b1496b394c21953e68c14aaa51b7b5de3d6e"
|
||||
integrity sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==
|
||||
dependencies:
|
||||
follow-redirects "^1.15.6"
|
||||
form-data "^4.0.0"
|
||||
|
@ -206,12 +201,12 @@ buffer-crc32@~0.2.3:
|
|||
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||
integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
|
||||
|
||||
builder-util-runtime@8.9.2:
|
||||
version "8.9.2"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.9.2.tgz#a9669ae5b5dcabfe411ded26678e7ae997246c28"
|
||||
integrity sha512-rhuKm5vh7E0aAmT6i8aoSfEjxzdYEFX7zDApK+eNgOhjofnWb74d9SRJv0H/8nsgOkos0TZ4zxW0P8J4N7xQ2A==
|
||||
builder-util-runtime@9.2.10:
|
||||
version "9.2.10"
|
||||
resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-9.2.10.tgz#a0f7d9e214158402e78b74a745c8d9f870c604bc"
|
||||
integrity sha512-6p/gfG1RJSQeIbz8TK5aPNkoztgY1q5TgmGFMAXcY8itsGW6Y2ld1ALsZ5UJn8rog7hKF3zHx5iQbNQ8uLcRlw==
|
||||
dependencies:
|
||||
debug "^4.3.2"
|
||||
debug "^4.3.4"
|
||||
sax "^1.2.4"
|
||||
|
||||
cacheable-lookup@^5.0.3:
|
||||
|
@ -296,7 +291,7 @@ debounce-fn@^4.0.0:
|
|||
dependencies:
|
||||
mimic-fn "^3.0.0"
|
||||
|
||||
debug@^4.1.0, debug@^4.1.1, debug@^4.3.2:
|
||||
debug@^4.1.0, debug@^4.1.1, debug@^4.3.4:
|
||||
version "4.3.7"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
|
||||
integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
|
||||
|
@ -333,7 +328,7 @@ define-properties@^1.2.1:
|
|||
has-property-descriptors "^1.0.0"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
del@^6.0.0:
|
||||
del@^6.1.1:
|
||||
version "6.1.1"
|
||||
resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a"
|
||||
integrity sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==
|
||||
|
@ -379,19 +374,19 @@ electron-store@^8.1.0:
|
|||
conf "^10.2.0"
|
||||
type-fest "^2.17.0"
|
||||
|
||||
electron-updater@^4.6.1:
|
||||
version "4.6.5"
|
||||
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.6.5.tgz#e9a75458bbfd6bb41a58a829839e150ad2eb2d3d"
|
||||
integrity sha512-kdTly8O9mSZfm9fslc1mnCY+mYOeaYRy7ERa2Fed240u01BKll3aiupzkd07qKw69KvhBSzuHroIW3mF0D8DWA==
|
||||
electron-updater@^6.3.9:
|
||||
version "6.3.9"
|
||||
resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-6.3.9.tgz#e1e7f155624c58e6f3760f376c3a584028165ec4"
|
||||
integrity sha512-2PJNONi+iBidkoC5D1nzT9XqsE8Q1X28Fn6xRQhO3YX8qRRyJ3mkV4F1aQsuRnYPqq6Hw+E51y27W75WgDoofw==
|
||||
dependencies:
|
||||
"@types/semver" "^7.3.6"
|
||||
builder-util-runtime "8.9.2"
|
||||
fs-extra "^10.0.0"
|
||||
builder-util-runtime "9.2.10"
|
||||
fs-extra "^10.1.0"
|
||||
js-yaml "^4.1.0"
|
||||
lazy-val "^1.0.5"
|
||||
lodash.escaperegexp "^4.1.2"
|
||||
lodash.isequal "^4.5.0"
|
||||
semver "^7.3.5"
|
||||
semver "^7.6.3"
|
||||
tiny-typed-emitter "^2.1.0"
|
||||
|
||||
electron@28.2.1:
|
||||
version "28.2.1"
|
||||
|
@ -436,7 +431,7 @@ escape-string-regexp@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||
|
||||
eventemitter2@^6.4.5:
|
||||
eventemitter2@^6.4.9:
|
||||
version "6.4.9"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.9.tgz#41f2750781b4230ed58827bc119d293471ecb125"
|
||||
integrity sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==
|
||||
|
@ -530,7 +525,7 @@ form-data@^4.0.0:
|
|||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
fs-extra@^10.0.0:
|
||||
fs-extra@^10.1.0:
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
|
||||
integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
|
||||
|
@ -1115,7 +1110,7 @@ semver@^6.2.0:
|
|||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
|
||||
semver@^7.3.2, semver@^7.3.5:
|
||||
semver@^7.3.2, semver@^7.3.5, semver@^7.6.3:
|
||||
version "7.6.3"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
|
||||
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||
|
@ -1166,6 +1161,11 @@ sumchecker@^3.0.1:
|
|||
dependencies:
|
||||
debug "^4.1.0"
|
||||
|
||||
tiny-typed-emitter@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz#b3b027fdd389ff81a152c8e847ee2f5be9fad7b5"
|
||||
integrity sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==
|
||||
|
||||
to-regex-range@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
|
||||
|
@ -1188,10 +1188,10 @@ undici-types@~5.26.4:
|
|||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
|
||||
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||
|
||||
undici-types@~6.19.8:
|
||||
version "6.19.8"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
|
||||
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
|
||||
undici-types@~6.20.0:
|
||||
version "6.20.0"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
|
||||
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
|
||||
|
||||
universalify@^0.1.0:
|
||||
version "0.1.2"
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
;; Saba Imran <saba@khoj.dev>
|
||||
;; Description: Your Second Brain
|
||||
;; Keywords: search, chat, ai, org-mode, outlines, markdown, pdf, image
|
||||
;; Version: 1.30.1
|
||||
;; Version: 1.31.0
|
||||
;; Package-Requires: ((emacs "27.1") (transient "0.3.0") (dash "2.19.1"))
|
||||
;; URL: https://github.com/khoj-ai/khoj/tree/master/src/interface/emacs
|
||||
|
||||
|
@ -434,9 +434,9 @@ Auto invokes setup steps on calling main entrypoint."
|
|||
Append 'TYPE-QUERY' as query parameter in request url.
|
||||
Specify `BOUNDARY' used to separate files in request header."
|
||||
(let ((url-request-method (if force "PUT" "PATCH"))
|
||||
(url-request-data body)
|
||||
(url-request-extra-headers `(("content-type" . ,(format "multipart/form-data; boundary=%s" boundary))
|
||||
("Authorization" . ,(format "Bearer %s" khoj-api-key)))))
|
||||
(url-request-data (encode-coding-string body 'utf-8))
|
||||
(url-request-extra-headers `(("content-type" . ,(format "multipart/form-data; boundary=%s" boundary))
|
||||
("Authorization" . ,(encode-coding-string (format "Bearer %s" khoj-api-key) 'utf-8)))))
|
||||
(with-current-buffer
|
||||
(url-retrieve (format "%s/api/content?%s&client=emacs" khoj-server-url type-query)
|
||||
;; render response from indexing API endpoint on server
|
||||
|
@ -668,9 +668,9 @@ Simplified fork of `org-cycle-content' from Emacs 29.1 to work with >=27.1."
|
|||
"Sync call API at PATH with METHOD, query PARAMS and BODY as kv assoc list.
|
||||
Optionally apply CALLBACK with JSON parsed response and CBARGS."
|
||||
(let* ((url-request-method (or method "GET"))
|
||||
(url-request-extra-headers `(("Authorization" . ,(format "Bearer %s" khoj-api-key))))
|
||||
(url-request-extra-headers `(("Authorization" . ,(format "Bearer %s" khoj-api-key)) ("Content-Type" . "application/json")))
|
||||
(url-request-data (if body (json-encode body) nil))
|
||||
(url-request-extra-headers `(("Authorization" . ,(encode-coding-string (format "Bearer %s" khoj-api-key) 'utf-8))
|
||||
("Content-Type" . "application/json")))
|
||||
(url-request-data (if body (encode-coding-string (json-encode body) 'utf-8) nil))
|
||||
(param-string (url-build-query-string (append params '((client "emacs")))))
|
||||
(query-url (format "%s%s?%s" khoj-server-url path param-string))
|
||||
(cbargs (if (and (listp cbargs) (listp (car cbargs))) (car cbargs) cbargs))) ; normalize cbargs to (a b) from ((a b)) if required
|
||||
|
@ -689,8 +689,9 @@ Optionally apply CALLBACK with JSON parsed response and CBARGS."
|
|||
"Async call to API at PATH with specified METHOD, query PARAMS and request BODY.
|
||||
Optionally apply CALLBACK with JSON parsed response and CBARGS."
|
||||
(let* ((url-request-method (or method "GET"))
|
||||
(url-request-extra-headers `(("Authorization" . ,(format "Bearer %s" khoj-api-key)) ("Content-Type" . "application/json")))
|
||||
(url-request-data (if body (json-encode body) nil))
|
||||
(url-request-extra-headers `(("Authorization" . ,(encode-coding-string (format "Bearer %s" khoj-api-key) 'utf-8))
|
||||
("Content-Type" . "application/json")))
|
||||
(url-request-data (if body (encode-coding-string (json-encode body) 'utf-8) nil))
|
||||
(param-string (url-build-query-string (append params '((client "emacs")))))
|
||||
(query-url (format "%s%s?%s" khoj-server-url path param-string))
|
||||
(cbargs (if (and (listp cbargs) (listp (car cbargs))) (car cbargs) cbargs))) ; normalize cbargs to (a b) from ((a b)) if required
|
||||
|
@ -716,7 +717,10 @@ Optionally apply CALLBACK with JSON parsed response and CBARGS."
|
|||
Render search results in BUFFER-NAME using CONTENT-TYPE and QUERY.
|
||||
Filter out first similar result if IS-FIND-SIMILAR set."
|
||||
(let* ((rerank (or rerank "false"))
|
||||
(params `((q ,query) (t ,content-type) (r ,rerank) (n ,khoj-results-count)))
|
||||
(params `((q ,(encode-coding-string query 'utf-8))
|
||||
(t ,content-type)
|
||||
(r ,rerank)
|
||||
(n ,khoj-results-count)))
|
||||
(path "/api/search"))
|
||||
(khoj--call-api-async path
|
||||
"GET"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "khoj",
|
||||
"name": "Khoj",
|
||||
"version": "1.30.1",
|
||||
"version": "1.31.0",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "Your Second Brain",
|
||||
"author": "Khoj Inc.",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Khoj",
|
||||
"version": "1.30.1",
|
||||
"version": "1.31.0",
|
||||
"description": "Your Second Brain",
|
||||
"author": "Debanjum Singh Solanky, Saba Imran <team@khoj.dev>",
|
||||
"license": "GPL-3.0-or-later",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {ItemView, MarkdownRenderer, Scope, WorkspaceLeaf, request, requestUrl, setIcon, Platform} from 'obsidian';
|
||||
import { ItemView, MarkdownRenderer, Scope, WorkspaceLeaf, request, requestUrl, setIcon, Platform } from 'obsidian';
|
||||
import * as DOMPurify from 'dompurify';
|
||||
import { KhojSetting } from 'src/settings';
|
||||
import { KhojPaneView } from 'src/pane_view';
|
||||
|
@ -27,6 +27,7 @@ interface ChatMessageState {
|
|||
newResponseEl: HTMLElement | null;
|
||||
loadingEllipsis: HTMLElement | null;
|
||||
references: any;
|
||||
generatedAssets: string;
|
||||
rawResponse: string;
|
||||
rawQuery: string;
|
||||
isVoice: boolean;
|
||||
|
@ -46,10 +47,10 @@ export class KhojChatView extends KhojPaneView {
|
|||
waitingForLocation: boolean;
|
||||
location: Location = { timezone: Intl.DateTimeFormat().resolvedOptions().timeZone };
|
||||
keyPressTimeout: NodeJS.Timeout | null = null;
|
||||
userMessages: string[] = []; // Store user sent messages for input history cycling
|
||||
currentMessageIndex: number = -1; // Track current message index in userMessages array
|
||||
private currentUserInput: string = ""; // Stores the current user input that is being typed in chat
|
||||
private startingMessage: string = "Message";
|
||||
userMessages: string[] = []; // Store user sent messages for input history cycling
|
||||
currentMessageIndex: number = -1; // Track current message index in userMessages array
|
||||
private currentUserInput: string = ""; // Stores the current user input that is being typed in chat
|
||||
private startingMessage: string = "Message";
|
||||
chatMessageState: ChatMessageState;
|
||||
|
||||
constructor(leaf: WorkspaceLeaf, setting: KhojSetting) {
|
||||
|
@ -102,14 +103,14 @@ export class KhojChatView extends KhojPaneView {
|
|||
|
||||
// Clear text after extracting message to send
|
||||
let user_message = input_el.value.trim();
|
||||
// Store the message in the array if it's not empty
|
||||
if (user_message) {
|
||||
this.userMessages.push(user_message);
|
||||
// Update starting message after sending a new message
|
||||
const modifierKey = Platform.isMacOS ? '⌘' : '^';
|
||||
this.startingMessage = `(${modifierKey}+↑/↓) for prev messages`;
|
||||
input_el.placeholder = this.startingMessage;
|
||||
}
|
||||
// Store the message in the array if it's not empty
|
||||
if (user_message) {
|
||||
this.userMessages.push(user_message);
|
||||
// Update starting message after sending a new message
|
||||
const modifierKey = Platform.isMacOS ? '⌘' : '^';
|
||||
this.startingMessage = `(${modifierKey}+↑/↓) for prev messages`;
|
||||
input_el.placeholder = this.startingMessage;
|
||||
}
|
||||
input_el.value = "";
|
||||
this.autoResize();
|
||||
|
||||
|
@ -162,9 +163,9 @@ export class KhojChatView extends KhojPaneView {
|
|||
})
|
||||
chatInput.addEventListener('input', (_) => { this.onChatInput() });
|
||||
chatInput.addEventListener('keydown', (event) => {
|
||||
this.incrementalChat(event);
|
||||
this.handleArrowKeys(event);
|
||||
});
|
||||
this.incrementalChat(event);
|
||||
this.handleArrowKeys(event);
|
||||
});
|
||||
|
||||
// Add event listeners for long press keybinding
|
||||
this.contentEl.addEventListener('keydown', this.handleKeyDown.bind(this));
|
||||
|
@ -199,7 +200,7 @@ export class KhojChatView extends KhojPaneView {
|
|||
// Get chat history from Khoj backend and set chat input state
|
||||
let getChatHistorySucessfully = await this.getChatHistory(chatBodyEl);
|
||||
|
||||
let placeholderText : string = getChatHistorySucessfully ? this.startingMessage : "Configure Khoj to enable chat";
|
||||
let placeholderText: string = getChatHistorySucessfully ? this.startingMessage : "Configure Khoj to enable chat";
|
||||
chatInput.placeholder = placeholderText;
|
||||
chatInput.disabled = !getChatHistorySucessfully;
|
||||
|
||||
|
@ -214,7 +215,7 @@ export class KhojChatView extends KhojPaneView {
|
|||
});
|
||||
}
|
||||
|
||||
startSpeechToText(event: KeyboardEvent | MouseEvent | TouchEvent, timeout=200) {
|
||||
startSpeechToText(event: KeyboardEvent | MouseEvent | TouchEvent, timeout = 200) {
|
||||
if (!this.keyPressTimeout) {
|
||||
this.keyPressTimeout = setTimeout(async () => {
|
||||
// Reset auto send voice message timer, UI if running
|
||||
|
@ -320,7 +321,7 @@ export class KhojChatView extends KhojPaneView {
|
|||
referenceButton.tabIndex = 0;
|
||||
|
||||
// Add event listener to toggle full reference on click
|
||||
referenceButton.addEventListener('click', function() {
|
||||
referenceButton.addEventListener('click', function () {
|
||||
if (this.classList.contains("collapsed")) {
|
||||
this.classList.remove("collapsed");
|
||||
this.classList.add("expanded");
|
||||
|
@ -375,7 +376,7 @@ export class KhojChatView extends KhojPaneView {
|
|||
referenceButton.tabIndex = 0;
|
||||
|
||||
// Add event listener to toggle full reference on click
|
||||
referenceButton.addEventListener('click', function() {
|
||||
referenceButton.addEventListener('click', function () {
|
||||
if (this.classList.contains("collapsed")) {
|
||||
this.classList.remove("collapsed");
|
||||
this.classList.add("expanded");
|
||||
|
@ -420,23 +421,23 @@ export class KhojChatView extends KhojPaneView {
|
|||
"Authorization": `Bearer ${this.setting.khojApiKey}`,
|
||||
},
|
||||
})
|
||||
.then(response => response.arrayBuffer())
|
||||
.then(arrayBuffer => context.decodeAudioData(arrayBuffer))
|
||||
.then(audioBuffer => {
|
||||
const source = context.createBufferSource();
|
||||
source.buffer = audioBuffer;
|
||||
source.connect(context.destination);
|
||||
source.start(0);
|
||||
source.onended = function() {
|
||||
.then(response => response.arrayBuffer())
|
||||
.then(arrayBuffer => context.decodeAudioData(arrayBuffer))
|
||||
.then(audioBuffer => {
|
||||
const source = context.createBufferSource();
|
||||
source.buffer = audioBuffer;
|
||||
source.connect(context.destination);
|
||||
source.start(0);
|
||||
source.onended = function () {
|
||||
speechButton.removeChild(loader);
|
||||
speechButton.disabled = false;
|
||||
};
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("Error playing speech:", err);
|
||||
speechButton.removeChild(loader);
|
||||
speechButton.disabled = false;
|
||||
};
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("Error playing speech:", err);
|
||||
speechButton.removeChild(loader);
|
||||
speechButton.disabled = false; // Consider enabling the button again to allow retrying
|
||||
});
|
||||
speechButton.disabled = false; // Consider enabling the button again to allow retrying
|
||||
});
|
||||
}
|
||||
|
||||
formatHTMLMessage(message: string, raw = false, willReplace = true) {
|
||||
|
@ -463,7 +464,7 @@ export class KhojChatView extends KhojPaneView {
|
|||
let virtualChatMessageBodyTextEl = document.createElement("div");
|
||||
|
||||
// Convert the message to html
|
||||
MarkdownRenderer.renderMarkdown(markdownText, virtualChatMessageBodyTextEl, '', component);
|
||||
MarkdownRenderer.render(this.app, markdownText, virtualChatMessageBodyTextEl, '', component);
|
||||
|
||||
// Remove image HTML elements with any non whitelisted src prefix
|
||||
virtualChatMessageBodyTextEl.innerHTML = virtualChatMessageBodyTextEl.innerHTML.replace(
|
||||
|
@ -485,12 +486,18 @@ export class KhojChatView extends KhojPaneView {
|
|||
intentType?: string,
|
||||
inferredQueries?: string[],
|
||||
conversationId?: string,
|
||||
images?: string[],
|
||||
excalidrawDiagram?: string
|
||||
) {
|
||||
if (!message) return;
|
||||
|
||||
let chatMessageEl;
|
||||
if (intentType?.includes("text-to-image") || intentType === "excalidraw") {
|
||||
let imageMarkdown = this.generateImageMarkdown(message, intentType, inferredQueries, conversationId);
|
||||
if (
|
||||
intentType?.includes("text-to-image") ||
|
||||
intentType === "excalidraw" ||
|
||||
(images && images.length > 0) ||
|
||||
excalidrawDiagram) {
|
||||
let imageMarkdown = this.generateImageMarkdown(message, intentType ?? "", inferredQueries, conversationId, images, excalidrawDiagram);
|
||||
chatMessageEl = this.renderMessage(chatEl, imageMarkdown, sender, dt);
|
||||
} else {
|
||||
chatMessageEl = this.renderMessage(chatEl, message, sender, dt);
|
||||
|
@ -510,7 +517,7 @@ export class KhojChatView extends KhojPaneView {
|
|||
chatMessageBodyEl.appendChild(this.createReferenceSection(references));
|
||||
}
|
||||
|
||||
generateImageMarkdown(message: string, intentType: string, inferredQueries?: string[], conversationId?: string): string {
|
||||
generateImageMarkdown(message: string, intentType: string, inferredQueries?: string[], conversationId?: string, images?: string[], excalidrawDiagram?: string): string {
|
||||
let imageMarkdown = "";
|
||||
if (intentType === "text-to-image") {
|
||||
imageMarkdown = `![](data:image/png;base64,${message})`;
|
||||
|
@ -518,12 +525,23 @@ export class KhojChatView extends KhojPaneView {
|
|||
imageMarkdown = `![](${message})`;
|
||||
} else if (intentType === "text-to-image-v3") {
|
||||
imageMarkdown = `![](data:image/webp;base64,${message})`;
|
||||
} else if (intentType === "excalidraw") {
|
||||
} else if (intentType === "excalidraw" || excalidrawDiagram) {
|
||||
const domain = this.setting.khojUrl.endsWith("/") ? this.setting.khojUrl : `${this.setting.khojUrl}/`;
|
||||
const redirectMessage = `Hey, I'm not ready to show you diagrams yet here. But you can view it in ${domain}chat?conversationId=${conversationId}`;
|
||||
imageMarkdown = redirectMessage;
|
||||
} else if (images && images.length > 0) {
|
||||
for (let image of images) {
|
||||
if (image.startsWith("https://")) {
|
||||
imageMarkdown += `![](${image})\n\n`;
|
||||
} else {
|
||||
imageMarkdown += `![](data:image/png;base64,${image})\n\n`;
|
||||
}
|
||||
}
|
||||
|
||||
imageMarkdown += `${message}`;
|
||||
}
|
||||
if (inferredQueries) {
|
||||
|
||||
if (images?.length === 0 && inferredQueries) {
|
||||
imageMarkdown += "\n\n**Inferred Query**:";
|
||||
for (let inferredQuery of inferredQueries) {
|
||||
imageMarkdown += `\n\n${inferredQuery}`;
|
||||
|
@ -534,13 +552,12 @@ export class KhojChatView extends KhojPaneView {
|
|||
|
||||
renderMessage(chatBodyEl: Element, message: string, sender: string, dt?: Date, raw: boolean = false, willReplace: boolean = true): Element {
|
||||
let message_time = this.formatDate(dt ?? new Date());
|
||||
let emojified_sender = sender == "khoj" ? "🏮 Khoj" : "🤔 You";
|
||||
|
||||
// Append message to conversation history HTML element.
|
||||
// The chat logs should display above the message input box to follow standard UI semantics
|
||||
let chatMessageEl = chatBodyEl.createDiv({
|
||||
attr: {
|
||||
"data-meta": `${emojified_sender} at ${message_time}`,
|
||||
"data-meta": message_time,
|
||||
class: `khoj-chat-message ${sender}`
|
||||
},
|
||||
})
|
||||
|
@ -580,7 +597,7 @@ export class KhojChatView extends KhojPaneView {
|
|||
let chatBodyEl = this.contentEl.getElementsByClassName("khoj-chat-body")[0];
|
||||
let chatMessageEl = chatBodyEl.createDiv({
|
||||
attr: {
|
||||
"data-meta": `🏮 Khoj at ${messageTime}`,
|
||||
"data-meta": messageTime,
|
||||
class: `khoj-chat-message khoj`
|
||||
},
|
||||
})
|
||||
|
@ -650,26 +667,26 @@ export class KhojChatView extends KhojPaneView {
|
|||
chatBodyEl.innerHTML = "";
|
||||
chatBodyEl.dataset.conversationId = "";
|
||||
chatBodyEl.dataset.conversationTitle = "";
|
||||
this.userMessages = [];
|
||||
this.startingMessage = "Message";
|
||||
this.userMessages = [];
|
||||
this.startingMessage = "Message";
|
||||
|
||||
// Update the placeholder of the chat input
|
||||
const chatInput = this.contentEl.querySelector('.khoj-chat-input') as HTMLTextAreaElement;
|
||||
if (chatInput) {
|
||||
chatInput.placeholder = this.startingMessage;
|
||||
}
|
||||
// Update the placeholder of the chat input
|
||||
const chatInput = this.contentEl.querySelector('.khoj-chat-input') as HTMLTextAreaElement;
|
||||
if (chatInput) {
|
||||
chatInput.placeholder = this.startingMessage;
|
||||
}
|
||||
this.renderMessage(chatBodyEl, "Hey 👋🏾, what's up?", "khoj");
|
||||
}
|
||||
|
||||
async toggleChatSessions(forceShow: boolean = false): Promise<boolean> {
|
||||
this.userMessages = []; // clear user previous message history
|
||||
this.userMessages = []; // clear user previous message history
|
||||
let chatBodyEl = this.contentEl.getElementsByClassName("khoj-chat-body")[0] as HTMLElement;
|
||||
if (!forceShow && this.contentEl.getElementsByClassName("side-panel")?.length > 0) {
|
||||
chatBodyEl.innerHTML = "";
|
||||
return this.getChatHistory(chatBodyEl);
|
||||
}
|
||||
chatBodyEl.innerHTML = "";
|
||||
const sidePanelEl = this.contentEl.createDiv("side-panel");
|
||||
const sidePanelEl = chatBodyEl.createDiv("side-panel");
|
||||
const newConversationEl = sidePanelEl.createDiv("new-conversation");
|
||||
const conversationHeaderTitleEl = newConversationEl.createDiv("conversation-header-title");
|
||||
conversationHeaderTitleEl.textContent = "Conversations";
|
||||
|
@ -727,7 +744,6 @@ export class KhojChatView extends KhojPaneView {
|
|||
|
||||
conversationSessionEl.appendChild(conversationMenuEl);
|
||||
conversationListBodyEl.appendChild(conversationSessionEl);
|
||||
chatBodyEl.appendChild(sidePanelEl);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
@ -768,10 +784,10 @@ export class KhojChatView extends KhojPaneView {
|
|||
let editConversationTitleInputEl = this.contentEl.createEl('input');
|
||||
editConversationTitleInputEl.classList.add("conversation-title-input");
|
||||
editConversationTitleInputEl.value = conversationTitle;
|
||||
editConversationTitleInputEl.addEventListener('click', function(event) {
|
||||
editConversationTitleInputEl.addEventListener('click', function (event) {
|
||||
event.stopPropagation();
|
||||
});
|
||||
editConversationTitleInputEl.addEventListener('keydown', function(event) {
|
||||
editConversationTitleInputEl.addEventListener('keydown', function (event) {
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
editConversationTitleSaveButtonEl.click();
|
||||
|
@ -890,15 +906,17 @@ export class KhojChatView extends KhojPaneView {
|
|||
chatLog.intent?.type,
|
||||
chatLog.intent?.["inferred-queries"],
|
||||
chatBodyEl.dataset.conversationId ?? "",
|
||||
chatLog.images,
|
||||
chatLog.excalidrawDiagram,
|
||||
);
|
||||
// push the user messages to the chat history
|
||||
if(chatLog.by === "you"){
|
||||
if (chatLog.by === "you") {
|
||||
this.userMessages.push(chatLog.message);
|
||||
}
|
||||
});
|
||||
|
||||
// Update starting message after loading history
|
||||
const modifierKey: string = Platform.isMacOS ? '⌘' : '^';
|
||||
const modifierKey: string = Platform.isMacOS ? '⌘' : '^';
|
||||
this.startingMessage = this.userMessages.length > 0
|
||||
? `(${modifierKey}+↑/↓) for prev messages`
|
||||
: "Message";
|
||||
|
@ -922,15 +940,15 @@ export class KhojChatView extends KhojPaneView {
|
|||
try {
|
||||
let jsonChunk = JSON.parse(rawChunk);
|
||||
if (!jsonChunk.type)
|
||||
jsonChunk = {type: 'message', data: jsonChunk};
|
||||
jsonChunk = { type: 'message', data: jsonChunk };
|
||||
return jsonChunk;
|
||||
} catch (e) {
|
||||
return {type: 'message', data: rawChunk};
|
||||
return { type: 'message', data: rawChunk };
|
||||
}
|
||||
} else if (rawChunk.length > 0) {
|
||||
return {type: 'message', data: rawChunk};
|
||||
return { type: 'message', data: rawChunk };
|
||||
}
|
||||
return {type: '', data: ''};
|
||||
return { type: '', data: '' };
|
||||
}
|
||||
|
||||
processMessageChunk(rawChunk: string): void {
|
||||
|
@ -941,6 +959,11 @@ export class KhojChatView extends KhojPaneView {
|
|||
console.log(`status: ${chunk.data}`);
|
||||
const statusMessage = chunk.data;
|
||||
this.handleStreamResponse(this.chatMessageState.newResponseTextEl, statusMessage, this.chatMessageState.loadingEllipsis, false);
|
||||
} else if (chunk.type === 'generated_assets') {
|
||||
const generatedAssets = chunk.data;
|
||||
const imageData = this.handleImageResponse(generatedAssets, this.chatMessageState.rawResponse);
|
||||
this.chatMessageState.generatedAssets = imageData;
|
||||
this.handleStreamResponse(this.chatMessageState.newResponseTextEl, imageData, this.chatMessageState.loadingEllipsis, false);
|
||||
} else if (chunk.type === 'start_llm_response') {
|
||||
console.log("Started streaming", new Date());
|
||||
} else if (chunk.type === 'end_llm_response') {
|
||||
|
@ -963,9 +986,10 @@ export class KhojChatView extends KhojPaneView {
|
|||
rawResponse: "",
|
||||
rawQuery: liveQuery,
|
||||
isVoice: false,
|
||||
generatedAssets: "",
|
||||
};
|
||||
} else if (chunk.type === "references") {
|
||||
this.chatMessageState.references = {"notes": chunk.data.context, "online": chunk.data.onlineContext};
|
||||
this.chatMessageState.references = { "notes": chunk.data.context, "online": chunk.data.onlineContext };
|
||||
} else if (chunk.type === 'message') {
|
||||
const chunkData = chunk.data;
|
||||
if (typeof chunkData === 'object' && chunkData !== null) {
|
||||
|
@ -978,17 +1002,17 @@ export class KhojChatView extends KhojPaneView {
|
|||
this.handleJsonResponse(jsonData);
|
||||
} catch (e) {
|
||||
this.chatMessageState.rawResponse += chunkData;
|
||||
this.handleStreamResponse(this.chatMessageState.newResponseTextEl, this.chatMessageState.rawResponse, this.chatMessageState.loadingEllipsis);
|
||||
this.handleStreamResponse(this.chatMessageState.newResponseTextEl, this.chatMessageState.rawResponse + this.chatMessageState.generatedAssets, this.chatMessageState.loadingEllipsis);
|
||||
}
|
||||
} else {
|
||||
this.chatMessageState.rawResponse += chunkData;
|
||||
this.handleStreamResponse(this.chatMessageState.newResponseTextEl, this.chatMessageState.rawResponse, this.chatMessageState.loadingEllipsis);
|
||||
this.handleStreamResponse(this.chatMessageState.newResponseTextEl, this.chatMessageState.rawResponse + this.chatMessageState.generatedAssets, this.chatMessageState.loadingEllipsis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleJsonResponse(jsonData: any): void {
|
||||
if (jsonData.image || jsonData.detail) {
|
||||
if (jsonData.image || jsonData.detail || jsonData.images || jsonData.excalidrawDiagram) {
|
||||
this.chatMessageState.rawResponse = this.handleImageResponse(jsonData, this.chatMessageState.rawResponse);
|
||||
} else if (jsonData.response) {
|
||||
this.chatMessageState.rawResponse = jsonData.response;
|
||||
|
@ -1088,6 +1112,7 @@ export class KhojChatView extends KhojPaneView {
|
|||
rawQuery: query,
|
||||
rawResponse: "",
|
||||
isVoice: isVoice,
|
||||
generatedAssets: "",
|
||||
};
|
||||
|
||||
let response = await fetch(chatUrl, {
|
||||
|
@ -1234,11 +1259,11 @@ export class KhojChatView extends KhojPaneView {
|
|||
const recordingConfig = { mimeType: 'audio/webm' };
|
||||
this.mediaRecorder = new MediaRecorder(stream, recordingConfig);
|
||||
|
||||
this.mediaRecorder.addEventListener("dataavailable", function(event) {
|
||||
this.mediaRecorder.addEventListener("dataavailable", function (event) {
|
||||
if (event.data.size > 0) audioChunks.push(event.data);
|
||||
});
|
||||
|
||||
this.mediaRecorder.addEventListener("stop", async function() {
|
||||
this.mediaRecorder.addEventListener("stop", async function () {
|
||||
const audioBlob = new Blob(audioChunks, { type: 'audio/webm' });
|
||||
await sendToServer(audioBlob);
|
||||
});
|
||||
|
@ -1368,7 +1393,22 @@ export class KhojChatView extends KhojPaneView {
|
|||
if (inferredQuery) {
|
||||
rawResponse += `\n\n**Inferred Query**:\n\n${inferredQuery}`;
|
||||
}
|
||||
} else if (imageJson.images) {
|
||||
// If response has images field, response is a list of generated images.
|
||||
imageJson.images.forEach((image: any) => {
|
||||
|
||||
if (image.startsWith("http")) {
|
||||
rawResponse += `![generated_image](${image})\n\n`;
|
||||
} else {
|
||||
rawResponse += `![generated_image](data:image/png;base64,${image})\n\n`;
|
||||
}
|
||||
});
|
||||
} else if (imageJson.excalidrawDiagram) {
|
||||
const domain = this.setting.khojUrl.endsWith("/") ? this.setting.khojUrl : `${this.setting.khojUrl}/`;
|
||||
const redirectMessage = `Hey, I'm not ready to show you diagrams yet here. But you can view it in ${domain}`;
|
||||
rawResponse += redirectMessage;
|
||||
}
|
||||
|
||||
// If response has detail field, response is an error message.
|
||||
if (imageJson.detail) rawResponse += imageJson.detail;
|
||||
|
||||
|
@ -1407,7 +1447,7 @@ export class KhojChatView extends KhojPaneView {
|
|||
referenceExpandButton.classList.add("reference-expand-button");
|
||||
referenceExpandButton.innerHTML = numReferences == 1 ? "1 reference" : `${numReferences} references`;
|
||||
|
||||
referenceExpandButton.addEventListener('click', function() {
|
||||
referenceExpandButton.addEventListener('click', function () {
|
||||
if (referenceSection.classList.contains("collapsed")) {
|
||||
referenceSection.classList.remove("collapsed");
|
||||
referenceSection.classList.add("expanded");
|
||||
|
|
|
@ -50,12 +50,12 @@ If your plugin does not need CSS, delete this file.
|
|||
overflow-y: scroll; /* Make chat body scroll to see history */
|
||||
}
|
||||
/* add chat metatdata to bottom of bubble */
|
||||
.khoj-chat-message::after {
|
||||
.khoj-chat-message.khoj::after {
|
||||
content: attr(data-meta);
|
||||
display: block;
|
||||
font-size: var(--font-ui-smaller);
|
||||
color: var(--text-muted);
|
||||
margin: -12px 7px 0 -5px;
|
||||
margin: -12px 7px 0 0px;
|
||||
}
|
||||
/* move message by khoj to left */
|
||||
.khoj-chat-message.khoj {
|
||||
|
@ -82,7 +82,8 @@ If your plugin does not need CSS, delete this file.
|
|||
}
|
||||
/* color chat bubble by khoj blue */
|
||||
.khoj-chat-message-text.khoj {
|
||||
border: 1px solid var(--khoj-sun);
|
||||
border-left: 2px solid var(--khoj-sun);
|
||||
border-radius: 0px;
|
||||
margin-left: auto;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
@ -104,8 +105,9 @@ If your plugin does not need CSS, delete this file.
|
|||
}
|
||||
/* color chat bubble by you dark grey */
|
||||
.khoj-chat-message-text.you {
|
||||
border: 1px solid var(--color-accent);
|
||||
color: var(--text-normal);
|
||||
margin-right: auto;
|
||||
background-color: var(--background-modifier-cover);
|
||||
}
|
||||
/* add right protrusion to you chat bubble */
|
||||
.khoj-chat-message-text.you:after {
|
||||
|
@ -240,6 +242,10 @@ div.new-conversation {
|
|||
grid-auto-flow: column;
|
||||
grid-template-columns: 1fr auto;
|
||||
margin-bottom: 16px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
background-color: var(--background-primary)
|
||||
}
|
||||
div.conversation-header-title {
|
||||
text-align: left;
|
||||
|
@ -314,6 +320,9 @@ div.selected-conversation {
|
|||
background: var(--background-primary);
|
||||
margin: 0 0 0 -8px;
|
||||
align-items: center;
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
#khoj-chat-input.option:hover {
|
||||
box-shadow: 0 0 11px var(--background-modifier-box-shadow);
|
||||
|
|
|
@ -92,5 +92,15 @@
|
|||
"1.29.0": "0.15.0",
|
||||
"1.29.1": "0.15.0",
|
||||
"1.30.0": "0.15.0",
|
||||
"1.30.1": "0.15.0"
|
||||
"1.30.1": "0.15.0",
|
||||
"1.30.2": "0.15.0",
|
||||
"1.30.3": "0.15.0",
|
||||
"1.30.4": "0.15.0",
|
||||
"1.30.5": "0.15.0",
|
||||
"1.30.6": "0.15.0",
|
||||
"1.30.7": "0.15.0",
|
||||
"1.30.8": "0.15.0",
|
||||
"1.30.9": "0.15.0",
|
||||
"1.30.10": "0.15.0",
|
||||
"1.31.0": "0.15.0"
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import type { Metadata } from "next";
|
||||
import { Noto_Sans } from "next/font/google";
|
||||
import { noto_sans, noto_sans_arabic } from "@/app/fonts";
|
||||
import "../globals.css";
|
||||
|
||||
const inter = Noto_Sans({ subsets: ["latin"] });
|
||||
import { ContentSecurityPolicy } from "../common/layoutHelper";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Khoj AI - Agents",
|
||||
|
@ -33,20 +32,9 @@ export default function RootLayout({
|
|||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<meta
|
||||
httpEquiv="Content-Security-Policy"
|
||||
content="default-src 'self' https://assets.khoj.dev;
|
||||
media-src * blob:;
|
||||
script-src 'self' https://assets.khoj.dev 'unsafe-inline' 'unsafe-eval';
|
||||
connect-src 'self' https://ipapi.co/json ws://localhost:42110;
|
||||
style-src 'self' https://assets.khoj.dev 'unsafe-inline' https://fonts.googleapis.com;
|
||||
img-src 'self' data: https://*.khoj.dev https://*.googleusercontent.com https://*.google.com/ https://*.gstatic.com;
|
||||
font-src 'self' https://assets.khoj.dev https://fonts.gstatic.com;
|
||||
child-src 'none';
|
||||
object-src 'none';"
|
||||
></meta>
|
||||
<body className={inter.className}>{children}</body>
|
||||
<html lang="en" className={`${noto_sans.variable} ${noto_sans_arabic.variable}`}>
|
||||
<ContentSecurityPolicy />
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|