@sleep2agi/agent-network-dashboard 0.6.3-preview.49 → 0.6.3-preview.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_COMMIT +1 -1
- package/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/diagnostics/route-bundle-stats.json +97 -97
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +5 -5
- package/.next/server/app/_not-found.rsc +13 -13
- package/.next/server/app/_not-found.segments/_full.segment.rsc +13 -13
- package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
- package/.next/server/app/_not-found.segments/_index.segment.rsc +8 -8
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/server/app/admin.html +5 -5
- package/.next/server/app/admin.rsc +15 -15
- package/.next/server/app/admin.segments/_full.segment.rsc +15 -15
- package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
- package/.next/server/app/admin.segments/_index.segment.rsc +8 -8
- package/.next/server/app/admin.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
- package/.next/server/app/index.html +5 -5
- package/.next/server/app/index.rsc +15 -15
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/index.segments/_full.segment.rsc +15 -15
- package/.next/server/app/index.segments/_head.segment.rsc +4 -4
- package/.next/server/app/index.segments/_index.segment.rsc +8 -8
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +2 -2
- package/.next/server/app/login.rsc +15 -15
- package/.next/server/app/login.segments/_full.segment.rsc +15 -15
- package/.next/server/app/login.segments/_head.segment.rsc +4 -4
- package/.next/server/app/login.segments/_index.segment.rsc +8 -8
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/login.segments/login.segment.rsc +3 -3
- package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/logs.html +5 -5
- package/.next/server/app/logs.rsc +15 -15
- package/.next/server/app/logs.segments/_full.segment.rsc +15 -15
- package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/logs.segments/_index.segment.rsc +8 -8
- package/.next/server/app/logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
- package/.next/server/app/messages/page.js.nft.json +1 -1
- package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
- package/.next/server/app/messages.html +5 -5
- package/.next/server/app/messages.rsc +15 -15
- package/.next/server/app/messages.segments/_full.segment.rsc +15 -15
- package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
- package/.next/server/app/messages.segments/_index.segment.rsc +8 -8
- package/.next/server/app/messages.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
- package/.next/server/app/node/page.js.nft.json +1 -1
- package/.next/server/app/node/page_client-reference-manifest.js +1 -1
- package/.next/server/app/node.html +5 -5
- package/.next/server/app/node.rsc +15 -15
- package/.next/server/app/node.segments/_full.segment.rsc +15 -15
- package/.next/server/app/node.segments/_head.segment.rsc +4 -4
- package/.next/server/app/node.segments/_index.segment.rsc +8 -8
- package/.next/server/app/node.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/node.segments/node.segment.rsc +3 -3
- package/.next/server/app/nodes/[alias]/page.js.nft.json +1 -1
- package/.next/server/app/nodes/[alias]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes/page.js.nft.json +1 -1
- package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
- package/.next/server/app/nodes.html +5 -5
- package/.next/server/app/nodes.rsc +15 -15
- package/.next/server/app/nodes.segments/_full.segment.rsc +15 -15
- package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/_index.segment.rsc +8 -8
- package/.next/server/app/nodes.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/providers/page_client-reference-manifest.js +1 -1
- package/.next/server/app/providers.html +5 -5
- package/.next/server/app/providers.rsc +15 -15
- package/.next/server/app/providers.segments/_full.segment.rsc +15 -15
- package/.next/server/app/providers.segments/_head.segment.rsc +4 -4
- package/.next/server/app/providers.segments/_index.segment.rsc +8 -8
- package/.next/server/app/providers.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/providers.segments/providers/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/providers.segments/providers.segment.rsc +3 -3
- package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/server-logs.html +5 -5
- package/.next/server/app/server-logs.rsc +15 -15
- package/.next/server/app/server-logs.segments/_full.segment.rsc +15 -15
- package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/_index.segment.rsc +8 -8
- package/.next/server/app/server-logs.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
- package/.next/server/app/servers/page_client-reference-manifest.js +1 -1
- package/.next/server/app/servers.html +5 -5
- package/.next/server/app/servers.rsc +15 -15
- package/.next/server/app/servers.segments/_full.segment.rsc +15 -15
- package/.next/server/app/servers.segments/_head.segment.rsc +4 -4
- package/.next/server/app/servers.segments/_index.segment.rsc +8 -8
- package/.next/server/app/servers.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/servers.segments/servers/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/servers.segments/servers.segment.rsc +3 -3
- package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/networks.html +5 -5
- package/.next/server/app/settings/networks.rsc +15 -15
- package/.next/server/app/settings/networks.segments/_full.segment.rsc +15 -15
- package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/_index.segment.rsc +8 -8
- package/.next/server/app/settings/networks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
- package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/tokens.html +5 -5
- package/.next/server/app/settings/tokens.rsc +15 -15
- package/.next/server/app/settings/tokens.segments/_full.segment.rsc +15 -15
- package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/_index.segment.rsc +8 -8
- package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
- package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
- package/.next/server/app/settings.html +5 -5
- package/.next/server/app/settings.rsc +15 -15
- package/.next/server/app/settings.segments/_full.segment.rsc +15 -15
- package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
- package/.next/server/app/settings.segments/_index.segment.rsc +8 -8
- package/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
- package/.next/server/app/tasks/[id]/page.js.nft.json +1 -1
- package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks/page.js.nft.json +1 -1
- package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
- package/.next/server/app/tasks.html +5 -5
- package/.next/server/app/tasks.rsc +15 -15
- package/.next/server/app/tasks.segments/_full.segment.rsc +15 -15
- package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/_index.segment.rsc +8 -8
- package/.next/server/app/tasks.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__05z_qq6._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__05z_qq6._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__06w5i08._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__06w5i08._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0jgqpfv._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0jgqpfv._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0w7k~3f._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0w7k~3f._.js.map +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__11wln8.._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__11wln8.._.js.map +1 -0
- package/.next/server/chunks/ssr/{dash-stageb-wt_app_0e7vp44._.js → dash-stageb-wt_app_045dcq5._.js} +2 -2
- package/.next/server/chunks/ssr/dash-stageb-wt_app_045dcq5._.js.map +1 -0
- package/.next/server/chunks/ssr/{dash-stageb-wt_app_0g~a.7i._.js → dash-stageb-wt_app_05u4_h~._.js} +8 -2
- package/.next/server/chunks/ssr/dash-stageb-wt_app_05u4_h~._.js.map +1 -0
- package/.next/server/chunks/ssr/{dash-stageb-wt_app_0qpn85e._.js → dash-stageb-wt_app_0nuizh~._.js} +2 -2
- package/.next/server/chunks/ssr/dash-stageb-wt_app_0nuizh~._.js.map +1 -0
- package/.next/server/chunks/ssr/dash-stageb-wt_app_0qn7hvc._.js +1 -1
- package/.next/server/chunks/ssr/dash-stageb-wt_app_0qn7hvc._.js.map +1 -1
- package/.next/server/chunks/ssr/dash-stageb-wt_app_0ss0pj-._.js +1 -1
- package/.next/server/chunks/ssr/dash-stageb-wt_app_0ss0pj-._.js.map +1 -1
- package/.next/server/chunks/ssr/dash-stageb-wt_app_components_0vzsmx1._.js +5 -5
- package/.next/server/chunks/ssr/dash-stageb-wt_app_components_0vzsmx1._.js.map +1 -1
- package/.next/server/chunks/ssr/dash-stageb-wt_app_nodes_NodesView_tsx_0patjj7._.js +1 -1
- package/.next/server/chunks/ssr/dash-stageb-wt_app_nodes_NodesView_tsx_0patjj7._.js.map +1 -1
- package/.next/server/chunks/ssr/dash-stageb-wt_app_tasks_TasksView_tsx_0pnz7dd._.js +3 -0
- package/.next/server/chunks/ssr/dash-stageb-wt_app_tasks_TasksView_tsx_0pnz7dd._.js.map +1 -0
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/02.5_nifak.uu.js +1 -0
- package/.next/static/chunks/03hzj129k~odb.js +1 -0
- package/.next/static/chunks/06q7-vhy~1-y3.js +7 -0
- package/.next/static/chunks/0_4ljrbp.stxy.js +1 -0
- package/.next/static/chunks/0cey4i.y0a8~j.js +1 -0
- package/.next/static/chunks/{0i8qbq4v47v3r.js → 0ep78sq90x_yc.js} +1 -1
- package/.next/static/chunks/{0_xyyy_x0k42j.js → 0hm52b0y~k0~z.js} +1 -1
- package/.next/static/chunks/0kncmrm2a_q43.js +1 -0
- package/.next/static/chunks/0mhaf6lrw5nn..css +1 -0
- package/.next/static/chunks/{12jbb28xy2hw..js → 0qf8xjihw3zi..js} +1 -1
- package/.next/static/chunks/0xyu92zdvvghl.js +7 -0
- package/.next/static/chunks/0zj_navpyhltx.js +2 -0
- package/.next/static/chunks/10px7ehmo7eq0.js +1 -0
- package/.next/static/chunks/{0tv-.l-_mw4ye.js → 11zz15q37~bz5.js} +1 -1
- package/.next/static/chunks/14y4x3de1lbug.js +1 -0
- package/.next/static/chunks/15ka68xi.w-i-.js +1 -0
- package/.next/static/chunks/17qkc714xy04..js +1 -0
- package/.next/trace +2 -2
- package/.next/trace-build +1 -1
- package/app/components/ChatPane.tsx +2 -2
- package/app/components/NodeList.tsx +40 -4
- package/app/components/Sidebar.tsx +180 -29
- package/app/components/TaskChatPanel.tsx +1 -1
- package/app/components/TaskDetail.tsx +407 -0
- package/app/components/TaskList.tsx +117 -0
- package/app/nodes/NodesView.tsx +28 -3
- package/app/tasks/TasksView.tsx +353 -0
- package/app/tasks/[id]/page.tsx +23 -176
- package/app/tasks/page.tsx +10 -453
- package/package.json +1 -1
- package/tests/e2e-sidebar-default.spec.ts +141 -0
- package/tests/e2e-sidebar-icon-rail.spec.ts +261 -0
- package/tests/e2e-stage-b-chat-pane.spec.ts +286 -0
- package/tests/e2e-stage-tasks-two-column.spec.ts +353 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0b-b6yh._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0b-b6yh._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0fs-j~c._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0fs-j~c._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0ma5-5u._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0ma5-5u._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0~hwpo-._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0~hwpo-._.js.map +0 -1
- package/.next/server/chunks/ssr/dash-stageb-wt_app_095q0~q._.js +0 -3
- package/.next/server/chunks/ssr/dash-stageb-wt_app_095q0~q._.js.map +0 -1
- package/.next/server/chunks/ssr/dash-stageb-wt_app_0e7vp44._.js.map +0 -1
- package/.next/server/chunks/ssr/dash-stageb-wt_app_0g~a.7i._.js.map +0 -1
- package/.next/server/chunks/ssr/dash-stageb-wt_app_0qpn85e._.js.map +0 -1
- package/.next/server/chunks/ssr/dash-stageb-wt_app_0tppjr_._.js +0 -9
- package/.next/server/chunks/ssr/dash-stageb-wt_app_0tppjr_._.js.map +0 -1
- package/.next/server/chunks/ssr/dash-stageb-wt_app_0y764mt._.js +0 -3
- package/.next/server/chunks/ssr/dash-stageb-wt_app_0y764mt._.js.map +0 -1
- package/.next/server/chunks/ssr/dash-stageb-wt_app_tasks_page_tsx_0.fhtup._.js +0 -3
- package/.next/server/chunks/ssr/dash-stageb-wt_app_tasks_page_tsx_0.fhtup._.js.map +0 -1
- package/.next/static/chunks/027yjbjm9afuk.js +0 -1
- package/.next/static/chunks/07wf61~wof.l~.js +0 -1
- package/.next/static/chunks/0ev252k-tf_aa.css +0 -1
- package/.next/static/chunks/0j-jjs3w89dei.js +0 -1
- package/.next/static/chunks/0ko39v-gezo7d.js +0 -1
- package/.next/static/chunks/0l8vf4q2t5x4d.js +0 -7
- package/.next/static/chunks/0xcazs6t8~l4q.js +0 -2
- package/.next/static/chunks/10sr651~7vm42.js +0 -7
- package/.next/static/chunks/13_i5b0qmko7z.js +0 -1
- package/.next/static/chunks/140suz13o9va6.js +0 -1
- package/.next/static/chunks/144cf7ds3_9~b.js +0 -1
- package/.next/static/chunks/17c4wlsbmmgk9.js +0 -1
- package/.next/static/chunks/17cfly-04z1j0.js +0 -1
- /package/.next/static/{HIKVEkFShWE481BxQDjwH → vc_aJHtYwefsVcl2_f14Z}/_buildManifest.js +0 -0
- /package/.next/static/{HIKVEkFShWE481BxQDjwH → vc_aJHtYwefsVcl2_f14Z}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{HIKVEkFShWE481BxQDjwH → vc_aJHtYwefsVcl2_f14Z}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import { test, expect, Page } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
const BASE = process.env.TEST_URL || 'http://localhost:3100';
|
|
4
|
+
|
|
5
|
+
// Stage-tasks — /tasks list-plus-detail two-column layout + shareable
|
|
6
|
+
// /tasks/[id] deep link. Migrated from the old one-big-table + inline
|
|
7
|
+
// expand + TaskDrawer implementation.
|
|
8
|
+
//
|
|
9
|
+
// Feature-parity target (通信龙 07-31 硬门, five blocks each with its
|
|
10
|
+
// own positive assertion + REAL data visible in the assertion — not
|
|
11
|
+
// just "the div is there"):
|
|
12
|
+
// 1. Timeline card — 4 steps, real ISO timestamps
|
|
13
|
+
// 2. Info card — 6 fields incl. task_id / from / to / expires
|
|
14
|
+
// 3. Content — body text
|
|
15
|
+
// 4. Result — body text (conditional on task.result)
|
|
16
|
+
// 5. Events feed — /api/hub/task-events, 10s poll, real event rows
|
|
17
|
+
//
|
|
18
|
+
// Plus:
|
|
19
|
+
// - Poll cadence assertions (5s task, 10s events) via data attributes
|
|
20
|
+
// - Deep link (/tasks/[id]) direct-URL load selects that task
|
|
21
|
+
// - Return entry (close button returns to /tasks + empty pane)
|
|
22
|
+
// - Old full-page detail (`Task Detail` <h1>) no longer mounts, paired
|
|
23
|
+
// with a positive assertion in the same test so "detail integrated
|
|
24
|
+
// into pane" and "detail feature gone" can't be confused.
|
|
25
|
+
//
|
|
26
|
+
// Witnessed-red methodology: each of the five blocks was verified to
|
|
27
|
+
// fail its own specific assertion (and only that one) when its
|
|
28
|
+
// component in TaskDetail.tsx was temporarily commented out. See PR
|
|
29
|
+
// body for the recorded red assertion names.
|
|
30
|
+
|
|
31
|
+
const TASK_LIST_ID = 'task_alpha_20260731';
|
|
32
|
+
const TASK_DEEP_ID = 'task_deep_20260731';
|
|
33
|
+
const TASK_BETA_ID = 'task_beta_20260731';
|
|
34
|
+
|
|
35
|
+
const TASK_LIST_RESPONSE = {
|
|
36
|
+
tasks: [
|
|
37
|
+
{
|
|
38
|
+
task_id: TASK_LIST_ID,
|
|
39
|
+
network_id: 'net_test',
|
|
40
|
+
from_name: 'sender-alpha',
|
|
41
|
+
to_name: 'receiver-alpha',
|
|
42
|
+
status: 'replied',
|
|
43
|
+
priority: 'normal',
|
|
44
|
+
content: 'ALPHA_CONTENT_LITERAL',
|
|
45
|
+
result: 'ALPHA_RESULT_LITERAL',
|
|
46
|
+
created_at: '2026-07-31 10:00:00',
|
|
47
|
+
updated_at: '2026-07-31 10:00:05',
|
|
48
|
+
delivered_at: '2026-07-31 10:00:01',
|
|
49
|
+
started_at: '2026-07-31 10:00:02',
|
|
50
|
+
completed_at: '2026-07-31 10:00:04',
|
|
51
|
+
expires_at: '2026-08-01 10:00:00',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
task_id: TASK_BETA_ID,
|
|
55
|
+
network_id: 'net_test',
|
|
56
|
+
from_name: 'sender-beta',
|
|
57
|
+
to_name: 'receiver-beta',
|
|
58
|
+
status: 'failed',
|
|
59
|
+
priority: 'high',
|
|
60
|
+
content: 'BETA_CONTENT_LITERAL',
|
|
61
|
+
result: '',
|
|
62
|
+
created_at: '2026-07-31 09:00:00',
|
|
63
|
+
updated_at: '2026-07-31 09:00:03',
|
|
64
|
+
delivered_at: '2026-07-31 09:00:01',
|
|
65
|
+
started_at: '',
|
|
66
|
+
completed_at: '',
|
|
67
|
+
expires_at: '',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
task_id: TASK_DEEP_ID,
|
|
71
|
+
network_id: 'net_test',
|
|
72
|
+
from_name: 'sender-deep',
|
|
73
|
+
to_name: 'receiver-deep',
|
|
74
|
+
status: 'running',
|
|
75
|
+
priority: 'normal',
|
|
76
|
+
content: 'DEEP_CONTENT_LITERAL',
|
|
77
|
+
result: '',
|
|
78
|
+
created_at: '2026-07-31 08:00:00',
|
|
79
|
+
updated_at: '2026-07-31 08:00:02',
|
|
80
|
+
delivered_at: '2026-07-31 08:00:01',
|
|
81
|
+
started_at: '2026-07-31 08:00:02',
|
|
82
|
+
completed_at: '',
|
|
83
|
+
expires_at: '',
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
count: 3,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const TASK_DETAILS: Record<string, { tasks: (typeof TASK_LIST_RESPONSE.tasks)[0][] }> = {
|
|
90
|
+
[TASK_LIST_ID]: { tasks: [TASK_LIST_RESPONSE.tasks[0]] },
|
|
91
|
+
[TASK_BETA_ID]: { tasks: [TASK_LIST_RESPONSE.tasks[1]] },
|
|
92
|
+
[TASK_DEEP_ID]: { tasks: [TASK_LIST_RESPONSE.tasks[2]] },
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const TASK_EVENTS: Record<string, { events: Array<Record<string, unknown>> }> = {
|
|
96
|
+
[TASK_LIST_ID]: {
|
|
97
|
+
events: [
|
|
98
|
+
{
|
|
99
|
+
id: 1,
|
|
100
|
+
event_type: 'ALPHA_EVENT_CREATED',
|
|
101
|
+
from_status: '',
|
|
102
|
+
to_status: 'created',
|
|
103
|
+
detail: '',
|
|
104
|
+
created_at: '2026-07-31 10:00:00',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: 2,
|
|
108
|
+
event_type: 'ALPHA_EVENT_REPLIED',
|
|
109
|
+
from_status: 'running',
|
|
110
|
+
to_status: 'replied',
|
|
111
|
+
detail: '',
|
|
112
|
+
created_at: '2026-07-31 10:00:04',
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
[TASK_BETA_ID]: { events: [] },
|
|
117
|
+
[TASK_DEEP_ID]: {
|
|
118
|
+
events: [
|
|
119
|
+
{
|
|
120
|
+
id: 3,
|
|
121
|
+
event_type: 'DEEP_EVENT_STARTED',
|
|
122
|
+
from_status: 'created',
|
|
123
|
+
to_status: 'running',
|
|
124
|
+
detail: '',
|
|
125
|
+
created_at: '2026-07-31 08:00:02',
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Rate limit avoidance (10/15min/IP). Login once per worker, reuse
|
|
132
|
+
// the session cookie. Same pattern as e2e-stage-b-chat-pane.spec.ts.
|
|
133
|
+
let cachedSessionCookie: string | null = null;
|
|
134
|
+
async function login(context: Page['context'] extends () => infer C ? C : never) {
|
|
135
|
+
if (!cachedSessionCookie) {
|
|
136
|
+
const res = await context.request.post(`${BASE}/api/auth/login`, {
|
|
137
|
+
data: { password: process.env.ANET_DASHBOARD_PASSWORD || 'admin123' },
|
|
138
|
+
});
|
|
139
|
+
const setCookie = res.headers()['set-cookie'] || '';
|
|
140
|
+
const m = setCookie.match(/anet_dashboard_session=([^;]+)/);
|
|
141
|
+
if (m) cachedSessionCookie = decodeURIComponent(m[1]);
|
|
142
|
+
}
|
|
143
|
+
if (cachedSessionCookie) {
|
|
144
|
+
await context.addCookies([
|
|
145
|
+
{
|
|
146
|
+
name: 'anet_dashboard_session',
|
|
147
|
+
value: cachedSessionCookie,
|
|
148
|
+
domain: new URL(BASE).hostname,
|
|
149
|
+
path: '/',
|
|
150
|
+
},
|
|
151
|
+
]);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function mockAll(page: Page) {
|
|
156
|
+
// /api/hub/tasks — matches both list (no task_id) and single-task
|
|
157
|
+
// fetch (?task_id=…). Route handler inspects the URL.
|
|
158
|
+
await page.route('**/api/hub/tasks**', (route) => {
|
|
159
|
+
const url = route.request().url();
|
|
160
|
+
const match = url.match(/[?&]task_id=([^&]+)/);
|
|
161
|
+
if (match) {
|
|
162
|
+
const id = decodeURIComponent(match[1]);
|
|
163
|
+
const detail = TASK_DETAILS[id];
|
|
164
|
+
route.fulfill({
|
|
165
|
+
status: 200,
|
|
166
|
+
contentType: 'application/json',
|
|
167
|
+
body: JSON.stringify(detail || { tasks: [] }),
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
route.fulfill({
|
|
172
|
+
status: 200,
|
|
173
|
+
contentType: 'application/json',
|
|
174
|
+
body: JSON.stringify(TASK_LIST_RESPONSE),
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
await page.route('**/api/hub/task-events**', (route) => {
|
|
178
|
+
const url = route.request().url();
|
|
179
|
+
const match = url.match(/[?&]task_id=([^&]+)/);
|
|
180
|
+
const id = match ? decodeURIComponent(match[1]) : '';
|
|
181
|
+
const events = TASK_EVENTS[id] || { events: [] };
|
|
182
|
+
route.fulfill({
|
|
183
|
+
status: 200,
|
|
184
|
+
contentType: 'application/json',
|
|
185
|
+
body: JSON.stringify(events),
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
// Prevent unrelated fetches from hanging tests.
|
|
189
|
+
await page.route('**/api/hub/status**', (r) =>
|
|
190
|
+
r.fulfill({ status: 200, contentType: 'application/json', body: '{"sessions":[]}' }),
|
|
191
|
+
);
|
|
192
|
+
await page.route('**/api/hub/nodes**', (r) =>
|
|
193
|
+
r.fulfill({ status: 200, contentType: 'application/json', body: '{"nodes":[]}' }),
|
|
194
|
+
);
|
|
195
|
+
await page.route('**/api/hub/health', (r) =>
|
|
196
|
+
r.fulfill({
|
|
197
|
+
status: 200,
|
|
198
|
+
contentType: 'application/json',
|
|
199
|
+
body: '{"ok":true,"sse_sessions":{}}',
|
|
200
|
+
}),
|
|
201
|
+
);
|
|
202
|
+
await page.route('**/api/hub/stats**', (r) =>
|
|
203
|
+
r.fulfill({ status: 200, contentType: 'application/json', body: '{}' }),
|
|
204
|
+
);
|
|
205
|
+
await page.route('**/api/hub/messages**', (r) =>
|
|
206
|
+
r.fulfill({ status: 200, contentType: 'application/json', body: '{"messages":[]}' }),
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const layout = (page: Page) => page.locator('[data-testid="tasks-layout"]');
|
|
211
|
+
const detailPane = (page: Page) => page.locator('[data-testid="task-detail-pane"]');
|
|
212
|
+
|
|
213
|
+
test.describe('Stage-tasks — /tasks two-column list + detail', () => {
|
|
214
|
+
test('list + selecting a task reveals the detail pane with 5 parity blocks', async ({
|
|
215
|
+
page,
|
|
216
|
+
context,
|
|
217
|
+
}) => {
|
|
218
|
+
await login(context);
|
|
219
|
+
await mockAll(page);
|
|
220
|
+
await page.goto(`${BASE}/tasks`);
|
|
221
|
+
await expect(layout(page)).toBeVisible({ timeout: 15000 });
|
|
222
|
+
|
|
223
|
+
// Before any selection: layout says nothing selected + no old h1.
|
|
224
|
+
await expect(layout(page)).toHaveAttribute('data-selected-id', '');
|
|
225
|
+
// Old full-page detail assertion (negative) — paired with positive
|
|
226
|
+
// below in the SAME test so "detail feature entirely gone" cannot
|
|
227
|
+
// masquerade as "old page gone".
|
|
228
|
+
await expect(page.getByRole('heading', { level: 1, name: 'Task Detail' })).toHaveCount(0);
|
|
229
|
+
|
|
230
|
+
// Click ALPHA row.
|
|
231
|
+
await page
|
|
232
|
+
.locator(`[data-task-row][data-task-id="${TASK_LIST_ID}"]`)
|
|
233
|
+
.click();
|
|
234
|
+
|
|
235
|
+
await expect(detailPane(page)).toBeVisible();
|
|
236
|
+
await expect(detailPane(page)).toHaveAttribute('data-task-id', TASK_LIST_ID);
|
|
237
|
+
await expect(layout(page)).toHaveAttribute('data-selected-id', TASK_LIST_ID);
|
|
238
|
+
await expect(page).toHaveURL(`${BASE}/tasks/${TASK_LIST_ID}`);
|
|
239
|
+
|
|
240
|
+
// ── Parity block #1: Timeline card ────────────────────────────
|
|
241
|
+
await expect(page.locator('[data-testid="task-detail-timeline"]')).toBeVisible();
|
|
242
|
+
// Real data — the ISO timestamp literal from the mock, present in
|
|
243
|
+
// the "created" step's title tooltip.
|
|
244
|
+
await expect(
|
|
245
|
+
page.locator('[data-testid="timeline-step-created"]'),
|
|
246
|
+
).toContainText('2026-07-31 10:00:00');
|
|
247
|
+
// "Completed" step has data-timeline-done=true because completed_at
|
|
248
|
+
// is set. Also proves the "done" propagation is real, not fake.
|
|
249
|
+
await expect(
|
|
250
|
+
page.locator('[data-testid="timeline-step-completed"]'),
|
|
251
|
+
).toHaveAttribute('data-timeline-done', 'true');
|
|
252
|
+
|
|
253
|
+
// ── Parity block #2: Info card ────────────────────────────────
|
|
254
|
+
await expect(page.locator('[data-testid="task-detail-info"]')).toBeVisible();
|
|
255
|
+
// task-id field carries the real id (not a truncated preview).
|
|
256
|
+
await expect(
|
|
257
|
+
page.locator('[data-info-field="task-id"]'),
|
|
258
|
+
).toContainText(TASK_LIST_ID);
|
|
259
|
+
await expect(page.locator('[data-info-field="from"]')).toContainText('sender-alpha');
|
|
260
|
+
await expect(page.locator('[data-info-field="to"]')).toContainText('receiver-alpha');
|
|
261
|
+
await expect(page.locator('[data-info-field="expires"]')).toContainText(
|
|
262
|
+
'2026-08-01 10:00:00',
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
// ── Parity block #3: Content ──────────────────────────────────
|
|
266
|
+
await expect(
|
|
267
|
+
page.locator('[data-testid="task-detail-content-body"]'),
|
|
268
|
+
).toContainText('ALPHA_CONTENT_LITERAL');
|
|
269
|
+
|
|
270
|
+
// ── Parity block #4: Result ───────────────────────────────────
|
|
271
|
+
await expect(
|
|
272
|
+
page.locator('[data-testid="task-detail-result-body"]'),
|
|
273
|
+
).toContainText('ALPHA_RESULT_LITERAL');
|
|
274
|
+
|
|
275
|
+
// ── Parity block #5: Events feed ──────────────────────────────
|
|
276
|
+
await expect(page.locator('[data-testid="task-detail-events"]')).toBeVisible();
|
|
277
|
+
await expect(page.locator('[data-testid="task-detail-events"]')).toHaveAttribute(
|
|
278
|
+
'data-events-count',
|
|
279
|
+
'2',
|
|
280
|
+
);
|
|
281
|
+
// Real event data — literal event_type value from the mock.
|
|
282
|
+
await expect(page.locator('[data-testid="task-detail-events"]')).toContainText(
|
|
283
|
+
'ALPHA_EVENT_CREATED',
|
|
284
|
+
);
|
|
285
|
+
await expect(page.locator('[data-testid="task-detail-events"]')).toContainText(
|
|
286
|
+
'ALPHA_EVENT_REPLIED',
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
// ── Poll cadence assertions ───────────────────────────────────
|
|
290
|
+
// Do NOT widen these without a paired PR. See TaskDetail.tsx.
|
|
291
|
+
await expect(detailPane(page)).toHaveAttribute('data-poll-task-ms', '5000');
|
|
292
|
+
await expect(detailPane(page)).toHaveAttribute('data-poll-events-ms', '10000');
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
test('events block stays visible with "No events yet" when list is empty (not hidden)', async ({
|
|
296
|
+
page,
|
|
297
|
+
context,
|
|
298
|
+
}) => {
|
|
299
|
+
// BETA has zero events. If the block hid itself entirely we would
|
|
300
|
+
// not be able to distinguish "feed removed" from "task has no
|
|
301
|
+
// events" — so the empty state must be an explicit visible label.
|
|
302
|
+
await login(context);
|
|
303
|
+
await mockAll(page);
|
|
304
|
+
await page.goto(`${BASE}/tasks/${TASK_BETA_ID}`);
|
|
305
|
+
await expect(detailPane(page)).toBeVisible({ timeout: 15000 });
|
|
306
|
+
|
|
307
|
+
await expect(page.locator('[data-testid="task-detail-events"]')).toBeVisible();
|
|
308
|
+
await expect(page.locator('[data-testid="task-detail-events"]')).toHaveAttribute(
|
|
309
|
+
'data-events-count',
|
|
310
|
+
'0',
|
|
311
|
+
);
|
|
312
|
+
await expect(page.locator('[data-testid="task-detail-events"]')).toContainText(
|
|
313
|
+
'No events yet',
|
|
314
|
+
);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test('deep link — direct load /tasks/[id] selects that task', async ({ page, context }) => {
|
|
318
|
+
await login(context);
|
|
319
|
+
await mockAll(page);
|
|
320
|
+
// Direct URL entry — NOT via clicking the list. This is the specific
|
|
321
|
+
// path that's easy to break silently (click works, deep link does
|
|
322
|
+
// not) — see 通信龙 07-31 witnessed-red requirement for this test.
|
|
323
|
+
await page.goto(`${BASE}/tasks/${TASK_DEEP_ID}`);
|
|
324
|
+
|
|
325
|
+
await expect(layout(page)).toBeVisible({ timeout: 15000 });
|
|
326
|
+
await expect(layout(page)).toHaveAttribute('data-selected-id', TASK_DEEP_ID);
|
|
327
|
+
await expect(detailPane(page)).toBeVisible();
|
|
328
|
+
await expect(detailPane(page)).toHaveAttribute('data-task-id', TASK_DEEP_ID);
|
|
329
|
+
// Real data check on the deep-link path: content literal is there.
|
|
330
|
+
await expect(
|
|
331
|
+
page.locator('[data-testid="task-detail-content-body"]'),
|
|
332
|
+
).toContainText('DEEP_CONTENT_LITERAL');
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
test('close button returns to /tasks and clears selection', async ({ page, context }) => {
|
|
336
|
+
await login(context);
|
|
337
|
+
await mockAll(page);
|
|
338
|
+
await page.goto(`${BASE}/tasks/${TASK_LIST_ID}`);
|
|
339
|
+
await expect(detailPane(page)).toBeVisible({ timeout: 15000 });
|
|
340
|
+
|
|
341
|
+
await page.locator('[data-testid="task-detail-close"]').click();
|
|
342
|
+
|
|
343
|
+
// Selection cleared + URL back to /tasks + empty pane placeholder
|
|
344
|
+
// rendered on desktop (return-entry positive assertion).
|
|
345
|
+
await expect(layout(page)).toHaveAttribute('data-selected-id', '');
|
|
346
|
+
await expect(page).toHaveURL(`${BASE}/tasks`);
|
|
347
|
+
// On desktop viewport (playwright default is desktop-sized),
|
|
348
|
+
// the empty-state placeholder pane replaces the detail pane.
|
|
349
|
+
await expect(page.locator('[data-testid="task-detail-empty"]')).toBeVisible();
|
|
350
|
+
// Old full-page detail also NOT re-emerging as a fallback.
|
|
351
|
+
await expect(page.getByRole('heading', { level: 1, name: 'Task Detail' })).toHaveCount(0);
|
|
352
|
+
});
|
|
353
|
+
});
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[50227,(a,b,c)=>{b.exports=a.x("node:path",()=>require("node:path"))},42023,a=>{"use strict";a.s(["SESSION_STATUS_CHIP_CLASS",0,{working:"bg-green-500/10 text-green-300 border-green-500/20",idle:"bg-gray-500/10 text-gray-300 border-gray-500/20",blocked:"bg-yellow-500/10 text-yellow-300 border-yellow-500/20",error:"bg-red-500/10 text-red-300 border-red-500/20",offline:"bg-gray-500/10 text-gray-500 border-gray-500/20"},"SESSION_STATUS_TEXT_CLASS",0,{working:"text-green-400",idle:"text-gray-400",blocked:"text-yellow-400",error:"text-red-400",offline:"text-gray-500"},"STATUS_BAR_CLASS",0,{created:"bg-gray-500",delivered:"bg-gray-400",acked:"bg-gray-300",running:"bg-green-500",replied:"bg-gray-400",closed:"bg-gray-600",failed:"bg-red-500",cancelled:"bg-gray-600",expired:"bg-gray-600"},"STATUS_CHIP_CLASS",0,{created:"bg-gray-500/10 text-gray-400 border-gray-500/20",delivered:"bg-gray-500/10 text-gray-300 border-gray-500/20",acked:"bg-gray-500/10 text-gray-300 border-gray-500/20",running:"bg-green-500/10 text-green-300 border-green-500/20",replied:"bg-gray-500/10 text-gray-300 border-gray-500/20",closed:"bg-gray-500/10 text-gray-500 border-gray-500/20",failed:"bg-red-500/10 text-red-300 border-red-500/20",cancelled:"bg-gray-500/10 text-gray-500 border-gray-500/20",expired:"bg-gray-500/10 text-gray-500 border-gray-500/20"},"STATUS_DOT_HEX",0,{created:"#9ca3af",delivered:"#9ca3af",acked:"#9ca3af",running:"#4ade80",replied:"#9ca3af",closed:"#6b7280",failed:"#f87171",cancelled:"#6b7280",expired:"#6b7280"},"TASK_STATUSES",0,["created","delivered","acked","running","replied","closed","failed","cancelled","expired"]])},91669,a=>{"use strict";var b=a.i(79616),c=a.i(20855),d=a.i(25059),e=a.i(43321),f=a.i(77739),g=a.i(25104),h=a.i(52231),i=a.i(42023),j=a.i(41480);function k({tmuxName:a}){let[d,e]=(0,c.useState)(""),[f,g]=(0,c.useState)(!1),[h,i]=(0,c.useState)(!1),j=async()=>{g(!0);try{let b=await fetch(`/api/hub/tmux?name=${encodeURIComponent(a)}`),c=await b.json();e(c.output||c.error||"No output")}catch{e("Failed to fetch")}g(!1)};return(0,b.jsxs)("div",{className:"bg-[#161618] border border-[#26262b] rounded-xl p-4",children:[(0,b.jsxs)("div",{className:"flex items-center justify-between mb-2",children:[(0,b.jsxs)("h2",{className:"text-sm font-semibold text-gray-300",children:["Terminal (",a,")"]}),(0,b.jsxs)("div",{className:"flex gap-2",children:[(0,b.jsx)("button",{onClick:j,disabled:f,className:"text-xs text-cyan-400 hover:text-cyan-300 disabled:text-gray-600",children:f?"Loading...":"Capture"}),d&&(0,b.jsx)("button",{onClick:()=>i(!h),className:"text-xs text-gray-500 hover:text-gray-300",children:h?"Collapse":"Expand"})]})]}),d&&(0,b.jsx)("pre",{className:`text-[11px] text-green-300 bg-[#060607] rounded-lg px-3 py-2 border border-[#1c1c1f] overflow-x-auto whitespace-pre font-mono ${h?"max-h-96":"max-h-32"} overflow-y-auto`,children:d}),!d&&(0,b.jsx)("p",{className:"text-xs text-gray-600",children:"Click Capture to view terminal output"})]})}function l(){let a=(0,d.useSearchParams)().get("alias")||"",[f,g]=(0,c.useState)(null),[j,k]=(0,c.useState)([]),[l,n]=(0,c.useState)(0),[o,p]=(0,c.useState)(""),[q,r]=(0,c.useState)(""),[s,t]=(0,c.useState)(!1),u=(0,c.useCallback)(async()=>{if(a)try{let b=await fetch(`/api/hub/session?alias=${encodeURIComponent(a)}`),c=await b.json();c.session&&g(c.session),c.inbox&&k(c.inbox),n(c.sse||0),p("")}catch(a){p(a instanceof Error?a.message:"fetch failed")}},[a]);(0,c.useEffect)(()=>{u();let a=setInterval(u,5e3);return()=>clearInterval(a)},[u]);let[v,w]=(0,c.useState)(""),x=async()=>{if(q.trim()&&a){t(!0),w("");try{let b=await fetch("/api/hub/send",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({alias:a,task:q,...f?.network_id?{network_id:f.network_id}:{}})}),c=await b.json();c.ok?(r(""),setTimeout(u,2e3)):w(c.error||"Send failed")}catch(a){w(a instanceof Error?a.message:"Send failed")}t(!1)}};if(!a)return(0,b.jsx)("div",{className:"min-h-screen bg-[#0b0b0d] text-gray-100 p-6 flex items-center justify-center",children:(0,b.jsxs)("p",{className:"text-gray-500",children:["No alias specified. ",(0,b.jsx)(e.default,{href:"/",className:"text-blue-400 hover:underline",children:"Back to dashboard"})]})});let y=i.SESSION_STATUS_TEXT_CLASS[f?.status||""]||i.SESSION_STATUS_TEXT_CLASS.offline;return(0,b.jsxs)("div",{className:"min-h-screen bg-[#0b0b0d] text-gray-100 p-4 sm:p-6",children:[(0,b.jsxs)("div",{className:"flex items-center gap-3 mb-6 lg:ml-0 ml-10",children:[(0,b.jsx)(e.default,{href:"/nodes",className:"text-gray-500 hover:text-gray-300 text-sm shrink-0",children:"← Nodes"}),(0,b.jsx)(h.AliasAvatar,{alias:a,size:32}),(0,b.jsxs)("div",{className:"min-w-0 flex-1",children:[(0,b.jsxs)("div",{className:"flex items-center gap-2",children:[(0,b.jsx)("h1",{className:"text-xl sm:text-2xl font-bold text-white truncate",children:a}),(0,b.jsx)("span",{className:`inline-block w-1.5 h-1.5 rounded-full shrink-0 ${l>0?f?.status==="working"?"bg-green-500 animate-pulse":"bg-emerald-400":"bg-gray-500"}`}),(0,b.jsx)("span",{className:`text-[10px] uppercase tracking-wide font-semibold ${y}`,children:f?.status||"unknown"})]}),f?.agent&&(0,b.jsxs)("div",{className:"text-xs text-gray-500 truncate",children:[f.agent,f.server?(0,b.jsxs)(b.Fragment,{children:[" ",(0,b.jsx)("span",{className:"text-gray-700 mx-1",children:"·"})," ",f.server]}):null]})]})]}),o&&(0,b.jsx)("div",{className:"bg-red-900/20 border border-red-800/40 text-red-300 px-4 py-2 rounded-lg mb-6 text-sm",children:o}),(0,b.jsx)(m,{alias:a,session:f,sse:l,sendMsg:q,setSendMsg:r,sending:s,sendTask:x,sendError:v})]})}function m({alias:a,session:d,sse:e,sendMsg:g,setSendMsg:h,sending:i,sendTask:l,sendError:n}){let[p,q]=(0,c.useState)("chat"),{hasUnread:r}=(0,j.useChatUnread)(),[s,t]=(0,c.useState)([]),[u,v]=(0,c.useState)(!1),w=r(a);return(0,c.useEffect)(()=>{"events"!==p||u||fetch("/api/hub/task-events?limit=50").then(a=>a.json()).then(a=>{t(a.events||[]),v(!0)}).catch(()=>v(!0))},[p,u]),(0,b.jsxs)("div",{className:"bg-[#111113] border border-[#26262b] rounded-xl overflow-hidden h-[calc(100vh-140px)] flex flex-col",children:[(0,b.jsx)("div",{className:"flex border-b border-[#26262b] bg-[#0e0e10] shrink-0",children:[{id:"chat",label:"Chat",icon:"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"},{id:"events",label:"Events",icon:"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6 M16 13H8 M16 17H8 M10 9H8"},{id:"info",label:"Info",icon:"M12 21a9 9 0 100-18 9 9 0 000 18z M12 8h.01 M11 12h1v4h1"}].map(a=>(0,b.jsxs)("button",{onClick:()=>q(a.id),className:`flex items-center gap-2 px-5 py-3 text-sm font-medium border-b-2 transition-colors ${p===a.id?"border-cyan-400 text-cyan-300":"border-transparent text-gray-500 hover:text-gray-300"}`,children:[(0,b.jsx)("svg",{className:"w-4 h-4 shrink-0",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",children:(0,b.jsx)("path",{d:a.icon})}),(0,b.jsxs)("span",{className:"relative inline-flex items-center gap-2",children:[a.label,"chat"===a.id&&w&&"chat"!==p&&(0,b.jsx)("span",{className:"inline-block h-2.5 w-2.5 rounded-full bg-red-500","aria-label":"Unread chat messages"})]})]},a.id))}),(0,b.jsxs)("div",{className:"flex-1 overflow-hidden",children:["chat"===p&&(0,b.jsx)(f.TaskChatPanel,{alias:a,onClose:()=>{},inline:!0}),"events"===p&&(0,b.jsx)(o,{events:s,loading:!u}),"info"===p&&(0,b.jsxs)("div",{className:"p-4 overflow-y-auto h-full space-y-4 max-w-2xl",children:[(0,b.jsxs)("div",{className:"bg-[#161618] border border-[#26262b] rounded-xl p-4",children:[(0,b.jsx)("h2",{className:"text-sm font-semibold text-gray-300 mb-3",children:"Node Info"}),(0,b.jsx)("div",{className:"space-y-2 text-xs",children:[{label:"Node ID",value:d?.node_id,alwaysShow:!0},{label:"Session ID",value:d?.session_id,alwaysShow:!0},{label:"Resume ID",value:d?.resume_id,alwaysShow:!0},{label:"Alias",value:d?.alias,alwaysShow:!0},{label:"Agent",value:d?.agent,alwaysShow:!0},{label:"Model",value:d?.model,alwaysShow:!0},{label:"Version",value:d?.version},{label:"Server",value:d?.server,alwaysShow:!0},{label:"Hostname",value:d?.hostname,alwaysShow:!0},{label:"IP",value:d?.ip,alwaysShow:!0},{label:"Project",value:d?.project_dir},{label:"Config",value:d?.config_path,alwaysShow:!0},{label:"Channels",value:d?.channels?.length?Array.isArray(d.channels)?d.channels.join(", "):d.channels:null,alwaysShow:!0},{label:"Tmux",value:d?.tmux_name},{label:"SSE",value:e>0?`Connected (${e})`:"Disconnected",alwaysShow:!0},{label:"Progress",value:d?.progress?`${d.progress}%`:null},{label:"Score",value:d?.score},{label:"Last Seen",value:d?.last_seen_at,alwaysShow:!0},{label:"Registered",value:d?.registered_at,alwaysShow:!0},{label:"Updated",value:d?.updated_at,alwaysShow:!0}].map(({label:a,value:c,alwaysShow:d})=>c||d?(0,b.jsxs)("div",{className:"flex justify-between gap-3",children:[(0,b.jsx)("span",{className:"text-gray-500 shrink-0",children:a}),(0,b.jsx)("span",{className:"text-gray-300 truncate max-w-[200px]",title:c?String(c):"--",children:c?String(c):"--"})]},a):null)})]}),d?.task&&(0,b.jsxs)("div",{className:"bg-[#161618] border border-[#26262b] rounded-xl p-4",children:[(0,b.jsx)("h2",{className:"text-sm font-semibold text-gray-300 mb-2",children:"Current Task"}),(0,b.jsx)("p",{className:"text-xs text-gray-400 whitespace-pre-wrap",children:d.task})]}),d?.output&&(0,b.jsxs)("div",{className:"bg-[#161618] border border-[#26262b] rounded-xl p-4",children:[(0,b.jsx)("h2",{className:"text-sm font-semibold text-gray-300 mb-2",children:"Last Output"}),(0,b.jsx)("p",{className:"text-xs text-gray-400 whitespace-pre-wrap max-h-48 overflow-y-auto",children:d.output})]}),(0,b.jsxs)("div",{className:"bg-[#161618] border border-[#26262b] rounded-xl p-4",children:[(0,b.jsx)("h2",{className:"text-sm font-semibold text-gray-300 mb-2",children:"Send Task"}),(0,b.jsxs)("div",{className:"flex gap-2",children:[(0,b.jsx)("input",{type:"text",value:g,onChange:a=>h(a.target.value),onKeyDown:a=>"Enter"===a.key&&l(),placeholder:`Send task to ${a}...`,className:"flex-1 bg-[#0e0e10] border border-[#26262b] rounded px-3 py-2 text-base sm:text-xs text-white placeholder-gray-600 focus:border-blue-500 focus:outline-none"}),(0,b.jsx)("button",{onClick:l,disabled:i||!g.trim(),className:"inline-flex min-h-[44px] items-center justify-center px-4 py-2 bg-blue-600 hover:bg-blue-500 disabled:bg-gray-700 text-white text-xs rounded transition-colors",children:i?"...":"Send"})]}),n&&(0,b.jsx)("div",{className:"mt-2 text-xs text-red-400",children:n})]}),d?.tmux_name&&(0,b.jsx)(k,{tmuxName:d.tmux_name})]})]})]})}let n={delivered:{dot:"bg-blue-400",line:"bg-blue-500/30",path:"M12 5v14 M5 12l7 7 7-7"},acked:{dot:"bg-yellow-400",line:"bg-yellow-500/30",path:"M20 6 9 17l-5-5"},running:{dot:"bg-green-400",line:"bg-green-500/30",path:"M13 2L4 14h6l-1 8 10-12h-6l1-8z"},replied:{dot:"bg-purple-400",line:"bg-purple-500/30",path:"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"},failed:{dot:"bg-red-400",line:"bg-red-500/30",path:"M18 6 6 18 M6 6l12 12"},created:{dot:"bg-gray-400",line:"bg-gray-500/30",path:"M12 5v14 M5 12h14"}};function o({events:a,loading:c}){if(c)return(0,b.jsx)("div",{className:"flex justify-center py-8",children:(0,b.jsx)("div",{className:"w-5 h-5 border-2 border-cyan-500/30 border-t-cyan-500 rounded-full animate-spin"})});if(0===a.length)return(0,b.jsx)("div",{className:"text-center py-12 text-gray-600 text-xs",children:"No task events yet"});let d=new Map;return a.forEach(a=>{let b=a.task_id||`ungrouped-${a.id}`,c=d.get(b)||[];c.push(a),d.set(b,c)}),(0,b.jsx)("div",{className:"p-4 overflow-y-auto h-full space-y-4",children:[...d.entries()].map(([a,c])=>(0,b.jsxs)("div",{className:"bg-[#161618] border border-[#26262b] rounded-xl p-4",children:[(0,b.jsxs)("div",{className:"text-[10px] text-gray-600 mb-3 truncate",children:["Task: ",a.slice(0,20),"..."]}),(0,b.jsxs)("div",{className:"relative pl-6",children:[(0,b.jsx)("div",{className:"absolute left-[9px] top-2 bottom-2 w-0.5 bg-[#1c1c1f]"}),c.map((a,c)=>{let d=n[a.to_status]||n.created;return(0,b.jsxs)("div",{className:"relative pb-4 last:pb-0",children:[(0,b.jsx)("div",{className:`absolute -left-6 top-0.5 w-[18px] h-[18px] rounded-full ${d.dot} flex items-center justify-center z-10 border-2 border-[#161618]`,children:(0,b.jsx)("svg",{width:"10",height:"10",viewBox:"0 0 24 24",fill:"none",stroke:"#0e0e10",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":!0,children:(0,b.jsx)("path",{d:d.path})})}),(0,b.jsxs)("div",{className:"ml-2",children:[(0,b.jsxs)("div",{className:"flex items-center gap-2",children:[(0,b.jsx)("span",{className:`text-xs font-medium ${"failed"===a.to_status?"text-red-400":"replied"===a.to_status?"text-purple-400":"text-gray-300"}`,children:a.event_type}),a.from_status&&(0,b.jsxs)("span",{className:"text-[10px] text-gray-600",children:[a.from_status," → ",a.to_status]}),(0,b.jsx)("span",{className:"text-[10px] text-gray-600 ml-auto",children:(0,g.timeAgo)(a.created_at)})]}),a.detail&&(0,b.jsxs)("div",{className:"text-[11px] text-gray-500 mt-0.5 truncate",children:[a.detail.slice(0,50),a.detail.length>50?"...":""]})]})]},a.id)})]})]},a))})}a.s(["default",0,function(){return(0,b.jsx)(c.Suspense,{fallback:(0,b.jsx)("div",{className:"min-h-screen bg-[#0b0b0d] text-gray-100 p-4 sm:p-6",children:(0,b.jsx)("div",{className:"lg:ml-0 ml-10 text-gray-500 text-sm",children:"Loading…"})}),children:(0,b.jsx)(l,{})})}])}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=%5Broot-of-the-server%5D__0b-b6yh._.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../dash-stageb-wt/app/lib/status.ts","../../../../../dash-stageb-wt/app/node/page.tsx"],"sourcesContent":["/** Single source of truth for task status colors. Both `/tasks` (chip\n * strip, badge, distribution bar) and the Overview Recent Activity row\n * used to ship their own incomplete maps; round 66 consolidates them\n * here so adding a new status updates every consumer at once.\n *\n * Order in `TASK_STATUSES` is chronological-ish: lifecycle progress on\n * the left, terminal-good (`closed`) in the middle, terminal-bad\n * (`failed`/`cancelled`/`expired`) on the right. */\n\nexport const TASK_STATUSES = [\n 'created',\n 'delivered',\n 'acked',\n 'running',\n 'replied',\n 'closed',\n 'failed',\n 'cancelled',\n 'expired',\n] as const;\n\n/** Pill / chip background+text+border. Tailwind classes, not inlined hex,\n * because chips are static class names safe from purge. */\n/** #217 D3 (OpenWebUI-style color restraint): the 9-hue rainbow is\n * collapsed to a semantic triad — green = actively running, red =\n * failed, gray = everything else (in-flight states in lighter gray,\n * terminal states dimmer). Color now means something instead of\n * decorating every enum value. */\nexport const STATUS_CHIP_CLASS: Record<string, string> = {\n created: 'bg-gray-500/10 text-gray-400 border-gray-500/20',\n delivered: 'bg-gray-500/10 text-gray-300 border-gray-500/20',\n acked: 'bg-gray-500/10 text-gray-300 border-gray-500/20',\n running: 'bg-green-500/10 text-green-300 border-green-500/20',\n replied: 'bg-gray-500/10 text-gray-300 border-gray-500/20',\n closed: 'bg-gray-500/10 text-gray-500 border-gray-500/20',\n failed: 'bg-red-500/10 text-red-300 border-red-500/20',\n cancelled: 'bg-gray-500/10 text-gray-500 border-gray-500/20',\n expired: 'bg-gray-500/10 text-gray-500 border-gray-500/20',\n};\n\n/** Inline hex dots — used wherever Tailwind would purge dynamic class\n * names (`bg-${family}-400`). Style attribute carries the color. */\nexport const STATUS_DOT_HEX: Record<string, string> = {\n created: '#9ca3af',\n delivered: '#9ca3af',\n acked: '#9ca3af',\n running: '#4ade80',\n replied: '#9ca3af',\n closed: '#6b7280',\n failed: '#f87171',\n cancelled: '#6b7280',\n expired: '#6b7280',\n};\n\n/** Session lifecycle (distinct from task lifecycle above). Shared by\n * /nodes status pills and /admin Online Sessions row chips so an\n * agent in `blocked` state reads the same color everywhere.\n * Round 91 — extracted from app/nodes/page.tsx. */\n/** D3: idle drops from blue to gray — only working (green), blocked\n * (amber, actionable) and error (red) earn color. */\nexport const SESSION_STATUS_CHIP_CLASS: Record<string, string> = {\n working: 'bg-green-500/10 text-green-300 border-green-500/20',\n idle: 'bg-gray-500/10 text-gray-300 border-gray-500/20',\n blocked: 'bg-yellow-500/10 text-yellow-300 border-yellow-500/20',\n error: 'bg-red-500/10 text-red-300 border-red-500/20',\n offline: 'bg-gray-500/10 text-gray-500 border-gray-500/20',\n};\n\n/** Text-only color for session status. Used where a chip background\n * would be too heavy — e.g. /node detail header status label. */\nexport const SESSION_STATUS_TEXT_CLASS: Record<string, string> = {\n working: 'text-green-400',\n idle: 'text-gray-400',\n blocked: 'text-yellow-400',\n error: 'text-red-400',\n offline: 'text-gray-500',\n};\n\n/** Solid bar segment background for the Tasks distribution bar. */\nexport const STATUS_BAR_CLASS: Record<string, string> = {\n created: 'bg-gray-500',\n delivered: 'bg-gray-400',\n acked: 'bg-gray-300',\n running: 'bg-green-500',\n replied: 'bg-gray-400',\n closed: 'bg-gray-600',\n failed: 'bg-red-500',\n cancelled: 'bg-gray-600',\n expired: 'bg-gray-600',\n};\n","'use client';\n\nimport { useEffect, useState, useCallback } from 'react';\nimport { useSearchParams } from 'next/navigation';\nimport Link from 'next/link';\nimport { Suspense } from 'react';\nimport { TaskChatPanel } from '../components/TaskChatPanel';\nimport { timeAgo } from '../components/utils';\nimport { AliasAvatar } from '../components/AliasAvatar';\nimport { SESSION_STATUS_TEXT_CLASS } from '../lib/status';\nimport { useChatUnread } from '../lib/chat-unread';\n\ninterface SessionDetail {\n resume_id: string;\n network_id?: string;\n alias: string;\n status: string;\n agent: string;\n server: string;\n hostname: string;\n task: string;\n output: string;\n progress: number;\n score: number;\n registered_at: string;\n updated_at: string;\n project_dir: string;\n tmux_name: string;\n ip: string;\n node_id: string;\n session_id: string;\n config_path: string;\n channels: string[];\n last_seen_at: string;\n model: string;\n version: string;\n}\n\ninterface Message {\n id: string;\n content: string;\n from_session: string;\n priority: string;\n created_at: string;\n type: string;\n}\n\nfunction TmuxViewer({ tmuxName }: { tmuxName: string }) {\n const [output, setOutput] = useState('');\n const [loading, setLoading] = useState(false);\n const [expanded, setExpanded] = useState(false);\n\n const fetchTmux = async () => {\n setLoading(true);\n try {\n const res = await fetch(`/api/hub/tmux?name=${encodeURIComponent(tmuxName)}`);\n const data = await res.json();\n setOutput(data.output || data.error || 'No output');\n } catch { setOutput('Failed to fetch'); }\n setLoading(false);\n };\n\n return (\n <div className=\"bg-[#161618] border border-[#26262b] rounded-xl p-4\">\n <div className=\"flex items-center justify-between mb-2\">\n <h2 className=\"text-sm font-semibold text-gray-300\">Terminal ({tmuxName})</h2>\n <div className=\"flex gap-2\">\n <button onClick={fetchTmux} disabled={loading}\n className=\"text-xs text-cyan-400 hover:text-cyan-300 disabled:text-gray-600\">\n {loading ? 'Loading...' : 'Capture'}\n </button>\n {output && (\n <button onClick={() => setExpanded(!expanded)} className=\"text-xs text-gray-500 hover:text-gray-300\">\n {expanded ? 'Collapse' : 'Expand'}\n </button>\n )}\n </div>\n </div>\n {output && (\n <pre className={`text-[11px] text-green-300 bg-[#060607] rounded-lg px-3 py-2 border border-[#1c1c1f] overflow-x-auto whitespace-pre font-mono ${expanded ? 'max-h-96' : 'max-h-32'} overflow-y-auto`}>\n {output}\n </pre>\n )}\n {!output && <p className=\"text-xs text-gray-600\">Click Capture to view terminal output</p>}\n </div>\n );\n}\n\nfunction NodeDetailContent() {\n const searchParams = useSearchParams();\n const alias = searchParams.get('alias') || '';\n const [session, setSession] = useState<SessionDetail | null>(null);\n const [inbox, setInbox] = useState<Message[]>([]);\n const [sse, setSse] = useState(0);\n const [error, setError] = useState('');\n const [sendMsg, setSendMsg] = useState('');\n const [sending, setSending] = useState(false);\n\n const fetchData = useCallback(async () => {\n if (!alias) return;\n try {\n const res = await fetch(`/api/hub/session?alias=${encodeURIComponent(alias)}`);\n const data = await res.json();\n if (data.session) setSession(data.session);\n if (data.inbox) setInbox(data.inbox);\n setSse(data.sse || 0);\n setError('');\n } catch (e: unknown) {\n setError(e instanceof Error ? e.message : 'fetch failed');\n }\n }, [alias]);\n\n useEffect(() => {\n // Initial fetch kick-off + poll; state lands async after the fetch.\n // eslint-disable-next-line react-hooks/set-state-in-effect\n fetchData();\n const interval = setInterval(fetchData, 5000);\n return () => clearInterval(interval);\n }, [fetchData]);\n\n const [sendError, setSendError] = useState('');\n\n const sendTask = async () => {\n if (!sendMsg.trim() || !alias) return;\n setSending(true);\n setSendError('');\n try {\n const res = await fetch('/api/hub/send', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n alias, task: sendMsg,\n // P0 network_id fix: scope to the node's own network.\n ...(session?.network_id ? { network_id: session.network_id } : {}),\n }),\n });\n const data = await res.json();\n if (data.ok) {\n setSendMsg('');\n setTimeout(fetchData, 2000);\n } else {\n setSendError(data.error || 'Send failed');\n }\n } catch (e: unknown) {\n setSendError(e instanceof Error ? e.message : 'Send failed');\n }\n setSending(false);\n };\n\n if (!alias) {\n return (\n <div className=\"min-h-screen bg-[#0b0b0d] text-gray-100 p-6 flex items-center justify-center\">\n <p className=\"text-gray-500\">No alias specified. <Link href=\"/\" className=\"text-blue-400 hover:underline\">Back to dashboard</Link></p>\n </div>\n );\n }\n\n /* Round 92: shared 5-state palette (was working / idle / other,\n collapsed blocked + error into gray). */\n const statusColor = SESSION_STATUS_TEXT_CLASS[session?.status || ''] || SESSION_STATUS_TEXT_CLASS.offline;\n\n return (\n <div className=\"min-h-screen bg-[#0b0b0d] text-gray-100 p-4 sm:p-6\">\n {/* Header — round 40: avatar joins the alias for cross-page hue\n consistency; agent type lives in a subtitle so users know what\n kind of node they're looking at. */}\n <div className=\"flex items-center gap-3 mb-6 lg:ml-0 ml-10\">\n <Link href=\"/nodes\" className=\"text-gray-500 hover:text-gray-300 text-sm shrink-0\">← Nodes</Link>\n <AliasAvatar alias={alias} size={32} />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <h1 className=\"text-xl sm:text-2xl font-bold text-white truncate\">{alias}</h1>\n <span className={`inline-block w-1.5 h-1.5 rounded-full shrink-0 ${\n sse > 0 ? (session?.status === 'working' ? 'bg-green-500 animate-pulse' : 'bg-emerald-400') : 'bg-gray-500'\n }`} />\n <span className={`text-[10px] uppercase tracking-wide font-semibold ${statusColor}`}>{session?.status || 'unknown'}</span>\n </div>\n {session?.agent && (\n <div className=\"text-xs text-gray-500 truncate\">{session.agent}{session.server ? <> <span className=\"text-gray-700 mx-1\">·</span> {session.server}</> : null}</div>\n )}\n </div>\n </div>\n\n {error && (\n <div className=\"bg-red-900/20 border border-red-800/40 text-red-300 px-4 py-2 rounded-lg mb-6 text-sm\">{error}</div>\n )}\n\n {/* Full-width tabbed layout */}\n <NodeFullPanel alias={alias} session={session} sse={sse} sendMsg={sendMsg} setSendMsg={setSendMsg} sending={sending} sendTask={sendTask} sendError={sendError} />\n </div>\n );\n}\n\nfunction NodeFullPanel({ alias, session, sse, sendMsg, setSendMsg, sending, sendTask, sendError }: {\n alias: string; session: SessionDetail | null; sse: number; sendMsg: string; setSendMsg: (v: string) => void; sending: boolean; sendTask: () => void; sendError: string;\n}) {\n const [tab, setTab] = useState<'chat' | 'events' | 'info'>('chat');\n const { hasUnread } = useChatUnread();\n const [events, setEvents] = useState<Array<{ id: number; event_type: string; from_status: string; to_status: string; detail: string; created_at: string }>>([]);\n const [eventsLoaded, setEventsLoaded] = useState(false);\n const chatUnread = hasUnread(alias);\n\n useEffect(() => {\n if (tab === 'events' && !eventsLoaded) {\n fetch('/api/hub/task-events?limit=50').then(r => r.json()).then(d => {\n setEvents(d.events || []);\n setEventsLoaded(true);\n }).catch(() => setEventsLoaded(true));\n }\n }, [tab, eventsLoaded]);\n\n return (\n <div className=\"bg-[#111113] border border-[#26262b] rounded-xl overflow-hidden h-[calc(100vh-140px)] flex flex-col\">\n {/* Tab bar — round 40: emoji icons (💬 📋 📊) replaced with stroke\n SVG to drop the \"AI generated\" tell. */}\n <div className=\"flex border-b border-[#26262b] bg-[#0e0e10] shrink-0\">\n {([\n { id: 'chat', label: 'Chat', icon: 'M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z' },\n { id: 'events', label: 'Events', icon: 'M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z M14 2v6h6 M16 13H8 M16 17H8 M10 9H8' },\n { id: 'info', label: 'Info', icon: 'M12 21a9 9 0 100-18 9 9 0 000 18z M12 8h.01 M11 12h1v4h1' },\n ] as const).map(t => (\n <button key={t.id} onClick={() => setTab(t.id)}\n className={`flex items-center gap-2 px-5 py-3 text-sm font-medium border-b-2 transition-colors ${\n tab === t.id ? 'border-cyan-400 text-cyan-300' : 'border-transparent text-gray-500 hover:text-gray-300'\n }`}>\n <svg className=\"w-4 h-4 shrink-0\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d={t.icon} />\n </svg>\n <span className=\"relative inline-flex items-center gap-2\">\n {t.label}\n {t.id === 'chat' && chatUnread && tab !== 'chat' && (\n <span className=\"inline-block h-2.5 w-2.5 rounded-full bg-red-500\" aria-label=\"Unread chat messages\" />\n )}\n </span>\n </button>\n ))}\n </div>\n\n {/* Tab content */}\n <div className=\"flex-1 overflow-hidden\">\n {tab === 'chat' && (\n <TaskChatPanel alias={alias} onClose={() => {}} inline />\n )}\n\n {tab === 'events' && (\n <EventsTimeline events={events} loading={!eventsLoaded} />\n )}\n\n {tab === 'info' && (\n <div className=\"p-4 overflow-y-auto h-full space-y-4 max-w-2xl\">\n <div className=\"bg-[#161618] border border-[#26262b] rounded-xl p-4\">\n <h2 className=\"text-sm font-semibold text-gray-300 mb-3\">Node Info</h2>\n <div className=\"space-y-2 text-xs\">\n {[\n { label: 'Node ID', value: session?.node_id, alwaysShow: true },\n { label: 'Session ID', value: session?.session_id, alwaysShow: true },\n { label: 'Resume ID', value: session?.resume_id, alwaysShow: true },\n { label: 'Alias', value: session?.alias, alwaysShow: true },\n { label: 'Agent', value: session?.agent, alwaysShow: true },\n { label: 'Model', value: session?.model, alwaysShow: true },\n { label: 'Version', value: session?.version },\n { label: 'Server', value: session?.server, alwaysShow: true },\n { label: 'Hostname', value: session?.hostname, alwaysShow: true },\n { label: 'IP', value: session?.ip, alwaysShow: true },\n { label: 'Project', value: session?.project_dir },\n { label: 'Config', value: session?.config_path, alwaysShow: true },\n { label: 'Channels', value: session?.channels?.length ? (Array.isArray(session.channels) ? session.channels.join(', ') : session.channels) : null, alwaysShow: true },\n { label: 'Tmux', value: session?.tmux_name },\n { label: 'SSE', value: sse > 0 ? `Connected (${sse})` : 'Disconnected', alwaysShow: true },\n { label: 'Progress', value: session?.progress ? `${session.progress}%` : null },\n { label: 'Score', value: session?.score },\n { label: 'Last Seen', value: session?.last_seen_at, alwaysShow: true },\n { label: 'Registered', value: session?.registered_at, alwaysShow: true },\n { label: 'Updated', value: session?.updated_at, alwaysShow: true },\n ].map(({ label, value, alwaysShow }) => (value || alwaysShow) ? (\n <div key={label} className=\"flex justify-between gap-3\">\n <span className=\"text-gray-500 shrink-0\">{label}</span>\n <span className=\"text-gray-300 truncate max-w-[200px]\" title={value ? String(value) : '--'}>\n {value ? String(value) : '--'}\n </span>\n </div>\n ) : null)}\n </div>\n </div>\n\n {/* Current Task */}\n {session?.task && (\n <div className=\"bg-[#161618] border border-[#26262b] rounded-xl p-4\">\n <h2 className=\"text-sm font-semibold text-gray-300 mb-2\">Current Task</h2>\n <p className=\"text-xs text-gray-400 whitespace-pre-wrap\">{session.task}</p>\n </div>\n )}\n\n {/* Output */}\n {session?.output && (\n <div className=\"bg-[#161618] border border-[#26262b] rounded-xl p-4\">\n <h2 className=\"text-sm font-semibold text-gray-300 mb-2\">Last Output</h2>\n <p className=\"text-xs text-gray-400 whitespace-pre-wrap max-h-48 overflow-y-auto\">{session.output}</p>\n </div>\n )}\n\n {/* Send Task */}\n <div className=\"bg-[#161618] border border-[#26262b] rounded-xl p-4\">\n <h2 className=\"text-sm font-semibold text-gray-300 mb-2\">Send Task</h2>\n <div className=\"flex gap-2\">\n <input\n type=\"text\"\n value={sendMsg}\n onChange={e => setSendMsg(e.target.value)}\n onKeyDown={e => e.key === 'Enter' && sendTask()}\n placeholder={`Send task to ${alias}...`}\n className=\"flex-1 bg-[#0e0e10] border border-[#26262b] rounded px-3 py-2 text-base sm:text-xs text-white placeholder-gray-600 focus:border-blue-500 focus:outline-none\"\n />\n <button\n onClick={sendTask}\n disabled={sending || !sendMsg.trim()}\n className=\"inline-flex min-h-[44px] items-center justify-center px-4 py-2 bg-blue-600 hover:bg-blue-500 disabled:bg-gray-700 text-white text-xs rounded transition-colors\"\n >\n {sending ? '...' : 'Send'}\n </button>\n </div>\n {sendError && <div className=\"mt-2 text-xs text-red-400\">{sendError}</div>}\n </div>\n\n {/* Tmux Terminal */}\n {session?.tmux_name && (\n <TmuxViewer tmuxName={session.tmux_name} />\n )}\n </div>\n )}\n </div>\n </div>\n );\n}\n\n/** Round 59: emoji icons replaced with stroke SVG paths to drop the\n * \"AI generated\" tell. Each path is sized to fit inside the 18px\n * colored event dot. */\nconst EVENT_COLORS: Record<string, { dot: string; line: string; path: string }> = {\n delivered: { dot: 'bg-blue-400', line: 'bg-blue-500/30', path: 'M12 5v14 M5 12l7 7 7-7' }, // down arrow\n acked: { dot: 'bg-yellow-400', line: 'bg-yellow-500/30', path: 'M20 6 9 17l-5-5' }, // check\n running: { dot: 'bg-green-400', line: 'bg-green-500/30', path: 'M13 2L4 14h6l-1 8 10-12h-6l1-8z' }, // bolt\n replied: { dot: 'bg-purple-400', line: 'bg-purple-500/30', path: 'M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z' }, // chat bubble\n failed: { dot: 'bg-red-400', line: 'bg-red-500/30', path: 'M18 6 6 18 M6 6l12 12' }, // X\n created: { dot: 'bg-gray-400', line: 'bg-gray-500/30', path: 'M12 5v14 M5 12h14' }, // plus\n};\n\nfunction EventsTimeline({ events, loading }: { events: Array<{ id: number; event_type: string; from_status: string; to_status: string; detail: string; created_at: string; task_id?: string }>; loading: boolean }) {\n if (loading) return <div className=\"flex justify-center py-8\"><div className=\"w-5 h-5 border-2 border-cyan-500/30 border-t-cyan-500 rounded-full animate-spin\" /></div>;\n if (events.length === 0) return <div className=\"text-center py-12 text-gray-600 text-xs\">No task events yet</div>;\n\n // Group by task_id\n const groups = new Map<string, typeof events>();\n events.forEach(e => {\n const key = (e as { task_id?: string }).task_id || `ungrouped-${e.id}`;\n const group = groups.get(key) || [];\n group.push(e);\n groups.set(key, group);\n });\n\n return (\n <div className=\"p-4 overflow-y-auto h-full space-y-4\">\n {[...groups.entries()].map(([taskId, taskEvents]) => (\n <div key={taskId} className=\"bg-[#161618] border border-[#26262b] rounded-xl p-4\">\n <div className=\"text-[10px] text-gray-600 mb-3 truncate\">Task: {taskId.slice(0, 20)}...</div>\n <div className=\"relative pl-6\">\n {/* Vertical line */}\n <div className=\"absolute left-[9px] top-2 bottom-2 w-0.5 bg-[#1c1c1f]\" />\n\n {taskEvents.map((e, i) => {\n const cfg = EVENT_COLORS[e.to_status] || EVENT_COLORS.created;\n return (\n <div key={e.id} className=\"relative pb-4 last:pb-0\">\n {/* Dot on line — round 59: emoji icons replaced with\n a 10px stroke SVG path matching event type. */}\n <div className={`absolute -left-6 top-0.5 w-[18px] h-[18px] rounded-full ${cfg.dot} flex items-center justify-center z-10 border-2 border-[#161618]`}>\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#0e0e10\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d={cfg.path} />\n </svg>\n </div>\n {/* Content */}\n <div className=\"ml-2\">\n <div className=\"flex items-center gap-2\">\n <span className={`text-xs font-medium ${e.to_status === 'failed' ? 'text-red-400' : e.to_status === 'replied' ? 'text-purple-400' : 'text-gray-300'}`}>\n {e.event_type}\n </span>\n {e.from_status && (\n <span className=\"text-[10px] text-gray-600\">{e.from_status} → {e.to_status}</span>\n )}\n <span className=\"text-[10px] text-gray-600 ml-auto\">{timeAgo(e.created_at)}</span>\n </div>\n {e.detail && (\n <div className=\"text-[11px] text-gray-500 mt-0.5 truncate\">{e.detail.slice(0, 50)}{e.detail.length > 50 ? '...' : ''}</div>\n )}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n ))}\n </div>\n );\n}\n\nexport default function NodePage() {\n return (\n <Suspense fallback={\n // #209 R36: same pattern R30 fixed on /tasks — bare \"Loading...\"\n // string used p-6 (no mobile padding tighten) + no left indent,\n // so the fixed top-3 left-3 mobile hamburger covered the \"Loa\".\n // Match the loaded layout: p-4 sm:p-6 + lg:ml-0 ml-10 mobile\n // indent on the text. Loading state now visually maps to the\n // populated /node detail page.\n <div className=\"min-h-screen bg-[#0b0b0d] text-gray-100 p-4 sm:p-6\">\n <div className=\"lg:ml-0 ml-10 text-gray-500 text-sm\">Loading…</div>\n </div>\n }>\n <NodeDetailContent />\n </Suspense>\n );\n}\n"],"names":["TASK_STATUSES","STATUS_CHIP_CLASS","created","delivered","acked","running","replied","closed","failed","cancelled","expired","STATUS_DOT_HEX","SESSION_STATUS_CHIP_CLASS","working","idle","blocked","error","offline","SESSION_STATUS_TEXT_CLASS","STATUS_BAR_CLASS","TmuxViewer","tmuxName","output","setOutput","loading","setLoading","expanded","setExpanded","fetchTmux","res","fetch","encodeURIComponent","data","json","className","onClick","disabled","NodeDetailContent","searchParams","alias","get","session","setSession","inbox","setInbox","sse","setSse","setError","sendMsg","setSendMsg","sending","setSending","fetchData","e","Error","message","interval","setInterval","clearInterval","sendError","setSendError","sendTask","trim","method","headers","body","JSON","stringify","task","network_id","ok","setTimeout","href","statusColor","status","size","agent","server","NodeFullPanel","tab","setTab","hasUnread","events","setEvents","eventsLoaded","setEventsLoaded","chatUnread","then","r","d","catch","id","label","icon","map","t","fill","viewBox","stroke","strokeWidth","strokeLinecap","strokeLinejoin","onClose","inline","EventsTimeline","value","node_id","alwaysShow","session_id","resume_id","model","version","hostname","ip","project_dir","config_path","channels","length","Array","isArray","join","tmux_name","progress","score","last_seen_at","registered_at","updated_at","title","String","type","onChange","target","onKeyDown","key","placeholder","EVENT_COLORS","dot","line","path","groups","Map","forEach","task_id","group","push","set","entries","taskId","taskEvents","slice","i","cfg","to_status","width","height","event_type","from_status","created_at","detail","NodePage","fallback"],"mappings":"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"}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[50227,(a,b,c)=>{b.exports=a.x("node:path",()=>require("node:path"))},42023,a=>{"use strict";a.s(["SESSION_STATUS_CHIP_CLASS",0,{working:"bg-green-500/10 text-green-300 border-green-500/20",idle:"bg-gray-500/10 text-gray-300 border-gray-500/20",blocked:"bg-yellow-500/10 text-yellow-300 border-yellow-500/20",error:"bg-red-500/10 text-red-300 border-red-500/20",offline:"bg-gray-500/10 text-gray-500 border-gray-500/20"},"SESSION_STATUS_TEXT_CLASS",0,{working:"text-green-400",idle:"text-gray-400",blocked:"text-yellow-400",error:"text-red-400",offline:"text-gray-500"},"STATUS_BAR_CLASS",0,{created:"bg-gray-500",delivered:"bg-gray-400",acked:"bg-gray-300",running:"bg-green-500",replied:"bg-gray-400",closed:"bg-gray-600",failed:"bg-red-500",cancelled:"bg-gray-600",expired:"bg-gray-600"},"STATUS_CHIP_CLASS",0,{created:"bg-gray-500/10 text-gray-400 border-gray-500/20",delivered:"bg-gray-500/10 text-gray-300 border-gray-500/20",acked:"bg-gray-500/10 text-gray-300 border-gray-500/20",running:"bg-green-500/10 text-green-300 border-green-500/20",replied:"bg-gray-500/10 text-gray-300 border-gray-500/20",closed:"bg-gray-500/10 text-gray-500 border-gray-500/20",failed:"bg-red-500/10 text-red-300 border-red-500/20",cancelled:"bg-gray-500/10 text-gray-500 border-gray-500/20",expired:"bg-gray-500/10 text-gray-500 border-gray-500/20"},"STATUS_DOT_HEX",0,{created:"#9ca3af",delivered:"#9ca3af",acked:"#9ca3af",running:"#4ade80",replied:"#9ca3af",closed:"#6b7280",failed:"#f87171",cancelled:"#6b7280",expired:"#6b7280"},"TASK_STATUSES",0,["created","delivered","acked","running","replied","closed","failed","cancelled","expired"]])},28010,a=>{"use strict";var b=a.i(79616),c=a.i(20855);a.s(["useCollapsibleSearch",0,function(a){let{value:d}=a,[e,f]=(0,c.useState)(!1),g=(0,c.useRef)(a);g.current=a;let h=(0,c.useRef)(e);h.current=e;let i=(0,c.useCallback)(()=>{g.current.onChange(""),f(!1)},[]),[j]=(0,c.useState)(()=>({Button:()=>{let{value:a,label:c="Search",enabled:d=!0}=g.current,e=h.current;if(!d)return(0,b.jsx)(b.Fragment,{});let i=e||a;return(0,b.jsxs)("button",{type:"button",onClick:()=>f(a=>!a),"aria-label":e?`Close ${c.toLowerCase()}`:`Open ${c.toLowerCase()}`,"aria-pressed":e,title:e?`Close ${c.toLowerCase()}`:c,className:`relative shrink-0 inline-flex items-center justify-center rounded-full border w-9 h-9 transition-colors ${i?"border-cyan-500/40 bg-cyan-500/10 text-cyan-300":"border-[#26262b] bg-[#161618] text-gray-400 hover:text-gray-200 hover:border-[#3a3a41]"}`,children:[(0,b.jsx)("svg",{className:"w-4 h-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:(0,b.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"})}),a&&!e&&(0,b.jsx)("span",{"aria-hidden":!0,className:"absolute -top-0.5 -right-0.5 w-2 h-2 rounded-full bg-cyan-400 ring-2 ring-[#0b0b0d]"})]})},Row:()=>{let{value:a,onChange:c,placeholder:d="Search…",enabled:e=!0}=g.current,f=h.current;return e&&(f||a)?(0,b.jsxs)("div",{className:"mb-3 sm:mb-4 flex items-center gap-2",children:[(0,b.jsx)("input",{type:"text",value:a,onChange:a=>c(a.target.value),onKeyDown:a=>{"Escape"===a.key&&i()},placeholder:d,autoFocus:f,className:"flex-1 bg-[#161618] border border-[#26262b] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/40 focus:outline-none"}),(0,b.jsx)("button",{type:"button",onClick:i,className:"shrink-0 text-xs text-gray-500 hover:text-gray-300 px-2 py-2","aria-label":"Clear and close search",children:"Cancel"})]}):null}}));return{Button:j.Button,Row:j.Row,value:d,reset:i}}])},95029,a=>{"use strict";var b=a.i(79616),c=a.i(88218);a.s(["default",0,function(){return(0,b.jsx)(c.NodesView,{})}])}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=%5Broot-of-the-server%5D__0fs-j~c._.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../dash-stageb-wt/app/lib/status.ts","../../../../../dash-stageb-wt/app/components/CollapsibleSearch.tsx","../../../../../dash-stageb-wt/app/nodes/page.tsx"],"sourcesContent":["/** Single source of truth for task status colors. Both `/tasks` (chip\n * strip, badge, distribution bar) and the Overview Recent Activity row\n * used to ship their own incomplete maps; round 66 consolidates them\n * here so adding a new status updates every consumer at once.\n *\n * Order in `TASK_STATUSES` is chronological-ish: lifecycle progress on\n * the left, terminal-good (`closed`) in the middle, terminal-bad\n * (`failed`/`cancelled`/`expired`) on the right. */\n\nexport const TASK_STATUSES = [\n 'created',\n 'delivered',\n 'acked',\n 'running',\n 'replied',\n 'closed',\n 'failed',\n 'cancelled',\n 'expired',\n] as const;\n\n/** Pill / chip background+text+border. Tailwind classes, not inlined hex,\n * because chips are static class names safe from purge. */\n/** #217 D3 (OpenWebUI-style color restraint): the 9-hue rainbow is\n * collapsed to a semantic triad — green = actively running, red =\n * failed, gray = everything else (in-flight states in lighter gray,\n * terminal states dimmer). Color now means something instead of\n * decorating every enum value. */\nexport const STATUS_CHIP_CLASS: Record<string, string> = {\n created: 'bg-gray-500/10 text-gray-400 border-gray-500/20',\n delivered: 'bg-gray-500/10 text-gray-300 border-gray-500/20',\n acked: 'bg-gray-500/10 text-gray-300 border-gray-500/20',\n running: 'bg-green-500/10 text-green-300 border-green-500/20',\n replied: 'bg-gray-500/10 text-gray-300 border-gray-500/20',\n closed: 'bg-gray-500/10 text-gray-500 border-gray-500/20',\n failed: 'bg-red-500/10 text-red-300 border-red-500/20',\n cancelled: 'bg-gray-500/10 text-gray-500 border-gray-500/20',\n expired: 'bg-gray-500/10 text-gray-500 border-gray-500/20',\n};\n\n/** Inline hex dots — used wherever Tailwind would purge dynamic class\n * names (`bg-${family}-400`). Style attribute carries the color. */\nexport const STATUS_DOT_HEX: Record<string, string> = {\n created: '#9ca3af',\n delivered: '#9ca3af',\n acked: '#9ca3af',\n running: '#4ade80',\n replied: '#9ca3af',\n closed: '#6b7280',\n failed: '#f87171',\n cancelled: '#6b7280',\n expired: '#6b7280',\n};\n\n/** Session lifecycle (distinct from task lifecycle above). Shared by\n * /nodes status pills and /admin Online Sessions row chips so an\n * agent in `blocked` state reads the same color everywhere.\n * Round 91 — extracted from app/nodes/page.tsx. */\n/** D3: idle drops from blue to gray — only working (green), blocked\n * (amber, actionable) and error (red) earn color. */\nexport const SESSION_STATUS_CHIP_CLASS: Record<string, string> = {\n working: 'bg-green-500/10 text-green-300 border-green-500/20',\n idle: 'bg-gray-500/10 text-gray-300 border-gray-500/20',\n blocked: 'bg-yellow-500/10 text-yellow-300 border-yellow-500/20',\n error: 'bg-red-500/10 text-red-300 border-red-500/20',\n offline: 'bg-gray-500/10 text-gray-500 border-gray-500/20',\n};\n\n/** Text-only color for session status. Used where a chip background\n * would be too heavy — e.g. /node detail header status label. */\nexport const SESSION_STATUS_TEXT_CLASS: Record<string, string> = {\n working: 'text-green-400',\n idle: 'text-gray-400',\n blocked: 'text-yellow-400',\n error: 'text-red-400',\n offline: 'text-gray-500',\n};\n\n/** Solid bar segment background for the Tasks distribution bar. */\nexport const STATUS_BAR_CLASS: Record<string, string> = {\n created: 'bg-gray-500',\n delivered: 'bg-gray-400',\n acked: 'bg-gray-300',\n running: 'bg-green-500',\n replied: 'bg-gray-400',\n closed: 'bg-gray-600',\n failed: 'bg-red-500',\n cancelled: 'bg-gray-600',\n expired: 'bg-gray-600',\n};\n","'use client';\n\n/**\n * CollapsibleSearch\n * ──────────────────\n * #209 R34 — Reusable WeChat-style search-toggle pattern.\n *\n * Two pieces of UI from one component:\n * 1. A magnifier button (`w-9 h-9 rounded-full`) that lives in a page\n * header — render it via `Button`.\n * 2. A collapsible search row that reveals below the header on open —\n * render it via `Row`.\n *\n * Used together they replicate WeChat's chat-list search affordance:\n * the search field is hidden by default behind a small circle at the\n * top-right of the page header. Tapping it slides the input open,\n * autofocuses, and lets Escape clear + close. A cyan dot appears in\n * the corner of the icon when there is an active search term but the\n * row is collapsed, so the user always knows a filter is in effect.\n *\n * Owner of the search string is the parent (this component is\n * uncontrolled w.r.t. value — pure UI). That keeps the filter logic\n * with the page so it's easy to plumb additional behaviour like\n * `from:alias` shortcuts or highlight rendering.\n *\n * Shipped as a single component with two named children so callers\n * can place `<Button>` inside their existing header flex row and\n * `<Row>` just after, without restructuring around a tighter API.\n *\n * First adopters: /nodes (R32), /messages (R33). R34 ships\n * /nodes + /messages migrated to use this component; future pages\n * can add search by importing it instead of copy-pasting the JSX.\n */\n\nimport { useState, useCallback, useRef } from 'react';\n\ninterface CollapsibleSearchAPI {\n /** Round magnifier button. Place in your page header (e.g. right edge). */\n Button: () => React.JSX.Element;\n /** Collapsible search input row. Place immediately after the header. */\n Row: () => React.JSX.Element | null;\n /** Current search value — pass to your filter logic. */\n value: string;\n /** Programmatically clear + close (useful for \"no results\" reset link). */\n reset: () => void;\n}\n\ninterface UseCollapsibleSearchProps {\n /** Controlled value + setter from the parent — owns the search string. */\n value: string;\n onChange: (next: string) => void;\n /** Input placeholder. */\n placeholder?: string;\n /** Accessible label for the button (also drives `title=`). */\n label?: string;\n /** Hide everything when false (e.g. when there is nothing to search). */\n enabled?: boolean;\n}\n\nexport function useCollapsibleSearch(props: UseCollapsibleSearchProps): CollapsibleSearchAPI {\n const { value } = props;\n const [open, setOpen] = useState(false);\n\n /* Loop R16 (R14 同类根因扫查): Button/Row were useCallback-RECREATED on\n every open/value change. Callers render them as JSX types\n (<ctl.Row />), so each keystroke produced a new component type →\n React unmounted and remounted the <input> mid-typing (measured: a\n data-probe tag on the node vanished after typing two chars). The\n controlled value + autoFocus masked it in English, but any IME\n composition (Chinese!) got cut off per keystroke. Fix: ONE stable\n component type per hook instance; renders read live state/props from\n refs, so updates re-render in place instead of remounting. */\n /* eslint-disable react-hooks/refs -- render-phase ref mirroring + reads\n are the POINT of the R16 fix: one stable component type per hook\n instance rendering live values (the useCallback version remounted the\n input per keystroke and broke IME composition). */\n const propsRef = useRef(props);\n propsRef.current = props;\n const openRef = useRef(open);\n openRef.current = open;\n\n const reset = useCallback(() => {\n propsRef.current.onChange('');\n setOpen(false);\n }, []);\n\n const [stable] = useState(() => {\n const Button = () => {\n const { value, label = 'Search', enabled = true } = propsRef.current;\n const open = openRef.current;\n if (!enabled) return <></>;\n const active = open || value;\n return (\n <button\n type=\"button\"\n onClick={() => setOpen(v => !v)}\n aria-label={open ? `Close ${label.toLowerCase()}` : `Open ${label.toLowerCase()}`}\n aria-pressed={open}\n title={open ? `Close ${label.toLowerCase()}` : label}\n className={`relative shrink-0 inline-flex items-center justify-center rounded-full border w-9 h-9 transition-colors ${\n active\n ? 'border-cyan-500/40 bg-cyan-500/10 text-cyan-300'\n : 'border-[#26262b] bg-[#161618] text-gray-400 hover:text-gray-200 hover:border-[#3a3a41]'\n }`}\n >\n <svg className=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" />\n </svg>\n {value && !open && (\n <span aria-hidden className=\"absolute -top-0.5 -right-0.5 w-2 h-2 rounded-full bg-cyan-400 ring-2 ring-[#0b0b0d]\" />\n )}\n </button>\n );\n };\n const Row = () => {\n const { value, onChange, placeholder = 'Search…', enabled = true } = propsRef.current;\n const open = openRef.current;\n if (!enabled) return null;\n if (!open && !value) return null;\n return (\n <div className=\"mb-3 sm:mb-4 flex items-center gap-2\">\n <input\n type=\"text\"\n value={value}\n onChange={e => onChange(e.target.value)}\n onKeyDown={e => { if (e.key === 'Escape') reset(); }}\n placeholder={placeholder}\n autoFocus={open}\n className=\"flex-1 bg-[#161618] border border-[#26262b] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/40 focus:outline-none\"\n />\n <button\n type=\"button\"\n onClick={reset}\n className=\"shrink-0 text-xs text-gray-500 hover:text-gray-300 px-2 py-2\"\n aria-label=\"Clear and close search\"\n >\n Cancel\n </button>\n </div>\n );\n };\n return { Button, Row };\n });\n /* eslint-enable react-hooks/refs */\n\n return { Button: stable.Button, Row: stable.Row, value, reset };\n}\n","'use client';\n\nimport { NodesView } from './NodesView';\n\n// #Stage B: /nodes with no alias segment = nothing selected. The page body\n// lives in NodesView so /nodes and /nodes/[alias] render the same component\n// (selection is state + shallow pushState, not a route remount).\nexport default function NodesPage() {\n return <NodesView />;\n}\n"],"names":["TASK_STATUSES","STATUS_CHIP_CLASS","created","delivered","acked","running","replied","closed","failed","cancelled","expired","STATUS_DOT_HEX","SESSION_STATUS_CHIP_CLASS","working","idle","blocked","error","offline","SESSION_STATUS_TEXT_CLASS","STATUS_BAR_CLASS","useCollapsibleSearch","props","value","open","setOpen","propsRef","current","openRef","reset","onChange","stable","Button","label","enabled","active","type","onClick","v","toLowerCase","title","className","fill","viewBox","stroke","strokeWidth","strokeLinecap","strokeLinejoin","d","Row","placeholder","e","target","onKeyDown","key","autoFocus","NodesPage"],"mappings":"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"}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
module.exports=[50227,(a,b,c)=>{b.exports=a.x("node:path",()=>require("node:path"))},42023,a=>{"use strict";a.s(["SESSION_STATUS_CHIP_CLASS",0,{working:"bg-green-500/10 text-green-300 border-green-500/20",idle:"bg-gray-500/10 text-gray-300 border-gray-500/20",blocked:"bg-yellow-500/10 text-yellow-300 border-yellow-500/20",error:"bg-red-500/10 text-red-300 border-red-500/20",offline:"bg-gray-500/10 text-gray-500 border-gray-500/20"},"SESSION_STATUS_TEXT_CLASS",0,{working:"text-green-400",idle:"text-gray-400",blocked:"text-yellow-400",error:"text-red-400",offline:"text-gray-500"},"STATUS_BAR_CLASS",0,{created:"bg-gray-500",delivered:"bg-gray-400",acked:"bg-gray-300",running:"bg-green-500",replied:"bg-gray-400",closed:"bg-gray-600",failed:"bg-red-500",cancelled:"bg-gray-600",expired:"bg-gray-600"},"STATUS_CHIP_CLASS",0,{created:"bg-gray-500/10 text-gray-400 border-gray-500/20",delivered:"bg-gray-500/10 text-gray-300 border-gray-500/20",acked:"bg-gray-500/10 text-gray-300 border-gray-500/20",running:"bg-green-500/10 text-green-300 border-green-500/20",replied:"bg-gray-500/10 text-gray-300 border-gray-500/20",closed:"bg-gray-500/10 text-gray-500 border-gray-500/20",failed:"bg-red-500/10 text-red-300 border-red-500/20",cancelled:"bg-gray-500/10 text-gray-500 border-gray-500/20",expired:"bg-gray-500/10 text-gray-500 border-gray-500/20"},"STATUS_DOT_HEX",0,{created:"#9ca3af",delivered:"#9ca3af",acked:"#9ca3af",running:"#4ade80",replied:"#9ca3af",closed:"#6b7280",failed:"#f87171",cancelled:"#6b7280",expired:"#6b7280"},"TASK_STATUSES",0,["created","delivered","acked","running","replied","closed","failed","cancelled","expired"]])},28010,a=>{"use strict";var b=a.i(79616),c=a.i(20855);a.s(["useCollapsibleSearch",0,function(a){let{value:d}=a,[e,f]=(0,c.useState)(!1),g=(0,c.useRef)(a);g.current=a;let h=(0,c.useRef)(e);h.current=e;let i=(0,c.useCallback)(()=>{g.current.onChange(""),f(!1)},[]),[j]=(0,c.useState)(()=>({Button:()=>{let{value:a,label:c="Search",enabled:d=!0}=g.current,e=h.current;if(!d)return(0,b.jsx)(b.Fragment,{});let i=e||a;return(0,b.jsxs)("button",{type:"button",onClick:()=>f(a=>!a),"aria-label":e?`Close ${c.toLowerCase()}`:`Open ${c.toLowerCase()}`,"aria-pressed":e,title:e?`Close ${c.toLowerCase()}`:c,className:`relative shrink-0 inline-flex items-center justify-center rounded-full border w-9 h-9 transition-colors ${i?"border-cyan-500/40 bg-cyan-500/10 text-cyan-300":"border-[#26262b] bg-[#161618] text-gray-400 hover:text-gray-200 hover:border-[#3a3a41]"}`,children:[(0,b.jsx)("svg",{className:"w-4 h-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:(0,b.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"})}),a&&!e&&(0,b.jsx)("span",{"aria-hidden":!0,className:"absolute -top-0.5 -right-0.5 w-2 h-2 rounded-full bg-cyan-400 ring-2 ring-[#0b0b0d]"})]})},Row:()=>{let{value:a,onChange:c,placeholder:d="Search…",enabled:e=!0}=g.current,f=h.current;return e&&(f||a)?(0,b.jsxs)("div",{className:"mb-3 sm:mb-4 flex items-center gap-2",children:[(0,b.jsx)("input",{type:"text",value:a,onChange:a=>c(a.target.value),onKeyDown:a=>{"Escape"===a.key&&i()},placeholder:d,autoFocus:f,className:"flex-1 bg-[#161618] border border-[#26262b] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/40 focus:outline-none"}),(0,b.jsx)("button",{type:"button",onClick:i,className:"shrink-0 text-xs text-gray-500 hover:text-gray-300 px-2 py-2","aria-label":"Clear and close search",children:"Cancel"})]}):null}}));return{Button:j.Button,Row:j.Row,value:d,reset:i}}])},65648,a=>{"use strict";var b=a.i(79616),c=a.i(20855),d=a.i(88218);a.s(["default",0,function({params:a}){let{alias:e}=(0,c.use)(a);return(0,b.jsx)(d.NodesView,{initialAlias:(0,d.decodeAliasSegment)(e)})}])}];
|
|
2
|
-
|
|
3
|
-
//# sourceMappingURL=%5Broot-of-the-server%5D__0ma5-5u._.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../dash-stageb-wt/app/lib/status.ts","../../../../../dash-stageb-wt/app/components/CollapsibleSearch.tsx","../../../../../dash-stageb-wt/app/nodes/%5Balias%5D/page.tsx"],"sourcesContent":["/** Single source of truth for task status colors. Both `/tasks` (chip\n * strip, badge, distribution bar) and the Overview Recent Activity row\n * used to ship their own incomplete maps; round 66 consolidates them\n * here so adding a new status updates every consumer at once.\n *\n * Order in `TASK_STATUSES` is chronological-ish: lifecycle progress on\n * the left, terminal-good (`closed`) in the middle, terminal-bad\n * (`failed`/`cancelled`/`expired`) on the right. */\n\nexport const TASK_STATUSES = [\n 'created',\n 'delivered',\n 'acked',\n 'running',\n 'replied',\n 'closed',\n 'failed',\n 'cancelled',\n 'expired',\n] as const;\n\n/** Pill / chip background+text+border. Tailwind classes, not inlined hex,\n * because chips are static class names safe from purge. */\n/** #217 D3 (OpenWebUI-style color restraint): the 9-hue rainbow is\n * collapsed to a semantic triad — green = actively running, red =\n * failed, gray = everything else (in-flight states in lighter gray,\n * terminal states dimmer). Color now means something instead of\n * decorating every enum value. */\nexport const STATUS_CHIP_CLASS: Record<string, string> = {\n created: 'bg-gray-500/10 text-gray-400 border-gray-500/20',\n delivered: 'bg-gray-500/10 text-gray-300 border-gray-500/20',\n acked: 'bg-gray-500/10 text-gray-300 border-gray-500/20',\n running: 'bg-green-500/10 text-green-300 border-green-500/20',\n replied: 'bg-gray-500/10 text-gray-300 border-gray-500/20',\n closed: 'bg-gray-500/10 text-gray-500 border-gray-500/20',\n failed: 'bg-red-500/10 text-red-300 border-red-500/20',\n cancelled: 'bg-gray-500/10 text-gray-500 border-gray-500/20',\n expired: 'bg-gray-500/10 text-gray-500 border-gray-500/20',\n};\n\n/** Inline hex dots — used wherever Tailwind would purge dynamic class\n * names (`bg-${family}-400`). Style attribute carries the color. */\nexport const STATUS_DOT_HEX: Record<string, string> = {\n created: '#9ca3af',\n delivered: '#9ca3af',\n acked: '#9ca3af',\n running: '#4ade80',\n replied: '#9ca3af',\n closed: '#6b7280',\n failed: '#f87171',\n cancelled: '#6b7280',\n expired: '#6b7280',\n};\n\n/** Session lifecycle (distinct from task lifecycle above). Shared by\n * /nodes status pills and /admin Online Sessions row chips so an\n * agent in `blocked` state reads the same color everywhere.\n * Round 91 — extracted from app/nodes/page.tsx. */\n/** D3: idle drops from blue to gray — only working (green), blocked\n * (amber, actionable) and error (red) earn color. */\nexport const SESSION_STATUS_CHIP_CLASS: Record<string, string> = {\n working: 'bg-green-500/10 text-green-300 border-green-500/20',\n idle: 'bg-gray-500/10 text-gray-300 border-gray-500/20',\n blocked: 'bg-yellow-500/10 text-yellow-300 border-yellow-500/20',\n error: 'bg-red-500/10 text-red-300 border-red-500/20',\n offline: 'bg-gray-500/10 text-gray-500 border-gray-500/20',\n};\n\n/** Text-only color for session status. Used where a chip background\n * would be too heavy — e.g. /node detail header status label. */\nexport const SESSION_STATUS_TEXT_CLASS: Record<string, string> = {\n working: 'text-green-400',\n idle: 'text-gray-400',\n blocked: 'text-yellow-400',\n error: 'text-red-400',\n offline: 'text-gray-500',\n};\n\n/** Solid bar segment background for the Tasks distribution bar. */\nexport const STATUS_BAR_CLASS: Record<string, string> = {\n created: 'bg-gray-500',\n delivered: 'bg-gray-400',\n acked: 'bg-gray-300',\n running: 'bg-green-500',\n replied: 'bg-gray-400',\n closed: 'bg-gray-600',\n failed: 'bg-red-500',\n cancelled: 'bg-gray-600',\n expired: 'bg-gray-600',\n};\n","'use client';\n\n/**\n * CollapsibleSearch\n * ──────────────────\n * #209 R34 — Reusable WeChat-style search-toggle pattern.\n *\n * Two pieces of UI from one component:\n * 1. A magnifier button (`w-9 h-9 rounded-full`) that lives in a page\n * header — render it via `Button`.\n * 2. A collapsible search row that reveals below the header on open —\n * render it via `Row`.\n *\n * Used together they replicate WeChat's chat-list search affordance:\n * the search field is hidden by default behind a small circle at the\n * top-right of the page header. Tapping it slides the input open,\n * autofocuses, and lets Escape clear + close. A cyan dot appears in\n * the corner of the icon when there is an active search term but the\n * row is collapsed, so the user always knows a filter is in effect.\n *\n * Owner of the search string is the parent (this component is\n * uncontrolled w.r.t. value — pure UI). That keeps the filter logic\n * with the page so it's easy to plumb additional behaviour like\n * `from:alias` shortcuts or highlight rendering.\n *\n * Shipped as a single component with two named children so callers\n * can place `<Button>` inside their existing header flex row and\n * `<Row>` just after, without restructuring around a tighter API.\n *\n * First adopters: /nodes (R32), /messages (R33). R34 ships\n * /nodes + /messages migrated to use this component; future pages\n * can add search by importing it instead of copy-pasting the JSX.\n */\n\nimport { useState, useCallback, useRef } from 'react';\n\ninterface CollapsibleSearchAPI {\n /** Round magnifier button. Place in your page header (e.g. right edge). */\n Button: () => React.JSX.Element;\n /** Collapsible search input row. Place immediately after the header. */\n Row: () => React.JSX.Element | null;\n /** Current search value — pass to your filter logic. */\n value: string;\n /** Programmatically clear + close (useful for \"no results\" reset link). */\n reset: () => void;\n}\n\ninterface UseCollapsibleSearchProps {\n /** Controlled value + setter from the parent — owns the search string. */\n value: string;\n onChange: (next: string) => void;\n /** Input placeholder. */\n placeholder?: string;\n /** Accessible label for the button (also drives `title=`). */\n label?: string;\n /** Hide everything when false (e.g. when there is nothing to search). */\n enabled?: boolean;\n}\n\nexport function useCollapsibleSearch(props: UseCollapsibleSearchProps): CollapsibleSearchAPI {\n const { value } = props;\n const [open, setOpen] = useState(false);\n\n /* Loop R16 (R14 同类根因扫查): Button/Row were useCallback-RECREATED on\n every open/value change. Callers render them as JSX types\n (<ctl.Row />), so each keystroke produced a new component type →\n React unmounted and remounted the <input> mid-typing (measured: a\n data-probe tag on the node vanished after typing two chars). The\n controlled value + autoFocus masked it in English, but any IME\n composition (Chinese!) got cut off per keystroke. Fix: ONE stable\n component type per hook instance; renders read live state/props from\n refs, so updates re-render in place instead of remounting. */\n /* eslint-disable react-hooks/refs -- render-phase ref mirroring + reads\n are the POINT of the R16 fix: one stable component type per hook\n instance rendering live values (the useCallback version remounted the\n input per keystroke and broke IME composition). */\n const propsRef = useRef(props);\n propsRef.current = props;\n const openRef = useRef(open);\n openRef.current = open;\n\n const reset = useCallback(() => {\n propsRef.current.onChange('');\n setOpen(false);\n }, []);\n\n const [stable] = useState(() => {\n const Button = () => {\n const { value, label = 'Search', enabled = true } = propsRef.current;\n const open = openRef.current;\n if (!enabled) return <></>;\n const active = open || value;\n return (\n <button\n type=\"button\"\n onClick={() => setOpen(v => !v)}\n aria-label={open ? `Close ${label.toLowerCase()}` : `Open ${label.toLowerCase()}`}\n aria-pressed={open}\n title={open ? `Close ${label.toLowerCase()}` : label}\n className={`relative shrink-0 inline-flex items-center justify-center rounded-full border w-9 h-9 transition-colors ${\n active\n ? 'border-cyan-500/40 bg-cyan-500/10 text-cyan-300'\n : 'border-[#26262b] bg-[#161618] text-gray-400 hover:text-gray-200 hover:border-[#3a3a41]'\n }`}\n >\n <svg className=\"w-4 h-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\" />\n </svg>\n {value && !open && (\n <span aria-hidden className=\"absolute -top-0.5 -right-0.5 w-2 h-2 rounded-full bg-cyan-400 ring-2 ring-[#0b0b0d]\" />\n )}\n </button>\n );\n };\n const Row = () => {\n const { value, onChange, placeholder = 'Search…', enabled = true } = propsRef.current;\n const open = openRef.current;\n if (!enabled) return null;\n if (!open && !value) return null;\n return (\n <div className=\"mb-3 sm:mb-4 flex items-center gap-2\">\n <input\n type=\"text\"\n value={value}\n onChange={e => onChange(e.target.value)}\n onKeyDown={e => { if (e.key === 'Escape') reset(); }}\n placeholder={placeholder}\n autoFocus={open}\n className=\"flex-1 bg-[#161618] border border-[#26262b] rounded-lg px-3 py-2 text-base sm:text-sm text-white placeholder-gray-600 focus:border-cyan-500/40 focus:outline-none\"\n />\n <button\n type=\"button\"\n onClick={reset}\n className=\"shrink-0 text-xs text-gray-500 hover:text-gray-300 px-2 py-2\"\n aria-label=\"Clear and close search\"\n >\n Cancel\n </button>\n </div>\n );\n };\n return { Button, Row };\n });\n /* eslint-enable react-hooks/refs */\n\n return { Button: stable.Button, Row: stable.Row, value, reset };\n}\n","'use client';\n\nimport { use } from 'react';\nimport { NodesView, decodeAliasSegment } from '../NodesView';\n\n// #Stage B: shareable deep link — /nodes/<alias> opens the node list with\n// that node's conversation already selected in the right pane (or the\n// full-screen overlay below md). Client component on purpose: NodesView is\n// a client module, so its exports (decodeAliasSegment) are client\n// references — calling one from a server component crashes the render\n// (\"Attempted to call … from the server\", caught by the deep-link e2e).\n// The segment is decoded exactly once (aliases are routinely Chinese →\n// percent-encoded; see the double-decode note on the helper).\nexport default function NodeAliasPage({ params }: { params: Promise<{ alias: string }> }) {\n const { alias } = use(params);\n return <NodesView initialAlias={decodeAliasSegment(alias)} />;\n}\n"],"names":["TASK_STATUSES","STATUS_CHIP_CLASS","created","delivered","acked","running","replied","closed","failed","cancelled","expired","STATUS_DOT_HEX","SESSION_STATUS_CHIP_CLASS","working","idle","blocked","error","offline","SESSION_STATUS_TEXT_CLASS","STATUS_BAR_CLASS","useCollapsibleSearch","props","value","open","setOpen","propsRef","current","openRef","reset","onChange","stable","Button","label","enabled","active","type","onClick","v","toLowerCase","title","className","fill","viewBox","stroke","strokeWidth","strokeLinecap","strokeLinejoin","d","Row","placeholder","e","target","onKeyDown","key","autoFocus","NodeAliasPage","params","alias","initialAlias"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../dash-stageb-wt/package.json.%5Bjson%5D.mjs","../../../../../dash-stageb-wt/app/lib/version.ts"],"sourcesContent":["\"use turbopack no side effects\";\nexport default {\"name\":\"@sleep2agi/agent-network-dashboard\",\"version\":\"0.6.3-preview.49\",\"description\":\"Agent Network Dashboard — Web UI for managing AI Agent networks\",\"main\":\"apps/desktop/electron/main.cjs\",\"scripts\":{\"dev\":\"next dev\",\"build\":\"next build\",\"postbuild\":\"node scripts/stamp-build-commit.mjs\",\"start\":\"next start\",\"lint\":\"eslint\",\"app:android:init\":\"npx cap add android\",\"app:android:sync\":\"npx cap sync android\",\"app:android:open\":\"npx cap open android\",\"app:ios:init\":\"npx cap add ios\",\"app:ios:sync\":\"npx cap sync ios\",\"app:ios:open\":\"npx cap open ios\",\"app:desktop\":\"electron apps/desktop/electron/main.cjs\",\"app:desktop:pack\":\"electron-builder --config electron-builder.json\",\"prepublishOnly\":\"[ -f .next/BUILD_ID ] || (echo 'prepublishOnly: .next/BUILD_ID missing — run npm run build first (see commit 05c1ebf body for R224 chunk-500 root cause)' >&2 && exit 1)\",\"avatars:check\":\"node scripts/check-avatars.mjs\"},\"bin\":{\"agent-network-dashboard\":\"./bin/start.js\"},\"homepage\":\"https://anet.sh\",\"repository\":{\"type\":\"git\",\"url\":\"https://github.com/sleep2agi/agent-network-dashboard\"},\"bugs\":{\"url\":\"https://github.com/sleep2agi/agent-network-dashboard/issues\"},\"author\":\"sleep2agi\",\"license\":\"Apache-2.0\",\"publishConfig\":{\"access\":\"public\"},\"dependencies\":{\"@capacitor/core\":\"^7.4.4\",\"next\":\"16.2.3\",\"pinyin-pro\":\"^3.28.1\",\"react\":\"19.2.4\",\"react-dom\":\"19.2.4\",\"react-markdown\":\"^10.1.0\",\"remark-gfm\":\"^4.0.1\",\"swr\":\"^2.4.1\"},\"devDependencies\":{\"@capacitor/android\":\"^7.4.4\",\"@capacitor/cli\":\"^7.4.4\",\"@capacitor/ios\":\"^7.4.4\",\"@playwright/test\":\"^1.59.1\",\"@tailwindcss/postcss\":\"^4\",\"@types/node\":\"^20\",\"@types/react\":\"^19\",\"@types/react-dom\":\"^19\",\"electron\":\"^39.2.6\",\"electron-builder\":\"^26.0.12\",\"eslint\":\"^9\",\"eslint-config-next\":\"16.2.3\",\"tailwindcss\":\"^4\",\"typescript\":\"^5\"}};\n//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInR1cmJvcGFjazovLy9kYXNoLXN0YWdlYi13dC9wYWNrYWdlLmpzb24iXSwic291cmNlc0NvbnRlbnQiOlsie1wibmFtZVwiOlwiQHNsZWVwMmFnaS9hZ2VudC1uZXR3b3JrLWRhc2hib2FyZFwiLFwidmVyc2lvblwiOlwiMC42LjMtcHJldmlldy40OVwiLFwiZGVzY3JpcHRpb25cIjpcIkFnZW50IE5ldHdvcmsgRGFzaGJvYXJkIOKAlCBXZWIgVUkgZm9yIG1hbmFnaW5nIEFJIEFnZW50IG5ldHdvcmtzXCIsXCJtYWluXCI6XCJhcHBzL2Rlc2t0b3AvZWxlY3Ryb24vbWFpbi5janNcIixcInNjcmlwdHNcIjp7XCJkZXZcIjpcIm5leHQgZGV2XCIsXCJidWlsZFwiOlwibmV4dCBidWlsZFwiLFwicG9zdGJ1aWxkXCI6XCJub2RlIHNjcmlwdHMvc3RhbXAtYnVpbGQtY29tbWl0Lm1qc1wiLFwic3RhcnRcIjpcIm5leHQgc3RhcnRcIixcImxpbnRcIjpcImVzbGludFwiLFwiYXBwOmFuZHJvaWQ6aW5pdFwiOlwibnB4IGNhcCBhZGQgYW5kcm9pZFwiLFwiYXBwOmFuZHJvaWQ6c3luY1wiOlwibnB4IGNhcCBzeW5jIGFuZHJvaWRcIixcImFwcDphbmRyb2lkOm9wZW5cIjpcIm5weCBjYXAgb3BlbiBhbmRyb2lkXCIsXCJhcHA6aW9zOmluaXRcIjpcIm5weCBjYXAgYWRkIGlvc1wiLFwiYXBwOmlvczpzeW5jXCI6XCJucHggY2FwIHN5bmMgaW9zXCIsXCJhcHA6aW9zOm9wZW5cIjpcIm5weCBjYXAgb3BlbiBpb3NcIixcImFwcDpkZXNrdG9wXCI6XCJlbGVjdHJvbiBhcHBzL2Rlc2t0b3AvZWxlY3Ryb24vbWFpbi5janNcIixcImFwcDpkZXNrdG9wOnBhY2tcIjpcImVsZWN0cm9uLWJ1aWxkZXIgLS1jb25maWcgZWxlY3Ryb24tYnVpbGRlci5qc29uXCIsXCJwcmVwdWJsaXNoT25seVwiOlwiWyAtZiAubmV4dC9CVUlMRF9JRCBdIHx8IChlY2hvICdwcmVwdWJsaXNoT25seTogLm5leHQvQlVJTERfSUQgbWlzc2luZyDigJQgcnVuIG5wbSBydW4gYnVpbGQgZmlyc3QgKHNlZSBjb21taXQgMDVjMWViZiBib2R5IGZvciBSMjI0IGNodW5rLTUwMCByb290IGNhdXNlKScgPiYyICYmIGV4aXQgMSlcIixcImF2YXRhcnM6Y2hlY2tcIjpcIm5vZGUgc2NyaXB0cy9jaGVjay1hdmF0YXJzLm1qc1wifSxcImJpblwiOntcImFnZW50LW5ldHdvcmstZGFzaGJvYXJkXCI6XCIuL2Jpbi9zdGFydC5qc1wifSxcImhvbWVwYWdlXCI6XCJodHRwczovL2FuZXQuc2hcIixcInJlcG9zaXRvcnlcIjp7XCJ0eXBlXCI6XCJnaXRcIixcInVybFwiOlwiaHR0cHM6Ly9naXRodWIuY29tL3NsZWVwMmFnaS9hZ2VudC1uZXR3b3JrLWRhc2hib2FyZFwifSxcImJ1Z3NcIjp7XCJ1cmxcIjpcImh0dHBzOi8vZ2l0aHViLmNvbS9zbGVlcDJhZ2kvYWdlbnQtbmV0d29yay1kYXNoYm9hcmQvaXNzdWVzXCJ9LFwiYXV0aG9yXCI6XCJzbGVlcDJhZ2lcIixcImxpY2Vuc2VcIjpcIkFwYWNoZS0yLjBcIixcInB1Ymxpc2hDb25maWdcIjp7XCJhY2Nlc3NcIjpcInB1YmxpY1wifSxcImRlcGVuZGVuY2llc1wiOntcIkBjYXBhY2l0b3IvY29yZVwiOlwiXjcuNC40XCIsXCJuZXh0XCI6XCIxNi4yLjNcIixcInBpbnlpbi1wcm9cIjpcIl4zLjI4LjFcIixcInJlYWN0XCI6XCIxOS4yLjRcIixcInJlYWN0LWRvbVwiOlwiMTkuMi40XCIsXCJyZWFjdC1tYXJrZG93blwiOlwiXjEwLjEuMFwiLFwicmVtYXJrLWdmbVwiOlwiXjQuMC4xXCIsXCJzd3JcIjpcIl4yLjQuMVwifSxcImRldkRlcGVuZGVuY2llc1wiOntcIkBjYXBhY2l0b3IvYW5kcm9pZFwiOlwiXjcuNC40XCIsXCJAY2FwYWNpdG9yL2NsaVwiOlwiXjcuNC40XCIsXCJAY2FwYWNpdG9yL2lvc1wiOlwiXjcuNC40XCIsXCJAcGxheXdyaWdodC90ZXN0XCI6XCJeMS41OS4xXCIsXCJAdGFpbHdpbmRjc3MvcG9zdGNzc1wiOlwiXjRcIixcIkB0eXBlcy9ub2RlXCI6XCJeMjBcIixcIkB0eXBlcy9yZWFjdFwiOlwiXjE5XCIsXCJAdHlwZXMvcmVhY3QtZG9tXCI6XCJeMTlcIixcImVsZWN0cm9uXCI6XCJeMzkuMi42XCIsXCJlbGVjdHJvbi1idWlsZGVyXCI6XCJeMjYuMC4xMlwiLFwiZXNsaW50XCI6XCJeOVwiLFwiZXNsaW50LWNvbmZpZy1uZXh0XCI6XCIxNi4yLjNcIixcInRhaWx3aW5kY3NzXCI6XCJeNFwiLFwidHlwZXNjcmlwdFwiOlwiXjVcIn19Il0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBIn0=","/** Dashboard package version sourced from package.json at build time.\n * Importing JSON requires `resolveJsonModule: true` in tsconfig, which\n * Next sets by default. Bumping is automated by the publish flow, so this\n * surfaces the real preview number in Settings → Dashboard. */\nimport pkg from '../../package.json' assert { type: 'json' };\n\nexport const DASHBOARD_VERSION: string = pkg.version;\n"],"names":[],"mappings":"uIACsE"}
|