kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
pull/718/head
rodzic
4faadbf166
commit
a23fc97c56
|
@ -1,51 +0,0 @@
|
||||||
import { parseProjectIdentifier } from '@agentic/platform-validators'
|
|
||||||
import { notFound } from 'next/navigation'
|
|
||||||
|
|
||||||
import { toastError } from '@/lib/notifications'
|
|
||||||
|
|
||||||
import { MarketplacePublicProjectDetail } from '../marketplace-public-project-detail'
|
|
||||||
import {
|
|
||||||
type MarketplacePublicProjectDetailTab,
|
|
||||||
marketplacePublicProjectDetailTabsSet
|
|
||||||
} from '../utils'
|
|
||||||
|
|
||||||
export default async function PublicProjectDetailPageTabPage({
|
|
||||||
params
|
|
||||||
}: {
|
|
||||||
params: Promise<{
|
|
||||||
namespace: string
|
|
||||||
'project-slug': string
|
|
||||||
tab: string
|
|
||||||
}>
|
|
||||||
}) {
|
|
||||||
const {
|
|
||||||
namespace: rawNamespace,
|
|
||||||
'project-slug': rawProjectSlug,
|
|
||||||
tab
|
|
||||||
} = await params
|
|
||||||
|
|
||||||
if (!marketplacePublicProjectDetailTabsSet.has(tab)) {
|
|
||||||
return notFound()
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const namespace = decodeURIComponent(rawNamespace)
|
|
||||||
const projectSlug = decodeURIComponent(rawProjectSlug)
|
|
||||||
|
|
||||||
const { projectIdentifier } = parseProjectIdentifier(
|
|
||||||
`${namespace}/${projectSlug}`,
|
|
||||||
{ strict: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MarketplacePublicProjectDetail
|
|
||||||
projectIdentifier={projectIdentifier}
|
|
||||||
tab={tab as MarketplacePublicProjectDetailTab}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
} catch (err: any) {
|
|
||||||
void toastError(err, { label: 'Invalid project identifier' })
|
|
||||||
|
|
||||||
return notFound()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,8 +8,8 @@ import { useRouter, useSearchParams } from 'next/navigation'
|
||||||
import plur from 'plur'
|
import plur from 'plur'
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
|
||||||
import { ActiveLink } from '@/components/active-link'
|
|
||||||
import { useAgentic } from '@/components/agentic-provider'
|
import { useAgentic } from '@/components/agentic-provider'
|
||||||
|
import { CodeBlock } from '@/components/code-block'
|
||||||
import { ExampleUsage } from '@/components/example-usage'
|
import { ExampleUsage } from '@/components/example-usage'
|
||||||
import { HeroButton } from '@/components/hero-button'
|
import { HeroButton } from '@/components/hero-button'
|
||||||
import { LoadingIndicator } from '@/components/loading-indicator'
|
import { LoadingIndicator } from '@/components/loading-indicator'
|
||||||
|
@ -29,15 +29,14 @@ import { useQuery } from '@/lib/query-client'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type MarketplacePublicProjectDetailTab,
|
type MarketplacePublicProjectDetailTab,
|
||||||
|
marketplacePublicProjectDetailTabsSet,
|
||||||
MAX_TOOLS_TO_SHOW
|
MAX_TOOLS_TO_SHOW
|
||||||
} from './utils'
|
} from './utils'
|
||||||
|
|
||||||
export function MarketplacePublicProjectDetail({
|
export function MarketplacePublicProjectDetail({
|
||||||
projectIdentifier,
|
projectIdentifier
|
||||||
tab = 'overview'
|
|
||||||
}: {
|
}: {
|
||||||
projectIdentifier: string
|
projectIdentifier: string
|
||||||
tab?: MarketplacePublicProjectDetailTab
|
|
||||||
}) {
|
}) {
|
||||||
const ctx = useAgentic()
|
const ctx = useAgentic()
|
||||||
const searchParams = useSearchParams()
|
const searchParams = useSearchParams()
|
||||||
|
@ -177,13 +176,18 @@ export function MarketplacePublicProjectDetail({
|
||||||
)
|
)
|
||||||
}, [deployment])
|
}, [deployment])
|
||||||
|
|
||||||
const inferredTab = useMemo(() => {
|
const tab = useMemo<MarketplacePublicProjectDetailTab>(() => {
|
||||||
|
const tab = searchParams.get('tab')?.toLowerCase()
|
||||||
|
if (!tab || !marketplacePublicProjectDetailTabsSet.has(tab)) {
|
||||||
|
return 'overview'
|
||||||
|
}
|
||||||
|
|
||||||
if (tab === 'readme' && !deployment?.readme?.trim()) {
|
if (tab === 'readme' && !deployment?.readme?.trim()) {
|
||||||
return 'overview'
|
return 'overview'
|
||||||
}
|
}
|
||||||
|
|
||||||
return tab
|
return tab as MarketplacePublicProjectDetailTab
|
||||||
}, [tab, deployment])
|
}, [searchParams, deployment])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
|
@ -196,16 +200,16 @@ export function MarketplacePublicProjectDetail({
|
||||||
<p>Project "{projectIdentifier}" not found</p>
|
<p>Project "{projectIdentifier}" not found</p>
|
||||||
) : (
|
) : (
|
||||||
<div className='flex flex-col gap-4 w-full'>
|
<div className='flex flex-col gap-4 w-full'>
|
||||||
<ProjectHeader project={project} tab={inferredTab} />
|
<ProjectHeader project={project} tab={tab} />
|
||||||
|
|
||||||
<Tabs
|
<Tabs
|
||||||
value={inferredTab}
|
value={tab}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
if (value === 'overview') {
|
if (value === 'overview') {
|
||||||
router.push(`/marketplace/projects/${projectIdentifier}`)
|
router.push(`/marketplace/projects/${projectIdentifier}`)
|
||||||
} else {
|
} else {
|
||||||
router.push(
|
router.push(
|
||||||
`/marketplace/projects/${projectIdentifier}/${value}`
|
`/marketplace/projects/${projectIdentifier}?tab=${value}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
@ -243,7 +247,7 @@ export function MarketplacePublicProjectDetail({
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
<div className='bg-card p-4 border rounded-lg shadow-sm color-card-foreground'>
|
<div className='bg-card p-4 border rounded-lg shadow-sm color-card-foreground'>
|
||||||
{inferredTab === 'overview' && (
|
{tab === 'overview' && (
|
||||||
<TabsContent value='overview' className='flex flex-col gap-4'>
|
<TabsContent value='overview' className='flex flex-col gap-4'>
|
||||||
<h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'>
|
<h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'>
|
||||||
Overview
|
Overview
|
||||||
|
@ -292,7 +296,7 @@ export function MarketplacePublicProjectDetail({
|
||||||
variant='outline'
|
variant='outline'
|
||||||
>
|
>
|
||||||
<Link
|
<Link
|
||||||
href={`/marketplace/projects/${projectIdentifier}/tools`}
|
href={`/marketplace/projects/${projectIdentifier}?tab=tools`}
|
||||||
>
|
>
|
||||||
View{' '}
|
View{' '}
|
||||||
{deployment.tools.length -
|
{deployment.tools.length -
|
||||||
|
@ -328,7 +332,7 @@ export function MarketplacePublicProjectDetail({
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{deployment?.readme?.trim() && inferredTab === 'readme' && (
|
{deployment?.readme?.trim() && tab === 'readme' && (
|
||||||
<TabsContent value='readme' className='flex flex-col gap-4'>
|
<TabsContent value='readme' className='flex flex-col gap-4'>
|
||||||
<SSRMarkdown
|
<SSRMarkdown
|
||||||
markdown={deployment.readme}
|
markdown={deployment.readme}
|
||||||
|
@ -337,7 +341,7 @@ export function MarketplacePublicProjectDetail({
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{inferredTab === 'tools' && (
|
{tab === 'tools' && (
|
||||||
<TabsContent value='tools' className='flex flex-col gap-4'>
|
<TabsContent value='tools' className='flex flex-col gap-4'>
|
||||||
<h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'>
|
<h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'>
|
||||||
Tools
|
Tools
|
||||||
|
@ -370,9 +374,15 @@ export function MarketplacePublicProjectDetail({
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
|
|
||||||
<CollapsibleContent>
|
<CollapsibleContent>
|
||||||
<pre className='max-w-full overflow-x-auto border p-4 rounded-sm'>
|
<CodeBlock
|
||||||
{JSON.stringify(tool.inputSchema, null, 2)}
|
lang='json'
|
||||||
</pre>
|
code={JSON.stringify(
|
||||||
|
tool.inputSchema,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}
|
||||||
|
className='border rounded-sm'
|
||||||
|
/>
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
|
|
||||||
|
@ -389,9 +399,15 @@ export function MarketplacePublicProjectDetail({
|
||||||
</CollapsibleTrigger>
|
</CollapsibleTrigger>
|
||||||
|
|
||||||
<CollapsibleContent>
|
<CollapsibleContent>
|
||||||
<pre className='max-w-full overflow-x-auto border p-4 rounded-sm'>
|
<CodeBlock
|
||||||
{JSON.stringify(tool.outputSchema, null, 2)}
|
lang='json'
|
||||||
</pre>
|
code={JSON.stringify(
|
||||||
|
tool.outputSchema,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}
|
||||||
|
className='border rounded-sm'
|
||||||
|
/>
|
||||||
</CollapsibleContent>
|
</CollapsibleContent>
|
||||||
</Collapsible>
|
</Collapsible>
|
||||||
)}
|
)}
|
||||||
|
@ -402,7 +418,7 @@ export function MarketplacePublicProjectDetail({
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{inferredTab === 'pricing' && (
|
{tab === 'pricing' && (
|
||||||
<TabsContent value='pricing' className='flex flex-col gap-4'>
|
<TabsContent value='pricing' className='flex flex-col gap-4'>
|
||||||
<h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'>
|
<h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'>
|
||||||
Pricing
|
Pricing
|
||||||
|
@ -419,7 +435,7 @@ export function MarketplacePublicProjectDetail({
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{inferredTab === 'debug' && (
|
{tab === 'debug' && (
|
||||||
<TabsContent value='debug' className='flex flex-col gap-4'>
|
<TabsContent value='debug' className='flex flex-col gap-4'>
|
||||||
<h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'>
|
<h2 className='text-balance leading-snug md:leading-none text-xl font-semibold'>
|
||||||
Debug
|
Debug
|
||||||
|
@ -476,11 +492,11 @@ function ProjectHeader({
|
||||||
className='justify-self-end'
|
className='justify-self-end'
|
||||||
disabled={tab === 'pricing'}
|
disabled={tab === 'pricing'}
|
||||||
>
|
>
|
||||||
<ActiveLink
|
<Link
|
||||||
href={`/marketplace/projects/${project.identifier}/pricing`}
|
href={`/marketplace/projects/${project.identifier}?tab=pricing`}
|
||||||
>
|
>
|
||||||
Subscribe to {project.identifier}
|
Subscribe to {project.identifier}
|
||||||
</ActiveLink>
|
</Link>
|
||||||
</HeroButton>
|
</HeroButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,13 @@ export function ProjectPricingPlans({
|
||||||
onSubscribe: (planSlug: string) => void
|
onSubscribe: (planSlug: string) => void
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
|
// TODO: add support for different pricing intervals
|
||||||
const numPricingPlans =
|
const numPricingPlans =
|
||||||
project.lastPublishedDeployment?.pricingPlans.length || 1
|
project.lastPublishedDeployment?.pricingPlans.length || 1
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`grid grid-cols grid-cols-1 gap-8 sm:grid-cols-${Math.min(
|
className={`grid grid-cols grid-cols-1 gap-4 sm:grid-cols-${Math.min(
|
||||||
2,
|
2,
|
||||||
numPricingPlans
|
numPricingPlans
|
||||||
)} xl:grid-cols-${Math.min(3, numPricingPlans)}`}
|
)} xl:grid-cols-${Math.min(3, numPricingPlans)}`}
|
||||||
|
|
25
todo.md
25
todo.md
|
@ -12,30 +12,24 @@
|
||||||
- example usage
|
- example usage
|
||||||
- double check example usage for all TS sdks now that real examples are working
|
- double check example usage for all TS sdks now that real examples are working
|
||||||
- fix mcp examples
|
- fix mcp examples
|
||||||
- import react example usage component into docs
|
- **replace json pricing plans and consumers with actual designs**
|
||||||
- replace json pricing plans and consumers with actual designs
|
|
||||||
- double-check free-tier rate-limits for `@agentic/search`
|
- double-check free-tier rate-limits for `@agentic/search`
|
||||||
- add feature about optimized context to docs
|
|
||||||
- add ts sdk examples to e2e tests
|
|
||||||
- submit to awesome mcp, mcp discord, etc
|
- submit to awesome mcp, mcp discord, etc
|
||||||
- add scroll appearance motion to hero animation
|
|
||||||
- replace how-it-works diagram with better version
|
- replace how-it-works diagram with better version
|
||||||
- docs: add notes about constraints on mcp origin servers (static tools)
|
- docs: add notes about constraints on mcp origin servers (static tools)
|
||||||
- improve public project detail page
|
- improve public project detail page
|
||||||
- mcp inspector
|
- mcp inspector
|
||||||
- **add support to example-usage for api keys**
|
- **add support to example-usage for api keys**
|
||||||
- **api keys should go beyond 1:1 consumers**
|
- **api keys should go beyond 1:1 consumers**
|
||||||
- replace JSON schemas (tool input/output schemas) with `json` code blocks
|
|
||||||
- improve upgrade flow UX
|
|
||||||
- **currently not obvious how to get api key**
|
- **currently not obvious how to get api key**
|
||||||
|
- improve upgrade flow UX
|
||||||
- marketplace project page
|
- marketplace project page
|
||||||
- replace tabs with links
|
- break out into a few subcomponents; some can be server components
|
||||||
- too much of a delay on individual pages and makes no sense to have these imports on the other tab pages
|
- add last published date somewhere
|
||||||
- add last published date to
|
|
||||||
- add breadcrumb nav: marketplace > @agentic > search
|
- add breadcrumb nav: marketplace > @agentic > search
|
||||||
- add a basic page + docs on pricing
|
- tool input/output schemas; move `$schema` to top
|
||||||
- [react query prefetching for public pages](https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#prefetching-and-dehydrating-data)
|
- add a basic page + docs on pricing => contact
|
||||||
- add [ping](https://modelcontextprotocol.io/specification/2025-03-26/basic/utilities/ping) support to mcp servers
|
- [**react query prefetching for public pages**](https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr#prefetching-and-dehydrating-data)
|
||||||
- add ability to point at remote readmes, icons, files, urls, etc and upload to our own blob storage at deploy time
|
- add ability to point at remote readmes, icons, files, urls, etc and upload to our own blob storage at deploy time
|
||||||
- **create agentic products for select legacy tools**
|
- **create agentic products for select legacy tools**
|
||||||
|
|
||||||
|
@ -131,3 +125,8 @@
|
||||||
- add support for [`@google/genai`](https://github.com/googleapis/js-genai) tools adapter
|
- add support for [`@google/genai`](https://github.com/googleapis/js-genai) tools adapter
|
||||||
- currently difficult due to their use of non-standard json schemas
|
- currently difficult due to their use of non-standard json schemas
|
||||||
- validate example args against the tool's input schema during config validation
|
- validate example args against the tool's input schema during config validation
|
||||||
|
- add scroll appearance motion to hero animation
|
||||||
|
- add ts sdk examples to e2e tests
|
||||||
|
- add feature about optimized context to docs
|
||||||
|
- import react example usage component into docs
|
||||||
|
- add [ping](https://modelcontextprotocol.io/specification/2025-03-26/basic/utilities/ping) support to mcp servers
|
||||||
|
|
Ładowanie…
Reference in New Issue