Skip to content

Implement Repo UI - Same URI format like Github (https://<url>/<ownerSlug>/<repoSlug>). (#25) #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {};
const nextConfig = {
transpilePackages: ['next-mdx-remote'],
}

export default nextConfig;
export default nextConfig
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,17 @@
"@google/generative-ai": "^0.21.0",
"@langchain/core": "^0.3.26",
"axios": "^1.7.9",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dotenv": "^16.4.7",
"langchain": "^0.3.7",
"lucide-react": "^0.469.0",
"next": "15.1.0",
"next-mdx-remote": "^5.0.0",
"pg": "^8.13.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
"react-dom": "^19.0.0",
"tailwind-merge": "^2.6.0"
},
"devDependencies": {
"@babel/cli": "^7.26.4",
Expand Down
34 changes: 4 additions & 30 deletions src/agent/structured-output/schema-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,36 +49,10 @@ File Schema Example Output:
summary: z
.string()
.describe(
'Summary of the file, its main purpose, and its role in the project. Include Markdown links to important code blocks within this file using the format `[{Description of Code Block}]({Full github url of the file including the start line with optional ending line}#L{startLine}-L{endLine})` where applicable.'
),
relevantCodeBlocks: z
.array(
z.object({
name: z
.string()
.describe(
'Name or identifier of the code block (e.g., function name, class name, key variable).'
),
description: z
.string()
.describe(
'Description of the code block and its significance within the file.'
),
startLine: z
.number()
.describe(
'Starting line number of the code block.'
),
endLine: z
.number()
.describe(
'Ending line number of the code block.'
),
})
)
.optional()
.describe(
'List of important code blocks (functions, classes, key sections) within the file, with line numbers.'
'Summary of the file talking about its main purpose, and its role in the project.\n'
+ 'Include Markdown links to important code blocks within this file using the format\n`'
+ '[{Description of Code Block}]({Full github url of the file including the start line with optional ending line}#L{startLine}-L{endLine})` where applicable.\n'
+ 'Also you should not return more than 2-3 paragraphs of summary.'
),
})

Expand Down
16 changes: 16 additions & 0 deletions src/app/[ownerSlug]/[repoSlug]/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Skeleton } from '@/components/ui/skeleton'

