Skip to content

Commit

Permalink
feat: Add multiple chat
Browse files Browse the repository at this point in the history
* feat(temp): multiple chat instances without persistance

* Update layout to allow navigation-drawer function

* Rename to ChatDrawer

* Add multi chat logic

* Format whitespace diff

* Use v-if to check hide instead of filter

* Update current translation for more clarity

* Add time when creating new chat

* Edit chat title style, icon size

* Skip selectChat when same index as current

* Add shortcut for toggle chat drawer

* Add shortcut for create new chat

* Open chat drawer to show new chat shortcut

* Remove comment

* Fix style

* Update drawer shortcut to ctrl + d

* Fix warn

* Remove unuse code

* Set cursor to wait after selecting chat

* Save isChatDrawerOpen

* Re-render chat thread when chat index changed

* Fix v-textarea scrollbar missing in v-bottom-navigation

---------

Co-authored-by: Lucas Robin <[email protected]>
  • Loading branch information
tanchekwei and k4lu-0p authored Jul 2, 2023
1 parent c94dd06 commit a82456d
Show file tree
Hide file tree
Showing 11 changed files with 534 additions and 203 deletions.
209 changes: 120 additions & 89 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,87 +1,102 @@
<template>
<div id="app">
<header>
<div class="header-content">
<img
:class="{ 'dark-png': store.state.theme === Theme.DARK }"
class="logo"
src="@/assets/logo-banner.png"
alt="ChatALL"
/>
<div class="column-icons">
<img
v-for="columnCount in 3"
:id="`column-${columnCount}`"
:key="columnCount"
:src="getColumnImage(columnCount)"
@click="changeColumns(columnCount)"
@shortkey="changeColumns(columnCount)"
v-shortkey.once="[`f${columnCount}`]"
:class="{
selected: columns === columnCount,
'dark-png': store.state.theme === Theme.DARK,
}"
/>
</div>
<div>
<v-icon
:id="SHORTCUT_FIND.elementId"
class="cursor-pointer"
color="primary"
icon="mdi-magnify"
size="x-large"
@click="openFind()"
></v-icon>
<v-icon
v-shortkey.once="SHORTCUT_CLEAR_MESSAGES.key"
@shortkey="clearMessages"
:id="SHORTCUT_CLEAR_MESSAGES.elementId"
class="cursor-pointer"
color="primary"
icon="mdi-broom"
size="x-large"
@click="clearMessages()"
></v-icon>
<v-icon
v-shortkey.once="SHORTCUT_SETTINGS.key"
@shortkey="openSettingsModal"
:id="SHORTCUT_SETTINGS.elementId"
class="cursor-pointer"
color="primary"
icon="mdi-cog"
size="x-large"
@click="openSettingsModal()"
></v-icon>
<v-icon
v-shortkey.once="SHORTCUT_SHORTCUT_GUIDE.key"
@shortkey="toggleShortcutGuide"
:id="SHORTCUT_SHORTCUT_GUIDE.elementId"
class="cursor-pointer"
color="primary"
icon="mdi-help"
size="x-large"
@click="toggleShortcutGuide()"
></v-icon>
</div>
</div>
<FindModal ref="findRef"></FindModal>
</header>

<main class="content">
<div id="content">
<ChatMessages :columns="columns"></ChatMessages>
</div>
</main>

