Skip to content

Commit 17e5d8a

Browse files
authored
Merge pull request #232 from nextcloud/feat/translate-layout
feat: update translate task form layout
2 parents eaad41b + 31e96f7 commit 17e5d8a

3 files changed

Lines changed: 255 additions & 1 deletion

File tree

src/components/AssistantFormOutputs.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
:optional-shape-options="selectedTaskType.optionalOutputShapeEnumValues ?? null"
1818
:values="outputs"
1919
:show-advanced="showAdvanced"
20-
@update:outputs="$emit('update:outputs', $event)"
20+
@update:values="$emit('update:outputs', $event)"
2121
@update:show-advanced="$emit('update:show-advanced', $event)" />
2222
<NcNoteCard v-if="outputEqualsInput"
2323
class="warning-note"
@@ -66,6 +66,10 @@ export default {
6666
},
6767
},
6868
69+
emits: [
70+
'update:outputs',
71+
],
72+
6973
computed: {
7074
selectedTaskTypeId() {
7175
return this.selectedTaskType?.id ?? null

src/components/AssistantTextProcessingForm.vue

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@
4040
@try-again="onHistoryTryAgain"
4141
@load-task="onHistoryLoadTask" />
4242
</div>
43+
<div v-else-if="mySelectedTaskTypeId === 'core:text2text:translate'"
44+
class="task-input-output-form">
45+
<TranslateForm v-if="selectedTaskType"
46+
:inputs.sync="myInputs"
47+
:outputs.sync="myOutputs"
48+
:translate-task-id="selectedTaskId"
49+
:translate-task-type="selectedTaskType"
50+
:show-advanced.sync="showAdvanced" />
51+
</div>
4352
<div v-else class="task-input-output-form">
4453
<AssistantFormInputs v-if="selectedTaskType"
4554
:inputs.sync="myInputs"
@@ -125,6 +134,7 @@ import AssistantFormOutputs from './AssistantFormOutputs.vue'
125134
import NoProviderEmptyContent from './NoProviderEmptyContent.vue'
126135
import TaskList from './TaskList.vue'
127136
import TaskTypeSelect from './TaskTypeSelect.vue'
137+
import TranslateForm from './Translate/TranslateForm.vue'
128138
129139
import { SHAPE_TYPE_NAMES } from '../constants.js'
130140
@@ -144,6 +154,7 @@ export default {
144154
NoProviderEmptyContent,
145155
TaskList,
146156
TaskTypeSelect,
157+
TranslateForm,
147158
NcButton,
148159
NcLoadingIcon,
149160
NcIconSvgWrapper,
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
<!--
2+
- SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
3+
- SPDX-License-Identifier: AGPL-3.0-or-later
4+
-->
5+
6+
<template>
7+
<div class="assistant-inputs">
8+
<div class="input-container">
9+
<div class="wrapper">
10+
<div class="col">
11+
<TaskTypeField
12+
field-key="origin_language"
13+
:field="translateTaskType.inputShape.origin_language"
14+
:value="inputs?.origin_language ?? null"
15+
:options="translateTaskType.inputShapeEnumValues.origin_language"
16+
:is-output="false"
17+
@update:value="onValueChange('origin_language', $event)" />
18+
<TaskTypeField
19+
field-key="input"
20+
:field="translateTaskType.inputShape.input"
21+
:value="inputs?.input ?? null"
22+
:is-output="false"
23+
@update:value="onValueChange('input', $event)" />
24+
<TaskTypeField v-if="showAdvanced"
25+
field-key="max_tokens"
26+
:field="translateTaskType.optionalInputShape.max_tokens"
27+
:value="inputs?.max_tokens ?? null"
28+
:is-output="false"
29+
@update:value="onValueChange('max_tokens', $event)" />
30+
<TaskTypeField v-if="showAdvanced"
31+
field-key="model"
32+
:field="translateTaskType.optionalInputShape.model"
33+
:value="inputs?.model ?? null"
34+
:options="translateTaskType.optionalInputShapeEnumValues.model"
35+
:is-output="false"
36+
@update:value="onValueChange('model', $event)" />
37+
</div>
38+
<div class="col">
39+
<TaskTypeField
40+
field-key="target_language"
41+
:field="translateTaskType.inputShape.target_language"
42+
:value="inputs?.target_language ?? null"
43+
:options="translateTaskType.inputShapeEnumValues.target_language"
44+
:is-output="false"
45+
@update:value="onValueChange('target_language', $event)" />
46+
<TaskTypeField
47+
field-key="output"
48+
:field="translateTaskType.outputShape.output"
49+
:value="outputs?.output ?? null"
50+
:is-output="true"
51+
@update:value="onOutputValueChange('output', $event)" />
52+
<NcNoteCard v-if="outputEqualsInput"
53+
class="warning-note"
54+
type="warning">
55+
{{ t('assistant', 'The task ran successfully but the result is identical to the input.') }}
56+
</NcNoteCard>
57+
<NcNoteCard v-else-if="hasInitialOutput"
58+
class="warning-note"
59+
type="warning">
60+
{{ t('assistant', 'This output was generated by AI. Make sure to double-check and adjust.') }}
61+
</NcNoteCard>
62+
</div>
63+
</div>
64+
</div>
65+
</div>
66+
</template>
67+
68+
<script>
69+
import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'
70+
71+
import TaskTypeField from '../fields/TaskTypeField.vue'
72+
73+
export default {
74+
name: 'TranslateForm',
75+
76+
components: {
77+
NcNoteCard,
78+
TaskTypeField,
79+
},
80+
81+
props: {
82+
translateTaskId: {
83+
type: [Number, null],
84+
default: null,
85+
},
86+
translateTaskType: {
87+
type: Object,
88+
required: true,
89+
},
90+
inputs: {
91+
type: [Object, null],
92+
default: null,
93+
},
94+
outputs: {
95+
type: [Object, null],
96+
default: null,
97+
},
98+
showAdvanced: {
99+
type: Boolean,
100+
default: false,
101+
},
102+
},
103+
104+
emits: [
105+
'update:inputs',
106+
],
107+
108+
data() {
109+
return {
110+
}
111+
},
112+
113+
computed: {
114+
outputEqualsInput() {
115+
if (typeof this.inputs?.input === 'string' && typeof this.outputs?.output === 'string') {
116+
return this.hasInitialOutput && this.outputs.output?.trim() === this.inputs.input?.trim()
117+
}
118+
return false
119+
},
120+
hasInitialOutput() {
121+
return !!this.outputs?.output?.trim()
122+
},
123+
},
124+
125+
watch: {
126+
translateTaskType() {
127+
console.debug('[assistant] watch translateTaskType', this.translateTaskType, this.translateTaskTypeId)
128+
this.setDefaultValues(true)
129+
},
130+
},
131+
132+
mounted() {
133+
console.debug('[assistant] mounted TranslateForm', this.translateTaskId, this.translateTaskType)
134+
// don't set the default values if there is a loaded task (initial or from history)
135+
if (this.translateTaskId === null) {
136+
this.setDefaultValues(false)
137+
}
138+
139+
this.autosize()
140+
},
141+
142+
methods: {
143+
autosize() {
144+
const inputField = document.getElementById('input-input')
145+
const outputField = document.getElementById('output-input')
146+
if (inputField && outputField) {
147+
inputField.style.overflowY = 'hidden'
148+
inputField.style.height = 'auto'
149+
const height = inputField.scrollHeight + 10
150+
inputField.style.height = height + 'px'
151+
outputField.style.height = height + 'px'
152+
inputField.style.overflowY = 'auto'
153+
}
154+
},
155+
onValueChange(key, value) {
156+
if (key === 'input') {
157+
this.autosize()
158+
}
159+
const newValues = {
160+
...this.inputs,
161+
[key]: value,
162+
}
163+
console.debug('[assistant] translate field value change', newValues)
164+
this.$emit('update:inputs', newValues)
165+
},
166+
onOutputValueChange(key, value) {
167+
const newValues = {
168+
...this.outputs,
169+
[key]: value,
170+
}
171+
console.debug('[assistant] translate output field value change', newValues)
172+
this.$emit('update:outputs', newValues)
173+
},
174+
setDefaultValues(clear = true) {
175+
console.debug('[assistant] set translate default values', this.translateTaskType?.inputShapeDefaults, this.translateTaskType?.optionalInputShapeDefaults)
176+
const inputs = clear
177+
? {}
178+
: {
179+
...this.inputs,
180+
}
181+
// set default values
182+
if (this.translateTaskType.inputShapeDefaults) {
183+
Object.keys(this.translateTaskType.inputShapeDefaults).forEach(key => {
184+
if (this.translateTaskType.inputShapeDefaults[key]) {
185+
inputs[key] = this.translateTaskType.inputShapeDefaults[key]
186+
}
187+
})
188+
}
189+
if (this.translateTaskType.optionalInputShapeDefaults) {
190+
Object.keys(this.translateTaskType.optionalInputShapeDefaults).forEach(key => {
191+
if (this.translateTaskType.optionalInputShapeDefaults[key]) {
192+
inputs[key] = this.translateTaskType.optionalInputShapeDefaults[key]
193+
}
194+
})
195+
}
196+
this.$emit('update:inputs', inputs)
197+
},
198+
},
199+
}
200+
</script>
201+
202+
<style lang="scss" scoped>
203+
.assistant-inputs {
204+
margin-bottom: 1rem;
205+
206+
.wrapper {
207+
display: flex;
208+
gap: 16px;
209+
210+
.col {
211+
width: 100%;
212+
}
213+
}
214+
215+
:deep(.rich-contenteditable__input) {
216+
display: block;
217+
width: 100%;
218+
margin-bottom: 12px;
219+
height: auto;
220+
resize: none;
221+
box-sizing: border-box;
222+
overflow-y: auto;
223+
min-height: 62px;
224+
max-height: 58vh;
225+
}
226+
}
227+
228+
@media (max-width: 670px) {
229+
.assistant-inputs {
230+
.wrapper {
231+
display: block;
232+
}
233+
234+
:deep(.rich-contenteditable__input) {
235+
max-height: 20vh;
236+
}
237+
}
238+
}
239+
</style>

0 commit comments

Comments
 (0)