mirror of
https://github.com/EstrellaXD/Auto_Bangumi.git
synced 2026-11-06 07:29:09 +08:00
chore(deps): 前端构建与测试工具链升级到 Vite 8 / Vitest 5 / TS 6 / Storybook 10
- vite 8、@vitejs/plugin-vue 6、vitest 5、happy-dom 21、@vue/test-utils 2.5、 typescript ~6.0.3、vue-tsc 3.3、unocss 66.11、unplugin-auto-import 21、 unplugin-vue-components 32、sass-embedded 1.105、playwright 1.64.1、storybook 10.6 - vite.config:去掉 defineModel 开关;Sass 改用 @use;esbuild.drop 换成 rolldown 的 dropConsole;__dirname 换成 import.meta.dirname;两处 alias 改为 resolve.tsconfigPaths - 删除 plugin-vue-jsx:三个 SFC 的 JSX 改写为 h() - 删除 preset-attributify:模板里的属性式工具类全部改回 class (含 space-y-*、text-*、line、i-carbon-* 等,不止 5 处 flex="~") - 删除 vite-plugin-pwa:改为静态 manifest.webmanifest + 自毁 sw.js(4.1 删除) - tsconfig:moduleResolution Bundler、去掉 baseUrl 与 JSX 配置, app 的 types 只留 vitest/globals - Storybook 10:addon-links + addon-docs,stories 改从 @storybook/vue3-vite 导入类型 - @intlify/unplugin-vue-i18n 0.11 -> 11.2.7(Vite 8 下旧版产物被 vite-json 再次解析而失败); 新版只产出 AST 文案,故打开 __INTLIFY_JIT_COMPILATION__,hasKey 识别 AST 叶子
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -197,7 +197,6 @@ webui/test-results/
|
||||
dist.zip
|
||||
dist-ssr
|
||||
*.local
|
||||
dev-dist
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
|
||||
@@ -54,7 +54,7 @@ Semantic scale only: `--z-dropdown` 10 · `--z-sticky` 20 · `--z-fixed` 30 · `
|
||||
|
||||
- **Universal `ab-*` layer** (`src/components/basic/`): pages and composites use only `ab-*` components — never raw `NButton`/`NInput`/bare styled `<button>`. Where naive-ui fits, the `ab-*` component wraps it (`ab-select`, `ab-switch`, `ab-tooltip`); where it doesn't, the component is custom on tokens (`ab-button`, `ab-tag`, `ab-modal`).
|
||||
- **Composites**: app-specific `ab-*` components (`ab-add-rss`, `ab-edit-rule`, calendar pieces, layout shells) compose the universal layer + tokens.
|
||||
- **Utility CSS**: UnoCSS with attributify preset; prefer token-backed values over arbitrary literals.
|
||||
- **Utility CSS**: UnoCSS (`presetWind3`), utilities go in `class` (no attributify); prefer token-backed values over arbitrary literals.
|
||||
- Every interactive component ships all states: default, hover, focus-visible, active, disabled, loading, error — and ≥44px touch targets on mobile (`--touch-target`).
|
||||
- Soft Ink component grammar: filled controls on `--color-surface-2` (border only on focus/error); tags are hairline chips with a square semantic mark and ink text; status is a square mark + ink label; tabs underline (no thumb); alerts are hairline outlines led by a bold colored word (no icons, no tinted fills); toasts are inverted ink chips; empty states are left-aligned prose with one action; loading is skeletons, not spinners-in-content.
|
||||
- Every universal component ships a Storybook story and (where it has behavior) a vitest test in `__tests__/`.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/build
|
||||
/dist
|
||||
/dev-dist
|
||||
/e2e/.auth
|
||||
/playwright-report
|
||||
/test-results
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/build
|
||||
/dist
|
||||
/dev-dist
|
||||
/e2e/.auth
|
||||
/playwright-report
|
||||
/test-results
|
||||
|
||||
@@ -3,18 +3,11 @@ import Unocss from 'unocss/vite';
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||
addons: [
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-interactions',
|
||||
],
|
||||
addons: ['@storybook/addon-links', '@storybook/addon-docs'],
|
||||
framework: {
|
||||
name: '@storybook/vue3-vite',
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
autodocs: 'tag',
|
||||
},
|
||||
viteFinal(config) {
|
||||
config.plugins?.push(Unocss());
|
||||
// Add other configuration here depending on your use case
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Preview } from '@storybook/vue3';
|
||||
import type { Preview } from '@storybook/vue3-vite';
|
||||
import '@unocss/reset/tailwind-compat.css';
|
||||
import 'uno.css';
|
||||
import 'virtual:uno.css';
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<meta name="theme-color" content="#FAFAFA" />
|
||||
<link rel="icon" href="/images/logo.svg" />
|
||||
<link rel="apple-touch-icon" href="/images/apple-touch-icon.png" />
|
||||
<link rel="manifest" href="/manifest.webmanifest" />
|
||||
<meta name="description" content="Automated Bangumi Download Tool" />
|
||||
<!-- Self-hosted Inter (variable, 300–700): no render-blocking Google
|
||||
Fonts round-trip, and it works where fonts.googleapis.com is slow
|
||||
|
||||
@@ -39,39 +39,31 @@
|
||||
"@autobangumi/plugin-ui": "workspace:*",
|
||||
"@icon-park/vue-next": "^1.4.2",
|
||||
"@iconify-json/simple-icons": "1.2.89",
|
||||
"@intlify/unplugin-vue-i18n": "^0.11.0",
|
||||
"@playwright/test": "1.61.1",
|
||||
"@storybook/addon-essentials": "^7.6.20",
|
||||
"@storybook/addon-interactions": "^7.6.20",
|
||||
"@storybook/addon-links": "^7.6.20",
|
||||
"@storybook/blocks": "^7.6.20",
|
||||
"@storybook/testing-library": "0.0.14-next.2",
|
||||
"@storybook/vue3": "^7.6.20",
|
||||
"@storybook/vue3-vite": "^7.6.20",
|
||||
"@intlify/unplugin-vue-i18n": "^11.2.7",
|
||||
"@playwright/test": "1.64.1",
|
||||
"@storybook/addon-docs": "^10.6.2",
|
||||
"@storybook/addon-links": "^10.6.2",
|
||||
"@storybook/vue3-vite": "^10.6.2",
|
||||
"@types/node": "^24",
|
||||
"@unocss/preset-attributify": "^0.55.7",
|
||||
"@unocss/preset-rem-to-px": "^0.51.13",
|
||||
"@unocss/reset": "^0.51.13",
|
||||
"@vitejs/plugin-vue": "^4.6.2",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"@vue/runtime-dom": "^3.5.8",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"@unocss/preset-rem-to-px": "^66.11.0",
|
||||
"@unocss/reset": "^66.11.0",
|
||||
"@vitejs/plugin-vue": "^6.0.10",
|
||||
"@vue/test-utils": "^2.5.1",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-prettier": "^8.10.0",
|
||||
"eslint-plugin-storybook": "^0.6.15",
|
||||
"happy-dom": "^12.10.3",
|
||||
"eslint-plugin-storybook": "^10.6.2",
|
||||
"happy-dom": "^21.0.3",
|
||||
"husky": "^8.0.3",
|
||||
"prettier": "^2.8.8",
|
||||
"sass-embedded": "^1.79.3",
|
||||
"storybook": "^7.6.20",
|
||||
"typescript": "^4.9.5",
|
||||
"unocss": "^0.51.13",
|
||||
"unplugin-auto-import": "^0.10.3",
|
||||
"unplugin-vue-components": "^0.24.1",
|
||||
"sass-embedded": "^1.105.2",
|
||||
"storybook": "^10.6.2",
|
||||
"typescript": "~6.0.3",
|
||||
"unocss": "^66.11.0",
|
||||
"unplugin-auto-import": "^21.1.0",
|
||||
"unplugin-vue-components": "^32.1.0",
|
||||
"unplugin-vue-router": "^0.6.4",
|
||||
"vite": "^4.5.5",
|
||||
"vite-plugin-pwa": "^0.16.7",
|
||||
"vitest": "^0.30.1",
|
||||
"vue-tsc": "^1.8.27"
|
||||
"vite": "^8.3.4",
|
||||
"vitest": "^5.0.5",
|
||||
"vue-tsc": "^3.3.13"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"moduleResolution": "Bundler",
|
||||
"strict": true,
|
||||
"isolatedModules": true,
|
||||
"lib": ["ESNext", "DOM"],
|
||||
|
||||
11207
webui/pnpm-lock.yaml
generated
11207
webui/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,10 @@
|
||||
packages:
|
||||
- packages/*
|
||||
publicHoistPattern:
|
||||
- '@vue/runtime-core'
|
||||
- '*eslint*'
|
||||
- '*prettier*'
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
vue-demi: true
|
||||
# sass 的可选依赖,平台预编译包已覆盖,无需跑源码构建
|
||||
'@parcel/watcher': false
|
||||
|
||||
30
webui/public/manifest.webmanifest
Normal file
30
webui/public/manifest.webmanifest
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"name": "AutoBangumi",
|
||||
"short_name": "AutoBangumi",
|
||||
"start_url": "./",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"lang": "en",
|
||||
"scope": "./",
|
||||
"description": "Automated Bangumi Download Tool",
|
||||
"theme_color": "#ffffff",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/images/logo.svg",
|
||||
"sizes": "any",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/images/pwa-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/images/pwa-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any"
|
||||
}
|
||||
]
|
||||
}
|
||||
16
webui/public/sw.js
Normal file
16
webui/public/sw.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// ponytail: 自毁 SW,清理 2023-09 前注册的旧 SW;4.1 删除本文件
|
||||
// 旧版 vite-plugin-pwa 注册的 SW 仍会按 /sw.js 检查更新,取到本文件后:
|
||||
// 清空它留下的缓存、注销自己,再让它控制的页面重新加载,回到无 SW 的普通请求
|
||||
self.addEventListener('install', () => self.skipWaiting());
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(
|
||||
(async () => {
|
||||
const keys = await caches.keys();
|
||||
await Promise.all(keys.map((key) => caches.delete(key)));
|
||||
await self.registration.unregister();
|
||||
const clients = await self.clients.matchAll({ type: 'window' });
|
||||
clients.forEach((client) => client.navigate(client.url));
|
||||
})()
|
||||
);
|
||||
});
|
||||
@@ -155,7 +155,7 @@ const naiveTheme = computed(() => (isDark.value ? darkTheme : null));
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import './style/var';
|
||||
@import './style/transition';
|
||||
@import './style/global';
|
||||
@use './style/var';
|
||||
@use './style/transition';
|
||||
@use './style/global';
|
||||
</style>
|
||||
|
||||
@@ -12,7 +12,7 @@ const { user, update } = useAuth();
|
||||
size="sm"
|
||||
:title="$t('topbar.profile.pop_title')"
|
||||
>
|
||||
<div space-y-16>
|
||||
<div class="space-y-16">
|
||||
<ab-field :label="$t('topbar.profile.username')">
|
||||
<ab-input
|
||||
v-model="user.username"
|
||||
|
||||
@@ -7,7 +7,7 @@ describe('ab-input', () => {
|
||||
const wrapper = mount(AbInput, {
|
||||
props: {
|
||||
modelValue: 'abc',
|
||||
'onUpdate:modelValue': (v: string) =>
|
||||
'onUpdate:modelValue': (v: string | number) =>
|
||||
wrapper.setProps({ modelValue: v }),
|
||||
},
|
||||
});
|
||||
@@ -27,7 +27,7 @@ describe('ab-input', () => {
|
||||
props: {
|
||||
modelValue: 'abc',
|
||||
clearable: true,
|
||||
'onUpdate:modelValue': (v: string) =>
|
||||
'onUpdate:modelValue': (v: string | number) =>
|
||||
wrapper.setProps({ modelValue: v }),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbAlert from './ab-alert.vue';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbBadge from './ab-badge.vue';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbButton from './ab-button.vue';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbEmpty from './ab-empty.vue';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbField from './ab-field.vue';
|
||||
import AbInput from './ab-input.vue';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { AddOne, Delete, Edit } from '@icon-park/vue-next';
|
||||
|
||||
import AbIconButton from './ab-icon-button.vue';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import AbInput from './ab-input.vue';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbList from './ab-list.vue';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbButton from './ab-button.vue';
|
||||
import AbMenu from './ab-menu.vue';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import AbButton from './ab-button.vue';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbPageTitle from './ab-page-title.vue';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbProgress from './ab-progress.vue';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbSearch from './ab-search.vue';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import AbSegmented from './ab-segmented.vue';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import AbSelect from './ab-select.vue';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbSkeleton from './ab-skeleton.vue';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import AbSplitButton from './ab-split-button.vue';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbStatus from './ab-status.vue';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import AbSwitch from './ab-switch.vue';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbTag from './ab-tag.vue';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbToolbar from './ab-toolbar.vue';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3';
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
||||
|
||||
import AbButton from './ab-button.vue';
|
||||
import AbTooltip from './ab-tooltip.vue';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script lang="tsx" setup>
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
AppStore,
|
||||
Calendar,
|
||||
@@ -119,21 +119,23 @@ const navItems = computed(() => {
|
||||
});
|
||||
|
||||
function Exit() {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t('sidebar.logout')}
|
||||
class={[
|
||||
return h(
|
||||
'button',
|
||||
{
|
||||
type: 'button',
|
||||
'aria-label': t('sidebar.logout'),
|
||||
class: [
|
||||
'sidebar-item sidebar-item--action',
|
||||
isMobileOrTablet.value ? 'h-40' : '',
|
||||
]}
|
||||
onClick={logout}
|
||||
>
|
||||
<Logout size={20} />
|
||||
{!isMobileOrTablet.value && show.value && (
|
||||
<div class="sidebar-item-label">{t('sidebar.logout')}</div>
|
||||
)}
|
||||
</button>
|
||||
],
|
||||
onClick: logout,
|
||||
},
|
||||
[
|
||||
h(Logout, { size: 20 }),
|
||||
!isMobileOrTablet.value &&
|
||||
show.value &&
|
||||
h('div', { class: 'sidebar-item-label' }, t('sidebar.logout')),
|
||||
]
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -22,7 +22,7 @@ let generation = 0;
|
||||
let teardown: (() => void) | null = null;
|
||||
|
||||
function fail(error: unknown) {
|
||||
// 不用 `[plugin:id]` 式模板串:UnoCSS 的 attributify 会把它当成样式规则
|
||||
// 不用 `[plugin:id]` 式模板串:UnoCSS 会把它当成任意属性规则
|
||||
console.error('plugin component failed:', props.ui.plugin_id, error);
|
||||
failed.value = true;
|
||||
loading.value = false;
|
||||
|
||||
@@ -218,7 +218,7 @@ const items = computed(() =>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="downloader" space-y-8>
|
||||
<div v-if="downloader" class="space-y-8">
|
||||
<ab-alert v-if="downloader.provider === 'aria2'">
|
||||
{{ $t('config.downloader_set.aria2_hint') }}
|
||||
</ab-alert>
|
||||
|
||||
@@ -91,7 +91,7 @@ const items = computed<SettingItem<BangumiManage>[]>(() => [
|
||||
|
||||
<template>
|
||||
<ab-fold-panel :title="$t('config.manage_set.title')">
|
||||
<div space-y-8>
|
||||
<div class="space-y-8">
|
||||
<ab-setting v-bind="enableItem" v-model:data="manage.enable"></ab-setting>
|
||||
<ab-setting
|
||||
v-for="i in slotItems"
|
||||
|
||||
@@ -40,7 +40,7 @@ const items: SettingItem<Network>[] = [
|
||||
|
||||
<template>
|
||||
<ab-fold-panel :title="$t('config.network_set.title')">
|
||||
<div space-y-8>
|
||||
<div class="space-y-8">
|
||||
<ab-setting
|
||||
v-for="i in items"
|
||||
:key="i.configKey"
|
||||
|
||||
@@ -51,7 +51,7 @@ const logItems: SettingItem<Log> = {
|
||||
|
||||
<template>
|
||||
<ab-fold-panel :title="$t('config.normal_set.title')">
|
||||
<div space-y-8>
|
||||
<div class="space-y-8">
|
||||
<ab-setting
|
||||
v-for="i in programItems"
|
||||
:key="i.configKey"
|
||||
|
||||
@@ -271,7 +271,7 @@ function getFieldsForType(type: string) {
|
||||
|
||||
<template>
|
||||
<ab-fold-panel :title="$t('config.notification_set.title')">
|
||||
<div space-y-8>
|
||||
<div class="space-y-8">
|
||||
<!-- Global enable switch -->
|
||||
<ab-setting
|
||||
v-model:data="notificationEnabled"
|
||||
@@ -282,7 +282,7 @@ function getFieldsForType(type: string) {
|
||||
/>
|
||||
|
||||
<!-- Provider list -->
|
||||
<div v-if="notificationEnabled" space-y-8>
|
||||
<div v-if="notificationEnabled" class="space-y-8">
|
||||
<div
|
||||
v-for="(provider, index) in providers"
|
||||
:key="index"
|
||||
@@ -307,17 +307,16 @@ function getFieldsForType(type: string) {
|
||||
>
|
||||
<div
|
||||
v-if="testingIndex === index"
|
||||
i-carbon-circle-dash
|
||||
animate-spin
|
||||
class="i-carbon-circle-dash animate-spin"
|
||||
/>
|
||||
<div v-else i-carbon-play />
|
||||
<div v-else class="i-carbon-play" />
|
||||
</ab-icon-button>
|
||||
<ab-icon-button
|
||||
size="sm"
|
||||
:label="$t('config.notification_set.edit')"
|
||||
@click="openEditDialog(index)"
|
||||
>
|
||||
<div i-carbon-edit />
|
||||
<div class="i-carbon-edit" />
|
||||
</ab-icon-button>
|
||||
<ab-icon-button
|
||||
size="sm"
|
||||
@@ -340,7 +339,7 @@ function getFieldsForType(type: string) {
|
||||
:label="$t('config.notification_set.remove')"
|
||||
@click="onRemoveProvider(index)"
|
||||
>
|
||||
<div i-carbon-trash-can />
|
||||
<div class="i-carbon-trash-can" />
|
||||
</ab-icon-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -354,12 +353,12 @@ function getFieldsForType(type: string) {
|
||||
{{ listResult.message }}
|
||||
</div>
|
||||
|
||||
<div line></div>
|
||||
<div class="line"></div>
|
||||
|
||||
<!-- Add provider button -->
|
||||
<div flex="~ justify-end">
|
||||
<div class="flex justify-end">
|
||||
<ab-button size="sm" variant="primary" @click="openAddDialog">
|
||||
<div i-carbon-add />
|
||||
<div class="i-carbon-add" />
|
||||
{{ $t('config.notification_set.add_provider') }}
|
||||
</ab-button>
|
||||
</div>
|
||||
@@ -372,7 +371,7 @@ function getFieldsForType(type: string) {
|
||||
size="sm"
|
||||
:title="$t('config.notification_set.add_provider')"
|
||||
>
|
||||
<div space-y-16>
|
||||
<div class="space-y-16">
|
||||
<ab-field :label="$t('config.notification_set.type')">
|
||||
<NSelect
|
||||
v-model:value="newProvider.type"
|
||||
@@ -421,8 +420,11 @@ function getFieldsForType(type: string) {
|
||||
:disabled="testingIndex === -999"
|
||||
@click="testNewProvider"
|
||||
>
|
||||
<div v-if="testingIndex === -999" i-carbon-circle-dash animate-spin />
|
||||
<div v-else i-carbon-play />
|
||||
<div
|
||||
v-if="testingIndex === -999"
|
||||
class="i-carbon-circle-dash animate-spin"
|
||||
/>
|
||||
<div v-else class="i-carbon-play" />
|
||||
{{ $t('config.notification_set.test') }}
|
||||
</ab-button>
|
||||
<ab-button size="sm" @click="showAddDialog = false">
|
||||
@@ -440,7 +442,7 @@ function getFieldsForType(type: string) {
|
||||
size="sm"
|
||||
:title="$t('config.notification_set.edit_provider')"
|
||||
>
|
||||
<div space-y-16>
|
||||
<div class="space-y-16">
|
||||
<ab-field :label="$t('config.notification_set.type')">
|
||||
<NSelect
|
||||
v-model:value="newProvider.type"
|
||||
@@ -490,8 +492,11 @@ function getFieldsForType(type: string) {
|
||||
:disabled="testingIndex === -999"
|
||||
@click="testNewProvider"
|
||||
>
|
||||
<div v-if="testingIndex === -999" i-carbon-circle-dash animate-spin />
|
||||
<div v-else i-carbon-play />
|
||||
<div
|
||||
v-if="testingIndex === -999"
|
||||
class="i-carbon-circle-dash animate-spin"
|
||||
/>
|
||||
<div v-else class="i-carbon-play" />
|
||||
{{ $t('config.notification_set.test') }}
|
||||
</ab-button>
|
||||
<ab-button size="sm" @click="showEditDialog = false">
|
||||
|
||||
@@ -40,7 +40,7 @@ const items: SettingItem<RssParser>[] = [
|
||||
|
||||
<template>
|
||||
<ab-fold-panel :title="$t('config.parser_set.title')">
|
||||
<div space-y-8>
|
||||
<div class="space-y-8">
|
||||
<ab-setting
|
||||
v-model:data="parser.enable"
|
||||
:label="() => t('config.parser_set.enable')"
|
||||
|
||||
@@ -81,43 +81,40 @@ function formatDate(dateString: string | null): string {
|
||||
|
||||
<template>
|
||||
<ab-fold-panel :title="$t('passkey.title')">
|
||||
<div space-y-8>
|
||||
<div class="space-y-8">
|
||||
<!-- 不支持提示 -->
|
||||
<div v-if="!isSupported" text-orange-500 text-14>
|
||||
<div v-if="!isSupported" class="text-orange-500 text-14">
|
||||
{{ $t('passkey.not_supported') }}
|
||||
</div>
|
||||
|
||||
<!-- 加载中 -->
|
||||
<div v-else-if="loading" text-gray-500 text-14>
|
||||
<div v-else-if="loading" class="text-gray-500 text-14">
|
||||
{{ $t('passkey.loading') }}
|
||||
</div>
|
||||
|
||||
<!-- 无 Passkey -->
|
||||
<div v-else-if="passkeys.length === 0" text-gray-500 text-14>
|
||||
<div v-else-if="passkeys.length === 0" class="text-gray-500 text-14">
|
||||
{{ $t('passkey.no_passkeys') }}
|
||||
</div>
|
||||
|
||||
<!-- Passkey 列表 -->
|
||||
<div v-else space-y-8>
|
||||
<div v-else class="space-y-8">
|
||||
<div
|
||||
v-for="passkey in passkeys"
|
||||
:key="passkey.id"
|
||||
flex="~ justify-between items-center"
|
||||
p-12
|
||||
bg-gray-50
|
||||
rounded-8
|
||||
class="flex justify-between items-center p-12 bg-gray-50 rounded-8"
|
||||
>
|
||||
<div>
|
||||
<div font-medium>{{ passkey.name }}</div>
|
||||
<div text-12 text-gray-500>
|
||||
<div class="font-medium">{{ passkey.name }}</div>
|
||||
<div class="text-12 text-gray-500">
|
||||
{{ $t('passkey.created_at') }}:
|
||||
{{ formatDate(passkey.created_at) }}
|
||||
</div>
|
||||
<div v-if="passkey.last_used_at" text-12 text-gray-500>
|
||||
<div v-if="passkey.last_used_at" class="text-12 text-gray-500">
|
||||
{{ $t('passkey.last_used') }}:
|
||||
{{ formatDate(passkey.last_used_at) }}
|
||||
</div>
|
||||
<div v-if="passkey.backup_eligible" text-12 text-green-600>
|
||||
<div v-if="passkey.backup_eligible" class="text-12 text-green-600">
|
||||
{{ $t('passkey.synced') }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -132,10 +129,10 @@ function formatDate(dateString: string | null): string {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div line></div>
|
||||
<div class="line"></div>
|
||||
|
||||
<!-- 添加按钮 -->
|
||||
<div flex="~ justify-end">
|
||||
<div class="flex justify-end">
|
||||
<ab-button
|
||||
v-if="isSupported"
|
||||
size="sm"
|
||||
@@ -153,7 +150,7 @@ function formatDate(dateString: string | null): string {
|
||||
size="sm"
|
||||
:title="$t('passkey.register_title')"
|
||||
>
|
||||
<div space-y-16>
|
||||
<div class="space-y-16">
|
||||
<ab-field :label="$t('passkey.device_name')">
|
||||
<ab-input
|
||||
v-model="deviceName"
|
||||
@@ -163,7 +160,7 @@ function formatDate(dateString: string | null): string {
|
||||
/>
|
||||
</ab-field>
|
||||
|
||||
<div text-14 text-gray-500>
|
||||
<div class="text-14 text-gray-500">
|
||||
{{ $t('passkey.register_hint') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ const { types, type, rawUrl } = storeToRefs(usePlayerStore());
|
||||
|
||||
<template>
|
||||
<ab-fold-panel :title="$t('config.media_player_set.title')">
|
||||
<div space-y-8>
|
||||
<div class="space-y-8">
|
||||
<ab-setting
|
||||
v-model:data="type"
|
||||
type="select"
|
||||
|
||||
@@ -64,7 +64,7 @@ const items: SettingItem<Proxy>[] = [
|
||||
|
||||
<template>
|
||||
<ab-fold-panel :title="$t('config.proxy_set.title')">
|
||||
<div space-y-8>
|
||||
<div class="space-y-8">
|
||||
<ab-setting
|
||||
v-for="i in items"
|
||||
:key="i.configKey"
|
||||
|
||||
@@ -208,19 +208,19 @@ function validateUrl(url: string): boolean {
|
||||
|
||||
<template>
|
||||
<ab-fold-panel :title="$t('config.search_provider_set.title')">
|
||||
<div space-y-8>
|
||||
<div class="space-y-8">
|
||||
<!-- Loading state -->
|
||||
<div v-if="loading" text-gray-500 text-14>
|
||||
<div v-if="loading" class="text-gray-500 text-14">
|
||||
{{ $t('passkey.loading') }}
|
||||
</div>
|
||||
|
||||
<!-- Empty state -->
|
||||
<div v-else-if="providers.length === 0" text-gray-500 text-14>
|
||||
<div v-else-if="providers.length === 0" class="text-gray-500 text-14">
|
||||
{{ $t('config.search_provider_set.no_providers') }}
|
||||
</div>
|
||||
|
||||
<!-- Provider list -->
|
||||
<div v-else space-y-8>
|
||||
<div v-else class="space-y-8">
|
||||
<div
|
||||
v-for="(provider, index) in providers"
|
||||
:key="provider.name"
|
||||
@@ -261,7 +261,7 @@ function validateUrl(url: string): boolean {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div line></div>
|
||||
<div class="line"></div>
|
||||
|
||||
<!-- Hint text -->
|
||||
<div class="hint-text">
|
||||
@@ -269,7 +269,7 @@ function validateUrl(url: string): boolean {
|
||||
</div>
|
||||
|
||||
<!-- Add button -->
|
||||
<div flex="~ justify-end">
|
||||
<div class="flex justify-end">
|
||||
<ab-button size="sm" variant="primary" @click="openAddDialog">
|
||||
<Plus size="16" />
|
||||
{{ $t('config.search_provider_set.add_new') }}
|
||||
@@ -283,7 +283,7 @@ function validateUrl(url: string): boolean {
|
||||
size="sm"
|
||||
:title="$t('config.search_provider_set.add_title')"
|
||||
>
|
||||
<div space-y-16>
|
||||
<div class="space-y-16">
|
||||
<ab-segmented
|
||||
v-model:value="formMode"
|
||||
size="sm"
|
||||
@@ -391,7 +391,7 @@ function validateUrl(url: string): boolean {
|
||||
size="sm"
|
||||
:title="$t('config.search_provider_set.edit_title')"
|
||||
>
|
||||
<div space-y-16>
|
||||
<div class="space-y-16">
|
||||
<ab-field :label="$t('config.search_provider_set.name')">
|
||||
<ab-input
|
||||
v-model="formName"
|
||||
|
||||
@@ -32,7 +32,7 @@ const items: SettingItem<Security>[] = [
|
||||
<template>
|
||||
<ab-fold-panel :title="$t('config.security_set.title')">
|
||||
<p class="hint-text">{{ $t('config.security_set.hint') }}</p>
|
||||
<div space-y-8>
|
||||
<div class="space-y-8">
|
||||
<ab-setting
|
||||
v-for="i in items"
|
||||
:key="i.configKey"
|
||||
|
||||
@@ -8,13 +8,17 @@ definePage({
|
||||
|
||||
const { t } = useI18n();
|
||||
const title = computed(() => t('homepage.others.title'));
|
||||
// 闭包放在脚本里:vue-tsc 3.3 把模板闭包里的导入对象误当 ref,补出 .value
|
||||
const loadFn = () => apiBangumi.getOrphanTorrents();
|
||||
const deleteOne = (id: number) => apiBangumi.deleteOrphanTorrent(id);
|
||||
const deleteAll = () => apiBangumi.deleteOrphanTorrents();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AbTorrentListPage
|
||||
:title="title"
|
||||
:load-fn="() => apiBangumi.getOrphanTorrents()"
|
||||
:delete-one="(id: number) => apiBangumi.deleteOrphanTorrent(id)"
|
||||
:delete-all="() => apiBangumi.deleteOrphanTorrents()"
|
||||
:load-fn="loadFn"
|
||||
:delete-one="deleteOne"
|
||||
:delete-all="deleteAll"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="tsx" setup>
|
||||
<script lang="ts" setup>
|
||||
import { type DataTableColumns, NDataTable } from 'naive-ui';
|
||||
import AbProgress from '@/components/basic/ab-progress.vue';
|
||||
import AbTag from '@/components/basic/ab-tag.vue';
|
||||
import { useConfirm } from '@/hooks/useConfirm';
|
||||
import type { QbTorrentInfo, TorrentGroup } from '#/downloader';
|
||||
import { useDownloaderInstances } from '@/hooks/useDownloaderInstances';
|
||||
@@ -110,7 +111,7 @@ function stateLabel(state: string): string {
|
||||
return map[state] || state;
|
||||
}
|
||||
|
||||
function stateType(state: string): string {
|
||||
function stateType(state: string): 'neutral' | 'success' | 'info' | 'danger' {
|
||||
if (state.includes('paused') || state.includes('stopped')) return 'neutral';
|
||||
if (state === 'downloading' || state === 'forcedDL') return 'success';
|
||||
if (state.includes('UP') || state === 'uploading') return 'info';
|
||||
@@ -142,17 +143,14 @@ const tableColumnsValue = computed<DataTableColumns<QbTorrentInfo>>(() => [
|
||||
key: 'progress',
|
||||
width: 160,
|
||||
render(row: QbTorrentInfo) {
|
||||
return (
|
||||
<AbProgress
|
||||
value={row.progress * 100}
|
||||
label={`${Math.round(row.progress * 100)}%`}
|
||||
state={
|
||||
row.state === 'error' || row.state === 'missingFiles'
|
||||
? 'error'
|
||||
: 'active'
|
||||
}
|
||||
/>
|
||||
);
|
||||
return h(AbProgress, {
|
||||
value: row.progress * 100,
|
||||
label: `${Math.round(row.progress * 100)}%`,
|
||||
state:
|
||||
row.state === 'error' || row.state === 'missingFiles'
|
||||
? 'error'
|
||||
: 'active',
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -160,9 +158,10 @@ const tableColumnsValue = computed<DataTableColumns<QbTorrentInfo>>(() => [
|
||||
key: 'state',
|
||||
width: 100,
|
||||
render(row: QbTorrentInfo) {
|
||||
return (
|
||||
<ab-tag type={stateType(row.state)} title={stateLabel(row.state)} />
|
||||
);
|
||||
return h(AbTag, {
|
||||
type: stateType(row.state),
|
||||
title: stateLabel(row.state),
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="tsx" setup>
|
||||
<script lang="ts" setup>
|
||||
import { type DataTableColumns, NDataTable, NTooltip } from 'naive-ui';
|
||||
import AbButton from '@/components/basic/ab-button.vue';
|
||||
import AbTag from '@/components/basic/ab-tag.vue';
|
||||
import { useConfirm } from '@/hooks/useConfirm';
|
||||
import type { RSS } from '#/rss';
|
||||
|
||||
@@ -63,15 +64,11 @@ const rssColumns = computed<DataTableColumns<RSS>>(() => [
|
||||
key: 'name',
|
||||
className: 'text-h3',
|
||||
render(rss: RSS) {
|
||||
return (
|
||||
<div class="rss-name-cell">
|
||||
<span class="rss-name" title={rss.name}>
|
||||
{rss.name}
|
||||
</span>
|
||||
{rss.parser && <ab-tag type="info" title={rss.parser} />}
|
||||
{rss.aggregate && <ab-tag type="info" title={t('rss.aggregate')} />}
|
||||
</div>
|
||||
);
|
||||
return h('div', { class: 'rss-name-cell' }, [
|
||||
h('span', { class: 'rss-name', title: rss.name }, rss.name),
|
||||
rss.parser && h(AbTag, { type: 'info', title: rss.parser }),
|
||||
rss.aggregate && h(AbTag, { type: 'info', title: t('rss.aggregate') }),
|
||||
]);
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -90,26 +87,18 @@ const rssColumns = computed<DataTableColumns<RSS>>(() => [
|
||||
align: 'right',
|
||||
minWidth: 140,
|
||||
render(rss: RSS) {
|
||||
return (
|
||||
<div flex="~ justify-end gap-x-8">
|
||||
{rss.connection_status === 'healthy' && (
|
||||
<ab-tag type="success" title={t('rss.connected')} />
|
||||
)}
|
||||
{rss.connection_status === 'error' && (
|
||||
<NTooltip>
|
||||
{{
|
||||
trigger: () => <ab-tag type="danger" title={t('rss.error')} />,
|
||||
default: () => rss.last_error || 'Unknown error',
|
||||
}}
|
||||
</NTooltip>
|
||||
)}
|
||||
{rss.enabled ? (
|
||||
<ab-tag type="success" title={t('rss.active')} />
|
||||
) : (
|
||||
<ab-tag type="neutral" title={t('rss.inactive')} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
return h('div', { class: 'flex justify-end gap-x-8' }, [
|
||||
rss.connection_status === 'healthy' &&
|
||||
h(AbTag, { type: 'success', title: t('rss.connected') }),
|
||||
rss.connection_status === 'error' &&
|
||||
h(NTooltip, null, {
|
||||
trigger: () => h(AbTag, { type: 'danger', title: t('rss.error') }),
|
||||
default: () => rss.last_error || 'Unknown error',
|
||||
}),
|
||||
rss.enabled
|
||||
? h(AbTag, { type: 'success', title: t('rss.active') })
|
||||
: h(AbTag, { type: 'neutral', title: t('rss.inactive') }),
|
||||
]);
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -120,20 +109,21 @@ const rssColumns = computed<DataTableColumns<RSS>>(() => [
|
||||
render(rss: RSS) {
|
||||
const isRowRefreshing = refreshingId.value === rss.id;
|
||||
const isRowFailed = refreshFailedId.value === rss.id;
|
||||
return (
|
||||
<AbButton
|
||||
size="sm"
|
||||
variant={isRowFailed ? 'danger' : 'secondary'}
|
||||
{...{ title: t('rss.refresh') }}
|
||||
disabled={isRowRefreshing}
|
||||
onClick={() => onRefreshOne(rss.id)}
|
||||
>
|
||||
<div
|
||||
class={
|
||||
isRowRefreshing ? 'i-carbon-renew animate-spin' : 'i-carbon-renew'
|
||||
}
|
||||
/>
|
||||
</AbButton>
|
||||
return h(
|
||||
AbButton,
|
||||
{
|
||||
size: 'sm',
|
||||
variant: isRowFailed ? 'danger' : 'secondary',
|
||||
title: t('rss.refresh'),
|
||||
disabled: isRowRefreshing,
|
||||
onClick: () => onRefreshOne(rss.id),
|
||||
},
|
||||
() =>
|
||||
h('div', {
|
||||
class: isRowRefreshing
|
||||
? 'i-carbon-renew animate-spin'
|
||||
: 'i-carbon-renew',
|
||||
})
|
||||
);
|
||||
},
|
||||
},
|
||||
@@ -184,19 +174,19 @@ function onSelectedRSSUpdate(keys: unknown[]) {
|
||||
<div class="rss-card-name">{{ item.name }}</div>
|
||||
<div class="rss-card-url">{{ item.url }}</div>
|
||||
<div class="rss-card-tags">
|
||||
<ab-tag v-if="item.parser" type="info" :title="item.parser" />
|
||||
<ab-tag v-if="item.aggregate" type="info" title="aggregate" />
|
||||
<ab-tag
|
||||
<AbTag v-if="item.parser" type="info" :title="item.parser" />
|
||||
<AbTag v-if="item.aggregate" type="info" title="aggregate" />
|
||||
<AbTag
|
||||
v-if="item.connection_status === 'healthy'"
|
||||
type="success"
|
||||
:title="$t('rss.connected')"
|
||||
/>
|
||||
<ab-tag
|
||||
<AbTag
|
||||
v-if="item.connection_status === 'error'"
|
||||
type="danger"
|
||||
:title="$t('rss.error')"
|
||||
/>
|
||||
<ab-tag
|
||||
<AbTag
|
||||
:type="item.enabled ? 'success' : 'neutral'"
|
||||
:title="
|
||||
item.enabled
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Design System Variables
|
||||
// Light theme (default) + Dark theme (.dark class on html)
|
||||
|
||||
@use 'mixin' as *;
|
||||
|
||||
:root {
|
||||
// --- Colors ---
|
||||
--color-primary: #6C4AB6;
|
||||
|
||||
@@ -5,12 +5,12 @@ import zhCN from '@/i18n/zh-CN.json';
|
||||
|
||||
const t = (key: string) => `t:${key}`;
|
||||
|
||||
// 应用里 @intlify/unplugin-vue-i18n 把 i18n/*.json 的文案预编译成消息函数,
|
||||
// 这里同样把叶子换成函数,按生产环境的形态测试
|
||||
// 应用里 @intlify/unplugin-vue-i18n 把 i18n/*.json 的文案预编译成 AST,
|
||||
// 这里同样把叶子换成生产构建的 AST,按生产环境的形态测试
|
||||
vi.mock('@/i18n/en.json', async (importOriginal) => {
|
||||
const compile = (node: unknown): unknown =>
|
||||
typeof node === 'string'
|
||||
? () => node
|
||||
? { t: 0, b: { t: 2, i: [{ t: 3 }], s: node } }
|
||||
: Object.fromEntries(
|
||||
Object.entries(node as object).map(([k, v]) => [k, compile(v)])
|
||||
);
|
||||
|
||||
@@ -22,6 +22,9 @@ export function hasKey(key: string): boolean {
|
||||
(node, part) => (node as Record<string, unknown> | undefined)?.[part],
|
||||
en
|
||||
);
|
||||
// 构建时 i18n 插件把文案预编译成消息函数,测试里则是原始字符串
|
||||
return typeof found === 'string' || typeof found === 'function';
|
||||
// 构建时 i18n 插件把文案预编译成 AST(根节点 t 为 0,开发态键名不压缩为 type),
|
||||
// 测试里则是原始字符串
|
||||
if (typeof found === 'string') return true;
|
||||
const node = found as { t?: unknown; type?: unknown } | undefined;
|
||||
return node?.t === 0 || node?.type === 0;
|
||||
}
|
||||
|
||||
@@ -3,29 +3,24 @@
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"moduleResolution": "Bundler",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "vue",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"skipLibCheck": true,
|
||||
"noImplicitAny": true,
|
||||
"baseUrl": "./",
|
||||
"types": ["vite-plugin-pwa/client"],
|
||||
"types": ["vitest/globals"],
|
||||
"paths": {
|
||||
"~/*": ["./*"],
|
||||
"@/*": ["src/*"],
|
||||
"#/*": ["types/*"]
|
||||
"@/*": ["./src/*"],
|
||||
"#/*": ["./types/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"types/*.ts",
|
||||
"types/dts/*.d.ts"
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true
|
||||
"moduleResolution": "Bundler",
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
|
||||
386
webui/types/dts/auto-imports.d.ts
vendored
386
webui/types/dts/auto-imports.d.ts
vendored
@@ -1,168 +1,224 @@
|
||||
// Generated by 'unplugin-auto-import'
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
/* oxlint-disable */
|
||||
/* oxfmt-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
declare global {
|
||||
const EffectScope: typeof import('vue')['EffectScope']
|
||||
const MARKET_CATEGORIES: typeof import('../../src/utils/plugin-market')['MARKET_CATEGORIES']
|
||||
const MARKET_STATES: typeof import('../../src/utils/plugin-market')['MARKET_STATES']
|
||||
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
||||
const apiAuth: typeof import('../../src/api/auth')['apiAuth']
|
||||
const apiBangumi: typeof import('../../src/api/bangumi')['apiBangumi']
|
||||
const apiCheck: typeof import('../../src/api/check')['apiCheck']
|
||||
const apiConfig: typeof import('../../src/api/config')['apiConfig']
|
||||
const apiDownload: typeof import('../../src/api/download')['apiDownload']
|
||||
const apiDownloader: typeof import('../../src/api/downloader')['apiDownloader']
|
||||
const apiLLM: typeof import('../../src/api/llm')['apiLLM']
|
||||
const apiLog: typeof import('../../src/api/log')['apiLog']
|
||||
const apiNotification: typeof import('../../src/api/notification')['apiNotification']
|
||||
const apiPasskey: typeof import('../../src/api/passkey')['apiPasskey']
|
||||
const apiPlugins: typeof import('../../src/api/plugins')['apiPlugins']
|
||||
const apiProgram: typeof import('../../src/api/program')['apiProgram']
|
||||
const apiRSS: typeof import('../../src/api/rss')['apiRSS']
|
||||
const apiSearch: typeof import('../../src/api/search')['apiSearch']
|
||||
const apiSetup: typeof import('../../src/api/setup')['apiSetup']
|
||||
const apiTokens: typeof import('../../src/api/access')['apiTokens']
|
||||
const apiUpdate: typeof import('../../src/api/update')['apiUpdate']
|
||||
const apiUsers: typeof import('../../src/api/access')['apiUsers']
|
||||
const axios: typeof import('../../src/utils/axios')['axios']
|
||||
const buildNexusPhpSearchUrl: typeof import('../../src/utils/nexusphp')['buildNexusPhpSearchUrl']
|
||||
const categoriesOf: typeof import('../../src/utils/plugin-market')['categoriesOf']
|
||||
const categoryCounts: typeof import('../../src/utils/plugin-market')['categoryCounts']
|
||||
const computed: typeof import('vue')['computed']
|
||||
const configSectionMatches: typeof import('../../src/utils/config-search')['configSectionMatches']
|
||||
const countLogLevels: typeof import('../../src/utils/log-parse')['countLogLevels']
|
||||
const createApp: typeof import('vue')['createApp']
|
||||
const createPinia: typeof import('pinia')['createPinia']
|
||||
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
|
||||
const customRef: typeof import('vue')['customRef']
|
||||
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||
const defineComponent: typeof import('vue')['defineComponent']
|
||||
const defineLoader: typeof import('vue-router/auto')['defineLoader']
|
||||
const definePage: typeof import('unplugin-vue-router/runtime')['_definePage']
|
||||
const defineStore: typeof import('pinia')['defineStore']
|
||||
const dirtyConfigGroups: typeof import('../../src/utils/config-diff')['dirtyConfigGroups']
|
||||
const effectScope: typeof import('vue')['effectScope']
|
||||
const entryState: typeof import('../../src/utils/plugin-market')['entryState']
|
||||
const fillSchemaDefaults: typeof import('../../src/utils/plugin-schema')['fillSchemaDefaults']
|
||||
const filterEntries: typeof import('../../src/utils/plugin-market')['filterEntries']
|
||||
const getActivePinia: typeof import('pinia')['getActivePinia']
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||
const h: typeof import('vue')['h']
|
||||
const hasConfiguredDownloader: typeof import('../../src/store/downloader')['hasConfiguredDownloader']
|
||||
const hasKey: typeof import('../../src/utils/id-label')['hasKey']
|
||||
const i18n: typeof import('../../src/hooks/useMyI18n')['i18n']
|
||||
const idLabel: typeof import('../../src/utils/id-label')['idLabel']
|
||||
const inject: typeof import('vue')['inject']
|
||||
const isProxy: typeof import('vue')['isProxy']
|
||||
const isReactive: typeof import('vue')['isReactive']
|
||||
const isReadonly: typeof import('vue')['isReadonly']
|
||||
const isRef: typeof import('vue')['isRef']
|
||||
const isValidNexusPhpCategoryIds: typeof import('../../src/utils/nexusphp')['isValidNexusPhpCategoryIds']
|
||||
const localizeFields: typeof import('../../src/utils/plugin-text')['localizeFields']
|
||||
const mapActions: typeof import('pinia')['mapActions']
|
||||
const mapGetters: typeof import('pinia')['mapGetters']
|
||||
const mapState: typeof import('pinia')['mapState']
|
||||
const mapStores: typeof import('pinia')['mapStores']
|
||||
const mapWritableState: typeof import('pinia')['mapWritableState']
|
||||
const markRaw: typeof import('vue')['markRaw']
|
||||
const nextTick: typeof import('vue')['nextTick']
|
||||
const onActivated: typeof import('vue')['onActivated']
|
||||
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||
const onBeforeRouteLeave: typeof import('vue-router/auto')['onBeforeRouteLeave']
|
||||
const onBeforeRouteUpdate: typeof import('vue-router/auto')['onBeforeRouteUpdate']
|
||||
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||
const onMounted: typeof import('vue')['onMounted']
|
||||
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||
const onResponseError: typeof import('../../src/utils/axios')['onResponseError']
|
||||
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||
const onUpdated: typeof import('vue')['onUpdated']
|
||||
const parseLogLines: typeof import('../../src/utils/log-parse')['parseLogLines']
|
||||
const permissionDiff: typeof import('../../src/utils/plugin-market')['permissionDiff']
|
||||
const pluginDescription: typeof import('../../src/utils/plugin-text')['pluginDescription']
|
||||
const pluginName: typeof import('../../src/utils/plugin-text')['pluginName']
|
||||
const pluginReason: typeof import('../../src/utils/plugin-text')['pluginReason']
|
||||
const pluginSetVersion: typeof import('../../src/hooks/usePluginRefresh')['pluginSetVersion']
|
||||
const provide: typeof import('vue')['provide']
|
||||
const reactive: typeof import('vue')['reactive']
|
||||
const readCachedCatalog: typeof import('../../src/utils/plugin-market')['readCachedCatalog']
|
||||
const readonly: typeof import('vue')['readonly']
|
||||
const ref: typeof import('vue')['ref']
|
||||
const refreshPluginProviders: typeof import('../../src/hooks/usePluginProviders')['refreshPluginProviders']
|
||||
const refreshPluginState: typeof import('../../src/hooks/usePluginRefresh')['refreshPluginState']
|
||||
const refreshPluginUi: typeof import('../../src/hooks/usePluginUi')['refreshPluginUi']
|
||||
const relativeTime: typeof import('../../src/utils/relative-time')['relativeTime']
|
||||
const renderMarkdown: typeof import('../../src/utils/markdown')['renderMarkdown']
|
||||
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||
const resolvePosterUrl: typeof import('../../src/utils/poster')['resolvePosterUrl']
|
||||
const schemaFields: typeof import('../../src/utils/plugin-schema')['schemaFields']
|
||||
const setActivePinia: typeof import('pinia')['setActivePinia']
|
||||
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
|
||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||
const shallowRef: typeof import('vue')['shallowRef']
|
||||
const slotTitle: typeof import('../../src/hooks/usePluginUi')['slotTitle']
|
||||
const sourceUrl: typeof import('../../src/utils/plugin-market')['sourceUrl']
|
||||
const storeToRefs: typeof import('pinia')['storeToRefs']
|
||||
const toRaw: typeof import('vue')['toRaw']
|
||||
const toRef: typeof import('vue')['toRef']
|
||||
const toRefs: typeof import('vue')['toRefs']
|
||||
const tokensCss: typeof import('../../src/hooks/usePluginHostDeps')['tokensCss']
|
||||
const torrentKey: typeof import('../../src/store/downloader')['torrentKey']
|
||||
const triggerRef: typeof import('vue')['triggerRef']
|
||||
const unref: typeof import('vue')['unref']
|
||||
const useAccessStore: typeof import('../../src/store/access')['useAccessStore']
|
||||
const useAddRss: typeof import('../../src/hooks/useAddRss')['useAddRss']
|
||||
const useApi: typeof import('../../src/hooks/useApi')['useApi']
|
||||
const useAppInfo: typeof import('../../src/hooks/useAppInfo')['useAppInfo']
|
||||
const useAttrs: typeof import('vue')['useAttrs']
|
||||
const useAuth: typeof import('../../src/hooks/useAuth')['useAuth']
|
||||
const useBangumiRuleForm: typeof import('../../src/hooks/useBangumiRuleForm')['useBangumiRuleForm']
|
||||
const useBangumiStore: typeof import('../../src/store/bangumi')['useBangumiStore']
|
||||
const useBreakpointQuery: typeof import('../../src/hooks/useBreakpointQuery')['useBreakpointQuery']
|
||||
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
|
||||
const useClipboard: typeof import('@vueuse/core')['useClipboard']
|
||||
const useConfigStore: typeof import('../../src/store/config')['useConfigStore']
|
||||
const useConfirm: typeof import('../../src/hooks/useConfirm')['useConfirm']
|
||||
const useConfirmState: typeof import('../../src/hooks/useConfirm')['useConfirmState']
|
||||
const useCssModule: typeof import('vue')['useCssModule']
|
||||
const useCssVars: typeof import('vue')['useCssVars']
|
||||
const useDarkMode: typeof import('../../src/hooks/useDarkMode')['useDarkMode']
|
||||
const useDownloaderInstances: typeof import('../../src/hooks/useDownloaderInstances')['useDownloaderInstances']
|
||||
const useDownloaderStore: typeof import('../../src/store/downloader')['useDownloaderStore']
|
||||
const useEventStream: typeof import('../../src/hooks/useEventStream')['useEventStream']
|
||||
const useI18n: typeof import('vue-i18n')['useI18n']
|
||||
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
|
||||
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
|
||||
const useLogStore: typeof import('../../src/store/log')['useLogStore']
|
||||
const useMessage: typeof import('../../src/hooks/useMessage')['useMessage']
|
||||
const useMyI18n: typeof import('../../src/hooks/useMyI18n')['useMyI18n']
|
||||
const useNotificationStore: typeof import('../../src/store/notification')['useNotificationStore']
|
||||
const usePasskey: typeof import('../../src/hooks/usePasskey')['usePasskey']
|
||||
const usePlayerStore: typeof import('../../src/store/player')['usePlayerStore']
|
||||
const usePluginHostDeps: typeof import('../../src/hooks/usePluginHostDeps')['usePluginHostDeps']
|
||||
const usePluginPages: typeof import('../../src/hooks/usePluginUi')['usePluginPages']
|
||||
const usePluginProviders: typeof import('../../src/hooks/usePluginProviders')['usePluginProviders']
|
||||
const usePluginUi: typeof import('../../src/hooks/usePluginUi')['usePluginUi']
|
||||
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
|
||||
const useProgramStore: typeof import('../../src/store/program')['useProgramStore']
|
||||
const useRSSStore: typeof import('../../src/store/rss')['useRSSStore']
|
||||
const useRoute: typeof import('vue-router/auto')['useRoute']
|
||||
const useRouter: typeof import('vue-router/auto')['useRouter']
|
||||
const useSafeArea: typeof import('../../src/hooks/useSafeArea')['useSafeArea']
|
||||
const useSearchStore: typeof import('../../src/store/search')['useSearchStore']
|
||||
const useSetupStore: typeof import('../../src/store/setup')['useSetupStore']
|
||||
const useSlots: typeof import('vue')['useSlots']
|
||||
const useTorrentList: typeof import('../../src/hooks/useTorrentList')['useTorrentList']
|
||||
const useUiSlots: typeof import('../../src/hooks/usePluginUi')['useUiSlots']
|
||||
const watch: typeof import('vue')['watch']
|
||||
const watchEffect: typeof import('vue')['watchEffect']
|
||||
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
|
||||
const writeCachedCatalog: typeof import('../../src/utils/plugin-market')['writeCachedCatalog']
|
||||
const EffectScope: typeof import('vue').EffectScope
|
||||
const MARKET_CATEGORIES: typeof import('../../src/utils/plugin-market').MARKET_CATEGORIES
|
||||
const MARKET_STATES: typeof import('../../src/utils/plugin-market').MARKET_STATES
|
||||
const acceptHMRUpdate: typeof import('pinia').acceptHMRUpdate
|
||||
const apiAuth: typeof import('../../src/api/auth').apiAuth
|
||||
const apiBangumi: typeof import('../../src/api/bangumi').apiBangumi
|
||||
const apiCheck: typeof import('../../src/api/check').apiCheck
|
||||
const apiConfig: typeof import('../../src/api/config').apiConfig
|
||||
const apiDownload: typeof import('../../src/api/download').apiDownload
|
||||
const apiDownloader: typeof import('../../src/api/downloader').apiDownloader
|
||||
const apiLLM: typeof import('../../src/api/llm').apiLLM
|
||||
const apiLog: typeof import('../../src/api/log').apiLog
|
||||
const apiNotification: typeof import('../../src/api/notification').apiNotification
|
||||
const apiPasskey: typeof import('../../src/api/passkey').apiPasskey
|
||||
const apiPlugins: typeof import('../../src/api/plugins').apiPlugins
|
||||
const apiProgram: typeof import('../../src/api/program').apiProgram
|
||||
const apiRSS: typeof import('../../src/api/rss').apiRSS
|
||||
const apiSearch: typeof import('../../src/api/search').apiSearch
|
||||
const apiSetup: typeof import('../../src/api/setup').apiSetup
|
||||
const apiTokens: typeof import('../../src/api/access').apiTokens
|
||||
const apiUpdate: typeof import('../../src/api/update').apiUpdate
|
||||
const apiUsers: typeof import('../../src/api/access').apiUsers
|
||||
const axios: typeof import('../../src/utils/axios').axios
|
||||
const buildNexusPhpSearchUrl: typeof import('../../src/utils/nexusphp').buildNexusPhpSearchUrl
|
||||
const categoriesOf: typeof import('../../src/utils/plugin-market').categoriesOf
|
||||
const categoryCounts: typeof import('../../src/utils/plugin-market').categoryCounts
|
||||
const computed: typeof import('vue').computed
|
||||
const configSectionMatches: typeof import('../../src/utils/config-search').configSectionMatches
|
||||
const countLogLevels: typeof import('../../src/utils/log-parse').countLogLevels
|
||||
const createApp: typeof import('vue').createApp
|
||||
const createPinia: typeof import('pinia').createPinia
|
||||
const createSharedComposable: typeof import('@vueuse/core').createSharedComposable
|
||||
const customRef: typeof import('vue').customRef
|
||||
const defineAsyncComponent: typeof import('vue').defineAsyncComponent
|
||||
const defineComponent: typeof import('vue').defineComponent
|
||||
const defineLoader: typeof import('vue-router/auto').defineLoader
|
||||
const definePage: typeof import('unplugin-vue-router/runtime')._definePage
|
||||
const defineStore: typeof import('pinia').defineStore
|
||||
const dirtyConfigGroups: typeof import('../../src/utils/config-diff').dirtyConfigGroups
|
||||
const effectScope: typeof import('vue').effectScope
|
||||
const entryState: typeof import('../../src/utils/plugin-market').entryState
|
||||
const fillSchemaDefaults: typeof import('../../src/utils/plugin-schema').fillSchemaDefaults
|
||||
const filterEntries: typeof import('../../src/utils/plugin-market').filterEntries
|
||||
const getActivePinia: typeof import('pinia').getActivePinia
|
||||
const getCurrentInstance: typeof import('vue').getCurrentInstance
|
||||
const getCurrentScope: typeof import('vue').getCurrentScope
|
||||
const getCurrentWatcher: typeof import('vue').getCurrentWatcher
|
||||
const h: typeof import('vue').h
|
||||
const hasConfiguredDownloader: typeof import('../../src/store/downloader').hasConfiguredDownloader
|
||||
const hasKey: typeof import('../../src/utils/id-label').hasKey
|
||||
const i18n: typeof import('../../src/hooks/useMyI18n').i18n
|
||||
const idLabel: typeof import('../../src/utils/id-label').idLabel
|
||||
const inject: typeof import('vue').inject
|
||||
const isProxy: typeof import('vue').isProxy
|
||||
const isReactive: typeof import('vue').isReactive
|
||||
const isReadonly: typeof import('vue').isReadonly
|
||||
const isRef: typeof import('vue').isRef
|
||||
const isShallow: typeof import('vue').isShallow
|
||||
const isValidNexusPhpCategoryIds: typeof import('../../src/utils/nexusphp').isValidNexusPhpCategoryIds
|
||||
const localizeFields: typeof import('../../src/utils/plugin-text').localizeFields
|
||||
const mapActions: typeof import('pinia').mapActions
|
||||
const mapGetters: typeof import('pinia').mapGetters
|
||||
const mapState: typeof import('pinia').mapState
|
||||
const mapStores: typeof import('pinia').mapStores
|
||||
const mapWritableState: typeof import('pinia').mapWritableState
|
||||
const markRaw: typeof import('vue').markRaw
|
||||
const nextTick: typeof import('vue').nextTick
|
||||
const onActivated: typeof import('vue').onActivated
|
||||
const onBeforeMount: typeof import('vue').onBeforeMount
|
||||
const onBeforeRouteLeave: typeof import('vue-router/auto').onBeforeRouteLeave
|
||||
const onBeforeRouteUpdate: typeof import('vue-router/auto').onBeforeRouteUpdate
|
||||
const onBeforeUnmount: typeof import('vue').onBeforeUnmount
|
||||
const onBeforeUpdate: typeof import('vue').onBeforeUpdate
|
||||
const onDeactivated: typeof import('vue').onDeactivated
|
||||
const onErrorCaptured: typeof import('vue').onErrorCaptured
|
||||
const onMounted: typeof import('vue').onMounted
|
||||
const onRenderTracked: typeof import('vue').onRenderTracked
|
||||
const onRenderTriggered: typeof import('vue').onRenderTriggered
|
||||
const onResponseError: typeof import('../../src/utils/axios').onResponseError
|
||||
const onScopeDispose: typeof import('vue').onScopeDispose
|
||||
const onServerPrefetch: typeof import('vue').onServerPrefetch
|
||||
const onUnmounted: typeof import('vue').onUnmounted
|
||||
const onUpdated: typeof import('vue').onUpdated
|
||||
const onWatcherCleanup: typeof import('vue').onWatcherCleanup
|
||||
const parseLogLines: typeof import('../../src/utils/log-parse').parseLogLines
|
||||
const permissionDiff: typeof import('../../src/utils/plugin-market').permissionDiff
|
||||
const pluginDescription: typeof import('../../src/utils/plugin-text').pluginDescription
|
||||
const pluginName: typeof import('../../src/utils/plugin-text').pluginName
|
||||
const pluginReason: typeof import('../../src/utils/plugin-text').pluginReason
|
||||
const pluginSetVersion: typeof import('../../src/hooks/usePluginRefresh').pluginSetVersion
|
||||
const provide: typeof import('vue').provide
|
||||
const reactive: typeof import('vue').reactive
|
||||
const readCachedCatalog: typeof import('../../src/utils/plugin-market').readCachedCatalog
|
||||
const readonly: typeof import('vue').readonly
|
||||
const ref: typeof import('vue').ref
|
||||
const refreshPluginProviders: typeof import('../../src/hooks/usePluginProviders').refreshPluginProviders
|
||||
const refreshPluginState: typeof import('../../src/hooks/usePluginRefresh').refreshPluginState
|
||||
const refreshPluginUi: typeof import('../../src/hooks/usePluginUi').refreshPluginUi
|
||||
const relativeTime: typeof import('../../src/utils/relative-time').relativeTime
|
||||
const renderMarkdown: typeof import('../../src/utils/markdown').renderMarkdown
|
||||
const resolveComponent: typeof import('vue').resolveComponent
|
||||
const resolvePosterUrl: typeof import('../../src/utils/poster').resolvePosterUrl
|
||||
const schemaFields: typeof import('../../src/utils/plugin-schema').schemaFields
|
||||
const setActivePinia: typeof import('pinia').setActivePinia
|
||||
const setMapStoreSuffix: typeof import('pinia').setMapStoreSuffix
|
||||
const shallowReactive: typeof import('vue').shallowReactive
|
||||
const shallowReadonly: typeof import('vue').shallowReadonly
|
||||
const shallowRef: typeof import('vue').shallowRef
|
||||
const slotTitle: typeof import('../../src/hooks/usePluginUi').slotTitle
|
||||
const sourceUrl: typeof import('../../src/utils/plugin-market').sourceUrl
|
||||
const storeToRefs: typeof import('pinia').storeToRefs
|
||||
const toRaw: typeof import('vue').toRaw
|
||||
const toRef: typeof import('vue').toRef
|
||||
const toRefs: typeof import('vue').toRefs
|
||||
const toValue: typeof import('vue').toValue
|
||||
const tokensCss: typeof import('../../src/hooks/usePluginHostDeps').tokensCss
|
||||
const torrentKey: typeof import('../../src/store/downloader').torrentKey
|
||||
const triggerRef: typeof import('vue').triggerRef
|
||||
const unref: typeof import('vue').unref
|
||||
const useAccessStore: typeof import('../../src/store/access').useAccessStore
|
||||
const useAddRss: typeof import('../../src/hooks/useAddRss').useAddRss
|
||||
const useApi: typeof import('../../src/hooks/useApi').useApi
|
||||
const useAppInfo: typeof import('../../src/hooks/useAppInfo').useAppInfo
|
||||
const useAttrs: typeof import('vue').useAttrs
|
||||
const useAuth: typeof import('../../src/hooks/useAuth').useAuth
|
||||
const useBangumiRuleForm: typeof import('../../src/hooks/useBangumiRuleForm').useBangumiRuleForm
|
||||
const useBangumiStore: typeof import('../../src/store/bangumi').useBangumiStore
|
||||
const useBreakpointQuery: typeof import('../../src/hooks/useBreakpointQuery').useBreakpointQuery
|
||||
const useBreakpoints: typeof import('@vueuse/core').useBreakpoints
|
||||
const useClipboard: typeof import('@vueuse/core').useClipboard
|
||||
const useConfigStore: typeof import('../../src/store/config').useConfigStore
|
||||
const useConfirm: typeof import('../../src/hooks/useConfirm').useConfirm
|
||||
const useConfirmState: typeof import('../../src/hooks/useConfirm').useConfirmState
|
||||
const useCssModule: typeof import('vue').useCssModule
|
||||
const useCssVars: typeof import('vue').useCssVars
|
||||
const useDarkMode: typeof import('../../src/hooks/useDarkMode').useDarkMode
|
||||
const useDownloaderInstances: typeof import('../../src/hooks/useDownloaderInstances').useDownloaderInstances
|
||||
const useDownloaderStore: typeof import('../../src/store/downloader').useDownloaderStore
|
||||
const useEventStream: typeof import('../../src/hooks/useEventStream').useEventStream
|
||||
const useI18n: typeof import('vue-i18n').useI18n
|
||||
const useId: typeof import('vue').useId
|
||||
const useIntervalFn: typeof import('@vueuse/core').useIntervalFn
|
||||
const useLocalStorage: typeof import('@vueuse/core').useLocalStorage
|
||||
const useLogStore: typeof import('../../src/store/log').useLogStore
|
||||
const useMessage: typeof import('../../src/hooks/useMessage').useMessage
|
||||
const useModel: typeof import('vue').useModel
|
||||
const useMyI18n: typeof import('../../src/hooks/useMyI18n').useMyI18n
|
||||
const useNotificationStore: typeof import('../../src/store/notification').useNotificationStore
|
||||
const usePasskey: typeof import('../../src/hooks/usePasskey').usePasskey
|
||||
const usePlayerStore: typeof import('../../src/store/player').usePlayerStore
|
||||
const usePluginHostDeps: typeof import('../../src/hooks/usePluginHostDeps').usePluginHostDeps
|
||||
const usePluginPages: typeof import('../../src/hooks/usePluginUi').usePluginPages
|
||||
const usePluginProviders: typeof import('../../src/hooks/usePluginProviders').usePluginProviders
|
||||
const usePluginUi: typeof import('../../src/hooks/usePluginUi').usePluginUi
|
||||
const usePreferredDark: typeof import('@vueuse/core').usePreferredDark
|
||||
const useProgramStore: typeof import('../../src/store/program').useProgramStore
|
||||
const useRSSStore: typeof import('../../src/store/rss').useRSSStore
|
||||
const useRoute: typeof import('vue-router/auto').useRoute
|
||||
const useRouter: typeof import('vue-router/auto').useRouter
|
||||
const useSafeArea: typeof import('../../src/hooks/useSafeArea').useSafeArea
|
||||
const useSearchStore: typeof import('../../src/store/search').useSearchStore
|
||||
const useSetupStore: typeof import('../../src/store/setup').useSetupStore
|
||||
const useSlots: typeof import('vue').useSlots
|
||||
const useTemplateRef: typeof import('vue').useTemplateRef
|
||||
const useTorrentList: typeof import('../../src/hooks/useTorrentList').useTorrentList
|
||||
const useUiSlots: typeof import('../../src/hooks/usePluginUi').useUiSlots
|
||||
const watch: typeof import('vue').watch
|
||||
const watchEffect: typeof import('vue').watchEffect
|
||||
const watchPostEffect: typeof import('vue').watchPostEffect
|
||||
const watchSyncEffect: typeof import('vue').watchSyncEffect
|
||||
const writeCachedCatalog: typeof import('../../src/utils/plugin-market').writeCachedCatalog
|
||||
}
|
||||
// for type re-export
|
||||
declare global {
|
||||
// @ts-ignore
|
||||
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
|
||||
import('vue')
|
||||
// @ts-ignore
|
||||
export type { LLMAuthKind, LLMProviderView, LLMAuthChallenge, LLMAuthStatus } from '../../src/api/llm'
|
||||
import('../../src/api/llm')
|
||||
// @ts-ignore
|
||||
export type { TestProviderRequest, TestProviderConfigRequest, TestResponse, InboxSeverity, InboxMessage, InboxListResponse, InboxListParams } from '../../src/api/notification'
|
||||
import('../../src/api/notification')
|
||||
// @ts-ignore
|
||||
export type { SearchFilters, FilterOptions, GroupedBangumi } from '../../src/store/search'
|
||||
import('../../src/store/search')
|
||||
// @ts-ignore
|
||||
export type { ApiExecuteResult } from '../../src/hooks/useApi'
|
||||
import('../../src/hooks/useApi')
|
||||
// @ts-ignore
|
||||
export type { InfoTag } from '../../src/hooks/useBangumiRuleForm'
|
||||
import('../../src/hooks/useBangumiRuleForm')
|
||||
// @ts-ignore
|
||||
export type { ConfirmOptions } from '../../src/hooks/useConfirm'
|
||||
import('../../src/hooks/useConfirm')
|
||||
// @ts-ignore
|
||||
export type { StatusPayload, NotificationStreamPayload, BusEvent } from '../../src/hooks/useEventStream'
|
||||
import('../../src/hooks/useEventStream')
|
||||
// @ts-ignore
|
||||
export type { AxiosRequestConfig } from '../../src/utils/axios'
|
||||
import('../../src/utils/axios')
|
||||
// @ts-ignore
|
||||
export type { LogLevel, LogEntry, LogLevelCounts } from '../../src/utils/log-parse'
|
||||
import('../../src/utils/log-parse')
|
||||
// @ts-ignore
|
||||
export type { NexusPhpProviderOptions } from '../../src/utils/nexusphp'
|
||||
import('../../src/utils/nexusphp')
|
||||
// @ts-ignore
|
||||
export type { MarketCategory, MarketState, MarketFilter, CachedCatalog } from '../../src/utils/plugin-market'
|
||||
import('../../src/utils/plugin-market')
|
||||
// @ts-ignore
|
||||
export type { SchemaFieldKind, SchemaField } from '../../src/utils/plugin-schema'
|
||||
import('../../src/utils/plugin-schema')
|
||||
}
|
||||
|
||||
8
webui/types/dts/components.d.ts
vendored
8
webui/types/dts/components.d.ts
vendored
@@ -1,13 +1,15 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// biome-ignore lint: disable
|
||||
// oxlint-disable
|
||||
// ------
|
||||
// Generated by unplugin-vue-components
|
||||
// Read more: https://github.com/vuejs/core/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
|
||||
export {}
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
/* prettier-ignore */
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
AbAddRss: typeof import('./../../src/components/ab-add-rss.vue')['default']
|
||||
AbAlert: typeof import('./../../src/components/basic/ab-alert.vue')['default']
|
||||
|
||||
9
webui/types/dts/html.d.ts
vendored
9
webui/types/dts/html.d.ts
vendored
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* https://unocss.dev/presets/attributify#vue-3
|
||||
*/
|
||||
|
||||
import type { AttributifyAttributes } from '@unocss/preset-attributify';
|
||||
|
||||
declare module '@vue/runtime-dom' {
|
||||
interface HTMLAttributes extends AttributifyAttributes {}
|
||||
}
|
||||
@@ -1,20 +1,12 @@
|
||||
import {
|
||||
defineConfig,
|
||||
presetAttributify,
|
||||
presetIcons,
|
||||
presetUno,
|
||||
} from 'unocss';
|
||||
import { defineConfig, presetIcons, presetWind3 } from 'unocss';
|
||||
import presetRemToPx from '@unocss/preset-rem-to-px';
|
||||
|
||||
export default defineConfig({
|
||||
presets: [
|
||||
presetUno({
|
||||
dark: 'class',
|
||||
}),
|
||||
presetWind3(),
|
||||
presetRemToPx({
|
||||
baseFontSize: 4,
|
||||
}),
|
||||
presetAttributify(),
|
||||
// Icon collections are installed locally so production builds remain
|
||||
// reproducible when the E2E runtime has no public network access.
|
||||
presetIcons(),
|
||||
|
||||
@@ -7,26 +7,19 @@ import Components from 'unplugin-vue-components/vite';
|
||||
import VueRouter from 'unplugin-vue-router/vite';
|
||||
import { VueRouterAutoImports } from 'unplugin-vue-router';
|
||||
import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
import VueJsx from '@vitejs/plugin-vue-jsx';
|
||||
|
||||
// 与后端给 SPA 文档加的响应头一致(backend/src/main.py)。只加在 preview:
|
||||
// dev 服务器与 vite-plugin-pwa 的开发态会注入内联脚本,会被这个策略拦下
|
||||
// dev 服务器会注入内联脚本,会被这个策略拦下
|
||||
const headers = { 'Content-Security-Policy': "script-src 'self'" };
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(({ mode }) => ({
|
||||
export default defineConfig({
|
||||
base: './',
|
||||
plugins: [
|
||||
VueJsx(),
|
||||
VueRouter({
|
||||
dts: 'types/dts/router-type.d.ts',
|
||||
}),
|
||||
vue({
|
||||
script: {
|
||||
defineModel: true,
|
||||
},
|
||||
}),
|
||||
vue(),
|
||||
UnoCSS(),
|
||||
AutoImport({
|
||||
imports: [
|
||||
@@ -52,65 +45,26 @@ export default defineConfig(({ mode }) => ({
|
||||
dts: 'types/dts/components.d.ts',
|
||||
}),
|
||||
VueI18nPlugin({
|
||||
include: resolve(__dirname, './src/i18n/**'),
|
||||
}),
|
||||
VitePWA({
|
||||
injectRegister: false,
|
||||
registerType: 'autoUpdate',
|
||||
devOptions: {
|
||||
enabled: true,
|
||||
},
|
||||
workbox: {
|
||||
globPatterns: ['**/*.{js,css,html,ico,png,svg}'],
|
||||
},
|
||||
manifest: {
|
||||
name: 'AutoBangumi',
|
||||
display: 'standalone',
|
||||
short_name: 'AutoBangumi',
|
||||
description: 'Automated Bangumi Download Tool',
|
||||
theme_color: '#ffffff',
|
||||
icons: [
|
||||
{
|
||||
src: '/images/logo.svg',
|
||||
sizes: 'any',
|
||||
type: 'image/svg+xml',
|
||||
purpose: 'any',
|
||||
},
|
||||
{
|
||||
src: '/images/pwa-192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: '/images/pwa-512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
purpose: 'any',
|
||||
},
|
||||
],
|
||||
},
|
||||
include: resolve(import.meta.dirname, './src/i18n/**'),
|
||||
}),
|
||||
],
|
||||
// unplugin-vue-i18n 11 只产出 AST 形式的文案,vue-i18n 9 要打开 JIT 才认 AST;
|
||||
// vue-i18n 升到 10+ 后默认开启,届时删掉这一项
|
||||
define: { __INTLIFY_JIT_COMPILATION__: true },
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: '@import "./src/style/mixin.scss";',
|
||||
additionalData: '@use "@/style/mixin.scss" as *;',
|
||||
},
|
||||
},
|
||||
},
|
||||
esbuild: {
|
||||
drop: mode === 'production' ? ['console', 'debugger'] : [],
|
||||
},
|
||||
build: {
|
||||
cssCodeSplit: false,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'~': resolve(__dirname, './'),
|
||||
'@': resolve(__dirname, 'src'),
|
||||
'#': resolve(__dirname, 'types'),
|
||||
rolldownOptions: {
|
||||
output: { minify: { compress: { dropConsole: true } } },
|
||||
},
|
||||
},
|
||||
resolve: { tsconfigPaths: true },
|
||||
preview: { headers },
|
||||
server: {
|
||||
proxy: {
|
||||
@@ -121,4 +75,4 @@ export default defineConfig(({ mode }) => ({
|
||||
'^/posters/.*': 'http://localhost:7892',
|
||||
},
|
||||
},
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { resolve } from 'node:path';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import AutoImport from 'unplugin-auto-import/vite';
|
||||
@@ -18,11 +17,5 @@ export default defineConfig({
|
||||
setupFiles: ['./src/test/setup.ts'],
|
||||
include: ['src/**/*.{test,spec}.{js,ts}'],
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'~': resolve(__dirname, './'),
|
||||
'@': resolve(__dirname, 'src'),
|
||||
'#': resolve(__dirname, 'types'),
|
||||
},
|
||||
},
|
||||
resolve: { tsconfigPaths: true },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user