<FooterBar></FooterBar>
<SettingsModal v-model:open="isSettingsOpen" />
<ConfirmModal ref="confirmModal" />
<UpdateNotification></UpdateNotification>
<ShortcutGuide
ref="shortcutGuideRef"
v-model:open="isShortcutGuideOpen"
></ShortcutGuide>
</div>
<v-app>
<v-container fluid style="padding: 0">
<ChatDrawer
ref="chatDrawerRef"
v-model:open="isChatDrawerOpen"
@create-chat="focusPromptTextareaAfterChatCreated"
></ChatDrawer>
<v-main class="content">
<v-app-bar class="header-content" style="padding: 0">
<div class="header-content">
<v-app-bar-nav-icon
:id="SHORTCUT_CHAT_DRAWER.elementId"
variant="text"
@click.stop="isChatDrawerOpen = !isChatDrawerOpen"
@shortkey="isChatDrawerOpen = !isChatDrawerOpen"
v-shortkey.once="SHORTCUT_CHAT_DRAWER.key"
>
</v-app-bar-nav-icon>
<img
:class="{ 'dark-png': store.state.theme === Theme.DARK }"
class="logo"
src="@/assets/logo-banner.png"
alt="ChatALL"
/>
</div>
<div class="column-icons header-content">
<img
v-for="columnCount in 3"
:id="`column-${columnCount}`"
:key="columnCount"
:src="getColumnImage(columnCount)"
@click="changeColumns(columnCount)"
@shortkey="changeColumns(columnCount)"
v-shortkey.once="[`f${columnCount}`]"
:class="{
selected: columns === columnCount,
'dark-png': store.state.theme === Theme.DARK,
}"
/>
</div>
<div class="header-content" style="padding-right: 16px">
<v-icon
:id="SHORTCUT_FIND.elementId"
class="cursor-pointer"
color="primary"
icon="mdi-magnify"
size="x-large"
@click="openFind()"
></v-icon>
<v-icon
v-shortkey.once="SHORTCUT_CLEAR_MESSAGES.key"
@shortkey="clearMessages"
:id="SHORTCUT_CLEAR_MESSAGES.elementId"
class="cursor-pointer"
color="primary"
icon="mdi-broom"
size="x-large"
@click="clearMessages()"
></v-icon>
<v-icon
v-shortkey.once="SHORTCUT_SETTINGS.key"
@shortkey="openSettingsModal"
:id="SHORTCUT_SETTINGS.elementId"
class="cursor-pointer"
color="primary"
icon="mdi-cog"
size="x-large"
@click="openSettingsModal()"
></v-icon>
<v-icon
v-shortkey.once="SHORTCUT_SHORTCUT_GUIDE.key"
@shortkey="toggleShortcutGuide"
:id="SHORTCUT_SHORTCUT_GUIDE.elementId"
class="cursor-pointer"
color="primary"
icon="mdi-help"
size="x-large"
@click="toggleShortcutGuide()"
></v-icon>
</div>
</v-app-bar>
<FindModal ref="findRef"></FindModal>

<ChatMessages
:chat-index="store.state.currentChatIndex"
:columns="columns"
></ChatMessages>
<FooterBar ref="footerBarRef"></FooterBar>
</v-main>
<SettingsModal v-model:open="isSettingsOpen" />
<ConfirmModal ref="confirmModal" />
<UpdateNotification></UpdateNotification>
<ShortcutGuide
ref="shortcutGuideRef"
v-model:open="isShortcutGuideOpen"
></ShortcutGuide>
</v-container>
</v-app>
</template>

