<script lang='ts'>
- import { defineComponent, ref, onMounted } from 'vue';
+ import { defineComponent } from 'vue';
export default defineComponent({
name: "Bubble",
props: ["modifier", "content", "child"],
- setup(props) {
-
- onMounted(() => {
-
- });
-
- return {
-
- };
+ setup() {
}
font-size: 14px;
padding: 10px;
border-radius: 18px;
- margin-left: 15px;
- margin-right: 15px;
margin-bottom: 4px;
- // animation-name: bubble-init-anim;
- // animation-duration: 0.5s;
-}
-
-@keyframes bubble-init-anim {
- from {
- opacity: 0;
- transform: translateY(25px);
- } to {
- opacity: 1;
- transform: translateY(0px);
- }
}
.ai-bubble {
background-color: #FFFFFF;
+ margin-left: 15px;
}
.client-bubble {
color: white;
background-color: #58C4FD;
+ margin-right: 15px;
}
.ai-first-child {
border-top-right-radius: 9px;
}
-
// .notify {
// position: absolute;
// width: 12px;
--- /dev/null
+<template>
+ <span :class="`context ${modifier}-context`">
+ <div v-if="modifier==='ai'" class="avatar"></div>
+ {{ contextRef }}
+ </span>
+</template>
+
+<script lang='ts'>
+
+ import { defineComponent, ref, watch } from 'vue';
+ import { annotateAnim } from "@/render/components/controllers/helpers";
+
+ export default defineComponent({
+ name: "Context",
+
+ props: ["modifier", "context", "uid"],
+
+ setup(props) {
+
+ const contextRef = ref(" ");
+ contextRef.value = props.context;
+
+ watch(() => props.context, (val: string, _oldval: string) => {
+ annotateAnim(props.uid, val, contextRef);
+ });
+
+ return {
+ contextRef
+ };
+
+ }
+
+ })
+
+</script>
+
+<style lang="scss" scoped>
+
+.context {
+ position: relative;
+ min-height: 14px;
+ display: flex;
+ align-items: center;
+ font-family: "SF Compact Display";
+ font-size: 12px;
+ font-weight: bold;
+ margin-top: 5px;
+}
+
+.ai-context {
+ margin-left: 15px;
+}
+
+.client-context {
+ margin-right: 15px;
+}
+
+.avatar {
+ margin-right: 5px;
+ width: 30px;
+ height: 30px;
+ background-color: white;
+ border-radius: 15px;
+}
+
+.playing {
+ animation-name: message-playback-anim;
+ animation-duration: 2s;
+ animation-iteration-count: infinite;
+}
+
+@keyframes message-playback-anim {
+ 0%,
+ 100% {
+ box-shadow: 0 0 0 0.4em rgba(195, 195, 195, 0.4), 0 0 0 0.25em rgba(195, 195, 195, 0.15);
+ }
+ 25% {
+ box-shadow: 0 0 0 0.15em rgba(195, 195, 195, 0.15), 0 0 0 0.4em rgba(195, 195, 195, 0.3);
+ }
+ 50% {
+ box-shadow: 0 0 0 0.55em rgba(195, 195, 195, 0.55), 0 0 0 0.15em rgba(195, 195, 195, 0.05);
+ }
+ 75% {
+ box-shadow: 0 0 0 0.25em rgba(195, 195, 195, 0.25), 0 0 0 0.55em rgba(195, 195, 195, 0.45);
+ }
+}
+
+</style>
\ No newline at end of file
import { ref, Ref } from "vue";
import anime from "animejs";
-import { v4 as uuidv4 } from 'uuid';
export function animateTextInput () {
anime({
targets: `#${uid} span`,
opacity: [1, 0],
- duration: 500,
+ duration: 250,
easing: 'easeOutExpo'
})
anime({
targets: `#${uid} span`,
opacity: [0, 1],
- duration: 500,
+ duration: 250,
easing: 'easeOutExpo'
})
})
import useTextInputController from
"@/render/components/controllers/inputItem.control.text";
-import useAudioInputController from
+// import useAudioInputController from
"@/render/components/controllers/inputItem.control.audio";
export default defineComponent({
// Controllers for text and audio.
const { typing } = useTextInputController(elementX)
- const { recording } = useAudioInputController(typing)
+ // const { recording } = useAudioInputController(typing)
+ const recording = false;
return {
elementX,
:child="calcChild(index, content.length)"
/>
- <span class="context">
- <div v-if="modifier==='ai'" class="avatar"></div>
- {{ contextRef }}
- </span>
+ <Context
+ :modifier="modifier"
+ :context="context"
+ :uid="uid"
+ />
</div>
</template>
<script lang='ts'>
- import { defineComponent, ref, Ref, onMounted, watch } from 'vue';
+ import { defineComponent } from 'vue';
import Bubble from "@/render/components/bubble.vue";
- import { annotateAnim, calcChild } from "@/render/components/controllers/helpers";
+ import Context from "@/render/components/context.vue";
+ import { calcChild } from "@/render/components/controllers/helpers";
export default defineComponent({
name: "Message",
props: ["modifier", "content", "context", "uid"],
components: {
- Bubble
+ Bubble,
+ Context
},
- setup(props) {
-
- const contextRef = ref(" ");
- contextRef.value = props.context;
-
- watch(() => props.context, (val: string, oldval: string) => {
- annotateAnim(props.uid, val, contextRef);
- });
-
- onMounted(() => {
-
- });
+ setup() {
return {
- calcChild,
- contextRef
+ calcChild
};
}
<style lang="scss" scoped>
- .message {
- width: 100vw;
- display: flex;
- flex-direction: column;
- padding-top: 9px;
- padding-bottom: 9px;
- animation-name: message-init-anim;
- animation-duration: 0.5s;
- }
-
- @keyframes message-init-anim {
- from {
- opacity: 0;
- transform: translateY(50px);
- } to {
- opacity: 1;
- transform: translateY(0px);
- }
- }
-
- .message:first-child {
- margin-top: 55px;
+.message {
+ width: 100vw;
+ display: flex;
+ flex-direction: column;
+ padding-top: 9px;
+ padding-bottom: 9px;
+ animation-name: message-init-anim;
+ animation-duration: 0.25s;
+}
+
+@keyframes message-init-anim {
+ from {
+ opacity: 0;
+ } to {
+ opacity: 1;
}
+}
- .message:last-child {
- margin-bottom: 18px;
- }
-
- .client-message {
- @extend .message;
- align-items: flex-end;
- }
-
- .ai-message {
- @extend .message;
- align-items: flex-start;
- }
+.message:first-child {
+ margin-top: 55px;
+}
- .context {
- position: relative;
- display: flex;
- align-items: center;
- font-family: "SF Compact Display";
- font-size: 12px;
- font-weight: bold;
- margin-top: 5px;
- margin-right: 15px;
- margin-left: 15px;
- }
+.message:last-child {
+ margin-bottom: 6px;
+}
- .avatar {
- margin-right: 5px;
- width: 30px;
- height: 30px;
- background-color: white;
- border-radius: 15px;
- }
+.client-message {
+ @extend .message;
+ align-items: flex-end;
+}
- .playing {
- animation-name: message-playback-anim;
- animation-duration: 2s;
- animation-iteration-count: infinite;
- }
-
- @keyframes message-playback-anim {
- 0%,
- 100% {
- box-shadow: 0 0 0 0.4em rgba(195, 195, 195, 0.4), 0 0 0 0.25em rgba(195, 195, 195, 0.15);
- }
- 25% {
- box-shadow: 0 0 0 0.15em rgba(195, 195, 195, 0.15), 0 0 0 0.4em rgba(195, 195, 195, 0.3);
- }
- 50% {
- box-shadow: 0 0 0 0.55em rgba(195, 195, 195, 0.55), 0 0 0 0.15em rgba(195, 195, 195, 0.05);
- }
- 75% {
- box-shadow: 0 0 0 0.25em rgba(195, 195, 195, 0.25), 0 0 0 0.55em rgba(195, 195, 195, 0.45);
- }
- }
+.ai-message {
+ @extend .message;
+ align-items: flex-start;
+}
</style>
<script lang="ts">
-import { defineComponent, onMounted, onUnmounted } from "vue";
+import { defineComponent } from "vue";
import InputItem from "@/render/components/inputItem.vue";
import Settings from "@/render/components/settings.vue";
import Message from "@/render/components/message.vue";
-export default function useScroll(element: string) {
-
- let isScrolledToBottom: boolean;
- const view = document.getElementById(element)
+export default function useScroll(elementId: string) {
// Update isScrolledToBottom
- const updateScrollRef = () => {
- if (view) isScrolledToBottom = view.scrollHeight - view.clientHeight <= view.scrollTop + 1;
- return isScrolledToBottom;
+ const isScrolledToBottom = () => {
+ const el = document.getElementById(elementId);
+
+ if (el) {
+ return el.scrollHeight - el.clientHeight <= el.scrollTop + 30;
+ }
+
}
// Adjust scroll after we add content to the messenger.
const adjustScroll = () => {
- if (view) {
- view.scrollTo({
- top: view.scrollHeight - view.clientHeight,
+ const el = document.getElementById(elementId);
+
+ if (el) {
+ el.scrollTo({
+ top: el.scrollHeight - el.clientHeight,
behavior: 'smooth'
});
}
+
}
return {
- updateScrollRef,
+ isScrolledToBottom,
adjustScroll,
};
import { ref, Ref } from "vue";
import useScroll from "@/render/composables/useScroll";
+const { isScrolledToBottom, adjustScroll } = useScroll("messenger");
+
export const messages: Ref<Array<Message>> = ref([]);
export const setMessages: IpcListenerCallback<Array<Message>> = (payload) => {
messages.value = payload as Array<Message>;
+ setTimeout(() => {
+ adjustScroll();
+ }, 1000);
}
export const addMessage: IpcListenerCallback<Message> = (payload) => {
+ const bottom = isScrolledToBottom();
messages.value.push(payload as Message);
+ if (bottom) {
+ setTimeout(() => {
+ adjustScroll();
+ }, 10);
+ }
}
export const updateMessage: IpcListenerCallback<Annotation> = (payload) => {
const annotation = payload as Annotation;
- for (let i in messages.value) {
+ for (const i in messages.value) {
if (messages.value[i].uid == annotation.uid) {
export const deleteMessage: IpcListenerCallback<string> = (payload) => {
const uid = payload as string;
- for (var i = 0; i < messages.value.length; i++) {
+ for (let i = 0; i < messages.value.length; i++) {
if (messages.value[i].uid === uid) {
messages.value.splice(i, 1);
break;