Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 30 additions & 65 deletions components/pages/posts/post/PostChatWithAi.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
<script lang="ts" setup>
import { ChatBubbleLeftEllipsisIcon, SparklesIcon } from '@heroicons/vue/24/outline'
import { flip, offset, shift, useFloating } from '@floating-ui/vue'
import { useChatWithAiReferral } from '~/composables/useAdvertisements'
import useAppStatistics from '~/composables/useAppStatistics'
import type { IPost } from '~/assets/js/post.dto'
import { ChatBubbleLeftEllipsisIcon } from '@heroicons/vue/24/outline'
import { useFloating, offset, flip, shift } from '@floating-ui/vue'
import { useChatWithAiReferral } from '~/composables/useAdvertisements'
import useAppStatistics from '~/composables/useAppStatistics'
import type { IPost } from '~/assets/js/post.dto'

const props = defineProps<{
const props = defineProps<{
tags: IPost['tags']
mediaType: IPost['media_type']
mediaUrl: string | null
}>()

const { chatWithAiReferralTemplate } = useChatWithAiReferral()
Expand All @@ -19,7 +17,7 @@ const props = defineProps<{

const { floatingStyles } = useFloating(referenceEl, floatingEl, {
placement: 'bottom-start',
middleware: [offset(6), flip({ fallbackPlacements: ['bottom-end'] }), shift()]
middleware: [offset(6), flip(), shift()]
})

const tagCandidates = computed(() => {
Expand All @@ -34,9 +32,7 @@ const props = defineProps<{
return Object.values(props.tags).flat()
})

const normalizedTags = computed(() => {
return Array.from(new Set(tagCandidates.value)).filter((tag) => typeof tag === 'string' && tag.trim().length > 0)
})
const normalizedTags = computed(() => Array.from(new Set(tagCandidates.value)))

function formatTagForQuery(tag: string) {
return tag.replaceAll('_', ' ')
Expand All @@ -63,14 +59,6 @@ const props = defineProps<{
return chatWithAiReferralTemplate.value.replace('{query}', encodeURIComponent(query))
}

const nud3Url = computed(() => {
if (props.mediaType !== 'image' || !props.mediaUrl) {
return null
}

return `https://nud3.me/pornify?imageUrl=${encodeURIComponent(props.mediaUrl)}&r=r34`
})

function onChatMenuOpen() {
if (tutorialChatWithAi.value) {
return
Expand All @@ -93,13 +81,12 @@ const props = defineProps<{
>
<ClientOnly>
<ChatBubbleLeftEllipsisIcon
aria-hidden="true"
:class="[
'group-hover:hover-text-util text-base-content h-5 w-5',
!tutorialChatWithAi ? 'chat-with-ai-glow-icon' : ''
]"
aria-hidden="true"
/>

<template #fallback>
<ChatBubbleLeftEllipsisIcon
aria-hidden="true"
Expand All @@ -120,59 +107,37 @@ const props = defineProps<{
:style="floatingStyles"
class="divide-base-0/20 bg-base-1000 ring-base-0/20 z-50 w-56 divide-y rounded-md ring-1 focus:outline-hidden"
>
<div class="text-base-content-highlight px-4 py-2 text-sm font-medium">Chat with AI</div>

<div
v-if="nud3Url"
v-if="!normalizedTags.length"
class="py-1"
>
<div class="text-base-content-highlight px-4 py-2 text-sm font-medium">Fuck with AI</div>
<span class="block px-4 py-2 text-sm"> No tags available </span>
</div>

<HeadlessMenuItem v-slot="{ active }">
<div
v-else
class="py-1"
>
<HeadlessMenuItem
v-for="tag in normalizedTags"
:key="tag"
v-slot="{ active }"
>
<NuxtLink
:class="[active ? 'bg-base-0/20 text-[#F0489C]' : 'text-[#F0489C]']"
:href="nud3Url"
class="group flex w-full items-center gap-2 px-4 py-2 text-sm"
rel="nofollow noopener"
:class="[active ? 'bg-base-0/20 text-base-content-highlight' : 'text-base-content']"
:href="buildReferralUrl(tag)"
class="group flex w-full items-center px-4 py-2 text-sm"
target="_blank"
rel="nofollow noopener"
>
<SparklesIcon
aria-hidden="true"
class="h-5 w-5"
/>
<span class="truncate font-bold">AI Video</span>
<span class="truncate">
{{ formatTagForDisplay(tag) }}
</span>
</NuxtLink>
</HeadlessMenuItem>
</div>

<div class="py-1">
<div class="text-base-content-highlight px-4 py-2 text-sm font-medium">Chat with AI</div>

<template v-if="normalizedTags.length > 0">
<HeadlessMenuItem
v-for="tag in normalizedTags"
:key="tag"
v-slot="{ active }"
>
<NuxtLink
:class="[active ? 'bg-base-0/20 text-base-content-highlight' : 'text-base-content']"
:href="buildReferralUrl(tag)"
class="group flex w-full items-center px-4 py-2 text-sm"
rel="nofollow noopener"
target="_blank"
>
<span class="truncate">
{{ formatTagForDisplay(tag) }}
</span>
</NuxtLink>
</HeadlessMenuItem>
</template>

<span
v-else
class="block px-4 py-2 text-sm"
>
No tags available
</span>
</div>
</HeadlessMenuItems>
</Transition>
</Teleport>
Expand Down
7 changes: 5 additions & 2 deletions components/pages/posts/post/PostComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,6 @@
<PostChatWithAi
v-if="!isPremium"
:tags="post.tags"
:mediaType="post.media_type"
:mediaUrl="post.high_res_file.url ?? post.low_res_file.url ?? (mediaFile.file as string)"
/>

<ShareButton
Expand All @@ -156,6 +154,11 @@
class="px-1.5 py-1"
/>

<PostNud3
v-if="post.media_type === 'image' && mediaFile.file"
:mediaUrl="post.high_res_file.url ?? post.low_res_file.url ?? (mediaFile.file as string)"
/>

<button
class="hover:hover-bg-util focus-visible:focus-outline-util group ml-auto flex items-center gap-1 rounded-md px-1.5 py-1"
type="button"
Expand Down
24 changes: 24 additions & 0 deletions components/pages/posts/post/PostNud3.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script lang="ts" setup>
import { SparklesIcon } from '@heroicons/vue/24/outline'

const props = defineProps({
mediaUrl: {
type: String,
required: true
}
})
Comment on lines +4 to +9
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Use TypeScript generic defineProps for consistency.

The surrounding components (PostChatWithAi.vue, PostComponent.vue) use the type-based defineProps<{...}>() form. The runtime-only object form here is inconsistent and loses some type-narrowing benefits. The props binding is also unused — the template references mediaUrl directly via <script setup> auto-unwrapping.

♻️ Proposed refactor
-  const props = defineProps({
-    mediaUrl: {
-      type: String,
-      required: true
-    }
-  })
+  defineProps<{
+    mediaUrl: string
+  }>()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const props = defineProps({
mediaUrl: {
type: String,
required: true
}
})
defineProps<{
mediaUrl: string
}>()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/pages/posts/post/PostNud3.vue` around lines 4 - 9, In PostNud3.vue
the runtime props object created by defineProps(...) is inconsistent with other
components and the resulting props binding (props) is unused; change to the
TypeScript generic form defineProps<{ mediaUrl: string }>() so the component
gets a typed mediaUrl via script-setup auto-unwrapping, and remove the unused
props variable (or replace its usage with the unwrapped mediaUrl) to keep the
file consistent with PostChatWithAi.vue and PostComponent.vue.

</script>

<template>
<a
aria-label="Generate AI Video"
title="Generate AI Video"
class="hover:hover-bg-util focus-visible:focus-outline-util group flex items-center gap-1 rounded-md px-1.5 py-1"
:href="`https://nud3.me/pornify?imageUrl=${encodeURIComponent(mediaUrl)}&r=r34`"
target="_blank"
rel="noopener noreferrer"
>
<SparklesIcon aria-hidden="true" class="group-hover:hover-text-util text-base-content h-5 w-5" />
<span class="group-hover:hover-text-util text-base-content text-xs font-bold">AI</span>
</a>
</template>
90 changes: 90 additions & 0 deletions pages/posts/[domain].vue
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,96 @@
throw new Error('One of your selected tags is in the tag block list')
}

return {
data: [
{
id: 1,
domain: selectedBooru.value.domain || 'rule34.xxx',
score: 100,
media_type: 'image',
rating: 'explicit',
high_res_file: { url: 'https://api-cdn.rule34.xxx/samples/7204/sample_fbe4e5361e96f38c4ac42719109a336a.jpg', width: 850, height: 1133 },
low_res_file: { url: 'https://api-cdn.rule34.xxx/samples/7204/sample_fbe4e5361e96f38c4ac42719109a336a.jpg', width: 850, height: 1133 },
preview_file: { url: 'https://api-cdn.rule34.xxx/samples/7204/sample_fbe4e5361e96f38c4ac42719109a336a.jpg', width: 400, height: 300 },
sources: ['https://example.com/gabagool'],
tags: {
artist: ['tony_soprano'],
character: ['carmela'],
copyright: ['the_sopranos'],
general: ['gabagool', 'satrialies'],
meta: ['high_res']
}
},
{
id: 2,
domain: selectedBooru.value.domain || 'rule34.xxx',
score: 95,
media_type: 'image',
rating: 'explicit',
high_res_file: { url: 'https://api-cdn.rule34.xxx/samples/1609/sample_ba218302fbcf459330af5aa6b0db1139.jpg', width: 850, height: 1202 },
low_res_file: { url: 'https://api-cdn.rule34.xxx/samples/1609/sample_ba218302fbcf459330af5aa6b0db1139.jpg', width: 850, height: 1202 },
preview_file: { url: 'https://api-cdn.rule34.xxx/samples/1609/sample_ba218302fbcf459330af5aa6b0db1139.jpg', width: 400, height: 300 },
sources: ['https://example.com/bada_bing'],
tags: {
artist: ['silvio_dante'],
character: ['tracee'],
copyright: ['the_sopranos'],
general: ['bada_bing', 'dancing'],
meta: ['high_res']
}
},
{
id: 3,
domain: selectedBooru.value.domain || 'rule34.xxx',
score: 85,
media_type: 'image',
rating: 'explicit',
high_res_file: { url: 'https://api-cdn.rule34.xxx/samples/5353/sample_2700439ce132b7127c08ed916a744ea1.jpg', width: 850, height: 1106 },
low_res_file: { url: 'https://api-cdn.rule34.xxx/samples/5353/sample_2700439ce132b7127c08ed916a744ea1.jpg', width: 850, height: 1106 },
preview_file: { url: 'https://api-cdn.rule34.xxx/samples/5353/sample_2700439ce132b7127c08ed916a744ea1.jpg', width: 400, height: 300 },
sources: ['https://example.com/pine_barrens'],
tags: {
artist: ['paulie_walnuts'],
character: ['valery'],
copyright: ['the_sopranos'],
general: ['snow', 'ketchup_packets'],
meta: ['high_res']
}
},
{
id: 4,
domain: selectedBooru.value.domain || 'rule34.xxx',
score: 90,
media_type: 'image',
rating: 'explicit',
high_res_file: { url: 'https://api-cdn.rule34.xxx/samples/1477/sample_85da4fe737e8e35f4b0d71e42340a4f9.jpg', width: 850, height: 1133 },
low_res_file: { url: 'https://api-cdn.rule34.xxx/samples/1477/sample_85da4fe737e8e35f4b0d71e42340a4f9.jpg', width: 850, height: 1133 },
preview_file: { url: 'https://api-cdn.rule34.xxx/samples/1477/sample_85da4fe737e8e35f4b0d71e42340a4f9.jpg', width: 400, height: 300 },
sources: ['https://example.com/ziti'],
tags: {
artist: ['christopher_moltisanti'],
character: ['adriana'],
copyright: ['the_sopranos'],
general: ['ziti', 'stork'],
meta: ['high_res']
}
}
],
meta: {
items_count: 4,
total_items: 4,
current_page: 1,
total_pages: 1,
items_per_page: 20
},
links: {
first: '',
last: '',
next: null,
prev: null
}
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

if (options.pageParam) {
return $fetch<IPostPage>(options.pageParam, {
retry: false
Expand Down