Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Console: Queue and Activity in the shared frame, Activity scrolls back through history

未关闭
#767 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

@V3RON 已经在做这个了。

开始于 2026年10月20日。

  • #789 来自 @V3RON —— 未关闭

评估

难度
4/5
预计耗时
3-5 天
新手友好度
48/100
Issue 类型
功能
描述清晰度
描述清楚
活跃度
活跃
技术栈
react, typescript

调研方向

Start with ui/src/data/event-feed.ts, ui/src/model/events.ts, ui/src/state/events.ts, and ui/src/ds/feed.tsx to trace event loading, merging, and virtual-list behavior. Then read the Activity and Queue screen entry points and the named model and feed tests. Done means older history loads and ends as specified, the list stays stable while reading, Queue and Activity use the shared frame and drawer, and the browser cases cover the listed states.

由索引模型根据 Issue 内容生成。

描述

task:ready

Part of #756.

Scope

Queue and Activity move to the shared frame, with the drawer. Activity scrolls back through the daemon's whole event history as far as it is kept, instead of stopping at one hour. After this PR an operator reads why each request waits in the same table and drawer as everything else. They can scroll Activity back days to see what happened overnight.

In short

Queue (/queue) is a table: place, requester and the device asked for, stage, why it waits, and how long it has waited. Above it, the Wait p95 number is shown as today. A row opens the drawer at /queue/<requestId>. The drawer shows today's inspector facts: Waiting, Place, Stage, Device, Queue, Why, and the one row per worker on a gateway.

