Repository navigation
feat(agency): Show item count and reversibility in confirmation dialog #732
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c499322
3a35ff8
534adfb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,22 +3,52 @@ | |
| - SPDX-License-Identifier: AGPL-3.0-or-later | ||
| --> | ||
| <template> | ||
| <NcNoteCard :type="destructive.destructive ? 'warning' : 'info'" | ||
| <NcNoteCard :type="(destructive.destructive || irreversible.irreversible) ? 'warning' : 'info'" | ||
| class="agency-confirmation"> | ||
| <div class="notecard-content"> | ||
| <span> | ||
| {{ t('assistant', 'The Assistant wants to perform sensitive actions on your behalf.') }} | ||
| </span> | ||
| <AgencyActions :actions="actions" /> | ||
| <div class="footer"> | ||
| <NcButton variant="tertiary" | ||
| :title="radiusInfo.description + (destructive.destructive ? '\n' : '') + destructive.description" | ||
| :text="radiusInfo.label" | ||
| class="help radius"> | ||
| <template #icon> | ||
| <component :is="radiusInfo.icon" :size="20" /> | ||
| </template> | ||
| </NcButton> | ||
| <div class="footer-left"> | ||
| <NcButton v-if="radiusInfo" | ||
| variant="tertiary" | ||
| :title="radiusInfo.description" | ||
| :text="radiusInfo.label" | ||
| class="help"> | ||
| <template #icon> | ||
| <component :is="radiusInfo.icon" :size="20" /> | ||
| </template> | ||
| </NcButton> | ||
| <NcButton v-if="destructive.destructive" | ||
| variant="tertiary" | ||
| :title="destructive.description" | ||
| :aria-label="destructive.description" | ||
| class="help"> | ||
| <template #icon> | ||
| <DeleteAlertOutlineIcon :size="20" /> | ||
| </template> | ||
| </NcButton> | ||
| <NcButton v-if="irreversible.irreversible" | ||
| variant="tertiary" | ||
| :title="irreversible.description" | ||
| :aria-label="irreversible.description" | ||
| class="help"> | ||
| <template #icon> | ||
| <RestoreAlertIcon :size="20" /> | ||
| </template> | ||
| </NcButton> | ||
| <NcButton v-if="itemCount.itemCount > 1" | ||
| variant="tertiary" | ||
| :title="itemCount.description" | ||
| :text="String(itemCount.itemCount)" | ||
| class="help"> | ||
| <template #icon> | ||
| <TallyMark5Icon :size="20" /> | ||
| </template> | ||
| </NcButton> | ||
| </div> | ||
| <NcButton variant="tertiary" | ||
| :title="hint" | ||
| class="help"> | ||
|
|
@@ -52,6 +82,9 @@ import AccountOutlineIcon from 'vue-material-design-icons/AccountOutline.vue' | |
| import AccountMultipleOutlineIcon from 'vue-material-design-icons/AccountMultipleOutline.vue' | ||
| import AccountGroupOutlineIcon from 'vue-material-design-icons/AccountGroupOutline.vue' | ||
| import EarthIcon from 'vue-material-design-icons/Earth.vue' | ||
| import TallyMark5Icon from 'vue-material-design-icons/TallyMark5.vue' | ||
| import DeleteAlertOutlineIcon from 'vue-material-design-icons/DeleteAlertOutline.vue' | ||
| import RestoreAlertIcon from 'vue-material-design-icons/RestoreAlert.vue' | ||
| import AssistantIcon from '../icons/AssistantIcon.vue' | ||
| import AgencyActions from './AgencyActions.vue' | ||
|
|
@@ -106,6 +139,9 @@ export default { | |
| AccountGroupOutlineIcon, | ||
| EarthIcon, | ||
| InformationOutlineIcon, | ||
| TallyMark5Icon, | ||
| DeleteAlertOutlineIcon, | ||
| RestoreAlertIcon, | ||
| }, | ||
| props: { | ||
|
|
@@ -129,9 +165,9 @@ export default { | |
| computed: { | ||
| destructive() { | ||
| // determine if any of the pending actions are destructive | ||
| // for actions whose destructive state is undefined or unknown, assume they are destructive | ||
| // actions that don't report a destructive state (older backends) are assumed to be non-destructive | ||
| for (const action of this.actions) { | ||
| if (action?.destructive !== false) { | ||
| if (action?.destructive !== undefined && action?.destructive !== null && action.destructive !== false) { | ||
|
Comment on lines
+168
to
+170
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| return { | ||
| destructive: true, | ||
| // TRANSLATORS Additional description for destructive AI agent actions | ||
|
|
@@ -144,14 +180,53 @@ export default { | |
| description: '', | ||
| } | ||
| }, | ||
| irreversible() { | ||
| // determine if any of the pending actions are irreversible | ||
| // actions that don't report their reversibility (older backends) are assumed to be reversible | ||
| for (const action of this.actions) { | ||
| if (action?.reversibility === 'irreversible') { | ||
| return { | ||
| irreversible: true, | ||
| // TRANSLATORS Additional description for irreversible AI agent actions | ||
| description: t('assistant', 'Some actions cannot be reversed.'), | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| irreversible: false, | ||
| description: '', | ||
| } | ||
| }, | ||
| itemCount() { | ||
| // determine how many items are affected by the pending actions | ||
| let itemCount = 0 | ||
| for (const action of this.actions) { | ||
| if (Number.isFinite(action?.item_count)) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we cast to int? Right now it seems possible to render float counts. |
||
| itemCount += action.item_count | ||
| } | ||
| } | ||
| return { | ||
| itemCount, | ||
| // TRANSLATORS Additional description for pending AI agent actions to be confirmed by the user | ||
| description: n('assistant', '%n item is affected by these actions.', '%n items are affected by these actions.', itemCount), | ||
| } | ||
| }, | ||
|
Comment on lines
+202
to
+215
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| radiusInfo() { | ||
| // show the largest radius among all actions | ||
| // for actions with an undefined or unknown radius, assume they are external | ||
| // actions that don't report a radius (older backends) are ignored | ||
| // actions with an unknown radius value are assumed to be external | ||
| // returns null if no action reports a radius | ||
| const largest = this.actions.reduce((max, action) => { | ||
| const index = radiusOrder.indexOf(action?.impulse_radius) | ||
| if (action?.impulse_radius === undefined || action?.impulse_radius === null) { | ||
| return max | ||
| } | ||
| const index = radiusOrder.indexOf(action.impulse_radius) | ||
| return Math.max(max, index === -1 ? radiusOrder.length - 1 : index) | ||
| }, 0) | ||
| return radii[radiusOrder[largest]] | ||
| }, -1) | ||
| return largest === -1 ? null : radii[radiusOrder[largest]] | ||
| }, | ||
| }, | ||
| } | ||
|
|
@@ -177,7 +252,10 @@ export default { | |
| align-items: center; | ||
| justify-content: end; | ||
| .radius { | ||
| .footer-left { | ||
| display: flex; | ||
| gap: 4px; | ||
| align-items: center; | ||
| margin-right: auto; | ||
| } | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
Repository: nextcloud/assistant
Length of output: 16132
Show the warning descriptions without hover.
Touch and keyboard users may not see the destructive or irreversible descriptions. The icon-only buttons also add tab stops that do nothing. Show the descriptions as text and use non-interactive indicators.
Suggested fix