<style lang="scss">
- html, body {
- margin: 0;
- padding: 0;
- background-color: #EBEBEB;
- }
+html, body {
+ margin: 0;
+ padding: 0;
+ background-color: #EBEBEB;
+}
- #app {
- font-family: "SF Pro Text";
+#app {
+ font-family: "SF Pro Text";
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
- height: 100vh;
- width: 100vw;
+ height: 100vh;
+ width: 100vw;
- border-radius: 15px;
- }
+ border-radius: 15px;
+ }
- .titlebar {
- position: fixed;
+.titlebar {
+ position: fixed;
- width: 100vw;
- height: 54px;
+ width: 100vw;
+ height: 54px;
- opacity: 0.75;
+ opacity: 0.75;
- background-color: #EBEBEB;
+ background-color: #EBEBEB;
- -webkit-user-select: none;
- -webkit-app-region: drag;
+ -webkit-user-select: none;
+ -webkit-app-region: drag;
- border: none;
- outline: none;
+ border: none;
+ outline: none;
- z-index: 1;
+ z-index: 1;
- }
+}
- .menu {
- position: fixed;
- margin-left: 20px;
- margin-top: 20px;
+.menu {
+ position: fixed;
+ margin-left: 20px;
+ margin-top: 20px;
- z-index: 2;
+ z-index: 2;
- }
+}
- .menuButton {
- border: none;
- outline:none;
- min-width: 14px;
- min-height: 14px;
- border-radius: 7px;
- }
+.menuButton {
+ border: none;
+ outline:none;
+ min-width: 14px;
+ min-height: 14px;
+ border-radius: 7px;
+}
- .exitButton {
- background-color: #FF6157;
- }
+.exitButton {
+ background-color: #FF6157;
+}
- .exitButton:active {
- background: #c14645;
- }
+.exitButton:active {
+ background: #c14645;
+}
- .minimizeButton {
- background-color: #FFC12F;
- margin-left: 8px;
- }
+.minimizeButton {
+ background-color: #FFC12F;
+ margin-left: 8px;
+}
- .minimizeButton:active {
- background-color: #c08e38;
- }
+.minimizeButton:active {
+ background-color: #c08e38;
+}
</style>
// Check to see if this is a Render Message.
if (m.content) {
- message = renderMessage({
- text: m.content.text,
- audio: m.content.audio,
- context: m.context,
- modifier: m.modifier,
- });
+ message = renderMessage(m.content.text, m.content.audio, m.context, m.modifier);
// Play audio if any.
if (message.content.audio) {
-import { onMounted } from "vue";
+import anime from "animejs";
+import { onMounted, onUnmounted, ref, Ref } from "vue";
import useMitt from "@/modules/mitt";
import { useIpc } from '@/modules/ipc';
import keyboardNameMap from "../keyBoardMaps/keyboardNameMap";
import { renderMessage } from '@/modules/message';
+function showRecIcon () {
+ anime({
+ targets: '#recIcon',
+ opacity: [0, 0.75],
+ scale: [0.0, 1],
+ duration: 250,
+ easing: 'linear',
+ })
+
+}
+function hideRecIcon () {
+ anime({
+ targets: '#recIcon',
+ opacity: [0.75, 0],
+ scale: [1, 0],
+ duration: 250,
+ easing: 'linear',
+ })
+
+}
-export default function useAudioInputController () {
+
+export default function useAudioInputController (typing: Ref) {
// For sending messages.
const { post } = useIpc();
const { emitter } = useMitt();
// Keepp track of when we are recording.
- let recording = false;
-
- // Utility function to render message.
- const render = (text: string, audio: boolean | string, context: string, modifier: string): string => {
- const m = renderMessage({ text, audio, context, modifier });
- emitter.emit("self-message", m);
- return m.uid;
- }
+ const recording = ref(false);
//---Callbacks-----------------------------------------------
const onKeyDown = (e: KeyboardEvent) => {
const cmd = keyboardNameMap[e.keyCode];
+ // console.log(cmd)
// Start recording on space bar.
- if (cmd == "SPACE" && !recording) {
+ if (cmd == "SPACE" && !recording.value && !typing.value) {
post('update-recorder', {
isRecording: true,
uid: null
});
- recording = true;
+ showRecIcon()
+ recording.value = true;
+ console.log("Recording...")
}
const cmd = keyboardNameMap[e.keyCode];
// Stop recording on space up.
- if (cmd == "SPACE" && recording) {
+ if (cmd == "SPACE" && recording.value) {
// Render audio immediately.
- const uid = render("", "", "", "sf");
+ const message = renderMessage("", "", "", "sf")
+ emitter.emit("self-message", message);
post('update-recorder', {
isRecording: false,
- uid: uid
+ uid: message.uid
});
- recording = false;
+ hideRecIcon()
+ recording.value = false;
+ console.log("Stopping record...")
}
window.addEventListener("keyup", onKeyUp);
})
+ onUnmounted(() => {
+ window.removeEventListener("keydown", onKeyDown);
+ window.removeEventListener("keyup", onKeyUp);
+ });
+
+
return {
recording
}
import anime from "animejs";
import useMitt from "@/modules/mitt";
import { useIpc } from '@/modules/ipc';
-import { Ref, watch, onMounted } from "vue";
+import { Ref, ref, watch, onMounted, onUnmounted } from "vue";
import keyboardNameMap from "../keyBoardMaps/keyboardNameMap";
import { clientMessage, renderMessage } from '@/modules/message';
-// Post a message to the backend.
-const { post } = useIpc();
-
-
//---Animations-----------------------------------------------
let side = "right"; // Side of parent we are on.
-let visible = false; // Whether textInput is visible.
function showTextInput () {
const t1 = (side === "right") ? 50 : -70;
easing: 'easeOutExpo',
})
- visible = true
}
function hideTextInput() {
anime({
targets: '#textInput',
- opacity: 0,
+ opacity: [1, 0],
translateX: t,
scale: 0.3,
duration: 500,
easing: 'easeOutExpo',
})
- visible = false
}
function switchSide(currentSide: string) {
export default function useTextInputController(elementX: Ref) {
let textInput: HTMLInputElement | null;
- let length: number;
-
- // For sending messages.
const { post } = useIpc();
const { emitter } = useMitt();
- // Keep track of first key press.
let firstKey = true;
+ const typing = ref(false);
- // Clear text input.
+ // Prep inputItem for typing.
+ const prepInput = () => {
+ showTextInput()
+ typing.value = true
+ }
+
+ // Clear and hide inputItem after done typing.
const clearInput = () => {
- if (textInput) textInput.value = "";
+
+ if (textInput) {
+ textInput.value = "";
+ textInput.blur();
+ }
+
hideTextInput()
firstKey = true;
- }
-
- // Utility function to render message.
- const render = (text: string, audio: boolean | string, context: string, modifier: string): string => {
- const m = renderMessage({ text, audio, context, modifier });
- emitter.emit("self-message", m);
- return m.uid;
+ typing.value = false;
}
// Send a message and clean up after.
if (textInput) {
// Render message
- const uid = render(textInput.value, false, "", "sf");
+ const message = renderMessage(textInput.value, false, "", "sf")
+ emitter.emit("self-message", message);
// Send it to the backend for processing.
- const clientM = clientMessage(textInput.value, false, uid);
+ const clientM = clientMessage(textInput.value, false, message.uid);
post('client-message', clientM);
+
+ clearInput()
}
}
//---Callbacks-----------------------------------------------
const onKeyDown = (e: KeyboardEvent) => {
+ const key = keyboardNameMap[e.keyCode]
+ console.log(key)
if (textInput) {
- const key = keyboardNameMap[e.keyCode]
-
- // Reveal textInput on first key press.
+
+ // First-key handler.
if (firstKey) {
-
- // Handle for space bar
- if (key == "SPACE") {
- return
- }
-
- showTextInput()
- firstKey = false
+ if (key == "SPACE") return
+ prepInput()
}
textInput.focus();
+ // Close input when no text or on ESC.
if (textInput.value == "" && !firstKey) {
clearInput()
+ return
}
- if (key == "ENTER") {
+ if (key === "ESCAPE") {
+ clearInput()
+ return
+ }
+
+ // Close and send on enter.
+ if (key === "ENTER") {
if (textInput.value) {
sendMessage()
- clearInput()
+ return
}
}
- if (key == "ESCAPE") {
- clearInput()
-
- }
+ if (firstKey) firstKey = false
}
}
window.addEventListener("keydown", onKeyDown);
})
- return {
+ onUnmounted(() => {
+ window.removeEventListener("keydown", onKeyDown);
+ });
+ return {
+ typing
}
}
>
AG
- <!-- Show audio input on spacebar -->
- <AudioInput />
-
-
+ <!-- Recording animation on space bar -->
<span v-if="recording" class="play"></span>
- <span v-if="recording" class="pause">
- <div class="rec-icon"></div>
- </span>
+ <span v-if="recording" class="pause"></span>
<!-- Show text input on key-down -->
<TextInput />
},
setup() {
+ // Initial position.
+ const xStart = 15;
+ const yStart = window.innerHeight - 200;
+
// Calculate position of inputItem on drag.
- const { elementX, elementY } = draggify({ elementId: "inputItem" });
+ const { elementX, elementY } = draggify(
+ { elementId: "inputItem" },
+ { x: xStart, y: yStart }
+ );
// Controllers for text and audio.
- useTextInputController(elementX)
- const { recording } = useAudioInputController()
+ const { typing } = useTextInputController(elementX)
+ const { recording } = useAudioInputController(typing)
return {
elementX,
.input-item {
position: absolute;
- opacity: 1.0;
-
display: flex;
align-items: center;
justify-content: center;
// border: 4px solid #C6C6C6;
border-radius: 20px;
- background-color: #EBEBEB;
+ // Set opacity here to not affect child.
+ background-color: rgba(235, 235, 235, 0.75);
cursor: pointer;
</template>
<script lang="ts">
-import { defineComponent, ref, watch } from "vue";
+import { defineComponent } from "vue";
export default defineComponent({
name: "TextInput",
}
-@keyframes appear {
- from {
- opacity: 0;
- transform: translateX(50px) scale(0.3);
- }
- to {
- opacity: 1;
- transform: translateX(110px) scale(1.0);
- }
-}
-
</style>
\ No newline at end of file
return elementPosition
}
-export default function draggify(input: { elementId: string }) {
+export default function draggify(input: { elementId: string }, initPosition: { x: number; y: number}) {
let element: HTMLElement | null;
});
// Initialize the coordinants.
- elementX.value = 15;
- elementY.value = 300;
+ elementX.value = initPosition.x;
+ elementY.value = initPosition.y;
// remove event listeners on component dismount.
onUnmounted(() => {
import { RenderMessage, ClientMessage } from "@/types/message/index";
import { v4 as uuidv4 } from 'uuid';
-interface RenderParams {
- text: string;
- audio: boolean | string;
- context: string;
- modifier: string;
-}
-
function getTimeStamp(): number {
const currentdate = new Date();
return currentdate.getTime();
}
// Create a RenderMessage object.
-export const renderMessage = (params: RenderParams): RenderMessage => (
+export const renderMessage = (text: boolean | string, audio: boolean | string, context: string, modifier: string): RenderMessage => (
{
content: {
- text: params.text,
- audio: params.audio
+ text: text,
+ audio: audio
},
- context: params.context,
- modifier: params.modifier,
+ context: context,
+ modifier: modifier,
time: getTimeStamp(),
uid: uuidv4(),
isChild: "none"
updateScrollRef()
// update message if in the map.
- const m = messages.value.get(a.uid)
- m.context = a.context
- m.text = a.text
+ const message = messages.value.get(a.uid)
+ message.context = a.context
+ message.content.text = a.text
setTimeout(adjustScroll, 10);
- return m
+ console.log(`Annotation: ${a.text}`)
+ console.log(`Annotation: ${a.context}`)
+
+ return message
}
const saveMessages = () => {
return {
emitter
}
-}
-
-
+}
\ No newline at end of file
export interface RenderMessage {
content: {
- text: string;
+ text: boolean | string;
audio: boolean | string;
};
context: string;
<InputItem />
<Settings />
+ <div id="recIcon" />
+
<!-- List of message bubbles. -->
<div id="messenger">
<MessageItem v-for="message in messages" :message="message[1]" :key="message[0]" />
#messenger::-webkit-scrollbar {
display: none;
}
+
+#recIcon {
+ position: fixed;
+ right: 0;
+
+ opacity: 0;
+ transform: scale(0.0);
+
+ margin-top: 24px;
+ margin-right: 66px;
+
+
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background-color: #FF3B3B;
+
+ z-index: 2;
+
+}
+
+
</style>