初始化
This commit is contained in:
+24
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar"]
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
# 提示词编辑器与词库管理工具
|
||||
|
||||
一个用于编辑、管理和翻译提示词的轻量工具,支持语言映射、预设保存以及导入/导出数据,帮助你快速组织和复用提示词。
|
||||
|
||||
主要功能
|
||||
- 提示词编辑:
|
||||
- 左侧输入框支持逗号分隔的提示词,Tab 或点击建议快速补全。
|
||||
- 一键替换中文逗号、格式化为标准提示词格式。
|
||||
- 拖拽调整提示词顺序、双击编辑、删除、在后追加新词。
|
||||
- 右侧显示多语言映射,未映射项高亮,并可直接添加翻译。
|
||||
- 精简/详细两种视图切换,便于快速浏览或逐项编辑。
|
||||
|
||||
- 预设管理:
|
||||
- 将当前提示词保存为命名预设,支持搜索、加载、重命名、删除。
|
||||
- 按更新时间排序,便于查找最近使用的预设。
|
||||
|
||||
- 词库管理:
|
||||
- 浏览分类与分组,编辑关键字与翻译,支持隐藏/显示词条。
|
||||
- 拖拽排序、快速新增/删除词条,支持关键字/翻译搜索。
|
||||
|
||||
- 数据导入/导出与持久化:
|
||||
- 导出 JSON(包含你的自定义差异与预设),导入 JSON 恢复配置。
|
||||
- 重置为内置词库。
|
||||
- 自动持久化到浏览器本地,刷新后可保留上次编辑内容与语言选择。
|
||||
|
||||
- 多语言与主题:
|
||||
- 内置多语言支持(如 `en`、`zh_CN`、`es_ES`)。
|
||||
- 支持亮色/暗色主题切换。
|
||||
|
||||
使用提示
|
||||
- 在编辑器中按 Tab 可对当前片段进行智能补全。
|
||||
- “导出 JSON”会同时导出你的预设,便于分享或备份。
|
||||
- 通过“导入 JSON”可恢复你此前导出的自定义数据与预设;如需回到最初状态,可使用“重置为内置词库”。
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/dog.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>提示词编辑器</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "prompt",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vue-tsc -b && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"pinia": "^3.0.4",
|
||||
"yaml": "^2.4.5",
|
||||
"vue": "^3.5.24"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.10.0",
|
||||
"@vitejs/plugin-vue": "^6.0.1",
|
||||
"@vue/tsconfig": "^0.8.1",
|
||||
"typescript": "~5.9.3",
|
||||
"vite": "npm:rolldown-vite@7.2.2",
|
||||
"vue-tsc": "^3.1.3"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"vite": "npm:rolldown-vite@7.2.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+893
@@ -0,0 +1,893 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
vite: npm:rolldown-vite@7.2.2
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
pinia:
|
||||
specifier: ^3.0.4
|
||||
version: 3.0.4(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3))
|
||||
vue:
|
||||
specifier: ^3.5.24
|
||||
version: 3.5.24(typescript@5.9.3)
|
||||
yaml:
|
||||
specifier: ^2.4.5
|
||||
version: 2.8.1
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^24.10.0
|
||||
version: 24.10.0
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^6.0.1
|
||||
version: 6.0.1(rolldown-vite@7.2.2(@types/node@24.10.0)(yaml@2.8.1))(vue@3.5.24(typescript@5.9.3))
|
||||
'@vue/tsconfig':
|
||||
specifier: ^0.8.1
|
||||
version: 0.8.1(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3))
|
||||
typescript:
|
||||
specifier: ~5.9.3
|
||||
version: 5.9.3
|
||||
vite:
|
||||
specifier: npm:rolldown-vite@7.2.2
|
||||
version: rolldown-vite@7.2.2(@types/node@24.10.0)(yaml@2.8.1)
|
||||
vue-tsc:
|
||||
specifier: ^3.1.3
|
||||
version: 3.1.3(typescript@5.9.3)
|
||||
|
||||
packages:
|
||||
|
||||
'@babel/helper-string-parser@7.27.1':
|
||||
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/helper-validator-identifier@7.28.5':
|
||||
resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/parser@7.28.5':
|
||||
resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@babel/types@7.28.5':
|
||||
resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@emnapi/core@1.7.0':
|
||||
resolution: {integrity: sha512-pJdKGq/1iquWYtv1RRSljZklxHCOCAJFJrImO5ZLKPJVJlVUcs8yFwNQlqS0Lo8xT1VAXXTCZocF9n26FWEKsw==}
|
||||
|
||||
'@emnapi/runtime@1.7.0':
|
||||
resolution: {integrity: sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==}
|
||||
|
||||
'@emnapi/wasi-threads@1.1.0':
|
||||
resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5':
|
||||
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
||||
|
||||
'@napi-rs/wasm-runtime@1.0.7':
|
||||
resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==}
|
||||
|
||||
'@oxc-project/runtime@0.96.0':
|
||||
resolution: {integrity: sha512-34lh4o9CcSw09Hx6fKihPu85+m+4pmDlkXwJrLvN5nMq5JrcGhhihVM415zDqT8j8IixO1PYYdQZRN4SwQCncg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
|
||||
'@oxc-project/types@0.96.0':
|
||||
resolution: {integrity: sha512-r/xkmoXA0xEpU6UGtn18CNVjXH6erU3KCpCDbpLmbVxBFor1U9MqN5Z2uMmCHJuXjJzlnDR+hWY+yPoLo8oHDw==}
|
||||
|
||||
'@rolldown/binding-android-arm64@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-vPP9/MZzESh9QtmvQYojXP/midjgkkc1E4AdnPPAzQXo668ncHJcVLKjJKzoBdsQmaIvNjrMdsCwES8vTQHRQw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@rolldown/binding-darwin-arm64@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-Lc3nrkxeaDVCVl8qR3qoxh6ltDZfkQ98j5vwIr5ALPkgjZtDK4BGCrrBoLpGVMg+csWcaqUbwbKwH5yvVa0oOw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rolldown/binding-darwin-x64@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-eBYxQDwP0O33plqNVqOtUHqRiSYVneAknviM5XMawke3mwMuVlAsohtOqEjbCEl/Loi/FWdVeks5WkqAkzkYWQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rolldown/binding-freebsd-x64@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-Ns+kgp2+1Iq/44bY/Z30DETUSiHY7ZuqaOgD5bHVW++8vme9rdiWsN4yG4rRPXkdgzjvQ9TDHmZZKfY4/G11AA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-4PecgWCJhTA2EFOlptYJiNyVP2MrVP4cWdndpOu3WmXqWqZUmSubhb4YUAIxAxnXATlGjC1WjxNPhV7ZllNgdA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-CyIunZ6D9U9Xg94roQI1INt/bLkOpPsZjZZkiaAZ0r6uccQdICmC99M9RUPlMLw/qg4yEWLlQhG73W/mG437NA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-doozc/Goe7qRCSnzfJbFINTHsMktqmZQmweull6hsZZ9sjNWQ6BWQnbvOlfZJe4xE5NxM1NhPnY5Giqnl3ZrYQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-fodvSMf6Aqwa0wEUSTPewmmZOD44rc5Tpr5p9NkwQ6W1SSpUKzD3SwpJIgANDOhwiYhDuiIaYPGB7Ujkx1q0UQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-Rxm5hYc0mGjwLh5sjlGmMygxAaV2gnsx7CNm2lsb47oyt5UQyPDZf3GP/ct8BEcwuikdqzsrrlIp8+kCSvMFNQ==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-YakuVe+Gc87jjxazBL34hbr8RJpRuFBhun7NEqoChVDlH5FLhLXjAPHqZd990TVGVNkemourf817Z8u2fONS8w==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@rolldown/binding-wasm32-wasi@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-ak2GvTFQz3UAOw8cuQq8pWE+TNygQB6O47rMhvevvTzETh7VkHRFtRUwJynX5hwzFvQMP6G0az5JrBGuwaMwYQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
cpu: [wasm32]
|
||||
|
||||
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-o5BpmBnXU+Cj+9+ndMcdKjhZlPb79dVPBZnWwMnI4RlNSSq5yOvFZqvfPYbyacvnW03Na4n5XXQAPhu3RydZ0w==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-FVOmfyYehNE92IfC9Kgs913UerDog2M1m+FADJypKz0gmRg3UyTt4o1cZMCAl7MiR89JpM9jegNO1nXuP1w1vw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-by/70F13IUE101Bat0oeH8miwWX5mhMFPk1yjCdxoTNHTyTdLgb0THNaebRM6AP7Kz+O3O2qx87sruYuF5UxHg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.29':
|
||||
resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==}
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.47':
|
||||
resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==}
|
||||
|
||||
'@tybys/wasm-util@0.10.1':
|
||||
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
|
||||
|
||||
'@types/node@24.10.0':
|
||||
resolution: {integrity: sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==}
|
||||
|
||||
'@vitejs/plugin-vue@6.0.1':
|
||||
resolution: {integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
peerDependencies:
|
||||
vite: ^5.0.0 || ^6.0.0 || ^7.0.0
|
||||
vue: ^3.2.25
|
||||
|
||||
'@volar/language-core@2.4.23':
|
||||
resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==}
|
||||
|
||||
'@volar/source-map@2.4.23':
|
||||
resolution: {integrity: sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==}
|
||||
|
||||
'@volar/typescript@2.4.23':
|
||||
resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==}
|
||||
|
||||
'@vue/compiler-core@3.5.24':
|
||||
resolution: {integrity: sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==}
|
||||
|
||||
'@vue/compiler-dom@3.5.24':
|
||||
resolution: {integrity: sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==}
|
||||
|
||||
'@vue/compiler-sfc@3.5.24':
|
||||
resolution: {integrity: sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==}
|
||||
|
||||
'@vue/compiler-ssr@3.5.24':
|
||||
resolution: {integrity: sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==}
|
||||
|
||||
'@vue/devtools-api@7.7.7':
|
||||
resolution: {integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==}
|
||||
|
||||
'@vue/devtools-kit@7.7.7':
|
||||
resolution: {integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==}
|
||||
|
||||
'@vue/devtools-shared@7.7.7':
|
||||
resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==}
|
||||
|
||||
'@vue/language-core@3.1.3':
|
||||
resolution: {integrity: sha512-KpR1F/eGAG9D1RZ0/T6zWJs6dh/pRLfY5WupecyYKJ1fjVmDMgTPw9wXmKv2rBjo4zCJiOSiyB8BDP1OUwpMEA==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@vue/reactivity@3.5.24':
|
||||
resolution: {integrity: sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==}
|
||||
|
||||
'@vue/runtime-core@3.5.24':
|
||||
resolution: {integrity: sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==}
|
||||
|
||||
'@vue/runtime-dom@3.5.24':
|
||||
resolution: {integrity: sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==}
|
||||
|
||||
'@vue/server-renderer@3.5.24':
|
||||
resolution: {integrity: sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==}
|
||||
peerDependencies:
|
||||
vue: 3.5.24
|
||||
|
||||
'@vue/shared@3.5.24':
|
||||
resolution: {integrity: sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==}
|
||||
|
||||
'@vue/tsconfig@0.8.1':
|
||||
resolution: {integrity: sha512-aK7feIWPXFSUhsCP9PFqPyFOcz4ENkb8hZ2pneL6m2UjCkccvaOhC/5KCKluuBufvp2KzkbdA2W2pk20vLzu3g==}
|
||||
peerDependencies:
|
||||
typescript: 5.x
|
||||
vue: ^3.4.0
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
vue:
|
||||
optional: true
|
||||
|
||||
alien-signals@3.1.0:
|
||||
resolution: {integrity: sha512-yufC6VpSy8tK3I0lO67pjumo5JvDQVQyr38+3OHqe6CHl1t2VZekKZ7EKKZSqk0cRmE7U7tfZbpXiKNzuc+ckg==}
|
||||
|
||||
birpc@2.8.0:
|
||||
resolution: {integrity: sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==}
|
||||
|
||||
copy-anything@4.0.5:
|
||||
resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
detect-libc@2.1.2:
|
||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
entities@4.5.0:
|
||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||
engines: {node: '>=0.12'}
|
||||
|
||||
estree-walker@2.0.2:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
|
||||
fdir@6.5.0:
|
||||
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
picomatch: ^3 || ^4
|
||||
peerDependenciesMeta:
|
||||
picomatch:
|
||||
optional: true
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
hookable@5.5.3:
|
||||
resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
|
||||
|
||||
is-what@5.5.0:
|
||||
resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
lightningcss-android-arm64@1.30.2:
|
||||
resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
lightningcss-darwin-arm64@1.30.2:
|
||||
resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
lightningcss-darwin-x64@1.30.2:
|
||||
resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
lightningcss-freebsd-x64@1.30.2:
|
||||
resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.30.2:
|
||||
resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.30.2:
|
||||
resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
lightningcss-linux-arm64-musl@1.30.2:
|
||||
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
lightningcss-linux-x64-gnu@1.30.2:
|
||||
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
lightningcss-linux-x64-musl@1.30.2:
|
||||
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.30.2:
|
||||
resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
lightningcss-win32-x64-msvc@1.30.2:
|
||||
resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
lightningcss@1.30.2:
|
||||
resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
mitt@3.0.1:
|
||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||
|
||||
muggle-string@0.4.1:
|
||||
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
|
||||
|
||||
nanoid@3.3.11:
|
||||
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
path-browserify@1.0.1:
|
||||
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
|
||||
|
||||
perfect-debounce@1.0.0:
|
||||
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
picomatch@4.0.3:
|
||||
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
pinia@3.0.4:
|
||||
resolution: {integrity: sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==}
|
||||
peerDependencies:
|
||||
typescript: '>=4.5.0'
|
||||
vue: ^3.5.11
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
postcss@8.5.6:
|
||||
resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
rfdc@1.4.1:
|
||||
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
|
||||
|
||||
rolldown-vite@7.2.2:
|
||||
resolution: {integrity: sha512-Fl3ZdmJhDMJGcqrr342pPVrhugXdOcuNBRBauz4S7QGSRXbQy7y8q5QYJtgkcrG8XjY0EENSZeTk58c3m20FxA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@types/node': ^20.19.0 || >=22.12.0
|
||||
esbuild: ^0.25.0
|
||||
jiti: '>=1.21.0'
|
||||
less: ^4.0.0
|
||||
sass: ^1.70.0
|
||||
sass-embedded: ^1.70.0
|
||||
stylus: '>=0.54.8'
|
||||
sugarss: ^5.0.0
|
||||
terser: ^5.16.0
|
||||
tsx: ^4.8.1
|
||||
yaml: ^2.4.2
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
esbuild:
|
||||
optional: true
|
||||
jiti:
|
||||
optional: true
|
||||
less:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
sass-embedded:
|
||||
optional: true
|
||||
stylus:
|
||||
optional: true
|
||||
sugarss:
|
||||
optional: true
|
||||
terser:
|
||||
optional: true
|
||||
tsx:
|
||||
optional: true
|
||||
yaml:
|
||||
optional: true
|
||||
|
||||
rolldown@1.0.0-beta.47:
|
||||
resolution: {integrity: sha512-Mid74GckX1OeFAOYz9KuXeWYhq3xkXbMziYIC+ULVdUzPTG9y70OBSBQDQn9hQP8u/AfhuYw1R0BSg15nBI4Dg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
hasBin: true
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
speakingurl@14.0.1:
|
||||
resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
superjson@2.2.5:
|
||||
resolution: {integrity: sha512-zWPTX96LVsA/eVYnqOM2+ofcdPqdS1dAF1LN4TS2/MWuUpfitd9ctTa87wt4xrYnZnkLtS69xpBdSxVBP5Rm6w==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tslib@2.8.1:
|
||||
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||
|
||||
typescript@5.9.3:
|
||||
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
undici-types@7.16.0:
|
||||
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
|
||||
|
||||
vscode-uri@3.1.0:
|
||||
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
|
||||
|
||||
vue-tsc@3.1.3:
|
||||
resolution: {integrity: sha512-StMNfZHwPIXQgY3KxPKM0Jsoc8b46mDV3Fn2UlHCBIwRJApjqrSwqeMYgWf0zpN+g857y74pv7GWuBm+UqQe1w==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
typescript: '>=5.0.0'
|
||||
|
||||
vue@3.5.24:
|
||||
resolution: {integrity: sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==}
|
||||
peerDependencies:
|
||||
typescript: '*'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
yaml@2.8.1:
|
||||
resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==}
|
||||
engines: {node: '>= 14.6'}
|
||||
hasBin: true
|
||||
|
||||
snapshots:
|
||||
|
||||
'@babel/helper-string-parser@7.27.1': {}
|
||||
|
||||
'@babel/helper-validator-identifier@7.28.5': {}
|
||||
|
||||
'@babel/parser@7.28.5':
|
||||
dependencies:
|
||||
'@babel/types': 7.28.5
|
||||
|
||||
'@babel/types@7.28.5':
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.28.5
|
||||
|
||||
'@emnapi/core@1.7.0':
|
||||
dependencies:
|
||||
'@emnapi/wasi-threads': 1.1.0
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@emnapi/runtime@1.7.0':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@emnapi/wasi-threads@1.1.0':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.5.5': {}
|
||||
|
||||
'@napi-rs/wasm-runtime@1.0.7':
|
||||
dependencies:
|
||||
'@emnapi/core': 1.7.0
|
||||
'@emnapi/runtime': 1.7.0
|
||||
'@tybys/wasm-util': 0.10.1
|
||||
optional: true
|
||||
|
||||
'@oxc-project/runtime@0.96.0': {}
|
||||
|
||||
'@oxc-project/types@0.96.0': {}
|
||||
|
||||
'@rolldown/binding-android-arm64@1.0.0-beta.47':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-darwin-arm64@1.0.0-beta.47':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-darwin-x64@1.0.0-beta.47':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-freebsd-x64@1.0.0-beta.47':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.47':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm64-gnu@1.0.0-beta.47':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@1.0.0-beta.47':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.0.0-beta.47':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@1.0.0-beta.47':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.0.0-beta.47':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-wasm32-wasi@1.0.0-beta.47':
|
||||
dependencies:
|
||||
'@napi-rs/wasm-runtime': 1.0.7
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-arm64-msvc@1.0.0-beta.47':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-ia32-msvc@1.0.0-beta.47':
|
||||
optional: true
|
||||
|
||||
'@rolldown/binding-win32-x64-msvc@1.0.0-beta.47':
|
||||
optional: true
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.29': {}
|
||||
|
||||
'@rolldown/pluginutils@1.0.0-beta.47': {}
|
||||
|
||||
'@tybys/wasm-util@0.10.1':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@types/node@24.10.0':
|
||||
dependencies:
|
||||
undici-types: 7.16.0
|
||||
|
||||
'@vitejs/plugin-vue@6.0.1(rolldown-vite@7.2.2(@types/node@24.10.0)(yaml@2.8.1))(vue@3.5.24(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@rolldown/pluginutils': 1.0.0-beta.29
|
||||
vite: rolldown-vite@7.2.2(@types/node@24.10.0)(yaml@2.8.1)
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
|
||||
'@volar/language-core@2.4.23':
|
||||
dependencies:
|
||||
'@volar/source-map': 2.4.23
|
||||
|
||||
'@volar/source-map@2.4.23': {}
|
||||
|
||||
'@volar/typescript@2.4.23':
|
||||
dependencies:
|
||||
'@volar/language-core': 2.4.23
|
||||
path-browserify: 1.0.1
|
||||
vscode-uri: 3.1.0
|
||||
|
||||
'@vue/compiler-core@3.5.24':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.5
|
||||
'@vue/shared': 3.5.24
|
||||
entities: 4.5.0
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-dom@3.5.24':
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.5.24
|
||||
'@vue/shared': 3.5.24
|
||||
|
||||
'@vue/compiler-sfc@3.5.24':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.5
|
||||
'@vue/compiler-core': 3.5.24
|
||||
'@vue/compiler-dom': 3.5.24
|
||||
'@vue/compiler-ssr': 3.5.24
|
||||
'@vue/shared': 3.5.24
|
||||
estree-walker: 2.0.2
|
||||
magic-string: 0.30.21
|
||||
postcss: 8.5.6
|
||||
source-map-js: 1.2.1
|
||||
|
||||
'@vue/compiler-ssr@3.5.24':
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.24
|
||||
'@vue/shared': 3.5.24
|
||||
|
||||
'@vue/devtools-api@7.7.7':
|
||||
dependencies:
|
||||
'@vue/devtools-kit': 7.7.7
|
||||
|
||||
'@vue/devtools-kit@7.7.7':
|
||||
dependencies:
|
||||
'@vue/devtools-shared': 7.7.7
|
||||
birpc: 2.8.0
|
||||
hookable: 5.5.3
|
||||
mitt: 3.0.1
|
||||
perfect-debounce: 1.0.0
|
||||
speakingurl: 14.0.1
|
||||
superjson: 2.2.5
|
||||
|
||||
'@vue/devtools-shared@7.7.7':
|
||||
dependencies:
|
||||
rfdc: 1.4.1
|
||||
|
||||
'@vue/language-core@3.1.3(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@volar/language-core': 2.4.23
|
||||
'@vue/compiler-dom': 3.5.24
|
||||
'@vue/shared': 3.5.24
|
||||
alien-signals: 3.1.0
|
||||
muggle-string: 0.4.1
|
||||
path-browserify: 1.0.1
|
||||
picomatch: 4.0.3
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
'@vue/reactivity@3.5.24':
|
||||
dependencies:
|
||||
'@vue/shared': 3.5.24
|
||||
|
||||
'@vue/runtime-core@3.5.24':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.24
|
||||
'@vue/shared': 3.5.24
|
||||
|
||||
'@vue/runtime-dom@3.5.24':
|
||||
dependencies:
|
||||
'@vue/reactivity': 3.5.24
|
||||
'@vue/runtime-core': 3.5.24
|
||||
'@vue/shared': 3.5.24
|
||||
csstype: 3.1.3
|
||||
|
||||
'@vue/server-renderer@3.5.24(vue@3.5.24(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@vue/compiler-ssr': 3.5.24
|
||||
'@vue/shared': 3.5.24
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
|
||||
'@vue/shared@3.5.24': {}
|
||||
|
||||
'@vue/tsconfig@0.8.1(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3))':
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
|
||||
alien-signals@3.1.0: {}
|
||||
|
||||
birpc@2.8.0: {}
|
||||
|
||||
copy-anything@4.0.5:
|
||||
dependencies:
|
||||
is-what: 5.5.0
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
detect-libc@2.1.2: {}
|
||||
|
||||
entities@4.5.0: {}
|
||||
|
||||
estree-walker@2.0.2: {}
|
||||
|
||||
fdir@6.5.0(picomatch@4.0.3):
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.3
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
hookable@5.5.3: {}
|
||||
|
||||
is-what@5.5.0: {}
|
||||
|
||||
lightningcss-android-arm64@1.30.2:
|
||||
optional: true
|
||||
|
||||
lightningcss-darwin-arm64@1.30.2:
|
||||
optional: true
|
||||
|
||||
lightningcss-darwin-x64@1.30.2:
|
||||
optional: true
|
||||
|
||||
lightningcss-freebsd-x64@1.30.2:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.30.2:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.30.2:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-arm64-musl@1.30.2:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-x64-gnu@1.30.2:
|
||||
optional: true
|
||||
|
||||
lightningcss-linux-x64-musl@1.30.2:
|
||||
optional: true
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.30.2:
|
||||
optional: true
|
||||
|
||||
lightningcss-win32-x64-msvc@1.30.2:
|
||||
optional: true
|
||||
|
||||
lightningcss@1.30.2:
|
||||
dependencies:
|
||||
detect-libc: 2.1.2
|
||||
optionalDependencies:
|
||||
lightningcss-android-arm64: 1.30.2
|
||||
lightningcss-darwin-arm64: 1.30.2
|
||||
lightningcss-darwin-x64: 1.30.2
|
||||
lightningcss-freebsd-x64: 1.30.2
|
||||
lightningcss-linux-arm-gnueabihf: 1.30.2
|
||||
lightningcss-linux-arm64-gnu: 1.30.2
|
||||
lightningcss-linux-arm64-musl: 1.30.2
|
||||
lightningcss-linux-x64-gnu: 1.30.2
|
||||
lightningcss-linux-x64-musl: 1.30.2
|
||||
lightningcss-win32-arm64-msvc: 1.30.2
|
||||
lightningcss-win32-x64-msvc: 1.30.2
|
||||
|
||||
magic-string@0.30.21:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
mitt@3.0.1: {}
|
||||
|
||||
muggle-string@0.4.1: {}
|
||||
|
||||
nanoid@3.3.11: {}
|
||||
|
||||
path-browserify@1.0.1: {}
|
||||
|
||||
perfect-debounce@1.0.0: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@4.0.3: {}
|
||||
|
||||
pinia@3.0.4(typescript@5.9.3)(vue@3.5.24(typescript@5.9.3)):
|
||||
dependencies:
|
||||
'@vue/devtools-api': 7.7.7
|
||||
vue: 3.5.24(typescript@5.9.3)
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
postcss@8.5.6:
|
||||
dependencies:
|
||||
nanoid: 3.3.11
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
rfdc@1.4.1: {}
|
||||
|
||||
rolldown-vite@7.2.2(@types/node@24.10.0)(yaml@2.8.1):
|
||||
dependencies:
|
||||
'@oxc-project/runtime': 0.96.0
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
lightningcss: 1.30.2
|
||||
picomatch: 4.0.3
|
||||
postcss: 8.5.6
|
||||
rolldown: 1.0.0-beta.47
|
||||
tinyglobby: 0.2.15
|
||||
optionalDependencies:
|
||||
'@types/node': 24.10.0
|
||||
fsevents: 2.3.3
|
||||
yaml: 2.8.1
|
||||
|
||||
rolldown@1.0.0-beta.47:
|
||||
dependencies:
|
||||
'@oxc-project/types': 0.96.0
|
||||
'@rolldown/pluginutils': 1.0.0-beta.47
|
||||
optionalDependencies:
|
||||
'@rolldown/binding-android-arm64': 1.0.0-beta.47
|
||||
'@rolldown/binding-darwin-arm64': 1.0.0-beta.47
|
||||
'@rolldown/binding-darwin-x64': 1.0.0-beta.47
|
||||
'@rolldown/binding-freebsd-x64': 1.0.0-beta.47
|
||||
'@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.47
|
||||
'@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.47
|
||||
'@rolldown/binding-linux-arm64-musl': 1.0.0-beta.47
|
||||
'@rolldown/binding-linux-x64-gnu': 1.0.0-beta.47
|
||||
'@rolldown/binding-linux-x64-musl': 1.0.0-beta.47
|
||||
'@rolldown/binding-openharmony-arm64': 1.0.0-beta.47
|
||||
'@rolldown/binding-wasm32-wasi': 1.0.0-beta.47
|
||||
'@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.47
|
||||
'@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.47
|
||||
'@rolldown/binding-win32-x64-msvc': 1.0.0-beta.47
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
speakingurl@14.0.1: {}
|
||||
|
||||
superjson@2.2.5:
|
||||
dependencies:
|
||||
copy-anything: 4.0.5
|
||||
|
||||
tinyglobby@0.2.15:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
picomatch: 4.0.3
|
||||
|
||||
tslib@2.8.1:
|
||||
optional: true
|
||||
|
||||
typescript@5.9.3: {}
|
||||
|
||||
undici-types@7.16.0: {}
|
||||
|
||||
vscode-uri@3.1.0: {}
|
||||
|
||||
vue-tsc@3.1.3(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@volar/typescript': 2.4.23
|
||||
'@vue/language-core': 3.1.3(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
|
||||
vue@3.5.24(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.24
|
||||
'@vue/compiler-sfc': 3.5.24
|
||||
'@vue/runtime-dom': 3.5.24
|
||||
'@vue/server-renderer': 3.5.24(vue@3.5.24(typescript@5.9.3))
|
||||
'@vue/shared': 3.5.24
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
yaml@2.8.1: {}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 448 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+311
@@ -0,0 +1,311 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import PromptEditor from './components/PromptEditor.vue'
|
||||
import PromptManager from './components/PromptManager.vue'
|
||||
|
||||
const currentView = ref<'editor' | 'manager'>('editor')
|
||||
const isDark = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
// 检测系统主题偏好
|
||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
isDark.value = localStorage.getItem('theme') === 'dark' || (localStorage.getItem('theme') === null && prefersDark)
|
||||
updateTheme()
|
||||
})
|
||||
|
||||
function toggleTheme() {
|
||||
isDark.value = !isDark.value
|
||||
localStorage.setItem('theme', isDark.value ? 'dark' : 'light')
|
||||
updateTheme()
|
||||
}
|
||||
|
||||
function updateTheme() {
|
||||
document.documentElement.classList.toggle('dark', isDark.value)
|
||||
}
|
||||
|
||||
function switchView(view: 'editor' | 'manager') {
|
||||
currentView.value = view
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="app-container" :class="{ dark: isDark }">
|
||||
<!-- 顶部导航栏 -->
|
||||
<header class="app-header">
|
||||
<div class="header-content">
|
||||
<div class="header-left">
|
||||
<div class="app-logo">
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 2L2 7L12 12L22 7L12 2Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||
<path d="M2 17L12 22L22 17" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||
<path d="M2 12L12 17L22 12" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<span class="app-title">提示词编辑器</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="header-nav">
|
||||
<button
|
||||
class="nav-btn"
|
||||
:class="{ active: currentView === 'editor' }"
|
||||
@click="switchView('editor')"
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="m18.5 2.5 3 3L12 15l-4 1 1-4 9.5-9.5z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
编辑器
|
||||
</button>
|
||||
<button
|
||||
class="nav-btn"
|
||||
:class="{ active: currentView === 'manager' }"
|
||||
@click="switchView('manager')"
|
||||
>
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 3h18v18H3zM9 9h6v6H9z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
词库管理
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="header-right">
|
||||
<button class="theme-toggle" @click="toggleTheme" title="切换主题">
|
||||
<svg v-if="!isDark" width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="12" cy="12" r="5" stroke="currentColor" stroke-width="2"/>
|
||||
<path d="m12 1 0 2m0 18 0 2M4.22 4.22l1.42 1.42m12.72 12.72 1.42 1.42M1 12l2 0m18 0 2 0M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<svg v-else width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<main class="app-main">
|
||||
<Transition name="view-transition" mode="out-in">
|
||||
<PromptEditor v-if="currentView === 'editor'" key="editor" />
|
||||
<PromptManager v-else key="manager" />
|
||||
</Transition>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* 全局样式重置和变量定义 */
|
||||
:root {
|
||||
/* 亮色主题 */
|
||||
--color-bg-primary: #ffffff;
|
||||
--color-bg-secondary: #f8fafc;
|
||||
--color-bg-tertiary: #f1f5f9;
|
||||
--color-border: #e2e8f0;
|
||||
--color-border-hover: #cbd5e1;
|
||||
--color-text-primary: #0f172a;
|
||||
--color-text-secondary: #475569;
|
||||
--color-text-tertiary: #64748b;
|
||||
--color-accent: #3b82f6;
|
||||
--color-accent-hover: #2563eb;
|
||||
--color-accent-light: #dbeafe;
|
||||
--color-success: #10b981;
|
||||
--color-warning: #f59e0b;
|
||||
--color-error: #ef4444;
|
||||
--shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||||
--shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
|
||||
--shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
--radius-sm: 0.375rem;
|
||||
--radius-md: 0.5rem;
|
||||
--radius-lg: 0.75rem;
|
||||
}
|
||||
|
||||
.dark {
|
||||
/* 暗色主题 */
|
||||
--color-bg-primary: #0f172a;
|
||||
--color-bg-secondary: #1e293b;
|
||||
--color-bg-tertiary: #334155;
|
||||
--color-border: #475569;
|
||||
--color-border-hover: #64748b;
|
||||
--color-text-primary: #f8fafc;
|
||||
--color-text-secondary: #cbd5e1;
|
||||
--color-text-tertiary: #94a3b8;
|
||||
--color-accent: #60a5fa;
|
||||
--color-accent-hover: #3b82f6;
|
||||
--color-accent-light: #1e3a8a;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
line-height: 1.6;
|
||||
color: var(--color-text-primary);
|
||||
background-color: var(--color-bg-primary);
|
||||
transition: color 0.3s ease, background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--color-bg-primary);
|
||||
}
|
||||
|
||||
/* 顶部导航栏样式 */
|
||||
.app-header {
|
||||
background-color: var(--color-bg-primary);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
backdrop-filter: blur(8px);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
height: 3.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.app-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: var(--color-accent);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.app-title {
|
||||
font-size: 1.125rem;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.header-nav {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
background-color: var(--color-bg-secondary);
|
||||
padding: 0.25rem;
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 1rem;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--color-text-secondary);
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.nav-btn:hover {
|
||||
background-color: var(--color-bg-tertiary);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.nav-btn.active {
|
||||
background-color: var(--color-accent);
|
||||
color: white;
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
border: none;
|
||||
background-color: var(--color-bg-secondary);
|
||||
color: var(--color-text-secondary);
|
||||
border-radius: var(--radius-md);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.theme-toggle:hover {
|
||||
background-color: var(--color-bg-tertiary);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
/* 主要内容区域 */
|
||||
.app-main {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 视图切换动画 */
|
||||
.view-transition-enter-active,
|
||||
.view-transition-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.view-transition-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.view-transition-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.header-content {
|
||||
padding: 0 0.75rem;
|
||||
}
|
||||
|
||||
.app-logo .app-title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.nav-btn svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.header-nav {
|
||||
gap: 0.125rem;
|
||||
padding: 0.125rem;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
padding: 0.375rem 0.5rem;
|
||||
}
|
||||
|
||||
.nav-btn span {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,97 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
message: string
|
||||
type: 'success' | 'error' | 'info'
|
||||
show: boolean
|
||||
}
|
||||
|
||||
defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition name="notification">
|
||||
<div v-if="show" class="notification-toast" :class="type">
|
||||
<div class="notification-content">
|
||||
<svg v-if="type === 'success'" width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<polyline points="22,4 12,14.01 9,11.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<svg v-else-if="type === 'error'" width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
|
||||
<line x1="15" y1="9" x2="9" y2="15" stroke="currentColor" stroke-width="2"/>
|
||||
<line x1="9" y1="9" x2="15" y2="15" stroke="currentColor" stroke-width="2"/>
|
||||
</svg>
|
||||
<svg v-else width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2"/>
|
||||
<path d="m9 12 2 2 4-4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<span>{{ message }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.notification-toast {
|
||||
position: fixed;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 1000;
|
||||
min-width: 300px;
|
||||
max-width: 500px;
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-lg);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.notification-toast.success {
|
||||
background-color: var(--color-success);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.notification-toast.error {
|
||||
background-color: var(--color-error);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.notification-toast.info {
|
||||
background-color: var(--color-accent);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.notification-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 通知动画 */
|
||||
.notification-enter-active {
|
||||
transition: all 0.3s ease-out;
|
||||
}
|
||||
|
||||
.notification-leave-active {
|
||||
transition: all 0.3s ease-in;
|
||||
}
|
||||
|
||||
.notification-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(100%) scale(0.9);
|
||||
}
|
||||
|
||||
.notification-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(100%) scale(0.9);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.notification-toast {
|
||||
left: 1rem;
|
||||
right: 1rem;
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,211 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, computed } from 'vue';
|
||||
import { usePromptStore } from '../stores/promptStore';
|
||||
import type { LangCode, PromptGroup, PromptTag } from '../types';
|
||||
|
||||
const store = usePromptStore();
|
||||
const draggingIndex = ref<number | null>(null);
|
||||
const overIndex = ref<number | null>(null);
|
||||
|
||||
onMounted(() => {
|
||||
store.initialize();
|
||||
});
|
||||
|
||||
const languages = computed(() => store.languages);
|
||||
const selectedLang = computed({
|
||||
get: () => store.selectedLang,
|
||||
set: (v: LangCode) => store.setLanguage(v),
|
||||
});
|
||||
const categories = computed(() => store.categories);
|
||||
const currentCategory = computed(() => store.currentCategory);
|
||||
const currentGroup = computed(() => store.currentGroup);
|
||||
const filteredTags = computed(() => store.filteredTags);
|
||||
|
||||
function onDragStart(index: number) {
|
||||
draggingIndex.value = index;
|
||||
}
|
||||
function onDragOver(index: number, e: DragEvent) {
|
||||
e.preventDefault();
|
||||
overIndex.value = index;
|
||||
}
|
||||
function onDrop(index: number) {
|
||||
if (draggingIndex.value == null) return;
|
||||
const from = draggingIndex.value;
|
||||
const to = index;
|
||||
const grpId = currentGroup.value?.id;
|
||||
if (!grpId) return;
|
||||
store.reorderTags(grpId, from, to);
|
||||
draggingIndex.value = null;
|
||||
overIndex.value = null;
|
||||
}
|
||||
|
||||
function displayTranslation(tag: PromptTag): string {
|
||||
return tag.translation?.[selectedLang.value] ?? tag.key;
|
||||
}
|
||||
|
||||
function updateKey(tag: PromptTag, val: string) {
|
||||
const gid = currentGroup.value?.id;
|
||||
if (!gid) return;
|
||||
store.updateTagKey(gid, tag.key, val);
|
||||
}
|
||||
|
||||
function updateTrans(tag: PromptTag, val: string) {
|
||||
const gid = currentGroup.value?.id;
|
||||
if (!gid) return;
|
||||
store.setTranslation(gid, tag.key, selectedLang.value, val);
|
||||
}
|
||||
|
||||
function addTag() {
|
||||
const gid = currentGroup.value?.id;
|
||||
if (!gid) return;
|
||||
store.addTag(gid);
|
||||
}
|
||||
|
||||
function removeTag(tag: PromptTag) {
|
||||
const gid = currentGroup.value?.id;
|
||||
if (!gid) return;
|
||||
store.removeTag(gid, tag.key);
|
||||
}
|
||||
|
||||
function toggleHidden(tag: PromptTag) {
|
||||
const gid = currentGroup.value?.id;
|
||||
if (!gid) return;
|
||||
store.toggleHidden(gid, tag.key);
|
||||
}
|
||||
|
||||
function exportAll() {
|
||||
const json = store.exportToJson();
|
||||
const blob = new Blob([json], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `prompt_dataset_${new Date().toISOString().slice(0,19)}.json`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function importAll(ev: Event) {
|
||||
const input = ev.target as HTMLInputElement;
|
||||
const file = input.files?.[0];
|
||||
if (!file) return;
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
try {
|
||||
const text = String(reader.result);
|
||||
const bundle = JSON.parse(text);
|
||||
store.importFromBundle(bundle);
|
||||
} catch (e) {
|
||||
alert('导入失败:JSON 格式不正确');
|
||||
} finally {
|
||||
input.value = '';
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
function resetDefault() {
|
||||
store.resetToDefault();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="pm-root">
|
||||
<header class="pm-toolbar">
|
||||
<div class="pm-left">
|
||||
<label>语言</label>
|
||||
<select v-model="selectedLang">
|
||||
<option v-for="l in languages" :key="l" :value="l">{{ l }}</option>
|
||||
</select>
|
||||
<input
|
||||
class="pm-search"
|
||||
type="search"
|
||||
placeholder="搜索关键字/翻译"
|
||||
:value="store.searchQuery"
|
||||
@input="store.setSearch(($event.target as HTMLInputElement).value)"
|
||||
/>
|
||||
</div>
|
||||
<div class="pm-right">
|
||||
<button class="pm-btn" @click="exportAll">导出 JSON</button>
|
||||
<span class="pm-tip">导出 JSON 会同时包含你的预设</span>
|
||||
<label class="pm-import pm-btn">导入 JSON
|
||||
<input type="file" accept="application/json" @change="importAll" />
|
||||
</label>
|
||||
<button class="pm-btn" @click="resetDefault">重置为内置词库</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="pm-main">
|
||||
<aside class="pm-cats">
|
||||
<div class="pm-section-title">分类</div>
|
||||
<ul>
|
||||
<li v-for="(c,ci) in categories" :key="c.id" :class="{ active: ci===store.selectedCategoryIndex }" @click="store.selectCategory(ci)">
|
||||
{{ c.name }}
|
||||
</li>
|
||||
</ul>
|
||||
<div class="pm-section-title">分组</div>
|
||||
<ul>
|
||||
<li v-for="(g,gi) in currentCategory?.groups" :key="g.id" :class="{ active: gi===store.selectedGroupIndex }" @click="store.selectGroup(gi)">
|
||||
<span class="pm-color" :style="{ background: g.color || 'transparent' }"></span>
|
||||
{{ g.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<section class="pm-list">
|
||||
<div class="pm-list-toolbar">
|
||||
<button @click="addTag">新增提示词</button>
|
||||
</div>
|
||||
<div v-if="!currentGroup" class="pm-empty">请选择一个分组</div>
|
||||
<ul v-else class="pm-tags">
|
||||
<li
|
||||
v-for="(t,ti) in filteredTags"
|
||||
:key="t.key + '_' + ti"
|
||||
:draggable="true"
|
||||
@dragstart="onDragStart(ti)"
|
||||
@dragover="onDragOver(ti, $event)"
|
||||
@drop="onDrop(ti)"
|
||||
:class="{ hidden: t.hidden }"
|
||||
>
|
||||
<span class="pm-handle">⋮⋮</span>
|
||||
<input class="pm-key" :value="t.key" @input="updateKey(t, ($event.target as HTMLInputElement).value)" />
|
||||
<input class="pm-trans" :value="displayTranslation(t)" @input="updateTrans(t, ($event.target as HTMLInputElement).value)" />
|
||||
<button class="pm-hide" @click="toggleHidden(t)">{{ t.hidden ? '显示' : '隐藏' }}</button>
|
||||
<button class="pm-del" @click="removeTag(t)">删除</button>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.pm-root { display: flex; flex-direction: column; height: 100vh; font-family: system-ui, -apple-system, Segoe UI, Roboto, 'Noto Sans', 'PingFang SC', 'Microsoft YaHei', sans-serif; }
|
||||
.pm-toolbar { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; border-bottom: 1px solid #e5e7eb; gap: 12px; }
|
||||
.pm-left { display: flex; align-items: center; gap: 8px; }
|
||||
.pm-right { display: flex; align-items: center; gap: 8px; }
|
||||
.pm-tip { font-size: 12px; color: #6b7280; }
|
||||
.pm-btn { display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; border: 1px solid #d1d5db; border-radius: 6px; background: white; cursor: pointer; line-height: 1; font-size: 14px; }
|
||||
.pm-import { display: inline-flex; align-items: center; gap: 6px; }
|
||||
.pm-import input { display: none; }
|
||||
.pm-search { width: 240px; padding: 6px 8px; border: 1px solid #d1d5db; border-radius: 6px; }
|
||||
.pm-main { display: grid; grid-template-columns: 280px 1fr; height: calc(100vh - 50px); }
|
||||
.pm-cats { border-right: 1px solid #e5e7eb; overflow: auto; padding: 8px; }
|
||||
.pm-section-title { font-size: 12px; color: #6b7280; margin: 8px 0; }
|
||||
.pm-cats ul { list-style: none; margin: 0; padding: 0; }
|
||||
.pm-cats li { padding: 6px 8px; border-radius: 6px; cursor: pointer; }
|
||||
.pm-cats li.active { background: #eef2ff; }
|
||||
.pm-color { display: inline-block; width: 12px; height: 12px; border-radius: 2px; margin-right: 6px; vertical-align: middle; }
|
||||
.pm-list { padding: 8px; overflow: auto; }
|
||||
.pm-list-toolbar { display: flex; justify-content: flex-end; margin-bottom: 8px; }
|
||||
.pm-empty { color: #6b7280; padding: 20px; }
|
||||
.pm-tags { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
|
||||
.pm-tags li { display: grid; grid-template-columns: 24px 1fr 1fr auto auto; align-items: center; gap: 6px; padding: 6px; border: 1px solid #e5e7eb; border-radius: 6px; }
|
||||
.pm-tags li.hidden { opacity: 0.5; }
|
||||
.pm-handle { cursor: grab; user-select: none; color: #6b7280; text-align: center; }
|
||||
.pm-key, .pm-trans { padding: 6px 8px; border: 1px solid #d1d5db; border-radius: 6px; }
|
||||
.pm-hide, .pm-del, .pm-list-toolbar button, .pm-right button { padding: 6px 10px; border: 1px solid #d1d5db; border-radius: 6px; background: white; cursor: pointer; line-height: 1; }
|
||||
.pm-hide:hover, .pm-del:hover, .pm-list-toolbar button:hover, .pm-right button:hover, .pm-btn:hover { background: #f3f4f6; }
|
||||
</style>
|
||||
@@ -0,0 +1,8 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
|
||||
const app = createApp(App)
|
||||
app.use(createPinia())
|
||||
app.mount('#app')
|
||||
@@ -0,0 +1,492 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { loadInitialDataset } from '../utils/yamlLoader';
|
||||
import type { PromptDataset, PromptCategory, PromptGroup, PromptTag, LangCode, ExportBundle, CustomDiff, PromptPreset } from '../types';
|
||||
|
||||
const LS_KEY = 'ops.prompt.dataset.v1';
|
||||
let saveTimer: number | null = null; // 非响应式计时器,避免递归更新
|
||||
let baseline: PromptDataset | null = null; // 基线词库(从 public/sd 加载)
|
||||
|
||||
function deepClone<T>(obj: T): T {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
export const usePromptStore = defineStore('promptStore', {
|
||||
state: () => ({
|
||||
dataset: null as PromptDataset | null,
|
||||
selectedLang: 'zh_CN' as LangCode,
|
||||
selectedCategoryIndex: 0,
|
||||
selectedGroupIndex: 0,
|
||||
searchQuery: '',
|
||||
// 编辑器相关
|
||||
promptText: '',
|
||||
presets: [] as PromptPreset[],
|
||||
}),
|
||||
getters: {
|
||||
categories: (s) => s.dataset?.categories ?? [],
|
||||
currentCategory: (s) => s.dataset?.categories[s.selectedCategoryIndex] ?? null,
|
||||
currentGroup(): PromptGroup | null {
|
||||
return this.currentCategory?.groups[this.selectedGroupIndex] ?? null;
|
||||
},
|
||||
languages: (s) => s.dataset?.languages ?? ['en'],
|
||||
filteredTags(): PromptTag[] {
|
||||
const grp = this.currentGroup;
|
||||
if (!grp) return [] as PromptTag[];
|
||||
const q = this.searchQuery.trim().toLowerCase();
|
||||
if (!q) return grp.tags;
|
||||
return grp.tags.filter((t) => {
|
||||
const trans = t.translation?.[this.selectedLang] ?? '';
|
||||
return (
|
||||
t.key.toLowerCase().includes(q) ||
|
||||
trans.toLowerCase().includes(q)
|
||||
);
|
||||
});
|
||||
},
|
||||
tokens(s): string[] {
|
||||
return splitTokens(s.promptText);
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async initialize() {
|
||||
// 先加载基线词库
|
||||
baseline = await loadInitialDataset();
|
||||
const raw = localStorage.getItem(LS_KEY);
|
||||
if (raw) {
|
||||
try {
|
||||
const bundle = JSON.parse(raw) as ExportBundle;
|
||||
if (bundle.dataset) {
|
||||
this.dataset = bundle.dataset;
|
||||
} else if (bundle.customDiff) {
|
||||
this.dataset = this.applyDiff(deepClone(baseline!), bundle.customDiff);
|
||||
} else {
|
||||
this.dataset = deepClone(baseline!);
|
||||
}
|
||||
this.presets = bundle.presets || [];
|
||||
// 恢复编辑器内容与语言
|
||||
if (typeof bundle.promptText === 'string') {
|
||||
this.promptText = bundle.promptText;
|
||||
}
|
||||
if (bundle.selectedLang) {
|
||||
this.selectedLang = bundle.selectedLang as LangCode;
|
||||
}
|
||||
} catch {
|
||||
this.dataset = deepClone(baseline!);
|
||||
}
|
||||
} else {
|
||||
this.dataset = deepClone(baseline!);
|
||||
}
|
||||
// 若无恢复语言,则按数据集进行推断
|
||||
if (!this.selectedLang) {
|
||||
const guessLang: LangCode = (this.dataset.languages.includes('zh_CN') ? 'zh_CN' : 'en') as LangCode;
|
||||
this.selectedLang = guessLang;
|
||||
}
|
||||
this.autoPersist();
|
||||
},
|
||||
autoPersist() {
|
||||
// 订阅状态变化并延迟保存(不写入 store,避免递归)
|
||||
this.$subscribe(() => {
|
||||
if (saveTimer) window.clearTimeout(saveTimer);
|
||||
saveTimer = window.setTimeout(() => {
|
||||
this.save();
|
||||
}, 400);
|
||||
}, { detached: true });
|
||||
},
|
||||
save() {
|
||||
if (!this.dataset) return;
|
||||
const bundle: ExportBundle = {
|
||||
version: 1,
|
||||
savedAt: new Date().toISOString(),
|
||||
dataset: deepClone(this.dataset),
|
||||
presets: deepClone(this.presets),
|
||||
promptText: this.promptText,
|
||||
selectedLang: this.selectedLang,
|
||||
};
|
||||
localStorage.setItem(LS_KEY, JSON.stringify(bundle));
|
||||
},
|
||||
resetToDefault() {
|
||||
this.importFromBundle(null);
|
||||
},
|
||||
async importFromBundle(bundle: ExportBundle | null) {
|
||||
if (!bundle) {
|
||||
if (!baseline) baseline = await loadInitialDataset();
|
||||
this.dataset = deepClone(baseline!);
|
||||
} else {
|
||||
if (bundle.dataset) {
|
||||
this.dataset = bundle.dataset;
|
||||
} else if (bundle.customDiff) {
|
||||
if (!baseline) baseline = await loadInitialDataset();
|
||||
this.dataset = this.applyDiff(deepClone(baseline!), bundle.customDiff);
|
||||
}
|
||||
this.presets = bundle.presets || [];
|
||||
}
|
||||
this.selectedCategoryIndex = 0;
|
||||
this.selectedGroupIndex = 0;
|
||||
this.save();
|
||||
},
|
||||
exportToJson(): string {
|
||||
// 导出仅包含自定义差异(不包含公共词库)
|
||||
const diff = this.buildDiff(baseline!, this.dataset!);
|
||||
const bundle: ExportBundle = {
|
||||
version: 1,
|
||||
savedAt: new Date().toISOString(),
|
||||
customDiff: diff,
|
||||
presets: deepClone(this.presets),
|
||||
};
|
||||
return JSON.stringify(bundle, null, 2);
|
||||
},
|
||||
setLanguage(lang: LangCode) {
|
||||
this.selectedLang = lang;
|
||||
},
|
||||
selectCategory(idx: number) {
|
||||
this.selectedCategoryIndex = idx;
|
||||
this.selectedGroupIndex = 0;
|
||||
},
|
||||
selectGroup(idx: number) {
|
||||
this.selectedGroupIndex = idx;
|
||||
},
|
||||
setSearch(q: string) {
|
||||
this.searchQuery = q;
|
||||
},
|
||||
addTag(groupId: string, key = 'new_tag') {
|
||||
const grp = this.findGroupById(groupId);
|
||||
if (!grp) return;
|
||||
grp.tags.push({ key, translation: { en: key, [this.selectedLang]: key } });
|
||||
},
|
||||
removeTag(groupId: string, key: string) {
|
||||
const grp = this.findGroupById(groupId);
|
||||
if (!grp) return;
|
||||
grp.tags = grp.tags.filter((t) => t.key !== key);
|
||||
},
|
||||
updateTagKey(groupId: string, oldKey: string, newKey: string) {
|
||||
const grp = this.findGroupById(groupId);
|
||||
if (!grp) return;
|
||||
const tag = grp.tags.find((t) => t.key === oldKey);
|
||||
if (!tag) return;
|
||||
tag.key = newKey;
|
||||
if (!tag.translation) tag.translation = {};
|
||||
tag.translation.en = newKey;
|
||||
},
|
||||
setTranslation(groupId: string, key: string, lang: LangCode, val: string) {
|
||||
const grp = this.findGroupById(groupId);
|
||||
if (!grp) return;
|
||||
const tag = grp.tags.find((t) => t.key === key);
|
||||
if (!tag) return;
|
||||
if (!tag.translation) tag.translation = {};
|
||||
tag.translation[lang] = val;
|
||||
},
|
||||
toggleHidden(groupId: string, key: string) {
|
||||
const grp = this.findGroupById(groupId);
|
||||
if (!grp) return;
|
||||
const tag = grp.tags.find((t) => t.key === key);
|
||||
if (!tag) return;
|
||||
tag.hidden = !tag.hidden;
|
||||
},
|
||||
reorderTags(groupId: string, fromIndex: number, toIndex: number) {
|
||||
const grp = this.findGroupById(groupId);
|
||||
if (!grp) return;
|
||||
const list = grp.tags;
|
||||
if (fromIndex < 0 || toIndex < 0 || fromIndex >= list.length || toIndex >= list.length) return;
|
||||
const [item] = list.splice(fromIndex, 1);
|
||||
if (item) list.splice(toIndex, 0, item);
|
||||
},
|
||||
// —— 编辑器核心:基于左侧文本的 token 操作 ——
|
||||
setPromptText(text: string) {
|
||||
this.promptText = normalizePrompt(text);
|
||||
},
|
||||
setPromptTextRaw(text: string) {
|
||||
// 原始赋值,不做格式化,保留光标与撤回体验
|
||||
this.promptText = text;
|
||||
},
|
||||
replaceChineseComma() {
|
||||
this.promptText = this.promptText.replace(/,/g, ',');
|
||||
},
|
||||
formatPrompt() {
|
||||
this.promptText = normalizePrompt(this.promptText);
|
||||
},
|
||||
updateToken(index: number, newToken: string) {
|
||||
const tokens = splitTokens(this.promptText);
|
||||
if (index < 0 || index >= tokens.length) return;
|
||||
tokens[index] = normalizeToken(newToken);
|
||||
this.promptText = tokens.join(', ');
|
||||
},
|
||||
reorderTokens(fromIndex: number, toIndex: number) {
|
||||
const tokens = splitTokens(this.promptText);
|
||||
if (fromIndex < 0 || toIndex < 0 || fromIndex >= tokens.length || toIndex >= tokens.length) return;
|
||||
const [item] = tokens.splice(fromIndex, 1);
|
||||
tokens.splice(toIndex, 0, item!);
|
||||
this.promptText = tokens.join(', ');
|
||||
},
|
||||
removeToken(index: number) {
|
||||
const tokens = splitTokens(this.promptText);
|
||||
if (index < 0 || index >= tokens.length) return;
|
||||
tokens.splice(index, 1);
|
||||
this.promptText = tokens.join(', ');
|
||||
},
|
||||
addTokenAfter(index: number, token: string) {
|
||||
const tokens = splitTokens(this.promptText);
|
||||
tokens.splice(index + 1, 0, normalizeToken(token));
|
||||
this.promptText = tokens.join(', ');
|
||||
},
|
||||
getTagByKey(key: string): PromptTag | null {
|
||||
for (const cat of this.dataset?.categories || []) {
|
||||
for (const g of cat.groups) {
|
||||
const t = g.tags.find((x) => x.key === key);
|
||||
if (t) return t;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getTranslation(key: string, lang: LangCode): string | null {
|
||||
const tag = this.getTagByKey(key);
|
||||
if (!tag) return null;
|
||||
return tag.translation?.[lang] ?? tag.key;
|
||||
},
|
||||
getSuggestions(prefix: string, limit = 8): string[] {
|
||||
const list: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
const p = prefix.trim().toLowerCase();
|
||||
if (!p) return [];
|
||||
for (const cat of this.dataset?.categories || []) {
|
||||
for (const g of cat.groups) {
|
||||
for (const t of g.tags) {
|
||||
const k = t.key;
|
||||
if (seen.has(k)) continue;
|
||||
if (k.toLowerCase().includes(p)) {
|
||||
list.push(k);
|
||||
seen.add(k);
|
||||
if (list.length >= limit) return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
},
|
||||
addMapping(key: string, lang: LangCode, val: string) {
|
||||
// 若 key 已存在则更新翻译;否则在自定义分组增加映射
|
||||
const exist = this.getTagByKey(key);
|
||||
if (exist) {
|
||||
if (!exist.translation) exist.translation = {};
|
||||
exist.translation[lang] = val;
|
||||
return;
|
||||
}
|
||||
const grp = this.ensureCustomGroup();
|
||||
grp.tags.push({ key, translation: { en: key, [lang]: val } });
|
||||
},
|
||||
ensureCustomGroup(): PromptGroup {
|
||||
const catName = 'Custom';
|
||||
let cat = this.dataset?.categories.find((c) => c.name === catName);
|
||||
if (!cat) {
|
||||
cat = { id: `cat_${Math.random().toString(36).slice(2, 9)}`, name: catName, groups: [] };
|
||||
this.dataset?.categories.push(cat);
|
||||
}
|
||||
let grp = cat.groups.find((g) => g.name === 'User Mapping');
|
||||
if (!grp) {
|
||||
grp = { id: `grp_${Math.random().toString(36).slice(2, 9)}`, name: 'User Mapping', color: '#6366f1', tags: [] };
|
||||
cat.groups.push(grp);
|
||||
}
|
||||
return grp;
|
||||
},
|
||||
savePreset(name: string) {
|
||||
const now = new Date().toISOString();
|
||||
const exist = this.presets.find((p) => p.name === name);
|
||||
if (exist) {
|
||||
exist.text = this.promptText;
|
||||
exist.updatedAt = now;
|
||||
} else {
|
||||
this.presets.push({ name, text: this.promptText, updatedAt: now });
|
||||
}
|
||||
this.save();
|
||||
},
|
||||
loadPreset(name: string) {
|
||||
const p = this.presets.find((x) => x.name === name);
|
||||
if (!p) return;
|
||||
this.promptText = p.text;
|
||||
},
|
||||
deletePreset(name: string) {
|
||||
this.presets = this.presets.filter((p) => p.name !== name);
|
||||
this.save();
|
||||
},
|
||||
renamePreset(oldName: string, newName: string) {
|
||||
const target = this.presets.find((p) => p.name === oldName);
|
||||
if (!target) return;
|
||||
const conflict = this.presets.find((p) => p.name === newName);
|
||||
const now = new Date().toISOString();
|
||||
if (conflict && conflict !== target) {
|
||||
// 合并到冲突项:用新名覆盖旧名文本
|
||||
conflict.text = target.text;
|
||||
conflict.updatedAt = now;
|
||||
this.deletePreset(oldName);
|
||||
} else {
|
||||
target.name = newName;
|
||||
target.updatedAt = now;
|
||||
}
|
||||
this.save();
|
||||
},
|
||||
findGroupById(id: string): PromptGroup | null {
|
||||
for (const cat of this.dataset?.categories || []) {
|
||||
const grp = cat.groups.find((g) => g.id === id);
|
||||
if (grp) return grp;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
// 工具方法:构建差异与应用差异
|
||||
buildDiff(base: PromptDataset, cur: PromptDataset): CustomDiff {
|
||||
const categories: CustomDiff['categories'] = [];
|
||||
const baseCatMap = new Map(base.categories.map((c) => [c.name, c]));
|
||||
const curCatMap = new Map(cur.categories.map((c) => [c.name, c]));
|
||||
|
||||
for (const [name, curCat] of curCatMap.entries()) {
|
||||
const baseCat = baseCatMap.get(name);
|
||||
const catDiff: CustomDiff['categories'][number] = { name };
|
||||
if (!baseCat) {
|
||||
catDiff.addedGroups = curCat.groups.map((g) => deepClone(g));
|
||||
} else {
|
||||
const baseGrpMap = new Map(baseCat.groups.map((g) => [g.name, g]));
|
||||
const curGrpMap = new Map(curCat.groups.map((g) => [g.name, g]));
|
||||
const groupsDiff: NonNullable<typeof catDiff.groups> = [];
|
||||
|
||||
for (const [gname, curGrp] of curGrpMap.entries()) {
|
||||
const baseGrp = baseGrpMap.get(gname);
|
||||
if (!baseGrp) {
|
||||
if (!catDiff.addedGroups) catDiff.addedGroups = [];
|
||||
catDiff.addedGroups.push(deepClone(curGrp));
|
||||
continue;
|
||||
}
|
||||
const updated: { name: string; color?: string; added?: PromptTag[]; removed?: string[]; updated?: Array<{ key: string; translation?: Partial<Record<LangCode, string>>; hidden?: boolean }>; order?: string[] } = { name: gname } as any;
|
||||
if ((curGrp.color || '') !== (baseGrp.color || '')) updated.color = curGrp.color;
|
||||
const baseTagMap = new Map(baseGrp.tags.map((t) => [t.key, t]));
|
||||
const curTagMap = new Map(curGrp.tags.map((t) => [t.key, t]));
|
||||
|
||||
const addList: PromptTag[] = [];
|
||||
const updList: Array<{ key: string; translation?: Partial<Record<LangCode, string>>; hidden?: boolean }> = [];
|
||||
for (const [key, curTag] of curTagMap.entries()) {
|
||||
const baseTag = baseTagMap.get(key);
|
||||
if (!baseTag) {
|
||||
addList.push(deepClone(curTag));
|
||||
} else {
|
||||
let changed = false;
|
||||
const change: { key: string; translation?: Partial<Record<LangCode, string>>; hidden?: boolean } = { key };
|
||||
for (const l of ['en', 'zh_CN', 'es_ES'] as LangCode[]) {
|
||||
const a = baseTag.translation?.[l] ?? '';
|
||||
const b = curTag.translation?.[l] ?? '';
|
||||
if (a !== b) {
|
||||
if (!change.translation) change.translation = {};
|
||||
change.translation[l] = b;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
const aHidden = !!baseTag.hidden;
|
||||
const bHidden = !!curTag.hidden;
|
||||
if (aHidden !== bHidden) { change.hidden = bHidden; changed = true; }
|
||||
if (changed) updList.push(change);
|
||||
}
|
||||
}
|
||||
const remList: string[] = [];
|
||||
for (const [key] of baseTagMap.entries()) {
|
||||
if (!curTagMap.has(key)) remList.push(key);
|
||||
}
|
||||
const baseOrder = baseGrp.tags.map((t) => t.key);
|
||||
const curOrder = curGrp.tags.map((t) => t.key);
|
||||
const orderChanged = baseOrder.length !== curOrder.length || baseOrder.some((k, i) => k !== curOrder[i]);
|
||||
|
||||
if (updated.color || addList.length || remList.length || updList.length || orderChanged) {
|
||||
if (updated.color) updated.color = updated.color;
|
||||
if (addList.length) updated.added = addList;
|
||||
if (remList.length) updated.removed = remList;
|
||||
if (updList.length) updated.updated = updList;
|
||||
if (orderChanged) updated.order = curOrder;
|
||||
groupsDiff.push(updated as any);
|
||||
}
|
||||
}
|
||||
const removedGroups = baseCat.groups.filter((bg) => !curGrpMap.has(bg.name)).map((g) => g.name);
|
||||
if (removedGroups.length) catDiff.removedGroups = removedGroups;
|
||||
if (groupsDiff.length) catDiff.groups = groupsDiff;
|
||||
}
|
||||
if (catDiff.addedGroups?.length || catDiff.removedGroups?.length || catDiff.groups?.length) {
|
||||
categories.push(catDiff);
|
||||
}
|
||||
}
|
||||
for (const baseCat of base.categories) {
|
||||
if (!curCatMap.has(baseCat.name)) {
|
||||
categories.push({ name: baseCat.name, removedGroups: baseCat.groups.map((g) => g.name) });
|
||||
}
|
||||
}
|
||||
return { categories };
|
||||
},
|
||||
applyDiff(target: PromptDataset, diff: CustomDiff): PromptDataset {
|
||||
const catMap = new Map(target.categories.map((c) => [c.name, c]));
|
||||
for (const c of diff.categories || []) {
|
||||
let cat = catMap.get(c.name);
|
||||
if (!cat) {
|
||||
cat = { id: `cat_${Math.random().toString(36).slice(2, 9)}`, name: c.name, groups: [] };
|
||||
target.categories.push(cat);
|
||||
catMap.set(c.name, cat);
|
||||
}
|
||||
const grpMap = new Map(cat.groups.map((g) => [g.name, g]));
|
||||
for (const g of c.addedGroups || []) {
|
||||
const copy = deepClone(g);
|
||||
// 保持现有结构:如果没有 id,生成一个
|
||||
(copy as PromptGroup).id = `grp_${Math.random().toString(36).slice(2, 9)}`;
|
||||
(copy as PromptGroup).id = `grp_${Math.random().toString(36).slice(2, 9)}`;
|
||||
cat.groups.push(copy as PromptGroup);
|
||||
grpMap.set(copy.name, copy as PromptGroup);
|
||||
}
|
||||
for (const gname of c.removedGroups || []) {
|
||||
cat.groups = cat.groups.filter((g) => g.name !== gname);
|
||||
grpMap.delete(gname);
|
||||
}
|
||||
for (const gdiff of c.groups || []) {
|
||||
let grp = grpMap.get(gdiff.name);
|
||||
if (!grp) {
|
||||
grp = { id: `grp_${Math.random().toString(36).slice(2, 9)}`, name: gdiff.name, color: gdiff.color, tags: [] };
|
||||
cat.groups.push(grp);
|
||||
grpMap.set(grp.name, grp);
|
||||
}
|
||||
if (gdiff.color !== undefined) grp.color = gdiff.color;
|
||||
const tagMap = new Map(grp.tags.map((t) => [t.key, t]));
|
||||
for (const t of gdiff.added || []) {
|
||||
const copy = deepClone(t);
|
||||
grp.tags.push(copy);
|
||||
tagMap.set(copy.key, copy);
|
||||
}
|
||||
for (const key of gdiff.removed || []) {
|
||||
grp.tags = grp.tags.filter((t) => t.key !== key);
|
||||
tagMap.delete(key);
|
||||
}
|
||||
for (const u of gdiff.updated || []) {
|
||||
const tag = tagMap.get(u.key);
|
||||
if (!tag) continue;
|
||||
if (u.translation) tag.translation = { ...(tag.translation || {}), ...u.translation };
|
||||
if (u.hidden !== undefined) tag.hidden = u.hidden;
|
||||
}
|
||||
if (gdiff.order && gdiff.order.length) {
|
||||
const keyToTag = new Map(grp.tags.map((t) => [t.key, t]));
|
||||
const reordered: PromptTag[] = [];
|
||||
for (const key of gdiff.order) {
|
||||
const tag = keyToTag.get(key);
|
||||
if (tag) reordered.push(tag);
|
||||
}
|
||||
// 保留未列出的条目
|
||||
for (const t of grp.tags) {
|
||||
if (!gdiff.order.includes(t.key)) reordered.push(t);
|
||||
}
|
||||
grp.tags = reordered;
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
},
|
||||
},
|
||||
});
|
||||
// —— 工具方法 ——
|
||||
function splitTokens(text: string): string[] {
|
||||
return text
|
||||
.split(/[,,]/)
|
||||
.map((s) => s.trim())
|
||||
.filter((s) => s.length > 0);
|
||||
}
|
||||
function normalizeToken(t: string): string {
|
||||
return t.trim();
|
||||
}
|
||||
function normalizePrompt(text: string): string {
|
||||
return splitTokens(text).join(', ');
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
:root {
|
||||
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
place-items: center;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
export type LangCode = 'en' | 'zh_CN' | 'es_ES';
|
||||
|
||||
export interface PromptTag {
|
||||
key: string;
|
||||
translation?: Partial<Record<LangCode, string>>;
|
||||
hidden?: boolean;
|
||||
}
|
||||
|
||||
export interface PromptGroup {
|
||||
id: string;
|
||||
name: string;
|
||||
color?: string;
|
||||
tags: PromptTag[];
|
||||
}
|
||||
|
||||
export interface PromptCategory {
|
||||
id: string;
|
||||
name: string;
|
||||
groups: PromptGroup[];
|
||||
}
|
||||
|
||||
export interface PromptDataset {
|
||||
categories: PromptCategory[];
|
||||
languages: LangCode[];
|
||||
updatedAt?: string;
|
||||
}
|
||||
|
||||
export interface ExportBundle {
|
||||
version: number;
|
||||
savedAt: string;
|
||||
dataset?: PromptDataset; // full snapshot (used for localStorage persistence)
|
||||
customDiff?: CustomDiff; // only user-defined changes for export/import
|
||||
presets?: PromptPreset[]; // saved prompt texts by name
|
||||
// editor state persistence
|
||||
promptText?: string;
|
||||
selectedLang?: LangCode;
|
||||
}
|
||||
|
||||
export interface CustomDiff {
|
||||
categories: Array<{
|
||||
name: string;
|
||||
addedGroups?: Array<{
|
||||
name: string;
|
||||
color?: string;
|
||||
tags: PromptTag[];
|
||||
}>;
|
||||
removedGroups?: string[];
|
||||
groups?: Array<{
|
||||
name: string;
|
||||
color?: string;
|
||||
added?: PromptTag[];
|
||||
removed?: string[]; // tag keys
|
||||
updated?: Array<{
|
||||
key: string;
|
||||
translation?: Partial<Record<LangCode, string>>;
|
||||
hidden?: boolean;
|
||||
}>;
|
||||
order?: string[]; // tag keys in desired order
|
||||
}>;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface PromptPreset {
|
||||
name: string;
|
||||
text: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import YAML from 'yaml';
|
||||
import type { PromptDataset, PromptCategory, PromptGroup, PromptTag, LangCode } from '../types';
|
||||
|
||||
type YamlGroup = {
|
||||
name: string;
|
||||
color?: string;
|
||||
tags: Record<string, string | null>;
|
||||
};
|
||||
|
||||
type YamlCategory = {
|
||||
name: string;
|
||||
groups: YamlGroup[];
|
||||
};
|
||||
|
||||
async function fetchYaml(path: string): Promise<YamlCategory[]> {
|
||||
const res = await fetch(path);
|
||||
if (!res.ok) throw new Error(`Failed to fetch ${path}: ${res.status}`);
|
||||
const text = await res.text();
|
||||
const parsed = YAML.parse(text) as YamlCategory[];
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function uid(prefix: string): string {
|
||||
return `${prefix}_${Math.random().toString(36).slice(2, 9)}`;
|
||||
}
|
||||
|
||||
function collectTranslations(root: YamlCategory[]): Record<string, string> {
|
||||
const map: Record<string, string> = {};
|
||||
for (const cat of root) {
|
||||
for (const grp of cat.groups || []) {
|
||||
const entries = grp.tags || {};
|
||||
for (const [key, val] of Object.entries(entries)) {
|
||||
if (key && typeof key === 'string') {
|
||||
const value = val == null ? '' : String(val);
|
||||
map[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
function buildBaseStructure(defaultYaml: YamlCategory[]): PromptCategory[] {
|
||||
const categories: PromptCategory[] = [];
|
||||
for (const cat of defaultYaml) {
|
||||
const groups: PromptGroup[] = [];
|
||||
for (const grp of cat.groups || []) {
|
||||
const tags: PromptTag[] = [];
|
||||
const entries = grp.tags || {};
|
||||
for (const key of Object.keys(entries)) {
|
||||
tags.push({ key, translation: { en: key } });
|
||||
}
|
||||
groups.push({ id: uid('grp'), name: grp.name, color: grp.color, tags });
|
||||
}
|
||||
categories.push({ id: uid('cat'), name: cat.name, groups });
|
||||
}
|
||||
return categories;
|
||||
}
|
||||
|
||||
function mergeLanguage(dataset: PromptDataset, translations: Record<string, string>, lang: LangCode) {
|
||||
for (const cat of dataset.categories) {
|
||||
for (const grp of cat.groups) {
|
||||
for (const tag of grp.tags) {
|
||||
const t = translations[tag.key];
|
||||
if (typeof t === 'string') {
|
||||
if (!tag.translation) tag.translation = {};
|
||||
tag.translation[lang] = t || tag.key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function loadInitialDataset(): Promise<PromptDataset> {
|
||||
// Base: default.yaml defines structure and keys
|
||||
const defaultYaml = await fetchYaml('/sd/default.yaml');
|
||||
const baseCategories = buildBaseStructure(defaultYaml);
|
||||
const dataset: PromptDataset = {
|
||||
categories: baseCategories,
|
||||
languages: ['en'],
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
|
||||
// zh_CN and es_ES translations are optional
|
||||
try {
|
||||
const zhRoot = await fetchYaml('/sd/zh_CN.yaml');
|
||||
const zhMap = collectTranslations(zhRoot);
|
||||
mergeLanguage(dataset, zhMap, 'zh_CN');
|
||||
if (!dataset.languages.includes('zh_CN')) dataset.languages.push('zh_CN');
|
||||
} catch (err) {
|
||||
// ignore if missing
|
||||
console.warn('zh_CN.yaml not found or invalid', err);
|
||||
}
|
||||
|
||||
try {
|
||||
const esRoot = await fetchYaml('/sd/es_ES.yaml');
|
||||
const esMap = collectTranslations(esRoot);
|
||||
mergeLanguage(dataset, esMap, 'es_ES');
|
||||
if (!dataset.languages.includes('es_ES')) dataset.languages.push('es_ES');
|
||||
} catch (err) {
|
||||
// ignore if missing
|
||||
console.warn('es_ES.yaml not found or invalid', err);
|
||||
}
|
||||
|
||||
return dataset;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.dom.json",
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"types": ["vite/client"],
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2023",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"types": ["node"],
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
})
|
||||
Reference in New Issue
Block a user