diff --git a/gecoosac/Makefile b/gecoosac/Makefile index b087be77..e474ee48 100644 --- a/gecoosac/Makefile +++ b/gecoosac/Makefile @@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=gecoosac PKG_VERSION:=2.2.20251015 -PKG_RELEASE:=19 +PKG_RELEASE:=20 PKG_MAINTAINER:=Roc Lai PKG_LICENSE:=AGPL-3.0-only diff --git a/gecoosac/files/etc/init.d/gecoosac b/gecoosac/files/etc/init.d/gecoosac index 7daa7d9c..b2bcd51e 100644 --- a/gecoosac/files/etc/init.d/gecoosac +++ b/gecoosac/files/etc/init.d/gecoosac @@ -86,50 +86,62 @@ normalize_path() { printf '%s\n' "$normalized" } +path_has_clear_stage_component() { + local path="$1" part rest + + path="$(normalize_path "$path")" || return 1 + rest="${path#/}" + while [ -n "$rest" ]; do + part="${rest%%/*}" + if [ "$part" = "$rest" ]; then + rest="" + else + rest="${rest#*/}" + fi + case "$part" in + .gecoosac-clear.*) return 0 ;; + esac + done + + return 1 +} + path_uses_clear_stage() { local path="$1" resolved - path="$(normalize_path "$path")" || return 1 - case "$path" in - /.gecoosac-clear.*|/.gecoosac-clear.*/*|*/.gecoosac-clear.*|*/.gecoosac-clear.*/*) - return 0 - ;; - esac - - if [ -e "$1" ] || [ -L "$1" ]; then - resolved="$(readlink -f "$1" 2>/dev/null)" || return 1 - resolved="$(normalize_path "$resolved")" || return 1 - case "$resolved" in - /.gecoosac-clear.*|/.gecoosac-clear.*/*|*/.gecoosac-clear.*|*/.gecoosac-clear.*/*) - return 0 - ;; - esac + path_has_clear_stage_component "$path" && return 0 + if [ -e "$path" ] || [ -L "$path" ]; then + resolved="$(readlink -f "$path" 2>/dev/null)" || return 1 + path_has_clear_stage_component "$resolved" && return 0 fi return 1 } +is_supported_upload_path() { + local path="$1" storage + + path="$(normalize_path "$path")" || return 1 + path_uses_clear_stage "$path" && return 1 + [ "$path" = "$DEFAULT_UPLOAD_DIR" ] && return 0 + + case "$path" in + /mnt/*/gecoosac/upload) + storage="${path#/mnt/}" + storage="${storage%/gecoosac/upload}" + [ -n "$storage" ] && [ "${storage#*/}" = "$storage" ] + ;; + *) return 1 ;; + esac +} + is_safe_upload_dir() { local path physical path="$(normalize_path "$1")" || return 1 physical="$(managed_dir_path upload "$path")" || return 1 - case "$path" in - /etc/gecoosac|/etc/gecoosac/*) return 1 ;; - esac - case "$physical" in - /etc/gecoosac|/etc/gecoosac/*) return 1 ;; - esac - - case "$path" in - */gecoosac/upload) ;; - *) return 1 ;; - esac - case "$physical" in - */gecoosac/upload) return 0 ;; - esac - - return 1 + is_supported_upload_path "$path" || return 1 + is_supported_upload_path "$physical" } is_path_in_dir() { @@ -614,18 +626,35 @@ managed_dir_path() { esac } +ensure_upload_storage_root() { + local path="$1" root + + is_supported_upload_path "$path" || return 1 + case "$path" in + "$DEFAULT_UPLOAD_DIR") root=/tmp ;; + /mnt/*/gecoosac/upload) root="${path%/gecoosac/upload}" ;; + *) return 1 ;; + esac + + [ -d "$root" ] && [ ! -L "$root" ] || return 1 + is_secure_upload_dir "$root" +} + ensure_managed_dir() { local role="$1" path physical path="$(normalize_path "$2")" || return 1 + physical="$(managed_dir_path "$role" "$path")" || return 1 case "$role" in - upload) is_safe_upload_dir "$path" || return 1 ;; + upload) + is_safe_upload_dir "$path" || return 1 + ensure_upload_storage_root "$physical" || return 1 + ;; db) is_safe_db_dir "$path" "$upload_dir" || return 1 ;; pid) is_safe_pid_dir "$path" "$upload_dir" || return 1 ;; tls) [ "$path" = "/etc/gecoosac/tls" ] || return 1 ;; *) return 1 ;; esac - physical="$(managed_dir_path "$role" "$path")" || return 1 ensure_secure_dir_tree "$physical" || return 1 [ -d "$path" ] } diff --git a/gecoosac/files/etc/uci-defaults/gecoosac b/gecoosac/files/etc/uci-defaults/gecoosac index f00d9034..817ecbef 100644 --- a/gecoosac/files/etc/uci-defaults/gecoosac +++ b/gecoosac/files/etc/uci-defaults/gecoosac @@ -90,27 +90,62 @@ managed_dir_path() { esac } +path_has_clear_stage_component() { + local path="$1" part rest + + path="$(normalize_path "$path")" || return 1 + rest="${path#/}" + while [ -n "$rest" ]; do + part="${rest%%/*}" + if [ "$part" = "$rest" ]; then + rest="" + else + rest="${rest#*/}" + fi + case "$part" in + .gecoosac-clear.*) return 0 ;; + esac + done + + return 1 +} + +path_uses_clear_stage() { + local path="$1" resolved + + path_has_clear_stage_component "$path" && return 0 + if [ -e "$path" ] || [ -L "$path" ]; then + resolved="$(readlink -f "$path" 2>/dev/null)" || return 1 + path_has_clear_stage_component "$resolved" && return 0 + fi + + return 1 +} + +is_supported_upload_path() { + local path="$1" storage + + path="$(normalize_path "$path")" || return 1 + path_uses_clear_stage "$path" && return 1 + [ "$path" = "$DEFAULT_UPLOAD_DIR" ] && return 0 + + case "$path" in + /mnt/*/gecoosac/upload) + storage="${path#/mnt/}" + storage="${storage%/gecoosac/upload}" + [ -n "$storage" ] && [ "${storage#*/}" = "$storage" ] + ;; + *) return 1 ;; + esac +} + is_safe_upload_dir() { local path physical path="$(normalize_path "$1")" || return 1 physical="$(managed_dir_path upload "$path")" || return 1 - case "$path" in - /etc/gecoosac|/etc/gecoosac/*) return 1 ;; - esac - case "$physical" in - /etc/gecoosac|/etc/gecoosac/*) return 1 ;; - esac - - case "$path" in - */gecoosac/upload) ;; - *) return 1 ;; - esac - case "$physical" in - */gecoosac/upload) return 0 ;; - esac - - return 1 + is_supported_upload_path "$path" || return 1 + is_supported_upload_path "$physical" } is_path_in_dir() { @@ -247,7 +282,7 @@ cleanup_legacy_upload_dir() { normalize_dir_option() { local option="$1" - local validator="$3" + local validator="$2" local value normalized upload_dir value="$(uci -q get "gecoosac.config.${option}")" @@ -320,8 +355,8 @@ migrate_config() { ensure_section || exit 1 migrate_config || exit 1 normalize_upload_dir || exit 1 -normalize_dir_option db_dir "$DEFAULT_DB_DIR" is_safe_db_dir || exit 1 -normalize_dir_option piddir "$DEFAULT_PID_DIR" is_safe_pid_dir || exit 1 +normalize_dir_option db_dir is_safe_db_dir || exit 1 +normalize_dir_option piddir is_safe_pid_dir || exit 1 set_default enabled 0 || exit 1 set_default port 60650 || exit 1 set_default isonlyoneprot 1 || exit 1 diff --git a/ghttpd/Makefile b/ghttpd/Makefile index 4f99aaf5..8fb26c11 100644 --- a/ghttpd/Makefile +++ b/ghttpd/Makefile @@ -10,8 +10,8 @@ include $(TOPDIR)/rules.mk PKG_ARCH_ghttpd:=$(ARCH) PKG_NAME:=ghttpd -PKG_VERSION:=0.0.2 -PKG_RELEASE:=4 +PKG_VERSION:=0.0.3 +PKG_RELEASE:=5 PKG_SOURCE:=$(PKG_NAME)-binary-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://github.com/Carseason/openwrt-packages/releases/download/prebuilt/ PKG_HASH:=skip @@ -49,6 +49,15 @@ if [ -z "$${IPKG_INSTROOT}" ]; then fi endef +define Package/$(PKG_NAME)/postrm +#!/bin/sh +for service in uhttpd nginx; do + [ -x "/etc/init.d/$$service" ] || continue + "/etc/init.d/$$service" enabled && "/etc/init.d/$$service" restart +done +exit 0 +endef + define Build/Configure endef diff --git a/ghttpd/files/ghttpd.init b/ghttpd/files/ghttpd.init index d010b7ab..f7e375b6 100755 --- a/ghttpd/files/ghttpd.init +++ b/ghttpd/files/ghttpd.init @@ -19,7 +19,7 @@ stop_default_httpd_services() { start_default_httpd_services() { for service in uhttpd nginx; do [ -x "/etc/init.d/$service" ] || continue - "/etc/init.d/$service" enabled && "/etc/init.d/$service" start + "/etc/init.d/$service" enabled && "/etc/init.d/$service" restart done } diff --git a/luci-app-gecoosac/Makefile b/luci-app-gecoosac/Makefile index 98d23a30..35cd0f1e 100644 --- a/luci-app-gecoosac/Makefile +++ b/luci-app-gecoosac/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-gecoosac PKG_VERSION:=2.2 -PKG_RELEASE:=19 +PKG_RELEASE:=20 LUCI_TITLE:=LuCI Support for gecoosac LUCI_DEPENDS:=+luci-base +gecoosac +gecoosac-files diff --git a/luci-app-gecoosac/htdocs/luci-static/resources/view/gecoosac.js b/luci-app-gecoosac/htdocs/luci-static/resources/view/gecoosac.js index dcadbec7..5c4d4392 100644 --- a/luci-app-gecoosac/htdocs/luci-static/resources/view/gecoosac.js +++ b/luci-app-gecoosac/htdocs/luci-static/resources/view/gecoosac.js @@ -11,7 +11,6 @@ const DEFAULT_DB_DIR = '/etc/gecoosac'; const DEFAULT_CRT_FILE = '/etc/gecoosac/tls/gecoosac.crt'; const DEFAULT_KEY_FILE = '/etc/gecoosac/tls/gecoosac.key'; const DEFAULT_PID_DIR = '/var/run'; -const CONFIG_BACKUP_DIR = '/etc/gecoosac'; const DB_DIR_PREFIXES = [ '/etc/gecoosac', '/tmp/gecoosac', '/var/lib/gecoosac' ]; const PID_DIR_PREFIXES = [ '/var/run', '/tmp/gecoosac' ]; const CLEAR_STAGE_PATH_ERROR = _('Paths under .gecoosac-clear.* are reserved for upload cleanup.'); @@ -181,13 +180,26 @@ function usesManagedPath(value) { (path !== null && (path.indexOf('/var/') === 0 || path.indexOf('/var/run/') === 0)); } +function uploadStorageRoot(path) { + if (path === DEFAULT_UPLOAD_DIR) + return '/tmp'; + + const segments = path === null ? [] : path.split('/'); + + if (segments.length === 5 && segments[1] === 'mnt' && segments[2] && + segments[3] === 'gecoosac' && segments[4] === 'upload') + return '/mnt/' + segments[2]; + + return null; +} + function validUploadDir(value, policy) { const path = normalizePath(value); const physical = managedPath(value, policy); + const storageRoot = uploadStorageRoot(path); + const physicalStorageRoot = uploadStorageRoot(physical); - return path !== null && physical !== null && path.endsWith('/gecoosac/upload') && - physical.endsWith('/gecoosac/upload') && !pathInDir(path, CONFIG_BACKUP_DIR) && - !pathInDir(physical, CONFIG_BACKUP_DIR); + return !usesClearStagePath(value) && storageRoot !== null && storageRoot === physicalStorageRoot; } function validPathPrefix(value, prefixes) { @@ -451,7 +463,7 @@ return view.extend({ httpsOption.onchange = revalidateProtocolOptions; o = s.option(form.Value, 'upload_dir', _('Upload dir path'), - _('Upload AP upgrade firmware here. Use an absolute path ending with /gecoosac/upload, for example /tmp/gecoosac/upload.
Do not place it under /etc/gecoosac because that directory is backed up during sysupgrade.')); + _('Upload AP upgrade firmware here. Use /tmp/gecoosac/upload or /mnt/storage-name/gecoosac/upload. The /mnt/storage-name directory must already exist and be root-owned and private.')); uploadDirOption = o; o.placeholder = DEFAULT_UPLOAD_DIR; o.default = DEFAULT_UPLOAD_DIR; @@ -465,7 +477,7 @@ return view.extend({ return validUploadDir(value, pathPolicy) ? true - : _('Upload directory must be an absolute path ending with /gecoosac/upload and must not be under /etc/gecoosac.'); + : _('Upload directory must be /tmp/gecoosac/upload or /mnt/storage-name/gecoosac/upload.'); }; o = s.option(form.Value, 'db_dir', _('Database dir path'), diff --git a/luci-app-gecoosac/po/zh_Hans/gecoosac.po b/luci-app-gecoosac/po/zh_Hans/gecoosac.po index 1efbc5db..8c51604c 100644 --- a/luci-app-gecoosac/po/zh_Hans/gecoosac.po +++ b/luci-app-gecoosac/po/zh_Hans/gecoosac.po @@ -55,17 +55,15 @@ msgid "Upload dir path" msgstr "上传目录" msgid "" -"Upload AP upgrade firmware here. Use an absolute path ending with " -"/gecoosac/upload, for example /tmp/gecoosac/upload.
Do not place it " -"under /etc/gecoosac because that directory is backed up " -"during sysupgrade." +"Upload AP upgrade firmware here. Use /tmp/gecoosac/upload or /mnt/storage-" +"name/gecoosac/upload. The /mnt/storage-name directory must already exist " +"and be root-owned and private." msgstr "" -"AP 升级固件会上传到此目录。请使用以 /gecoosac/upload 结尾的绝对路径,例如 " -"/tmp/gecoosac/upload。
不要放在 /etc/gecoosac 下,因为该目录会在 " -"sysupgrade 时被备份。" +"AP 升级固件会上传到此目录。请使用 /tmp/gecoosac/upload 或 /mnt/storage-" +"name/gecoosac/upload。/mnt/storage-name 目录必须已存在、归 root 所有且权限安全。" -msgid "Upload directory must be an absolute path ending with /gecoosac/upload and must not be under /etc/gecoosac." -msgstr "上传目录必须是以 /gecoosac/upload 结尾的绝对路径,且不能位于 /etc/gecoosac 下。" +msgid "Upload directory must be /tmp/gecoosac/upload or /mnt/storage-name/gecoosac/upload." +msgstr "上传目录必须是 /tmp/gecoosac/upload 或 /mnt/storage-name/gecoosac/upload。" msgid "Database dir path" msgstr "数据库目录" diff --git a/luci-app-gecoosac/po/zh_Hant/gecoosac.po b/luci-app-gecoosac/po/zh_Hant/gecoosac.po index ca2dd4a8..01d24d62 100644 --- a/luci-app-gecoosac/po/zh_Hant/gecoosac.po +++ b/luci-app-gecoosac/po/zh_Hant/gecoosac.po @@ -55,17 +55,15 @@ msgid "Upload dir path" msgstr "上傳目錄" msgid "" -"Upload AP upgrade firmware here. Use an absolute path ending with " -"/gecoosac/upload, for example /tmp/gecoosac/upload.
Do not place it " -"under /etc/gecoosac because that directory is backed up " -"during sysupgrade." +"Upload AP upgrade firmware here. Use /tmp/gecoosac/upload or /mnt/storage-" +"name/gecoosac/upload. The /mnt/storage-name directory must already exist " +"and be root-owned and private." msgstr "" -"AP 升級韌體會上傳到此目錄。請使用以 /gecoosac/upload 結尾的絕對路徑,例如 " -"/tmp/gecoosac/upload。
不要放在 /etc/gecoosac 下,因為該目錄會在 " -"sysupgrade 時被備份。" +"AP 升級韌體會上傳到此目錄。請使用 /tmp/gecoosac/upload 或 /mnt/storage-" +"name/gecoosac/upload。/mnt/storage-name 目錄必須已存在、由 root 擁有且權限安全。" -msgid "Upload directory must be an absolute path ending with /gecoosac/upload and must not be under /etc/gecoosac." -msgstr "上傳目錄必須是以 /gecoosac/upload 結尾的絕對路徑,且不能位於 /etc/gecoosac 下。" +msgid "Upload directory must be /tmp/gecoosac/upload or /mnt/storage-name/gecoosac/upload." +msgstr "上傳目錄必須是 /tmp/gecoosac/upload 或 /mnt/storage-name/gecoosac/upload。" msgid "Database dir path" msgstr "資料庫目錄" diff --git a/luci-app-gecoosac/root/usr/libexec/rpcd/luci.gecoosac b/luci-app-gecoosac/root/usr/libexec/rpcd/luci.gecoosac index 9884e9f2..4106ce88 100755 --- a/luci-app-gecoosac/root/usr/libexec/rpcd/luci.gecoosac +++ b/luci-app-gecoosac/root/usr/libexec/rpcd/luci.gecoosac @@ -58,6 +58,55 @@ normalize_path() { printf '%s\n' "$normalized" } +path_has_clear_stage_component() { + local path="$1" part rest + + path="$(normalize_path "$path")" || return 1 + rest="${path#/}" + while [ -n "$rest" ]; do + part="${rest%%/*}" + if [ "$part" = "$rest" ]; then + rest="" + else + rest="${rest#*/}" + fi + case "$part" in + .gecoosac-clear.*) return 0 ;; + esac + done + + return 1 +} + +path_uses_clear_stage() { + local path="$1" resolved + + path_has_clear_stage_component "$path" && return 0 + if [ -e "$path" ] || [ -L "$path" ]; then + resolved="$(readlink -f "$path" 2>/dev/null)" || return 1 + path_has_clear_stage_component "$resolved" && return 0 + fi + + return 1 +} + +is_supported_upload_path() { + local path="$1" storage + + path="$(normalize_path "$path")" || return 1 + path_uses_clear_stage "$path" && return 1 + [ "$path" = "$DEFAULT_UPLOAD_DIR" ] && return 0 + + case "$path" in + /mnt/*/gecoosac/upload) + storage="${path#/mnt/}" + storage="${storage%/gecoosac/upload}" + [ -n "$storage" ] && [ "${storage#*/}" = "$storage" ] + ;; + *) return 1 ;; + esac +} + managed_dir_path() { local role="$1" path anchor @@ -106,16 +155,7 @@ safe_upload_path() { local path path="$(normalize_path "$1")" || return 1 - case "$path" in - /etc/gecoosac|/etc/gecoosac/*) return 1 ;; - esac - [ "$path" = "/tmp/gecoosac/upload" ] && return 0 - - case "$path" in - */gecoosac/upload) return 0 ;; - esac - - return 1 + is_supported_upload_path "$path" } path_in_dir() { diff --git a/luci-app-passwall/Makefile b/luci-app-passwall/Makefile index fc9b94ea..fed4d100 100644 --- a/luci-app-passwall/Makefile +++ b/luci-app-passwall/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall PKG_VERSION:=26.8.1 -PKG_RELEASE:=210 +PKG_RELEASE:=211 PKG_PO_VERSION:=$(PKG_VERSION) PKG_CONFIG_DEPENDS:= \ diff --git a/luci-app-passwall/root/usr/share/passwall/0_default_config b/luci-app-passwall/root/usr/share/passwall/0_default_config index a0c018ce..d337370f 100644 --- a/luci-app-passwall/root/usr/share/passwall/0_default_config +++ b/luci-app-passwall/root/usr/share/passwall/0_default_config @@ -103,30 +103,7 @@ config nodes 'myshunt' config shunt_rules 'DirectGame' option remarks 'DirectGame' option network 'tcp,udp' - option domain_list '# steam直连域名获取国内CDN走国内线路下载 -cm.steampowered.com -steamserver.net - -# steam国内CDN华为云 -steampipe.steamcontent.tnkjmec.com -# steam国内CDN白山云 -st.dl.eccdnx.com -st.dl.bscstorage.net -st.dl.pinyuncloud.com -# steam国内CDN新流云(原金山云)(支持ipv6) -dl.steam.clngaa.com -# steam国内CDN网宿 -cdn.mileweb.cs.steampowered.com.8686c.com -cdn-ws.content.steamchina.com -# steam国内CDN腾讯云 (蒸汽中国独占) -cdn-qc.content.steamchina.com -# steam国内CDN阿里云(支持ipv6) -cdn-ali.content.steamchina.com -xz.pphimalayanrt.com -lv.queniujq.cn -alibaba.cdn.steampipe.steamcontent.com - -# 国内游戏geosite域名 + option domain_list '# 国内游戏geosite域名 geosite:category-games@cn' option ip_list '# steam直连IP 45.121.184.0/24 @@ -196,6 +173,9 @@ steamcloud-sydney.storage.googleapis.com steamcloud-taiwan.storage.googleapis.com steamcloud-eu.storage.googleapis.com domain:blizzard.com +#Steam支付 +domain:checkout.steampowered.com +domain:globalapi.smart2pay.com geosite:category-games' config shunt_rules 'AIGC' diff --git a/luci-theme-footstrap/Makefile b/luci-theme-footstrap/Makefile index d39dc469..1ff4ecb2 100644 --- a/luci-theme-footstrap/Makefile +++ b/luci-theme-footstrap/Makefile @@ -17,7 +17,7 @@ LUCI_NAME:=luci-theme-footstrap FOOTSTRAP_VERSION?= ifneq ($(FOOTSTRAP_VERSION),) PKG_VERSION:=$(FOOTSTRAP_VERSION) -PKG_RELEASE:=8 +PKG_RELEASE:=9 endif LUCI_TITLE:=Footstrap Theme diff --git a/luci-theme-footstrap/styles/pages/20-overview.css b/luci-theme-footstrap/styles/pages/20-overview.css index f72e56d4..c56f7af4 100644 --- a/luci-theme-footstrap/styles/pages/20-overview.css +++ b/luci-theme-footstrap/styles/pages/20-overview.css @@ -281,9 +281,36 @@ white-space: normal; /* overflow-wrap dropped — base/40-tables.css breaks every cell; the copy that stood here * moved nothing against 267 matching cells. */ - padding-inline-end: min(200px, 50%); + padding-inline-end: min(230px, 50%); font-size: var(--fs-type); font-weight: var(--fs-weight); color: var(--fs-text); } + + /* ON A NARROW CARD THE VALUE TAKES ITS OWN LINE — a reserve cannot be made to fit there. + * + * The value is out of flow (the bar's ::after, pinned over its far edge), so the only thing + * keeping it off the label is that padding, and the two are sized by different things: the + * padding is a fraction of the CARD, the value is however wide LuCI's formatter made it. + * `16.66 GiB / 16.66 GiB (100%)` measures 218px in the mono face at --fs-type-xs, and 50% of a + * 380px phone card is 190 — so the figure printed straight through the end of the label: + * "Дисковое пространство330.54 MiB / 7.98 GiB (4%)", reported from a phone. Measured across the + * range: the reserve is at its 230px cap and clear of every value from a ~460px card up, and + * short of the longest ones below that. + * + * Below the threshold the label keeps the whole line and the bar drops by one --fs-type-xs + * line, which is the band the value then sits in — right-aligned, on its own line, above its + * own bar. It costs one line per row on a phone and cannot collide at any label length. + * `fs-view` rather than a container of its own: the Memory/Storage cards are already single + * column at 800px (the grid collapse above), so below 560px the card IS the view minus its + * gutters, and adding `container-type` to the card would size-contain a box the port tiles + * deliberately keep uncontained. */ + @container fs-view (max-width: 560px) { + body[data-page="admin-status-overview"] .cbi-section .table:not(:has(.tr.table-titles)) .tr:has(.cbi-progressbar) .td:first-child { + padding-inline-end: 0; + } + body[data-page="admin-status-overview"] .cbi-section .table:not(:has(.tr.table-titles)) .tr:has(.cbi-progressbar) .cbi-progressbar { + margin-block-start: calc(var(--fs-type-xs) * var(--fs-leading)); + } + } /* bar cell — spans full width on row 2 */ body[data-page="admin-status-overview"] .cbi-section .table:not(:has(.tr.table-titles)) .tr:has(.cbi-progressbar) .td:nth-child(2) { grid-column: 1 / -1; grid-row: 2; diff --git a/luci-theme-footstrap/styles/pages/70-syslog.css b/luci-theme-footstrap/styles/pages/70-syslog.css index fd71c985..5e1ce6d7 100644 --- a/luci-theme-footstrap/styles/pages/70-syslog.css +++ b/luci-theme-footstrap/styles/pages/70-syslog.css @@ -1,4 +1,65 @@ @layer page { + /* THE FILTER BAR, which LuCI writes as a run of inline text. + * + * `tools/views.js` (LogreadBox, shared by System Log, Kernel Log and every third-party page + * built on it) emits two bare `
`, each holding label / control + * pairs with nothing around a pair: ` + * + * carries the theme's own 210px field width, which a `minmax(0, 1fr)` track does not + * override — measured at 360px, where both selects hung 3px past the view. */ + min-width: 0; + max-width: 100%; + } + /* the two numeric-ish fields keep a sane ceiling: a max-rows box as wide as the filter reads as + * a second search field. */ + #logMaxRows { flex-basis: 6ch; max-width: 12ch; } + + /* ON A PHONE, ONE FILTER PER LINE. Wrapping alone still fills each line greedily, so the second + * `Не` rode the end of the first line while the `уровень:` it inverts opened the next one. The + * first row is a fixed alternation of exactly two filters — invert label, checkbox, name label, + * control, twice — so a four-column grid puts one filter on each line. + * + * The SECOND row is deliberately left to the wrapping above. It holds two filters of different + * shapes (a checkbox filter and a bare `Макс. строк:` pair), so the same four tracks would be + * sized by items that are not each other's counterparts: measured, the max-rows box defined + * column 2 and pushed `содержит:` and its field off the card. Wrapping puts that row's two + * filters on two lines by itself, which is all the grid was for. */ + @container fs-view (max-width: 560px) { + div:has(> #logFacilitySelect) { + display: grid; + grid-template-columns: auto auto auto minmax(0, 1fr); + column-gap: var(--fs-space-1); + } + } + /* System Log / Kernel Log (luci-mod-status): the whole log is a readonly *