heartbeads 0.4.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/.next/BUILD_ID +1 -0
- package/.next/app-build-manifest.json +49 -0
- package/.next/app-path-routes-manifest.json +1 -0
- package/.next/build-manifest.json +32 -0
- package/.next/export-marker.json +1 -0
- package/.next/images-manifest.json +1 -0
- package/.next/next-minimal-server.js.nft.json +1 -0
- package/.next/next-server.js.nft.json +1 -0
- package/.next/package.json +1 -0
- package/.next/prerender-manifest.json +1 -0
- package/.next/react-loadable-manifest.json +8 -0
- package/.next/required-server-files.json +1 -0
- package/.next/routes-manifest.json +1 -0
- package/.next/server/app/_not-found/page.js +1 -0
- package/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -0
- package/.next/server/app/_not-found.html +1 -0
- package/.next/server/app/_not-found.meta +6 -0
- package/.next/server/app/_not-found.rsc +9 -0
- package/.next/server/app/api/auth/route.js +1 -0
- package/.next/server/app/api/auth/route.js.nft.json +1 -0
- package/.next/server/app/api/beads/route.js +8 -0
- package/.next/server/app/api/beads/route.js.nft.json +1 -0
- package/.next/server/app/api/beads/stream/route.js +10 -0
- package/.next/server/app/api/beads/stream/route.js.nft.json +1 -0
- package/.next/server/app/api/beads.body +1 -0
- package/.next/server/app/api/beads.meta +1 -0
- package/.next/server/app/api/config/route.js +8 -0
- package/.next/server/app/api/config/route.js.nft.json +1 -0
- package/.next/server/app/api/docs/page.js +120 -0
- package/.next/server/app/api/docs/page.js.nft.json +1 -0
- package/.next/server/app/api/docs/page_client-reference-manifest.js +1 -0
- package/.next/server/app/api/docs.html +120 -0
- package/.next/server/app/api/docs.meta +5 -0
- package/.next/server/app/api/docs.rsc +70 -0
- package/.next/server/app/api/login/route.js +1 -0
- package/.next/server/app/api/login/route.js.nft.json +1 -0
- package/.next/server/app/api/logout/route.js +1 -0
- package/.next/server/app/api/logout/route.js.nft.json +1 -0
- package/.next/server/app/api/oauth/callback/route.js +1 -0
- package/.next/server/app/api/oauth/callback/route.js.nft.json +1 -0
- package/.next/server/app/api/oauth/client-metadata.json/route.js +1 -0
- package/.next/server/app/api/oauth/client-metadata.json/route.js.nft.json +1 -0
- package/.next/server/app/api/oauth/jwks.json/route.js +1 -0
- package/.next/server/app/api/oauth/jwks.json/route.js.nft.json +1 -0
- package/.next/server/app/api/records/route.js +1 -0
- package/.next/server/app/api/records/route.js.nft.json +1 -0
- package/.next/server/app/api/status/route.js +1 -0
- package/.next/server/app/api/status/route.js.nft.json +1 -0
- package/.next/server/app/api/v1/graph/route.js +1 -0
- package/.next/server/app/api/v1/graph/route.js.nft.json +1 -0
- package/.next/server/app/api/v1/issues/[id]/route.js +1 -0
- package/.next/server/app/api/v1/issues/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/v1/ready/route.js +1 -0
- package/.next/server/app/api/v1/ready/route.js.nft.json +1 -0
- package/.next/server/app/index.html +1 -0
- package/.next/server/app/index.meta +5 -0
- package/.next/server/app/index.rsc +9 -0
- package/.next/server/app/login/page.js +1 -0
- package/.next/server/app/login/page.js.nft.json +1 -0
- package/.next/server/app/login/page_client-reference-manifest.js +1 -0
- package/.next/server/app/login.html +1 -0
- package/.next/server/app/login.meta +5 -0
- package/.next/server/app/login.rsc +9 -0
- package/.next/server/app/opengraph-image.png/route.js +1 -0
- package/.next/server/app/opengraph-image.png/route.js.nft.json +1 -0
- package/.next/server/app/opengraph-image.png.body +0 -0
- package/.next/server/app/opengraph-image.png.meta +1 -0
- package/.next/server/app/page.js +24 -0
- package/.next/server/app/page.js.nft.json +1 -0
- package/.next/server/app/page_client-reference-manifest.js +1 -0
- package/.next/server/app/twitter-image.png/route.js +1 -0
- package/.next/server/app/twitter-image.png/route.js.nft.json +1 -0
- package/.next/server/app/twitter-image.png.body +0 -0
- package/.next/server/app/twitter-image.png.meta +1 -0
- package/.next/server/app-paths-manifest.json +22 -0
- package/.next/server/chunks/247.js +12 -0
- package/.next/server/chunks/29.js +1 -0
- package/.next/server/chunks/343.js +1 -0
- package/.next/server/chunks/460.js +12 -0
- package/.next/server/chunks/533.js +38 -0
- package/.next/server/chunks/542.js +27 -0
- package/.next/server/chunks/590.js +6 -0
- package/.next/server/chunks/615.js +15 -0
- package/.next/server/chunks/696.js +25 -0
- package/.next/server/chunks/719.js +2 -0
- package/.next/server/chunks/739.js +1 -0
- package/.next/server/chunks/950.js +2 -0
- package/.next/server/chunks/font-manifest.json +1 -0
- package/.next/server/edge-runtime-webpack.js +2 -0
- package/.next/server/edge-runtime-webpack.js.map +1 -0
- package/.next/server/font-manifest.json +1 -0
- package/.next/server/functions-config-manifest.json +1 -0
- package/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/.next/server/middleware-build-manifest.js +1 -0
- package/.next/server/middleware-manifest.json +32 -0
- package/.next/server/middleware-react-loadable-manifest.js +1 -0
- package/.next/server/middleware.js +14 -0
- package/.next/server/middleware.js.map +1 -0
- package/.next/server/next-font-manifest.js +1 -0
- package/.next/server/next-font-manifest.json +1 -0
- package/.next/server/pages/404.html +1 -0
- package/.next/server/pages/500.html +1 -0
- package/.next/server/pages/_app.js +1 -0
- package/.next/server/pages/_app.js.nft.json +1 -0
- package/.next/server/pages/_document.js +1 -0
- package/.next/server/pages/_document.js.nft.json +1 -0
- package/.next/server/pages/_error.js +1 -0
- package/.next/server/pages/_error.js.nft.json +1 -0
- package/.next/server/pages-manifest.json +1 -0
- package/.next/server/server-reference-manifest.js +1 -0
- package/.next/server/server-reference-manifest.json +1 -0
- package/.next/server/webpack-runtime.js +1 -0
- package/.next/static/chunks/149.a3e3a5dc03e21086.js +1 -0
- package/.next/static/chunks/2200cc46-7c93a0e00b0bb825.js +1 -0
- package/.next/static/chunks/788-aa413085174e935a.js +1 -0
- package/.next/static/chunks/945-3ff1d381a0af1ecd.js +2 -0
- package/.next/static/chunks/971-bb44d52bcd9ee2a9.js +1 -0
- package/.next/static/chunks/app/_not-found/page-200b7a7a6cfc29df.js +1 -0
- package/.next/static/chunks/app/api/docs/page-1dc18f40154cdce6.js +1 -0
- package/.next/static/chunks/app/layout-13e3cdaaa416edb6.js +1 -0
- package/.next/static/chunks/app/login/page-60d930d64f021753.js +1 -0
- package/.next/static/chunks/app/not-found-ae1139bed2018dd8.js +1 -0
- package/.next/static/chunks/app/page-583300dd8af66e5a.js +1 -0
- package/.next/static/chunks/framework-6e06c675866dc992.js +1 -0
- package/.next/static/chunks/main-app-8b0c4a1007dbb7f4.js +1 -0
- package/.next/static/chunks/main-e680fb049d7426e1.js +1 -0
- package/.next/static/chunks/pages/_app-0c3037849002a4aa.js +1 -0
- package/.next/static/chunks/pages/_error-a647cd2c75dc4dc7.js +1 -0
- package/.next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/.next/static/chunks/webpack-117444a4bfe51057.js +1 -0
- package/.next/static/css/8c1b520a38ba4ccd.css +3 -0
- package/.next/static/vFM69sDrBUf_9ULwPmVAE/_buildManifest.js +1 -0
- package/.next/static/vFM69sDrBUf_9ULwPmVAE/_ssgManifest.js +1 -0
- package/README.md +389 -0
- package/app/api/auth/route.ts +103 -0
- package/app/api/beads/route.ts +27 -0
- package/app/api/beads/stream/route.ts +83 -0
- package/app/api/config/route.ts +48 -0
- package/app/api/docs/page.tsx +497 -0
- package/app/api/login/route.ts +42 -0
- package/app/api/logout/route.ts +14 -0
- package/app/api/oauth/callback/route.ts +97 -0
- package/app/api/oauth/client-metadata.json/route.ts +33 -0
- package/app/api/oauth/jwks.json/route.ts +32 -0
- package/app/api/records/route.ts +168 -0
- package/app/api/status/route.ts +25 -0
- package/app/api/v1/graph/route.ts +251 -0
- package/app/api/v1/issues/[id]/route.ts +158 -0
- package/app/api/v1/ready/route.ts +229 -0
- package/app/globals.css +230 -0
- package/app/layout.tsx +51 -0
- package/app/login/page.tsx +164 -0
- package/app/not-found.tsx +91 -0
- package/app/opengraph-image.png +0 -0
- package/app/page.tsx +2041 -0
- package/app/twitter-image.png +0 -0
- package/bin/heartbeads.mjs +225 -0
- package/components/ActivityItem.tsx +326 -0
- package/components/ActivityOverlay.tsx +125 -0
- package/components/ActivityPanel.tsx +345 -0
- package/components/AllCommentsPanel.tsx +270 -0
- package/components/AuthButton.tsx +202 -0
- package/components/BeadTooltip.tsx +246 -0
- package/components/BeadsGraph.tsx +2493 -0
- package/components/BeadsLogo.tsx +94 -0
- package/components/CommentTooltip.tsx +338 -0
- package/components/ContextMenu.tsx +272 -0
- package/components/DescriptionModal.tsx +595 -0
- package/components/GraphStats.tsx +121 -0
- package/components/HeartIcon.tsx +33 -0
- package/components/HelpPanel.tsx +339 -0
- package/components/MobileActionSheet.tsx +255 -0
- package/components/NodeDetail.tsx +793 -0
- package/components/SettingsModal.tsx +315 -0
- package/components/StatusLegend.tsx +99 -0
- package/components/TimelineBar.tsx +116 -0
- package/components/TutorialOverlay.tsx +235 -0
- package/hooks/useBeadsComments.ts +81 -0
- package/hooks/useIsMobile.ts +19 -0
- package/lib/activity.ts +377 -0
- package/lib/agent.ts +29 -0
- package/lib/api-helpers.ts +46 -0
- package/lib/auth/client.ts +221 -0
- package/lib/auth.tsx +159 -0
- package/lib/comments.ts +413 -0
- package/lib/diff-beads.ts +128 -0
- package/lib/discover.ts +228 -0
- package/lib/env.ts +33 -0
- package/lib/gate.ts +55 -0
- package/lib/parse-beads.ts +234 -0
- package/lib/session.ts +52 -0
- package/lib/settings.ts +42 -0
- package/lib/timeline.ts +138 -0
- package/lib/tts.ts +397 -0
- package/lib/types.ts +271 -0
- package/lib/utils.ts +48 -0
- package/lib/watch-beads.ts +97 -0
- package/next.config.mjs +4 -0
- package/package.json +81 -0
- package/postcss.config.mjs +9 -0
- package/public/image.png +0 -0
- package/scripts/generate-jwk.js +38 -0
- package/tailwind.config.ts +41 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
interface GraphStatsProps {
|
|
4
|
+
stats: {
|
|
5
|
+
total: number;
|
|
6
|
+
open: number;
|
|
7
|
+
inProgress: number;
|
|
8
|
+
blocked: number;
|
|
9
|
+
closed: number;
|
|
10
|
+
actionable: number;
|
|
11
|
+
edges: number;
|
|
12
|
+
prefixes: string[];
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function GraphStats({ stats }: GraphStatsProps) {
|
|
17
|
+
return (
|
|
18
|
+
<div className="grid grid-cols-2 gap-2">
|
|
19
|
+
<StatCard
|
|
20
|
+
value={stats.total}
|
|
21
|
+
label="Nodes"
|
|
22
|
+
icon={
|
|
23
|
+
<svg
|
|
24
|
+
className="w-3.5 h-3.5"
|
|
25
|
+
fill="none"
|
|
26
|
+
stroke="currentColor"
|
|
27
|
+
viewBox="0 0 24 24"
|
|
28
|
+
>
|
|
29
|
+
<circle cx="12" cy="12" r="3" strokeWidth={2} />
|
|
30
|
+
</svg>
|
|
31
|
+
}
|
|
32
|
+
/>
|
|
33
|
+
<StatCard
|
|
34
|
+
value={stats.edges}
|
|
35
|
+
label="Edges"
|
|
36
|
+
icon={
|
|
37
|
+
<svg
|
|
38
|
+
className="w-3.5 h-3.5"
|
|
39
|
+
fill="none"
|
|
40
|
+
stroke="currentColor"
|
|
41
|
+
viewBox="0 0 24 24"
|
|
42
|
+
>
|
|
43
|
+
<path
|
|
44
|
+
strokeLinecap="round"
|
|
45
|
+
strokeWidth={2}
|
|
46
|
+
d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101"
|
|
47
|
+
/>
|
|
48
|
+
</svg>
|
|
49
|
+
}
|
|
50
|
+
/>
|
|
51
|
+
<StatCard
|
|
52
|
+
value={stats.actionable}
|
|
53
|
+
label="Actionable"
|
|
54
|
+
color="text-emerald-600"
|
|
55
|
+
icon={
|
|
56
|
+
<svg
|
|
57
|
+
className="w-3.5 h-3.5"
|
|
58
|
+
fill="none"
|
|
59
|
+
stroke="currentColor"
|
|
60
|
+
viewBox="0 0 24 24"
|
|
61
|
+
>
|
|
62
|
+
<path
|
|
63
|
+
strokeLinecap="round"
|
|
64
|
+
strokeLinejoin="round"
|
|
65
|
+
strokeWidth={2}
|
|
66
|
+
d="M13 10V3L4 14h7v7l9-11h-7z"
|
|
67
|
+
/>
|
|
68
|
+
</svg>
|
|
69
|
+
}
|
|
70
|
+
/>
|
|
71
|
+
<StatCard
|
|
72
|
+
value={stats.open}
|
|
73
|
+
label="Open"
|
|
74
|
+
color="text-emerald-500"
|
|
75
|
+
icon={
|
|
76
|
+
<svg
|
|
77
|
+
className="w-3.5 h-3.5"
|
|
78
|
+
fill="none"
|
|
79
|
+
stroke="currentColor"
|
|
80
|
+
viewBox="0 0 24 24"
|
|
81
|
+
>
|
|
82
|
+
<path
|
|
83
|
+
strokeLinecap="round"
|
|
84
|
+
strokeLinejoin="round"
|
|
85
|
+
strokeWidth={2}
|
|
86
|
+
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"
|
|
87
|
+
/>
|
|
88
|
+
</svg>
|
|
89
|
+
}
|
|
90
|
+
/>
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function StatCard({
|
|
96
|
+
value,
|
|
97
|
+
label,
|
|
98
|
+
color,
|
|
99
|
+
icon,
|
|
100
|
+
}: {
|
|
101
|
+
value: number;
|
|
102
|
+
label: string;
|
|
103
|
+
color?: string;
|
|
104
|
+
icon: React.ReactNode;
|
|
105
|
+
}) {
|
|
106
|
+
return (
|
|
107
|
+
<div className="bg-zinc-50 rounded-lg px-3 py-2.5 border border-zinc-100">
|
|
108
|
+
<div className="flex items-center gap-1.5">
|
|
109
|
+
<span className="text-zinc-400">{icon}</span>
|
|
110
|
+
<span
|
|
111
|
+
className={`text-lg font-bold ${color || "text-zinc-700"}`}
|
|
112
|
+
>
|
|
113
|
+
{value}
|
|
114
|
+
</span>
|
|
115
|
+
</div>
|
|
116
|
+
<div className="text-[10px] text-zinc-400 uppercase tracking-wider mt-0.5">
|
|
117
|
+
{label}
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// HeartIcon — ported from Hyperscan ReviewSection
|
|
2
|
+
// Shared by NodeDetail and AllCommentsPanel
|
|
3
|
+
|
|
4
|
+
export function HeartIcon({
|
|
5
|
+
className = "w-3 h-3",
|
|
6
|
+
filled = false,
|
|
7
|
+
}: {
|
|
8
|
+
className?: string;
|
|
9
|
+
filled?: boolean;
|
|
10
|
+
}) {
|
|
11
|
+
if (filled) {
|
|
12
|
+
return (
|
|
13
|
+
<svg className={className} viewBox="0 0 24 24" fill="currentColor">
|
|
14
|
+
<path d="M11.645 20.91l-.007-.003-.022-.012a15.247 15.247 0 01-.383-.218 25.18 25.18 0 01-4.244-3.17C4.688 15.36 2.25 12.174 2.25 8.25 2.25 5.322 4.714 3 7.688 3A5.5 5.5 0 0112 5.052 5.5 5.5 0 0116.313 3c2.973 0 5.437 2.322 5.437 5.25 0 3.925-2.438 7.111-4.739 9.256a25.175 25.175 0 01-4.244 3.17 15.247 15.247 0 01-.383.219l-.022.012-.007.004-.003.001a.752.752 0 01-.704 0l-.003-.001z" />
|
|
15
|
+
</svg>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
return (
|
|
19
|
+
<svg
|
|
20
|
+
className={className}
|
|
21
|
+
fill="none"
|
|
22
|
+
viewBox="0 0 24 24"
|
|
23
|
+
strokeWidth={1.5}
|
|
24
|
+
stroke="currentColor"
|
|
25
|
+
>
|
|
26
|
+
<path
|
|
27
|
+
strokeLinecap="round"
|
|
28
|
+
strokeLinejoin="round"
|
|
29
|
+
d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12z"
|
|
30
|
+
/>
|
|
31
|
+
</svg>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { TUTORIAL_STEPS } from "./TutorialOverlay";
|
|
4
|
+
|
|
5
|
+
// Catppuccin Latte accents
|
|
6
|
+
const CAT = {
|
|
7
|
+
red: "#d20f39",
|
|
8
|
+
teal: "#179299",
|
|
9
|
+
peach: "#fe640b",
|
|
10
|
+
blue: "#1e66f5",
|
|
11
|
+
green: "#40a02b",
|
|
12
|
+
mauve: "#8839ef",
|
|
13
|
+
sapphire: "#209fb5",
|
|
14
|
+
pink: "#ea76cb",
|
|
15
|
+
surface: "#dce0e8",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
interface HelpPanelProps {
|
|
19
|
+
isOpen: boolean;
|
|
20
|
+
onClose: () => void;
|
|
21
|
+
tutorialStep?: number | null;
|
|
22
|
+
onStartTutorial?: () => void;
|
|
23
|
+
onNextStep?: () => void;
|
|
24
|
+
onPrevStep?: () => void;
|
|
25
|
+
onEndTutorial?: () => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function HelpPanel({
|
|
29
|
+
isOpen,
|
|
30
|
+
onClose,
|
|
31
|
+
tutorialStep = null,
|
|
32
|
+
onStartTutorial,
|
|
33
|
+
onNextStep,
|
|
34
|
+
onPrevStep,
|
|
35
|
+
onEndTutorial,
|
|
36
|
+
}: HelpPanelProps) {
|
|
37
|
+
const isTutorialActive = tutorialStep !== null;
|
|
38
|
+
const headerText = isTutorialActive ? "Tutorial" : "Welcome to Heartbeads";
|
|
39
|
+
|
|
40
|
+
const content = isTutorialActive ? (
|
|
41
|
+
<TutorialContent
|
|
42
|
+
step={tutorialStep}
|
|
43
|
+
onNext={onNextStep!}
|
|
44
|
+
onPrev={onPrevStep!}
|
|
45
|
+
onEnd={onEndTutorial!}
|
|
46
|
+
/>
|
|
47
|
+
) : (
|
|
48
|
+
<HelpContent onStartTutorial={onStartTutorial} />
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<>
|
|
53
|
+
{/* Desktop: right sidebar — z-[60] during tutorial so it sits above the overlay */}
|
|
54
|
+
<aside
|
|
55
|
+
className={`hidden md:flex absolute top-0 right-0 h-full w-[360px] bg-white border-l border-zinc-200 flex-col shadow-xl transform transition-transform duration-300 ease-out ${
|
|
56
|
+
isOpen ? "translate-x-0" : "translate-x-full"
|
|
57
|
+
} ${isTutorialActive ? "z-[60]" : "z-30"}`}
|
|
58
|
+
>
|
|
59
|
+
<div className="flex items-center justify-between px-5 py-3 border-b border-zinc-100 shrink-0">
|
|
60
|
+
<h2 className="text-sm font-semibold text-zinc-900">
|
|
61
|
+
{headerText}
|
|
62
|
+
</h2>
|
|
63
|
+
<button
|
|
64
|
+
onClick={onClose}
|
|
65
|
+
className="p-1 text-zinc-400 hover:text-zinc-600 transition-colors"
|
|
66
|
+
>
|
|
67
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2}>
|
|
68
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
69
|
+
</svg>
|
|
70
|
+
</button>
|
|
71
|
+
</div>
|
|
72
|
+
<div className="flex-1 overflow-y-auto custom-scrollbar">
|
|
73
|
+
{content}
|
|
74
|
+
</div>
|
|
75
|
+
</aside>
|
|
76
|
+
|
|
77
|
+
{/* Mobile: bottom drawer */}
|
|
78
|
+
<div
|
|
79
|
+
className={`md:hidden fixed inset-x-0 bottom-0 transform transition-transform duration-300 ease-out ${
|
|
80
|
+
isOpen ? "translate-y-0" : "translate-y-full"
|
|
81
|
+
} ${isTutorialActive ? "z-[60]" : "z-20"}`}
|
|
82
|
+
>
|
|
83
|
+
<div className="bg-white rounded-t-2xl shadow-2xl border-t border-zinc-200 max-h-[70vh] flex flex-col">
|
|
84
|
+
<div className="flex items-center justify-between px-5 py-3 border-b border-zinc-100 shrink-0">
|
|
85
|
+
<h2 className="text-sm font-semibold text-zinc-900">
|
|
86
|
+
{headerText}
|
|
87
|
+
</h2>
|
|
88
|
+
<button
|
|
89
|
+
onClick={onClose}
|
|
90
|
+
className="p-1 text-zinc-400 hover:text-zinc-600 transition-colors"
|
|
91
|
+
>
|
|
92
|
+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2}>
|
|
93
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
94
|
+
</svg>
|
|
95
|
+
</button>
|
|
96
|
+
</div>
|
|
97
|
+
<div className="flex-1 overflow-y-auto custom-scrollbar">
|
|
98
|
+
{content}
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* ------------------------------------------------------------------ */
|
|
107
|
+
/* Tutorial step-by-step content */
|
|
108
|
+
/* ------------------------------------------------------------------ */
|
|
109
|
+
|
|
110
|
+
function TutorialContent({
|
|
111
|
+
step,
|
|
112
|
+
onNext,
|
|
113
|
+
onPrev,
|
|
114
|
+
onEnd,
|
|
115
|
+
}: {
|
|
116
|
+
step: number;
|
|
117
|
+
onNext: () => void;
|
|
118
|
+
onPrev: () => void;
|
|
119
|
+
onEnd: () => void;
|
|
120
|
+
}) {
|
|
121
|
+
const currentStep = TUTORIAL_STEPS[step];
|
|
122
|
+
const totalSteps = TUTORIAL_STEPS.length;
|
|
123
|
+
const isFirst = step === 0;
|
|
124
|
+
const isLast = step === totalSteps - 1;
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<div className="px-5 py-4 flex flex-col min-h-[300px]">
|
|
128
|
+
{/* Step indicator */}
|
|
129
|
+
<div className="flex items-center justify-between mb-4">
|
|
130
|
+
<span className="text-xs font-medium" style={{ color: CAT.teal }}>
|
|
131
|
+
{step + 1} / {totalSteps}
|
|
132
|
+
</span>
|
|
133
|
+
<div className="flex gap-1">
|
|
134
|
+
{Array.from({ length: totalSteps }).map((_, i) => (
|
|
135
|
+
<div
|
|
136
|
+
key={i}
|
|
137
|
+
className="w-1.5 h-1.5 rounded-full transition-colors"
|
|
138
|
+
style={{
|
|
139
|
+
backgroundColor:
|
|
140
|
+
i === step ? CAT.green : i < step ? CAT.teal : CAT.surface,
|
|
141
|
+
}}
|
|
142
|
+
/>
|
|
143
|
+
))}
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
{/* Step title */}
|
|
148
|
+
<h3 className="text-base font-semibold text-zinc-900 mb-2">
|
|
149
|
+
{currentStep.title}
|
|
150
|
+
</h3>
|
|
151
|
+
|
|
152
|
+
{/* Step description */}
|
|
153
|
+
<p className="text-[13px] text-zinc-600 leading-relaxed mb-6">
|
|
154
|
+
{currentStep.description}
|
|
155
|
+
</p>
|
|
156
|
+
|
|
157
|
+
{/* Spacer */}
|
|
158
|
+
<div className="flex-1" />
|
|
159
|
+
|
|
160
|
+
{/* Navigation */}
|
|
161
|
+
<div className="flex items-center gap-2 pt-4 border-t border-zinc-100">
|
|
162
|
+
{isFirst ? (
|
|
163
|
+
<button
|
|
164
|
+
onClick={onEnd}
|
|
165
|
+
className="px-4 py-2 text-sm font-medium text-zinc-400 hover:text-zinc-600 rounded-lg hover:bg-zinc-50 transition-colors"
|
|
166
|
+
>
|
|
167
|
+
Skip
|
|
168
|
+
</button>
|
|
169
|
+
) : (
|
|
170
|
+
<button
|
|
171
|
+
onClick={onPrev}
|
|
172
|
+
className="px-4 py-2 text-sm font-medium rounded-lg hover:bg-zinc-50 transition-colors"
|
|
173
|
+
style={{ color: CAT.blue }}
|
|
174
|
+
>
|
|
175
|
+
{"Back"}
|
|
176
|
+
</button>
|
|
177
|
+
)}
|
|
178
|
+
<div className="flex-1" />
|
|
179
|
+
{isLast ? (
|
|
180
|
+
<button
|
|
181
|
+
onClick={onEnd}
|
|
182
|
+
className="px-4 py-2 text-sm font-medium text-white rounded-lg bg-emerald-500 hover:bg-emerald-600 transition-colors"
|
|
183
|
+
>
|
|
184
|
+
{"Done"}
|
|
185
|
+
</button>
|
|
186
|
+
) : (
|
|
187
|
+
<button
|
|
188
|
+
onClick={onNext}
|
|
189
|
+
className="px-4 py-2 text-sm font-medium text-white rounded-lg bg-emerald-500 hover:bg-emerald-600 transition-colors"
|
|
190
|
+
>
|
|
191
|
+
{"Next"}
|
|
192
|
+
</button>
|
|
193
|
+
)}
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* ------------------------------------------------------------------ */
|
|
200
|
+
/* Bullet with Catppuccin colored dot */
|
|
201
|
+
/* ------------------------------------------------------------------ */
|
|
202
|
+
|
|
203
|
+
function Bullet({ color, children }: { color: string; children: React.ReactNode }) {
|
|
204
|
+
return (
|
|
205
|
+
<li className="flex gap-2.5 items-start">
|
|
206
|
+
<span
|
|
207
|
+
className="w-1.5 h-1.5 rounded-full mt-[6px] shrink-0"
|
|
208
|
+
style={{ backgroundColor: color }}
|
|
209
|
+
/>
|
|
210
|
+
<span>{children}</span>
|
|
211
|
+
</li>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function SectionTitle({ children, color }: { children: React.ReactNode; color: string }) {
|
|
216
|
+
return (
|
|
217
|
+
<h3
|
|
218
|
+
className="text-[11px] font-semibold uppercase tracking-widest mb-2 mt-5 first:mt-0"
|
|
219
|
+
style={{ color }}
|
|
220
|
+
>
|
|
221
|
+
{children}
|
|
222
|
+
</h3>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* ------------------------------------------------------------------ */
|
|
227
|
+
/* Static help content */
|
|
228
|
+
/* ------------------------------------------------------------------ */
|
|
229
|
+
|
|
230
|
+
function HelpContent({ onStartTutorial }: { onStartTutorial?: () => void }) {
|
|
231
|
+
return (
|
|
232
|
+
<div className="px-5 py-4 text-[13px] text-zinc-600 leading-relaxed">
|
|
233
|
+
{/* Start Tutorial */}
|
|
234
|
+
{onStartTutorial && (
|
|
235
|
+
<button
|
|
236
|
+
onClick={onStartTutorial}
|
|
237
|
+
className="w-full flex items-center justify-center gap-2 px-4 py-2.5 mb-5 text-white text-sm font-medium rounded-lg bg-emerald-500 hover:bg-emerald-600 transition-colors"
|
|
238
|
+
>
|
|
239
|
+
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
|
|
240
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M12 18v-5.25m0 0a6.01 6.01 0 001.5-.189m-1.5.189a6.01 6.01 0 01-1.5-.189m3.75 7.478a12.06 12.06 0 01-4.5 0m3.75 2.383a14.406 14.406 0 01-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 10-7.517 0c.85.493 1.509 1.333 1.509 2.316V18" />
|
|
241
|
+
</svg>
|
|
242
|
+
Take the guided tour
|
|
243
|
+
</button>
|
|
244
|
+
)}
|
|
245
|
+
|
|
246
|
+
<p className="text-zinc-900 font-medium mb-1">
|
|
247
|
+
Your command center for AI coding tasks.
|
|
248
|
+
</p>
|
|
249
|
+
<p className="mb-4 text-zinc-500">
|
|
250
|
+
Heartbeads shows everything your AI agents are working on as a
|
|
251
|
+
live, interactive graph — tasks, dependencies, who's doing what, and
|
|
252
|
+
what's blocking progress.
|
|
253
|
+
</p>
|
|
254
|
+
|
|
255
|
+
<SectionTitle color={CAT.red}>The graph</SectionTitle>
|
|
256
|
+
<p className="mb-2">
|
|
257
|
+
Each <strong>circle</strong> is a task. <strong>Flowing particles</strong>{" "}
|
|
258
|
+
stream between them to show dependency direction.
|
|
259
|
+
</p>
|
|
260
|
+
<ul className="space-y-1.5 mb-3">
|
|
261
|
+
<Bullet color={CAT.red}><strong>Bigger circles</strong> — more connected, more important</Bullet>
|
|
262
|
+
<Bullet color={CAT.red}><strong>Solid lines + particles</strong> — “blocks” (A must finish before B)</Bullet>
|
|
263
|
+
<Bullet color={CAT.red}><strong>Dashed lines</strong> — parent-child grouping</Bullet>
|
|
264
|
+
<Bullet color={CAT.red}><strong>Colored ring</strong> — which project it belongs to</Bullet>
|
|
265
|
+
</ul>
|
|
266
|
+
|
|
267
|
+
<SectionTitle color={CAT.blue}>Navigation</SectionTitle>
|
|
268
|
+
<ul className="space-y-1.5 mb-3">
|
|
269
|
+
<Bullet color={CAT.blue}><strong>Click</strong> a node to open its details</Bullet>
|
|
270
|
+
<Bullet color={CAT.blue}><strong>Hover</strong> for a quick summary</Bullet>
|
|
271
|
+
<Bullet color={CAT.blue}><strong>Right-click</strong> for actions — descriptions, comments, claims, collapse, or focus on an epic</Bullet>
|
|
272
|
+
<Bullet color={CAT.blue}><strong>Scroll</strong> to zoom, <strong>drag</strong> to pan</Bullet>
|
|
273
|
+
<Bullet color={CAT.blue}><strong>Cmd/Ctrl+F</strong> to search by name, ID, owner, or commenter</Bullet>
|
|
274
|
+
</ul>
|
|
275
|
+
|
|
276
|
+
<SectionTitle color={CAT.teal}>Layouts</SectionTitle>
|
|
277
|
+
<p className="mb-2">
|
|
278
|
+
Top-left buttons rearrange the graph:
|
|
279
|
+
</p>
|
|
280
|
+
<ul className="space-y-1.5 mb-3">
|
|
281
|
+
<Bullet color={CAT.teal}><strong>Force</strong> — organic, physics-based</Bullet>
|
|
282
|
+
<Bullet color={CAT.teal}><strong>DAG</strong> — clean top-down tree</Bullet>
|
|
283
|
+
<Bullet color={CAT.teal}><strong>Radial</strong> — rings from center outward</Bullet>
|
|
284
|
+
<Bullet color={CAT.teal}><strong>Cluster</strong> — grouped by project</Bullet>
|
|
285
|
+
<Bullet color={CAT.teal}><strong>Spread</strong> — spaced out for screenshots</Bullet>
|
|
286
|
+
</ul>
|
|
287
|
+
|
|
288
|
+
<SectionTitle color={CAT.peach}>Color modes</SectionTitle>
|
|
289
|
+
<p className="mb-2">
|
|
290
|
+
Bottom-right panel — paint nodes by:
|
|
291
|
+
</p>
|
|
292
|
+
<ul className="space-y-1.5 mb-3">
|
|
293
|
+
<Bullet color={CAT.peach}><strong>Status</strong> — open, in progress, blocked, closed</Bullet>
|
|
294
|
+
<Bullet color={CAT.peach}><strong>Priority</strong> — P0 critical to P4 backlog</Bullet>
|
|
295
|
+
<Bullet color={CAT.peach}><strong>Owner</strong> — who created it</Bullet>
|
|
296
|
+
<Bullet color={CAT.peach}><strong>Assignee</strong> — who's working on it</Bullet>
|
|
297
|
+
<Bullet color={CAT.peach}><strong>Prefix</strong> — which project</Bullet>
|
|
298
|
+
</ul>
|
|
299
|
+
|
|
300
|
+
<SectionTitle color={CAT.mauve}>More</SectionTitle>
|
|
301
|
+
<ul className="space-y-1.5 mb-3">
|
|
302
|
+
<Bullet color={CAT.mauve}><strong>Collapse/Expand</strong> — tidy up epics into single nodes</Bullet>
|
|
303
|
+
<Bullet color={CAT.mauve}><strong>Focus on epic</strong> — right-click an epic to isolate its subgraph (children + dependencies). Click the banner or right-click again to return</Bullet>
|
|
304
|
+
<Bullet color={CAT.mauve}><strong>Clusters</strong> — dashed circles grouping projects when zoomed out</Bullet>
|
|
305
|
+
<Bullet color={CAT.mauve}><strong>Replay</strong> — step through your project's history</Bullet>
|
|
306
|
+
<Bullet color={CAT.mauve}><strong>Comments</strong> — leave notes on tasks (sign in first)</Bullet>
|
|
307
|
+
<Bullet color={CAT.mauve}><strong>Activity feed</strong> — real-time feed filtered to only your local beads, not global</Bullet>
|
|
308
|
+
<Bullet color={CAT.mauve}><strong>Claim tasks</strong> — right-click to mark as yours</Bullet>
|
|
309
|
+
<Bullet color={CAT.mauve}><strong>Minimap</strong> — click to jump, drag edges to resize</Bullet>
|
|
310
|
+
<Bullet color={CAT.mauve}><strong>Auto-fit</strong> — top-left toggle to lock/unlock automatic camera reframing</Bullet>
|
|
311
|
+
<Bullet color={CAT.mauve}><strong>Pulse</strong> — the most recently active node pulses with emerald ripples. Toggle it on/off in view controls</Bullet>
|
|
312
|
+
<Bullet color={CAT.mauve}><strong>Copy</strong> — clipboard icon copies task with project info</Bullet>
|
|
313
|
+
</ul>
|
|
314
|
+
|
|
315
|
+
<div className="mt-5 pt-4 border-t border-zinc-100 text-xs text-zinc-400">
|
|
316
|
+
Built with{" "}
|
|
317
|
+
<a
|
|
318
|
+
href="https://github.com/GainForest/beads"
|
|
319
|
+
target="_blank"
|
|
320
|
+
rel="noopener noreferrer"
|
|
321
|
+
className="underline underline-offset-2 transition-colors"
|
|
322
|
+
style={{ color: CAT.green }}
|
|
323
|
+
>
|
|
324
|
+
beads
|
|
325
|
+
</a>{" "}
|
|
326
|
+
— open-source issue tracking for AI-native development. Heartbeads is built for the GainForest agentic workflow and open-sourced at{" "}
|
|
327
|
+
<a
|
|
328
|
+
href="https://github.com/daviddao/heartbeads"
|
|
329
|
+
target="_blank"
|
|
330
|
+
rel="noopener noreferrer"
|
|
331
|
+
className="underline underline-offset-2 transition-colors"
|
|
332
|
+
style={{ color: CAT.green }}
|
|
333
|
+
>
|
|
334
|
+
github.com/daviddao/heartbeads
|
|
335
|
+
</a>.
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
);
|
|
339
|
+
}
|