Skip to content

Commit

Permalink
refactor(fe2): Gendo pre-launch tidyup (#3794)
Browse files Browse the repository at this point in the history
* Styling updates

* Gendo Icon update

* Testing updates

* updates from testing

* Small design changes

* Fix build

* Fix dialog buttons

* Testing updates

* Lightbox effect

* Fix tippy

* Lightbox changes

* Updates

* Updates from testing

* Fix import

* Updates from testing

* Add mixpanel

* Reuse prompt

* Move reuse button

* Add v-tippy to truncated text

* Container Query

* Reorder buttons

* Copy prompt. Image loading

* Changes from testing

* Alert consolodation

* Final updates

* Feedback

* Typo

* Updates form call with Benjamin

* Controls to top

* Changes from testing

* Generic Feedback

* Small changes from testing

* Changes from Fabs

* Small change

* No max width on prompt

* Add Dialog Transparent story

* Open feedback dialog on click of button
  • Loading branch information
andrewwallacespeckle authored Jan 13, 2025
1 parent b55a9e5 commit 4483531
Show file tree
Hide file tree
Showing 10 changed files with 375 additions and 108 deletions.
4 changes: 0 additions & 4 deletions packages/frontend-2/assets/images/gendo/logo.svg

This file was deleted.

31 changes: 26 additions & 5 deletions packages/frontend-2/components/feedback/Dialog.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
<template>
<LayoutDialog
v-model:open="isOpen"
title="Give us feedback"
:title="dialogTitle"
:buttons="dialogButtons"
:on-submit="onSubmit"
max-width="md"
>
<div class="flex flex-col gap-2">
<p class="text-body-xs text-foreground font-medium">
How can we improve Speckle? If you have a feature request, please also share how
you would use it and why it's important to you
{{ dialogIntro }}
</p>
<FormTextArea
v-model="feedback"
Expand All @@ -18,7 +17,7 @@
label="Feedback"
color="foundation"
/>
<p class="text-body-xs !leading-4">
<p v-if="!hideSuppport" class="text-body-xs !leading-4">
Need help? For support, head over to our
<FormButton to="https://speckle.community/" target="_blank" link text>
community forum
Expand All @@ -39,8 +38,21 @@ import { isRequired } from '~/lib/common/helpers/validation'
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
import { defaultZapierWebhookUrl } from '~/lib/common/helpers/route'
type FeedbackType = 'general' | 'gendo'
type FormValues = { feedback: string }
const props = withDefaults(
defineProps<{
type?: FeedbackType
title?: string
intro?: string
hideSuppport?: boolean
}>(),
{
type: 'general'
}
)
const isOpen = defineModel<boolean>('open', { required: true })
const { activeUser: user } = useActiveUser()
Expand All @@ -60,6 +72,14 @@ const dialogButtons = computed((): LayoutDialogButton[] => [
}
])
const dialogTitle = computed(() => props.title || 'Give us feedback')
const dialogIntro = computed(
() =>
props.intro ||
'How can we improve Speckle? If you have a feature request, please also share how you would use it and why its important to you'
)
const onSubmit = handleSubmit(async () => {
if (!feedback.value) return
Expand All @@ -71,7 +91,8 @@ const onSubmit = handleSubmit(async () => {
})
mixpanel.track('Feedback Sent', {
message: feedback.value
message: feedback.value,
feedbackType: props.type
})
await sendWebhook(defaultZapierWebhookUrl, {
Expand Down
16 changes: 16 additions & 0 deletions packages/frontend-2/components/global/icon/Gendo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<svg
width="38"
height="37"
viewBox="0 0 38 37"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M30.5695 7.84167C30.4459 7.87905 30.4465 8.05327 30.5704 8.08974L30.6008 8.09869C32.0949 8.53857 33.2612 9.70436 33.6956 11.1923C33.7314 11.3148 33.9059 11.3148 33.9425 11.1925C34.386 9.71452 35.5462 8.55207 37.0303 8.10336L37.0652 8.09282C37.1885 8.05553 37.1877 7.88161 37.064 7.84544L37.0283 7.835C35.5338 7.39799 34.3679 6.23128 33.938 4.74249C33.9028 4.62035 33.7287 4.62043 33.6922 4.7422C33.249 6.21893 32.0893 7.38202 30.6065 7.83048L30.5695 7.84167ZM31.4317 23.8166L31.4317 14.0863H31.4318V10.3168H31.4317V10.3162L26.998 10.3162V10.3168H17.2469L11.255 16.849V26.5794H11.2549V30.349H11.255V30.3494H15.6888V30.349L25.4397 30.349L31.4317 23.8166ZM26.998 26.5794L26.998 14.0863H15.6888L15.6888 26.5794H26.998Z"
fill="currentColor"
/>
</svg>
</template>
10 changes: 4 additions & 6 deletions packages/frontend-2/components/viewer/Controls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -148,15 +148,13 @@
<!-- Gendo -->
<ViewerControlsButtonToggle
v-show="isGendoEnabled"
v-tippy="'Real time AI rendering powered by Gendo'"
v-tippy="'AI Render by Gendo'"
:active="activePanel === 'gendo'"
class="hover:hue-rotate-30 bg-[radial-gradient(ellipse_at_top_right,_var(--tw-gradient-stops))] from-amber-200 via-violet-600 to-sky-900"
@click="toggleActivePanel('gendo')"
>
<img
src="~/assets/images/gendo/logo.svg"
alt="gendo Logo"
class="h-6 w-6 md:h-8 md:w-8 -ml-1 -mt-1"
<IconGendo
class="h-6 w-6 md:h-7 md:w-7 -ml-1 -mt-1"
:class="activePanel === 'gendo' ? 'text-white' : 'text-foreground-2'"
/>
</ViewerControlsButtonToggle>
</div>
Expand Down
45 changes: 45 additions & 0 deletions packages/frontend-2/components/viewer/gendo/Dialog.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<template>
<LayoutDialog v-model:open="isOpen" max-width="xl" is-transparent>
<div class="relative flex flex-col items-center justify-center gap-2 w-full h-full">
<!-- Fullscreen button behind image to handle background clicks -->
<!-- that are still inside dialog and show loading state -->
<button
class="absolute inset-0 flex items-center justify-center"
@click="isOpen = false"
>
<CommonLoadingIcon />
</button>
<NuxtImg
:src="renderUrl"
:alt="renderPrompt"
class="relative z-10 w-full h-full max-h-[70vh] max-w-[80vw] object-contain"
/>
<div class="relative z-10 flex gap-2">
<FormButton
:to="renderUrl"
external
target="_blank"
download
color="outline"
:icon-left="ArrowDownTrayIcon"
>
Download
</FormButton>
<FormButton color="outline" :icon-left="XMarkIcon" @click="isOpen = false">
Close
</FormButton>
</div>
</div>
</LayoutDialog>
</template>

<script setup lang="ts">
import { XMarkIcon, ArrowDownTrayIcon } from '@heroicons/vue/24/solid'
defineProps<{
renderUrl?: string
renderPrompt?: string
}>()
const isOpen = defineModel<boolean>('open', { required: true })
</script>
148 changes: 124 additions & 24 deletions packages/frontend-2/components/viewer/gendo/Item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,55 @@
<div v-if="detailedRender">
<div class="relative">
<div v-if="detailedRender.status === 'COMPLETED' && renderUrl" class="group">
<img :src="renderUrl" alt="render" class="rounded-lg shadow" />
<div
class="absolute top-2 left-2 bg-foundation p-1 rounded opacity-0 group-hover:opacity-100 transition"
>
<!-- eslint-disable-next-line vuejs-accessibility/anchor-has-content -->
<a :href="renderUrl" target="_blank" title="download image">
<ArrowDownTrayIcon class="w-4" />
</a>
<button class="relative flex cursor-zoom-in" @click="openPreview">
<div
class="absolute inset-0 bg-highlight-3 flex items-center justify-center rounded-lg"
>
<CommonLoadingIcon />
</div>
<NuxtImg
:src="renderUrl"
:alt="detailedRender.prompt"
class="relative z-10 rounded-lg shadow aspect-video w-full object-cover"
/>
</button>
<div class="hidden group-hover:flex absolute top-2 left-2 gap-1 z-10">
<div v-tippy="`Set view`">
<FormButton
:icon-left="VideoCameraIcon"
hide-text
color="outline"
size="sm"
@click="setView()"
>
Set View
</FormButton>
</div>
<div v-tippy="`Copy prompt`">
<FormButton
:icon-left="ClipboardIcon"
hide-text
color="outline"
size="sm"
@click="copyPrompt"
>
Copy prompt
</FormButton>
</div>
<div v-tippy="`Download`">
<FormButton
:to="renderUrl"
external
target="_blank"
download
:icon-left="ArrowDownTrayIcon"
hide-text
color="outline"
size="sm"
>
Download
</FormButton>
</div>
</div>
</div>
<div
Expand All @@ -25,24 +66,35 @@
<ExclamationCircleIcon v-else class="w-6 text-danger" />
</div>
<div
class="absolute bottom-2 left-2 text-sm rounded-md pr-4 space-x-2 flex items-center min-w-0 max-w-full overflow-hidden"
class="absolute bottom-2 left-2 right-2 space-x-2 flex items-center min-w-0 max-w-full overflow-hidden z-10"
>
<div
class="bg-foundation p-2 flex items-center space-x-2 min-w-0 max-w-full rounded-md"
class="bg-foundation p-0.5 flex items-center gap-x-1 min-w-0 max-w-full rounded-md"
>
<UserAvatar :user="detailedRender.user" size="sm" />
<button
v-if="detailedRender.camera"
v-tippy="'Set view'"
class="mt-[2px] hover:text-blue-500 transition"
@click="setView()"
>
<VideoCameraIcon class="w-4" />
</button>
<span class="truncate max-w-full">{{ detailedRender.prompt }}</span>
<div v-tippy="capitalizedPrompt" class="truncate select-none text-body-2xs">
{{ capitalizedPrompt }}
</div>
<div v-tippy="`Reuse prompt`" class="shrink-0 h-6">
<FormButton
:icon-left="ArrowUturnUpIcon"
hide-text
color="subtle"
size="sm"
@click="reusePrompt"
>
Reuse prompt
</FormButton>
</div>
</div>
</div>
</div>
<FeedbackDialog v-model:open="isFeedbackOpen" :intro="feedbackIntro" type="gendo" />
<ViewerGendoDialog
v-model:open="isPreviewOpen"
:render-url="renderUrl"
:render-prompt="detailedRender.prompt"
/>
</div>
<div v-else />
</template>
Expand All @@ -57,22 +109,41 @@ import { useInjectedViewerState } from '~/lib/viewer/composables/setup'
import {
VideoCameraIcon,
ExclamationCircleIcon,
ArrowDownTrayIcon
ArrowDownTrayIcon,
ArrowUturnUpIcon,
ClipboardIcon
} from '@heroicons/vue/24/outline'
import { useCameraUtilities } from '~/lib/viewer/composables/ui'
import { Vector3 } from 'three'
import { CommonLoadingIcon } from '@speckle/ui-components'
import { useMixpanel } from '~/lib/core/composables/mp'
import { upperFirst } from 'lodash-es'
const props = defineProps<{
renderRequest: GendoAiRender
}>()
const emit = defineEmits<{
(e: 'reuse-prompt', prompt: string): void
}>()
const {
projectId,
resources: {
response: { resourceItems }
}
} = useInjectedViewerState()
const { copy } = useClipboard()
const { triggerNotification } = useGlobalToast()
const isPreviewOpen = ref(false)
const isFeedbackOpen = ref(false)
const feedbackIntro = ref(
'How can we improve the AI rendering experience? Let us know about the quality of renders, prompts that you have had success with, or any features that would make Gendo more useful for your workflow'
)
const versionId = computed(() => {
return resourceItems.value[0].versionId as string
})
Expand All @@ -92,20 +163,33 @@ onRenderUpdated(() => {
refetch()
})
const detailedRender = computed(() => result.value?.project?.version?.gendoAIRender)
const { setView: setViewInternal } = useCameraUtilities()
const mixpanel = useMixpanel()
const apiOrigin = useApiOrigin()
const detailedRender = computed(() => result.value?.project?.version?.gendoAIRender)
const renderUrl = computed(() => {
if (detailedRender.value?.status !== 'COMPLETED') return null
if (detailedRender.value?.status !== 'COMPLETED') return undefined
const url = new URL(
`/api/stream/${projectId.value}/blob/${detailedRender.value?.responseImage}`,
apiOrigin
)
return url.toString()
})
const capitalizedPrompt = computed(() => {
return upperFirst(detailedRender.value?.prompt)
})
const reusePrompt = () => {
mixpanel.track('Gendo Prompt Reused', {
renderId: detailedRender.value?.id,
prompt: detailedRender.value?.prompt
})
emit('reuse-prompt', capitalizedPrompt.value || '')
}
const setView = () => {
const cam = detailedRender.value?.camera as { target: Vector3; position: Vector3 }
Expand All @@ -117,4 +201,20 @@ const setView = () => {
true
)
}
const openPreview = () => {
mixpanel.track('Gendo Render Preview Opened', {
renderId: detailedRender.value?.id,
prompt: detailedRender.value?.prompt
})
isPreviewOpen.value = true
}
const copyPrompt = async () => {
await copy(capitalizedPrompt.value)
triggerNotification({
type: ToastNotificationType.Info,
title: 'Prompt copied to clipboard'
})
}
</script>
Loading

0 comments on commit 4483531

Please sign in to comment.