@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.
Files changed (126) hide show
  1. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue.js +9 -0
  2. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue.js.map +1 -0
  3. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue2.js +226 -0
  4. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue2.js.map +1 -0
  5. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue3.js +211 -0
  6. package/dist/esm/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue3.js.map +1 -0
  7. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue.js +9 -0
  8. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue.js.map +1 -0
  9. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue2.js +64 -0
  10. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue2.js.map +1 -0
  11. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue3.js +89 -0
  12. package/dist/esm/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue3.js.map +1 -0
  13. package/dist/esm/components/CnIcon/semanticIcons.js +2 -2
  14. package/dist/esm/components/CnIcon/semanticIcons.js.map +1 -1
  15. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue.js +9 -0
  16. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue.js.map +1 -0
  17. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue2.js +257 -0
  18. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue2.js.map +1 -0
  19. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue3.js +266 -0
  20. package/dist/esm/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue3.js.map +1 -0
  21. package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue.js +1 -1
  22. package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js +14 -7
  23. package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue2.js.map +1 -1
  24. package/dist/esm/components/CnObjectSidebar/CnObjectSidebar.vue3.js.map +1 -1
  25. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue.js +9 -0
  26. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue.js.map +1 -0
  27. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue2.js +278 -0
  28. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue2.js.map +1 -0
  29. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue3.js +128 -0
  30. package/dist/esm/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue3.js.map +1 -0
  31. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue.js +10 -0
  32. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue.js.map +1 -0
  33. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue2.js +944 -0
  34. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue2.js.map +1 -0
  35. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue3.js +276 -0
  36. package/dist/esm/components/CnStateMachineEditor/CnStateMachineEditor.vue3.js.map +1 -0
  37. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue.js +9 -0
  38. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue.js.map +1 -0
  39. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue2.js +354 -0
  40. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue2.js.map +1 -0
  41. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue3.js +392 -0
  42. package/dist/esm/components/CnStateMachineEditor/CnStateMachineFields.vue3.js.map +1 -0
  43. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue.js +9 -0
  44. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue.js.map +1 -0
  45. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue2.js +180 -0
  46. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue2.js.map +1 -0
  47. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue3.js +208 -0
  48. package/dist/esm/components/CnStateMachineEditor/CnStateMachineNode.vue3.js.map +1 -0
  49. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue.js +9 -0
  50. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue.js.map +1 -0
  51. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue2.js +244 -0
  52. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue2.js.map +1 -0
  53. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue3.js +76 -0
  54. package/dist/esm/components/CnStateMachineEditor/CnStateMachinePanel.vue3.js.map +1 -0
  55. package/dist/esm/components/CnStateMachineEditor/stateMachineFields.js +290 -0
  56. package/dist/esm/components/CnStateMachineEditor/stateMachineFields.js.map +1 -0
  57. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js +9 -0
  58. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue.js.map +1 -0
  59. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js +448 -0
  60. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue2.js.map +1 -0
  61. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js +303 -0
  62. package/dist/esm/components/CnTimelineWidget/CnTimelineManage.vue3.js.map +1 -0
  63. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue.js +1 -1
  64. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js +19 -2
  65. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue2.js.map +1 -1
  66. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js +73 -62
  67. package/dist/esm/components/CnTimelineWidget/CnTimelineWidget.vue3.js.map +1 -1
  68. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +6 -0
  69. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  70. package/dist/esm/index.js +5 -0
  71. package/dist/esm/index.js.map +1 -1
  72. package/dist/esm/l10n/en.json.js +166 -1
  73. package/dist/esm/l10n/en.json.js.map +1 -1
  74. package/dist/esm/l10n/nl.json.js +166 -1
  75. package/dist/esm/l10n/nl.json.js.map +1 -1
  76. package/dist/esm/library-style.js +1 -1
  77. package/dist/esm/nextcloud-vue.css +889 -101
  78. package/dist/esm/utils/aiFeatures.js +245 -0
  79. package/dist/esm/utils/aiFeatures.js.map +1 -0
  80. package/dist/esm/utils/aiSuggestionTracker.js +182 -0
  81. package/dist/esm/utils/aiSuggestionTracker.js.map +1 -0
  82. package/dist/esm/utils/libraryWidgetKeys.js +1 -0
  83. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  84. package/dist/esm/utils/objectPermissions.js +552 -0
  85. package/dist/esm/utils/objectPermissions.js.map +1 -0
  86. package/dist/esm/utils/objectTranslations.js +180 -0
  87. package/dist/esm/utils/objectTranslations.js.map +1 -0
  88. package/dist/esm/utils/stateMachineValidation.js +254 -0
  89. package/dist/esm/utils/stateMachineValidation.js.map +1 -0
  90. package/dist/esm/utils/widgetDispatch.js +3 -1
  91. package/dist/esm/utils/widgetDispatch.js.map +1 -1
  92. package/dist/nextcloud-vue.cjs.js +20052 -13138
  93. package/dist/nextcloud-vue.cjs.js.map +2 -2
  94. package/dist/nextcloud-vue.css +889 -101
  95. package/l10n/en.json +166 -1
  96. package/l10n/nl.json +166 -1
  97. package/package.json +1 -1
  98. package/src/components/CnAiFeaturesWidget/CnAiFeaturesWidget.vue +416 -0
  99. package/src/components/CnAiFeaturesWidget/CnAiSuggestionDecision.vue +136 -0
  100. package/src/components/CnAiFeaturesWidget/__tests__/CnAiFeaturesWidget.spec.js +213 -0
  101. package/src/components/CnAiFeaturesWidget/index.js +14 -0
  102. package/src/components/CnObjectPermissionsTab/CnObjectPermissionsTab.vue +499 -0
  103. package/src/components/CnObjectPermissionsTab/__tests__/CnObjectPermissionsTab.spec.js +186 -0
  104. package/src/components/CnObjectPermissionsTab/index.js +13 -0
  105. package/src/components/CnObjectSidebar/CnObjectSidebar.vue +8 -1
  106. package/src/components/CnObjectTranslationsWidget/CnObjectTranslationsWidget.vue +424 -0
  107. package/src/components/CnObjectTranslationsWidget/__tests__/CnObjectTranslationsWidget.spec.js +189 -0
  108. package/src/components/CnObjectTranslationsWidget/index.js +13 -0
  109. package/src/components/CnStateMachineEditor/CnStateMachineEditor.vue +1332 -0
  110. package/src/components/CnStateMachineEditor/CnStateMachineFields.vue +791 -0
  111. package/src/components/CnStateMachineEditor/CnStateMachineNode.vue +442 -0
  112. package/src/components/CnStateMachineEditor/CnStateMachinePanel.vue +326 -0
  113. package/src/components/CnStateMachineEditor/index.js +8 -0
  114. package/src/components/CnStateMachineEditor/stateMachineFields.js +287 -0
  115. package/src/components/CnTimelineWidget/CnTimelineManage.vue +827 -0
  116. package/src/components/CnTimelineWidget/CnTimelineWidget.vue +29 -3
  117. package/src/components/CnWidgetGrid/builtInWidgets.js +6 -0
  118. package/src/components/index.js +4 -0
  119. package/src/index.js +5 -0
  120. package/src/utils/aiFeatures.js +241 -0
  121. package/src/utils/aiSuggestionTracker.js +178 -0
  122. package/src/utils/libraryWidgetKeys.js +1 -0
  123. package/src/utils/objectPermissions.js +548 -0
  124. package/src/utils/objectTranslations.js +176 -0
  125. package/src/utils/stateMachineValidation.js +250 -0
  126. 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