Skip to content

Commit

Permalink
ui: interface revamp (janhq#429)
Browse files Browse the repository at this point in the history
* feat: adding create bot functionality

Signed-off-by: James <[email protected]>

* update the temperature progress bar

Signed-off-by: James <[email protected]>

* WIP baselayout

* Mapping plugins with available preferences

* Added loader component

* WIP working another screen

* Cleanup types and avoid import one by one

* Prepare bottom bar

* Add css variables colors to enable user select the accent

* Enable change accent color

* Seperate css variable

* Fix conflict

* Add blank state of my model empty

* Restyle explore models page

* Enable user config left sidebar

* Restyle my models page

* WIP styling chat page

* Restyling chat message

* Fix conflict

* Adde form preferences setting plugins

* Fixed form bot info

* Sidebar bot chat

* Showing rightbar for both setting when user created bot

* Fix style bot info

* Using overflow auto intead of scroll

* Remove script built UI from root package

* Fix missig import

* Resolve error linter

* fix e2e tests

Signed-off-by: James <[email protected]>

---------

Signed-off-by: James <[email protected]>
Co-authored-by: James <[email protected]>
  • Loading branch information
urmauur and James authored Oct 24, 2023
1 parent e332a7c commit 539b467
Show file tree
Hide file tree
Showing 209 changed files with 3,287 additions and 1,914 deletions.
41 changes: 34 additions & 7 deletions electron/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { app, BrowserWindow, ipcMain, dialog, shell } from "electron";
import {
app,
BrowserWindow,
ipcMain,
dialog,
shell,
nativeTheme,
} from "electron";
import { readdirSync, writeFileSync } from "fs";
import { resolve, join, extname } from "path";
import { rmdir, unlink, createWriteStream } from "fs";
Expand Down Expand Up @@ -36,12 +43,30 @@ app.on("window-all-closed", () => {
app.quit();
});

ipcMain.handle("setNativeThemeLight", () => {
nativeTheme.themeSource = "light";
});

ipcMain.handle("setNativeThemeDark", () => {
nativeTheme.themeSource = "dark";
});

ipcMain.handle("setNativeThemeSystem", () => {
nativeTheme.themeSource = "system";
});

function createMainWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
frame: false,
show: false,
backgroundColor: "white",
trafficLightPosition: {
x: 16,
y: 10,
},
titleBarStyle: "hidden",
vibrancy: "sidebar",
webPreferences: {
nodeIntegration: true,
preload: join(__dirname, "preload.js"),
Expand Down Expand Up @@ -118,11 +143,13 @@ function handleIPCs() {
ipcMain.handle(
"invokePluginFunc",
async (_event, modulePath, method, ...args) => {
const module = require(/* webpackIgnore: true */ join(
app.getPath("userData"),
"plugins",
modulePath
));
const module = require(
/* webpackIgnore: true */ join(
app.getPath("userData"),
"plugins",
modulePath
)
);
requiredModules[modulePath] = module;

if (typeof module[method] === "function") {
Expand Down
30 changes: 22 additions & 8 deletions electron/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ contextBridge.exposeInMainWorld("electronAPI", {
invokePluginFunc: (plugin: any, method: any, ...args: any[]) =>
ipcRenderer.invoke("invokePluginFunc", plugin, method, ...args),

setNativeThemeLight: () => ipcRenderer.invoke("setNativeThemeLight"),

setNativeThemeDark: () => ipcRenderer.invoke("setNativeThemeDark"),

setNativeThemeSystem: () => ipcRenderer.invoke("setNativeThemeSystem"),

basePlugins: () => ipcRenderer.invoke("basePlugins"),

pluginPath: () => ipcRenderer.invoke("pluginPath"),
Expand All @@ -23,19 +29,27 @@ contextBridge.exposeInMainWorld("electronAPI", {

deleteFile: (filePath: string) => ipcRenderer.invoke("deleteFile", filePath),

installRemotePlugin: (pluginName: string) => ipcRenderer.invoke("installRemotePlugin", pluginName),
installRemotePlugin: (pluginName: string) =>
ipcRenderer.invoke("installRemotePlugin", pluginName),

downloadFile: (url: string, path: string) => ipcRenderer.invoke("downloadFile", url, path),
downloadFile: (url: string, path: string) =>
ipcRenderer.invoke("downloadFile", url, path),

onFileDownloadUpdate: (callback: any) => ipcRenderer.on("FILE_DOWNLOAD_UPDATE", callback),
onFileDownloadUpdate: (callback: any) =>
ipcRenderer.on("FILE_DOWNLOAD_UPDATE", callback),

onFileDownloadError: (callback: any) => ipcRenderer.on("FILE_DOWNLOAD_ERROR", callback),
onFileDownloadError: (callback: any) =>
ipcRenderer.on("FILE_DOWNLOAD_ERROR", callback),

onFileDownloadSuccess: (callback: any) => ipcRenderer.on("FILE_DOWNLOAD_COMPLETE", callback),
onFileDownloadSuccess: (callback: any) =>
ipcRenderer.on("FILE_DOWNLOAD_COMPLETE", callback),

onAppUpdateDownloadUpdate: (callback: any) => ipcRenderer.on("APP_UPDATE_PROGRESS", callback),
onAppUpdateDownloadUpdate: (callback: any) =>
ipcRenderer.on("APP_UPDATE_PROGRESS", callback),

onAppUpdateDownloadError: (callback: any) => ipcRenderer.on("APP_UPDATE_ERROR", callback),
onAppUpdateDownloadError: (callback: any) =>
ipcRenderer.on("APP_UPDATE_ERROR", callback),

onAppUpdateDownloadSuccess: (callback: any) => ipcRenderer.on("APP_UPDATE_COMPLETE", callback),
onAppUpdateDownloadSuccess: (callback: any) =>
ipcRenderer.on("APP_UPDATE_COMPLETE", callback),
});
6 changes: 2 additions & 4 deletions electron/tests/main.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,8 @@ test("renders the home page", async () => {

// Welcome text is available
const welcomeText = await page
.locator(".text-5xl", {
hasText: "Welcome,let’s download your first model",
})
.getByTestId("testid-welcome-title")
.first()
.isDisabled();
.isVisible();
expect(welcomeText).toBe(false);
});
2 changes: 1 addition & 1 deletion electron/tests/my-models.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ test("shows my models", async () => {
.getByRole("heading")
.filter({ hasText: "My Models" })
.first()
.isDisabled();
.isVisible();
expect(header).toBe(false);
// More test cases here...
});
36 changes: 6 additions & 30 deletions electron/tests/navigation.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,37 +35,13 @@ test.afterAll(async () => {
});

test("renders left navigation panel", async () => {
// Chat History section is available
const chatSection = await page
.getByRole("heading")
.filter({ hasText: "CHAT HISTORY" })
.first()
.isDisabled();
// Chat section should be there
const chatSection = await page.getByTestId("Chat").first().isVisible();
expect(chatSection).toBe(false);

// Home actions
const createBotBtn = await page
.getByRole("button", { name: "Create bot" })
.first()
.isEnabled();
const exploreBtn = await page
.getByRole("button", { name: "Explore Models" })
.first()
.isEnabled();
const myModelsBtn = await page
.getByTestId("My Models")
.first()
.isEnabled();
const settingsBtn = await page
.getByTestId("Settings")
.first()
.isEnabled();
expect(
[
createBotBtn,
exploreBtn,
myModelsBtn,
settingsBtn,
].filter((e) => !e).length
).toBe(0);
const botBtn = await page.getByTestId("Bot").first().isEnabled();
const myModelsBtn = await page.getByTestId("My Models").first().isEnabled();
const settingsBtn = await page.getByTestId("Settings").first().isEnabled();
expect([botBtn, myModelsBtn, settingsBtn].filter((e) => !e).length).toBe(0);
});
4 changes: 1 addition & 3 deletions electron/tests/settings.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,5 @@ test.afterAll(async () => {

test("shows settings", async () => {
await page.getByTestId("Settings").first().click();

const pluginList = await page.getByTestId("plugin-item").count();
expect(pluginList).toBe(4);
await page.getByTestId("testid-setting-description").isVisible();
});
6 changes: 1 addition & 5 deletions plugins/data-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,5 @@
"@janhq/core": "^0.1.6",
"pouchdb-find": "^8.0.1",
"pouchdb-node": "^8.0.1"
},
"bundleDependencies": [
"pouchdb-node",
"pouchdb-find"
]
}
}
54 changes: 40 additions & 14 deletions plugins/inference-plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ import {
} from "@janhq/core";
import { Observable } from "rxjs";

const initModel = async (product) => invokePluginFunc(MODULE_PATH, "initModel", product);
const initModel = async (product) =>
invokePluginFunc(MODULE_PATH, "initModel", product);

const stopModel = () => {
invokePluginFunc(MODULE_PATH, "killSubprocess");
};

function requestInference(recentMessages: any[], bot?: any): Observable<string> {
function requestInference(
recentMessages: any[],
bot?: any
): Observable<string> {
return new Observable((subscriber) => {
const requestBody = JSON.stringify({
messages: recentMessages,
Expand Down Expand Up @@ -69,10 +73,15 @@ function requestInference(recentMessages: any[], bot?: any): Observable<string>

async function retrieveLastTenMessages(conversationId: string, bot?: any) {
// TODO: Common collections should be able to access via core functions instead of store
const messageHistory = (await store.findMany("messages", { conversationId }, [{ createdAt: "asc" }])) ?? [];
const messageHistory =
(await store.findMany("messages", { conversationId }, [
{ createdAt: "asc" },
])) ?? [];

let recentMessages = messageHistory
.filter((e) => e.message !== "" && (e.user === "user" || e.user === "assistant"))
.filter(
(e) => e.message !== "" && (e.user === "user" || e.user === "assistant")
)
.slice(-9)
.map((message) => ({
content: message.message.trim(),
Expand All @@ -81,10 +90,13 @@ async function retrieveLastTenMessages(conversationId: string, bot?: any) {

if (bot && bot.systemPrompt) {
// append bot's system prompt
recentMessages = [{
content: `[INST] ${bot.systemPrompt}`,
role: 'system'
},...recentMessages];
recentMessages = [
{
content: `[INST] ${bot.systemPrompt}`,
role: "system",
},
...recentMessages,
];
}

console.debug(`Last 10 messages: ${JSON.stringify(recentMessages, null, 2)}`);
Expand All @@ -93,13 +105,19 @@ async function retrieveLastTenMessages(conversationId: string, bot?: any) {
}

async function handleMessageRequest(data: NewMessageRequest) {
const conversation = await store.findOne("conversations", data.conversationId);
const conversation = await store.findOne(
"conversations",
data.conversationId
);
let bot = undefined;
if (conversation.botId != null) {
bot = await store.findOne("bots", conversation.botId);
}

const recentMessages = await retrieveLastTenMessages(data.conversationId, bot);

const recentMessages = await retrieveLastTenMessages(
data.conversationId,
bot
);
const message = {
...data,
message: "",
Expand All @@ -124,7 +142,8 @@ async function handleMessageRequest(data: NewMessageRequest) {
await store.updateOne("messages", message._id, message);
},
error: async (err) => {
message.message = message.message.trim() + "\n" + "Error occurred: " + err;
message.message =
message.message.trim() + "\n" + "Error occurred: " + err;
// TODO: Common collections should be able to access via core functions instead of store
await store.updateOne("messages", message._id, message);
},
Expand All @@ -140,7 +159,10 @@ async function inferenceRequest(data: NewMessageRequest): Promise<any> {
};
return new Promise(async (resolve, reject) => {
const recentMessages = await retrieveLastTenMessages(data.conversationId);
requestInference([...recentMessages, { role: "user", content: data.message }]).subscribe({
requestInference([
...recentMessages,
{ role: "user", content: data.message },
]).subscribe({
next: (content) => {
message.message = content;
},
Expand All @@ -166,5 +188,9 @@ export function init({ register }) {
register(PluginService.OnStart, PLUGIN_NAME, onStart);
register(InferenceService.InitModel, initModel.name, initModel);
register(InferenceService.StopModel, stopModel.name, stopModel);
register(InferenceService.InferenceRequest, inferenceRequest.name, inferenceRequest);
register(
InferenceService.InferenceRequest,
inferenceRequest.name,
inferenceRequest
);
}
2 changes: 1 addition & 1 deletion web/app/_components/ActiveModelTable/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useAtomValue } from 'jotai'
import React from 'react'
import ModelTable from '../ModelTable'
import { activeAssistantModelAtom } from '@/_helpers/atoms/Model.atom'
import { activeAssistantModelAtom } from '@helpers/atoms/Model.atom'

const ActiveModelTable: React.FC = () => {
const activeModel = useAtomValue(activeAssistantModelAtom)
Expand Down
3 changes: 1 addition & 2 deletions web/app/_components/AvailableModelCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import DownloadModelContent from '../DownloadModelContent'
import ModelDownloadButton from '../ModelDownloadButton'
import ModelDownloadingButton from '../ModelDownloadingButton'
import { useAtomValue } from 'jotai'
import { modelDownloadStateAtom } from '@/_helpers/atoms/DownloadState.atom'
import { AssistantModel } from '@/_models/AssistantModel'
import { modelDownloadStateAtom } from '@helpers/atoms/DownloadState.atom'

type Props = {
model: AssistantModel
Expand Down
2 changes: 1 addition & 1 deletion web/app/_components/Avatar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type Props = {

const Avatar: React.FC<Props> = ({ allowEdit = false }) => (
<div className="mx-auto flex flex-col gap-5">
<span className="mx-auto inline-block h-14 w-14 overflow-hidden rounded-full bg-gray-100">
<span className="mx-auto inline-block h-10 w-10 overflow-hidden rounded-full bg-gray-100">
<svg
className="mx-auto h-full w-full text-gray-300"
fill="currentColor"
Expand Down
4 changes: 2 additions & 2 deletions web/app/_components/BasicPromptAccessories/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
import { useSetAtom } from 'jotai'
import { InformationCircleIcon } from '@heroicons/react/24/outline'
import SendButton from '../SendButton'
import { showingAdvancedPromptAtom } from '@/_helpers/atoms/Modal.atom'
import { showingAdvancedPromptAtom } from '@helpers/atoms/Modal.atom'

const BasicPromptAccessories: React.FC = () => {
const setShowingAdvancedPrompt = useSetAtom(showingAdvancedPromptAtom)

const shouldShowAdvancedPrompt = false

return (
<div className="absolute inset-x-0 bottom-0 flex justify-between py-2 pl-3 pr-2">
<div className="absolute inset-x-0 bottom-0 flex justify-between p-3">
{/* Add future accessories here, e.g upload a file */}
<div className="flex items-center space-x-5">
<div className="flex items-center">
Expand Down
2 changes: 1 addition & 1 deletion web/app/_components/BasicPromptButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import { useSetAtom } from 'jotai'
import { ChevronLeftIcon } from '@heroicons/react/24/outline'
import { showingAdvancedPromptAtom } from '@/_helpers/atoms/Modal.atom'
import { showingAdvancedPromptAtom } from '@helpers/atoms/Modal.atom'

const BasicPromptButton: React.FC = () => {
const setShowingAdvancedPrompt = useSetAtom(showingAdvancedPromptAtom)
Expand Down
Loading

0 comments on commit 539b467

Please sign in to comment.