Update On Sat Feb 28 19:45:37 CET 2026

This commit is contained in:
github-action[bot]
2026-02-28 19:45:37 +01:00
parent a8a55f4e68
commit b5662fef38
455 changed files with 46641 additions and 9601 deletions
+1
View File
@@ -1285,3 +1285,4 @@ Update On Tue Feb 24 20:15:23 CET 2026
Update On Wed Feb 25 20:17:35 CET 2026
Update On Thu Feb 26 20:04:04 CET 2026
Update On Fri Feb 27 19:57:47 CET 2026
Update On Sat Feb 28 19:45:29 CET 2026
@@ -18,7 +18,7 @@
"lodash-es": "4.17.23",
"ofetch": "1.5.1",
"react": "19.2.4",
"swr": "2.4.0"
"swr": "2.4.1"
},
"devDependencies": {
"@types/lodash-es": "4.17.12",
@@ -67,7 +67,7 @@
"react-split-grid": "1.0.4",
"react-use": "17.6.0",
"rxjs": "7.8.2",
"swr": "2.4.0",
"swr": "2.4.1",
"virtua": "0.46.6",
"vite-bundle-visualizer": "1.2.1"
},
@@ -75,7 +75,7 @@
"@csstools/normalize.css": "12.1.1",
"@emotion/babel-plugin": "11.13.5",
"@emotion/react": "11.14.0",
"@iconify/json": "2.2.443",
"@iconify/json": "2.2.444",
"@monaco-editor/react": "4.7.0",
"@tanstack/react-query": "5.90.21",
"@tanstack/react-router": "1.161.4",
+2 -2
View File
@@ -2,7 +2,7 @@
"manifest_version": 1,
"latest": {
"mihomo": "v1.19.20",
"mihomo_alpha": "alpha-f6722ab",
"mihomo_alpha": "alpha-3035ae8",
"clash_rs": "v0.9.4",
"clash_premium": "2023-09-05-gdcc8d87",
"clash_rs_alpha": "0.9.4-alpha+sha.ce8c715"
@@ -69,5 +69,5 @@
"linux-armv7hf": "clash-armv7-unknown-linux-gnueabihf"
}
},
"updated_at": "2026-02-25T22:29:27.456Z"
"updated_at": "2026-02-27T22:21:57.836Z"
}
+2 -2
View File
@@ -62,14 +62,14 @@
"@tauri-apps/cli": "2.10.0",
"@types/fs-extra": "11.0.4",
"@types/lodash-es": "4.17.12",
"@types/node": "24.10.15",
"@types/node": "24.11.0",
"autoprefixer": "10.4.27",
"conventional-changelog-conventionalcommits": "9.1.0",
"cross-env": "10.1.0",
"dedent": "1.7.1",
"globals": "17.3.0",
"knip": "5.85.0",
"lint-staged": "16.2.7",
"lint-staged": "16.3.0",
"npm-run-all2": "8.0.4",
"oxlint": "1.50.0",
"postcss": "8.5.6",
+106 -87
View File
@@ -24,7 +24,7 @@ importers:
devDependencies:
'@commitlint/cli':
specifier: 20.4.2
version: 20.4.2(@types/node@24.10.15)(typescript@5.9.3)
version: 20.4.2(@types/node@24.11.0)(typescript@5.9.3)
'@commitlint/config-conventional':
specifier: 20.4.2
version: 20.4.2
@@ -41,8 +41,8 @@ importers:
specifier: 4.17.12
version: 4.17.12
'@types/node':
specifier: 24.10.15
version: 24.10.15
specifier: 24.11.0
version: 24.11.0
autoprefixer:
specifier: 10.4.27
version: 10.4.27(postcss@8.5.6)
@@ -60,10 +60,10 @@ importers:
version: 17.3.0
knip:
specifier: 5.85.0
version: 5.85.0(@types/node@24.10.15)(typescript@5.9.3)
version: 5.85.0(@types/node@24.11.0)(typescript@5.9.3)
lint-staged:
specifier: 16.2.7
version: 16.2.7
specifier: 16.3.0
version: 16.3.0
npm-run-all2:
specifier: 8.0.4
version: 8.0.4
@@ -149,8 +149,8 @@ importers:
specifier: 19.2.4
version: 19.2.4
swr:
specifier: 2.4.0
version: 2.4.0(react@19.2.4)
specifier: 2.4.1
version: 2.4.1(react@19.2.4)
devDependencies:
'@types/lodash-es':
specifier: 4.17.12
@@ -333,8 +333,8 @@ importers:
specifier: 7.8.2
version: 7.8.2
swr:
specifier: 2.4.0
version: 2.4.0(react@19.2.4)
specifier: 2.4.1
version: 2.4.1(react@19.2.4)
virtua:
specifier: 0.46.6
version: 0.46.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(solid-js@1.9.5)
@@ -352,8 +352,8 @@ importers:
specifier: 11.14.0
version: 11.14.0(@types/react@19.2.14)(react@19.2.4)
'@iconify/json':
specifier: 2.2.443
version: 2.2.443
specifier: 2.2.444
version: 2.2.444
'@monaco-editor/react':
specifier: 4.7.0
version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -368,7 +368,7 @@ importers:
version: 1.161.4(@tanstack/react-router@1.161.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.161.4)(csstype@3.2.3)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tanstack/router-plugin':
specifier: 1.161.4
version: 1.161.4(@tanstack/react-router@1.161.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))
version: 1.161.4(@tanstack/react-router@1.161.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))
'@tauri-apps/plugin-clipboard-manager':
specifier: 2.3.2
version: 2.3.2
@@ -404,13 +404,13 @@ importers:
version: 13.15.10
'@vitejs/plugin-legacy':
specifier: 7.2.1
version: 7.2.1(terser@5.36.0)(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))
version: 7.2.1(terser@5.36.0)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))
'@vitejs/plugin-react':
specifier: 5.1.4
version: 5.1.4(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))
version: 5.1.4(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))
'@vitejs/plugin-react-swc':
specifier: 4.2.3
version: 4.2.3(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))
version: 4.2.3(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))
change-case:
specifier: 5.4.4
version: 5.4.4
@@ -449,19 +449,19 @@ importers:
version: 13.15.26
vite:
specifier: 7.3.1
version: 7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)
version: 7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)
vite-plugin-html:
specifier: 3.2.2
version: 3.2.2(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))
version: 3.2.2(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))
vite-plugin-sass-dts:
specifier: 1.3.35
version: 1.3.35(postcss@8.5.6)(prettier@3.8.1)(sass-embedded@1.97.3)(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))
version: 1.3.35(postcss@8.5.6)(prettier@3.8.1)(sass-embedded@1.97.3)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))
vite-plugin-svgr:
specifier: 4.5.0
version: 4.5.0(rollup@4.46.2)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))
version: 4.5.0(rollup@4.46.2)(typescript@5.9.3)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))
vite-tsconfig-paths:
specifier: 6.1.1
version: 6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))
version: 6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))
zod:
specifier: 4.3.6
version: 4.3.6
@@ -497,7 +497,7 @@ importers:
version: 19.2.14
'@vitejs/plugin-react':
specifier: 5.1.4
version: 5.1.4(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))
version: 5.1.4(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))
ahooks:
specifier: 3.9.6
version: 3.9.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -527,10 +527,10 @@ importers:
version: 4.2.1
vite:
specifier: 7.3.1
version: 7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)
version: 7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)
vite-tsconfig-paths:
specifier: 6.1.1
version: 6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))
version: 6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))
devDependencies:
'@emotion/react':
specifier: 11.14.0
@@ -555,7 +555,7 @@ importers:
version: 5.2.0(typescript@5.9.3)
vite-plugin-dts:
specifier: 4.5.4
version: 4.5.4(@types/node@24.10.15)(rollup@4.46.2)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))
version: 4.5.4(@types/node@24.11.0)(rollup@4.46.2)(typescript@5.9.3)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))
scripts:
dependencies:
@@ -1686,8 +1686,8 @@ packages:
prettier-plugin-ember-template-tag:
optional: true
'@iconify/json@2.2.443':
resolution: {integrity: sha512-xyDqw1FeuNWPhYj+Sp2I1kyD6J5U5s8GxEW+dgIY1/Vl0b65t+PlRVCxEBtx73CanfDUPrSEHbxKQJwzXrcV/w==}
'@iconify/json@2.2.444':
resolution: {integrity: sha512-z0UwFaVtaN/h/iWZ1kzEjqFU3sp0rRy93tzOtpepZU89DY39WsNeYZv2mxtft/2La6Bz2b4z1C/HkU5Cqv3gbw==}
'@iconify/types@2.0.0':
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
@@ -3894,8 +3894,8 @@ packages:
'@types/ms@0.7.34':
resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
'@types/node@24.10.15':
resolution: {integrity: sha512-BgjLoRuSr0MTI5wA6gMw9Xy0sFudAaUuvrnjgGx9wZ522fYYLA5SYJ+1Y30vTcJEG+DRCyDHx/gzQVfofYzSdg==}
'@types/node@24.11.0':
resolution: {integrity: sha512-fPxQqz4VTgPI/IQ+lj9r0h+fDR66bzoeMGHp8ASee+32OSGIkeASsoZuJixsQoVef1QJbeubcPBxKk22QVoWdw==}
'@types/parse-json@4.0.2':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
@@ -4490,6 +4490,10 @@ packages:
resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==}
engines: {node: '>=20'}
commander@14.0.3:
resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==}
engines: {node: '>=20'}
commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
@@ -5682,8 +5686,8 @@ packages:
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
lint-staged@16.2.7:
resolution: {integrity: sha512-lDIj4RnYmK7/kXMya+qJsmkRFkGolciXjrsZ6PC25GdTfWOAWetR0ZbsNXRAj1EHHImRSalc+whZFg56F5DVow==}
lint-staged@16.3.0:
resolution: {integrity: sha512-YVHHy/p6U4/No9Af+35JLh3umJ9dPQnGTvNCbfO/T5fC60us0jFnc+vw33cqveI+kqxIFJQakcMVTO2KM+653A==}
engines: {node: '>=20.17'}
hasBin: true
@@ -7079,8 +7083,8 @@ packages:
svg-tags@1.0.0:
resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
swr@2.4.0:
resolution: {integrity: sha512-sUlC20T8EOt1pHmDiqueUWMmRRX03W7w5YxovWX7VR2KHEPCTMly85x05vpkP5i6Bu4h44ePSMD9Tc+G2MItFw==}
swr@2.4.1:
resolution: {integrity: sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA==}
peerDependencies:
react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
@@ -7134,6 +7138,10 @@ packages:
tinyexec@1.0.1:
resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==}
tinyexec@1.0.2:
resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==}
engines: {node: '>=18'}
tinyglobby@0.2.15:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
@@ -7591,6 +7599,11 @@ packages:
engines: {node: '>= 14.6'}
hasBin: true
yaml@2.8.2:
resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==}
engines: {node: '>= 14.6'}
hasBin: true
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
@@ -8485,11 +8498,11 @@ snapshots:
hashery: 1.3.0
keyv: 5.6.0
'@commitlint/cli@20.4.2(@types/node@24.10.15)(typescript@5.9.3)':
'@commitlint/cli@20.4.2(@types/node@24.11.0)(typescript@5.9.3)':
dependencies:
'@commitlint/format': 20.4.0
'@commitlint/lint': 20.4.2
'@commitlint/load': 20.4.0(@types/node@24.10.15)(typescript@5.9.3)
'@commitlint/load': 20.4.0(@types/node@24.11.0)(typescript@5.9.3)
'@commitlint/read': 20.4.0
'@commitlint/types': 20.4.0
tinyexec: 1.0.1
@@ -8536,14 +8549,14 @@ snapshots:
'@commitlint/rules': 20.4.2
'@commitlint/types': 20.4.0
'@commitlint/load@20.4.0(@types/node@24.10.15)(typescript@5.9.3)':
'@commitlint/load@20.4.0(@types/node@24.11.0)(typescript@5.9.3)':
dependencies:
'@commitlint/config-validator': 20.4.0
'@commitlint/execute-rule': 20.0.0
'@commitlint/resolve-extends': 20.4.0
'@commitlint/types': 20.4.0
cosmiconfig: 9.0.0(typescript@5.9.3)
cosmiconfig-typescript-loader: 6.1.0(@types/node@24.10.15)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3)
cosmiconfig-typescript-loader: 6.1.0(@types/node@24.11.0)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3)
is-plain-obj: 4.1.0
lodash.mergewith: 4.6.2
picocolors: 1.1.1
@@ -8894,7 +8907,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@iconify/json@2.2.443':
'@iconify/json@2.2.444':
dependencies:
'@iconify/types': 2.0.0
pathe: 2.0.3
@@ -8989,23 +9002,23 @@ snapshots:
'@material/material-color-utilities@0.4.0': {}
'@microsoft/api-extractor-model@7.30.3(@types/node@24.10.15)':
'@microsoft/api-extractor-model@7.30.3(@types/node@24.11.0)':
dependencies:
'@microsoft/tsdoc': 0.15.1
'@microsoft/tsdoc-config': 0.17.1
'@rushstack/node-core-library': 5.11.0(@types/node@24.10.15)
'@rushstack/node-core-library': 5.11.0(@types/node@24.11.0)
transitivePeerDependencies:
- '@types/node'
'@microsoft/api-extractor@7.51.0(@types/node@24.10.15)':
'@microsoft/api-extractor@7.51.0(@types/node@24.11.0)':
dependencies:
'@microsoft/api-extractor-model': 7.30.3(@types/node@24.10.15)
'@microsoft/api-extractor-model': 7.30.3(@types/node@24.11.0)
'@microsoft/tsdoc': 0.15.1
'@microsoft/tsdoc-config': 0.17.1
'@rushstack/node-core-library': 5.11.0(@types/node@24.10.15)
'@rushstack/node-core-library': 5.11.0(@types/node@24.11.0)
'@rushstack/rig-package': 0.5.3
'@rushstack/terminal': 0.15.0(@types/node@24.10.15)
'@rushstack/ts-command-line': 4.23.5(@types/node@24.10.15)
'@rushstack/terminal': 0.15.0(@types/node@24.11.0)
'@rushstack/ts-command-line': 4.23.5(@types/node@24.11.0)
lodash: 4.17.21
minimatch: 3.0.8
resolve: 1.22.8
@@ -10249,7 +10262,7 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.46.2':
optional: true
'@rushstack/node-core-library@5.11.0(@types/node@24.10.15)':
'@rushstack/node-core-library@5.11.0(@types/node@24.11.0)':
dependencies:
ajv: 8.13.0
ajv-draft-04: 1.0.0(ajv@8.13.0)
@@ -10260,23 +10273,23 @@ snapshots:
resolve: 1.22.8
semver: 7.5.4
optionalDependencies:
'@types/node': 24.10.15
'@types/node': 24.11.0
'@rushstack/rig-package@0.5.3':
dependencies:
resolve: 1.22.8
strip-json-comments: 3.1.1
'@rushstack/terminal@0.15.0(@types/node@24.10.15)':
'@rushstack/terminal@0.15.0(@types/node@24.11.0)':
dependencies:
'@rushstack/node-core-library': 5.11.0(@types/node@24.10.15)
'@rushstack/node-core-library': 5.11.0(@types/node@24.11.0)
supports-color: 8.1.1
optionalDependencies:
'@types/node': 24.10.15
'@types/node': 24.11.0
'@rushstack/ts-command-line@4.23.5(@types/node@24.10.15)':
'@rushstack/ts-command-line@4.23.5(@types/node@24.11.0)':
dependencies:
'@rushstack/terminal': 0.15.0(@types/node@24.10.15)
'@rushstack/terminal': 0.15.0(@types/node@24.11.0)
'@types/argparse': 1.0.38
argparse: 1.0.10
string-argv: 0.3.2
@@ -10609,7 +10622,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@tanstack/router-plugin@1.161.4(@tanstack/react-router@1.161.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))':
'@tanstack/router-plugin@1.161.4(@tanstack/react-router@1.161.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))':
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.29.0)
@@ -10626,7 +10639,7 @@ snapshots:
zod: 3.25.76
optionalDependencies:
'@tanstack/react-router': 1.161.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
vite: 7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)
vite: 7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)
transitivePeerDependencies:
- supports-color
@@ -10766,7 +10779,7 @@ snapshots:
'@types/adm-zip@0.5.7':
dependencies:
'@types/node': 24.10.15
'@types/node': 24.11.0
'@types/argparse@1.0.38': {}
@@ -10927,7 +10940,7 @@ snapshots:
'@types/fs-extra@11.0.4':
dependencies:
'@types/jsonfile': 6.1.4
'@types/node': 24.10.15
'@types/node': 24.11.0
'@types/geojson@7946.0.14': {}
@@ -10943,7 +10956,7 @@ snapshots:
'@types/jsonfile@6.1.4':
dependencies:
'@types/node': 24.10.15
'@types/node': 24.11.0
'@types/lodash-es@4.17.12':
dependencies:
@@ -10957,7 +10970,7 @@ snapshots:
'@types/ms@0.7.34': {}
'@types/node@24.10.15':
'@types/node@24.11.0':
dependencies:
undici-types: 7.16.0
@@ -11209,7 +11222,7 @@ snapshots:
'@ungap/structured-clone@1.2.0': {}
'@vitejs/plugin-legacy@7.2.1(terser@5.36.0)(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))':
'@vitejs/plugin-legacy@7.2.1(terser@5.36.0)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))':
dependencies:
'@babel/core': 7.28.0
'@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.0)
@@ -11224,19 +11237,19 @@ snapshots:
regenerator-runtime: 0.14.1
systemjs: 6.15.1
terser: 5.36.0
vite: 7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)
vite: 7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)
transitivePeerDependencies:
- supports-color
'@vitejs/plugin-react-swc@4.2.3(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))':
'@vitejs/plugin-react-swc@4.2.3(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.2
'@swc/core': 1.15.11
vite: 7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)
vite: 7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)
transitivePeerDependencies:
- '@swc/helpers'
'@vitejs/plugin-react@5.1.4(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1))':
'@vitejs/plugin-react@5.1.4(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2))':
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0)
@@ -11244,7 +11257,7 @@ snapshots:
'@rolldown/pluginutils': 1.0.0-rc.3
'@types/babel__core': 7.20.5
react-refresh: 0.18.0
vite: 7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)
vite: 7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)
transitivePeerDependencies:
- supports-color
@@ -11632,6 +11645,8 @@ snapshots:
commander@14.0.2: {}
commander@14.0.3: {}
commander@2.20.3: {}
commander@7.2.0: {}
@@ -11703,9 +11718,9 @@ snapshots:
core-util-is@1.0.3: {}
cosmiconfig-typescript-loader@6.1.0(@types/node@24.10.15)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3):
cosmiconfig-typescript-loader@6.1.0(@types/node@24.11.0)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3):
dependencies:
'@types/node': 24.10.15
'@types/node': 24.11.0
cosmiconfig: 9.0.0(typescript@5.9.3)
jiti: 2.6.1
typescript: 5.9.3
@@ -12658,10 +12673,10 @@ snapshots:
kind-of@6.0.3: {}
knip@5.85.0(@types/node@24.10.15)(typescript@5.9.3):
knip@5.85.0(@types/node@24.11.0)(typescript@5.9.3):
dependencies:
'@nodelib/fs.walk': 1.2.8
'@types/node': 24.10.15
'@types/node': 24.11.0
fast-glob: 3.3.3
formatly: 0.3.0
jiti: 2.6.1
@@ -12748,15 +12763,15 @@ snapshots:
lines-and-columns@1.2.4: {}
lint-staged@16.2.7:
lint-staged@16.3.0:
dependencies:
commander: 14.0.2
commander: 14.0.3
listr2: 9.0.5
micromatch: 4.0.8
nano-spawn: 2.0.0
pidtree: 0.6.0
string-argv: 0.3.2
yaml: 2.8.1
tinyexec: 1.0.2
yaml: 2.8.2
listr2@9.0.5:
dependencies:
@@ -14299,7 +14314,7 @@ snapshots:
svg-tags@1.0.0: {}
swr@2.4.0(react@19.2.4):
swr@2.4.1(react@19.2.4):
dependencies:
dequal: 2.0.3
react: 19.2.4
@@ -14372,6 +14387,8 @@ snapshots:
tinyexec@1.0.1: {}
tinyexec@1.0.2: {}
tinyglobby@0.2.15:
dependencies:
fdir: 6.5.0(picomatch@4.0.3)
@@ -14653,9 +14670,9 @@ snapshots:
- rollup
- supports-color
vite-plugin-dts@4.5.4(@types/node@24.10.15)(rollup@4.46.2)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)):
vite-plugin-dts@4.5.4(@types/node@24.11.0)(rollup@4.46.2)(typescript@5.9.3)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)):
dependencies:
'@microsoft/api-extractor': 7.51.0(@types/node@24.10.15)
'@microsoft/api-extractor': 7.51.0(@types/node@24.11.0)
'@rollup/pluginutils': 5.1.4(rollup@4.46.2)
'@volar/typescript': 2.4.11
'@vue/language-core': 2.2.0(typescript@5.9.3)
@@ -14666,13 +14683,13 @@ snapshots:
magic-string: 0.30.17
typescript: 5.9.3
optionalDependencies:
vite: 7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)
vite: 7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)
transitivePeerDependencies:
- '@types/node'
- rollup
- supports-color
vite-plugin-html@3.2.2(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)):
vite-plugin-html@3.2.2(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)):
dependencies:
'@rollup/pluginutils': 4.2.1
colorette: 2.0.20
@@ -14686,38 +14703,38 @@ snapshots:
html-minifier-terser: 6.1.0
node-html-parser: 5.4.2
pathe: 0.2.0
vite: 7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)
vite: 7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)
vite-plugin-sass-dts@1.3.35(postcss@8.5.6)(prettier@3.8.1)(sass-embedded@1.97.3)(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)):
vite-plugin-sass-dts@1.3.35(postcss@8.5.6)(prettier@3.8.1)(sass-embedded@1.97.3)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)):
dependencies:
postcss: 8.5.6
postcss-js: 4.0.1(postcss@8.5.6)
prettier: 3.8.1
sass-embedded: 1.97.3
vite: 7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)
vite: 7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)
vite-plugin-svgr@4.5.0(rollup@4.46.2)(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)):
vite-plugin-svgr@4.5.0(rollup@4.46.2)(typescript@5.9.3)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)):
dependencies:
'@rollup/pluginutils': 5.2.0(rollup@4.46.2)
'@svgr/core': 8.1.0(typescript@5.9.3)
'@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.3))
vite: 7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)
vite: 7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)
transitivePeerDependencies:
- rollup
- supports-color
- typescript
vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)):
vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)):
dependencies:
debug: 4.4.3
globrex: 0.1.2
tsconfck: 3.0.3(typescript@5.9.3)
vite: 7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1)
vite: 7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2)
transitivePeerDependencies:
- supports-color
- typescript
vite@7.3.1(@types/node@24.10.15)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.1):
vite@7.3.1(@types/node@24.11.0)(jiti@2.6.1)(less@4.2.0)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(terser@5.36.0)(tsx@4.21.0)(yaml@2.8.2):
dependencies:
esbuild: 0.27.2
fdir: 6.5.0(picomatch@4.0.3)
@@ -14726,7 +14743,7 @@ snapshots:
rollup: 4.46.2
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 24.10.15
'@types/node': 24.11.0
fsevents: 2.3.3
jiti: 2.6.1
less: 4.2.0
@@ -14736,7 +14753,7 @@ snapshots:
stylus: 0.62.0
terser: 5.36.0
tsx: 4.21.0
yaml: 2.8.1
yaml: 2.8.2
void-elements@3.1.0: {}
@@ -14819,6 +14836,8 @@ snapshots:
yaml@2.8.1: {}
yaml@2.8.2: {}
yargs-parser@21.1.1: {}
yargs-parser@22.0.0: {}
+12
View File
@@ -2,6 +2,18 @@
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.61.0](https://github.com/filebrowser/filebrowser/compare/v2.60.0...v2.61.0) (2026-02-28)
### Features
* improved conflict resolution when uploading/copying/moving files ([#5765](https://github.com/filebrowser/filebrowser/issues/5765)) ([aa80909](https://github.com/filebrowser/filebrowser/commit/aa809096eb35fdfbdeb6784b1ebfe2ca1e42f52b))
### Bug Fixes
* correctly clean path ([31194fb](https://github.com/filebrowser/filebrowser/commit/31194fb57a5b92e7155219d7ec7273028fcb2e83))
## [2.60.0](https://github.com/filebrowser/filebrowser/compare/v2.59.0...v2.60.0) (2026-02-21)
+1 -1
View File
@@ -72,5 +72,5 @@
"vite-plugin-compression2": "^2.3.1",
"vue-tsc": "^3.1.3"
},
"packageManager": "pnpm@10.30.1+sha512.3590e550d5384caa39bd5c7c739f72270234b2f6059e13018f975c313b1eb9fefcc09714048765d4d9efe961382c312e624572c0420762bdc5d5940cdf9be73a"
"packageManager": "pnpm@10.30.3+sha512.c961d1e0a2d8e354ecaa5166b822516668b7f44cb5bd95122d590dd81922f606f5473b6d23ec4a5be05e7fcd18e8488d47d978bbe981872f1145d06e9a740017"
}
+355 -172
View File
File diff suppressed because it is too large Load Diff
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "حفظ التغييرات",
"editAsText": "تعديل على شكل نص",
"increaseFontSize": "زيادة حجم الخط",
"decreaseFontSize": "تصغير حجم الخط"
"decreaseFontSize": "تصغير حجم الخط",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "تحميل الملف",
@@ -161,7 +165,17 @@
"uploadMessage": "إختر الملفات التي تريد رفعها.",
"optionalPassword": "كلمة مرور إختيارية",
"resolution": "الدقة",
"discardEditorChanges": "هل تريد بالتأكيد إلغاء التغييرات؟"
"discardEditorChanges": "هل تريد بالتأكيد إلغاء التغييرات؟",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "الصور",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Запиши промените",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Свали файл",
@@ -161,7 +165,17 @@
"uploadMessage": "Изберете опция за качване.",
"optionalPassword": "Опционална парола",
"resolution": "Резолюция",
"discardEditorChanges": "Сигурни ли сте, че искате да откажете направените промени?"
"discardEditorChanges": "Сигурни ли сте, че искате да откажете направените промени?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Изображения",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Descarregar fitxer",
@@ -161,7 +165,17 @@
"uploadMessage": "Seleccioneu una opció per pujar.",
"optionalPassword": "Contrasenya opcional",
"resolution": "Resolució",
"discardEditorChanges": "Esteu segur que voleu descartar els canvis que heu fet?"
"discardEditorChanges": "Esteu segur que voleu descartar els canvis que heu fet?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Imatges",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Stáhnout soubor",
@@ -161,7 +165,17 @@
"uploadMessage": "Vyberte možnost pro nahrání.",
"optionalPassword": "Volitelné heslo",
"resolution": "Rozlišení",
"discardEditorChanges": "Opravdu chcete zrušit provedené změny?"
"discardEditorChanges": "Opravdu chcete zrušit provedené změny?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Obrázky",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Änderungen speichern",
"editAsText": "Als Text bearbeiten",
"increaseFontSize": "Schrift vergrößern",
"decreaseFontSize": "Schrift verkleinern"
"decreaseFontSize": "Schrift verkleinern",
"overrideAll": "Alle Dateien im Zielordner ersetzen",
"skipAll": "Alle in Konflikt stehenden Dateien überspringen",
"renameAll": "Alle Dateien umbenennen (Kopie erstellen)",
"singleDecision": "Bei jeder Datei mit Konflikt entscheiden"
},
"download": {
"downloadFile": "Datei herunterladen",
@@ -161,7 +165,17 @@
"uploadMessage": "Wähle eine Upload-Option.",
"optionalPassword": "Optionales Passwort",
"resolution": "Auflösung",
"discardEditorChanges": "Möchtest du deine Änderungen wirklich verwerfen?"
"discardEditorChanges": "Möchtest du deine Änderungen wirklich verwerfen?",
"replaceOrSkip": "Dateien ersetzen oder überspringen",
"resolveConflict": "Welche Dateien möchtest du behalten?",
"singleConflictResolve": "Wenn du beide Versionen auswählst, wird dem Namen der kopierten Datei eine Nummer hinzugefügt.",
"fastConflictResolve": "Der Zielordner enthält {count} Dateien mit demselben Namen.",
"uploadingFiles": "Dateien werden hochgeladen",
"filesInOrigin": "Dateien im Ursprungsordner",
"filesInDest": "Dateien im Zielordner",
"override": "Überschreiben",
"skip": "Überspringen",
"forbiddenError": "Zugriff verweigert"
},
"search": {
"images": "Bilder",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Λήψη αρχείου",
@@ -161,7 +165,17 @@
"uploadMessage": "Επιλέξτε μια επιλογή για τη μεταφόρτωση.",
"optionalPassword": "Προαιρετικός κωδικός πρόσβασης",
"resolution": "Resolution",
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Εικόνες",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Guardar cambios",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Descargar fichero",
@@ -161,7 +165,17 @@
"uploadMessage": "Seleccione una opción para subir.",
"optionalPassword": "Contraseña opcional",
"resolution": "Resolution",
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Imágenes",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "دانلود فایل",
@@ -161,7 +165,17 @@
"uploadMessage": "یک گزینه برای آپلود انتخاب کنید.",
"optionalPassword": "رمز عبور اختیاری",
"resolution": "وضوح تصویر",
"discardEditorChanges": "آیا مطمئن هستید که می‌خواهید تغییراتی را که ایجاد کرده‌اید، لغو کنید؟"
"discardEditorChanges": "آیا مطمئن هستید که می‌خواهید تغییراتی را که ایجاد کرده‌اید، لغو کنید؟",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "تصاویر",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Enregistrer changements",
"editAsText": "Editer comme Texte",
"increaseFontSize": "Augmenter taille police",
"decreaseFontSize": "Réduire taille police"
"decreaseFontSize": "Réduire taille police",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Télécharger le fichier",
@@ -161,7 +165,17 @@
"uploadMessage": "Sélectionnez une option d'import.",
"optionalPassword": "Mot de passe optionnel",
"resolution": "Résolution",
"discardEditorChanges": "Êtes-vous sûr de vouloir annuler les modifications apportées ?"
"discardEditorChanges": "Êtes-vous sûr de vouloir annuler les modifications apportées ?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Images",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "הורד קובץ",
@@ -161,7 +165,17 @@
"uploadMessage": "בחר אפשרות העלאה.",
"optionalPassword": "סיסמא אופציונלית",
"resolution": "Resolution",
"discardEditorChanges": "האם אתה בטוח שברצונך לבטל את השינויים שביצעת?"
"discardEditorChanges": "האם אתה בטוח שברצונך לבטל את השינויים שביצעת?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "תמונות",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Spremi promjene",
"editAsText": "Uredi kao tekst",
"increaseFontSize": "Povećaj veličinu fonta",
"decreaseFontSize": "Smanji veličinu fonta"
"decreaseFontSize": "Smanji veličinu fonta",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Preuzmi Datoteku",
@@ -161,7 +165,17 @@
"uploadMessage": "Odaberite opciju za prijenos.",
"optionalPassword": "Opcionalna lozinka",
"resolution": "Rezolucija",
"discardEditorChanges": "Jeste li sigurni da želite odbaciti promjene koje ste napravili?"
"discardEditorChanges": "Jeste li sigurni da želite odbaciti promjene koje ste napravili?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Slike",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Fájl letöltése",
@@ -161,7 +165,17 @@
"uploadMessage": "Válasszon egy feltöltési módot.",
"optionalPassword": "Választható jelszó",
"resolution": "Resolution",
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Képek",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Sækja skjal",
@@ -161,7 +165,17 @@
"uploadMessage": "Select an option to upload.",
"optionalPassword": "Optional password",
"resolution": "Resolution",
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Myndir",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Scarica file",
@@ -161,7 +165,17 @@
"uploadMessage": "Seleziona un'opzione per il caricamento.",
"optionalPassword": "Password opzionale",
"resolution": "Risoluzione",
"discardEditorChanges": "Sei sicuro di voler scartare le modifiche apportate?"
"discardEditorChanges": "Sei sicuro di voler scartare le modifiche apportate?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Immagini",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "ファイルのダウンロード",
@@ -161,7 +165,17 @@
"uploadMessage": "アップロードするオプションを選択してください。",
"optionalPassword": "パスワード(オプション)",
"resolution": "Resolution",
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "画像",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "변경사항 저장",
"editAsText": "텍스트로 편집",
"increaseFontSize": "글꼴 크기 증가",
"decreaseFontSize": "글꼴 크기 감소"
"decreaseFontSize": "글꼴 크기 감소",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "파일 다운로드",
@@ -161,7 +165,17 @@
"uploadMessage": "업로드 옵션을 선택하세요.",
"optionalPassword": "비밀번호 (선택)",
"resolution": "해상도",
"discardEditorChanges": "변경 사항을 취소하시겠습니까?"
"discardEditorChanges": "변경 사항을 취소하시겠습니까?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "이미지",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Saglabāt izmaiņas",
"editAsText": "Rediģēt kā tekstu",
"increaseFontSize": "Palieliniet fonta lielumu",
"decreaseFontSize": "Samaziniet fonta lielumu"
"decreaseFontSize": "Samaziniet fonta lielumu",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Lejupielādēt failu",
@@ -161,7 +165,17 @@
"uploadMessage": "Atlasiet augšupielādes opciju.",
"optionalPassword": "Izvēles parole",
"resolution": "Izšķirtspēja",
"discardEditorChanges": "Vai tiešām vēlaties atmest veiktās izmaiņas?"
"discardEditorChanges": "Vai tiešām vēlaties atmest veiktās izmaiņas?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Attēli",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Saglabāt izmaiņas",
"editAsText": "Rediģēt kā tekstu",
"increaseFontSize": "Palieliniet fonta lielumu",
"decreaseFontSize": "Samaziniet fonta lielumu"
"decreaseFontSize": "Samaziniet fonta lielumu",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Lejupielādēt failu",
@@ -161,7 +165,17 @@
"uploadMessage": "Atlasiet augšupielādes opciju.",
"optionalPassword": "Izvēles parole",
"resolution": "Izšķirtspēja",
"discardEditorChanges": "Vai tiešām vēlaties atmest veiktās izmaiņas?"
"discardEditorChanges": "Vai tiešām vēlaties atmest veiktās izmaiņas?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Attēli",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Bestand downloaden",
@@ -161,7 +165,17 @@
"uploadMessage": "Select an option to upload.",
"optionalPassword": "Optional password",
"resolution": "Resolution",
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Afbeeldingen",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Wijzigingen opslaan",
"editAsText": "Als tekst bewerken",
"increaseFontSize": "Lettertype vergroten",
"decreaseFontSize": "Lettertype verkleinen"
"decreaseFontSize": "Lettertype verkleinen",
"overrideAll": "Alle bestanden in doelmap vervangen",
"skipAll": "Alle conflicterende bestanden overslaan",
"renameAll": "Alle bestanden hernoemen (een kopie maken)",
"singleDecision": "Voor elk conflicterend bestand besluiten"
},
"download": {
"downloadFile": "Bestand downloaden",
@@ -161,7 +165,17 @@
"uploadMessage": "Kies een optie bij de upload.",
"optionalPassword": "Optioneel wachtwoord",
"resolution": "Resolutie",
"discardEditorChanges": "Weet u zeker dat u de door u aangebrachte wijzigingen wilt weggooien?"
"discardEditorChanges": "Weet u zeker dat u de door u aangebrachte wijzigingen wilt weggooien?",
"replaceOrSkip": "Bestanden vervangen of overslaan",
"resolveConflict": "Welke bestanden wilt u behouden?",
"singleConflictResolve": "Als u beide versies selecteert, wordt er een nummer toegevoegd aan de naam van het gekopieerde bestand.",
"fastConflictResolve": "De doelmap daar bevat {count} bestanden met dezelfde naam.",
"uploadingFiles": "Bestanden uploaden",
"filesInOrigin": "Bestanden in origineel",
"filesInDest": "Bestanden in bestemming",
"override": "Overschrijven",
"skip": "Overslaan",
"forbiddenError": "Verboden Fout"
},
"search": {
"images": "Afbeeldingen",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Lagre Endringane ",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Nedlast filen",
@@ -161,7 +165,17 @@
"uploadMessage": "Velg et alternativ for opplasting.",
"optionalPassword": "Valgfritt passord",
"resolution": "Oppløysning",
"discardEditorChanges": "Er du sikker på at du vil forkaste endringene du har gjort?"
"discardEditorChanges": "Er du sikker på at du vil forkaste endringene du har gjort?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Bilde",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Zapisz zmiany",
"editAsText": "Edytuj jako tekst",
"increaseFontSize": "Zwiększ rozmiar czcionki",
"decreaseFontSize": "Zmniejsz rozmiar czcionki"
"decreaseFontSize": "Zmniejsz rozmiar czcionki",
"overrideAll": "Zastąp wszystkie pliki w folderze docelowym",
"skipAll": "Pomiń wszystkie pliki powodujące konflikty",
"renameAll": "Zmień nazwy wszystkich plików (utwórz kopię)",
"singleDecision": "Podejmij decyzję dla każdego pliku powodującego konflikt"
},
"download": {
"downloadFile": "Pobierz plik",
@@ -161,7 +165,17 @@
"uploadMessage": "Wybierz opcję przesyłania.",
"optionalPassword": "Opcjonalne hasło",
"resolution": "Rozdzielczość",
"discardEditorChanges": "Czy na pewno chcesz odrzucić wprowadzone zmiany?"
"discardEditorChanges": "Czy na pewno chcesz odrzucić wprowadzone zmiany?",
"replaceOrSkip": "Zastąp lub pomiń pliki",
"resolveConflict": "Które pliki chcesz zachować?",
"singleConflictResolve": "Jeśli wybierzesz obie wersje, do nazwy kopiowanego pliku zostanie dodany numer.",
"fastConflictResolve": "W folderze docelowym liczba plików o tej samej nazwie to {count}.",
"uploadingFiles": "Wysyłanie plików",
"filesInOrigin": "Pliki w miejscu pochodzenia",
"filesInDest": "Pliki w miejscu docelowym",
"override": "Zastąp",
"skip": "Pomiń",
"forbiddenError": "Błąd zabronionego dostępu"
},
"search": {
"images": "Obrazy",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Baixar arquivo",
@@ -161,7 +165,17 @@
"uploadMessage": "Selecione uma opção para enviar.",
"optionalPassword": "Senha opcional",
"resolution": "Resolution",
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Imagens",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Descarregar ficheiro",
@@ -161,7 +165,17 @@
"uploadMessage": "Select an option to upload.",
"optionalPassword": "Optional password",
"resolution": "Resolution",
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Imagens",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Descarcă fișier",
@@ -161,7 +165,17 @@
"uploadMessage": "Select an option to upload.",
"optionalPassword": "Optional password",
"resolution": "Resolution",
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Imagini",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Сохранить",
"editAsText": "Редактировать как текст",
"increaseFontSize": "Увеличить размер шрифта",
"decreaseFontSize": "Уменьшить размер шрифта"
"decreaseFontSize": "Уменьшить размер шрифта",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Скачать файл",
@@ -161,7 +165,17 @@
"uploadMessage": "Выберите вариант для загрузки.",
"optionalPassword": "Необязательный пароль",
"resolution": "Разрешение",
"discardEditorChanges": "Вы действительно желаете отменить ваши правки?"
"discardEditorChanges": "Вы действительно желаете отменить ваши правки?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Изображения",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Uložiť zmeny",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Stiahnuť súbor",
@@ -161,7 +165,17 @@
"uploadMessage": "Zvoľte možnosť nahrávania.",
"optionalPassword": "Voliteľné heslo",
"resolution": "Rozlíšenie",
"discardEditorChanges": "Naozaj chcete zahodiť vykonané zmeny?"
"discardEditorChanges": "Naozaj chcete zahodiť vykonané zmeny?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Obrázky",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Spara ändringar",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Ladda ner fil",
@@ -161,7 +165,17 @@
"uploadMessage": "Välj ett alternativ att ladda upp.",
"optionalPassword": "Valfritt lösenord",
"resolution": "Upplösning",
"discardEditorChanges": "Är du säker på att du vill förkasta ändringarna du gjort?"
"discardEditorChanges": "Är du säker på att du vill förkasta ändringarna du gjort?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Bilder",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Dosyayı indir",
@@ -161,7 +165,17 @@
"uploadMessage": "Yüklemek için bir seçenek belirleyin.",
"optionalPassword": "İsteğe bağlı şifre",
"resolution": "Resolution",
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?"
"discardEditorChanges": "Are you sure you wish to discard the changes you've made?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Görseller",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Завантажити файл",
@@ -161,7 +165,17 @@
"uploadMessage": "Виберіть варіант для вивантаження.",
"optionalPassword": "Необов'язковий пароль",
"resolution": "Розширення",
"discardEditorChanges": "Чи дійсно ви хочете скасувати поточні зміни?"
"discardEditorChanges": "Чи дійсно ви хочете скасувати поточні зміни?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Зображення",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "Tải xuống tệp tin",
@@ -161,7 +165,17 @@
"uploadMessage": "Chọn một tùy chọn để tải lên.",
"optionalPassword": "Mật khẩu tùy chọn",
"resolution": "Độ phân giải",
"discardEditorChanges": "Bạn có chắc chắn muốn hủy bỏ các thay đổi đã thực hiện không?"
"discardEditorChanges": "Bạn có chắc chắn muốn hủy bỏ các thay đổi đã thực hiện không?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "Hình ảnh",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "保存更改",
"editAsText": "以文本形式编辑",
"increaseFontSize": "增大字体大小",
"decreaseFontSize": "减小字体大小"
"decreaseFontSize": "减小字体大小",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "下载文件",
@@ -161,7 +165,17 @@
"uploadMessage": "选择上传选项。",
"optionalPassword": "密码(选填,不填即无密码)",
"resolution": "分辨率",
"discardEditorChanges": "你确定要放弃所做的更改吗?"
"discardEditorChanges": "你确定要放弃所做的更改吗?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "图像",
+16 -2
View File
@@ -48,7 +48,11 @@
"saveChanges": "Save changes",
"editAsText": "Edit as Text",
"increaseFontSize": "Increase font size",
"decreaseFontSize": "Decrease font size"
"decreaseFontSize": "Decrease font size",
"overrideAll": "Replace all files in destination folder",
"skipAll": "Skip all conflicting files",
"renameAll": "Rename all files (create a copy)",
"singleDecision": "Decide for each conflicting file"
},
"download": {
"downloadFile": "下載檔案",
@@ -161,7 +165,17 @@
"uploadMessage": "選擇上傳項。",
"optionalPassword": "密碼(選填,不填即無密碼)",
"resolution": "解析度",
"discardEditorChanges": "你確定要放棄所做的變更嗎?"
"discardEditorChanges": "你確定要放棄所做的變更嗎?",
"replaceOrSkip": "Replace or skip files",
"resolveConflict": "Which files do you want to keep?",
"singleConflictResolve": "If you select both versions, a number will be added to the name of the copied file.",
"fastConflictResolve": "The destination folder there are {count} files with same name.",
"uploadingFiles": "Uploading files",
"filesInOrigin": "Files in origin",
"filesInDest": "Files in destination",
"override": "Overwrite",
"skip": "Skip",
"forbiddenError": "Forbidden Error"
},
"search": {
"images": "影像",
+1 -1
View File
@@ -4,7 +4,7 @@ go 1.25
require (
github.com/asdine/storm/v3 v3.2.1
github.com/asticode/go-astisub v0.38.0
github.com/asticode/go-astisub v0.39.0
github.com/disintegration/imaging v1.6.2
github.com/dsoprea/go-exif/v3 v3.0.1
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568
+2 -2
View File
@@ -31,8 +31,8 @@ github.com/asticode/go-astikit v0.20.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xbl
github.com/asticode/go-astikit v0.30.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
github.com/asticode/go-astikit v0.56.0 h1:DmD2p7YnvxiPdF0h+dRmos3bsejNEXbycENsY5JfBqw=
github.com/asticode/go-astikit v0.56.0/go.mod h1:fV43j20UZYfXzP9oBn33udkvCvDvCDhzjVqoLFuuYZE=
github.com/asticode/go-astisub v0.38.0 h1:Qh3IO8Cotn0wwok5maid7xqsIJTwn2DtABT1UajKJaI=
github.com/asticode/go-astisub v0.38.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8=
github.com/asticode/go-astisub v0.39.0 h1:j1/rFLRUH0TT2CW9YCtBek9lRdMp96oxaZm6vbgE96M=
github.com/asticode/go-astisub v0.39.0/go.mod h1:WTkuSzFB+Bp7wezuSf2Oxulj5A8zu2zLRVFf6bIFQK8=
github.com/asticode/go-astits v1.8.0/go.mod h1:DkOWmBNQpnr9mv24KfZjq4JawCFX1FCqjLVGvO0DygQ=
github.com/asticode/go-astits v1.13.0 h1:XOgkaadfZODnyZRR5Y0/DWkA9vrkLLPLeeOvDwfKZ1c=
github.com/asticode/go-astits v1.13.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI=
+1 -1
View File
@@ -56,7 +56,7 @@ var withHashFile = func(fn handleFunc) handleFunc {
filePath := ""
if file.IsDir {
basePath = filepath.Dir(basePath)
basePath = filepath.Clean(link.Path)
filePath = ifPath
}
@@ -46,7 +46,8 @@ openmesh,a42|\
openmesh,a62|\
pakedge,wr-1|\
plasmacloud,pa1200|\
plasmacloud,pa2200)
plasmacloud,pa2200|\
thinkplus,fogpod800)
ubootenv_add_uci_config "/dev/mtd5" "0x0" "0x10000" "0x10000"
;;
aruba,ap-303)
+2
View File
@@ -64,6 +64,7 @@ ALLWIFIBOARDS:= \
qnap_301w \
redmi_ax5-jdcloud \
redmi_ax6 \
thinkplus_fogpod800 \
wallys_dr40x9 \
xiaomi_ax3600 \
xiaomi_ax6000 \
@@ -199,6 +200,7 @@ $(eval $(call generate-ipq-wifi-package,qnap_301w,QNAP 301w))
$(eval $(call generate-ipq-wifi-package,prpl_haze,prpl Haze))
$(eval $(call generate-ipq-wifi-package,redmi_ax5-jdcloud,Redmi AX5 JDCloud))
$(eval $(call generate-ipq-wifi-package,redmi_ax6,Redmi AX6))
$(eval $(call generate-ipq-wifi-package,thinkplus_fogpod800,ThinkPlus FogPOD800))
$(eval $(call generate-ipq-wifi-package,wallys_dr40x9,Wallys DR40X9))
$(eval $(call generate-ipq-wifi-package,xiaomi_ax3600,Xiaomi AX3600))
$(eval $(call generate-ipq-wifi-package,xiaomi_ax6000,Xiaomi AX6000))
@@ -22,6 +22,16 @@ ipq40xx_setup_interfaces()
plasmacloud,pa2200)
ucidef_set_interfaces_lan_wan "eth0" "eth1"
;;
alibaba,ap4220-48m|\
alibaba,ap4220-128m|\
asus,map-ac2200|\
cilab,meshpoint-one|\
edgecore,ecw5211|\
edgecore,oap100|\
openmesh,a42|\
openmesh,a62)
ucidef_set_interfaces_lan_wan "eth1" "eth0"
;;
aruba,ap-303|\
aruba,ap-365|\
avm,fritzrepeater-1200|\
@@ -42,20 +52,6 @@ ipq40xx_setup_interfaces()
ucidef_add_switch "switch0" \
"0u@eth0" "2:lan:1" "3:lan:2" "4:lan:3" "0u@eth1" "5:wan"
;;
alibaba,ap4220-48m|\
alibaba,ap4220-128m|\
asus,map-ac2200|\
cilab,meshpoint-one|\
edgecore,ecw5211|\
edgecore,oap100|\
openmesh,a42|\
openmesh,a62)
ucidef_set_interfaces_lan_wan "eth1" "eth0"
;;
mikrotik,cap-ac)
ucidef_add_switch "switch0" \
"0t@eth0" "4:lan" "5:wan"
;;
asus,rt-ac42u|\
asus,rt-ac58u|\
mikrotik,hap-ac2|\
@@ -70,16 +66,12 @@ ipq40xx_setup_interfaces()
avm,fritzbox-4040|\
linksys,ea6350v3|\
linksys,ea8300|\
thinkplus,fogpod800|\
yyets,le1)
ucidef_set_interfaces_lan_wan "eth0" "eth1"
ucidef_add_switch "switch0" \
"0u@eth0" "1:lan" "2:lan" "3:lan" "4:lan"
;;
linksys,mr8300)
ucidef_set_interfaces_lan_wan "eth0" "eth1"
ucidef_add_switch "switch0" \
"0u@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "0u@eth1" "5:wan"
;;
avm,fritzbox-7530)
ucidef_add_switch "switch0" \
"0u@eth0" "1:lan:4" "2:lan:3" "3:lan:2" "4:lan:1"
@@ -88,11 +80,6 @@ ipq40xx_setup_interfaces()
ucidef_add_switch "switch0" \
"0u@eth0" "4:lan:1" "5:lan:2"
;;
compex,wpj419|\
compex,wpj428|\
engenius,eap2200)
ucidef_set_interface_lan "eth0 eth1"
;;
buffalo,wtr-m2133hp)
ucidef_set_interfaces_lan_wan "eth0" "eth1"
ucidef_add_switch "switch0" \
@@ -103,6 +90,11 @@ ipq40xx_setup_interfaces()
ucidef_add_switch "switch0" \
"0u@eth0" "3:lan" "4:lan"
;;
compex,wpj419|\
compex,wpj428|\
engenius,eap2200)
ucidef_set_interface_lan "eth0 eth1"
;;
devolo,magic-2-wifi-next)
ucidef_set_interface_lan "eth0 eth1 eth2"
;;
@@ -123,6 +115,15 @@ ipq40xx_setup_interfaces()
ucidef_add_switch "switch0" \
"0u@eth0" "1:lan" "2:lan" "3:lan" "5:lan" "0u@eth1" "4:wan"
;;
linksys,mr8300)
ucidef_set_interfaces_lan_wan "eth0" "eth1"
ucidef_add_switch "switch0" \
"0u@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "0u@eth1" "5:wan"
;;
mikrotik,cap-ac)
ucidef_add_switch "switch0" \
"0t@eth0" "4:lan" "5:wan"
;;
mobipromo,cm520-79f)
ucidef_add_switch "switch0" \
"0u@eth0" "3:lan:2" "4:lan:1"
@@ -136,14 +137,14 @@ ipq40xx_setup_interfaces()
"0u@eth0" "2:lan" "3:lan" "4:lan"
ucidef_set_interface_wan "eth1"
;;
qxwlan,e2600ac-c1 |\
qxwlan,e2600ac-c1|\
qxwlan,e2600ac-c2)
ucidef_set_interfaces_lan_wan "eth0" "eth1"
ucidef_add_switch "switch0" \
"0u@eth0" "3:lan" "4:lan" "0u@eth1" "5:wan"
;;
unielec,u4019-32m |\
tel,x1pro)
tel,x1pro|\
unielec,u4019-32m)
ucidef_set_interfaces_lan_wan "eth0" "eth1"
ucidef_add_switch "switch0" \
"0u@eth0" "1:lan" "2:lan" "3:lan" "4:lan" "0u@eth1" "5:wan"
@@ -172,15 +173,15 @@ ipq40xx_setup_macs()
local label_mac=""
case "$board" in
8dev,habanero-dvk)
label_mac=$(mtd_get_mac_binary "ART" 0x1006)
;;
alibaba,ap4220-48m|\
alibaba,ap4220-128m)
wan_mac=$(mtd_get_mac_text product_info 0x40)
lan_mac=$(macaddr_add "$wan_mac" 1)
label_mac="$wan_mac"
;;
8dev,habanero-dvk)
label_mac=$(mtd_get_mac_binary "ART" 0x1006)
;;
asus,rt-ac58u)
wan_mac=$(mtd_get_mac_binary_ubi Factory 0x1006)
lan_mac=$(mtd_get_mac_binary_ubi Factory 0x5006)
@@ -230,6 +231,11 @@ ipq40xx_setup_macs()
lan_mac=$(cat /sys/firmware/mikrotik/hard_config/mac_base)
label_mac="$lan_mac"
;;
thinkplus,fogpod800)
wan_mac=$(mtd_get_mac_binary "0:ART" 0x0)
lan_mac=$(mtd_get_mac_binary "0:ART" 0x6)
label_mac=$wan_mac
;;
esac
[ -n "$lan_mac" ] && ucidef_set_interface_macaddr "lan" $lan_mac
@@ -234,7 +234,8 @@ platform_do_upgrade() {
netgear,wac510 |\
p2w,r619ac-64m |\
p2w,r619ac-128m |\
qxwlan,e2600ac-c2)
qxwlan,e2600ac-c2 |\
thinkplus,fogpod800)
nand_do_upgrade "$1"
;;
glinet,gl-b2200)
@@ -0,0 +1,315 @@
// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
#include "qcom-ipq4019.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/soc/qcom,tcsr.h>
/ {
model = "ThinkPlus FogPOD800";
compatible = "thinkplus,fogpod800";
aliases {
led-boot = &led_power;
led-failsafe = &led_power;
led-running = &led_power;
led-upgrade = &led_power;
};
chosen {
bootargs-append = " ubi.mtd=rootfs root=/dev/ubiblock0_1";
};
soc {
rng@22000 {
status = "okay";
};
mdio@90000 {
status = "okay";
};
ess-psgmii@98000 {
status = "okay";
};
tcsr@1949000 {
compatible = "qcom,tcsr";
reg = <0x1949000 0x100>;
qcom,wifi_glb_cfg = <TCSR_WIFI_GLB_CFG>;
};
tcsr@194b000 {
compatible = "qcom,tcsr";
reg = <0x194b000 0x100>;
qcom,usb-hsphy-mode-select = <TCSR_USB_HSPHY_HOST_MODE>;
};
ess_tcsr@1953000 {
compatible = "qcom,tcsr";
reg = <0x1953000 0x1000>;
qcom,ess-interface-select = <TCSR_ESS_PSGMII>;
};
tcsr@1957000 {
compatible = "qcom,tcsr";
reg = <0x1957000 0x100>;
qcom,wifi_noc_memtype_m0_m2 = <TCSR_WIFI_NOC_MEMTYPE_M0_M2>;
};
usb2@60f8800 {
status = "okay";
dwc3@6000000 {
#address-cells = <1>;
#size-cells = <0>;
usb2_port1: port@1 {
reg = <1>;
#trigger-source-cells = <0>;
};
};
};
usb3@8af8800 {
status = "okay";
dwc3@8a00000 {
#address-cells = <1>;
#size-cells = <0>;
usb3_port1: port@1 {
reg = <1>;
#trigger-source-cells = <0>;
};
usb3_port2: port@2 {
reg = <2>;
#trigger-source-cells = <0>;
};
};
};
crypto@8e3a000 {
status = "okay";
};
watchdog@b017000 {
status = "okay";
};
ess-switch@c000000 {
status = "okay";
};
edma@c080000 {
status = "okay";
};
};
keys {
compatible = "gpio-keys";
reset {
label = "reset";
gpios = <&tlmm 63 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};
};
leds {
compatible = "gpio-leds";
led_power: status {
label = "red:status";
gpios = <&tlmm 2 GPIO_ACTIVE_LOW>;
};
wan {
label = "green:wan";
gpios = <&tlmm 3 GPIO_ACTIVE_LOW>;
linux,default-trigger = "90000.mdio-1:04:link";
};
wlan5g {
label = "blue:wlan5g";
gpios = <&tlmm 4 GPIO_ACTIVE_LOW>;
linux,default-trigger = "phy1tpt";
};
usb {
label = "blue:usb";
gpios = <&tlmm 5 GPIO_ACTIVE_LOW>;
linux,default-trigger = "usbport";
trigger-sources = <&usb2_port1>, <&usb3_port1>, <&usb3_port2>;
};
wlan2g {
label = "blue:wlan2g";
gpios = <&tlmm 58 GPIO_ACTIVE_LOW>;
linux,default-trigger = "phy0tpt";
};
};
};
&cryptobam {
status = "okay";
};
&blsp_dma {
status = "okay";
};
&tlmm {
serial0_pins: serial0_pinmux {
mux {
pins = "gpio60", "gpio61";
function = "blsp_uart0";
bias-disable;
};
};
spi0_pins: spi0_pinmux {
mux {
function = "blsp_spi0";
pins = "gpio55", "gpio56", "gpio57";
drive-strength = <12>;
bias-disable;
};
mux_cs {
function = "gpio";
pins = "gpio54", "gpio59";
drive-strength = <2>;
bias-disable;
output-high;
};
};
};
&blsp1_spi1 {
status = "okay";
pinctrl-0 = <&spi0_pins>;
pinctrl-names = "default";
cs-gpios = <&tlmm 54 GPIO_ACTIVE_HIGH>, <&tlmm 59 GPIO_ACTIVE_HIGH>;
flash@0 {
compatible = "jedec,spi-nor";
reg = <0>;
spi-max-frequency = <24000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "0:SBL1";
reg = <0x0 0x40000>;
read-only;
};
partition@40000 {
label = "0:MIBIB";
reg = <0x40000 0x20000>;
read-only;
};
partition@60000 {
label = "0:QSEE";
reg = <0x60000 0x60000>;
read-only;
};
partition@c0000 {
label = "0:CDT";
reg = <0xc0000 0x10000>;
read-only;
};
partition@d0000 {
label = "0:DDRPARAMS";
reg = <0xd0000 0x10000>;
read-only;
};
partition@e0000 {
label = "0:APPSBLENV";
reg = <0xe0000 0x10000>;
};
partition@f0000 {
label = "0:APPSBL";
reg = <0xf0000 0x80000>;
read-only;
};
partition@170000 {
label = "0:ART";
reg = <0x170000 0x10000>;
read-only;
compatible = "nvmem-cells";
#address-cells = <1>;
#size-cells = <1>;
precal_art_1000: precal@1000 {
reg = <0x1000 0x2f20>;
};
precal_art_5000: precal@5000 {
reg = <0x5000 0x2f20>;
};
};
};
};
nand@1 {
compatible = "spi-nand";
reg = <1>;
spi-max-frequency = <24000000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "rootfs";
reg = <0x0 0x8000000>;
};
};
};
};
&blsp1_uart1 {
pinctrl-0 = <&serial0_pins>;
pinctrl-names = "default";
status = "okay";
};
&usb2_hs_phy {
status = "okay";
};
&usb3_ss_phy {
status = "okay";
};
&usb3_hs_phy {
status = "okay";
};
&wifi0 {
status = "okay";
nvmem-cell-names = "pre-calibration";
nvmem-cells = <&precal_art_1000>;
qcom,ath10k-calibration-variant = "ThinkPlus FogPOD800";
};
&wifi1 {
status = "okay";
nvmem-cell-names = "pre-calibration";
nvmem-cells = <&precal_art_5000>;
qcom,ath10k-calibration-variant = "ThinkPlus FogPOD800";
};
@@ -997,6 +997,20 @@ define Device/tel_x1pro
endef
TARGET_DEVICES += tel_x1pro
define Device/thinkplus_fogpod800
$(call Device/FitImage)
$(call Device/UbiFit)
DEVICE_VENDOR := ThinkPlus
DEVICE_MODEL := FogPOD800
SOC := qcom-ipq4028
KERNEL_SIZE := 4096k
DEVICE_DTS_CONFIG := config@ap.dk01.1-c2
BLOCKSIZE := 128k
PAGESIZE := 2048
DEVICE_PACKAGES := kmod-usb-ledtrig-usbport ipq-wifi-thinkplus_fogpod800
endef
TARGET_DEVICES += thinkplus_fogpod800
define Device/unielec_u4019-32m
$(call Device/FitImage)
DEVICE_VENDOR := Unielec
@@ -10,7 +10,7 @@ Signed-off-by: John Crispin <john@phrozen.org>
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -904,11 +904,79 @@ dtb-$(CONFIG_ARCH_QCOM) += \
@@ -904,11 +904,80 @@ dtb-$(CONFIG_ARCH_QCOM) += \
qcom-apq8074-dragonboard.dtb \
qcom-apq8084-ifc6540.dtb \
qcom-apq8084-mtp.dtb \
@@ -79,6 +79,7 @@ Signed-off-by: John Crispin <john@phrozen.org>
+ qcom-ipq4019-u4019-32m.dtb \
+ qcom-ipq4019-wpj419.dtb \
+ qcom-ipq4019-wtr-m2133hp.dtb \
+ qcom-ipq4028-fogpod800.dtb \
+ qcom-ipq4028-wpj428.dtb \
+ qcom-ipq4029-ap-303.dtb \
+ qcom-ipq4029-ap-303h.dtb \
+10
View File
@@ -265,6 +265,16 @@ func TestEncodeDecode(t *testing.T) {
}
}
func TestDecodeEmptyString(t *testing.T) {
trafficPattern, err := Decode("")
if err != nil {
t.Fatalf("Failed to decode empty string: %v", err)
}
if trafficPattern == nil {
t.Errorf("Returned nil traffic pattern")
}
}
func TestValidate(t *testing.T) {
cases := []struct {
name string
+16
View File
@@ -136,6 +136,18 @@ func RegisterClientCommands() {
},
clientExportTrafficPatternFunc,
)
RegisterCallback(
[]string{"", "explain", "traffic-pattern"},
func(s []string) error {
if len(s) < 4 {
return fmt.Errorf("usage: mieru explain traffic-pattern <STRING>. No string is provided")
} else if len(s) > 4 {
return fmt.Errorf("usage: mieru explain traffic-pattern <STRING>. More than 1 string is provided")
}
return nil
},
explainTrafficPatternFunc,
)
RegisterCallback(
[]string{"", "import", "config"},
func(s []string) error {
@@ -329,6 +341,10 @@ var clientHelpFunc = func(s []string) error {
cmd: "export traffic-pattern",
help: []string{"Export traffic pattern as an encoded base64 string."},
},
{
cmd: "explain traffic-pattern <STRING>",
help: []string{"Decode and explain a traffic pattern from an encoded base64 string."},
},
{
cmd: "delete profile <PROFILE_NAME>",
help: []string{"Delete an inactive client configuration profile."},
+16
View File
@@ -125,6 +125,18 @@ func RegisterServerCommands() {
},
serverExportTrafficPatternFunc,
)
RegisterCallback(
[]string{"", "explain", "traffic-pattern"},
func(s []string) error {
if len(s) < 4 {
return fmt.Errorf("usage: mita explain traffic-pattern <STRING>. No string is provided")
} else if len(s) > 4 {
return fmt.Errorf("usage: mita explain traffic-pattern <STRING>. More than 1 string is provided")
}
return nil
},
explainTrafficPatternFunc,
)
RegisterCallback(
[]string{"", "delete", "user"},
func(s []string) error {
@@ -274,6 +286,10 @@ var serverHelpFunc = func(s []string) error {
cmd: "export traffic-pattern",
help: []string{"Export traffic pattern as an encoded base64 string."},
},
{
cmd: "explain traffic-pattern <STRING>",
help: []string{"Decode and explain a traffic pattern from an encoded base64 string."},
},
{
cmd: "delete user <USER_NAME>",
help: []string{"Delete a user from server configuration."},
+15
View File
@@ -21,7 +21,9 @@ import (
"strings"
"time"
"github.com/enfein/mieru/v3/apis/trafficpattern"
"github.com/enfein/mieru/v3/pkg/appctl/appctlpb"
"github.com/enfein/mieru/v3/pkg/common"
"github.com/enfein/mieru/v3/pkg/log"
"github.com/enfein/mieru/v3/pkg/mathext"
"github.com/enfein/mieru/v3/pkg/version"
@@ -41,6 +43,19 @@ var describeBuildFunc = func(_ []string) error {
return nil
}
var explainTrafficPatternFunc = func(s []string) error {
pattern, err := trafficpattern.Decode(s[3])
if err != nil {
return err
}
jsonBytes, err := common.MarshalJSON(pattern)
if err != nil {
return fmt.Errorf("common.MarshalJSON() failed: %w", err)
}
log.Infof("%s", string(jsonBytes))
return nil
}
func printSessionInfoList(info *appctlpb.SessionInfoList) {
header := []string{
"SessionID",
@@ -38,8 +38,10 @@ echo "mieru server config:"
./mita describe config
echo "mieru server effective traffic pattern:"
./mita describe effective-traffic-pattern
encoded_server_traffic_pattern=$(./mita export traffic-pattern)
echo "mieru server encoded traffic pattern:"
./mita export traffic-pattern
echo ${encoded_server_traffic_pattern}
./mita explain traffic-pattern $(echo ${encoded_server_traffic_pattern})
sleep 1
# Start mieru server proxy.
@@ -76,8 +78,10 @@ echo "mieru client config after import:"
./mieru describe config
echo "mieru client effective traffic pattern:"
./mieru describe effective-traffic-pattern
encoded_client_traffic_pattern=$(./mieru export traffic-pattern)
echo "mieru client encoded traffic pattern:"
./mieru export traffic-pattern
echo ${encoded_client_traffic_pattern}
./mieru explain traffic-pattern $(echo ${encoded_client_traffic_pattern})
sleep 1
# Start mieru client.
+2 -2
View File
@@ -5,12 +5,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=filebrowser
PKG_VERSION:=2.60.0
PKG_VERSION:=2.61.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:=6ab1f5bfb68f13799e58db304361d8bbf7d2a42e893f4c1873cb6c1688912df0
PKG_HASH:=9c85502cbb28b3812aeec921fb8fe51694d5a837aa4415c026c327522492ba05
PKG_LICENSE:=Apache-2.0
PKG_LICENSE_FILES:=LICENSE
+2 -2
View File
@@ -7,8 +7,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-ddns-go
PKG_VERSION:=1.6.5
PKG_RELEASE:=20260123
PKG_VERSION:=1.6.6
PKG_RELEASE:=20260228
PKG_MAINTAINER:=sirpdboy <herboy2008@gmail.com>
PKG_CONFIG_DEPENDS:=
@@ -113,29 +113,30 @@ return view.extend({
uci.load('ddns-go')
]);
},
handleResetPassword: async function () {
try {
ui.showModal(_('Resetting Password'), [
E('p', { 'class': 'spinning' }, _('Resetting admin password, please wait...'))
E('p', { 'class': 'spinning' }, _('Resetting admin username and password, please wait...'))
]);
const result = await fs.exec('/usr/bin/ddns-go', ['-resetPassword', 'admin12345', '-c', '/etc/ddns-go/ddns-go-config.yaml']);
const configFile = '/etc/ddns-go/ddns-go-config.yaml';
const readResult = await fs.read(configFile);
if (readResult && readResult.trim() !== '') {
let configContent = readResult;
configContent = configContent.replace(/(username:\s*).*/g, '$1admin');
if (!configContent.includes('user:')) {
configContent += '\nuser:\n username: admin\n password: $2a$10$G1xO1cVUYtSpPYwV/Jk3l.u7PxLUxo03wntWG6VA9BxAftNWfZEhK';
}
await fs.write(configFile, configContent);
}
ui.hideModal();
const output = (result.stdout + result.stderr).trim();
let success = false;
let message = '';
if (result.code === 0) {
message = _('Password reset successfully to admin12345');
ui.showModal(_('Password Reset Successful'), [
E('p', _('Reset User:admin ,Reset password: admin12345')),
ui.showModal(_('Username and Password Reset Successful'), [
E('p', _('Username: admin, Password: admin12345')),
E('p', _('You need to restart DDNS-Go service for the changes to take effect.')),
E('div', { 'class': 'right' }, [
E('button', {
@@ -153,15 +154,33 @@ return view.extend({
])
]);
} else {
alert(_('Reset may have failed:') + '\n' + output);
ui.showModal(_('Partial Reset'), [
E('p', _('DDNS-Go command reset may have failed, but configuration file has been updated.')),
E('p', _('Username: admin, Password: admin12345')),
E('p', _('You may need to restart DDNS-Go service manually.')),
E('div', { 'class': 'right' }, [
E('button', {
'class': 'btn cbi-button cbi-button-positive',
'click': ui.createHandlerFn(this, function() {
ui.hideModal();
this.handleRestartService();
})
}, _('Restart Service Now')),
' ',
E('button', {
'class': 'btn cbi-button cbi-button-neutral',
'click': ui.hideModal
}, _('Close'))
])
]);
}
} catch (error) {
ui.hideModal();
console.error('Reset password failed:', error);
alert(_('ERROR:') + '\n' + _('Reset password failed:') + '\n' + error.message);
//console.error('Reset username/password failed:', error);
alert(_('ERROR:') + '\n' + _('Resetusername/ password failed:') + '\n' + error.message);
}
},
},
handleRestartService: async function() {
try {
@@ -310,7 +329,7 @@ return view.extend({
o.default = '60';
o = s.option(form.Button, '_newpassword', _('Reset account password'));
o.inputtitle = _('ResetPassword');
o.inputtitle = _('Reset');
o.inputstyle = 'apply';
o.onclick = L.bind(this.handleResetPassword, this, data);
@@ -1,4 +1,4 @@
/* Copyright (C) 2021-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-ddns-go */
/* Copyright (C) 2021-2026 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-ddns-go */
'use strict';
'require view';
@@ -95,7 +95,7 @@ msgid "Update status unknown"
msgstr "更新状态未知"
msgid "Reset account password"
msgstr "重置登录密码"
msgstr "重置帐号密码"
msgid "ResetPassword"
msgstr "重置密码"
@@ -103,18 +103,33 @@ msgstr "重置密码"
msgid "SUCCESS:"
msgstr "完成执行:"
msgid "Password reset successfully to admin12345"
msgstr "成功重置密码admin12345"
msgid "Resetting admin username and password, please wait...5"
msgstr "重置用户名与密码,请稍等"
msgid "Password Reset Successful"
msgstr "重置密码成功"
msgid "Username and Password Reset Successful"
msgstr "用户名和密码重置成功"
msgid "Reset User:admin ,Reset password: admin12345"
msgid "Reset username to: admin, Reset password to: admin12345"
msgstr "重置用户名:admin 重置密码:admin12345"
msgid "Username: admin, Password: admin12345"
msgstr "用户名:admin 密码:admin12345"
msgid "You need to restart DDNS-Go service for the changes to take effect."
msgstr "需要重启DDNS-GO服务更改才生效"
msgid "You may need to restart DDNS-Go service manually."
msgstr "必须要重启DDNS-GO服务."
msgid "DDNS-Go command reset may have failed, but configuration file has been updated."
msgstr "ddns-go重置命令失败,尝试通过直接修改配置文件来重置"
msgid "Reset username/password"
msgstr "重置用户名/密码"
msgid "DDNS-Go service restarted successfully"
msgstr "DDNS-GO服务重启成功"
msgid "Restart Service Now"
msgstr "立刻重启服务"
@@ -122,4 +137,4 @@ msgid "Restart Later"
msgstr "稍后重启"
msgid "Due to browser security policies, the DDNS-GO interface https cannot be embedded directly."
msgstr "由于浏览器安全策略,DDNS-GO接口https不能直接嵌入。"
msgstr "由于浏览器安全策略,DDNS-GO接口https不能直接嵌入。"
@@ -13,8 +13,8 @@
"admin/services/ddns-go/ddns-go": {
"title": "DDNS-GO Control panel",
"order": 10,
"action": {
"order": 10,
"action": {
"type": "view",
"path": "ddns-go/ddns-go"
}
@@ -27,7 +27,7 @@
"path": "ddns-go/config"
}
},
"admin/services/ddns-go/log": {
"title": "Log",
"order": 30,
@@ -2,7 +2,7 @@
"luci-app-ddns-go": {
"description": "Grant UCI access for luci-app-ddns-go",
"read": {
"uci": ["*"],
"uci": [ "ddns-go" ],
"file": {
"/etc/init.d/ddns-go": ["exec"],
"/usr/libexec/ddns-go-call": ["exec"],
@@ -10,6 +10,7 @@
"/bin/pidof": ["exec"],
"/bin/ps": ["exec"],
"/bin/ash": ["exec"],
"/usr/bin/ddns-go": ["exec"],
"/etc/ddns-go/ddns-go-config.yaml": ["read"],
"/var/log/ddns-go.log": ["read"]
},
@@ -17,7 +18,6 @@
"rc": ["*"],
"service": ["list"],
"luci.ddns-go": ["*"],
"network.interface.*": ["status"],
"network": ["*"]
}
},
@@ -28,7 +28,7 @@
"file": {
"/etc/ddns-go/ddns-go-config.yaml": ["write"]
},
"uci": ["*"]
"uci": ["ddns-go"]
}
}
}
}
@@ -9,7 +9,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=lucky
PKG_VERSION:=2.26.1
PKG_VERSION:=2.26.2
PKG_RELEASE:=1
PKGARCH:=all
@@ -10,8 +10,8 @@ PKG_NAME:=luci-app-partexp
LUCI_TITLE:=LuCI Support for Automatic Partition Mount
LUCI_PKGARCH:=all
LUCI_DEPENDS:=+fdisk +block-mount +bc +blkid +parted +btrfs-progs +losetup +resize2fs +e2fsprogs +f2fs-tools +kmod-loop
PKG_VERSION:=2.0.2
PKG_RELEASE:=20251221
PKG_VERSION:=2.0.3
PKG_RELEASE:=20260228
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=Sirpdboy <herboy2008@gmail.com>
@@ -266,32 +266,74 @@ return view.extend({
this.updateFormVisibility();
}
},
// 加载设备列表
loadDevices: function() {
var self = this;
loadDevices: function() {
var self = this;
if (self.dom.targetDisk) {
var loadingOption = document.createElement('option');
loadingOption.value = '';
loadingOption.textContent = _('Loading devices...');
loadingOption.disabled = true;
loadingOption.selected = true;
self.dom.targetDisk.innerHTML = '';
self.dom.targetDisk.appendChild(loadingOption);
}
function loadDevicesWithRetry(retryCount = 0) {
callPartExpGetDevices().then(function(response) {
if (!response || !response.devices || response.devices.length === 0) {
return;
if (!response) {
throw new Error('Empty response');
}
// 清空设备列表
if (self.dom.targetDisk) {
self.dom.targetDisk.innerHTML = '';
// 添加设备选项
response.devices.forEach(function(device) {
var option = document.createElement('option');
option.value = device.name;
option.textContent = device.name + ' (' + device.dev + ', ' + device.size + ' MB)';
self.dom.targetDisk.appendChild(option);
});
if (response.devices && response.devices.length > 0) {
response.devices.forEach(function(device) {
var option = document.createElement('option');
option.value = device.name;
option.textContent = device.name + ' (' + device.dev + ', ' + device.size + ' MB)';
self.dom.targetDisk.appendChild(option);
});
} else {
var noDeviceOption = document.createElement('option');
noDeviceOption.value = '';
noDeviceOption.textContent = _('no find device');
noDeviceOption.disabled = true;
noDeviceOption.selected = true;
self.dom.targetDisk.appendChild(noDeviceOption);
}
}
}).catch(function(error) {
console.error('Failed to load devices:', error);
if (retryCount < 3) {
setTimeout(function() {
loadDevicesWithRetry(retryCount + 1);
}, 1000 * (retryCount + 1));
} else {
if (self.dom.targetDisk) {
self.dom.targetDisk.innerHTML = '';
var errorOption = document.createElement('option');
errorOption.value = '';
errorOption.textContent = _('load error');
errorOption.disabled = true;
errorOption.selected = true;
self.dom.targetDisk.appendChild(errorOption);
}
ui.addNotification({
title: _('load device error'),
text: _('Failed to load devices:'),
type: 'error',
delay: 5000
});
}
});
},
}
loadDevicesWithRetry();
},
// 加载现有的日志文件内容
loadExistingLog: function() {
@@ -1,19 +1,21 @@
{
"luci-app-partexp": {
"description": "Grant UCI access for luci-app-partexp",
"read": {
"description": "Grant access for luci-app-partexp",
"read": {
"ubus": {
"file": ["exec", "list", "stat", "read"],
"uci": [ "*" ],
"partexp": ["*"]
"file": ["exec", "list", "stat", "read"],
"uci": ["partexp"],
"partexp": ["get_devices", "get_log", "get_status"]
}
},
"write": {
},
"write": {
"ubus": {
"partexp": ["*"],
"file": ["write"],
"uci": ["*"]
}
"partexp": ["autopart", "save_config"],
"file": ["write"]
},
"file": {
"/etc/config/partexp": ["write"]
}
}
}
}
}
@@ -153,7 +153,8 @@ if load_balancing_options then -- [[ Load balancing Start ]]
local descrStr = "Example: <code>^A && B && !C && D$</code><br>"
descrStr = descrStr .. "This means the node remark must start with A (^), include B, exclude C (!), and end with D ($).<br>"
descrStr = descrStr .. "Conditions are joined by <code>&&</code>, and their order does not affect the result."
o.description = translate(descrStr)
o.description = translate(descrStr) .. string.format("<br><font color='red'>%s</font>",
translate("Keep the match scope small. Too many nodes can impact router performance."))
o = s:option(ListValue, _n("balancingStrategy"), translate("Balancing Strategy"))
o:depends({ [_n("protocol")] = "_balancing" })
@@ -152,7 +152,8 @@ if load_urltest_options then -- [[ URLTest Start ]]
local descrStr = "Example: <code>^A && B && !C && D$</code><br>"
descrStr = descrStr .. "This means the node remark must start with A (^), include B, exclude C (!), and end with D ($).<br>"
descrStr = descrStr .. "Conditions are joined by <code>&&</code>, and their order does not affect the result."
o.description = translate(descrStr)
o.description = translate(descrStr) .. string.format("<br><font color='red'>%s</font>",
translate("Keep the match scope small. Too many nodes can impact router performance."))
o = s:option(Value, _n("urltest_url"), translate("Probe URL"))
o:depends({ [_n("protocol")] = "_urltest" })
@@ -45,8 +45,17 @@ _M["sing-box"] = {
default_path = "/usr/bin/sing-box",
match_fmt_str = "linux%%-%s",
file_tree = {
x86_64 = "amd64",
mips64el = "mips64le"
x86_64 = "amd64%-musl",
x86 = "386%-musl",
aarch64 = "arm64%-musl",
rockchip = "arm64%-musl",
mips = "mips%-softfloat",
mips64 = "mips64%-softfloat",
mipsel = "mipsle%-softfloat%-musl",
mips64el = "mips64le%-softfloat",
armv7 = "armv7%-musl",
armv8 = "arm64%-musl",
riscv64 = "riscv64%-musl"
}
}
@@ -222,11 +222,11 @@ function gen_outbound(flag, node, tag, proxy_table)
} or nil,
grpcSettings = (node.transport == "grpc") and {
serviceName = node.grpc_serviceName,
multiMode = (node.grpc_mode == "multi") and true or nil,
idle_timeout = tonumber(node.grpc_idle_timeout) or nil,
multiMode = (node.grpc_mode == "multi") and true or false,
idle_timeout = node.grpc_idle_timeout and (tonumber(node.grpc_idle_timeout) < 10 and 10 or tonumber(node.grpc_idle_timeout)) or nil,
health_check_timeout = tonumber(node.grpc_health_check_timeout) or nil,
permit_without_stream = (node.grpc_permit_without_stream == "1") and true or nil,
initial_windows_size = tonumber(node.grpc_initial_windows_size) or nil,
permit_without_stream = (node.grpc_permit_without_stream == "1") and true or false,
initial_windows_size = node.grpc_initial_windows_size and tonumber(node.grpc_initial_windows_size) or 0,
user_agent = node.user_agent
} or nil,
httpupgradeSettings = (node.transport == "httpupgrade") and {
@@ -610,7 +610,7 @@ function gen_config_server(node)
host = node.ws_host or nil,
path = node.ws_path
} or nil,
grpcSettings = (node.transport == "grpc") and {
grpcSettings = (node.transport == "grpc" and node.grpc_serviceName) and {
serviceName = node.grpc_serviceName
} or nil,
httpupgradeSettings = (node.transport == "httpupgrade") and {
@@ -496,6 +496,9 @@ msgstr ""
"表示节点备注需同时满足:以 A 开头(^)、包含 B、不包含 C(!)、并以 D 结尾($)。<br>"
"多个条件使用 <code>&&</code> 连接,条件顺序不影响结果。"
msgid "Keep the match scope small. Too many nodes can impact router performance."
msgstr "建议尽量缩小匹配范围,节点过多会增加路由器负载。"
msgid "Balancing Strategy"
msgstr "负载均衡策略"
@@ -35,8 +35,17 @@ _M["sing-box"] = {
default_path = "/usr/bin/sing-box",
match_fmt_str = "linux%%-%s",
file_tree = {
x86_64 = "amd64",
mips64el = "mips64le"
x86_64 = "amd64%-musl",
x86 = "386%-musl",
aarch64 = "arm64%-musl",
rockchip = "arm64%-musl",
mips = "mips%-softfloat",
mips64 = "mips64%-softfloat",
mipsel = "mipsle%-softfloat%-musl",
mips64el = "mips64le%-softfloat",
armv7 = "armv7%-musl",
armv8 = "arm64%-musl",
riscv64 = "riscv64%-musl"
}
}
@@ -218,11 +218,11 @@ function gen_outbound(flag, node, tag, proxy_table)
} or nil,
grpcSettings = (node.transport == "grpc") and {
serviceName = node.grpc_serviceName,
multiMode = (node.grpc_mode == "multi") and true or nil,
idle_timeout = tonumber(node.grpc_idle_timeout) or nil,
multiMode = (node.grpc_mode == "multi") and true or false,
idle_timeout = node.grpc_idle_timeout and (tonumber(node.grpc_idle_timeout) < 10 and 10 or tonumber(node.grpc_idle_timeout)) or nil,
health_check_timeout = tonumber(node.grpc_health_check_timeout) or nil,
permit_without_stream = (node.grpc_permit_without_stream == "1") and true or nil,
initial_windows_size = tonumber(node.grpc_initial_windows_size) or nil,
permit_without_stream = (node.grpc_permit_without_stream == "1") and true or false,
initial_windows_size = node.grpc_initial_windows_size and tonumber(node.grpc_initial_windows_size) or 0,
user_agent = node.user_agent
} or nil,
httpupgradeSettings = (node.transport == "httpupgrade") and {
@@ -605,7 +605,7 @@ function gen_config_server(node)
host = node.ws_host or nil,
path = node.ws_path
} or nil,
grpcSettings = (node.transport == "grpc") and {
grpcSettings = (node.transport == "grpc" and node.grpc_serviceName) and {
serviceName = node.grpc_serviceName
} or nil,
httpupgradeSettings = (node.transport == "httpupgrade") and {
@@ -97,7 +97,7 @@ jobs:
./build-release -t ${{ matrix.platform.target }} $compile_features $compile_compress $compile_nightly $compile_cargo_flags
- name: Upload Artifacts
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.platform.target }}
path: build/release/*
@@ -138,7 +138,7 @@ jobs:
./build/build-host-release -t ${{ matrix.target }}
- name: Upload Artifacts
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.target }}
path: build/release/*
@@ -164,7 +164,7 @@ jobs:
pwsh ./build/build-host-release.ps1 "full winservice"
- name: Upload Artifacts
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: windows-native
path: build/release/*
+9 -9
View File
@@ -1923,9 +1923,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.180"
version = "0.2.182"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
[[package]]
name = "libloading"
@@ -2208,9 +2208,9 @@ dependencies = [
[[package]]
name = "nix"
version = "0.31.1"
version = "0.31.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225e7cfe711e0ba79a68baeddb2982723e4235247aefce1482f2f16c27865b66"
checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3"
dependencies = [
"bitflags 2.10.0",
"cfg-if",
@@ -2419,18 +2419,18 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "pin-project"
version = "1.1.10"
version = "1.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.10"
version = "1.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6"
dependencies = [
"proc-macro2",
"quote",
@@ -3332,7 +3332,7 @@ dependencies = [
"lru_time_cache",
"mime",
"native-tls",
"nix 0.31.1",
"nix 0.31.2",
"pin-project",
"rand 0.10.0",
"regex",
+37
View File
@@ -0,0 +1,37 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{kt,kts}]
indent_size = 4
indent_style = space
max_line_length = 140
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
ktlint_function_naming_ignore_when_annotated_with = Composable
ktlint_standard_function-naming = disabled
ktlint_standard_no-wildcard-imports = disabled
ktlint_standard_property-naming = disabled
[*.xml]
indent_size = 2
indent_style = space
[*.gradle]
indent_size = 4
indent_style = space
[*.gradle.kts]
indent_size = 4
indent_style = space
[*.json]
indent_size = 2
indent_style = space
[*.md]
trim_trailing_whitespace = false
+1 -1
View File
@@ -3,7 +3,7 @@
/local.properties
/.idea/
.DS_Store
/build
build/
/captures
.externalNativeBuild
.cxx
-202
View File
@@ -1,202 +0,0 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id "com.android.application"
id "kotlin-android"
id "kotlin-parcelize"
id "com.google.devtools.ksp"
id "org.jetbrains.kotlin.plugin.compose"
id "com.github.triplet.play"
}
android {
namespace "io.nekohasekai.sfa"
compileSdk 36
ndkVersion "28.0.13004108"
def ndkPathFromEnv = System.getenv("ANDROID_NDK_HOME")
if (ndkPathFromEnv != null) {
ndkPath ndkPathFromEnv
}
ksp {
arg("room.incremental", "true")
arg("room.schemaLocation", "$projectDir/schemas")
}
defaultConfig {
applicationId "io.nekohasekai.sfa"
minSdk 21
targetSdk 35
versionCode getVersionProps("VERSION_CODE").toInteger()
versionName getVersionProps("VERSION_NAME")
setProperty("archivesBaseName", "SFA-" + versionName)
}
signingConfigs {
release {
storeFile file("release.keystore")
storePassword getProps("KEYSTORE_PASS")
keyAlias getProps("ALIAS_NAME")
keyPassword getProps("ALIAS_PASS")
}
}
buildTypes {
debug {
if (getProps("KEYSTORE_PASS") != "") {
signingConfig signingConfigs.release
}
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
signingConfig signingConfigs.release
vcsInfo.include false
}
}
dependenciesInfo {
includeInApk = false
}
flavorDimensions "vendor"
productFlavors {
play {
}
other {
}
}
splits {
abi {
enable true
universalApk true
reset()
include "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildFeatures {
viewBinding true
aidl true
compose true
}
applicationVariants.configureEach { variant ->
variant.outputs.configureEach {
outputFileName = (outputFileName as String).replace("-release", "")
outputFileName = (outputFileName as String).replace("-play", "")
outputFileName = (outputFileName as String).replace("-other", "-foss")
}
}
}
dependencies {
implementation(fileTree("libs"))
implementation "androidx.core:core-ktx:1.16.0"
implementation "androidx.appcompat:appcompat:1.7.1"
implementation "com.google.android.material:material:1.12.0"
implementation "androidx.constraintlayout:constraintlayout:2.2.1"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.9.2"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.2"
implementation "androidx.navigation:navigation-fragment-ktx:2.9.3"
implementation "androidx.navigation:navigation-ui-ktx:2.9.3"
implementation "com.google.zxing:core:3.5.3"
implementation "androidx.room:room-runtime:2.7.2"
implementation "androidx.coordinatorlayout:coordinatorlayout:1.3.0"
implementation "androidx.preference:preference-ktx:1.2.1"
implementation "androidx.camera:camera-view:1.4.2"
implementation "androidx.camera:camera-lifecycle:1.4.2"
implementation "androidx.camera:camera-camera2:1.4.2"
ksp "androidx.room:room-compiler:2.7.2"
implementation "androidx.work:work-runtime-ktx:2.10.3"
implementation "androidx.browser:browser:1.9.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2"
// DO NOT UPDATE (minSdkVersion updated)
implementation "com.blacksquircle.ui:editorkit:2.2.0"
implementation "com.blacksquircle.ui:language-json:2.2.0"
implementation("com.android.tools.smali:smali-dexlib2:3.0.9") {
exclude group: "com.google.guava", module: "guava"
}
implementation "com.google.guava:guava:33.4.8-android"
playImplementation "com.google.android.play:app-update-ktx:2.1.0"
playImplementation "com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1"
def composeBom = platform('androidx.compose:compose-bom:2025.07.00')
implementation composeBom
androidTestImplementation composeBom
implementation 'androidx.compose.material3:material3'
implementation 'androidx.compose.ui:ui-tooling-preview'
debugImplementation 'androidx.compose.ui:ui-tooling'
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
debugImplementation 'androidx.compose.ui:ui-test-manifest'
implementation 'androidx.compose.material:material-icons-extended'
implementation 'androidx.activity:activity-compose:1.10.1'
implementation 'me.zhanghai.compose.preference:library:1.1.1'
implementation "androidx.navigation:navigation-compose:2.9.3"
}
def playCredentialsJSON = rootProject.file("service-account-credentials.json")
if (playCredentialsJSON.exists()) {
play {
serviceAccountCredentials = playCredentialsJSON
defaultToAppBundles = true
def version = getVersionProps("VERSION_NAME")
if (version.contains("alpha") || version.contains("beta") || version.contains("rc")) {
track = "beta"
} else {
track = "production"
}
}
}
tasks.withType(KotlinCompile.class).configureEach {
kotlinOptions {
jvmTarget = "1.8"
}
}
def getProps(String propName) {
def propsInEnv = System.getenv("LOCAL_PROPERTIES")
if (propsInEnv != null) {
def props = new Properties()
props.load(new ByteArrayInputStream(Base64.decoder.decode(propsInEnv)))
String value = props[propName]
if (value != null) {
return value
}
}
def propsFile = rootProject.file("local.properties")
if (propsFile.exists()) {
def props = new Properties()
props.load(new FileInputStream(propsFile))
String value = props[propName]
if (value != null) {
return value
}
}
return ""
}
def getVersionProps(String propName) {
def propsFile = rootProject.file("version.properties")
if (propsFile.exists()) {
def props = new Properties()
props.load(new FileInputStream(propsFile))
String value = props[propName]
if (value != null) {
return value
}
}
return ""
}
@@ -0,0 +1,366 @@
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.tasks.Sync
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.io.ByteArrayInputStream
import java.io.FileInputStream
import java.util.Base64
import java.util.Properties
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.parcelize")
id("com.google.devtools.ksp")
id("org.jetbrains.kotlin.plugin.compose")
id("org.jetbrains.kotlin.plugin.serialization")
id("com.github.triplet.play")
alias(libs.plugins.spotless)
}
fun getProps(propName: String): String {
val propsInEnv = System.getenv("LOCAL_PROPERTIES")
if (propsInEnv != null) {
val props = Properties()
props.load(ByteArrayInputStream(Base64.getDecoder().decode(propsInEnv)))
val value = props.getProperty(propName)
if (value != null) {
return value
}
}
val propsFile = rootProject.file("local.properties")
if (propsFile.exists()) {
val props = Properties()
props.load(FileInputStream(propsFile))
val value = props.getProperty(propName)
if (value != null) {
return value
}
}
return ""
}
fun getVersionProps(propName: String): String {
val propsFile = rootProject.file("version.properties")
if (propsFile.exists()) {
val props = Properties()
props.load(FileInputStream(propsFile))
val value = props.getProperty(propName)
if (value != null) {
return value
}
}
return ""
}
android {
namespace = "io.nekohasekai.sfa"
compileSdk = 36
ndkVersion = "28.0.13004108"
System.getenv("ANDROID_NDK_HOME")?.let { ndkPath = it }
ksp {
arg("room.incremental", "true")
arg("room.schemaLocation", "${projectDir}/schemas")
}
defaultConfig {
applicationId = "io.nekohasekai.sfa"
minSdk = 21
targetSdk = 35
versionCode = getVersionProps("VERSION_CODE").toInt()
versionName = getVersionProps("VERSION_NAME")
base.archivesName.set("SFA-${versionName}")
}
signingConfigs {
create("release") {
storeFile = file("release.keystore")
storePassword = getProps("KEYSTORE_PASS")
keyAlias = getProps("ALIAS_NAME")
keyPassword = getProps("ALIAS_PASS")
}
}
buildTypes {
debug {
if (getProps("KEYSTORE_PASS").isNotEmpty()) {
signingConfig = signingConfigs.getByName("release")
}
}
release {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
signingConfig = signingConfigs.getByName("release")
vcsInfo.include = false
}
}
dependenciesInfo {
includeInApk = false
}
flavorDimensions += "vendor"
productFlavors {
create("play") {
minSdk = 23
}
create("other") {
minSdk = 23
}
create("otherLegacy") {
minSdk = 21
}
}
sourceSets {
getByName("play") {
java.directories.add("src/minApi23/java")
aidl.directories.add("src/minApi23/aidl")
}
getByName("other") {
java.directories.addAll(listOf("src/minApi23/java", "src/github/java"))
aidl.directories.add("src/minApi23/aidl")
}
getByName("otherLegacy") {
java.directories.addAll(listOf("src/minApi21/java", "src/github/java"))
aidl.directories.add("src/minApi23/aidl")
}
}
splits {
abi {
isEnable = true
isUniversalApk = true
reset()
include("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
androidResources {
generateLocaleConfig = true
}
buildFeatures {
viewBinding = true
aidl = true
compose = true
buildConfig = true
}
packaging {
jniLibs {
useLegacyPackaging = true
}
}
applicationVariants.configureEach {
outputs.configureEach {
val output = this as com.android.build.gradle.internal.api.BaseVariantOutputImpl
var fileName = output.outputFileName
fileName = fileName.replace("-release", "")
fileName = fileName.replace("-play", "-play")
fileName = fileName.replace("-otherLegacy", "-legacy-android-5")
fileName = fileName.replace("-other", "")
output.outputFileName = fileName
}
}
}
dependencies {
// libbox
"playImplementation"(files("libs/libbox.aar"))
"otherImplementation"(files("libs/libbox.aar"))
"otherLegacyImplementation"(files("libs/libbox-legacy.aar"))
// API level specific versions
val lifecycleVersion23 = "2.10.0"
val roomVersion23 = "2.8.4"
val workVersion23 = "2.11.1"
val cameraVersion23 = "1.5.3"
val browserVersion23 = "1.9.0"
val lifecycleVersion21 = "2.9.4"
val roomVersion21 = "2.7.2"
val workVersion21 = "2.10.5"
val cameraVersion21 = "1.4.2"
val browserVersion21 = "1.9.0"
// Common dependencies (no API level difference)
implementation("androidx.core:core-ktx:1.17.0")
implementation("androidx.appcompat:appcompat:1.7.1")
implementation("com.google.android.material:material:1.13.0")
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
implementation("androidx.navigation:navigation-fragment-ktx:2.9.7")
implementation("androidx.navigation:navigation-ui-ktx:2.9.7")
implementation("com.google.zxing:core:3.5.4")
implementation("androidx.coordinatorlayout:coordinatorlayout:1.3.0")
implementation("androidx.preference:preference-ktx:1.2.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.10.0")
implementation("com.blacksquircle.ui:editorkit:2.2.0")
implementation("com.blacksquircle.ui:language-json:2.2.0")
implementation("com.android.tools.smali:smali-dexlib2:3.0.9") {
exclude(group = "com.google.guava", module = "guava")
}
implementation("com.google.guava:guava:33.5.0-android")
// API 23+ dependencies (play/other)
"playImplementation"("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion23")
"playImplementation"("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion23")
"playImplementation"("androidx.lifecycle:lifecycle-process:$lifecycleVersion23")
"playImplementation"("androidx.room:room-runtime:$roomVersion23")
"playImplementation"("androidx.work:work-runtime-ktx:$workVersion23")
"playImplementation"("androidx.camera:camera-view:$cameraVersion23")
"playImplementation"("androidx.camera:camera-lifecycle:$cameraVersion23")
"playImplementation"("androidx.camera:camera-camera2:$cameraVersion23")
"playImplementation"("androidx.browser:browser:$browserVersion23")
"playAnnotationProcessor"("androidx.room:room-compiler:$roomVersion23")
"kspPlay"("androidx.room:room-compiler:$roomVersion23")
"otherImplementation"("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion23")
"otherImplementation"("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion23")
"otherImplementation"("androidx.lifecycle:lifecycle-process:$lifecycleVersion23")
"otherImplementation"("androidx.room:room-runtime:$roomVersion23")
"otherImplementation"("androidx.work:work-runtime-ktx:$workVersion23")
"otherImplementation"("androidx.camera:camera-view:$cameraVersion23")
"otherImplementation"("androidx.camera:camera-lifecycle:$cameraVersion23")
"otherImplementation"("androidx.camera:camera-camera2:$cameraVersion23")
"otherImplementation"("androidx.browser:browser:$browserVersion23")
"kspOther"("androidx.room:room-compiler:$roomVersion23")
// API 21 dependencies (otherLegacy)
"otherLegacyImplementation"("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion21")
"otherLegacyImplementation"("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion21")
"otherLegacyImplementation"("androidx.lifecycle:lifecycle-process:$lifecycleVersion21")
"otherLegacyImplementation"("androidx.room:room-runtime:$roomVersion21")
"otherLegacyImplementation"("androidx.work:work-runtime-ktx:$workVersion21")
"otherLegacyImplementation"("androidx.camera:camera-view:$cameraVersion21")
"otherLegacyImplementation"("androidx.camera:camera-lifecycle:$cameraVersion21")
"otherLegacyImplementation"("androidx.camera:camera-camera2:$cameraVersion21")
"otherLegacyImplementation"("androidx.browser:browser:$browserVersion21")
"kspOtherLegacy"("androidx.room:room-compiler:$roomVersion21")
// Play Store specific
"playImplementation"("com.google.android.play:app-update-ktx:2.1.0")
"playImplementation"("com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1")
// Shizuku (play and other flavors, API 23+ only)
val shizukuVersion = "12.2.0"
"playImplementation"("dev.rikka.shizuku:api:$shizukuVersion")
"playImplementation"("dev.rikka.shizuku:provider:$shizukuVersion")
"otherImplementation"("dev.rikka.shizuku:api:$shizukuVersion")
"otherImplementation"("dev.rikka.shizuku:provider:$shizukuVersion")
// libsu for ROOT package query (all flavors)
val libsuVersion = "6.0.0"
"playImplementation"("com.github.topjohnwu.libsu:core:$libsuVersion")
"playImplementation"("com.github.topjohnwu.libsu:service:$libsuVersion")
"otherImplementation"("com.github.topjohnwu.libsu:core:$libsuVersion")
"otherImplementation"("com.github.topjohnwu.libsu:service:$libsuVersion")
"otherLegacyImplementation"("com.github.topjohnwu.libsu:core:$libsuVersion")
"otherLegacyImplementation"("com.github.topjohnwu.libsu:service:$libsuVersion")
// Compose dependencies - API 23+ (play/other)
val composeBom23 = platform("androidx.compose:compose-bom:2026.02.00")
val activityVersion23 = "1.12.4"
val lifecycleComposeVersion23 = "2.10.0"
"playImplementation"(composeBom23)
"playImplementation"("androidx.compose.material3:material3")
"playImplementation"("androidx.compose.material3.adaptive:adaptive")
"playImplementation"("androidx.compose.ui:ui")
"playImplementation"("androidx.compose.ui:ui-tooling-preview")
"playImplementation"("androidx.compose.material:material-icons-extended")
"playImplementation"("androidx.activity:activity-compose:$activityVersion23")
"playImplementation"("androidx.navigation:navigation-compose:2.9.7")
"playImplementation"("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycleComposeVersion23")
"playImplementation"("androidx.compose.runtime:runtime-livedata")
"otherImplementation"(composeBom23)
"otherImplementation"("androidx.compose.material3:material3")
"otherImplementation"("androidx.compose.material3.adaptive:adaptive")
"otherImplementation"("androidx.compose.ui:ui")
"otherImplementation"("androidx.compose.ui:ui-tooling-preview")
"otherImplementation"("androidx.compose.material:material-icons-extended")
"otherImplementation"("androidx.activity:activity-compose:$activityVersion23")
"otherImplementation"("androidx.navigation:navigation-compose:2.9.7")
"otherImplementation"("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycleComposeVersion23")
"otherImplementation"("androidx.compose.runtime:runtime-livedata")
// Compose dependencies - API 21 (otherLegacy)
val composeBom21 = platform("androidx.compose:compose-bom:2025.01.00")
val activityVersion21 = "1.11.0"
val lifecycleComposeVersion21 = "2.9.4"
"otherLegacyImplementation"(composeBom21)
"otherLegacyImplementation"("androidx.compose.material3:material3")
"otherLegacyImplementation"("androidx.compose.material3.adaptive:adaptive")
"otherLegacyImplementation"("androidx.compose.ui:ui")
"otherLegacyImplementation"("androidx.compose.ui:ui-tooling-preview")
"otherLegacyImplementation"("androidx.compose.material:material-icons-extended")
"otherLegacyImplementation"("androidx.activity:activity-compose:$activityVersion21")
"otherLegacyImplementation"("androidx.navigation:navigation-compose:2.9.7")
"otherLegacyImplementation"("androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycleComposeVersion21")
"otherLegacyImplementation"("androidx.compose.runtime:runtime-livedata")
// Debug/Test dependencies
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
// Common Compose-related libraries
implementation("sh.calvin.reorderable:reorderable:3.0.0")
implementation("com.github.jeziellago:compose-markdown:0.5.8")
implementation("org.kodein.emoji:emoji-kt:2.3.0")
// Xposed API for self-hooking VPN hide module
compileOnly("de.robv.android.xposed:api:82")
compileOnly(project(":libxposed-api"))
}
val playCredentialsJSON = rootProject.file("service-account-credentials.json")
if (playCredentialsJSON.exists()) {
play {
serviceAccountCredentials.set(playCredentialsJSON)
defaultToAppBundles.set(true)
val version = getVersionProps("VERSION_NAME")
track.set(
if (version.contains("alpha") || version.contains("beta")/* || version.contains("rc")*/) {
"beta"
} else {
"production"
}
)
}
}
tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
}
spotless {
kotlin {
target("src/**/*.kt")
ktlint(libs.versions.ktlint.get())
.editorConfigOverride(mapOf(
"ktlint_standard_backing-property-naming" to "disabled",
"ktlint_standard_filename" to "disabled",
"ktlint_standard_max-line-length" to "disabled",
"ktlint_standard_property-naming" to "disabled",
))
}
java {
target("src/**/*.java")
googleJavaFormat()
}
}
@@ -2,11 +2,11 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "b7bfa362ec191b0a18660e615da81e46",
"identityHash": "24de05fe91b147c75b870f91b2f4871b",
"entities": [
{
"tableName": "profiles",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `name` TEXT NOT NULL, `typed` BLOB NOT NULL)",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `name` TEXT NOT NULL, `icon` TEXT, `typed` BLOB NOT NULL)",
"fields": [
{
"fieldPath": "id",
@@ -26,6 +26,11 @@
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "icon",
"columnName": "icon",
"affinity": "TEXT"
},
{
"fieldPath": "typed",
"columnName": "typed",
@@ -38,15 +43,12 @@
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b7bfa362ec191b0a18660e615da81e46')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '24de05fe91b147c75b870f91b2f4871b')"
]
}
}
@@ -0,0 +1,55 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "dc5fb65e389df8c8391b3435652f4c64",
"entities": [
{
"tableName": "profiles",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `name` TEXT NOT NULL, `icon` TEXT DEFAULT NULL, `typed` BLOB NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "icon",
"columnName": "icon",
"affinity": "TEXT",
"defaultValue": "NULL"
},
{
"fieldPath": "typed",
"columnName": "typed",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
}
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'dc5fb65e389df8c8391b3435652f4c64')"
]
}
}
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
<application>
<receiver
android:name=".vendor.InstallResultReceiver"
android:exported="false">
<intent-filter>
<action android:name="io.nekohasekai.sfa.INSTALL_COMPLETE" />
</intent-filter>
</receiver>
</application>
</manifest>
@@ -0,0 +1,43 @@
package io.nekohasekai.sfa.vendor
import io.nekohasekai.libbox.Libbox
import io.nekohasekai.sfa.Application
import io.nekohasekai.sfa.update.UpdateState
import io.nekohasekai.sfa.utils.HTTPClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.Closeable
import java.io.File
class ApkDownloader : Closeable {
private val client = Libbox.newHTTPClient().apply {
modernTLS()
keepAlive()
}
suspend fun download(url: String): File = withContext(Dispatchers.IO) {
val cacheDir = File(Application.application.cacheDir, "updates")
cacheDir.mkdirs()
val apkFile = File(cacheDir, "update.apk")
if (apkFile.exists()) apkFile.delete()
val request = client.newRequest()
request.setUserAgent(HTTPClient.userAgent)
request.setURL(url)
val response = request.execute()
response.writeTo(apkFile.absolutePath)
if (!apkFile.exists() || apkFile.length() == 0L) {
throw Exception("Download failed: empty file")
}
UpdateState.saveApkPath(apkFile)
apkFile
}
override fun close() {
client.close()
}
}
@@ -0,0 +1,149 @@
package io.nekohasekai.sfa.vendor
import android.os.Build
import io.nekohasekai.libbox.Libbox
import io.nekohasekai.sfa.BuildConfig
import io.nekohasekai.sfa.ktx.unwrap
import io.nekohasekai.sfa.update.UpdateInfo
import io.nekohasekai.sfa.update.UpdateTrack
import io.nekohasekai.sfa.utils.HTTPClient
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import java.io.Closeable
class GitHubUpdateChecker : Closeable {
companion object {
private const val RELEASES_URL = "https://api.github.com/repos/SagerNet/sing-box/releases"
private const val METADATA_FILENAME = "SFA-version-metadata.json"
}
private val client = Libbox.newHTTPClient().apply {
modernTLS()
keepAlive()
}
private val json = Json { ignoreUnknownKeys = true }
fun checkUpdate(track: UpdateTrack): UpdateInfo? {
val releases = getReleases()
var selected: ReleaseCandidate? = null
for (release in releases) {
if (!isReleaseInTrack(release, track)) {
continue
}
val metadata = runCatching { downloadMetadata(release) }.getOrNull() ?: continue
if (!isNewerThanCurrent(metadata.versionName)) {
continue
}
val currentBest = selected
if (currentBest == null || isBetterVersion(metadata, currentBest.metadata)) {
selected = ReleaseCandidate(release, metadata)
}
}
val release = selected?.release ?: return null
val metadata = selected.metadata
val isLegacy = Build.VERSION.SDK_INT < Build.VERSION_CODES.M
val apkAsset = release.assets.find { asset ->
asset.name.endsWith(".apk") &&
!asset.name.contains("play") &&
asset.name.contains("legacy-android-5") == isLegacy
}
return UpdateInfo(
versionCode = metadata.versionCode,
versionName = metadata.versionName,
downloadUrl = apkAsset?.browserDownloadUrl ?: release.htmlUrl,
releaseUrl = release.htmlUrl,
releaseNotes = release.body,
isPrerelease = release.prerelease,
fileSize = apkAsset?.size ?: 0,
)
}
private fun getReleases(): List<GitHubRelease> {
val request = client.newRequest()
request.setURL(RELEASES_URL)
request.setHeader("Accept", "application/vnd.github.v3+json")
request.setUserAgent(HTTPClient.userAgent)
val response = request.execute()
val content = response.content.unwrap
return json.decodeFromString(content)
}
private fun isReleaseInTrack(release: GitHubRelease, track: UpdateTrack): Boolean {
if (release.draft) {
return false
}
return when (track) {
UpdateTrack.STABLE -> !release.prerelease
UpdateTrack.BETA -> true
}
}
private fun isNewerThanCurrent(versionName: String): Boolean {
return Libbox.compareSemver(versionName, BuildConfig.VERSION_NAME)
}
private fun isBetterVersion(version: VersionMetadata, other: VersionMetadata): Boolean {
if (Libbox.compareSemver(version.versionName, other.versionName)) {
return true
}
if (Libbox.compareSemver(other.versionName, version.versionName)) {
return false
}
return version.versionCode > other.versionCode
}
private fun downloadMetadata(release: GitHubRelease): VersionMetadata? {
val metadataAsset = release.assets.find { it.name == METADATA_FILENAME }
?: return null
val request = client.newRequest()
request.setURL(metadataAsset.browserDownloadUrl)
request.setUserAgent(HTTPClient.userAgent)
val response = request.execute()
val content = response.content.unwrap
return json.decodeFromString<VersionMetadata>(content)
}
override fun close() {
client.close()
}
@Serializable
data class GitHubRelease(
@SerialName("tag_name") val tagName: String = "",
val name: String = "",
val body: String? = null,
val draft: Boolean = false,
val prerelease: Boolean = false,
@SerialName("html_url") val htmlUrl: String = "",
val assets: List<GitHubAsset> = emptyList(),
)
@Serializable
data class GitHubAsset(
val name: String = "",
@SerialName("browser_download_url") val browserDownloadUrl: String = "",
val size: Long = 0,
)
@Serializable
data class VersionMetadata(
@SerialName("version_code") val versionCode: Int = 0,
@SerialName("version_name") val versionName: String = "",
)
private data class ReleaseCandidate(
val release: GitHubRelease,
val metadata: VersionMetadata,
)
}
@@ -0,0 +1,47 @@
package io.nekohasekai.sfa.vendor
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
import android.util.Log
import io.nekohasekai.sfa.update.UpdateState
class InstallResultReceiver : BroadcastReceiver() {
companion object {
const val ACTION_INSTALL_COMPLETE = "io.nekohasekai.sfa.INSTALL_COMPLETE"
private const val TAG = "InstallResultReceiver"
}
override fun onReceive(context: Context, intent: Intent) {
if (intent.action != ACTION_INSTALL_COMPLETE) return
val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE)
val message = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
Log.d(TAG, "Install result: status=$status, message=$message")
when (status) {
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
val confirmIntent = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
} else {
@Suppress("DEPRECATION")
intent.getParcelableExtra(Intent.EXTRA_INTENT)
}
confirmIntent?.let {
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(it)
}
}
PackageInstaller.STATUS_SUCCESS -> {
Log.d(TAG, "Installation successful")
UpdateState.setInstallStatus(UpdateState.InstallStatus.Success)
}
else -> {
Log.e(TAG, "Installation failed: $status - $message")
UpdateState.setInstallStatus(UpdateState.InstallStatus.Failed(message ?: "Unknown error"))
}
}
}
}
@@ -0,0 +1,79 @@
package io.nekohasekai.sfa.vendor
import android.content.Intent
import android.content.ServiceConnection
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.os.ParcelFileDescriptor
import com.topjohnwu.superuser.ipc.RootService
import io.nekohasekai.sfa.Application
import io.nekohasekai.sfa.bg.IRootService
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import java.io.File
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
object RootInstaller {
suspend fun install(apkFile: File) {
withContext(Dispatchers.IO) {
bindRootService().use { handle ->
ParcelFileDescriptor.open(apkFile, ParcelFileDescriptor.MODE_READ_ONLY).use { pfd ->
handle.service.installPackage(
pfd,
apkFile.length(),
android.os.Process.myUserHandle().hashCode(),
)
}
}
}
}
private suspend fun bindRootService(): RootServiceHandle {
return withContext(Dispatchers.Main) {
suspendCancellableCoroutine { continuation ->
val conn = object : ServiceConnection {
override fun onServiceConnected(name: android.content.ComponentName?, binder: IBinder?) {
val svc = if (binder != null && binder.pingBinder()) {
IRootService.Stub.asInterface(binder)
} else {
null
}
if (svc == null) {
continuation.resumeWithException(IllegalStateException("Invalid root service binder"))
return
}
continuation.resume(RootServiceHandle(this, svc))
}
override fun onServiceDisconnected(name: android.content.ComponentName?) {
// Ignored
}
}
try {
val intent = Intent(Application.application, Class.forName("io.nekohasekai.sfa.bg.RootServer"))
RootService.bind(intent, conn)
} catch (e: Throwable) {
continuation.resumeWithException(e)
return@suspendCancellableCoroutine
}
continuation.invokeOnCancellation {
RootService.unbind(conn)
}
}
}
}
private class RootServiceHandle(val connection: ServiceConnection, val service: IRootService) : java.io.Closeable {
override fun close() {
Handler(Looper.getMainLooper()).post {
RootService.unbind(connection)
}
}
}
}
@@ -0,0 +1,45 @@
package io.nekohasekai.sfa.vendor
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import java.io.File
import java.io.FileInputStream
import android.content.pm.PackageInstaller as AndroidPackageInstaller
object SystemPackageInstaller {
fun canSystemSilentInstall(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
fun install(context: Context, apkFile: File) {
val packageInstaller = context.packageManager.packageInstaller
val params = AndroidPackageInstaller.SessionParams(AndroidPackageInstaller.SessionParams.MODE_FULL_INSTALL)
params.setAppPackageName(context.packageName)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
params.setRequireUserAction(AndroidPackageInstaller.SessionParams.USER_ACTION_NOT_REQUIRED)
}
val sessionId = packageInstaller.createSession(params)
packageInstaller.openSession(sessionId).use { session ->
session.openWrite("update.apk", 0, apkFile.length()).use { outputStream ->
FileInputStream(apkFile).use { inputStream ->
inputStream.copyTo(outputStream)
}
session.fsync(outputStream)
}
val intent = Intent(context, InstallResultReceiver::class.java).apply {
action = InstallResultReceiver.ACTION_INSTALL_COMPLETE
}
val pendingIntent = PendingIntent.getBroadcast(
context,
sessionId,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE,
)
session.commit(pendingIntent.intentSender)
}
}
}
@@ -0,0 +1,90 @@
package io.nekohasekai.sfa.vendor
import android.content.Context
import android.util.Log
import androidx.work.BackoffPolicy
import androidx.work.Constraints
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import io.nekohasekai.sfa.database.Settings
import io.nekohasekai.sfa.update.UpdateState
import io.nekohasekai.sfa.update.UpdateTrack
import java.util.concurrent.TimeUnit
class UpdateWorker(private val appContext: Context, params: WorkerParameters) : CoroutineWorker(appContext, params) {
companion object {
private const val WORK_NAME = "AutoUpdate"
private const val TAG = "UpdateWorker"
fun schedule(context: Context) {
if (!Settings.autoUpdateEnabled) {
WorkManager.getInstance(context).cancelUniqueWork(WORK_NAME)
Log.d(TAG, "Auto update disabled, cancelled scheduled work")
return
}
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
val workRequest = PeriodicWorkRequestBuilder<UpdateWorker>(
24,
TimeUnit.HOURS,
)
.setConstraints(constraints)
.setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 1, TimeUnit.HOURS)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
WORK_NAME,
ExistingPeriodicWorkPolicy.KEEP,
workRequest,
)
Log.d(TAG, "Auto update scheduled")
}
}
override suspend fun doWork(): Result {
if (!Settings.autoUpdateEnabled) {
Log.d(TAG, "Auto update disabled, skipping")
return Result.success()
}
Log.d(TAG, "Checking for updates...")
return try {
val track = UpdateTrack.fromString(Settings.updateTrack)
val updateInfo = GitHubUpdateChecker().use { it.checkUpdate(track) }
if (updateInfo == null) {
Log.d(TAG, "No update available")
return Result.success()
}
Log.d(TAG, "Update available: ${updateInfo.versionName}")
UpdateState.setUpdate(updateInfo)
if (Settings.silentInstallEnabled && ApkInstaller.canSilentInstall()) {
Log.d(TAG, "Downloading update...")
val apkFile = ApkDownloader().use { it.download(updateInfo.downloadUrl) }
Log.d(TAG, "Installing update...")
ApkInstaller.install(appContext, apkFile)
Log.d(TAG, "Update installed successfully")
} else {
Log.d(TAG, "Silent install not available, update will be shown on next app launch")
}
Result.success()
} catch (e: Exception) {
Log.e(TAG, "Auto update failed", e)
Result.retry()
}
}
}
@@ -21,6 +21,10 @@
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<!-- For saving images to gallery on older Android versions -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
@@ -29,6 +33,7 @@
android:name=".Application"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:description="@string/xposed_description"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
@@ -36,30 +41,23 @@
android:theme="@style/AppTheme"
tools:targetApi="31">
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
<activity
android:name=".ui.MainActivity"
android:name=".compose.MainActivity"
android:exported="true"
android:icon="@mipmap/ic_launcher"
android:launchMode="singleTask">
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="false" />
android:launchMode="singleTask"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="de.robv.android.xposed.category.MODULE_SETTINGS" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
@@ -93,50 +91,24 @@
<data android:pathPattern="/.*\\.bpf" />
</intent-filter>
<intent-filter android:priority="998">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.OPENABLE" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="application/octet-stream" />
</intent-filter>
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
</intent-filter>
</activity>
<activity
android:name=".ui.ShortcutActivity"
android:excludeFromRecents="true"
android:exported="true"
android:label="@string/quick_toggle"
android:launchMode="singleTask"
android:taskAffinity=""
android:theme="@style/AppTheme.Translucent">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
</intent-filter>
</activity>
<activity
android:name="io.nekohasekai.sfa.ui.profile.NewProfileActivity"
android:exported="false" />
<activity
android:name="io.nekohasekai.sfa.ui.profile.EditProfileActivity"
android:exported="false" />
<activity
android:name="io.nekohasekai.sfa.ui.profile.EditProfileContentActivity"
android:exported="false" />
<activity
android:name="io.nekohasekai.sfa.ui.profileoverride.ProfileOverrideActivity"
android:exported="false" />
<activity
android:name="io.nekohasekai.sfa.ui.profileoverride.PerAppProxyActivity"
android:exported="false" />
<activity
android:name="io.nekohasekai.sfa.ui.debug.DebugActivity"
android:exported="false" />
<activity
android:name="io.nekohasekai.sfa.ui.debug.VPNScanActivity"
android:exported="false" />
<activity
android:name="io.nekohasekai.sfa.ui.profile.QRScanActivity"
android:exported="false" />
<service
android:name=".bg.TileService"
android:directBootAware="true"
@@ -180,6 +152,11 @@
</intent-filter>
</receiver>
<provider
android:name="io.github.libxposed.service.XposedProvider"
android:authorities="${applicationId}.XposedService"
android:exported="true" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.cache"
@@ -190,6 +167,17 @@
android:resource="@xml/cache_paths" />
</provider>
<provider
android:name=".WorkingDirectoryProvider"
android:authorities="${applicationId}.workingdir"
android:exported="true"
android:grantUriPermissions="true"
android:permission="android.permission.MANAGE_DOCUMENTS">
<intent-filter>
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
</intent-filter>
</provider>
</application>
</manifest>
</manifest>
@@ -0,0 +1,9 @@
package io.github.libxposed.service;
interface IXposedScopeCallback {
oneway void onScopeRequestPrompted(String packageName) = 1;
oneway void onScopeRequestApproved(String packageName) = 2;
oneway void onScopeRequestDenied(String packageName) = 3;
oneway void onScopeRequestTimeout(String packageName) = 4;
oneway void onScopeRequestFailed(String packageName, String message) = 5;
}
@@ -0,0 +1,36 @@
package io.github.libxposed.service;
import io.github.libxposed.service.IXposedScopeCallback;
interface IXposedService {
const int API = 100;
const int FRAMEWORK_PRIVILEGE_ROOT = 0;
const int FRAMEWORK_PRIVILEGE_CONTAINER = 1;
const int FRAMEWORK_PRIVILEGE_APP = 2;
const int FRAMEWORK_PRIVILEGE_EMBEDDED = 3;
const String AUTHORITY_SUFFIX = ".XposedService";
const String SEND_BINDER = "SendBinder";
// framework details
int getAPIVersion() = 1;
String getFrameworkName() = 2;
String getFrameworkVersion() = 3;
long getFrameworkVersionCode() = 4;
int getFrameworkPrivilege() = 5;
// scope utilities
List<String> getScope() = 10;
oneway void requestScope(String packageName, IXposedScopeCallback callback) = 11;
String removeScope(String packageName) = 12;
// remote preference utilities
Bundle requestRemotePreferences(String group) = 20;
void updateRemotePreferences(String group, in Bundle diff) = 21;
void deleteRemotePreferences(String group) = 22;
// remote file utilities
String[] listRemoteFiles() = 30;
ParcelFileDescriptor openRemoteFile(String name) = 31;
boolean deleteRemoteFile(String name) = 32;
}
@@ -0,0 +1,14 @@
package io.nekohasekai.sfa.bg;
import android.os.ParcelFileDescriptor;
import io.nekohasekai.sfa.bg.ParceledListSlice;
interface IRootService {
void destroy() = 16777114; // Destroy method defined by Shizuku server
ParceledListSlice getInstalledPackages(int flags, int userId) = 1;
void installPackage(in ParcelFileDescriptor apk, long size, int userId) = 2;
String exportDebugInfo(String outputPath) = 3;
}
@@ -0,0 +1,12 @@
package io.nekohasekai.sfa.bg;
import android.os.ParcelFileDescriptor;
import io.nekohasekai.sfa.bg.ParceledListSlice;
interface IShizukuService {
void destroy() = 16777114; // Destroy method defined by Shizuku server
ParceledListSlice getInstalledPackages(int flags, int userId) = 1;
void installPackage(in ParcelFileDescriptor apk, long size, int userId) = 2;
}
@@ -0,0 +1,3 @@
package io.nekohasekai.sfa.bg;
parcelable LogEntry;
@@ -0,0 +1,3 @@
package io.nekohasekai.sfa.bg;
parcelable PackageEntry;
@@ -0,0 +1,3 @@
package io.nekohasekai.sfa.bg;
parcelable ParceledListSlice;
@@ -0,0 +1,15 @@
package android.content;
import android.os.Bundle;
import android.os.IInterface;
public interface IIntentReceiver extends IInterface {
void performReceive(
Intent intent,
int resultCode,
String data,
Bundle extras,
boolean ordered,
boolean sticky,
int sendingUser);
}

Some files were not shown because too many files have changed in this diff Show More