export default function Loading() {
return (
<div className="space-y-4">
<Skeleton className="h-8 w-[250px]" />
<Skeleton className="h-4 w-[200px]" />
<Skeleton className="h-4 w-[150px]" />
<div className="space-y-2">
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-2/3" />
</div>
</div>
)
}
60 changes: 60 additions & 0 deletions src/app/[ownerSlug]/[repoSlug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { JSX, Suspense } from 'react'
import { RepoCard } from '@/components/RepoCard'
import Loading from '@/app/[ownerSlug]/[repoSlug]/loading'
import { FetchRepoService, FullRepository } from '@/db/get-db'
import { MarkdownContent } from '@/components/MarkdownContent'
import { notFound } from 'next/navigation'

interface PageProps {
params: {
ownerSlug: string
repoSlug: string
}
}

interface RepoPageProps {
ownerSlug: string
repoSlug: string
}

const placeholder = `
# parser.cpp\n\n

- Reference \`parser/parser.cpp\`\n

This file defines the \`Parser\` class, responsible for transforming a stream of tokens into an Abstract Syntax Tree (AST). The parser utilizes a queue of \`TokenPtr\` objects (\`tok_queue_\`) and provides methods for consuming tokens (\`Eat\`), peeking at the next token (\`Peek\`), and expecting specific token types (\`ExpectedTokenType\`). The core functionality resides in \`ProduceAST\` which drives the parsing process by repeatedly calling \`ParseStatement\` until an end-of-line token is encountered. Different parsing methods are present to handle various expressions such as \`ParsePrimaryExpression\`, \`ParseAdditionExpression\`, \`ParseMultiplicationExpression\`, and \`ParseComparisonExpression\`. It supports variable declarations and assignments, and also handles whitespace using \`ParseWhitespaceExpression\`. The parser uses recursive descent parsing strategy with helper functions for each type of expression. It throws \`UnexpectedTokenParsedException\` when unexpected tokens are encountered. The \`Parser\` class depends on the \`TokenPtr\` and the \`ast.hpp\` module for the AST node definitions. Key methods include: [\`Eat\`](https://github.com/daeisbae/AParser/blob/9bbea84efa9f8eeed5576f53e4a65ca87a7f023c/parser/parser.cpp#L11-L15) for consuming tokens, [\`Peek\`](https://github.com/daeisbae/AParser/blob/9bbea84efa9f8eeed5576f53e4a65ca87a7f023c/parser/parser.cpp#L17-L17) for peeking at tokens, [\`ExpectedTokenType\`](https://github.com/daeisbae/AParser/blob/9bbea84efa9f8eeed5576f53e4a65ca87a7f023c/parser/parser.cpp#L19-L39) for validating token types, [\`ProduceAST\`](https://github.com/daeisbae/AParser/blob/9bbea84efa9f8eeed5576f53e4a65ca87a7f023c/parser/parser.cpp#L41-L53) for generating the AST, [\`ParseStatement\`](https://github.com/daeisbae/AParser/blob/9bbea84efa9f8eeed5576f53e4a65ca87a7f023c/parser/parser.cpp#L55-L62) for parsing statements, and [\`ParseExpression\`](https://github.com/daeisbae/AParser/blob/9bbea84efa9f8eeed5576f53e4a65ca87a7f023c/parser/parser.cpp#L64-L66) for parsing expressions. The file also handles different types of expressions using separate parsing methods like [\`ParsePrimaryExpression\`](https://github.com/daeisbae/AParser/blob/9bbea84efa9f8eeed5576f53e4a65ca87a7f023c/parser/parser.cpp#L68-L145), [\`ParseAdditionExpression\`](https://github.com/daeisbae/AParser/blob/9bbea84efa9f8eeed5576f53e4a65ca87a7f023c/parser/parser.cpp#L147-L165), [\`ParseMultiplicationExpression\`](https://github.com/daeisbae/AParser/blob/9bbea84efa9f8eeed5576f53e4a65ca87a7f023c/parser/parser.cpp#L167-L185), [\`ParseWhitespaceExpression\`](https://github.com/daeisbae/AParser/blob/9bbea84efa9f8eeed5576f53e4a65ca87a7f023c/parser/parser.cpp#L187-L192), [\`ParseIdentifierDeclarationExpression\`](https://github.com/daeisbae/AParser/blob/9bbea84efa9f8eeed5576f53e4a65ca87a7f023c/parser/parser.cpp#L194-L217), [\`ParseIdentifierAssignmentExpression\`](https://github.com/daeisbae/AParser/blob/9bbea84efa9f8eeed5576f53e4a65ca87a7f023c/parser/parser.cpp#L219-L243), and [\`ParseComparisonExpression\`](https://github.com/daeisbae/AParser/blob/9bbea84efa9f8eeed5576f53e4a65ca87a7f023c/parser/parser.cpp#L245-L263).
`

async function RepoPage({
ownerSlug,
repoSlug,
}: RepoPageProps): Promise<JSX.Element> {
const fetchRepoService = new FetchRepoService()
const repoDetails: FullRepository | null =
await fetchRepoService.getFullRepositoryTree(ownerSlug, repoSlug)

if (!repoDetails) {
notFound()
}

return (
<div className="flex gap-6 p-6">
<div className="flex-1">
<MarkdownContent content={placeholder} />
</div>
<div className="w-[300px]">
<RepoCard repoInfo={repoDetails.repository} />
</div>
</div>
)
}

export default function DocumentationPage({ params }: PageProps) {
const { ownerSlug, repoSlug } = params

return (
<Suspense fallback={<Loading />}>
<RepoPage ownerSlug={ownerSlug} repoSlug={repoSlug} />
</Suspense>
)
}
49 changes: 49 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

body {
font-family: Arial, Helvetica, sans-serif;
}

@layer base {
:root {
--radius: 0.5rem;
}
}

h1 {
font-size: 48px;
font-weight: bold;
}

h2 {
font-size: 40px;
font-weight: bold;
}

h3 {
font-size: 32px;
font-weight: bold;
}

h4 {
font-size: 24px;
font-weight: bold;
}

h5 {
font-size: 20px;
font-weight: bold;
}

ul > li {
padding: 20px 0px;
}

a > code {
background-color: lightblue;
color:blue;
padding: 2px 4px;
border-radius: 4px;
}
22 changes: 22 additions & 0 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import './globals.css'
import Navbar from '@/components/NavBar'

export const metadata = {
title: 'OpenRepoWiki',
description: 'A Wikipedia of Github Repositories of how it is made.',
}

interface RootLayoutProps {
children: React.ReactNode
}

export default function RootLayout({ children } : RootLayoutProps) {
return (
<html lang="en">
<body className="bg-white">
<Navbar />
<main className="max-w-7xl mx-auto p-6">{children}</main>
</body>
</html>
)
}
33 changes: 33 additions & 0 deletions src/components/MarkdownContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use client'

import { MDXRemote } from 'next-mdx-remote'
import { serialize } from 'next-mdx-remote/serialize'
import { useState, useEffect } from 'react'
import React from 'react'
import { Skeleton } from '@/components/ui/skeleton'

interface MarkdownContentProps {
content: string
}

export function MarkdownContent({ content }: MarkdownContentProps) {
const [serializedContent, setSerializedContent] = useState<any>(null)

useEffect(() => {
const serializeMarkdown = async () => {
const serialized = await serialize(content)
setSerializedContent(serialized)
}
serializeMarkdown()
}, [content])

if (!serializedContent) {
return <Skeleton className="h-[200px] w-full" />
}

return (
<div className="prose dark:prose-invert max-w-none">
<MDXRemote {...serializedContent} />
</div>
)
}
64 changes: 64 additions & 0 deletions src/components/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as React from 'react'
import Link from 'next/link'

import { cn } from '@/lib/utils'

import {
NavigationMenu,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
navigationMenuTriggerStyle,
} from '@/components/ui/navigation-menu'

export default function Navbar() {
return (
<div className="border-b">
<div className="flex h-16 items-center px-4">
<Link href="/" className="mr-6 flex items-center space-x-2">
<span className="text-xl font-bold">OpenRepoWiki</span>
</Link>
<NavigationMenu className="hidden md:flex">
<NavigationMenuList>
<NavigationMenuItem>
<Link href="/repositories" legacyBehavior passHref>
<NavigationMenuLink
className={cn(
navigationMenuTriggerStyle(),
'w-28'
)}
>
Repo
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href="/blogs" legacyBehavior passHref>
<NavigationMenuLink
className={cn(
navigationMenuTriggerStyle(),
'w-28'
)}
>
Blogs
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
<NavigationMenuItem>
<Link href="/about" legacyBehavior passHref>
<NavigationMenuLink
className={cn(
navigationMenuTriggerStyle(),
'w-28 h-auto'
)}
>
About
</NavigationMenuLink>
</Link>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
</div>
</div>
)
}
66 changes: 66 additions & 0 deletions src/components/RepoCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Card, CardHeader, CardContent, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Separator } from '@/components/ui/separator'
import { Star, GitFork } from 'lucide-react'
import React from 'react'

export interface RepoDetails {
owner: string
repo: string
descriptions: string
stars: number
forks: number
url: string
default_branch: string
topics: string[]
}

export interface RepoCardProps {
repoInfo: RepoDetails
}

export function RepoCard({ repoInfo }: RepoCardProps) {
return (
<Card>
<CardHeader>
<CardTitle>
{repoInfo.owner}/{repoInfo.repo}
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground mb-4">
{repoInfo.descriptions}
</p>
<div className="flex items-center gap-4 mb-4">
<div className="flex items-center">
<Star className="w-4 h-4 mr-1" />
<span>{repoInfo.stars}</span>
</div>
<div className="flex items-center">
<GitFork className="w-4 h-4 mr-1" />
<span>{repoInfo.forks}</span>
</div>
</div>
<a
href={repoInfo.url}
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
View on GitHub
</a>
<Separator className="my-4" />
<div>
<h4 className="font-semibold mb-2">Topics</h4>
<div className="flex flex-wrap gap-2">
{repoInfo.topics.map((topic, index) => (
<Badge key={index} variant="secondary">
{topic}
</Badge>
))}
</div>
</div>
</CardContent>
</Card>
)
}
Loading
Loading