Activity (/activity) is the event list, newest first, in the shared toolbar: the type chips (All, Leases, Devices, Workers, Components, Other) on the left and a search box on the right. A row opens the drawer at /activity/<eventId> with the payload and the link to what it is about. New events arrive on top as today. While the reader has scrolled down, nothing moves, and the button "<n> new events" appears at the top.

  • Infinite scroll: the page first loads the last hour, as today (GET /v1/events?since=1h). When the reader comes within 20 rows of the end, it asks for the 100 events before the oldest one it holds (GET /v1/events?before=<oldest id>&limit=100, from #764). With no event held, it asks for the newest 100 (?limit=100). It adds them at the bottom and repeats until the history ends, or until the list holds 10,000 events, today's cap.
  • It loads by itself only while the chips are on All and the search is empty. With a filter on, the last row is the button Load older events: each press loads one page of 100, which may add no visible row.
  • Search matches, ignoring case, any part of a row's event name or its summary line (who, which device, which worker), over the events held. The text is in the address as ?q=.
  • The last row says why the list stops. Times are the local date and time, YYYY-MM-DD HH:MM.
    • At the cap: "Showing the newest 10,000 events."
    • When the history ends (a page shorter than 100 in the raw reply, or one that adds no event the list does not already hold): "Start of the history ()".
    • On a 422: "Older events are no longer kept."
    • On a 503, any other error, or no answer within the client's timeout: "Older events are not available right now." with Try again. A 401 signs out, as for any read.
  • When the first load returns no events, the page asks for the newest 100 (?limit=100) at once, so a quiet last hour still shows older history. Only when that also returns none does it say "No events yet."
  • When a chip or search matches none of the held events, the page says "No events among those loaded." (for search, "No events match among those loaded."), with Load older events under it while the history has not ended.
  • The last row sits below the scrolling list, outside it, so it never counts as an event row.
  • An address /activity/<id> for an event not held opens the drawer with "This event is not among those loaded. Older events load as you scroll the list, or with Load older events."
  • The Events per minute chart and its table stay above the list, as today.
flowchart LR
  A["/activity (changed)"] -->|scroll near the end| B["GET /v1/events?before=…&limit=100 (new)"]
  B -->|100 rows| A
  B -->|fewer| C[Start of the history]
  A -->|row| D["/activity/&lt;id&gt; drawer"]

If this goes wrong, Activity shows an event twice or skips some while scrolling back, or the list jumps while someone reads it.

Technical spec

Facts
# Fact Source
F1 The console loads GET /v1/events?since=1h and, after a stream reopens, the missed window ui/src/data/event-feed.ts:78, ui/src/model/events.ts:10-13
F2 mergeEvents dedups by id, sorts newest first by timestamp then seq, and keeps at most MAX_EVENTS = 10_000 ui/src/model/events.ts:10-13,46-64
F3 One EventFeed per screen; the stream feeds streamEvent ui/src/state/events.ts:30-37
F4 The feed is a virtual list (useVirtualizer) anchored to the start at the top and to the end when scrolled; it has no near-end callback ui/src/ds/feed.tsx:32-40
F5 The Events page shows "No events in the last hour." and gives Feed count, newText ("N new events") and startAt ui/src/screens/events/index.tsx:93-106
F6 Events page today: chart, filter all/leases/devices/workers/components/other in the address, payload inspector with a link docs/CONSOLE.md:451-486
F7 Waiting page today: Wait p95 tile, rows place, requester, wait, device, stage; inspector facts and per-worker rows docs/CONSOLE.md:388-415, ui/src/state/waiting.ts:22
F8 GET /v1/events?before=&limit= returns up to limit events older than the named one, oldest first; 422 HISTORY_NOT_KEPT, 503 HISTORY_UNAVAILABLE #764 In short
F9 long-lists.spec.ts measures the events feed's frame time; #730 tracks its WebKit flake e2e/console/long-lists.spec.ts, #730
F10 Drawer, Toolbar, the search field and the addresses /queue[/<id>] and /activity[/<id>] come from #765 #756 Shared values
F11 The long-lists stubs answer every /v1/events request the same, whatever the query; the frame-time case seeds 5,000 events and waits for them e2e/console/long-lists.spec.ts:27,119-127,703-751
F12 With no rows after filtering the screen draws Empty and no Feed ui/src/screens/events/index.tsx:95-99
F13 readEvent drops malformed events from a reply ui/src/model/events.ts:34-38
Modules touched

Searches: EventFeed, mergeEvents, MAX_EVENTS, Feed, useVirtualizer, WaitingScreen, EventsScreen, "Events per minute".

  • ui/src/data/event-feed.ts: loadOlder() calls before (or limit alone when nothing is held) and reports the end, 422 or an error. The first load and the gap load after a reconnect stay since.
  • ui/src/model/events.ts: mergeEvents keeps older pages, under today's 10,000 cap; the end rule (a raw page under 100, or a page adding nothing new); the search match.
  • ui/src/state/events.ts: exposes loadOlder and the list's end state.
  • ui/src/ds/feed.tsx: an onNearEnd callback when the last rendered row is within 20 of the end, and an end row.
  • ui/src/screens/events/ (renamed activity/), ui/src/screens/waiting/ (renamed queue/): table or feed, drawer, toolbar, the end rows, Load older events.
  • e2e/console/events.spec.ts (becomes activity.spec.ts), waiting.spec.ts (becomes queue.spec.ts), views.spec.ts, and long-lists.spec.ts, whose /v1/events stubs answer before= with no events, so the stubbed list ends where it does today.
  • docs/CONSOLE.md (Waiting → Queue, Events → Activity; "Event not in the last hour." goes), ui/AGENTS.md.
flowchart LR
  s["screens/activity (changed)"] --> st["state/events (changed)"]
  s --> feed["ds/feed (changed)"]
  st --> ef["data/event-feed (changed)"]
  st --> m["model/events (changed)"]
  ef --> api["GET /v1/events?before (#764)"]
Contract and event changes

None in this task (#764 adds the query).

Other code on the same state

The state is the console's list of held events. Searches: mergeEvents(, streamEvent, EventFeed. The writers are the initial load, the gap load after a reconnect, the stream, and now loadOlder.

  • Stream and loadOlder at once: both go through mergeEvents, which dedups by id and sorts, so an event appears once in its place.
  • The gap load after a reconnect adds newer events only, and loadOlder adds older ones. Neither moves what the reader is looking at when scrolled down (F4's end anchor).
  • Overview (#768) reads recent events through its own feed and is unaffected.
Failure modes
Call Outcome What happens Limit, counted from
loadOlder 100 rows appended at the bottom; may load again when near the end the client's request timeout
loadOlder fewer than 100 in the raw reply (before malformed events are dropped, F13) appended; end row "Start of the history ()"; no more loads —
loadOlder 422 end row "Older events are no longer kept."; no more loads —
loadOlder 503, 400, 403, 500, network error end row "Older events are not available right now." with Try again —
loadOlder no answer the same end row the client's 10 s request timeout, from send
loadOlder a full page that adds no new event treated as the end: "Start of the history (…)", no more loads —
loadOlder 401 signed out, as for any read —
loadOlder the list holds 10,000 no request; end row "Showing the newest 10,000 events." —
two near-end signals before the first answer — one request at a time; the second is dropped —
Rules in play
  • ADR 0013 §3: the stream and the refetch work as today; loadOlder is a read of a route.
  • ADR 0037 §2: the drawer at /activity/<id> and /queue/<id>.
  • DESIGN.md "Charts." bullet (docs/internal/DESIGN.md:208-215): the chart keeps its table twin, as today.
  • ADR 0037 is Proposed until this feature's approval; the task follows it as accepted.
Tests

Seam: the browser lane (e2e/console/events.spec.ts becomes activity.spec.ts, waiting.spec.ts becomes queue.spec.ts), with a real daemon, and with /v1/events stubbed through page.route for the history's end states, as long-lists.spec.ts stubs it. The merge, end and search rules in Node in ui/src/model/events.test.ts and ui/src/data/event-feed.test.ts.

  • Queue lists each waiting request with place, requester, device, stage, why and wait; a row opens /queue/<id> with today's inspector facts and, on a gateway, one row per worker (move waiting.spec.ts cases)
  • a request that is granted while its drawer is open shows "Request not waiting." (as today)
  • Activity lists events newest first; the chips and the search filter the list and are kept in the address (?q=); search matches part of the event name or summary, ignoring case, and not other payload values (model test)
  • a row opens /activity/<id> with every payload key and the link to its lease, worker or request
  • scrolling within 20 rows of the end loads the 100 events before the oldest held and appends them without moving the rows in view (stub: a last hour of 30 events, then pages of 100)
  • with a chip or search on, nothing loads by itself, and Load older events loads one page per press
  • an address /activity/<id> for an event not held shows "This event is not among those loaded. Older events load as you scroll the list, or with Load older events."
  • each end row shows its wording in the browser, with stubbed answers: a page of 40 gives "Start of the history ()" and no further request; 10,000 held gives "Showing the newest 10,000 events."; a 422 gives "Older events are no longer kept."; a 503 and a 500 give "Older events are not available right now.", and Try again asks again; a full page of events already held ends the list
  • a daemon with no events in the last hour but older history shows them at once (?limit=100 sent with no scrolling); an empty history shows "No events yet."
  • a chip that matches no held event shows "No events among those loaded." and Load older events, which loads one page
  • an event that arrives by the stream while older pages load appears once, at the top
  • while scrolled down, new events show " new events" and the rows in view do not move (extend the existing case)
  • mergeEvents keeps older pages in order and dedups by id across a page and the stream (extend events.test.ts)
  • the long-lists frame-time check still passes with its 5,000 seeded events, its stubs answering before= with none (long-lists.spec.ts)
  • axe passes on Queue and Activity with a drawer open, in light and dark; no <style> element or CSP violation (extend views.spec.ts)

Done when

  • pnpm test:console --project=chromium passes, and CI's Console check passes in every browser.

Out of scope

  • Cancel and every other action (#769).

Depends on

The console tasks run one after another: #766 and this task both edit the route table, views.spec.ts's page list and the manual's Pages table.

  • #765
  • #766
  • #764

Written by an agent.

主要语言
TypeScript
星标
20
派生
2
平均合并
8 小时 13 分钟
30 天内合并 PR
297

环境准备

这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

callstackincubator/simlock 的其他 Issue

查看 callstackincubator/simlock 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。