@conduction/nextcloud-vue 2.119.0 → 2.120.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/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue.js +9 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue2.js +226 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue3.js +211 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue.js +9 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue2.js +64 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue2.js.map +1 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue3.js +89 -0
- package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue3.js.map +1 -0
- package/dist/esm/components/CnIcon/semanticIcons.js +2 -2
- package/dist/esm/components/CnIcon/semanticIcons.js.map +1 -1
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue.js +9 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue.js.map +1 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue2.js +257 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue2.js.map +1 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue3.js +266 -0
- package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue3.js.map +1 -0
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue.js +1 -1
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js +14 -7
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue.js +9 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue.js.map +1 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue2.js +278 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue3.js +128 -0
- package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue.js +10 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue2.js +944 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue2.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue3.js +276 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue.js +9 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue2.js +354 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue2.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue3.js +392 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue.js +9 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue2.js +180 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue2.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue3.js +208 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue.js +9 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue2.js +244 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue2.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue3.js +76 -0
- package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue3.js.map +1 -0
- package/dist/esm/components/CnStateMachineEditor/stateMachineFields.js +290 -0
- package/dist/esm/components/CnStateMachineEditor/stateMachineFields.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js +9 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js +448 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js +303 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js.map +1 -0
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +19 -2
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +73 -62
- package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +6 -0
- package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +166 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +166 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +889 -101
- package/dist/esm/utils/aiFeatures.js +245 -0
- package/dist/esm/utils/aiFeatures.js.map +1 -0
- package/dist/esm/utils/aiSuggestionTracker.js +182 -0
- package/dist/esm/utils/aiSuggestionTracker.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/esm/utils/objectPermissions.js +552 -0
- package/dist/esm/utils/objectPermissions.js.map +1 -0
- package/dist/esm/utils/objectTranslations.js +180 -0
- package/dist/esm/utils/objectTranslations.js.map +1 -0
- package/dist/esm/utils/stateMachineValidation.js +254 -0
- package/dist/esm/utils/stateMachineValidation.js.map +1 -0
- package/dist/esm/utils/widgetDispatch.js +3 -1
- package/dist/esm/utils/widgetDispatch.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +20052 -13138
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +889 -101
- package/l10n/en.json +166 -1
- package/l10n/nl.json +166 -1
- package/package.json +1 -1
- package/src/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue +416 -0
- package/src/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue +136 -0
- package/src/components/CnAiFeaturesWidget/__tests__/CnAiFeaturesWidget.spec.js +213 -0
- package/src/components/CnAiFeaturesWidget/index.js +14 -0
- package/src/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue +499 -0
- package/src/components/CnObjectPermissionsTab/__tests__/CnObjectPermissionsTab.spec.js +186 -0
- package/src/components/CnObjectPermissionsTab/index.js +13 -0
- package/src/components/CnObjectSidebar/CnObjectSidebar.vue +8 -1
- package/src/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue +424 -0
- package/src/components/CnObjectTranslationsWidget/__tests__/CnObjectTranslationsWidget.spec.js +189 -0
- package/src/components/CnObjectTranslationsWidget/index.js +13 -0
- package/src/components/CnStateMachineEditor/CnStateMachineEditor.vue +1332 -0
- package/src/components/CnStateMachineEditor/CnStateMachineFields.vue +791 -0
- package/src/components/CnStateMachineEditor/CnStateMachineNode.vue +442 -0
- package/src/components/CnStateMachineEditor/CnStateMachinePanel.vue +326 -0
- package/src/components/CnStateMachineEditor/index.js +8 -0
- package/src/components/CnStateMachineEditor/stateMachineFields.js +287 -0
- package/src/components/CnTimelineWidget/CnTimelineManage.vue +827 -0
- package/src/components/CnTimelineWidget/CnTimelineWidget.vue +29 -3
- package/src/components/CnWidgetGrid/builtInWidgets.js +6 -0
- package/src/components/index.js +4 -0
- package/src/index.js +5 -0
- package/src/utils/aiFeatures.js +241 -0
- package/src/utils/aiSuggestionTracker.js +178 -0
- package/src/utils/libraryWidgetKeys.js +1 -0
- package/src/utils/objectPermissions.js +548 -0
- package/src/utils/objectTranslations.js +176 -0
- package/src/utils/stateMachineValidation.js +250 -0
- package/src/utils/widgetDispatch.js +3 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import script from './CnAiFeaturesWidget.vue2.js';
|
|
2
|
+
import { render } from './CnAiFeaturesWidget.vue3.js';
|
|
3
|
+
|
|
4
|
+
script.render = render;
|
|
5
|
+
script.__scopeId = "data-v-c7b3ee28";
|
|
6
|
+
const __sfc_main = script;
|
|
7
|
+
|
|
8
|
+
export { __sfc_main as default };
|
|
9
|
+
//# sourceMappingURL=CnAiFeaturesWidget.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnAiFeaturesWidget.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { translate } from '@nextcloud/l10n';
|
|
2
|
+
import { NcButton, NcLoadingIcon, NcNoteCard, NcSelect } from '@nextcloud/vue';
|
|
3
|
+
import __sfc_main from './CnAiSuggestionDecision.vue.js';
|
|
4
|
+
import { DEFAULT_RUN_ENDPOINT, whereItRuns, fetchFeatures, fetchPrompts, fetchDocuments, askAbout, runFeatureOnDocument, AiFeatureRefusal, refusalSentence } from '../../utils/aiFeatures.js';
|
|
5
|
+
import { createSuggestionTracker } from '../../utils/aiSuggestionTracker.js';
|
|
6
|
+
|
|
7
|
+
var script = {
|
|
8
|
+
name: 'CnAiFeaturesWidget',
|
|
9
|
+
components: { CnAiSuggestionDecision: __sfc_main, NcButton, NcLoadingIcon, NcNoteCard, NcSelect },
|
|
10
|
+
|
|
11
|
+
props: {
|
|
12
|
+
/** The record id (injected by CnObjectSidebar). */
|
|
13
|
+
objectId: { type: String, default: '' },
|
|
14
|
+
/** Register slug or id (injected by CnObjectSidebar). */
|
|
15
|
+
register: { type: String, default: '' },
|
|
16
|
+
/** Schema slug or id (injected by CnObjectSidebar). */
|
|
17
|
+
schema: { type: String, default: '' },
|
|
18
|
+
/** OpenRegister API root. */
|
|
19
|
+
apiBase: { type: String, default: '/apps/openregister/api' },
|
|
20
|
+
/**
|
|
21
|
+
* Where the features are declared: the record's `typeField` references
|
|
22
|
+
* a `typeSchema` object whose `field` maps feature slug to surface;
|
|
23
|
+
* the slugs placed on `surface` are shown.
|
|
24
|
+
*/
|
|
25
|
+
declaration: {
|
|
26
|
+
type: Object,
|
|
27
|
+
default: () => ({ typeField: '', typeSchema: '', field: 'aiFeatures', surface: '' }),
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
/** The endpoint that answers where each declared feature runs (`{features: [...]}`). */
|
|
31
|
+
featuresEndpoint: { type: String, default: '' },
|
|
32
|
+
/** Query key the declared slugs are sent under: `<key>[<slug>]=<surface>`. */
|
|
33
|
+
featuresQueryKey: { type: String, default: 'aiFeatures' },
|
|
34
|
+
/** The record's documents; empty uses OpenRegister's files of the record. */
|
|
35
|
+
filesEndpoint: { type: String, default: '' },
|
|
36
|
+
/** hermiq's run endpoint, with `{slug}`. */
|
|
37
|
+
runEndpoint: { type: String, default: DEFAULT_RUN_ENDPOINT },
|
|
38
|
+
/** Prompts offered as questions (`{prompts: [...]}`); empty offers none. */
|
|
39
|
+
promptsEndpoint: { type: String, default: '' },
|
|
40
|
+
/** Where a question is posted (`{<objectKey>: id, message}` → `{reply}`). */
|
|
41
|
+
askEndpoint: { type: String, default: '' },
|
|
42
|
+
/** The AI oversight record-action endpoint; empty reports nothing. */
|
|
43
|
+
decisionEndpoint: { type: String, default: '' },
|
|
44
|
+
/** Body key naming the record in the ask and decision bodies. */
|
|
45
|
+
objectKey: { type: String, default: 'objectId' },
|
|
46
|
+
/** Heading; empty uses "Assistant". */
|
|
47
|
+
title: { type: String, default: '' },
|
|
48
|
+
/** Whether the heading is drawn. */
|
|
49
|
+
showTitle: { type: Boolean, default: true },
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
data() {
|
|
53
|
+
return {
|
|
54
|
+
loading: true,
|
|
55
|
+
features: [],
|
|
56
|
+
documents: [],
|
|
57
|
+
runs: {},
|
|
58
|
+
prompts: [],
|
|
59
|
+
asking: false,
|
|
60
|
+
answer: '',
|
|
61
|
+
answerFailed: '',
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
computed: {
|
|
66
|
+
documentOptions() {
|
|
67
|
+
return (this.documents || []).map((doc) => ({ id: doc.id, label: doc.title }))
|
|
68
|
+
},
|
|
69
|
+
|
|
70
|
+
resolvedFilesEndpoint() {
|
|
71
|
+
return this.filesEndpoint
|
|
72
|
+
|| `${this.apiBase.replace(/\/$/, '')}/objects/${this.register}/${this.schema}/${encodeURIComponent(this.objectId)}/files`
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
created() {
|
|
77
|
+
this.tracker = createSuggestionTracker({ objectId: this.objectId, objectKey: this.objectKey, endpoint: this.decisionEndpoint });
|
|
78
|
+
this.onPageHide = () => this.tracker.closeAll({ leaving: true });
|
|
79
|
+
window.addEventListener('pagehide', this.onPageHide);
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
mounted() {
|
|
83
|
+
this.load();
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
beforeUnmount() {
|
|
87
|
+
window.removeEventListener('pagehide', this.onPageHide);
|
|
88
|
+
this.tracker.closeAll();
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
methods: {
|
|
92
|
+
t: translate,
|
|
93
|
+
whereItRuns,
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Read the features, the prompts and (when a feature can run) the documents.
|
|
97
|
+
*
|
|
98
|
+
* @return {Promise<void>}
|
|
99
|
+
*/
|
|
100
|
+
async load() {
|
|
101
|
+
this.loading = true;
|
|
102
|
+
if (this.objectId === '' || !this.featuresEndpoint) {
|
|
103
|
+
this.features = null;
|
|
104
|
+
this.loading = false;
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const [features, prompts] = await Promise.all([
|
|
109
|
+
fetchFeatures({
|
|
110
|
+
apiBase: this.apiBase.replace(/\/$/, ''),
|
|
111
|
+
register: this.register,
|
|
112
|
+
schema: this.schema,
|
|
113
|
+
objectId: this.objectId,
|
|
114
|
+
declaration: this.declaration,
|
|
115
|
+
featuresEndpoint: this.featuresEndpoint,
|
|
116
|
+
featuresQueryKey: this.featuresQueryKey,
|
|
117
|
+
}),
|
|
118
|
+
this.promptsEndpoint ? fetchPrompts(this.promptsEndpoint) : [],
|
|
119
|
+
]);
|
|
120
|
+
this.features = features;
|
|
121
|
+
this.prompts = prompts || [];
|
|
122
|
+
|
|
123
|
+
const runs = {};
|
|
124
|
+
for (const feature of features || []) {
|
|
125
|
+
runs[feature.slug] = { documentReference: '', instruction: '', busy: false, refusal: null, failed: false, output: '' };
|
|
126
|
+
}
|
|
127
|
+
this.runs = runs;
|
|
128
|
+
|
|
129
|
+
const offersARun = (features || []).some((feature) => feature.available === true);
|
|
130
|
+
this.documents = offersARun ? await fetchDocuments(this.resolvedFilesEndpoint) : [];
|
|
131
|
+
this.loading = false;
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
selectedDocument(feature) {
|
|
135
|
+
const id = this.runs[feature.slug]?.documentReference;
|
|
136
|
+
return this.documentOptions.find((option) => option.id === id) || null
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
chooseDocument(feature, option) {
|
|
140
|
+
this.runs[feature.slug].documentReference = option ? String(option.id) : '';
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Ask one of the offered prompts about this record.
|
|
145
|
+
*
|
|
146
|
+
* @param {object} prompt The prompt.
|
|
147
|
+
* @return {Promise<void>}
|
|
148
|
+
*/
|
|
149
|
+
async ask(prompt) {
|
|
150
|
+
this.tracker.dismiss('question', 'superseded');
|
|
151
|
+
this.asking = true;
|
|
152
|
+
this.answer = '';
|
|
153
|
+
this.answerFailed = '';
|
|
154
|
+
try {
|
|
155
|
+
const result = await askAbout(this.askEndpoint, this.objectKey, this.objectId, prompt.prompt);
|
|
156
|
+
this.answer = String(result?.reply ?? '');
|
|
157
|
+
if (this.answer !== '') {
|
|
158
|
+
this.tracker.show('question', { type: 'qa', text: this.answer, prompt: prompt.prompt, model: String(result?.model ?? '') });
|
|
159
|
+
}
|
|
160
|
+
} catch (error) {
|
|
161
|
+
this.answerFailed = String(error?.response?.data?.error ?? '') || translate('nextcloud-vue', 'The assistant did not answer.');
|
|
162
|
+
} finally {
|
|
163
|
+
this.asking = false;
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Report what the person did with the suggestion in a slot.
|
|
169
|
+
*
|
|
170
|
+
* @param {string} slot The slot key.
|
|
171
|
+
* @param {object} decision `{userAction, actualValue?}`.
|
|
172
|
+
* @return {Promise<void>}
|
|
173
|
+
*/
|
|
174
|
+
decide(slot, decision) {
|
|
175
|
+
return this.tracker.decide(slot, decision.userAction, { actualValue: decision.actualValue })
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
fieldId(feature, field) {
|
|
179
|
+
return `cn-ai-${this.objectId || 'unknown'}-${feature.slug}-${field}`
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Run one feature on the chosen document, and show what came back.
|
|
184
|
+
*
|
|
185
|
+
* @param {object} feature The feature.
|
|
186
|
+
* @return {Promise<void>}
|
|
187
|
+
*/
|
|
188
|
+
async run(feature) {
|
|
189
|
+
const state = this.runs[feature.slug];
|
|
190
|
+
this.tracker.dismiss(`feature:${feature.slug}`, 'superseded');
|
|
191
|
+
state.busy = true;
|
|
192
|
+
state.refusal = null;
|
|
193
|
+
state.failed = false;
|
|
194
|
+
state.output = '';
|
|
195
|
+
try {
|
|
196
|
+
const answer = await runFeatureOnDocument({
|
|
197
|
+
feature: feature.slug,
|
|
198
|
+
documentReference: state.documentReference,
|
|
199
|
+
instruction: state.instruction,
|
|
200
|
+
runEndpoint: this.runEndpoint,
|
|
201
|
+
});
|
|
202
|
+
state.output = String(answer?.output ?? '');
|
|
203
|
+
if (state.output !== '') {
|
|
204
|
+
this.tracker.show(`feature:${feature.slug}`, {
|
|
205
|
+
type: 'decision_support',
|
|
206
|
+
text: state.output,
|
|
207
|
+
prompt: state.instruction,
|
|
208
|
+
model: String(answer?.model ?? ''),
|
|
209
|
+
documentId: state.documentReference,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
} catch (error) {
|
|
213
|
+
if (error instanceof AiFeatureRefusal) {
|
|
214
|
+
state.refusal = { sentence: refusalSentence(error, feature.slug), gate: error.gate };
|
|
215
|
+
} else {
|
|
216
|
+
state.failed = true;
|
|
217
|
+
}
|
|
218
|
+
} finally {
|
|
219
|
+
state.busy = false;
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export { script as default };
|
|
226
|
+
//# sourceMappingURL=CnAiFeaturesWidget.vue2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnAiFeaturesWidget.vue2.js","sources":["../../../../src/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n\n CnAiFeaturesWidget — the hermiq AI features a record's type switches on for\n a surface: where each one runs (hermiq's answer; a feature hermiq does not\n offer reads unavailable, never local), a run on one of the record's\n documents with a refusal shown in words naming the feature, the reason and\n hermiq's gate, optional question prompts, and a use / adapt / reject\n decision on every suggestion for the AI oversight trail.\n-->\n<template>\n\t<div class=\"cn-ai-features\" data-testid=\"cn-ai-features\">\n\t\t<h3 v-if=\"showTitle\">\n\t\t\t{{ title || t('nextcloud-vue', 'Assistant') }}\n\t\t</h3>\n\n\t\t<div v-if=\"loading\" class=\"cn-ai-features__loading\">\n\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t{{ t('nextcloud-vue', 'Reading the AI features on this record') }}\n\t\t</div>\n\n\t\t<template v-else>\n\t\t\t<section v-if=\"prompts && prompts.length > 0\"\n\t\t\t\tclass=\"cn-ai-features__questions\"\n\t\t\t\tdata-testid=\"cn-ai-questions\">\n\t\t\t\t<h4>{{ t('nextcloud-vue', 'Questions') }}</h4>\n\t\t\t\t<ul class=\"cn-ai-features__prompts\">\n\t\t\t\t\t<li v-for=\"prompt in prompts\" :key=\"prompt.id || prompt.label\">\n\t\t\t\t\t\t<NcButton :disabled=\"asking\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-ai-question-${prompt.id}`\"\n\t\t\t\t\t\t\t@click=\"ask(prompt)\">\n\t\t\t\t\t\t\t{{ prompt.label }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"answer\" class=\"cn-ai-features__output\" data-testid=\"cn-ai-question-answer\">\n\t\t\t\t\t{{ answer }}\n\t\t\t\t</div>\n\t\t\t\t<CnAiSuggestionDecision v-if=\"answer\"\n\t\t\t\t\t:text=\"answer\"\n\t\t\t\t\ttestid=\"cn-ai-question-decision\"\n\t\t\t\t\t@decide=\"decide('question', $event)\" />\n\t\t\t\t<p v-if=\"answerFailed\" class=\"cn-ai-features__unreadable\" data-testid=\"cn-ai-question-failed\">\n\t\t\t\t\t{{ answerFailed }}\n\t\t\t\t</p>\n\t\t\t</section>\n\n\t\t\t<p v-if=\"features === null\" class=\"cn-ai-features__unreadable\" data-testid=\"cn-ai-features-unreadable\">\n\t\t\t\t{{ t('nextcloud-vue', 'The AI features on this record could not be read. This panel is incomplete, not empty.') }}\n\t\t\t</p>\n\n\t\t\t<p v-else-if=\"features.length === 0\" class=\"cn-ai-features__hint\" data-testid=\"cn-ai-features-empty\">\n\t\t\t\t{{ t('nextcloud-vue', 'The type of this record switches on no AI feature here.') }}\n\t\t\t</p>\n\n\t\t\t<template v-else>\n\t\t\t\t<section v-for=\"feature in features\"\n\t\t\t\t\t:key=\"feature.slug\"\n\t\t\t\t\tclass=\"cn-ai-features__feature\"\n\t\t\t\t\t:data-testid=\"`cn-ai-feature-${feature.slug}`\">\n\t\t\t\t\t<h4>{{ feature.slug }}</h4>\n\t\t\t\t\t<p class=\"cn-ai-features__hint\" :data-testid=\"`cn-ai-feature-where-${feature.slug}`\">\n\t\t\t\t\t\t{{ whereItRuns(feature) }}\n\t\t\t\t\t</p>\n\n\t\t\t\t\t<form v-if=\"feature.available === true\"\n\t\t\t\t\t\tclass=\"cn-ai-features__run\"\n\t\t\t\t\t\t@submit.prevent=\"run(feature)\">\n\t\t\t\t\t\t<NcSelect :inputId=\"fieldId(feature, 'document')\"\n\t\t\t\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Document')\"\n\t\t\t\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Choose a document')\"\n\t\t\t\t\t\t\t:options=\"documentOptions\"\n\t\t\t\t\t\t\t:modelValue=\"selectedDocument(feature)\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-ai-feature-document-${feature.slug}`\"\n\t\t\t\t\t\t\t@update:modelValue=\"chooseDocument(feature, $event)\" />\n\t\t\t\t\t\t<p v-if=\"documents === null\" class=\"cn-ai-features__unreadable\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'The documents on this record could not be read.') }}\n\t\t\t\t\t\t</p>\n\n\t\t\t\t\t\t<label :for=\"fieldId(feature, 'instruction')\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'What should it do with the document?') }}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea :id=\"fieldId(feature, 'instruction')\"\n\t\t\t\t\t\t\tv-model=\"runs[feature.slug].instruction\"\n\t\t\t\t\t\t\trows=\"2\" />\n\n\t\t\t\t\t\t<NcButton type=\"submit\"\n\t\t\t\t\t\t\t:disabled=\"runs[feature.slug].busy\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-ai-feature-run-${feature.slug}`\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Run') }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</form>\n\n\t\t\t\t\t<NcNoteCard v-if=\"runs[feature.slug] && runs[feature.slug].refusal\"\n\t\t\t\t\t\ttype=\"warning\"\n\t\t\t\t\t\t:data-testid=\"`cn-ai-feature-refusal-${feature.slug}`\">\n\t\t\t\t\t\t<p>{{ runs[feature.slug].refusal.sentence }}</p>\n\t\t\t\t\t\t<p v-if=\"runs[feature.slug].refusal.gate\" class=\"cn-ai-features__gate\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Gate: {gate}', { gate: runs[feature.slug].refusal.gate }) }}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</NcNoteCard>\n\n\t\t\t\t\t<p v-if=\"runs[feature.slug] && runs[feature.slug].failed\"\n\t\t\t\t\t\tclass=\"cn-ai-features__unreadable\"\n\t\t\t\t\t\t:data-testid=\"`cn-ai-feature-failed-${feature.slug}`\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'hermiq could not be reached. Nothing was run.') }}\n\t\t\t\t\t</p>\n\n\t\t\t\t\t<div v-if=\"runs[feature.slug] && runs[feature.slug].output\"\n\t\t\t\t\t\tclass=\"cn-ai-features__output\"\n\t\t\t\t\t\t:data-testid=\"`cn-ai-feature-output-${feature.slug}`\">\n\t\t\t\t\t\t{{ runs[feature.slug].output }}\n\t\t\t\t\t</div>\n\t\t\t\t\t<CnAiSuggestionDecision v-if=\"runs[feature.slug] && runs[feature.slug].output\"\n\t\t\t\t\t\t:text=\"runs[feature.slug].output\"\n\t\t\t\t\t\t:testid=\"`cn-ai-feature-decision-${feature.slug}`\"\n\t\t\t\t\t\t@decide=\"decide(`feature:${feature.slug}`, $event)\" />\n\t\t\t\t</section>\n\n\t\t\t\t<p class=\"cn-ai-features__hint\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'The assistant does nothing without your confirmation. It sees only what you see on this record.') }}\n\t\t\t\t</p>\n\t\t\t</template>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcLoadingIcon, NcNoteCard, NcSelect } from '@nextcloud/vue'\nimport CnAiSuggestionDecision from './CnAiSuggestionDecision.vue'\nimport {\n\tAiFeatureRefusal,\n\taskAbout,\n\tDEFAULT_RUN_ENDPOINT,\n\tfetchDocuments,\n\tfetchFeatures,\n\tfetchPrompts,\n\trefusalSentence,\n\trunFeatureOnDocument,\n\twhereItRuns,\n} from '../../utils/aiFeatures.js'\nimport { createSuggestionTracker } from '../../utils/aiSuggestionTracker.js'\n\nexport default {\n\tname: 'CnAiFeaturesWidget',\n\tcomponents: { CnAiSuggestionDecision, NcButton, NcLoadingIcon, NcNoteCard, NcSelect },\n\n\tprops: {\n\t\t/** The record id (injected by CnObjectSidebar). */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** Register slug or id (injected by CnObjectSidebar). */\n\t\tregister: { type: String, default: '' },\n\t\t/** Schema slug or id (injected by CnObjectSidebar). */\n\t\tschema: { type: String, default: '' },\n\t\t/** OpenRegister API root. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/**\n\t\t * Where the features are declared: the record's `typeField` references\n\t\t * a `typeSchema` object whose `field` maps feature slug to surface;\n\t\t * the slugs placed on `surface` are shown.\n\t\t */\n\t\tdeclaration: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({ typeField: '', typeSchema: '', field: 'aiFeatures', surface: '' }),\n\t\t},\n\n\t\t/** The endpoint that answers where each declared feature runs (`{features: [...]}`). */\n\t\tfeaturesEndpoint: { type: String, default: '' },\n\t\t/** Query key the declared slugs are sent under: `<key>[<slug>]=<surface>`. */\n\t\tfeaturesQueryKey: { type: String, default: 'aiFeatures' },\n\t\t/** The record's documents; empty uses OpenRegister's files of the record. */\n\t\tfilesEndpoint: { type: String, default: '' },\n\t\t/** hermiq's run endpoint, with `{slug}`. */\n\t\trunEndpoint: { type: String, default: DEFAULT_RUN_ENDPOINT },\n\t\t/** Prompts offered as questions (`{prompts: [...]}`); empty offers none. */\n\t\tpromptsEndpoint: { type: String, default: '' },\n\t\t/** Where a question is posted (`{<objectKey>: id, message}` → `{reply}`). */\n\t\taskEndpoint: { type: String, default: '' },\n\t\t/** The AI oversight record-action endpoint; empty reports nothing. */\n\t\tdecisionEndpoint: { type: String, default: '' },\n\t\t/** Body key naming the record in the ask and decision bodies. */\n\t\tobjectKey: { type: String, default: 'objectId' },\n\t\t/** Heading; empty uses \"Assistant\". */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Whether the heading is drawn. */\n\t\tshowTitle: { type: Boolean, default: true },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: true,\n\t\t\tfeatures: [],\n\t\t\tdocuments: [],\n\t\t\truns: {},\n\t\t\tprompts: [],\n\t\t\tasking: false,\n\t\t\tanswer: '',\n\t\t\tanswerFailed: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tdocumentOptions() {\n\t\t\treturn (this.documents || []).map((doc) => ({ id: doc.id, label: doc.title }))\n\t\t},\n\n\t\tresolvedFilesEndpoint() {\n\t\t\treturn this.filesEndpoint\n\t\t\t\t|| `${this.apiBase.replace(/\\/$/, '')}/objects/${this.register}/${this.schema}/${encodeURIComponent(this.objectId)}/files`\n\t\t},\n\t},\n\n\tcreated() {\n\t\tthis.tracker = createSuggestionTracker({ objectId: this.objectId, objectKey: this.objectKey, endpoint: this.decisionEndpoint })\n\t\tthis.onPageHide = () => this.tracker.closeAll({ leaving: true })\n\t\twindow.addEventListener('pagehide', this.onPageHide)\n\t},\n\n\tmounted() {\n\t\tthis.load()\n\t},\n\n\tbeforeUnmount() {\n\t\twindow.removeEventListener('pagehide', this.onPageHide)\n\t\tthis.tracker.closeAll()\n\t},\n\n\tmethods: {\n\t\tt,\n\t\twhereItRuns,\n\n\t\t/**\n\t\t * Read the features, the prompts and (when a feature can run) the documents.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tif (this.objectId === '' || !this.featuresEndpoint) {\n\t\t\t\tthis.features = null\n\t\t\t\tthis.loading = false\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst [features, prompts] = await Promise.all([\n\t\t\t\tfetchFeatures({\n\t\t\t\t\tapiBase: this.apiBase.replace(/\\/$/, ''),\n\t\t\t\t\tregister: this.register,\n\t\t\t\t\tschema: this.schema,\n\t\t\t\t\tobjectId: this.objectId,\n\t\t\t\t\tdeclaration: this.declaration,\n\t\t\t\t\tfeaturesEndpoint: this.featuresEndpoint,\n\t\t\t\t\tfeaturesQueryKey: this.featuresQueryKey,\n\t\t\t\t}),\n\t\t\t\tthis.promptsEndpoint ? fetchPrompts(this.promptsEndpoint) : [],\n\t\t\t])\n\t\t\tthis.features = features\n\t\t\tthis.prompts = prompts || []\n\n\t\t\tconst runs = {}\n\t\t\tfor (const feature of features || []) {\n\t\t\t\truns[feature.slug] = { documentReference: '', instruction: '', busy: false, refusal: null, failed: false, output: '' }\n\t\t\t}\n\t\t\tthis.runs = runs\n\n\t\t\tconst offersARun = (features || []).some((feature) => feature.available === true)\n\t\t\tthis.documents = offersARun ? await fetchDocuments(this.resolvedFilesEndpoint) : []\n\t\t\tthis.loading = false\n\t\t},\n\n\t\tselectedDocument(feature) {\n\t\t\tconst id = this.runs[feature.slug]?.documentReference\n\t\t\treturn this.documentOptions.find((option) => option.id === id) || null\n\t\t},\n\n\t\tchooseDocument(feature, option) {\n\t\t\tthis.runs[feature.slug].documentReference = option ? String(option.id) : ''\n\t\t},\n\n\t\t/**\n\t\t * Ask one of the offered prompts about this record.\n\t\t *\n\t\t * @param {object} prompt The prompt.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync ask(prompt) {\n\t\t\tthis.tracker.dismiss('question', 'superseded')\n\t\t\tthis.asking = true\n\t\t\tthis.answer = ''\n\t\t\tthis.answerFailed = ''\n\t\t\ttry {\n\t\t\t\tconst result = await askAbout(this.askEndpoint, this.objectKey, this.objectId, prompt.prompt)\n\t\t\t\tthis.answer = String(result?.reply ?? '')\n\t\t\t\tif (this.answer !== '') {\n\t\t\t\t\tthis.tracker.show('question', { type: 'qa', text: this.answer, prompt: prompt.prompt, model: String(result?.model ?? '') })\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthis.answerFailed = String(error?.response?.data?.error ?? '') || t('nextcloud-vue', 'The assistant did not answer.')\n\t\t\t} finally {\n\t\t\t\tthis.asking = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Report what the person did with the suggestion in a slot.\n\t\t *\n\t\t * @param {string} slot The slot key.\n\t\t * @param {object} decision `{userAction, actualValue?}`.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tdecide(slot, decision) {\n\t\t\treturn this.tracker.decide(slot, decision.userAction, { actualValue: decision.actualValue })\n\t\t},\n\n\t\tfieldId(feature, field) {\n\t\t\treturn `cn-ai-${this.objectId || 'unknown'}-${feature.slug}-${field}`\n\t\t},\n\n\t\t/**\n\t\t * Run one feature on the chosen document, and show what came back.\n\t\t *\n\t\t * @param {object} feature The feature.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync run(feature) {\n\t\t\tconst state = this.runs[feature.slug]\n\t\t\tthis.tracker.dismiss(`feature:${feature.slug}`, 'superseded')\n\t\t\tstate.busy = true\n\t\t\tstate.refusal = null\n\t\t\tstate.failed = false\n\t\t\tstate.output = ''\n\t\t\ttry {\n\t\t\t\tconst answer = await runFeatureOnDocument({\n\t\t\t\t\tfeature: feature.slug,\n\t\t\t\t\tdocumentReference: state.documentReference,\n\t\t\t\t\tinstruction: state.instruction,\n\t\t\t\t\trunEndpoint: this.runEndpoint,\n\t\t\t\t})\n\t\t\t\tstate.output = String(answer?.output ?? '')\n\t\t\t\tif (state.output !== '') {\n\t\t\t\t\tthis.tracker.show(`feature:${feature.slug}`, {\n\t\t\t\t\t\ttype: 'decision_support',\n\t\t\t\t\t\ttext: state.output,\n\t\t\t\t\t\tprompt: state.instruction,\n\t\t\t\t\t\tmodel: String(answer?.model ?? ''),\n\t\t\t\t\t\tdocumentId: state.documentReference,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof AiFeatureRefusal) {\n\t\t\t\t\tstate.refusal = { sentence: refusalSentence(error, feature.slug), gate: error.gate }\n\t\t\t\t} else {\n\t\t\t\t\tstate.failed = true\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tstate.busy = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-ai-features {\n\tpadding: 12px;\n}\n\n.cn-ai-features__loading {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-ai-features__feature,\n.cn-ai-features__questions {\n\tmargin-bottom: 16px;\n\tpadding-bottom: 12px;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-ai-features__prompts {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px;\n\tlist-style: none;\n\tmargin: 0 0 8px;\n\tpadding: 0;\n}\n\n.cn-ai-features__run {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tmargin: 8px 0;\n}\n\n.cn-ai-features__hint,\n.cn-ai-features__gate {\n\tdisplay: block;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n}\n\n.cn-ai-features__output {\n\twhite-space: pre-wrap;\n\tpadding: 8px;\n\tbackground: var(--color-background-dark);\n\tborder-radius: var(--border-radius);\n}\n\n.cn-ai-features__unreadable {\n\tcolor: var(--color-warning-text, var(--color-main-text));\n}\n</style>\n"],"names":["CnAiSuggestionDecision","t"],"mappings":"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"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { resolveComponent, openBlock, createElementBlock, toDisplayString, createCommentVNode, createVNode, createTextVNode, Fragment, createElementVNode, renderList, withCtx, createBlock, withModifiers, withDirectives, vModelText } from 'vue';
|
|
2
|
+
|
|
3
|
+
const _hoisted_1 = {
|
|
4
|
+
class: "cn-ai-features",
|
|
5
|
+
"data-testid": "cn-ai-features"
|
|
6
|
+
};
|
|
7
|
+
const _hoisted_2 = { key: 0 };
|
|
8
|
+
const _hoisted_3 = {
|
|
9
|
+
key: 1,
|
|
10
|
+
class: "cn-ai-features__loading"
|
|
11
|
+
};
|
|
12
|
+
const _hoisted_4 = {
|
|
13
|
+
key: 0,
|
|
14
|
+
class: "cn-ai-features__questions",
|
|
15
|
+
"data-testid": "cn-ai-questions"
|
|
16
|
+
};
|
|
17
|
+
const _hoisted_5 = { class: "cn-ai-features__prompts" };
|
|
18
|
+
const _hoisted_6 = {
|
|
19
|
+
key: 0,
|
|
20
|
+
class: "cn-ai-features__output",
|
|
21
|
+
"data-testid": "cn-ai-question-answer"
|
|
22
|
+
};
|
|
23
|
+
const _hoisted_7 = {
|
|
24
|
+
key: 2,
|
|
25
|
+
class: "cn-ai-features__unreadable",
|
|
26
|
+
"data-testid": "cn-ai-question-failed"
|
|
27
|
+
};
|
|
28
|
+
const _hoisted_8 = {
|
|
29
|
+
key: 1,
|
|
30
|
+
class: "cn-ai-features__unreadable",
|
|
31
|
+
"data-testid": "cn-ai-features-unreadable"
|
|
32
|
+
};
|
|
33
|
+
const _hoisted_9 = {
|
|
34
|
+
key: 2,
|
|
35
|
+
class: "cn-ai-features__hint",
|
|
36
|
+
"data-testid": "cn-ai-features-empty"
|
|
37
|
+
};
|
|
38
|
+
const _hoisted_10 = ["data-testid"];
|
|
39
|
+
const _hoisted_11 = ["data-testid"];
|
|
40
|
+
const _hoisted_12 = ["onSubmit"];
|
|
41
|
+
const _hoisted_13 = {
|
|
42
|
+
key: 0,
|
|
43
|
+
class: "cn-ai-features__unreadable"
|
|
44
|
+
};
|
|
45
|
+
const _hoisted_14 = ["for"];
|
|
46
|
+
const _hoisted_15 = ["id", "onUpdate:modelValue"];
|
|
47
|
+
const _hoisted_16 = {
|
|
48
|
+
key: 0,
|
|
49
|
+
class: "cn-ai-features__gate"
|
|
50
|
+
};
|
|
51
|
+
const _hoisted_17 = ["data-testid"];
|
|
52
|
+
const _hoisted_18 = ["data-testid"];
|
|
53
|
+
const _hoisted_19 = { class: "cn-ai-features__hint" };
|
|
54
|
+
|
|
55
|
+
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
56
|
+
const _component_NcLoadingIcon = resolveComponent("NcLoadingIcon");
|
|
57
|
+
const _component_NcButton = resolveComponent("NcButton");
|
|
58
|
+
const _component_CnAiSuggestionDecision = resolveComponent("CnAiSuggestionDecision");
|
|
59
|
+
const _component_NcSelect = resolveComponent("NcSelect");
|
|
60
|
+
const _component_NcNoteCard = resolveComponent("NcNoteCard");
|
|
61
|
+
|
|
62
|
+
return (openBlock(), createElementBlock("div", _hoisted_1, [
|
|
63
|
+
($props.showTitle)
|
|
64
|
+
? (openBlock(), createElementBlock("h3", _hoisted_2, toDisplayString($props.title || $options.t('nextcloud-vue', 'Assistant')), 1 /* TEXT */))
|
|
65
|
+
: createCommentVNode("v-if", true),
|
|
66
|
+
($data.loading)
|
|
67
|
+
? (openBlock(), createElementBlock("div", _hoisted_3, [
|
|
68
|
+
createVNode(_component_NcLoadingIcon, { size: 20 }),
|
|
69
|
+
createTextVNode(" " + toDisplayString($options.t('nextcloud-vue', 'Reading the AI features on this record')), 1 /* TEXT */)
|
|
70
|
+
]))
|
|
71
|
+
: (openBlock(), createElementBlock(Fragment, { key: 2 }, [
|
|
72
|
+
($data.prompts && $data.prompts.length > 0)
|
|
73
|
+
? (openBlock(), createElementBlock("section", _hoisted_4, [
|
|
74
|
+
createElementVNode("h4", null, toDisplayString($options.t('nextcloud-vue', 'Questions')), 1 /* TEXT */),
|
|
75
|
+
createElementVNode("ul", _hoisted_5, [
|
|
76
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList($data.prompts, (prompt) => {
|
|
77
|
+
return (openBlock(), createElementBlock("li", {
|
|
78
|
+
key: prompt.id || prompt.label
|
|
79
|
+
}, [
|
|
80
|
+
createVNode(_component_NcButton, {
|
|
81
|
+
disabled: $data.asking,
|
|
82
|
+
"data-testid": `cn-ai-question-${prompt.id}`,
|
|
83
|
+
onClick: $event => ($options.ask(prompt))
|
|
84
|
+
}, {
|
|
85
|
+
default: withCtx(() => [
|
|
86
|
+
createTextVNode(toDisplayString(prompt.label), 1 /* TEXT */)
|
|
87
|
+
]),
|
|
88
|
+
_: 2 /* DYNAMIC */
|
|
89
|
+
}, 1032 /* PROPS, DYNAMIC_SLOTS */, ["disabled", "data-testid", "onClick"])
|
|
90
|
+
]))
|
|
91
|
+
}), 128 /* KEYED_FRAGMENT */))
|
|
92
|
+
]),
|
|
93
|
+
($data.answer)
|
|
94
|
+
? (openBlock(), createElementBlock("div", _hoisted_6, toDisplayString($data.answer), 1 /* TEXT */))
|
|
95
|
+
: createCommentVNode("v-if", true),
|
|
96
|
+
($data.answer)
|
|
97
|
+
? (openBlock(), createBlock(_component_CnAiSuggestionDecision, {
|
|
98
|
+
key: 1,
|
|
99
|
+
text: $data.answer,
|
|
100
|
+
testid: "cn-ai-question-decision",
|
|
101
|
+
onDecide: _cache[0] || (_cache[0] = $event => ($options.decide('question', $event)))
|
|
102
|
+
}, null, 8 /* PROPS */, ["text"]))
|
|
103
|
+
: createCommentVNode("v-if", true),
|
|
104
|
+
($data.answerFailed)
|
|
105
|
+
? (openBlock(), createElementBlock("p", _hoisted_7, toDisplayString($data.answerFailed), 1 /* TEXT */))
|
|
106
|
+
: createCommentVNode("v-if", true)
|
|
107
|
+
]))
|
|
108
|
+
: createCommentVNode("v-if", true),
|
|
109
|
+
($data.features === null)
|
|
110
|
+
? (openBlock(), createElementBlock("p", _hoisted_8, toDisplayString($options.t('nextcloud-vue', 'The AI features on this record could not be read. This panel is incomplete, not empty.')), 1 /* TEXT */))
|
|
111
|
+
: ($data.features.length === 0)
|
|
112
|
+
? (openBlock(), createElementBlock("p", _hoisted_9, toDisplayString($options.t('nextcloud-vue', 'The type of this record switches on no AI feature here.')), 1 /* TEXT */))
|
|
113
|
+
: (openBlock(), createElementBlock(Fragment, { key: 3 }, [
|
|
114
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList($data.features, (feature) => {
|
|
115
|
+
return (openBlock(), createElementBlock("section", {
|
|
116
|
+
key: feature.slug,
|
|
117
|
+
class: "cn-ai-features__feature",
|
|
118
|
+
"data-testid": `cn-ai-feature-${feature.slug}`
|
|
119
|
+
}, [
|
|
120
|
+
createElementVNode("h4", null, toDisplayString(feature.slug), 1 /* TEXT */),
|
|
121
|
+
createElementVNode("p", {
|
|
122
|
+
class: "cn-ai-features__hint",
|
|
123
|
+
"data-testid": `cn-ai-feature-where-${feature.slug}`
|
|
124
|
+
}, toDisplayString($options.whereItRuns(feature)), 9 /* TEXT, PROPS */, _hoisted_11),
|
|
125
|
+
(feature.available === true)
|
|
126
|
+
? (openBlock(), createElementBlock("form", {
|
|
127
|
+
key: 0,
|
|
128
|
+
class: "cn-ai-features__run",
|
|
129
|
+
onSubmit: withModifiers($event => ($options.run(feature)), ["prevent"])
|
|
130
|
+
}, [
|
|
131
|
+
createVNode(_component_NcSelect, {
|
|
132
|
+
inputId: $options.fieldId(feature, 'document'),
|
|
133
|
+
inputLabel: $options.t('nextcloud-vue', 'Document'),
|
|
134
|
+
placeholder: $options.t('nextcloud-vue', 'Choose a document'),
|
|
135
|
+
options: $options.documentOptions,
|
|
136
|
+
modelValue: $options.selectedDocument(feature),
|
|
137
|
+
"data-testid": `cn-ai-feature-document-${feature.slug}`,
|
|
138
|
+
"onUpdate:modelValue": $event => ($options.chooseDocument(feature, $event))
|
|
139
|
+
}, null, 8 /* PROPS */, ["inputId", "inputLabel", "placeholder", "options", "modelValue", "data-testid", "onUpdate:modelValue"]),
|
|
140
|
+
($data.documents === null)
|
|
141
|
+
? (openBlock(), createElementBlock("p", _hoisted_13, toDisplayString($options.t('nextcloud-vue', 'The documents on this record could not be read.')), 1 /* TEXT */))
|
|
142
|
+
: createCommentVNode("v-if", true),
|
|
143
|
+
createElementVNode("label", {
|
|
144
|
+
for: $options.fieldId(feature, 'instruction')
|
|
145
|
+
}, toDisplayString($options.t('nextcloud-vue', 'What should it do with the document?')), 9 /* TEXT, PROPS */, _hoisted_14),
|
|
146
|
+
withDirectives(createElementVNode("textarea", {
|
|
147
|
+
id: $options.fieldId(feature, 'instruction'),
|
|
148
|
+
"onUpdate:modelValue": $event => (($data.runs[feature.slug].instruction) = $event),
|
|
149
|
+
rows: "2"
|
|
150
|
+
}, null, 8 /* PROPS */, _hoisted_15), [
|
|
151
|
+
[vModelText, $data.runs[feature.slug].instruction]
|
|
152
|
+
]),
|
|
153
|
+
createVNode(_component_NcButton, {
|
|
154
|
+
type: "submit",
|
|
155
|
+
disabled: $data.runs[feature.slug].busy,
|
|
156
|
+
"data-testid": `cn-ai-feature-run-${feature.slug}`
|
|
157
|
+
}, {
|
|
158
|
+
default: withCtx(() => [
|
|
159
|
+
createTextVNode(toDisplayString($options.t('nextcloud-vue', 'Run')), 1 /* TEXT */)
|
|
160
|
+
]),
|
|
161
|
+
_: 1 /* STABLE */
|
|
162
|
+
}, 8 /* PROPS */, ["disabled", "data-testid"])
|
|
163
|
+
], 40 /* PROPS, NEED_HYDRATION */, _hoisted_12))
|
|
164
|
+
: createCommentVNode("v-if", true),
|
|
165
|
+
($data.runs[feature.slug] && $data.runs[feature.slug].refusal)
|
|
166
|
+
? (openBlock(), createBlock(_component_NcNoteCard, {
|
|
167
|
+
key: 1,
|
|
168
|
+
type: "warning",
|
|
169
|
+
"data-testid": `cn-ai-feature-refusal-${feature.slug}`
|
|
170
|
+
}, {
|
|
171
|
+
default: withCtx(() => [
|
|
172
|
+
createElementVNode("p", null, toDisplayString($data.runs[feature.slug].refusal.sentence), 1 /* TEXT */),
|
|
173
|
+
($data.runs[feature.slug].refusal.gate)
|
|
174
|
+
? (openBlock(), createElementBlock("p", _hoisted_16, toDisplayString($options.t('nextcloud-vue', 'Gate: {gate}', { gate: $data.runs[feature.slug].refusal.gate })), 1 /* TEXT */))
|
|
175
|
+
: createCommentVNode("v-if", true)
|
|
176
|
+
]),
|
|
177
|
+
_: 2 /* DYNAMIC */
|
|
178
|
+
}, 1032 /* PROPS, DYNAMIC_SLOTS */, ["data-testid"]))
|
|
179
|
+
: createCommentVNode("v-if", true),
|
|
180
|
+
($data.runs[feature.slug] && $data.runs[feature.slug].failed)
|
|
181
|
+
? (openBlock(), createElementBlock("p", {
|
|
182
|
+
key: 2,
|
|
183
|
+
class: "cn-ai-features__unreadable",
|
|
184
|
+
"data-testid": `cn-ai-feature-failed-${feature.slug}`
|
|
185
|
+
}, toDisplayString($options.t('nextcloud-vue', 'hermiq could not be reached. Nothing was run.')), 9 /* TEXT, PROPS */, _hoisted_17))
|
|
186
|
+
: createCommentVNode("v-if", true),
|
|
187
|
+
($data.runs[feature.slug] && $data.runs[feature.slug].output)
|
|
188
|
+
? (openBlock(), createElementBlock("div", {
|
|
189
|
+
key: 3,
|
|
190
|
+
class: "cn-ai-features__output",
|
|
191
|
+
"data-testid": `cn-ai-feature-output-${feature.slug}`
|
|
192
|
+
}, toDisplayString($data.runs[feature.slug].output), 9 /* TEXT, PROPS */, _hoisted_18))
|
|
193
|
+
: createCommentVNode("v-if", true),
|
|
194
|
+
($data.runs[feature.slug] && $data.runs[feature.slug].output)
|
|
195
|
+
? (openBlock(), createBlock(_component_CnAiSuggestionDecision, {
|
|
196
|
+
key: 4,
|
|
197
|
+
text: $data.runs[feature.slug].output,
|
|
198
|
+
testid: `cn-ai-feature-decision-${feature.slug}`,
|
|
199
|
+
onDecide: $event => ($options.decide(`feature:${feature.slug}`, $event))
|
|
200
|
+
}, null, 8 /* PROPS */, ["text", "testid", "onDecide"]))
|
|
201
|
+
: createCommentVNode("v-if", true)
|
|
202
|
+
], 8 /* PROPS */, _hoisted_10))
|
|
203
|
+
}), 128 /* KEYED_FRAGMENT */)),
|
|
204
|
+
createElementVNode("p", _hoisted_19, toDisplayString($options.t('nextcloud-vue', 'The assistant does nothing without your confirmation. It sees only what you see on this record.')), 1 /* TEXT */)
|
|
205
|
+
], 64 /* STABLE_FRAGMENT */))
|
|
206
|
+
], 64 /* STABLE_FRAGMENT */))
|
|
207
|
+
]))
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export { render };
|
|
211
|
+
//# sourceMappingURL=CnAiFeaturesWidget.vue3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnAiFeaturesWidget.vue3.js","sources":["../../../../src/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue?vue&type=template&id=c7b3ee28&lang.js"],"sourcesContent":["<!--\n SPDX-FileCopyrightText: 2026 Conduction B.V.\n SPDX-License-Identifier: EUPL-1.2\n\n CnAiFeaturesWidget — the hermiq AI features a record's type switches on for\n a surface: where each one runs (hermiq's answer; a feature hermiq does not\n offer reads unavailable, never local), a run on one of the record's\n documents with a refusal shown in words naming the feature, the reason and\n hermiq's gate, optional question prompts, and a use / adapt / reject\n decision on every suggestion for the AI oversight trail.\n-->\n<template>\n\t<div class=\"cn-ai-features\" data-testid=\"cn-ai-features\">\n\t\t<h3 v-if=\"showTitle\">\n\t\t\t{{ title || t('nextcloud-vue', 'Assistant') }}\n\t\t</h3>\n\n\t\t<div v-if=\"loading\" class=\"cn-ai-features__loading\">\n\t\t\t<NcLoadingIcon :size=\"20\" />\n\t\t\t{{ t('nextcloud-vue', 'Reading the AI features on this record') }}\n\t\t</div>\n\n\t\t<template v-else>\n\t\t\t<section v-if=\"prompts && prompts.length > 0\"\n\t\t\t\tclass=\"cn-ai-features__questions\"\n\t\t\t\tdata-testid=\"cn-ai-questions\">\n\t\t\t\t<h4>{{ t('nextcloud-vue', 'Questions') }}</h4>\n\t\t\t\t<ul class=\"cn-ai-features__prompts\">\n\t\t\t\t\t<li v-for=\"prompt in prompts\" :key=\"prompt.id || prompt.label\">\n\t\t\t\t\t\t<NcButton :disabled=\"asking\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-ai-question-${prompt.id}`\"\n\t\t\t\t\t\t\t@click=\"ask(prompt)\">\n\t\t\t\t\t\t\t{{ prompt.label }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</li>\n\t\t\t\t</ul>\n\t\t\t\t<div v-if=\"answer\" class=\"cn-ai-features__output\" data-testid=\"cn-ai-question-answer\">\n\t\t\t\t\t{{ answer }}\n\t\t\t\t</div>\n\t\t\t\t<CnAiSuggestionDecision v-if=\"answer\"\n\t\t\t\t\t:text=\"answer\"\n\t\t\t\t\ttestid=\"cn-ai-question-decision\"\n\t\t\t\t\t@decide=\"decide('question', $event)\" />\n\t\t\t\t<p v-if=\"answerFailed\" class=\"cn-ai-features__unreadable\" data-testid=\"cn-ai-question-failed\">\n\t\t\t\t\t{{ answerFailed }}\n\t\t\t\t</p>\n\t\t\t</section>\n\n\t\t\t<p v-if=\"features === null\" class=\"cn-ai-features__unreadable\" data-testid=\"cn-ai-features-unreadable\">\n\t\t\t\t{{ t('nextcloud-vue', 'The AI features on this record could not be read. This panel is incomplete, not empty.') }}\n\t\t\t</p>\n\n\t\t\t<p v-else-if=\"features.length === 0\" class=\"cn-ai-features__hint\" data-testid=\"cn-ai-features-empty\">\n\t\t\t\t{{ t('nextcloud-vue', 'The type of this record switches on no AI feature here.') }}\n\t\t\t</p>\n\n\t\t\t<template v-else>\n\t\t\t\t<section v-for=\"feature in features\"\n\t\t\t\t\t:key=\"feature.slug\"\n\t\t\t\t\tclass=\"cn-ai-features__feature\"\n\t\t\t\t\t:data-testid=\"`cn-ai-feature-${feature.slug}`\">\n\t\t\t\t\t<h4>{{ feature.slug }}</h4>\n\t\t\t\t\t<p class=\"cn-ai-features__hint\" :data-testid=\"`cn-ai-feature-where-${feature.slug}`\">\n\t\t\t\t\t\t{{ whereItRuns(feature) }}\n\t\t\t\t\t</p>\n\n\t\t\t\t\t<form v-if=\"feature.available === true\"\n\t\t\t\t\t\tclass=\"cn-ai-features__run\"\n\t\t\t\t\t\t@submit.prevent=\"run(feature)\">\n\t\t\t\t\t\t<NcSelect :inputId=\"fieldId(feature, 'document')\"\n\t\t\t\t\t\t\t:inputLabel=\"t('nextcloud-vue', 'Document')\"\n\t\t\t\t\t\t\t:placeholder=\"t('nextcloud-vue', 'Choose a document')\"\n\t\t\t\t\t\t\t:options=\"documentOptions\"\n\t\t\t\t\t\t\t:modelValue=\"selectedDocument(feature)\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-ai-feature-document-${feature.slug}`\"\n\t\t\t\t\t\t\t@update:modelValue=\"chooseDocument(feature, $event)\" />\n\t\t\t\t\t\t<p v-if=\"documents === null\" class=\"cn-ai-features__unreadable\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'The documents on this record could not be read.') }}\n\t\t\t\t\t\t</p>\n\n\t\t\t\t\t\t<label :for=\"fieldId(feature, 'instruction')\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'What should it do with the document?') }}\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<textarea :id=\"fieldId(feature, 'instruction')\"\n\t\t\t\t\t\t\tv-model=\"runs[feature.slug].instruction\"\n\t\t\t\t\t\t\trows=\"2\" />\n\n\t\t\t\t\t\t<NcButton type=\"submit\"\n\t\t\t\t\t\t\t:disabled=\"runs[feature.slug].busy\"\n\t\t\t\t\t\t\t:data-testid=\"`cn-ai-feature-run-${feature.slug}`\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Run') }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</form>\n\n\t\t\t\t\t<NcNoteCard v-if=\"runs[feature.slug] && runs[feature.slug].refusal\"\n\t\t\t\t\t\ttype=\"warning\"\n\t\t\t\t\t\t:data-testid=\"`cn-ai-feature-refusal-${feature.slug}`\">\n\t\t\t\t\t\t<p>{{ runs[feature.slug].refusal.sentence }}</p>\n\t\t\t\t\t\t<p v-if=\"runs[feature.slug].refusal.gate\" class=\"cn-ai-features__gate\">\n\t\t\t\t\t\t\t{{ t('nextcloud-vue', 'Gate: {gate}', { gate: runs[feature.slug].refusal.gate }) }}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</NcNoteCard>\n\n\t\t\t\t\t<p v-if=\"runs[feature.slug] && runs[feature.slug].failed\"\n\t\t\t\t\t\tclass=\"cn-ai-features__unreadable\"\n\t\t\t\t\t\t:data-testid=\"`cn-ai-feature-failed-${feature.slug}`\">\n\t\t\t\t\t\t{{ t('nextcloud-vue', 'hermiq could not be reached. Nothing was run.') }}\n\t\t\t\t\t</p>\n\n\t\t\t\t\t<div v-if=\"runs[feature.slug] && runs[feature.slug].output\"\n\t\t\t\t\t\tclass=\"cn-ai-features__output\"\n\t\t\t\t\t\t:data-testid=\"`cn-ai-feature-output-${feature.slug}`\">\n\t\t\t\t\t\t{{ runs[feature.slug].output }}\n\t\t\t\t\t</div>\n\t\t\t\t\t<CnAiSuggestionDecision v-if=\"runs[feature.slug] && runs[feature.slug].output\"\n\t\t\t\t\t\t:text=\"runs[feature.slug].output\"\n\t\t\t\t\t\t:testid=\"`cn-ai-feature-decision-${feature.slug}`\"\n\t\t\t\t\t\t@decide=\"decide(`feature:${feature.slug}`, $event)\" />\n\t\t\t\t</section>\n\n\t\t\t\t<p class=\"cn-ai-features__hint\">\n\t\t\t\t\t{{ t('nextcloud-vue', 'The assistant does nothing without your confirmation. It sees only what you see on this record.') }}\n\t\t\t\t</p>\n\t\t\t</template>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcLoadingIcon, NcNoteCard, NcSelect } from '@nextcloud/vue'\nimport CnAiSuggestionDecision from './CnAiSuggestionDecision.vue'\nimport {\n\tAiFeatureRefusal,\n\taskAbout,\n\tDEFAULT_RUN_ENDPOINT,\n\tfetchDocuments,\n\tfetchFeatures,\n\tfetchPrompts,\n\trefusalSentence,\n\trunFeatureOnDocument,\n\twhereItRuns,\n} from '../../utils/aiFeatures.js'\nimport { createSuggestionTracker } from '../../utils/aiSuggestionTracker.js'\n\nexport default {\n\tname: 'CnAiFeaturesWidget',\n\tcomponents: { CnAiSuggestionDecision, NcButton, NcLoadingIcon, NcNoteCard, NcSelect },\n\n\tprops: {\n\t\t/** The record id (injected by CnObjectSidebar). */\n\t\tobjectId: { type: String, default: '' },\n\t\t/** Register slug or id (injected by CnObjectSidebar). */\n\t\tregister: { type: String, default: '' },\n\t\t/** Schema slug or id (injected by CnObjectSidebar). */\n\t\tschema: { type: String, default: '' },\n\t\t/** OpenRegister API root. */\n\t\tapiBase: { type: String, default: '/apps/openregister/api' },\n\t\t/**\n\t\t * Where the features are declared: the record's `typeField` references\n\t\t * a `typeSchema` object whose `field` maps feature slug to surface;\n\t\t * the slugs placed on `surface` are shown.\n\t\t */\n\t\tdeclaration: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({ typeField: '', typeSchema: '', field: 'aiFeatures', surface: '' }),\n\t\t},\n\n\t\t/** The endpoint that answers where each declared feature runs (`{features: [...]}`). */\n\t\tfeaturesEndpoint: { type: String, default: '' },\n\t\t/** Query key the declared slugs are sent under: `<key>[<slug>]=<surface>`. */\n\t\tfeaturesQueryKey: { type: String, default: 'aiFeatures' },\n\t\t/** The record's documents; empty uses OpenRegister's files of the record. */\n\t\tfilesEndpoint: { type: String, default: '' },\n\t\t/** hermiq's run endpoint, with `{slug}`. */\n\t\trunEndpoint: { type: String, default: DEFAULT_RUN_ENDPOINT },\n\t\t/** Prompts offered as questions (`{prompts: [...]}`); empty offers none. */\n\t\tpromptsEndpoint: { type: String, default: '' },\n\t\t/** Where a question is posted (`{<objectKey>: id, message}` → `{reply}`). */\n\t\taskEndpoint: { type: String, default: '' },\n\t\t/** The AI oversight record-action endpoint; empty reports nothing. */\n\t\tdecisionEndpoint: { type: String, default: '' },\n\t\t/** Body key naming the record in the ask and decision bodies. */\n\t\tobjectKey: { type: String, default: 'objectId' },\n\t\t/** Heading; empty uses \"Assistant\". */\n\t\ttitle: { type: String, default: '' },\n\t\t/** Whether the heading is drawn. */\n\t\tshowTitle: { type: Boolean, default: true },\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: true,\n\t\t\tfeatures: [],\n\t\t\tdocuments: [],\n\t\t\truns: {},\n\t\t\tprompts: [],\n\t\t\tasking: false,\n\t\t\tanswer: '',\n\t\t\tanswerFailed: '',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tdocumentOptions() {\n\t\t\treturn (this.documents || []).map((doc) => ({ id: doc.id, label: doc.title }))\n\t\t},\n\n\t\tresolvedFilesEndpoint() {\n\t\t\treturn this.filesEndpoint\n\t\t\t\t|| `${this.apiBase.replace(/\\/$/, '')}/objects/${this.register}/${this.schema}/${encodeURIComponent(this.objectId)}/files`\n\t\t},\n\t},\n\n\tcreated() {\n\t\tthis.tracker = createSuggestionTracker({ objectId: this.objectId, objectKey: this.objectKey, endpoint: this.decisionEndpoint })\n\t\tthis.onPageHide = () => this.tracker.closeAll({ leaving: true })\n\t\twindow.addEventListener('pagehide', this.onPageHide)\n\t},\n\n\tmounted() {\n\t\tthis.load()\n\t},\n\n\tbeforeUnmount() {\n\t\twindow.removeEventListener('pagehide', this.onPageHide)\n\t\tthis.tracker.closeAll()\n\t},\n\n\tmethods: {\n\t\tt,\n\t\twhereItRuns,\n\n\t\t/**\n\t\t * Read the features, the prompts and (when a feature can run) the documents.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync load() {\n\t\t\tthis.loading = true\n\t\t\tif (this.objectId === '' || !this.featuresEndpoint) {\n\t\t\t\tthis.features = null\n\t\t\t\tthis.loading = false\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst [features, prompts] = await Promise.all([\n\t\t\t\tfetchFeatures({\n\t\t\t\t\tapiBase: this.apiBase.replace(/\\/$/, ''),\n\t\t\t\t\tregister: this.register,\n\t\t\t\t\tschema: this.schema,\n\t\t\t\t\tobjectId: this.objectId,\n\t\t\t\t\tdeclaration: this.declaration,\n\t\t\t\t\tfeaturesEndpoint: this.featuresEndpoint,\n\t\t\t\t\tfeaturesQueryKey: this.featuresQueryKey,\n\t\t\t\t}),\n\t\t\t\tthis.promptsEndpoint ? fetchPrompts(this.promptsEndpoint) : [],\n\t\t\t])\n\t\t\tthis.features = features\n\t\t\tthis.prompts = prompts || []\n\n\t\t\tconst runs = {}\n\t\t\tfor (const feature of features || []) {\n\t\t\t\truns[feature.slug] = { documentReference: '', instruction: '', busy: false, refusal: null, failed: false, output: '' }\n\t\t\t}\n\t\t\tthis.runs = runs\n\n\t\t\tconst offersARun = (features || []).some((feature) => feature.available === true)\n\t\t\tthis.documents = offersARun ? await fetchDocuments(this.resolvedFilesEndpoint) : []\n\t\t\tthis.loading = false\n\t\t},\n\n\t\tselectedDocument(feature) {\n\t\t\tconst id = this.runs[feature.slug]?.documentReference\n\t\t\treturn this.documentOptions.find((option) => option.id === id) || null\n\t\t},\n\n\t\tchooseDocument(feature, option) {\n\t\t\tthis.runs[feature.slug].documentReference = option ? String(option.id) : ''\n\t\t},\n\n\t\t/**\n\t\t * Ask one of the offered prompts about this record.\n\t\t *\n\t\t * @param {object} prompt The prompt.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync ask(prompt) {\n\t\t\tthis.tracker.dismiss('question', 'superseded')\n\t\t\tthis.asking = true\n\t\t\tthis.answer = ''\n\t\t\tthis.answerFailed = ''\n\t\t\ttry {\n\t\t\t\tconst result = await askAbout(this.askEndpoint, this.objectKey, this.objectId, prompt.prompt)\n\t\t\t\tthis.answer = String(result?.reply ?? '')\n\t\t\t\tif (this.answer !== '') {\n\t\t\t\t\tthis.tracker.show('question', { type: 'qa', text: this.answer, prompt: prompt.prompt, model: String(result?.model ?? '') })\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tthis.answerFailed = String(error?.response?.data?.error ?? '') || t('nextcloud-vue', 'The assistant did not answer.')\n\t\t\t} finally {\n\t\t\t\tthis.asking = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Report what the person did with the suggestion in a slot.\n\t\t *\n\t\t * @param {string} slot The slot key.\n\t\t * @param {object} decision `{userAction, actualValue?}`.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tdecide(slot, decision) {\n\t\t\treturn this.tracker.decide(slot, decision.userAction, { actualValue: decision.actualValue })\n\t\t},\n\n\t\tfieldId(feature, field) {\n\t\t\treturn `cn-ai-${this.objectId || 'unknown'}-${feature.slug}-${field}`\n\t\t},\n\n\t\t/**\n\t\t * Run one feature on the chosen document, and show what came back.\n\t\t *\n\t\t * @param {object} feature The feature.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync run(feature) {\n\t\t\tconst state = this.runs[feature.slug]\n\t\t\tthis.tracker.dismiss(`feature:${feature.slug}`, 'superseded')\n\t\t\tstate.busy = true\n\t\t\tstate.refusal = null\n\t\t\tstate.failed = false\n\t\t\tstate.output = ''\n\t\t\ttry {\n\t\t\t\tconst answer = await runFeatureOnDocument({\n\t\t\t\t\tfeature: feature.slug,\n\t\t\t\t\tdocumentReference: state.documentReference,\n\t\t\t\t\tinstruction: state.instruction,\n\t\t\t\t\trunEndpoint: this.runEndpoint,\n\t\t\t\t})\n\t\t\t\tstate.output = String(answer?.output ?? '')\n\t\t\t\tif (state.output !== '') {\n\t\t\t\t\tthis.tracker.show(`feature:${feature.slug}`, {\n\t\t\t\t\t\ttype: 'decision_support',\n\t\t\t\t\t\ttext: state.output,\n\t\t\t\t\t\tprompt: state.instruction,\n\t\t\t\t\t\tmodel: String(answer?.model ?? ''),\n\t\t\t\t\t\tdocumentId: state.documentReference,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof AiFeatureRefusal) {\n\t\t\t\t\tstate.refusal = { sentence: refusalSentence(error, feature.slug), gate: error.gate }\n\t\t\t\t} else {\n\t\t\t\t\tstate.failed = true\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tstate.busy = false\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-ai-features {\n\tpadding: 12px;\n}\n\n.cn-ai-features__loading {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-ai-features__feature,\n.cn-ai-features__questions {\n\tmargin-bottom: 16px;\n\tpadding-bottom: 12px;\n\tborder-bottom: 1px solid var(--color-border);\n}\n\n.cn-ai-features__prompts {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px;\n\tlist-style: none;\n\tmargin: 0 0 8px;\n\tpadding: 0;\n}\n\n.cn-ai-features__run {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 4px;\n\tmargin: 8px 0;\n}\n\n.cn-ai-features__hint,\n.cn-ai-features__gate {\n\tdisplay: block;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.85em;\n}\n\n.cn-ai-features__output {\n\twhite-space: pre-wrap;\n\tpadding: 8px;\n\tbackground: var(--color-background-dark);\n\tborder-radius: var(--border-radius);\n}\n\n.cn-ai-features__unreadable {\n\tcolor: var(--color-warning-text, var(--color-main-text));\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_toDisplayString","_createTextVNode","_createElementVNode","_Fragment","_renderList","_createVNode","_createBlock","_vModelText"],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import script from './CnAiSuggestionDecision.vue2.js';
|
|
2
|
+
import { render } from './CnAiSuggestionDecision.vue3.js';
|
|
3
|
+
|
|
4
|
+
script.render = render;
|
|
5
|
+
script.__scopeId = "data-v-77db0b7a";
|
|
6
|
+
const __sfc_main = script;
|
|
7
|
+
|
|
8
|
+
export { __sfc_main as default };
|
|
9
|
+
//# sourceMappingURL=CnAiSuggestionDecision.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnAiSuggestionDecision.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { translate } from '@nextcloud/l10n';
|
|
2
|
+
import { NcButton } from '@nextcloud/vue';
|
|
3
|
+
|
|
4
|
+
var script = {
|
|
5
|
+
name: 'CnAiSuggestionDecision',
|
|
6
|
+
components: { NcButton },
|
|
7
|
+
|
|
8
|
+
props: {
|
|
9
|
+
/** The suggestion text. */
|
|
10
|
+
text: { type: String, required: true },
|
|
11
|
+
/** Test id prefix, also the base of the draft field id. */
|
|
12
|
+
testid: { type: String, default: 'cn-ai-suggestion-decision' },
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
emits: ['decide'],
|
|
16
|
+
|
|
17
|
+
data() {
|
|
18
|
+
return { editing: false, draft: '', decided: '' }
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
watch: {
|
|
22
|
+
text() {
|
|
23
|
+
this.editing = false;
|
|
24
|
+
this.draft = '';
|
|
25
|
+
this.decided = '';
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
methods: {
|
|
30
|
+
t: translate,
|
|
31
|
+
|
|
32
|
+
startEditing() {
|
|
33
|
+
this.draft = this.text;
|
|
34
|
+
this.editing = true;
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Report the decision, and copy a used suggestion to the clipboard.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} userAction accepted, modified or rejected.
|
|
41
|
+
* @return {Promise<void>}
|
|
42
|
+
*/
|
|
43
|
+
async decide(userAction) {
|
|
44
|
+
const used = userAction === 'modified' ? this.draft : this.text;
|
|
45
|
+
this.$emit('decide', { userAction, actualValue: userAction === 'modified' ? this.draft : undefined });
|
|
46
|
+
this.decided = {
|
|
47
|
+
accepted: translate('nextcloud-vue', 'Used. Copied to your clipboard.'),
|
|
48
|
+
modified: translate('nextcloud-vue', 'Your version is used. Copied to your clipboard.'),
|
|
49
|
+
rejected: translate('nextcloud-vue', 'Rejected.'),
|
|
50
|
+
}[userAction];
|
|
51
|
+
if (userAction === 'rejected') {
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
await navigator.clipboard.writeText(used);
|
|
56
|
+
} catch {
|
|
57
|
+
this.decided = userAction === 'modified' ? translate('nextcloud-vue', 'Your version is used.') : translate('nextcloud-vue', 'Used.');
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export { script as default };
|
|
64
|
+
//# sourceMappingURL=CnAiSuggestionDecision.vue2.js.map
|