From 9ba6bd09a11b04f324122c03e6863c3d304ee93f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 31 Aug 2026 11:11:11 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8C=B4=20Sync=202026-08-31=2011:11:11?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luci-theme-footstrap/Makefile | 2 +- .../htdocs/luci-static/resources/fs-search.js | 27 ++++++++++++---- .../resources/menu-footstrap-common.js | 32 ++++++++++++++----- mise/Makefile | 4 +-- wwand/Makefile | 19 +++++++---- 5 files changed, 60 insertions(+), 24 deletions(-) diff --git a/luci-theme-footstrap/Makefile b/luci-theme-footstrap/Makefile index 62fe88909..90a602346 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:=46 +PKG_RELEASE:=47 endif LUCI_TITLE:=Footstrap Theme diff --git a/luci-theme-footstrap/htdocs/luci-static/resources/fs-search.js b/luci-theme-footstrap/htdocs/luci-static/resources/fs-search.js index 5d8a17156..01041d723 100644 --- a/luci-theme-footstrap/htdocs/luci-static/resources/fs-search.js +++ b/luci-theme-footstrap/htdocs/luci-static/resources/fs-search.js @@ -104,7 +104,9 @@ function index() { * An optional package can add rows to the same list — one indexes the SECTION titles inside each * page, so "footstrap" finds System -> Appearance. A source hands over entries in the shape * buildIndex() produces and nothing else: the matching, the ranking and the rendering stay here, - * or two lists would disagree about what a hit is. + * or two lists would disagree about what a hit is. Two fields are the source's alone: `onTake`, + * called when the row is chosen, and `key`, what the recents list stores it under when its `path` + * is not its own (see keyOf). * * Registration is a GLOBAL ARRAY, not an export a package requires. This module is fetched on the * first gesture and most sessions never make it; a package that had to `require` it to register @@ -177,20 +179,31 @@ function search(q, limit) { /* What the palette shows before anything is typed: an admin lives in three or four pages, so the * empty state is its most-used view. * - * Only the path is stored, never the title — the title is resolved through the index on every - * render, so it follows the UI language and a page removed with its package drops out instead of - * lingering as a dead row. */ + * Only the KEY is stored, never the title — the title is resolved through the pool on every + * render, so it follows the UI language and a row whose package went away drops out instead of + * lingering as a dead row. A page's key is its menu path; a row from a source carries its own + * `key`, because a section has no dispatcher node and therefore no path that is only its own — + * the sections source keys one `admin/system/system#Footstrap`, the page it is on plus its own + * heading. */ const RECENT_KEY = 'fs-recent'; const RECENT_MAX = 8; +/* the string a row is remembered under, and the one menu-footstrap-common's remember() writes */ +function keyOf(e) { + return e.key || e.path; +} + /* The list is WRITTEN by menu-footstrap-common.js, which is on every page — this module is not any * more, and a palette that only loads when it is opened cannot be what records where the admin has * been. Read here, at open time, so it is always current. `prefs.lsGetArr` owns the parse, the - * corruption guard and the Array check; only the "these are paths" filter belongs here. */ + * corruption guard and the Array check; only the "these are keys" filter belongs here. */ function recentEntries() { const recent = prefs.lsGetArr(RECENT_KEY).filter((x) => typeof x === 'string'); - const byPath = new Map(index().map((e) => [ e.path, e ])); - return recent.map((p) => byPath.get(p)).filter(Boolean).slice(0, RECENT_MAX); + /* pool(), not index(): a section is recalled exactly as a page is. Against the index alone a + * section key resolved to nothing and the row silently vanished, so taking "Footstrap" left + * only "System" in the list — the page path is all either row carries. */ + const byKey = new Map(pool().map((e) => [ keyOf(e), e ])); + return recent.map((k) => byKey.get(k)).filter(Boolean).slice(0, RECENT_MAX); } /* ---- the palette -------------------------------------------------------- */ diff --git a/luci-theme-footstrap/htdocs/luci-static/resources/menu-footstrap-common.js b/luci-theme-footstrap/htdocs/luci-static/resources/menu-footstrap-common.js index f5f654aaa..43ba80a87 100644 --- a/luci-theme-footstrap/htdocs/luci-static/resources/menu-footstrap-common.js +++ b/luci-theme-footstrap/htdocs/luci-static/resources/menu-footstrap-common.js @@ -54,11 +54,20 @@ const RECENT_KEY = 'fs-recent'; const RECENT_MAX = 8; const RECENT_WARM = 5; -function remember(segs) { - if (!Array.isArray(segs) || !segs.length) return; - const path = segs.join('/'); +/* A key is a menu path, or a page path plus the heading of a section inside it + * (`admin/system/system#Footstrap`) — a section has no dispatcher node to name it, and only the + * source that produced the row can build that half. Exported for exactly that: the writer stays + * one function, or the two halves would drift on the cap and the de-duplication. */ +function remember(key) { + if (typeof key !== 'string' || !key) return; const recent = prefs.lsGetArr(RECENT_KEY).filter((x) => typeof x === 'string'); - prefs.lsSet(RECENT_KEY, JSON.stringify([ path ].concat(recent.filter((p) => p !== path)).slice(0, RECENT_MAX))); + prefs.lsSet(RECENT_KEY, JSON.stringify([ key ].concat(recent.filter((p) => p !== key)).slice(0, RECENT_MAX))); +} + +/* the page half of a key: what the router can navigate to and what warmRecent() prefetches */ +function pageOf(key) { + const h = key.indexOf('#'); + return h < 0 ? key : key.slice(0, h); } /* ---- warm the pages this admin actually uses ---- @@ -75,8 +84,10 @@ function remember(segs) { function warmRecent() { try { if (navigator.connection && navigator.connection.saveData) return; } catch (e) {} const here = (L.env.dispatchpath || []).join('/'); - const paths = prefs.lsGetArr(RECENT_KEY) - .filter((p) => typeof p === 'string' && p !== here).slice(0, RECENT_WARM); + /* Keys, not paths: a section key names the page it sits on, and two sections of one page must + * warm it once — the module chain is the page's. */ + const keys = prefs.lsGetArr(RECENT_KEY).filter((p) => typeof p === 'string'); + const paths = [ ...new Set(keys.map(pageOf)) ].filter((p) => p !== here).slice(0, RECENT_WARM); if (!paths.length) return; const go = () => paths.forEach((p) => router.prefetchSegs(p.split('/'))); if (typeof window.requestIdleCallback === 'function') @@ -91,8 +102,9 @@ function wireSearch() { const RT = window.L; /* the page this full load landed on; onNavigate covers the SPA path afterwards */ - remember(L.env.dispatchpath || []); - router.onNavigate(remember); + const rememberSegs = (segs) => remember((segs || []).join('/')); + rememberSegs(L.env.dispatchpath); + router.onNavigate(rememberSegs); warmRecent(); /* One fetch, on the first gesture. The module builds its overlay and opens itself; every later @@ -219,6 +231,10 @@ ensureOverviewHelpers(); * halves (fs-menutree, fs-prefs) are separate modules. */ return baseclass.extend({ + /* the seam a companion package writes its own rows into the recents list through; see + * remember() for what a key is */ + remember, + init(renderMainMenu) { /* First, and outside the promise: a third-party sheet that outranks the chrome is already * painting (fs-sheets: openclash's `* { margin: 0; padding: 0 }`). Deferring this to diff --git a/mise/Makefile b/mise/Makefile index cdd6f5c66..226031c4d 100644 --- a/mise/Makefile +++ b/mise/Makefile @@ -14,8 +14,8 @@ MISE_HASH_x86_64:=3832f39c325e343f81fe3d92b2447c5d1a5eea1bc85092bb7b6c2580622264 MISE_HASH_aarch64:=06186cfbfe947049b21d58575fb0ea800cc26ed1375f20f4b678cb3a9d679437 PKG_NAME:=mise -PKG_VERSION:=2026.8.14 -PKG_RELEASE:=1 +PKG_VERSION:=2026.8.15 +PKG_RELEASE:=2 PKG_SOURCE:=$(PKG_NAME)-v$(PKG_VERSION)-linux-$(MISE_ARCH)-musl.tar.gz PKG_SOURCE_URL:=https://github.com/jdx/mise/releases/download/v$(PKG_VERSION)/ PKG_HASH:=skip diff --git a/wwand/Makefile b/wwand/Makefile index 675969f90..8ffd5ded0 100644 --- a/wwand/Makefile +++ b/wwand/Makefile @@ -43,12 +43,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=wwand -PKG_RELEASE:=14 +PKG_RELEASE:=15 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL:=https://github.com/ddimension/wwand.git -PKG_SOURCE_VERSION:=199a2f8a2f242376adb6e3781443750f3d71c502 -PKG_SOURCE_DATE:=2026-08-30 +PKG_SOURCE_VERSION:=95acd0064d8307cde67a05c5a93bf9e62254ab8d +PKG_SOURCE_DATE:=2026-08-31 PKG_MIRROR_HASH:=skip PKG_LICENSE:=GPL-2.0-only @@ -89,15 +89,15 @@ UCDIR:=/usr/share/ucode/wwand # silently ship in the base too. The per-backend install lists below carry the # QMI/MBIM/NCM/eSIM modules; main.uc/wwandctl.uc install as executables. WWAND_BASE_UC:=apndb.uc atcmd.uc atcmd_parse.uc atport.uc backend.uc board.uc \ - client.uc config.uc config_check.uc context_common.uc context_monitor_qmi.uc \ + carrier_config.uc client.uc config.uc config_check.uc context_common.uc context_monitor_qmi.uc \ ctx_settings.uc daemon.uc discovery.uc hwops.uc log.uc modem_datapath_qmi.uc \ modem_common.uc modem_init_qmi.uc modem_quirks.uc modeswitch.uc ncm_vendors.uc \ netlink.uc netsel_ops.uc protocol_switch.uc reconnect.uc recovery.uc regdetail.uc \ sim.uc sim_plmn.uc simops.uc sms.uc sms_pdu.uc telemetry_mbim.uc telemetry_ncm.uc \ telemetry_qmi.uc transport.uc ubus.uc WWAND_BASE_CODEC:=arfcn_bands.uc hex.uc qmux.uc tlv.uc -WWAND_BASE_SCHEMA:=ctl.uc dms.uc dsd.uc loc.uc loc_lazy.uc merge.uc nas.uc rat.uc \ - uim.uc wda.uc wds.uc wms.uc wms_lazy.uc +WWAND_BASE_SCHEMA:=cat.uc ctl.uc dms.uc dsd.uc loc.uc loc_lazy.uc merge.uc nas.uc pdc.uc rat.uc \ + tmd.uc uim.uc wda.uc wds.uc wms.uc wms_lazy.uc # DEVELOPERS: set CONFIG_WWAND_UCODE_SOURCE to ship readable .uc source # instead of bytecode — for editing modules live under /usr/share/ucode/wwand @@ -201,6 +201,13 @@ define Package/wwand/install # new_id bind / late kmodloader) re-kick a modem parked in no_at_port backoff $(INSTALL_DIR) $(1)/etc/hotplug.d/tty $(INSTALL_DATA) $(PKG_BUILD_DIR)/files/wwand.hotplug.tty $(1)/etc/hotplug.d/tty/20-wwand + # usb hotplug: bind qmi_wwan to the Huawei E1820's ethernet function. The + # kernel hands the device to qmi_wwan (cdc_ether blacklists it) but its + # table entry wants a vendor-specific class this old 802.3 layout does not + # carry, so a scoped dynamic new_id is what makes the control channel appear + # at all — and it is volatile, so it has to be re-created on every replug. + $(INSTALL_DIR) $(1)/etc/hotplug.d/usb + $(INSTALL_DATA) $(PKG_BUILD_DIR)/files/wwand.hotplug.e1820 $(1)/etc/hotplug.d/usb/21-wwand-e1820 # NOTE: the kernel-wwan-subsystem hotplug (/etc/hotplug.d/wwan/20-wwand) is # NOT installed here — it belongs to wwand-mhi, which also pulls the MHI # drivers that create /sys/class/wwan in the first place (procd only arms a