tangled
alpha
login
or
join now
e.xyehr.cn
/
standard.site
forked from
standard.site/standard.site
0
fork
atom
Standard.site landing page built in Next.js
0
fork
atom
overview
issues
pulls
pipelines
Inline code styling
brookie.blog
2 months ago
b180817a
ac9e7b71
verified
This commit was signed with the committer's
known signature
.
brookie.blog
SSH Key Fingerprint:
SHA256:mpAy8tRb8/3HN/v8kJeSgvRPQnKQq5zVHkq1TxQaVls=
+107
-75
1 changed file
expand all
collapse all
unified
split
app
components
ExpandableField.tsx
+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
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
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
)