@shepai/cli 1.103.1-pr297.1d67d7d → 1.103.1
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/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.js +8 -50
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts +1 -4
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.js +11 -31
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.js +2 -35
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +3 -3
- package/web/.next/required-server-files.json +3 -3
- package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +20 -20
- package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +25 -25
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +25 -25
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +18 -18
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +20 -20
- package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +25 -25
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +25 -25
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +18 -18
- package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +18 -18
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/settings/page/server-reference-manifest.json +5 -5
- package/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/chunks/[root-of-the-server]__f47bb8a5._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_0a7546ed.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_0a7546ed.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_978cbb4c.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_978cbb4c.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__24dd0927._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__51fc98b0._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__51fc98b0._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__57f37786._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__57f37786._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__5d87af2e._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__708dcad1._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__708dcad1._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__845a9d2a._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__845a9d2a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__89e8a5cc._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8e002910._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8e002910._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_135edb9c._.js +1 -1
- package/web/.next/server/chunks/ssr/_135edb9c._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_7129cff0._.js → _23e5ab2c._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_7129cff0._.js.map → _23e5ab2c._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/{_80fc856a._.js → _82e0cb85._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_80fc856a._.js.map → _82e0cb85._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_8dacd1e7._.js +1 -1
- package/web/.next/server/chunks/ssr/_8dacd1e7._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_2381749d._.js → _b3352e34._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_2381749d._.js.map → _b3352e34._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_cc77a143._.js +1 -1
- package/web/.next/server/chunks/ssr/_cc77a143._.js.map +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_7275ce5d._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_7275ce5d._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_758dfa10._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_758dfa10._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_84803221.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_84803221.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
- package/web/.next/server/pages/500.html +2 -2
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +30 -30
- package/web/.next/static/chunks/0c1c3c49b2145c9b.js +2 -0
- package/web/.next/static/chunks/{dbf1dbf895bb3b2b.js → 2016bacd1f5fcc12.js} +1 -1
- package/web/.next/static/chunks/{e1199a4481977a1d.js → 2e2bbd77e10e036c.js} +1 -1
- package/web/.next/static/chunks/{c38c8ef1091abf1b.js → 396a8b5222dfe0f1.js} +1 -1
- package/web/.next/static/chunks/{f2d47cc7f90ea785.js → 3e1f5c0f67be293f.js} +1 -1
- package/web/.next/static/chunks/{deb64031250a421f.js → 4fd27329675da3a8.js} +1 -1
- package/web/.next/static/chunks/{074f5910ba53b9a9.js → 8b1d0f725365fc83.js} +1 -1
- package/web/.next/static/chunks/8d98f1d9341e7698.js +1 -0
- package/web/.next/static/chunks/a0f87e10178f4c0e.js +1 -0
- package/web/.next/static/chunks/{3df97cf6d9bb267d.js → d7cc1b6cf702a840.js} +2 -2
- package/web/.next/static/chunks/03dca61cab65a6d6.js +0 -1
- package/web/.next/static/chunks/77a6011b9ecff04e.js +0 -2
- package/web/.next/static/chunks/d6d49d0239bf3f10.js +0 -1
- /package/web/.next/static/{8bFxxZyznRJ9YrunDv0xj → 7gAZbyat8KbbWMGw1K8nJ}/_buildManifest.js +0 -0
- /package/web/.next/static/{8bFxxZyznRJ9YrunDv0xj → 7gAZbyat8KbbWMGw1K8nJ}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{8bFxxZyznRJ9YrunDv0xj → 7gAZbyat8KbbWMGw1K8nJ}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-drawer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG/D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,UAAU,CAAC;IACjB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,wBAAwB,
|
|
1
|
+
{"version":3,"file":"feature-drawer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG/D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,UAAU,CAAC;IACjB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,wBAAwB,2CA4iB1F"}
|
package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js
CHANGED
|
@@ -329,7 +329,7 @@ export function FeatureDrawerClient({ view: initialView, urlTab }) {
|
|
|
329
329
|
// ── Body ──────────────────────────────────────────────────────────────
|
|
330
330
|
let body = null;
|
|
331
331
|
if (view.type === 'feature' && featureNode) {
|
|
332
|
-
body = (_jsx(FeatureDrawerTabs, { featureNode: featureNode, featureId: featureNode.featureId, initialTab: view.initialTab, urlTab: urlTab,
|
|
332
|
+
body = (_jsx(FeatureDrawerTabs, { featureNode: featureNode, featureId: featureNode.featureId, initialTab: view.initialTab, urlTab: urlTab, prdData: prdData, prdSelections: prdSelections, onPrdSelect: (qId, oId) => setPrdSelections((prev) => ({ ...prev, [qId]: oId })), onPrdApprove: handlePrdApprove, onPrdReject: handlePrdReject, isPrdLoading: isLoadingPrd, techData: techData, onTechApprove: handleTechApprove, onTechReject: handleTechReject, isTechLoading: isLoadingTech, productData: isLoadingTechProduct ? null : techProductData, mergeData: mergeData, onMergeApprove: handleMergeApprove, onMergeReject: handleMergeReject, isMergeLoading: isLoadingMerge, isRejecting: isRejecting, chatInput: chatInput, onChatInputChange: setChatInput }));
|
|
333
333
|
}
|
|
334
334
|
return (_jsx(BaseDrawer, { open: isOpen, onClose: attemptClose, size: "md", modal: false, header: header, "data-testid": view.type === 'feature' ? 'feature-drawer' : 'repository-drawer', children: body }));
|
|
335
335
|
}
|
package/dist/src/presentation/web/components/common/feature-drawer-tabs/activity-tab.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activity-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/activity-tab.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"activity-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/activity-tab.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,eAAe,EACf,qBAAqB,EACtB,MAAM,yCAAyC,CAAC;AAIjD,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,eAAe,EAAE,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,iBAAiB,CAAC,EAAE,qBAAqB,EAAE,CAAC;CAC7C;AA6BD;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,eAAe,EAAE,EAC1B,QAAQ,CAAC,EAAE,qBAAqB,EAAE,GACjC,iBAAiB,EAAE,CAgErB;AAMD,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,2CAyD3F"}
|
|
@@ -1,48 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useEffect, useRef } from 'react';
|
|
4
3
|
import { Loader2, AlertCircle, Clock } from 'lucide-react';
|
|
5
4
|
import { InlineAttachments } from '../../common/inline-attachments/index.js';
|
|
6
5
|
import { formatDuration } from '../../../lib/format-duration.js';
|
|
7
|
-
/**
|
|
8
|
-
* Computes the effective duration for a phase timing entry.
|
|
9
|
-
* For completed phases, uses the server-provided durationMs.
|
|
10
|
-
* For in-progress phases (has startedAt but no completedAt), computes
|
|
11
|
-
* elapsed time client-side so the UI shows a live-updating timer.
|
|
12
|
-
*/
|
|
13
|
-
function getEffectiveDuration(timing, now) {
|
|
14
|
-
if (timing.durationMs != null && timing.durationMs > 0)
|
|
15
|
-
return timing.durationMs;
|
|
16
|
-
if (!timing.completedAt && timing.startedAt) {
|
|
17
|
-
const started = new Date(timing.startedAt).getTime();
|
|
18
|
-
if (!Number.isNaN(started))
|
|
19
|
-
return Math.max(0, now - started);
|
|
20
|
-
}
|
|
21
|
-
return 0;
|
|
22
|
-
}
|
|
23
|
-
/** Returns true if any non-lifecycle phase is still in progress. */
|
|
24
|
-
function hasRunningPhase(timings) {
|
|
25
|
-
return timings.some((t) => !t.phase.startsWith('run:') && !t.completedAt && t.startedAt);
|
|
26
|
-
}
|
|
27
|
-
/** Hook that ticks every second while there are running phases. */
|
|
28
|
-
function useTickingNow(timings) {
|
|
29
|
-
const [now, setNow] = useState(Date.now);
|
|
30
|
-
const intervalRef = useRef(null);
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
const running = timings ? hasRunningPhase(timings) : false;
|
|
33
|
-
if (running) {
|
|
34
|
-
// Tick every second
|
|
35
|
-
intervalRef.current = setInterval(() => setNow(Date.now()), 1000);
|
|
36
|
-
}
|
|
37
|
-
return () => {
|
|
38
|
-
if (intervalRef.current) {
|
|
39
|
-
clearInterval(intervalRef.current);
|
|
40
|
-
intervalRef.current = null;
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
}, [timings]);
|
|
44
|
-
return now;
|
|
45
|
-
}
|
|
46
6
|
const NODE_TO_PHASE = {
|
|
47
7
|
analyze: 'Analyzing',
|
|
48
8
|
requirements: 'Requirements',
|
|
@@ -134,7 +94,6 @@ export function buildLifecycleTimeline(timings, feedback) {
|
|
|
134
94
|
* Component
|
|
135
95
|
* ------------------------------------------------------------------------- */
|
|
136
96
|
export function ActivityTab({ timings, loading, error, rejectionFeedback }) {
|
|
137
|
-
const now = useTickingNow(timings);
|
|
138
97
|
if (loading) {
|
|
139
98
|
return (_jsx("div", { "data-testid": "activity-tab-loading", className: "flex items-center justify-center p-8", children: _jsx(Loader2, { className: "text-muted-foreground h-6 w-6 animate-spin" }) }));
|
|
140
99
|
}
|
|
@@ -145,24 +104,24 @@ export function ActivityTab({ timings, loading, error, rejectionFeedback }) {
|
|
|
145
104
|
return (_jsxs("div", { className: "flex flex-col items-center justify-center gap-2 p-8", children: [_jsx(Clock, { className: "text-muted-foreground h-8 w-8" }), _jsx("p", { className: "text-muted-foreground text-sm", children: "No activity recorded yet" })] }));
|
|
146
105
|
}
|
|
147
106
|
const nodeTimings = timings.filter((t) => !isLifecycleEvent(t.phase));
|
|
148
|
-
const maxDurationMs = Math.max(...nodeTimings.map((t) =>
|
|
107
|
+
const maxDurationMs = Math.max(...nodeTimings.map((t) => t.durationMs ?? 0), ...nodeTimings.map((t) => t.approvalWaitMs ?? 0), 0);
|
|
149
108
|
const iterations = buildLifecycleTimeline(timings, rejectionFeedback);
|
|
150
109
|
const hasMultipleIterations = iterations.length > 1;
|
|
151
|
-
const totalExecMs = nodeTimings.reduce((sum, t) => sum +
|
|
110
|
+
const totalExecMs = nodeTimings.reduce((sum, t) => sum + (t.durationMs ?? 0), 0);
|
|
152
111
|
const totalWaitMs = nodeTimings.reduce((sum, t) => sum + (t.approvalWaitMs ?? 0), 0);
|
|
153
|
-
return (_jsxs("div", { className: "flex flex-col gap-4 p-4", children: [_jsx("div", { "data-testid": "activity-timings", className: "flex flex-col gap-3", children: iterations.map((iteration) => (_jsx(IterationGroup, { iteration: iteration, showHeader: hasMultipleIterations, maxDurationMs: maxDurationMs
|
|
112
|
+
return (_jsxs("div", { className: "flex flex-col gap-4 p-4", children: [_jsx("div", { "data-testid": "activity-timings", className: "flex flex-col gap-3", children: iterations.map((iteration) => (_jsx(IterationGroup, { iteration: iteration, showHeader: hasMultipleIterations, maxDurationMs: maxDurationMs }, iteration.number))) }), totalExecMs > 0 ? (_jsx(SummaryTotals, { totalExecMs: totalExecMs, totalWaitMs: totalWaitMs })) : null] }));
|
|
154
113
|
}
|
|
155
114
|
/* ---------------------------------------------------------------------------
|
|
156
115
|
* Iteration group — one cycle of work
|
|
157
116
|
* ------------------------------------------------------------------------- */
|
|
158
|
-
function IterationGroup({ iteration, showHeader, maxDurationMs,
|
|
117
|
+
function IterationGroup({ iteration, showHeader, maxDurationMs, }) {
|
|
159
118
|
return (_jsxs("div", { "data-testid": `iteration-${iteration.number}`, className: "flex flex-col gap-1.5", children: [showHeader ? (_jsxs("div", { className: "text-muted-foreground text-xs font-medium", children: ["Iteration ", iteration.number] })) : null, iteration.timings.map((t) => {
|
|
160
119
|
if (isLifecycleEvent(t.phase)) {
|
|
161
120
|
// For run:rejected, the message comes from the iteration, not a separate map
|
|
162
121
|
const isRejection = t.phase === 'run:rejected';
|
|
163
122
|
return (_jsx(LifecycleEventRow, { timing: t, message: isRejection ? iteration.rejectionMessage : undefined, attachments: isRejection ? iteration.rejectionAttachments : undefined }, `${t.agentRunId}-${t.phase}-${t.startedAt}`));
|
|
164
123
|
}
|
|
165
|
-
return (_jsx(NodeTimingRow, { timing: t, maxDurationMs: maxDurationMs
|
|
124
|
+
return (_jsx(NodeTimingRow, { timing: t, maxDurationMs: maxDurationMs }, `${t.agentRunId}-${t.phase}-${t.startedAt}`));
|
|
166
125
|
})] }));
|
|
167
126
|
}
|
|
168
127
|
/* ---------------------------------------------------------------------------
|
|
@@ -175,15 +134,14 @@ function LifecycleEventRow({ timing, message, attachments, }) {
|
|
|
175
134
|
const hasAttachments = attachments && attachments.length > 0;
|
|
176
135
|
return (_jsxs("div", { className: `text-xs ${event.colorClass}`, children: [_jsx("span", { children: event.label }), message ? (_jsxs("span", { "data-testid": "rejection-feedback-text", className: "text-muted-foreground ml-2 font-normal italic", children: ["\u2014 ", message] })) : null, hasAttachments ? (_jsx("div", { "data-testid": "rejection-feedback-attachments", className: "mt-1.5 ml-2", children: _jsx(InlineAttachments, { text: "", attachmentPaths: attachments }) })) : null] }));
|
|
177
136
|
}
|
|
178
|
-
function NodeTimingRow({ timing, maxDurationMs,
|
|
137
|
+
function NodeTimingRow({ timing, maxDurationMs, }) {
|
|
179
138
|
const label = timing.phase.includes(':')
|
|
180
139
|
? `rev ${timing.phase.split(':')[1]}`
|
|
181
140
|
: (NODE_TO_PHASE[timing.phase] ?? timing.phase);
|
|
182
|
-
const durationMs =
|
|
141
|
+
const durationMs = timing.durationMs ?? 0;
|
|
183
142
|
const barPercent = maxDurationMs > 0 ? Math.max(2, (durationMs / maxDurationMs) * 100) : 2;
|
|
184
|
-
const isRunning = !timing.completedAt && !!timing.startedAt;
|
|
185
143
|
const barColorClass = timing.completedAt ? 'bg-emerald-500' : 'bg-blue-500';
|
|
186
|
-
return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { "data-testid": `timing-bar-${timing.phase}`, className: "flex items-center gap-3", children: [_jsx("span", { className: "text-muted-foreground w-24 shrink-0 text-xs", children: label }), _jsx("div", { className: "bg-muted h-3 flex-1 overflow-hidden rounded-full", children: _jsx("div", { className: `h-full rounded-full ${barColorClass}
|
|
144
|
+
return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { "data-testid": `timing-bar-${timing.phase}`, className: "flex items-center gap-3", children: [_jsx("span", { className: "text-muted-foreground w-24 shrink-0 text-xs", children: label }), _jsx("div", { className: "bg-muted h-3 flex-1 overflow-hidden rounded-full", children: _jsx("div", { className: `h-full rounded-full ${barColorClass}`, style: { width: `${Math.min(barPercent, 100)}%` } }) }), _jsx("span", { className: "text-muted-foreground w-14 shrink-0 text-right text-xs", children: formatDuration(durationMs) })] }), timing.approvalWaitMs != null && timing.approvalWaitMs > 0 ? (_jsx(ApprovalWaitRow, { timing: timing, maxDurationMs: maxDurationMs })) : null] }));
|
|
187
145
|
}
|
|
188
146
|
function ApprovalWaitRow({ timing, maxDurationMs, }) {
|
|
189
147
|
const waitMs = timing.approvalWaitMs ?? 0;
|
package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { NotificationEvent } from '../../../../../../packages/core/src/domain/generated/output.js';
|
|
2
1
|
import type { FeatureNodeData } from '../../common/feature-node/index.js';
|
|
3
2
|
import type { PrdQuestionnaireData } from '../../common/prd-questionnaire/index.js';
|
|
4
3
|
import type { TechDecisionsReviewData } from '../../common/tech-decisions-review/index.js';
|
|
@@ -12,8 +11,6 @@ export interface FeatureDrawerTabsProps {
|
|
|
12
11
|
initialTab?: FeatureTabKey;
|
|
13
12
|
/** Tab key from URL path segment (e.g. /feature/[id]/activity → 'activity'). */
|
|
14
13
|
urlTab?: FeatureTabKey;
|
|
15
|
-
/** SSE events from the agent events provider, used to trigger tab data refresh. */
|
|
16
|
-
sseEvents?: readonly NotificationEvent[];
|
|
17
14
|
prdData?: PrdQuestionnaireData | null;
|
|
18
15
|
prdSelections?: Record<string, string>;
|
|
19
16
|
onPrdSelect?: (questionId: string, optionId: string) => void;
|
|
@@ -33,5 +30,5 @@ export interface FeatureDrawerTabsProps {
|
|
|
33
30
|
chatInput?: string;
|
|
34
31
|
onChatInputChange?: (value: string) => void;
|
|
35
32
|
}
|
|
36
|
-
export declare function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab, prdData, prdSelections, onPrdSelect, onPrdApprove, onPrdReject, isPrdLoading, techData, onTechApprove, onTechReject, isTechLoading, productData, mergeData, onMergeApprove, onMergeReject, isMergeLoading, isRejecting, chatInput, onChatInputChange,
|
|
33
|
+
export declare function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab, prdData, prdSelections, onPrdSelect, onPrdApprove, onPrdReject, isPrdLoading, techData, onTechApprove, onTechReject, isTechLoading, productData, mergeData, onMergeApprove, onMergeReject, isMergeLoading, isRejecting, chatInput, onChatInputChange, }: FeatureDrawerTabsProps): import("react/jsx-runtime").JSX.Element;
|
|
37
34
|
//# sourceMappingURL=feature-drawer-tabs.d.ts.map
|
package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-drawer-tabs.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"feature-drawer-tabs.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAC;AACzF,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,+CAA+C,CAAC;AACjG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAMxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAQ9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uDAAuD,CAAC;AAiD3F,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,eAAe,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,gFAAgF;IAChF,MAAM,CAAC,EAAE,aAAa,CAAC;IAGvB,OAAO,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7D,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC1E,YAAY,CAAC,EAAE,OAAO,CAAC;IAGvB,QAAQ,CAAC,EAAE,uBAAuB,GAAG,IAAI,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAC;IAGxB,WAAW,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAGjD,SAAS,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IAC5E,cAAc,CAAC,EAAE,OAAO,CAAC;IAGzB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAwBD,wBAAgB,iBAAiB,CAAC,EAChC,WAAW,EACX,SAAS,EACT,UAAU,EACV,MAAM,EACN,OAAO,EACP,aAAa,EACb,WAAW,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,YAAY,EACZ,aAAa,EACb,WAAW,EACX,SAAS,EACT,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,EACX,SAAS,EACT,iBAAiB,GAClB,EAAE,sBAAsB,2CAmSxB"}
|
package/dist/src/presentation/web/components/common/feature-drawer-tabs/feature-drawer-tabs.js
CHANGED
|
@@ -64,7 +64,7 @@ const TAB_FETCHERS = {
|
|
|
64
64
|
activity: fetchActivity,
|
|
65
65
|
plan: fetchPlan,
|
|
66
66
|
};
|
|
67
|
-
export function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab, prdData, prdSelections, onPrdSelect, onPrdApprove, onPrdReject, isPrdLoading, techData, onTechApprove, onTechReject, isTechLoading, productData, mergeData, onMergeApprove, onMergeReject, isMergeLoading, isRejecting, chatInput, onChatInputChange,
|
|
67
|
+
export function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab, prdData, prdSelections, onPrdSelect, onPrdApprove, onPrdReject, isPrdLoading, techData, onTechApprove, onTechReject, isTechLoading, productData, mergeData, onMergeApprove, onMergeReject, isMergeLoading, isRejecting, chatInput, onChatInputChange, }) {
|
|
68
68
|
const pathname = usePathname();
|
|
69
69
|
const featureLogs = useFeatureLogs(featureId);
|
|
70
70
|
const visibleTabs = useMemo(() => computeVisibleTabs(featureNode), [featureNode]);
|
|
@@ -159,7 +159,6 @@ export function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab,
|
|
|
159
159
|
}, [featureId]);
|
|
160
160
|
// When initialTab changes (e.g. SSE updates lifecycle), switch to it if visible
|
|
161
161
|
// and sync the URL with replaceState so the address bar reflects the new tab.
|
|
162
|
-
// Also trigger lazy fetch for the new tab so data is ready immediately.
|
|
163
162
|
const prevInitialTabRef = useRef(initialTab);
|
|
164
163
|
useEffect(() => {
|
|
165
164
|
if (prevInitialTabRef.current !== initialTab &&
|
|
@@ -167,17 +166,13 @@ export function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab,
|
|
|
167
166
|
visibleTabs.includes(initialTab)) {
|
|
168
167
|
prevInitialTabRef.current = initialTab;
|
|
169
168
|
setActiveTab(initialTab);
|
|
170
|
-
// Trigger lazy fetch for the new tab if it's a lazy-loaded tab
|
|
171
|
-
if (initialTab === 'activity' || initialTab === 'plan') {
|
|
172
|
-
fetchTab(initialTab);
|
|
173
|
-
}
|
|
174
169
|
// Sync URL to match the new tab
|
|
175
170
|
const targetUrl = initialTab === 'overview' ? basePath : `${basePath}/${initialTab}`;
|
|
176
171
|
if (targetUrl !== window.location.pathname) {
|
|
177
172
|
window.history.replaceState(null, '', targetUrl);
|
|
178
173
|
}
|
|
179
174
|
}
|
|
180
|
-
}, [initialTab, visibleTabs, basePath
|
|
175
|
+
}, [initialTab, visibleTabs, basePath]);
|
|
181
176
|
// If the active tab becomes invisible (lifecycle changed), fall back to overview
|
|
182
177
|
// and sync the URL accordingly.
|
|
183
178
|
useEffect(() => {
|
|
@@ -188,34 +183,19 @@ export function FeatureDrawerTabs({ featureNode, featureId, initialTab, urlTab,
|
|
|
188
183
|
}
|
|
189
184
|
}
|
|
190
185
|
}, [visibleTabs, activeTab, basePath]);
|
|
191
|
-
// SSE refresh: re-fetch lazy tab
|
|
192
|
-
|
|
193
|
-
// Always refresh 'activity' data (even if not the active tab) so switching
|
|
194
|
-
// to the activity tab shows up-to-date timings without a loading flash.
|
|
186
|
+
// SSE refresh: re-fetch active lazy tab when featureNode reference changes
|
|
187
|
+
const featureNodeRef = useRef(featureNode);
|
|
195
188
|
const activeTabRef = useRef(activeTab);
|
|
196
189
|
activeTabRef.current = activeTab;
|
|
197
|
-
const sseProcessedRef = useRef(0);
|
|
198
190
|
useEffect(() => {
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
if (sseEvents.length <= sseProcessedRef.current)
|
|
206
|
-
return;
|
|
207
|
-
const newEvents = sseEvents.slice(sseProcessedRef.current);
|
|
208
|
-
sseProcessedRef.current = sseEvents.length;
|
|
209
|
-
const hasRelevantEvent = newEvents.some((e) => e.featureId === featureId);
|
|
210
|
-
if (!hasRelevantEvent)
|
|
211
|
-
return;
|
|
212
|
-
// Always refresh activity data so it stays current for when the user switches tabs
|
|
213
|
-
refreshTab('activity');
|
|
214
|
-
const current = activeTabRef.current;
|
|
215
|
-
if (current === 'plan') {
|
|
216
|
-
refreshTab(current);
|
|
191
|
+
if (featureNodeRef.current !== featureNode) {
|
|
192
|
+
featureNodeRef.current = featureNode;
|
|
193
|
+
const current = activeTabRef.current;
|
|
194
|
+
if (current === 'activity' || current === 'plan') {
|
|
195
|
+
refreshTab(current);
|
|
196
|
+
}
|
|
217
197
|
}
|
|
218
|
-
}, [
|
|
198
|
+
}, [featureNode, refreshTab]);
|
|
219
199
|
const handleTabChange = useCallback((value) => {
|
|
220
200
|
const tab = value;
|
|
221
201
|
isUserInteraction.current = true;
|
package/dist/src/presentation/web/components/common/feature-drawer-tabs/overview-tab.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overview-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/overview-tab.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"overview-tab.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-drawer-tabs/overview-tab.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAMxE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CAwCrD"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useEffect, useRef } from 'react';
|
|
4
3
|
import { AlertTriangle, ExternalLink, GitBranch, GitCommitHorizontal, Zap } from 'lucide-react';
|
|
5
4
|
import { InlineAttachments } from '../../common/inline-attachments/index.js';
|
|
6
5
|
import { PrStatus } from '../../../../../../packages/core/src/domain/generated/output.js';
|
|
@@ -11,7 +10,6 @@ import { CiStatusBadge } from '../../common/ci-status-badge/index.js';
|
|
|
11
10
|
import { CometSpinner } from '../../ui/comet-spinner.js';
|
|
12
11
|
import { featureNodeStateConfig, lifecycleDisplayLabels } from '../../common/feature-node/index.js';
|
|
13
12
|
import { getAgentTypeIcon, agentTypeLabels, } from '../../common/feature-node/agent-type-icons.js';
|
|
14
|
-
import { formatDuration } from '../../../lib/format-duration.js';
|
|
15
13
|
export function OverviewTab({ data }) {
|
|
16
14
|
const isCompleted = data.lifecycle === 'maintain';
|
|
17
15
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { "data-testid": "feature-drawer-status", className: "flex flex-col gap-3 p-4", children: [_jsx("div", { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: lifecycleDisplayLabels[data.lifecycle] }), _jsx(FeatureStateBadge, { data: data }), !isCompleted && data.progress > 0 ? (_jsxs("div", { "data-testid": "feature-drawer-progress", className: "flex flex-col gap-1", children: [_jsxs("div", { className: "text-muted-foreground flex items-center justify-between text-xs", children: [_jsx("span", { children: "Progress" }), _jsxs("span", { children: [data.progress, "%"] })] }), _jsx("div", { className: "bg-muted h-2 w-full overflow-hidden rounded-full", children: _jsx("div", { className: cn('h-full rounded-full transition-all', featureNodeStateConfig[data.state].progressClass), style: { width: `${data.progress}%` } }) })] })) : null, isCompleted && data.pr ? _jsx(FeaturePrInfo, { pr: data.pr }) : null] }), _jsx(FeatureInfo, { data: data }), !isCompleted && data.pr ? (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx("div", { className: "p-4", children: _jsx(FeaturePrInfo, { pr: data.pr }) })] })) : null, _jsx(FeatureDetails, { data: data })] }));
|
|
@@ -65,42 +63,11 @@ function FeaturePrInfo({ pr }) {
|
|
|
65
63
|
return (_jsx("div", { "data-testid": "feature-drawer-pr", children: _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("a", { href: pr.url, target: "_blank", rel: "noopener noreferrer", className: "text-primary flex items-center gap-1.5 text-sm font-semibold underline underline-offset-2", children: ["PR #", pr.number, _jsx(ExternalLink, { className: "h-3.5 w-3.5" })] }), _jsx(Badge, { className: prStatusStyles[pr.status], children: pr.status })] }), pr.ciStatus ? (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "CI Status" }), _jsx(CiStatusBadge, { status: pr.ciStatus })] })) : null, pr.mergeable === false ? (_jsxs("div", { "data-testid": "pr-merge-conflict", className: "flex items-center justify-between", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Merge Status" }), _jsxs(Badge, { className: "border-transparent bg-orange-50 text-orange-700 hover:bg-orange-50", children: [_jsx(AlertTriangle, { className: "mr-1 h-3.5 w-3.5" }), "Conflicts"] })] })) : null, pr.commitHash ? (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Commit" }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(GitCommitHorizontal, { className: "text-muted-foreground h-3.5 w-3.5" }), _jsx("code", { className: "bg-muted text-foreground rounded-md px-1.5 py-0.5 font-mono text-[11px]", children: pr.commitHash.slice(0, 7) })] })] })) : null] }) }));
|
|
66
64
|
}
|
|
67
65
|
// ── Details section ──────────────────────────────────────────────────
|
|
68
|
-
/** Hook that returns a live-updating elapsed time string for running features. */
|
|
69
|
-
function useElapsedTime(startedAt) {
|
|
70
|
-
const [elapsed, setElapsed] = useState(null);
|
|
71
|
-
const intervalRef = useRef(null);
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
if (!startedAt) {
|
|
74
|
-
setElapsed(null);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
// Compute immediately
|
|
78
|
-
setElapsed(formatDuration(Math.max(0, Date.now() - startedAt)));
|
|
79
|
-
// Tick every second
|
|
80
|
-
intervalRef.current = setInterval(() => {
|
|
81
|
-
setElapsed(formatDuration(Math.max(0, Date.now() - startedAt)));
|
|
82
|
-
}, 1000);
|
|
83
|
-
return () => {
|
|
84
|
-
if (intervalRef.current) {
|
|
85
|
-
clearInterval(intervalRef.current);
|
|
86
|
-
intervalRef.current = null;
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
}, [startedAt]);
|
|
90
|
-
return elapsed;
|
|
91
|
-
}
|
|
92
66
|
function FeatureDetails({ data }) {
|
|
93
|
-
const
|
|
94
|
-
const elapsedTime = useElapsedTime(isRunning ? data.startedAt : undefined);
|
|
95
|
-
const hasAnyDetail = data.fastMode ??
|
|
96
|
-
data.agentType ??
|
|
97
|
-
data.runtime ??
|
|
98
|
-
elapsedTime ??
|
|
99
|
-
data.blockedBy ??
|
|
100
|
-
data.errorMessage;
|
|
67
|
+
const hasAnyDetail = data.fastMode ?? data.agentType ?? data.runtime ?? data.blockedBy ?? data.errorMessage;
|
|
101
68
|
if (!hasAnyDetail)
|
|
102
69
|
return null;
|
|
103
|
-
return (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { "data-testid": "feature-drawer-details", className: "flex flex-col gap-3 p-4", children: [data.fastMode ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Mode" }), _jsxs("span", { className: "flex items-center gap-2 text-sm", children: [_jsx(Zap, { className: "h-4 w-4 shrink-0 text-amber-500" }), "Fast Mode"] })] })) : null, data.agentType ? _jsx(AgentDetailRow, { agentType: data.agentType }) : null, data.runtime ? _jsx(DetailRow, { label: "Runtime", value: data.runtime }) : null,
|
|
70
|
+
return (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsxs("div", { "data-testid": "feature-drawer-details", className: "flex flex-col gap-3 p-4", children: [data.fastMode ? (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: "Mode" }), _jsxs("span", { className: "flex items-center gap-2 text-sm", children: [_jsx(Zap, { className: "h-4 w-4 shrink-0 text-amber-500" }), "Fast Mode"] })] })) : null, data.agentType ? _jsx(AgentDetailRow, { agentType: data.agentType }) : null, data.runtime ? _jsx(DetailRow, { label: "Runtime", value: data.runtime }) : null, data.blockedBy ? _jsx(DetailRow, { label: "Blocked by", value: data.blockedBy }) : null, data.errorMessage ? _jsx(DetailRow, { label: "Error", value: data.errorMessage }) : null] })] }));
|
|
104
71
|
}
|
|
105
72
|
function AgentDetailRow({ agentType }) {
|
|
106
73
|
const Icon = getAgentTypeIcon(agentType);
|