beads-map 0.1.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 +27 -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 +10 -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/config.body +1 -0
- package/.next/server/app/api/config.meta +1 -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/index.html +1 -0
- package/.next/server/app/index.meta +5 -0
- package/.next/server/app/index.rsc +8 -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-paths-manifest.json +14 -0
- package/.next/server/chunks/247.js +12 -0
- package/.next/server/chunks/251.js +2 -0
- package/.next/server/chunks/29.js +1 -0
- package/.next/server/chunks/343.js +1 -0
- package/.next/server/chunks/533.js +38 -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/font-manifest.json +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 +6 -0
- package/.next/server/middleware-react-loadable-manifest.js +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/99eOjoTtoO32H-c1faxZ5/_buildManifest.js +1 -0
- package/.next/static/99eOjoTtoO32H-c1faxZ5/_ssgManifest.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/666-fb778298a77f3754.js +1 -0
- package/.next/static/chunks/945-bf736d0119e7437b.js +2 -0
- package/.next/static/chunks/app/_not-found/page-b568fd9238f85f27.js +1 -0
- package/.next/static/chunks/app/layout-13e3cdaaa416edb6.js +1 -0
- package/.next/static/chunks/app/page-49d569c912d5af9d.js +1 -0
- package/.next/static/chunks/framework-6e06c675866dc992.js +1 -0
- package/.next/static/chunks/main-62aa0e18004db880.js +1 -0
- package/.next/static/chunks/main-app-8b0c4a1007dbb7f4.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-c8b9ebfd35ae1d92.js +1 -0
- package/.next/static/css/10ef08b24212fe36.css +3 -0
- package/README.md +243 -0
- package/app/api/beads/route.ts +27 -0
- package/app/api/beads/stream/route.ts +83 -0
- package/app/api/config/route.ts +46 -0
- package/app/api/login/route.ts +42 -0
- package/app/api/logout/route.ts +14 -0
- package/app/api/oauth/callback/route.ts +94 -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/globals.css +192 -0
- package/app/layout.tsx +30 -0
- package/app/page.tsx +1151 -0
- package/bin/beads-map.mjs +175 -0
- package/components/AllCommentsPanel.tsx +265 -0
- package/components/AuthButton.tsx +197 -0
- package/components/BeadsGraph.tsx +1539 -0
- package/components/CommentTooltip.tsx +310 -0
- package/components/GraphStats.tsx +121 -0
- package/components/HeartIcon.tsx +33 -0
- package/components/NodeDetail.tsx +741 -0
- package/components/StatusLegend.tsx +99 -0
- package/components/TimelineBar.tsx +116 -0
- package/hooks/useBeadsComments.ts +412 -0
- package/lib/agent.ts +29 -0
- package/lib/auth/client.ts +221 -0
- package/lib/auth.tsx +159 -0
- package/lib/diff-beads.ts +125 -0
- package/lib/discover.ts +228 -0
- package/lib/env.ts +28 -0
- package/lib/parse-beads.ts +232 -0
- package/lib/session.ts +52 -0
- package/lib/timeline.ts +138 -0
- package/lib/types.ts +202 -0
- package/lib/utils.ts +25 -0
- package/lib/watch-beads.ts +97 -0
- package/next.config.mjs +4 -0
- package/package.json +75 -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,168 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { getAuthenticatedAgent } from "@/lib/agent";
|
|
3
|
+
import { getSession } from "@/lib/session";
|
|
4
|
+
|
|
5
|
+
export const dynamic = "force-dynamic";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* POST /api/records — Create a new record
|
|
9
|
+
* Body: { collection, rkey?, record }
|
|
10
|
+
*/
|
|
11
|
+
export async function POST(request: NextRequest) {
|
|
12
|
+
try {
|
|
13
|
+
const session = await getSession();
|
|
14
|
+
if (!session.did) {
|
|
15
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const agent = await getAuthenticatedAgent();
|
|
19
|
+
if (!agent) {
|
|
20
|
+
return NextResponse.json(
|
|
21
|
+
{ error: "Failed to authenticate" },
|
|
22
|
+
{ status: 401 }
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const body = await request.json();
|
|
27
|
+
const { collection, rkey, record } = body;
|
|
28
|
+
|
|
29
|
+
if (!collection || typeof collection !== "string") {
|
|
30
|
+
return NextResponse.json(
|
|
31
|
+
{ error: "Collection is required" },
|
|
32
|
+
{ status: 400 }
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (!record || typeof record !== "object") {
|
|
36
|
+
return NextResponse.json(
|
|
37
|
+
{ error: "Record is required" },
|
|
38
|
+
{ status: 400 }
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const res = await agent.com.atproto.repo.createRecord({
|
|
43
|
+
repo: session.did,
|
|
44
|
+
collection,
|
|
45
|
+
rkey: rkey || undefined,
|
|
46
|
+
record,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return NextResponse.json({
|
|
50
|
+
success: true,
|
|
51
|
+
uri: res.data.uri,
|
|
52
|
+
cid: res.data.cid,
|
|
53
|
+
});
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error("Failed to create record:", error);
|
|
56
|
+
const message =
|
|
57
|
+
error instanceof Error ? error.message : "Failed to create record";
|
|
58
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* PUT /api/records — Update an existing record
|
|
64
|
+
* Body: { collection, rkey, record }
|
|
65
|
+
*/
|
|
66
|
+
export async function PUT(request: NextRequest) {
|
|
67
|
+
try {
|
|
68
|
+
const session = await getSession();
|
|
69
|
+
if (!session.did) {
|
|
70
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const agent = await getAuthenticatedAgent();
|
|
74
|
+
if (!agent) {
|
|
75
|
+
return NextResponse.json(
|
|
76
|
+
{ error: "Failed to authenticate" },
|
|
77
|
+
{ status: 401 }
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const body = await request.json();
|
|
82
|
+
const { collection, rkey, record } = body;
|
|
83
|
+
|
|
84
|
+
if (!collection || typeof collection !== "string") {
|
|
85
|
+
return NextResponse.json(
|
|
86
|
+
{ error: "Collection is required" },
|
|
87
|
+
{ status: 400 }
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (!rkey || typeof rkey !== "string") {
|
|
91
|
+
return NextResponse.json(
|
|
92
|
+
{ error: "Record key is required" },
|
|
93
|
+
{ status: 400 }
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
if (!record || typeof record !== "object") {
|
|
97
|
+
return NextResponse.json(
|
|
98
|
+
{ error: "Record is required" },
|
|
99
|
+
{ status: 400 }
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
await agent.com.atproto.repo.putRecord({
|
|
104
|
+
repo: session.did,
|
|
105
|
+
collection,
|
|
106
|
+
rkey,
|
|
107
|
+
record,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return NextResponse.json({ success: true });
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error("Failed to update record:", error);
|
|
113
|
+
const message =
|
|
114
|
+
error instanceof Error ? error.message : "Failed to update record";
|
|
115
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* DELETE /api/records?collection=...&rkey=...
|
|
121
|
+
* Deletes a record from the authenticated user's repo.
|
|
122
|
+
*/
|
|
123
|
+
export async function DELETE(request: NextRequest) {
|
|
124
|
+
try {
|
|
125
|
+
const session = await getSession();
|
|
126
|
+
if (!session.did) {
|
|
127
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const agent = await getAuthenticatedAgent();
|
|
131
|
+
if (!agent) {
|
|
132
|
+
return NextResponse.json(
|
|
133
|
+
{ error: "Failed to authenticate" },
|
|
134
|
+
{ status: 401 }
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const { searchParams } = new URL(request.url);
|
|
139
|
+
const collection = searchParams.get("collection");
|
|
140
|
+
const rkey = searchParams.get("rkey");
|
|
141
|
+
|
|
142
|
+
if (!collection) {
|
|
143
|
+
return NextResponse.json(
|
|
144
|
+
{ error: "Collection is required" },
|
|
145
|
+
{ status: 400 }
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
if (!rkey) {
|
|
149
|
+
return NextResponse.json(
|
|
150
|
+
{ error: "Record key is required" },
|
|
151
|
+
{ status: 400 }
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
await agent.com.atproto.repo.deleteRecord({
|
|
156
|
+
repo: session.did,
|
|
157
|
+
collection,
|
|
158
|
+
rkey,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return NextResponse.json({ success: true });
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error("Failed to delete record:", error);
|
|
164
|
+
const message =
|
|
165
|
+
error instanceof Error ? error.message : "Failed to delete record";
|
|
166
|
+
return NextResponse.json({ error: message }, { status: 500 });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { getSession } from "@/lib/session";
|
|
3
|
+
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
|
|
6
|
+
export async function GET() {
|
|
7
|
+
try {
|
|
8
|
+
const session = await getSession();
|
|
9
|
+
|
|
10
|
+
if (!session.did) {
|
|
11
|
+
return NextResponse.json({ authenticated: false });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return NextResponse.json({
|
|
15
|
+
authenticated: true,
|
|
16
|
+
did: session.did,
|
|
17
|
+
handle: session.handle,
|
|
18
|
+
displayName: session.displayName,
|
|
19
|
+
avatar: session.avatar,
|
|
20
|
+
});
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.error("Status check failed:", error);
|
|
23
|
+
return NextResponse.json({ authenticated: false });
|
|
24
|
+
}
|
|
25
|
+
}
|
package/app/globals.css
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
@tailwind base;
|
|
2
|
+
@tailwind components;
|
|
3
|
+
@tailwind utilities;
|
|
4
|
+
|
|
5
|
+
/* ============================================================================
|
|
6
|
+
Beads Map - Custom Styles
|
|
7
|
+
Design: Clean, minimal graph visualization
|
|
8
|
+
============================================================================ */
|
|
9
|
+
|
|
10
|
+
@layer base {
|
|
11
|
+
html,
|
|
12
|
+
body {
|
|
13
|
+
@apply h-full overflow-hidden;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
body {
|
|
17
|
+
@apply bg-white text-zinc-800 antialiased;
|
|
18
|
+
font-feature-settings: "rlig" 1, "calt" 1;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@layer components {
|
|
23
|
+
/* Card with subtle hover lift */
|
|
24
|
+
.card-lift {
|
|
25
|
+
@apply transition-all duration-200 ease-out;
|
|
26
|
+
}
|
|
27
|
+
.card-lift:hover {
|
|
28
|
+
@apply shadow-md -translate-y-0.5;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Status badge base */
|
|
32
|
+
.status-badge {
|
|
33
|
+
@apply inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Scrollbar styling */
|
|
37
|
+
.custom-scrollbar::-webkit-scrollbar {
|
|
38
|
+
width: 4px;
|
|
39
|
+
}
|
|
40
|
+
.custom-scrollbar::-webkit-scrollbar-track {
|
|
41
|
+
@apply bg-transparent;
|
|
42
|
+
}
|
|
43
|
+
.custom-scrollbar::-webkit-scrollbar-thumb {
|
|
44
|
+
@apply bg-zinc-200 rounded-full;
|
|
45
|
+
}
|
|
46
|
+
.custom-scrollbar::-webkit-scrollbar-thumb:hover {
|
|
47
|
+
@apply bg-zinc-300;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@layer utilities {
|
|
52
|
+
/* Fade in animation */
|
|
53
|
+
@keyframes fadeIn {
|
|
54
|
+
from {
|
|
55
|
+
opacity: 0;
|
|
56
|
+
transform: translateY(8px);
|
|
57
|
+
}
|
|
58
|
+
to {
|
|
59
|
+
opacity: 1;
|
|
60
|
+
transform: translateY(0);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
.animate-fade-in {
|
|
64
|
+
animation: fadeIn 0.3s ease-out forwards;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Pulse subtle */
|
|
68
|
+
@keyframes pulseSoft {
|
|
69
|
+
0%,
|
|
70
|
+
100% {
|
|
71
|
+
opacity: 1;
|
|
72
|
+
}
|
|
73
|
+
50% {
|
|
74
|
+
opacity: 0.6;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
.animate-pulse-soft {
|
|
78
|
+
animation: pulseSoft 2s ease-in-out infinite;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* Markdown prose inside description box */
|
|
83
|
+
.description-markdown h1,
|
|
84
|
+
.description-markdown h2,
|
|
85
|
+
.description-markdown h3,
|
|
86
|
+
.description-markdown h4 {
|
|
87
|
+
@apply font-semibold text-zinc-700 mt-2 mb-1;
|
|
88
|
+
}
|
|
89
|
+
.description-markdown h1 { font-size: 0.85rem; }
|
|
90
|
+
.description-markdown h2 { font-size: 0.8rem; }
|
|
91
|
+
.description-markdown h3 { font-size: 0.75rem; }
|
|
92
|
+
.description-markdown h4 { font-size: 0.7rem; }
|
|
93
|
+
|
|
94
|
+
.description-markdown p {
|
|
95
|
+
@apply mb-1.5;
|
|
96
|
+
}
|
|
97
|
+
.description-markdown p:last-child {
|
|
98
|
+
@apply mb-0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.description-markdown ul,
|
|
102
|
+
.description-markdown ol {
|
|
103
|
+
@apply pl-4 mb-1.5 space-y-0.5;
|
|
104
|
+
}
|
|
105
|
+
.description-markdown ul { @apply list-disc; }
|
|
106
|
+
.description-markdown ol { @apply list-decimal; }
|
|
107
|
+
|
|
108
|
+
.description-markdown code {
|
|
109
|
+
@apply bg-zinc-200/60 text-zinc-700 px-1 py-0.5 rounded text-[10px] font-mono;
|
|
110
|
+
}
|
|
111
|
+
.description-markdown pre {
|
|
112
|
+
@apply bg-zinc-200/60 rounded-md p-2 mb-1.5 overflow-x-auto;
|
|
113
|
+
}
|
|
114
|
+
.description-markdown pre code {
|
|
115
|
+
@apply bg-transparent p-0 text-[10px];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.description-markdown a {
|
|
119
|
+
@apply text-emerald-600 underline underline-offset-2 hover:text-emerald-700;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.description-markdown blockquote {
|
|
123
|
+
@apply border-l-2 border-zinc-300 pl-2 text-zinc-500 italic my-1.5;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.description-markdown table {
|
|
127
|
+
@apply w-full text-[10px] border-collapse mb-1.5;
|
|
128
|
+
}
|
|
129
|
+
.description-markdown th {
|
|
130
|
+
@apply text-left font-semibold text-zinc-600 border-b border-zinc-200 pb-0.5 pr-2;
|
|
131
|
+
}
|
|
132
|
+
.description-markdown td {
|
|
133
|
+
@apply border-b border-zinc-100 py-0.5 pr-2;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.description-markdown hr {
|
|
137
|
+
@apply border-zinc-200 my-2;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.description-markdown strong {
|
|
141
|
+
@apply font-semibold text-zinc-700;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.description-markdown img {
|
|
145
|
+
@apply max-w-full rounded;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Timeline slider custom styling */
|
|
149
|
+
.timeline-slider {
|
|
150
|
+
-webkit-appearance: none;
|
|
151
|
+
appearance: none;
|
|
152
|
+
background: transparent;
|
|
153
|
+
cursor: pointer;
|
|
154
|
+
}
|
|
155
|
+
.timeline-slider::-webkit-slider-runnable-track {
|
|
156
|
+
height: 4px;
|
|
157
|
+
background: #e4e4e7; /* zinc-200 */
|
|
158
|
+
border-radius: 2px;
|
|
159
|
+
}
|
|
160
|
+
.timeline-slider::-webkit-slider-thumb {
|
|
161
|
+
-webkit-appearance: none;
|
|
162
|
+
width: 12px;
|
|
163
|
+
height: 12px;
|
|
164
|
+
background: #10b981; /* emerald-500 */
|
|
165
|
+
border-radius: 50%;
|
|
166
|
+
margin-top: -4px;
|
|
167
|
+
cursor: pointer;
|
|
168
|
+
transition: transform 0.1s ease;
|
|
169
|
+
}
|
|
170
|
+
.timeline-slider::-webkit-slider-thumb:hover {
|
|
171
|
+
transform: scale(1.2);
|
|
172
|
+
}
|
|
173
|
+
.timeline-slider::-moz-range-track {
|
|
174
|
+
height: 4px;
|
|
175
|
+
background: #e4e4e7;
|
|
176
|
+
border-radius: 2px;
|
|
177
|
+
border: none;
|
|
178
|
+
}
|
|
179
|
+
.timeline-slider::-moz-range-thumb {
|
|
180
|
+
width: 12px;
|
|
181
|
+
height: 12px;
|
|
182
|
+
background: #10b981;
|
|
183
|
+
border-radius: 50%;
|
|
184
|
+
border: none;
|
|
185
|
+
cursor: pointer;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/* Force-graph container overrides */
|
|
189
|
+
.force-graph-container canvas {
|
|
190
|
+
@apply rounded-lg;
|
|
191
|
+
touch-action: none; /* Prevent browser gestures from conflicting with graph pan/zoom */
|
|
192
|
+
}
|
package/app/layout.tsx
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Metadata, Viewport } from "next";
|
|
2
|
+
import { AuthProvider } from "@/lib/auth";
|
|
3
|
+
import "./globals.css";
|
|
4
|
+
|
|
5
|
+
export const metadata: Metadata = {
|
|
6
|
+
title: "Beads Map",
|
|
7
|
+
description:
|
|
8
|
+
"Interactive dependency graph viewer for beads issues",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const viewport: Viewport = {
|
|
12
|
+
width: "device-width",
|
|
13
|
+
initialScale: 1,
|
|
14
|
+
maximumScale: 1,
|
|
15
|
+
userScalable: false,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default function RootLayout({
|
|
19
|
+
children,
|
|
20
|
+
}: {
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
}) {
|
|
23
|
+
return (
|
|
24
|
+
<html lang="en">
|
|
25
|
+
<body>
|
|
26
|
+
<AuthProvider>{children}</AuthProvider>
|
|
27
|
+
</body>
|
|
28
|
+
</html>
|
|
29
|
+
);
|
|
30
|
+
}
|