Update On Sat Jan 10 19:39:32 CET 2026

This commit is contained in:
github-action[bot]
2026-01-10 19:39:32 +01:00
parent 3082bd0484
commit b72bc1d59f
92 changed files with 1425 additions and 603 deletions
+1
View File
@@ -1238,3 +1238,4 @@ Update On Tue Jan 6 19:44:08 CET 2026
Update On Wed Jan 7 19:44:04 CET 2026
Update On Thu Jan 8 19:40:15 CET 2026
Update On Fri Jan 9 19:44:47 CET 2026
Update On Sat Jan 10 19:39:24 CET 2026
+37 -2
View File
@@ -18,7 +18,27 @@ public class MediaListFetcher : IFetcher
var api = $"https://api.bilibili.com/x/v1/medialist/info?type=8&biz_id={id}&tid=0";
var json = await GetWebSourceAsync(api);
using var infoJson = JsonDocument.Parse(json);
var data = infoJson.RootElement.GetProperty("data");
var root = infoJson.RootElement;
var data = root.GetProperty("data");
if (data.ValueKind != JsonValueKind.Object)
{
// 部分情况下(合集被删除、设为私密或无权访问)data 会是 null
// 也有可能是“系列”却被误识别为合集,这里优先尝试按系列解析
try
{
return await new SeriesListFetcher().FetchAsync($"seriesBizId:{id}");
}
catch
{
var code = root.TryGetProperty("code", out var codeElem) && codeElem.ValueKind == JsonValueKind.Number
? codeElem.GetInt32()
: 0;
var message = root.TryGetProperty("message", out var msgElem) && msgElem.ValueKind == JsonValueKind.String
? msgElem.GetString()
: "未知错误";
throw new Exception($"获取合集信息失败(code={code}): {message}");
}
}
var listTitle = data.GetProperty("title").GetString()!;
var intro = data.GetProperty("intro").GetString()!;
long pubTime = data.GetProperty("ctime").GetInt64()!;
@@ -32,10 +52,25 @@ public class MediaListFetcher : IFetcher
var listApi = $"https://api.bilibili.com/x/v2/medialist/resource/list?type=8&oid={oid}&otype=2&biz_id={id}&with_current=true&mobi_app=web&ps=20&direction=false&sort_field=1&tid=0&desc=false";
json = await GetWebSourceAsync(listApi);
using var listJson = JsonDocument.Parse(json);
data = listJson.RootElement.GetProperty("data");
var listRoot = listJson.RootElement;
data = listRoot.GetProperty("data");
if (data.ValueKind != JsonValueKind.Object)
{
var code = listRoot.TryGetProperty("code", out var codeElem) && codeElem.ValueKind == JsonValueKind.Number
? codeElem.GetInt32()
: 0;
var message = listRoot.TryGetProperty("message", out var msgElem) && msgElem.ValueKind == JsonValueKind.String
? msgElem.GetString()
: "未知错误";
throw new Exception($"获取合集视频列表失败(code={code}): {message}");
}
hasMore = data.GetProperty("has_more").GetBoolean();
foreach (var m in data.GetProperty("media_list").EnumerateArray())
{
// 只处理未失效的视频条目(与收藏夹解析逻辑保持一致)
if (m.TryGetProperty("attr", out var attrElem) && attrElem.GetInt32() != 0)
continue;
var pageCount = m.GetProperty("page").GetInt32();
var desc = m.GetProperty("intro").GetString()!;
var ownerName = m.GetProperty("upper").GetProperty("name").ToString();
@@ -37,6 +37,10 @@ public class SeriesListFetcher : IFetcher
hasMore = data.GetProperty("has_more").GetBoolean();
foreach (var m in data.GetProperty("media_list").EnumerateArray())
{
// 只处理未失效的视频条目(与收藏夹解析逻辑保持一致)
if (m.TryGetProperty("attr", out var attrElem) && attrElem.GetInt32() != 0)
continue;
var pageCount = m.GetProperty("page").GetInt32();
var desc = m.GetProperty("intro").GetString()!;
var ownerName = m.GetProperty("upper").GetProperty("name").ToString();
+25
View File
@@ -100,6 +100,31 @@ static partial class BBDownUtil
string bizId = GetQueryString("sid", input);
avid = $"seriesBizId:{bizId}";
}
// 新版个人空间合集/系列链接兼容:
// 例如:
// 合集: https://space.bilibili.com/392959666/lists/1560264?type=season
// 系列: https://space.bilibili.com/392959666/lists/1560264?type=series
else if (input.Contains("/space.bilibili.com/") && input.Contains("/lists/"))
{
var type = GetQueryString("type", input).ToLower();
// path 最后一个 / 后到 ? 前即为 sid
var path = input.Split('?', '#')[0];
var sidPart = path[(path.LastIndexOf('/') + 1)..];
if (type == "season")
{
avid = $"listBizId:{sidPart}";
}
else if (type == "series")
{
avid = $"seriesBizId:{sidPart}";
}
else
{
// 未知类型按合集处理,至少不会识别失败
avid = $"listBizId:{sidPart}";
}
}
else if (input.Contains("/space.bilibili.com/") && input.Contains("/favlist"))
{
string mid = UidRegex().Match(input).Groups[1].Value;
@@ -422,11 +422,11 @@ export const commands = {
else return { status: 'error', error: e as any }
}
},
async saveWindowSizeState(): Promise<Result<null, string>> {
async saveMainWindowSizeState(): Promise<Result<null, string>> {
try {
return {
status: 'ok',
data: await TAURI_INVOKE('save_window_size_state'),
data: await TAURI_INVOKE('save_main_window_size_state'),
}
} catch (e) {
if (e instanceof Error) throw e
@@ -9,7 +9,7 @@ import {
PushPinOutlined,
} from '@mui/icons-material'
import { Button, ButtonProps } from '@mui/material'
import { saveWindowSizeState, useSetting } from '@nyanpasu/interface'
import { commands, useSetting } from '@nyanpasu/interface'
import { alpha, cn } from '@nyanpasu/ui'
import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query'
import { listen, TauriEvent, UnlistenFn } from '@tauri-apps/api/event'
@@ -98,7 +98,7 @@ export const LayoutControl = ({ className }: { className?: string }) => {
<CtrlButton
onClick={() => {
if (platform.current === 'windows') {
saveWindowSizeState().finally(() => {
commands.saveMainWindowSizeState().finally(() => {
appWindow.close()
})
} else {
@@ -4,6 +4,7 @@ import { cn } from '@nyanpasu/ui'
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'
interface ScrollAreaContextValue {
isScrolling: boolean
isTop: boolean
isBottom: boolean
scrollDirection: 'up' | 'down' | 'left' | 'right' | 'none'
@@ -23,6 +24,7 @@ export function useScrollArea() {
}
function useScrollTracking(threshold = 50) {
const [isScrolling, setIsScrolling] = useState(false)
const [isTop, setIsTop] = useState(true)
const [isBottom, setIsBottom] = useState(false)
const [scrollDirection, setScrollDirection] = useState<
@@ -32,10 +34,18 @@ function useScrollTracking(threshold = 50) {
const lastScrollTop = useRef(0)
const lastScrollLeft = useRef(0)
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
const target = e.currentTarget as HTMLElement
const { scrollTop, scrollLeft, scrollHeight, clientHeight } = target
if (timeoutRef.current) {
clearTimeout(timeoutRef.current)
}
setIsScrolling(true)
setIsTop(scrollTop === 0)
// check if is at bottom, allow a small threshold
@@ -62,9 +72,17 @@ function useScrollTracking(threshold = 50) {
lastScrollTop.current = scrollTop
lastScrollLeft.current = scrollLeft
timeoutRef.current = setTimeout(() => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current)
}
setIsScrolling(false)
}, threshold)
}
return { isTop, isBottom, scrollDirection, handleScroll }
return { isTop, isBottom, scrollDirection, handleScroll, isScrolling }
}
export function Viewport({
@@ -151,11 +169,13 @@ export function AppContentScrollArea({
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
const viewportRef = useRef<HTMLDivElement>(null)
const { isTop, isBottom, scrollDirection, handleScroll } = useScrollTracking()
const { isTop, isBottom, scrollDirection, handleScroll, isScrolling } =
useScrollTracking()
return (
<ScrollAreaContext.Provider
value={{
isScrolling,
isTop,
isBottom,
scrollDirection,
@@ -175,6 +195,7 @@ export function AppContentScrollArea({
data-slot="app-content-scroll-area"
type="scroll"
scrollHideDelay={600}
data-scrolling={String(isScrolling)}
data-top={String(isTop)}
data-bottom={String(isBottom)}
data-scroll-direction={scrollDirection}
+15
View File
@@ -2,6 +2,21 @@
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
## [2.54.0](https://github.com/filebrowser/filebrowser/compare/v2.53.1...v2.54.0) (2026-01-10)
### Features
* add "redirect after copy/move" user setting ([#5662](https://github.com/filebrowser/filebrowser/issues/5662)) ([fda8a99](https://github.com/filebrowser/filebrowser/commit/fda8a992929b1466e75fb2813f2c4e293c12d244))
* force file sync while uploading file ([#5668](https://github.com/filebrowser/filebrowser/issues/5668)) ([4fd18a3](https://github.com/filebrowser/filebrowser/commit/4fd18a382c31bbe7059d6733ffa371e70051865b))
* update translations ([#5659](https://github.com/filebrowser/filebrowser/issues/5659)) ([464b581](https://github.com/filebrowser/filebrowser/commit/464b581953139c17e3276b774e381e4052827125))
### Bug Fixes
* clear selection by clicking on empty area ([#5663](https://github.com/filebrowser/filebrowser/issues/5663)) ([208535a](https://github.com/filebrowser/filebrowser/commit/208535a8cc23254de0013dfab9008486707ee6c2))
* hide "change password form" in noauth setting ([#5652](https://github.com/filebrowser/filebrowser/issues/5652)) ([219582c](https://github.com/filebrowser/filebrowser/commit/219582c0b03fd90979b1d1398dba7919d086a23f))
## [2.53.1](https://github.com/filebrowser/filebrowser/compare/v2.53.0...v2.53.1) (2026-01-03)
+20 -17
View File
@@ -158,16 +158,17 @@ func (a *HookAuth) SaveUser() (*users.User, error) {
// create user with the provided credentials
d := &users.User{
Username: a.Cred.Username,
Password: pass,
Scope: a.Settings.Defaults.Scope,
Locale: a.Settings.Defaults.Locale,
ViewMode: a.Settings.Defaults.ViewMode,
SingleClick: a.Settings.Defaults.SingleClick,
Sorting: a.Settings.Defaults.Sorting,
Perm: a.Settings.Defaults.Perm,
Commands: a.Settings.Defaults.Commands,
HideDotfiles: a.Settings.Defaults.HideDotfiles,
Username: a.Cred.Username,
Password: pass,
Scope: a.Settings.Defaults.Scope,
Locale: a.Settings.Defaults.Locale,
ViewMode: a.Settings.Defaults.ViewMode,
SingleClick: a.Settings.Defaults.SingleClick,
RedirectAfterCopyMove: a.Settings.Defaults.RedirectAfterCopyMove,
Sorting: a.Settings.Defaults.Sorting,
Perm: a.Settings.Defaults.Perm,
Commands: a.Settings.Defaults.Commands,
HideDotfiles: a.Settings.Defaults.HideDotfiles,
}
u = a.GetUser(d)
@@ -219,13 +220,14 @@ func (a *HookAuth) GetUser(d *users.User) *users.User {
Download: isAdmin || a.Fields.GetBoolean("user.perm.download", d.Perm.Download),
}
user := users.User{
ID: d.ID,
Username: d.Username,
Password: d.Password,
Scope: a.Fields.GetString("user.scope", d.Scope),
Locale: a.Fields.GetString("user.locale", d.Locale),
ViewMode: users.ViewMode(a.Fields.GetString("user.viewMode", string(d.ViewMode))),
SingleClick: a.Fields.GetBoolean("user.singleClick", d.SingleClick),
ID: d.ID,
Username: d.Username,
Password: d.Password,
Scope: a.Fields.GetString("user.scope", d.Scope),
Locale: a.Fields.GetString("user.locale", d.Locale),
ViewMode: users.ViewMode(a.Fields.GetString("user.viewMode", string(d.ViewMode))),
SingleClick: a.Fields.GetBoolean("user.singleClick", d.SingleClick),
RedirectAfterCopyMove: a.Fields.GetBoolean("user.redirectAfterCopyMove", d.RedirectAfterCopyMove),
Sorting: files.Sorting{
Asc: a.Fields.GetBoolean("user.sorting.asc", d.Sorting.Asc),
By: a.Fields.GetString("user.sorting.by", d.Sorting.By),
@@ -251,6 +253,7 @@ var validHookFields = []string{
"user.locale",
"user.viewMode",
"user.singleClick",
"user.redirectAfterCopyMove",
"user.sorting.by",
"user.sorting.asc",
"user.commands",
+1
View File
@@ -240,6 +240,7 @@ func printSettings(ser *settings.Server, set *settings.Settings, auther auth.Aut
fmt.Fprintf(w, "\tLocale:\t%s\n", set.Defaults.Locale)
fmt.Fprintf(w, "\tView mode:\t%s\n", set.Defaults.ViewMode)
fmt.Fprintf(w, "\tSingle Click:\t%t\n", set.Defaults.SingleClick)
fmt.Fprintf(w, "\tRedirect after Copy/Move:\t%t\n", set.Defaults.RedirectAfterCopyMove)
fmt.Fprintf(w, "\tFile Creation Mode:\t%O\n", set.FileMode)
fmt.Fprintf(w, "\tDirectory Creation Mode:\t%O\n", set.DirMode)
fmt.Fprintf(w, "\tCommands:\t%s\n", strings.Join(set.Defaults.Commands, " "))
+5 -4
View File
@@ -393,10 +393,11 @@ func quickSetup(v *viper.Viper, s *storage.Storage) error {
MinimumPasswordLength: settings.DefaultMinimumPasswordLength,
UserHomeBasePath: settings.DefaultUsersHomeBasePath,
Defaults: settings.UserDefaults{
Scope: ".",
Locale: "en",
SingleClick: false,
AceEditorTheme: v.GetString("defaults.aceEditorTheme"),
Scope: ".",
Locale: "en",
SingleClick: false,
RedirectAfterCopyMove: true,
AceEditorTheme: v.GetString("defaults.aceEditorTheme"),
Perm: users.Permissions{
Admin: false,
Execute: true,
+5 -1
View File
@@ -30,13 +30,14 @@ func printUsers(usrs []*users.User) {
fmt.Fprintln(w, "ID\tUsername\tScope\tLocale\tV. Mode\tS.Click\tAdmin\tExecute\tCreate\tRename\tModify\tDelete\tShare\tDownload\tPwd Lock")
for _, u := range usrs {
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t\n",
fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t%t\t\n",
u.ID,
u.Username,
u.Scope,
u.Locale,
u.ViewMode,
u.SingleClick,
u.RedirectAfterCopyMove,
u.Perm.Admin,
u.Perm.Execute,
u.Perm.Create,
@@ -77,6 +78,7 @@ func addUserFlags(flags *pflag.FlagSet) {
flags.String("locale", "en", "locale for users")
flags.String("viewMode", string(users.ListViewMode), "view mode for users")
flags.Bool("singleClick", false, "use single clicks only")
flags.Bool("redirectAfterCopyMove", false, "redirect to destination after copy/move")
flags.Bool("dateFormat", false, "use date format (true for absolute time, false for relative)")
flags.Bool("hideDotfiles", false, "hide dotfiles")
flags.String("aceEditorTheme", "", "ace editor's syntax highlighting theme for users")
@@ -110,6 +112,8 @@ func getUserDefaults(flags *pflag.FlagSet, defaults *settings.UserDefaults, all
defaults.ViewMode, err = getAndParseViewMode(flags)
case "singleClick":
defaults.SingleClick, err = flags.GetBool(flag.Name)
case "redirectAfterCopyMove":
defaults.RedirectAfterCopyMove, err = flags.GetBool(flag.Name)
case "aceEditorTheme":
defaults.AceEditorTheme, err = flags.GetString(flag.Name)
case "perm.admin":
+9 -7
View File
@@ -52,13 +52,14 @@ options you want to change.`,
}
defaults := settings.UserDefaults{
Scope: user.Scope,
Locale: user.Locale,
ViewMode: user.ViewMode,
SingleClick: user.SingleClick,
Perm: user.Perm,
Sorting: user.Sorting,
Commands: user.Commands,
Scope: user.Scope,
Locale: user.Locale,
ViewMode: user.ViewMode,
SingleClick: user.SingleClick,
RedirectAfterCopyMove: user.RedirectAfterCopyMove,
Perm: user.Perm,
Sorting: user.Sorting,
Commands: user.Commands,
}
err = getUserDefaults(flags, &defaults, false)
@@ -70,6 +71,7 @@ options you want to change.`,
user.Locale = defaults.Locale
user.ViewMode = defaults.ViewMode
user.SingleClick = defaults.SingleClick
user.RedirectAfterCopyMove = defaults.RedirectAfterCopyMove
user.Perm = defaults.Perm
user.Commands = defaults.Commands
user.Sorting = defaults.Sorting
+1 -1
View File
@@ -71,5 +71,5 @@
"vite-plugin-compression2": "^2.3.1",
"vue-tsc": "^3.1.3"
},
"packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a"
"packageManager": "pnpm@10.28.0+sha512.05df71d1421f21399e053fde567cea34d446fa02c76571441bfc1c7956e98e363088982d940465fd34480d4d90a0668bc12362f8aa88000a64e83d0b0e47be48"
}
+198 -171
View File
@@ -98,7 +98,7 @@ importers:
devDependencies:
'@intlify/unplugin-vue-i18n':
specifier: ^11.0.1
version: 11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.54.0)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
version: 11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.55.1)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
'@tsconfig/node24':
specifier: ^24.0.2
version: 24.0.3
@@ -107,16 +107,16 @@ importers:
version: 4.17.12
'@types/node':
specifier: ^24.10.1
version: 24.10.4
version: 24.10.6
'@typescript-eslint/eslint-plugin':
specifier: ^8.37.0
version: 8.51.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)
version: 8.52.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)
'@vitejs/plugin-legacy':
specifier: ^7.2.1
version: 7.2.1(terser@5.44.1)(vite@7.3.0(@types/node@24.10.4)(terser@5.44.1)(yaml@2.8.2))
version: 7.2.1(terser@5.44.1)(vite@7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2))
'@vitejs/plugin-vue':
specifier: ^6.0.1
version: 6.0.3(vite@7.3.0(@types/node@24.10.4)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))
version: 6.0.3(vite@7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))
'@vue/eslint-config-prettier':
specifier: ^10.2.0
version: 10.2.0(eslint@9.39.2)(prettier@3.7.4)
@@ -155,13 +155,13 @@ importers:
version: 5.9.3
vite:
specifier: ^7.2.2
version: 7.3.0(@types/node@24.10.4)(terser@5.44.1)(yaml@2.8.2)
version: 7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2)
vite-plugin-compression2:
specifier: ^2.3.1
version: 2.4.0(rollup@4.54.0)
version: 2.4.0(rollup@4.55.1)
vue-tsc:
specifier: ^3.1.3
version: 3.2.1(typescript@5.9.3)
version: 3.2.2(typescript@5.9.3)
packages:
@@ -1143,113 +1143,128 @@ packages:
rollup:
optional: true
'@rollup/rollup-android-arm-eabi@4.54.0':
resolution: {integrity: sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==}
'@rollup/rollup-android-arm-eabi@4.55.1':
resolution: {integrity: sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==}
cpu: [arm]
os: [android]
'@rollup/rollup-android-arm64@4.54.0':
resolution: {integrity: sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==}
'@rollup/rollup-android-arm64@4.55.1':
resolution: {integrity: sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==}
cpu: [arm64]
os: [android]
'@rollup/rollup-darwin-arm64@4.54.0':
resolution: {integrity: sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==}
'@rollup/rollup-darwin-arm64@4.55.1':
resolution: {integrity: sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==}
cpu: [arm64]
os: [darwin]
'@rollup/rollup-darwin-x64@4.54.0':
resolution: {integrity: sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==}
'@rollup/rollup-darwin-x64@4.55.1':
resolution: {integrity: sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==}
cpu: [x64]
os: [darwin]
'@rollup/rollup-freebsd-arm64@4.54.0':
resolution: {integrity: sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==}
'@rollup/rollup-freebsd-arm64@4.55.1':
resolution: {integrity: sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==}
cpu: [arm64]
os: [freebsd]
'@rollup/rollup-freebsd-x64@4.54.0':
resolution: {integrity: sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==}
'@rollup/rollup-freebsd-x64@4.55.1':
resolution: {integrity: sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==}
cpu: [x64]
os: [freebsd]
'@rollup/rollup-linux-arm-gnueabihf@4.54.0':
resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==}
'@rollup/rollup-linux-arm-gnueabihf@4.55.1':
resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm-musleabihf@4.54.0':
resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==}
'@rollup/rollup-linux-arm-musleabihf@4.55.1':
resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==}
cpu: [arm]
os: [linux]
'@rollup/rollup-linux-arm64-gnu@4.54.0':
resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==}
'@rollup/rollup-linux-arm64-gnu@4.55.1':
resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-arm64-musl@4.54.0':
resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==}
'@rollup/rollup-linux-arm64-musl@4.55.1':
resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==}
cpu: [arm64]
os: [linux]
'@rollup/rollup-linux-loong64-gnu@4.54.0':
resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==}
'@rollup/rollup-linux-loong64-gnu@4.55.1':
resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-ppc64-gnu@4.54.0':
resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==}
'@rollup/rollup-linux-loong64-musl@4.55.1':
resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==}
cpu: [loong64]
os: [linux]
'@rollup/rollup-linux-ppc64-gnu@4.55.1':
resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-riscv64-gnu@4.54.0':
resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==}
'@rollup/rollup-linux-ppc64-musl@4.55.1':
resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==}
cpu: [ppc64]
os: [linux]
'@rollup/rollup-linux-riscv64-gnu@4.55.1':
resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-riscv64-musl@4.54.0':
resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==}
'@rollup/rollup-linux-riscv64-musl@4.55.1':
resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==}
cpu: [riscv64]
os: [linux]
'@rollup/rollup-linux-s390x-gnu@4.54.0':
resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==}
'@rollup/rollup-linux-s390x-gnu@4.55.1':
resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==}
cpu: [s390x]
os: [linux]
'@rollup/rollup-linux-x64-gnu@4.54.0':
resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==}
'@rollup/rollup-linux-x64-gnu@4.55.1':
resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==}
cpu: [x64]
os: [linux]
'@rollup/rollup-linux-x64-musl@4.54.0':
resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==}
'@rollup/rollup-linux-x64-musl@4.55.1':
resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==}
cpu: [x64]
os: [linux]
'@rollup/rollup-openharmony-arm64@4.54.0':
resolution: {integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==}
'@rollup/rollup-openbsd-x64@4.55.1':
resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==}
cpu: [x64]
os: [openbsd]
'@rollup/rollup-openharmony-arm64@4.55.1':
resolution: {integrity: sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==}
cpu: [arm64]
os: [openharmony]
'@rollup/rollup-win32-arm64-msvc@4.54.0':
resolution: {integrity: sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==}
'@rollup/rollup-win32-arm64-msvc@4.55.1':
resolution: {integrity: sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==}
cpu: [arm64]
os: [win32]
'@rollup/rollup-win32-ia32-msvc@4.54.0':
resolution: {integrity: sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==}
'@rollup/rollup-win32-ia32-msvc@4.55.1':
resolution: {integrity: sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==}
cpu: [ia32]
os: [win32]
'@rollup/rollup-win32-x64-gnu@4.54.0':
resolution: {integrity: sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==}
'@rollup/rollup-win32-x64-gnu@4.55.1':
resolution: {integrity: sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==}
cpu: [x64]
os: [win32]
'@rollup/rollup-win32-x64-msvc@4.54.0':
resolution: {integrity: sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==}
'@rollup/rollup-win32-x64-msvc@4.55.1':
resolution: {integrity: sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==}
cpu: [x64]
os: [win32]
@@ -1272,8 +1287,8 @@ packages:
'@types/lodash@4.17.13':
resolution: {integrity: sha512-lfx+dftrEZcdBPczf9d0Qv0x+j/rfNCMuC6OcfXmO8gkfeNAY88PgKUbvG56whcN23gc27yenwF6oJZXGFpYxg==}
'@types/node@24.10.4':
resolution: {integrity: sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==}
'@types/node@24.10.6':
resolution: {integrity: sha512-B8h60xgJMR/xmgyX9fncRzEW9gCxoJjdenUhke2v1JGOd/V66KopmWrLPXi5oUI4VuiGK+d+HlXJjDRZMj21EQ==}
'@types/trusted-types@2.0.7':
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
@@ -1289,11 +1304,11 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/eslint-plugin@8.51.0':
resolution: {integrity: sha512-XtssGWJvypyM2ytBnSnKtHYOGT+4ZwTnBVl36TA4nRO2f4PRNGz5/1OszHzcZCvcBMh+qb7I06uoCmLTRdR9og==}
'@typescript-eslint/eslint-plugin@8.52.0':
resolution: {integrity: sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
'@typescript-eslint/parser': ^8.51.0
'@typescript-eslint/parser': ^8.52.0
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <6.0.0'
@@ -1316,8 +1331,8 @@ packages:
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/project-service@8.51.0':
resolution: {integrity: sha512-Luv/GafO07Z7HpiI7qeEW5NW8HUtZI/fo/kE0YbtQEFpJRUuR0ajcWfCE5bnMvL7QQFrmT/odMe8QZww8X2nfQ==}
'@typescript-eslint/project-service@8.52.0':
resolution: {integrity: sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
@@ -1330,8 +1345,8 @@ packages:
resolution: {integrity: sha512-npgS3zi+/30KSOkXNs0LQXtsg9ekZ8OISAOLGWA/ZOEn0ZH74Ginfl7foziV8DT+D98WfQ5Kopwqb/PZOaIJGg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/scope-manager@8.51.0':
resolution: {integrity: sha512-JhhJDVwsSx4hiOEQPeajGhCWgBMBwVkxC/Pet53EpBVs7zHHtayKefw1jtPaNRXpI9RA2uocdmpdfE7T+NrizA==}
'@typescript-eslint/scope-manager@8.52.0':
resolution: {integrity: sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/tsconfig-utils@8.37.0':
@@ -1346,8 +1361,8 @@ packages:
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/tsconfig-utils@8.51.0':
resolution: {integrity: sha512-Qi5bSy/vuHeWyir2C8u/uqGMIlIDu8fuiYWv48ZGlZ/k+PRPHtaAu7erpc7p5bzw2WNNSniuxoMSO4Ar6V9OXw==}
'@typescript-eslint/tsconfig-utils@8.52.0':
resolution: {integrity: sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
@@ -1359,8 +1374,8 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/type-utils@8.51.0':
resolution: {integrity: sha512-0XVtYzxnobc9K0VU7wRWg1yiUrw4oQzexCG2V2IDxxCxhqBMSMbjB+6o91A+Uc0GWtgjCa3Y8bi7hwI0Tu4n5Q==}
'@typescript-eslint/type-utils@8.52.0':
resolution: {integrity: sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -1374,8 +1389,8 @@ packages:
resolution: {integrity: sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/types@8.51.0':
resolution: {integrity: sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag==}
'@typescript-eslint/types@8.52.0':
resolution: {integrity: sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/typescript-estree@8.37.0':
@@ -1390,8 +1405,8 @@ packages:
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
'@typescript-eslint/typescript-estree@8.51.0':
resolution: {integrity: sha512-1qNjGqFRmlq0VW5iVlcyHBbCjPB7y6SxpBkrbhNWMy/65ZoncXCEPJxkRZL8McrseNH6lFhaxCIaX+vBuFnRng==}
'@typescript-eslint/typescript-estree@8.52.0':
resolution: {integrity: sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
typescript: '>=4.8.4 <6.0.0'
@@ -1403,8 +1418,8 @@ packages:
eslint: ^8.57.0 || ^9.0.0
typescript: '>=4.8.4 <5.9.0'
'@typescript-eslint/utils@8.51.0':
resolution: {integrity: sha512-11rZYxSe0zabiKaCP2QAwRf/dnmgFgvTmeDTtZvUvXG3UuAdg/GU02NExmmIXzz3vLGgMdtrIosI84jITQOxUA==}
'@typescript-eslint/utils@8.52.0':
resolution: {integrity: sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
peerDependencies:
eslint: ^8.57.0 || ^9.0.0
@@ -1418,8 +1433,8 @@ packages:
resolution: {integrity: sha512-LlKaciDe3GmZFphXIc79THF/YYBugZ7FS1pO581E/edlVVNbZKDy93evqmrfQ9/Y4uN0vVhX4iuchq26mK/iiA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@typescript-eslint/visitor-keys@8.51.0':
resolution: {integrity: sha512-mM/JRQOzhVN1ykejrvwnBRV3+7yTKK8tVANVN3o1O0t0v7o+jqdVu9crPy5Y9dov15TJk/FTIgoUGHrTOVL3Zg==}
'@typescript-eslint/visitor-keys@8.52.0':
resolution: {integrity: sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@videojs/http-streaming@3.17.2':
@@ -1499,8 +1514,8 @@ packages:
typescript:
optional: true
'@vue/language-core@3.2.1':
resolution: {integrity: sha512-g6oSenpnGMtpxHGAwKuu7HJJkNZpemK/zg3vZzZbJ6cnnXq1ssxuNrXSsAHYM3NvH8p4IkTw+NLmuxyeYz4r8A==}
'@vue/language-core@3.2.2':
resolution: {integrity: sha512-5DAuhxsxBN9kbriklh3Q5AMaJhyOCNiQJvCskN9/30XOpdLiqZU9Q+WvjArP17ubdGEyZtBzlIeG5nIjEbNOrQ==}
'@vue/reactivity@3.5.26':
resolution: {integrity: sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ==}
@@ -2421,8 +2436,8 @@ packages:
rfdc@1.4.1:
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
rollup@4.54.0:
resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==}
rollup@4.55.1:
resolution: {integrity: sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
@@ -2618,8 +2633,8 @@ packages:
vite-plugin-compression2@2.4.0:
resolution: {integrity: sha512-8J4CBF1+dM1I06azba/eXJuJHinLF0Am7lUvRH8AZpu0otJoBaDEnxrIEr5iPZJSwH0AEglJGYCveh7pN52jCg==}
vite@7.3.0:
resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==}
vite@7.3.1:
resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
@@ -2697,8 +2712,8 @@ packages:
peerDependencies:
vue: ^3.0.2
vue-tsc@3.2.1:
resolution: {integrity: sha512-I23Rk8dkQfmcSbxDO0dmg9ioMLjKA1pjlU3Lz6Jfk2pMGu3Uryu9810XkcZH24IzPbhzPCnkKo2rEMRX0skSrw==}
vue-tsc@3.2.2:
resolution: {integrity: sha512-r9YSia/VgGwmbbfC06hDdAatH634XJ9nVl6Zrnz1iK4ucp8Wu78kawplXnIDa3MSu1XdQQePTHLXYwPDWn+nyQ==}
hasBin: true
peerDependencies:
typescript: '>=5.0.0'
@@ -3649,13 +3664,13 @@ snapshots:
'@intlify/shared@11.2.8': {}
'@intlify/unplugin-vue-i18n@11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.54.0)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))':
'@intlify/unplugin-vue-i18n@11.0.3(@vue/compiler-dom@3.5.26)(eslint@9.39.2)(rollup@4.55.1)(typescript@5.9.3)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))':
dependencies:
'@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2)
'@intlify/bundle-utils': 11.0.3(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))
'@intlify/shared': 11.2.2
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.2.2)(@vue/compiler-dom@3.5.26)(vue-i18n@11.2.8(vue@3.5.26(typescript@5.9.3)))(vue@3.5.26(typescript@5.9.3))
'@rollup/pluginutils': 5.3.0(rollup@4.54.0)
'@rollup/pluginutils': 5.3.0(rollup@4.55.1)
'@typescript-eslint/scope-manager': 8.49.0
'@typescript-eslint/typescript-estree': 8.49.0(typescript@5.9.3)
debug: 4.4.3
@@ -3722,78 +3737,87 @@ snapshots:
'@rolldown/pluginutils@1.0.0-beta.53': {}
'@rollup/pluginutils@5.3.0(rollup@4.54.0)':
'@rollup/pluginutils@5.3.0(rollup@4.55.1)':
dependencies:
'@types/estree': 1.0.8
estree-walker: 2.0.2
picomatch: 4.0.3
optionalDependencies:
rollup: 4.54.0
rollup: 4.55.1
'@rollup/rollup-android-arm-eabi@4.54.0':
'@rollup/rollup-android-arm-eabi@4.55.1':
optional: true
'@rollup/rollup-android-arm64@4.54.0':
'@rollup/rollup-android-arm64@4.55.1':
optional: true
'@rollup/rollup-darwin-arm64@4.54.0':
'@rollup/rollup-darwin-arm64@4.55.1':
optional: true
'@rollup/rollup-darwin-x64@4.54.0':
'@rollup/rollup-darwin-x64@4.55.1':
optional: true
'@rollup/rollup-freebsd-arm64@4.54.0':
'@rollup/rollup-freebsd-arm64@4.55.1':
optional: true
'@rollup/rollup-freebsd-x64@4.54.0':
'@rollup/rollup-freebsd-x64@4.55.1':
optional: true
'@rollup/rollup-linux-arm-gnueabihf@4.54.0':
'@rollup/rollup-linux-arm-gnueabihf@4.55.1':
optional: true
'@rollup/rollup-linux-arm-musleabihf@4.54.0':
'@rollup/rollup-linux-arm-musleabihf@4.55.1':
optional: true
'@rollup/rollup-linux-arm64-gnu@4.54.0':
'@rollup/rollup-linux-arm64-gnu@4.55.1':
optional: true
'@rollup/rollup-linux-arm64-musl@4.54.0':
'@rollup/rollup-linux-arm64-musl@4.55.1':
optional: true
'@rollup/rollup-linux-loong64-gnu@4.54.0':
'@rollup/rollup-linux-loong64-gnu@4.55.1':
optional: true
'@rollup/rollup-linux-ppc64-gnu@4.54.0':
'@rollup/rollup-linux-loong64-musl@4.55.1':
optional: true
'@rollup/rollup-linux-riscv64-gnu@4.54.0':
'@rollup/rollup-linux-ppc64-gnu@4.55.1':
optional: true
'@rollup/rollup-linux-riscv64-musl@4.54.0':
'@rollup/rollup-linux-ppc64-musl@4.55.1':
optional: true
'@rollup/rollup-linux-s390x-gnu@4.54.0':
'@rollup/rollup-linux-riscv64-gnu@4.55.1':
optional: true
'@rollup/rollup-linux-x64-gnu@4.54.0':
'@rollup/rollup-linux-riscv64-musl@4.55.1':
optional: true
'@rollup/rollup-linux-x64-musl@4.54.0':
'@rollup/rollup-linux-s390x-gnu@4.55.1':
optional: true
'@rollup/rollup-openharmony-arm64@4.54.0':
'@rollup/rollup-linux-x64-gnu@4.55.1':
optional: true
'@rollup/rollup-win32-arm64-msvc@4.54.0':
'@rollup/rollup-linux-x64-musl@4.55.1':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.54.0':
'@rollup/rollup-openbsd-x64@4.55.1':
optional: true
'@rollup/rollup-win32-x64-gnu@4.54.0':
'@rollup/rollup-openharmony-arm64@4.55.1':
optional: true
'@rollup/rollup-win32-x64-msvc@4.54.0':
'@rollup/rollup-win32-arm64-msvc@4.55.1':
optional: true
'@rollup/rollup-win32-ia32-msvc@4.55.1':
optional: true
'@rollup/rollup-win32-x64-gnu@4.55.1':
optional: true
'@rollup/rollup-win32-x64-msvc@4.55.1':
optional: true
'@tsconfig/node24@24.0.3': {}
@@ -3812,7 +3836,7 @@ snapshots:
'@types/lodash@4.17.13': {}
'@types/node@24.10.4':
'@types/node@24.10.6':
dependencies:
undici-types: 7.16.0
@@ -3838,14 +3862,14 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/eslint-plugin@8.51.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)':
'@typescript-eslint/eslint-plugin@8.52.0(@typescript-eslint/parser@8.37.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.37.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.51.0
'@typescript-eslint/type-utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.51.0
'@typescript-eslint/scope-manager': 8.52.0
'@typescript-eslint/type-utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.52.0
eslint: 9.39.2
ignore: 7.0.5
natural-compare: 1.4.0
@@ -3884,10 +3908,10 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/project-service@8.51.0(typescript@5.9.3)':
'@typescript-eslint/project-service@8.52.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/tsconfig-utils': 8.51.0(typescript@5.9.3)
'@typescript-eslint/types': 8.51.0
'@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3)
'@typescript-eslint/types': 8.52.0
debug: 4.4.3
typescript: 5.9.3
transitivePeerDependencies:
@@ -3903,10 +3927,10 @@ snapshots:
'@typescript-eslint/types': 8.49.0
'@typescript-eslint/visitor-keys': 8.49.0
'@typescript-eslint/scope-manager@8.51.0':
'@typescript-eslint/scope-manager@8.52.0':
dependencies:
'@typescript-eslint/types': 8.51.0
'@typescript-eslint/visitor-keys': 8.51.0
'@typescript-eslint/types': 8.52.0
'@typescript-eslint/visitor-keys': 8.52.0
'@typescript-eslint/tsconfig-utils@8.37.0(typescript@5.9.3)':
dependencies:
@@ -3916,7 +3940,7 @@ snapshots:
dependencies:
typescript: 5.9.3
'@typescript-eslint/tsconfig-utils@8.51.0(typescript@5.9.3)':
'@typescript-eslint/tsconfig-utils@8.52.0(typescript@5.9.3)':
dependencies:
typescript: 5.9.3
@@ -3932,11 +3956,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/type-utils@8.51.0(eslint@9.39.2)(typescript@5.9.3)':
'@typescript-eslint/type-utils@8.52.0(eslint@9.39.2)(typescript@5.9.3)':
dependencies:
'@typescript-eslint/types': 8.51.0
'@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.51.0(eslint@9.39.2)(typescript@5.9.3)
'@typescript-eslint/types': 8.52.0
'@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3)
'@typescript-eslint/utils': 8.52.0(eslint@9.39.2)(typescript@5.9.3)
debug: 4.4.3
eslint: 9.39.2
ts-api-utils: 2.4.0(typescript@5.9.3)
@@ -3948,7 +3972,7 @@ snapshots:
'@typescript-eslint/types@8.49.0': {}
'@typescript-eslint/types@8.51.0': {}
'@typescript-eslint/types@8.52.0': {}
'@typescript-eslint/typescript-estree@8.37.0(typescript@5.9.3)':
dependencies:
@@ -3981,12 +4005,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/typescript-estree@8.51.0(typescript@5.9.3)':
'@typescript-eslint/typescript-estree@8.52.0(typescript@5.9.3)':
dependencies:
'@typescript-eslint/project-service': 8.51.0(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.51.0(typescript@5.9.3)
'@typescript-eslint/types': 8.51.0
'@typescript-eslint/visitor-keys': 8.51.0
'@typescript-eslint/project-service': 8.52.0(typescript@5.9.3)
'@typescript-eslint/tsconfig-utils': 8.52.0(typescript@5.9.3)
'@typescript-eslint/types': 8.52.0
'@typescript-eslint/visitor-keys': 8.52.0
debug: 4.4.3
minimatch: 9.0.5
semver: 7.7.3
@@ -4007,12 +4031,12 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@typescript-eslint/utils@8.51.0(eslint@9.39.2)(typescript@5.9.3)':
'@typescript-eslint/utils@8.52.0(eslint@9.39.2)(typescript@5.9.3)':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2)
'@typescript-eslint/scope-manager': 8.51.0
'@typescript-eslint/types': 8.51.0
'@typescript-eslint/typescript-estree': 8.51.0(typescript@5.9.3)
'@typescript-eslint/scope-manager': 8.52.0
'@typescript-eslint/types': 8.52.0
'@typescript-eslint/typescript-estree': 8.52.0(typescript@5.9.3)
eslint: 9.39.2
typescript: 5.9.3
transitivePeerDependencies:
@@ -4028,9 +4052,9 @@ snapshots:
'@typescript-eslint/types': 8.49.0
eslint-visitor-keys: 4.2.1
'@typescript-eslint/visitor-keys@8.51.0':
'@typescript-eslint/visitor-keys@8.52.0':
dependencies:
'@typescript-eslint/types': 8.51.0
'@typescript-eslint/types': 8.52.0
eslint-visitor-keys: 4.2.1
'@videojs/http-streaming@3.17.2(video.js@8.23.4)':
@@ -4055,7 +4079,7 @@ snapshots:
global: 4.4.0
is-function: 1.0.2
'@vitejs/plugin-legacy@7.2.1(terser@5.44.1)(vite@7.3.0(@types/node@24.10.4)(terser@5.44.1)(yaml@2.8.2))':
'@vitejs/plugin-legacy@7.2.1(terser@5.44.1)(vite@7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2))':
dependencies:
'@babel/core': 7.28.5
'@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.5)
@@ -4070,14 +4094,14 @@ snapshots:
regenerator-runtime: 0.14.1
systemjs: 6.15.1
terser: 5.44.1
vite: 7.3.0(@types/node@24.10.4)(terser@5.44.1)(yaml@2.8.2)
vite: 7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2)
transitivePeerDependencies:
- supports-color
'@vitejs/plugin-vue@6.0.3(vite@7.3.0(@types/node@24.10.4)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))':
'@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-beta.53
vite: 7.3.0(@types/node@24.10.4)(terser@5.44.1)(yaml@2.8.2)
vite: 7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2)
vue: 3.5.26(typescript@5.9.3)
'@volar/language-core@2.4.27':
@@ -4164,7 +4188,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@vue/language-core@3.2.1':
'@vue/language-core@3.2.2':
dependencies:
'@volar/language-core': 2.4.27
'@vue/compiler-dom': 3.5.26
@@ -5068,32 +5092,35 @@ snapshots:
rfdc@1.4.1: {}
rollup@4.54.0:
rollup@4.55.1:
dependencies:
'@types/estree': 1.0.8
optionalDependencies:
'@rollup/rollup-android-arm-eabi': 4.54.0
'@rollup/rollup-android-arm64': 4.54.0
'@rollup/rollup-darwin-arm64': 4.54.0
'@rollup/rollup-darwin-x64': 4.54.0
'@rollup/rollup-freebsd-arm64': 4.54.0
'@rollup/rollup-freebsd-x64': 4.54.0
'@rollup/rollup-linux-arm-gnueabihf': 4.54.0
'@rollup/rollup-linux-arm-musleabihf': 4.54.0
'@rollup/rollup-linux-arm64-gnu': 4.54.0
'@rollup/rollup-linux-arm64-musl': 4.54.0
'@rollup/rollup-linux-loong64-gnu': 4.54.0
'@rollup/rollup-linux-ppc64-gnu': 4.54.0
'@rollup/rollup-linux-riscv64-gnu': 4.54.0
'@rollup/rollup-linux-riscv64-musl': 4.54.0
'@rollup/rollup-linux-s390x-gnu': 4.54.0
'@rollup/rollup-linux-x64-gnu': 4.54.0
'@rollup/rollup-linux-x64-musl': 4.54.0
'@rollup/rollup-openharmony-arm64': 4.54.0
'@rollup/rollup-win32-arm64-msvc': 4.54.0
'@rollup/rollup-win32-ia32-msvc': 4.54.0
'@rollup/rollup-win32-x64-gnu': 4.54.0
'@rollup/rollup-win32-x64-msvc': 4.54.0
'@rollup/rollup-android-arm-eabi': 4.55.1
'@rollup/rollup-android-arm64': 4.55.1
'@rollup/rollup-darwin-arm64': 4.55.1
'@rollup/rollup-darwin-x64': 4.55.1
'@rollup/rollup-freebsd-arm64': 4.55.1
'@rollup/rollup-freebsd-x64': 4.55.1
'@rollup/rollup-linux-arm-gnueabihf': 4.55.1
'@rollup/rollup-linux-arm-musleabihf': 4.55.1
'@rollup/rollup-linux-arm64-gnu': 4.55.1
'@rollup/rollup-linux-arm64-musl': 4.55.1
'@rollup/rollup-linux-loong64-gnu': 4.55.1
'@rollup/rollup-linux-loong64-musl': 4.55.1
'@rollup/rollup-linux-ppc64-gnu': 4.55.1
'@rollup/rollup-linux-ppc64-musl': 4.55.1
'@rollup/rollup-linux-riscv64-gnu': 4.55.1
'@rollup/rollup-linux-riscv64-musl': 4.55.1
'@rollup/rollup-linux-s390x-gnu': 4.55.1
'@rollup/rollup-linux-x64-gnu': 4.55.1
'@rollup/rollup-linux-x64-musl': 4.55.1
'@rollup/rollup-openbsd-x64': 4.55.1
'@rollup/rollup-openharmony-arm64': 4.55.1
'@rollup/rollup-win32-arm64-msvc': 4.55.1
'@rollup/rollup-win32-ia32-msvc': 4.55.1
'@rollup/rollup-win32-x64-gnu': 4.55.1
'@rollup/rollup-win32-x64-msvc': 4.55.1
fsevents: 2.3.3
run-parallel@1.2.0:
@@ -5282,23 +5309,23 @@ snapshots:
dependencies:
global: 4.4.0
vite-plugin-compression2@2.4.0(rollup@4.54.0):
vite-plugin-compression2@2.4.0(rollup@4.55.1):
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.54.0)
'@rollup/pluginutils': 5.3.0(rollup@4.55.1)
tar-mini: 0.2.0
transitivePeerDependencies:
- rollup
vite@7.3.0(@types/node@24.10.4)(terser@5.44.1)(yaml@2.8.2):
vite@7.3.1(@types/node@24.10.6)(terser@5.44.1)(yaml@2.8.2):
dependencies:
esbuild: 0.27.2
fdir: 6.5.0(picomatch@4.0.3)
picomatch: 4.0.3
postcss: 8.5.6
rollup: 4.54.0
rollup: 4.55.1
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 24.10.4
'@types/node': 24.10.6
fsevents: 2.3.3
terser: 5.44.1
yaml: 2.8.2
@@ -5346,10 +5373,10 @@ snapshots:
dependencies:
vue: 3.5.26(typescript@5.9.3)
vue-tsc@3.2.1(typescript@5.9.3):
vue-tsc@3.2.2(typescript@5.9.3):
dependencies:
'@volar/typescript': 2.4.27
'@vue/language-core': 3.2.1
'@vue/language-core': 3.2.2
typescript: 5.9.3
vue@3.5.26(typescript@5.9.3):
@@ -109,7 +109,8 @@ export default {
return;
}
this.$router.push({ path: this.dest });
if (this.user.redirectAfterCopyMove)
this.$router.push({ path: this.dest });
})
.catch((e) => {
buttons.done("copy");
@@ -79,7 +79,7 @@ export default {
computed: {
...mapState(useFileStore, ["req", "selected"]),
...mapState(useAuthStore, ["user"]),
...mapWritableState(useFileStore, ["preselect"]),
...mapWritableState(useFileStore, ["reload", "preselect"]),
excludedFolders() {
return this.selected
.filter((idx) => this.req.items[idx].isDir)
@@ -108,7 +108,9 @@ export default {
.then(() => {
buttons.success("move");
this.preselect = removePrefix(items[0].to);
this.$router.push({ path: this.dest });
if (this.user.redirectAfterCopyMove)
this.$router.push({ path: this.dest });
else this.reload = true;
})
.catch((e) => {
buttons.done("move");
+13 -12
View File
@@ -24,7 +24,7 @@
"ok": "موافق",
"permalink": "الحصول على رابط دائم",
"previous": "السابق",
"preview": "Preview",
"preview": "معاينة",
"publish": "نشر",
"rename": "إعادة تسمية",
"replace": "استبدال",
@@ -43,11 +43,11 @@
"upload": "رفع",
"openFile": "فتح الملف",
"discardChanges": "إلغاء التغييرات",
"stopSearch": "Stop searching",
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"stopSearch": "توقف عن البحث",
"saveChanges": "حفظ التغييرات",
"editAsText": "تعديل على شكل نص",
"increaseFontSize": "زيادة حجم الخط",
"decreaseFontSize": "تصغير حجم الخط"
},
"download": {
"downloadFile": "تحميل الملف",
@@ -80,7 +80,7 @@
"sortByName": "الترتيب باﻹسم",
"sortBySize": "الترتيب بالحجم",
"noPreview": "لا يوجد عرض مسبق لهذا الملف.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvTooLarge": "حجم الملف اكبر من (<5MB), يرجى تحميل الملف للمعاينة",
"csvLoadFailed": "Failed to load CSV file.",
"showingRows": "Showing {count} row(s)",
"columnSeparator": "Column Separator",
@@ -138,7 +138,7 @@
"filesSelected": "تم تحديد {count} ملفات.",
"lastModified": "آخر تعديل",
"move": "نقل",
"moveMessage": "إختر مكان جديد للملفات أو المجلدات المراد نقلها:",
"moveMessage": "اختر منزلاً جديداً لملفك (ملفاتك)/مجلدك (مجلداتك):",
"newArchetype": "إنشاء منشور من المنشور اﻷصلي. الملف سيتم انشاءه في مجلد المحتويات.",
"newDir": "مجلد جديد",
"newDirMessage": "أدخل اسم المجلد الجديد.",
@@ -189,15 +189,15 @@
"commandRunner": "منفذ اﻷوامر",
"commandRunnerHelp": "هنا بإمكانك تعيين اﻷوامر التي سيتم تنفيذها في اﻷحداث المسماة. يجب كتابة أمر واحد في كل سطر. ستكون المتغيرات البيئية (env) {0} و {1} متاحة، حيث {0} نسبي لـ {1}. لمزيد من المعلومات حول هذه الميزة و المتغيرات البيئية المتاحة، يرجى قراءة {2}.",
"commandsUpdated": "تم تحديث اﻷوامر",
"createUserDir": "إنشاء مجلد المستخدم (home) تلقائياً عند إنشاء مستخدم جديد",
"createUserDir": "إنشاء مجلد المستخدم الرئيسي تلقائياً عند إنشاء مستخدم جديد",
"minimumPasswordLength": "Minimum password length",
"tusUploads": "التحميلات المتقطعة",
"tusUploadsHelp": "يدعم متصفح الملفات تحميل الملفات المتقطعة، مما يسمح بتحميلات الملفات بشكل فعال و موثوق و قابلة للمتابغة و متقطعة حتى على الشبكات غير الموثوقة.",
"tusUploadsChunkSize": "يشير إلى الحد اﻷقصى لحجم الطلب (سيتم استخدام التحميل المباشر للتحميلات صغيرة الخحم). يمكنك إدخال عدد صحيح عادي يدل على الحجم بوحدة البايت أو نمظ مثل10MB, 1GB, إلخ.",
"tusUploadsRetryCount": "عدد مرات إعادة المحاولة إذا فشلت عملية تحميل القطعة.",
"userHomeBasePath": "المسار الرئيسي لمجلد المستخدم (home)",
"userHomeBasePath": "المسار الرئيسي لمجلد المستخدم الرئيسي",
"userScopeGenerationPlaceholder": "سيتم تعيين نطاق المستخدم تلقائياً",
"createUserHomeDirectory": "إنشاء مجلد المستخدم (home)",
"createUserHomeDirectory": "إنشاء مجلد المستخدم الرئيسي",
"customStylesheet": "ستايل مخصص",
"defaultUserDescription": "هذه اﻹعدادات اﻹفتراضية للمستخدمين الجدد.",
"disableExternalLinks": "تعطيل الروابط الخارجية (بإسثناء الوثائق)",
@@ -258,7 +258,8 @@
"userManagement": "إدارة المستخدمين",
"userUpdated": "تم تعديل المستخدم",
"username": "إسم المستخدم",
"users": "المستخدمين"
"users": "المستخدمين",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "مساعدة",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Управление на потребители",
"userUpdated": "Потребителя е обновен!",
"username": "Потребителско име",
"users": "Потребители"
"users": "Потребители",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Помощ",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Administració d'usuaris",
"userUpdated": "Usuari actualitzat!",
"username": "Usuari",
"users": "Usuaris"
"users": "Usuaris",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Ajuda",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Správa uživatelů",
"userUpdated": "Uživatel aktualizován!",
"username": "Uživatelské jméno",
"users": "Uživatelé"
"users": "Uživatelé",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Nápověda",
+11 -10
View File
@@ -43,11 +43,11 @@
"upload": "Upload",
"openFile": "Datei öffnen",
"discardChanges": "Verwerfen",
"stopSearch": "Stop searching",
"stopSearch": "Suche abbrechen",
"saveChanges": "Änderungen speichern",
"editAsText": "Als Text bearbeiten",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"increaseFontSize": "Schriftgröße vergrößern",
"decreaseFontSize": "Schriftgröße verkleinern"
},
"download": {
"downloadFile": "Download Datei",
@@ -82,12 +82,12 @@
"noPreview": "Für diese Datei ist keine Vorschau verfügbar.",
"csvTooLarge": "Die CSV-Datei ist zu groß für die Vorschau (>5 MB). Bitte herunterladen, um sie anzuzeigen.",
"csvLoadFailed": "Fehler beim Laden der CSV-Datei.",
"showingRows": "Showing {count} row(s)",
"columnSeparator": "Column Separator",
"showingRows": "{count} Zeile(n) werden angezeigt",
"columnSeparator": "Spaltentrennzeichen",
"csvSeparators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
"comma": "Komma (,)",
"semicolon": "Semikolon (;)",
"both": "Sowohl (,) als auch (;)"
}
},
"help": {
@@ -214,7 +214,7 @@
"instanceName": "Instanzname",
"language": "Sprache",
"lockPassword": "Verhindere, dass der Benutzer sein Passwort ändert",
"newPassword": "Ihr neues Passwort.",
"newPassword": "Ihr neues Passwort",
"newPasswordConfirm": "Bestätigen Sie Ihr neues Passwort",
"newUser": "Neuer Benutzer",
"password": "Passwort",
@@ -258,7 +258,8 @@
"userManagement": "Benutzerverwaltung",
"userUpdated": "Benutzer aktualisiert!",
"username": "Nutzername",
"users": "Nutzer"
"users": "Nutzer",
"currentPassword": "Ihr aktuelles Passwort"
},
"sidebar": {
"help": "Hilfe",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Διαχείριση χρηστών",
"userUpdated": "Ο χρήστης ενημερώθηκε!",
"username": "Όνομα χρήστη",
"users": "Χρήστες"
"users": "Χρήστες",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Βοήθεια",
+1
View File
@@ -232,6 +232,7 @@
"permissions": "Permissions",
"permissionsHelp": "You can set the user to be an administrator or choose the permissions individually. If you select \"Administrator\", all of the other options will be automatically checked. The management of users remains a privilege of an administrator.\n",
"profileSettings": "Profile Settings",
"redirectAfterCopyMove": "Redirect to destination after copy/move",
"ruleExample1": "prevents the access to any dotfile (such as .git, .gitignore) in every folder.\n",
"ruleExample2": "blocks the access to the file named Caddyfile on the root of the scope.",
"rules": "Rules",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Administración de usuarios",
"userUpdated": "¡Usuario actualizado!",
"username": "Usuario",
"users": "Usuarios"
"users": "Usuarios",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Ayuda",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "مدیریت کاربران",
"userUpdated": "کاربر به روز شد!",
"username": "نام کاربری",
"users": "کاربران"
"users": "کاربران",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "راهنما",
+18 -17
View File
@@ -43,11 +43,11 @@
"upload": "Importer",
"openFile": "Ouvrir le fichier",
"discardChanges": "Annuler",
"stopSearch": "Stop searching",
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"stopSearch": "Arrêter recherche",
"saveChanges": "Enregistrer changements",
"editAsText": "Editer comme Texte",
"increaseFontSize": "Augmenter taille police",
"decreaseFontSize": "Réduire taille police"
},
"download": {
"downloadFile": "Télécharger le fichier",
@@ -80,14 +80,14 @@
"sortByName": "Trier par nom",
"sortBySize": "Trier par taille",
"noPreview": "L'aperçu n'est pas disponible pour ce fichier.",
"csvTooLarge": "CSV file is too large for preview (>5MB). Please download to view.",
"csvLoadFailed": "Failed to load CSV file.",
"showingRows": "Showing {count} row(s)",
"columnSeparator": "Column Separator",
"csvTooLarge": "Le fichier CSV est trop volumineux pour être prévisualisé (>5MB). Veuillez le télécharger pour le voir.",
"csvLoadFailed": "Impossible de charger le fichier CSV.",
"showingRows": "Affichage de {count} ligne(s)",
"columnSeparator": "Séparateur de Colonnes",
"csvSeparators": {
"comma": "Comma (,)",
"semicolon": "Semicolon (;)",
"both": "Both (,) and (;)"
"comma": "Virgule (,)",
"semicolon": "Point-virgule (;)",
"both": "Les (,) et (;)"
}
},
"help": {
@@ -115,9 +115,9 @@
"username": "Utilisateur·ice",
"usernameTaken": "Le nom d'utilisateur·ice est déjà pris",
"wrongCredentials": "Identifiants incorrects !",
"passwordTooShort": "Password must be at least {min} characters",
"passwordTooShort": "Le mot de passe doit contenir au moins {min} caractères",
"logout_reasons": {
"inactivity": "You have been logged out due to inactivity."
"inactivity": "Vous avez été déconnecté(e) en raison d'une inactivité prolongée."
}
},
"permanent": "Permanent",
@@ -172,7 +172,7 @@
"video": "Vidéo"
},
"settings": {
"aceEditorTheme": "Ace editor theme",
"aceEditorTheme": "Éditeur de Thème Ace",
"admin": "Admin",
"administrator": "Administrateur·ice",
"allowCommands": "Exécuter des commandes",
@@ -180,7 +180,7 @@
"allowNew": "Créer de nouveaux fichiers et dossiers",
"allowPublish": "Publier de nouveaux posts et pages",
"allowSignup": "Autoriser les utilisateur·ices à s'inscrire",
"hideLoginButton": "Hide the login button from public pages",
"hideLoginButton": "Cacher le bouton didentification sur les pages publiques",
"avoidChanges": "(Laisser vide pour conserver l'actuel)",
"branding": "Image de marque",
"brandingDirectoryPath": "Chemin du dossier d'image de marque",
@@ -258,7 +258,8 @@
"userManagement": "Gestion des utilisateur·ices",
"userUpdated": "Utilisateur·ice mis à jour !",
"username": "Nom d'utilisateur·ice",
"users": "Utilisateur·ices"
"users": "Utilisateur·ices",
"currentPassword": "Mot de Passe Actuel"
},
"sidebar": {
"help": "Aide",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "ניהול משתמש",
"userUpdated": "המשתמש עודכן!",
"username": "שם משתמש",
"users": "משתמשים"
"users": "משתמשים",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "עזרה",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Upravljanje Korisnicima",
"userUpdated": "Korisnik ažuriran!",
"username": "Korisničko ime",
"users": "Korisnici"
"users": "Korisnici",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Pomoć",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Felhasználókezelés",
"userUpdated": "Felhasználó frissítve!",
"username": "Felhasználói név",
"users": "Felhasználók"
"users": "Felhasználók",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Súgó",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Notendastýring",
"userUpdated": "Notandastillingar vistaðar!",
"username": "Notendanafn",
"users": "Notendur"
"users": "Notendur",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Hjálp",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Gestione degli utenti",
"userUpdated": "Utente aggiornato!",
"username": "Nome utente",
"users": "Utenti"
"users": "Utenti",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Aiuto",
+2 -2
View File
@@ -42,7 +42,6 @@
"update": "更新",
"upload": "アップロード",
"openFile": "ファイルを開く",
"stopSearch": "検索を停止",
"discardChanges": "Discard",
"stopSearch": "Stop searching",
"saveChanges": "Save changes",
@@ -259,7 +258,8 @@
"userManagement": "ユーザー管理",
"userUpdated": "ユーザーを更新しました!",
"username": "ユーザー名",
"users": "ユーザー"
"users": "ユーザー",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "ヘルプ",
+2 -2
View File
@@ -41,7 +41,6 @@
"toggleSidebar": "사이드바 전환",
"update": "업데이트",
"upload": "업로드",
"stopSearch": "검색 중지",
"openFile": "파일 열기",
"discardChanges": "변경 사항 취소",
"stopSearch": "Stop searching",
@@ -259,7 +258,8 @@
"userManagement": "사용자 관리",
"userUpdated": "사용자 수정됨!",
"username": "사용자 이름",
"users": "사용자"
"users": "사용자",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "도움말",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Lietotāju pārvaldība",
"userUpdated": "Lietotājs atjaunināts!",
"username": "Lietotājvārds",
"users": "Lietotāji"
"users": "Lietotāji",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Palīdzība",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Lietotāju pārvaldība",
"userUpdated": "Lietotājs atjaunināts!",
"username": "Lietotājvārds",
"users": "Lietotāji"
"users": "Lietotāji",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Palīdzība",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Gebruikersbeheer",
"userUpdated": "Gebruiker bijgewerkt!",
"username": "Gebruikersnaam",
"users": "Gebruikers"
"users": "Gebruikers",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Help",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Brukeradministrasjon",
"userUpdated": "Bruker opprettet!",
"username": "Brukernavn",
"users": "Bruker"
"users": "Bruker",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Hjelp",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Zarządzanie użytkownikami",
"userUpdated": "Użytkownik zapisany!",
"username": "Nazwa użytkownika",
"users": "Użytkownicy"
"users": "Użytkownicy",
"currentPassword": "Twoje aktualne hasło"
},
"sidebar": {
"help": "Pomoc",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Gerenciamento de usuários",
"userUpdated": "Usuário atualizado!",
"username": "Nome do usuário",
"users": "Usuários"
"users": "Usuários",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Ajuda",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Gestão de utilizadores",
"userUpdated": "Utilizador atualizado!",
"username": "Nome de utilizador",
"users": "Utilizadores"
"users": "Utilizadores",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Ajuda",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Gestionare utilizatori",
"userUpdated": "Utilizator actualizat!",
"username": "Utilizator",
"users": "Utilizatori"
"users": "Utilizatori",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Ajutor",
+2 -2
View File
@@ -42,7 +42,6 @@
"update": "Обновить",
"upload": "Загрузить",
"openFile": "Открыть файл",
"stopSearch": "Прекратить поиск",
"discardChanges": "Отказаться",
"stopSearch": "Stop searching",
"saveChanges": "Сохранить",
@@ -259,7 +258,8 @@
"userManagement": "Управление пользователями",
"userUpdated": "Пользователь изменен!",
"username": "Имя пользователя",
"users": "Пользователи"
"users": "Пользователи",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Помощь",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Správa používateľov",
"userUpdated": "Používateľ upravený!",
"username": "Meno používateľa",
"users": "Používatelia"
"users": "Používatelia",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Pomoc",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Användarehantering",
"userUpdated": "Användare uppdaterad!",
"username": "Användarnamn",
"users": "Användare"
"users": "Användare",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Hjälp",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Kullanıcı yönetimi",
"userUpdated": "Kullanıcı güncellendi!",
"username": "Kullanıcı adı",
"users": "Kullanıcılar"
"users": "Kullanıcılar",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Yardım",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Керування користувачами",
"userUpdated": "Користувача змінено!",
"username": "Ім'я користувача",
"users": "Користувачі"
"users": "Користувачі",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Допомога",
+2 -1
View File
@@ -258,7 +258,8 @@
"userManagement": "Quản lý người dùng",
"userUpdated": "Người dùng đã được cập nhật!",
"username": "Tên người dùng",
"users": "Người dùng"
"users": "Người dùng",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "Trợ giúp",
+2 -2
View File
@@ -42,7 +42,6 @@
"update": "更新",
"upload": "上传",
"openFile": "打开文件",
"stopSearch": "停止搜索",
"discardChanges": "放弃更改",
"stopSearch": "Stop searching",
"saveChanges": "保存更改",
@@ -259,7 +258,8 @@
"userManagement": "用户管理",
"userUpdated": "用户已更新!",
"username": "用户名",
"users": "用户"
"users": "用户",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "帮助",
+2 -2
View File
@@ -42,7 +42,6 @@
"update": "更新",
"upload": "上傳",
"openFile": "開啟檔案",
"stopSearch": "停止搜尋",
"discardChanges": "放棄變更",
"stopSearch": "Stop searching",
"saveChanges": "Save changes",
@@ -259,7 +258,8 @@
"userManagement": "使用者管理",
"userUpdated": "使用者已更新!",
"username": "使用者名稱",
"users": "使用者"
"users": "使用者",
"currentPassword": "Your Current Password"
},
"sidebar": {
"help": "幫助",
+1
View File
@@ -18,6 +18,7 @@ interface SettingsDefaults {
locale: string;
viewMode: ViewModeType;
singleClick: boolean;
redirectAfterCopyMove: boolean;
sorting: Sorting;
perm: Permissions;
commands: any[];
+2
View File
@@ -10,6 +10,7 @@ interface IUser {
lockPassword: boolean;
hideDotfiles: boolean;
singleClick: boolean;
redirectAfterCopyMove: boolean;
dateFormat: boolean;
viewMode: ViewModeType;
sorting?: Sorting;
@@ -30,6 +31,7 @@ interface IUserForm {
lockPassword?: boolean;
hideDotfiles?: boolean;
singleClick?: boolean;
redirectAfterCopyMove?: boolean;
dateFormat?: boolean;
}
@@ -159,6 +159,7 @@
ref="listing"
class="file-icons"
:class="authStore.user?.viewMode ?? ''"
@click="handleEmptyAreaClick"
>
<div>
<div class="item header">
@@ -1051,4 +1052,18 @@ const showContextMenu = (event: MouseEvent) => {
const hideContextMenu = () => {
isContextMenuVisible.value = false;
};
const handleEmptyAreaClick = (e: MouseEvent) => {
const target = e.target;
if (!(target instanceof HTMLElement)) return;
if (target.closest("item") || target.closest(".item")) return;
fileStore.selected = [];
};
</script>
<style scoped>
#listing {
min-height: calc(100vh - 8rem);
}
</style>
@@ -15,6 +15,14 @@
<input type="checkbox" name="singleClick" v-model="singleClick" />
{{ t("settings.singleClick") }}
</p>
<p>
<input
type="checkbox"
name="redirectAfterCopyMove"
v-model="redirectAfterCopyMove"
/>
{{ t("settings.redirectAfterCopyMove") }}
</p>
<p>
<input type="checkbox" name="dateFormat" v-model="dateFormat" />
{{ t("settings.setDateFormat") }}
@@ -44,7 +52,7 @@
</form>
</div>
<div class="column">
<div v-if="!noAuth" class="column">
<form
class="card"
v-if="!authStore.user?.lockPassword"
@@ -96,11 +104,12 @@
<script setup lang="ts">
import { useAuthStore } from "@/stores/auth";
import { useLayoutStore } from "@/stores/layout";
import { users as api, settings } from "@/api";
import { users as api } from "@/api";
import AceEditorTheme from "@/components/settings/AceEditorTheme.vue";
import Languages from "@/components/settings/Languages.vue";
import { computed, inject, onMounted, ref } from "vue";
import { useI18n } from "vue-i18n";
import { authMethod, noAuth } from "@/utils/constants";
const layoutStore = useLayoutStore();
const authStore = useAuthStore();
@@ -115,6 +124,7 @@ const currentPassword = ref<string>("");
const isCurrentPasswordRequired = ref<boolean>(false);
const hideDotfiles = ref<boolean>(false);
const singleClick = ref<boolean>(false);
const redirectAfterCopyMove = ref<boolean>(false);
const dateFormat = ref<boolean>(false);
const locale = ref<string>("");
const aceEditorTheme = ref<string>("");
@@ -139,10 +149,10 @@ onMounted(async () => {
locale.value = authStore.user.locale;
hideDotfiles.value = authStore.user.hideDotfiles;
singleClick.value = authStore.user.singleClick;
redirectAfterCopyMove.value = authStore.user.redirectAfterCopyMove;
dateFormat.value = authStore.user.dateFormat;
aceEditorTheme.value = authStore.user.aceEditorTheme;
layoutStore.loading = false;
const { authMethod } = await settings.get();
isCurrentPasswordRequired.value = authMethod == "json";
return true;
@@ -187,6 +197,7 @@ const updateSettings = async (event: Event) => {
locale: locale.value,
hideDotfiles: hideDotfiles.value,
singleClick: singleClick.value,
redirectAfterCopyMove: redirectAfterCopyMove.value,
dateFormat: dateFormat.value,
aceEditorTheme: aceEditorTheme.value,
};
@@ -195,6 +206,7 @@ const updateSettings = async (event: Event) => {
"locale",
"hideDotfiles",
"singleClick",
"redirectAfterCopyMove",
"dateFormat",
"aceEditorTheme",
]);
+1 -1
View File
@@ -26,7 +26,7 @@ require (
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce
golang.org/x/crypto v0.46.0
golang.org/x/image v0.34.0
golang.org/x/text v0.32.0
golang.org/x/text v0.33.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
)
+2 -2
View File
@@ -372,8 +372,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+24 -22
View File
@@ -23,17 +23,18 @@ const (
)
type userInfo struct {
ID uint `json:"id"`
Locale string `json:"locale"`
ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"`
Perm users.Permissions `json:"perm"`
Commands []string `json:"commands"`
LockPassword bool `json:"lockPassword"`
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
Username string `json:"username"`
AceEditorTheme string `json:"aceEditorTheme"`
ID uint `json:"id"`
Locale string `json:"locale"`
ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"`
RedirectAfterCopyMove bool `json:"redirectAfterCopyMove"`
Perm users.Permissions `json:"perm"`
Commands []string `json:"commands"`
LockPassword bool `json:"lockPassword"`
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
Username string `json:"username"`
AceEditorTheme string `json:"aceEditorTheme"`
}
type authToken struct {
@@ -204,17 +205,18 @@ func renewHandler(tokenExpireTime time.Duration) handleFunc {
func printToken(w http.ResponseWriter, _ *http.Request, d *data, user *users.User, tokenExpirationTime time.Duration) (int, error) {
claims := &authToken{
User: userInfo{
ID: user.ID,
Locale: user.Locale,
ViewMode: user.ViewMode,
SingleClick: user.SingleClick,
Perm: user.Perm,
LockPassword: user.LockPassword,
Commands: user.Commands,
HideDotfiles: user.HideDotfiles,
DateFormat: user.DateFormat,
Username: user.Username,
AceEditorTheme: user.AceEditorTheme,
ID: user.ID,
Locale: user.Locale,
ViewMode: user.ViewMode,
SingleClick: user.SingleClick,
RedirectAfterCopyMove: user.RedirectAfterCopyMove,
Perm: user.Perm,
LockPassword: user.LockPassword,
Commands: user.Commands,
HideDotfiles: user.HideDotfiles,
DateFormat: user.DateFormat,
Username: user.Username,
AceEditorTheme: user.AceEditorTheme,
},
RegisteredClaims: jwt.RegisteredClaims{
IssuedAt: jwt.NewNumericDate(time.Now()),
+6
View File
@@ -280,6 +280,12 @@ func writeFile(afs afero.Fs, dst string, in io.Reader, fileMode, dirMode fs.File
return nil, err
}
// Sync the file to ensure all data is written to storage.
// to prevent file corruption.
if err := file.Sync(); err != nil {
return nil, err
}
// Gets the info about the file.
info, err := file.Stat()
if err != nil {
+6
View File
@@ -256,6 +256,12 @@ func tusPatchHandler() handleFunc {
return http.StatusInternalServerError, fmt.Errorf("could not write to file: %w", err)
}
// Sync the file to ensure all data is written to storage
// to prevent file corruption.
if err := openFile.Sync(); err != nil {
return http.StatusInternalServerError, fmt.Errorf("could not sync file: %w", err)
}
newOffset := uploadOffset + bytesWritten
w.Header().Set("Upload-Offset", strconv.FormatInt(newOffset, 10))
+12 -10
View File
@@ -8,16 +8,17 @@ import (
// UserDefaults is a type that holds the default values
// for some fields on User.
type UserDefaults struct {
Scope string `json:"scope"`
Locale string `json:"locale"`
ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"`
Sorting files.Sorting `json:"sorting"`
Perm users.Permissions `json:"perm"`
Commands []string `json:"commands"`
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
AceEditorTheme string `json:"aceEditorTheme"`
Scope string `json:"scope"`
Locale string `json:"locale"`
ViewMode users.ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"`
RedirectAfterCopyMove bool `json:"redirectAfterCopyMove"`
Sorting files.Sorting `json:"sorting"`
Perm users.Permissions `json:"perm"`
Commands []string `json:"commands"`
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
AceEditorTheme string `json:"aceEditorTheme"`
}
// Apply applies the default options to a user.
@@ -26,6 +27,7 @@ func (d *UserDefaults) Apply(u *users.User) {
u.Locale = d.Locale
u.ViewMode = d.ViewMode
u.SingleClick = d.SingleClick
u.RedirectAfterCopyMove = d.RedirectAfterCopyMove
u.Perm = d.Perm
u.Sorting = d.Sorting
u.Commands = d.Commands
+17 -16
View File
@@ -20,22 +20,23 @@ const (
// User describes a user.
type User struct {
ID uint `storm:"id,increment" json:"id"`
Username string `storm:"unique" json:"username"`
Password string `json:"password"`
Scope string `json:"scope"`
Locale string `json:"locale"`
LockPassword bool `json:"lockPassword"`
ViewMode ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"`
Perm Permissions `json:"perm"`
Commands []string `json:"commands"`
Sorting files.Sorting `json:"sorting"`
Fs afero.Fs `json:"-" yaml:"-"`
Rules []rules.Rule `json:"rules"`
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
AceEditorTheme string `json:"aceEditorTheme"`
ID uint `storm:"id,increment" json:"id"`
Username string `storm:"unique" json:"username"`
Password string `json:"password"`
Scope string `json:"scope"`
Locale string `json:"locale"`
LockPassword bool `json:"lockPassword"`
ViewMode ViewMode `json:"viewMode"`
SingleClick bool `json:"singleClick"`
RedirectAfterCopyMove bool `json:"redirectAfterCopyMove"`
Perm Permissions `json:"perm"`
Commands []string `json:"commands"`
Sorting files.Sorting `json:"sorting"`
Fs afero.Fs `json:"-" yaml:"-"`
Rules []rules.Rule `json:"rules"`
HideDotfiles bool `json:"hideDotfiles"`
DateFormat bool `json:"dateFormat"`
AceEditorTheme string `json:"aceEditorTheme"`
}
// GetRules implements rules.Provider.
@@ -61,6 +61,7 @@ filebrowser config init [flags]
--recaptcha.host string use another host for ReCAPTCHA. recaptcha.net might be useful in China (default "https://www.google.com")
--recaptcha.key string ReCaptcha site key
--recaptcha.secret string ReCaptcha secret
--redirectAfterCopyMove redirect to destination after copy/move
-r, --root string root to prepend to relative paths (default ".")
--scope string scope for users (default ".")
--shell string shell command to which other commands should be appended
@@ -58,6 +58,7 @@ filebrowser config set [flags]
--recaptcha.host string use another host for ReCAPTCHA. recaptcha.net might be useful in China (default "https://www.google.com")
--recaptcha.key string ReCaptcha site key
--recaptcha.secret string ReCaptcha secret
--redirectAfterCopyMove redirect to destination after copy/move
-r, --root string root to prepend to relative paths (default ".")
--scope string scope for users (default ".")
--shell string shell command to which other commands should be appended
@@ -28,6 +28,7 @@ filebrowser users add <username> <password> [flags]
--perm.modify modify perm for users (default true)
--perm.rename rename perm for users (default true)
--perm.share share perm for users (default true)
--redirectAfterCopyMove redirect to destination after copy/move
--scope string scope for users (default ".")
--singleClick use single clicks only
--sorting.asc sorting by ascending order
@@ -30,6 +30,7 @@ filebrowser users update <id|username> [flags]
--perm.modify modify perm for users (default true)
--perm.rename rename perm for users (default true)
--perm.share share perm for users (default true)
--redirectAfterCopyMove redirect to destination after copy/move
--scope string scope for users (default ".")
--singleClick use single clicks only
--sorting.asc sorting by ascending order
+1 -1
View File
@@ -1,4 +1,4 @@
# 简介
# GeoIP 简介 [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/Loyalsoldier/geoip)
本项目每周四自动生成多种格式 GeoIP 文件,同时提供命令行界面(CLI)工具供用户自行定制 GeoIP 文件,包括但不限于 V2Ray `dat` 格式文件 `geoip.dat`、MaxMind `mmdb` 格式文件 `Country.mmdb`、sing-box `SRS` 格式文件、mihomo `MRS` 格式文件、Clash ruleset 和 Surge ruleset。
+5
View File
@@ -21,8 +21,13 @@ WORKDIR /test
# Copy binaries, data and test script into the container.
COPY bin/sing-box bin/mita bin/httpserver bin/sockshttpclient bin/socksudpclient bin/udpserver \
test/deploy/singbox/client_tcp.json \
test/deploy/singbox/client_tcp_no_wait.json \
test/deploy/singbox/client_udp.json \
test/deploy/singbox/client_udp_no_wait.json \
test/deploy/singbox/singbox-client-config-tcp.json \
test/deploy/singbox/singbox-client-config-udp.json \
test/deploy/singbox/singbox-server-config.json \
test/deploy/singbox/server_tcp.json \
test/deploy/singbox/server_udp.json \
test/deploy/singbox/libtest.sh \
+32
View File
@@ -0,0 +1,32 @@
{
"profiles": [
{
"profileName": "default",
"user": {
"name": "baozi",
"password": "manlianpenfen"
},
"servers": [
{
"ipAddress": "127.0.0.1",
"portBindings": [
{
"port": 8966,
"protocol": "TCP"
}
]
}
],
"multiplexing": {
"level": "MULTIPLEXING_HIGH"
}
}
],
"activeProfile": "default",
"rpcPort": 8989,
"socks5Port": 1082,
"advancedSettings": {
"noCheckUpdate": true
},
"loggingLevel": "INFO"
}
@@ -0,0 +1,33 @@
{
"profiles": [
{
"profileName": "default",
"user": {
"name": "baozi",
"password": "manlianpenfen"
},
"servers": [
{
"ipAddress": "127.0.0.1",
"portBindings": [
{
"port": 8966,
"protocol": "TCP"
}
]
}
],
"multiplexing": {
"level": "MULTIPLEXING_HIGH"
},
"handshakeMode": "HANDSHAKE_NO_WAIT"
}
],
"activeProfile": "default",
"rpcPort": 8989,
"socks5Port": 1082,
"advancedSettings": {
"noCheckUpdate": true
},
"loggingLevel": "INFO"
}
+32
View File
@@ -0,0 +1,32 @@
{
"profiles": [
{
"profileName": "default",
"user": {
"name": "baozi",
"password": "manlianpenfen"
},
"servers": [
{
"ipAddress": "127.0.0.1",
"portBindings": [
{
"port": 8966,
"protocol": "UDP"
}
]
}
],
"multiplexing": {
"level": "MULTIPLEXING_HIGH"
}
}
],
"activeProfile": "default",
"rpcPort": 8989,
"socks5Port": 1085,
"advancedSettings": {
"noCheckUpdate": true
},
"loggingLevel": "INFO"
}
@@ -0,0 +1,33 @@
{
"profiles": [
{
"profileName": "default",
"user": {
"name": "baozi",
"password": "manlianpenfen"
},
"servers": [
{
"ipAddress": "127.0.0.1",
"portBindings": [
{
"port": 8966,
"protocol": "UDP"
}
]
}
],
"multiplexing": {
"level": "MULTIPLEXING_HIGH"
},
"handshakeMode": "HANDSHAKE_NO_WAIT"
}
],
"activeProfile": "default",
"rpcPort": 8989,
"socks5Port": 1085,
"advancedSettings": {
"noCheckUpdate": true
},
"loggingLevel": "INFO"
}
@@ -0,0 +1,35 @@
{
"inbounds": [
{
"type": "mieru",
"tag": "mieru-tcp",
"listen": "127.0.0.1",
"listen_port": 8966,
"transport": "TCP",
"users": [
{
"name": "baozi",
"password": "manlianpenfen"
}
]
},
{
"type": "mieru",
"tag": "mieru-udp",
"listen": "127.0.0.1",
"listen_port": 8966,
"transport": "UDP",
"users": [
{
"name": "baozi",
"password": "manlianpenfen"
}
]
}
],
"outbounds": [],
"route": {},
"log": {
"level": "warn"
}
}
+2 -2
View File
@@ -5,12 +5,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=filebrowser
PKG_VERSION:=2.53.1
PKG_VERSION:=2.54.0
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/filebrowser/filebrowser/tar.gz/v${PKG_VERSION}?
PKG_HASH:=68f55a90cf25c4e147dc50d45de2a619f4b24e9c2f6fa7c7de05130ca0b4e123
PKG_HASH:=0752cc7444e2f327cb4beefe6eb40493bfd2f5a077daa2e5dbf6ace013cfc34d
PKG_LICENSE:=Apache-2.0
PKG_LICENSE_FILES:=LICENSE
+1 -1
View File
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI support for ddnsto
LUCI_DEPENDS:=+ddnsto +block-mount
LUCI_PKGARCH:=all
PKG_VERSION:=3.2.0-r4
PKG_VERSION:=3.2.0-r5
PKG_RELEASE:=
include $(TOPDIR)/feeds/luci/luci.mk
File diff suppressed because one or more lines are too long
@@ -4,10 +4,12 @@ local api = require "luci.passwall.api"
<script src="<%=resource%>/view/<%=api.appname%>/Sortable.min.js?v=26.1.9"></script>
<style>
<% if api.is_js_luci() then -%>
table .cbi-button-up,
table .cbi-button-down {
display: none !important;
}
<%- end %>
.drag-handle {
cursor: grab !important;
@@ -166,6 +166,6 @@ o:value(0, translate("Primary"))
o:value(1, translate("Standby"))
o.rmempty = false
s:append(Template(appname .. "/haproxy/js"))
m:append(Template(appname .. "/haproxy/js"))
return m
@@ -1,4 +1,35 @@
<%
local api = require "luci.passwall.api"
-%>
<script src="<%=resource%>/view/<%=api.appname%>/Sortable.min.js?v=26.1.9"></script>
<style>
<% if api.is_js_luci() then -%>
table .cbi-button-up,
table .cbi-button-down {
display: none !important;
}
<%- end %>
.drag-handle {
cursor: grab !important;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: 100;
padding: 0 !important;
line-height: inherit;
user-select: none;
color: #00000070;
align-self: stretch;
}
.dragging-row {
background-color: rgba(131, 191, 255, 0.7) !important;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
</style>
<script type="text/javascript">
//<![CDATA[
document.addEventListener("DOMContentLoaded", function () {
@@ -45,5 +76,97 @@
}
}, 300);
});
//节点列表添加拖拽排序
(function () {
function initSortableForTable() {
var section = document.getElementById("cbi-<%=api.appname%>-haproxy_config");
if (!section) return;
// === 插入 drag handle ===
var delBtns = section.getElementsByClassName("cbi-button-remove");
for (var i = 0; i < delBtns.length; i++) {
var btn = delBtns[i];
var parent = btn && btn.parentNode;
if (!parent || parent.getElementsByClassName("drag-handle").length)
continue;
var handle = document.createElement("span");
handle.className = "drag-handle center";
handle.title = "<%:Drag to reorder%>";
handle.innerHTML = "⠿";
parent.insertBefore(handle, btn.nextSibling);
}
// === 初始化 Sortable ===
var table = section.getElementsByTagName("table")[0];
if (!table) return;
var root = table.tBodies[0] || table;
if (root._sortable_initialized) return root._sortable_instance;
root._sortable_initialized = true;
// 保存原始顺序
root._origOrder = getCurrentOrder(root);
try {
root._sortable_instance = Sortable.create(root, {
handle: ".drag-handle",
draggable: "tr.cbi-section-table-row",
animation: 150,
ghostClass: "dragging-row",
fallbackOnBody: true,
forceFallback: false,
swapThreshold: 0.65,
onEnd: function (evt) {
updateHiddenInput(root, section);
}
});
return root._sortable_instance;
} catch (e) {
root._sortable_initialized = false;
console.error("Sortable init failed:", e);
}
}
// 获取 table 当前行顺序
function getCurrentOrder(root) {
var order = [];
var rows = root.querySelectorAll("tr.cbi-section-table-row");
rows.forEach(function (tr) {
var id = tr.id || "";
if (id.startsWith("cbi-<%=api.appname%>-"))
id = id.replace("cbi-<%=api.appname%>-", "");
order.push(id);
});
return order;
}
// 拖拽完成后更新 hidden input
function updateHiddenInput(root, section) {
var newOrder = getCurrentOrder(root);
var changed = newOrder.join(" ") !== root._origOrder.join(" ");
var hiddenInput = section.querySelector('input[type="hidden"][id^="cbi.sts."]');
if (hiddenInput) {
hiddenInput.value = changed ? newOrder.join(" ") : "";
}
}
// === 等待 TypedSection 行稳定 ===
(function waitStable() {
var last = 0, stable = 0;
var THRESHOLD = 5;
function tick() {
var count = document.querySelectorAll("tr.cbi-section-table-row").length;
if (count && count === last) stable++;
else stable = 0;
last = count;
if (stable >= THRESHOLD)
initSortableForTable();
else
requestAnimationFrame(tick);
}
tick();
})();
})();
//]]>
</script>
@@ -1,6 +1,7 @@
<%
local api = require "luci.passwall.api"
-%>
<script src="<%=resource%>/view/<%=api.appname%>/Sortable.min.js?v=26.1.9"></script>
<style>
div.cbi-value[id$="-gfwlist_update"],
@@ -11,6 +12,25 @@ local api = require "luci.passwall.api"
div.cbi-value[id$="-geosite_update"] {
display: none !important;
}
.drag-handle {
cursor: grab !important;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: 100;
padding: 0 !important;
line-height: inherit;
user-select: none;
color: #00000070;
align-self: stretch;
}
.dragging-row {
background-color: rgba(131, 191, 255, 0.7) !important;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
</style>
<div class="cbi-value" id="_rule_div">
@@ -116,5 +136,97 @@ local api = require "luci.passwall.api"
}
);
}
//分流规则添加拖拽排序
(function () {
function initSortableForTable() {
var section = document.getElementById("cbi-<%=api.appname%>-shunt_rules");
if (!section) return;
// === 插入 drag handle ===
var delBtns = section.getElementsByClassName("cbi-button-remove");
for (var i = 0; i < delBtns.length; i++) {
var btn = delBtns[i];
var parent = btn && btn.parentNode;
if (!parent || parent.getElementsByClassName("drag-handle").length)
continue;
var handle = document.createElement("span");
handle.className = "drag-handle center";
handle.title = "<%:Drag to reorder%>";
handle.innerHTML = "⠿";
parent.insertBefore(handle, btn.nextSibling);
}
// === 初始化 Sortable ===
var table = section.getElementsByTagName("table")[0];
if (!table) return;
var root = table.tBodies[0] || table;
if (root._sortable_initialized) return root._sortable_instance;
root._sortable_initialized = true;
// 保存原始顺序
root._origOrder = getCurrentOrder(root);
try {
root._sortable_instance = Sortable.create(root, {
handle: ".drag-handle",
draggable: "tr.cbi-section-table-row",
animation: 150,
ghostClass: "dragging-row",
fallbackOnBody: true,
forceFallback: false,
swapThreshold: 0.65,
onEnd: function (evt) {
updateHiddenInput(root, section);
}
});
return root._sortable_instance;
} catch (e) {
root._sortable_initialized = false;
console.error("Sortable init failed:", e);
}
}
// 获取 table 当前行顺序
function getCurrentOrder(root) {
var order = [];
var rows = root.querySelectorAll("tr.cbi-section-table-row");
rows.forEach(function (tr) {
var id = tr.id || "";
if (id.startsWith("cbi-<%=api.appname%>-"))
id = id.replace("cbi-<%=api.appname%>-", "");
order.push(id);
});
return order;
}
// 拖拽完成后更新 hidden input
function updateHiddenInput(root, section) {
var newOrder = getCurrentOrder(root);
var changed = newOrder.join(" ") !== root._origOrder.join(" ");
var hiddenInput = section.querySelector('input[type="hidden"][id^="cbi.sts."]');
if (hiddenInput) {
hiddenInput.value = changed ? newOrder.join(" ") : "";
}
}
// === 等待 TypedSection 行稳定 ===
(function waitStable() {
var last = 0, stable = 0;
var THRESHOLD = 5;
function tick() {
var count = document.querySelectorAll("tr.cbi-section-table-row").length;
if (count && count === last) stable++;
else stable = 0;
last = count;
if (stable >= THRESHOLD)
initSortableForTable();
else
requestAnimationFrame(tick);
}
tick();
})();
})();
//]]>
</script>
@@ -691,7 +691,7 @@ run_socks() {
case "$type" in
socks)
local _socks_address _socks_port _socks_username _socks_password _extra_param microsocks_fwd
local _socks_address _socks_port _socks_username _socks_password
if [ "$node2socks_port" = "0" ]; then
_socks_address=$(config_n_get $node address)
_socks_port=$(config_n_get $node port)
@@ -701,24 +701,13 @@ run_socks() {
_socks_address="127.0.0.1"
_socks_port=$node2socks_port
fi
if [ "$http_port" != "0" ]; then
[ "$http_port" != "0" ] && {
http_flag=1
config_file="${config_file//SOCKS/HTTP_SOCKS}"
_extra_param="-local_http_address $bind -local_http_port $http_port"
else
# 仅 passwall-packages 专用的 microsocks 才支持 socks 转发规则!
microsocks_fwd="$($(first_type microsocks) -V 2>/dev/null | grep -i forward)"
fi
local _extra_param="-local_http_address $bind -local_http_port $http_port"
}
local bin=$(first_type $(config_t_get global_app sing_box_file) sing-box)
if [ -n "$microsocks_fwd" ]; then
local ext_name=$(echo "$config_file" | sed "s|^${TMP_PATH}/||; s|\.json\$||; s|/|_|g")
if [ -n "$_socks_username" ] && [ -n "$_socks_password" ]; then
_extra_param="-f \"0.0.0.0:0,${_socks_username}:${_socks_password}@${_socks_address}:${_socks_port},0.0.0.0:0\""
else
_extra_param="-f \"0.0.0.0:0,${_socks_address}:${_socks_port},0.0.0.0:0\""
fi
ln_run "$(first_type microsocks)" "microsocks_${ext_name}" $log_file -i $bind -p $socks_port ${_extra_param}
elif [ -n "$bin" ]; then
if [ -n "$bin" ]; then
type="sing-box"
lua $UTIL_SINGBOX gen_proto_config -local_socks_address $bind -local_socks_port $socks_port ${_extra_param} -server_proto socks -server_address ${_socks_address} -server_port ${_socks_port} -server_username ${_socks_username} -server_password ${_socks_password} > $config_file
ln_run "$bin" ${type} $log_file run -c "$config_file"
+2 -2
View File
@@ -21,13 +21,13 @@ define Download/geoip
HASH:=ed2de9add79623e2e5dbc5930ee39cc7037a7c6e0ecd58ba528b6f73d61457b5
endef
GEOSITE_VER:=20260109015452
GEOSITE_VER:=20260110084845
GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER)
define Download/geosite
URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/
URL_FILE:=dlc.dat
FILE:=$(GEOSITE_FILE)
HASH:=379bee918c0241df853bac6855d0fafa188c6cae262fc6c9639f1afcd5f6121e
HASH:=274d189c37b7af64bd14d4ac76bc05a56683d9b502e01bc68a34df25711e11f1
endef
GEOSITE_IRAN_VER:=202601050049
@@ -56,7 +56,8 @@ public class ProfilesViewModel : MyReactiveObject
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
public ReactiveCommand<SubItem, Unit> MoveToGroupCmd { get; }
//servers ping
public ReactiveCommand<Unit, Unit> MixedTestServerCmd { get; }
@@ -179,6 +180,10 @@ public class ProfilesViewModel : MyReactiveObject
{
await MoveServer(EMove.Bottom);
}, canEditRemove);
MoveToGroupCmd = ReactiveCommand.CreateFromTask<SubItem>(async sub =>
{
SelectedMoveToGroup = sub;
});
//servers ping
FastRealPingCmd = ReactiveCommand.CreateFromTask(async () =>
@@ -7,6 +7,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
x:Name="Root"
d:DesignHeight="450"
d:DesignWidth="800"
x:DataType="vms:ProfilesViewModel"
@@ -141,19 +142,18 @@
InputGesture="Ctrl+T" />
<MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" />
<Separator />
<MenuItem x:Name="menuMoveToGroup" Header="{x:Static resx:ResUI.menuMoveToGroup}">
<MenuItem>
<MenuItem.Header>
<DockPanel>
<ComboBox
x:Name="cmbMoveToGroup"
Width="200"
DisplayMemberBinding="{Binding Remarks}"
ItemsSource="{Binding SubItems}"
ToolTip.Tip="{x:Static resx:ResUI.menuSubscription}" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem
x:Name="menuMoveToGroup"
Header="{x:Static resx:ResUI.menuMoveToGroup}"
ItemsSource="{Binding DataContext.SubItems, ElementName=Root}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem
Command="{Binding DataContext.MoveToGroupCmd, ElementName=Root}"
CommandParameter="{Binding}"
Header="{Binding Remarks}" />
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
<MenuItem Header="{x:Static resx:ResUI.menuMoveTo}">
<MenuItem
@@ -69,7 +69,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
//servers move
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedMoveToGroup, v => v.cmbMoveToGroup.SelectedItem).DisposeWith(disposables);
//this.Bind(ViewModel, vm => vm.SelectedMoveToGroup, v => v.cmbMoveToGroup.SelectedItem).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
@@ -34,6 +34,8 @@ class AngApplication : MultiDexApplication() {
MMKV.initialize(this)
// Ensure critical preference defaults are present in MMKV early
SettingsManager.ensureDefaultSettings()
SettingsManager.setNightMode()
// Initialize WorkManager with the custom configuration
WorkManager.initialize(this, workManagerConfiguration)
@@ -493,6 +493,28 @@ object MmkvManager {
return settingsStorage.encode(key, value)
}
/**
* Encodes the settings.
*
* @param key The settings key.
* @param value The settings value.
* @return Whether the encoding was successful.
*/
fun encodeSettings(key: String, value: Long): Boolean {
return settingsStorage.encode(key, value)
}
/**
* Encodes the settings.
*
* @param key The settings key.
* @param value The settings value.
* @return Whether the encoding was successful.
*/
fun encodeSettings(key: String, value: Float): Boolean {
return settingsStorage.encode(key, value)
}
/**
* Encodes the settings.
*
@@ -536,6 +558,39 @@ object MmkvManager {
return settingsStorage.decodeString(key, defaultValue)
}
/**
* Decodes the settings integer.
*
* @param key The settings key.
* @param defaultValue The default value.
* @return The settings value.
*/
fun decodeSettingsInt(key: String, defaultValue: Int): Int {
return settingsStorage.decodeInt(key, defaultValue)
}
/**
* Decodes the settings long.
*
* @param key The settings key.
* @param defaultValue The default value.
* @return The settings value.
*/
fun decodeSettingsLong(key: String, defaultValue: Long): Long {
return settingsStorage.decodeLong(key, defaultValue)
}
/**
* Decodes the settings float.
*
* @param key The settings key.
* @param defaultValue The default value.
* @return The settings value.
*/
fun decodeSettingsFloat(key: String, defaultValue: Float): Float {
return settingsStorage.decodeFloat(key, defaultValue)
}
/**
* Decodes the settings boolean.
*
@@ -0,0 +1,80 @@
package com.v2ray.ang.handler
import androidx.preference.PreferenceDataStore
import com.v2ray.ang.AppConfig
/**
* PreferenceDataStore implementation that bridges AndroidX Preference framework to MMKV storage.
* This ensures that all Preference UI operations read/write directly from/to MMKV,
* avoiding inconsistencies between SharedPreferences and MMKV.
*/
class MmkvPreferenceDataStore : PreferenceDataStore() {
override fun putString(key: String, value: String?) {
MmkvManager.encodeSettings(key, value)
notifySettingChanged(key)
}
override fun getString(key: String, defaultValue: String?): String? {
return MmkvManager.decodeSettingsString(key, defaultValue)
}
override fun putInt(key: String, value: Int) {
MmkvManager.encodeSettings(key, value)
notifySettingChanged(key)
}
override fun getInt(key: String, defaultValue: Int): Int {
return MmkvManager.decodeSettingsInt(key, defaultValue)
}
override fun putLong(key: String, value: Long) {
MmkvManager.encodeSettings(key, value)
notifySettingChanged(key)
}
override fun getLong(key: String, defaultValue: Long): Long {
return MmkvManager.decodeSettingsLong(key, defaultValue)
}
override fun putFloat(key: String, value: Float) {
MmkvManager.encodeSettings(key, value)
notifySettingChanged(key)
}
override fun getFloat(key: String, defaultValue: Float): Float {
return MmkvManager.decodeSettingsFloat(key, defaultValue)
}
override fun putBoolean(key: String, value: Boolean) {
MmkvManager.encodeSettings(key, value)
notifySettingChanged(key)
}
override fun getBoolean(key: String, defaultValue: Boolean): Boolean {
return MmkvManager.decodeSettingsBool(key, defaultValue)
}
override fun putStringSet(key: String, values: MutableSet<String>?) {
if (values == null) {
MmkvManager.encodeSettings(key, null as String?)
} else {
MmkvManager.encodeSettings(key, values)
}
notifySettingChanged(key)
}
override fun getStringSet(key: String, defaultValues: MutableSet<String>?): MutableSet<String>? {
return MmkvManager.decodeSettingsStringSet(key) ?: defaultValues
}
// Internal helper: notify other modules about setting changes
private fun notifySettingChanged(key: String) {
// Call SettingsManager.setNightMode if UI mode changed
if (key == AppConfig.PREF_UI_MODE_NIGHT) {
SettingsManager.setNightMode()
}
// Notify listeners that require service restart or reinit
SettingsChangeManager.makeRestartService()
}
}
@@ -377,4 +377,31 @@ object SettingsManager {
fun getVpnMtu(): Int {
return Utils.parseInt(MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_MTU), AppConfig.VPN_MTU)
}
/**
* Ensure default settings are present in MMKV.
*/
fun ensureDefaultSettings() {
// Write defaults in the exact order requested by the user
ensureDefaultValue(AppConfig.PREF_MODE, AppConfig.VPN)
ensureDefaultValue(AppConfig.PREF_VPN_DNS, AppConfig.DNS_VPN)
ensureDefaultValue(AppConfig.PREF_VPN_MTU, AppConfig.VPN_MTU.toString())
ensureDefaultValue(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL, AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL)
ensureDefaultValue(AppConfig.PREF_SOCKS_PORT, AppConfig.PORT_SOCKS.toString())
ensureDefaultValue(AppConfig.PREF_REMOTE_DNS, AppConfig.DNS_PROXY)
ensureDefaultValue(AppConfig.PREF_DOMESTIC_DNS, AppConfig.DNS_DIRECT)
ensureDefaultValue(AppConfig.PREF_DELAY_TEST_URL, AppConfig.DELAY_TEST_URL)
ensureDefaultValue(AppConfig.PREF_IP_API_URL, AppConfig.IP_API_URL)
ensureDefaultValue(AppConfig.PREF_HEV_TUNNEL_RW_TIMEOUT, AppConfig.HEVTUN_RW_TIMEOUT)
ensureDefaultValue(AppConfig.PREF_MUX_CONCURRENCY, "8")
ensureDefaultValue(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "8")
ensureDefaultValue(AppConfig.PREF_FRAGMENT_LENGTH, "50-100")
ensureDefaultValue(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20")
}
private fun ensureDefaultValue(key: String, default: String) {
if (MmkvManager.decodeSettingsString(key).isNullOrEmpty()) {
MmkvManager.encodeSettings(key, default)
}
}
}
@@ -36,6 +36,7 @@ class V2RayTestService : Service() {
// simple counter for currently running tasks
private val realTestRunningCount = AtomicInteger(0)
private val realTestCount = AtomicInteger(0)
/**
* Initializes the V2Ray environment.
@@ -56,15 +57,22 @@ class V2RayTestService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.getIntExtra("key", 0)) {
MSG_MEASURE_CONFIG -> {
val guid = intent.serializable<String>("content") ?: ""
realTestScope.launch {
realTestRunningCount.incrementAndGet()
try {
val result = startRealPing(guid)
MessageUtil.sendMsg2UI(this@V2RayTestService, MSG_MEASURE_CONFIG_SUCCESS, Pair(guid, result))
} finally {
val left = realTestRunningCount.decrementAndGet()
MessageUtil.sendMsg2UI(this@V2RayTestService, AppConfig.MSG_MEASURE_CONFIG_FINISH, left.toString())
val guidsList = intent.serializable<ArrayList<String>>("content")
if (guidsList == null || guidsList.isEmpty()) {
return super.onStartCommand(intent, flags, startId)
}
for (guid in guidsList) {
realTestCount.incrementAndGet()
realTestScope.launch {
realTestRunningCount.incrementAndGet()
try {
val result = startRealPing(guid)
MessageUtil.sendMsg2UI(this@V2RayTestService, MSG_MEASURE_CONFIG_SUCCESS, Pair(guid, result))
} finally {
val count = realTestCount.decrementAndGet()
val left = realTestRunningCount.decrementAndGet()
MessageUtil.sendMsg2UI(this@V2RayTestService, AppConfig.MSG_MEASURE_CONFIG_FINISH, "$left / $count")
}
}
}
}
@@ -1,17 +0,0 @@
package com.v2ray.ang.ui
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
class FragmentAdapter(fragmentActivity: FragmentActivity, private val mFragments: List<Fragment>) :
FragmentStateAdapter(fragmentActivity) {
override fun createFragment(position: Int): Fragment {
return mFragments[position]
}
override fun getItemCount(): Int {
return mFragments.size
}
}
@@ -3,7 +3,6 @@ package com.v2ray.ang.ui
import android.os.Bundle
import android.text.TextUtils
import android.view.View
import androidx.activity.viewModels
import androidx.preference.CheckBoxPreference
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
@@ -17,20 +16,20 @@ import com.v2ray.ang.AppConfig.VPN
import com.v2ray.ang.R
import com.v2ray.ang.extension.toLongEx
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.MmkvPreferenceDataStore
import com.v2ray.ang.handler.SubscriptionUpdater
import com.v2ray.ang.util.Utils
import com.v2ray.ang.viewmodel.SettingsViewModel
import java.util.concurrent.TimeUnit
class SettingsActivity : BaseActivity() {
private val settingsViewModel: SettingsViewModel by viewModels()
//private val settingsViewModel: SettingsViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_settings)
setContentViewWithToolbar(R.layout.activity_settings, showHomeAsUp = true, title = getString(R.string.title_settings))
settingsViewModel.startListenPreferenceChange()
//settingsViewModel.startListenPreferenceChange()
}
class SettingsFragment : PreferenceFragmentCompat() {
@@ -58,21 +57,27 @@ class SettingsActivity : BaseActivity() {
private val autoUpdateCheck by lazy { findPreference<CheckBoxPreference>(AppConfig.SUBSCRIPTION_AUTO_UPDATE) }
private val autoUpdateInterval by lazy { findPreference<EditTextPreference>(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL) }
private val socksPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_SOCKS_PORT) }
private val remoteDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_REMOTE_DNS) }
private val domesticDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DOMESTIC_DNS) }
private val dnsHosts by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DNS_HOSTS) }
private val delayTestUrl by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DELAY_TEST_URL) }
private val ipApiUrl by lazy { findPreference<EditTextPreference>(AppConfig.PREF_IP_API_URL) }
// private val socksPort by lazy { findPreference<EditTextPreference>(AppConfig.PREF_SOCKS_PORT) }
// private val remoteDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_REMOTE_DNS) }
// private val domesticDns by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DOMESTIC_DNS) }
// private val dnsHosts by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DNS_HOSTS) }
// private val delayTestUrl by lazy { findPreference<EditTextPreference>(AppConfig.PREF_DELAY_TEST_URL) }
// private val ipApiUrl by lazy { findPreference<EditTextPreference>(AppConfig.PREF_IP_API_URL) }
private val mode by lazy { findPreference<ListPreference>(AppConfig.PREF_MODE) }
private val hevTunLogLevel by lazy { findPreference<ListPreference>(AppConfig.PREF_HEV_TUNNEL_LOGLEVEL) }
private val hevTunRwTimeout by lazy { findPreference<EditTextPreference>(AppConfig.PREF_HEV_TUNNEL_RW_TIMEOUT) }
// private val hevTunLogLevel by lazy { findPreference<ListPreference>(AppConfig.PREF_HEV_TUNNEL_LOGLEVEL) }
// private val hevTunRwTimeout by lazy { findPreference<EditTextPreference>(AppConfig.PREF_HEV_TUNNEL_RW_TIMEOUT) }
// private val useHevTun by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_USE_HEV_TUNNEL) }
override fun onCreatePreferences(bundle: Bundle?, s: String?) {
// Use MMKV as the storage backend for all Preferences
// This prevents inconsistencies between SharedPreferences and MMKV
preferenceManager.preferenceDataStore = MmkvPreferenceDataStore()
addPreferencesFromResource(R.xml.pref_settings)
initPreferenceSummaries()
// perAppProxy?.setOnPreferenceClickListener {
// startActivity(Intent(activity, PerAppProxyActivity::class.java))
// perAppProxy?.isChecked = true
@@ -87,16 +92,16 @@ class SettingsActivity : BaseActivity() {
// localDnsPort?.summary = nval.ifEmpty { AppConfig.PORT_LOCAL_DNS }
// true
// }
vpnDns?.setOnPreferenceChangeListener { _, any ->
vpnDns?.summary = any as String
true
}
// vpnDns?.setOnPreferenceChangeListener { _, any ->
// vpnDns?.summary = any as String
// true
// }
vpnMtu?.setOnPreferenceChangeListener { _, any ->
val nval = any as String
vpnMtu?.summary = nval.ifEmpty { AppConfig.VPN_MTU.toString() }
true
}
// vpnMtu?.setOnPreferenceChangeListener { _, any ->
// val nval = any as String
// vpnMtu?.summary = nval.ifEmpty { AppConfig.VPN_MTU.toString() }
// true
// }
mux?.setOnPreferenceChangeListener { _, newValue ->
updateMux(newValue as Boolean)
@@ -115,18 +120,18 @@ class SettingsActivity : BaseActivity() {
updateFragment(newValue as Boolean)
true
}
fragmentPackets?.setOnPreferenceChangeListener { _, newValue ->
updateFragmentPackets(newValue as String)
true
}
fragmentLength?.setOnPreferenceChangeListener { _, newValue ->
updateFragmentLength(newValue as String)
true
}
fragmentInterval?.setOnPreferenceChangeListener { _, newValue ->
updateFragmentInterval(newValue as String)
true
}
// fragmentPackets?.setOnPreferenceChangeListener { _, newValue ->
// updateFragmentPackets(newValue as String)
// true
// }
// fragmentLength?.setOnPreferenceChangeListener { _, newValue ->
// updateFragmentLength(newValue as String)
// true
// }
// fragmentInterval?.setOnPreferenceChangeListener { _, newValue ->
// updateFragmentInterval(newValue as String)
// true
// }
autoUpdateCheck?.setOnPreferenceChangeListener { _, newValue ->
val value = newValue as Boolean
@@ -137,48 +142,48 @@ class SettingsActivity : BaseActivity() {
}
true
}
autoUpdateInterval?.setOnPreferenceChangeListener { _, any ->
var nval = any as String
// autoUpdateInterval?.setOnPreferenceChangeListener { _, any ->
// var nval = any as String
//
// // It must be greater than 15 minutes because WorkManager couldn't run tasks under 15 minutes intervals
// nval =
// if (TextUtils.isEmpty(nval) || nval.toLongEx() < 15) AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL else nval
// autoUpdateInterval?.summary = nval
// configureUpdateTask(nval.toLongEx())
// true
// }
// It must be greater than 15 minutes because WorkManager couldn't run tasks under 15 minutes intervals
nval =
if (TextUtils.isEmpty(nval) || nval.toLongEx() < 15) AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL else nval
autoUpdateInterval?.summary = nval
configureUpdateTask(nval.toLongEx())
true
}
socksPort?.setOnPreferenceChangeListener { _, any ->
val nval = any as String
socksPort?.summary = nval.ifEmpty { AppConfig.PORT_SOCKS }
true
}
remoteDns?.setOnPreferenceChangeListener { _, any ->
val nval = any as String
remoteDns?.summary = nval.ifEmpty { AppConfig.DNS_PROXY }
true
}
domesticDns?.setOnPreferenceChangeListener { _, any ->
val nval = any as String
domesticDns?.summary = nval.ifEmpty { AppConfig.DNS_DIRECT }
true
}
dnsHosts?.setOnPreferenceChangeListener { _, any ->
val nval = any as String
dnsHosts?.summary = nval
true
}
delayTestUrl?.setOnPreferenceChangeListener { _, any ->
val nval = any as String
delayTestUrl?.summary = nval.ifEmpty { AppConfig.DELAY_TEST_URL }
true
}
ipApiUrl?.setOnPreferenceChangeListener { _, any ->
val nval = any as String
ipApiUrl?.summary = nval.ifEmpty { AppConfig.IP_API_URL }
true
}
// socksPort?.setOnPreferenceChangeListener { _, any ->
// val nval = any as String
// socksPort?.summary = nval.ifEmpty { AppConfig.PORT_SOCKS }
// true
// }
//
// remoteDns?.setOnPreferenceChangeListener { _, any ->
// val nval = any as String
// remoteDns?.summary = nval.ifEmpty { AppConfig.DNS_PROXY }
// true
// }
// domesticDns?.setOnPreferenceChangeListener { _, any ->
// val nval = any as String
// domesticDns?.summary = nval.ifEmpty { AppConfig.DNS_DIRECT }
// true
// }
// dnsHosts?.setOnPreferenceChangeListener { _, any ->
// val nval = any as String
// dnsHosts?.summary = nval
// true
// }
// delayTestUrl?.setOnPreferenceChangeListener { _, any ->
// val nval = any as String
// delayTestUrl?.summary = nval.ifEmpty { AppConfig.DELAY_TEST_URL }
// true
// }
// ipApiUrl?.setOnPreferenceChangeListener { _, any ->
// val nval = any as String
// ipApiUrl?.summary = nval.ifEmpty { AppConfig.IP_API_URL }
// true
// }
mode?.setOnPreferenceChangeListener { _, newValue ->
updateMode(newValue.toString())
true
@@ -191,113 +196,151 @@ class SettingsActivity : BaseActivity() {
// true
// }
hevTunRwTimeout?.setOnPreferenceChangeListener { _, any ->
val nval = any as String
hevTunRwTimeout?.summary = nval.ifEmpty { AppConfig.HEVTUN_RW_TIMEOUT }
true
// hevTunRwTimeout?.setOnPreferenceChangeListener { _, any ->
// val nval = any as String
// hevTunRwTimeout?.summary = nval.ifEmpty { AppConfig.HEVTUN_RW_TIMEOUT }
// true
// }
}
private fun initPreferenceSummaries() {
fun updateSummary(pref: androidx.preference.Preference) {
when (pref) {
is EditTextPreference -> {
pref.summary = pref.text.orEmpty()
pref.setOnPreferenceChangeListener { p, newValue ->
p.summary = (newValue as? String).orEmpty()
true
}
}
is ListPreference -> {
pref.summary = pref.entry ?: ""
pref.setOnPreferenceChangeListener { p, newValue ->
val lp = p as ListPreference
val idx = lp.findIndexOfValue(newValue as? String)
lp.summary = (if (idx >= 0) lp.entries[idx] else newValue) as CharSequence?
true
}
}
is CheckBoxPreference, is androidx.preference.SwitchPreferenceCompat -> {
}
}
}
fun traverse(group: androidx.preference.PreferenceGroup) {
for (i in 0 until group.preferenceCount) {
val p = group.getPreference(i)
when (p) {
is androidx.preference.PreferenceGroup -> traverse(p)
else -> updateSummary(p)
}
}
}
preferenceScreen?.let { traverse(it) }
}
override fun onStart() {
super.onStart()
// Initialize mode-dependent UI states
updateMode(MmkvManager.decodeSettingsString(AppConfig.PREF_MODE, VPN))
localDns?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED, false)
fakeDns?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_FAKE_DNS_ENABLED, false)
appendHttpProxy?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_APPEND_HTTP_PROXY, false)
// localDnsPort?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_LOCAL_DNS_PORT, AppConfig.PORT_LOCAL_DNS)
vpnDns?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_DNS, AppConfig.DNS_VPN)
vpnMtu?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_MTU, AppConfig.VPN_MTU.toString())
// Initialize mux-dependent UI states
updateMux(MmkvManager.decodeSettingsBool(AppConfig.PREF_MUX_ENABLED, false))
mux?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_MUX_ENABLED, false)
muxConcurrency?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_CONCURRENCY, "8")
muxXudpConcurrency?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "8")
// Initialize fragment-dependent UI states
updateFragment(MmkvManager.decodeSettingsBool(AppConfig.PREF_FRAGMENT_ENABLED, false))
fragment?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_FRAGMENT_ENABLED, false)
fragmentPackets?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_PACKETS, "tlshello")
fragmentLength?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_LENGTH, "50-100")
fragmentInterval?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20")
autoUpdateCheck?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.SUBSCRIPTION_AUTO_UPDATE, false)
autoUpdateInterval?.summary =
MmkvManager.decodeSettingsString(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL, AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL)
// Initialize auto-update interval state
autoUpdateInterval?.isEnabled = MmkvManager.decodeSettingsBool(AppConfig.SUBSCRIPTION_AUTO_UPDATE, false)
socksPort?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_SOCKS_PORT, AppConfig.PORT_SOCKS)
remoteDns?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_REMOTE_DNS, AppConfig.DNS_PROXY)
domesticDns?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_DOMESTIC_DNS, AppConfig.DNS_DIRECT)
dnsHosts?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_DNS_HOSTS)
delayTestUrl?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_DELAY_TEST_URL, AppConfig.DELAY_TEST_URL)
ipApiUrl?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_IP_API_URL, AppConfig.IP_API_URL)
// localDns?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED, false)
// fakeDns?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_FAKE_DNS_ENABLED, false)
// appendHttpProxy?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_APPEND_HTTP_PROXY, false)
// vpnDns?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_DNS, AppConfig.DNS_VPN)
// vpnMtu?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_VPN_MTU, AppConfig.VPN_MTU.toString())
// mux?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_MUX_ENABLED, false)
// muxConcurrency?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_CONCURRENCY, "8")
// muxXudpConcurrency?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "8")
// fragment?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_FRAGMENT_ENABLED, false)
// fragmentPackets?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_PACKETS, "tlshello")
// fragmentLength?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_LENGTH, "50-100")
// fragmentInterval?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20")
// autoUpdateCheck?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.SUBSCRIPTION_AUTO_UPDATE, false)
// autoUpdateInterval?.summary =
// MmkvManager.decodeSettingsString(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL, AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL)
// socksPort?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_SOCKS_PORT, AppConfig.PORT_SOCKS)
// remoteDns?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_REMOTE_DNS, AppConfig.DNS_PROXY)
// domesticDns?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_DOMESTIC_DNS, AppConfig.DNS_DIRECT)
// dnsHosts?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_DNS_HOSTS)
// delayTestUrl?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_DELAY_TEST_URL, AppConfig.DELAY_TEST_URL)
// ipApiUrl?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_IP_API_URL, AppConfig.IP_API_URL)
// hevTunRwTimeout?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_HEV_TUNNEL_RW_TIMEOUT, AppConfig.HEVTUN_RW_TIMEOUT)
//updateHevTunSettings(MmkvManager.decodeSettingsBool(AppConfig.PREF_USE_HEV_TUNNEL, true))
hevTunRwTimeout?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_HEV_TUNNEL_RW_TIMEOUT, AppConfig.HEVTUN_RW_TIMEOUT)
initSharedPreference()
// initSharedPreference()
}
private fun initSharedPreference() {
listOf(
//localDnsPort,
vpnDns,
vpnMtu,
muxConcurrency,
muxXudpConcurrency,
fragmentLength,
fragmentInterval,
autoUpdateInterval,
socksPort,
remoteDns,
domesticDns,
delayTestUrl,
ipApiUrl,
hevTunRwTimeout
).forEach { key ->
key?.text = key.summary.toString()
}
// listOf(
// //localDnsPort,
// vpnDns,
// vpnMtu,
// muxConcurrency,
// muxXudpConcurrency,
// fragmentLength,
// fragmentInterval,
// autoUpdateInterval,
// socksPort,
// remoteDns,
// domesticDns,
// delayTestUrl,
// ipApiUrl,
// hevTunRwTimeout
// ).forEach { key ->
// key?.summary = key.text.toString()
// }
listOf(
AppConfig.PREF_SNIFFING_ENABLED,
AppConfig.PREF_USE_HEV_TUNNEL
).forEach { key ->
findPreference<CheckBoxPreference>(key)?.isChecked =
MmkvManager.decodeSettingsBool(key, true)
}
listOf(
AppConfig.PREF_ROUTE_ONLY_ENABLED,
AppConfig.PREF_IS_BOOTED,
AppConfig.PREF_BYPASS_APPS,
AppConfig.PREF_SPEED_ENABLED,
AppConfig.PREF_CONFIRM_REMOVE,
AppConfig.PREF_START_SCAN_IMMEDIATE,
AppConfig.PREF_DOUBLE_COLUMN_DISPLAY,
AppConfig.PREF_PREFER_IPV6,
AppConfig.PREF_PROXY_SHARING,
AppConfig.PREF_ALLOW_INSECURE
).forEach { key ->
findPreference<CheckBoxPreference>(key)?.isChecked =
MmkvManager.decodeSettingsBool(key, false)
}
listOf(
AppConfig.PREF_VPN_BYPASS_LAN,
AppConfig.PREF_VPN_INTERFACE_ADDRESS_CONFIG_INDEX,
AppConfig.PREF_ROUTING_DOMAIN_STRATEGY,
AppConfig.PREF_MUX_XUDP_QUIC,
AppConfig.PREF_FRAGMENT_PACKETS,
AppConfig.PREF_LANGUAGE,
AppConfig.PREF_UI_MODE_NIGHT,
AppConfig.PREF_LOGLEVEL,
AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD,
AppConfig.PREF_MODE,
AppConfig.PREF_HEV_TUNNEL_LOGLEVEL
).forEach { key ->
if (MmkvManager.decodeSettingsString(key) != null) {
findPreference<ListPreference>(key)?.value = MmkvManager.decodeSettingsString(key)
}
}
// listOf(
// AppConfig.PREF_SNIFFING_ENABLED,
// AppConfig.PREF_USE_HEV_TUNNEL
// ).forEach { key ->
// findPreference<CheckBoxPreference>(key)?.isChecked =
// MmkvManager.decodeSettingsBool(key, true)
// }
//
// listOf(
// AppConfig.PREF_ROUTE_ONLY_ENABLED,
// AppConfig.PREF_IS_BOOTED,
// AppConfig.PREF_BYPASS_APPS,
// AppConfig.PREF_SPEED_ENABLED,
// AppConfig.PREF_CONFIRM_REMOVE,
// AppConfig.PREF_START_SCAN_IMMEDIATE,
// AppConfig.PREF_DOUBLE_COLUMN_DISPLAY,
// AppConfig.PREF_PREFER_IPV6,
// AppConfig.PREF_PROXY_SHARING,
// AppConfig.PREF_ALLOW_INSECURE
// ).forEach { key ->
// findPreference<CheckBoxPreference>(key)?.isChecked =
// MmkvManager.decodeSettingsBool(key, false)
// }
//
// listOf(
// AppConfig.PREF_VPN_BYPASS_LAN,
// AppConfig.PREF_VPN_INTERFACE_ADDRESS_CONFIG_INDEX,
// AppConfig.PREF_ROUTING_DOMAIN_STRATEGY,
// AppConfig.PREF_MUX_XUDP_QUIC,
// AppConfig.PREF_FRAGMENT_PACKETS,
// AppConfig.PREF_LANGUAGE,
// AppConfig.PREF_UI_MODE_NIGHT,
// AppConfig.PREF_LOGLEVEL,
// AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD,
// AppConfig.PREF_MODE,
// AppConfig.PREF_HEV_TUNNEL_LOGLEVEL
// ).forEach { key ->
// if (MmkvManager.decodeSettingsString(key) != null) {
// findPreference<ListPreference>(key)?.value = MmkvManager.decodeSettingsString(key)
// }
// }
}
private fun updateMode(mode: String?) {
@@ -381,29 +424,29 @@ class SettingsActivity : BaseActivity() {
fragmentPackets?.isEnabled = enabled
fragmentLength?.isEnabled = enabled
fragmentInterval?.isEnabled = enabled
if (enabled) {
updateFragmentPackets(MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_PACKETS, "tlshello"))
updateFragmentLength(MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_LENGTH, "50-100"))
updateFragmentInterval(MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20"))
}
}
private fun updateFragmentPackets(value: String?) {
fragmentPackets?.summary = value.toString()
}
private fun updateFragmentLength(value: String?) {
fragmentLength?.summary = value.toString()
}
private fun updateFragmentInterval(value: String?) {
fragmentInterval?.summary = value.toString()
}
private fun updateHevTunSettings(enabled: Boolean) {
hevTunLogLevel?.isEnabled = enabled
hevTunRwTimeout?.isEnabled = enabled
// if (enabled) {
// updateFragmentPackets(MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_PACKETS, "tlshello"))
// updateFragmentLength(MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_LENGTH, "50-100"))
// updateFragmentInterval(MmkvManager.decodeSettingsString(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20"))
// }
}
//
// private fun updateFragmentPackets(value: String?) {
// fragmentPackets?.summary = value.toString()
// }
//
// private fun updateFragmentLength(value: String?) {
// fragmentLength?.summary = value.toString()
// }
//
// private fun updateFragmentInterval(value: String?) {
// fragmentInterval?.summary = value.toString()
// }
//
// private fun updateHevTunSettings(enabled: Boolean) {
// hevTunLogLevel?.isEnabled = enabled
// hevTunRwTimeout?.isEnabled = enabled
// }
}
fun onModeHelpClicked(view: View) {
@@ -237,9 +237,11 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
val serversCopy = serversCache.toList()
viewModelScope.launch(Dispatchers.Default) {
for (item in serversCopy) {
MessageUtil.sendMsg2TestService(getApplication(), AppConfig.MSG_MEASURE_CONFIG, item.guid)
val guids = ArrayList<String>(serversCopy.map { it.guid })
if (guids.isEmpty()) {
return@launch
}
MessageUtil.sendMsg2TestService(getApplication(), AppConfig.MSG_MEASURE_CONFIG, guids)
}
}