@jskit-ai/assistant-core 0.1.244 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +539 -0
- package/examples/conversation/package.json +5 -5
- package/locales/en.json +383 -0
- package/package.json +7 -6
- package/src/client/components/AssistantSettingsFormCard.vue +4 -1
- package/src/client/conversation/AssistantAttachmentPreview.vue +6 -3
- package/src/client/conversation/AssistantAttachmentQueue.vue +36 -37
- package/src/client/conversation/AssistantComposerActions.vue +6 -3
- package/src/client/conversation/AssistantComposerSupport.vue +8 -5
- package/src/client/conversation/AssistantConversationElement.vue +16 -13
- package/src/client/conversation/AssistantConversationStatus.vue +5 -2
- package/src/client/conversation/AssistantGoalControl.vue +22 -16
- package/src/client/conversation/AssistantMessageAttachments.vue +7 -4
- package/src/client/conversation/AssistantModelControl.vue +23 -20
- package/src/client/conversation/AssistantProgress.vue +6 -3
- package/src/client/conversation/AssistantQuestionInputs.vue +9 -6
- package/src/client/conversation/AssistantTranscript.vue +39 -28
- package/src/client/conversation/LongTextPreviewBlocks.vue +10 -2
- package/src/client/conversation/attachmentSize.js +38 -0
- package/src/client/conversation/messageDelivery.js +14 -5
- package/src/client/conversation/submitText.js +11 -4
- package/src/client/conversation/useAssistantAttachments.js +40 -30
- package/src/client/conversation/useAssistantSuggestions.js +4 -1
- package/src/client/i18n.js +6 -0
- package/src/client/lib/assistantApi.js +14 -2
- package/src/server/conversation/attachments.js +24 -14
- package/src/server/conversation/claudeProcess.js +4 -2
- package/src/server/conversation/claudeTurn.js +14 -11
- package/src/server/conversation/codexConversations.js +4 -0
- package/src/server/conversation/codexGoalCommands.js +9 -9
- package/src/server/conversation/codexHelperExecution.js +4 -0
- package/src/server/conversation/codexHelperThreads.js +2 -0
- package/src/server/conversation/codexMessageCommands.js +7 -1
- package/src/server/conversation/codexTurnControl.js +2 -1
- package/src/server/conversation/codexTurnState.js +2 -0
- package/src/server/conversation/continuity.js +16 -6
- package/src/server/conversation/runtime.js +193 -141
- package/src/server/lib/ndjson.js +19 -1
- package/src/server/lib/providers/common.js +7 -2
- package/src/shared/conversation/numberedQuestionSugar.js +7 -1
- package/src/shared/conversation/turns.js +10 -2
- package/test/i18nCatalog.test.js +22 -0
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
class="assistant-attachment-queue"
|
|
6
6
|
:class="{ 'assistant-attachment-queue--standalone': !joined }"
|
|
7
7
|
:aria-busy="hasUnresolved ? 'true' : undefined"
|
|
8
|
-
aria-label="
|
|
8
|
+
:aria-label="t('jskit.assistant.attachments.queue')"
|
|
9
9
|
>
|
|
10
10
|
<header class="assistant-attachment-queue__summary">
|
|
11
11
|
<span>{{ aggregateLabel }}</span>
|
|
12
|
-
<span class="assistant-attachment-queue__limit">{{ retainedItems.length
|
|
12
|
+
<span class="assistant-attachment-queue__limit">{{ t("jskit.assistant.attachments.limit", { count: retainedItems.length, max: maximumFiles }) }}</span>
|
|
13
13
|
</header>
|
|
14
14
|
|
|
15
15
|
<ol class="assistant-attachment-queue__items">
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
>
|
|
39
39
|
{{ item.receipt?.reference }} {{ itemName(item) }}
|
|
40
40
|
</component>
|
|
41
|
-
<span class="assistant-attachment-queue__size">{{
|
|
41
|
+
<span class="assistant-attachment-queue__size">{{ attachmentSizeText(item.size) }}</span>
|
|
42
42
|
</div>
|
|
43
43
|
<div class="assistant-attachment-queue__secondary">
|
|
44
44
|
<span>{{ itemStatusLabel(item) }}</span>
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
/>
|
|
52
52
|
<v-progress-linear
|
|
53
53
|
v-else-if="itemProgressVisible(item)"
|
|
54
|
-
:aria-label="
|
|
54
|
+
:aria-label="t('jskit.assistant.attachments.uploadProgress', { name: itemName(item) })"
|
|
55
55
|
class="assistant-attachment-queue__progress"
|
|
56
56
|
color="primary"
|
|
57
57
|
height="3"
|
|
@@ -72,11 +72,11 @@
|
|
|
72
72
|
<v-btn
|
|
73
73
|
v-if="itemCancelable(item)"
|
|
74
74
|
:ref="(element) => setActionElement(item, 'cancel', element)"
|
|
75
|
-
:aria-label="
|
|
75
|
+
:aria-label="t('jskit.assistant.attachments.cancelItem', { name: itemName(item) })"
|
|
76
76
|
class="assistant-attachment-queue__action"
|
|
77
77
|
:icon="mdiCloseCircleOutline"
|
|
78
78
|
size="small"
|
|
79
|
-
:title="
|
|
79
|
+
:title="t('jskit.assistant.attachments.cancelItem', { name: itemName(item) })"
|
|
80
80
|
type="button"
|
|
81
81
|
variant="text"
|
|
82
82
|
@click="handleAction('cancel', item, index)"
|
|
@@ -84,11 +84,11 @@
|
|
|
84
84
|
<v-btn
|
|
85
85
|
v-if="itemPhase(item) === 'failed' || itemPhase(item) === 'cancelled'"
|
|
86
86
|
:ref="(element) => setActionElement(item, 'retry', element)"
|
|
87
|
-
:aria-label="
|
|
87
|
+
:aria-label="t('jskit.assistant.attachments.retryItem', { name: itemName(item) })"
|
|
88
88
|
class="assistant-attachment-queue__action"
|
|
89
89
|
:icon="mdiRefresh"
|
|
90
90
|
size="small"
|
|
91
|
-
:title="
|
|
91
|
+
:title="t('jskit.assistant.attachments.retryItem', { name: itemName(item) })"
|
|
92
92
|
type="button"
|
|
93
93
|
variant="text"
|
|
94
94
|
@click="handleAction('retry', item, index)"
|
|
@@ -96,11 +96,11 @@
|
|
|
96
96
|
<v-btn
|
|
97
97
|
v-if="itemPhase(item) === 'ready' || itemPhase(item) === 'failed' || itemPhase(item) === 'cancelled'"
|
|
98
98
|
:ref="(element) => setActionElement(item, 'remove', element)"
|
|
99
|
-
:aria-label="
|
|
99
|
+
:aria-label="t('jskit.assistant.attachments.removeItem', { name: itemName(item) })"
|
|
100
100
|
class="assistant-attachment-queue__action"
|
|
101
101
|
:icon="mdiClose"
|
|
102
102
|
size="small"
|
|
103
|
-
:title="
|
|
103
|
+
:title="t('jskit.assistant.attachments.removeItem', { name: itemName(item) })"
|
|
104
104
|
type="button"
|
|
105
105
|
variant="text"
|
|
106
106
|
@click="handleAction('remove', item, index)"
|
|
@@ -131,7 +131,10 @@ import {
|
|
|
131
131
|
mdiRefresh
|
|
132
132
|
} from "@mdi/js";
|
|
133
133
|
|
|
134
|
-
import {
|
|
134
|
+
import { useJskitText } from "../i18n.js";
|
|
135
|
+
import { attachmentSizeText } from "./attachmentSize.js";
|
|
136
|
+
|
|
137
|
+
const { t } = useJskitText();
|
|
135
138
|
|
|
136
139
|
const emit = defineEmits(["cancel", "remove", "retry", "preview", "focus-input"]);
|
|
137
140
|
|
|
@@ -187,47 +190,43 @@ const aggregateLabel = computed(() => {
|
|
|
187
190
|
const cancelled = props.items.filter((item) => itemPhase(item) === "cancelled").length;
|
|
188
191
|
if (active) {
|
|
189
192
|
return aggregatePercent.value === null
|
|
190
|
-
?
|
|
191
|
-
:
|
|
193
|
+
? t("jskit.assistant.attachments.summary.inProgress", { completed: completedCount.value, total })
|
|
194
|
+
: t("jskit.assistant.attachments.summary.percent", { completed: completedCount.value, total, percent: aggregatePercent.value });
|
|
192
195
|
}
|
|
193
196
|
if (failed) {
|
|
194
|
-
return
|
|
197
|
+
return t("jskit.assistant.attachments.summary.needAttention", { completed: completedCount.value, total, count: failed });
|
|
195
198
|
}
|
|
196
199
|
if (cancelled) {
|
|
197
|
-
return
|
|
200
|
+
return t("jskit.assistant.attachments.summary.cancelled", { completed: completedCount.value, cancelled });
|
|
198
201
|
}
|
|
199
|
-
return
|
|
202
|
+
return t("jskit.assistant.attachments.summary.ready", { completed: completedCount.value, total });
|
|
200
203
|
});
|
|
201
204
|
const announcement = computed(() => {
|
|
202
205
|
const messages = [];
|
|
203
206
|
const failed = props.items.filter((item) => itemPhase(item) === "failed").length;
|
|
204
207
|
if (failed) {
|
|
205
|
-
messages.push(
|
|
208
|
+
messages.push(t("jskit.assistant.attachments.announcement.needAttention", { count: failed }));
|
|
206
209
|
}
|
|
207
210
|
const active = props.items.filter((item) => activePhases.has(itemPhase(item))).length;
|
|
208
211
|
if (active) {
|
|
209
|
-
messages.push(
|
|
212
|
+
messages.push(t("jskit.assistant.attachments.announcement.inProgress", { count: active }));
|
|
210
213
|
}
|
|
211
214
|
const cancelled = props.items.filter((item) => itemPhase(item) === "cancelled").length;
|
|
212
215
|
if (cancelled) {
|
|
213
|
-
messages.push(
|
|
216
|
+
messages.push(t("jskit.assistant.attachments.announcement.cancelled", { count: cancelled }));
|
|
214
217
|
}
|
|
215
218
|
if (completedCount.value) {
|
|
216
|
-
messages.push(
|
|
219
|
+
messages.push(t("jskit.assistant.attachments.announcement.ready", { count: completedCount.value }));
|
|
217
220
|
}
|
|
218
221
|
return messages.join(" ");
|
|
219
222
|
});
|
|
220
223
|
|
|
221
|
-
function attachmentCountLabel(count = 0) {
|
|
222
|
-
return `${count} attachment${count === 1 ? "" : "s"}`;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
224
|
function itemKey(item = {}) {
|
|
226
225
|
return String(item.clientId || item.id || item.attachmentId || item.fileName || "attachment");
|
|
227
226
|
}
|
|
228
227
|
|
|
229
228
|
function itemName(item = {}) {
|
|
230
|
-
return String(item.fileName || item.file?.name || "
|
|
229
|
+
return String(item.fileName || item.file?.name || t("jskit.assistant.attachments.unnamed"));
|
|
231
230
|
}
|
|
232
231
|
|
|
233
232
|
function itemPhase(item = {}) {
|
|
@@ -278,25 +277,25 @@ function itemBytesLabel(item = {}) {
|
|
|
278
277
|
const sent = Math.min(Math.max(0, Number(item.bytesSent) || 0), size);
|
|
279
278
|
const percent = itemPercent(item);
|
|
280
279
|
return percent === null
|
|
281
|
-
?
|
|
282
|
-
:
|
|
280
|
+
? t("jskit.assistant.attachments.bytes", { sent: attachmentSizeText(sent), size: attachmentSizeText(size) })
|
|
281
|
+
: t("jskit.assistant.attachments.bytesPercent", { sent: attachmentSizeText(sent), size: attachmentSizeText(size), percent });
|
|
283
282
|
}
|
|
284
283
|
|
|
285
284
|
function itemStatusLabel(item = {}) {
|
|
286
285
|
const labels = {
|
|
287
|
-
cancelled: "
|
|
288
|
-
delivering: "
|
|
286
|
+
cancelled: "jskit.assistant.attachments.status.cancelled",
|
|
287
|
+
delivering: "jskit.assistant.attachments.status.delivering",
|
|
289
288
|
failed: item.failureStage === "preparing"
|
|
290
|
-
? "
|
|
289
|
+
? "jskit.assistant.attachments.status.preparationFailed"
|
|
291
290
|
: item.failureStage === "handoff"
|
|
292
|
-
? "
|
|
293
|
-
: "
|
|
294
|
-
preparing: "
|
|
295
|
-
queued: "
|
|
296
|
-
ready: "
|
|
297
|
-
uploading: "
|
|
291
|
+
? "jskit.assistant.attachments.status.sendingFailed"
|
|
292
|
+
: "jskit.assistant.attachments.status.uploadFailed",
|
|
293
|
+
preparing: "jskit.assistant.attachments.status.preparing",
|
|
294
|
+
queued: "jskit.assistant.attachments.status.queued",
|
|
295
|
+
ready: "jskit.assistant.attachments.status.ready",
|
|
296
|
+
uploading: "jskit.assistant.attachments.status.uploading"
|
|
298
297
|
};
|
|
299
|
-
return labels[itemPhase(item)]
|
|
298
|
+
return t(Object.hasOwn(labels, itemPhase(item)) ? labels[itemPhase(item)] : "jskit.assistant.attachments.status.queued");
|
|
300
299
|
}
|
|
301
300
|
|
|
302
301
|
function itemCancelable(item = {}) {
|
|
@@ -7,15 +7,15 @@
|
|
|
7
7
|
:aria-busy="state.stopPending ? 'true' : undefined" :prepend-icon="mdiStopCircleOutline"
|
|
8
8
|
size="small" variant="tonal" @click="emit('stop')"
|
|
9
9
|
>
|
|
10
|
-
{{ state.stopPending ? '
|
|
10
|
+
{{ state.stopPending ? t('jskit.assistant.composer.stopping') : t('jskit.assistant.composer.stop') }}
|
|
11
11
|
</v-btn>
|
|
12
12
|
<v-btn
|
|
13
13
|
ref="sendButton" class="assistant-composer-actions__send" color="primary" :disabled="!state.canSend"
|
|
14
|
-
:aria-busy="state.pending && !state.canSend ? 'true' : undefined" :aria-label="state.submitAriaLabel || state.submitLabel || '
|
|
14
|
+
:aria-busy="state.pending && !state.canSend ? 'true' : undefined" :aria-label="state.submitAriaLabel || state.submitLabel || t('jskit.assistant.composer.sendMessage')"
|
|
15
15
|
:title="state.submitTitle" :prepend-icon="state.submitIcon || mdiSend" size="small" variant="flat"
|
|
16
16
|
@click="emit('submit')"
|
|
17
17
|
>
|
|
18
|
-
{{ state.submitLabel || (state.pending && !state.canSend ? '
|
|
18
|
+
{{ state.submitLabel || (state.pending && !state.canSend ? t('jskit.assistant.composer.sending') : t('jskit.assistant.composer.send')) }}
|
|
19
19
|
</v-btn>
|
|
20
20
|
</div>
|
|
21
21
|
<slot name="feedback" />
|
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
<script setup>
|
|
25
25
|
import { ref } from "vue";
|
|
26
26
|
import { mdiSend, mdiStopCircleOutline } from "@mdi/js";
|
|
27
|
+
import { useJskitText } from "../i18n.js";
|
|
28
|
+
|
|
29
|
+
const { t } = useJskitText();
|
|
27
30
|
defineProps({ state: { type: Object, required: true } });
|
|
28
31
|
const emit = defineEmits(["submit", "stop"]);
|
|
29
32
|
const sendButton = ref(null);
|
|
@@ -38,20 +38,20 @@
|
|
|
38
38
|
aria-hidden="true"
|
|
39
39
|
class="assistant-composer-support__loading"
|
|
40
40
|
>
|
|
41
|
-
<span>
|
|
41
|
+
<span>{{ t("jskit.assistant.composer.thinkingOfIdeas") }}</span>
|
|
42
42
|
</div>
|
|
43
43
|
|
|
44
44
|
<div
|
|
45
45
|
v-else
|
|
46
46
|
class="assistant-composer-support__options"
|
|
47
|
-
aria-label="
|
|
47
|
+
:aria-label="t('jskit.assistant.composer.suggestedPrompts')"
|
|
48
48
|
aria-orientation="horizontal"
|
|
49
49
|
role="group"
|
|
50
50
|
>
|
|
51
51
|
<v-btn
|
|
52
52
|
v-for="suggestion in suggestions"
|
|
53
53
|
:key="suggestion.prompt"
|
|
54
|
-
:aria-label="
|
|
54
|
+
:aria-label="t('jskit.assistant.composer.useSuggestion', { prompt: suggestion.prompt })"
|
|
55
55
|
class="assistant-composer-support__option"
|
|
56
56
|
rounded="xl"
|
|
57
57
|
size="small"
|
|
@@ -74,6 +74,9 @@
|
|
|
74
74
|
<script setup>
|
|
75
75
|
import { computed } from "vue";
|
|
76
76
|
import { mdiLightbulbOnOutline } from "@mdi/js";
|
|
77
|
+
import { useJskitText } from "../i18n.js";
|
|
78
|
+
|
|
79
|
+
const { t } = useJskitText();
|
|
77
80
|
|
|
78
81
|
defineEmits(["dismiss", "focusout", "preview", "select"]);
|
|
79
82
|
const props = defineProps({
|
|
@@ -106,10 +109,10 @@ const statusAnnouncement = computed(() => {
|
|
|
106
109
|
return String(props.activity.label || "").trim();
|
|
107
110
|
}
|
|
108
111
|
if (mode.value === "loading") {
|
|
109
|
-
return "
|
|
112
|
+
return t("jskit.assistant.composer.thinkingOfIdeasStatus");
|
|
110
113
|
}
|
|
111
114
|
if (mode.value === "ready") {
|
|
112
|
-
return
|
|
115
|
+
return t("jskit.assistant.composer.suggestionsAvailable", { count: props.suggestions.length });
|
|
113
116
|
}
|
|
114
117
|
return "";
|
|
115
118
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<section ref="container" class="assistant-conversation" :class="{ 'assistant-conversation--error': conversation.error }" :aria-label="label">
|
|
2
|
+
<section ref="container" class="assistant-conversation" :class="{ 'assistant-conversation--error': conversation.error }" :aria-label="label ?? t('jskit.assistant.chat.label')">
|
|
3
3
|
<section
|
|
4
4
|
v-if="$slots.avatar" class="assistant-conversation__avatar"
|
|
5
|
-
:class="{ 'assistant-conversation__avatar--hidden': avatarSize === 'hidden' }" aria-label="
|
|
5
|
+
:class="{ 'assistant-conversation__avatar--hidden': avatarSize === 'hidden' }" :aria-label="t('jskit.assistant.chat.avatar')"
|
|
6
6
|
>
|
|
7
7
|
<div v-show="avatarSize === 'hidden'" ref="avatarControls" class="assistant-conversation__avatar-controls">
|
|
8
8
|
<slot name="avatar-control" :size="avatarSize" :height="avatarHeight" />
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
:icon="mdiAccountCircleOutline" size="small"
|
|
11
11
|
variant="text" :width="44" :min-width="44" :min-height="44"
|
|
12
12
|
class="assistant-conversation__avatar-size"
|
|
13
|
-
aria-label="
|
|
13
|
+
:aria-label="t('jskit.assistant.chat.showAvatar')" :title="t('jskit.assistant.chat.showAvatar')"
|
|
14
14
|
:aria-expanded="avatarSize !== 'hidden'" @click="toggleAvatar"
|
|
15
15
|
>
|
|
16
16
|
<span class="assistant-conversation__avatar-size-disc">
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
<v-btn
|
|
28
28
|
:icon="mdiMinus" size="small" variant="text" color="primary"
|
|
29
29
|
:width="40" :min-width="40" :min-height="44" class="assistant-conversation__avatar-size"
|
|
30
|
-
aria-label="
|
|
30
|
+
:aria-label="t('jskit.assistant.chat.minimiseAvatar')" :title="t('jskit.assistant.chat.minimiseAvatar')" :aria-expanded="true"
|
|
31
31
|
@click="toggleAvatar"
|
|
32
32
|
>
|
|
33
33
|
<span class="assistant-conversation__avatar-size-disc assistant-conversation__avatar-size-disc--minimise">
|
|
@@ -49,13 +49,13 @@
|
|
|
49
49
|
<div v-if="scope.turn.preview && scope.turn.previewMessage?.actions" class="assistant-conversation__preview-actions">
|
|
50
50
|
<v-btn
|
|
51
51
|
v-if="scope.turn.previewMessage.actions.discard" :icon="mdiClose" size="small" variant="text"
|
|
52
|
-
aria-label="
|
|
52
|
+
:aria-label="t('jskit.assistant.chat.discardUnsent')" :title="t('jskit.assistant.chat.discardUnsent')"
|
|
53
53
|
:disabled="!scope.turn.previewMessage.actions.canDiscard"
|
|
54
54
|
@click="scope.turn.previewMessage.actions.discard(scope.turn.turnId)"
|
|
55
55
|
/>
|
|
56
56
|
<v-btn
|
|
57
57
|
v-if="scope.turn.previewMessage.actions.edit" :icon="mdiPencil" size="small" variant="text"
|
|
58
|
-
aria-label="
|
|
58
|
+
:aria-label="t('jskit.assistant.chat.editUnsent')" :title="t('jskit.assistant.chat.editUnsent')"
|
|
59
59
|
:disabled="!scope.turn.previewMessage.actions.canEdit"
|
|
60
60
|
@click="scope.turn.previewMessage.actions.edit(scope.turn.turnId)"
|
|
61
61
|
/>
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
:disabled="!scope.turn.previewMessage.actions.canSend"
|
|
65
65
|
@click="scope.turn.previewMessage.actions.send(scope.turn.turnId)"
|
|
66
66
|
>
|
|
67
|
-
{{ scope.turn.previewMessage.actions.sending ? '
|
|
67
|
+
{{ scope.turn.previewMessage.actions.sending ? t('jskit.assistant.composer.sending') : t('jskit.assistant.composer.send') }}
|
|
68
68
|
</v-btn>
|
|
69
69
|
</div>
|
|
70
70
|
<slot name="user-message-actions" v-bind="scope" />
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
<AssistantPromptInput
|
|
89
89
|
v-if="adapter.composer" ref="input" v-bind="adapter.composer" class="assistant-conversation__composer"
|
|
90
90
|
:model-value="adapter.composer.draft || ''" :submit-enabled="canSend"
|
|
91
|
-
:aria-label="adapter.composer.ariaLabel || '
|
|
91
|
+
:aria-label="adapter.composer.ariaLabel || t('jskit.assistant.chat.messageAssistant')" :rows="adapter.composer.rows || 2"
|
|
92
92
|
:described-by="activity.label || adapter.suggestions?.visible ? statusId : adapter.composer.describedBy"
|
|
93
93
|
:placeholder="suggestionPreview || adapter.composer.placeholder" :placeholder-affects-height="!suggestionPreview"
|
|
94
94
|
:attachment-state="attachmentState" :dragging="attachmentsEnabled && adapter.attachments.dragActive"
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
@select-variant="adapter.models.selectVariant?.($event)" @apply="applyModel" @reload="adapter.models.reload?.()"
|
|
126
126
|
/>
|
|
127
127
|
<v-btn
|
|
128
|
-
v-if="attachmentsEnabled" aria-label="
|
|
128
|
+
v-if="attachmentsEnabled" :aria-label="t('jskit.assistant.chat.attachFiles')" :title="t('jskit.assistant.chat.attachFiles')" size="small" variant="text" :icon="mdiPaperclip"
|
|
129
129
|
:disabled="!adapter.attachments.canAddFiles" @click="fileInput?.click()"
|
|
130
130
|
/>
|
|
131
131
|
<slot
|
|
@@ -163,10 +163,13 @@ import AssistantGoalControl from "./AssistantGoalControl.vue";
|
|
|
163
163
|
import AssistantAttachmentQueue from "./AssistantAttachmentQueue.vue";
|
|
164
164
|
import AssistantQuestionInputs from "./AssistantQuestionInputs.vue";
|
|
165
165
|
import AssistantModelControl from "./AssistantModelControl.vue";
|
|
166
|
+
import { useJskitText } from "../i18n.js";
|
|
167
|
+
|
|
168
|
+
const { t } = useJskitText();
|
|
166
169
|
const props = defineProps({
|
|
167
170
|
adapter: { type: Object, required: true },
|
|
168
171
|
avatarSize: { type: String, default: "compact", validator: (value) => ["hidden", "compact", "standard", "large"].includes(value) },
|
|
169
|
-
label: { type: String, default:
|
|
172
|
+
label: { type: String, default: undefined },
|
|
170
173
|
configuration: { type: Object, default: () => ({}) },
|
|
171
174
|
configurationFields: { type: Array, default: () => [] },
|
|
172
175
|
configurationMode: { type: String, default: "hidden", validator: (value) => ["hidden", "readonly", "editable"].includes(value) }
|
|
@@ -298,8 +301,8 @@ const working = computed(() => conversation.value.working ?? (!pending.value &&
|
|
|
298
301
|
props.adapter.composer?.canStop || conversation.value.turns?.some(turn => turn.pending)
|
|
299
302
|
)));
|
|
300
303
|
const activity = computed(() => props.adapter.activity ?? {
|
|
301
|
-
label: props.adapter.composer?.stopPending ? "
|
|
302
|
-
: pending.value ? "
|
|
304
|
+
label: props.adapter.composer?.stopPending ? t("jskit.assistant.composer.stopping")
|
|
305
|
+
: pending.value ? t("jskit.assistant.chat.sendingToAssistant") : working.value ? t("jskit.assistant.chat.working") : ""
|
|
303
306
|
});
|
|
304
307
|
const suggestionPreview = computed(() => !props.adapter.composer?.draft && props.adapter.suggestions?.visible
|
|
305
308
|
? props.adapter.suggestions.preview : "");
|
|
@@ -364,7 +367,7 @@ async function submit() {
|
|
|
364
367
|
const controller = props.adapter.delivery;
|
|
365
368
|
if (!controller || !props.adapter.actions?.setDraft) return;
|
|
366
369
|
const message = String(props.adapter.composer.draft || "").trim() ||
|
|
367
|
-
(files.length ? props.adapter.composer.attachmentMessage || "
|
|
370
|
+
(files.length ? props.adapter.composer.attachmentMessage || t("jskit.assistant.chat.reviewAttachedFiles") : "");
|
|
368
371
|
if (!message) return;
|
|
369
372
|
const payload = {
|
|
370
373
|
...props.adapter.composer.payload,
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
variant="text"
|
|
6
6
|
color="error"
|
|
7
7
|
size="x-small"
|
|
8
|
-
aria-label="
|
|
8
|
+
:aria-label="t('jskit.assistant.delivery.retryCheck')"
|
|
9
9
|
:disabled="message.delivery.checking"
|
|
10
10
|
@click="$emit('check-delivery', message.delivery.messageId)"
|
|
11
11
|
>
|
|
12
|
-
{{ message.delivery.checking ? '
|
|
12
|
+
{{ message.delivery.checking ? t('jskit.assistant.delivery.checking') : t('jskit.assistant.delivery.retry') }}
|
|
13
13
|
</v-btn>
|
|
14
14
|
</div>
|
|
15
15
|
<slot v-else>
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
|
|
20
20
|
<script setup>
|
|
21
21
|
import LongTextPreviewBlocks from "./LongTextPreviewBlocks.vue";
|
|
22
|
+
import { useJskitText } from "../i18n.js";
|
|
23
|
+
|
|
24
|
+
const { t } = useJskitText();
|
|
22
25
|
|
|
23
26
|
defineProps({ message: { type: Object, required: true } });
|
|
24
27
|
defineEmits(["check-delivery", "link-click"]);
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed, onMounted, onScopeDispose, ref, watch } from "vue";
|
|
3
|
+
import { useJskitText } from "../i18n.js";
|
|
4
|
+
|
|
5
|
+
const { t } = useJskitText();
|
|
6
|
+
const STATUS_KEYS = Object.freeze({
|
|
7
|
+
active: "jskit.assistant.goal.running", paused: "jskit.assistant.goal.paused", blocked: "jskit.assistant.goal.blocked",
|
|
8
|
+
usageLimited: "jskit.assistant.goal.usageLimited", budgetLimited: "jskit.assistant.goal.budgetLimited", complete: "jskit.assistant.goal.complete"
|
|
9
|
+
});
|
|
3
10
|
|
|
4
11
|
const props = defineProps({ state: { type: Object, default: null } });
|
|
5
12
|
const open = ref(false);
|
|
@@ -9,13 +16,12 @@ const now = ref(Date.now());
|
|
|
9
16
|
const goal = computed(() => props.state?.loading || props.state?.available === false ? null : props.state?.goal);
|
|
10
17
|
const running = computed(() => goal.value?.status === "active");
|
|
11
18
|
const working = computed(() => running.value && props.state?.working === true);
|
|
12
|
-
const label = computed(() => props.state?.loading ? "
|
|
13
|
-
|
|
14
|
-
usageLimited: "Goal waiting for allowance", budgetLimited: "Goal budget reached", complete: "Goal complete"
|
|
15
|
-
})[goal.value?.status] || "Set goal");
|
|
19
|
+
const label = computed(() => props.state?.loading ? t("jskit.assistant.goal.loading") : props.state?.available === false ? t("jskit.assistant.goal.unavailable")
|
|
20
|
+
: t(Object.hasOwn(STATUS_KEYS, goal.value?.status ?? "") ? STATUS_KEYS[goal.value.status] : "jskit.assistant.goal.set"));
|
|
16
21
|
const nativeElapsed = computed(() => Number.isFinite(goal.value?.elapsedSeconds));
|
|
17
22
|
const createdAt = computed(() => typeof goal.value?.createdAt === "string" ? Date.parse(goal.value.createdAt) : NaN);
|
|
18
|
-
const elapsedLabel = computed(() => nativeElapsed.value ? "
|
|
23
|
+
const elapsedLabel = computed(() => nativeElapsed.value ? t("jskit.assistant.goal.runningTime") : t("jskit.assistant.goal.elapsedSinceStart"));
|
|
24
|
+
const elapsedText = computed(() => elapsed.value ? t("jskit.assistant.goal.elapsed", { label: elapsedLabel.value, elapsed: elapsed.value }) : "");
|
|
19
25
|
const clockRunning = computed(() => nativeElapsed.value ? running.value && Number.isFinite(goal.value?.sampledAt)
|
|
20
26
|
: Boolean(goal.value && goal.value.status !== "complete" && Number.isFinite(createdAt.value)));
|
|
21
27
|
const elapsed = computed(() => {
|
|
@@ -56,26 +62,26 @@ async function setGoal() {
|
|
|
56
62
|
<template>
|
|
57
63
|
<v-menu v-if="state && state.enabled !== false" v-model="open" location="top" :close-on-content-click="false">
|
|
58
64
|
<template #activator="{ props: menuProps }">
|
|
59
|
-
<v-btn v-bind="menuProps" class="assistant-goal" size="small" variant="text" :aria-label="label" :title="[label,
|
|
65
|
+
<v-btn v-bind="menuProps" class="assistant-goal" size="small" variant="text" :aria-label="label" :title="[label, elapsedText, goal?.objective].filter(Boolean).join('\n')">
|
|
60
66
|
<span v-if="goal" class="assistant-goal__light" :class="{ 'assistant-goal__light--running': running, 'assistant-goal__light--working': working, 'assistant-goal__light--paused': goal.status === 'paused' }" aria-hidden="true" />
|
|
61
|
-
<span class="assistant-goal__label">{{ state.loading ? '
|
|
67
|
+
<span class="assistant-goal__label">{{ state.loading ? t('jskit.assistant.goal.loading') : state.available === false ? t('jskit.assistant.goal.unavailable') : goal ? t('jskit.assistant.goal.goal') : t('jskit.assistant.goal.set') }}</span>
|
|
62
68
|
<span v-if="elapsed" class="assistant-goal__elapsed" aria-hidden="true">{{ elapsed }}</span>
|
|
63
69
|
</v-btn>
|
|
64
70
|
</template>
|
|
65
71
|
<v-card class="assistant-goal__details pa-3" max-width="360">
|
|
66
72
|
<strong role="status">{{ label }}</strong>
|
|
67
|
-
<v-btn v-if="(running || (state.pauseBlocked && goal?.status === 'blocked')) && state.pause" size="small" :disabled="state.pending" @click="state.pause()">{{ state.action === 'pause' ? '
|
|
68
|
-
<v-btn v-if="['paused', 'blocked', 'usageLimited'].includes(goal?.status) && state.resume" size="small" :disabled="state.pending" @click="state.resume()">{{ state.action === 'resume' ? '
|
|
69
|
-
<v-btn v-if="goal && goal.status !== 'complete' && state.cancel" color="error" size="small" variant="text" :disabled="state.pending" @click="state.cancel()">{{ state.action === 'cancel' ? '
|
|
73
|
+
<v-btn v-if="(running || (state.pauseBlocked && goal?.status === 'blocked')) && state.pause" size="small" :disabled="state.pending" @click="state.pause()">{{ state.action === 'pause' ? t('jskit.assistant.goal.pausing') : t('jskit.assistant.goal.pause') }}</v-btn>
|
|
74
|
+
<v-btn v-if="['paused', 'blocked', 'usageLimited'].includes(goal?.status) && state.resume" size="small" :disabled="state.pending" @click="state.resume()">{{ state.action === 'resume' ? t('jskit.assistant.goal.resuming') : t('jskit.assistant.goal.resume') }}</v-btn>
|
|
75
|
+
<v-btn v-if="goal && goal.status !== 'complete' && state.cancel" color="error" size="small" variant="text" :disabled="state.pending" @click="state.cancel()">{{ state.action === 'cancel' ? t('jskit.assistant.goal.cancelling') : t('jskit.assistant.goal.cancel') }}</v-btn>
|
|
70
76
|
<p v-if="goal" class="my-2">{{ goal.objective }}</p>
|
|
71
77
|
<p v-if="goal?.reason" class="text-body-small my-2">{{ goal.reason }}</p>
|
|
72
|
-
<p v-if="elapsed" class="text-body-small my-2">{{
|
|
73
|
-
<p v-if="running || goal?.status === 'paused'" class="text-body-small mt-2">{{ state.pauseInterruptsTurn ? '
|
|
74
|
-
<p v-if="goal && goal.status !== 'complete' && state.cancel" class="text-body-small mt-2">{{ state.cancelInterruptsTurn ? '
|
|
78
|
+
<p v-if="elapsed" class="text-body-small my-2">{{ elapsedText }}</p>
|
|
79
|
+
<p v-if="running || goal?.status === 'paused'" class="text-body-small mt-2">{{ state.pauseInterruptsTurn ? t('jskit.assistant.goal.pauseInterruptsTurn') : t('jskit.assistant.goal.pausePreventsTurns') }}</p>
|
|
80
|
+
<p v-if="goal && goal.status !== 'complete' && state.cancel" class="text-body-small mt-2">{{ state.cancelInterruptsTurn ? t('jskit.assistant.goal.cancelInterruptsTurn') : t('jskit.assistant.goal.cancelRemovesGoal') }}</p>
|
|
75
81
|
<form v-if="canSet" class="assistant-goal__form mt-2" @submit.prevent="setGoal">
|
|
76
|
-
<v-textarea v-model="objective" label="
|
|
77
|
-
<v-text-field v-if="state.tokenBudgetSupported !== false" v-model="tokenBudget" label="
|
|
78
|
-
<v-btn type="submit" size="small" :disabled="state.pending || !objective.trim() || !validBudget">{{ state.action === 'set' ? '
|
|
82
|
+
<v-textarea v-model="objective" :label="t('jskit.assistant.goal.objective')" rows="2" max-rows="4" maxlength="4000" auto-grow :disabled="state.pending" hide-details />
|
|
83
|
+
<v-text-field v-if="state.tokenBudgetSupported !== false" v-model="tokenBudget" :label="t('jskit.assistant.goal.tokenBudget')" type="number" min="1" step="1" :disabled="state.pending" :error="!validBudget" hide-details />
|
|
84
|
+
<v-btn type="submit" size="small" :disabled="state.pending || !objective.trim() || !validBudget">{{ state.action === 'set' ? t('jskit.assistant.goal.starting') : t('jskit.assistant.goal.start') }}</v-btn>
|
|
79
85
|
</form>
|
|
80
86
|
<p v-if="state.error" role="alert" class="text-error text-body-small mt-2">{{ state.error }}</p>
|
|
81
87
|
<slot :goal="goal" />
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<ul
|
|
3
3
|
v-if="attachments.length"
|
|
4
|
-
aria-label="
|
|
4
|
+
:aria-label="t('jskit.assistant.attachments.attached')"
|
|
5
5
|
class="assistant-conversation-attachments"
|
|
6
6
|
>
|
|
7
7
|
<li
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
{{ attachment.reference }} {{ attachment.fileName }}
|
|
30
30
|
</span>
|
|
31
31
|
<span
|
|
32
|
-
v-if="
|
|
32
|
+
v-if="attachmentSizeText(attachment.size)"
|
|
33
33
|
class="assistant-conversation-attachments__size text-label-small"
|
|
34
34
|
>
|
|
35
|
-
{{
|
|
35
|
+
{{ attachmentSizeText(attachment.size) }}
|
|
36
36
|
</span>
|
|
37
37
|
</span>
|
|
38
38
|
</component>
|
|
@@ -42,7 +42,10 @@
|
|
|
42
42
|
|
|
43
43
|
<script setup>
|
|
44
44
|
import { mdiFileOutline, mdiImageOutline } from "@mdi/js";
|
|
45
|
-
import {
|
|
45
|
+
import { useJskitText } from "../i18n.js";
|
|
46
|
+
import { attachmentSizeText } from "./attachmentSize.js";
|
|
47
|
+
|
|
48
|
+
const { t } = useJskitText();
|
|
46
49
|
|
|
47
50
|
defineProps({
|
|
48
51
|
attachments: { type: Array, default: () => [] },
|