Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Jan into an Electron app #175

Merged
merged 51 commits into from
Sep 25, 2023
Merged
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
cf2092b
hackathon: Refactor Jan into an Electron app
louis-menlo Sep 14, 2023
c9c11b9
chore: correct NextJS export output path
louis-menlo Sep 14, 2023
d80e454
chore: build electron app for all production targets
louis-menlo Sep 14, 2023
7b2c6a8
fix: correct assetPrefix for production build
louis-menlo Sep 14, 2023
df15fa7
chore: preferences shortcut
louis-menlo Sep 18, 2023
d7149ac
chore: refactor
louis-menlo Sep 18, 2023
3476f1d
chore: refactor into ts
louis-menlo Sep 18, 2023
22dacaa
feature/#52-compile-plugin-with-webpack
louis-menlo Sep 18, 2023
7a25f94
chore: introduce renderer <=> plugins <=> main invocation
louis-menlo Sep 19, 2023
acd29c3
chore: suppress errors - deprecate graphql & next-auth
louis-menlo Sep 19, 2023
aa496d3
chore: data plugin functions
louis-menlo Sep 19, 2023
f133db5
add llm support
Sep 20, 2023
c021f2b
chore: update plugin
louis-menlo Sep 20, 2023
cdd5888
chore: introduce data-plugin
louis-menlo Sep 20, 2023
6e6546b
chore: plugin invokes main with args and synchronously
louis-menlo Sep 20, 2023
5c08ca6
chore: install db plugin should setup db
louis-menlo Sep 20, 2023
6c0d88e
feature: Data Driver Plugin - Load conversations and messages from da…
louis-menlo Sep 20, 2023
99d29a8
chore: store text message sent
louis-menlo Sep 20, 2023
5658a29
chore: shared core services
louis-menlo Sep 20, 2023
d6a44f0
feature: inference service
louis-menlo Sep 20, 2023
b52869e
chore: conversations ordering
louis-menlo Sep 20, 2023
16bc40a
adding model management service
Sep 21, 2023
9be188d
chore: strict type
louis-menlo Sep 21, 2023
e0563ad
feature: abstract plugin preferences
louis-menlo Sep 21, 2023
c0c07d6
chore: abstract plugin preference
louis-menlo Sep 21, 2023
5c58a64
Revert "chore: strict type"
louis-menlo Sep 21, 2023
4c962c4
chore: base-plugin styling
louis-menlo Sep 21, 2023
b90f91a
feature: create and delete conversation
louis-menlo Sep 21, 2023
9e249a3
chore: fix plugin search & clean messages
louis-menlo Sep 21, 2023
b4395c6
chore: typing indicator
louis-menlo Sep 21, 2023
22e1c23
chore: refactor useSendChatMessage
louis-menlo Sep 21, 2023
2845a71
chore: persists inserted id to in-memory messages
louis-menlo Sep 21, 2023
43d2dc4
chore: search conversation history
louis-menlo Sep 21, 2023
7b10739
add delete and download model (#189)
namchuai Sep 21, 2023
f6ce072
chore: add empty state for conversation list
louis-menlo Sep 21, 2023
45928f6
chore: prompt missing extension function & fix app crashes
louis-menlo Sep 21, 2023
3ed4db2
chore: prompt user to install required plugins
louis-menlo Sep 21, 2023
7e04d09
chore: add launch background
louis-menlo Sep 21, 2023
5df48c4
chore: relaunch app on model downloaded
louis-menlo Sep 21, 2023
eb82bfe
Jan app add installation instruction (#191)
hiento09 Sep 21, 2023
3091cc2
Chore: rename folder web-client to app (#192)
hiento09 Sep 21, 2023
0dec38c
revert: add pre-install package
Sep 21, 2023
be2cbd9
add progress for downloading model
Sep 22, 2023
9fabd64
feature: production bundle
louis-menlo Sep 22, 2023
c6dda93
add download progress
Sep 22, 2023
a47a766
chore: add new chat function
louis-menlo Sep 22, 2023
6370092
fix: electron asar unpack modules & dynamic import
louis-menlo Sep 22, 2023
2f7afc4
chore: fix unpack
louis-menlo Sep 22, 2023
cf84b21
chore: fix dev pack
louis-menlo Sep 22, 2023
cd1be09
Add instruction to build dmg file to README.md
Sep 24, 2023
83c6847
init model dynamically
Sep 24, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -4,3 +4,9 @@
# Jan inference
models/**
error.log
app/electron/core/*/node_modules
app/electron/core/*/dist
app/electron/core/*/package-lock.json
*.tgz
app/yarn.lock
app/dist
File renamed without changes.
File renamed without changes.
File renamed without changes.
96 changes: 96 additions & 0 deletions app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# App

Jan Desktop is an Electron application designed to allow users to interact with the Language Model (LLM) through chat or create art using Stable Diffusion.

## Features

- Chat with the Language Model: Engage in interactive conversations with the Language Model. Ask questions, seek information, or simply have a chat.

- Generate Art with Stable Diffusion: Utilize the power of Stable Diffusion to generate unique and captivating pieces of art. Experiment with various parameters to achieve desired results.

## Installation and Usage

### Pre-requisites
- node >= 20.0.0
- yarn >= 1.22.0

### Use as complete suite (in progress)

### For interactive development

Note: This instruction is tested on MacOS only.

1. **Clone the Repository:**

```
git clone https://github.com/janhq/jan
git checkout feature/hackathon-refactor-jan-into-electron-app
cd jan/app
```

2. **Install dependencies:**

```
yarn install
```

3. **Download Model and copy to userdata directory** (this is a hacky step, will be remove in future versions)

```
# Determining the path to save model with /Users/<username>/Library/Application Support/jan-web/
mkdir /Users/<username>/Library/Application Support/jan-web
# Now download the model to correct location by running command
wget -O /Users/<username>/Library/Application Support/jan-web/llama-2-7b-chat.gguf.q4_0.bin https://huggingface.co/TheBloke/Llama-2-7b-Chat-GGUF/resolve/main/llama-2-7b-chat.Q4_0.gguf
```

4. **Run development and Using Jan Desktop**

```
yarn electron:start
```
This will start the development server and open the desktop app.
In this step, there are a few notification about installing base plugin, just click `OK` and `Next` to continue.
![](./images/jan-desktop-dev-instruction-1.png)
![](./images/jan-desktop-dev-instruction-2.png)
![](./images/jan-desktop-dev-instruction-3.png)

After that, you can use Jan Desktop as normal.
![](./images/jan-desktop-dev-instruction-4.png)
![](./images/jan-desktop-dev-instruction-5.png)
![](./images/jan-desktop-dev-instruction-6.png)

### For production build

```bash
# Do step 1 and 2 in previous section
git clone https://github.com/janhq/jan
git checkout feature/hackathon-refactor-jan-into-electron-app
cd jan/app
yarn install

# Build the app
yarn electron:build:all
```

This will build the app MacOS m1/m2 for production (with code signing already done) and put the result in `dist` folder.

## Configuration

TO DO

## Dependencies

TO DO

## Contributing

Contributions are welcome! If you find a bug or have suggestions for improvements, feel free to open an issue or submit a pull request on the [GitHub repository](https://github.com/janhq/jan).

## License

This project is licensed under the Fair-code License - see the [License](https://faircode.io/#licenses) for more details.

---

Feel free to reach out [Discord](https://jan.ai/discord) if you have any questions or need further assistance. Happy coding with Jan Web and exploring the capabilities of the Language Model and Stable Diffusion! 🚀🎨🤖
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ const AdvancedPromptGenerationParams = () => {
return (
<>
<TogglableHeader
icon={"/icons/unicorn_layers-alt.svg"}
icon={"icons/unicorn_layers-alt.svg"}
title={"Generation Parameters"}
expand={expand}
onTitleClick={() => setExpand(!expand)}
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ const AdvancedPromptImageUpload: React.FC<Props> = ({ register }) => {
return (
<>
<TogglableHeader
icon={"/icons/ic_image.svg"}
icon={"icons/ic_image.svg"}
title={"Image"}
expand={expand}
onTitleClick={() => setExpand(!expand)}
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ const AdvancedPromptResolution = () => {
return (
<>
<TogglableHeader
icon={"/icons/unicorn_layers-alt.svg"}
icon={"icons/unicorn_layers-alt.svg"}
title={"Resolution"}
expand={expand}
onTitleClick={() => setExpand(!expand)}
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ const AdvancedPromptText: React.FC<Props> = ({ register }) => {
return (
<>
<TogglableHeader
icon={"/icons/messicon.svg"}
icon={"icons/messicon.svg"}
title={"Prompt"}
expand={expand}
onTitleClick={() => setExpand(!expand)}
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ const Search: React.FC = () => {
return (
<div className="flex bg-gray-200 w-[343px] h-[36px] items-center px-2 gap-[6px] rounded-md">
<Image
src={"/icons/magnifyingglass.svg"}
src={"icons/magnifyingglass.svg"}
width={15.63}
height={15.78}
alt=""
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -13,8 +13,8 @@ const BasicPromptAccessories: React.FC = () => {
const setShowingAdvancedPrompt = useSetAtom(showingAdvancedPromptAtom);
const currentConversation = useAtomValue(currentConversationAtom);

const shouldShowAdvancedPrompt =
currentConversation?.product.type === ProductType.ControlNet;
const shouldShowAdvancedPrompt = false;
// currentConversation?.product.type === ProductType.ControlNet;

return (
<div
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -4,11 +4,16 @@ import React, { useCallback, useRef, useState } from "react";
import ChatItem from "../ChatItem";
import { ChatMessage } from "@/_models/ChatMessage";
import useChatMessages from "@/_hooks/useChatMessages";
import { currentChatMessagesAtom } from "@/_helpers/JotaiWrapper";
import {
currentChatMessagesAtom,
showingTyping,
} from "@/_helpers/JotaiWrapper";
import { useAtomValue } from "jotai";
import LoadingIndicator from "../LoadingIndicator";

const ChatBody: React.FC = () => {
const messages = useAtomValue(currentChatMessagesAtom);
const isTyping = useAtomValue(showingTyping);
const [offset, setOffset] = useState(0);
const { loading, hasMore } = useChatMessages(offset);
const intersectObs = useRef<any>(null);
@@ -32,13 +37,19 @@ const ChatBody: React.FC = () => {

const content = messages.map((message, index) => {
if (messages.length === index + 1) {
// @ts-ignore
return <ChatItem ref={lastPostRef} message={message} key={message.id} />;
}
return <ChatItem message={message} key={message.id} />;
});

return (
<div className="flex flex-col-reverse flex-1 py-4 overflow-y-auto scroll">
{isTyping && (
<div className="ml-4 mb-2" key="indicator">
<LoadingIndicator />
</div>
)}
{content}
</div>
);
Original file line number Diff line number Diff line change
@@ -8,23 +8,25 @@ import {
showingProductDetailAtom,
} from "@/_helpers/JotaiWrapper";
import { ReactNode } from "react";
import ModelManagement from "../ModelManagement";

type Props = {
children: ReactNode;
};

export default function ChatContainer({ children }: Props) {
const activeConvoId = useAtomValue(getActiveConvoIdAtom);
const showingProductDetail = useAtomValue(showingProductDetailAtom);
// const showingProductDetail = useAtomValue(showingProductDetailAtom);

if (!activeConvoId) {
return <ProductOverview />;
// return <ProductOverview />;
return <ModelManagement />;
}

return (
<div className="flex flex-1 overflow-hidden">
{children}
{showingProductDetail ? <ModelDetailSideBar /> : null}
{/* {showingProductDetail ? <ModelDetailSideBar /> : null} */}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable react/display-name */
import React, { forwardRef } from "react";
import renderChatMessage from "../ChatBody/renderChatMessage";
import { ChatMessage } from "@/_models/ChatMessage";
Original file line number Diff line number Diff line change
@@ -7,11 +7,11 @@ const CompactHistoryList: React.FC = () => {

return (
<div className="flex flex-col flex-1 gap-1 mt-3">
{conversations.map(({ id, product }) => (
{conversations.map(({ id, image }) => (
<CompactHistoryItem
key={id}
conversationId={id}
imageUrl={product.avatarUrl ?? ""}
conversationId={id ?? ""}
imageUrl={image ?? ""}
/>
))}
</div>
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ const CompactLogo: React.FC = () => {

return (
<button onClick={() => setActiveConvoId(undefined)}>
<JanImage imageUrl="/icons/app_icon.svg" width={28} height={28} />
<JanImage imageUrl="icons/app_icon.svg" width={28} height={28} />
</button>
);
};
89 changes: 89 additions & 0 deletions app/app/_components/ConfirmDeleteModelModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React, { Fragment } from "react";
import { Dialog, Transition } from "@headlessui/react";
import { QuestionMarkCircleIcon } from "@heroicons/react/24/outline";
import { showConfirmDeleteModalAtom } from "@/_helpers/JotaiWrapper";
import { useAtom } from "jotai";
import useSignOut from "@/_hooks/useSignOut";

const ConfirmDeleteModelModal: React.FC = () => {
const [show, setShow] = useAtom(showConfirmDeleteModalAtom);
const { signOut } = useSignOut();

const onLogOutClick = () => {
signOut().then(() => setShow(false));
};

return (
<Transition.Root show={show} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={setShow}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
</Transition.Child>

<div className="fixed inset-0 z-10 overflow-y-auto">
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<Dialog.Panel className="relative transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6">
<div className="sm:flex sm:items-start">
<div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
<QuestionMarkCircleIcon
className="h-6 w-6 text-green-600"
aria-hidden="true"
/>
</div>
<div className="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
<Dialog.Title
as="h3"
className="text-base font-semibold leading-6 text-gray-900"
>
Log out
</Dialog.Title>
<div className="mt-2">
<p className="text-sm text-gray-500">
Are you sure you want to delete this model?
</p>
</div>
</div>
</div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<button
type="button"
className="inline-flex w-full justify-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 sm:ml-3 sm:w-auto"
onClick={onLogOutClick}
>
Log out
</button>
<button
type="button"
className="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:mt-0 sm:w-auto"
onClick={() => setShow(false)}
>
Cancel
</button>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition.Root>
);
};

export default React.memo(ConfirmDeleteModelModal);
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ const ConversationalCard: React.FC<Props> = ({ product }) => {
return (
<button
onClick={() =>
requestCreateConvo(product)
requestCreateConvo(1)
}
className="flex flex-col justify-between flex-shrink-0 gap-3 bg-white p-4 w-52 rounded-lg text-left dark:bg-gray-700 hover:opacity-20"
>
@@ -35,7 +35,7 @@ const ConversationalCard: React.FC<Props> = ({ product }) => {
</span>
</div>
<span className="flex text-xs leading-5 text-gray-500 items-center gap-[2px]">
<Image src={"/icons/play.svg"} width={16} height={16} alt="" />
<Image src={"icons/play.svg"} width={16} height={16} alt="" />
32.2k runs
</span>
</button>
Original file line number Diff line number Diff line change
@@ -4,25 +4,12 @@ import Image from "next/image";

const DiscordContainer = () => (
<div className="border-t border-gray-200 p-3 gap-3 flex items-center justify-between">
<Link
className="flex gap-2 items-center rounded-lg text-gray-900 text-xs leading-[18px]"
href="/download"
target="_blank_"
>
<Image
src={"/icons/ico_mobile-android.svg"}
width={16}
height={16}
alt=""
/>
Get the app
</Link>
<Link
className="flex items-center rounded-lg text-purple-700 text-xs leading-[18px] font-semibold gap-2"
href={process.env.NEXT_PUBLIC_DISCORD_INVITATION_URL ?? "#"}
target="_blank_"
>
<Image src={"/icons/ico_Discord.svg"} width={20} height={20} alt="" />
<Image src={"icons/ico_Discord.svg"} width={20} height={20} alt="" />
Discord
</Link>
</div>
Loading