Skip to content
Open
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
5 changes: 4 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX
NEXT_PUBLIC_ENV=development/production

# Koala Analytics
NEXT_PUBLIC_KOALA_PUBLIC_API_KEY=pk_xxxxxxxxxxxxxxxxxxxxxxxx
NEXT_PUBLIC_KOALA_PUBLIC_API_KEY=pk_xxxxxxxxxxxxxxxxxxxxxxxx

# Tavily AI Search API Key (get from https://tavily.com)
TAVILY_API_KEY=tvly-XXXXXXXXXX
1,156 changes: 215 additions & 941 deletions .source/index.ts

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions .source/source.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// source.config.ts
import {
defineConfig,
defineDocs,
frontmatterSchema,
metaSchema
} from "fumadocs-mdx/config";
import { remarkAdmonition } from "fumadocs-core/mdx-plugins";
var docs = defineDocs({
// The root directory for all documentation
dir: "content/docs",
docs: {
schema: frontmatterSchema
},
meta: {
schema: metaSchema
}
});
var releaseNotes = defineDocs({
// The root directory for release notes
dir: "content/release-notes",
docs: {
schema: frontmatterSchema
},
meta: {
schema: metaSchema
}
});
var source_config_default = defineConfig({
mdxOptions: {
remarkPlugins: [remarkAdmonition],
rehypePlugins: []
}
});
export {
source_config_default as default,
docs,
releaseNotes
};
19 changes: 17 additions & 2 deletions app/(docs)/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { notFound } from 'next/navigation';
import { createRelativeLink } from 'fumadocs-ui/mdx';
import { getMDXComponents } from '@/mdx-components';
import { EditOnGitHub } from './page.client';
import { CopyPageDropdown } from '@/components/CopyPageDropdown';

const owner = 'parseablehq';
const repo = 'developer-hub';
Expand All @@ -33,11 +34,16 @@ export default async function Page(props: {
const MDXContent = page.data.body;

return (
<DocsPage toc={page.data.toc} full={page.data.full}>
<DocsPage
toc={page.data.toc}
full={page.data.full}
tableOfContent={{
header: <CopyPageDropdown slug={params.slug} filePath={page.file.path} />,
}}
>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<div className="flex flex-row gap-2 items-center mb-4">
{/* <LLMCopyButton slug={params.slug} /> */}
<EditOnGitHub
url={`https://github.com/${owner}/${repo}/tree/main/${path}`}
/>
Expand Down Expand Up @@ -65,6 +71,15 @@ export async function generateMetadata(props: {
params: Promise<{ slug?: string[] }>;
}) {
const params = await props.params;

// Handle root /docs path
if (!params.slug || params.slug.length === 0) {
return {
title: 'Documentation | Parseable',
description: 'Parseable documentation and guides',
};
}

const page = source.getPage(params.slug);
if (!page) notFound();

Expand Down
10 changes: 7 additions & 3 deletions app/(docs)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import type { ReactNode } from 'react';
import { baseOptions } from '@/app/layout.config';
import { source } from '@/lib/source';
import SearchButton from '@/components/SearchButton';

export default function Layout({ children }: { children: ReactNode }) {
return (
<DocsLayout
tree={source.pageTree}
{...baseOptions}
// sidebar={{
// enabled: false
// }}
searchToggle={{
components: {
sm: <SearchButton />,
lg: <SearchButton />,
},
}}
>
{children}
</DocsLayout>
Expand Down
71 changes: 71 additions & 0 deletions app/api/ai-search/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
try {
const { query } = await request.json();

if (!query || typeof query !== 'string') {
return NextResponse.json(
{ error: 'Query is required' },
{ status: 400 }
);
}

const apiKey = process.env.TAVILY_API_KEY;
if (!apiKey) {
return NextResponse.json(
{ error: 'Tavily API key not configured' },
{ status: 500 }
);
}

// Search specifically within Parseable documentation
const response = await fetch('https://api.tavily.com/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
api_key: apiKey,
query: `${query} site:parseable.com OR site:parseable.io`,
search_depth: 'advanced',
include_answer: true,
include_raw_content: false,
max_results: 5,
include_domains: ['parseable.com', 'parseable.io', 'docs.parseable.com'],
}),
});

if (!response.ok) {
const errorText = await response.text();
console.error('Tavily API error:', errorText);
return NextResponse.json(
{ error: 'Failed to fetch AI search results' },
{ status: response.status }
);
}

const data = await response.json();

return NextResponse.json({
answer: data.answer || null,
results: data.results?.map((result: {
title: string;
url: string;
content: string;
score?: number;
}) => ({
title: result.title,
url: result.url,
content: result.content,
score: result.score,
})) || [],
});
} catch (error) {
console.error('AI search error:', error);
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
);
}
}
Loading