mirror of
https://github.com/MetaCubeX/metacubexd.git
synced 2024-11-14 06:25:35 +08:00
feat: show current page title, closes #474
This commit is contained in:
parent
443cb251ba
commit
9c76d7ff76
1
auto-imports.d.ts
vendored
1
auto-imports.d.ts
vendored
@ -3,6 +3,7 @@
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
// biome-ignore lint: disable
|
||||
export { }
|
||||
declare global {
|
||||
const $DEVCOMP: (typeof import('solid-js'))['$DEVCOMP']
|
||||
|
@ -9,9 +9,8 @@
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="apple-touch-icon" href="/pwa-192x192.png" />
|
||||
|
||||
<title>metacubexd</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
"@solid-primitives/storage": "^4.2.1",
|
||||
"@solid-primitives/timer": "^1.3.10",
|
||||
"@solid-primitives/websocket": "^1.2.2",
|
||||
"@solidjs/meta": "^0.29.4",
|
||||
"@solidjs/router": "^0.14.7",
|
||||
"@tabler/icons-solidjs": "^3.19.0",
|
||||
"@tanstack/match-sorter-utils": "^8.19.4",
|
||||
@ -44,7 +45,7 @@
|
||||
"autoprefixer": "^10.4.20",
|
||||
"byte-size": "^9.0.0",
|
||||
"commitlint": "^19.5.0",
|
||||
"daisyui": "^4.12.12",
|
||||
"daisyui": "^4.12.13",
|
||||
"dayjs": "^1.11.13",
|
||||
"eslint": "^9.12.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
@ -73,5 +74,5 @@
|
||||
"vite-plugin-solid": "^2.10.2",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"packageManager": "pnpm@9.10.0"
|
||||
"packageManager": "pnpm@9.12.1"
|
||||
}
|
||||
|
4205
pnpm-lock.yaml
4205
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
6
src/components/DocumentTitle.tsx
Normal file
6
src/components/DocumentTitle.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import { Title } from '@solidjs/meta'
|
||||
import { ParentComponent } from 'solid-js'
|
||||
|
||||
export default (({ children }) => {
|
||||
return <Title>{children} - MetaCubeXD</Title>
|
||||
}) as ParentComponent
|
@ -119,7 +119,7 @@ export const Header = () => {
|
||||
<div class="drawer-side">
|
||||
<label for="navs" class="drawer-overlay" />
|
||||
|
||||
<ul class="menu min-h-full w-2/5 gap-2 rounded-r-box bg-base-300 pt-20">
|
||||
<ul class="min-w-2/5 menu min-h-full gap-2 rounded-r-box bg-base-300 pt-20">
|
||||
<For each={navs()}>
|
||||
{({ href, name, icon }) => (
|
||||
<li onClick={() => setOpenedDrawer(false)}>
|
||||
|
@ -1,5 +1,6 @@
|
||||
export default {
|
||||
add: 'Add',
|
||||
setup: 'Setup',
|
||||
overview: 'Overview',
|
||||
proxies: 'Proxies',
|
||||
proxiesSettings: 'Proxies Settings',
|
||||
|
@ -2,6 +2,7 @@ import { Dict } from '~/i18n/dict'
|
||||
|
||||
export default {
|
||||
add: '添加',
|
||||
setup: '设置',
|
||||
overview: '概览',
|
||||
proxies: '代理',
|
||||
proxiesSettings: '代理设置',
|
||||
|
@ -1,6 +1,7 @@
|
||||
/* @refresh reload */
|
||||
import '~/index.css'
|
||||
|
||||
import { MetaProvider } from '@solidjs/meta'
|
||||
import dayjs from 'dayjs'
|
||||
import 'dayjs/locale/zh-cn'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
@ -24,6 +25,7 @@ dayjs.extend(relativeTime)
|
||||
render(
|
||||
() => (
|
||||
<I18nProvider locale={locale()}>
|
||||
<MetaProvider>
|
||||
<HashRouter root={App}>
|
||||
<Route path={ROUTES.Setup} component={Setup} />
|
||||
<Route path="*" component={Overview} />
|
||||
@ -34,6 +36,7 @@ render(
|
||||
<Route path={ROUTES.Log} component={Logs} />
|
||||
<Route path={ROUTES.Config} component={Config} />
|
||||
</HashRouter>
|
||||
</MetaProvider>
|
||||
|
||||
<Toaster position="bottom-center" />
|
||||
</I18nProvider>
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
upgradingUI,
|
||||
} from '~/apis'
|
||||
import { Button, ConfigTitle } from '~/components'
|
||||
import DocumentTitle from '~/components/DocumentTitle'
|
||||
import { LANG, ROUTES, themes } from '~/constants'
|
||||
import { locale, setLocale, useI18n } from '~/i18n'
|
||||
import {
|
||||
@ -540,6 +541,9 @@ export default () => {
|
||||
updateBackendVersion()
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocumentTitle>{t('config')}</DocumentTitle>
|
||||
|
||||
<div class="mx-auto flex max-w-screen-md flex-col gap-4">
|
||||
<Show when={!isSingBox()}>
|
||||
<ConfigTitle withDivider>{t('dnsQuery')}</ConfigTitle>
|
||||
@ -559,5 +563,6 @@ export default () => {
|
||||
|
||||
<Versions backendVersion={backendVersion} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import {
|
||||
ConnectionsSettingsModal,
|
||||
ConnectionsTableDetailsModal,
|
||||
} from '~/components'
|
||||
import DocumentTitle from '~/components/DocumentTitle'
|
||||
import { CONNECTIONS_TABLE_ACCESSOR_KEY } from '~/constants'
|
||||
import { useI18n } from '~/i18n'
|
||||
import {
|
||||
@ -355,6 +356,9 @@ export default () => {
|
||||
])
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocumentTitle>{t('connections')}</DocumentTitle>
|
||||
|
||||
<div class="flex h-full flex-col gap-2">
|
||||
<div class="flex w-full flex-wrap items-center gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
@ -376,7 +380,9 @@ export default () => {
|
||||
</div>
|
||||
|
||||
<div class="flex items-center">
|
||||
<span class="mr-2 hidden lg:inline-block">{t('quickFilter')}:</span>
|
||||
<span class="mr-2 hidden lg:inline-block">
|
||||
{t('quickFilter')}:
|
||||
</span>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="toggle"
|
||||
@ -513,7 +519,8 @@ export default () => {
|
||||
e.preventDefault()
|
||||
|
||||
const value = cell.renderValue() as null | string
|
||||
value && writeClipboard(value).catch(() => {})
|
||||
|
||||
if (value) writeClipboard(value).catch(() => {})
|
||||
}}
|
||||
>
|
||||
{cell.getIsGrouped() ? (
|
||||
@ -581,5 +588,6 @@ export default () => {
|
||||
selectedConnectionID={selectedConnectionID()}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import {
|
||||
} from '@tanstack/solid-table'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { Button, LogsSettingsModal } from '~/components'
|
||||
import DocumentTitle from '~/components/DocumentTitle'
|
||||
import { LOG_LEVEL } from '~/constants'
|
||||
import { useI18n } from '~/i18n'
|
||||
import { endpoint, logsTableSize, tableSizeClassName } from '~/signals'
|
||||
@ -120,6 +121,9 @@ export default () => {
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocumentTitle>{t('logs')}</DocumentTitle>
|
||||
|
||||
<div class="flex h-full flex-col gap-2">
|
||||
<div class="join w-full">
|
||||
<input
|
||||
@ -179,7 +183,8 @@ export default () => {
|
||||
{{
|
||||
asc: <IconSortAscending />,
|
||||
desc: <IconSortDescending />,
|
||||
}[header.column.getIsSorted() as string] ?? null}
|
||||
}[header.column.getIsSorted() as string] ??
|
||||
null}
|
||||
</div>
|
||||
</th>
|
||||
)
|
||||
@ -214,5 +219,6 @@ export default () => {
|
||||
|
||||
<LogsSettingsModal ref={(el) => (logsSettingsModalRef = el)} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import byteSize from 'byte-size'
|
||||
import { merge } from 'lodash'
|
||||
import { SolidApexCharts } from 'solid-apexcharts'
|
||||
import type { JSX, ParentComponent } from 'solid-js'
|
||||
import DocumentTitle from '~/components/DocumentTitle'
|
||||
import { CHART_MAX_XAXIS, DEFAULT_CHART_OPTIONS } from '~/constants'
|
||||
import { useI18n } from '~/i18n'
|
||||
import { endpoint, latestConnectionMsg, useWsRequest } from '~/signals'
|
||||
@ -87,6 +88,9 @@ export default () => {
|
||||
])
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocumentTitle>{t('overview')}</DocumentTitle>
|
||||
|
||||
<div class="flex flex-col gap-2 lg:h-full">
|
||||
<div class="stats stats-vertical w-full flex-shrink-0 grid-cols-2 bg-gradient-to-br from-primary to-secondary shadow lg:stats-horizontal lg:flex">
|
||||
<TrafficWidget label={t('upload')}>
|
||||
@ -135,5 +139,6 @@ export default () => {
|
||||
{endpoint()?.url}
|
||||
</footer>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ import {
|
||||
ProxyNodePreview,
|
||||
SubscriptionInfo,
|
||||
} from '~/components'
|
||||
import DocumentTitle from '~/components/DocumentTitle'
|
||||
import {
|
||||
filterProxiesByAvailability,
|
||||
sortProxiesByOrderingType,
|
||||
@ -125,6 +126,9 @@ export default () => {
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocumentTitle>{t('proxies')}</DocumentTitle>
|
||||
|
||||
<div class="flex h-full flex-col gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="tabs-boxed tabs gap-2">
|
||||
@ -173,7 +177,9 @@ export default () => {
|
||||
<div
|
||||
class={twMerge(
|
||||
'grid grid-cols-1 place-items-start gap-2',
|
||||
renderProxiesInTwoColumns() ? 'sm:grid-cols-2' : 'sm:grid-cols-1',
|
||||
renderProxiesInTwoColumns()
|
||||
? 'sm:grid-cols-2'
|
||||
: 'sm:grid-cols-1',
|
||||
)}
|
||||
>
|
||||
<For each={renderProxies()}>
|
||||
@ -218,8 +224,9 @@ export default () => {
|
||||
icon={
|
||||
<IconBrandSpeedtest
|
||||
class={twMerge(
|
||||
proxyGroupLatencyTestingMap()[proxyGroup.name] &&
|
||||
'animate-pulse text-success',
|
||||
proxyGroupLatencyTestingMap()[
|
||||
proxyGroup.name
|
||||
] && 'animate-pulse text-success',
|
||||
)}
|
||||
/>
|
||||
}
|
||||
@ -229,7 +236,8 @@ export default () => {
|
||||
<div class="flex items-center justify-between text-sm text-slate-500">
|
||||
<span>
|
||||
{proxyGroup.type}{' '}
|
||||
{proxyGroup.now?.length > 0 && ` :: ${proxyGroup.now}`}
|
||||
{proxyGroup.now?.length > 0 &&
|
||||
` :: ${proxyGroup.now}`}
|
||||
</span>
|
||||
<span>
|
||||
{byteSize(
|
||||
@ -278,7 +286,9 @@ export default () => {
|
||||
<div
|
||||
class={twMerge(
|
||||
'grid grid-cols-1 place-items-start gap-2',
|
||||
renderProxiesInTwoColumns() ? 'sm:grid-cols-2' : 'sm:grid-cols-1',
|
||||
renderProxiesInTwoColumns()
|
||||
? 'sm:grid-cols-2'
|
||||
: 'sm:grid-cols-1',
|
||||
)}
|
||||
>
|
||||
<For each={proxyProviders()}>
|
||||
@ -320,7 +330,9 @@ export default () => {
|
||||
<Button
|
||||
class="btn btn-circle btn-sm"
|
||||
disabled={
|
||||
proxyProviderLatencyTestingMap()[proxyProvider.name]
|
||||
proxyProviderLatencyTestingMap()[
|
||||
proxyProvider.name
|
||||
]
|
||||
}
|
||||
onClick={(e) =>
|
||||
onProxyProviderLatencyTestClick(
|
||||
@ -377,5 +389,6 @@ export default () => {
|
||||
|
||||
<ProxiesSettingsModal ref={(el) => (proxiesSettingsModalRef = el)} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import { createVirtualizer } from '@tanstack/solid-virtual'
|
||||
import { matchSorter } from 'match-sorter'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { Button } from '~/components'
|
||||
import DocumentTitle from '~/components/DocumentTitle'
|
||||
import { useStringBooleanMap } from '~/helpers'
|
||||
import { useI18n } from '~/i18n'
|
||||
import { endpoint, formatTimeFromNow, useRules } from '~/signals'
|
||||
@ -124,6 +125,9 @@ export default () => {
|
||||
const ruleProviderVirtualizerItems = ruleProviderVirtualizer.getVirtualItems()
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocumentTitle>{t('rules')}</DocumentTitle>
|
||||
|
||||
<div class="flex h-full flex-col gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="tabs-boxed tabs gap-2">
|
||||
@ -218,7 +222,8 @@ export default () => {
|
||||
{ruleProviderVirtualizerItems.map((virtualizerItem) => {
|
||||
const ruleProvider = ruleProviders().find(
|
||||
(ruleProvider) =>
|
||||
getRuleProviderItemKey(ruleProvider) === virtualizerItem.key,
|
||||
getRuleProviderItemKey(ruleProvider) ===
|
||||
virtualizerItem.key,
|
||||
)!
|
||||
|
||||
return (
|
||||
@ -235,12 +240,15 @@ export default () => {
|
||||
<div class="card card-bordered card-compact bg-base-200 p-4">
|
||||
<div class="flex items-center gap-2 pr-8">
|
||||
<span class="break-all">{ruleProvider.name}</span>
|
||||
<div class="badge badge-sm">{ruleProvider.ruleCount}</div>
|
||||
<div class="badge badge-sm">
|
||||
{ruleProvider.ruleCount}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-xs text-slate-500">
|
||||
{ruleProvider.vehicleType} / {ruleProvider.behavior} /
|
||||
{t('updated')} {formatTimeFromNow(ruleProvider.updatedAt)}
|
||||
{t('updated')}{' '}
|
||||
{formatTimeFromNow(ruleProvider.updatedAt)}
|
||||
</div>
|
||||
|
||||
<Button
|
||||
@ -266,5 +274,6 @@ export default () => {
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import { toast } from 'solid-toast'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
import { z } from 'zod'
|
||||
import { Button } from '~/components'
|
||||
import DocumentTitle from '~/components/DocumentTitle'
|
||||
import { transformEndpointURL } from '~/helpers'
|
||||
import { useI18n } from '~/i18n'
|
||||
import {
|
||||
@ -140,6 +141,9 @@ export default () => {
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<DocumentTitle>{t('setup')}</DocumentTitle>
|
||||
|
||||
<div class="mx-auto flex max-w-screen-sm flex-col items-center gap-4 py-10">
|
||||
<form class="contents" use:form={form}>
|
||||
<div class="flex w-full flex-col gap-4">
|
||||
@ -206,5 +210,6 @@ export default () => {
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user