Standard.site landing page built in Next.js

Inline code styling

brookie.blog b180817a ac9e7b71

verified
+107 -75
+107 -75
app/components/ExpandableField.tsx
··· 85 return entries.map(([name, prop]) => { 86 const constraints: string[] = [] 87 88 - if (prop.maxLength) constraints.push(`maxLength: ${ prop.maxLength }`) 89 - if (prop.maxGraphemes) constraints.push(`maxGraphemes: ${ prop.maxGraphemes }`) 90 if (prop.maxSize) { 91 const bytes = prop.maxSize 92 - const formatted = bytes >= 1_000_000 ? `${ bytes / 1_000_000 }MB` : bytes >= 1_000 ? `${ bytes / 1_000 }KB` : `${ bytes }B` 93 - constraints.push(`maxSize: ${ formatted }`) 94 } 95 - if (prop.accept) constraints.push(`accept: [${ prop.accept.join(', ') }]`) 96 97 if (prop.items) { 98 - if (prop.items.maxLength) constraints.push(`items.maxLength: ${ prop.items.maxLength }`) 99 - if (prop.items.maxGraphemes) constraints.push(`items.maxGraphemes: ${ prop.items.maxGraphemes }`) 100 } 101 102 let type = prop.type 103 - if (prop.format) type = `${ prop.type }:${ prop.format }` 104 if (prop.ref) type = prop.ref 105 if (prop.type === 'array' && prop.items) { 106 - type = `array<${ prop.items.type }>` 107 } 108 109 const refs = (prop as { refs?: string[] }).refs ··· 142 let type = def.type 143 144 if (def.type === 'array' && def.items) { 145 - type = `array<${ def.items.type }>` 146 - if (def.items.maxLength) constraints.push(`items.maxLength: ${ def.items.maxLength }`) 147 - if (def.items.maxGraphemes) constraints.push(`items.maxGraphemes: ${ def.items.maxGraphemes }`) 148 } 149 150 if ((def as ExtendedLexiconDef).minimum !== undefined) { 151 - constraints.push(`minimum: ${ (def as ExtendedLexiconDef).minimum }`) 152 } 153 if ((def as ExtendedLexiconDef).maximum !== undefined) { 154 - constraints.push(`maximum: ${ (def as ExtendedLexiconDef).maximum }`) 155 } 156 157 return { ··· 162 } 163 } 164 165 function TypeBadge({ name, type, required }: { name: string; type: string; required?: boolean }) { 166 return ( 167 <div className="flex flex-wrap items-center gap-2"> 168 <span className="font-mono font-semibold text-base leading-snug tracking-tight text-base-content"> 169 - { name } 170 </span> 171 <span className="rounded border border-sky-200 bg-sky-100 text-sky-800 dark:border-sky-900 dark:bg-sky-950 dark:text-sky-100 px-1 py-0.5 font-mono font-medium text-sm leading-none tracking-tight"> 172 - { type } 173 </span> 174 - { required && ( 175 <span className="rounded border border-red-200 bg-red-100 text-red-800 dark:border-red-900 dark:bg-red-950 dark:text-red-100 px-1 py-0.5 font-mono font-medium text-sm leading-none tracking-tight"> 176 required 177 </span> 178 - ) } 179 </div> 180 ) 181 } ··· 183 function RefBadge({ ref }: { ref: string }) { 184 return ( 185 <span className="rounded border border-emerald-200 bg-emerald-100 text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-100 px-1 py-0.5 font-mono font-medium text-sm leading-none tracking-tight"> 186 - { ref } 187 - </span> 188 ) 189 } 190 ··· 200 return ( 201 <div className="flex flex-col gap-1 p-4"> 202 <div className="flex flex-wrap items-center gap-2"> 203 - <span className="rounded border border-sky-200 bg-sky-100 text-sky-800 dark:border-sky-900 dark:bg-sky-950 dark:text-sky-100 px-1 py-0.5 font-mono font-medium text-sm leading-none tracking-tight"> 204 - { summary.type } 205 - </span> 206 </div> 207 208 - {/* Union refs as badges */ } 209 - { summary.refs && summary.refs.length > 0 && ( 210 <div className="flex flex-wrap gap-2"> 211 - { summary.refs.map((ref) => ( 212 - <RefBadge key={ ref } ref={ ref } /> 213 - )) } 214 </div> 215 - ) } 216 217 - { summary.constraints.length > 0 && ( 218 <div className="flex flex-wrap gap-4 font-mono text-sm leading-none tracking-tight text-muted"> 219 - { summary.constraints.map((constraint) => ( 220 - <span key={ constraint }>{ constraint }</span> 221 - )) } 222 </div> 223 - ) } 224 225 - { summary.description && ( 226 <p className="text-sm italic leading-snug tracking-tight text-muted"> 227 - { summary.description } 228 </p> 229 - ) } 230 </div> 231 ) 232 } ··· 253 const isExpandable = !!field.ref && !NON_EXPANDABLE_REFS.includes(field.ref) 254 255 const toggleExpand = () => { 256 - setExpanded( !expanded) 257 } 258 259 const resolvedDef = field.ref ? resolveRef(field.ref, schema) : null ··· 268 269 return ( 270 <div className="flex flex-col gap-1 border-b border-border p-4 last:border-b-0"> 271 - <TypeBadge name={ field.name } type={ field.type } required={ field.required } /> 272 273 - {/* Union refs as badges */ } 274 - { field.refs && field.refs.length > 0 && ( 275 <div className="flex flex-wrap gap-2"> 276 - { field.refs.map((ref) => ( 277 - <RefBadge key={ ref } ref={ ref } /> 278 - )) } 279 </div> 280 - ) } 281 282 - { field.constraints.length > 0 && ( 283 <div className="flex flex-wrap gap-4 font-mono text-sm leading-none tracking-tight text-muted"> 284 - { field.constraints.map((constraint) => ( 285 - <span key={ constraint }>{ constraint }</span> 286 - )) } 287 </div> 288 - ) } 289 290 - { field.description && ( 291 <p className="text-sm italic leading-snug tracking-tight text-muted"> 292 - { field.description } 293 </p> 294 - ) } 295 296 - { isExpandable && ( 297 <div className="pt-2"> 298 <div className="flex flex-col rounded-xl border border-border bg-base-200 overflow-hidden"> 299 - {/* Header button */ } 300 <button 301 - onClick={ toggleExpand } 302 - className={ `flex items-center gap-2 px-4 py-2.5 hover:bg-base-300 transition-colors hover:cursor-pointer ${ expanded ? 'border-b border-border' : '' }` } 303 > 304 <FileIcon className="size-4 text-base-content" /> 305 <span className="font-medium text-base leading-snug tracking-tight text-base-content"> 306 - { refDisplayName } 307 </span> 308 <motion.div 309 className="ml-auto" 310 - initial={ false } 311 animate={{ opacity: 1 }} 312 > 313 - { expanded ? ( 314 <EyeOffIcon className="size-4 text-muted" /> 315 ) : ( 316 <EyeIcon className="size-4 text-muted" /> 317 - ) } 318 </motion.div> 319 </button> 320 321 - {/* Expandable content */ } 322 - <AnimatePresence initial={ false }> 323 - { expanded && ( 324 <motion.div 325 initial={{ height: 0, opacity: 0 }} 326 animate={{ height: 'auto', opacity: 1 }} ··· 330 > 331 <div className="p-1"> 332 <div className="flex flex-col rounded-xl border border-border bg-card overflow-hidden"> 333 - { hasNestedFields ? ( 334 nestedFields.map((nestedField) => ( 335 <ExpandableField 336 - key={ nestedField.name } 337 - field={ nestedField } 338 - schema={ schema } 339 /> 340 )) 341 ) : resolvedDef ? ( 342 - <DefSummaryDisplay def={ resolvedDef as ExtendedLexiconDef } /> 343 - ) : null } 344 </div> 345 </div> 346 </motion.div> 347 - ) } 348 </AnimatePresence> 349 </div> 350 </div> 351 - ) } 352 </div> 353 ) 354 } ··· 368 return ( 369 <div className="p-1"> 370 <div className="flex flex-col rounded-xl border border-border bg-card overflow-hidden"> 371 - { fields.map((field) => ( 372 - <ExpandableField key={ field.name } field={ field } schema={ schema } /> 373 - )) } 374 </div> 375 </div> 376 )
··· 85 return entries.map(([name, prop]) => { 86 const constraints: string[] = [] 87 88 + if (prop.maxLength) constraints.push(`maxLength: ${prop.maxLength}`) 89 + if (prop.maxGraphemes) constraints.push(`maxGraphemes: ${prop.maxGraphemes}`) 90 if (prop.maxSize) { 91 const bytes = prop.maxSize 92 + const formatted = bytes >= 1_000_000 ? `${bytes / 1_000_000}MB` : bytes >= 1_000 ? `${bytes / 1_000}KB` : `${bytes}B` 93 + constraints.push(`maxSize: ${formatted}`) 94 } 95 + if (prop.accept) constraints.push(`accept: [${prop.accept.join(', ')}]`) 96 97 if (prop.items) { 98 + if (prop.items.maxLength) constraints.push(`items.maxLength: ${prop.items.maxLength}`) 99 + if (prop.items.maxGraphemes) constraints.push(`items.maxGraphemes: ${prop.items.maxGraphemes}`) 100 } 101 102 let type = prop.type 103 + if (prop.format) type = `${prop.type}:${prop.format}` 104 if (prop.ref) type = prop.ref 105 if (prop.type === 'array' && prop.items) { 106 + type = `array<${prop.items.type}>` 107 } 108 109 const refs = (prop as { refs?: string[] }).refs ··· 142 let type = def.type 143 144 if (def.type === 'array' && def.items) { 145 + type = `array<${def.items.type}>` 146 + if (def.items.maxLength) constraints.push(`items.maxLength: ${def.items.maxLength}`) 147 + if (def.items.maxGraphemes) constraints.push(`items.maxGraphemes: ${def.items.maxGraphemes}`) 148 } 149 150 if ((def as ExtendedLexiconDef).minimum !== undefined) { 151 + constraints.push(`minimum: ${(def as ExtendedLexiconDef).minimum}`) 152 } 153 if ((def as ExtendedLexiconDef).maximum !== undefined) { 154 + constraints.push(`maximum: ${(def as ExtendedLexiconDef).maximum}`) 155 } 156 157 return { ··· 162 } 163 } 164 165 + // Helper to parse description with inline code (backticks) 166 + function parseInlineCode(text: string): React.ReactNode { 167 + const parts: React.ReactNode[] = [] 168 + let lastIndex = 0 169 + const regex = /`([^`]+)`/g 170 + let match 171 + 172 + while ((match = regex.exec(text)) !== null) { 173 + // Add text before the match 174 + if (match.index > lastIndex) { 175 + parts.push(text.slice(lastIndex, match.index)) 176 + } 177 + // Add the code part with styling 178 + parts.push( 179 + <code 180 + key={match.index} 181 + className="rounded bg-base-200 px-1 mx-0.5 font-mono text-sm text-base-content italic" 182 + > 183 + {match[1]} 184 + </code> 185 + ) 186 + lastIndex = match.index + match[0].length 187 + } 188 + 189 + // Add remaining text 190 + if (lastIndex < text.length) { 191 + parts.push(text.slice(lastIndex)) 192 + } 193 + 194 + return parts.length > 0 ? parts : text 195 + } 196 + 197 function TypeBadge({ name, type, required }: { name: string; type: string; required?: boolean }) { 198 return ( 199 <div className="flex flex-wrap items-center gap-2"> 200 <span className="font-mono font-semibold text-base leading-snug tracking-tight text-base-content"> 201 + {name} 202 </span> 203 <span className="rounded border border-sky-200 bg-sky-100 text-sky-800 dark:border-sky-900 dark:bg-sky-950 dark:text-sky-100 px-1 py-0.5 font-mono font-medium text-sm leading-none tracking-tight"> 204 + {type} 205 </span> 206 + {required && ( 207 <span className="rounded border border-red-200 bg-red-100 text-red-800 dark:border-red-900 dark:bg-red-950 dark:text-red-100 px-1 py-0.5 font-mono font-medium text-sm leading-none tracking-tight"> 208 required 209 </span> 210 + )} 211 </div> 212 ) 213 } ··· 215 function RefBadge({ ref }: { ref: string }) { 216 return ( 217 <span className="rounded border border-emerald-200 bg-emerald-100 text-emerald-800 dark:border-emerald-900 dark:bg-emerald-950 dark:text-emerald-100 px-1 py-0.5 font-mono font-medium text-sm leading-none tracking-tight"> 218 + {ref} 219 + </span> 220 ) 221 } 222 ··· 232 return ( 233 <div className="flex flex-col gap-1 p-4"> 234 <div className="flex flex-wrap items-center gap-2"> 235 + <span className="rounded border border-sky-200 bg-sky-100 text-sky-800 dark:border-sky-900 dark:bg-sky-950 dark:text-sky-100 px-1 py-0.5 font-mono font-medium text-sm leading-none tracking-tight"> 236 + {summary.type} 237 + </span> 238 </div> 239 240 + {/* Union refs as badges */} 241 + {summary.refs && summary.refs.length > 0 && ( 242 <div className="flex flex-wrap gap-2"> 243 + {summary.refs.map((ref) => ( 244 + <RefBadge key={ref} ref={ref} /> 245 + ))} 246 </div> 247 + )} 248 249 + {summary.constraints.length > 0 && ( 250 <div className="flex flex-wrap gap-4 font-mono text-sm leading-none tracking-tight text-muted"> 251 + {summary.constraints.map((constraint) => ( 252 + <span key={constraint}>{constraint}</span> 253 + ))} 254 </div> 255 + )} 256 257 + {summary.description && ( 258 <p className="text-sm italic leading-snug tracking-tight text-muted"> 259 + {parseInlineCode(summary.description)} 260 </p> 261 + )} 262 </div> 263 ) 264 } ··· 285 const isExpandable = !!field.ref && !NON_EXPANDABLE_REFS.includes(field.ref) 286 287 const toggleExpand = () => { 288 + setExpanded(!expanded) 289 } 290 291 const resolvedDef = field.ref ? resolveRef(field.ref, schema) : null ··· 300 301 return ( 302 <div className="flex flex-col gap-1 border-b border-border p-4 last:border-b-0"> 303 + <TypeBadge name={field.name} type={field.type} required={field.required} /> 304 305 + {/* Union refs as badges */} 306 + {field.refs && field.refs.length > 0 && ( 307 <div className="flex flex-wrap gap-2"> 308 + {field.refs.map((ref) => ( 309 + <RefBadge key={ref} ref={ref} /> 310 + ))} 311 </div> 312 + )} 313 314 + {field.constraints.length > 0 && ( 315 <div className="flex flex-wrap gap-4 font-mono text-sm leading-none tracking-tight text-muted"> 316 + {field.constraints.map((constraint) => ( 317 + <span key={constraint}>{constraint}</span> 318 + ))} 319 </div> 320 + )} 321 322 + {field.description && ( 323 <p className="text-sm italic leading-snug tracking-tight text-muted"> 324 + {parseInlineCode(field.description)} 325 </p> 326 + )} 327 328 + {isExpandable && ( 329 <div className="pt-2"> 330 <div className="flex flex-col rounded-xl border border-border bg-base-200 overflow-hidden"> 331 + {/* Header button */} 332 <button 333 + onClick={toggleExpand} 334 + className={`flex items-center gap-2 px-4 py-2.5 hover:bg-base-300 transition-colors hover:cursor-pointer ${expanded ? 'border-b border-border' : ''}`} 335 > 336 <FileIcon className="size-4 text-base-content" /> 337 <span className="font-medium text-base leading-snug tracking-tight text-base-content"> 338 + {refDisplayName} 339 </span> 340 <motion.div 341 className="ml-auto" 342 + initial={false} 343 animate={{ opacity: 1 }} 344 > 345 + {expanded ? ( 346 <EyeOffIcon className="size-4 text-muted" /> 347 ) : ( 348 <EyeIcon className="size-4 text-muted" /> 349 + )} 350 </motion.div> 351 </button> 352 353 + {/* Expandable content */} 354 + <AnimatePresence initial={false}> 355 + {expanded && ( 356 <motion.div 357 initial={{ height: 0, opacity: 0 }} 358 animate={{ height: 'auto', opacity: 1 }} ··· 362 > 363 <div className="p-1"> 364 <div className="flex flex-col rounded-xl border border-border bg-card overflow-hidden"> 365 + {hasNestedFields ? ( 366 nestedFields.map((nestedField) => ( 367 <ExpandableField 368 + key={nestedField.name} 369 + field={nestedField} 370 + schema={schema} 371 /> 372 )) 373 ) : resolvedDef ? ( 374 + <DefSummaryDisplay def={resolvedDef as ExtendedLexiconDef} /> 375 + ) : null} 376 </div> 377 </div> 378 </motion.div> 379 + )} 380 </AnimatePresence> 381 </div> 382 </div> 383 + )} 384 </div> 385 ) 386 } ··· 400 return ( 401 <div className="p-1"> 402 <div className="flex flex-col rounded-xl border border-border bg-card overflow-hidden"> 403 + {fields.map((field) => ( 404 + <ExpandableField key={field.name} field={field} schema={schema} /> 405 + ))} 406 </div> 407 </div> 408 )