import { RenderMessage, ClientMessage } from "@/types/message/index";
import { v4 as uuidv4 } from 'uuid';
import {clientMessage, renderMessage} from '@/modules/message';
+import anime from "animejs";
let textInput: HTMLInputElement | null;
let m: RenderMessage | ClientMessage;
let uid: string;
+// 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 isRecording = ref(false);
const isTyping = ref(false);
const input = ref("");
+ const capsLock = ref(false);
// send message on ENTER.
const onEnter = () => {
if (input.value) {
- // remove first character of input.
- const text = input.value.substring(1);
uid = uuidv4();
- m = renderMessage(text, false, "sf", uid);
+ m = renderMessage(input.value, false, "sf", uid);
// render user message.
emitter.emit("self-message", m);
m = clientMessage(m.content.text, m.content.audio, uid);
- // send message to backend.
+ // send message to main process.
post('client-message', m);
// reset text input.
// update input on keydown event & listen for special commands.
const onKeyDown = (e: KeyboardEvent) => {
-
- if (textInput) textInput.focus();
+ 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.
// MODIFY keyboardCharMap to suit your needs if you want
// more/less/different characters to be considered "printable".
let iCol = 0;
- if (e.shiftKey) {
+ if (e.shiftKey || capsLock.value) {
iCol = 1;
}
break;
case "BACK_SPACE":
input.value = input.value.slice(0, -1);
- break;
+ 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;
}
}
input.value = ''
}
+ else if (e.code === "CapsLock") {
+ capsLock.value = false;
+ }
}
onMounted(() => {
window.removeEventListener("keyup", onKeyUp);
});
- // watch keyboard input & update current state.
+
+ // 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) {
- if (!isRecording.value) {
- isTyping.value = true;
- }
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
}
isTyping.value = true;
-
+ expandInput()
});
return {
- input,
isRecording,
isTyping
}
</span>
<!-- Show text input on key-down -->
- <input id="textInput" type="text" v-show="isTyping && input.length > 0"/>
+ <input id="textInput" type="text" />
</div>
</template>
// Calculate position of inputItem on drag.
const { elementX, elementY } = draggify({ elementId: "inputItem" });
- //
- const { input, isRecording, isTyping } = useInputController();
+ // determine whether the user is typing or recording.
+ const { isRecording, isTyping } = useInputController();
return {
elementX,
elementY,
isRecording,
isTyping,
- input,
};
},
});
<style lang="scss" scoped>
#textInput {
- width: 150px;
+ width: 0px;
height: 36px;
border-top-right-radius: 18px;
outline: none;
border: none;
pointer-events: none;
- animation-name: expand;
- animation-duration: 0.5s;
-}
-
-@keyframes expand {
- from {
- width: 0px;
- }
- to {
- width: 150px;
- }
}
.rec-icon {