Skip to content

Commit

Permalink
feat: make things work
Browse files Browse the repository at this point in the history
  • Loading branch information
2color committed Jan 21, 2025
1 parent c2e7624 commit 5dbece2
Show file tree
Hide file tree
Showing 19 changed files with 11,207 additions and 903 deletions.
21 changes: 21 additions & 0 deletions components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "styles/globals.css",
"baseColor": "slate",
"cssVariables": false,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
182 changes: 182 additions & 0 deletions components/ipns-inspector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import React from 'react'
import { useMachine } from '@xstate/react'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { createBrowserInspector } from '@statelyai/inspect'
import { ipnsMachine, Mode } from '../lib/ipns-machine'
import { Spinner } from './ui/spinner'

const MIN_SEQUENCE = 0
const MAX_VALIDITY = 365 * 24 * 60 * 60 // 1 year in seconds

const inspector = createBrowserInspector({
autoStart: false,
})

interface RecordFieldProps {
label: string;
value: string;
monospace?: boolean;
}



// Simplified component
export default function IPNSInspector() {
const [state, send] = useMachine(ipnsMachine, {
inspect: inspector?.inspect,
})
const isLoading = state.value === 'init'
console.log(state.value)
console.log(state.context)

return (
<Card className="w-full max-w-2xl mx-auto">
<CardHeader>
<CardTitle>IPNS Record Inspector & Creator</CardTitle>
</CardHeader>
<CardContent>
<Tabs
value={state.value}
onValueChange={(value) => send({ type: 'UPDATE_MODE', value: value as Mode })}
>
<TabsList className="mb-4">
<TabsTrigger value="inspect">Inspect Record</TabsTrigger>
<TabsTrigger value="create">Create Record</TabsTrigger>
</TabsList>

{state.value === 'init' && <div>Loading...</div>}

<TabsContent value="inspect">
<div className="space-y-4">
<div className="space-y-2">
<label className="block text-sm font-medium">IPNS Name</label>
<div className="flex gap-2">
<Input
value={state.context.nameToInspect}
onChange={(e) => send({ type: 'UPDATE_NAME_TO_INSPECT', value: e.target.value })}
placeholder="k51... or 12D..."
/>
{
<Button
onClick={() => send({ type: 'INSPECT_NAME' })}
disabled={
isLoading ||
state.context.nameValidationError != null ||
state.context.nameToInspect.length === 0
}
>
Fetch Record {state.context.fetchingRecord ? <Spinner /> : null}
</Button>
}
</div>
{state.context.nameValidationError && (
<Alert variant="destructive" className="mt-4">
<AlertDescription>{state.context.nameValidationError}</AlertDescription>
</Alert>
)}
</div>
</div>
</TabsContent>

<TabsContent value="create">
<div className="space-y-4">
<div className="space-y-2">
<label className="block text-sm font-medium">Value</label>
<Input
value={state.context.formData.value}
onChange={(e) => send({ type: 'UPDATE_FORM', field: 'value', value: e.target.value })}
placeholder="CID or path to publish"
/>
</div>

<div className="space-y-2">
<label className="block text-sm font-medium">TTL (seconds)</label>
<Input
type="number"
value={state.context.formData.ttl}
onChange={(e) => send({ type: 'UPDATE_FORM', field: 'ttl', value: e.target.value })}
min="1"
/>
</div>

<div className="space-y-2">
<label className="block text-sm font-medium">Validity (seconds)</label>
<Input
type="number"
value={state.context.formData.validity}
onChange={(e) => send({ type: 'UPDATE_FORM', field: 'validity', value: e.target.value })}
min="1"
max={MAX_VALIDITY}
/>
</div>

<div className="space-y-2">
<label className="block text-sm font-medium">Sequence Number</label>
<Input
type="number"
value={state.context.formData.sequence}
onChange={(e) => send({ type: 'UPDATE_FORM', field: 'sequence', value: e.target.value })}
min={MIN_SEQUENCE}
/>
</div>

<Button onClick={() => send({ type: 'CREATE' })} disabled={isLoading} className="w-full">
Create Record
</Button>
</div>
</TabsContent>

{state.context.error && (
<Alert variant="destructive" className="mt-4">
<AlertDescription>{state.context.error.toString()}</AlertDescription>
</Alert>
)}

{state.context.record && (
<div className="mt-4 p-4 bg-gray-50 rounded">
<h3 className="font-medium mb-2">Name: {state.context.nameInspecting}</h3>
<h3 className="font-medium mb-2">
IPNS Record Version: {state.context.record.hasOwnProperty('signatureV1') ? 'V1+V2' : 'V2'}
</h3>
<div className="grid grid-cols-1 gap-3">
<RecordField label="Value" value={state.context.record.value} />
<RecordField label="Validity Type" value={state.context.record.validityType} />
<RecordField label="Validity" value={state.context.record.validity} />
<RecordField label="Sequence" value={state.context.record.sequence.toString()} />
<RecordField
label="TTL"
value={state.context.record.ttl ? (Number(state.context.record.ttl) / 1e9).toString() + ' seconds' : 'Not set'}
/>
<RecordField
label="Signature V2"
value={state.context.record.signatureV2.toString()}
monospace
/>
<RecordField
label="Data"
value={state.context.record.data.toString()}
monospace
/>
</div>
</div>
)}
</Tabs>
</CardContent>
</Card>
)
}

