Improve Theming of Web, Desktop and Obsidian Client App (#532)

- Update theme for Desktop, Web and Obsidian client apps to use lighter colors
- Show splash screen on starting Desktop app
- Make chat the landing page on Desktop and Web clients
- Simplify style of login page on Web app
- Add About page for Desktop app accessible from system tray menu
This commit is contained in:
Debanjum
2023-11-04 12:29:56 -07:00
committed by GitHub
40 changed files with 1610 additions and 147 deletions

View File

@@ -10,7 +10,8 @@
Offline chat stays completely private and works without internet. But it is slower, lower quality and more compute intensive. Offline chat stays completely private and works without internet. But it is slower, lower quality and more compute intensive.
> **System Requirements**: > **System Requirements**:
> - Machine with at least **6 GB of RAM** and **4 GB of Disk** available > - Minimum 8 GB RAM. Recommend **16Gb VRAM**
> - Minimum **5 GB of Disk** available
> - A CPU supporting [AVX or AVX2 instructions](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions) is required > - A CPU supporting [AVX or AVX2 instructions](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions) is required
> - A Mac M1+ or [Vulcan supported GPU](https://vulkan.gpuinfo.org/) should significantly speed up chat response times > - A Mac M1+ or [Vulcan supported GPU](https://vulkan.gpuinfo.org/) should significantly speed up chat response times

View File

@@ -62,8 +62,8 @@ dependencies = [
"pymupdf >= 1.23.5", "pymupdf >= 1.23.5",
"django == 4.2.5", "django == 4.2.5",
"authlib == 1.2.1", "authlib == 1.2.1",
"gpt4all == 1.0.12; platform_system == 'Linux' and platform_machine == 'x86_64'", "gpt4all >= 2.0.0; platform_system == 'Linux' and platform_machine == 'x86_64'",
"gpt4all == 1.0.12; platform_system == 'Windows' or platform_system == 'Darwin'", "gpt4all >= 2.0.0; platform_system == 'Windows' or platform_system == 'Darwin'",
"itsdangerous == 2.1.2", "itsdangerous == 2.1.2",
"httpx == 0.25.0", "httpx == 0.25.0",
"pgvector == 0.2.3", "pgvector == 0.2.3",

View File

@@ -0,0 +1,88 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0 maximum-scale=1.0">
<title>Khoj - About</title>
<link rel="icon" type="image/png" sizes="128x128" href="./assets/icons/favicon-128x128.png">
<link rel="manifest" href="/static/khoj_chat.webmanifest">
<link rel="stylesheet" href="./assets/khoj.css">
</head>
<script type="text/javascript" src="./utils.js"></script>
<style>
html, body {
height: 100%;
width: 100%;
padding: 0px;
margin: 0px;
}
body {
display: grid;
grid-template-rows: auto;
background: var(--background-color);
color: var(--main-text-color);
text-align: center;
font-family: roboto, karma, segoe ui, sans-serif;
font-size: small;
font-weight: 300;
line-height: 1.5em;
}
header > *,
body > * {
padding: 0px;
margin: 0px;
}
header > * {
margin-top: 20px;
}
img {
width: 100px;
height: 100px;
margin-top: 32px;
}
p {
font-size: 14px;
}
#about-page-version {
margin: 0;
}
.button {
display: block;
width: 60%;
padding: 10px 16px;
margin: 10px auto;
background-color: var(--primary);
border: none;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.3s;
}
.button:hover {
background-color: var(--primary-hover);
}
footer {
font-size: 10px;
color: slategray;
margin-top: 10px;
}
</style>
<body>
<header>
<img id="logo" src="./assets/icons/favicon-128x128.png" alt="Khoj Logo">
<p id="about-page-title"><b>Khoj for Desktop</b>
<p id="about-page-version"></p>
</header>
<div class="action">
<button class="button" onclick="window.open('https://khoj.dev/terms-of-service', '_blank')">Terms of Service</button>
<button class="button" onclick="window.open('https://khoj.dev/privacy-policy', '_blank')">Privacy Policy</button>
</div>
<footer>
© 2023 Khoj Inc. All rights reserved.
</footer>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -2,29 +2,44 @@
/* Can be forced with data-theme="light" */ /* Can be forced with data-theme="light" */
[data-theme="light"], [data-theme="light"],
:root:not([data-theme="dark"]) { :root:not([data-theme="dark"]) {
--primary: #ffb300; --primary: #fee285;
--primary-hover: #ffa000; --primary-hover: #fcc50b;
--primary-focus: rgba(255, 179, 0, 0.125); --primary-focus: rgba(255, 179, 0, 0.125);
--primary-inverse: rgba(0, 0, 0, 0.75); --primary-inverse: rgba(0, 0, 0, 0.75);
--background-color: #f5f4f3;
--main-text-color: #475569;
--water: #44b9da;
--leaf: #7b990a;
--flower: #d1684e;
} }
/* Amber Dark scheme (Auto) */ /* Amber Dark scheme (Auto) */
/* Automatically enabled if user has Dark mode enabled */ /* Automatically enabled if user has Dark mode enabled */
@media only screen and (prefers-color-scheme: dark) { @media only screen and (prefers-color-scheme: dark) {
:root:not([data-theme]) { :root:not([data-theme]) {
--primary: #ffb300; --primary: #fee285;
--primary-hover: #ffc107; --primary-hover: #fcc50b;
--primary-focus: rgba(255, 179, 0, 0.25); --primary-focus: rgba(255, 179, 0, 0.25);
--primary-inverse: rgba(0, 0, 0, 0.75); --primary-inverse: rgba(0, 0, 0, 0.75);
--background-color: #f5f4f3;
--main-text-color: #475569;
--water: #44b9da;
--leaf: #7b990a;
--flower: #d1684e;
} }
} }
/* Amber Dark scheme (Forced) */ /* Amber Dark scheme (Forced) */
/* Enabled if forced with data-theme="dark" */ /* Enabled if forced with data-theme="dark" */
[data-theme="dark"] { [data-theme="dark"] {
--primary: #ffb300; --primary: #fee285;
--primary-hover: #ffc107; --primary-hover: #fcc50b;
--primary-focus: rgba(255, 179, 0, 0.25); --primary-focus: rgba(255, 179, 0, 0.25);
--primary-inverse: rgba(0, 0, 0, 0.75); --primary-inverse: rgba(0, 0, 0, 0.75);
--background-color: #f5f4f3;
--main-text-color: #475569;
--water: #44b9da;
--leaf: #7b990a;
--flower: #d1684e;
} }
/* Amber (Common styles) */ /* Amber (Common styles) */
:root { :root {
@@ -37,8 +52,10 @@
.khoj-configure { .khoj-configure {
display: grid; display: grid;
grid-template-columns: 1fr; grid-template-columns: 1fr;
padding: 0 24px; font-family: roboto, karma, segoe ui, sans-serif;
font-weight: 300;
} }
.khoj-header { .khoj-header {
display: grid; display: grid;
grid-auto-flow: column; grid-auto-flow: column;
@@ -64,7 +81,7 @@ a.khoj-logo {
} }
.khoj-nav a { .khoj-nav a {
color: #333; color: var(--main-text-color);
text-decoration: none; text-decoration: none;
font-size: small; font-size: small;
font-weight: normal; font-weight: normal;
@@ -75,8 +92,9 @@ a.khoj-logo {
} }
.khoj-nav a:hover { .khoj-nav a:hover {
background-color: var(--primary-hover); background-color: var(--primary-hover);
color: var(--main-text-color);
} }
.khoj-nav-selected { a.khoj-nav-selected {
background-color: var(--primary); background-color: var(--primary);
} }
img.khoj-logo { img.khoj-logo {

File diff suppressed because one or more lines are too long

View File

@@ -8,6 +8,8 @@
<link rel="manifest" href="/static/khoj_chat.webmanifest"> <link rel="manifest" href="/static/khoj_chat.webmanifest">
<link rel="stylesheet" href="./assets/khoj.css"> <link rel="stylesheet" href="./assets/khoj.css">
</head> </head>
<script src="./utils.js"></script>
<script> <script>
let chatOptions = []; let chatOptions = [];
function copyProgrammaticOutput(event) { function copyProgrammaticOutput(event) {
@@ -66,6 +68,8 @@
// Replace any ** with <b> and __ with <u> // Replace any ** with <b> and __ with <u>
newHTML = newHTML.replace(/\*\*([\s\S]*?)\*\*/g, '<b>$1</b>'); newHTML = newHTML.replace(/\*\*([\s\S]*?)\*\*/g, '<b>$1</b>');
newHTML = newHTML.replace(/__([\s\S]*?)__/g, '<u>$1</u>'); newHTML = newHTML.replace(/__([\s\S]*?)__/g, '<u>$1</u>');
// Remove any text between <s>[INST] and </s> tags. These are spurious instructions for the AI chat model.
newHTML = newHTML.replace(/<s>\[INST\].+(<\/s>)?/g, '');
return newHTML; return newHTML;
} }
@@ -166,6 +170,7 @@
function incrementalChat(event) { function incrementalChat(event) {
if (!event.shiftKey && event.key === 'Enter') { if (!event.shiftKey && event.key === 'Enter') {
event.preventDefault();
chat(); chat();
} }
} }
@@ -278,7 +283,7 @@
</a> </a>
<nav class="khoj-nav"> <nav class="khoj-nav">
<a class="khoj-nav khoj-nav-selected" href="./chat.html">💬 Chat</a> <a class="khoj-nav khoj-nav-selected" href="./chat.html">💬 Chat</a>
<a class="khoj-nav" href="./index.html">🔎 Search</a> <a class="khoj-nav" href="./search.html">🔎 Search</a>
<a class="khoj-nav" href="./config.html">⚙️ Settings</a> <a class="khoj-nav" href="./config.html">⚙️ Settings</a>
</nav> </nav>
</div> </div>
@@ -289,8 +294,7 @@
<!-- Chat Footer --> <!-- Chat Footer -->
<div id="chat-footer"> <div id="chat-footer">
<div id="chat-tooltip" style="display: none;"></div> <div id="chat-tooltip" style="display: none;"></div>
<textarea id="chat-input" class="option" oninput="onChatInput()" onkeyup=incrementalChat(event) autofocus="autofocus" placeholder="Type / to see a list of commands, or just type your questions and hit enter."> <textarea id="chat-input" class="option" oninput="onChatInput()" onkeydown=incrementalChat(event) autofocus="autofocus" placeholder="Type / to see a list of commands, or just type your questions and hit enter."></textarea>
</textarea>
</div> </div>
</body> </body>
@@ -303,8 +307,8 @@
} }
body { body {
display: grid; display: grid;
background: #fff; background: var(--background-color);
color: #475569; color: var(--main-text-color);
text-align: center; text-align: center;
font-family: roboto, karma, segoe ui, sans-serif; font-family: roboto, karma, segoe ui, sans-serif;
font-size: small; font-size: small;

View File

@@ -8,18 +8,17 @@
<link rel="manifest" href="./khoj.webmanifest"> <link rel="manifest" href="./khoj.webmanifest">
<link rel="stylesheet" href="./assets/khoj.css"> <link rel="stylesheet" href="./assets/khoj.css">
</head> </head>
<script type="text/javascript" src="./assets/org.min.js"></script> <script src="./utils.js"></script>
<script type="text/javascript" src="./assets/markdown-it.min.js"></script>
<body> <body>
<!--Add Header Logo and Nav Pane--> <!--Add Header Logo and Nav Pane-->
<div class="khoj-header"> <div class="khoj-header">
<a class="khoj-logo" href="./index.html"> <a class="khoj-logo" href="./chat.html">
<img class="khoj-logo" src="./assets/icons/khoj-logo-sideways-500.png" alt="Khoj"></img> <img class="khoj-logo" src="./assets/icons/khoj-logo-sideways-500.png" alt="Khoj"></img>
</a> </a>
<nav class="khoj-nav"> <nav class="khoj-nav">
<a class="khoj-nav" href="./chat.html">💬 Chat</a> <a class="khoj-nav" href="./chat.html">💬 Chat</a>
<a class="khoj-nav" href="./index.html">🔎 Search</a> <a class="khoj-nav" href="./search.html">🔎 Search</a>
<a class="khoj-nav khoj-nav-selected" href="./config.html">⚙️ Settings</a> <a class="khoj-nav khoj-nav-selected" href="./config.html">⚙️ Settings</a>
</nav> </nav>
</div> </div>
@@ -38,11 +37,11 @@
<div class="card-title-row"> <div class="card-title-row">
<img class="card-icon" src="./assets/icons/key.svg" alt="Khoj Access Key"> <img class="card-icon" src="./assets/icons/key.svg" alt="Khoj Access Key">
<h3 class="card-title"> <h3 class="card-title">
Access Key API Key
</h3> </h3>
</div> </div>
<div class="card-description-row"> <div class="card-description-row">
<input id="khoj-access-key" class="card-input" type="text" placeholder="Enter key to access your Khoj"> <input id="khoj-access-key" class="card-input" type="text" placeholder="Enter API key to access your Khoj">
</div> </div>
</div> </div>
</div> </div>
@@ -131,7 +130,7 @@
body, input { body, input {
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;
background: #fff; background: var(--background-color);
color: #475569; color: #475569;
font-family: roboto, karma, segoe ui, sans-serif; font-family: roboto, karma, segoe ui, sans-serif;
font-size: small; font-size: small;
@@ -181,7 +180,7 @@
.card-input { .card-input {
padding: 4px; padding: 4px;
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.2); box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.3);
border: none; border: none;
width: 450px; width: 450px;
} }
@@ -191,7 +190,7 @@
gap: 8px; gap: 8px;
padding: 24px 16px; padding: 24px 16px;
width: 450px; width: 450px;
background: white; background: var(--background-color);
border: 1px solid rgb(229, 229, 229); border: 1px solid rgb(229, 229, 229);
border-radius: 4px; 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); box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.1),0px 1px 2px -1px rgba(0,0,0,0.1);
@@ -259,7 +258,7 @@
} }
.primary-button { .primary-button {
border: none; border: none;
color: white; color: var(--background-color);
padding: 15px 32px; padding: 15px 32px;
text-align: center; text-align: center;
text-decoration: none; text-decoration: none;
@@ -268,7 +267,7 @@
} }
button.card-button.disabled { button.card-button.disabled {
color: rgb(255, 136, 136); color: var(--flower);
background: transparent; background: transparent;
font-size: small; font-size: small;
cursor: pointer; cursor: pointer;
@@ -280,11 +279,7 @@
} }
button.card-button.happy { button.card-button.happy {
color: rgb(0, 146, 0); color: var(--leaf);
}
button.card-button.happy {
color: rgb(0, 146, 0);
} }
img.configured-icon { img.configured-icon {
@@ -308,7 +303,9 @@
div.folder-element { div.folder-element {
display: grid; display: grid;
grid-template-columns: auto 1fr; grid-template-columns: auto 1fr;
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.2); 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; padding: 4px;
margin-bottom: 8px; margin-bottom: 8px;
} }
@@ -326,7 +323,7 @@
background-color: rgb(253 214 214); background-color: rgb(253 214 214);
border-radius: 3px; border-radius: 3px;
border: none; border: none;
color: rgb(207, 67, 59); color: var(--flower);
padding: 4px; padding: 4px;
} }
@@ -335,14 +332,14 @@
background-color: rgb(255 235 235); background-color: rgb(255 235 235);
border-radius: 3px; border-radius: 3px;
border: none; border: none;
color: rgb(207, 67, 59); color: var(--flower);
padding: 4px; padding: 4px;
cursor: pointer; cursor: pointer;
} }
#sync-data { #sync-data {
background-color: #ffb300; background-color: var(--primary);
border: none; border: none;
color: white; color: var(--main-text-color);
padding: 12px; padding: 12px;
text-align: center; text-align: center;
text-decoration: none; text-decoration: none;
@@ -351,12 +348,12 @@
border-radius: 4px; border-radius: 4px;
cursor: pointer; cursor: pointer;
transition: background-color 0.3s ease; transition: background-color 0.3s ease;
box-shadow: 0px 5px 0px #f9f5de; box-shadow: 0px 5px 0px var(--background-color);
} }
#sync-data:hover { #sync-data:hover {
background-color: #ffcc00; background-color: var(--primary-hover);
box-shadow: 0px 3px 0px #f9f5de; box-shadow: 0px 3px 0px var(--background-color);
} }
.sync-force-toggle { .sync-force-toggle {
align-content: center; align-content: center;

View File

@@ -0,0 +1,129 @@
var $wrap = document.getElementById('loading-animation'),
canvassize = 380,
length = 40,
radius = 6.8,
rotatevalue = 0.02,
acceleration = 0,
animatestep = 0,
toend = false,
pi2 = Math.PI*2,
group = new THREE.Group(),
mesh, ringcover, ring,
camera, scene, renderer;
camera = new THREE.PerspectiveCamera(65, 1, 1, 10000);
camera.position.z = 120;
scene = new THREE.Scene();
// scene.add(new THREE.AxisHelper(30));
scene.add(group);
mesh = new THREE.Mesh(
new THREE.TubeGeometry(new (THREE.Curve.create(function() {},
function(percent) {
var x = length*Math.sin(pi2*percent),
y = radius*Math.cos(pi2*3*percent),
z, t;
t = percent%0.25/0.25;
t = percent%0.25-(2*(1-t)*t* -0.0185 +t*t*0.25);
if (Math.floor(percent/0.25) == 0 || Math.floor(percent/0.25) == 2) {
t *= -1;
}
z = radius*Math.sin(pi2*2* (percent-t));
return new THREE.Vector3(x, y, z);
}
))(), 200, 1.1, 2, true),
new THREE.MeshBasicMaterial({
color: 0xfcc50b
// , wireframe: true
})
);
group.add(mesh);
ringcover = new THREE.Mesh(new THREE.PlaneGeometry(50, 15, 1), new THREE.MeshBasicMaterial({color: 0xd1684e, opacity: 0, transparent: true}));
ringcover.position.x = length+1;
ringcover.rotation.y = Math.PI/2;
group.add(ringcover);
ring = new THREE.Mesh(new THREE.RingGeometry(4.3, 5.55, 32), new THREE.MeshBasicMaterial({color: 0xfcc50b, opacity: 0, transparent: true}));
ring.position.x = length+1.1;
ring.rotation.y = Math.PI/2;
group.add(ring);
// fake shadow
(function() {
var plain, i;
for (i = 0; i < 10; i++) {
plain = new THREE.Mesh(new THREE.PlaneGeometry(length*2+1, radius*3, 1), new THREE.MeshBasicMaterial({color: 0xd1684e, transparent: true, opacity: 0.15}));
plain.position.z = -2.5+i*0.5;
group.add(plain);
}
})();
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvassize, canvassize);
renderer.setClearColor('#d1684e');
$wrap.appendChild(renderer.domElement);
function start() {
toend = true;
}
function back() {
toend = false;
}
function tilt(percent) {
group.rotation.y = percent*0.5;
}
function render() {
var progress;
animatestep = Math.max(0, Math.min(240, toend ? animatestep+1 : animatestep-4));
acceleration = easing(animatestep, 0, 1, 240);
if (acceleration > 0.35) {
progress = (acceleration-0.35)/0.65;
group.rotation.y = -Math.PI/2 *progress;
group.position.z = 20*progress;
progress = Math.max(0, (acceleration-0.99)/0.01);
mesh.material.opacity = 1-progress;
ringcover.material.opacity = ring.material.opacity = progress;
ring.scale.x = ring.scale.y = 0.9 + 0.1*progress;
}
renderer.render(scene, camera);
}
function animate() {
mesh.rotation.x += rotatevalue + acceleration*Math.sin(Math.PI*acceleration);
render();
requestAnimationFrame(animate);
}
function easing(t, b, c, d) {
if ((t /= d/2) < 1)
return c/2*t*t+b;
return c/2*((t-=2)*t*t+2)+b;
}
animate();
setTimeout(start, 30);

View File

@@ -1,5 +1,6 @@
const { app, BrowserWindow, ipcMain, Tray, Menu, nativeImage } = require('electron'); const { app, BrowserWindow, ipcMain, Tray, Menu, nativeImage, shell } = require('electron');
const todesktop = require("@todesktop/runtime"); const todesktop = require("@todesktop/runtime");
const khojPackage = require('./package.json');
todesktop.init(); todesktop.init();
@@ -305,11 +306,13 @@ async function syncData (regenerate = false) {
} }
} }
let firstRun = true;
let win = null; let win = null;
const createWindow = (tab = 'index.html') => { const createWindow = (tab = 'chat.html') => {
win = new BrowserWindow({ win = new BrowserWindow({
width: 800, width: 800,
height: 800, height: 800,
show: false,
// titleBarStyle: 'hidden', // titleBarStyle: 'hidden',
webPreferences: { webPreferences: {
preload: path.join(__dirname, 'preload.js'), preload: path.join(__dirname, 'preload.js'),
@@ -330,12 +333,30 @@ const createWindow = (tab = 'index.html') => {
win.setResizable(true); win.setResizable(true);
win.setOpacity(0.95); win.setOpacity(0.95);
win.setBackgroundColor('#FFFFFF'); win.setBackgroundColor('#f5f4f3');
win.setHasShadow(true); win.setHasShadow(true);
job.start(); job.start();
win.loadFile(tab) win.loadFile(tab)
if (firstRun === true) {
firstRun = false;
// Create splash screen
var splash = new BrowserWindow({width: 400, height: 400, transparent: true, frame: false, alwaysOnTop: true});
splash.setOpacity(1.0);
splash.setBackgroundColor('#d16b4e');
splash.loadFile('splash.html');
// Show splash screen on app load
win.once('ready-to-show', () => {
setTimeout(function(){ splash.close(); win.show(); }, 4500);
});
} else {
// Show main window directly if not first run
win.once('ready-to-show', () => { win.show(); });
}
} }
app.whenReady().then(() => { app.whenReady().then(() => {
@@ -370,11 +391,12 @@ app.whenReady().then(() => {
app.setAboutPanelOptions({ app.setAboutPanelOptions({
applicationName: "Khoj", applicationName: "Khoj",
applicationVersion: "0.0.1", applicationVersion: khojPackage.version,
version: "0.0.1", version: khojPackage.version,
authors: "Khoj Team", authors: "Saba Imran, Debanjum Singh Solanky and contributors",
website: "https://khoj.dev", website: "https://khoj.dev",
iconPath: path.join(__dirname, 'assets', 'khoj.png') copyright: "GPL v3",
iconPath: path.join(__dirname, 'assets', 'icons', 'favicon-128x128.png')
}); });
app.on('ready', async() => { app.on('ready', async() => {
@@ -398,6 +420,43 @@ app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit() if (process.platform !== 'darwin') app.quit()
}) })
/*
** About Page
*/
let aboutWindow;
function openAboutWindow() {
if (aboutWindow) { aboutWindow.focus(); return; }
aboutWindow = new BrowserWindow({
width: 400,
height: 400,
titleBarStyle: 'hidden',
show: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
},
});
aboutWindow.loadFile('about.html');
// Pass OS, Khoj version to About page
aboutWindow.webContents.on('did-finish-load', () => {
aboutWindow.webContents.send('appInfo', { version: khojPackage.version, platform: process.platform });
});
// Open links in external browser
aboutWindow.webContents.setWindowOpenHandler(({ url }) => {
shell.openExternal(url);
return { action: 'deny' };
});
aboutWindow.once('ready-to-show', () => { aboutWindow.show(); });
aboutWindow.on('closed', () => { aboutWindow = null; });
}
/* /*
** System Tray Icon ** System Tray Icon
*/ */
@@ -418,9 +477,10 @@ app.whenReady().then(() => {
const contextMenu = Menu.buildFromTemplate([ const contextMenu = Menu.buildFromTemplate([
{ label: 'Chat', type: 'normal', click: () => { openWindow('chat.html'); }}, { label: 'Chat', type: 'normal', click: () => { openWindow('chat.html'); }},
{ label: 'Search', type: 'normal', click: () => { openWindow('index.html') }}, { label: 'Search', type: 'normal', click: () => { openWindow('search.html') }},
{ label: 'Configure', type: 'normal', click: () => { openWindow('config.html') }}, { label: 'Configure', type: 'normal', click: () => { openWindow('config.html') }},
{ type: 'separator' }, { type: 'separator' },
{ label: 'About Khoj', type: 'normal', click: () => { openAboutWindow(); } },
{ label: 'Quit', type: 'normal', click: () => { app.quit() } } { label: 'Quit', type: 'normal', click: () => { app.quit() } }
]) ])

View File

@@ -52,3 +52,7 @@ contextBridge.exposeInMainWorld('tokenAPI', {
setToken: (token) => ipcRenderer.invoke('setToken', token), setToken: (token) => ipcRenderer.invoke('setToken', token),
getToken: () => ipcRenderer.invoke('getToken') getToken: () => ipcRenderer.invoke('getToken')
}) })
contextBridge.exposeInMainWorld('appInfoAPI', {
getInfo: (callback) => ipcRenderer.on('appInfo', callback)
})

View File

@@ -61,6 +61,7 @@ toggleFoldersButton.addEventListener('click', () => {
function makeFileElement(file) { function makeFileElement(file) {
let fileElement = document.createElement("div"); let fileElement = document.createElement("div");
fileElement.classList.add("file-element"); fileElement.classList.add("file-element");
let fileNameElement = document.createElement("div"); let fileNameElement = document.createElement("div");
fileNameElement.classList.add("content-name"); fileNameElement.classList.add("content-name");
fileNameElement.innerHTML = file.path; fileNameElement.innerHTML = file.path;
@@ -82,6 +83,7 @@ function makeFileElement(file) {
function makeFolderElement(folder) { function makeFolderElement(folder) {
let folderElement = document.createElement("div"); let folderElement = document.createElement("div");
folderElement.classList.add("folder-element"); folderElement.classList.add("folder-element");
let folderNameElement = document.createElement("div"); let folderNameElement = document.createElement("div");
folderNameElement.classList.add("content-name"); folderNameElement.classList.add("content-name");
folderNameElement.innerHTML = folder.path; folderNameElement.innerHTML = folder.path;

View File

@@ -10,6 +10,7 @@
</head> </head>
<script type="text/javascript" src="./assets/org.min.js"></script> <script type="text/javascript" src="./assets/org.min.js"></script>
<script type="text/javascript" src="./assets/markdown-it.min.js"></script> <script type="text/javascript" src="./assets/markdown-it.min.js"></script>
<script src="./utils.js"></script>
<script> <script>
function render_image(item) { function render_image(item) {
@@ -264,7 +265,7 @@
</a> </a>
<nav class="khoj-nav"> <nav class="khoj-nav">
<a class="khoj-nav" href="./chat.html">💬 Chat</a> <a class="khoj-nav" href="./chat.html">💬 Chat</a>
<a class="khoj-nav khoj-nav-selected" href="./index.html">🔎 Search</a> <a class="khoj-nav khoj-nav-selected" href="./search.html">🔎 Search</a>
<a class="khoj-nav" href="./config.html">⚙️ Settings</a> <a class="khoj-nav" href="./config.html">⚙️ Settings</a>
</nav> </nav>
</div> </div>
@@ -302,8 +303,8 @@
body { body {
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;
background: #fff; background: var(--background-color);
color: #475569; color: var(--main-text-color);
font-family: roboto, karma, segoe ui, sans-serif; font-family: roboto, karma, segoe ui, sans-serif;
font-size: small; font-size: small;
font-weight: 300; font-weight: 300;

View File

@@ -0,0 +1,15 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0 maximum-scale=1.0">
<title>Khoj</title>
<link rel="icon" type="image/png" sizes="128x128" href="./assets/icons/favicon-128x128.png">
<link rel="manifest" href="./khoj.webmanifest">
</head>
<script type="text/javascript" src="./assets/three.min.js"></script>
<body>
<div id="loading-animation"></div>
</body>
<script src="./loading-animation.js"></script>
</html>

View File

@@ -0,0 +1,26 @@
console.log(`%c %s`, "font-family:monospace", `
__ __ __ __ ______ __ _____ __
/\\ \\/ / /\\ \\_\\ \\ /\\ __ \\ /\\ \\ /\\ __ \\ /\\ \\
\\ \\ _"-. \\ \\ __ \\ \\ \\ \\/\\ \\ _\\_\\ \\ \\ \\ __ \\ \\ \\ \\
\\ \\_\\ \\_\\ \\ \\_\\ \\_\\ \\ \\_____\\ /\\_____\\ \\ \\_\\ \\_\\ \\ \\_\\
\\/_/\\/_/ \\/_/\\/_/ \\/_____/ \\/_____/ \\/_/\\/_/ \\/_/
Greetings traveller,
I am ✨Khoj✨, your open-source, personal AI copilot.
See my source code at https://github.com/khoj-ai/khoj
Read my operating manual at https://docs.khoj.dev
`);
window.appInfoAPI.getInfo((_, info) => {
let khojVersionElement = document.getElementById("about-page-version");
if (khojVersionElement) {
khojVersionElement.innerHTML = `<code>${info.version}</code>`;
}
let khojTitleElement = document.getElementById("about-page-title");
if (khojTitleElement) {
khojTitleElement.innerHTML = '<b>Khoj for ' + (info.platform === 'win32' ? 'Windows' : info.platform === 'darwin' ? 'macOS' : 'Linux') + '</b>';
}
});

View File

@@ -38,7 +38,7 @@ export class KhojChatModal extends Modal {
await this.getChatHistory(); await this.getChatHistory();
// Add chat input field // Add chat input field
contentEl.createEl("input", const chatInput = contentEl.createEl("input",
{ {
attr: { attr: {
type: "text", type: "text",
@@ -48,10 +48,11 @@ export class KhojChatModal extends Modal {
class: "khoj-chat-input option" class: "khoj-chat-input option"
} }
}) })
.addEventListener('change', (event) => { this.result = (<HTMLInputElement>event.target).value }); chatInput.addEventListener('change', (event) => { this.result = (<HTMLInputElement>event.target).value });
// Scroll to bottom of modal, till the send message input box // Scroll to bottom of modal, till the send message input box
this.modalEl.scrollTop = this.modalEl.scrollHeight; this.modalEl.scrollTop = this.modalEl.scrollHeight;
chatInput.focus();
} }
generateReference(messageEl: any, reference: string, index: number) { generateReference(messageEl: any, reference: string, index: number) {

View File

@@ -8,7 +8,7 @@ If your plugin does not need CSS, delete this file.
*/ */
:root { :root {
--khoj-chat-primary: #ffb300; --khoj-chat-primary: #fee285;
--khoj-chat-dark-grey: #475569; --khoj-chat-dark-grey: #475569;
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -2,35 +2,44 @@
/* Can be forced with data-theme="light" */ /* Can be forced with data-theme="light" */
[data-theme="light"], [data-theme="light"],
:root:not([data-theme="dark"]) { :root:not([data-theme="dark"]) {
--primary: #ffb300; --primary: #fee285;
--primary-hover: #ffa000; --primary-hover: #fcc50b;
--primary-focus: rgba(255, 179, 0, 0.125); --primary-focus: rgba(255, 179, 0, 0.125);
--primary-inverse: rgba(0, 0, 0, 0.75); --primary-inverse: rgba(0, 0, 0, 0.75);
--background-color: #fff; --background-color: #f5f4f3;
--main-text-color: #475569; --main-text-color: #475569;
--water: #44b9da;
--leaf: #7b990a;
--flower: #ffaeae;
} }
/* Amber Dark scheme (Auto) */ /* Amber Dark scheme (Auto) */
/* Automatically enabled if user has Dark mode enabled */ /* Automatically enabled if user has Dark mode enabled */
@media only screen and (prefers-color-scheme: dark) { @media only screen and (prefers-color-scheme: dark) {
:root:not([data-theme]) { :root:not([data-theme]) {
--primary: #ffb300; --primary: #fee285;
--primary-hover: #ffc107; --primary-hover: #fcc50b;
--primary-focus: rgba(255, 179, 0, 0.25); --primary-focus: rgba(255, 179, 0, 0.25);
--primary-inverse: rgba(0, 0, 0, 0.75); --primary-inverse: rgba(0, 0, 0, 0.75);
--background-color: #fff; --background-color: #f5f4f3;
--main-text-color: #475569; --main-text-color: #475569;
--water: #44b9da;
--leaf: #7b990a;
--flower: #ffaeae;
} }
} }
/* Amber Dark scheme (Forced) */ /* Amber Dark scheme (Forced) */
/* Enabled if forced with data-theme="dark" */ /* Enabled if forced with data-theme="dark" */
[data-theme="dark"] { [data-theme="dark"] {
--primary: #ffb300; --primary: #fee285;
--primary-hover: #ffc107; --primary-hover: #fcc50b;
--primary-focus: rgba(255, 179, 0, 0.25); --primary-focus: rgba(255, 179, 0, 0.25);
--primary-inverse: rgba(0, 0, 0, 0.75); --primary-inverse: rgba(0, 0, 0, 0.75);
--background-color: #fff; --background-color: #f5f4f3;
--main-text-color: #475569; --main-text-color: #475569;
--water: #44b9da;
--leaf: #7b990a;
--flower: #ffaeae;
} }
/* Amber (Common styles) */ /* Amber (Common styles) */
:root { :root {
@@ -46,6 +55,8 @@
font-family: roboto, karma, segoe ui, sans-serif; font-family: roboto, karma, segoe ui, sans-serif;
font-weight: 300; font-weight: 300;
} }
.khoj-footer,
.khoj-header { .khoj-header {
display: grid; display: grid;
grid-auto-flow: column; grid-auto-flow: column;
@@ -53,6 +64,9 @@
padding: 16px 0; padding: 16px 0;
margin: 0 0 16px 0; margin: 0 0 16px 0;
} }
.khoj-footer {
margin: 16px 0 0 0;
}
nav.khoj-nav { nav.khoj-nav {
display: grid; display: grid;
@@ -71,7 +85,7 @@ a.khoj-logo {
} }
.khoj-nav a { .khoj-nav a {
color: #333; color: var(--main-text-color);
text-decoration: none; text-decoration: none;
font-size: 20px; font-size: 20px;
font-weight: normal; font-weight: normal;
@@ -148,10 +162,9 @@ p#khoj-banner {
} }
.circle { .circle {
border-radius: 50%; border-radius: 50%;
border: 2px solid var(--primary-inverse); border: 3px solid var(--primary-hover);
width: 40px; width: 40px;
height: 40px; height: 40px;
vertical-align: text-top;
padding: 3px; padding: 3px;
cursor: pointer; cursor: pointer;
} }
@@ -159,7 +172,7 @@ p#khoj-banner {
background-color: var(--primary-hover); background-color: var(--primary-hover);
} }
.user-initial { .user-initial {
background-color: white; background-color: var(--background-color);
color: black; color: black;
display: grid; display: grid;
justify-content: center; justify-content: center;

View File

@@ -1,15 +0,0 @@
// Toggle the navigation menu
function toggleMenu() {
var menu = document.getElementById("khoj-nav-menu");
menu.classList.toggle("show");
}
// Close the dropdown menu if the user clicks outside of it
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;
if (isClickOnMenu === false && menu.classList.contains("show")) {
menu.classList.remove("show");
}
});

View File

@@ -0,0 +1,31 @@
// Toggle the navigation menu
function toggleMenu() {
var menu = document.getElementById("khoj-nav-menu");
menu.classList.toggle("show");
}
// Close the dropdown menu if the user clicks outside of it
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;
if (isClickOnMenu === false && menu.classList.contains("show")) {
menu.classList.remove("show");
}
});
console.log(`%c %s`, "font-family:monospace", `
__ __ __ __ ______ __ _____ __
/\\ \\/ / /\\ \\_\\ \\ /\\ __ \\ /\\ \\ /\\ __ \\ /\\ \\
\\ \\ _"-. \\ \\ __ \\ \\ \\ \\/\\ \\ _\\_\\ \\ \\ \\ __ \\ \\ \\ \\
\\ \\_\\ \\_\\ \\ \\_\\ \\_\\ \\ \\_____\\ /\\_____\\ \\ \\_\\ \\_\\ \\ \\_\\
\\/_/\\/_/ \\/_/\\/_/ \\/_____/ \\/_____/ \\/_/\\/_/ \\/_/
Greetings traveller,
I am ✨Khoj✨, your open-source, personal AI copilot.
See my source code at https://github.com/khoj-ai/khoj
Read my operating manual at https://docs.khoj.dev
`);

View File

@@ -8,7 +8,7 @@
<link rel="stylesheet" href="/static/assets/pico.min.css"> <link rel="stylesheet" href="/static/assets/pico.min.css">
<link rel="stylesheet" href="/static/assets/khoj.css"> <link rel="stylesheet" href="/static/assets/khoj.css">
</head> </head>
<script type="text/javascript" src="/static/assets/khoj.js"></script> <script type="text/javascript" src="/static/assets/utils.js"></script>
<body class="khoj-configure"> <body class="khoj-configure">
<div class="khoj-header-wrapper"> <div class="khoj-header-wrapper">
<div class="filler"></div> <div class="filler"></div>
@@ -38,9 +38,9 @@
display: grid; display: grid;
grid-template-columns: 1fr min(70vw, 100%) 1fr; grid-template-columns: 1fr min(70vw, 100%) 1fr;
} }
img.circle { .circle {
width: 49px; width: 51px;
height: 49px; height: 51px;
} }
.page { .page {
@@ -60,10 +60,10 @@
justify-items: start; justify-items: start;
gap: 8px; gap: 8px;
padding: 24px 24px; padding: 24px 24px;
background: white; background: var(--background-color);
border: 1px solid rgb(229, 229, 229); border: 1px solid rgb(229, 229, 229);
border-radius: 4px; 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); box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.1),0px 1px 2px -1px rgba(0,0,0,0.8);
} }
#api-settings-card-description { #api-settings-card-description {
margin: 8px 0 0 0; margin: 8px 0 0 0;
@@ -95,10 +95,10 @@
padding: 24px 16px; padding: 24px 16px;
width: 320px; width: 320px;
height: 180px; height: 180px;
background: white; background: var(--background-color);
border: 1px solid rgb(229, 229, 229); border: 1px solid rgb(229, 229, 229);
border-radius: 4px; 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); box-shadow: 0px 1px 3px 0px rgba(0,0,0,0.1),0px 1px 2px -1px rgba(0,0,0,0.8);
overflow: hidden; overflow: hidden;
} }
div.finalize-buttons { div.finalize-buttons {
@@ -106,7 +106,6 @@
gap: 8px; gap: 8px;
padding: 24px 16px; padding: 24px 16px;
width: 320px; width: 320px;
background: white;
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
} }
@@ -174,7 +173,7 @@
} }
button.card-button { button.card-button {
color: rgb(255, 136, 136); color: var(--flower);
background: transparent; background: transparent;
font-size: 16px; font-size: 16px;
cursor: pointer; cursor: pointer;
@@ -186,7 +185,7 @@
} }
button.card-button.happy { button.card-button.happy {
color: rgb(0, 146, 0); color: var(--leaf);
} }
img.configured-icon { img.configured-icon {

View File

@@ -8,7 +8,7 @@
<link rel="manifest" href="/static/khoj_chat.webmanifest"> <link rel="manifest" href="/static/khoj_chat.webmanifest">
<link rel="stylesheet" href="/static/assets/khoj.css"> <link rel="stylesheet" href="/static/assets/khoj.css">
</head> </head>
<script type="text/javascript" src="/static/assets/khoj.js"></script> <script type="text/javascript" src="/static/assets/utils.js"></script>
<script> <script>
let chatOptions = []; let chatOptions = [];
function copyProgrammaticOutput(event) { function copyProgrammaticOutput(event) {
@@ -67,6 +67,8 @@
// Replace any ** with <b> and __ with <u> // Replace any ** with <b> and __ with <u>
newHTML = newHTML.replace(/\*\*([\s\S]*?)\*\*/g, '<b>$1</b>'); newHTML = newHTML.replace(/\*\*([\s\S]*?)\*\*/g, '<b>$1</b>');
newHTML = newHTML.replace(/__([\s\S]*?)__/g, '<u>$1</u>'); newHTML = newHTML.replace(/__([\s\S]*?)__/g, '<u>$1</u>');
// Remove any text between <s>[INST] and </s> tags. These are spurious instructions for the AI chat model.
newHTML = newHTML.replace(/<s>\[INST\].+(<\/s>)?/g, '');
return newHTML; return newHTML;
} }
@@ -163,6 +165,7 @@
function incrementalChat(event) { function incrementalChat(event) {
if (!event.shiftKey && event.key === 'Enter') { if (!event.shiftKey && event.key === 'Enter') {
e.preventDefault();
chat(); chat();
} }
} }
@@ -281,8 +284,7 @@
<!-- Chat Footer --> <!-- Chat Footer -->
<div id="chat-footer"> <div id="chat-footer">
<div id="chat-tooltip" style="display: none;"></div> <div id="chat-tooltip" style="display: none;"></div>
<textarea id="chat-input" class="option" oninput="onChatInput()" onkeyup=incrementalChat(event) autofocus="autofocus" placeholder="Type / to see a list of commands, or just type your questions and hit enter."> <textarea id="chat-input" class="option" oninput="onChatInput()" onkeydown=incrementalChat(event) autofocus="autofocus" placeholder="Type / to see a list of commands, or just type your questions and hit enter."></textarea>
</textarea>
</div> </div>
</body> </body>

View File

@@ -22,36 +22,30 @@
<button id="khoj-banner-submit" class="khoj-banner-button">Submit</button> <button id="khoj-banner-submit" class="khoj-banner-button">Submit</button>
</div> </div>
{% endif %} {% endif %}
<!--Add Header Logo and Nav Pane--> <div class="khoj-header"></div>
<div class="khoj-header">
{% if demo %}
<a class="khoj-logo" href="https://khoj.dev" target="_blank">
<img class="khoj-logo" src="/static/assets/icons/khoj-logo-sideways-500.png" alt="Khoj"></img>
</a>
{% else %}
<a class="khoj-logo" href="/">
<img class="khoj-logo" src="/static/assets/icons/khoj-logo-sideways-500.png" alt="Khoj"></img>
</a>
{% endif %}
</div>
<!-- Sign Up button for Google OAuth --> <!-- Login Modal -->
<div id="login-modal"> <div id="login-modal">
<h1 class="login-modal-title">Become superhuman with your personal knowledge base copilot</h1> <img class="khoj-logo" src="/static/assets/icons/favicon-128x128.png" alt="Khoj"></img>
<div class="login-modal-title">Log in to Khoj</div>
<!-- Sign Up/Login with Google OAuth -->
<div <div
class="g_id_signin" class="g_id_signin"
data-shape="circle" data-shape="circle"
data-text="continue_with" data-text="continue_with"
data-logo_alignment="center" data-logo_alignment="center"
data-size="large" data-size="large"
data-width="300"
data-type="standard"> data-type="standard">
</div> </div>
<div id="g_id_onload"
data-client_id="{{ google_client_id }}"
data-ux_mode="redirect"
data-login_uri="{{ redirect_uri }}"
data-auto-select="true">
</div>
</div> </div>
<div id="g_id_onload"
data-client_id="{{ google_client_id }}" <div class="khoj-footer"></div>
data-ux_mode="redirect"
data-login_uri="{{ redirect_uri }}">
</div> </div>
@@ -62,7 +56,7 @@
body { body {
display: grid; display: grid;
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-template-rows: 1fr auto auto auto minmax(80px, 100%); grid-template-rows: 1fr auto 1fr;
font-size: small!important; font-size: small!important;
} }
body > * { body > * {
@@ -73,8 +67,7 @@
body { body {
display: grid; display: grid;
grid-template-columns: 1fr min(70vw, 100%) 1fr; grid-template-columns: 1fr min(70vw, 100%) 1fr;
grid-template-rows: 1fr auto auto auto minmax(80px, 100%); grid-template-rows: 1fr auto 1fr;
padding-top: 60vw;
} }
body > * { body > * {
grid-column: 2; grid-column: 2;
@@ -83,8 +76,9 @@
body { body {
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;
background: #fff; height: 100%;
color: #475569; background: var(--background-color);
color: var(--main-text-color);
font-family: roboto, karma, segoe ui, sans-serif; font-family: roboto, karma, segoe ui, sans-serif;
font-size: 20px; font-size: 20px;
font-weight: 300; font-weight: 300;
@@ -109,6 +103,7 @@
a.khoj-logo { a.khoj-logo {
text-align: center; text-align: center;
justify-self: center;
} }
button#khoj-banner-submit, button#khoj-banner-submit,
@@ -127,14 +122,10 @@
div#login-modal { div#login-modal {
display: grid; display: grid;
grid-template-columns: 1fr; grid-template-columns: 1fr;
grid-template-rows: 1fr auto auto auto; grid-template-rows: 1fr auto auto 1fr;
gap: 10px; gap: 32px;
padding: 10px; min-height: 300px;
margin: 10px; background: var(--background-color);
background: #fff;
border-radius: 5px;
border: 1px solid #475569;
box-shadow: 0 0 11px #aaa;
margin-left: 25%; margin-left: 25%;
margin-right: 25%; margin-right: 25%;
} }
@@ -144,10 +135,11 @@
display: block; display: block;
} }
h1.login-modal-title { div.login-modal-title {
text-align: center; text-align: center;
line-height: 28px; line-height: 28px;
font-size: x-large; font-size: 24px;
font-weight: 500;
} }
@media only screen and (max-width: 700px) { @media only screen and (max-width: 700px) {

View File

@@ -10,7 +10,7 @@
</head> </head>
<script type="text/javascript" src="/static/assets/org.min.js"></script> <script type="text/javascript" src="/static/assets/org.min.js"></script>
<script type="text/javascript" src="/static/assets/markdown-it.min.js"></script> <script type="text/javascript" src="/static/assets/markdown-it.min.js"></script>
<script type="text/javascript" src="/static/assets/khoj.js"></script> <script type="text/javascript" src="/static/assets/utils.js"></script>
<script> <script>
function render_image(item) { function render_image(item) {

View File

@@ -5,7 +5,7 @@
</a> </a>
<nav class="khoj-nav"> <nav class="khoj-nav">
<a class="khoj-nav" href="/chat">💬 Chat</a> <a class="khoj-nav" href="/chat">💬 Chat</a>
<a class="khoj-nav" href="/">🔎 Search</a> <a class="khoj-nav" href="/search">🔎 Search</a>
<!-- Dropdown Menu --> <!-- Dropdown Menu -->
<div id="khoj-nav-menu-container" class="khoj-nav dropdown"> <div id="khoj-nav-menu-container" class="khoj-nav dropdown">
{% if user_photo and user_photo != "None" %} {% if user_photo and user_photo != "None" %}

View File

@@ -122,6 +122,7 @@ def set_state(args):
state.demo = args.demo state.demo = args.demo
state.anonymous_mode = args.anonymous_mode state.anonymous_mode = args.anonymous_mode
state.khoj_version = version("khoj-assistant") state.khoj_version = version("khoj-assistant")
state.chat_on_gpu = args.chat_on_gpu
def start_server(app, host=None, port=None, socket=None): def start_server(app, host=None, port=None, socket=None):

View File

@@ -0,0 +1,69 @@
"""
Current format of khoj.yml
---
app:
...
content-type:
...
processor:
conversation:
offline-chat:
enable-offline-chat: false
chat-model: llama-2-7b-chat.ggmlv3.q4_0.bin
...
search-type:
...
New format of khoj.yml
---
app:
...
content-type:
...
processor:
conversation:
offline-chat:
enable-offline-chat: false
chat-model: mistral-7b-instruct-v0.1.Q4_0.gguf
...
search-type:
...
"""
import logging
from packaging import version
from khoj.utils.yaml import load_config_from_file, save_config_to_file
logger = logging.getLogger(__name__)
def migrate_offline_chat_default_model(args):
schema_version = "0.12.4"
raw_config = load_config_from_file(args.config_file)
previous_version = raw_config.get("version")
if "processor" not in raw_config:
return args
if raw_config["processor"] is None:
return args
if "conversation" not in raw_config["processor"]:
return args
if "offline-chat" not in raw_config["processor"]["conversation"]:
return args
if "chat-model" not in raw_config["processor"]["conversation"]["offline-chat"]:
return args
if previous_version is None or version.parse(previous_version) < version.parse("0.12.4"):
logger.info(
f"Upgrading config schema to {schema_version} from {previous_version} to change default (offline) chat model to mistral GGUF"
)
raw_config["version"] = schema_version
# Update offline chat model to mistral in GGUF format to use latest GPT4All
offline_chat_model = raw_config["processor"]["conversation"]["offline-chat"]["chat-model"]
if offline_chat_model.endswith(".bin"):
raw_config["processor"]["conversation"]["offline-chat"]["chat-model"] = "mistral-7b-instruct-v0.1.Q4_0.gguf"
save_config_to_file(raw_config, args.config_file)
return args

View File

@@ -9,7 +9,7 @@ processor:
conversation-logfile: ~/.khoj/processor/conversation/conversation_logs.json conversation-logfile: ~/.khoj/processor/conversation/conversation_logs.json
max-prompt-size: null max-prompt-size: null
offline-chat: offline-chat:
chat-model: llama-2-7b-chat.ggmlv3.q4_0.bin chat-model: mistral-7b-instruct-v0.1.Q4_0.gguf
enable-offline-chat: false enable-offline-chat: false
openai: openai:
api-key: sk-blah api-key: sk-blah
@@ -46,7 +46,7 @@ processor:
- chat-model: gpt-3.5-turbo - chat-model: gpt-3.5-turbo
tokenizer: null tokenizer: null
type: openai type: openai
- chat-model: llama-2-7b-chat.ggmlv3.q4_0.bin - chat-model: mistral-7b-instruct-v0.1.Q4_0.gguf
tokenizer: null tokenizer: null
type: offline type: offline
search-type: search-type:

View File

@@ -16,7 +16,7 @@ logger = logging.getLogger(__name__)
def extract_questions_offline( def extract_questions_offline(
text: str, text: str,
model: str = "llama-2-7b-chat.ggmlv3.q4_0.bin", model: str = "mistral-7b-instruct-v0.1.Q4_0.gguf",
loaded_model: Union[Any, None] = None, loaded_model: Union[Any, None] = None,
conversation_log={}, conversation_log={},
use_history: bool = True, use_history: bool = True,
@@ -123,7 +123,7 @@ def converse_offline(
references, references,
user_query, user_query,
conversation_log={}, conversation_log={},
model: str = "llama-2-7b-chat.ggmlv3.q4_0.bin", model: str = "mistral-7b-instruct-v0.1.Q4_0.gguf",
loaded_model: Union[Any, None] = None, loaded_model: Union[Any, None] = None,
completion_func=None, completion_func=None,
conversation_command=ConversationCommand.Default, conversation_command=ConversationCommand.Default,

View File

@@ -1,5 +1,6 @@
import logging import logging
from khoj.utils import state
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -16,8 +17,13 @@ def download_model(model_name: str):
# Decide whether to load model to GPU or CPU # Decide whether to load model to GPU or CPU
try: try:
# Check if machine has GPU and GPU has enough free memory to load the chat model # Try load chat model to GPU if:
device = "gpu" if gpt4all.pyllmodel.LLModel().list_gpu(chat_model_config["path"]) else "cpu" # 1. Loading chat model to GPU isn't disabled via CLI and
# 2. Machine has GPU
# 3. GPU has enough free memory to load the chat model
device = (
"gpu" if state.chat_on_gpu and gpt4all.pyllmodel.LLModel().list_gpu(chat_model_config["path"]) else "cpu"
)
except ValueError: except ValueError:
device = "cpu" device = "cpu"

View File

@@ -20,9 +20,11 @@ model_to_prompt_size = {
"gpt-4": 8192, "gpt-4": 8192,
"llama-2-7b-chat.ggmlv3.q4_0.bin": 1548, "llama-2-7b-chat.ggmlv3.q4_0.bin": 1548,
"gpt-3.5-turbo-16k": 15000, "gpt-3.5-turbo-16k": 15000,
"mistral-7b-instruct-v0.1.Q4_0.gguf": 1548,
} }
model_to_tokenizer = { model_to_tokenizer = {
"llama-2-7b-chat.ggmlv3.q4_0.bin": "hf-internal-testing/llama-tokenizer", "llama-2-7b-chat.ggmlv3.q4_0.bin": "hf-internal-testing/llama-tokenizer",
"mistral-7b-instruct-v0.1.Q4_0.gguf": "mistralai/Mistral-7B-Instruct-v0.1",
} }

View File

@@ -35,7 +35,7 @@ def index(request: Request):
user_picture = request.session.get("user", {}).get("picture") user_picture = request.session.get("user", {}).get("picture")
return templates.TemplateResponse( return templates.TemplateResponse(
"index.html", "chat.html",
context={ context={
"request": request, "request": request,
"demo": state.demo, "demo": state.demo,
@@ -52,7 +52,24 @@ def index_post(request: Request):
user_picture = request.session.get("user", {}).get("picture") user_picture = request.session.get("user", {}).get("picture")
return templates.TemplateResponse( return templates.TemplateResponse(
"index.html", "chat.html",
context={
"request": request,
"demo": state.demo,
"username": user.username,
"user_photo": user_picture,
},
)
@web_client.get("/search", response_class=FileResponse)
@requires(["authenticated"], redirect="login_page")
def search_page(request: Request):
user = request.user.object
user_picture = request.session.get("user", {}).get("picture")
return templates.TemplateResponse(
"search.html",
context={ context={
"request": request, "request": request,
"demo": state.demo, "demo": state.demo,

View File

@@ -14,6 +14,7 @@ from khoj.migrations.migrate_version import migrate_config_to_version
from khoj.migrations.migrate_processor_config_openai import migrate_processor_conversation_schema from khoj.migrations.migrate_processor_config_openai import migrate_processor_conversation_schema
from khoj.migrations.migrate_offline_model import migrate_offline_model from khoj.migrations.migrate_offline_model import migrate_offline_model
from khoj.migrations.migrate_offline_chat_schema import migrate_offline_chat_schema from khoj.migrations.migrate_offline_chat_schema import migrate_offline_chat_schema
from khoj.migrations.migrate_offline_chat_default_model import migrate_offline_chat_default_model
from khoj.migrations.migrate_server_pg import migrate_server_pg from khoj.migrations.migrate_server_pg import migrate_server_pg
@@ -38,6 +39,9 @@ def cli(args=None):
help="Path to UNIX socket for server. Use to run server behind reverse proxy. Default: /tmp/uvicorn.sock", help="Path to UNIX socket for server. Use to run server behind reverse proxy. Default: /tmp/uvicorn.sock",
) )
parser.add_argument("--version", "-V", action="store_true", help="Print the installed Khoj version and exit") parser.add_argument("--version", "-V", action="store_true", help="Print the installed Khoj version and exit")
parser.add_argument(
"--disable-chat-on-gpu", action="store_true", default=False, help="Disable using GPU for the offline chat model"
)
parser.add_argument("--demo", action="store_true", default=False, help="Run Khoj in demo mode") parser.add_argument("--demo", action="store_true", default=False, help="Run Khoj in demo mode")
parser.add_argument( parser.add_argument(
"--anonymous-mode", "--anonymous-mode",
@@ -50,6 +54,9 @@ def cli(args=None):
logger.debug(f"Ignoring unknown commandline args: {remaining_args}") logger.debug(f"Ignoring unknown commandline args: {remaining_args}")
# Set default values for arguments
args.chat_on_gpu = not args.disable_chat_on_gpu
args.version_no = version("khoj-assistant") args.version_no = version("khoj-assistant")
if args.version: if args.version:
# Show version of khoj installed and exit # Show version of khoj installed and exit
@@ -76,6 +83,7 @@ def run_migrations(args):
migrate_processor_conversation_schema, migrate_processor_conversation_schema,
migrate_offline_model, migrate_offline_model,
migrate_offline_chat_schema, migrate_offline_chat_schema,
migrate_offline_chat_default_model,
migrate_server_pg, migrate_server_pg,
] ]
for migration in migrations: for migration in migrations:

View File

@@ -84,7 +84,7 @@ class OpenAIProcessorConfig(ConfigBase):
class OfflineChatProcessorConfig(ConfigBase): class OfflineChatProcessorConfig(ConfigBase):
enable_offline_chat: Optional[bool] = False enable_offline_chat: Optional[bool] = False
chat_model: Optional[str] = "llama-2-7b-chat.ggmlv3.q4_0.bin" chat_model: Optional[str] = "mistral-7b-instruct-v0.1.Q4_0.gguf"
class ConversationProcessorConfig(ConfigBase): class ConversationProcessorConfig(ConfigBase):

View File

@@ -33,5 +33,6 @@ SearchType = utils_config.SearchType
telemetry: List[Dict[str, str]] = [] telemetry: List[Dict[str, str]] = []
demo: bool = False demo: bool = False
khoj_version: str = None khoj_version: str = None
anonymous_mode: bool = False
device = get_device() device = get_device()
chat_on_gpu: bool = True
anonymous_mode: bool = False

View File

@@ -211,7 +211,7 @@ def chat_client_no_background(search_config: SearchConfig, default_user2: KhojUs
# Initialize Processor from Config # Initialize Processor from Config
if os.getenv("OPENAI_API_KEY"): if os.getenv("OPENAI_API_KEY"):
OpenAIProcessorConversationConfigFactory(user=default_user2) OpenAIProcessorConversationConfigFactory()
state.anonymous_mode = True state.anonymous_mode = True

View File

@@ -14,4 +14,4 @@ search-type:
asymmetric: asymmetric:
cross-encoder: cross-encoder/ms-marco-MiniLM-L-6-v2 cross-encoder: cross-encoder/ms-marco-MiniLM-L-6-v2
encoder: sentence-transformers/msmarco-MiniLM-L-6-v3 encoder: sentence-transformers/msmarco-MiniLM-L-6-v3
version: 0.10.1 version: 0.14.0

View File

@@ -37,7 +37,7 @@ class ChatModelOptionsFactory(factory.django.DjangoModelFactory):
max_prompt_size = 2000 max_prompt_size = 2000
tokenizer = None tokenizer = None
chat_model = "llama-2-7b-chat.ggmlv3.q4_0.bin" chat_model = "mistral-7b-instruct-v0.1.Q4_0.gguf"
model_type = "offline" model_type = "offline"

View File

@@ -24,7 +24,7 @@ from khoj.processor.conversation.gpt4all.utils import download_model
from khoj.processor.conversation.utils import message_to_log from khoj.processor.conversation.utils import message_to_log
MODEL_NAME = "llama-2-7b-chat.ggmlv3.q4_0.bin" MODEL_NAME = "mistral-7b-instruct-v0.1.Q4_0.gguf"
@pytest.fixture(scope="session") @pytest.fixture(scope="session")