--- /dev/null
+import { onMounted } from "vue";
+import useMitt from "@/modules/mitt";
+import { useIpc } from '@/modules/ipc';
+import keyboardNameMap from "../keyBoardMaps/keyboardNameMap";
+import { renderMessage } from '@/modules/message';
+
+
+
+
+
+
+export default function useAudioInputController () {
+
+ // 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;
+ }
+
+ //---Callbacks-----------------------------------------------
+
+ const onKeyDown = (e: KeyboardEvent) => {
+ const cmd = keyboardNameMap[e.keyCode];
+
+ // Start recording on space bar.
+ if (cmd == "SPACE" && !recording) {
+
+ post('update-recorder', {
+ isRecording: true,
+ uid: null
+ });
+
+ recording = true;
+
+ }
+
+ }
+
+ const onKeyUp = (e: KeyboardEvent) => {
+ const cmd = keyboardNameMap[e.keyCode];
+
+ // Stop recording on space up.
+ if (cmd == "SPACE" && recording) {
+
+ // Render audio immediately.
+ const uid = render("", "", "", "sf");
+
+ post('update-recorder', {
+ isRecording: false,
+ uid: uid
+ });
+
+ recording = false;
+
+ }
+
+ }
+
+ //-----------------------------------------------------------
+
+ onMounted(() => {
+ window.addEventListener("keydown", onKeyDown);
+ window.addEventListener("keyup", onKeyUp);
+ })
+
+ return {
+ recording
+ }
+
+}
--- /dev/null
+import anime from "animejs";
+import useMitt from "@/modules/mitt";
+import { useIpc } from '@/modules/ipc';
+import { Ref, watch, onMounted } 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 () {
+ let t1 = (side === "right") ? 50 : -70;
+ let t2 = (side === "right") ? 110 : -130;
+
+ anime({
+ targets: '#textInput',
+ opacity: [0, 1],
+ translateX: [t1, t2],
+ scale: [0.3, 1],
+ duration: 500,
+ easing: 'easeOutExpo',
+ })
+
+ visible = true
+}
+
+function hideTextInput() {
+ let t = (side === "right") ? 50 : -80;
+
+ anime({
+ targets: '#textInput',
+ opacity: 0,
+ translateX: t,
+ scale: 0.3,
+ duration: 500,
+ easing: 'easeOutExpo',
+ })
+
+ visible = false
+}
+
+function switchSide(currentSide: string) {
+ let t = (currentSide === "right") ? -130 : 110;
+
+ anime({
+ targets: '#textInput',
+ translateX: t,
+ duration: 500,
+ easing: 'easeOutExpo',
+ })
+
+}
+
+//------------------------------------------------------------
+
+
+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;
+
+ // Clear text input.
+ const clearInput = () => {
+ if (textInput) textInput.value = "";
+ 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;
+ }
+
+ // Send a message and clean up after.
+ const sendMessage = () => {
+ if (textInput) {
+
+ // Render message
+ const uid = render(textInput.value, false, "", "sf");
+
+ // Send it to the backend for processing.
+ const clientM = clientMessage(textInput.value, false, uid);
+ post('client-message', clientM);
+ }
+ }
+
+ //---Callbacks-----------------------------------------------
+
+ const onKeyDown = (e: KeyboardEvent) => {
+
+ if (textInput) {
+ const key = keyboardNameMap[e.keyCode]
+
+ // Reveal textInput on first key press.
+ if (firstKey) {
+
+ // Handle for space bar
+ if (key == "SPACE") {
+ return
+ }
+
+ showTextInput()
+ firstKey = false
+ }
+
+ textInput.focus();
+
+ if (textInput.value == "" && !firstKey) {
+ clearInput()
+ }
+
+ if (key == "ENTER") {
+ if (textInput.value) {
+ sendMessage()
+ clearInput()
+ }
+ }
+
+ if (key == "ESCAPE") {
+ clearInput()
+
+ }
+ }
+ }
+
+ //-----------------------------------------------------------
+
+ // Watch parent position and update side.
+ watch(elementX, (elementX, previous) => {
+ const winW = window.innerWidth
+
+ // Logic depends on the side we are on.
+ if (side === "right") {
+ if (winW - elementX < 230) {
+ switchSide(side)
+ side = "left"
+ }
+ }
+
+ else {
+ if (winW - elementX > 230) {
+ switchSide(side)
+ side = "right"
+ }
+ }
+
+ });
+
+ onMounted(() => {
+ textInput = document.getElementById("textInput") as HTMLInputElement;
+ window.addEventListener("keydown", onKeyDown);
+ })
+
+ return {
+
+ }
+
+}
+++ /dev/null
-/*
-* Main logic used by inputItem component.
-* Will visualize text input by default as user types.
-* If user holds space bar down, input audio stream will
-* be recorded and sent to the backend for analysis.
-*/
-
-import { ref, watch, onUnmounted, onMounted } from "vue";
-import useMitt from "@/modules/mitt";
-import { useIpc } from '@/modules/ipc';
-import keyboardNameMap from "./keyBoardMaps/keyboardNameMap";
-import keyboardCharMap from "./keyBoardMaps/keyboardCharMap";
-import anime from "animejs";
-import { clientMessage, renderMessage } from '@/modules/message';
-
-let textInput: HTMLInputElement | null;
-
-// Animation functions for text input element.
-function expandInput() {
- anime({
- targets: textInput,
- width: ['0px', '150px'],
- duration: 500,
- easing: 'easeOutExpo',
- })
-}
-
-function retractInput() {
- anime({
- targets: textInput,
- width: ['150px', '0px'],
- duration: 500,
- easing: 'easeOutExpo',
- begin: function() {
- if (textInput) textInput.blur();
- }
- })
-}
-
-export default function useInputController() {
-
- const { emitter } = useMitt();
- const { post } = useIpc();
- const isRecording = ref(false);
- const isTyping = ref(false);
- const input = ref("");
- const capsLock = ref(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);
- console.log(m.time)
- return m.uid;
- }
-
- // send message on ENTER.
- const onEnter = () => {
-
- if (input.value) {
-
- // Render the message immediately and get its unique id.
- const uid = render(input.value, false, "", "sf");
-
- // Then send it to the backend for processing.
- const clientM = clientMessage(input.value, false, uid);
- post('client-message', clientM);
-
- // Reset the text input.
- if (textInput) textInput.value = '';
- input.value = " ";
- }
-
- }
-
- // update input on keydown event & listen for special commands.
- const onKeyDown = (e: KeyboardEvent) => {
- if (textInput) {
- textInput.focus();
- textInput.value = input.value;
- }
-
- // keyboardCharMap is an array of arrays, with each inner
- // array having 2 columns - un-shifted, and shifted values.
- // iCol = 0 is the un-shifted value, while iCol=1 is the shifted value.
- // See the "keyboardCharMap" module for possible keys.
- // MODIFY keyboardCharMap to suit your needs if you want
- // more/less/different characters to be considered "printable".
- let iCol = 0;
- if (e.shiftKey || capsLock.value) {
- iCol = 1;
- }
-
- const ch = keyboardCharMap[e.keyCode][iCol];
-
- // Optionally do things with non-printables,
- // like CR, ESC, Backspace, LF, Tab, Arrows, F1-F24, etc.
- // See the arrary "keyboardNameMap" module for possible keys.
- const cmd = keyboardNameMap[e.keyCode];
-
- switch (cmd) {
- case "ENTER":
- onEnter();
- break;
- case "BACK_SPACE":
- input.value = input.value.slice(0, -1);
- break;
- case "CAPS_LOCK":
- capsLock.value = true;
- break;
- case "ESCAPE":
- if (textInput) textInput.value = ''
- input.value = "";
- break;
- default:
- input.value += ch;
- input.value.trim();
- break;
- }
-
- }
-
- // stop stream recording and render audio message on space key up event.
- const onKeyUp = (e: KeyboardEvent) => {
- if (e.key === " " && isRecording.value) {
-
- isRecording.value = false;
-
- // // Render the message immediately and get its unique id.
- const uid = render("", "", "", "sf");
- // Notify main process to stop recording and send send audio to backend for analysis.
- post('update-recorder', {
- isRecording: isRecording.value,
- uid
- });
-
- // Reset input.
- input.value = ''
- }
- else if (e.code === "CapsLock") {
- capsLock.value = false;
- }
- }
-
- onMounted(() => {
- textInput = document.getElementById('textInput') as HTMLInputElement;
- // add window event keyboard listeners
- window.addEventListener("keydown", onKeyDown);
- window.addEventListener('keyup', onKeyUp);
- })
-
- onUnmounted(() => {
- window.removeEventListener("keydown", onKeyDown);
- window.removeEventListener("keyup", onKeyUp);
- });
-
- // watch keyboard input & update inputItem state.
- watch(input, (input, prevInput) => {
- if (isTyping.value && input.length === 0) {
- retractInput();
- }
-
- // Do nothing if user is typing.
- if (prevInput !== "" || prevInput.length > 0) {
- return;
- }
-
- // Begin stream recording on space bar.
- if (input === " " && isRecording.value === false) {
- isTyping.value = false;
- isRecording.value = true;
- // tell main process to begin stream recording.
- post('update-recorder', {
- isRecording: isRecording.value,
- uid: null
- });
- return;
- }
-
- isTyping.value = true;
- expandInput()
- });
-
- return {
- isRecording,
- isTyping
- }
-
-}
<div
id="inputItem"
class="input-item"
- :class="{ playing: isRecording }"
+ :class="{ playing: recording }"
:style="{ top: `${elementY}px`, left: `${elementX}px` }"
>
AG
- <!-- animations for recording and on-standby -->
- <span v-if="isRecording" class="play"></span>
- <span v-if="isRecording" class="pause">
+ <!-- Show audio input on spacebar -->
+ <AudioInput />
+
+
+ <span v-if="recording" class="play"></span>
+ <span v-if="recording" class="pause">
<div class="rec-icon"></div>
</span>
<!-- Show text input on key-down -->
- <TextInput :position="elementX"/>
+ <TextInput />
<!-- Show suggestions menu on click -->
- <!-- <SuggestionsMenu /> -->
+ <!-- <Menu /> -->
</div>
</template>
import { defineComponent } from "vue";
import draggify from "@/modules/draggify";
-import useInputController from "./inputController";
+
import TextInput from "@/components/inputItem/textInput.vue"
+import useTextInputController from
+ "@/components/inputItem/controllers/textCtrl"
+import useAudioInputController from
+ "@/components/inputItem/controllers/audioCtrl"
export default defineComponent({
name: "InputItem",
// Calculate position of inputItem on drag.
const { elementX, elementY } = draggify({ elementId: "inputItem" });
- // determine whether the user is typing or recording.
- const { isRecording, isTyping } = useInputController();
+ // Controllers for text and audio.
+ useTextInputController(elementX)
+ const { recording } = useAudioInputController()
return {
elementX,
elementY,
- isRecording,
- isTyping,
+ recording
};
},
});
.input-item {
position: absolute;
+ opacity: 1.0;
+
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
- border: 4px solid #C6C6C6;
- border-radius: 24px;
-
z-index: 3;
- background-color: white;
+ // border: 4px solid #C6C6C6;
+ border-radius: 20px;
+
+ background-color: #EBEBEB;
cursor: pointer;
+++ /dev/null
-<template>
- <div id="menu">
- <ul id="menu-list">
- <li
- v-for="suggestion in menuItems.values()"
- @click="submitSuggestion(suggestion)"
- >
- {{ suggestion }}
- </li>
- </ul>
- </div>
-</template>
-
-<script lang="ts">
-import { defineComponent, ref } from "vue";
-import useMenu from "./menuController";
-
-export default defineComponent({
- name: "SuggestionsMenu",
-
- setup() {
- const menuItems = ref(new Map())
- // menuItems.value.set(0, 'add contact')
- // menuItems.value.set(1, 'delete contact')
- // menuItems.value.set(2, 'show contacts')
- // menuItems.value.set(3, 'send a message')
-
- const { submitSuggestion } = useMenu();
-
- return {
- menuItems,
- submitSuggestion
- }
- }
-})
-</script>
-
-<style lang="scss" scoped>
- #menu {
- margin-top: 20px;
- min-width: 200px;
- height: 200px;
- background-color: white;
- border-radius: 18px;
-
- margin: 0;
- padding: 0;
-
- animation-name: menu-appear;
- animation-duration: 1s;
- box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.15);
- }
-
- #menu-list {
- margin: 0;
- padding: 0;
- }
-
- @keyframes menu-appear {
- from {
- height: 0px;
- }
- to {
- height: 200px;
- }
- }
-
-
-
-</style>
+++ /dev/null
-
-
-
-
-export default function useMenu() {
-
- const submitSuggestion = (suggestion: string) => {
- console.log(`Submit...${suggestion}`)
-
- // // Render the message immediately and get its unique id.
- // const uid = render(suggestion, false, "", "sf");
-
- // // Then send it to the backend for processing.
- // const clientM = clientMessage(suggestion, false, uid);
- // post('client-message', clientM);
- }
-
- return {
- submitSuggestion
- }
-
-}
\ No newline at end of file
<template>
- <div
- class="text-input"
- :class="`text-input-${updateSide}`"
- >
- <input id="textInput" type="text" />
- </div>
+ <input
+ id="textInput"
+ type="text"
+ />
</template>
export default defineComponent({
name: "TextInput",
- data: {
-
- // Where is avatar?
- position: Number
-
- },
-
- setup(data) {
-
- const side = ref("")
- side.value = "left"
-
- // Watch the position of parent.
- // watch(data.position, (position, prevPosition) => {
- // console.log("position moved")
- // })
-
- return {
- side
- }
- }
})
</script>
<style lang="scss" scoped>
-.text-input {
+#textInput {
position: absolute;
+ opacity: 0;
+
min-width: 150px;
- height: 36px;
+ height: 16px;
border-radius: 18px;
+ padding: 10px;
+ margin-right: 10px;
+ margin-left: 10px;
+
outline: none;
border: none;
pointer-events: none;
background-color: white;
+ z-index: -1;
+
box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.15);
-}
-// Activate if we need
-.text-input-left {
+ transform: translateX(50px) scale(0.3);
- left: -200px;
- background-color: red;
+}
+@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
});
// Initialize the coordinants.
- elementX.value = 20;
- elementY.value = 200 - 38 - 20;
+ elementX.value = 15;
+ elementY.value = 300;
// remove event listeners on component dismount.
onUnmounted(() => {