Skip to content

Commit

Permalink
feat: add get data from server api
Browse files Browse the repository at this point in the history
  • Loading branch information
YunYouJun committed Feb 4, 2024
1 parent 650d251 commit 48b080d
Show file tree
Hide file tree
Showing 13 changed files with 175 additions and 24 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
OPENAI_API_KEY=
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# AI Couplets - AI 对联/春联

## Todo

- [ ] 分享春联

## Dev

```bash
Expand Down
26 changes: 23 additions & 3 deletions components/AiPrompt.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
<script lang="ts" setup>
import consola from 'consola'
const app = useAppStore()
async function generate() {
app.loading = true
const data = await $fetch('/api/generate', {
query: {
prompt: app.prompt,
},
})
consola.info(data)
app.setCoupletsData(data)
app.loading = false
}
</script>

<template>
Expand All @@ -8,12 +22,18 @@ const app = useAppStore()
v-model="app.prompt"
placeholder="想要什么样的春联?"
class="w-full rounded-lg p-4 shadow dark:bg-dark-800 outline-none!"
border="~ gray focus:(blue-500)"
border="~ gray focus:(yellow-500)"
maxlength="200"
/>

<button class="font-zmx w-full btn" text="black 2xl">
生成!
<button
class="font-zmx w-full btn" text="black 2xl"
:class="{ 'btn-disabled': app.loading }"
flex items-center justify-center
@click="generate"
>
{{ app.loading ? '生成中...' : '生成春联' }}
<div v-if="app.loading" class="i-svg-spinners:pulse ml-2" />
</button>
</div>
</template>
42 changes: 34 additions & 8 deletions components/SpringFestivalCouplets.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
<script lang="ts" setup>
import { toPng } from 'html-to-image'
import { downloadDataUrlAsImage } from '@yunlefun/utils'
import { SwitchRoot, SwitchThumb } from 'radix-vue'
import { suggestedCoupletsFilename } from '~/config'
import type { SprintFestivalCouplets } from '~/packages/ai/src'
defineProps<{
coupletsData: SprintFestivalCouplets
}>()
const app = useAppStore()
async function download() {
const container = document.getElementById('spring-festival-container')
Expand All @@ -21,27 +29,47 @@ async function download() {
<template>
<div id="spring-festival-container" flex="col" class="font-zmx spring-festival-container">
<div class="font-zmx m-auto bg-#ff0000 p-2" w="50" text="4xl black">
早上好
{{ coupletsData['横批'] }}
</div>
<div flex class="mt-4 items-center justify-between">
<div class="spring-festival-couplet">
春眠不觉晓
{{ coupletsData['上联'] }}
</div>

<div relative class="spring-festival-fu-container">
<div
relative class="spring-festival-fu-container transition duration-400"
:class="{
'rotate-180': app.inverseFu,
}"
>
<div class="spring-festival-fu" />
<!-- not inset-0 for compatibility -->
<span class="fu-char absolute bottom-0 left-0 right-0 top-0">
{{ coupletsData['总结'].slice(0, 1) }}
</span>
</div>

<div class="spring-festival-couplet">
处处闻啼鸟
{{ coupletsData['下联'] }}
</div>
</div>
</div>

<div class="font-zmx flex items-center justify-center gap-2" text="2xl" m="4">
<label class="select-none pr-[15px] leading-none" for="airplane-mode">
倒转福字
</label>
<SwitchRoot
id="airplane-mode"
v-model:checked="app.inverseFu"
class="relative h-[25px] w-[42px] flex cursor-default rounded-full bg-red-500 shadow-sm data-[state=checked]:bg-yellow-500 focus-within:outline-yellow focus-within:outline"
>
<SwitchThumb
class="my-auto block h-[21px] w-[21px] translate-x-0.5 rounded-full bg-white shadow-sm transition-transform duration-100 will-change-transform data-[state=checked]:translate-x-[19px]"
/>
</SwitchRoot>
</div>

<div class="font-zmx mt-4 flex" text="black" gap="2">
<button class="w-full btn" text="black" @click="download">
下载图片
Expand Down Expand Up @@ -71,10 +99,8 @@ async function download() {
font-size: calc(var(--ac-fu-font-size) - 0.5rem);
color: black;
transform: rotate(180deg);
.fu-char {
margin-top: -0.8rem;
margin-top: -1rem;
}
}
}
Expand Down
27 changes: 26 additions & 1 deletion composables/app.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,35 @@
import { acceptHMRUpdate, defineStore } from 'pinia'
import { useStorage } from '@vueuse/core'
import type { SprintFestivalCouplets } from '~/packages/ai/src'

const ns = 'ai-sfc'

export const useAppStore = defineStore('app', () => {
const prompt = ref('')
const loading = ref(false)
const prompt = useStorage(`${ns}:prompt`, '')

const coupletsData = useStorage<SprintFestivalCouplets>(`${ns}:couplets-data`, {
上联: '这里是上联',
下联: '这里是下联',
横批: '这里是横批',
总结: '福',
})

/**
* 是否反转福字
*/
const inverseFu = useStorage(`${ns}:inverse-fu`, true)

return {
loading,
prompt,
inverseFu,

coupletsData,

setCoupletsData(data: SprintFestivalCouplets) {
coupletsData.value = data
},
}
})

Expand Down
2 changes: 2 additions & 0 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { pwa } from './config/pwa'
import { appDescription } from './constants/index'

export default defineNuxtConfig({
ssr: false,

modules: [
'@vueuse/nuxt',
'@unocss/nuxt',
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@antfu/eslint-config": "^2.6.3",
"@iconify-json/carbon": "^1.1.27",
"@iconify-json/ri": "^1.1.19",
"@iconify-json/svg-spinners": "^1.1.2",
"@iconify-json/twemoji": "^1.1.15",
"@nuxtjs/color-mode": "^3.3.2",
"@pinia/nuxt": "^0.5.1",
Expand All @@ -34,6 +35,7 @@
"nuxt": "^3.9.3",
"nuxt-module-eslint-config": "^0.0.2",
"pinia": "^2.1.7",
"radix-vue": "^1.4.0",
"sass": "^1.70.0",
"tsx": "^4.7.0",
"typescript": "^5.3.3",
Expand Down
23 changes: 14 additions & 9 deletions packages/ai/src/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import consola from 'consola'
import type OpenAI from 'openai'
import { baseChatCompletionCreateParams, baseModel, openai } from './config'

export async function getCompletion(msg: string) {
Expand All @@ -20,9 +21,9 @@ export interface SprintFestivalCouplets {

export async function getCouplets(couplet: string) {
const tooltip = [
'请为我生成一组春联,包含上联、下联各一句,每句字数在五到十三字之间,并附上一个恰当的横批。',
'请根据我的提示生成一组春联,包含上联、下联各一句,每句字数在五到十三字之间,并附上一个恰当的横批。',
'并给出一个字总结。',
'尽量不要使用生僻字。',
'不需要标点符号,尽量不要使用生僻字。',
'以下述 JSON 给出:',
`export interface SprintFestivalCouplets {
上联: string
Expand All @@ -32,14 +33,18 @@ export async function getCouplets(couplet: string) {
}`,
]

const messages: OpenAI.ChatCompletionMessageParam[] = [
{
role: 'system',
content: tooltip.join('\n'),
},
]

if (couplet)
messages.push({ role: 'user', content: `我的提示是:${couplet}` })

const chatCompletion = await openai.chat.completions.create({
messages: [
{
role: 'system',
content: tooltip.join('\n'),
},
// { role: 'user', content: couplet },
],
messages,
model: 'deepseek-chat',
max_tokens: 300,
// stream: true
Expand Down
1 change: 1 addition & 0 deletions packages/ai/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './api'
export * from './cli'
5 changes: 3 additions & 2 deletions pages/index.vue
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
<script setup lang="ts">
// const online = useOnline()
const app = useAppStore()
</script>

<template>
<div>
<!-- <Logos mb-6 /> -->

<h2 class="font-zmx my-4" text="4xl">
<h2 class="font-zmx my-4" text="dark:yellow 4xl">
AI 春联
</h2>

<AiPrompt class="mb-4" />

<Suspense>
<ClientOnly>
<SpringFestivalCouplets />
<SpringFestivalCouplets :couplets-data="app.coupletsData" />
<!-- <PageView v-if="online" /> -->
<!-- <div v-else text-gray:80>
You're offline
Expand Down
51 changes: 51 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions server/api/generate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { SprintFestivalCouplets } from '~/packages/ai/src'
import { getCouplets } from '~/packages/ai/src'

export default defineEventHandler(async (event) => {
const query = getQuery(event)
const data = await getCouplets(query.prompt as string)
const { content } = data

const unwrapperContent = (content || '{}')?.replace('```json\n', '').replace('```', '')
const coupletData = JSON.parse(unwrapperContent) as SprintFestivalCouplets

return coupletData
})
2 changes: 1 addition & 1 deletion uno.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {

export default defineConfig({
shortcuts: [
['btn', 'text-xl px-4 py-2 rounded inline-block bg-yellow-500 text-white cursor-pointer hover:bg-yellow-600 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'],
['btn', 'text-xl px-4 py-2 rounded inline-block bg-yellow-500 text-white cursor-pointer focus:bg-yellow-600 disabled:cursor-default disabled:bg-gray-600 disabled:opacity-50'],
['icon-btn', 'inline-block cursor-pointer select-none opacity-75 transition duration-200 ease-in-out hover:opacity-100 hover:text-red-600'],
],
presets: [
Expand Down

0 comments on commit 48b080d

Please sign in to comment.