33 Commits
Author SHA1 Message Date
DrSmoothl 5924346823 chore(release): v1.9.2 2026-05-24 18:01:50 +08:00
DrSmoothl 607a7b319b chore(release): v1.9.1 2026-05-21 17:33:35 +08:00
手瓜一十雪 36c3ed031b Apply formatting and fix web request utilities
Add consistent indent rule to ESLint and normalize formatting/indentation across multiple files. Refactor RequestUtil: improve cookie extraction, redirect handling, and HttpGetJson/HttpGetText request flow; fix cookie string builder formatting. Fix uploadGroupNoticeImage promise/error handling and network request flow. Also apply minor style/refactor tweaks in bridge web APIs, onebot handlers, message-store SQL alignment, and Proton AST collector utilities to improve readability and correctness.
2026-05-20 19:16:06 +08:00
手瓜一十雪 aea2676c31 Add TypeScript ESLint configs and lint scripts
Add a repository .editorconfig and a root eslint.config.ts to standardize formatting and linting. Migrate packages/webui ESLint config to TypeScript (eslint.config.ts) with project-specific rules and tsconfigRootDir handling, and remove the old JS config. Update package.json to run ESLint directly (add lint and lint:fix) and add required ESLint/dev dependencies; also add jiti to packages/webui/package.json. Include a small formatting tweak to tsconfig.base.json.
2026-05-20 19:08:45 +08:00
DrSmoothl 2de1ff4f0d chore(release): v1.9.0 2026-05-20 14:21:03 +08:00
H3CoF6andClaude Sonnet 4.6 312df29b5f feat: 添加注入前探测 QQ 登录状态功能 (#56)
通过扫描 QQ 进程开放的 9210-9219 端口,在注入 DLL 前即可获取登录状态和账号信息。

- 新增端口探测模块 (qq-port-probe.ts)
- 前端添加"探测登录"按钮和状态显示对话框
- 兼容 Windows 和 Linux 平台
- 修复端口查询逻辑,正确识别每个 PID 开放的端口

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 10:29:41 +08:00
DrSmoothl ecb8064ca9 perf(webui): code-split routes; cut initial JS from 606 kB to 284 kB
`vite build` was emitting a single ~606 kB chunk for the whole SPA
(Vite even printed its >500 kB warning). Every route — overview,
config (with its full edit-dialog form surface), logs, settings —
was eagerly imported in `router/index.tsx`, so users paid the full
parse + execute cost before the login redirect even resolved.
That's the most plausible cause of the "loading node config is
somewhat slow" report.

Switch each route to `lazyRouteComponent(() => import('…'), '<Name>')`
so the page chunk is fetched on demand, and wrap `<Outlet />` in
`AppLayout` with a `<Suspense>` boundary backed by a small skeleton
fallback. `defaultPreload: 'intent'` is already set on the router,
so hovering a sidebar link warms the next chunk before the click —
navigation still feels instant after the first idle moment.

Build now emits:
  index.js          606 kB  →  284 kB    (-53%)
  overview-page                  15 kB
  config-page                    25 kB
  logs-page                      5.6 kB
  settings-page                  5.8 kB
  button (shared primitives)   164 kB
  + smaller per-component chunks Vite shook out
And the >500 kB warning is gone.

Bonus: replace the per-render
`list.filter(...).map(...).includes(item.name)` O(n²) duplicate-name
check in `NetworkTabView` with a one-shot `Map<name, count>` —
matters every time the user types in the edit dialog and the tab
underneath re-renders.
2026-05-19 11:48:41 +08:00
DrSmoothl 941e60dea5 chore(release): v1.8.5 2026-05-18 15:03:45 +08:00
DrSmoothl ba899782fe feat(webui): mask the access token and add copy + visibility buttons
The adapter edit dialog now ships a dedicated TokenField for the
access-token slot instead of reusing the plain text Field. It:

  - defaults to a password-style input so the value stays out of
    casual screen reads / screenshots,
  - exposes an Eye / EyeOff button that flips the input type rather
    than masking the string, so paste / autocomplete still behave like
    a native password field,
  - exposes a Copy button that pushes the current value to
    navigator.clipboard, flashing a check icon for ~1.4 s on success,
    and disables itself when the value is empty,
  - falls back to a transient textarea + document.execCommand('copy')
    for non-secure-context environments (LAN HTTP outside localhost),
    routed through a local alias so the deprecation warning on the
    live signature doesn't fire at the call site.
2026-05-18 14:47:33 +08:00
DrSmoothl 7a2de230bf fix(webui): pass aria-checked as a string literal on switch buttons
A11y checkers (axe-core / jsx-a11y) flag the boolean form because the
ARIA spec defines aria-checked as the enum "true" | "false" | "mixed",
not a boolean. React serialized the boolean to the right wire value at
runtime, but the inspection layer keys off the JSX expression's static
type. Switching to the explicit ternary silences both.
2026-05-18 14:31:08 +08:00
DrSmoothl a460433175 refactor(webui): redesign per-UIN config page into sidebar + tabs + modal
The original page stacked four expanded network sections vertically with
inline editing, so the first view drowned in every field across every
adapter at once. Restructured to a sidebar + tabs + modal layout:

- Account sidebar collapses to a 56px avatar column with name/UIN
  tooltips; expanded it keeps the original 260px nickname + UIN layout.
- Right pane swaps the stacked sections for a 5-tab strip (通用设置 /
  HTTP API / HTTP 客户端 / WS 服务端 / WS 客户端) with a per-tab badge
  count.
- Each network tab is a list of summary cards: name + projected key
  info (host:port/path, url · role, etc.) + format/report/token badges
  + inline enable toggle + edit/delete buttons. No live-typed fields.
- Create and edit both route through a single NodeEditDialog with
  kind-specific field strips; cancel truly discards the draft.
- Per-kind metadata (title, summary projector, defaultEntry factory)
  lives in config/defaults.ts so adding a new adapter kind only needs
  one descriptor entry.

Behavior preserved end-to-end: same per-UIN config payload, same
useOneBotInstanceConfig hook, same dirty-switch confirm flow, same
save endpoint.
2026-05-18 14:29:52 +08:00
DrSmoothl b94775cb6f chore(release): v1.8.4 2026-05-18 13:31:17 +08:00
DrSmoothl 5524c73eed chore(webui): keep search engines out of the admin surface
Even the login page leaks "there's a SnowLuma admin panel at this
IP / hostname" to anyone scanning, and right now that page is
publicly indexable. Three overlapping signals so we don't depend on
any one being honoured:

1. Hono middleware on every response sends
     X-Robots-Tag: noindex, nofollow, noarchive, nosnippet, noimageindex
   Hardest for compliant bots to ignore — it's a header, not
   content. noimageindex also covers logo.png / avatar/* from
   showing up in image search.

2. A GET /robots.txt route returns "User-agent: * / Disallow: /"
   for crawlers that read it before fetching pages. Sits before
   the auth middleware so no token is required (otherwise the
   crawler would get 401 here, which it'd then refuse to obey).

3. <meta name="robots"> (plus googlebot / bingbot) in index.html
   as a fallback for the case where a reverse proxy strips the
   X-Robots-Tag header.
2026-05-18 13:18:31 +08:00
DrSmoothl 4c513c015d feat(logger): runtime log level toggle via WebUI
The console / subscriber level was captured at module load
(`const MIN_LEVEL = resolveMinLevel()`), so any change to
SNOWLUMA_LOG_LEVEL after Node started had no effect — restart was
the only way to flip levels, which makes "go to debug for 30s to
see what's happening" pretty painful.

logger.ts now keeps `currentLevel` mutable and exports
setLogLevel() / getLogLevel(). The env var still seeds it on
startup; nothing about the boot-time behavior changes. File
transport remains unaffected — it always sees debug-and-up.

WebUI gets two new endpoints, both behind the existing auth
middleware:
  GET  /api/logs/level       → { level, levels }
  POST /api/logs/level body  → { level: 'debug' } updates it

Logs page picks up a "服务端级别" row of pills next to the
existing client-side filter pills, so an operator can flip the
console firehose on/off in one click without touching env vars
or restarting the bot. The label clarifies that file logging
stays at debug regardless.
2026-05-18 13:15:05 +08:00
DrSmoothl c6f4d5c5a2 chore(release): v1.8.3 2026-05-18 00:10:43 +08:00
DrSmoothl 90884dfbb1 chore(release): v1.8.2 2026-05-17 19:28:58 +08:00
DrSmoothl a1a49944df refactor(webui): migrate routing to TanStack Router
The four-page switch lived as a string union (Page) plus an if/else
chain in AppInner, with TopBar reverse-importing Sidebar's NAV_ITEMS
to find the icon for the active tab. Adding a page touched four
places and there was no URL, no history, no deep-linking.

Move routing behind @tanstack/react-router with a layout route
(AppLayout) that owns the polling lists, processOps, selectedUin
and the unload-failed alert; child routes render through <Outlet/>
and read shared state from a new AppStateContext. AuthBoundary still
gates login / forced-password-change above the router. Sidebar uses
<Link> + useRouterState for active state; TopBar and MainLayout look
up the current path the same way.

Behaviour-preserving:
- LoginPage / ChangePasswordPage still mount above the router.
- AppLayout never unmounts during navigation, so selectedUin,
  processOps state, the polling state and the unload-failed dialog
  all survive page switches just like before.
- Logout calls history.replaceState('/') so the next login lands on
  the overview page, matching pre-router behaviour.
- The sidebar-active-pill layoutId animation and the page-transition
  AnimatePresence are preserved, keyed off pathname instead of the
  removed `active` prop.

New URLs (/, /config, /logs, /settings) work on refresh because the
backend already had SPA fallback in core/src/webui/server.ts. Bundle
grows ~84KB (gzip +27KB) for the router runtime.
2026-05-16 02:35:49 +08:00
DrSmoothl caf724b25c fix(webui): preserve config edits on save and UIN choice across tabs
Two regressions slipped into the ApiClient/ConfigPage refactor:

1. api.config.save() ran the POST response through normalizeOneBotConfig
   and used it to overwrite local state. But POST /api/config/:uin
   returns { success, reloaded, message } — no config body — so the
   normaliser produced an empty config and wiped the user's edits as
   soon as the save succeeded. Honour the "save returns canonical view"
   contract by following the POST with a GET inside the client; the
   round-trip stays hidden behind the seam.

2. selectedUin lived inside useOneBotInstanceConfig, so navigating away
   from ConfigPage unmounted the hook and lost the user's selection.
   Lift selectedUin to AppInner and make the hook externally controlled
   via { selectedUin, onSelectedUinChange }. Clear it on logout.
2026-05-16 02:22:48 +08:00
DrSmoothl 7cef104f93 refactor(webui): deepen Hook process ops behind a single seam
App.tsx held three copy-pasted handlers (load / unload / refresh) and
three parallel processXxxPid state slots that were mutually exclusive
but unenforced by the type system. The unload-failed alert was a
separate path, and OverviewPage needed 11 props to drive the table.

Extract useHookProcessOps: one inflight Set for dedup, one Map<pid,
OpKind> for view state, one run() that branches on kind via a
message table, and ownership of the unload-failed alert. OverviewPage
takes a single HookProcessOps prop and queries statusOf(pid).

App.tsx 270 → 216 LOC; OverviewPage props 11 → 7.
2026-05-16 02:13:09 +08:00
DrSmoothl b89d51d984 refactor(webui): extract ApiClient + lift OneBotInstance config editor
App.tsx was the single source of truth for token, fetch policy, 401
handling, and per-UIN config edit state — fetchApi was drilled into
every page and LogsPage read localStorage directly to build its SSE
URL, giving two parallel truths for the bearer token.

Promote the closure to a deep ApiClient module (domain methods +
request() escape hatch) injected via ApiProvider/useApi, and lift
selectedUin/config/saveStatus into useOneBotInstanceConfig so
ConfigPage is self-contained. normalizeOneBotConfig becomes a pure
function. ConfigPage now guards UIN switches when the editor is
dirty and refetches the server-normalised config after save.

ConfigPage prop count drops from 7 to 1; App.tsx from 421 to 270 LOC.
2026-05-16 02:04:11 +08:00
DrSmoothl 7da52bc591 chore(release): v1.8.1 2026-05-15 23:17:44 +08:00
DrSmoothl 85b59bc3a1 chore(release): v1.8.0 2026-05-15 01:47:06 +08:00
DrSmoothl ada591b1b4 bump 1.7.9 2026-05-13 16:52:19 +08:00
DrSmoothl 61e3867197 chore(release): single-source app version + add pnpm bump
- WebUI sidebar/login display and OneBot get_version_info now read the
  root package.json version via Vite `define`, removing hardcoded
  literals in webui/src/types.ts and core/onebot/actions/info.ts.
- tools/bump-version.mjs (exposed as `pnpm bump <ver>`) syncs the
  version field across the root and all five workspace packages.
- Bump everything to 1.7.8.
2026-05-13 16:51:44 +08:00
手瓜一十雪 b62a6edd37 Canonicalize WsRole strings and migrate legacy values
Normalize OneBot WS role values to canonical PascalCase ('Api'|'Event'|'Universal') across core and web UI, and support auto-migration from legacy lowercase on-disk values. Updated types, defaults, serializers/parsers, and runtime checks in ws-transport, adjusted WebUI select options and default entries, and updated the client User-Agent string. Added a test to verify that legacy lowercase role values are migrated to the new canonical form on load.
2026-05-09 10:21:47 +08:00
手瓜一十雪 2a4ca46a99 Refactor OneBot networks and event dispatch
Unify per-adapter network configuration into a single `networks` structure and add migration/normalization for legacy on-disk formats. Introduce per-adapter options (messageFormat, reportSelfMessage, enabled, name) and stronger typing in types.ts. Add event-filter utilities to prebuild two serialized payload variants and pick the right one per adapter to avoid repeated JSON.stringify calls. Update HTTP/WS transports to use adapter names/signatures for hot-reload, respect per-adapter options, and improve logging; WS transport also improves client/server lifecycle/reconnect handling. Mark self-sent messages as `message_sent` in the event converter and wire instance dispatch to use the new DispatchPayload. Misc: README edits and accompanying tests added/updated for the refactor.
2026-05-07 16:01:28 +08:00
手瓜一十雪 f7cfec1a93 Replace GPU detection with runtime memory info
Remove external GPU detection (no more exec/promisify or detectGpus) and stop returning GPU list from /api/system. Instead expose the Node.js process runtime info (pid, rss, heapTotal, heapUsed, external, arrayBuffers) from the server. Update types to replace gpus with runtime and update the Overview UI: remove GPU list, show PID and formatted runtime memory stats, and adjust imports/icons accordingly. This simplifies system probing (avoids spawning platform commands) and surfaces useful process memory metrics in the WebUI.
2026-05-07 09:39:12 +08:00
venti1112 64178893e4 fix: show uninstall failure notification in WebUI
Previous behavior: when an uninstall operation failed, the system did not provide any error feedback to the user and would simply reconnect during a polling check.

Fixed behavior: if an uninstall fails, a failure notification is now displayed in the WebUI, informing the user that the uninstall has failed.
2026-05-06 18:09:31 +08:00
手瓜一十雪 4b02367a5f Add pipe-watcher and manual refresh for hooks
Introduce a background pipe-watcher and per-PID operation serialization in HookManager to reliably detect and reconnect to SnowLuma pipes, reduce races, and remove the previous connect retry loop. Add new status 'connecting' and a refreshProcess API endpoint to force a probe/reconcile for a PID. Implement QqHookClient.listLivePipes() (and isClosed/probePipe updates) to enumerate live mojo pipes efficiently. Simplify client teardown logic and improve state transitions/logging. Update web UI: add a Refresh button, track inflight process ops, show connecting status and updated action messages. Update shared types to include the new 'connecting' status.
2026-05-06 16:10:14 +08:00
venti1112 34a572f153 fix: fix reverse-ws reload/reconnect idempotency, preserve explicit no-token config values, and make WebUI config rendering consistent after refresh
Previous behavior: after editing an unreachable reverse-ws OneBot config entry, the new config took effect, but reconnect attempts could still target the old URL.
Fixed behavior: reconnect now only applies to currently active OneBot config entries; removed or changed old entries are no longer retried.

Previous behavior: after refreshing the WebUI, OneBot config entries for the same account were already loaded and active in backend, but appeared empty in the WebUI config page.
Fixed behavior: OneBot config API response shape is now unified and backward-compatible, so persisted OneBot config entries render correctly after WebUI refresh.

Previous behavior: no-token OneBot config entries could be automatically assigned a token during load/refresh.
Fixed behavior: fixed the issue where no-token config entries were auto-populated with tokens; OneBot config entry token values now strictly follow explicit user configuration (empty stays empty).

Previous behavior: after multiple reconnect failures for a reverse-ws OneBot config entry, multiple connections could be created concurrently when the target server came back.
Fixed behavior: added per-entry connection/reconnect deduplication so each OneBot config entry maintains only one active connection and one reconnect timer.
2026-05-05 17:15:13 +08:00
DrSmoothl 5746dc628f chore: fix ci fail 2026-05-05 00:43:29 +08:00
DrSmoothl 1426d7adb2 feat: add settings page and theme toggle component
- Implemented a new SettingsPage component for user preferences including appearance, behavior, and account security settings.
- Added a ThemeToggle component to switch between light, dark, and system modes.
- Created reusable UI components: AlertDialog, Avatar, Badge, Button, Card, Dialog, Input, Label, Progress, ScrollArea, Separator, Sheet, Skeleton, Tooltip.
- Introduced utility functions for class name merging and formatting bytes/uptime.
- Added a custom hook for media query handling
2026-05-05 00:39:52 +08:00
手瓜一十雪 39f1672b29 Migrate project to pnpm monorepo
Restructure the repository into a pnpm workspace under packages/ (core, webui, runtime). Move source files for core and webui into packages/*, add package manifests for @snowluma/core and @snowluma/runtime, and remove the old launcher package.json. Update root package.json to be a monorepo controller (scripts delegate to pnpm filters, set packageManager and Node engine). Configure build outputs to emit into the repo-root dist/ (vite config changes) so the existing release pipeline continues to work. Add .npmrc with mirrors, expand .gitignore for monorepo layout, add tsconfig base and package-level tsconfigs, and add vitest config for the core package. Adjust injector native search paths and update tests import paths to the new package layout. Small CI/workflow comment added to clarify build behavior and README updated with monorepo structure and common commands.
2026-05-03 14:02:29 +08:00