diff --git a/app/assets/scss/app.scss b/app/assets/scss/app.scss
index 0e608bf..51aad31 100644
--- a/app/assets/scss/app.scss
+++ b/app/assets/scss/app.scss
@@ -350,6 +350,7 @@ a.nav-link.active {
position:absolute;
bottom:0; // make transcription start from the bottom
max-height:100%;
+ min-width:5px;
}
.final:focus {
diff --git a/app/components/Navbar.vue b/app/components/Navbar.vue
index b4a639b..f63db09 100644
--- a/app/components/Navbar.vue
+++ b/app/components/Navbar.vue
@@ -25,6 +25,12 @@
Listening to "{{microphoneName}}"
+
+
+
+
+ Typing Mode
+
@@ -131,6 +137,9 @@ export default {
captioningOn: function() {
return this.$store.state.captioner.shouldBeOn;
},
+ typingModeOn: function() {
+ return this.$store.state.captioner.typingModeOn;
+ },
microphoneName: function() {
return this.$store.state.captioner.microphoneName;
},
@@ -186,11 +195,11 @@ export default {
stopCaptioning: function() {
this.$store.dispatch('captioner/stopManual');
},
- startTyping: function() {
- this.$store.dispatch('captioner/startTyping');
+ startTypingMode: function() {
+ this.$store.dispatch('captioner/startTypingMode');
},
- stopTyping: function() {
- this.$store.dispatch('captioner/stopTyping');
+ stopTypingMode: function() {
+ this.$store.dispatch('captioner/stopTypingMode');
},
startSaveToFileModal: function() {
this.$router.push('/captioner/save-to-file');
diff --git a/app/components/Transcript.vue b/app/components/Transcript.vue
index 294e3b2..f3c6687 100644
--- a/app/components/Transcript.vue
+++ b/app/components/Transcript.vue
@@ -2,13 +2,14 @@
+ v-bind:style="{height, color, backgroundColor, fontFamily, fontSize, lineHeight, letterSpacing, textTransform, padding, textShadow, cursor}"
+ @click="focusIfInTypingMode()">
- {{finalTranscript}} {{interimTranscript}}
+ {{finalTranscript}} {{interimTranscript}} Hello world
@@ -27,6 +28,7 @@ export default {
data: function() {
return {
height: '100vh',
+ transcriptTypedForDisplay: '',
}
},
methods: {
@@ -37,6 +39,14 @@ export default {
}
});
},
+ typedTranscriptDidChange: function() {
+ this.$store.commit('captioner/SET_TRANSCRIPT_TYPED', {transcriptTyped: this.$refs.typedTranscript.innerText})
+ },
+ focusIfInTypingMode: function() {
+ if (this.typingModeOn) {
+ this.$refs.typedTranscript.focus();
+ }
+ },
},
mounted: function() {
this.scrollToBottom();
@@ -54,6 +64,37 @@ export default {
this.height = this.adjustAppHeight();
});
},
+ watch: {
+ typingModeOn: function (on) {
+ if (on) {
+ // Turned on. Copy the transcript over once.
+ this.transcriptTypedForDisplay = this.typedTranscript;
+ this.$nextTick(() => {
+ this.$refs.typedTranscript.focus();
+
+ // Put caret at end
+ if (typeof window.getSelection != "undefined"
+ && typeof document.createRange != "undefined") {
+ var range = document.createRange();
+ range.selectNodeContents(this.$refs.typedTranscript);
+ range.collapse(false);
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ sel.addRange(range);
+ } else if (typeof document.body.createTextRange != "undefined") {
+ var textRange = document.body.createTextRange();
+ textRange.moveToElementText(this.$refs.typedTranscript);
+ textRange.collapse(false);
+ textRange.select();
+ }
+ });
+ }
+ else {
+ // Turned off.
+ this.transcriptTypedForDisplay = '';
+ }
+ },
+ },
computed: {
// Appearance
color () {
@@ -107,6 +148,12 @@ export default {
return (this.$store.state.captioner.transcript.interim && this.$store.state.captioner.transcript.interim.length ? ' ' : '')
+ this.$store.state.captioner.transcript.interim;
},
+ typingModeOn () {
+ return this.$store.state.captioner.typingModeOn;
+ },
+ typedTranscript () {
+ return this.$store.state.captioner.transcript.typed;
+ },
textPositionClass: function () {
return {
@@ -130,6 +177,9 @@ export default {
'align-items-end': ['bottom','lowerThird'].includes(this.$store.state.settings.appearance.text.alignment.vertical),
}
},
+ cursor: function() {
+ return this.typingModeOn ? 'text' : 'default';
+ },
largerLayout: function() {
return this.$store.state.settings.controls.layout.larger;
},
@@ -138,4 +188,10 @@ export default {
diff --git a/app/nuxt.config.js b/app/nuxt.config.js
index a63b1eb..378f6ab 100644
--- a/app/nuxt.config.js
+++ b/app/nuxt.config.js
@@ -32,7 +32,7 @@ module.exports = {
imports: [
{
set: '@fortawesome/free-solid-svg-icons',
- icons: ['faFileAlt', 'faFileWord', 'faExclamationTriangle', 'faTimes', 'faMicrophone', 'faDesktop', 'faExternalLinkAlt', 'faSave', 'faTrashAlt', 'faCog', 'faCheckCircle', 'faSpinner', 'faChevronRight', 'faMinusCircle', 'faPlusCircle', 'faArrowLeft', 'faFlask', 'faCaretRight', 'faCaretDown', ],
+ icons: ['faFileAlt', 'faFileWord', 'faExclamationTriangle', 'faTimes', 'faMicrophone', 'faDesktop', 'faExternalLinkAlt', 'faSave', 'faTrashAlt', 'faCog', 'faCheckCircle', 'faSpinner', 'faChevronRight', 'faMinusCircle', 'faPlusCircle', 'faArrowLeft', 'faFlask', 'faCaretRight', 'faCaretDown', 'faKeyboard', ],
},
{
set: '@fortawesome/free-regular-svg-icons',
@@ -45,6 +45,9 @@ module.exports = {
]
}],
],
+ plugins: [
+ '~/node_modules/vue-contenteditable-directive',
+ ],
css: [
'@/assets/scss/app.scss',
],
diff --git a/app/package-lock.json b/app/package-lock.json
index 3450c60..b7afa55 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -11107,6 +11107,12 @@
"integrity": "sha512-pRy3/QYWvNwCESrkVEyz9NjVfzUUkp9OLcK4RQlx6L1UY54CdFeVMs/+KdzxBppt38HSpXh+IX/p2tB4pDIgFQ==",
"dev": true
},
+ "vue-contenteditable-directive": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/vue-contenteditable-directive/-/vue-contenteditable-directive-1.2.0.tgz",
+ "integrity": "sha512-9RuW1cboQBOUhURXiQpBD8XldyK2BYWhkWTnRw4Qmv8ZeQy+tGnnPs4XfemoPNf4KQW31Mx6UqEszlZYgoPeYw==",
+ "dev": true
+ },
"vue-eslint-parser": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz",
diff --git a/app/package.json b/app/package.json
index b6706c9..eaf8b63 100644
--- a/app/package.json
+++ b/app/package.json
@@ -54,6 +54,7 @@
"sass-loader": "^7.1.0",
"screenfull": "^3.3.2",
"semver-compare": "^1.0.0",
+ "vue-contenteditable-directive": "^1.2.0",
"whatwg-fetch": "^2.0.4"
}
}
diff --git a/app/pages/captioner.vue b/app/pages/captioner.vue
index 0e681b2..a244203 100755
--- a/app/pages/captioner.vue
+++ b/app/pages/captioner.vue
@@ -217,7 +217,6 @@ export default {
mode: 'cors',
headers: {
'Accept': 'application/json',
- 'Content-Type': 'application/json',
},
body,
})
diff --git a/app/store/modules/captioner/index.js b/app/store/modules/captioner/index.js
index bc4e0bb..bc78128 100644
--- a/app/store/modules/captioner/index.js
+++ b/app/store/modules/captioner/index.js
@@ -13,6 +13,7 @@ let speechRecognizer,
const state = {
on: false,
shouldBeOn: false,
+ typingModeOn: false,
microphonePermission: {
needed: false,
denied: false,
@@ -22,6 +23,7 @@ const state = {
transcript: {
interim: '',
final: '',
+ typed: '',
lastStart: null,
lastUpdate: null,
waitingForInitial: false,
@@ -198,6 +200,21 @@ const actions = {
});
}
},
+
+ startTypingMode: ({state, commit, dispatch}) => {
+ dispatch('stopManual');
+ setTimeout(() => {
+ commit('SET_TRANSCRIPT_TYPED', {transcriptTyped: state.transcript.final});
+ commit('CLEAR_TRANSCRIPT_FINAL');
+ commit('SET_TYPING_MODE_ON');
+ },500);
+ },
+
+ stopTypingMode: ({state, commit, dispatch}) => {
+ commit('APPEND_TRANSCRIPT_FINAL', {transcriptFinal: state.transcript.typed });
+ commit('CLEAR_TRANSCRIPT_TYPED');
+ commit('SET_TYPING_MODE_OFF');
+ },
}
const mutations = {
@@ -227,13 +244,23 @@ const mutations = {
state.transcript.interim = transcriptInterim;
state.transcript.lastUpdate = Date.now();
},
+ SET_TRANSCRIPT_TYPED (state, { transcriptTyped }) {
+ state.transcript.typed = transcriptTyped;
+ },
CLEAR_TRANSCRIPT (state) {
state.transcript.interim = '';
state.transcript.final = '';
+ state.transcript.typed = '';
},
CLEAR_TRANSCRIPT_INTERIM (state) {
state.transcript.interim = '';
},
+ CLEAR_TRANSCRIPT_FINAL (state) {
+ state.transcript.final = '';
+ },
+ CLEAR_TRANSCRIPT_TYPED (state) {
+ state.transcript.typed = '';
+ },
APPEND_TRANSCRIPT_FINAL (state, { transcriptFinal }) {
if (state.transcript.final.length && state.transcript.final.charAt(state.transcript.final.length - 1) != ' ') {
// Current final string is not empty and doesn't end in a
@@ -258,6 +285,14 @@ const mutations = {
SET_WAITING_FOR_INITIAL_TRANSCRIPT (state, { waitingForInitial }) {
state.transcript.waitingForInitial = waitingForInitial;
},
+
+ SET_TYPING_MODE_ON (state) {
+ state.typingModeOn = true;
+ },
+
+ SET_TYPING_MODE_OFF (state) {
+ state.typingModeOn = false;
+ },
}
const getters = {
diff --git a/app/store/mutations.js b/app/store/mutations.js
index 68246e8..51acf57 100755
--- a/app/store/mutations.js
+++ b/app/store/mutations.js
@@ -136,6 +136,10 @@ export default {
state.detached = false;
},
+ SET_DETACHED_MODE_OFF: (state) => {
+ state.detached = false;
+ },
+
SET_LAST_WHATS_NEW_VERSION_SEEN: (state, { version }) => {
state.settings.lastWhatsNewVersionSeen = version;
},