const RecordField: React.FC<RecordFieldProps> = ({ label, value, monospace }) => (
<div className="border rounded p-3 bg-white">
<div className="text-sm font-medium text-gray-500 mb-1">{label}</div>
<div className={`break-all ${monospace ? 'font-mono text-sm' : ''}`}>
{value}
</div>
</div>
);


59 changes: 59 additions & 0 deletions components/ui/alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"

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

const alertVariants = cva(
"relative w-full rounded-lg border border-slate-200 p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-slate-950 dark:border-slate-800 dark:[&>svg]:text-slate-50",
{
variants: {
variant: {
default: "bg-white text-slate-950 dark:bg-slate-950 dark:text-slate-50",
destructive:
"border-red-500/50 text-red-500 dark:border-red-500 [&>svg]:text-red-500 dark:border-red-900/50 dark:text-red-900 dark:dark:border-red-900 dark:[&>svg]:text-red-900",
},
},
defaultVariants: {
variant: "default",
},
}
)

const Alert = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
<div
ref={ref}
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
))
Alert.displayName = "Alert"

const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5
ref={ref}
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...props}
/>
))
AlertTitle.displayName = "AlertTitle"

const AlertDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm [&_p]:leading-relaxed", className)}
{...props}
/>
))
AlertDescription.displayName = "AlertDescription"

export { Alert, AlertTitle, AlertDescription }
56 changes: 56 additions & 0 deletions components/ui/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"

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

const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 dark:ring-offset-slate-950 dark:focus-visible:ring-slate-300",
{
variants: {
variant: {
default: "bg-slate-900 text-slate-50 hover:bg-slate-900/90 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-50/90",
destructive:
"bg-red-500 text-slate-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-slate-50 dark:hover:bg-red-900/90",
outline:
"border border-slate-200 bg-white hover:bg-slate-100 hover:text-slate-900 dark:border-slate-800 dark:bg-slate-950 dark:hover:bg-slate-800 dark:hover:text-slate-50",
secondary:
"bg-slate-100 text-slate-900 hover:bg-slate-100/80 dark:bg-slate-800 dark:text-slate-50 dark:hover:bg-slate-800/80",
ghost: "hover:bg-slate-100 hover:text-slate-900 dark:hover:bg-slate-800 dark:hover:text-slate-50",
link: "text-slate-900 underline-offset-4 hover:underline dark:text-slate-50",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)

export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"

export { Button, buttonVariants }
Loading

0 comments on commit 5dbece2

Please sign in to comment.