Compare commits
7 Commits
9336098cd9
...
03e1e5662d
| Author | SHA1 | Date | |
|---|---|---|---|
| 03e1e5662d | |||
| 0df00ba33e | |||
| f9b031e8da | |||
| ccdbbabb1e | |||
| 7181da5b7b | |||
| 7b056d476d | |||
| e8c034937e |
14
README.md
14
README.md
@ -1,2 +1,16 @@
|
||||
# LingTropy
|
||||
|
||||
减小包体
|
||||
yarn global add depcheck
|
||||
depcheck
|
||||
|
||||
然后yarn remove <unused-package>
|
||||
|
||||
还有配置修改
|
||||
module.exports = {
|
||||
files: [
|
||||
"dist/**/*", // 只打包 dist 目录
|
||||
"!node_modules/**/*" // 排除 node_modules
|
||||
],
|
||||
asar: true, // 使用 asar 压缩
|
||||
};
|
||||
@ -3,7 +3,7 @@ const dotenv = require('dotenv')
|
||||
const packageJson = require('../../package.json')
|
||||
|
||||
const baseConfig = {
|
||||
productName: packageJson.name,
|
||||
productName: '文案助手',
|
||||
appId: packageJson.appId,
|
||||
asar: true,
|
||||
extends: null,
|
||||
|
||||
36
lingtropy-client/package-lock.json
generated
36
lingtropy-client/package-lock.json
generated
@ -1,27 +1,28 @@
|
||||
{
|
||||
"name": "lingtropy",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "lingtropy",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.2",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mdi/font": "^7.4.47",
|
||||
"axios": "^1.8.3",
|
||||
"electron-store": "^10.0.1",
|
||||
"lingtropy": "file:",
|
||||
"lingtropy2": "file:",
|
||||
"pinia": "^3.0.1",
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "^11.1.1",
|
||||
"vue-router": "^4.5.0",
|
||||
"vuetify": "^3.7.14",
|
||||
"vutron": "file:"
|
||||
"vuetify": "^3.7.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@types/electron-store": "^3.2.2",
|
||||
"@typescript-eslint/eslint-plugin": "7.16.1",
|
||||
"@typescript-eslint/parser": "7.16.1",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
@ -41,7 +42,7 @@
|
||||
"playwright": "^1.50.1",
|
||||
"prettier": "^3.5.2",
|
||||
"tree-kill": "^1.2.2",
|
||||
"typescript": "5.7.3",
|
||||
"typescript": "^5.8.2",
|
||||
"vite": "^6.2.0",
|
||||
"vite-plugin-electron": "^0.29.0",
|
||||
"vite-plugin-electron-renderer": "^0.14.6",
|
||||
@ -1321,6 +1322,17 @@
|
||||
"@types/ms": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/electron-store": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/electron-store/-/electron-store-3.2.2.tgz",
|
||||
"integrity": "sha512-N3X45mnsfnwmeZoXSZmeE7/Tne8kdbIKO1vQdbbEV04TzrMbWIeDVJJjnX2n5GH9O61zI612tet4s2jCZ55DXw==",
|
||||
"deprecated": "This is a stub types definition. electron-store provides its own type definitions, so you do not need this installed.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"electron-store": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/eslint": {
|
||||
"version": "8.56.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.11.tgz",
|
||||
@ -6333,6 +6345,10 @@
|
||||
"resolved": "",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/lingtropy2": {
|
||||
"resolved": "",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||
@ -8697,9 +8713,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.7.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
||||
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
||||
"version": "5.8.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
|
||||
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
@ -9151,10 +9167,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vutron": {
|
||||
"resolved": "",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/wcwidth": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "lingtropy",
|
||||
"appId": "com.lingnite.lingtropy",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.2",
|
||||
"description": "文案助手",
|
||||
"homepage": "https://www.lingnite.com",
|
||||
"author": "沈阳泠启网络科技有限公司",
|
||||
@ -45,6 +45,8 @@
|
||||
"@mdi/font": "^7.4.47",
|
||||
"axios": "^1.8.3",
|
||||
"electron-store": "^10.0.1",
|
||||
"lingtropy": "file:",
|
||||
"lingtropy2": "file:",
|
||||
"pinia": "^3.0.1",
|
||||
"vue": "^3.5.13",
|
||||
"vue-i18n": "^11.1.1",
|
||||
@ -53,6 +55,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.50.1",
|
||||
"@types/electron-store": "^3.2.2",
|
||||
"@typescript-eslint/eslint-plugin": "7.16.1",
|
||||
"@typescript-eslint/parser": "7.16.1",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
@ -72,7 +75,7 @@
|
||||
"playwright": "^1.50.1",
|
||||
"prettier": "^3.5.2",
|
||||
"tree-kill": "^1.2.2",
|
||||
"typescript": "5.7.3",
|
||||
"typescript": "^5.8.2",
|
||||
"vite": "^6.2.0",
|
||||
"vite-plugin-electron": "^0.29.0",
|
||||
"vite-plugin-electron-renderer": "^0.14.6",
|
||||
|
||||
42
lingtropy-client/src/constants/index.ts
Normal file
42
lingtropy-client/src/constants/index.ts
Normal file
@ -0,0 +1,42 @@
|
||||
// API 相关
|
||||
export const API_ENDPOINTS = {
|
||||
GET_BASE_URL: 'https://aqq-jbjsjuxivc.cn-hangzhou.fcapp.run',
|
||||
FETCH_MODELS: 'https://get-model-list-vcwjgnvcld.cn-hangzhou.fcapp.run'
|
||||
} as const
|
||||
|
||||
// AI 模型相关
|
||||
export const AI_CONFIG = {
|
||||
DEFAULT_MODEL: 'gpt-4o',
|
||||
CONTENT_MAX_TOKENS: 1800, // 约900字
|
||||
TITLE_MAX_TOKENS: 50, // 约20字
|
||||
TEMPERATURE: 0.8,
|
||||
MAX_RETRIES: 2,
|
||||
TIMEOUT: 30000
|
||||
} as const
|
||||
|
||||
// 提示词模板
|
||||
export const PROMPTS = {
|
||||
CONTENT_REWRITE: '你是一个小红书文案写手,能够熟练地根据用户的输入,改写成内容相近,但表达方式不同的新文案。你的文案中需要具备吸人眼球的钩子,能够牢牢抓住用户的注意力。请直接输出新的文案,不要输出其他任何提示性词语, 以纯文本的形式输出。注意:输出的文案不要超过900字。',
|
||||
TITLE_REWRITE: '你是一个小红书标题写手,能够熟练地根据用户的输入,改写成内容相近,但表达方式不同的新标题。你的标题中需要具备吸人眼球的钩子,能够牢牢抓住用户的注意力。请直接输出新的标题,不要输出其他任何提示性词语, 以纯文本的形式输出。注意:输出的标题不要超过20字。'
|
||||
} as const
|
||||
|
||||
// 存储键名
|
||||
export const STORE_KEYS = {
|
||||
API_KEY: 'api-key',
|
||||
LAYOUT_MODE: 'layoutMode',
|
||||
GENERATION_COUNT: 'generationCount'
|
||||
} as const
|
||||
|
||||
// UI 相关
|
||||
export const UI_CONFIG = {
|
||||
DEFAULT_LAYOUT: 'list',
|
||||
DEFAULT_COUNT: 3,
|
||||
MAX_COUNT: 10,
|
||||
MIN_COUNT: 1
|
||||
} as const
|
||||
|
||||
// 应用信息
|
||||
// export const APP_INFO = {
|
||||
// VERSION: '1.0.0',
|
||||
// NAME: 'LingTropy'
|
||||
// } as const
|
||||
@ -3,7 +3,14 @@ import Constants from './utils/Constants'
|
||||
import Store from 'electron-store'
|
||||
import axios from 'axios'
|
||||
import OpenAI from 'openai'
|
||||
import { HttpsProxyAgent } from 'https-proxy-agent'
|
||||
import {
|
||||
API_ENDPOINTS,
|
||||
AI_CONFIG,
|
||||
PROMPTS,
|
||||
STORE_KEYS,
|
||||
APP_INFO
|
||||
} from '../constants'
|
||||
|
||||
const store = new Store()
|
||||
/*
|
||||
* IPC Communications
|
||||
@ -20,6 +27,7 @@ const store = new Store()
|
||||
// }
|
||||
// }
|
||||
// const baseUrl = ""
|
||||
|
||||
export default class IPCs {
|
||||
static initialize(): void {
|
||||
// Get application version
|
||||
@ -37,7 +45,7 @@ export default class IPCs {
|
||||
|
||||
ipcMain.handle('fetch-models', async () => {
|
||||
try {
|
||||
const response = await axios.get('https://get-model-list-vcwjgnvcld.cn-hangzhou.fcapp.run') // 从网络获取数据
|
||||
const response = await axios.get(API_ENDPOINTS.FETCH_MODELS)
|
||||
console.log('获取模型数据成功:', response.data)
|
||||
return response.data.data
|
||||
} catch (error) {
|
||||
@ -48,7 +56,7 @@ export default class IPCs {
|
||||
|
||||
ipcMain.handle('getBaseUrl', async () => {
|
||||
try {
|
||||
const response = await axios.get('https://aqq-jbjsjuxivc.cn-hangzhou.fcapp.run')
|
||||
const response = await axios.get(API_ENDPOINTS.GET_BASE_URL)
|
||||
console.log('获取baseUrl成功:', response.data)
|
||||
return response.data.data
|
||||
} catch (error) {
|
||||
@ -70,11 +78,10 @@ export default class IPCs {
|
||||
ipcMain.handle('call-openai', async (event, baseURL, apiKey, model, count, rawArticle) => {
|
||||
try {
|
||||
const client = new OpenAI({
|
||||
apiKey: apiKey, // 从环境变量中获取API密钥
|
||||
baseURL: baseURL // 设置代理地址
|
||||
apiKey: apiKey,
|
||||
baseURL: baseURL
|
||||
})
|
||||
|
||||
// 创建 count 个请求
|
||||
const requests = Array.from({ length: count }, (_, index) => {
|
||||
return client.chat.completions
|
||||
.create({
|
||||
@ -82,12 +89,13 @@ export default class IPCs {
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content:
|
||||
'你是一个小红书文案写手,能够熟练地根据用户的输入,改写成内容相近,但表达方式不同的新文案。你的文案中需要具备吸人眼球的钩子,能够牢牢抓住用户的注意力。请直接输出新的文案,不要输出其他任何提示性词语, 以纯文本的形式输出'
|
||||
content: PROMPTS.CONTENT_REWRITE
|
||||
},
|
||||
{ role: 'user', content: rawArticle }
|
||||
],
|
||||
stream: true // 启用流式输出
|
||||
max_tokens: AI_CONFIG.CONTENT_MAX_TOKENS,
|
||||
temperature: AI_CONFIG.TEMPERATURE,
|
||||
stream: true
|
||||
})
|
||||
.then(async (stream) => {
|
||||
let fullResponse = ''
|
||||
@ -113,6 +121,57 @@ export default class IPCs {
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.handle(
|
||||
'call-openai-title',
|
||||
async (event, baseUrl: string, apiKey: string, model: string, count: number, text: string) => {
|
||||
try {
|
||||
const client = new OpenAI({
|
||||
baseURL: baseUrl,
|
||||
apiKey: apiKey,
|
||||
maxRetries: 2,
|
||||
timeout: 30000
|
||||
})
|
||||
|
||||
const requests = Array.from({ length: count }, (_, index) => {
|
||||
return client.chat.completions
|
||||
.create({
|
||||
model: model,
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: '你是一个小红书标题写手,能够熟练地根据用户的输入,改写成内容相近,但表达方式不同的新标题。你的标题中需要具备吸人眼球的钩子,能够牢牢抓住用户的注意力。请直接输出新的标题,不要输出其他任何提示性词语, 以纯文本的形式输出。注意:输出的标题不要超过20字。'
|
||||
},
|
||||
{ role: 'user', content: text }
|
||||
],
|
||||
max_tokens: 50, // 约20字
|
||||
temperature: 0.8,
|
||||
stream: true
|
||||
})
|
||||
.then(async (stream) => {
|
||||
let fullResponse = ''
|
||||
for await (const chunk of stream) {
|
||||
const content = chunk.choices[0].delta.content || ''
|
||||
fullResponse += content
|
||||
// 实时发送每个请求的部分结果
|
||||
event.sender.send('openai-partial-response', { index, content })
|
||||
}
|
||||
// 返回最终结果
|
||||
return { index, response: fullResponse.trim() }
|
||||
})
|
||||
})
|
||||
|
||||
// 并发执行所有请求
|
||||
const results = await Promise.all(requests)
|
||||
|
||||
// 返回所有请求的最终结果
|
||||
return results
|
||||
} catch (error) {
|
||||
console.error('生成标题失败:', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
ipcMain.handle('get-api-key', () => {
|
||||
return store.get('api-key') || null
|
||||
})
|
||||
@ -144,5 +203,7 @@ export default class IPCs {
|
||||
})
|
||||
return dialogResult
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,13 +10,14 @@ import { createTray, hideWindow, showWindow } from './tray.ts'
|
||||
|
||||
const options = {
|
||||
width: Constants.IS_DEV_ENV ? 1500 : 1250,
|
||||
height: 850,
|
||||
height: 900,
|
||||
tray: {
|
||||
// all optional values from DEFAULT_TRAY_OPTIONS can de defined here
|
||||
enabled: true,
|
||||
menu: false, // true, to use a tray menu ; false to toggle visibility on click on tray icon
|
||||
trayWindow: false // true, to use a tray floating window attached to top try icon
|
||||
}
|
||||
},
|
||||
title: '文案助手'
|
||||
}
|
||||
|
||||
const exitApp = (mainWindow: BrowserWindow): void => {
|
||||
|
||||
@ -19,6 +19,7 @@ app.on('ready', async () => {
|
||||
*/
|
||||
|
||||
mainWindow = await createMainWindow()
|
||||
// mainWindow.setTile("23")
|
||||
})
|
||||
|
||||
app.on('activate', async () => {
|
||||
|
||||
@ -15,7 +15,7 @@ export interface TrayOptions {
|
||||
|
||||
export default class Constants {
|
||||
// Display app name (uppercase first letter)
|
||||
static APP_NAME = name.charAt(0).toUpperCase() + name.slice(1)
|
||||
static APP_NAME = '文案助手' //name.charAt(0).toUpperCase() + name.slice(1)
|
||||
|
||||
static APP_VERSION = version
|
||||
|
||||
|
||||
@ -12,7 +12,8 @@ const mainAvailChannels: string[] = [
|
||||
'call-openai',
|
||||
'openai-partial-response',
|
||||
'get-api-key',
|
||||
'set-api-key'
|
||||
'set-api-key',
|
||||
'call-openai-title'
|
||||
]
|
||||
const rendererAvailChannels: string[] = ['openai-partial-response', 'get-api-key', 'set-api-key']
|
||||
|
||||
|
||||
193
lingtropy-client/src/renderer/components/DisclaimerComponent.vue
Normal file
193
lingtropy-client/src/renderer/components/DisclaimerComponent.vue
Normal file
@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<!-- 免责声明弹窗 -->
|
||||
<div v-if="showDisclaimer" class="disclaimer-modal">
|
||||
<div class="disclaimer-content">
|
||||
<h2 class="disclaimer-title">免责声明</h2>
|
||||
<div class="disclaimer-text">
|
||||
<p>您正在使用本软件,请您仔细阅读以下内容并确认:</p>
|
||||
<ol>
|
||||
<li
|
||||
>根据小红书平台的相关要求,通过反复发布重复、近似交易笔记,或发布大量发布工业化、低质量创作的交易笔记等方式获取流量的行为系违规行为。</li
|
||||
>
|
||||
<li>本软件充分尊重原创者的著作权等知识产权,仅为用户提供参考素材与灵感。</li>
|
||||
<li
|
||||
>本软件郑重提醒用户:请各用户遵守知识产权相关规定,秉持“真诚分享”精神,自行对在小红书平台及其他社交媒体平台发布的内容进行判断、负责,不要直接搬运原创作品,严禁用于辅助违规后端操作引流。</li
|
||||
>
|
||||
<li>对用户违反本声明,所产生的后果,炬梦私域与泠启科技不承担任何责任。</li>
|
||||
<li>本软件使用的ai仅供内部测试,严禁生成违背各种法律法规或公序良俗的内容。</li>
|
||||
</ol>
|
||||
<p>继续使用本工具,即表示您已阅读并同意上述声明。</p>
|
||||
</div>
|
||||
<div class="disclaimer-actions">
|
||||
<label class="disclaimer-checkbox">
|
||||
<input type="checkbox" v-model="doNotShowAgain" />
|
||||
<span>不再显示</span>
|
||||
</label>
|
||||
<button @click="acceptDisclaimer" class="disclaimer-button">我已阅读并同意</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
// 免责声明状态
|
||||
const showDisclaimer = ref(false)
|
||||
const doNotShowAgain = ref(false)
|
||||
|
||||
// 检查是否需要显示免责声明
|
||||
const checkDisclaimerStatus = async () => {
|
||||
try {
|
||||
const hideDisclaimer = await window.mainApi.invoke('store-get', 'hideDisclaimer')
|
||||
showDisclaimer.value = !hideDisclaimer
|
||||
// showDisclaimer.value = true
|
||||
// console.log('显示免责声明:', showDisclaimer.value)
|
||||
} catch (err) {
|
||||
// console.error('获取免责声明状态失败:', err)
|
||||
showDisclaimer.value = true // 默认显示
|
||||
}
|
||||
}
|
||||
|
||||
// 接受免责声明
|
||||
const acceptDisclaimer = async () => {
|
||||
if (doNotShowAgain.value) {
|
||||
try {
|
||||
await window.mainApi.invoke('store-set', 'hideDisclaimer', true)
|
||||
} catch (err) {
|
||||
console.error('保存免责声明偏好失败:', err)
|
||||
}
|
||||
}
|
||||
showDisclaimer.value = false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
checkDisclaimerStatus()
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.disclaimer-modal {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 90;
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
.disclaimer-content {
|
||||
background-color: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
width: 90%;
|
||||
max-width: 600px;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
padding: 30px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.disclaimer-title {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
margin: 0 0 20px 0;
|
||||
color: #2ecc71;
|
||||
text-align: center;
|
||||
border-bottom: 2px solid #f0f0f0;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.disclaimer-text {
|
||||
margin-bottom: 25px;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.disclaimer-text p {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.disclaimer-text ol {
|
||||
padding-left: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.disclaimer-text li {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.disclaimer-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 20px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.disclaimer-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.disclaimer-checkbox input {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.disclaimer-button {
|
||||
background: linear-gradient(135deg, #2ecc71, #27ae60);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
padding: 12px 24px;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 12px rgba(46, 204, 113, 0.3);
|
||||
}
|
||||
|
||||
.disclaimer-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(46, 204, 113, 0.4);
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.disclaimer-content {
|
||||
width: 95%;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.disclaimer-actions {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.disclaimer-checkbox {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.disclaimer-button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.disclaimer-content {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.disclaimer-title {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
52
lingtropy-client/src/renderer/components/FooterComponent.vue
Normal file
52
lingtropy-client/src/renderer/components/FooterComponent.vue
Normal file
@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div class="company-footer">
|
||||
<div class="company-footer-content">
|
||||
<p>炬梦私域X泠启科技</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 组件逻辑
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.company-footer {
|
||||
position: fixed;
|
||||
margin-top: 10px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
padding: 15px 0;
|
||||
text-align: center;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.company-footer-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.company-footer-content p {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 2px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.company-footer {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.company-footer-content p {
|
||||
font-size: 17px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -12,7 +12,7 @@
|
||||
<div class="logo-icon">AI</div>
|
||||
</div>
|
||||
<div class="header-content">
|
||||
<h1 class="title">文案生成工具</h1>
|
||||
<h1 class="title">文案助手</h1>
|
||||
<p class="subtitle">智能改写,提升文案质量</p>
|
||||
</div>
|
||||
<button @click="toggleSettings" class="settings-button">
|
||||
@ -46,7 +46,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="copyright"> © 2025 沈阳泠启网络科技有限公司版权所有 </div>
|
||||
<div class="copyright"> 仅供测试开发使用,严禁对外销售 </div>
|
||||
<div class="button-group">
|
||||
<button @click="toggleSettings" class="cancel-button">取消</button>
|
||||
<button @click="saveSettings" class="save-button">保存设置</button>
|
||||
|
||||
468
lingtropy-client/src/renderer/components/InputSection.vue
Normal file
468
lingtropy-client/src/renderer/components/InputSection.vue
Normal file
@ -0,0 +1,468 @@
|
||||
<template>
|
||||
<div class="input-section">
|
||||
<HeaderComponent v-model:tokenvalue="currentKey" />
|
||||
<SelectModel v-model:modelvalue="currentModel" />
|
||||
|
||||
<div class="rewrite-mode-section">
|
||||
<h2 class="section-title">
|
||||
<span class="section-icon">⚙️</span>
|
||||
改写模式
|
||||
</h2>
|
||||
<div class="mode-tabs">
|
||||
<button
|
||||
:class="{ active: mode === 'single' }"
|
||||
@click="switchMode('single')"
|
||||
class="mode-tab"
|
||||
>
|
||||
<span class="mode-icon">🔄</span>
|
||||
单次改写
|
||||
</button>
|
||||
<button
|
||||
:class="{ active: mode === 'batch' }"
|
||||
@click="switchMode('batch')"
|
||||
class="mode-tab"
|
||||
>
|
||||
<span class="mode-icon">🔀</span>
|
||||
批量改写
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<transition name="fade">
|
||||
<div v-if="mode === 'batch'" class="generation-count">
|
||||
<label for="count-input">生成数量:</label>
|
||||
<div class="count-control">
|
||||
<button
|
||||
:disabled="generationCount <= 1"
|
||||
@click="decrementCount"
|
||||
class="count-button"
|
||||
>-</button>
|
||||
<input
|
||||
id="count-input"
|
||||
v-model="generationCount"
|
||||
@input="validateInput"
|
||||
type="number"
|
||||
class="count-input"
|
||||
/>
|
||||
<button
|
||||
:disabled="generationCount >= ARTICLE_MAX_COUNT"
|
||||
@click="incrementCount"
|
||||
class="count-button"
|
||||
>+</button>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
<div class="original-text-section">
|
||||
<h2 class="section-title">
|
||||
<span class="section-icon">📝</span>
|
||||
原始文案
|
||||
<span v-if="originalText" class="char-count">{{ originalText.length }} 字</span>
|
||||
</h2>
|
||||
<textarea
|
||||
v-model="originalText"
|
||||
class="text-input"
|
||||
placeholder="请在此输入需要改写的文本..."
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button
|
||||
:disabled="isGenerating || isGeneratingTitle"
|
||||
@click="handleRewrite('content')"
|
||||
class="rewrite-button"
|
||||
>
|
||||
<span v-if="isGenerating" class="loading-spinner"></span>
|
||||
<span v-else>改写文案</span>
|
||||
</button>
|
||||
<button
|
||||
:disabled="isGenerating || isGeneratingTitle"
|
||||
@click="handleRewrite('title')"
|
||||
class="rewrite-button title-button"
|
||||
>
|
||||
<span v-if="isGeneratingTitle" class="loading-spinner"></span>
|
||||
<span v-else>改写标题</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import { ARTICLE_MAX_COUNT } from '../constants/constants'
|
||||
import HeaderComponent from './HeaderComponent.vue'
|
||||
import SelectModel from './SelectModelComponent.vue'
|
||||
|
||||
const { isGenerating, isGeneratingTitle } = defineProps({
|
||||
isGenerating: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isGeneratingTitle: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['rewrite', 'update:model'])
|
||||
|
||||
const mode = ref('single')
|
||||
const originalText = ref('')
|
||||
const generationCount = ref(3)
|
||||
const currentModel = ref('gpt-4o')
|
||||
const currentKey = ref('')
|
||||
|
||||
onMounted(async () => {
|
||||
// 从存储中获取生成数量
|
||||
const count = await window.mainApi.invoke('store-get', 'generationCount')
|
||||
if (count) {
|
||||
generationCount.value = count
|
||||
}
|
||||
})
|
||||
|
||||
watch(currentModel, (newValue) => {
|
||||
emit('update:model', newValue)
|
||||
})
|
||||
|
||||
const validateInput = () => {
|
||||
if (!originalText.value.trim()) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (generationCount.value < 1) {
|
||||
setGenerationCount(1)
|
||||
} else if (generationCount.value > ARTICLE_MAX_COUNT) {
|
||||
setGenerationCount(ARTICLE_MAX_COUNT)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const setGenerationCount = async (value) => {
|
||||
generationCount.value = value
|
||||
await window.mainApi.invoke('store-set', 'generationCount', value)
|
||||
}
|
||||
|
||||
const switchMode = (newMode) => {
|
||||
if (mode.value !== newMode) {
|
||||
mode.value = newMode
|
||||
}
|
||||
}
|
||||
|
||||
const incrementCount = () => {
|
||||
if (generationCount.value < ARTICLE_MAX_COUNT) {
|
||||
setGenerationCount(generationCount.value + 1)
|
||||
}
|
||||
}
|
||||
|
||||
const decrementCount = () => {
|
||||
if (generationCount.value > 1) {
|
||||
setGenerationCount(generationCount.value - 1)
|
||||
}
|
||||
}
|
||||
|
||||
const handleRewrite = async (type) => {
|
||||
if (!validateInput()) {
|
||||
showToast('请输入需要改写的文本')
|
||||
return
|
||||
}
|
||||
|
||||
emit('rewrite', {
|
||||
mode: mode.value,
|
||||
text: originalText.value,
|
||||
count: mode.value === 'single' ? 1 : generationCount.value,
|
||||
model: currentModel.value,
|
||||
key: currentKey.value,
|
||||
type
|
||||
})
|
||||
}
|
||||
|
||||
const showToast = (message) => {
|
||||
const toast = document.createElement('div')
|
||||
toast.className = 'toast-message'
|
||||
toast.textContent = message
|
||||
document.body.appendChild(toast)
|
||||
|
||||
setTimeout(() => {
|
||||
toast.classList.add('show')
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('show')
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(toast)
|
||||
}, 300)
|
||||
}, 2000)
|
||||
}, 10)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.input-section {
|
||||
flex: 0 0 40%;
|
||||
max-width: 600px;
|
||||
min-width: 320px;
|
||||
background-color: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
padding: 28px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
transition: all 0.3s ease;
|
||||
backdrop-filter: blur(10px);
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 16px 0;
|
||||
color: #333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.section-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.rewrite-mode-section {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.mode-tabs {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.mode-tab {
|
||||
background-color: white;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 10px;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
flex: 1;
|
||||
font-size: 16px;
|
||||
padding: 14px;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.mode-tab:hover {
|
||||
border-color: #2ecc71;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.mode-tab.active {
|
||||
background: linear-gradient(135deg, #2ecc71, #27ae60);
|
||||
border-color: transparent;
|
||||
color: white;
|
||||
box-shadow: 0 4px 12px rgba(46, 204, 113, 0.3);
|
||||
}
|
||||
|
||||
.mode-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.generation-count {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
gap: 16px;
|
||||
padding: 16px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 10px;
|
||||
border-left: 4px solid #2ecc71;
|
||||
}
|
||||
|
||||
.count-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.count-button {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: 1px solid #e0e0e0;
|
||||
background-color: white;
|
||||
color: #333;
|
||||
font-size: 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.count-button:first-child {
|
||||
border-radius: 8px 0 0 8px;
|
||||
}
|
||||
|
||||
.count-button:last-child {
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
|
||||
.count-button:hover:not(:disabled) {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.count-button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.count-input {
|
||||
border: 1px solid #e0e0e0;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
padding: 8px 0;
|
||||
width: 50px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.original-text-section {
|
||||
margin-bottom: 24px;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.char-count {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
font-weight: normal;
|
||||
margin-left: auto;
|
||||
background-color: #f0f0f0;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.text-input {
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 10px;
|
||||
font-family: inherit;
|
||||
font-size: 16px;
|
||||
padding: 16px;
|
||||
resize: none;
|
||||
width: 100%;
|
||||
flex-grow: 1;
|
||||
min-height: 150px;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
transition: border-color 0.3s, box-shadow 0.3s;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.text-input:focus {
|
||||
outline: none;
|
||||
border-color: #2ecc71;
|
||||
box-shadow: 0 0 0 3px rgba(46, 204, 113, 0.2);
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.rewrite-button {
|
||||
flex: 1;
|
||||
background: linear-gradient(135deg, #2ecc71, #27ae60);
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
padding: 16px;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 12px rgba(46, 204, 113, 0.3);
|
||||
}
|
||||
|
||||
.rewrite-button:hover:not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 16px rgba(46, 204, 113, 0.4);
|
||||
}
|
||||
|
||||
.rewrite-button:active:not(:disabled) {
|
||||
transform: translateY(1px);
|
||||
box-shadow: 0 2px 8px rgba(46, 204, 113, 0.3);
|
||||
}
|
||||
|
||||
.rewrite-button:disabled {
|
||||
opacity: 0.7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: white;
|
||||
animation: spin 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.title-button {
|
||||
background: linear-gradient(135deg, #3498db, #2980b9);
|
||||
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.3);
|
||||
}
|
||||
|
||||
.title-button:hover:not(:disabled) {
|
||||
box-shadow: 0 6px 16px rgba(52, 152, 219, 0.4);
|
||||
}
|
||||
|
||||
.title-button:active:not(:disabled) {
|
||||
box-shadow: 0 2px 8px rgba(52, 152, 219, 0.3);
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.input-section {
|
||||
flex: none;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.input-section {
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.mode-tabs {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.generation-count {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.input-section {
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
543
lingtropy-client/src/renderer/components/ResultSection.vue
Normal file
543
lingtropy-client/src/renderer/components/ResultSection.vue
Normal file
@ -0,0 +1,543 @@
|
||||
<template>
|
||||
<transition name="fade">
|
||||
<div v-if="(results?.length > 0) || isGenerating" class="result-section">
|
||||
<div class="result-header-bar">
|
||||
<h2 class="section-title">
|
||||
<span class="section-icon">✨</span>
|
||||
改写结果
|
||||
</h2>
|
||||
<div class="result-stats">
|
||||
<div class="layout-toggle">
|
||||
<button
|
||||
@click="$emit('update:layout', 'list')"
|
||||
:class="{ active: layoutMode === 'list' }"
|
||||
class="layout-button"
|
||||
>
|
||||
<span class="layout-icon">📋</span>
|
||||
</button>
|
||||
<button
|
||||
@click="$emit('update:layout', 'grid')"
|
||||
:class="{ active: layoutMode === 'grid' }"
|
||||
class="layout-button"
|
||||
>
|
||||
<span class="layout-icon">📊</span>
|
||||
</button>
|
||||
</div>
|
||||
<span class="model-badge">{{ model }}</span>
|
||||
<span class="count-badge">{{ results?.length || 0 }} 个结果</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="isGenerating && results.length === 0" class="generating-placeholder">
|
||||
<div class="generating-animation">
|
||||
<div class="dot"></div>
|
||||
<div class="dot"></div>
|
||||
<div class="dot"></div>
|
||||
</div>
|
||||
<p>正在使用 {{ model }} 生成改写结果...</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="results-container">
|
||||
<div :class="['results-list', layoutMode === 'grid' ? 'grid-layout' : '']">
|
||||
<div
|
||||
v-for="(text, index) in results"
|
||||
:key="index"
|
||||
:class="{
|
||||
'expanded-result': expandedResults[index],
|
||||
'grid-item': layoutMode === 'grid'
|
||||
}"
|
||||
:style="{ animationDelay: `${index * 0.1}s` }"
|
||||
class="result-block"
|
||||
>
|
||||
<div class="result-header">
|
||||
<div class="result-number">
|
||||
结果 #{{ index + 1 }}
|
||||
<span class="char-count">{{ text.length }} 字</span>
|
||||
</div>
|
||||
<div class="result-actions">
|
||||
<button @click="handleCopy(text, index)" class="action-button copy-button">
|
||||
<span v-if="copiedIndex === index">已复制!</span>
|
||||
<span v-else>复制</span>
|
||||
</button>
|
||||
<button @click="toggleExpand(index)" class="action-button expand-button">
|
||||
{{ expandedResults[index] ? '收起' : '展开' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="{ expanded: expandedResults[index] }" class="result-content">
|
||||
{{ text }}
|
||||
<!-- <div v-if="text.length > (type === 'title' ? 20 : 900)" class="warning-badge">
|
||||
超出字数限制
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-result-section">
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">✨</div>
|
||||
<h3>等待生成结果</h3>
|
||||
<p>请在左侧输入文本并点击生成按钮</p>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'
|
||||
|
||||
const { results = [], isGenerating, model, layoutMode } = defineProps({
|
||||
results: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
isGenerating: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
model: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
layoutMode: {
|
||||
type: String,
|
||||
default: 'list'
|
||||
}
|
||||
})
|
||||
|
||||
defineEmits(['update:layout'])
|
||||
|
||||
const expandedResults = reactive({})
|
||||
const copiedIndex = ref(null)
|
||||
|
||||
const handleCopy = (text, index) => {
|
||||
navigator.clipboard
|
||||
.writeText(text)
|
||||
.then(() => {
|
||||
copiedIndex.value = index
|
||||
setTimeout(() => {
|
||||
copiedIndex.value = null
|
||||
}, 2000)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('复制失败:', err)
|
||||
showToast('复制失败,请重试')
|
||||
})
|
||||
}
|
||||
|
||||
const toggleExpand = (index) => {
|
||||
expandedResults[index] = !expandedResults[index]
|
||||
}
|
||||
|
||||
const showToast = (message) => {
|
||||
const toast = document.createElement('div')
|
||||
toast.className = 'toast-message'
|
||||
toast.textContent = message
|
||||
document.body.appendChild(toast)
|
||||
|
||||
setTimeout(() => {
|
||||
toast.classList.add('show')
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('show')
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(toast)
|
||||
}, 300)
|
||||
}, 2000)
|
||||
}, 10)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 右侧结果区域 */
|
||||
.result-section,
|
||||
.empty-result-section {
|
||||
flex: 1;
|
||||
background-color: white;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
padding: 28px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
backdrop-filter: blur(10px);
|
||||
background-color: rgba(255, 255, 255, 0.95);
|
||||
}
|
||||
|
||||
.result-header-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.result-stats {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.model-badge,
|
||||
.count-badge {
|
||||
padding: 6px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.model-badge {
|
||||
background-color: #e8f5e9;
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
.count-badge {
|
||||
background-color: #e3f2fd;
|
||||
color: #1565c0;
|
||||
}
|
||||
|
||||
.layout-toggle {
|
||||
display: flex;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.layout-button {
|
||||
background-color: white;
|
||||
border: none;
|
||||
padding: 6px 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.layout-button.active {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
.layout-icon {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.results-container {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.results-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.results-list.grid-layout {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.result-block {
|
||||
background-color: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e8e8;
|
||||
width: 100%;
|
||||
transition: all 0.3s ease;
|
||||
animation: fadeIn 0.5s ease forwards;
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.result-block.grid-item {
|
||||
height: 250px;
|
||||
transition: height 0.5s ease;
|
||||
}
|
||||
|
||||
.result-block.grid-item.expanded-result {
|
||||
height: auto;
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
.result-block:hover {
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.result-header {
|
||||
background-color: #f9f9f9;
|
||||
padding: 14px 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.result-number {
|
||||
font-weight: 600;
|
||||
color: #2ecc71;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.result-number::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: #2ecc71;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.result-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.action-button {
|
||||
background-color: white;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 6px;
|
||||
padding: 6px 12px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
position: relative;
|
||||
min-width: 60px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.action-button:hover {
|
||||
background-color: #f0f0f0;
|
||||
border-color: #ccc;
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
color: #2ecc71;
|
||||
border-color: #2ecc71;
|
||||
}
|
||||
|
||||
.copy-button:hover {
|
||||
background-color: rgba(46, 204, 113, 0.1);
|
||||
border-color: #2ecc71;
|
||||
}
|
||||
|
||||
.expand-button {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.result-content {
|
||||
padding: 16px;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
line-height: 1.6;
|
||||
max-height: 200px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
text-align: justify;
|
||||
hyphens: auto;
|
||||
transition: max-height 0.5s ease;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.result-content:not(.expanded)::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
background: linear-gradient(transparent, white);
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.result-content.expanded {
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
/* 生成中占位符 */
|
||||
.generating-placeholder {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.generating-placeholder p {
|
||||
margin-top: 20px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.generating-animation {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #2ecc71;
|
||||
border-radius: 50%;
|
||||
animation: bounce 1.4s infinite ease-in-out both;
|
||||
}
|
||||
|
||||
.dot:nth-child(1) {
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
.dot:nth-child(2) {
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.empty-state h3 {
|
||||
font-size: 20px;
|
||||
margin-bottom: 8px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 标题样式 */
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 16px 0;
|
||||
color: #333;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.section-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
/* 字数统计 */
|
||||
.char-count {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
font-weight: normal;
|
||||
margin-left: auto;
|
||||
background-color: #f0f0f0;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
/* 动画效果 */
|
||||
@keyframes fadeIn {
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
40% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s, transform 0.3s;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1200px) {
|
||||
.result-section,
|
||||
.empty-result-section {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.results-list.grid-layout {
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.result-section,
|
||||
.empty-result-section {
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.result-section,
|
||||
.empty-result-section {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.result-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.result-actions {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.results-list.grid-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.result-block.grid-item {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.warning-badge {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
background-color: #ff5252;
|
||||
color: white;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
5
package.json
Normal file
5
package.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"openai": "^4.87.4"
|
||||
}
|
||||
}
|
||||
268
yarn.lock
Normal file
268
yarn.lock
Normal file
@ -0,0 +1,268 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@types/node-fetch@^2.6.4":
|
||||
version "2.6.12"
|
||||
resolved "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz#8ab5c3ef8330f13100a7479e2cd56d3386830a03"
|
||||
integrity sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
form-data "^4.0.0"
|
||||
|
||||
"@types/node@*":
|
||||
version "22.13.10"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz#df9ea358c5ed991266becc3109dc2dc9125d77e4"
|
||||
integrity sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==
|
||||
dependencies:
|
||||
undici-types "~6.20.0"
|
||||
|
||||
"@types/node@^18.11.18":
|
||||
version "18.19.80"
|
||||
resolved "https://registry.npmjs.org/@types/node/-/node-18.19.80.tgz#6d6008e8920dddcd23f9dd33da24684ef57d487c"
|
||||
integrity sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
|
||||
abort-controller@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
|
||||
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
|
||||
dependencies:
|
||||
event-target-shim "^5.0.0"
|
||||
|
||||
agentkeepalive@^4.2.1:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a"
|
||||
integrity sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==
|
||||
dependencies:
|
||||
humanize-ms "^1.2.1"
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||
|
||||
call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
|
||||
integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
function-bind "^1.1.2"
|
||||
|
||||
combined-stream@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
delayed-stream@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
|
||||
|
||||
dunder-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
|
||||
integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
|
||||
dependencies:
|
||||
call-bind-apply-helpers "^1.0.1"
|
||||
es-errors "^1.3.0"
|
||||
gopd "^1.2.0"
|
||||
|
||||
es-define-property@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
|
||||
integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
|
||||
|
||||
es-errors@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
|
||||
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
|
||||
|
||||
es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
|
||||
integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
|
||||
es-set-tostringtag@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d"
|
||||
integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
|
||||
dependencies:
|
||||
es-errors "^1.3.0"
|
||||
get-intrinsic "^1.2.6"
|
||||
has-tostringtag "^1.0.2"
|
||||
hasown "^2.0.2"
|
||||
|
||||
event-target-shim@^5.0.0:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
|
||||
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
|
||||
|
||||
form-data-encoder@1.7.2:
|
||||
version "1.7.2"
|
||||
resolved "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040"
|
||||
integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz#35cabbdd30c3ce73deb2c42d3c8d3ed9ca51794c"
|
||||
integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
es-set-tostringtag "^2.1.0"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
formdata-node@^4.3.2:
|
||||
version "4.4.1"
|
||||
resolved "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2"
|
||||
integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==
|
||||
dependencies:
|
||||
node-domexception "1.0.0"
|
||||
web-streams-polyfill "4.0.0-beta.3"
|
||||
|
||||
function-bind@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
|
||||
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
|
||||
|
||||
get-intrinsic@^1.2.6:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
|
||||
integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
|
||||
dependencies:
|
||||
call-bind-apply-helpers "^1.0.2"
|
||||
es-define-property "^1.0.1"
|
||||
es-errors "^1.3.0"
|
||||
es-object-atoms "^1.1.1"
|
||||
function-bind "^1.1.2"
|
||||
get-proto "^1.0.1"
|
||||
gopd "^1.2.0"
|
||||
has-symbols "^1.1.0"
|
||||
hasown "^2.0.2"
|
||||
math-intrinsics "^1.1.0"
|
||||
|
||||
get-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
|
||||
integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
|
||||
dependencies:
|
||||
dunder-proto "^1.0.1"
|
||||
es-object-atoms "^1.0.0"
|
||||
|
||||
gopd@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
|
||||
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
|
||||
|
||||
has-symbols@^1.0.3, has-symbols@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
|
||||
integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
|
||||
|
||||
has-tostringtag@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc"
|
||||
integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
|
||||
dependencies:
|
||||
has-symbols "^1.0.3"
|
||||
|
||||
hasown@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
|
||||
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
|
||||
dependencies:
|
||||
function-bind "^1.1.2"
|
||||
|
||||
humanize-ms@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
|
||||
integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==
|
||||
dependencies:
|
||||
ms "^2.0.0"
|
||||
|
||||
math-intrinsics@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
|
||||
integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
|
||||
|
||||
mime-db@1.52.0:
|
||||
version "1.52.0"
|
||||
resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||
|
||||
mime-types@^2.1.12:
|
||||
version "2.1.35"
|
||||
resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||
dependencies:
|
||||
mime-db "1.52.0"
|
||||
|
||||
ms@^2.0.0:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
node-domexception@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
|
||||
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
|
||||
|
||||
node-fetch@^2.6.7:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
|
||||
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
openai@^4.87.4:
|
||||
version "4.87.4"
|
||||
resolved "https://registry.npmjs.org/openai/-/openai-4.87.4.tgz#f9d8da366a1ded2c7aa92cb9f2256755d0e58902"
|
||||
integrity sha512-lsfM20jZY4A0lNexfoUAkfmrEXxaTXvv8OKYicpeAJUNHObpRgkvC7pxPgMnB6gc9ID8OCwzzhEhBpNy69UR7w==
|
||||
dependencies:
|
||||
"@types/node" "^18.11.18"
|
||||
"@types/node-fetch" "^2.6.4"
|
||||
abort-controller "^3.0.0"
|
||||
agentkeepalive "^4.2.1"
|
||||
form-data-encoder "1.7.2"
|
||||
formdata-node "^4.3.2"
|
||||
node-fetch "^2.6.7"
|
||||
|
||||
tr46@~0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
|
||||
|
||||
undici-types@~5.26.4:
|
||||
version "5.26.5"
|
||||
resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
|
||||
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||
|
||||
undici-types@~6.20.0:
|
||||
version "6.20.0"
|
||||
resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433"
|
||||
integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==
|
||||
|
||||
web-streams-polyfill@4.0.0-beta.3:
|
||||
version "4.0.0-beta.3"
|
||||
resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38"
|
||||
integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==
|
||||
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
|
||||
|
||||
whatwg-url@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
|
||||
dependencies:
|
||||
tr46 "~0.0.3"
|
||||
webidl-conversions "^3.0.0"
|
||||
Loading…
Reference in New Issue
Block a user