<script setup>
Expand All @@ -96,11 +111,13 @@ import {
SHORTCUT_SETTINGS,
SHORTCUT_SHORTCUT_GUIDE,
SHORTCUT_CLEAR_MESSAGES,
SHORTCUT_CHAT_DRAWER,
} from "./components/ShortcutGuide/shortcut.const";
import i18n from "./i18n";
// Components
import ChatDrawer from "@/components/ChatDrawer/ChatDrawer.vue";
import ChatMessages from "@/components/Messages/ChatMessages.vue";
import SettingsModal from "@/components/SettingsModal.vue";
import ConfirmModal from "@/components/ConfirmModal.vue";
Expand All @@ -126,9 +143,12 @@ ipcRenderer.on("on-updated-system-theme", onUpdatedSystemTheme);
const confirmModal = ref(null);
const findRef = ref(null);
const footerBarRef = ref(null);
const shortcutGuideRef = ref(null);
const isShortcutGuideOpen = ref(false);
const isSettingsOpen = ref(false);
const isChatDrawerOpen = ref(store.state.isChatDrawerOpen);
const chatDrawerRef = ref();
const columns = computed(() => store.state.columns);
Expand All @@ -149,7 +169,15 @@ function openFind() {
}
function toggleShortcutGuide() {
shortcutGuideRef.value.toggleShortcutGuide();
if (!isChatDrawerOpen.value) {
// open chat drawer to show new chat shortcut
isChatDrawerOpen.value = true;
setTimeout(() => {
shortcutGuideRef.value.toggleShortcutGuide();
}, 200);
} else {
shortcutGuideRef.value.toggleShortcutGuide();
}
}
async function clearMessages() {
Expand All @@ -161,6 +189,10 @@ async function clearMessages() {
}
}
function focusPromptTextareaAfterChatCreated() {
footerBarRef.value.focusPromptTextarea();
}
onMounted(() => {
!store.state.uuid && setUuid(uuidv4());
window._paq.push(["setUserId", store.state.uuid]);
Expand Down Expand Up @@ -193,9 +225,6 @@ body {
}
header {
position: fixed;
top: 0;
left: 0;
width: 100%;
background-color: rgb(var(--v-theme-header));
box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25);
Expand Down Expand Up @@ -226,9 +255,7 @@ img.selected {
}
.content {
flex: 1;
background-color: rgb(var(--v-theme-background));
padding: 16px;
}
.cursor-pointer {
Expand All @@ -238,4 +265,8 @@ img.selected {
.dark-png {
filter: grayscale(1) brightness(5);
}
.v-toolbar__content {
justify-content: space-between
}
</style>
70 changes: 70 additions & 0 deletions src/components/ChatDrawer/ChatDrawer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<template>
<v-navigation-drawer permanent :model-value="props.open">
<v-list nav>
<v-list-item
:id="SHORTCUT_NEW_CHAT.elementId"
density="comfortable"
class="mb-1 border"
:title="$t('chat.newChat')"
@click="onAddNewChat"
@shortkey="onAddNewChat"
v-shortkey.once="SHORTCUT_NEW_CHAT.key"
>
<template v-slot:prepend>
<v-icon color="primary"> mdi-plus </v-icon>
</template>
</v-list-item>
</v-list>

<template v-for="chat in store.state.chats.slice().reverse()">
<ChatDrawerItem
v-if="!chat.hide"
:chat="chat"
@confirm-hide-chat="confirmHideChat"
:key="chat.index"
></ChatDrawerItem>
</template>
</v-navigation-drawer>
<ConfirmModal ref="confirmModal" />
</template>

<script setup>
import { ref, onUpdated } from "vue";
import { useStore } from "vuex";
import i18n from "@/i18n";
import ConfirmModal from "@/components/ConfirmModal.vue";
import ChatDrawerItem from "@/components/ChatDrawer/ChatDrawerItem.vue";
import { SHORTCUT_NEW_CHAT } from "@/components/ShortcutGuide/shortcut.const";
const store = useStore();
const props = defineProps(["open"]);
const emit = defineEmits(["update:open", "createChat"]);
onUpdated(setIsChatDrawerOpen);
const confirmModal = ref(null);
function setIsChatDrawerOpen() {
store.commit("setIsChatDrawerOpen", props.open);
}
function onAddNewChat() {
store.commit("createChat");
store.commit("selectChat", store.state.chats.length - 1);
emit("createChat");
}
async function confirmHideChat() {
const result = await confirmModal.value.showModal(
i18n.global.t("modal.confirmHideChat"),
);
if (result) {
store.commit("hideChat");
}
}
</script>
<style scoped>
:deep() .v-list-item-title {
font-size: 1rem!important;
}
</style>
Loading

1 comment on commit a82456d

@vercel
Copy link

@vercel vercel bot commented on a82456d Jul 2, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

chatall – ./

chatall-git-main-sunner.vercel.app
chatall-llm.vercel.app
chatall-sunner.vercel.app

Please sign in to comment.