Feat: Rename and Delete for imported Models ☁️ (#6969)

## Summary

Add Rename and Delete options for Personal Models.

Also updates and standardizes some styles for Cards and adds a simple
Confirmation dialog.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6969-WIP-Feat-Rename-and-Delete-for-custom-Models-2b86d73d36508140a687e929b0544ae6)
by [Unito](https://www.unito.io)

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
Alexander Brown
2025-12-01 17:16:05 -08:00
committed by GitHub
parent 072f1f6ced
commit 04158deb02
21 changed files with 457 additions and 162 deletions

View File

@@ -1,17 +1,15 @@
<template>
<div :class="iconGroupClasses">
<div
:class="
cn(
'flex justify-center items-center shrink-0 outline-hidden border-none p-0 rounded-lg bg-secondary-background shadow-sm transition-all duration-200 cursor-pointer'
)
"
>
<slot></slot>
</div>
</template>
<script setup lang="ts">
import { cn } from '@/utils/tailwindUtil'
const iconGroupClasses = cn(
'flex justify-center items-center shrink-0',
'outline-hidden border-none p-0 rounded-lg',
'bg-secondary-background shadow-sm',
'transition-all duration-200',
'cursor-pointer'
)
</script>

View File

@@ -1,6 +1,6 @@
<template>
<div class="relative inline-flex items-center">
<IconButton :size="size" :type="type" @click="toggle">
<IconButton :size="size" :type="type" @click="popover?.toggle">
<i v-if="!isVertical" class="icon-[lucide--ellipsis] text-sm" />
<i v-else class="icon-[lucide--more-vertical] text-sm" />
</IconButton>
@@ -25,8 +25,18 @@
)
}
}"
@show="$emit('menuOpened')"
@hide="$emit('menuClosed')"
@show="
() => {
isOpen = true
$emit('menuOpened')
}
"
@hide="
() => {
isOpen = false
$emit('menuClosed')
}
"
>
<div class="flex min-w-40 flex-col gap-2 p-2">
<slot :close="hide" />
@@ -48,8 +58,6 @@ interface MoreButtonProps extends BaseButtonProps {
isVertical?: boolean
}
const popover = ref<InstanceType<typeof Popover>>()
const {
size = 'md',
type = 'secondary',
@@ -61,15 +69,15 @@ defineEmits<{
menuClosed: []
}>()
const toggle = (event: Event) => {
popover.value?.toggle(event)
}
const isOpen = ref(false)
const popover = ref<InstanceType<typeof Popover>>()
const hide = () => {
function hide() {
popover.value?.hide()
}
defineExpose({
hide
hide,
isOpen
})
</script>

View File

@@ -18,8 +18,8 @@
...inputAttrs
}
}"
@keyup.enter="blurInputElement"
@keyup.escape="cancelEditing"
@keyup.enter.capture.stop="blurInputElement"
@keyup.escape.stop="cancelEditing"
@click.stop
@pointerdown.stop.capture
@pointermove.stop.capture
@@ -38,7 +38,7 @@ const {
} = defineProps<{
modelValue: string
isEditing?: boolean
inputAttrs?: Record<string, any>
inputAttrs?: Record<string, string>
}>()
const emit = defineEmits(['update:modelValue', 'edit', 'cancel'])

View File

@@ -14,6 +14,7 @@
<component
:is="item.headerComponent"
v-if="item.headerComponent"
v-bind="item.headerProps"
:id="item.key"
/>
<h3 v-else :id="item.key">

View File

@@ -0,0 +1,19 @@
<template>
<div
class="flex flex-col px-4 py-2 text-sm text-muted-foreground border-t border-border-default"
>
<p v-if="promptTextReal">
{{ promptTextReal }}
</p>
</div>
</template>
<script setup lang="ts">
import { computed, toValue } from 'vue'
import type { MaybeRefOrGetter } from 'vue'
const { promptText } = defineProps<{
promptText?: MaybeRefOrGetter<string>
}>()
const promptTextReal = computed(() => toValue(promptText))
</script>

View File

@@ -0,0 +1,43 @@
<template>
<section class="w-full flex gap-2 justify-end px-2 pb-2">
<TextButton
:label="cancelTextX"
:disabled
type="transparent"
autofocus
@click="$emit('cancel')"
/>
<TextButton
:label="confirmTextX"
:disabled
type="transparent"
:class="confirmClass"
@click="$emit('confirm')"
/>
</section>
</template>
<script setup lang="ts">
import { computed, toValue } from 'vue'
import type { MaybeRefOrGetter } from 'vue'
import { useI18n } from 'vue-i18n'
import TextButton from '@/components/button/TextButton.vue'
const { t } = useI18n()
const { cancelText, confirmText, confirmClass, optionsDisabled } = defineProps<{
cancelText?: string
confirmText?: string
confirmClass?: string
optionsDisabled?: MaybeRefOrGetter<boolean>
}>()
defineEmits<{
cancel: []
confirm: []
}>()
const confirmTextX = computed(() => confirmText || t('g.confirm'))
const cancelTextX = computed(() => cancelText || t('g.cancel'))
const disabled = computed(() => toValue(optionsDisabled))
</script>

View File

@@ -0,0 +1,12 @@
<template>
<div
class="flex items-center gap-2 p-4 font-bold text-sm text-base-foreground font-inter"
>
<span v-if="title" class="flex-auto">{{ title }}</span>
</div>
</template>
<script setup lang="ts">
defineProps<{
title?: string
}>()
</script>

View File

@@ -0,0 +1,31 @@
import ConfirmBody from '@/components/dialog/confirm/ConfirmBody.vue'
import ConfirmFooter from '@/components/dialog/confirm/ConfirmFooter.vue'
import ConfirmHeader from '@/components/dialog/confirm/ConfirmHeader.vue'
import { useDialogStore } from '@/stores/dialogStore'
import type { ComponentAttrs } from 'vue-component-type-helpers'
interface ConfirmDialogOptions {
headerProps?: ComponentAttrs<typeof ConfirmHeader>
props?: ComponentAttrs<typeof ConfirmBody>
footerProps?: ComponentAttrs<typeof ConfirmFooter>
}
export function showConfirmDialog(options: ConfirmDialogOptions = {}) {
const dialogStore = useDialogStore()
const { headerProps, props, footerProps } = options
return dialogStore.showDialog({
headerComponent: ConfirmHeader,
component: ConfirmBody,
footerComponent: ConfirmFooter,
headerProps,
props,
footerProps,
dialogComponentProps: {
pt: {
header: 'py-0! px-0!',
content: 'p-0!',
footer: 'p-0!'
}
}
})
}