working new protocols

This commit is contained in:
Andrew Gundersen 2021-06-29 16:21:18 -05:00
commit b6b1f3e894
8 changed files with 168 additions and 139 deletions

View file

@ -6,22 +6,14 @@
<script lang='ts'> <script lang='ts'>
import { defineComponent, ref, onMounted } from 'vue'; import { defineComponent } from 'vue';
export default defineComponent({ export default defineComponent({
name: "Bubble", name: "Bubble",
props: ["modifier", "content", "child"], props: ["modifier", "content", "child"],
setup(props) { setup() {
onMounted(() => {
});
return {
};
} }
@ -38,30 +30,18 @@
font-size: 14px; font-size: 14px;
padding: 10px; padding: 10px;
border-radius: 18px; border-radius: 18px;
margin-left: 15px;
margin-right: 15px;
margin-bottom: 4px; 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 { .ai-bubble {
background-color: #FFFFFF; background-color: #FFFFFF;
margin-left: 15px;
} }
.client-bubble { .client-bubble {
color: white; color: white;
background-color: #58C4FD; background-color: #58C4FD;
margin-right: 15px;
} }
.ai-first-child { .ai-first-child {
@ -94,7 +74,6 @@
border-top-right-radius: 9px; border-top-right-radius: 9px;
} }
// .notify { // .notify {
// position: absolute; // position: absolute;
// width: 12px; // width: 12px;

View file

@ -0,0 +1,88 @@
<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>

View file

@ -1,6 +1,5 @@
import { ref, Ref } from "vue"; import { ref, Ref } from "vue";
import anime from "animejs"; import anime from "animejs";
import { v4 as uuidv4 } from 'uuid';
export function animateTextInput () { export function animateTextInput () {
@ -93,7 +92,7 @@ export function annotateAnim (uid: string, val: string, contextRef: Ref) {
anime({ anime({
targets: `#${uid} span`, targets: `#${uid} span`,
opacity: [1, 0], opacity: [1, 0],
duration: 500, duration: 250,
easing: 'easeOutExpo' easing: 'easeOutExpo'
}) })
@ -105,7 +104,7 @@ export function annotateAnim (uid: string, val: string, contextRef: Ref) {
anime({ anime({
targets: `#${uid} span`, targets: `#${uid} span`,
opacity: [0, 1], opacity: [0, 1],
duration: 500, duration: 250,
easing: 'easeOutExpo' easing: 'easeOutExpo'
}) })
}) })

View file

@ -32,7 +32,7 @@ import draggify from "@/render/composables/useDraggify";
import useTextInputController from import useTextInputController from
"@/render/components/controllers/inputItem.control.text"; "@/render/components/controllers/inputItem.control.text";
import useAudioInputController from // import useAudioInputController from
"@/render/components/controllers/inputItem.control.audio"; "@/render/components/controllers/inputItem.control.audio";
export default defineComponent({ export default defineComponent({
@ -51,7 +51,8 @@ export default defineComponent({
// Controllers for text and audio. // Controllers for text and audio.
const { typing } = useTextInputController(elementX) const { typing } = useTextInputController(elementX)
const { recording } = useAudioInputController(typing) // const { recording } = useAudioInputController(typing)
const recording = false;
return { return {
elementX, elementX,

View file

@ -8,19 +8,21 @@
:child="calcChild(index, content.length)" :child="calcChild(index, content.length)"
/> />
<span class="context"> <Context
<div v-if="modifier==='ai'" class="avatar"></div> :modifier="modifier"
{{ contextRef }} :context="context"
</span> :uid="uid"
/>
</div> </div>
</template> </template>
<script lang='ts'> <script lang='ts'>
import { defineComponent, ref, Ref, onMounted, watch } from 'vue'; import { defineComponent } from 'vue';
import Bubble from "@/render/components/bubble.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({ export default defineComponent({
name: "Message", name: "Message",
@ -28,25 +30,14 @@
props: ["modifier", "content", "context", "uid"], props: ["modifier", "content", "context", "uid"],
components: { components: {
Bubble Bubble,
Context
}, },
setup(props) { setup() {
const contextRef = ref(" ");
contextRef.value = props.context;
watch(() => props.context, (val: string, oldval: string) => {
annotateAnim(props.uid, val, contextRef);
});
onMounted(() => {
});
return { return {
calcChild, calcChild
contextRef
}; };
} }
@ -57,85 +48,41 @@
<style lang="scss" scoped> <style lang="scss" scoped>
.message { .message {
width: 100vw; width: 100vw;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding-top: 9px; padding-top: 9px;
padding-bottom: 9px; padding-bottom: 9px;
animation-name: message-init-anim; animation-name: message-init-anim;
animation-duration: 0.5s; animation-duration: 0.25s;
} }
@keyframes message-init-anim { @keyframes message-init-anim {
from { from {
opacity: 0; opacity: 0;
transform: translateY(50px); } to {
} to { opacity: 1;
opacity: 1;
transform: translateY(0px);
}
} }
}
.message:first-child { .message:first-child {
margin-top: 55px; margin-top: 55px;
} }
.message:last-child { .message:last-child {
margin-bottom: 18px; margin-bottom: 6px;
} }
.client-message { .client-message {
@extend .message; @extend .message;
align-items: flex-end; align-items: flex-end;
} }
.ai-message { .ai-message {
@extend .message; @extend .message;
align-items: flex-start; align-items: flex-start;
} }
.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;
}
.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> </style>

View file

@ -20,7 +20,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted, onUnmounted } from "vue"; import { defineComponent } from "vue";
import InputItem from "@/render/components/inputItem.vue"; import InputItem from "@/render/components/inputItem.vue";
import Settings from "@/render/components/settings.vue"; import Settings from "@/render/components/settings.vue";
import Message from "@/render/components/message.vue"; import Message from "@/render/components/message.vue";

View file

@ -1,28 +1,32 @@
export default function useScroll(element: string) { export default function useScroll(elementId: string) {
let isScrolledToBottom: boolean;
const view = document.getElementById(element)
// Update isScrolledToBottom // Update isScrolledToBottom
const updateScrollRef = () => { const isScrolledToBottom = () => {
if (view) isScrolledToBottom = view.scrollHeight - view.clientHeight <= view.scrollTop + 1; const el = document.getElementById(elementId);
return isScrolledToBottom;
if (el) {
return el.scrollHeight - el.clientHeight <= el.scrollTop + 30;
}
} }
// Adjust scroll after we add content to the messenger. // Adjust scroll after we add content to the messenger.
const adjustScroll = () => { const adjustScroll = () => {
if (view) { const el = document.getElementById(elementId);
view.scrollTo({
top: view.scrollHeight - view.clientHeight, if (el) {
el.scrollTo({
top: el.scrollHeight - el.clientHeight,
behavior: 'smooth' behavior: 'smooth'
}); });
} }
} }
return { return {
updateScrollRef, isScrolledToBottom,
adjustScroll, adjustScroll,
}; };

View file

@ -2,20 +2,31 @@
import { ref, Ref } from "vue"; import { ref, Ref } from "vue";
import useScroll from "@/render/composables/useScroll"; import useScroll from "@/render/composables/useScroll";
const { isScrolledToBottom, adjustScroll } = useScroll("messenger");
export const messages: Ref<Array<Message>> = ref([]); export const messages: Ref<Array<Message>> = ref([]);
export const setMessages: IpcListenerCallback<Array<Message>> = (payload) => { export const setMessages: IpcListenerCallback<Array<Message>> = (payload) => {
messages.value = payload as Array<Message>; messages.value = payload as Array<Message>;
setTimeout(() => {
adjustScroll();
}, 1000);
} }
export const addMessage: IpcListenerCallback<Message> = (payload) => { export const addMessage: IpcListenerCallback<Message> = (payload) => {
const bottom = isScrolledToBottom();
messages.value.push(payload as Message); messages.value.push(payload as Message);
if (bottom) {
setTimeout(() => {
adjustScroll();
}, 10);
}
} }
export const updateMessage: IpcListenerCallback<Annotation> = (payload) => { export const updateMessage: IpcListenerCallback<Annotation> = (payload) => {
const annotation = payload as Annotation; const annotation = payload as Annotation;
for (let i in messages.value) { for (const i in messages.value) {
if (messages.value[i].uid == annotation.uid) { if (messages.value[i].uid == annotation.uid) {
@ -36,7 +47,7 @@ export const updateMessage: IpcListenerCallback<Annotation> = (payload) => {
export const deleteMessage: IpcListenerCallback<string> = (payload) => { export const deleteMessage: IpcListenerCallback<string> = (payload) => {
const uid = payload as string; 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) { if (messages.value[i].uid === uid) {
messages.value.splice(i, 1); messages.value.splice(i, 1);
break; break;