@shepai/cli 1.26.0 → 1.27.0
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/packages/core/src/application/ports/output/index.d.ts +1 -1
- package/dist/packages/core/src/application/ports/output/index.d.ts.map +1 -1
- package/dist/packages/core/src/application/ports/output/services/index.d.ts +1 -0
- package/dist/packages/core/src/application/ports/output/services/index.d.ts.map +1 -1
- package/dist/packages/core/src/application/ports/output/services/notification-service.interface.d.ts +29 -0
- package/dist/packages/core/src/application/ports/output/services/notification-service.interface.d.ts.map +1 -0
- package/dist/packages/core/src/application/ports/output/services/notification-service.interface.js +12 -0
- package/dist/packages/core/src/application/use-cases/features/create/metadata-generator.d.ts +3 -2
- package/dist/packages/core/src/application/use-cases/features/create/metadata-generator.d.ts.map +1 -1
- package/dist/packages/core/src/application/use-cases/features/create/metadata-generator.js +36 -15
- package/dist/packages/core/src/domain/factories/settings-defaults.factory.d.ts.map +1 -1
- package/dist/packages/core/src/domain/factories/settings-defaults.factory.js +13 -0
- package/dist/packages/core/src/domain/generated/output.d.ts +105 -0
- package/dist/packages/core/src/domain/generated/output.d.ts.map +1 -1
- package/dist/packages/core/src/domain/generated/output.js +15 -0
- package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/di/container.js +17 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/mappers/settings.mapper.d.ts +8 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/mappers/settings.mapper.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/persistence/sqlite/mappers/settings.mapper.js +22 -0
- package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/persistence/sqlite/migrations.js +14 -0
- package/dist/packages/core/src/infrastructure/repositories/sqlite-settings.repository.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/repositories/sqlite-settings.repository.js +17 -3
- package/dist/packages/core/src/infrastructure/services/notifications/desktop-notifier.d.ts +22 -0
- package/dist/packages/core/src/infrastructure/services/notifications/desktop-notifier.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/notifications/desktop-notifier.js +39 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.d.ts +52 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.js +64 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.d.ts +60 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.js +225 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification.service.d.ts +21 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification.service.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/notifications/notification.service.js +43 -0
- package/dist/src/presentation/cli/commands/ui.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/ui.command.js +8 -0
- package/dist/src/presentation/web/app/api/agent-events/route.d.ts +14 -0
- package/dist/src/presentation/web/app/api/agent-events/route.d.ts.map +1 -0
- package/dist/src/presentation/web/app/api/agent-events/route.js +77 -0
- package/dist/src/presentation/web/app/page.d.ts.map +1 -1
- package/dist/src/presentation/web/app/page.js +9 -12
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.js +2 -2
- package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.js +3 -3
- package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts +5 -2
- package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.js +9 -0
- package/dist/src/presentation/web/components/common/feature-node/feature-node.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-node/feature-node.js +9 -7
- package/dist/src/presentation/web/components/layouts/app-shell/app-shell.d.ts.map +1 -1
- package/dist/src/presentation/web/components/layouts/app-shell/app-shell.js +3 -0
- package/dist/src/presentation/web/components/ui/button.d.ts +1 -1
- package/dist/src/presentation/web/hooks/use-agent-events.d.ts +12 -0
- package/dist/src/presentation/web/hooks/use-agent-events.d.ts.map +1 -0
- package/dist/src/presentation/web/hooks/use-agent-events.js +58 -0
- package/dist/src/presentation/web/hooks/use-notifications.d.ts +10 -0
- package/dist/src/presentation/web/hooks/use-notifications.d.ts.map +1 -0
- package/dist/src/presentation/web/hooks/use-notifications.js +57 -0
- package/dist/src/presentation/web/hooks/use-notifications.stories.d.ts +6 -0
- package/dist/src/presentation/web/hooks/use-notifications.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/hooks/use-notifications.stories.js +62 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/app-path-routes-manifest.json +1 -0
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/cache/.previewinfo +1 -1
- package/web/.next/cache/.rscinfo +1 -1
- package/web/.next/cache/.tsbuildinfo +1 -1
- package/web/.next/cache/config.json +3 -3
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +1 -1
- package/web/.next/required-server-files.json +1 -1
- package/web/.next/routes-manifest.json +6 -0
- 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_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_not-found.html +2 -2
- package/web/.next/server/app/_not-found.rsc +10 -10
- package/web/.next/server/app/_not-found.segments/_full.segment.rsc +10 -10
- package/web/.next/server/app/_not-found.segments/_head.segment.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_index.segment.rsc +6 -6
- package/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/api/agent-events/route/app-paths-manifest.json +3 -0
- package/web/.next/server/app/api/agent-events/route/build-manifest.json +11 -0
- package/web/.next/server/app/api/agent-events/route/server-reference-manifest.json +4 -0
- package/web/.next/server/app/api/agent-events/route.js +6 -0
- package/web/.next/server/app/api/agent-events/route.js.map +5 -0
- package/web/.next/server/app/api/agent-events/route.js.nft.json +1 -0
- package/web/.next/server/app/api/agent-events/route_client-reference-manifest.js +2 -0
- package/web/.next/server/app/api/dialog/pick-files/route.js +3 -2
- package/web/.next/server/app/api/dialog/pick-files/route.js.nft.json +1 -1
- package/web/.next/server/app/api/dialog/pick-folder/route.js +3 -2
- package/web/.next/server/app/api/dialog/pick-folder/route.js.nft.json +1 -1
- package/web/.next/server/app/api/features/create/route.js +3 -2
- package/web/.next/server/app/api/features/create/route.js.nft.json +1 -1
- package/web/.next/server/app/api/ide/open/route.js +4 -2
- package/web/.next/server/app/api/ide/open/route.js.nft.json +1 -1
- package/web/.next/server/app/api/shell/open/route.js +3 -2
- package/web/.next/server/app/api/shell/open/route.js.nft.json +1 -1
- package/web/.next/server/app/page.js +1 -1
- package/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version.html +2 -2
- package/web/.next/server/app/version.rsc +5 -5
- package/web/.next/server/app/version.segments/_full.segment.rsc +5 -5
- package/web/.next/server/app/version.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/version.segments/_index.segment.rsc +4 -4
- package/web/.next/server/app/version.segments/_tree.segment.rsc +2 -2
- package/web/.next/server/app/version.segments/version/__PAGE__.segment.rsc +2 -2
- package/web/.next/server/app/version.segments/version.segment.rsc +1 -1
- package/web/.next/server/app-paths-manifest.json +1 -0
- package/web/.next/server/chunks/403f9_next_567de315._.js +17 -0
- package/web/.next/server/chunks/403f9_next_567de315._.js.map +1 -0
- package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_12059e3e.js +3 -0
- package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_12059e3e.js.map +1 -0
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_agent-events_route_actions_686edb08.js +3 -0
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_agent-events_route_actions_686edb08.js.map +1 -0
- package/web/.next/server/chunks/{[root-of-the-server]__eaa478b7._.js → [root-of-the-server]__4e987cf3._.js} +2 -2
- package/web/.next/server/chunks/[root-of-the-server]__57104a4c._.js +7 -0
- package/web/.next/server/chunks/[root-of-the-server]__57104a4c._.js.map +1 -0
- package/web/.next/server/chunks/{[root-of-the-server]__602cce1b._.js → [root-of-the-server]__61349adc._.js} +2 -2
- package/web/.next/server/chunks/{[root-of-the-server]__bb4fc5b5._.js → [root-of-the-server]__a6bf88cc._.js} +2 -2
- package/web/.next/server/chunks/[root-of-the-server]__aa2074ca._.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__aa2074ca._.js.map +1 -0
- package/web/.next/server/chunks/{[root-of-the-server]__1913013c._.js → [root-of-the-server]__ec87d735._.js} +2 -2
- package/web/.next/server/chunks/[root-of-the-server]__ecaf05bc._.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__ecaf05bc._.js.map +1 -0
- package/web/.next/server/chunks/ssr/403f9_next_dist_esm_build_templates_app-page_dbf21e04.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__80e5bed2._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__f12b60be._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fa87595c._.js +1 -1
- package/web/.next/server/chunks/ssr/_09afa42a._.js +1 -1
- package/web/.next/server/chunks/ssr/_09afa42a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_884ddade._.js +1 -1
- package/web/.next/server/chunks/ssr/_c5d377cc._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js.map +1 -1
- package/web/.next/server/pages/404.html +2 -2
- 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 +1 -1
- package/web/.next/static/chunks/b32b17f102226f32.js +1 -0
- package/web/.next/static/chunks/d8905884f147599c.css +2 -0
- package/web/.next/static/chunks/{c2854b6c6d0ca1f3.js → fd961ef835ff4867.js} +2 -2
- package/web/.next/trace +1 -1
- package/web/.next/trace-build +1 -1
- package/web/.next/types/link.d.ts +1 -0
- package/web/.next/types/routes.d.ts +2 -1
- package/web/.next/types/validator.ts +9 -0
- package/web/.next/server/chunks/[root-of-the-server]__56ac1509._.js +0 -3
- package/web/.next/server/chunks/[root-of-the-server]__56ac1509._.js.map +0 -1
- package/web/.next/server/chunks/[root-of-the-server]__ba6c5641._.js +0 -21
- package/web/.next/server/chunks/[root-of-the-server]__ba6c5641._.js.map +0 -1
- package/web/.next/static/chunks/78ef4acb26e4d6ea.js +0 -1
- package/web/.next/static/chunks/86ff2b6444e06e88.css +0 -2
- /package/web/.next/server/chunks/{[root-of-the-server]__eaa478b7._.js.map → [root-of-the-server]__4e987cf3._.js.map} +0 -0
- /package/web/.next/server/chunks/{[root-of-the-server]__602cce1b._.js.map → [root-of-the-server]__61349adc._.js.map} +0 -0
- /package/web/.next/server/chunks/{[root-of-the-server]__bb4fc5b5._.js.map → [root-of-the-server]__a6bf88cc._.js.map} +0 -0
- /package/web/.next/server/chunks/{[root-of-the-server]__1913013c._.js.map → [root-of-the-server]__ec87d735._.js.map} +0 -0
- /package/web/.next/static/{_6j6GS-S6zPiASj5tsxJ8 → AmQVHj-yhxaqpAg8gC7MH}/_buildManifest.js +0 -0
- /package/web/.next/static/{_6j6GS-S6zPiASj5tsxJ8 → AmQVHj-yhxaqpAg8gC7MH}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{_6j6GS-S6zPiASj5tsxJ8 → AmQVHj-yhxaqpAg8gC7MH}/_ssgManifest.js +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { ControlCenter } from '../components/features/control-center/index.js';
|
|
3
3
|
import { getAgentRun, getFeatures } from '../../../../packages/core/src/infrastructure/di/use-cases-bridge.js';
|
|
4
|
-
import {
|
|
4
|
+
import { deriveNodeState, deriveProgress, } from '../components/common/feature-node/derive-feature-state.js';
|
|
5
5
|
import { layoutWithDagre } from '../lib/layout-with-dagre.js';
|
|
6
6
|
/** Force request-time rendering so the globalThis DI bridge is available. */
|
|
7
7
|
export const dynamic = 'force-dynamic';
|
|
@@ -26,12 +26,7 @@ export default async function HomePage() {
|
|
|
26
26
|
const features = await getFeatures();
|
|
27
27
|
const featuresWithRuns = await Promise.all(features.map(async (feature) => {
|
|
28
28
|
const run = feature.agentRunId ? await getAgentRun(feature.agentRunId) : null;
|
|
29
|
-
return {
|
|
30
|
-
feature,
|
|
31
|
-
agentStatus: run?.status,
|
|
32
|
-
agentError: run?.error,
|
|
33
|
-
agentResult: run?.result,
|
|
34
|
-
};
|
|
29
|
+
return { feature, run };
|
|
35
30
|
}));
|
|
36
31
|
// Group features by repository path
|
|
37
32
|
const featuresByRepo = {};
|
|
@@ -53,9 +48,9 @@ export default async function HomePage() {
|
|
|
53
48
|
position: { x: 0, y: 0 },
|
|
54
49
|
data: { name: repoName },
|
|
55
50
|
});
|
|
56
|
-
repoFeatures.forEach(({ feature,
|
|
57
|
-
const agentNode =
|
|
58
|
-
const lifecycle =
|
|
51
|
+
repoFeatures.forEach(({ feature, run }) => {
|
|
52
|
+
const agentNode = run?.result?.startsWith('node:') ? run.result.slice(5) : undefined;
|
|
53
|
+
const lifecycle = run?.status === 'completed'
|
|
59
54
|
? 'maintain'
|
|
60
55
|
: ((agentNode ? nodeToLifecyclePhase[agentNode] : undefined) ??
|
|
61
56
|
lifecycleMap[feature.lifecycle] ??
|
|
@@ -67,8 +62,10 @@ export default async function HomePage() {
|
|
|
67
62
|
lifecycle,
|
|
68
63
|
repositoryPath: feature.repositoryPath,
|
|
69
64
|
branch: feature.branch,
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
state: deriveNodeState(feature, run),
|
|
66
|
+
progress: deriveProgress(feature),
|
|
67
|
+
...(run?.agentType && { agentType: run.agentType }),
|
|
68
|
+
...(run?.error && { errorMessage: run.error }),
|
|
72
69
|
};
|
|
73
70
|
const featureNodeId = `feat-${feature.id}`;
|
|
74
71
|
nodes.push({
|
|
@@ -20,10 +20,10 @@ function StateBadge({ data }) {
|
|
|
20
20
|
return (_jsxs("div", { className: cn('flex items-center gap-2 rounded-full px-3 py-1.5 text-sm font-medium', config.badgeBgClass, config.badgeClass), children: [data.state === 'running' ? (_jsx(CometSpinner, { size: "sm", className: "shrink-0" })) : (_jsx(Icon, { className: "h-4 w-4 shrink-0" })), _jsx("span", { children: config.label })] }));
|
|
21
21
|
}
|
|
22
22
|
function DetailsSection({ data }) {
|
|
23
|
-
const hasAnyDetail = data.description ?? data.
|
|
23
|
+
const hasAnyDetail = data.description ?? data.agentType ?? data.runtime ?? data.blockedBy ?? data.errorMessage;
|
|
24
24
|
if (!hasAnyDetail)
|
|
25
25
|
return null;
|
|
26
|
-
return (_jsxs("div", { "data-testid": "feature-drawer-details", className: "flex flex-col gap-3 p-4", children: [data.description ? _jsx(DetailRow, { label: "Description", value: data.description }) : null, data.
|
|
26
|
+
return (_jsxs("div", { "data-testid": "feature-drawer-details", className: "flex flex-col gap-3 p-4", children: [data.description ? _jsx(DetailRow, { label: "Description", value: data.description }) : null, data.agentType ? _jsx(DetailRow, { label: "Agent", value: 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] }));
|
|
27
27
|
}
|
|
28
28
|
function DetailRow({ label, value }) {
|
|
29
29
|
return (_jsxs("div", { className: "flex flex-col gap-0.5", children: [_jsx("span", { className: "text-muted-foreground text-xs font-medium", children: label }), _jsx("span", { className: "text-sm", children: value })] }));
|
package/dist/src/presentation/web/components/common/feature-drawer/feature-drawer.stories.js
CHANGED
|
@@ -30,7 +30,7 @@ const runningData = {
|
|
|
30
30
|
lifecycle: 'implementation',
|
|
31
31
|
state: 'running',
|
|
32
32
|
progress: 45,
|
|
33
|
-
|
|
33
|
+
agentType: 'claude-code',
|
|
34
34
|
repositoryPath: '/home/user/my-repo',
|
|
35
35
|
branch: 'feat/auth-module',
|
|
36
36
|
};
|
|
@@ -118,7 +118,7 @@ export const AllFields = {
|
|
|
118
118
|
lifecycle: 'implementation',
|
|
119
119
|
state: 'running',
|
|
120
120
|
progress: 68,
|
|
121
|
-
|
|
121
|
+
agentType: 'cursor',
|
|
122
122
|
runtime: '3h 15m',
|
|
123
123
|
blockedBy: 'Database Migration',
|
|
124
124
|
errorMessage: 'Token refresh failed: invalid_grant',
|
|
@@ -169,7 +169,7 @@ function AllLifecyclesRender() {
|
|
|
169
169
|
lifecycle: phase,
|
|
170
170
|
state: 'running',
|
|
171
171
|
progress: 50,
|
|
172
|
-
|
|
172
|
+
agentType: 'claude-code',
|
|
173
173
|
repositoryPath: '/home/user/my-repo',
|
|
174
174
|
branch: 'feat/feature-name',
|
|
175
175
|
});
|
package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { type LucideIcon } from 'lucide-react';
|
|
2
2
|
import type { Node } from '@xyflow/react';
|
|
3
|
+
import type { AgentTypeValue } from './agent-type-icons.js';
|
|
3
4
|
export type FeatureNodeState = 'running' | 'action-required' | 'done' | 'blocked' | 'error';
|
|
4
5
|
export type FeatureLifecyclePhase = 'requirements' | 'research' | 'implementation' | 'review' | 'deploy' | 'maintain';
|
|
5
6
|
/** Human-readable display labels for lifecycle phases. */
|
|
6
7
|
export declare const lifecycleDisplayLabels: Record<FeatureLifecyclePhase, string>;
|
|
8
|
+
/** Present-participle verbs for the running badge, keyed by lifecycle phase. */
|
|
9
|
+
export declare const lifecycleRunningVerbs: Record<FeatureLifecyclePhase, string>;
|
|
7
10
|
export interface FeatureNodeData {
|
|
8
11
|
[key: string]: unknown;
|
|
9
12
|
name: string;
|
|
@@ -22,8 +25,8 @@ export interface FeatureNodeData {
|
|
|
22
25
|
blockedBy?: string;
|
|
23
26
|
/** Short error message for error state */
|
|
24
27
|
errorMessage?: string;
|
|
25
|
-
/** Agent
|
|
26
|
-
|
|
28
|
+
/** Agent executor type (e.g. "claude-code", "cursor"). */
|
|
29
|
+
agentType?: AgentTypeValue;
|
|
27
30
|
onAction?: () => void;
|
|
28
31
|
onSettings?: () => void;
|
|
29
32
|
showHandles?: boolean;
|
package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-node-state-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node-state-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmD,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"feature-node-state-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node-state-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmD,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAChG,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,iBAAiB,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5F,MAAM,MAAM,qBAAqB,GAC7B,cAAc,GACd,UAAU,GACV,gBAAgB,GAChB,QAAQ,GACR,QAAQ,GACR,UAAU,CAAC;AAEf,0DAA0D;AAC1D,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAOxE,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAOvE,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,qBAAqB,CAAC;IACjC,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,cAAc,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;AAEnE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,UAAU,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,gBAAgB,EAAE,sBAAsB,CAmDnF,CAAC"}
|
package/dist/src/presentation/web/components/common/feature-node/feature-node-state-config.js
CHANGED
|
@@ -8,6 +8,15 @@ export const lifecycleDisplayLabels = {
|
|
|
8
8
|
deploy: 'DEPLOY & QA',
|
|
9
9
|
maintain: 'COMPLETED',
|
|
10
10
|
};
|
|
11
|
+
/** Present-participle verbs for the running badge, keyed by lifecycle phase. */
|
|
12
|
+
export const lifecycleRunningVerbs = {
|
|
13
|
+
requirements: 'Analyzing',
|
|
14
|
+
research: 'Researching',
|
|
15
|
+
implementation: 'Implementing',
|
|
16
|
+
review: 'Reviewing',
|
|
17
|
+
deploy: 'Deploying',
|
|
18
|
+
maintain: 'Maintaining',
|
|
19
|
+
};
|
|
11
20
|
export const featureNodeStateConfig = {
|
|
12
21
|
running: {
|
|
13
22
|
icon: Loader2,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"feature-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"feature-node.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-node.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAwBnE,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,IAAI,EAAE,eAAe,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,2CAqJA"}
|
|
@@ -3,15 +3,17 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { Handle, Position } from '@xyflow/react';
|
|
4
4
|
import { Settings, Plus } from 'lucide-react';
|
|
5
5
|
import { cn } from '../../../lib/utils.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { featureNodeStateConfig, lifecycleDisplayLabels, lifecycleRunningVerbs, } from './feature-node-state-config.js';
|
|
7
|
+
import { getAgentTypeIcon } from './agent-type-icons.js';
|
|
8
|
+
function AgentIcon({ agentType, className }) {
|
|
9
|
+
const IconComponent = getAgentTypeIcon(agentType);
|
|
10
|
+
return _jsx(IconComponent, { className: className });
|
|
11
|
+
}
|
|
8
12
|
function getBadgeText(data) {
|
|
9
13
|
const config = featureNodeStateConfig[data.state];
|
|
10
14
|
switch (data.state) {
|
|
11
|
-
case 'running':
|
|
12
|
-
|
|
13
|
-
return `${agent} ${data.featureId} running`;
|
|
14
|
-
}
|
|
15
|
+
case 'running':
|
|
16
|
+
return lifecycleRunningVerbs[data.lifecycle];
|
|
15
17
|
case 'done':
|
|
16
18
|
return data.runtime ? `Completed in ${data.runtime}` : 'Completed';
|
|
17
19
|
case 'blocked':
|
|
@@ -28,7 +30,7 @@ export function FeatureNode({ data, selected, }) {
|
|
|
28
30
|
return (_jsxs("div", { className: "group relative", children: [data.showHandles ? (_jsx(Handle, { type: "target", position: Position.Left, isConnectable: false, className: "opacity-0!" })) : null, _jsxs("div", { "data-testid": "feature-node-card", className: cn('bg-card flex min-h-35 w-72 flex-col rounded-lg border p-3 shadow-sm', selected && 'ring-primary ring-2'), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { "data-testid": "feature-node-lifecycle-label", className: cn('text-[10px] font-semibold tracking-wider'), children: lifecycleDisplayLabels[data.lifecycle] }), data.onSettings ? (_jsx("button", { type: "button", "aria-label": "Settings", "data-testid": "feature-node-settings-button", onClick: (e) => {
|
|
29
31
|
e.stopPropagation();
|
|
30
32
|
data.onSettings?.();
|
|
31
|
-
}, className: "nodrag text-muted-foreground hover:text-foreground -mt-1 -mr-1 p-1", children: _jsx(Settings, { className: "h-3.5 w-3.5" }) })) : null] }), _jsx("h3", { className: "mt-1 truncate text-sm font-bold", children: data.name }), data.description ? (_jsx("p", { "data-testid": "feature-node-description", className: "text-muted-foreground mt-0.5 truncate text-xs", children: data.description })) : null, _jsx("div", { className: "mt-auto pt-2", children:
|
|
33
|
+
}, className: "nodrag text-muted-foreground hover:text-foreground -mt-1 -mr-1 p-1", children: _jsx(Settings, { className: "h-3.5 w-3.5" }) })) : null] }), _jsx("h3", { className: "mt-1 truncate text-sm font-bold", children: data.name }), data.description ? (_jsx("p", { "data-testid": "feature-node-description", className: "text-muted-foreground mt-0.5 truncate text-xs", children: data.description })) : null, _jsx("div", { className: "mt-auto pt-2", children: data.state === 'running' ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 text-xs", children: [_jsx(AgentIcon, { agentType: data.agentType, className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "text-muted-foreground", children: getBadgeText(data) })] }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: "animate-indeterminate-progress bg-foreground/30 h-full w-1/3 rounded-full" }) })] })) : config.showProgressBar ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground flex items-center justify-end text-[10px]", children: _jsxs("span", { children: [data.progress, "%"] }) }), _jsx("div", { "data-testid": "feature-node-progress-bar", className: "bg-muted mt-1.5 h-1 w-full overflow-hidden rounded-full", children: _jsx("div", { className: cn('h-full rounded-full transition-all', config.progressClass), style: { width: `${data.progress}%` } }) })] })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "text-muted-foreground text-[10px]", children: _jsx("span", { children: data.featureId }) }), _jsxs("div", { "data-testid": "feature-node-badge", className: cn('mt-1.5 flex items-center justify-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-medium', config.badgeBgClass, config.badgeClass), children: [_jsx(Icon, { className: "h-3.5 w-3.5 shrink-0" }), _jsx("span", { className: "truncate", children: getBadgeText(data) })] })] })) })] }), data.onAction ? (_jsx(Handle, { type: "source", position: Position.Right, className: "h-0! w-0! border-0! bg-transparent!", children: _jsx("button", { type: "button", "aria-label": "Add", "data-testid": "feature-node-action-button", onClick: (e) => {
|
|
32
34
|
e.stopPropagation();
|
|
33
35
|
data.onAction?.();
|
|
34
36
|
}, className: "nodrag absolute top-1/2 left-1/2 flex h-6 w-6 -translate-x-1/2 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full bg-blue-500 text-white opacity-0 shadow-md transition-opacity group-hover:opacity-100 hover:bg-blue-600", children: _jsx(Plus, { className: "h-3.5 w-3.5" }) }) })) : data.showHandles ? (_jsx(Handle, { type: "source", position: Position.Right, isConnectable: false, className: "opacity-0!" })) : null] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-shell.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/layouts/app-shell/app-shell.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"app-shell.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/layouts/app-shell/app-shell.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAMpD,UAAU,aAAa;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,QAAQ,CAAC,EAAE,QAAQ,EAAE,EAAE,aAAa,2CAqBnD"}
|
|
@@ -4,7 +4,10 @@ import { useCallback } from 'react';
|
|
|
4
4
|
import { SidebarProvider, SidebarInset } from '../../ui/sidebar.js';
|
|
5
5
|
import { AppSidebar } from '../../layouts/app-sidebar/index.js';
|
|
6
6
|
import { ThemeToggle } from '../../common/theme-toggle/index.js';
|
|
7
|
+
import { useNotifications } from '../../../hooks/use-notifications.js';
|
|
7
8
|
export function AppShell({ children }) {
|
|
9
|
+
// Subscribe to agent lifecycle events and dispatch toast/browser notifications
|
|
10
|
+
useNotifications();
|
|
8
11
|
const handleNewFeature = useCallback(() => {
|
|
9
12
|
window.dispatchEvent(new CustomEvent('shep:open-create-drawer'));
|
|
10
13
|
}, []);
|
|
@@ -2,7 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
import { type VariantProps } from 'class-variance-authority';
|
|
3
3
|
declare const buttonVariants: (props?: ({
|
|
4
4
|
variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
5
|
-
size?: "default" | "icon" | "
|
|
5
|
+
size?: "default" | "icon" | "xs" | "sm" | "lg" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
7
|
declare function Button({ className, variant, size, asChild, ...props }: React.ComponentProps<'button'> & VariantProps<typeof buttonVariants> & {
|
|
8
8
|
asChild?: boolean;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NotificationEvent } from '../../../../packages/core/src/domain/generated/output.js';
|
|
2
|
+
export type ConnectionStatus = 'connected' | 'connecting' | 'disconnected';
|
|
3
|
+
export interface UseAgentEventsOptions {
|
|
4
|
+
runId?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface UseAgentEventsResult {
|
|
7
|
+
events: NotificationEvent[];
|
|
8
|
+
lastEvent: NotificationEvent | null;
|
|
9
|
+
connectionStatus: ConnectionStatus;
|
|
10
|
+
}
|
|
11
|
+
export declare function useAgentEvents(options?: UseAgentEventsOptions): UseAgentEventsResult;
|
|
12
|
+
//# sourceMappingURL=use-agent-events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-agent-events.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-agent-events.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAE9E,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,YAAY,GAAG,cAAc,CAAC;AAE3E,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,SAAS,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACpC,gBAAgB,EAAE,gBAAgB,CAAC;CACpC;AAKD,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,oBAAoB,CAkEpF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
const BASE_BACKOFF_MS = 1000;
|
|
4
|
+
const MAX_BACKOFF_MS = 30_000;
|
|
5
|
+
export function useAgentEvents(options) {
|
|
6
|
+
const [events, setEvents] = useState([]);
|
|
7
|
+
const [lastEvent, setLastEvent] = useState(null);
|
|
8
|
+
const [connectionStatus, setConnectionStatus] = useState('disconnected');
|
|
9
|
+
const backoffRef = useRef(BASE_BACKOFF_MS);
|
|
10
|
+
const reconnectTimerRef = useRef(null);
|
|
11
|
+
const esRef = useRef(null);
|
|
12
|
+
const runId = options?.runId;
|
|
13
|
+
const connect = useCallback(() => {
|
|
14
|
+
if (typeof window === 'undefined' || typeof EventSource === 'undefined') {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const url = runId
|
|
18
|
+
? `/api/agent-events?runId=${encodeURIComponent(runId)}`
|
|
19
|
+
: '/api/agent-events';
|
|
20
|
+
const es = new EventSource(url);
|
|
21
|
+
esRef.current = es;
|
|
22
|
+
setConnectionStatus('connecting');
|
|
23
|
+
es.onopen = () => {
|
|
24
|
+
setConnectionStatus('connected');
|
|
25
|
+
backoffRef.current = BASE_BACKOFF_MS;
|
|
26
|
+
};
|
|
27
|
+
es.onerror = () => {
|
|
28
|
+
es.close();
|
|
29
|
+
esRef.current = null;
|
|
30
|
+
setConnectionStatus('disconnected');
|
|
31
|
+
const delay = backoffRef.current;
|
|
32
|
+
backoffRef.current = Math.min(delay * 2, MAX_BACKOFF_MS);
|
|
33
|
+
reconnectTimerRef.current = setTimeout(() => {
|
|
34
|
+
reconnectTimerRef.current = null;
|
|
35
|
+
connect();
|
|
36
|
+
}, delay);
|
|
37
|
+
};
|
|
38
|
+
es.addEventListener('notification', ((event) => {
|
|
39
|
+
const parsed = JSON.parse(event.data);
|
|
40
|
+
setEvents((prev) => [...prev, parsed]);
|
|
41
|
+
setLastEvent(parsed);
|
|
42
|
+
}));
|
|
43
|
+
}, [runId]);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
connect();
|
|
46
|
+
return () => {
|
|
47
|
+
if (reconnectTimerRef.current !== null) {
|
|
48
|
+
clearTimeout(reconnectTimerRef.current);
|
|
49
|
+
reconnectTimerRef.current = null;
|
|
50
|
+
}
|
|
51
|
+
if (esRef.current) {
|
|
52
|
+
esRef.current.close();
|
|
53
|
+
esRef.current = null;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}, [connect]);
|
|
57
|
+
return { events, lastEvent, connectionStatus };
|
|
58
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface UseNotificationsOptions {
|
|
2
|
+
browserEnabled?: boolean;
|
|
3
|
+
runId?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface UseNotificationsResult {
|
|
6
|
+
requestBrowserPermission: () => Promise<void>;
|
|
7
|
+
browserPermissionState: NotificationPermission;
|
|
8
|
+
}
|
|
9
|
+
export declare function useNotifications(options?: UseNotificationsOptions): UseNotificationsResult;
|
|
10
|
+
//# sourceMappingURL=use-notifications.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-notifications.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-notifications.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,uBAAuB;IACtC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACrC,wBAAwB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,sBAAsB,EAAE,sBAAsB,CAAC;CAChD;AAqBD,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,uBAAuB,GAAG,sBAAsB,CAyC1F"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { toast } from 'sonner';
|
|
4
|
+
import { NotificationSeverity } from '../../../../packages/core/src/domain/generated/output.js';
|
|
5
|
+
import { useAgentEvents } from './use-agent-events.js';
|
|
6
|
+
const SEVERITY_TO_TOAST = {
|
|
7
|
+
[NotificationSeverity.Success]: 'success',
|
|
8
|
+
[NotificationSeverity.Error]: 'error',
|
|
9
|
+
[NotificationSeverity.Warning]: 'warning',
|
|
10
|
+
[NotificationSeverity.Info]: 'info',
|
|
11
|
+
};
|
|
12
|
+
function dispatchToast(event) {
|
|
13
|
+
const method = SEVERITY_TO_TOAST[event.severity] ?? 'info';
|
|
14
|
+
toast[method](event.featureName, { description: event.message });
|
|
15
|
+
}
|
|
16
|
+
function dispatchBrowserNotification(event) {
|
|
17
|
+
if (globalThis.Notification?.permission !== 'granted') {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
new Notification(event.featureName, { body: event.message });
|
|
21
|
+
}
|
|
22
|
+
export function useNotifications(options) {
|
|
23
|
+
const browserEnabled = options?.browserEnabled ?? false;
|
|
24
|
+
const { lastEvent } = useAgentEvents({ runId: options?.runId });
|
|
25
|
+
const [browserPermissionState, setBrowserPermissionState] = useState(() => {
|
|
26
|
+
if (typeof globalThis.Notification === 'undefined')
|
|
27
|
+
return 'default';
|
|
28
|
+
return globalThis.Notification.permission;
|
|
29
|
+
});
|
|
30
|
+
// Track which events we've already dispatched to avoid re-dispatching on re-render
|
|
31
|
+
const processedRef = useRef(new Set());
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!lastEvent)
|
|
34
|
+
return;
|
|
35
|
+
// Create a key for deduplication
|
|
36
|
+
const key = `${lastEvent.agentRunId}-${lastEvent.eventType}-${lastEvent.timestamp}`;
|
|
37
|
+
if (processedRef.current.has(key))
|
|
38
|
+
return;
|
|
39
|
+
processedRef.current.add(key);
|
|
40
|
+
// Always dispatch toast
|
|
41
|
+
dispatchToast(lastEvent);
|
|
42
|
+
// Dispatch browser notification if enabled and permitted
|
|
43
|
+
if (browserEnabled) {
|
|
44
|
+
dispatchBrowserNotification(lastEvent);
|
|
45
|
+
}
|
|
46
|
+
}, [lastEvent, browserEnabled]);
|
|
47
|
+
const requestBrowserPermission = useCallback(async () => {
|
|
48
|
+
if (typeof globalThis.Notification === 'undefined')
|
|
49
|
+
return;
|
|
50
|
+
const result = await globalThis.Notification.requestPermission();
|
|
51
|
+
setBrowserPermissionState(result);
|
|
52
|
+
}, []);
|
|
53
|
+
return {
|
|
54
|
+
requestBrowserPermission,
|
|
55
|
+
browserPermissionState,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-notifications.stories.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-notifications.stories.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAwIvD,QAAA,MAAM,IAAI,EAAE,IAcX,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC;AAEtB,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { toast } from 'sonner';
|
|
4
|
+
import { Toaster } from '../components/ui/sonner.js';
|
|
5
|
+
import { Button } from '../components/ui/button.js';
|
|
6
|
+
function simulateNotification(event) {
|
|
7
|
+
const method = event.severity ?? 'info';
|
|
8
|
+
toast[method](event.featureName, { description: event.message });
|
|
9
|
+
if (globalThis.Notification?.permission === 'granted') {
|
|
10
|
+
new Notification(event.featureName, { body: event.message });
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function NotificationDemo() {
|
|
14
|
+
const [browserPermission, setBrowserPermission] = useState(typeof globalThis.Notification !== 'undefined' ? globalThis.Notification.permission : 'default');
|
|
15
|
+
const requestPermission = async () => {
|
|
16
|
+
if (typeof globalThis.Notification === 'undefined')
|
|
17
|
+
return;
|
|
18
|
+
const result = await globalThis.Notification.requestPermission();
|
|
19
|
+
setBrowserPermission(result);
|
|
20
|
+
};
|
|
21
|
+
return (_jsxs("div", { className: "flex w-[420px] flex-col gap-6", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Browser Notification Permission" }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("span", { className: "text-muted-foreground text-sm", children: ["Status: ", _jsx("code", { className: "bg-muted rounded px-1.5 py-0.5", children: browserPermission })] }), _jsx(Button, { size: "sm", variant: "outline", onClick: requestPermission, children: "Request Permission" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("h3", { className: "text-sm font-semibold", children: "Simulate Notification Events" }), _jsx("p", { className: "text-muted-foreground text-xs", children: "Click a button to fire a toast and browser notification." }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Button, { size: "sm", variant: "outline", onClick: () => simulateNotification({
|
|
22
|
+
eventType: 'agent_started',
|
|
23
|
+
severity: 'info',
|
|
24
|
+
featureName: 'Auth Module',
|
|
25
|
+
message: 'Agent started running',
|
|
26
|
+
}), children: "Agent Started" }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => simulateNotification({
|
|
27
|
+
eventType: 'phase_completed',
|
|
28
|
+
severity: 'info',
|
|
29
|
+
featureName: 'Auth Module',
|
|
30
|
+
message: 'Completed analyze phase',
|
|
31
|
+
phaseName: 'analyze',
|
|
32
|
+
}), children: "Phase Completed" }), _jsx(Button, { size: "sm", variant: "secondary", onClick: () => simulateNotification({
|
|
33
|
+
eventType: 'waiting_approval',
|
|
34
|
+
severity: 'warning',
|
|
35
|
+
featureName: 'Auth Module',
|
|
36
|
+
message: 'Waiting for user approval',
|
|
37
|
+
}), children: "Waiting Approval" }), _jsx(Button, { size: "sm", onClick: () => simulateNotification({
|
|
38
|
+
eventType: 'agent_completed',
|
|
39
|
+
severity: 'success',
|
|
40
|
+
featureName: 'Auth Module',
|
|
41
|
+
message: 'Agent completed successfully',
|
|
42
|
+
}), children: "Agent Completed" }), _jsx(Button, { size: "sm", variant: "destructive", onClick: () => simulateNotification({
|
|
43
|
+
eventType: 'agent_failed',
|
|
44
|
+
severity: 'error',
|
|
45
|
+
featureName: 'Auth Module',
|
|
46
|
+
message: 'Agent failed: token refresh error',
|
|
47
|
+
}), children: "Agent Failed" })] })] })] }));
|
|
48
|
+
}
|
|
49
|
+
const meta = {
|
|
50
|
+
title: 'Hooks/useNotifications',
|
|
51
|
+
parameters: {
|
|
52
|
+
layout: 'centered',
|
|
53
|
+
},
|
|
54
|
+
tags: ['autodocs'],
|
|
55
|
+
decorators: [
|
|
56
|
+
(Story) => (_jsxs(_Fragment, { children: [_jsx(Story, {}), _jsx(Toaster, {})] })),
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
export default meta;
|
|
60
|
+
export const Default = {
|
|
61
|
+
render: () => _jsx(NotificationDemo, {}),
|
|
62
|
+
};
|